forked from mirrors/linux
		
	ipv6: Plumb support for nexthop object in a fib6_info
Add struct nexthop and nh_list list_head to fib6_info. nh_list is the fib6_info side of the nexthop <-> fib_info relationship. Since a fib6_info referencing a nexthop object can not have 'sibling' entries (the old way of doing multipath routes), the nh_list is a union with fib6_siblings. Add f6i_list list_head to 'struct nexthop' to track fib6_info entries using a nexthop instance. Update __remove_nexthop_fib to walk f6_list and delete fib entries using the nexthop. Add a few nexthop helpers for use when a nexthop is added to fib6_info: - nexthop_fib6_nh - return first fib6_nh in a nexthop object - fib6_info_nh_dev moved to nexthop.h and updated to use nexthop_fib6_nh if the fib6_info references a nexthop object - nexthop_path_fib6_result - similar to ipv4, select a path within a multipath nexthop object. If the nexthop is a blackhole, set fib6_result type to RTN_BLACKHOLE, and set the REJECT flag Update the fib6_info references to check for nh and take a different path as needed: - rt6_qualify_for_ecmp - if a fib entry uses a nexthop object it can NOT be coalesced with other fib entries into a multipath route - rt6_duplicate_nexthop - use nexthop_cmp if either fib6_info references a nexthop - addrconf (host routes), RA's and info entries (anything configured via ndisc) does not use nexthop objects - fib6_info_destroy_rcu - put reference to nexthop object - fib6_purge_rt - drop fib6_info from f6i_list - fib6_select_path - update to use the new nexthop_path_fib6_result when fib entry uses a nexthop object - rt6_device_match - update to catch use of nexthop object as a blackhole and set fib6_type and flags. - ip6_route_info_create - don't add space for fib6_nh if fib entry is going to reference a nexthop object, take a reference to nexthop object, disallow use of source routing - rt6_nlmsg_size - add space for RTA_NH_ID - add rt6_fill_node_nexthop to add nexthop data on a dump As with ipv4, most of the changes push existing code into the else branch of whether the fib entry uses a nexthop object. Update the nexthop code to walk f6i_list on a nexthop deleted to remove fib entries referencing it. Signed-off-by: David Ahern <dsahern@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									4c7e8084fd
								
							
						
					
					
						commit
						f88d8ea67f
					
				
					 8 changed files with 260 additions and 36 deletions
				
			
		| 
						 | 
					@ -146,7 +146,10 @@ struct fib6_info {
 | 
				
			||||||
	 * destination, but not the same gateway. nsiblings is just a cache
 | 
						 * destination, but not the same gateway. nsiblings is just a cache
 | 
				
			||||||
	 * to speed up lookup.
 | 
						 * to speed up lookup.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
 | 
						union {
 | 
				
			||||||
		struct list_head	fib6_siblings;
 | 
							struct list_head	fib6_siblings;
 | 
				
			||||||
 | 
							struct list_head	nh_list;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
	unsigned int			fib6_nsiblings;
 | 
						unsigned int			fib6_nsiblings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	refcount_t			fib6_ref;
 | 
						refcount_t			fib6_ref;
 | 
				
			||||||
| 
						 | 
					@ -170,6 +173,7 @@ struct fib6_info {
 | 
				
			||||||
					unused:3;
 | 
										unused:3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct rcu_head			rcu;
 | 
						struct rcu_head			rcu;
 | 
				
			||||||
 | 
						struct nexthop			*nh;
 | 
				
			||||||
	struct fib6_nh			fib6_nh[0];
 | 
						struct fib6_nh			fib6_nh[0];
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -441,11 +445,6 @@ void rt6_get_prefsrc(const struct rt6_info *rt, struct in6_addr *addr)
 | 
				
			||||||
	rcu_read_unlock();
 | 
						rcu_read_unlock();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline struct net_device *fib6_info_nh_dev(const struct fib6_info *f6i)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return f6i->fib6_nh->fib_nh_dev;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int fib6_nh_init(struct net *net, struct fib6_nh *fib6_nh,
 | 
					int fib6_nh_init(struct net *net, struct fib6_nh *fib6_nh,
 | 
				
			||||||
		 struct fib6_config *cfg, gfp_t gfp_flags,
 | 
							 struct fib6_config *cfg, gfp_t gfp_flags,
 | 
				
			||||||
		 struct netlink_ext_ack *extack);
 | 
							 struct netlink_ext_ack *extack);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,6 +27,7 @@ struct route_info {
 | 
				
			||||||
#include <linux/ip.h>
 | 
					#include <linux/ip.h>
 | 
				
			||||||
#include <linux/ipv6.h>
 | 
					#include <linux/ipv6.h>
 | 
				
			||||||
#include <linux/route.h>
 | 
					#include <linux/route.h>
 | 
				
			||||||
 | 
					#include <net/nexthop.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define RT6_LOOKUP_F_IFACE		0x00000001
 | 
					#define RT6_LOOKUP_F_IFACE		0x00000001
 | 
				
			||||||
#define RT6_LOOKUP_F_REACHABLE		0x00000002
 | 
					#define RT6_LOOKUP_F_REACHABLE		0x00000002
 | 
				
			||||||
| 
						 | 
					@ -66,10 +67,13 @@ static inline bool rt6_need_strict(const struct in6_addr *daddr)
 | 
				
			||||||
		(IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK);
 | 
							(IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* fib entries using a nexthop object can not be coalesced into
 | 
				
			||||||
 | 
					 * a multipath route
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
static inline bool rt6_qualify_for_ecmp(const struct fib6_info *f6i)
 | 
					static inline bool rt6_qualify_for_ecmp(const struct fib6_info *f6i)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	/* the RTF_ADDRCONF flag filters out RA's */
 | 
						/* the RTF_ADDRCONF flag filters out RA's */
 | 
				
			||||||
	return !(f6i->fib6_flags & RTF_ADDRCONF) &&
 | 
						return !(f6i->fib6_flags & RTF_ADDRCONF) && !f6i->nh &&
 | 
				
			||||||
		f6i->fib6_nh->fib_nh_gw_family;
 | 
							f6i->fib6_nh->fib_nh_gw_family;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -275,8 +279,13 @@ static inline struct in6_addr *rt6_nexthop(struct rt6_info *rt,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline bool rt6_duplicate_nexthop(struct fib6_info *a, struct fib6_info *b)
 | 
					static inline bool rt6_duplicate_nexthop(struct fib6_info *a, struct fib6_info *b)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct fib6_nh *nha = a->fib6_nh, *nhb = b->fib6_nh;
 | 
						struct fib6_nh *nha, *nhb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (a->nh || b->nh)
 | 
				
			||||||
 | 
							return nexthop_cmp(a->nh, b->nh);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nha = a->fib6_nh;
 | 
				
			||||||
 | 
						nhb = b->fib6_nh;
 | 
				
			||||||
	return nha->fib_nh_dev == nhb->fib_nh_dev &&
 | 
						return nha->fib_nh_dev == nhb->fib_nh_dev &&
 | 
				
			||||||
	       ipv6_addr_equal(&nha->fib_nh_gw6, &nhb->fib_nh_gw6) &&
 | 
						       ipv6_addr_equal(&nha->fib_nh_gw6, &nhb->fib_nh_gw6) &&
 | 
				
			||||||
	       !lwtunnel_cmp_encap(nha->fib_nh_lws, nhb->fib_nh_lws);
 | 
						       !lwtunnel_cmp_encap(nha->fib_nh_lws, nhb->fib_nh_lws);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,6 +10,7 @@
 | 
				
			||||||
#define __LINUX_NEXTHOP_H
 | 
					#define __LINUX_NEXTHOP_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <linux/netdevice.h>
 | 
					#include <linux/netdevice.h>
 | 
				
			||||||
 | 
					#include <linux/route.h>
 | 
				
			||||||
#include <linux/types.h>
 | 
					#include <linux/types.h>
 | 
				
			||||||
#include <net/ip_fib.h>
 | 
					#include <net/ip_fib.h>
 | 
				
			||||||
#include <net/ip6_fib.h>
 | 
					#include <net/ip6_fib.h>
 | 
				
			||||||
| 
						 | 
					@ -78,6 +79,7 @@ struct nh_group {
 | 
				
			||||||
struct nexthop {
 | 
					struct nexthop {
 | 
				
			||||||
	struct rb_node		rb_node;    /* entry on netns rbtree */
 | 
						struct rb_node		rb_node;    /* entry on netns rbtree */
 | 
				
			||||||
	struct list_head	fi_list;    /* v4 entries using nh */
 | 
						struct list_head	fi_list;    /* v4 entries using nh */
 | 
				
			||||||
 | 
						struct list_head	f6i_list;   /* v6 entries using nh */
 | 
				
			||||||
	struct list_head	grp_list;   /* nh group entries using this nh */
 | 
						struct list_head	grp_list;   /* nh group entries using this nh */
 | 
				
			||||||
	struct net		*net;
 | 
						struct net		*net;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -255,4 +257,52 @@ static inline struct fib_nh *fib_info_nh(struct fib_info *fi, int nhsel)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return &fi->fib_nh[nhsel];
 | 
						return &fi->fib_nh[nhsel];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * IPv6 variants
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int fib6_check_nexthop(struct nexthop *nh, struct fib6_config *cfg,
 | 
				
			||||||
 | 
							       struct netlink_ext_ack *extack);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline struct fib6_nh *nexthop_fib6_nh(struct nexthop *nh)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct nh_info *nhi;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (nexthop_is_multipath(nh)) {
 | 
				
			||||||
 | 
							nh = nexthop_mpath_select(nh, 0);
 | 
				
			||||||
 | 
							if (!nh)
 | 
				
			||||||
 | 
								return NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nhi = rcu_dereference_rtnl(nh->nh_info);
 | 
				
			||||||
 | 
						if (nhi->family == AF_INET6)
 | 
				
			||||||
 | 
							return &nhi->fib6_nh;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline struct net_device *fib6_info_nh_dev(struct fib6_info *f6i)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct fib6_nh *fib6_nh;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fib6_nh = f6i->nh ? nexthop_fib6_nh(f6i->nh) : f6i->fib6_nh;
 | 
				
			||||||
 | 
						return fib6_nh->fib_nh_dev;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline void nexthop_path_fib6_result(struct fib6_result *res, int hash)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct nexthop *nh = res->f6i->nh;
 | 
				
			||||||
 | 
						struct nh_info *nhi;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nh = nexthop_select_path(nh, hash);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nhi = rcu_dereference_rtnl(nh->nh_info);
 | 
				
			||||||
 | 
						if (nhi->reject_nh) {
 | 
				
			||||||
 | 
							res->fib6_type = RTN_BLACKHOLE;
 | 
				
			||||||
 | 
							res->fib6_flags |= RTF_REJECT;
 | 
				
			||||||
 | 
							res->nh = nexthop_fib6_nh(nh);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							res->nh = &nhi->fib6_nh;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -106,6 +106,7 @@ static struct nexthop *nexthop_alloc(void)
 | 
				
			||||||
	nh = kzalloc(sizeof(struct nexthop), GFP_KERNEL);
 | 
						nh = kzalloc(sizeof(struct nexthop), GFP_KERNEL);
 | 
				
			||||||
	if (nh) {
 | 
						if (nh) {
 | 
				
			||||||
		INIT_LIST_HEAD(&nh->fi_list);
 | 
							INIT_LIST_HEAD(&nh->fi_list);
 | 
				
			||||||
 | 
							INIT_LIST_HEAD(&nh->f6i_list);
 | 
				
			||||||
		INIT_LIST_HEAD(&nh->grp_list);
 | 
							INIT_LIST_HEAD(&nh->grp_list);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nh;
 | 
						return nh;
 | 
				
			||||||
| 
						 | 
					@ -516,6 +517,41 @@ struct nexthop *nexthop_select_path(struct nexthop *nh, int hash)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL_GPL(nexthop_select_path);
 | 
					EXPORT_SYMBOL_GPL(nexthop_select_path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int fib6_check_nexthop(struct nexthop *nh, struct fib6_config *cfg,
 | 
				
			||||||
 | 
							       struct netlink_ext_ack *extack)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct nh_info *nhi;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* fib6_src is unique to a fib6_info and limits the ability to cache
 | 
				
			||||||
 | 
						 * routes in fib6_nh within a nexthop that is potentially shared
 | 
				
			||||||
 | 
						 * across multiple fib entries. If the config wants to use source
 | 
				
			||||||
 | 
						 * routing it can not use nexthop objects. mlxsw also does not allow
 | 
				
			||||||
 | 
						 * fib6_src on routes.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (!ipv6_addr_any(&cfg->fc_src)) {
 | 
				
			||||||
 | 
							NL_SET_ERR_MSG(extack, "IPv6 routes using source address can not use nexthop objects");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (nh->is_group) {
 | 
				
			||||||
 | 
							struct nh_group *nhg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							nhg = rtnl_dereference(nh->nh_grp);
 | 
				
			||||||
 | 
							if (nhg->has_v4)
 | 
				
			||||||
 | 
								goto no_v4_nh;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							nhi = rtnl_dereference(nh->nh_info);
 | 
				
			||||||
 | 
							if (nhi->family == AF_INET)
 | 
				
			||||||
 | 
								goto no_v4_nh;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					no_v4_nh:
 | 
				
			||||||
 | 
						NL_SET_ERR_MSG(extack, "IPv6 routes can not use an IPv4 nexthop");
 | 
				
			||||||
 | 
						return -EINVAL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(fib6_check_nexthop);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int nexthop_check_scope(struct nexthop *nh, u8 scope,
 | 
					static int nexthop_check_scope(struct nexthop *nh, u8 scope,
 | 
				
			||||||
			       struct netlink_ext_ack *extack)
 | 
								       struct netlink_ext_ack *extack)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -658,6 +694,7 @@ static void remove_nexthop_group(struct nexthop *nh, struct nl_info *nlinfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void __remove_nexthop_fib(struct net *net, struct nexthop *nh)
 | 
					static void __remove_nexthop_fib(struct net *net, struct nexthop *nh)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct fib6_info *f6i, *tmp;
 | 
				
			||||||
	bool do_flush = false;
 | 
						bool do_flush = false;
 | 
				
			||||||
	struct fib_info *fi;
 | 
						struct fib_info *fi;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -667,6 +704,13 @@ static void __remove_nexthop_fib(struct net *net, struct nexthop *nh)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (do_flush)
 | 
						if (do_flush)
 | 
				
			||||||
		fib_flush(net);
 | 
							fib_flush(net);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* ip6_del_rt removes the entry from this list hence the _safe */
 | 
				
			||||||
 | 
						list_for_each_entry_safe(f6i, tmp, &nh->f6i_list, nh_list) {
 | 
				
			||||||
 | 
							/* __ip6_del_rt does a release, so do a hold here */
 | 
				
			||||||
 | 
							fib6_info_hold(f6i);
 | 
				
			||||||
 | 
							ipv6_stub->ip6_del_rt(net, f6i);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void __remove_nexthop(struct net *net, struct nexthop *nh,
 | 
					static void __remove_nexthop(struct net *net, struct nexthop *nh,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2421,6 +2421,10 @@ static struct fib6_info *addrconf_get_prefix_route(const struct in6_addr *pfx,
 | 
				
			||||||
		goto out;
 | 
							goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for_each_fib6_node_rt_rcu(fn) {
 | 
						for_each_fib6_node_rt_rcu(fn) {
 | 
				
			||||||
 | 
							/* prefix routes only use builtin fib6_nh */
 | 
				
			||||||
 | 
							if (rt->nh)
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (rt->fib6_nh->fib_nh_dev->ifindex != dev->ifindex)
 | 
							if (rt->fib6_nh->fib_nh_dev->ifindex != dev->ifindex)
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
		if (no_gw && rt->fib6_nh->fib_nh_gw_family)
 | 
							if (no_gw && rt->fib6_nh->fib_nh_gw_family)
 | 
				
			||||||
| 
						 | 
					@ -6352,6 +6356,7 @@ void addrconf_disable_policy_idev(struct inet6_dev *idev, int val)
 | 
				
			||||||
	list_for_each_entry(ifa, &idev->addr_list, if_list) {
 | 
						list_for_each_entry(ifa, &idev->addr_list, if_list) {
 | 
				
			||||||
		spin_lock(&ifa->lock);
 | 
							spin_lock(&ifa->lock);
 | 
				
			||||||
		if (ifa->rt) {
 | 
							if (ifa->rt) {
 | 
				
			||||||
 | 
								/* host routes only use builtin fib6_nh */
 | 
				
			||||||
			struct fib6_nh *nh = ifa->rt->fib6_nh;
 | 
								struct fib6_nh *nh = ifa->rt->fib6_nh;
 | 
				
			||||||
			int cpu;
 | 
								int cpu;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -159,6 +159,7 @@ struct fib6_info *fib6_info_alloc(gfp_t gfp_flags, bool with_fib6_nh)
 | 
				
			||||||
	if (!f6i)
 | 
						if (!f6i)
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* fib6_siblings is a union with nh_list, so this initializes both */
 | 
				
			||||||
	INIT_LIST_HEAD(&f6i->fib6_siblings);
 | 
						INIT_LIST_HEAD(&f6i->fib6_siblings);
 | 
				
			||||||
	refcount_set(&f6i->fib6_ref, 1);
 | 
						refcount_set(&f6i->fib6_ref, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -171,7 +172,11 @@ void fib6_info_destroy_rcu(struct rcu_head *head)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	WARN_ON(f6i->fib6_node);
 | 
						WARN_ON(f6i->fib6_node);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (f6i->nh)
 | 
				
			||||||
 | 
							nexthop_put(f6i->nh);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
		fib6_nh_release(f6i->fib6_nh);
 | 
							fib6_nh_release(f6i->fib6_nh);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ip_fib_metrics_put(f6i->fib6_metrics);
 | 
						ip_fib_metrics_put(f6i->fib6_metrics);
 | 
				
			||||||
	kfree(f6i);
 | 
						kfree(f6i);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -927,6 +932,9 @@ static void fib6_purge_rt(struct fib6_info *rt, struct fib6_node *fn,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fib6_drop_pcpu_from(rt, table);
 | 
						fib6_drop_pcpu_from(rt, table);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (rt->nh && !list_empty(&rt->nh_list))
 | 
				
			||||||
 | 
							list_del_init(&rt->nh_list);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (refcount_read(&rt->fib6_ref) != 1) {
 | 
						if (refcount_read(&rt->fib6_ref) != 1) {
 | 
				
			||||||
		/* This route is used as dummy address holder in some split
 | 
							/* This route is used as dummy address holder in some split
 | 
				
			||||||
		 * nodes. It is not leaked, but it still holds other resources,
 | 
							 * nodes. It is not leaked, but it still holds other resources,
 | 
				
			||||||
| 
						 | 
					@ -1334,6 +1342,8 @@ int fib6_add(struct fib6_node *root, struct fib6_info *rt,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = fib6_add_rt2node(fn, rt, info, extack);
 | 
						err = fib6_add_rt2node(fn, rt, info, extack);
 | 
				
			||||||
	if (!err) {
 | 
						if (!err) {
 | 
				
			||||||
 | 
							if (rt->nh)
 | 
				
			||||||
 | 
								list_add(&rt->nh_list, &rt->nh->f6i_list);
 | 
				
			||||||
		__fib6_update_sernum_upto_root(rt, sernum);
 | 
							__fib6_update_sernum_upto_root(rt, sernum);
 | 
				
			||||||
		fib6_start_gc(info->nl_net, rt);
 | 
							fib6_start_gc(info->nl_net, rt);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -2295,9 +2305,13 @@ static int ipv6_route_seq_show(struct seq_file *seq, void *v)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct fib6_info *rt = v;
 | 
						struct fib6_info *rt = v;
 | 
				
			||||||
	struct ipv6_route_iter *iter = seq->private;
 | 
						struct ipv6_route_iter *iter = seq->private;
 | 
				
			||||||
 | 
						struct fib6_nh *fib6_nh = rt->fib6_nh;
 | 
				
			||||||
	unsigned int flags = rt->fib6_flags;
 | 
						unsigned int flags = rt->fib6_flags;
 | 
				
			||||||
	const struct net_device *dev;
 | 
						const struct net_device *dev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (rt->nh)
 | 
				
			||||||
 | 
							fib6_nh = nexthop_fib6_nh(rt->nh);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	seq_printf(seq, "%pi6 %02x ", &rt->fib6_dst.addr, rt->fib6_dst.plen);
 | 
						seq_printf(seq, "%pi6 %02x ", &rt->fib6_dst.addr, rt->fib6_dst.plen);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_IPV6_SUBTREES
 | 
					#ifdef CONFIG_IPV6_SUBTREES
 | 
				
			||||||
| 
						 | 
					@ -2305,14 +2319,14 @@ static int ipv6_route_seq_show(struct seq_file *seq, void *v)
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
	seq_puts(seq, "00000000000000000000000000000000 00 ");
 | 
						seq_puts(seq, "00000000000000000000000000000000 00 ");
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	if (rt->fib6_nh->fib_nh_gw_family) {
 | 
						if (fib6_nh->fib_nh_gw_family) {
 | 
				
			||||||
		flags |= RTF_GATEWAY;
 | 
							flags |= RTF_GATEWAY;
 | 
				
			||||||
		seq_printf(seq, "%pi6", &rt->fib6_nh->fib_nh_gw6);
 | 
							seq_printf(seq, "%pi6", &fib6_nh->fib_nh_gw6);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		seq_puts(seq, "00000000000000000000000000000000");
 | 
							seq_puts(seq, "00000000000000000000000000000000");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dev = rt->fib6_nh->fib_nh_dev;
 | 
						dev = fib6_nh->fib_nh_dev;
 | 
				
			||||||
	seq_printf(seq, " %08x %08x %08x %08x %8s\n",
 | 
						seq_printf(seq, " %08x %08x %08x %08x %8s\n",
 | 
				
			||||||
		   rt->fib6_metric, refcount_read(&rt->fib6_ref), 0,
 | 
							   rt->fib6_metric, refcount_read(&rt->fib6_ref), 0,
 | 
				
			||||||
		   flags, dev ? dev->name : "");
 | 
							   flags, dev ? dev->name : "");
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1289,9 +1289,8 @@ static void ndisc_router_discovery(struct sk_buff *skb)
 | 
				
			||||||
	    !in6_dev->cnf.accept_ra_rtr_pref)
 | 
						    !in6_dev->cnf.accept_ra_rtr_pref)
 | 
				
			||||||
		pref = ICMPV6_ROUTER_PREF_MEDIUM;
 | 
							pref = ICMPV6_ROUTER_PREF_MEDIUM;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
						/* routes added from RAs do not use nexthop objects */
 | 
				
			||||||
	rt = rt6_get_dflt_router(net, &ipv6_hdr(skb)->saddr, skb->dev);
 | 
						rt = rt6_get_dflt_router(net, &ipv6_hdr(skb)->saddr, skb->dev);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (rt) {
 | 
						if (rt) {
 | 
				
			||||||
		neigh = ip6_neigh_lookup(&rt->fib6_nh->fib_nh_gw6,
 | 
							neigh = ip6_neigh_lookup(&rt->fib6_nh->fib_nh_gw6,
 | 
				
			||||||
					 rt->fib6_nh->fib_nh_dev, NULL,
 | 
										 rt->fib6_nh->fib_nh_dev, NULL,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										132
									
								
								net/ipv6/route.c
									
									
									
									
									
								
							
							
						
						
									
										132
									
								
								net/ipv6/route.c
									
									
									
									
									
								
							| 
						 | 
					@ -432,15 +432,21 @@ void fib6_select_path(const struct net *net, struct fib6_result *res,
 | 
				
			||||||
	struct fib6_info *sibling, *next_sibling;
 | 
						struct fib6_info *sibling, *next_sibling;
 | 
				
			||||||
	struct fib6_info *match = res->f6i;
 | 
						struct fib6_info *match = res->f6i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!match->fib6_nsiblings || have_oif_match)
 | 
						if ((!match->fib6_nsiblings && !match->nh) || have_oif_match)
 | 
				
			||||||
		goto out;
 | 
							goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* We might have already computed the hash for ICMPv6 errors. In such
 | 
						/* We might have already computed the hash for ICMPv6 errors. In such
 | 
				
			||||||
	 * case it will always be non-zero. Otherwise now is the time to do it.
 | 
						 * case it will always be non-zero. Otherwise now is the time to do it.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (!fl6->mp_hash)
 | 
						if (!fl6->mp_hash &&
 | 
				
			||||||
 | 
						    (!match->nh || nexthop_is_multipath(match->nh)))
 | 
				
			||||||
		fl6->mp_hash = rt6_multipath_hash(net, fl6, skb, NULL);
 | 
							fl6->mp_hash = rt6_multipath_hash(net, fl6, skb, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (unlikely(match->nh)) {
 | 
				
			||||||
 | 
							nexthop_path_fib6_result(res, fl6->mp_hash);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (fl6->mp_hash <= atomic_read(&match->fib6_nh->fib_nh_upper_bound))
 | 
						if (fl6->mp_hash <= atomic_read(&match->fib6_nh->fib_nh_upper_bound))
 | 
				
			||||||
		goto out;
 | 
							goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -496,7 +502,13 @@ static void rt6_device_match(struct net *net, struct fib6_result *res,
 | 
				
			||||||
	struct fib6_nh *nh;
 | 
						struct fib6_nh *nh;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!oif && ipv6_addr_any(saddr)) {
 | 
						if (!oif && ipv6_addr_any(saddr)) {
 | 
				
			||||||
 | 
							if (unlikely(f6i->nh)) {
 | 
				
			||||||
 | 
								nh = nexthop_fib6_nh(f6i->nh);
 | 
				
			||||||
 | 
								if (nexthop_is_blackhole(f6i->nh))
 | 
				
			||||||
 | 
									goto out_blackhole;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
			nh = f6i->fib6_nh;
 | 
								nh = f6i->fib6_nh;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		if (!(nh->fib_nh_flags & RTNH_F_DEAD))
 | 
							if (!(nh->fib_nh_flags & RTNH_F_DEAD))
 | 
				
			||||||
			goto out;
 | 
								goto out;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -515,7 +527,14 @@ static void rt6_device_match(struct net *net, struct fib6_result *res,
 | 
				
			||||||
		goto out;
 | 
							goto out;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (unlikely(f6i->nh)) {
 | 
				
			||||||
 | 
							nh = nexthop_fib6_nh(f6i->nh);
 | 
				
			||||||
 | 
							if (nexthop_is_blackhole(f6i->nh))
 | 
				
			||||||
 | 
								goto out_blackhole;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
		nh = f6i->fib6_nh;
 | 
							nh = f6i->fib6_nh;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (nh->fib_nh_flags & RTNH_F_DEAD) {
 | 
						if (nh->fib_nh_flags & RTNH_F_DEAD) {
 | 
				
			||||||
		res->f6i = net->ipv6.fib6_null_entry;
 | 
							res->f6i = net->ipv6.fib6_null_entry;
 | 
				
			||||||
		nh = res->f6i->fib6_nh;
 | 
							nh = res->f6i->fib6_nh;
 | 
				
			||||||
| 
						 | 
					@ -524,6 +543,12 @@ static void rt6_device_match(struct net *net, struct fib6_result *res,
 | 
				
			||||||
	res->nh = nh;
 | 
						res->nh = nh;
 | 
				
			||||||
	res->fib6_type = res->f6i->fib6_type;
 | 
						res->fib6_type = res->f6i->fib6_type;
 | 
				
			||||||
	res->fib6_flags = res->f6i->fib6_flags;
 | 
						res->fib6_flags = res->f6i->fib6_flags;
 | 
				
			||||||
 | 
						return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					out_blackhole:
 | 
				
			||||||
 | 
						res->fib6_flags |= RTF_REJECT;
 | 
				
			||||||
 | 
						res->fib6_type = RTN_BLACKHOLE;
 | 
				
			||||||
 | 
						res->nh = nh;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_IPV6_ROUTER_PREF
 | 
					#ifdef CONFIG_IPV6_ROUTER_PREF
 | 
				
			||||||
| 
						 | 
					@ -1117,6 +1142,8 @@ static struct rt6_info *ip6_pol_route_lookup(struct net *net,
 | 
				
			||||||
		rt = net->ipv6.ip6_null_entry;
 | 
							rt = net->ipv6.ip6_null_entry;
 | 
				
			||||||
		dst_hold(&rt->dst);
 | 
							dst_hold(&rt->dst);
 | 
				
			||||||
		goto out;
 | 
							goto out;
 | 
				
			||||||
 | 
						} else if (res.fib6_flags & RTF_REJECT) {
 | 
				
			||||||
 | 
							goto do_create;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fib6_select_path(net, &res, fl6, fl6->flowi6_oif,
 | 
						fib6_select_path(net, &res, fl6, fl6->flowi6_oif,
 | 
				
			||||||
| 
						 | 
					@ -1128,6 +1155,7 @@ static struct rt6_info *ip6_pol_route_lookup(struct net *net,
 | 
				
			||||||
		if (ip6_hold_safe(net, &rt))
 | 
							if (ip6_hold_safe(net, &rt))
 | 
				
			||||||
			dst_use_noref(&rt->dst, jiffies);
 | 
								dst_use_noref(&rt->dst, jiffies);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
 | 
					do_create:
 | 
				
			||||||
		rt = ip6_create_rt_rcu(&res);
 | 
							rt = ip6_create_rt_rcu(&res);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3217,7 +3245,9 @@ static struct fib6_info *ip6_route_info_create(struct fib6_config *cfg,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct net *net = cfg->fc_nlinfo.nl_net;
 | 
						struct net *net = cfg->fc_nlinfo.nl_net;
 | 
				
			||||||
	struct fib6_info *rt = NULL;
 | 
						struct fib6_info *rt = NULL;
 | 
				
			||||||
 | 
						struct nexthop *nh = NULL;
 | 
				
			||||||
	struct fib6_table *table;
 | 
						struct fib6_table *table;
 | 
				
			||||||
 | 
						struct fib6_nh *fib6_nh;
 | 
				
			||||||
	int err = -EINVAL;
 | 
						int err = -EINVAL;
 | 
				
			||||||
	int addr_type;
 | 
						int addr_type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3270,7 +3300,7 @@ static struct fib6_info *ip6_route_info_create(struct fib6_config *cfg,
 | 
				
			||||||
		goto out;
 | 
							goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = -ENOMEM;
 | 
						err = -ENOMEM;
 | 
				
			||||||
	rt = fib6_info_alloc(gfp_flags, true);
 | 
						rt = fib6_info_alloc(gfp_flags, !nh);
 | 
				
			||||||
	if (!rt)
 | 
						if (!rt)
 | 
				
			||||||
		goto out;
 | 
							goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3310,19 +3340,35 @@ static struct fib6_info *ip6_route_info_create(struct fib6_config *cfg,
 | 
				
			||||||
	ipv6_addr_prefix(&rt->fib6_src.addr, &cfg->fc_src, cfg->fc_src_len);
 | 
						ipv6_addr_prefix(&rt->fib6_src.addr, &cfg->fc_src, cfg->fc_src_len);
 | 
				
			||||||
	rt->fib6_src.plen = cfg->fc_src_len;
 | 
						rt->fib6_src.plen = cfg->fc_src_len;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
						if (nh) {
 | 
				
			||||||
 | 
							if (!nexthop_get(nh)) {
 | 
				
			||||||
 | 
								NL_SET_ERR_MSG(extack, "Nexthop has been deleted");
 | 
				
			||||||
 | 
								goto out;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (rt->fib6_src.plen) {
 | 
				
			||||||
 | 
								NL_SET_ERR_MSG(extack, "Nexthops can not be used wtih source routing");
 | 
				
			||||||
 | 
								goto out;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							rt->nh = nh;
 | 
				
			||||||
 | 
							fib6_nh = nexthop_fib6_nh(rt->nh);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
		err = fib6_nh_init(net, rt->fib6_nh, cfg, gfp_flags, extack);
 | 
							err = fib6_nh_init(net, rt->fib6_nh, cfg, gfp_flags, extack);
 | 
				
			||||||
		if (err)
 | 
							if (err)
 | 
				
			||||||
			goto out;
 | 
								goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* We cannot add true routes via loopback here,
 | 
							fib6_nh = rt->fib6_nh;
 | 
				
			||||||
	 * they would result in kernel looping; promote them to reject routes
 | 
					
 | 
				
			||||||
 | 
							/* We cannot add true routes via loopback here, they would
 | 
				
			||||||
 | 
							 * result in kernel looping; promote them to reject routes
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
		addr_type = ipv6_addr_type(&cfg->fc_dst);
 | 
							addr_type = ipv6_addr_type(&cfg->fc_dst);
 | 
				
			||||||
	if (fib6_is_reject(cfg->fc_flags, rt->fib6_nh->fib_nh_dev, addr_type))
 | 
							if (fib6_is_reject(cfg->fc_flags, rt->fib6_nh->fib_nh_dev,
 | 
				
			||||||
 | 
									   addr_type))
 | 
				
			||||||
			rt->fib6_flags = RTF_REJECT | RTF_NONEXTHOP;
 | 
								rt->fib6_flags = RTF_REJECT | RTF_NONEXTHOP;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
 | 
						if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
 | 
				
			||||||
		struct net_device *dev = fib6_info_nh_dev(rt);
 | 
							struct net_device *dev = fib6_nh->fib_nh_dev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
 | 
							if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
 | 
				
			||||||
			NL_SET_ERR_MSG(extack, "Invalid source address");
 | 
								NL_SET_ERR_MSG(extack, "Invalid source address");
 | 
				
			||||||
| 
						 | 
					@ -3678,6 +3724,9 @@ static struct fib6_info *rt6_get_route_info(struct net *net,
 | 
				
			||||||
		goto out;
 | 
							goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for_each_fib6_node_rt_rcu(fn) {
 | 
						for_each_fib6_node_rt_rcu(fn) {
 | 
				
			||||||
 | 
							/* these routes do not use nexthops */
 | 
				
			||||||
 | 
							if (rt->nh)
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
		if (rt->fib6_nh->fib_nh_dev->ifindex != ifindex)
 | 
							if (rt->fib6_nh->fib_nh_dev->ifindex != ifindex)
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
		if (!(rt->fib6_flags & RTF_ROUTEINFO) ||
 | 
							if (!(rt->fib6_flags & RTF_ROUTEINFO) ||
 | 
				
			||||||
| 
						 | 
					@ -3741,8 +3790,13 @@ struct fib6_info *rt6_get_dflt_router(struct net *net,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rcu_read_lock();
 | 
						rcu_read_lock();
 | 
				
			||||||
	for_each_fib6_node_rt_rcu(&table->tb6_root) {
 | 
						for_each_fib6_node_rt_rcu(&table->tb6_root) {
 | 
				
			||||||
		struct fib6_nh *nh = rt->fib6_nh;
 | 
							struct fib6_nh *nh;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* RA routes do not use nexthops */
 | 
				
			||||||
 | 
							if (rt->nh)
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							nh = rt->fib6_nh;
 | 
				
			||||||
		if (dev == nh->fib_nh_dev &&
 | 
							if (dev == nh->fib_nh_dev &&
 | 
				
			||||||
		    ((rt->fib6_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
 | 
							    ((rt->fib6_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
 | 
				
			||||||
		    ipv6_addr_equal(&nh->fib_nh_gw6, addr))
 | 
							    ipv6_addr_equal(&nh->fib_nh_gw6, addr))
 | 
				
			||||||
| 
						 | 
					@ -3993,7 +4047,8 @@ static int fib6_remove_prefsrc(struct fib6_info *rt, void *arg)
 | 
				
			||||||
	struct net *net = ((struct arg_dev_net_ip *)arg)->net;
 | 
						struct net *net = ((struct arg_dev_net_ip *)arg)->net;
 | 
				
			||||||
	struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
 | 
						struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (((void *)rt->fib6_nh->fib_nh_dev == dev || !dev) &&
 | 
						if (!rt->nh &&
 | 
				
			||||||
 | 
						    ((void *)rt->fib6_nh->fib_nh_dev == dev || !dev) &&
 | 
				
			||||||
	    rt != net->ipv6.fib6_null_entry &&
 | 
						    rt != net->ipv6.fib6_null_entry &&
 | 
				
			||||||
	    ipv6_addr_equal(addr, &rt->fib6_prefsrc.addr)) {
 | 
						    ipv6_addr_equal(addr, &rt->fib6_prefsrc.addr)) {
 | 
				
			||||||
		spin_lock_bh(&rt6_exception_lock);
 | 
							spin_lock_bh(&rt6_exception_lock);
 | 
				
			||||||
| 
						 | 
					@ -4021,8 +4076,13 @@ void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
 | 
				
			||||||
static int fib6_clean_tohost(struct fib6_info *rt, void *arg)
 | 
					static int fib6_clean_tohost(struct fib6_info *rt, void *arg)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct in6_addr *gateway = (struct in6_addr *)arg;
 | 
						struct in6_addr *gateway = (struct in6_addr *)arg;
 | 
				
			||||||
	struct fib6_nh *nh = rt->fib6_nh;
 | 
						struct fib6_nh *nh;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* RA routes do not use nexthops */
 | 
				
			||||||
 | 
						if (rt->nh)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nh = rt->fib6_nh;
 | 
				
			||||||
	if (((rt->fib6_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) &&
 | 
						if (((rt->fib6_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) &&
 | 
				
			||||||
	    nh->fib_nh_gw_family && ipv6_addr_equal(gateway, &nh->fib_nh_gw6))
 | 
						    nh->fib_nh_gw_family && ipv6_addr_equal(gateway, &nh->fib_nh_gw6))
 | 
				
			||||||
		return -1;
 | 
							return -1;
 | 
				
			||||||
| 
						 | 
					@ -4069,6 +4129,7 @@ static struct fib6_info *rt6_multipath_first_sibling(const struct fib6_info *rt)
 | 
				
			||||||
	return NULL;
 | 
						return NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* only called for fib entries with builtin fib6_nh */
 | 
				
			||||||
static bool rt6_is_dead(const struct fib6_info *rt)
 | 
					static bool rt6_is_dead(const struct fib6_info *rt)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (rt->fib6_nh->fib_nh_flags & RTNH_F_DEAD ||
 | 
						if (rt->fib6_nh->fib_nh_flags & RTNH_F_DEAD ||
 | 
				
			||||||
| 
						 | 
					@ -4147,7 +4208,7 @@ static int fib6_ifup(struct fib6_info *rt, void *p_arg)
 | 
				
			||||||
	const struct arg_netdev_event *arg = p_arg;
 | 
						const struct arg_netdev_event *arg = p_arg;
 | 
				
			||||||
	struct net *net = dev_net(arg->dev);
 | 
						struct net *net = dev_net(arg->dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (rt != net->ipv6.fib6_null_entry &&
 | 
						if (rt != net->ipv6.fib6_null_entry && !rt->nh &&
 | 
				
			||||||
	    rt->fib6_nh->fib_nh_dev == arg->dev) {
 | 
						    rt->fib6_nh->fib_nh_dev == arg->dev) {
 | 
				
			||||||
		rt->fib6_nh->fib_nh_flags &= ~arg->nh_flags;
 | 
							rt->fib6_nh->fib_nh_flags &= ~arg->nh_flags;
 | 
				
			||||||
		fib6_update_sernum_upto_root(net, rt);
 | 
							fib6_update_sernum_upto_root(net, rt);
 | 
				
			||||||
| 
						 | 
					@ -4172,6 +4233,7 @@ void rt6_sync_up(struct net_device *dev, unsigned char nh_flags)
 | 
				
			||||||
	fib6_clean_all(dev_net(dev), fib6_ifup, &arg);
 | 
						fib6_clean_all(dev_net(dev), fib6_ifup, &arg);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* only called for fib entries with inline fib6_nh */
 | 
				
			||||||
static bool rt6_multipath_uses_dev(const struct fib6_info *rt,
 | 
					static bool rt6_multipath_uses_dev(const struct fib6_info *rt,
 | 
				
			||||||
				   const struct net_device *dev)
 | 
									   const struct net_device *dev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -4232,7 +4294,7 @@ static int fib6_ifdown(struct fib6_info *rt, void *p_arg)
 | 
				
			||||||
	const struct net_device *dev = arg->dev;
 | 
						const struct net_device *dev = arg->dev;
 | 
				
			||||||
	struct net *net = dev_net(dev);
 | 
						struct net *net = dev_net(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (rt == net->ipv6.fib6_null_entry)
 | 
						if (rt == net->ipv6.fib6_null_entry || rt->nh)
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (arg->event) {
 | 
						switch (arg->event) {
 | 
				
			||||||
| 
						 | 
					@ -4786,6 +4848,9 @@ static size_t rt6_nlmsg_size(struct fib6_info *rt)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int nexthop_len = 0;
 | 
						int nexthop_len = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (rt->nh)
 | 
				
			||||||
 | 
							nexthop_len += nla_total_size(4); /* RTA_NH_ID */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (rt->fib6_nsiblings) {
 | 
						if (rt->fib6_nsiblings) {
 | 
				
			||||||
		nexthop_len = nla_total_size(0)	 /* RTA_MULTIPATH */
 | 
							nexthop_len = nla_total_size(0)	 /* RTA_MULTIPATH */
 | 
				
			||||||
			    + NLA_ALIGN(sizeof(struct rtnexthop))
 | 
								    + NLA_ALIGN(sizeof(struct rtnexthop))
 | 
				
			||||||
| 
						 | 
					@ -4812,6 +4877,35 @@ static size_t rt6_nlmsg_size(struct fib6_info *rt)
 | 
				
			||||||
	       + nexthop_len;
 | 
						       + nexthop_len;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int rt6_fill_node_nexthop(struct sk_buff *skb, struct nexthop *nh,
 | 
				
			||||||
 | 
									 unsigned char *flags)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (nexthop_is_multipath(nh)) {
 | 
				
			||||||
 | 
							struct nlattr *mp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							mp = nla_nest_start(skb, RTA_MULTIPATH);
 | 
				
			||||||
 | 
							if (!mp)
 | 
				
			||||||
 | 
								goto nla_put_failure;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (nexthop_mpath_fill_node(skb, nh))
 | 
				
			||||||
 | 
								goto nla_put_failure;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							nla_nest_end(skb, mp);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							struct fib6_nh *fib6_nh;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							fib6_nh = nexthop_fib6_nh(nh);
 | 
				
			||||||
 | 
							if (fib_nexthop_info(skb, &fib6_nh->nh_common,
 | 
				
			||||||
 | 
									     flags, false) < 0)
 | 
				
			||||||
 | 
								goto nla_put_failure;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					nla_put_failure:
 | 
				
			||||||
 | 
						return -EMSGSIZE;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int rt6_fill_node(struct net *net, struct sk_buff *skb,
 | 
					static int rt6_fill_node(struct net *net, struct sk_buff *skb,
 | 
				
			||||||
			 struct fib6_info *rt, struct dst_entry *dst,
 | 
								 struct fib6_info *rt, struct dst_entry *dst,
 | 
				
			||||||
			 struct in6_addr *dest, struct in6_addr *src,
 | 
								 struct in6_addr *dest, struct in6_addr *src,
 | 
				
			||||||
| 
						 | 
					@ -4821,6 +4915,7 @@ static int rt6_fill_node(struct net *net, struct sk_buff *skb,
 | 
				
			||||||
	struct rt6_info *rt6 = (struct rt6_info *)dst;
 | 
						struct rt6_info *rt6 = (struct rt6_info *)dst;
 | 
				
			||||||
	struct rt6key *rt6_dst, *rt6_src;
 | 
						struct rt6key *rt6_dst, *rt6_src;
 | 
				
			||||||
	u32 *pmetrics, table, rt6_flags;
 | 
						u32 *pmetrics, table, rt6_flags;
 | 
				
			||||||
 | 
						unsigned char nh_flags = 0;
 | 
				
			||||||
	struct nlmsghdr *nlh;
 | 
						struct nlmsghdr *nlh;
 | 
				
			||||||
	struct rtmsg *rtm;
 | 
						struct rtmsg *rtm;
 | 
				
			||||||
	long expires = 0;
 | 
						long expires = 0;
 | 
				
			||||||
| 
						 | 
					@ -4940,9 +5035,18 @@ static int rt6_fill_node(struct net *net, struct sk_buff *skb,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		nla_nest_end(skb, mp);
 | 
							nla_nest_end(skb, mp);
 | 
				
			||||||
	} else {
 | 
						} else if (rt->nh) {
 | 
				
			||||||
		unsigned char nh_flags = 0;
 | 
							if (nla_put_u32(skb, RTA_NH_ID, rt->nh->id))
 | 
				
			||||||
 | 
								goto nla_put_failure;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (nexthop_is_blackhole(rt->nh))
 | 
				
			||||||
 | 
								rtm->rtm_type = RTN_BLACKHOLE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (rt6_fill_node_nexthop(skb, rt->nh, &nh_flags) < 0)
 | 
				
			||||||
 | 
								goto nla_put_failure;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							rtm->rtm_flags |= nh_flags;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
		if (fib_nexthop_info(skb, &rt->fib6_nh->nh_common,
 | 
							if (fib_nexthop_info(skb, &rt->fib6_nh->nh_common,
 | 
				
			||||||
				     &nh_flags, false) < 0)
 | 
									     &nh_flags, false) < 0)
 | 
				
			||||||
			goto nla_put_failure;
 | 
								goto nla_put_failure;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue