The fill-in fields put the label on its own line with dotted lines to write on. handout.tex takes \studentnum and \studenthex; build-class.py compiles one copy per student with the prefix filled in, checks each is two pages and joins them for double-sided printing. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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 |
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. - Both ferrix and the FRR routers dial each other, so connection collisions (RFC 4271 §6.8) are normal at start-up: one of the two TCP connections is closed with a Cease / Connection Collision Resolution NOTIFICATION and the other carries the session. ferrix has the lowest BGP Identifier here, so the connection the FRR router opened is the one that survives; the "opened by" column on the status page shows which side that was.
- 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.