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>
This commit is contained in:
David Ahern 2019-06-08 14:53:24 -07:00 committed by David S. Miller
parent 2ab75bfb17
commit 962b680383
1 changed files with 52 additions and 2 deletions

View File

@ -490,6 +490,45 @@ static bool __rt6_device_match(struct net *net, const struct fib6_nh *nh,
return false; 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, static void rt6_device_match(struct net *net, struct fib6_result *res,
const struct in6_addr *saddr, int oif, int flags) 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)) { for (spf6i = f6i; spf6i; spf6i = rcu_dereference(spf6i->fib6_next)) {
nh = spf6i->fib6_nh; bool matched = false;
if (__rt6_device_match(net, nh, saddr, oif, flags)) {
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; res->f6i = spf6i;
goto out; goto out;
} }