OSDN Git Service

ipv4: nexthop: Correctly update nexthop group when removing a nexthop
authorIdo Schimmel <idosch@nvidia.com>
Wed, 26 Aug 2020 16:48:54 +0000 (19:48 +0300)
committerDavid S. Miller <davem@davemloft.net>
Wed, 26 Aug 2020 23:00:51 +0000 (16:00 -0700)
Each nexthop group contains an indication if it has IPv4 nexthops
('has_v4'). Its purpose is to prevent IPv6 routes from using groups with
IPv4 nexthops.

However, the indication is not updated when a nexthop is removed. This
results in the kernel wrongly rejecting IPv6 routes from pointing to
groups that only contain IPv6 nexthops. Example:

# ip nexthop replace id 1 via 192.0.2.2 dev dummy10
# ip nexthop replace id 2 via 2001:db8:1::2 dev dummy10
# ip nexthop replace id 10 group 1/2
# ip nexthop del id 1
# ip route replace 2001:db8:10::/64 nhid 10
Error: IPv6 routes can not use an IPv4 nexthop.

Solve this by updating the indication according to the new set of
member nexthops.

Signed-off-by: Ido Schimmel <idosch@nvidia.com>
Reviewed-by: David Ahern <dsahern@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv4/nexthop.c

index 1b736e3..5199a28 100644 (file)
@@ -797,7 +797,7 @@ static void remove_nh_grp_entry(struct net *net, struct nh_grp_entry *nhge,
                return;
        }
 
-       newg->has_v4 = nhg->has_v4;
+       newg->has_v4 = false;
        newg->mpath = nhg->mpath;
        newg->fdb_nh = nhg->fdb_nh;
        newg->num_nh = nhg->num_nh;
@@ -806,12 +806,18 @@ static void remove_nh_grp_entry(struct net *net, struct nh_grp_entry *nhge,
        nhges = nhg->nh_entries;
        new_nhges = newg->nh_entries;
        for (i = 0, j = 0; i < nhg->num_nh; ++i) {
+               struct nh_info *nhi;
+
                /* current nexthop getting removed */
                if (nhg->nh_entries[i].nh == nh) {
                        newg->num_nh--;
                        continue;
                }
 
+               nhi = rtnl_dereference(nhges[i].nh->nh_info);
+               if (nhi->family == AF_INET)
+                       newg->has_v4 = true;
+
                list_del(&nhges[i].nh_list);
                new_nhges[j].nh_parent = nhges[i].nh_parent;
                new_nhges[j].nh = nhges[i].nh;