ipv6: Handle all fib6_nh in a nexthop in __find_rr_leaf
authorDavid Ahern <dsahern@gmail.com>
Sat, 8 Jun 2019 21:53:25 +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 __find_rr_leaf to handle nexthop struct in a fib6_info.
nexthop_for_each_fib6_nh is used to walk each fib6_nh in a nexthop and
call find_match. On a match, use the fib6_nh saved in the callback arg
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 aac2093819039f028f2b55c0b51d857101f62117..740df725b9fc78773a0242590c79f99da39209f3 100644 (file)
@@ -765,6 +765,24 @@ out:
        return rc;
 }
 
+struct fib6_nh_frl_arg {
+       u32             flags;
+       int             oif;
+       int             strict;
+       int             *mpri;
+       bool            *do_rr;
+       struct fib6_nh  *nh;
+};
+
+static int rt6_nh_find_match(struct fib6_nh *nh, void *_arg)
+{
+       struct fib6_nh_frl_arg *arg = _arg;
+
+       arg->nh = nh;
+       return find_match(nh, arg->flags, arg->oif, arg->strict,
+                         arg->mpri, arg->do_rr);
+}
+
 static void __find_rr_leaf(struct fib6_info *f6i_start,
                           struct fib6_info *nomatch, u32 metric,
                           struct fib6_result *res, struct fib6_info **cont,
@@ -775,6 +793,7 @@ static void __find_rr_leaf(struct fib6_info *f6i_start,
        for (f6i = f6i_start;
             f6i && f6i != nomatch;
             f6i = rcu_dereference(f6i->fib6_next)) {
+               bool matched = false;
                struct fib6_nh *nh;
 
                if (cont && f6i->fib6_metric != metric) {
@@ -785,8 +804,34 @@ static void __find_rr_leaf(struct fib6_info *f6i_start,
                if (fib6_check_expired(f6i))
                        continue;
 
-               nh = f6i->fib6_nh;
-               if (find_match(nh, f6i->fib6_flags, oif, strict, mpri, do_rr)) {
+               if (unlikely(f6i->nh)) {
+                       struct fib6_nh_frl_arg arg = {
+                               .flags  = f6i->fib6_flags,
+                               .oif    = oif,
+                               .strict = strict,
+                               .mpri   = mpri,
+                               .do_rr  = do_rr
+                       };
+
+                       if (nexthop_is_blackhole(f6i->nh)) {
+                               res->fib6_flags = RTF_REJECT;
+                               res->fib6_type = RTN_BLACKHOLE;
+                               res->f6i = f6i;
+                               res->nh = nexthop_fib6_nh(f6i->nh);
+                               return;
+                       }
+                       if (nexthop_for_each_fib6_nh(f6i->nh, rt6_nh_find_match,
+                                                    &arg)) {
+                               matched = true;
+                               nh = arg.nh;
+                       }
+               } else {
+                       nh = f6i->fib6_nh;
+                       if (find_match(nh, f6i->fib6_flags, oif, strict,
+                                      mpri, do_rr))
+                               matched = true;
+               }
+               if (matched) {
                        res->f6i = f6i;
                        res->nh = nh;
                        res->fib6_flags = f6i->fib6_flags;