Commit b2c709cc authored by David Ahern's avatar David Ahern Committed by David S. Miller

ipv6: Convert gateway validation to use fib6_info

Gateway validation does not need a dst_entry, it only needs the fib
entry to validate the gateway resolution and egress device. So,
convert ip6_nh_lookup_table from ip6_pol_route to fib6_table_lookup
and ip6_route_check_nh to use fib6_lookup over rt6_lookup.

ip6_pol_route is a call to fib6_table_lookup and if successful a call
to fib6_select_path. From there the exception cache is searched for an
entry or a dst_entry is created to return to the caller. The exception
entry is not relevant for gateway validation, so what matters are the
calls to fib6_table_lookup and then fib6_select_path.

Similarly, rt6_lookup can be replaced with a call to fib6_lookup with
RT6_LOOKUP_F_IFACE set in flags. Again, the exception cache search is
not relevant, only the lookup with path selection. The primary difference
in the lookup paths is the use of rt6_select with fib6_lookup versus
rt6_device_match with rt6_lookup. When you remove complexities in the
rt6_select path, e.g.,
1. saddr is not set for gateway validation, so RT6_LOOKUP_F_HAS_SADDR
   is not relevant
2. rt6_check_neigh is not called so that removes the RT6_NUD_FAIL_DO_RR
   return and round-robin logic.

the code paths are believed to be equivalent for the given use case -
validate the gateway and optionally given the device. Furthermore, it
aligns the validation with onlink code path and the lookup path actually
used for rx and tx.

Adjust the users, ip6_route_check_nh_onlink and ip6_route_check_nh to
handle a fib6_info vs a rt6_info when performing validation checks.

Existing selftests fib-onlink-tests.sh and fib_tests.sh are used to
verify the changes.
Signed-off-by: default avatarDavid Ahern <dsahern@gmail.com>
Reviewed-by: default avatarWei Wang <weiwan@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 5b1bf3f6
...@@ -3144,10 +3144,9 @@ static int ip6_dst_gc(struct dst_ops *ops) ...@@ -3144,10 +3144,9 @@ static int ip6_dst_gc(struct dst_ops *ops)
return entries > rt_max_size; return entries > rt_max_size;
} }
static struct rt6_info *ip6_nh_lookup_table(struct net *net, static int ip6_nh_lookup_table(struct net *net, struct fib6_config *cfg,
struct fib6_config *cfg, const struct in6_addr *gw_addr, u32 tbid,
const struct in6_addr *gw_addr, int flags, struct fib6_result *res)
u32 tbid, int flags)
{ {
struct flowi6 fl6 = { struct flowi6 fl6 = {
.flowi6_oif = cfg->fc_ifindex, .flowi6_oif = cfg->fc_ifindex,
...@@ -3155,25 +3154,23 @@ static struct rt6_info *ip6_nh_lookup_table(struct net *net, ...@@ -3155,25 +3154,23 @@ static struct rt6_info *ip6_nh_lookup_table(struct net *net,
.saddr = cfg->fc_prefsrc, .saddr = cfg->fc_prefsrc,
}; };
struct fib6_table *table; struct fib6_table *table;
struct rt6_info *rt; int err;
table = fib6_get_table(net, tbid); table = fib6_get_table(net, tbid);
if (!table) if (!table)
return NULL; return -EINVAL;
if (!ipv6_addr_any(&cfg->fc_prefsrc)) if (!ipv6_addr_any(&cfg->fc_prefsrc))
flags |= RT6_LOOKUP_F_HAS_SADDR; flags |= RT6_LOOKUP_F_HAS_SADDR;
flags |= RT6_LOOKUP_F_IGNORE_LINKSTATE; flags |= RT6_LOOKUP_F_IGNORE_LINKSTATE;
rt = ip6_pol_route(net, table, cfg->fc_ifindex, &fl6, NULL, flags);
/* if table lookup failed, fall back to full lookup */ err = fib6_table_lookup(net, table, cfg->fc_ifindex, &fl6, res, flags);
if (rt == net->ipv6.ip6_null_entry) { if (!err && res->f6i != net->ipv6.fib6_null_entry)
ip6_rt_put(rt); fib6_select_path(net, res, &fl6, cfg->fc_ifindex,
rt = NULL; cfg->fc_ifindex != 0, NULL, flags);
}
return rt; return err;
} }
static int ip6_route_check_nh_onlink(struct net *net, static int ip6_route_check_nh_onlink(struct net *net,
...@@ -3181,29 +3178,19 @@ static int ip6_route_check_nh_onlink(struct net *net, ...@@ -3181,29 +3178,19 @@ static int ip6_route_check_nh_onlink(struct net *net,
const struct net_device *dev, const struct net_device *dev,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
u32 tbid = l3mdev_fib_table(dev) ? : RT_TABLE_MAIN; u32 tbid = l3mdev_fib_table_rcu(dev) ? : RT_TABLE_MAIN;
const struct in6_addr *gw_addr = &cfg->fc_gateway; const struct in6_addr *gw_addr = &cfg->fc_gateway;
u32 flags = RTF_LOCAL | RTF_ANYCAST | RTF_REJECT; struct fib6_result res = {};
struct fib6_info *from;
struct rt6_info *grt;
int err; int err;
err = 0; err = ip6_nh_lookup_table(net, cfg, gw_addr, tbid, 0, &res);
grt = ip6_nh_lookup_table(net, cfg, gw_addr, tbid, 0); if (!err && !(res.fib6_flags & RTF_REJECT) &&
if (grt) { /* ignore match if it is the default route */
rcu_read_lock(); !ipv6_addr_any(&res.f6i->fib6_dst.addr) &&
from = rcu_dereference(grt->from); (res.fib6_type != RTN_UNICAST || dev != res.nh->fib_nh_dev)) {
if (!grt->dst.error && NL_SET_ERR_MSG(extack,
/* ignore match if it is the default route */ "Nexthop has invalid gateway or device mismatch");
from && !ipv6_addr_any(&from->fib6_dst.addr) && err = -EINVAL;
(grt->rt6i_flags & flags || dev != grt->dst.dev)) {
NL_SET_ERR_MSG(extack,
"Nexthop has invalid gateway or device mismatch");
err = -EINVAL;
}
rcu_read_unlock();
ip6_rt_put(grt);
} }
return err; return err;
...@@ -3216,47 +3203,50 @@ static int ip6_route_check_nh(struct net *net, ...@@ -3216,47 +3203,50 @@ static int ip6_route_check_nh(struct net *net,
{ {
const struct in6_addr *gw_addr = &cfg->fc_gateway; const struct in6_addr *gw_addr = &cfg->fc_gateway;
struct net_device *dev = _dev ? *_dev : NULL; struct net_device *dev = _dev ? *_dev : NULL;
struct rt6_info *grt = NULL; int flags = RT6_LOOKUP_F_IFACE;
struct fib6_result res = {};
int err = -EHOSTUNREACH; int err = -EHOSTUNREACH;
if (cfg->fc_table) { if (cfg->fc_table) {
int flags = RT6_LOOKUP_F_IFACE; err = ip6_nh_lookup_table(net, cfg, gw_addr,
cfg->fc_table, flags, &res);
grt = ip6_nh_lookup_table(net, cfg, gw_addr, /* gw_addr can not require a gateway or resolve to a reject
cfg->fc_table, flags); * route. If a device is given, it must match the result.
if (grt) { */
if (grt->rt6i_flags & RTF_GATEWAY || if (err || res.fib6_flags & RTF_REJECT ||
(dev && dev != grt->dst.dev)) { res.nh->fib_nh_gw_family ||
ip6_rt_put(grt); (dev && dev != res.nh->fib_nh_dev))
grt = NULL; err = -EHOSTUNREACH;
}
}
} }
if (!grt) if (err < 0) {
grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, NULL, 1); struct flowi6 fl6 = {
.flowi6_oif = cfg->fc_ifindex,
.daddr = *gw_addr,
};
if (!grt) err = fib6_lookup(net, cfg->fc_ifindex, &fl6, &res, flags);
goto out; if (err || res.fib6_flags & RTF_REJECT ||
res.nh->fib_nh_gw_family)
err = -EHOSTUNREACH;
if (err)
return err;
fib6_select_path(net, &res, &fl6, cfg->fc_ifindex,
cfg->fc_ifindex != 0, NULL, flags);
}
err = 0;
if (dev) { if (dev) {
if (dev != grt->dst.dev) { if (dev != res.nh->fib_nh_dev)
ip6_rt_put(grt); err = -EHOSTUNREACH;
goto out;
}
} else { } else {
*_dev = dev = grt->dst.dev; *_dev = dev = res.nh->fib_nh_dev;
*idev = grt->rt6i_idev;
dev_hold(dev); dev_hold(dev);
in6_dev_hold(grt->rt6i_idev); *idev = in6_dev_get(dev);
} }
if (!(grt->rt6i_flags & RTF_GATEWAY))
err = 0;
ip6_rt_put(grt);
out:
return err; return err;
} }
...@@ -3297,11 +3287,15 @@ static int ip6_validate_gw(struct net *net, struct fib6_config *cfg, ...@@ -3297,11 +3287,15 @@ static int ip6_validate_gw(struct net *net, struct fib6_config *cfg,
goto out; goto out;
} }
rcu_read_lock();
if (cfg->fc_flags & RTNH_F_ONLINK) if (cfg->fc_flags & RTNH_F_ONLINK)
err = ip6_route_check_nh_onlink(net, cfg, dev, extack); err = ip6_route_check_nh_onlink(net, cfg, dev, extack);
else else
err = ip6_route_check_nh(net, cfg, _dev, idev); err = ip6_route_check_nh(net, cfg, _dev, idev);
rcu_read_unlock();
if (err) if (err)
goto out; goto out;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment