Commit 0121383d authored by David S. Miller's avatar David S. Miller

Merge branch 'net-ipv6-Add-support-for-ONLINK-flag'

David Ahern says:

====================
net/ipv6: Add support for ONLINK flag

Add support for RTNH_F_ONLINK with ipv6 routes.

First patch moves existing gateway validation into helper. The onlink
flag requires a different set of checks and the existing validation
makes ip6_route_info_create long enough.

Second patch makes the table id and lookup flag an option to
ip6_nh_lookup_table. onlink check needs to verify the gateway without
the RT6_LOOKUP_F_IFACE flag and PBR with VRF means the table id can
vary between the table the route is inserted and the VRF the egress
device is enslaved to.

Third patch adds support for RTNH_F_ONLINK.

I have a set of test cases in a format based on the framework Ido and
Jiri are working on. Once that goes in I will adapt the script and
submit.

v2
- removed table id check. Too constraining for PBR with VRF use cases
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 9515a2e0 fc1e64e1
...@@ -2440,7 +2440,8 @@ static int ip6_convert_metrics(struct mx6_config *mxc, ...@@ -2440,7 +2440,8 @@ static int ip6_convert_metrics(struct mx6_config *mxc,
static struct rt6_info *ip6_nh_lookup_table(struct net *net, static struct rt6_info *ip6_nh_lookup_table(struct net *net,
struct fib6_config *cfg, struct fib6_config *cfg,
const struct in6_addr *gw_addr) const struct in6_addr *gw_addr,
u32 tbid, int flags)
{ {
struct flowi6 fl6 = { struct flowi6 fl6 = {
.flowi6_oif = cfg->fc_ifindex, .flowi6_oif = cfg->fc_ifindex,
...@@ -2449,15 +2450,15 @@ static struct rt6_info *ip6_nh_lookup_table(struct net *net, ...@@ -2449,15 +2450,15 @@ static struct rt6_info *ip6_nh_lookup_table(struct net *net,
}; };
struct fib6_table *table; struct fib6_table *table;
struct rt6_info *rt; struct rt6_info *rt;
int flags = RT6_LOOKUP_F_IFACE | RT6_LOOKUP_F_IGNORE_LINKSTATE;
table = fib6_get_table(net, cfg->fc_table); table = fib6_get_table(net, tbid);
if (!table) if (!table)
return NULL; return NULL;
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;
rt = ip6_pol_route(net, table, cfg->fc_ifindex, &fl6, flags); rt = ip6_pol_route(net, table, cfg->fc_ifindex, &fl6, flags);
/* if table lookup failed, fall back to full lookup */ /* if table lookup failed, fall back to full lookup */
...@@ -2469,6 +2470,82 @@ static struct rt6_info *ip6_nh_lookup_table(struct net *net, ...@@ -2469,6 +2470,82 @@ static struct rt6_info *ip6_nh_lookup_table(struct net *net,
return rt; return rt;
} }
static int ip6_route_check_nh_onlink(struct net *net,
struct fib6_config *cfg,
struct net_device *dev,
struct netlink_ext_ack *extack)
{
u32 tbid = l3mdev_fib_table(dev) ? : RT_TABLE_LOCAL;
const struct in6_addr *gw_addr = &cfg->fc_gateway;
u32 flags = RTF_LOCAL | RTF_ANYCAST | RTF_REJECT;
struct rt6_info *grt;
int err;
err = 0;
grt = ip6_nh_lookup_table(net, cfg, gw_addr, tbid, 0);
if (grt) {
if (grt->rt6i_flags & flags || dev != grt->dst.dev) {
NL_SET_ERR_MSG(extack, "Nexthop has invalid gateway");
err = -EINVAL;
}
ip6_rt_put(grt);
}
return err;
}
static int ip6_route_check_nh(struct net *net,
struct fib6_config *cfg,
struct net_device **_dev,
struct inet6_dev **idev)
{
const struct in6_addr *gw_addr = &cfg->fc_gateway;
struct net_device *dev = _dev ? *_dev : NULL;
struct rt6_info *grt = NULL;
int err = -EHOSTUNREACH;
if (cfg->fc_table) {
int flags = RT6_LOOKUP_F_IFACE;
grt = ip6_nh_lookup_table(net, cfg, gw_addr,
cfg->fc_table, flags);
if (grt) {
if (grt->rt6i_flags & RTF_GATEWAY ||
(dev && dev != grt->dst.dev)) {
ip6_rt_put(grt);
grt = NULL;
}
}
}
if (!grt)
grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
if (!grt)
goto out;
if (dev) {
if (dev != grt->dst.dev) {
ip6_rt_put(grt);
goto out;
}
} else {
*_dev = dev = grt->dst.dev;
*idev = grt->rt6i_idev;
dev_hold(dev);
in6_dev_hold(grt->rt6i_idev);
}
if (!(grt->rt6i_flags & RTF_GATEWAY))
err = 0;
ip6_rt_put(grt);
out:
return err;
}
static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg, static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
...@@ -2520,6 +2597,21 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg, ...@@ -2520,6 +2597,21 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg,
if (cfg->fc_metric == 0) if (cfg->fc_metric == 0)
cfg->fc_metric = IP6_RT_PRIO_USER; cfg->fc_metric = IP6_RT_PRIO_USER;
if (cfg->fc_flags & RTNH_F_ONLINK) {
if (!dev) {
NL_SET_ERR_MSG(extack,
"Nexthop device required for onlink");
err = -ENODEV;
goto out;
}
if (!(dev->flags & IFF_UP)) {
NL_SET_ERR_MSG(extack, "Nexthop device is not up");
err = -ENETDOWN;
goto out;
}
}
err = -ENOBUFS; err = -ENOBUFS;
if (cfg->fc_nlinfo.nlh && if (cfg->fc_nlinfo.nlh &&
!(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) { !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
...@@ -2664,8 +2756,6 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg, ...@@ -2664,8 +2756,6 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg,
rt->rt6i_gateway = *gw_addr; rt->rt6i_gateway = *gw_addr;
if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) { if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
struct rt6_info *grt = NULL;
/* IPv6 strictly inhibits using not link-local /* IPv6 strictly inhibits using not link-local
addresses as nexthop address. addresses as nexthop address.
Otherwise, router will not able to send redirects. Otherwise, router will not able to send redirects.
...@@ -2682,40 +2772,12 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg, ...@@ -2682,40 +2772,12 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg,
goto out; goto out;
} }
if (cfg->fc_table) { if (cfg->fc_flags & RTNH_F_ONLINK) {
grt = ip6_nh_lookup_table(net, cfg, gw_addr); err = ip6_route_check_nh_onlink(net, cfg, dev,
extack);
if (grt) {
if (grt->rt6i_flags & RTF_GATEWAY ||
(dev && dev != grt->dst.dev)) {
ip6_rt_put(grt);
grt = NULL;
}
}
}
if (!grt)
grt = rt6_lookup(net, gw_addr, NULL,
cfg->fc_ifindex, 1);
err = -EHOSTUNREACH;
if (!grt)
goto out;
if (dev) {
if (dev != grt->dst.dev) {
ip6_rt_put(grt);
goto out;
}
} else { } else {
dev = grt->dst.dev; err = ip6_route_check_nh(net, cfg, &dev, &idev);
idev = grt->rt6i_idev;
dev_hold(dev);
in6_dev_hold(grt->rt6i_idev);
} }
if (!(grt->rt6i_flags & RTF_GATEWAY))
err = 0;
ip6_rt_put(grt);
if (err) if (err)
goto out; goto out;
} }
...@@ -2757,6 +2819,7 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg, ...@@ -2757,6 +2819,7 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg,
if (!(rt->rt6i_flags & (RTF_LOCAL | RTF_ANYCAST)) && if (!(rt->rt6i_flags & (RTF_LOCAL | RTF_ANYCAST)) &&
!netif_carrier_ok(dev)) !netif_carrier_ok(dev))
rt->rt6i_nh_flags |= RTNH_F_LINKDOWN; rt->rt6i_nh_flags |= RTNH_F_LINKDOWN;
rt->rt6i_nh_flags |= (cfg->fc_flags & RTNH_F_ONLINK);
rt->dst.dev = dev; rt->dst.dev = dev;
rt->rt6i_idev = idev; rt->rt6i_idev = idev;
rt->rt6i_table = table; rt->rt6i_table = table;
...@@ -3826,6 +3889,8 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -3826,6 +3889,8 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
if (rtm->rtm_flags & RTM_F_CLONED) if (rtm->rtm_flags & RTM_F_CLONED)
cfg->fc_flags |= RTF_CACHE; cfg->fc_flags |= RTF_CACHE;
cfg->fc_flags |= (rtm->rtm_flags & RTNH_F_ONLINK);
cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid; cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid;
cfg->fc_nlinfo.nlh = nlh; cfg->fc_nlinfo.nlh = nlh;
cfg->fc_nlinfo.nl_net = sock_net(skb->sk); cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
...@@ -4231,6 +4296,7 @@ static int rt6_nexthop_info(struct sk_buff *skb, struct rt6_info *rt, ...@@ -4231,6 +4296,7 @@ static int rt6_nexthop_info(struct sk_buff *skb, struct rt6_info *rt,
goto nla_put_failure; goto nla_put_failure;
} }
*flags |= (rt->rt6i_nh_flags & RTNH_F_ONLINK);
if (rt->rt6i_nh_flags & RTNH_F_OFFLOAD) if (rt->rt6i_nh_flags & RTNH_F_OFFLOAD)
*flags |= RTNH_F_OFFLOAD; *flags |= RTNH_F_OFFLOAD;
......
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