Do not choose a route whose next hop cannot be reached (RFC 4271 §9.1.2.1)

Where routes go into the kernel, a route the kernel could not be given
is no longer taken into the table: its next hop is no neighbor on any
interface, or its prefix is a network the machine is itself on. Such a
route used to be able to win selection and then leave its prefix without
a route in the kernel, though another peer had one that worked.

It is held aside the way an RPKI-invalid route is, and by the same
means: judged on arrival against the interfaces as the kernel last
described them, looked for in the table when they change, and taken
from what is held when its next hop can be reached. The fib publishes
the interfaces for this; the next hop is resolved once for an UPDATE
and not for each of its routes.

The lab has a phase, nexthops: FRR's better route with a next hop that
is nowhere is held and BIRD's is installed, and they change places as
an address comes and goes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Rayhaan JaufeerallyandClaude Fable 5.1 committed 2026-10-02 20:29:31 +02:00
1 parent 3d946c32ce
commit f1d80c66df
15 files changed
+680 -63

No files matched your search

+13 -2
View File
@@ -142,9 +142,20 @@ async fn test_routes_from_a_session_reach_the_kernel() -> Result<()> {
assert_eq!(kernel["enabled"], true);
assert_eq!(kernel["table"], TABLE);
assert_eq!(kernel["installed"], 2);
// The route by way of a router that is no neighbor is not in the
// table for the kernel to be asked for: it is held, in case the
// router becomes one.
assert_eq!(kernel["unresolved"], serde_json::json!({}));
let session = server.registry.list().pop().expect("the session");
assert_eq!(session.counters.held(), 1);
assert_eq!(server.rib.len(), 2);
let refused: Value = reqwest::get(format!("{base}/api/routes/rejected"))
.await?
.json()
.await?;
assert_eq!(
kernel["unresolved"],
serde_json::json!({"next hop not directly connected": 1})
refused[0]["by_reason"],
serde_json::json!({"next hop unreachable": 1})
);
let shown = kernel["interfaces"]
.as_array()