forked from mirrors/linux
		
	net/ipv6: move metrics from dst to rt6_info
Similar to IPv4, add fib metrics to the fib struct, which at the moment
is rt6_info. Will be moved to fib6_info in a later patch. Copy metrics
into dst by reference using refcount.
To make the transition:
- add dst_metrics to rt6_info. Default to dst_default_metrics if no
  metrics are passed during route add. No need for a separate pmtu
  entry; it can reference the MTU slot in fib6_metrics
- ip6_convert_metrics allocates memory in the FIB entry and uses
  ip_metrics_convert to copy from netlink attribute to metrics entry
- the convert metrics call is done in ip6_route_info_create simplifying
  the route add path
  + fib6_commit_metrics and fib6_copy_metrics and the temporary
    mx6_config are no longer needed
- add fib6_metric_set helper to change the value of a metric in the
  fib entry since dst_metric_set can no longer be used
- cow_metrics for IPv6 can drop to dst_cow_metrics_generic
- rt6_dst_from_metrics_check is no longer needed
- rt6_fill_node needs the FIB entry and dst as separate arguments to
  keep compatibility with existing output. Current dst address is
  renamed to dest.
  (to be consistent with IPv4 rt6_fill_node really should be split
  into 2 functions similar to fib_dump_info and rt_fill_info)
- rt6_fill_node no longer needs the temporary metrics variable
Signed-off-by: David Ahern <dsahern@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
			
			
This commit is contained in:
		
							parent
							
								
									6edb3c96a5
								
							
						
					
					
						commit
						d4ead6b34b
					
				
					 5 changed files with 133 additions and 218 deletions
				
			
		| 
						 | 
					@ -94,11 +94,6 @@ struct fib6_gc_args {
 | 
				
			||||||
#define FIB6_SUBTREE(fn)	(rcu_dereference_protected((fn)->subtree, 1))
 | 
					#define FIB6_SUBTREE(fn)	(rcu_dereference_protected((fn)->subtree, 1))
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct mx6_config {
 | 
					 | 
				
			||||||
	const u32 *mx;
 | 
					 | 
				
			||||||
	DECLARE_BITMAP(mx_valid, RTAX_MAX);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 *	routing information
 | 
					 *	routing information
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
| 
						 | 
					@ -176,7 +171,6 @@ struct rt6_info {
 | 
				
			||||||
	struct rt6_exception_bucket __rcu *rt6i_exception_bucket;
 | 
						struct rt6_exception_bucket __rcu *rt6i_exception_bucket;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	u32				rt6i_metric;
 | 
						u32				rt6i_metric;
 | 
				
			||||||
	u32				rt6i_pmtu;
 | 
					 | 
				
			||||||
	/* more non-fragment space at head required */
 | 
						/* more non-fragment space at head required */
 | 
				
			||||||
	unsigned short			rt6i_nfheader_len;
 | 
						unsigned short			rt6i_nfheader_len;
 | 
				
			||||||
	u8				rt6i_protocol;
 | 
						u8				rt6i_protocol;
 | 
				
			||||||
| 
						 | 
					@ -185,6 +179,8 @@ struct rt6_info {
 | 
				
			||||||
					should_flush:1,
 | 
										should_flush:1,
 | 
				
			||||||
					unused:6;
 | 
										unused:6;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct dst_metrics		*fib6_metrics;
 | 
				
			||||||
 | 
					#define fib6_pmtu		fib6_metrics->metrics[RTAX_MTU-1]
 | 
				
			||||||
	struct fib6_nh			fib6_nh;
 | 
						struct fib6_nh			fib6_nh;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -390,8 +386,7 @@ void fib6_clean_all(struct net *net, int (*func)(struct rt6_info *, void *arg),
 | 
				
			||||||
		    void *arg);
 | 
							    void *arg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int fib6_add(struct fib6_node *root, struct rt6_info *rt,
 | 
					int fib6_add(struct fib6_node *root, struct rt6_info *rt,
 | 
				
			||||||
	     struct nl_info *info, struct mx6_config *mxc,
 | 
						     struct nl_info *info, struct netlink_ext_ack *extack);
 | 
				
			||||||
	     struct netlink_ext_ack *extack);
 | 
					 | 
				
			||||||
int fib6_del(struct rt6_info *rt, struct nl_info *info);
 | 
					int fib6_del(struct rt6_info *rt, struct nl_info *info);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info,
 | 
					void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info,
 | 
				
			||||||
| 
						 | 
					@ -420,6 +415,12 @@ int fib6_tables_dump(struct net *net, struct notifier_block *nb);
 | 
				
			||||||
void fib6_update_sernum(struct net *net, struct rt6_info *rt);
 | 
					void fib6_update_sernum(struct net *net, struct rt6_info *rt);
 | 
				
			||||||
void fib6_update_sernum_upto_root(struct net *net, struct rt6_info *rt);
 | 
					void fib6_update_sernum_upto_root(struct net *net, struct rt6_info *rt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void fib6_metric_set(struct rt6_info *f6i, int metric, u32 val);
 | 
				
			||||||
 | 
					static inline bool fib6_metric_locked(struct rt6_info *f6i, int metric)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return !!(f6i->fib6_metrics->metrics[RTAX_LOCK - 1] & (1 << metric));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_IPV6_MULTIPLE_TABLES
 | 
					#ifdef CONFIG_IPV6_MULTIPLE_TABLES
 | 
				
			||||||
int fib6_rules_init(void);
 | 
					int fib6_rules_init(void);
 | 
				
			||||||
void fib6_rules_cleanup(void);
 | 
					void fib6_rules_cleanup(void);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -58,6 +58,7 @@ const struct dst_metrics dst_default_metrics = {
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	.refcnt = REFCOUNT_INIT(1),
 | 
						.refcnt = REFCOUNT_INIT(1),
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					EXPORT_SYMBOL(dst_default_metrics);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void dst_init(struct dst_entry *dst, struct dst_ops *ops,
 | 
					void dst_init(struct dst_entry *dst, struct dst_ops *ops,
 | 
				
			||||||
	      struct net_device *dev, int initial_ref, int initial_obsolete,
 | 
						      struct net_device *dev, int initial_ref, int initial_obsolete,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -578,6 +578,24 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
 | 
				
			||||||
	return res;
 | 
						return res;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void fib6_metric_set(struct rt6_info *f6i, int metric, u32 val)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (!f6i)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (f6i->fib6_metrics == &dst_default_metrics) {
 | 
				
			||||||
 | 
							struct dst_metrics *p = kzalloc(sizeof(*p), GFP_ATOMIC);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!p)
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							refcount_set(&p->refcnt, 1);
 | 
				
			||||||
 | 
							f6i->fib6_metrics = p;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						f6i->fib6_metrics->metrics[metric - 1] = val;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 *	Routing Table
 | 
					 *	Routing Table
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
| 
						 | 
					@ -801,38 +819,6 @@ static struct fib6_node *fib6_add_1(struct net *net,
 | 
				
			||||||
	return ln;
 | 
						return ln;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void fib6_copy_metrics(u32 *mp, const struct mx6_config *mxc)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int i;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for (i = 0; i < RTAX_MAX; i++) {
 | 
					 | 
				
			||||||
		if (test_bit(i, mxc->mx_valid))
 | 
					 | 
				
			||||||
			mp[i] = mxc->mx[i];
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int fib6_commit_metrics(struct dst_entry *dst, struct mx6_config *mxc)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (!mxc->mx)
 | 
					 | 
				
			||||||
		return 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (dst->flags & DST_HOST) {
 | 
					 | 
				
			||||||
		u32 *mp = dst_metrics_write_ptr(dst);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (unlikely(!mp))
 | 
					 | 
				
			||||||
			return -ENOMEM;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		fib6_copy_metrics(mp, mxc);
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		dst_init_metrics(dst, mxc->mx, false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* We've stolen mx now. */
 | 
					 | 
				
			||||||
		mxc->mx = NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void fib6_purge_rt(struct rt6_info *rt, struct fib6_node *fn,
 | 
					static void fib6_purge_rt(struct rt6_info *rt, struct fib6_node *fn,
 | 
				
			||||||
			  struct net *net)
 | 
								  struct net *net)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -866,7 +852,7 @@ static void fib6_purge_rt(struct rt6_info *rt, struct fib6_node *fn,
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
 | 
					static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
 | 
				
			||||||
			    struct nl_info *info, struct mx6_config *mxc,
 | 
								    struct nl_info *info,
 | 
				
			||||||
			    struct netlink_ext_ack *extack)
 | 
								    struct netlink_ext_ack *extack)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct rt6_info *leaf = rcu_dereference_protected(fn->leaf,
 | 
						struct rt6_info *leaf = rcu_dereference_protected(fn->leaf,
 | 
				
			||||||
| 
						 | 
					@ -923,7 +909,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
 | 
				
			||||||
					rt6_clean_expires(iter);
 | 
										rt6_clean_expires(iter);
 | 
				
			||||||
				else
 | 
									else
 | 
				
			||||||
					rt6_set_expires(iter, rt->dst.expires);
 | 
										rt6_set_expires(iter, rt->dst.expires);
 | 
				
			||||||
				iter->rt6i_pmtu = rt->rt6i_pmtu;
 | 
									fib6_metric_set(iter, RTAX_MTU, rt->fib6_pmtu);
 | 
				
			||||||
				return -EEXIST;
 | 
									return -EEXIST;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			/* If we have the same destination and the same metric,
 | 
								/* If we have the same destination and the same metric,
 | 
				
			||||||
| 
						 | 
					@ -1002,9 +988,6 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
add:
 | 
					add:
 | 
				
			||||||
		nlflags |= NLM_F_CREATE;
 | 
							nlflags |= NLM_F_CREATE;
 | 
				
			||||||
		err = fib6_commit_metrics(&rt->dst, mxc);
 | 
					 | 
				
			||||||
		if (err)
 | 
					 | 
				
			||||||
			return err;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		err = call_fib6_entry_notifiers(info->nl_net,
 | 
							err = call_fib6_entry_notifiers(info->nl_net,
 | 
				
			||||||
						FIB_EVENT_ENTRY_ADD,
 | 
											FIB_EVENT_ENTRY_ADD,
 | 
				
			||||||
| 
						 | 
					@ -1035,10 +1018,6 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
 | 
				
			||||||
			return -ENOENT;
 | 
								return -ENOENT;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		err = fib6_commit_metrics(&rt->dst, mxc);
 | 
					 | 
				
			||||||
		if (err)
 | 
					 | 
				
			||||||
			return err;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		err = call_fib6_entry_notifiers(info->nl_net,
 | 
							err = call_fib6_entry_notifiers(info->nl_net,
 | 
				
			||||||
						FIB_EVENT_ENTRY_REPLACE,
 | 
											FIB_EVENT_ENTRY_REPLACE,
 | 
				
			||||||
						rt, extack);
 | 
											rt, extack);
 | 
				
			||||||
| 
						 | 
					@ -1135,8 +1114,7 @@ void fib6_update_sernum_upto_root(struct net *net, struct rt6_info *rt)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int fib6_add(struct fib6_node *root, struct rt6_info *rt,
 | 
					int fib6_add(struct fib6_node *root, struct rt6_info *rt,
 | 
				
			||||||
	     struct nl_info *info, struct mx6_config *mxc,
 | 
						     struct nl_info *info, struct netlink_ext_ack *extack)
 | 
				
			||||||
	     struct netlink_ext_ack *extack)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct fib6_table *table = rt->rt6i_table;
 | 
						struct fib6_table *table = rt->rt6i_table;
 | 
				
			||||||
	struct fib6_node *fn, *pn = NULL;
 | 
						struct fib6_node *fn, *pn = NULL;
 | 
				
			||||||
| 
						 | 
					@ -1244,7 +1222,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = fib6_add_rt2node(fn, rt, info, mxc, extack);
 | 
						err = fib6_add_rt2node(fn, rt, info, extack);
 | 
				
			||||||
	if (!err) {
 | 
						if (!err) {
 | 
				
			||||||
		__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);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1323,8 +1323,7 @@ static void ndisc_router_discovery(struct sk_buff *skb)
 | 
				
			||||||
	    ra_msg->icmph.icmp6_hop_limit) {
 | 
						    ra_msg->icmph.icmp6_hop_limit) {
 | 
				
			||||||
		if (in6_dev->cnf.accept_ra_min_hop_limit <= ra_msg->icmph.icmp6_hop_limit) {
 | 
							if (in6_dev->cnf.accept_ra_min_hop_limit <= ra_msg->icmph.icmp6_hop_limit) {
 | 
				
			||||||
			in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
 | 
								in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
 | 
				
			||||||
			if (rt)
 | 
								fib6_metric_set(rt, RTAX_HOPLIMIT,
 | 
				
			||||||
				dst_metric_set(&rt->dst, RTAX_HOPLIMIT,
 | 
					 | 
				
			||||||
					ra_msg->icmph.icmp6_hop_limit);
 | 
										ra_msg->icmph.icmp6_hop_limit);
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			ND_PRINTK(2, warn, "RA: Got route advertisement with lower hop_limit than minimum\n");
 | 
								ND_PRINTK(2, warn, "RA: Got route advertisement with lower hop_limit than minimum\n");
 | 
				
			||||||
| 
						 | 
					@ -1477,10 +1476,7 @@ static void ndisc_router_discovery(struct sk_buff *skb)
 | 
				
			||||||
			ND_PRINTK(2, warn, "RA: invalid mtu: %d\n", mtu);
 | 
								ND_PRINTK(2, warn, "RA: invalid mtu: %d\n", mtu);
 | 
				
			||||||
		} else if (in6_dev->cnf.mtu6 != mtu) {
 | 
							} else if (in6_dev->cnf.mtu6 != mtu) {
 | 
				
			||||||
			in6_dev->cnf.mtu6 = mtu;
 | 
								in6_dev->cnf.mtu6 = mtu;
 | 
				
			||||||
 | 
								fib6_metric_set(rt, RTAX_MTU, mtu);
 | 
				
			||||||
			if (rt)
 | 
					 | 
				
			||||||
				dst_metric_set(&rt->dst, RTAX_MTU, mtu);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			rt6_mtu_change(skb->dev, mtu);
 | 
								rt6_mtu_change(skb->dev, mtu);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										253
									
								
								net/ipv6/route.c
									
									
									
									
									
								
							
							
						
						
									
										253
									
								
								net/ipv6/route.c
									
									
									
									
									
								
							| 
						 | 
					@ -96,12 +96,11 @@ static void		ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
 | 
				
			||||||
					   struct sk_buff *skb, u32 mtu);
 | 
										   struct sk_buff *skb, u32 mtu);
 | 
				
			||||||
static void		rt6_do_redirect(struct dst_entry *dst, struct sock *sk,
 | 
					static void		rt6_do_redirect(struct dst_entry *dst, struct sock *sk,
 | 
				
			||||||
					struct sk_buff *skb);
 | 
										struct sk_buff *skb);
 | 
				
			||||||
static void		rt6_dst_from_metrics_check(struct rt6_info *rt);
 | 
					 | 
				
			||||||
static int rt6_score_route(struct rt6_info *rt, int oif, int strict);
 | 
					static int rt6_score_route(struct rt6_info *rt, int oif, int strict);
 | 
				
			||||||
static size_t rt6_nlmsg_size(struct rt6_info *rt);
 | 
					static size_t rt6_nlmsg_size(struct rt6_info *rt);
 | 
				
			||||||
static int rt6_fill_node(struct net *net,
 | 
					static int rt6_fill_node(struct net *net, struct sk_buff *skb,
 | 
				
			||||||
			 struct sk_buff *skb, struct rt6_info *rt,
 | 
								 struct rt6_info *rt, struct dst_entry *dst,
 | 
				
			||||||
			 struct in6_addr *dst, struct in6_addr *src,
 | 
								 struct in6_addr *dest, struct in6_addr *src,
 | 
				
			||||||
			 int iif, int type, u32 portid, u32 seq,
 | 
								 int iif, int type, u32 portid, u32 seq,
 | 
				
			||||||
			 unsigned int flags);
 | 
								 unsigned int flags);
 | 
				
			||||||
static struct rt6_info *rt6_find_cached_rt(struct rt6_info *rt,
 | 
					static struct rt6_info *rt6_find_cached_rt(struct rt6_info *rt,
 | 
				
			||||||
| 
						 | 
					@ -183,23 +182,6 @@ static void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static u32 *rt6_pcpu_cow_metrics(struct rt6_info *rt)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return dst_metrics_write_ptr(&rt->from->dst);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct rt6_info *rt = (struct rt6_info *)dst;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (rt->rt6i_flags & RTF_PCPU)
 | 
					 | 
				
			||||||
		return rt6_pcpu_cow_metrics(rt);
 | 
					 | 
				
			||||||
	else if (rt->rt6i_flags & RTF_CACHE)
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		return dst_cow_metrics_generic(dst, old);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static inline const void *choose_neigh_daddr(struct rt6_info *rt,
 | 
					static inline const void *choose_neigh_daddr(struct rt6_info *rt,
 | 
				
			||||||
					     struct sk_buff *skb,
 | 
										     struct sk_buff *skb,
 | 
				
			||||||
					     const void *daddr)
 | 
										     const void *daddr)
 | 
				
			||||||
| 
						 | 
					@ -249,7 +231,7 @@ static struct dst_ops ip6_dst_ops_template = {
 | 
				
			||||||
	.check			=	ip6_dst_check,
 | 
						.check			=	ip6_dst_check,
 | 
				
			||||||
	.default_advmss		=	ip6_default_advmss,
 | 
						.default_advmss		=	ip6_default_advmss,
 | 
				
			||||||
	.mtu			=	ip6_mtu,
 | 
						.mtu			=	ip6_mtu,
 | 
				
			||||||
	.cow_metrics		=	ipv6_cow_metrics,
 | 
						.cow_metrics		=	dst_cow_metrics_generic,
 | 
				
			||||||
	.destroy		=	ip6_dst_destroy,
 | 
						.destroy		=	ip6_dst_destroy,
 | 
				
			||||||
	.ifdown			=	ip6_dst_ifdown,
 | 
						.ifdown			=	ip6_dst_ifdown,
 | 
				
			||||||
	.negative_advice	=	ip6_negative_advice,
 | 
						.negative_advice	=	ip6_negative_advice,
 | 
				
			||||||
| 
						 | 
					@ -353,6 +335,7 @@ static void rt6_info_init(struct rt6_info *rt)
 | 
				
			||||||
	memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst));
 | 
						memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst));
 | 
				
			||||||
	INIT_LIST_HEAD(&rt->rt6i_siblings);
 | 
						INIT_LIST_HEAD(&rt->rt6i_siblings);
 | 
				
			||||||
	INIT_LIST_HEAD(&rt->rt6i_uncached);
 | 
						INIT_LIST_HEAD(&rt->rt6i_uncached);
 | 
				
			||||||
 | 
						rt->fib6_metrics = (struct dst_metrics *)&dst_default_metrics;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* allocate dst with ip6_dst_ops */
 | 
					/* allocate dst with ip6_dst_ops */
 | 
				
			||||||
| 
						 | 
					@ -395,6 +378,7 @@ static void ip6_dst_destroy(struct dst_entry *dst)
 | 
				
			||||||
	struct rt6_exception_bucket *bucket;
 | 
						struct rt6_exception_bucket *bucket;
 | 
				
			||||||
	struct rt6_info *from = rt->from;
 | 
						struct rt6_info *from = rt->from;
 | 
				
			||||||
	struct inet6_dev *idev;
 | 
						struct inet6_dev *idev;
 | 
				
			||||||
 | 
						struct dst_metrics *m;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dst_destroy_metrics_generic(dst);
 | 
						dst_destroy_metrics_generic(dst);
 | 
				
			||||||
	free_percpu(rt->rt6i_pcpu);
 | 
						free_percpu(rt->rt6i_pcpu);
 | 
				
			||||||
| 
						 | 
					@ -411,6 +395,10 @@ static void ip6_dst_destroy(struct dst_entry *dst)
 | 
				
			||||||
		kfree(bucket);
 | 
							kfree(bucket);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						m = rt->fib6_metrics;
 | 
				
			||||||
 | 
						if (m != &dst_default_metrics && refcount_dec_and_test(&m->refcnt))
 | 
				
			||||||
 | 
							kfree(m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rt->from = NULL;
 | 
						rt->from = NULL;
 | 
				
			||||||
	dst_release(&from->dst);
 | 
						dst_release(&from->dst);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -996,7 +984,11 @@ static void rt6_set_from(struct rt6_info *rt, struct rt6_info *from)
 | 
				
			||||||
	rt->rt6i_flags &= ~RTF_EXPIRES;
 | 
						rt->rt6i_flags &= ~RTF_EXPIRES;
 | 
				
			||||||
	dst_hold(&from->dst);
 | 
						dst_hold(&from->dst);
 | 
				
			||||||
	rt->from = from;
 | 
						rt->from = from;
 | 
				
			||||||
	dst_init_metrics(&rt->dst, dst_metrics_ptr(&from->dst), true);
 | 
						dst_init_metrics(&rt->dst, from->fib6_metrics->metrics, true);
 | 
				
			||||||
 | 
						if (from->fib6_metrics != &dst_default_metrics) {
 | 
				
			||||||
 | 
							rt->dst._metrics |= DST_METRICS_REFCOUNTED;
 | 
				
			||||||
 | 
							refcount_inc(&from->fib6_metrics->refcnt);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort)
 | 
					static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort)
 | 
				
			||||||
| 
						 | 
					@ -1140,7 +1132,6 @@ EXPORT_SYMBOL(rt6_lookup);
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info,
 | 
					static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info,
 | 
				
			||||||
			struct mx6_config *mxc,
 | 
					 | 
				
			||||||
			struct netlink_ext_ack *extack)
 | 
								struct netlink_ext_ack *extack)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int err;
 | 
						int err;
 | 
				
			||||||
| 
						 | 
					@ -1148,7 +1139,7 @@ static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	table = rt->rt6i_table;
 | 
						table = rt->rt6i_table;
 | 
				
			||||||
	spin_lock_bh(&table->tb6_lock);
 | 
						spin_lock_bh(&table->tb6_lock);
 | 
				
			||||||
	err = fib6_add(&table->tb6_root, rt, info, mxc, extack);
 | 
						err = fib6_add(&table->tb6_root, rt, info, extack);
 | 
				
			||||||
	spin_unlock_bh(&table->tb6_lock);
 | 
						spin_unlock_bh(&table->tb6_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return err;
 | 
						return err;
 | 
				
			||||||
| 
						 | 
					@ -1157,11 +1148,10 @@ static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info,
 | 
				
			||||||
int ip6_ins_rt(struct net *net, struct rt6_info *rt)
 | 
					int ip6_ins_rt(struct net *net, struct rt6_info *rt)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct nl_info info = {	.nl_net = net, };
 | 
						struct nl_info info = {	.nl_net = net, };
 | 
				
			||||||
	struct mx6_config mxc = { .mx = NULL, };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Hold dst to account for the reference from the fib6 tree */
 | 
						/* Hold dst to account for the reference from the fib6 tree */
 | 
				
			||||||
	dst_hold(&rt->dst);
 | 
						dst_hold(&rt->dst);
 | 
				
			||||||
	return __ip6_ins_rt(rt, &info, &mxc, NULL);
 | 
						return __ip6_ins_rt(rt, &info, NULL);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct rt6_info *ip6_rt_cache_alloc(struct rt6_info *ort,
 | 
					static struct rt6_info *ip6_rt_cache_alloc(struct rt6_info *ort,
 | 
				
			||||||
| 
						 | 
					@ -1232,8 +1222,8 @@ static struct rt6_info *rt6_get_pcpu_route(struct rt6_info *rt)
 | 
				
			||||||
	p = this_cpu_ptr(rt->rt6i_pcpu);
 | 
						p = this_cpu_ptr(rt->rt6i_pcpu);
 | 
				
			||||||
	pcpu_rt = *p;
 | 
						pcpu_rt = *p;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (pcpu_rt && ip6_hold_safe(NULL, &pcpu_rt, false))
 | 
						if (pcpu_rt)
 | 
				
			||||||
		rt6_dst_from_metrics_check(pcpu_rt);
 | 
							ip6_hold_safe(NULL, &pcpu_rt, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return pcpu_rt;
 | 
						return pcpu_rt;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1254,7 +1244,6 @@ static struct rt6_info *rt6_make_pcpu_route(struct net *net,
 | 
				
			||||||
	prev = cmpxchg(p, NULL, pcpu_rt);
 | 
						prev = cmpxchg(p, NULL, pcpu_rt);
 | 
				
			||||||
	BUG_ON(prev);
 | 
						BUG_ON(prev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rt6_dst_from_metrics_check(pcpu_rt);
 | 
					 | 
				
			||||||
	return pcpu_rt;
 | 
						return pcpu_rt;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1384,6 +1373,16 @@ __rt6_find_exception_rcu(struct rt6_exception_bucket **bucket,
 | 
				
			||||||
	return NULL;
 | 
						return NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static unsigned int fib6_mtu(const struct rt6_info *rt)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned int mtu;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mtu = rt->fib6_pmtu ? : rt->rt6i_idev->cnf.mtu6;
 | 
				
			||||||
 | 
						mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return mtu - lwtunnel_headroom(rt->fib6_nh.nh_lwtstate, mtu);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int rt6_insert_exception(struct rt6_info *nrt,
 | 
					static int rt6_insert_exception(struct rt6_info *nrt,
 | 
				
			||||||
				struct rt6_info *ort)
 | 
									struct rt6_info *ort)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -1436,7 +1435,7 @@ static int rt6_insert_exception(struct rt6_info *nrt,
 | 
				
			||||||
	 * Only insert this exception route if its mtu
 | 
						 * Only insert this exception route if its mtu
 | 
				
			||||||
	 * is less than ort's mtu value.
 | 
						 * is less than ort's mtu value.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (nrt->rt6i_pmtu >= dst_mtu(&ort->dst)) {
 | 
						if (dst_metric_raw(&nrt->dst, RTAX_MTU) >= fib6_mtu(ort)) {
 | 
				
			||||||
		err = -EINVAL;
 | 
							err = -EINVAL;
 | 
				
			||||||
		goto out;
 | 
							goto out;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -1673,12 +1672,12 @@ static void rt6_exceptions_update_pmtu(struct inet6_dev *idev,
 | 
				
			||||||
			struct rt6_info *entry = rt6_ex->rt6i;
 | 
								struct rt6_info *entry = rt6_ex->rt6i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			/* For RTF_CACHE with rt6i_pmtu == 0 (i.e. a redirected
 | 
								/* For RTF_CACHE with rt6i_pmtu == 0 (i.e. a redirected
 | 
				
			||||||
			 * route), the metrics of its rt->dst.from have already
 | 
								 * route), the metrics of its rt->from have already
 | 
				
			||||||
			 * been updated.
 | 
								 * been updated.
 | 
				
			||||||
			 */
 | 
								 */
 | 
				
			||||||
			if (entry->rt6i_pmtu &&
 | 
								if (dst_metric_raw(&entry->dst, RTAX_MTU) &&
 | 
				
			||||||
			    rt6_mtu_change_route_allowed(idev, entry, mtu))
 | 
								    rt6_mtu_change_route_allowed(idev, entry, mtu))
 | 
				
			||||||
				entry->rt6i_pmtu = mtu;
 | 
									dst_metric_set(&entry->dst, RTAX_MTU, mtu);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		bucket++;
 | 
							bucket++;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -1844,10 +1843,9 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
 | 
				
			||||||
		trace_fib6_table_lookup(net, rt, table, fl6);
 | 
							trace_fib6_table_lookup(net, rt, table, fl6);
 | 
				
			||||||
		return rt;
 | 
							return rt;
 | 
				
			||||||
	} else if (rt->rt6i_flags & RTF_CACHE) {
 | 
						} else if (rt->rt6i_flags & RTF_CACHE) {
 | 
				
			||||||
		if (ip6_hold_safe(net, &rt, true)) {
 | 
							if (ip6_hold_safe(net, &rt, true))
 | 
				
			||||||
			dst_use_noref(&rt->dst, jiffies);
 | 
								dst_use_noref(&rt->dst, jiffies);
 | 
				
			||||||
			rt6_dst_from_metrics_check(rt);
 | 
					
 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		rcu_read_unlock();
 | 
							rcu_read_unlock();
 | 
				
			||||||
		trace_fib6_table_lookup(net, rt, table, fl6);
 | 
							trace_fib6_table_lookup(net, rt, table, fl6);
 | 
				
			||||||
		return rt;
 | 
							return rt;
 | 
				
			||||||
| 
						 | 
					@ -2147,13 +2145,6 @@ struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_ori
 | 
				
			||||||
 *	Destination cache support functions
 | 
					 *	Destination cache support functions
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void rt6_dst_from_metrics_check(struct rt6_info *rt)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (rt->from &&
 | 
					 | 
				
			||||||
	    dst_metrics_ptr(&rt->dst) != dst_metrics_ptr(&rt->from->dst))
 | 
					 | 
				
			||||||
		dst_init_metrics(&rt->dst, dst_metrics_ptr(&rt->from->dst), true);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct dst_entry *rt6_check(struct rt6_info *rt, u32 cookie)
 | 
					static struct dst_entry *rt6_check(struct rt6_info *rt, u32 cookie)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	u32 rt_cookie = 0;
 | 
						u32 rt_cookie = 0;
 | 
				
			||||||
| 
						 | 
					@ -2188,8 +2179,6 @@ static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
 | 
				
			||||||
	 * into this function always.
 | 
						 * into this function always.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rt6_dst_from_metrics_check(rt);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (rt->rt6i_flags & RTF_PCPU ||
 | 
						if (rt->rt6i_flags & RTF_PCPU ||
 | 
				
			||||||
	    (unlikely(!list_empty(&rt->rt6i_uncached)) && rt->from))
 | 
						    (unlikely(!list_empty(&rt->rt6i_uncached)) && rt->from))
 | 
				
			||||||
		return rt6_dst_from_check(rt, cookie);
 | 
							return rt6_dst_from_check(rt, cookie);
 | 
				
			||||||
| 
						 | 
					@ -2242,8 +2231,8 @@ static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct net *net = dev_net(rt->dst.dev);
 | 
						struct net *net = dev_net(rt->dst.dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dst_metric_set(&rt->dst, RTAX_MTU, mtu);
 | 
				
			||||||
	rt->rt6i_flags |= RTF_MODIFIED;
 | 
						rt->rt6i_flags |= RTF_MODIFIED;
 | 
				
			||||||
	rt->rt6i_pmtu = mtu;
 | 
					 | 
				
			||||||
	rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires);
 | 
						rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2289,10 +2278,10 @@ static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
 | 
				
			||||||
	} else if (daddr) {
 | 
						} else if (daddr) {
 | 
				
			||||||
		struct rt6_info *nrt6;
 | 
							struct rt6_info *nrt6;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		nrt6 = ip6_rt_cache_alloc(rt6, daddr, saddr);
 | 
							nrt6 = ip6_rt_cache_alloc(rt6->from, daddr, saddr);
 | 
				
			||||||
		if (nrt6) {
 | 
							if (nrt6) {
 | 
				
			||||||
			rt6_do_update_pmtu(nrt6, mtu);
 | 
								rt6_do_update_pmtu(nrt6, mtu);
 | 
				
			||||||
			if (rt6_insert_exception(nrt6, rt6))
 | 
								if (rt6_insert_exception(nrt6, rt6->from))
 | 
				
			||||||
				dst_release_immediate(&nrt6->dst);
 | 
									dst_release_immediate(&nrt6->dst);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -2533,12 +2522,8 @@ static unsigned int ip6_default_advmss(const struct dst_entry *dst)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static unsigned int ip6_mtu(const struct dst_entry *dst)
 | 
					static unsigned int ip6_mtu(const struct dst_entry *dst)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	const struct rt6_info *rt = (const struct rt6_info *)dst;
 | 
					 | 
				
			||||||
	unsigned int mtu = rt->rt6i_pmtu;
 | 
					 | 
				
			||||||
	struct inet6_dev *idev;
 | 
						struct inet6_dev *idev;
 | 
				
			||||||
 | 
						unsigned int mtu;
 | 
				
			||||||
	if (mtu)
 | 
					 | 
				
			||||||
		goto out;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mtu = dst_metric_raw(dst, RTAX_MTU);
 | 
						mtu = dst_metric_raw(dst, RTAX_MTU);
 | 
				
			||||||
	if (mtu)
 | 
						if (mtu)
 | 
				
			||||||
| 
						 | 
					@ -2622,60 +2607,24 @@ static int ip6_dst_gc(struct dst_ops *ops)
 | 
				
			||||||
	return entries > rt_max_size;
 | 
						return entries > rt_max_size;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int ip6_convert_metrics(struct mx6_config *mxc,
 | 
					static int ip6_convert_metrics(struct net *net, struct rt6_info *rt,
 | 
				
			||||||
			       const struct fib6_config *cfg)
 | 
								       struct fib6_config *cfg)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct net *net = cfg->fc_nlinfo.nl_net;
 | 
						int err = 0;
 | 
				
			||||||
	bool ecn_ca = false;
 | 
					 | 
				
			||||||
	struct nlattr *nla;
 | 
					 | 
				
			||||||
	int remaining;
 | 
					 | 
				
			||||||
	u32 *mp;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!cfg->fc_mx)
 | 
						if (cfg->fc_mx) {
 | 
				
			||||||
		return 0;
 | 
							rt->fib6_metrics = kzalloc(sizeof(*rt->fib6_metrics),
 | 
				
			||||||
 | 
										   GFP_KERNEL);
 | 
				
			||||||
	mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
 | 
							if (unlikely(!rt->fib6_metrics))
 | 
				
			||||||
	if (unlikely(!mp))
 | 
					 | 
				
			||||||
			return -ENOMEM;
 | 
								return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
 | 
							refcount_set(&rt->fib6_metrics->refcnt, 1);
 | 
				
			||||||
		int type = nla_type(nla);
 | 
					 | 
				
			||||||
		u32 val;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!type)
 | 
							err = ip_metrics_convert(net, cfg->fc_mx, cfg->fc_mx_len,
 | 
				
			||||||
			continue;
 | 
										 rt->fib6_metrics->metrics);
 | 
				
			||||||
		if (unlikely(type > RTAX_MAX))
 | 
					 | 
				
			||||||
			goto err;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (type == RTAX_CC_ALGO) {
 | 
					 | 
				
			||||||
			char tmp[TCP_CA_NAME_MAX];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			nla_strlcpy(tmp, nla, sizeof(tmp));
 | 
					 | 
				
			||||||
			val = tcp_ca_get_key_by_name(net, tmp, &ecn_ca);
 | 
					 | 
				
			||||||
			if (val == TCP_CA_UNSPEC)
 | 
					 | 
				
			||||||
				goto err;
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			val = nla_get_u32(nla);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (type == RTAX_HOPLIMIT && val > 255)
 | 
					 | 
				
			||||||
			val = 255;
 | 
					 | 
				
			||||||
		if (type == RTAX_FEATURES && (val & ~RTAX_FEATURE_MASK))
 | 
					 | 
				
			||||||
			goto err;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		mp[type - 1] = val;
 | 
					 | 
				
			||||||
		__set_bit(type - 1, mxc->mx_valid);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (ecn_ca) {
 | 
						return err;
 | 
				
			||||||
		__set_bit(RTAX_FEATURES - 1, mxc->mx_valid);
 | 
					 | 
				
			||||||
		mp[RTAX_FEATURES - 1] |= DST_FEATURE_ECN_CA;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	mxc->mx = mp;
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
 err:
 | 
					 | 
				
			||||||
	kfree(mp);
 | 
					 | 
				
			||||||
	return -EINVAL;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct rt6_info *ip6_nh_lookup_table(struct net *net,
 | 
					static struct rt6_info *ip6_nh_lookup_table(struct net *net,
 | 
				
			||||||
| 
						 | 
					@ -2955,6 +2904,10 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg,
 | 
				
			||||||
		goto out;
 | 
							goto out;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = ip6_convert_metrics(net, rt, cfg);
 | 
				
			||||||
 | 
						if (err < 0)
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (cfg->fc_flags & RTF_EXPIRES)
 | 
						if (cfg->fc_flags & RTF_EXPIRES)
 | 
				
			||||||
		rt6_set_expires(rt, jiffies +
 | 
							rt6_set_expires(rt, jiffies +
 | 
				
			||||||
				clock_t_to_jiffies(cfg->fc_expires));
 | 
									clock_t_to_jiffies(cfg->fc_expires));
 | 
				
			||||||
| 
						 | 
					@ -3078,32 +3031,16 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg,
 | 
				
			||||||
	return ERR_PTR(err);
 | 
						return ERR_PTR(err);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int ip6_route_add(struct fib6_config *cfg,
 | 
					int ip6_route_add(struct fib6_config *cfg, struct netlink_ext_ack *extack)
 | 
				
			||||||
		  struct netlink_ext_ack *extack)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct mx6_config mxc = { .mx = NULL, };
 | 
					 | 
				
			||||||
	struct rt6_info *rt;
 | 
						struct rt6_info *rt;
 | 
				
			||||||
	int err;
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rt = ip6_route_info_create(cfg, extack);
 | 
						rt = ip6_route_info_create(cfg, extack);
 | 
				
			||||||
	if (IS_ERR(rt)) {
 | 
						if (IS_ERR(rt))
 | 
				
			||||||
		err = PTR_ERR(rt);
 | 
							return PTR_ERR(rt);
 | 
				
			||||||
		rt = NULL;
 | 
					 | 
				
			||||||
		goto out;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = ip6_convert_metrics(&mxc, cfg);
 | 
						err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, extack);
 | 
				
			||||||
	if (err)
 | 
					 | 
				
			||||||
		goto out;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, &mxc, extack);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	kfree(mxc.mx);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return err;
 | 
					 | 
				
			||||||
out:
 | 
					 | 
				
			||||||
	if (rt)
 | 
					 | 
				
			||||||
		dst_release_immediate(&rt->dst);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return err;
 | 
						return err;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -3157,7 +3094,7 @@ static int __ip6_del_rt_siblings(struct rt6_info *rt, struct fib6_config *cfg)
 | 
				
			||||||
		if (skb) {
 | 
							if (skb) {
 | 
				
			||||||
			u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
 | 
								u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (rt6_fill_node(net, skb, rt,
 | 
								if (rt6_fill_node(net, skb, rt, NULL,
 | 
				
			||||||
					  NULL, NULL, 0, RTM_DELROUTE,
 | 
										  NULL, NULL, 0, RTM_DELROUTE,
 | 
				
			||||||
					  info->portid, seq, 0) < 0) {
 | 
										  info->portid, seq, 0) < 0) {
 | 
				
			||||||
				kfree_skb(skb);
 | 
									kfree_skb(skb);
 | 
				
			||||||
| 
						 | 
					@ -3348,7 +3285,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
 | 
				
			||||||
	 * a cached route because rt6_insert_exception() will
 | 
						 * a cached route because rt6_insert_exception() will
 | 
				
			||||||
	 * takes care of it
 | 
						 * takes care of it
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (rt6_insert_exception(nrt, rt)) {
 | 
						if (rt6_insert_exception(nrt, rt->from)) {
 | 
				
			||||||
		dst_release_immediate(&nrt->dst);
 | 
							dst_release_immediate(&nrt->dst);
 | 
				
			||||||
		goto out;
 | 
							goto out;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -4018,11 +3955,14 @@ static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
 | 
				
			||||||
	   update PMTU increase is a MUST. (i.e. jumbo frame)
 | 
						   update PMTU increase is a MUST. (i.e. jumbo frame)
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (rt->fib6_nh.nh_dev == arg->dev &&
 | 
						if (rt->fib6_nh.nh_dev == arg->dev &&
 | 
				
			||||||
	    !dst_metric_locked(&rt->dst, RTAX_MTU)) {
 | 
						    !fib6_metric_locked(rt, RTAX_MTU)) {
 | 
				
			||||||
 | 
							u32 mtu = rt->fib6_pmtu;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (mtu >= arg->mtu ||
 | 
				
			||||||
 | 
							    (mtu < arg->mtu && mtu == idev->cnf.mtu6))
 | 
				
			||||||
 | 
								fib6_metric_set(rt, RTAX_MTU, arg->mtu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		spin_lock_bh(&rt6_exception_lock);
 | 
							spin_lock_bh(&rt6_exception_lock);
 | 
				
			||||||
		if (dst_metric_raw(&rt->dst, RTAX_MTU) &&
 | 
					 | 
				
			||||||
		    rt6_mtu_change_route_allowed(idev, rt, arg->mtu))
 | 
					 | 
				
			||||||
			dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
 | 
					 | 
				
			||||||
		rt6_exceptions_update_pmtu(idev, rt, arg->mtu);
 | 
							rt6_exceptions_update_pmtu(idev, rt, arg->mtu);
 | 
				
			||||||
		spin_unlock_bh(&rt6_exception_lock);
 | 
							spin_unlock_bh(&rt6_exception_lock);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -4183,7 +4123,6 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
 | 
				
			||||||
struct rt6_nh {
 | 
					struct rt6_nh {
 | 
				
			||||||
	struct rt6_info *rt6_info;
 | 
						struct rt6_info *rt6_info;
 | 
				
			||||||
	struct fib6_config r_cfg;
 | 
						struct fib6_config r_cfg;
 | 
				
			||||||
	struct mx6_config mxc;
 | 
					 | 
				
			||||||
	struct list_head next;
 | 
						struct list_head next;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4198,7 +4137,8 @@ static void ip6_print_replace_route_err(struct list_head *rt6_nh_list)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int ip6_route_info_append(struct list_head *rt6_nh_list,
 | 
					static int ip6_route_info_append(struct net *net,
 | 
				
			||||||
 | 
									 struct list_head *rt6_nh_list,
 | 
				
			||||||
				 struct rt6_info *rt, struct fib6_config *r_cfg)
 | 
									 struct rt6_info *rt, struct fib6_config *r_cfg)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct rt6_nh *nh;
 | 
						struct rt6_nh *nh;
 | 
				
			||||||
| 
						 | 
					@ -4214,7 +4154,7 @@ static int ip6_route_info_append(struct list_head *rt6_nh_list,
 | 
				
			||||||
	if (!nh)
 | 
						if (!nh)
 | 
				
			||||||
		return -ENOMEM;
 | 
							return -ENOMEM;
 | 
				
			||||||
	nh->rt6_info = rt;
 | 
						nh->rt6_info = rt;
 | 
				
			||||||
	err = ip6_convert_metrics(&nh->mxc, r_cfg);
 | 
						err = ip6_convert_metrics(net, rt, r_cfg);
 | 
				
			||||||
	if (err) {
 | 
						if (err) {
 | 
				
			||||||
		kfree(nh);
 | 
							kfree(nh);
 | 
				
			||||||
		return err;
 | 
							return err;
 | 
				
			||||||
| 
						 | 
					@ -4305,7 +4245,8 @@ static int ip6_route_multipath_add(struct fib6_config *cfg,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		rt->fib6_nh.nh_weight = rtnh->rtnh_hops + 1;
 | 
							rt->fib6_nh.nh_weight = rtnh->rtnh_hops + 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		err = ip6_route_info_append(&rt6_nh_list, rt, &r_cfg);
 | 
							err = ip6_route_info_append(info->nl_net, &rt6_nh_list,
 | 
				
			||||||
 | 
										    rt, &r_cfg);
 | 
				
			||||||
		if (err) {
 | 
							if (err) {
 | 
				
			||||||
			dst_release_immediate(&rt->dst);
 | 
								dst_release_immediate(&rt->dst);
 | 
				
			||||||
			goto cleanup;
 | 
								goto cleanup;
 | 
				
			||||||
| 
						 | 
					@ -4323,7 +4264,7 @@ static int ip6_route_multipath_add(struct fib6_config *cfg,
 | 
				
			||||||
	err_nh = NULL;
 | 
						err_nh = NULL;
 | 
				
			||||||
	list_for_each_entry(nh, &rt6_nh_list, next) {
 | 
						list_for_each_entry(nh, &rt6_nh_list, next) {
 | 
				
			||||||
		rt_last = nh->rt6_info;
 | 
							rt_last = nh->rt6_info;
 | 
				
			||||||
		err = __ip6_ins_rt(nh->rt6_info, info, &nh->mxc, extack);
 | 
							err = __ip6_ins_rt(nh->rt6_info, info, extack);
 | 
				
			||||||
		/* save reference to first route for notification */
 | 
							/* save reference to first route for notification */
 | 
				
			||||||
		if (!rt_notif && !err)
 | 
							if (!rt_notif && !err)
 | 
				
			||||||
			rt_notif = nh->rt6_info;
 | 
								rt_notif = nh->rt6_info;
 | 
				
			||||||
| 
						 | 
					@ -4372,7 +4313,6 @@ static int ip6_route_multipath_add(struct fib6_config *cfg,
 | 
				
			||||||
	list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, next) {
 | 
						list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, next) {
 | 
				
			||||||
		if (nh->rt6_info)
 | 
							if (nh->rt6_info)
 | 
				
			||||||
			dst_release_immediate(&nh->rt6_info->dst);
 | 
								dst_release_immediate(&nh->rt6_info->dst);
 | 
				
			||||||
		kfree(nh->mxc.mx);
 | 
					 | 
				
			||||||
		list_del(&nh->next);
 | 
							list_del(&nh->next);
 | 
				
			||||||
		kfree(nh);
 | 
							kfree(nh);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -4546,16 +4486,16 @@ static int rt6_add_nexthop(struct sk_buff *skb, struct rt6_info *rt)
 | 
				
			||||||
	return -EMSGSIZE;
 | 
						return -EMSGSIZE;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int rt6_fill_node(struct net *net,
 | 
					static int rt6_fill_node(struct net *net, struct sk_buff *skb,
 | 
				
			||||||
			 struct sk_buff *skb, struct rt6_info *rt,
 | 
								 struct rt6_info *rt, struct dst_entry *dst,
 | 
				
			||||||
			 struct in6_addr *dst, struct in6_addr *src,
 | 
								 struct in6_addr *dest, struct in6_addr *src,
 | 
				
			||||||
			 int iif, int type, u32 portid, u32 seq,
 | 
								 int iif, int type, u32 portid, u32 seq,
 | 
				
			||||||
			 unsigned int flags)
 | 
								 unsigned int flags)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	u32 metrics[RTAX_MAX];
 | 
					 | 
				
			||||||
	struct rtmsg *rtm;
 | 
						struct rtmsg *rtm;
 | 
				
			||||||
	struct nlmsghdr *nlh;
 | 
						struct nlmsghdr *nlh;
 | 
				
			||||||
	long expires;
 | 
						long expires = 0;
 | 
				
			||||||
 | 
						u32 *pmetrics;
 | 
				
			||||||
	u32 table;
 | 
						u32 table;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags);
 | 
						nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags);
 | 
				
			||||||
| 
						 | 
					@ -4583,8 +4523,8 @@ static int rt6_fill_node(struct net *net,
 | 
				
			||||||
	if (rt->rt6i_flags & RTF_CACHE)
 | 
						if (rt->rt6i_flags & RTF_CACHE)
 | 
				
			||||||
		rtm->rtm_flags |= RTM_F_CLONED;
 | 
							rtm->rtm_flags |= RTM_F_CLONED;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (dst) {
 | 
						if (dest) {
 | 
				
			||||||
		if (nla_put_in6_addr(skb, RTA_DST, dst))
 | 
							if (nla_put_in6_addr(skb, RTA_DST, dest))
 | 
				
			||||||
			goto nla_put_failure;
 | 
								goto nla_put_failure;
 | 
				
			||||||
		rtm->rtm_dst_len = 128;
 | 
							rtm->rtm_dst_len = 128;
 | 
				
			||||||
	} else if (rtm->rtm_dst_len)
 | 
						} else if (rtm->rtm_dst_len)
 | 
				
			||||||
| 
						 | 
					@ -4612,9 +4552,9 @@ static int rt6_fill_node(struct net *net,
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
			if (nla_put_u32(skb, RTA_IIF, iif))
 | 
								if (nla_put_u32(skb, RTA_IIF, iif))
 | 
				
			||||||
				goto nla_put_failure;
 | 
									goto nla_put_failure;
 | 
				
			||||||
	} else if (dst) {
 | 
						} else if (dest) {
 | 
				
			||||||
		struct in6_addr saddr_buf;
 | 
							struct in6_addr saddr_buf;
 | 
				
			||||||
		if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 &&
 | 
							if (ip6_route_get_saddr(net, rt, dest, 0, &saddr_buf) == 0 &&
 | 
				
			||||||
		    nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
 | 
							    nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
 | 
				
			||||||
			goto nla_put_failure;
 | 
								goto nla_put_failure;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -4626,10 +4566,8 @@ static int rt6_fill_node(struct net *net,
 | 
				
			||||||
			goto nla_put_failure;
 | 
								goto nla_put_failure;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	memcpy(metrics, dst_metrics_ptr(&rt->dst), sizeof(metrics));
 | 
						pmetrics = dst ? dst_metrics_ptr(dst) : rt->fib6_metrics->metrics;
 | 
				
			||||||
	if (rt->rt6i_pmtu)
 | 
						if (rtnetlink_put_metrics(skb, pmetrics) < 0)
 | 
				
			||||||
		metrics[RTAX_MTU - 1] = rt->rt6i_pmtu;
 | 
					 | 
				
			||||||
	if (rtnetlink_put_metrics(skb, metrics) < 0)
 | 
					 | 
				
			||||||
		goto nla_put_failure;
 | 
							goto nla_put_failure;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
 | 
						if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
 | 
				
			||||||
| 
						 | 
					@ -4661,9 +4599,10 @@ static int rt6_fill_node(struct net *net,
 | 
				
			||||||
			goto nla_put_failure;
 | 
								goto nla_put_failure;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0;
 | 
						if (rt->rt6i_flags & RTF_EXPIRES && dst)
 | 
				
			||||||
 | 
							expires = dst->expires - jiffies;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0)
 | 
						if (rtnl_put_cacheinfo(skb, dst, 0, expires, dst ? dst->error : 0) < 0)
 | 
				
			||||||
		goto nla_put_failure;
 | 
							goto nla_put_failure;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags)))
 | 
						if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags)))
 | 
				
			||||||
| 
						 | 
					@ -4697,10 +4636,9 @@ int rt6_dump_route(struct rt6_info *rt, void *p_arg)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return rt6_fill_node(net,
 | 
						return rt6_fill_node(net, arg->skb, rt, NULL, NULL, NULL, 0,
 | 
				
			||||||
		     arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
 | 
								     RTM_NEWROUTE, NETLINK_CB(arg->cb->skb).portid,
 | 
				
			||||||
		     NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq,
 | 
								     arg->cb->nlh->nlmsg_seq, NLM_F_MULTI);
 | 
				
			||||||
		     NLM_F_MULTI);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
 | 
					static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
 | 
				
			||||||
| 
						 | 
					@ -4814,13 +4752,14 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	skb_dst_set(skb, &rt->dst);
 | 
						skb_dst_set(skb, &rt->dst);
 | 
				
			||||||
	if (fibmatch)
 | 
						if (fibmatch)
 | 
				
			||||||
		err = rt6_fill_node(net, skb, rt, NULL, NULL, iif,
 | 
							err = rt6_fill_node(net, skb, rt, NULL, NULL, NULL, iif,
 | 
				
			||||||
				    RTM_NEWROUTE, NETLINK_CB(in_skb).portid,
 | 
									    RTM_NEWROUTE, NETLINK_CB(in_skb).portid,
 | 
				
			||||||
				    nlh->nlmsg_seq, 0);
 | 
									    nlh->nlmsg_seq, 0);
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
		err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
 | 
							err = rt6_fill_node(net, skb, rt, dst, &fl6.daddr, &fl6.saddr,
 | 
				
			||||||
				    RTM_NEWROUTE, NETLINK_CB(in_skb).portid,
 | 
									    iif, RTM_NEWROUTE,
 | 
				
			||||||
				    nlh->nlmsg_seq, 0);
 | 
									    NETLINK_CB(in_skb).portid, nlh->nlmsg_seq,
 | 
				
			||||||
 | 
									    0);
 | 
				
			||||||
	if (err < 0) {
 | 
						if (err < 0) {
 | 
				
			||||||
		kfree_skb(skb);
 | 
							kfree_skb(skb);
 | 
				
			||||||
		goto errout;
 | 
							goto errout;
 | 
				
			||||||
| 
						 | 
					@ -4846,7 +4785,7 @@ void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info,
 | 
				
			||||||
	if (!skb)
 | 
						if (!skb)
 | 
				
			||||||
		goto errout;
 | 
							goto errout;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
 | 
						err = rt6_fill_node(net, skb, rt, NULL, NULL, NULL, 0,
 | 
				
			||||||
			    event, info->portid, seq, nlm_flags);
 | 
								    event, info->portid, seq, nlm_flags);
 | 
				
			||||||
	if (err < 0) {
 | 
						if (err < 0) {
 | 
				
			||||||
		/* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
 | 
							/* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue