Skip to content

casablanque-code/netconv

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

94 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

netconv

CI Rust WASM License: MIT

Vendor-agnostic network config compiler.

netconv reads network device configurations, builds a vendor-neutral intermediate representation, and compiles it to any target platform — with an explicit, annotated report of every decision made.

netconv build router.cfg --target vrp

Like a compiler: source → IR → target. Unlike a converter: every approximation is documented, every risk is flagged, nothing is silently wrong.

Live demo: https://netconv.casablanque.com


Why netconv

Migrating network configs between vendors is painful:

  • Manual migration — slow, error-prone, no audit trail
  • Ansible/Terraform — manage desired state, can't read and explain what you already have
  • Vendor tools — locked to their ecosystem, won't tell you what you'll lose migrating away

netconv occupies a different position: it reads your existing config, explains every translation decision, and makes every limitation explicit. A silent conversion that produces a wrong config is worse than no conversion at all.

Primary use case: Cisco → Huawei migration driven by import substitution requirements in Russia and CIS markets. Secondary use cases: pre-migration audit, config review, engineer onboarding.


Core principle: failure is explicit

Every output element carries a confidence level:

Level Meaning
Exact Guaranteed correct — direct syntactic equivalent
Approximate Functional equivalent with caveats — report explains what to verify
Manual No direct equivalent — commented block with recommended approach
Platform Vendor-specific command with no cross-platform concept

netconv never silently drops commands. Everything unrecognised is preserved as a commented block with context.


L2 and L3 are never mixed

A switch is not a router, and no one replaces one with the other. netconv treats "convert a switch config" and "convert a router config" as two different jobs, not two branches of the same function:

  • L2 profile — VLANs, access/trunk ports, STP, voice VLAN, storm-control. Targets a switch platform (e.g. Huawei S-series).
  • L3 profile — IP addressing, static/OSPF/BGP routing, ACLs, NAT, HSRP/VRRP. Targets a router platform (e.g. Huawei AR/NE).

You pick the profile explicitly (CLI --profile l2|l3, web UI tabs) — netconv never guesses it from the config's contents. If the source config contains commands from the other domain (OSPF found while converting under --profile l2, for example), netconv reports it explicitly instead of either rendering nonsense or quietly dropping it.

netconv --input switch.cfg  --to vrp --profile l2   # VLANs/switchport only
netconv --input router.cfg  --to vrp --profile l3   # routing/ACL/NAT only

This is implemented per-vendor-pair, not as a blanket rule — see the coverage table below for which pairs already enforce it (currently ios → vrp and ios → eltex).


Quickstart

Option 1 — Browser (no install)

Open live demo, paste config, press convert or Ctrl+Enter.

Config is processed locally in the browser via WASM — never sent to a server.

Option 2 — Local with full WASM parser

git clone https://github.com/casablanque-code/netconv.git
cd netconv

# Build WASM
cargo install wasm-pack
wasm-pack build crates/netconv-wasm --target web --out-dir ../../web/wasm --release

# Serve locally
python3 -m http.server 8080 --directory web/

# Open http://localhost:8080
# "demo mode" badge disappears — full Rust parser active

WSL: ports forward automatically in WSL2. Use http://localhost:8080 in Windows browser.

Option 3 — CLI

cargo build --release -p netconv

# Convert (stdout)
./target/release/netconv --input router.cfg --from ios --to vrp

# Convert with explicit device profile — filters L2 vs L3 domain (see below)
./target/release/netconv --input switch.cfg --to vrp --profile l2
./target/release/netconv --input router.cfg --to vrp --profile l3

# Write to file
./target/release/netconv --input router.cfg --to vrp --output router_vrp.cfg

# Show warnings and manual items
./target/release/netconv --input router.cfg --warnings

# Full report as JSON (for scripts / CI gates)
./target/release/netconv --input router.cfg --json > report.json

CI/CD integration

Use netconv as a network config linter in your pipeline:

# Fail pipeline if any manual intervention required
./netconv --input router.cfg --json | jq '.report.manual_required == 0'

# Fail if coverage drops below threshold
./netconv --input router.cfg --json | jq '.report.coverage_pct >= 95'

Supported conversions

Source Target L2 profile L3 profile
Cisco IOS Huawei VRP ✓ Implemented (S-series) ✓ Implemented (AR/NE)
Cisco IOS Eltex ✓ Implemented (MES, switch) ✓ Implemented (ESR, router/firewall)
Cisco IOS VyOS 🚧 Planned 🚧 Planned
Cisco ASA n/a (ASA has no L2 role) 🚧 Planned

--profile without a value (legacy VrpRenderer/EltexRenderer) still renders everything unfiltered — kept only for backward compatibility with existing scripts.


Coverage: Cisco IOS → Huawei VRP

System

Feature Status Notes
hostname ✓ Exact → sysname
NTP server ✓ Exact → ntp-service unicast-server
SNMP community ✓ Exact → snmp-agent community
SNMP reserved name conflict ⚠ Manual e.g. community "write" conflicts with VRP keyword
Logging buffered ⚠ Approx → info-center logbuffer size
SSH version ⚠ Approx → stelnet server enable
line vty exec-timeout ⚠ Approx → user-interface vty / idle-timeout
line vty transport input ⚠ Approx → protocol inbound
username + privilege ✗ Manual → local-user (password cannot be migrated)
enable secret ✗ Manual No VRP equivalent — AAA config required
aaa new-model ✗ Manual → authentication-scheme + domain
ip http server ✗ Manual → http server enable / undo

VLAN & STP

Feature Status Notes
vlan batch ⚠ Approx Auto-collected from all interface references
vlan name ⚠ Approx → description
spanning-tree mode rapid-pvst ✗ Manual HIGH RISK → rstp (one tree vs per-VLAN). MSTP starting point auto-generated
spanning-tree loopguard ⚠ Approx → stp loop-protection
spanning-tree portfast ⚠ Approx → stp edged-port enable
spanning-tree bpduguard ⚠ Approx → stp bpdu-protection
spanning-tree bpdufilter (access) ⚠ Approx → stp bpdu-filter enable
spanning-tree bpdufilter (trunk) ✗ Manual RISK — not auto-applied on trunk ports
spanning-tree vlan priority ✗ Manual No per-VLAN priority in RSTP

Interfaces

Feature Status Notes
ip address ✓ Exact
shutdown / no shutdown ✓ Exact → shutdown / undo shutdown
description ✓ Exact
switchport access vlan ⚠ Approx → port default vlan
switchport trunk allowed ⚠ Approx → port trunk allow-pass vlan
switchport voice vlan (access) ⚠ Approx → voice-vlan X enable + global voice-vlan enable
switchport voice vlan (trunk) ⚠ Warn Non-standard — applied with warning
storm-control level ⚠ Approx → percent (assumption flagged inline)
ip helper-address ⚠ Approx → dhcp relay server-ip
ip access-group ⚠ Approx → traffic-filter
ip nat inside/outside ⚠ Approx NAT moves to interface context

Routing

Feature Status Notes
ip default-gateway ⚠ Approx → ip route-static 0.0.0.0/0
ip route (static) ⚠ Approx → ip route-static, AD → preference
OSPF process ⚠ Approx network inside area, log-peer-change
OSPF redistribute ⚠ Approx → import-route
OSPF passive-interface ⚠ Approx → silent-interface
BGP neighbors ⚠ Approx neighbor → peer, remote-as → as-number
BGP peer-groups ⚠ Approx → peer-group NAME
BGP address-family ⚠ Approx → ipv4-family unicast
BGP route-map ⚠ Approx → route-policy import/export
BGP next-hop-self ⚠ Approx → next-hop-local
HSRP → VRRP ⚠ Approx Protocols incompatible at wire level — MAC differs
HSRP track ✗ Manual → NQA/BFD required
EIGRP ✗ Manual Cisco proprietary — not supported on VRP

ACL & NAT

Feature Status Notes
ip access-list named/numbered ⚠ Approx acl name, rule, numbering remapped
NAT overload (PAT) ⚠ Approx → nat outbound on interface
NAT static ⚠ Approx Reversed global/inside order

Architecture

crates/
  netconv-core/        # Vendor-neutral IR, ConfigParser/ConfigRenderer traits, ConversionReport,
                        #   profile.rs — DeviceProfile (L2Switch/L3Router) + domain mismatch detection
                        #   → published to crates.io as `netconv-core`
  ios-config/           # Cisco IOS parser: pass1 structural tree, pass2 semantic analysis
                        #   → published to crates.io as `ios-config` (own version, see below)
  netconv-render-vrp/  # Huawei VRP renderer — VrpL2Renderer / VrpL3Renderer (domain-filtered) +
                        #   legacy VrpRenderer (unfiltered, kept for backward compat)
  netconv-render-eltex/# Eltex renderer — EltexL2Renderer (MES, switch) / EltexL3Renderer (ESR, router) +
                        #   legacy EltexRenderer (= L3, kept for backward compat)
  netconv-wasm/        # WASM bindings (wasm-bindgen): convert_config (legacy) + convert_config_profiled
cli/                   # CLI binary (clap) — --profile l2|l3
web/
  index.html           # Single-file UI: L2/L3 tabs, WASM auto-load, demo fallback
  worker.js             # Cloudflare Worker

netconv-core and ios-config are general-purpose enough to be useful standalone (e.g. just parsing IOS config into a typed IR without converting anything), so they're published to crates.io straight from this repo. Unlike every other crate here, they version independently of the netconv app — ios-config continues its own pre-existing crates.io history (it used to live in a separate casablanque-code/ios-config repository, alongside a now-deprecated ios-config-core that has been folded into netconv-core), and netconv-core is versioned on its own as a new crate. Tagging an app release (see Releasing) publishes whichever of the two has a manifest version not yet on crates.io — it does not force a bump of either just because the app was tagged.

Adding a new target vendor

One new crate, implement ConfigRenderer. Parser and IR are untouched. If the platform has distinct switch and router product lines (most do — that's the point), split the renderer into two structs from the start rather than adding a "convert everything" renderer that has to be un-mixed later:

pub struct FooL2Renderer; // switch: VLANs, switchport, STP
pub struct FooL3Renderer; // router: addressing, routing, ACL, NAT

impl ConfigRenderer for FooL3Renderer {
    fn render(&self, config: &NetworkConfig, report: &mut ConversionReport) -> Result<String, _> {
        // render only L3-relevant IR fields to Foo router syntax
    }
    fn vendor_name(&self) -> &str { "Foo Router" }
}

Register both in netconv-wasm/src/lib.rs::run_conversion_profiled and in cli/src/main.rs's profile match arms. Add to the web UI's dst-vendor select, gated per-profile the same way updateVendorOptionsForProfile() already gates Eltex for L2.

See crates/netconv-render-vrp/src/scope.rs for the pattern used to share code between the two renderers where a vendor's switch and router syntax overlap (e.g. common system settings).


Embedded expertise

netconv encodes cross-vendor knowledge that engineers learn the hard way:

  • HSRP → VRRP: protocols are wire-incompatible. MAC address changes from 0000.0c07.acXX to 0000.5e00.01XX. All nodes must be migrated simultaneously.
  • rapid-pvst → rstp: Cisco runs a separate STP tree per VLAN. Huawei RSTP runs one tree for all VLANs. Topology can change silently if different VLANs had different root bridges.
  • BPDU filter on trunk: context-aware — not applied automatically on trunk ports where it can cause loops.
  • SNMP community name conflicts: VRP reserves read, write, trap as keywords — using them as community names produces ambiguous syntax.
  • storm-control level: Cisco level units vary by platform (%, pps, kbps). Every generated command is flagged with an explicit assumption.

Requirements

Component Version When needed
Rust 1.75+ always
wasm-pack 0.13+ WASM build only
Python 3 any local HTTP server only
Node.js 18+ Cloudflare Workers deploy only

Install Rust: https://rustup.rs
Install wasm-pack: cargo install wasm-pack


Tests

cargo test                        # all tests
cargo test -p ios-config  # parser only (19 tests)

Releasing

The netconv app's own version (CLI, WASM bindings, both renderers, integration tests) lives in exactly one place: [workspace.package] version in the root Cargo.toml, inherited via version.workspace = true — nothing else to bump by hand for an app release.

netconv-core and ios-config are the exception: they're published libraries with their own independent version in their own Cargo.toml, unrelated to the app version above. Bump one of those by hand (in its Cargo.toml, following semver for the change you made) when you want to ship a new release of that crate specifically.

To cut an app release: Actions → Prepare Release → Run workflow, enter a version (X.Y.Z, no v prefix). It bumps Cargo.toml, commits, tags, and pushes — which triggers release.yml:

  • builds the CLI for linux/macos/windows and publishes a GitHub Release with the binaries
  • publish-crates job: checks crates.io's index for netconv-core's and ios-config's current manifest version and publishes whichever one isn't already there (in that dependency order). If you didn't bump either this time, both are skipped — tagging an app release never forces a library release. Requires a CARGO_REGISTRY_TOKEN repo secret.

The web app's WASM binary (web/wasm/*.wasm) is a committed build artifact rebuilt automatically by rebuild-wasm.yml on every push to main that touches a WASM-feeding crate — no manual wasm-pack step required for that either.

See CHANGELOG.md for what shipped in each version.


License

MIT

About

Vendor-agnostic network config compiler

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages