The library crates returned eyre reports, so a failure was a string and the session code could not tell a truncated UPDATE from a bad header. Whatever went wrong, the peer got an FSM error or nothing. Each crate now has its own error types: - bgp-primitives: PrefixError, UnknownAfi and UnknownSafi, and EncodeError for every to_wire. - bgp-packet: Message::decode returns a DecodeError naming the NOTIFICATION code, subcode and data for the failure (header errors per section 6.1, OPEN per 6.2, UPDATE per 6.3). InvalidValue and CommunityError replace the remaining strings. from_wire is now a thin wrapper around decode. - bgp-server: SessionError, ServerError and ConfigError. Every abnormal end of a session goes through one function that sends the NOTIFICATION the error maps to. The message length is checked against the 4096-octet maximum before the body is read. - bgp-web: serve returns io::Error. eyre remains in the binary and in tests. Bugs found and fixed on the way: - An OPEN with no optional parameters, which is valid, failed to parse. - An unsupported BGP version was reported as an unknown optional parameter. It now gets Unsupported Version Number with the version we speak as data. - A SAFI above 255 was truncated to its low octet and could match a known one. - Prefix text with a length beyond the address size (10.0.0.0/99) was accepted. - Message::to_wire truncated the body length to 16 bits before checking it. - A peer hold time above 65535 seconds in the configuration wrapped. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
BGP
This crate provides the bgp binary, which runs the Border Gateway Protocol
daemon together with its read-only HTTP status interface.
cargo run -- path/to/config.textpb
The configuration format is defined in crates/server/proto/config.proto;
testdata/server.textpb is a worked example. The daemon is configured
entirely through that file: peers, and the routes it originates
(originate { prefix: ... }). There is no way to change either at runtime.
When http_endpoint is set the binary also serves:
| Path | Purpose |
|---|---|
GET /statusz |
Status page: configuration, peers and session state, originated and received routes, live event log |
GET /api/status |
Router ID, ASN, uptime and table sizes |
GET /api/config |
The configuration file as read at startup |
GET /api/peers |
Configured peers and established sessions |
GET /api/routes/local |
Originated routes |
GET /api/routes/received |
Adj-RIB-In contents |
GET /api/events |
Server-sent events for session and route changes |
GET /metricz |
Prometheus metrics |
Everything under it is read-only and unauthenticated. Bind it to loopback or put it behind a reverse proxy.
What it does today
It establishes sessions (both dialling configured peers and accepting connections, with RFC 4271 collision detection), announces its originated routes to every peer with itself as next hop, and records what peers send in an Adj-RIB-In. It does not select best paths, re-advertise received routes or program a forwarding table, and it has no import or export policy.
Trying it without a router
fake_peer is a minimal BGP speaker that connects to the daemon, announces a
prefix, prints every UPDATE it receives and, with --echo, re-announces each
received prefix back with its own ASN prepended (dropping NO_EXPORT routes).
That makes originated prefixes show up in the received table as if they had
travelled through a neighbouring AS:
cargo run -- testdata/server.textpb
cargo run -p bgp-integration-tests --example fake_peer -- \
--server 127.0.0.1:179 --asn 65001 --announce 203.0.113.0/24 --echo
Please refer to the other implementation crates in this repository where most of the actual implementation lives.
Warning: This is still very much a work in progress and is not yet ready for production use.