diff options
Diffstat (limited to 'net/ipv6/addrconf.c')
| -rw-r--r-- | net/ipv6/addrconf.c | 898 | 
1 files changed, 546 insertions, 352 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index cd3fb301da3..5667b3003af 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -83,11 +83,7 @@  #include <linux/if_tunnel.h>  #include <linux/rtnetlink.h>  #include <linux/netconf.h> - -#ifdef CONFIG_IPV6_PRIVACY  #include <linux/random.h> -#endif -  #include <linux/uaccess.h>  #include <asm/unaligned.h> @@ -124,11 +120,9 @@ static inline void addrconf_sysctl_unregister(struct inet6_dev *idev)  }  #endif -#ifdef CONFIG_IPV6_PRIVACY  static void __ipv6_regen_rndid(struct inet6_dev *idev);  static void __ipv6_try_regen_rndid(struct inet6_dev *idev, struct in6_addr *tmpaddr);  static void ipv6_regen_rndid(unsigned long data); -#endif  static int ipv6_generate_eui64(u8 *eui, struct net_device *dev);  static int ipv6_count_addresses(struct inet6_dev *idev); @@ -139,10 +133,12 @@ static int ipv6_count_addresses(struct inet6_dev *idev);  static struct hlist_head inet6_addr_lst[IN6_ADDR_HSIZE];  static DEFINE_SPINLOCK(addrconf_hash_lock); -static void addrconf_verify(unsigned long); +static void addrconf_verify(void); +static void addrconf_verify_rtnl(void); +static void addrconf_verify_work(struct work_struct *); -static DEFINE_TIMER(addr_chk_timer, addrconf_verify, 0, 0); -static DEFINE_SPINLOCK(addrconf_verify_lock); +static struct workqueue_struct *addrconf_wq; +static DECLARE_DELAYED_WORK(addr_chk_work, addrconf_verify_work);  static void addrconf_join_anycast(struct inet6_ifaddr *ifp);  static void addrconf_leave_anycast(struct inet6_ifaddr *ifp); @@ -157,7 +153,7 @@ static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx,  						  u32 flags, u32 noflags);  static void addrconf_dad_start(struct inet6_ifaddr *ifp); -static void addrconf_dad_timer(unsigned long data); +static void addrconf_dad_work(struct work_struct *w);  static void addrconf_dad_completed(struct inet6_ifaddr *ifp);  static void addrconf_dad_run(struct inet6_dev *idev);  static void addrconf_rs_timer(unsigned long data); @@ -183,13 +179,11 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {  	.rtr_solicits		= MAX_RTR_SOLICITATIONS,  	.rtr_solicit_interval	= RTR_SOLICITATION_INTERVAL,  	.rtr_solicit_delay	= MAX_RTR_SOLICITATION_DELAY, -#ifdef CONFIG_IPV6_PRIVACY  	.use_tempaddr 		= 0,  	.temp_valid_lft		= TEMP_VALID_LIFETIME,  	.temp_prefered_lft	= TEMP_PREFERRED_LIFETIME,  	.regen_max_retry	= REGEN_MAX_RETRY,  	.max_desync_factor	= MAX_DESYNC_FACTOR, -#endif  	.max_addresses		= IPV6_MAX_ADDRESSES,  	.accept_ra_defrtr	= 1,  	.accept_ra_pinfo	= 1, @@ -221,13 +215,11 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {  	.rtr_solicits		= MAX_RTR_SOLICITATIONS,  	.rtr_solicit_interval	= RTR_SOLICITATION_INTERVAL,  	.rtr_solicit_delay	= MAX_RTR_SOLICITATION_DELAY, -#ifdef CONFIG_IPV6_PRIVACY  	.use_tempaddr		= 0,  	.temp_valid_lft		= TEMP_VALID_LIFETIME,  	.temp_prefered_lft	= TEMP_PREFERRED_LIFETIME,  	.regen_max_retry	= REGEN_MAX_RETRY,  	.max_desync_factor	= MAX_DESYNC_FACTOR, -#endif  	.max_addresses		= IPV6_MAX_ADDRESSES,  	.accept_ra_defrtr	= 1,  	.accept_ra_pinfo	= 1, @@ -257,9 +249,9 @@ static void addrconf_del_rs_timer(struct inet6_dev *idev)  		__in6_dev_put(idev);  } -static void addrconf_del_dad_timer(struct inet6_ifaddr *ifp) +static void addrconf_del_dad_work(struct inet6_ifaddr *ifp)  { -	if (del_timer(&ifp->dad_timer)) +	if (cancel_delayed_work(&ifp->dad_work))  		__in6_ifa_put(ifp);  } @@ -271,20 +263,29 @@ static void addrconf_mod_rs_timer(struct inet6_dev *idev,  	mod_timer(&idev->rs_timer, jiffies + when);  } -static void addrconf_mod_dad_timer(struct inet6_ifaddr *ifp, -				   unsigned long when) +static void addrconf_mod_dad_work(struct inet6_ifaddr *ifp, +				   unsigned long delay)  { -	if (!timer_pending(&ifp->dad_timer)) +	if (!delayed_work_pending(&ifp->dad_work))  		in6_ifa_hold(ifp); -	mod_timer(&ifp->dad_timer, jiffies + when); +	mod_delayed_work(addrconf_wq, &ifp->dad_work, delay);  }  static int snmp6_alloc_dev(struct inet6_dev *idev)  { -	if (snmp_mib_init((void __percpu **)idev->stats.ipv6, -			  sizeof(struct ipstats_mib), -			  __alignof__(struct ipstats_mib)) < 0) +	int i; + +	idev->stats.ipv6 = alloc_percpu(struct ipstats_mib); +	if (!idev->stats.ipv6)  		goto err_ip; + +	for_each_possible_cpu(i) { +		struct ipstats_mib *addrconf_stats; +		addrconf_stats = per_cpu_ptr(idev->stats.ipv6, i); +		u64_stats_init(&addrconf_stats->syncp); +	} + +  	idev->stats.icmpv6dev = kzalloc(sizeof(struct icmpv6_mib_device),  					GFP_KERNEL);  	if (!idev->stats.icmpv6dev) @@ -299,7 +300,7 @@ static int snmp6_alloc_dev(struct inet6_dev *idev)  err_icmpmsg:  	kfree(idev->stats.icmpv6dev);  err_icmp: -	snmp_mib_free((void __percpu **)idev->stats.ipv6); +	free_percpu(idev->stats.ipv6);  err_ip:  	return -ENOMEM;  } @@ -371,7 +372,6 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)  	}  #endif -#ifdef CONFIG_IPV6_PRIVACY  	INIT_LIST_HEAD(&ndev->tempaddr_list);  	setup_timer(&ndev->regen_timer, ipv6_regen_rndid, (unsigned long)ndev);  	if ((dev->flags&IFF_LOOPBACK) || @@ -384,7 +384,7 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)  		in6_dev_hold(ndev);  		ipv6_regen_rndid((unsigned long) ndev);  	} -#endif +  	ndev->token = in6addr_any;  	if (netif_running(dev) && addrconf_qdisc_ok(dev)) @@ -439,6 +439,8 @@ static int inet6_netconf_msgsize_devconf(int type)  	if (type == -1 || type == NETCONFA_MC_FORWARDING)  		size += nla_total_size(4);  #endif +	if (type == -1 || type == NETCONFA_PROXY_NEIGH) +		size += nla_total_size(4);  	return size;  } @@ -472,6 +474,10 @@ static int inet6_netconf_fill_devconf(struct sk_buff *skb, int ifindex,  			devconf->mc_forwarding) < 0)  		goto nla_put_failure;  #endif +	if ((type == -1 || type == NETCONFA_PROXY_NEIGH) && +	    nla_put_s32(skb, NETCONFA_PROXY_NEIGH, devconf->proxy_ndp) < 0) +		goto nla_put_failure; +  	return nlmsg_end(skb, nlh);  nla_put_failure: @@ -506,6 +512,7 @@ errout:  static const struct nla_policy devconf_ipv6_policy[NETCONFA_MAX+1] = {  	[NETCONFA_IFINDEX]	= { .len = sizeof(int) },  	[NETCONFA_FORWARDING]	= { .len = sizeof(int) }, +	[NETCONFA_PROXY_NEIGH]	= { .len = sizeof(int) },  };  static int inet6_netconf_get_devconf(struct sk_buff *in_skb, @@ -741,8 +748,9 @@ void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp)  	in6_dev_put(ifp->idev); -	if (del_timer(&ifp->dad_timer)) -		pr_notice("Timer is still running, when freeing ifa=%p\n", ifp); +	if (cancel_delayed_work(&ifp->dad_work)) +		pr_notice("delayed DAD work was pending while freeing ifa=%p\n", +			  ifp);  	if (ifp->state != INET6_IFADDR_STATE_DEAD) {  		pr_warn("Freeing alive inet6 address %p\n", ifp); @@ -831,14 +839,15 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr,  		goto out;  	} +	neigh_parms_data_state_setall(idev->nd_parms); +  	ifa->addr = *addr;  	if (peer_addr)  		ifa->peer_addr = *peer_addr;  	spin_lock_init(&ifa->lock);  	spin_lock_init(&ifa->state_lock); -	setup_timer(&ifa->dad_timer, addrconf_dad_timer, -		    (unsigned long)ifa); +	INIT_DELAYED_WORK(&ifa->dad_work, addrconf_dad_work);  	INIT_HLIST_NODE(&ifa->addr_lst);  	ifa->scope = scope;  	ifa->prefix_len = pfxlen; @@ -865,12 +874,10 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr,  	/* Add to inet6_dev unicast addr list. */  	ipv6_link_dev_addr(idev, ifa); -#ifdef CONFIG_IPV6_PRIVACY  	if (ifa->flags&IFA_F_TEMPORARY) {  		list_add(&ifa->tmp_list, &idev->tempaddr_list);  		in6_ifa_hold(ifa);  	} -#endif  	in6_ifa_hold(ifa);  	write_unlock(&idev->lock); @@ -890,15 +897,97 @@ out:  	goto out2;  } +enum cleanup_prefix_rt_t { +	CLEANUP_PREFIX_RT_NOP,    /* no cleanup action for prefix route */ +	CLEANUP_PREFIX_RT_DEL,    /* delete the prefix route */ +	CLEANUP_PREFIX_RT_EXPIRE, /* update the lifetime of the prefix route */ +}; + +/* + * Check, whether the prefix for ifp would still need a prefix route + * after deleting ifp. The function returns one of the CLEANUP_PREFIX_RT_* + * constants. + * + * 1) we don't purge prefix if address was not permanent. + *    prefix is managed by its own lifetime. + * 2) we also don't purge, if the address was IFA_F_NOPREFIXROUTE. + * 3) if there are no addresses, delete prefix. + * 4) if there are still other permanent address(es), + *    corresponding prefix is still permanent. + * 5) if there are still other addresses with IFA_F_NOPREFIXROUTE, + *    don't purge the prefix, assume user space is managing it. + * 6) otherwise, update prefix lifetime to the + *    longest valid lifetime among the corresponding + *    addresses on the device. + *    Note: subsequent RA will update lifetime. + **/ +static enum cleanup_prefix_rt_t +check_cleanup_prefix_route(struct inet6_ifaddr *ifp, unsigned long *expires) +{ +	struct inet6_ifaddr *ifa; +	struct inet6_dev *idev = ifp->idev; +	unsigned long lifetime; +	enum cleanup_prefix_rt_t action = CLEANUP_PREFIX_RT_DEL; + +	*expires = jiffies; + +	list_for_each_entry(ifa, &idev->addr_list, if_list) { +		if (ifa == ifp) +			continue; +		if (!ipv6_prefix_equal(&ifa->addr, &ifp->addr, +				       ifp->prefix_len)) +			continue; +		if (ifa->flags & (IFA_F_PERMANENT | IFA_F_NOPREFIXROUTE)) +			return CLEANUP_PREFIX_RT_NOP; + +		action = CLEANUP_PREFIX_RT_EXPIRE; + +		spin_lock(&ifa->lock); + +		lifetime = addrconf_timeout_fixup(ifa->valid_lft, HZ); +		/* +		 * Note: Because this address is +		 * not permanent, lifetime < +		 * LONG_MAX / HZ here. +		 */ +		if (time_before(*expires, ifa->tstamp + lifetime * HZ)) +			*expires = ifa->tstamp + lifetime * HZ; +		spin_unlock(&ifa->lock); +	} + +	return action; +} + +static void +cleanup_prefix_route(struct inet6_ifaddr *ifp, unsigned long expires, bool del_rt) +{ +	struct rt6_info *rt; + +	rt = addrconf_get_prefix_route(&ifp->addr, +				       ifp->prefix_len, +				       ifp->idev->dev, +				       0, RTF_GATEWAY | RTF_DEFAULT); +	if (rt) { +		if (del_rt) +			ip6_del_rt(rt); +		else { +			if (!(rt->rt6i_flags & RTF_EXPIRES)) +				rt6_set_expires(rt, expires); +			ip6_rt_put(rt); +		} +	} +} + +  /* This function wants to get referenced ifp and releases it before return */  static void ipv6_del_addr(struct inet6_ifaddr *ifp)  { -	struct inet6_ifaddr *ifa, *ifn; -	struct inet6_dev *idev = ifp->idev;  	int state; -	int deleted = 0, onlink = 0; -	unsigned long expires = jiffies; +	enum cleanup_prefix_rt_t action = CLEANUP_PREFIX_RT_NOP; +	unsigned long expires; + +	ASSERT_RTNL();  	spin_lock_bh(&ifp->state_lock);  	state = ifp->state; @@ -912,8 +1001,8 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)  	hlist_del_init_rcu(&ifp->addr_lst);  	spin_unlock_bh(&addrconf_hash_lock); -	write_lock_bh(&idev->lock); -#ifdef CONFIG_IPV6_PRIVACY +	write_lock_bh(&ifp->idev->lock); +  	if (ifp->flags&IFA_F_TEMPORARY) {  		list_del(&ifp->tmp_list);  		if (ifp->ifpub) { @@ -922,89 +1011,24 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)  		}  		__in6_ifa_put(ifp);  	} -#endif -	list_for_each_entry_safe(ifa, ifn, &idev->addr_list, if_list) { -		if (ifa == ifp) { -			list_del_init(&ifp->if_list); -			__in6_ifa_put(ifp); +	if (ifp->flags & IFA_F_PERMANENT && !(ifp->flags & IFA_F_NOPREFIXROUTE)) +		action = check_cleanup_prefix_route(ifp, &expires); -			if (!(ifp->flags & IFA_F_PERMANENT) || onlink > 0) -				break; -			deleted = 1; -			continue; -		} else if (ifp->flags & IFA_F_PERMANENT) { -			if (ipv6_prefix_equal(&ifa->addr, &ifp->addr, -					      ifp->prefix_len)) { -				if (ifa->flags & IFA_F_PERMANENT) { -					onlink = 1; -					if (deleted) -						break; -				} else { -					unsigned long lifetime; - -					if (!onlink) -						onlink = -1; - -					spin_lock(&ifa->lock); - -					lifetime = addrconf_timeout_fixup(ifa->valid_lft, HZ); -					/* -					 * Note: Because this address is -					 * not permanent, lifetime < -					 * LONG_MAX / HZ here. -					 */ -					if (time_before(expires, -							ifa->tstamp + lifetime * HZ)) -						expires = ifa->tstamp + lifetime * HZ; -					spin_unlock(&ifa->lock); -				} -			} -		} -	} -	write_unlock_bh(&idev->lock); +	list_del_init(&ifp->if_list); +	__in6_ifa_put(ifp); -	addrconf_del_dad_timer(ifp); +	write_unlock_bh(&ifp->idev->lock); + +	addrconf_del_dad_work(ifp);  	ipv6_ifa_notify(RTM_DELADDR, ifp);  	inet6addr_notifier_call_chain(NETDEV_DOWN, ifp); -	/* -	 * Purge or update corresponding prefix -	 * -	 * 1) we don't purge prefix here if address was not permanent. -	 *    prefix is managed by its own lifetime. -	 * 2) if there're no addresses, delete prefix. -	 * 3) if there're still other permanent address(es), -	 *    corresponding prefix is still permanent. -	 * 4) otherwise, update prefix lifetime to the -	 *    longest valid lifetime among the corresponding -	 *    addresses on the device. -	 *    Note: subsequent RA will update lifetime. -	 * -	 * --yoshfuji -	 */ -	if ((ifp->flags & IFA_F_PERMANENT) && onlink < 1) { -		struct in6_addr prefix; -		struct rt6_info *rt; - -		ipv6_addr_prefix(&prefix, &ifp->addr, ifp->prefix_len); - -		rt = addrconf_get_prefix_route(&prefix, -					       ifp->prefix_len, -					       ifp->idev->dev, -					       0, RTF_GATEWAY | RTF_DEFAULT); - -		if (rt) { -			if (onlink == 0) { -				ip6_del_rt(rt); -				rt = NULL; -			} else if (!(rt->rt6i_flags & RTF_EXPIRES)) { -				rt6_set_expires(rt, expires); -			} -		} -		ip6_rt_put(rt); +	if (action != CLEANUP_PREFIX_RT_NOP) { +		cleanup_prefix_route(ifp, expires, +			action == CLEANUP_PREFIX_RT_DEL);  	}  	/* clean up prefsrc entries */ @@ -1013,7 +1037,6 @@ out:  	in6_ifa_put(ifp);  } -#ifdef CONFIG_IPV6_PRIVACY  static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *ift)  {  	struct inet6_dev *idev = ifp->idev; @@ -1025,7 +1048,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *i  	u32 addr_flags;  	unsigned long now = jiffies; -	write_lock(&idev->lock); +	write_lock_bh(&idev->lock);  	if (ift) {  		spin_lock_bh(&ift->lock);  		memcpy(&addr.s6_addr[8], &ift->addr.s6_addr[8], 8); @@ -1037,7 +1060,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *i  retry:  	in6_dev_hold(idev);  	if (idev->cnf.use_tempaddr <= 0) { -		write_unlock(&idev->lock); +		write_unlock_bh(&idev->lock);  		pr_info("%s: use_tempaddr is disabled\n", __func__);  		in6_dev_put(idev);  		ret = -1; @@ -1047,7 +1070,7 @@ retry:  	if (ifp->regen_count++ >= idev->cnf.regen_max_retry) {  		idev->cnf.use_tempaddr = -1;	/*XXX*/  		spin_unlock_bh(&ifp->lock); -		write_unlock(&idev->lock); +		write_unlock_bh(&idev->lock);  		pr_warn("%s: regeneration time exceeded - disabled temporary address support\n",  			__func__);  		in6_dev_put(idev); @@ -1072,15 +1095,18 @@ retry:  	regen_advance = idev->cnf.regen_max_retry *  	                idev->cnf.dad_transmits * -	                idev->nd_parms->retrans_time / HZ; -	write_unlock(&idev->lock); +	                NEIGH_VAR(idev->nd_parms, RETRANS_TIME) / HZ; +	write_unlock_bh(&idev->lock);  	/* A temporary address is created only if this calculated Preferred  	 * Lifetime is greater than REGEN_ADVANCE time units.  In particular,  	 * an implementation must not create a temporary address with a zero  	 * Preferred Lifetime. +	 * Use age calculation as in addrconf_verify to avoid unnecessary +	 * temporary addresses being generated.  	 */ -	if (tmp_prefered_lft <= regen_advance) { +	age = (now - tmp_tstamp + ADDRCONF_TIMER_FUZZ_MINUS) / HZ; +	if (tmp_prefered_lft <= regen_advance + age) {  		in6_ifa_put(ifp);  		in6_dev_put(idev);  		ret = -1; @@ -1100,7 +1126,7 @@ retry:  		in6_dev_put(idev);  		pr_info("%s: retry temporary address regeneration\n", __func__);  		tmpaddr = &addr; -		write_lock(&idev->lock); +		write_lock_bh(&idev->lock);  		goto retry;  	} @@ -1116,7 +1142,6 @@ retry:  out:  	return ret;  } -#endif  /*   *	Choose an appropriate source address (RFC3484) @@ -1131,9 +1156,7 @@ enum {  #endif  	IPV6_SADDR_RULE_OIF,  	IPV6_SADDR_RULE_LABEL, -#ifdef CONFIG_IPV6_PRIVACY  	IPV6_SADDR_RULE_PRIVACY, -#endif  	IPV6_SADDR_RULE_ORCHID,  	IPV6_SADDR_RULE_PREFIX,  	IPV6_SADDR_RULE_MAX @@ -1204,7 +1227,7 @@ static int ipv6_get_saddr_eval(struct net *net,  		 *       |             d is scope of the destination.  		 *  B-d  |  \  		 *       |   \      <- smaller scope is better if -		 *  B-15 |    \        if scope is enough for destinaion. +		 *  B-15 |    \        if scope is enough for destination.  		 *       |             ret = B - scope (-1 <= scope >= d <= 15).  		 * d-C-1 | /  		 *       |/         <- greater is better @@ -1247,7 +1270,6 @@ static int ipv6_get_saddr_eval(struct net *net,  				      &score->ifa->addr, score->addr_type,  				      score->ifa->idev->dev->ifindex) == dst->label;  		break; -#ifdef CONFIG_IPV6_PRIVACY  	case IPV6_SADDR_RULE_PRIVACY:  	    {  		/* Rule 7: Prefer public address @@ -1259,7 +1281,6 @@ static int ipv6_get_saddr_eval(struct net *net,  		ret = (!(score->ifa->flags & IFA_F_TEMPORARY)) ^ preftmp;  		break;  	    } -#endif  	case IPV6_SADDR_RULE_ORCHID:  		/* Rule 8-: Prefer ORCHID vs ORCHID or  		 *	    non-ORCHID vs non-ORCHID @@ -1413,12 +1434,14 @@ try_nextdev:  EXPORT_SYMBOL(ipv6_dev_get_saddr);  int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr, -		      unsigned char banned_flags) +		      u32 banned_flags)  {  	struct inet6_ifaddr *ifp;  	int err = -EADDRNOTAVAIL; -	list_for_each_entry(ifp, &idev->addr_list, if_list) { +	list_for_each_entry_reverse(ifp, &idev->addr_list, if_list) { +		if (ifp->scope > IFA_LINK) +			break;  		if (ifp->scope == IFA_LINK &&  		    !(ifp->flags & banned_flags)) {  			*addr = ifp->addr; @@ -1430,7 +1453,7 @@ int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr,  }  int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, -		    unsigned char banned_flags) +		    u32 banned_flags)  {  	struct inet6_dev *idev;  	int err = -EADDRNOTAVAIL; @@ -1580,7 +1603,7 @@ static void addrconf_dad_stop(struct inet6_ifaddr *ifp, int dad_failed)  {  	if (ifp->flags&IFA_F_PERMANENT) {  		spin_lock_bh(&ifp->lock); -		addrconf_del_dad_timer(ifp); +		addrconf_del_dad_work(ifp);  		ifp->flags |= IFA_F_TENTATIVE;  		if (dad_failed)  			ifp->flags |= IFA_F_DADFAILED; @@ -1588,7 +1611,6 @@ static void addrconf_dad_stop(struct inet6_ifaddr *ifp, int dad_failed)  		if (dad_failed)  			ipv6_ifa_notify(0, ifp);  		in6_ifa_put(ifp); -#ifdef CONFIG_IPV6_PRIVACY  	} else if (ifp->flags&IFA_F_TEMPORARY) {  		struct inet6_ifaddr *ifpub;  		spin_lock_bh(&ifp->lock); @@ -1602,21 +1624,21 @@ static void addrconf_dad_stop(struct inet6_ifaddr *ifp, int dad_failed)  			spin_unlock_bh(&ifp->lock);  		}  		ipv6_del_addr(ifp); -#endif -	} else +	} else {  		ipv6_del_addr(ifp); +	}  }  static int addrconf_dad_end(struct inet6_ifaddr *ifp)  {  	int err = -ENOENT; -	spin_lock(&ifp->state_lock); +	spin_lock_bh(&ifp->state_lock);  	if (ifp->state == INET6_IFADDR_STATE_DAD) {  		ifp->state = INET6_IFADDR_STATE_POSTDAD;  		err = 0;  	} -	spin_unlock(&ifp->state_lock); +	spin_unlock_bh(&ifp->state_lock);  	return err;  } @@ -1649,7 +1671,12 @@ void addrconf_dad_failure(struct inet6_ifaddr *ifp)  		}  	} -	addrconf_dad_stop(ifp, 1); +	spin_lock_bh(&ifp->state_lock); +	/* transition from _POSTDAD to _ERRDAD */ +	ifp->state = INET6_IFADDR_STATE_ERRDAD; +	spin_unlock_bh(&ifp->state_lock); + +	addrconf_mod_dad_work(ifp, 0);  }  /* Join to solicited addr multicast group. */ @@ -1658,6 +1685,8 @@ void addrconf_join_solict(struct net_device *dev, const struct in6_addr *addr)  {  	struct in6_addr maddr; +	ASSERT_RTNL(); +  	if (dev->flags&(IFF_LOOPBACK|IFF_NOARP))  		return; @@ -1669,6 +1698,8 @@ void addrconf_leave_solict(struct inet6_dev *idev, const struct in6_addr *addr)  {  	struct in6_addr maddr; +	ASSERT_RTNL(); +  	if (idev->dev->flags&(IFF_LOOPBACK|IFF_NOARP))  		return; @@ -1679,7 +1710,10 @@ void addrconf_leave_solict(struct inet6_dev *idev, const struct in6_addr *addr)  static void addrconf_join_anycast(struct inet6_ifaddr *ifp)  {  	struct in6_addr addr; -	if (ifp->prefix_len == 127) /* RFC 6164 */ + +	ASSERT_RTNL(); + +	if (ifp->prefix_len >= 127) /* RFC 6164 */  		return;  	ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len);  	if (ipv6_addr_any(&addr)) @@ -1690,7 +1724,10 @@ static void addrconf_join_anycast(struct inet6_ifaddr *ifp)  static void addrconf_leave_anycast(struct inet6_ifaddr *ifp)  {  	struct in6_addr addr; -	if (ifp->prefix_len == 127) /* RFC 6164 */ + +	ASSERT_RTNL(); + +	if (ifp->prefix_len >= 127) /* RFC 6164 */  		return;  	ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len);  	if (ipv6_addr_any(&addr)) @@ -1824,6 +1861,7 @@ static int ipv6_generate_eui64(u8 *eui, struct net_device *dev)  		return addrconf_ifid_sit(eui, dev);  	case ARPHRD_IPGRE:  		return addrconf_ifid_gre(eui, dev); +	case ARPHRD_6LOWPAN:  	case ARPHRD_IEEE802154:  		return addrconf_ifid_eui64(eui, dev);  	case ARPHRD_IEEE1394: @@ -1840,7 +1878,9 @@ static int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev)  	struct inet6_ifaddr *ifp;  	read_lock_bh(&idev->lock); -	list_for_each_entry(ifp, &idev->addr_list, if_list) { +	list_for_each_entry_reverse(ifp, &idev->addr_list, if_list) { +		if (ifp->scope > IFA_LINK) +			break;  		if (ifp->scope == IFA_LINK && !(ifp->flags&IFA_F_TENTATIVE)) {  			memcpy(eui, ifp->addr.s6_addr+8, 8);  			err = 0; @@ -1851,7 +1891,6 @@ static int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev)  	return err;  } -#ifdef CONFIG_IPV6_PRIVACY  /* (re)generation of randomized interface identifier (RFC 3041 3.2, 3.5) */  static void __ipv6_regen_rndid(struct inet6_dev *idev)  { @@ -1897,7 +1936,8 @@ static void ipv6_regen_rndid(unsigned long data)  	expires = jiffies +  		idev->cnf.temp_prefered_lft * HZ - -		idev->cnf.regen_max_retry * idev->cnf.dad_transmits * idev->nd_parms->retrans_time - +		idev->cnf.regen_max_retry * idev->cnf.dad_transmits * +		NEIGH_VAR(idev->nd_parms, RETRANS_TIME) -  		idev->cnf.max_desync_factor * HZ;  	if (time_before(expires, jiffies)) {  		pr_warn("%s: too short regeneration interval; timer disabled for %s\n", @@ -1919,7 +1959,6 @@ static void  __ipv6_try_regen_rndid(struct inet6_dev *idev, struct in6_addr *tmp  	if (tmpaddr && memcmp(idev->rndid, &tmpaddr->s6_addr[8], 8) == 0)  		__ipv6_regen_rndid(idev);  } -#endif  /*   *	Add prefix route. @@ -2006,23 +2045,6 @@ static void addrconf_add_mroute(struct net_device *dev)  	ip6_route_add(&cfg);  } -#if IS_ENABLED(CONFIG_IPV6_SIT) -static void sit_route_add(struct net_device *dev) -{ -	struct fib6_config cfg = { -		.fc_table = RT6_TABLE_MAIN, -		.fc_metric = IP6_RT_PRIO_ADDRCONF, -		.fc_ifindex = dev->ifindex, -		.fc_dst_len = 96, -		.fc_flags = RTF_UP | RTF_NONEXTHOP, -		.fc_nlinfo.nl_net = dev_net(dev), -	}; - -	/* prefix length - 96 bits "::d.d.d.d" */ -	ip6_route_add(&cfg); -} -#endif -  static struct inet6_dev *addrconf_add_dev(struct net_device *dev)  {  	struct inet6_dev *idev; @@ -2043,6 +2065,73 @@ static struct inet6_dev *addrconf_add_dev(struct net_device *dev)  	return idev;  } +static void manage_tempaddrs(struct inet6_dev *idev, +			     struct inet6_ifaddr *ifp, +			     __u32 valid_lft, __u32 prefered_lft, +			     bool create, unsigned long now) +{ +	u32 flags; +	struct inet6_ifaddr *ift; + +	read_lock_bh(&idev->lock); +	/* update all temporary addresses in the list */ +	list_for_each_entry(ift, &idev->tempaddr_list, tmp_list) { +		int age, max_valid, max_prefered; + +		if (ifp != ift->ifpub) +			continue; + +		/* RFC 4941 section 3.3: +		 * If a received option will extend the lifetime of a public +		 * address, the lifetimes of temporary addresses should +		 * be extended, subject to the overall constraint that no +		 * temporary addresses should ever remain "valid" or "preferred" +		 * for a time longer than (TEMP_VALID_LIFETIME) or +		 * (TEMP_PREFERRED_LIFETIME - DESYNC_FACTOR), respectively. +		 */ +		age = (now - ift->cstamp) / HZ; +		max_valid = idev->cnf.temp_valid_lft - age; +		if (max_valid < 0) +			max_valid = 0; + +		max_prefered = idev->cnf.temp_prefered_lft - +			       idev->cnf.max_desync_factor - age; +		if (max_prefered < 0) +			max_prefered = 0; + +		if (valid_lft > max_valid) +			valid_lft = max_valid; + +		if (prefered_lft > max_prefered) +			prefered_lft = max_prefered; + +		spin_lock(&ift->lock); +		flags = ift->flags; +		ift->valid_lft = valid_lft; +		ift->prefered_lft = prefered_lft; +		ift->tstamp = now; +		if (prefered_lft > 0) +			ift->flags &= ~IFA_F_DEPRECATED; + +		spin_unlock(&ift->lock); +		if (!(flags&IFA_F_TENTATIVE)) +			ipv6_ifa_notify(0, ift); +	} + +	if ((create || list_empty(&idev->tempaddr_list)) && +	    idev->cnf.use_tempaddr > 0) { +		/* When a new public address is created as described +		 * in [ADDRCONF], also create a new temporary address. +		 * Also create a temporary address if it's enabled but +		 * no temporary address currently exists. +		 */ +		read_unlock_bh(&idev->lock); +		ipv6_create_tempaddr(ifp, NULL); +	} else { +		read_unlock_bh(&idev->lock); +	} +} +  void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)  {  	struct prefix_info *pinfo; @@ -2199,17 +2288,17 @@ ok:  			update_lft = 0;  			create = 1; +			spin_lock_bh(&ifp->lock); +			ifp->flags |= IFA_F_MANAGETEMPADDR;  			ifp->cstamp = jiffies;  			ifp->tokenized = tokenized; +			spin_unlock_bh(&ifp->lock);  			addrconf_dad_start(ifp);  		}  		if (ifp) { -			int flags; +			u32 flags;  			unsigned long now; -#ifdef CONFIG_IPV6_PRIVACY -			struct inet6_ifaddr *ift; -#endif  			u32 stored_lft;  			/* update lifetime (RFC2462 5.5.3 e) */ @@ -2250,74 +2339,11 @@ ok:  			} else  				spin_unlock(&ifp->lock); -#ifdef CONFIG_IPV6_PRIVACY -			read_lock_bh(&in6_dev->lock); -			/* update all temporary addresses in the list */ -			list_for_each_entry(ift, &in6_dev->tempaddr_list, -					    tmp_list) { -				int age, max_valid, max_prefered; +			manage_tempaddrs(in6_dev, ifp, valid_lft, prefered_lft, +					 create, now); -				if (ifp != ift->ifpub) -					continue; - -				/* -				 * RFC 4941 section 3.3: -				 * If a received option will extend the lifetime -				 * of a public address, the lifetimes of -				 * temporary addresses should be extended, -				 * subject to the overall constraint that no -				 * temporary addresses should ever remain -				 * "valid" or "preferred" for a time longer than -				 * (TEMP_VALID_LIFETIME) or -				 * (TEMP_PREFERRED_LIFETIME - DESYNC_FACTOR), -				 * respectively. -				 */ -				age = (now - ift->cstamp) / HZ; -				max_valid = in6_dev->cnf.temp_valid_lft - age; -				if (max_valid < 0) -					max_valid = 0; - -				max_prefered = in6_dev->cnf.temp_prefered_lft - -					       in6_dev->cnf.max_desync_factor - -					       age; -				if (max_prefered < 0) -					max_prefered = 0; - -				if (valid_lft > max_valid) -					valid_lft = max_valid; - -				if (prefered_lft > max_prefered) -					prefered_lft = max_prefered; - -				spin_lock(&ift->lock); -				flags = ift->flags; -				ift->valid_lft = valid_lft; -				ift->prefered_lft = prefered_lft; -				ift->tstamp = now; -				if (prefered_lft > 0) -					ift->flags &= ~IFA_F_DEPRECATED; - -				spin_unlock(&ift->lock); -				if (!(flags&IFA_F_TENTATIVE)) -					ipv6_ifa_notify(0, ift); -			} - -			if ((create || list_empty(&in6_dev->tempaddr_list)) && in6_dev->cnf.use_tempaddr > 0) { -				/* -				 * When a new public address is created as -				 * described in [ADDRCONF], also create a new -				 * temporary address. Also create a temporary -				 * address if it's enabled but no temporary -				 * address currently exists. -				 */ -				read_unlock_bh(&in6_dev->lock); -				ipv6_create_tempaddr(ifp, NULL); -			} else { -				read_unlock_bh(&in6_dev->lock); -			} -#endif  			in6_ifa_put(ifp); -			addrconf_verify(0); +			addrconf_verify();  		}  	}  	inet6_prefix_notify(RTM_NEWPREFIX, in6_dev, pinfo); @@ -2393,10 +2419,11 @@ err_exit:  /*   *	Manual configuration of address on an interface   */ -static int inet6_addr_add(struct net *net, int ifindex, const struct in6_addr *pfx, +static int inet6_addr_add(struct net *net, int ifindex, +			  const struct in6_addr *pfx,  			  const struct in6_addr *peer_pfx, -			  unsigned int plen, __u8 ifa_flags, __u32 prefered_lft, -			  __u32 valid_lft) +			  unsigned int plen, __u32 ifa_flags, +			  __u32 prefered_lft, __u32 valid_lft)  {  	struct inet6_ifaddr *ifp;  	struct inet6_dev *idev; @@ -2415,6 +2442,9 @@ static int inet6_addr_add(struct net *net, int ifindex, const struct in6_addr *p  	if (!valid_lft || prefered_lft > valid_lft)  		return -EINVAL; +	if (ifa_flags & IFA_F_MANAGETEMPADDR && plen != 64) +		return -EINVAL; +  	dev = __dev_get_by_index(net, ifindex);  	if (!dev)  		return -ENODEV; @@ -2447,24 +2477,30 @@ static int inet6_addr_add(struct net *net, int ifindex, const struct in6_addr *p  			    valid_lft, prefered_lft);  	if (!IS_ERR(ifp)) { -		addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev, -				      expires, flags); +		if (!(ifa_flags & IFA_F_NOPREFIXROUTE)) { +			addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev, +					      expires, flags); +		} +  		/*  		 * Note that section 3.1 of RFC 4429 indicates  		 * that the Optimistic flag should not be set for  		 * manually configured addresses  		 */  		addrconf_dad_start(ifp); +		if (ifa_flags & IFA_F_MANAGETEMPADDR) +			manage_tempaddrs(idev, ifp, valid_lft, prefered_lft, +					 true, jiffies);  		in6_ifa_put(ifp); -		addrconf_verify(0); +		addrconf_verify_rtnl();  		return 0;  	}  	return PTR_ERR(ifp);  } -static int inet6_addr_del(struct net *net, int ifindex, const struct in6_addr *pfx, -			  unsigned int plen) +static int inet6_addr_del(struct net *net, int ifindex, u32 ifa_flags, +			  const struct in6_addr *pfx, unsigned int plen)  {  	struct inet6_ifaddr *ifp;  	struct inet6_dev *idev; @@ -2487,7 +2523,12 @@ static int inet6_addr_del(struct net *net, int ifindex, const struct in6_addr *p  			in6_ifa_hold(ifp);  			read_unlock_bh(&idev->lock); +			if (!(ifp->flags & IFA_F_TEMPORARY) && +			    (ifa_flags & IFA_F_MANAGETEMPADDR)) +				manage_tempaddrs(idev, ifp, 0, 0, false, +						 jiffies);  			ipv6_del_addr(ifp); +			addrconf_verify_rtnl();  			return 0;  		}  	} @@ -2527,7 +2568,7 @@ int addrconf_del_ifaddr(struct net *net, void __user *arg)  		return -EFAULT;  	rtnl_lock(); -	err = inet6_addr_del(net, ireq.ifr6_ifindex, &ireq.ifr6_addr, +	err = inet6_addr_del(net, ireq.ifr6_ifindex, 0, &ireq.ifr6_addr,  			     ireq.ifr6_prefixlen);  	rtnl_unlock();  	return err; @@ -2539,7 +2580,8 @@ static void add_addr(struct inet6_dev *idev, const struct in6_addr *addr,  	struct inet6_ifaddr *ifp;  	ifp = ipv6_add_addr(idev, addr, NULL, plen, -			    scope, IFA_F_PERMANENT, 0, 0); +			    scope, IFA_F_PERMANENT, +			    INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);  	if (!IS_ERR(ifp)) {  		spin_lock_bh(&ifp->lock);  		ifp->flags &= ~IFA_F_TENTATIVE; @@ -2555,7 +2597,8 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)  	struct in6_addr addr;  	struct net_device *dev;  	struct net *net = dev_net(idev->dev); -	int scope; +	int scope, plen; +	u32 pflags = 0;  	ASSERT_RTNL(); @@ -2565,12 +2608,16 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)  	if (idev->dev->flags&IFF_POINTOPOINT) {  		addr.s6_addr32[0] = htonl(0xfe800000);  		scope = IFA_LINK; +		plen = 64;  	} else {  		scope = IPV6_ADDR_COMPATv4; +		plen = 96; +		pflags |= RTF_NONEXTHOP;  	}  	if (addr.s6_addr32[3]) { -		add_addr(idev, &addr, 128, scope); +		add_addr(idev, &addr, plen, scope); +		addrconf_prefix_route(&addr, plen, idev->dev, 0, pflags);  		return;  	} @@ -2582,7 +2629,6 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)  			int flag = scope;  			for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { -				int plen;  				addr.s6_addr32[3] = ifa->ifa_local; @@ -2593,12 +2639,10 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)  						continue;  					flag |= IFA_HOST;  				} -				if (idev->dev->flags&IFF_POINTOPOINT) -					plen = 64; -				else -					plen = 96;  				add_addr(idev, &addr, plen, flag); +				addrconf_prefix_route(&addr, plen, idev->dev, 0, +						      pflags);  			}  		}  	} @@ -2638,10 +2682,20 @@ static void init_loopback(struct net_device *dev)  			if (sp_ifa->flags & (IFA_F_DADFAILED | IFA_F_TENTATIVE))  				continue; -			if (sp_ifa->rt) -				continue; +			if (sp_ifa->rt) { +				/* This dst has been added to garbage list when +				 * lo device down, release this obsolete dst and +				 * reallocate a new router for ifa. +				 */ +				if (sp_ifa->rt->dst.obsolete > 0) { +					ip6_rt_put(sp_ifa->rt); +					sp_ifa->rt = NULL; +				} else { +					continue; +				} +			} -			sp_rt = addrconf_dst_alloc(idev, &sp_ifa->addr, 0); +			sp_rt = addrconf_dst_alloc(idev, &sp_ifa->addr, false);  			/* Failure cases are ignored */  			if (!IS_ERR(sp_rt)) { @@ -2665,7 +2719,8 @@ static void addrconf_add_linklocal(struct inet6_dev *idev, const struct in6_addr  #endif -	ifp = ipv6_add_addr(idev, addr, NULL, 64, IFA_LINK, addr_flags, 0, 0); +	ifp = ipv6_add_addr(idev, addr, NULL, 64, IFA_LINK, addr_flags, +			    INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);  	if (!IS_ERR(ifp)) {  		addrconf_prefix_route(&ifp->addr, ifp->prefix_len, idev->dev, 0, 0);  		addrconf_dad_start(ifp); @@ -2686,7 +2741,8 @@ static void addrconf_dev_config(struct net_device *dev)  	    (dev->type != ARPHRD_INFINIBAND) &&  	    (dev->type != ARPHRD_IEEE802154) &&  	    (dev->type != ARPHRD_IEEE1394) && -	    (dev->type != ARPHRD_TUNNEL6)) { +	    (dev->type != ARPHRD_TUNNEL6) && +	    (dev->type != ARPHRD_6LOWPAN)) {  		/* Alas, we support only Ethernet autoconfiguration. */  		return;  	} @@ -2724,7 +2780,6 @@ static void addrconf_sit_config(struct net_device *dev)  		struct in6_addr addr;  		ipv6_addr_set(&addr,  htonl(0xFE800000), 0, 0, 0); -		addrconf_prefix_route(&addr, 64, dev, 0, 0);  		if (!ipv6_generate_eui64(addr.s6_addr + 8, dev))  			addrconf_add_linklocal(idev, &addr);  		return; @@ -2734,8 +2789,6 @@ static void addrconf_sit_config(struct net_device *dev)  	if (dev->flags&IFF_POINTOPOINT)  		addrconf_add_mroute(dev); -	else -		sit_route_add(dev);  }  #endif @@ -2753,25 +2806,13 @@ static void addrconf_gre_config(struct net_device *dev)  	}  	ipv6_addr_set(&addr,  htonl(0xFE800000), 0, 0, 0); -	addrconf_prefix_route(&addr, 64, dev, 0, 0); -  	if (!ipv6_generate_eui64(addr.s6_addr + 8, dev))  		addrconf_add_linklocal(idev, &addr); +	else +		addrconf_prefix_route(&addr, 64, dev, 0, 0);  }  #endif -static inline int -ipv6_inherit_linklocal(struct inet6_dev *idev, struct net_device *link_dev) -{ -	struct in6_addr lladdr; - -	if (!ipv6_get_lladdr(link_dev, &lladdr, IFA_F_TENTATIVE)) { -		addrconf_add_linklocal(idev, &lladdr); -		return 0; -	} -	return -1; -} -  static int addrconf_notify(struct notifier_block *this, unsigned long event,  			   void *ptr)  { @@ -2888,7 +2929,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,  		}  		/* -		 * MTU falled under IPV6_MIN_MTU. +		 * if MTU under IPV6_MIN_MTU.  		 * Stop IPv6 on this interface.  		 */ @@ -2980,7 +3021,7 @@ static int addrconf_ifdown(struct net_device *dev, int how)  		hlist_for_each_entry_rcu(ifa, h, addr_lst) {  			if (ifa->idev == idev) {  				hlist_del_init_rcu(&ifa->addr_lst); -				addrconf_del_dad_timer(ifa); +				addrconf_del_dad_work(ifa);  				goto restart;  			}  		} @@ -2995,7 +3036,6 @@ static int addrconf_ifdown(struct net_device *dev, int how)  	if (!how)  		idev->if_flags &= ~(IF_RS_SENT|IF_RA_RCVD|IF_READY); -#ifdef CONFIG_IPV6_PRIVACY  	if (how && del_timer(&idev->regen_timer))  		in6_dev_put(idev); @@ -3015,12 +3055,11 @@ static int addrconf_ifdown(struct net_device *dev, int how)  		in6_ifa_put(ifa);  		write_lock_bh(&idev->lock);  	} -#endif  	while (!list_empty(&idev->addr_list)) {  		ifa = list_first_entry(&idev->addr_list,  				       struct inet6_ifaddr, if_list); -		addrconf_del_dad_timer(ifa); +		addrconf_del_dad_work(ifa);  		list_del(&ifa->if_list); @@ -3116,20 +3155,20 @@ static void addrconf_dad_kick(struct inet6_ifaddr *ifp)  	if (ifp->flags & IFA_F_OPTIMISTIC)  		rand_num = 0;  	else -		rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1); +		rand_num = prandom_u32() % (idev->cnf.rtr_solicit_delay ? : 1);  	ifp->dad_probes = idev->cnf.dad_transmits; -	addrconf_mod_dad_timer(ifp, rand_num); +	addrconf_mod_dad_work(ifp, rand_num);  } -static void addrconf_dad_start(struct inet6_ifaddr *ifp) +static void addrconf_dad_begin(struct inet6_ifaddr *ifp)  {  	struct inet6_dev *idev = ifp->idev;  	struct net_device *dev = idev->dev;  	addrconf_join_solict(dev, &ifp->addr); -	net_srandom(ifp->addr.s6_addr32[3]); +	prandom_seed((__force u32) ifp->addr.s6_addr32[3]);  	read_lock_bh(&idev->lock);  	spin_lock(&ifp->lock); @@ -3174,25 +3213,68 @@ out:  	read_unlock_bh(&idev->lock);  } -static void addrconf_dad_timer(unsigned long data) +static void addrconf_dad_start(struct inet6_ifaddr *ifp)  { -	struct inet6_ifaddr *ifp = (struct inet6_ifaddr *) data; +	bool begin_dad = false; + +	spin_lock_bh(&ifp->state_lock); +	if (ifp->state != INET6_IFADDR_STATE_DEAD) { +		ifp->state = INET6_IFADDR_STATE_PREDAD; +		begin_dad = true; +	} +	spin_unlock_bh(&ifp->state_lock); + +	if (begin_dad) +		addrconf_mod_dad_work(ifp, 0); +} + +static void addrconf_dad_work(struct work_struct *w) +{ +	struct inet6_ifaddr *ifp = container_of(to_delayed_work(w), +						struct inet6_ifaddr, +						dad_work);  	struct inet6_dev *idev = ifp->idev;  	struct in6_addr mcaddr; +	enum { +		DAD_PROCESS, +		DAD_BEGIN, +		DAD_ABORT, +	} action = DAD_PROCESS; + +	rtnl_lock(); + +	spin_lock_bh(&ifp->state_lock); +	if (ifp->state == INET6_IFADDR_STATE_PREDAD) { +		action = DAD_BEGIN; +		ifp->state = INET6_IFADDR_STATE_DAD; +	} else if (ifp->state == INET6_IFADDR_STATE_ERRDAD) { +		action = DAD_ABORT; +		ifp->state = INET6_IFADDR_STATE_POSTDAD; +	} +	spin_unlock_bh(&ifp->state_lock); + +	if (action == DAD_BEGIN) { +		addrconf_dad_begin(ifp); +		goto out; +	} else if (action == DAD_ABORT) { +		addrconf_dad_stop(ifp, 1); +		goto out; +	} +  	if (!ifp->dad_probes && addrconf_dad_end(ifp))  		goto out; -	write_lock(&idev->lock); +	write_lock_bh(&idev->lock);  	if (idev->dead || !(idev->if_flags & IF_READY)) { -		write_unlock(&idev->lock); +		write_unlock_bh(&idev->lock);  		goto out;  	}  	spin_lock(&ifp->lock);  	if (ifp->state == INET6_IFADDR_STATE_DEAD) {  		spin_unlock(&ifp->lock); -		write_unlock(&idev->lock); +		write_unlock_bh(&idev->lock);  		goto out;  	} @@ -3203,7 +3285,7 @@ static void addrconf_dad_timer(unsigned long data)  		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED);  		spin_unlock(&ifp->lock); -		write_unlock(&idev->lock); +		write_unlock_bh(&idev->lock);  		addrconf_dad_completed(ifp); @@ -3211,15 +3293,35 @@ static void addrconf_dad_timer(unsigned long data)  	}  	ifp->dad_probes--; -	addrconf_mod_dad_timer(ifp, ifp->idev->nd_parms->retrans_time); +	addrconf_mod_dad_work(ifp, +			      NEIGH_VAR(ifp->idev->nd_parms, RETRANS_TIME));  	spin_unlock(&ifp->lock); -	write_unlock(&idev->lock); +	write_unlock_bh(&idev->lock);  	/* send a neighbour solicitation for our addr */  	addrconf_addr_solict_mult(&ifp->addr, &mcaddr);  	ndisc_send_ns(ifp->idev->dev, NULL, &ifp->addr, &mcaddr, &in6addr_any);  out:  	in6_ifa_put(ifp); +	rtnl_unlock(); +} + +/* ifp->idev must be at least read locked */ +static bool ipv6_lonely_lladdr(struct inet6_ifaddr *ifp) +{ +	struct inet6_ifaddr *ifpiter; +	struct inet6_dev *idev = ifp->idev; + +	list_for_each_entry_reverse(ifpiter, &idev->addr_list, if_list) { +		if (ifpiter->scope > IFA_LINK) +			break; +		if (ifp != ifpiter && ifpiter->scope == IFA_LINK && +		    (ifpiter->flags & (IFA_F_PERMANENT|IFA_F_TENTATIVE| +				       IFA_F_OPTIMISTIC|IFA_F_DADFAILED)) == +		    IFA_F_PERMANENT) +			return false; +	} +	return true;  }  static void addrconf_dad_completed(struct inet6_ifaddr *ifp) @@ -3228,7 +3330,7 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp)  	struct in6_addr lladdr;  	bool send_rs, send_mld; -	addrconf_del_dad_timer(ifp); +	addrconf_del_dad_work(ifp);  	/*  	 *	Configure the address for reception. Now it is valid. @@ -3241,14 +3343,11 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp)  	 */  	read_lock_bh(&ifp->idev->lock); -	spin_lock(&ifp->lock); -	send_mld = ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL && -		   ifp->idev->valid_ll_addr_cnt == 1; +	send_mld = ifp->scope == IFA_LINK && ipv6_lonely_lladdr(ifp);  	send_rs = send_mld &&  		  ipv6_accept_ra(ifp->idev) &&  		  ifp->idev->cnf.rtr_solicits > 0 &&  		  (dev->flags&IFF_LOOPBACK) == 0; -	spin_unlock(&ifp->lock);  	read_unlock_bh(&ifp->idev->lock);  	/* While dad is in progress mld report's source address is in6_addrany. @@ -3391,7 +3490,7 @@ static int if6_seq_show(struct seq_file *seq, void *v)  		   ifp->idev->dev->ifindex,  		   ifp->prefix_len,  		   ifp->scope, -		   ifp->flags, +		   (u8) ifp->flags,  		   ifp->idev->dev->name);  	return 0;  } @@ -3472,26 +3571,31 @@ int ipv6_chk_home_addr(struct net *net, const struct in6_addr *addr)   *	Periodic address status verification   */ -static void addrconf_verify(unsigned long foo) +static void addrconf_verify_rtnl(void)  {  	unsigned long now, next, next_sec, next_sched;  	struct inet6_ifaddr *ifp;  	int i; +	ASSERT_RTNL(); +  	rcu_read_lock_bh(); -	spin_lock(&addrconf_verify_lock);  	now = jiffies;  	next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY); -	del_timer(&addr_chk_timer); +	cancel_delayed_work(&addr_chk_work);  	for (i = 0; i < IN6_ADDR_HSIZE; i++) {  restart: -		hlist_for_each_entry_rcu_bh(ifp, -					 &inet6_addr_lst[i], addr_lst) { +		hlist_for_each_entry_rcu_bh(ifp, &inet6_addr_lst[i], addr_lst) {  			unsigned long age; -			if (ifp->flags & IFA_F_PERMANENT) +			/* When setting preferred_lft to a value not zero or +			 * infinity, while valid_lft is infinity +			 * IFA_F_PERMANENT has a non-infinity life time. +			 */ +			if ((ifp->flags & IFA_F_PERMANENT) && +			    (ifp->prefered_lft == INFINITY_LIFE_TIME))  				continue;  			spin_lock(&ifp->lock); @@ -3516,7 +3620,8 @@ restart:  					ifp->flags |= IFA_F_DEPRECATED;  				} -				if (time_before(ifp->tstamp + ifp->valid_lft * HZ, next)) +				if ((ifp->valid_lft != INFINITY_LIFE_TIME) && +				    (time_before(ifp->tstamp + ifp->valid_lft * HZ, next)))  					next = ifp->tstamp + ifp->valid_lft * HZ;  				spin_unlock(&ifp->lock); @@ -3528,12 +3633,11 @@ restart:  					in6_ifa_put(ifp);  					goto restart;  				} -#ifdef CONFIG_IPV6_PRIVACY  			} else if ((ifp->flags&IFA_F_TEMPORARY) &&  				   !(ifp->flags&IFA_F_TENTATIVE)) {  				unsigned long regen_advance = ifp->idev->cnf.regen_max_retry *  					ifp->idev->cnf.dad_transmits * -					ifp->idev->nd_parms->retrans_time / HZ; +					NEIGH_VAR(ifp->idev->nd_parms, RETRANS_TIME) / HZ;  				if (age >= ifp->prefered_lft - regen_advance) {  					struct inet6_ifaddr *ifpub = ifp->ifpub; @@ -3556,7 +3660,6 @@ restart:  				} else if (time_before(ifp->tstamp + ifp->prefered_lft * HZ - regen_advance * HZ, next))  					next = ifp->tstamp + ifp->prefered_lft * HZ - regen_advance * HZ;  				spin_unlock(&ifp->lock); -#endif  			} else {  				/* ifp->prefered_lft <= ifp->valid_lft */  				if (time_before(ifp->tstamp + ifp->prefered_lft * HZ, next)) @@ -3579,13 +3682,22 @@ restart:  	ADBG(KERN_DEBUG "now = %lu, schedule = %lu, rounded schedule = %lu => %lu\n",  	      now, next, next_sec, next_sched); - -	addr_chk_timer.expires = next_sched; -	add_timer(&addr_chk_timer); -	spin_unlock(&addrconf_verify_lock); +	mod_delayed_work(addrconf_wq, &addr_chk_work, next_sched - now);  	rcu_read_unlock_bh();  } +static void addrconf_verify_work(struct work_struct *w) +{ +	rtnl_lock(); +	addrconf_verify_rtnl(); +	rtnl_unlock(); +} + +static void addrconf_verify(void) +{ +	mod_delayed_work(addrconf_wq, &addr_chk_work, 0); +} +  static struct in6_addr *extract_addr(struct nlattr *addr, struct nlattr *local,  				     struct in6_addr **peer_pfx)  { @@ -3609,6 +3721,7 @@ static const struct nla_policy ifa_ipv6_policy[IFA_MAX+1] = {  	[IFA_ADDRESS]		= { .len = sizeof(struct in6_addr) },  	[IFA_LOCAL]		= { .len = sizeof(struct in6_addr) },  	[IFA_CACHEINFO]		= { .len = sizeof(struct ifa_cacheinfo) }, +	[IFA_FLAGS]		= { .len = sizeof(u32) },  };  static int @@ -3618,6 +3731,7 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh)  	struct ifaddrmsg *ifm;  	struct nlattr *tb[IFA_MAX+1];  	struct in6_addr *pfx, *peer_pfx; +	u32 ifa_flags;  	int err;  	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy); @@ -3629,19 +3743,33 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh)  	if (pfx == NULL)  		return -EINVAL; -	return inet6_addr_del(net, ifm->ifa_index, pfx, ifm->ifa_prefixlen); +	ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : ifm->ifa_flags; + +	/* We ignore other flags so far. */ +	ifa_flags &= IFA_F_MANAGETEMPADDR; + +	return inet6_addr_del(net, ifm->ifa_index, ifa_flags, pfx, +			      ifm->ifa_prefixlen);  } -static int inet6_addr_modify(struct inet6_ifaddr *ifp, u8 ifa_flags, +static int inet6_addr_modify(struct inet6_ifaddr *ifp, u32 ifa_flags,  			     u32 prefered_lft, u32 valid_lft)  {  	u32 flags;  	clock_t expires;  	unsigned long timeout; +	bool was_managetempaddr; +	bool had_prefixroute; + +	ASSERT_RTNL();  	if (!valid_lft || (prefered_lft > valid_lft))  		return -EINVAL; +	if (ifa_flags & IFA_F_MANAGETEMPADDR && +	    (ifp->flags & IFA_F_TEMPORARY || ifp->prefix_len != 64)) +		return -EINVAL; +  	timeout = addrconf_timeout_fixup(valid_lft, HZ);  	if (addrconf_finite_timeout(timeout)) {  		expires = jiffies_to_clock_t(timeout * HZ); @@ -3661,7 +3789,13 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, u8 ifa_flags,  	}  	spin_lock_bh(&ifp->lock); -	ifp->flags = (ifp->flags & ~(IFA_F_DEPRECATED | IFA_F_PERMANENT | IFA_F_NODAD | IFA_F_HOMEADDRESS)) | ifa_flags; +	was_managetempaddr = ifp->flags & IFA_F_MANAGETEMPADDR; +	had_prefixroute = ifp->flags & IFA_F_PERMANENT && +			  !(ifp->flags & IFA_F_NOPREFIXROUTE); +	ifp->flags &= ~(IFA_F_DEPRECATED | IFA_F_PERMANENT | IFA_F_NODAD | +			IFA_F_HOMEADDRESS | IFA_F_MANAGETEMPADDR | +			IFA_F_NOPREFIXROUTE); +	ifp->flags |= ifa_flags;  	ifp->tstamp = jiffies;  	ifp->valid_lft = valid_lft;  	ifp->prefered_lft = prefered_lft; @@ -3670,9 +3804,31 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, u8 ifa_flags,  	if (!(ifp->flags&IFA_F_TENTATIVE))  		ipv6_ifa_notify(0, ifp); -	addrconf_prefix_route(&ifp->addr, ifp->prefix_len, ifp->idev->dev, -			      expires, flags); -	addrconf_verify(0); +	if (!(ifa_flags & IFA_F_NOPREFIXROUTE)) { +		addrconf_prefix_route(&ifp->addr, ifp->prefix_len, ifp->idev->dev, +				      expires, flags); +	} else if (had_prefixroute) { +		enum cleanup_prefix_rt_t action; +		unsigned long rt_expires; + +		write_lock_bh(&ifp->idev->lock); +		action = check_cleanup_prefix_route(ifp, &rt_expires); +		write_unlock_bh(&ifp->idev->lock); + +		if (action != CLEANUP_PREFIX_RT_NOP) { +			cleanup_prefix_route(ifp, rt_expires, +				action == CLEANUP_PREFIX_RT_DEL); +		} +	} + +	if (was_managetempaddr || ifp->flags & IFA_F_MANAGETEMPADDR) { +		if (was_managetempaddr && !(ifp->flags & IFA_F_MANAGETEMPADDR)) +			valid_lft = prefered_lft = 0; +		manage_tempaddrs(ifp->idev, ifp, valid_lft, prefered_lft, +				 !was_managetempaddr, jiffies); +	} + +	addrconf_verify_rtnl();  	return 0;  } @@ -3687,7 +3843,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)  	struct inet6_ifaddr *ifa;  	struct net_device *dev;  	u32 valid_lft = INFINITY_LIFE_TIME, preferred_lft = INFINITY_LIFE_TIME; -	u8 ifa_flags; +	u32 ifa_flags;  	int err;  	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy); @@ -3714,14 +3870,17 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)  	if (dev == NULL)  		return -ENODEV; +	ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : ifm->ifa_flags; +  	/* We ignore other flags so far. */ -	ifa_flags = ifm->ifa_flags & (IFA_F_NODAD | IFA_F_HOMEADDRESS); +	ifa_flags &= IFA_F_NODAD | IFA_F_HOMEADDRESS | IFA_F_MANAGETEMPADDR | +		     IFA_F_NOPREFIXROUTE;  	ifa = ipv6_get_ifaddr(net, pfx, dev, 1);  	if (ifa == NULL) {  		/*  		 * It would be best to check for !NLM_F_CREATE here but -		 * userspace alreay relies on not having to provide this. +		 * userspace already relies on not having to provide this.  		 */  		return inet6_addr_add(net, ifm->ifa_index, pfx, peer_pfx,  				      ifm->ifa_prefixlen, ifa_flags, @@ -3739,7 +3898,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)  	return err;  } -static void put_ifaddrmsg(struct nlmsghdr *nlh, u8 prefixlen, u8 flags, +static void put_ifaddrmsg(struct nlmsghdr *nlh, u8 prefixlen, u32 flags,  			  u8 scope, int ifindex)  {  	struct ifaddrmsg *ifm; @@ -3782,7 +3941,8 @@ static inline int inet6_ifaddr_msgsize(void)  	return NLMSG_ALIGN(sizeof(struct ifaddrmsg))  	       + nla_total_size(16) /* IFA_LOCAL */  	       + nla_total_size(16) /* IFA_ADDRESS */ -	       + nla_total_size(sizeof(struct ifa_cacheinfo)); +	       + nla_total_size(sizeof(struct ifa_cacheinfo)) +	       + nla_total_size(4)  /* IFA_FLAGS */;  }  static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa, @@ -3798,7 +3958,8 @@ static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa,  	put_ifaddrmsg(nlh, ifa->prefix_len, ifa->flags, rt_scope(ifa->scope),  		      ifa->idev->dev->ifindex); -	if (!(ifa->flags&IFA_F_PERMANENT)) { +	if (!((ifa->flags&IFA_F_PERMANENT) && +	      (ifa->prefered_lft == INFINITY_LIFE_TIME))) {  		preferred = ifa->prefered_lft;  		valid = ifa->valid_lft;  		if (preferred != INFINITY_LIFE_TIME) { @@ -3830,6 +3991,9 @@ static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa,  	if (put_cacheinfo(skb, ifa->cstamp, ifa->tstamp, preferred, valid) < 0)  		goto error; +	if (nla_put_u32(skb, IFA_FLAGS, ifa->flags) < 0) +		goto error; +  	return nlmsg_end(skb, nlh);  error: @@ -4128,13 +4292,11 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,  		jiffies_to_msecs(cnf->mldv1_unsolicited_report_interval);  	array[DEVCONF_MLDV2_UNSOLICITED_REPORT_INTERVAL] =  		jiffies_to_msecs(cnf->mldv2_unsolicited_report_interval); -#ifdef CONFIG_IPV6_PRIVACY  	array[DEVCONF_USE_TEMPADDR] = cnf->use_tempaddr;  	array[DEVCONF_TEMP_VALID_LFT] = cnf->temp_valid_lft;  	array[DEVCONF_TEMP_PREFERED_LFT] = cnf->temp_prefered_lft;  	array[DEVCONF_REGEN_MAX_RETRY] = cnf->regen_max_retry;  	array[DEVCONF_MAX_DESYNC_FACTOR] = cnf->max_desync_factor; -#endif  	array[DEVCONF_MAX_ADDRESSES] = cnf->max_addresses;  	array[DEVCONF_ACCEPT_RA_DEFRTR] = cnf->accept_ra_defrtr;  	array[DEVCONF_ACCEPT_RA_PINFO] = cnf->accept_ra_pinfo; @@ -4196,7 +4358,7 @@ static inline void __snmp6_fill_statsdev(u64 *stats, atomic_long_t *mib,  	memset(&stats[items], 0, pad);  } -static inline void __snmp6_fill_stats64(u64 *stats, void __percpu **mib, +static inline void __snmp6_fill_stats64(u64 *stats, void __percpu *mib,  				      int items, int bytes, size_t syncpoff)  {  	int i; @@ -4216,7 +4378,7 @@ static void snmp6_fill_stats(u64 *stats, struct inet6_dev *idev, int attrtype,  {  	switch (attrtype) {  	case IFLA_INET6_STATS: -		__snmp6_fill_stats64(stats, (void __percpu **)idev->stats.ipv6, +		__snmp6_fill_stats64(stats, idev->stats.ipv6,  				     IPSTATS_MIB_MAX, bytes, offsetof(struct ipstats_mib, syncp));  		break;  	case IFLA_INET6_ICMP6STATS: @@ -4235,7 +4397,7 @@ static int inet6_fill_ifla6_attrs(struct sk_buff *skb, struct inet6_dev *idev)  	ci.max_reasm_len = IPV6_MAXPLEN;  	ci.tstamp = cstamp_delta(idev->tstamp);  	ci.reachable_time = jiffies_to_msecs(idev->nd_parms->reachable_time); -	ci.retrans_time = jiffies_to_msecs(idev->nd_parms->retrans_time); +	ci.retrans_time = jiffies_to_msecs(NEIGH_VAR(idev->nd_parms, RETRANS_TIME));  	if (nla_put(skb, IFLA_INET6_CACHEINFO, sizeof(ci), &ci))  		goto nla_put_failure;  	nla = nla_reserve(skb, IFLA_INET6_CONF, DEVCONF_MAX * sizeof(s32)); @@ -4296,6 +4458,8 @@ static int inet6_set_iftoken(struct inet6_dev *idev, struct in6_addr *token)  	bool update_rs = false;  	struct in6_addr ll_addr; +	ASSERT_RTNL(); +  	if (token == NULL)  		return -EINVAL;  	if (ipv6_addr_any(token)) @@ -4344,7 +4508,7 @@ static int inet6_set_iftoken(struct inet6_dev *idev, struct in6_addr *token)  	}  	write_unlock_bh(&idev->lock); -	addrconf_verify(0); +	addrconf_verify_rtnl();  	return 0;  } @@ -4542,29 +4706,17 @@ errout:  		rtnl_set_sk_err(net, RTNLGRP_IPV6_PREFIX, err);  } -static void update_valid_ll_addr_cnt(struct inet6_ifaddr *ifp, int count) -{ -	write_lock_bh(&ifp->idev->lock); -	spin_lock(&ifp->lock); -	if (((ifp->flags & (IFA_F_PERMANENT|IFA_F_TENTATIVE|IFA_F_OPTIMISTIC| -			    IFA_F_DADFAILED)) == IFA_F_PERMANENT) && -	    (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL)) -		ifp->idev->valid_ll_addr_cnt += count; -	WARN_ON(ifp->idev->valid_ll_addr_cnt < 0); -	spin_unlock(&ifp->lock); -	write_unlock_bh(&ifp->idev->lock); -} -  static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)  {  	struct net *net = dev_net(ifp->idev->dev); +	if (event) +		ASSERT_RTNL(); +  	inet6_ifa_notify(event ? : RTM_NEWADDR, ifp);  	switch (event) {  	case RTM_NEWADDR: -		update_valid_ll_addr_cnt(ifp, 1); -  		/*  		 * If the address was optimistic  		 * we inserted the route at the start of @@ -4580,8 +4732,6 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)  					      ifp->idev->dev, 0, 0);  		break;  	case RTM_DELADDR: -		update_valid_ll_addr_cnt(ifp, -1); -  		if (ifp->idev->cnf.forwarding)  			addrconf_leave_anycast(ifp);  		addrconf_leave_solict(ifp->idev, &ifp->addr); @@ -4728,6 +4878,46 @@ int addrconf_sysctl_disable(struct ctl_table *ctl, int write,  	return ret;  } +static +int addrconf_sysctl_proxy_ndp(struct ctl_table *ctl, int write, +			      void __user *buffer, size_t *lenp, loff_t *ppos) +{ +	int *valp = ctl->data; +	int ret; +	int old, new; + +	old = *valp; +	ret = proc_dointvec(ctl, write, buffer, lenp, ppos); +	new = *valp; + +	if (write && old != new) { +		struct net *net = ctl->extra2; + +		if (!rtnl_trylock()) +			return restart_syscall(); + +		if (valp == &net->ipv6.devconf_dflt->proxy_ndp) +			inet6_netconf_notify_devconf(net, NETCONFA_PROXY_NEIGH, +						     NETCONFA_IFINDEX_DEFAULT, +						     net->ipv6.devconf_dflt); +		else if (valp == &net->ipv6.devconf_all->proxy_ndp) +			inet6_netconf_notify_devconf(net, NETCONFA_PROXY_NEIGH, +						     NETCONFA_IFINDEX_ALL, +						     net->ipv6.devconf_all); +		else { +			struct inet6_dev *idev = ctl->extra1; + +			inet6_netconf_notify_devconf(net, NETCONFA_PROXY_NEIGH, +						     idev->dev->ifindex, +						     &idev->cnf); +		} +		rtnl_unlock(); +	} + +	return ret; +} + +  static struct addrconf_sysctl_table  {  	struct ctl_table_header *sysctl_header; @@ -4828,7 +5018,6 @@ static struct addrconf_sysctl_table  			.mode		= 0644,  			.proc_handler	= proc_dointvec_ms_jiffies,  		}, -#ifdef CONFIG_IPV6_PRIVACY  		{  			.procname	= "use_tempaddr",  			.data		= &ipv6_devconf.use_tempaddr, @@ -4864,7 +5053,6 @@ static struct addrconf_sysctl_table  			.mode		= 0644,  			.proc_handler	= proc_dointvec,  		}, -#endif  		{  			.procname	= "max_addresses",  			.data		= &ipv6_devconf.max_addresses, @@ -4916,7 +5104,7 @@ static struct addrconf_sysctl_table  			.data		= &ipv6_devconf.proxy_ndp,  			.maxlen		= sizeof(int),  			.mode		= 0644, -			.proc_handler	= proc_dointvec, +			.proc_handler	= addrconf_sysctl_proxy_ndp,  		},  		{  			.procname	= "accept_source_route", @@ -5032,7 +5220,7 @@ static void __addrconf_sysctl_unregister(struct ipv6_devconf *p)  static void addrconf_sysctl_register(struct inet6_dev *idev)  { -	neigh_sysctl_register(idev->dev, idev->nd_parms, "ipv6", +	neigh_sysctl_register(idev->dev, idev->nd_parms,  			      &ndisc_ifinfo_sysctl_change);  	__addrconf_sysctl_register(dev_net(idev->dev), idev->dev->name,  					idev, &idev->cnf); @@ -5133,6 +5321,12 @@ int __init addrconf_init(void)  	if (err < 0)  		goto out_addrlabel; +	addrconf_wq = create_workqueue("ipv6_addrconf"); +	if (!addrconf_wq) { +		err = -ENOMEM; +		goto out_nowq; +	} +  	/* The addrconf netdev notifier requires that loopback_dev  	 * has it's ipv6 private information allocated and setup  	 * before it can bring up and give link-local addresses @@ -5163,11 +5357,9 @@ int __init addrconf_init(void)  	register_netdevice_notifier(&ipv6_dev_notf); -	addrconf_verify(0); +	addrconf_verify(); -	err = rtnl_af_register(&inet6_ops); -	if (err < 0) -		goto errout_af; +	rtnl_af_register(&inet6_ops);  	err = __rtnl_register(PF_INET6, RTM_GETLINK, NULL, inet6_dump_ifinfo,  			      NULL); @@ -5191,9 +5383,10 @@ int __init addrconf_init(void)  	return 0;  errout:  	rtnl_af_unregister(&inet6_ops); -errout_af:  	unregister_netdevice_notifier(&ipv6_dev_notf);  errlo: +	destroy_workqueue(addrconf_wq); +out_nowq:  	unregister_pernet_subsys(&addrconf_ops);  out_addrlabel:  	ipv6_addr_label_cleanup(); @@ -5229,7 +5422,8 @@ void addrconf_cleanup(void)  	for (i = 0; i < IN6_ADDR_HSIZE; i++)  		WARN_ON(!hlist_empty(&inet6_addr_lst[i]));  	spin_unlock_bh(&addrconf_hash_lock); - -	del_timer(&addr_chk_timer); +	cancel_delayed_work(&addr_chk_work);  	rtnl_unlock(); + +	destroy_workqueue(addrconf_wq);  }  | 
