diff options
author | David Ahern <dsahern@gmail.com> | 2019-06-08 14:53:24 -0700 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2019-06-10 10:44:56 -0700 |
commit | 962b68038351396ae218560c2f5b7af3e54d304c (patch) | |
tree | c1813369186229762c8475c7822df0cd3f51e88d /net/ipv6/route.c | |
parent | 2ab75bfb17e2cf3159033e91ebf2eb11efc1f0d3 (diff) | |
download | linux-stable-962b68038351396ae218560c2f5b7af3e54d304c.tar.gz linux-stable-962b68038351396ae218560c2f5b7af3e54d304c.tar.bz2 linux-stable-962b68038351396ae218560c2f5b7af3e54d304c.zip |
ipv6: Handle all fib6_nh in a nexthop in rt6_device_match
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>
Diffstat (limited to 'net/ipv6/route.c')
-rw-r--r-- | net/ipv6/route.c | 54 |
1 files changed, 52 insertions, 2 deletions
diff --git a/net/ipv6/route.c b/net/ipv6/route.c index fd0dc18ec574..aac209381903 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -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; } |