Commit cea6abfd7c for frr
commit cea6abfd7ccce1128712337318c3b3d288b8c1eb
Author: Abdul Wasey <w453y.me@gmail.com>
Date: Fri Nov 6 00:56:35 2026 +0000
zebra: delete the old nexthops on BSD after an interface goes down
When an interface goes down, zebra_nhg_check_valid() and
zebra_nhg_set_valid() clear NEXTHOP_FLAG_ACTIVE on the nexthops of
the routes using it, while those routes are still in the kernel. The
routing socket dataplane then removes the old route with one
RTM_DELETE per nexthop, but kernel_rtm() skipped every nexthop
without ACTIVE, so nothing was deleted. The route through the dead
interface stayed in the FIB next to the new one, or with
net.route.multipath=0 kept the new one from being installed.
Only skip inactive nexthops when adding. Deleting a gateway that is
not in the kernel returns ESRCH, which is already ignored for
RTM_DELETE.
Fixes: #22418
Signed-off-by: Abdul Wasey <w453y.me@gmail.com>
diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c
index c91f3d07b9..7ee228da2a 100644
--- a/zebra/rt_socket.c
+++ b/zebra/rt_socket.c
@@ -108,10 +108,15 @@ static int kernel_rtm(int cmd, const struct prefix *p,
/* Make gateway. */
for (ALL_NEXTHOPS_PTR(ng, nexthop)) {
/*
- * We only want to use the actual good nexthops
+ * We only want to install the actual good nexthops. When
+ * deleting, an interface going down has already cleared
+ * ACTIVE on the nexthops that are still in the kernel, so
+ * try all of them; a gateway that isn't there just gives
+ * ESRCH.
*/
- if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE) ||
- !CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+ continue;
+ if (cmd != RTM_DELETE && !CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
continue;
smplsp = NULL;