diff options
Diffstat (limited to 'net/ipv6/route.c')
| -rw-r--r-- | net/ipv6/route.c | 78 | 
1 files changed, 56 insertions, 22 deletions
diff --git a/net/ipv6/route.c b/net/ipv6/route.c index bdbc38e8bf29..1b57e11e6e0d 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -102,11 +102,13 @@ static int rt6_score_route(struct rt6_info *rt, int oif, int strict);  #ifdef CONFIG_IPV6_ROUTE_INFO  static struct rt6_info *rt6_add_route_info(struct net *net,  					   const struct in6_addr *prefix, int prefixlen, -					   const struct in6_addr *gwaddr, int ifindex, +					   const struct in6_addr *gwaddr, +					   struct net_device *dev,  					   unsigned int pref);  static struct rt6_info *rt6_get_route_info(struct net *net,  					   const struct in6_addr *prefix, int prefixlen, -					   const struct in6_addr *gwaddr, int ifindex); +					   const struct in6_addr *gwaddr, +					   struct net_device *dev);  #endif  struct uncached_list { @@ -656,7 +658,8 @@ static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,  	struct net_device *dev = rt->dst.dev;  	if (dev && !netif_carrier_ok(dev) && -	    idev->cnf.ignore_routes_with_linkdown) +	    idev->cnf.ignore_routes_with_linkdown && +	    !(strict & RT6_LOOKUP_F_IGNORE_LINKSTATE))  		goto out;  	if (rt6_check_expired(rt)) @@ -803,7 +806,7 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,  		rt = rt6_get_dflt_router(gwaddr, dev);  	else  		rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, -					gwaddr, dev->ifindex); +					gwaddr, dev);  	if (rt && !lifetime) {  		ip6_del_rt(rt); @@ -811,8 +814,8 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,  	}  	if (!rt && lifetime) -		rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex, -					pref); +		rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, +					dev, pref);  	else if (rt)  		rt->rt6i_flags = RTF_ROUTEINFO |  				 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref); @@ -1050,6 +1053,7 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,  	int strict = 0;  	strict |= flags & RT6_LOOKUP_F_IFACE; +	strict |= flags & RT6_LOOKUP_F_IGNORE_LINKSTATE;  	if (net->ipv6.devconf_all->forwarding == 0)  		strict |= RT6_LOOKUP_F_REACHABLE; @@ -1360,6 +1364,9 @@ static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,  	if (rt6->rt6i_flags & RTF_LOCAL)  		return; +	if (dst_metric_locked(dst, RTAX_MTU)) +		return; +  	dst_confirm(dst);  	mtu = max_t(u32, mtu, IPV6_MIN_MTU);  	if (mtu >= dst_mtu(dst)) @@ -1789,7 +1796,7 @@ static struct rt6_info *ip6_nh_lookup_table(struct net *net,  	};  	struct fib6_table *table;  	struct rt6_info *rt; -	int flags = RT6_LOOKUP_F_IFACE; +	int flags = RT6_LOOKUP_F_IFACE | RT6_LOOKUP_F_IGNORE_LINKSTATE;  	table = fib6_get_table(net, cfg->fc_table);  	if (!table) @@ -2325,13 +2332,16 @@ static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort)  #ifdef CONFIG_IPV6_ROUTE_INFO  static struct rt6_info *rt6_get_route_info(struct net *net,  					   const struct in6_addr *prefix, int prefixlen, -					   const struct in6_addr *gwaddr, int ifindex) +					   const struct in6_addr *gwaddr, +					   struct net_device *dev)  { +	u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO; +	int ifindex = dev->ifindex;  	struct fib6_node *fn;  	struct rt6_info *rt = NULL;  	struct fib6_table *table; -	table = fib6_get_table(net, RT6_TABLE_INFO); +	table = fib6_get_table(net, tb_id);  	if (!table)  		return NULL; @@ -2357,12 +2367,13 @@ out:  static struct rt6_info *rt6_add_route_info(struct net *net,  					   const struct in6_addr *prefix, int prefixlen, -					   const struct in6_addr *gwaddr, int ifindex, +					   const struct in6_addr *gwaddr, +					   struct net_device *dev,  					   unsigned int pref)  {  	struct fib6_config cfg = {  		.fc_metric	= IP6_RT_PRIO_USER, -		.fc_ifindex	= ifindex, +		.fc_ifindex	= dev->ifindex,  		.fc_dst_len	= prefixlen,  		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |  				  RTF_UP | RTF_PREF(pref), @@ -2371,7 +2382,7 @@ static struct rt6_info *rt6_add_route_info(struct net *net,  		.fc_nlinfo.nl_net = net,  	}; -	cfg.fc_table = l3mdev_fib_table_by_index(net, ifindex) ? : RT6_TABLE_INFO; +	cfg.fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO,  	cfg.fc_dst = *prefix;  	cfg.fc_gateway = *gwaddr; @@ -2381,16 +2392,17 @@ static struct rt6_info *rt6_add_route_info(struct net *net,  	ip6_route_add(&cfg); -	return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex); +	return rt6_get_route_info(net, prefix, prefixlen, gwaddr, dev);  }  #endif  struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)  { +	u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT;  	struct rt6_info *rt;  	struct fib6_table *table; -	table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT); +	table = fib6_get_table(dev_net(dev), tb_id);  	if (!table)  		return NULL; @@ -2424,20 +2436,20 @@ struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,  	cfg.fc_gateway = *gwaddr; -	ip6_route_add(&cfg); +	if (!ip6_route_add(&cfg)) { +		struct fib6_table *table; + +		table = fib6_get_table(dev_net(dev), cfg.fc_table); +		if (table) +			table->flags |= RT6_TABLE_HAS_DFLT_ROUTER; +	}  	return rt6_get_dflt_router(gwaddr, dev);  } -void rt6_purge_dflt_routers(struct net *net) +static void __rt6_purge_dflt_routers(struct fib6_table *table)  {  	struct rt6_info *rt; -	struct fib6_table *table; - -	/* NOTE: Keep consistent with rt6_get_dflt_router */ -	table = fib6_get_table(net, RT6_TABLE_DFLT); -	if (!table) -		return;  restart:  	read_lock_bh(&table->tb6_lock); @@ -2451,6 +2463,27 @@ restart:  		}  	}  	read_unlock_bh(&table->tb6_lock); + +	table->flags &= ~RT6_TABLE_HAS_DFLT_ROUTER; +} + +void rt6_purge_dflt_routers(struct net *net) +{ +	struct fib6_table *table; +	struct hlist_head *head; +	unsigned int h; + +	rcu_read_lock(); + +	for (h = 0; h < FIB6_TABLE_HASHSZ; h++) { +		head = &net->ipv6.fib_table_hash[h]; +		hlist_for_each_entry_rcu(table, head, tb6_hlist) { +			if (table->flags & RT6_TABLE_HAS_DFLT_ROUTER) +				__rt6_purge_dflt_routers(table); +		} +	} + +	rcu_read_unlock();  }  static void rtmsg_to_fib6_config(struct net *net, @@ -2728,6 +2761,7 @@ static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)  	   PMTU discouvery.  	 */  	if (rt->dst.dev == arg->dev && +	    dst_metric_raw(&rt->dst, RTAX_MTU) &&  	    !dst_metric_locked(&rt->dst, RTAX_MTU)) {  		if (rt->rt6i_flags & RTF_CACHE) {  			/* For RTF_CACHE with rt6i_pmtu == 0  | 
