New crate bgp-fib. It follows the routing table's best routes and keeps
the kernel's routing table the same, and learns the machine's interfaces
and addresses from the kernel, which is what says whether a next hop can
be reached.
- netlink: the rtnetlink messages used (routes, addresses, links, errors),
as bytes and back. No I/O; property tested and fuzzed.
- interfaces: what is directly reachable, and from which interface.
- plan: what to ask the kernel for, given the best routes and what the
kernel has. No I/O; tested against a model kernel.
- kernel (Linux only): the sockets and the task. Requests go in batches;
routes of ours found at startup are kept if still right and removed if
not; a change of interfaces has every route worked out again; a clean
stop removes our routes.
Only routes marked proto bgp with the configured table and metric are
ever touched. A best route is installed if its next hop is a neighbor;
next hops further away are not resolved through other routes yet, and
such prefixes are counted and shown instead.
Configured with `kernel { table metric }`; off by default. The daemon
needs CAP_NET_ADMIN and stops if the kernel refuses it. On shutdown it
closes sessions and then removes its routes.
/api/kernel and the status page show installed, unresolved and refused
counts, the last error and the interfaces; five bgp_kernel_* metrics.
Testing: scripts/kernel-tests.sh runs, in network namespaces of their
own and with only the capabilities the daemon needs, the installer
against `ip route`, the daemon with a peer, and two daemons that route
for each other with pings across over IPv4 and IPv6.
scripts/linux-vm.sh runs that, or anything else, in a Lima VM from
macOS. CI runs the kernel tests and fuzzes the netlink decoder.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
1.9 KiB
Fuzz targets
Coverage-guided fuzzing of everything that reads input we do not control.
The property tests (cargo test, e.g. crates/packet/tests/properties.rs)
check the same things on generated input and run on every build; these
targets go further by letting libFuzzer follow code coverage for as long as
you leave them running.
| Target | Input |
|---|---|
message |
Raw bytes as they come off a TCP connection |
message_body |
A message body behind a correct header (reaches the body parsers) |
prefix_text |
A prefix as text |
config |
The configuration file |
rtr |
A stream of RTR PDUs, as an RPKI cache sends them |
netlink |
Messages from the kernel's routing socket |
For the two message targets a finding is either a panic, or a message the decoder accepted that cannot be written and read back as itself.
Running
libFuzzer needs a nightly toolchain and cargo-fuzz:
rustup toolchain install nightly
cargo install cargo-fuzz
Then, from this directory:
cargo +nightly fuzz run message_body corpus/message_body seeds/message_body -- -max_total_time=300
The first directory is the working corpus, which the fuzzer adds to and
which is not checked in. seeds/ holds a few valid inputs to start from.
Leave out -max_total_time to run until interrupted.
When it finds something
The input is saved under artifacts/<target>/. Reproduce it with
cargo +nightly fuzz run message_body artifacts/message_body/crash-...
Fix the bug, and add a unit test with the input next to the code it broke,
so that cargo test keeps it fixed without anyone having to run the fuzzer.