A routing protocol suite in Rust — router + Rust.
Rouster is not an FRR clone. It fuses BIRD's and Holo's core architecture, keeps FRR compatibility strictly at the edge, and adds native source address validation: a BIRD-style typed multi-table RIB, Holo-style async and AF-generic protocol crates, FRR compatibility (ZAPI, vtysh, frr.conf) as edge adapters only, and SAVNET-first source address validation as a first-class subsystem. The reasoning is recorded in docs/architecture.md; the phase plan lives in docs/implementation-plan.md.
All plan phases through Phase 5's first protocol are landed and
exercised by a container-based smoke suite
(scripts/run-smokes.sh). What actually runs today:
- zebra-equivalent core (
rousterd): a BIRD-style typed multi-table RIB with a Linux netlink dataplane — interface, address, route and neighbour state mirrored from the kernel, static routes programmed back (including ECMP and non-main tables), VRF devices and their routing tables mirrored and programmed without leaking into the main table. SIGHUP reloads the config without bouncing anything. Recursive multi-hop nexthop resolution; bound-ACK netlink programming with overrun resync. - ZAPI edge — proven against real FRR 10.2.2: unmodified FRR daemons
(staticd, bgpd, ospfd, isisd, bfdd) attach to rousterd as their zebra,
and their routes reach the kernel through the Rust RIB (
proto static,proto bgp,proto ospf,proto isis). The wire codec is pinned byte-for-byte against the real 10.2.2 binary in docs/interop/zapi-10.2-pinned.md; session-establish registration replay heals reconnecting clients. - native protocol daemons — all live with real-FRR interop:
- bgpd (
scripts/test-bgp-*.sh): RFC 4271 FSM and codec, 4-byte AS, MP-BGP v4/v6, route refresh, ADD-PATH both directions, route reflection, route-server mode, graceful restart (helper + restarting speaker, Hard Reset), RFC 7606 revised error handling, RFC 4271 §6.3 first-AS enforcement — and RPKI from the start: an RTR client (rpkicrate, proven against a real gortr cache) feeding RFC 6811 ROV, ASPA verification and RFC 9234 roles/OTC into the decision path, gated by the typed policy engine (policycrate, BIRD-cited semantics + an FRR route-map compatibility grammar). - ospfd (
scripts/test-ospf*.sh,test-ospfv3-*.sh): the RFC 2328 stack end-to-end — LSA database, flooding, SPF with back-off and partial recompute, broadcast networks and DR election, inter-area summary routes, stub/NSSA areas (including the RFC 3101 translator), Appendix-D authentication (MD5 + HMAC-SHA-256 key chains), area ranges, virtual links — and OSPFv3 on the same AF-generic codebase, at Full adjacency with real FRR ospf6d and exchanging routes both ways. - isisd (
scripts/test-isis-*.sh): the RFC 1195 / ISO 10589 stack — TLV codec, LSPDB, per-level SPF, three-way adjacency, broadcast/LAN circuits with DIS election, RFC 5302 inter-area, RFC 5304 auth, LSP fragmenting, administrative knobs — at Full adjacency with real FRR isisd. - bfdd (
scripts/test-bfd-*.sh): RFC 5880–5883 — the exact state machine, TTL-255 single-hop enforcement on both address families (receive-side, kernel-proven), multihop, echo, and the ZAPI-BFD integration feeding bgpd's fast-down path.
- bgpd (
- savd — native source address validation: a SAV Information Base fed
from the RIB and config; the IBA/IBB/PBA/PBB validation-mode rule model
(draft-ietf-savnet-general-sav-capabilities); uRPF-family rule
generation (RFC 3704/8704); BAR-SAV allowlist derivation from
BGP AS_PATHs + ROA/ASPA (draft-ietf-sidrops-bar-sav); enforcement
behind the dataplane trait with two backends (
rp_filterand nftables —scripts/test-sav-nft.sh); event-driven re-evaluation with RFC 8704 deletion hysteresis. Monitor-only by default — drop mode refuses without an explicit opt-in, and spoof-block/legit-pass are exercised with real traffic (scripts/test-w7-sav.sh,scripts/test-w8-savd.sh). No general-purpose suite (FRR, BIRD, Holo) ships packet-plane SAV today. - management plane (Phase 4, complete):
- vtyd — a vtysh-compatible VTY server (Cisco-style grammar,
run-dir show commands, config mode with
write fileapplying through configd); - configd — the transactional config manager: one canonical
rouster.conf, YANG-modeled (a pure-Rust RFC 7950-subset core, seven per-daemon models — the tree is the internal source of truth), structural + per-daemon deep validation, atomic staged apply with generation tracking, automatic rollback on daemon failure, and a RESTCONF-shaped HTTP northbound (restconfd, unix-socket default + optional bearer token). - rousterctl — the read-only operator CLI over the run-dir
contract (
show sav/status/ the interface, neighbour and VRF views).
- vtyd — a vtysh-compatible VTY server (Cisco-style grammar,
run-dir show commands, config mode with
- performance (the v1.1.0 headline, every receipt in
docs/benchmarks.md §R, same rig, same feed,
same injector as FRR 10.2.2):
- full 1,126,490-prefix table in-kernel in 8.02 s median (FRR: 30.36 s — 3.79× ahead);
- speaker memory 449 B/prefix (FRR: 897 — 2.1× ahead);
- core memory 229 B/prefix (FRR: 519 — 2.27× ahead);
- CPU ahead on both processes; zero malformed/rejected messages.
The full suite runs as scripts/run-smokes.sh (41 scripts, green —
proven against the testbed image rebuilt from the in-repo recipe
docker/frr-testbed.Dockerfile); it runs nightly in CI
(smokes.yml) plus nightly codec fuzzing (fuzz.yml, whose first
dispatch caught a real OOB in the RTR decoder). The push/PR gate is
cargo fmt --check + clippy -D warnings (host + Linux-target
legs) + 1859 tests.
Still ahead per the plan: the Phase-5 breadth menu (RIPng, PIM, LDP/MPLS, EVPN, SRv6, FPM, BMP), and the inter-domain SAVNET architecture as IETF drafts mature.
Prerequisites: Linux (netlink), Rust stable, and Docker for the smoke suite. On macOS, run the smokes in a Linux VM/Docker Desktop — the daemon needs a Linux kernel.
cargo build --release
# rouster.conf — an FRR-flavored strict subset (valid input for the
# future frr.conf importer):
# ip route <PREFIX> {<GATEWAY>... | blackhole} [table <N>]
# ipv6 route <PREFIX> {<GATEWAY>... | blackhole} [table <N>]
# zapi socket <PATH> # enable the ZAPI edge server (default: off)
cat > rouster.conf <<'EOF'
ip route 198.51.100.0/24 blackhole
zapi socket /run/rouster/zserv.api
EOF
./target/release/rousterd --config rouster.conf # --once: install and exitUnit and property tests:
cargo test --workspaceSmoke suite (per-script container topologies, self-cleaning):
scripts/run-smokes.sh # everything
scripts/run-smokes.sh 'z5*' # just the FRR-interop smokesThe smoke scripts never pull images. The kernel-level smokes need any
cached Linux base image; the FRR interop smokes need a locally cached FRR
10.2.2 test image (FRR daemons + full iproute2) tagged frr-testbed:10.2.2
— build one from frrouting/frr:10.2.2 or point
ROUSTER_TEST_BASE_IMAGE at an equivalent. Rousterd itself is built on
the host for musl and copied in; no Rust build image is needed. Logs land
in target/smoke-logs/.
crates/
common/ shared types: prefixes, nexthops, interfaces, link flags
rib/ BIRD-style typed multi-table RIB core
netlink-dp/ Linux netlink dataplane behind the Dataplane trait
zapi/ FRR ZAPI codec + edge server (plus sniff/probe dev tools)
rousterd/ the daemon; also builds the staticd ZAPI client (FRR shape)
rousterctl/ operator CLI over the run-dir contract (show sav, status)
sav/ SIB, SAV rule model, uRPF generation, table manager, savd
scripts/ smoke suite (scripts/test-*.sh) + the run-smokes.sh runner
docker/ test image definitions
topologies/ Containerlab topologies
docs/ architecture, implementation plan, interop records
Start at docs/README.md for the index. The two documents that define the project are docs/architecture.md (the core/edge decision) and docs/implementation-plan.md (phases, SAVNET workstream rules, risks, and the append-only status log in its Appendix C).
MIT — see LICENSE.
