ipv6: Handle all fib6_nh in a nexthop in rt6_device_match
authorDavid Ahern <dsahern@gmail.com>
Sat, 8 Jun 2019 21:53:24 +0000 (14:53 -0700)
committerDavid S. Miller <davem@davemloft.net>
Mon, 10 Jun 2019 17:44:56 +0000 (10:44 -0700)
Add a hook in rt6_device_match to handle nexthop struct in a fib6_info.
The new rt6_nh_dev_match uses nexthop_for_each_fib6_nh to walk each
fib6_nh in a nexthop and call __rt6_device_match. On match,
rt6_nh_dev_match returns the fib6_nh and rt6_device_match uses it to
setup fib6_result.

Signed-off-by: David Ahern <dsahern@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv6/route.c

index fd0dc18ec5742aee4153a42644bb779325d04656..aac2093819039f028f2b55c0b51d857101f62117 100644 (file)
@@ -490,6 +490,45 @@ static bool __rt6_device_match(struct net *net, const struct fib6_nh *nh,
        return false;
 }
 
+struct fib6_nh_dm_arg {
+       struct net              *net;
+       const struct in6_addr   *saddr;
+       int                     oif;
+       int                     flags;
+       struct fib6_nh          *nh;
+};
+
+static int __rt6_nh_dev_match(struct fib6_nh *nh, void *_arg)
+{
+       struct fib6_nh_dm_arg *arg = _arg;
+
+       arg->nh = nh;
+       return __rt6_device_match(arg->net, nh, arg->saddr, arg->oif,
+                                 arg->flags);
+}
+
+/* returns fib6_nh from nexthop or NULL */
+static struct fib6_nh *rt6_nh_dev_match(struct net *net, struct nexthop *nh,
+                                       struct fib6_result *res,
+                                       const struct in6_addr *saddr,
+                                       int oif, int flags)
+{
+       struct fib6_nh_dm_arg arg = {
+               .net   = net,
+               .saddr = saddr,
+               .oif   = oif,
+               .flags = flags,
+       };
+
+       if (nexthop_is_blackhole(nh))
+               return NULL;
+
+       if (nexthop_for_each_fib6_nh(nh, __rt6_nh_dev_match, &arg))
+               return arg.nh;
+
+       return NULL;
+}
+
 static void rt6_device_match(struct net *net, struct fib6_result *res,
                             const struct in6_addr *saddr, int oif, int flags)
 {
@@ -510,8 +549,19 @@ static void rt6_device_match(struct net *net, struct fib6_result *res,
        }
 
        for (spf6i = f6i; spf6i; spf6i = rcu_dereference(spf6i->fib6_next)) {
-               nh = spf6i->fib6_nh;
-               if (__rt6_device_match(net, nh, saddr, oif, flags)) {
+               bool matched = false;
+
+               if (unlikely(spf6i->nh)) {
+                       nh = rt6_nh_dev_match(net, spf6i->nh, res, saddr,
+                                             oif, flags);
+                       if (nh)
+                               matched = true;
+               } else {
+                       nh = spf6i->fib6_nh;
+                       if (__rt6_device_match(net, nh, saddr, oif, flags))
+                               matched = true;
+               }
+               if (matched) {
                        res->f6i = spf6i;
                        goto out;
                }