forked from mirrors/linux
		
	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: David Ahern <dsahern@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									6c48ea5fe6
								
							
						
					
					
						commit
						5b98324ebe
					
				
					 2 changed files with 82 additions and 8 deletions
				
			
		| 
						 | 
					@ -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,
 | 
				
			||||||
		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,
 | 
				
			||||||
	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,
 | 
				
			||||||
		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;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								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);
 | 
										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,
 | 
				
			||||||
				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,
 | 
				
			||||||
			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] = {
 | 
				
			||||||
	[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,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	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,
 | 
				
			||||||
	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 {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue