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>
3.7 KiB
BGP teaching lab
A small topology in which a prefix announced from the ferrix_bgp web UI is carried around a loop of two routers and arrives back, so students can watch what their path attributes and communities do.
announce re-advertise
┌─────────┐ ──────▶ ┌────────┐ ──────▶ ┌────────┐
│ ferrix │ │ r1 │ │ r2 │
│ AS65000 │ │ AS65001│ │ AS65002│
│ web UI │ ◀──────────────────────────────────────┘
└─────────┘ comes back with AS path 65002 65001 65000
- ferrix (AS65000,
10.200.0.1) dials r1 and r2 and announces whatever students enter onhttp://localhost:8080/statusz. It does not forward routes it learns, so the only routes it sends are the students'. - r1 (AS65001, FRR) applies policy to routes from ferrix based on the
communities they carry, then passes them to r2. It originates
192.0.2.0/24. - r2 (AS65002, FRR) refuses routes directly from ferrix, so it only learns
student prefixes via r1, and re-advertises everything to ferrix. It
originates
198.51.100.0/24.
Running it
From the repository root:
docker compose -f docs/lab/docker-compose.yml up --build
Then open http://localhost:8080/statusz. Both peers should show
Established within a few seconds and the received table should already
list the two prefixes r1 and r2 originate.
Useful on the routers:
docker exec r1 vtysh -c 'show bgp ipv4 unicast'
docker exec r2 vtysh -c 'show bgp ipv4 unicast 10.100.1.0/24'
docker exec r1 vtysh -c 'show bgp neighbors 10.200.0.1 advertised-routes'
Policy on r1
| Students set | r1 does | Visible effect in ferrix |
|---|---|---|
| nothing | forwards to r2 | prefix returns, AS path 65002 65001 65000 |
NO_EXPORT |
keeps it, does not send to r2 (RFC 1997) | prefix does not return |
65001:100 |
prepends 65001 three times |
returns with path 65002 65001 65001 65001 65001 65000 |
BLACKHOLE (65535:666) |
drops it on import | prefix does not return |
| AS-path prepends on ferrix | nothing special | returns with extra 65000s in the path |
Without containers
fake_peer in crates/integration-tests/examples is enough for the core
loop on one machine: it echoes every prefix back with its own ASN prepended
and honours NO_EXPORT. See bin/bgp/README.md.
Caveats
These container files were written without a container runtime to hand, so they have not been executed. Things most likely to need a touch:
- the FRR image tag, and FRR's route-map / community-list syntax if your version differs from 10.x;
no bgp ebgp-requires-policyis required on FRR 7.4+ or eBGP routes are silently dropped; it is present in both configs;- ferrix binds port 179 inside its container, which requires no special
capability there. If you run it on the host instead, either run as root,
grant
CAP_NET_BIND_SERVICE, or changelisten_endpointsand the FRR neighbor port. - ferrix does not check AS paths for its own ASN. That is what lets the looped route be accepted; a production BGP speaker would reject it.