On 8/26/20 10:48 AM, Ido Schimmel wrote: > From: Ido Schimmel <ido...@nvidia.com> > > 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 replaced. 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 10 group 1 > # ip nexthop replace id 1 via 2001:db8:1::2 dev dummy10 > # ip route replace 2001:db8:10::/64 nhid 10 > Error: IPv6 routes can not use an IPv4 nexthop. > > Solve this by iterating over all the nexthop groups that the replaced > nexthop is a member of and potentially update their IPv4 indication > according to the new set of member nexthops. > > Avoid wasting cycles by only performing the update in case an IPv4 > nexthop is replaced by an IPv6 nexthop. > > Signed-off-by: Ido Schimmel <ido...@nvidia.com> > --- > net/ipv4/nexthop.c | 32 ++++++++++++++++++++++++++++++++ > 1 file changed, 32 insertions(+) > > diff --git a/net/ipv4/nexthop.c b/net/ipv4/nexthop.c > index 5199a2815df6..bf9d4cd2d6e5 100644 > --- a/net/ipv4/nexthop.c > +++ b/net/ipv4/nexthop.c > @@ -964,6 +964,23 @@ static int replace_nexthop_grp(struct net *net, struct > nexthop *old, > return 0; > } > > +static void nh_group_v4_update(struct nh_group *nhg) > +{ > + struct nh_grp_entry *nhges; > + bool has_v4 = false; > + int i; > + > + nhges = nhg->nh_entries; > + for (i = 0; i < nhg->num_nh; i++) { > + struct nh_info *nhi; > + > + nhi = rtnl_dereference(nhges[i].nh->nh_info); > + if (nhi->family == AF_INET) > + has_v4 = true; > + } > + nhg->has_v4 = has_v4; > +} > + > static int replace_nexthop_single(struct net *net, struct nexthop *old, > struct nexthop *new, > struct netlink_ext_ack *extack) > @@ -987,6 +1004,21 @@ static int replace_nexthop_single(struct net *net, > struct nexthop *old, > rcu_assign_pointer(old->nh_info, newi); > rcu_assign_pointer(new->nh_info, oldi); > > + /* When replacing an IPv4 nexthop with an IPv6 nexthop, potentially > + * update IPv4 indication in all the groups using the nexthop. > + */ > + if (oldi->family == AF_INET && newi->family == AF_INET6) { > + struct nh_grp_entry *nhge; > + > + list_for_each_entry(nhge, &old->grp_list, nh_list) { > + struct nexthop *nhp = nhge->nh_parent; > + struct nh_group *nhg; > + > + nhg = rtnl_dereference(nhp->nh_grp); > + nh_group_v4_update(nhg); > + } > + } > + > return 0; > } > >
Hopefully userspace apps create a new nexthop versus overwriting existing ones with different address family. Thanks for handling this case and adding a test case. Reviewed-by: David Ahern <dsah...@gmail.com>