Install best routes in the Linux kernel over netlink
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>
This commit is contained in:
1 parent
1f84235e87
commit
1fc44798b7
38 files changed
+3745
-16
No files matched your search
@@ -2,6 +2,7 @@ use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
|
||||
use bgp_fib::FibConfig;
|
||||
use bgp_packet::community;
|
||||
use bgp_packet::message::OriginPathAttribute;
|
||||
use bgp_packet::open::AddPathSendRecv;
|
||||
@@ -43,6 +44,8 @@ pub struct Config {
|
||||
pub originate: Vec<LocalRoute>,
|
||||
/// Address for the HTTP API; `None` disables it.
|
||||
pub http_endpoint: Option<SocketAddr>,
|
||||
/// Where to install the best routes in the kernel, if at all.
|
||||
pub kernel: Option<FibConfig>,
|
||||
/// Where route origin validation gets its data; `None` for no
|
||||
/// validation.
|
||||
pub rpki: Option<RpkiConfig>,
|
||||
@@ -174,6 +177,14 @@ impl Config {
|
||||
accept_unconfigured_peers: proto.accept_unconfigured_peers,
|
||||
originate,
|
||||
http_endpoint,
|
||||
kernel: proto.kernel.as_ref().map(|kernel| {
|
||||
let defaults = FibConfig::default();
|
||||
let or = |value: u32, default: u32| if value == 0 { default } else { value };
|
||||
FibConfig {
|
||||
table: or(kernel.table, defaults.table),
|
||||
metric: or(kernel.metric, defaults.metric),
|
||||
}
|
||||
}),
|
||||
rpki,
|
||||
})
|
||||
}
|
||||
@@ -725,6 +736,27 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_kernel_is_left_alone_unless_asked_for() {
|
||||
assert_eq!(parse(BASE).unwrap().kernel, None);
|
||||
assert_eq!(
|
||||
parse(&format!("{BASE}kernel {{}}")).unwrap().kernel,
|
||||
Some(FibConfig {
|
||||
table: 254,
|
||||
metric: 20
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
parse(&format!("{BASE}kernel {{ table: 1000 metric: 150 }}"))
|
||||
.unwrap()
|
||||
.kernel,
|
||||
Some(FibConfig {
|
||||
table: 1000,
|
||||
metric: 150
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn route_origin_validation_is_off_unless_caches_are_given() {
|
||||
assert!(parse(BASE).unwrap().rpki.is_none());
|
||||
|
||||
Reference in new issue
Block a user