]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ipv4: Honor "ignore_routes_with_linkdown" sysctl in nexthop selection
authorIdo Schimmel <idosch@nvidia.com>
Wed, 30 Apr 2025 10:02:40 +0000 (13:02 +0300)
committerDavid S. Miller <davem@davemloft.net>
Sat, 3 May 2025 20:52:38 +0000 (21:52 +0100)
Commit 32607a332cfe ("ipv4: prefer multipath nexthop that matches source
address") changed IPv4 nexthop selection to prefer a nexthop whose
nexthop device is assigned the specified source address for locally
generated traffic.

While the selection honors the "fib_multipath_use_neigh" sysctl and will
not choose a nexthop with an invalid neighbour, it does not honor the
"ignore_routes_with_linkdown" sysctl and can choose a nexthop without a
carrier:

 $ sysctl net.ipv4.conf.all.ignore_routes_with_linkdown
 net.ipv4.conf.all.ignore_routes_with_linkdown = 1
 $ ip route show 198.51.100.0/24
 198.51.100.0/24
         nexthop via 192.0.2.2 dev dummy1 weight 1
         nexthop via 192.0.2.18 dev dummy2 weight 1 dead linkdown
 $ ip route get 198.51.100.1 from 192.0.2.17
 198.51.100.1 from 192.0.2.17 via 192.0.2.18 dev dummy2 uid 0

Solve this by skipping over nexthops whose assigned hash upper bound is
minus one, which is the value assigned to nexthops that do not have a
carrier when the "ignore_routes_with_linkdown" sysctl is set.

In practice, this probably does not matter a lot as the initial route
lookup for the source address would not choose a nexthop that does not
have a carrier in the first place, but the change does make the code
clearer.

Signed-off-by: Ido Schimmel <idosch@nvidia.com>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Reviewed-by: David Ahern <dsahern@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv4/fib_semantics.c

index 03959c60d1285fa75285bacd7a7e0ae72b004c9d..dabe2b7044ab8eac4d8f217b53d9832b3b79f260 100644 (file)
@@ -2188,7 +2188,14 @@ void fib_select_multipath(struct fib_result *res, int hash,
        saddr = fl4 ? fl4->saddr : 0;
 
        change_nexthops(fi) {
-               if (use_neigh && !fib_good_nh(nexthop_nh))
+               int nh_upper_bound;
+
+               /* Nexthops without a carrier are assigned an upper bound of
+                * minus one when "ignore_routes_with_linkdown" is set.
+                */
+               nh_upper_bound = atomic_read(&nexthop_nh->fib_nh_upper_bound);
+               if (nh_upper_bound == -1 ||
+                   (use_neigh && !fib_good_nh(nexthop_nh)))
                        continue;
 
                if (!found) {
@@ -2197,7 +2204,7 @@ void fib_select_multipath(struct fib_result *res, int hash,
                        found = !saddr || nexthop_nh->nh_saddr == saddr;
                }
 
-               if (hash > atomic_read(&nexthop_nh->fib_nh_upper_bound))
+               if (hash > nh_upper_bound)
                        continue;
 
                if (!saddr || nexthop_nh->nh_saddr == saddr) {