Property tests (run by `cargo test`):
- crates/packet/tests/properties.rs: every kind of message, generated,
must be written, be as long as wire_len says, and read back equal; and
noise, noise behind a valid header, and valid messages with bytes damaged
must never do more than return an error. Anything the decoder accepts
must be showable and survive being written and read again.
- IpPrefix: text round trip, noise as text, `contains` against a bitwise
model.
- The session's framing: a stream of messages yields the same messages
however it is cut into reads.
Fuzz targets (fuzz/, cargo-fuzz on nightly, a minute each in CI): raw
messages, message bodies behind a valid header, prefix text, the
configuration file.
Bugs found, each with a named regression test:
- A prefix longer than its address family (e.g. an IPv4 NLRI of length 99)
was accepted, and showing it panicked. The session logs every route, so
one UPDATE from a peer ended the session task. Such a prefix is now
malformed, and showing a prefix cannot fail.
- Bits past a prefix's length were kept, so the same prefix sent with stray
bits was a different key in the RIB. Prefixes now have one form.
- An opaque extended community was read as seven octets instead of eight,
putting every community after it out of step.
- AGGREGATOR could be neither read from nor written to a peer with
two-octet AS numbers.
- The Outbound Route Filter and Long-Lived Graceful Restart capabilities
were written differently from how they are read.
- A capability value or unknown attribute too long for its length field
was written with the length wrapped around; it is now an error, or for
the attribute the two-octet length.
Also: ROUTE-REFRESH carries its AFI and SAFI instead of being modelled as
an empty message, and one with a body of the wrong length is refused.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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>
Lints are declared once under [workspace.lints] and every crate opts in:
clippy's default set, plus dbg!, todo! and unimplemented!, and unsafe code
is forbidden. Every crate now inherits its version, licence, authors and
repository from the workspace, so they no longer disagree (four were at
0.0.0).
The poptrie module is unfinished and unused; it says so and expects the
dead-code lint instead of warning on every build. Its construction method
is renamed from new, which took &mut self, to build.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>