[PATCH net-next v3 3/4] nexthop: flush cached routes when a nexthop leaves a group
From: Qiliang Yuan
Date: Mon Nov 09 2026 - 03:20:09 EST
When a nexthop is removed while other members keep its group alive, only
the group array changes: the routes through the group stay in the FIB,
and the dsts they cached through the removed nexthop are released when
that nexthop is freed, from an RCU callback.
Until then those dsts stay valid. IPv6 route exceptions, such as the
ones created on Packet Too Big, are checked against the cookie of their
FIB node, which does not change, so a socket keeps sending through the
removed nexthop. When the nexthop goes away with its device, removing
the device's IPv6 addresses usually flushes the routing tree first, but
not when the device carries no IPv6 address.
Flush the cached routes of each affected group once all readers see its
new array, as replacing a group already does. A connected UDP socket in
netns A sends to a route through group {nh 1, nh 2}, on an IPv6
exception of nh 1 created by Packet Too Big, while nh 1 is deleted.
Datagrams still sent through nh 1 after the new group is published and
before nh 1 is freed, 10 trials, the one or two left after the change
being sent between synchronize_net() returning and the flush:
before after
datagrams per trial 395-4423 1-2
Fixes: 430a049190de ("nexthop: Add support for nexthop groups")
Signed-off-by: Qiliang Yuan <qiliang.yuan@xxxxxxxxx>
---
net/ipv4/nexthop.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/net/ipv4/nexthop.c b/net/ipv4/nexthop.c
index 42e55b5a755ee..e0089a6450e75 100644
--- a/net/ipv4/nexthop.c
+++ b/net/ipv4/nexthop.c
@@ -2109,6 +2109,9 @@ remove_nh_grp_entry(struct net *net, struct nh_grp_entry *nhge,
return false;
}
+static void nh_rt_cache_flush(struct net *net, struct nexthop *nh,
+ struct nexthop *replaced_nh);
+
static bool __must_check
remove_nexthop_from_groups(struct net *net, struct nexthop *nh,
struct nl_info *nlinfo)
@@ -2130,6 +2133,13 @@ remove_nexthop_from_groups(struct net *net, struct nexthop *nh,
/* make sure all see the newly published array before releasing rtnl */
synchronize_net();
+ /* Routes through the groups can still hold dsts cached through @nh,
+ * which are only released when @nh is freed. Invalidate them now that
+ * no reader can pick @nh from the old groups any more.
+ */
+ list_for_each_entry(nhge, &deferred_free, nh_list)
+ nh_rt_cache_flush(net, nhge->nh_parent, nh);
+
/* Now safe to free percpu stats — all RCU readers have finished */
list_for_each_entry_safe(nhge, tmp, &deferred_free, nh_list) {
list_del(&nhge->nh_list);
--
2.43.0