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

ipv6: Allow routes to use nexthop objects

Add support for RTA_NH_ID attribute to allow a user to specify a
nexthop id to use with a route. fc_nh_id is added to fib6_config to
hold the value passed in the RTA_NH_ID attribute. If a nexthop id
is given, the gateway, device, encap and multipath attributes can
not be set.

Update ip6_route_del to check metric and protocol before nexthop
specs. If fc_nh_id is set, then it must match the id in the route
entry. Since IPv6 allows delete of a cached entry (an exception),
add ip6_del_cached_rt_nh to cycle through all of the fib6_nh in
a fib entry if it is using a nexthop.
Signed-off-by: default avatarDavid Ahern <dsahern@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 6c48ea5f
...@@ -49,6 +49,7 @@ struct fib6_config { ...@@ -49,6 +49,7 @@ struct fib6_config {
u16 fc_delete_all_nh : 1, u16 fc_delete_all_nh : 1,
fc_ignore_dev_down:1, fc_ignore_dev_down:1,
__unused : 14; __unused : 14;
u32 fc_nh_id;
struct in6_addr fc_dst; struct in6_addr fc_dst;
struct in6_addr fc_src; struct in6_addr fc_src;
......
...@@ -3531,6 +3531,16 @@ static struct fib6_info *ip6_route_info_create(struct fib6_config *cfg, ...@@ -3531,6 +3531,16 @@ static struct fib6_info *ip6_route_info_create(struct fib6_config *cfg,
goto out; goto out;
} }
#endif #endif
if (cfg->fc_nh_id) {
nh = nexthop_find_by_id(net, cfg->fc_nh_id);
if (!nh) {
NL_SET_ERR_MSG(extack, "Nexthop id does not exist");
goto out;
}
err = fib6_check_nexthop(nh, cfg, extack);
if (err)
goto out;
}
err = -ENOBUFS; err = -ENOBUFS;
if (cfg->fc_nlinfo.nlh && if (cfg->fc_nlinfo.nlh &&
...@@ -3762,6 +3772,30 @@ static int ip6_del_cached_rt(struct fib6_config *cfg, struct fib6_info *rt, ...@@ -3762,6 +3772,30 @@ static int ip6_del_cached_rt(struct fib6_config *cfg, struct fib6_info *rt,
return 0; return 0;
} }
struct fib6_nh_del_cached_rt_arg {
struct fib6_config *cfg;
struct fib6_info *f6i;
};
static int fib6_nh_del_cached_rt(struct fib6_nh *nh, void *_arg)
{
struct fib6_nh_del_cached_rt_arg *arg = _arg;
int rc;
rc = ip6_del_cached_rt(arg->cfg, arg->f6i, nh);
return rc != -ESRCH ? rc : 0;
}
static int ip6_del_cached_rt_nh(struct fib6_config *cfg, struct fib6_info *f6i)
{
struct fib6_nh_del_cached_rt_arg arg = {
.cfg = cfg,
.f6i = f6i
};
return nexthop_for_each_fib6_nh(f6i->nh, fib6_nh_del_cached_rt, &arg);
}
static int ip6_route_del(struct fib6_config *cfg, static int ip6_route_del(struct fib6_config *cfg,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
...@@ -3787,11 +3821,20 @@ static int ip6_route_del(struct fib6_config *cfg, ...@@ -3787,11 +3821,20 @@ static int ip6_route_del(struct fib6_config *cfg,
for_each_fib6_node_rt_rcu(fn) { for_each_fib6_node_rt_rcu(fn) {
struct fib6_nh *nh; struct fib6_nh *nh;
nh = rt->fib6_nh; if (rt->nh && rt->nh->id != cfg->fc_nh_id)
if (cfg->fc_flags & RTF_CACHE) { continue;
int rc;
rc = ip6_del_cached_rt(cfg, rt, nh); if (cfg->fc_flags & RTF_CACHE) {
int rc = 0;
if (rt->nh) {
rc = ip6_del_cached_rt_nh(cfg, rt);
} else if (cfg->fc_nh_id) {
continue;
} else {
nh = rt->fib6_nh;
rc = ip6_del_cached_rt(cfg, rt, nh);
}
if (rc != -ESRCH) { if (rc != -ESRCH) {
rcu_read_unlock(); rcu_read_unlock();
return rc; return rc;
...@@ -3799,6 +3842,23 @@ static int ip6_route_del(struct fib6_config *cfg, ...@@ -3799,6 +3842,23 @@ static int ip6_route_del(struct fib6_config *cfg,
continue; continue;
} }
if (cfg->fc_metric && cfg->fc_metric != rt->fib6_metric)
continue;
if (cfg->fc_protocol &&
cfg->fc_protocol != rt->fib6_protocol)
continue;
if (rt->nh) {
if (!fib6_info_hold_safe(rt))
continue;
rcu_read_unlock();
return __ip6_del_rt(rt, &cfg->fc_nlinfo);
}
if (cfg->fc_nh_id)
continue;
nh = rt->fib6_nh;
if (cfg->fc_ifindex && if (cfg->fc_ifindex &&
(!nh->fib_nh_dev || (!nh->fib_nh_dev ||
nh->fib_nh_dev->ifindex != cfg->fc_ifindex)) nh->fib_nh_dev->ifindex != cfg->fc_ifindex))
...@@ -3806,10 +3866,6 @@ static int ip6_route_del(struct fib6_config *cfg, ...@@ -3806,10 +3866,6 @@ static int ip6_route_del(struct fib6_config *cfg,
if (cfg->fc_flags & RTF_GATEWAY && if (cfg->fc_flags & RTF_GATEWAY &&
!ipv6_addr_equal(&cfg->fc_gateway, &nh->fib_nh_gw6)) !ipv6_addr_equal(&cfg->fc_gateway, &nh->fib_nh_gw6))
continue; continue;
if (cfg->fc_metric && cfg->fc_metric != rt->fib6_metric)
continue;
if (cfg->fc_protocol && cfg->fc_protocol != rt->fib6_protocol)
continue;
if (!fib6_info_hold_safe(rt)) if (!fib6_info_hold_safe(rt))
continue; continue;
rcu_read_unlock(); rcu_read_unlock();
...@@ -4709,6 +4765,7 @@ static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = { ...@@ -4709,6 +4765,7 @@ static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
[RTA_IP_PROTO] = { .type = NLA_U8 }, [RTA_IP_PROTO] = { .type = NLA_U8 },
[RTA_SPORT] = { .type = NLA_U16 }, [RTA_SPORT] = { .type = NLA_U16 },
[RTA_DPORT] = { .type = NLA_U16 }, [RTA_DPORT] = { .type = NLA_U16 },
[RTA_NH_ID] = { .type = NLA_U32 },
}; };
static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
...@@ -4755,6 +4812,16 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -4755,6 +4812,16 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
cfg->fc_flags |= (rtm->rtm_flags & RTNH_F_ONLINK); cfg->fc_flags |= (rtm->rtm_flags & RTNH_F_ONLINK);
if (tb[RTA_NH_ID]) {
if (tb[RTA_GATEWAY] || tb[RTA_OIF] ||
tb[RTA_MULTIPATH] || tb[RTA_ENCAP]) {
NL_SET_ERR_MSG(extack,
"Nexthop specification and nexthop id are mutually exclusive");
goto errout;
}
cfg->fc_nh_id = nla_get_u32(tb[RTA_NH_ID]);
}
if (tb[RTA_GATEWAY]) { if (tb[RTA_GATEWAY]) {
cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]); cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]);
cfg->fc_flags |= RTF_GATEWAY; cfg->fc_flags |= RTF_GATEWAY;
...@@ -5089,6 +5156,12 @@ static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -5089,6 +5156,12 @@ static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh,
if (err < 0) if (err < 0)
return err; return err;
if (cfg.fc_nh_id &&
!nexthop_find_by_id(sock_net(skb->sk), cfg.fc_nh_id)) {
NL_SET_ERR_MSG(extack, "Nexthop id does not exist");
return -EINVAL;
}
if (cfg.fc_mp) if (cfg.fc_mp)
return ip6_route_multipath_del(&cfg, extack); return ip6_route_multipath_del(&cfg, extack);
else { else {
......
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