Phase 5 of the teaching demo plus two fixes found while exercising the
dialer on the status page.
- Adj-RIB-In: routes learned from a peer are removed when its session ends
(RFC 4271 §8.2.2), emitting a Withdrawn event per prefix before the
session Down event. Previously stale routes from dead sessions stayed in
the received table.
- statusz: poll the peers table every 5 s so dial state (Connecting /
Backoff and its retry countdown) stays current; those changes carry no
event.
- docs/lab: docker-compose topology with ferrix (AS65000) and two FRR
routers. r2 accepts nothing directly from ferrix so student prefixes
only reach it via r1, then come back with AS path 65002 65001 65000. r1
applies community-driven policy: NO_EXPORT stops forwarding, 65001:100
prepends, BLACKHOLE drops. Dockerfile for the daemon.
- docs/lab/HANDOUT.md: student exercises walking through announcement,
reading the bytes, prepending, NO_EXPORT, action communities,
blackholing and withdrawal.
The container files have not been executed (no runtime available here);
docs/lab/README.md lists what is most likely to need adjusting.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Phase 4 of the teaching demo: the daemon now brings up its own sessions.
- New dialer task per configured peer: connect with a timeout, run the
same FSM as the passive side, back off exponentially (from
connect_retry_seconds, capped at two minutes) and retry forever. Stays
idle while a session with the same remote IP and ASN already exists.
- The peer's OPEN must carry the configured ASN, otherwise the session is
refused with a Bad Peer AS notification. Inbound connections from a
configured peer's address get the same check plus the configured name
and hold time.
- SessionRegistry tracks configured peers and their dial state
(Idle / Connecting / Backoff with last error and retry time).
- /api/peers merges configured peers with live sessions so the page shows
peers that are down, why, and when the next attempt is; unconfigured
inbound sessions are listed after them.
- fake_peer gains --listen so it can stand in for a router the daemon
dials.
- Integration tests: dial and establish, reconnect after the peer closes,
wrong ASN refused, inbound from a configured address checked and named.
Not implemented: RFC 4271 §6.8 collision detection; a real router on the
other end resolves the rare race by closing one connection.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Phase 3 of the teaching demo.
- crates/web serves an embedded, dependency-free status page at /statusz
(/ redirects there). Sections: router header with live indicator, peers,
announce form (owner remembered locally, well-known community picker,
prepend / MED / large communities), local routes with per-session UPDATE
bytes segmented into header / lengths / attributes / NLRI, received
routes with rows highlighted when a locally announced prefix has come
back, and a live event log. Tables refresh on every SSE event. Light
and dark themes.
- fake_peer example in crates/integration-tests: connects, announces,
prints received UPDATEs and with --echo re-announces everything back
with its ASN prepended (dropping NO_EXPORT), so one daemon plus one
process demonstrates the full loop without lab routers.
- README for the binary documents the endpoints and the demo setup.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Phase 2 of the teaching demo: everything the status page will need is now
reachable over HTTP.
- New crate crates/web (axum). JSON endpoints for status, peers, local
routes (announce / withdraw), received routes, the exact UPDATE bytes a
local route produces per session, and well-known community names.
Communities are accepted as ASN:VALUE or by name (NO_EXPORT, BLACKHOLE,
...); large communities as A:B:C. Policy errors map to 403/409/422.
- GET /api/events is a server-sent event stream merging session up/down,
local route and Adj-RIB-In changes, with lag notifications.
- GET /metricz serves Prometheus metrics; the server crate now counts
messages sent/received by type and sessions established.
- The runnable main moves from crates/server to bin/bgp so it can depend
on both server and web without a cycle. `cargo run -- config.textpb`.
- Config gains http_endpoint; unset disables the API.
- Integration tests drive the API against a live server with a real BGP
peer, including verifying the wire view matches the bytes the peer got.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Phase 1 of the BGP teaching demo: the daemon can now originate routes and
advertise them to every established peer.
- rib: add LocalRib actor holding locally originated routes with owner,
origin, AS-path prepend, MED, standard and large communities. Enforces a
LocalRibPolicy (prefix allowlist, per-owner limit, owner match on
replace/withdraw) and emits LocalRibEvents.
- server: add SessionRegistry with per-session outbound queues; sessions
register on Established and deregister via a drop guard. Emits
SessionEvent::{Up,Down}.
- server: advertiser task fans LocalRib changes out to all sessions and
replays the table to new sessions. UPDATE builders handle IPv4 (legacy
NLRI) and IPv6 (MP_REACH/MP_UNREACH).
- fsm: merge the timer and timer-less established loops into one select!
with an outbound branch; reset the hold timer on any received message
per RFC 4271 §8.2.2 (previously only on KEEPALIVE).
- config: new local_route_policy proto message.
- packet: fix Communitites typo, expose CommunitiesPathAttribute inner vec.
- primitives: IpPrefix::contains.
- integration tests: announce/withdraw/replay/fan-out/policy coverage.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- Implement passive-side BGP FSM (OpenSent → OpenConfirm → Established)
with hold timer, keepalive timer, and NOTIFICATION on expiry
- Add bgp-server crate: config validation, TCP listener accept loop
(BgpServerBuilder → BgpServer), connection dispatch
- Add bgp-rib crate: Adj-RIB-In actor owning route state with event
broadcast for streaming consumers and snapshot for introspection
- Wire Adj-RIB-In into FSM: announce/withdraw on UPDATE for both legacy
IPv4 NLRI fields and MP_REACH/MP_UNREACH path attributes
- Add bgp-integration-tests crate with 15 end-to-end tests covering
session establishment, timers, UPDATE handling, and RIB introspection
- Fix require_empty! macro bug in open.rs (checked wrong buffer)
- Fix FourByteAsn capability parser returning consumed buffer as remainder
- Fix tracing subscriber not being installed in TracingArgs::install
- Expand docs/RFCs.md with full implementation status across 15 RFCs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>