route: check sysctl_fib_multipath_use_neigh earlier than hash
authorXin Long <lucien.xin@gmail.com>
Sun, 1 Apr 2018 14:40:35 +0000 (22:40 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 13 Apr 2018 17:48:36 +0000 (19:48 +0200)
[ Upstream commit 6174a30df1b902e1fedbd728f5343937e83e64e6 ]

Prior to this patch, when one packet is hashed into path [1]
(hash <= nh_upper_bound) and it's neigh is dead, it will try
path [2]. However, if path [2]'s neigh is alive but it's
hash > nh_upper_bound, it will not return this alive path.
This packet will never be sent even if path [2] is alive.

 3.3.3.1/24:
  nexthop via 1.1.1.254 dev eth1 weight 1 <--[1] (dead neigh)
  nexthop via 2.2.2.254 dev eth2 weight 1 <--[2]

With sysctl_fib_multipath_use_neigh set is supposed to find an
available path respecting to the l3/l4 hash. But if there is
no available route with this hash, it should at least return
an alive route even with other hash.

This patch is to fix it by processing fib_multipath_use_neigh
earlier than the hash check, so that it will at least return
an alive route if there is when fib_multipath_use_neigh is
enabled. It's also compatible with before when there are alive
routes with the l3/l4 hash.

Fixes: a6db4494d218 ("net: ipv4: Consider failed nexthops in multipath routes")
Reported-by: Jianlin Shi <jishi@redhat.com>
Signed-off-by: Xin Long <lucien.xin@gmail.com>
Acked-by: David Ahern <dsa@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
net/ipv4/fib_semantics.c

index 7e7b7a3efa99e92c59e1b240ec93d88c3dda77c1..e1be24416c0e569989927a07e15a58b3c5554b70 100644 (file)
@@ -1611,18 +1611,20 @@ void fib_select_multipath(struct fib_result *res, int hash)
        bool first = false;
 
        for_nexthops(fi) {
+               if (net->ipv4.sysctl_fib_multipath_use_neigh) {
+                       if (!fib_good_nh(nh))
+                               continue;
+                       if (!first) {
+                               res->nh_sel = nhsel;
+                               first = true;
+                       }
+               }
+
                if (hash > atomic_read(&nh->nh_upper_bound))
                        continue;
 
-               if (!net->ipv4.sysctl_fib_multipath_use_neigh ||
-                   fib_good_nh(nh)) {
-                       res->nh_sel = nhsel;
-                       return;
-               }
-               if (!first) {
-                       res->nh_sel = nhsel;
-                       first = true;
-               }
+               res->nh_sel = nhsel;
+               return;
        } endfor_nexthops(fi);
 }
 #endif