diff options
Diffstat (limited to 'net/ipv6/addrconf.c')
| -rw-r--r-- | net/ipv6/addrconf.c | 341 | 
1 files changed, 209 insertions, 132 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 4ab4c38958c6..da4241c8c7da 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -253,37 +253,32 @@ static inline bool addrconf_qdisc_ok(const struct net_device *dev)  	return !qdisc_tx_is_noop(dev);  } -static void addrconf_del_timer(struct inet6_ifaddr *ifp) +static void addrconf_del_rs_timer(struct inet6_dev *idev)  { -	if (del_timer(&ifp->timer)) +	if (del_timer(&idev->rs_timer)) +		__in6_dev_put(idev); +} + +static void addrconf_del_dad_timer(struct inet6_ifaddr *ifp) +{ +	if (del_timer(&ifp->dad_timer))  		__in6_ifa_put(ifp);  } -enum addrconf_timer_t { -	AC_NONE, -	AC_DAD, -	AC_RS, -}; +static void addrconf_mod_rs_timer(struct inet6_dev *idev, +				  unsigned long when) +{ +	if (!timer_pending(&idev->rs_timer)) +		in6_dev_hold(idev); +	mod_timer(&idev->rs_timer, jiffies + when); +} -static void addrconf_mod_timer(struct inet6_ifaddr *ifp, -			       enum addrconf_timer_t what, -			       unsigned long when) +static void addrconf_mod_dad_timer(struct inet6_ifaddr *ifp, +				   unsigned long when)  { -	if (!del_timer(&ifp->timer)) +	if (!timer_pending(&ifp->dad_timer))  		in6_ifa_hold(ifp); - -	switch (what) { -	case AC_DAD: -		ifp->timer.function = addrconf_dad_timer; -		break; -	case AC_RS: -		ifp->timer.function = addrconf_rs_timer; -		break; -	default: -		break; -	} -	ifp->timer.expires = jiffies + when; -	add_timer(&ifp->timer); +	mod_timer(&ifp->dad_timer, jiffies + when);  }  static int snmp6_alloc_dev(struct inet6_dev *idev) @@ -326,6 +321,7 @@ void in6_dev_finish_destroy(struct inet6_dev *idev)  	WARN_ON(!list_empty(&idev->addr_list));  	WARN_ON(idev->mc_list != NULL); +	WARN_ON(timer_pending(&idev->rs_timer));  #ifdef NET_REFCNT_DEBUG  	pr_debug("%s: %s\n", __func__, dev ? dev->name : "NIL"); @@ -357,7 +353,8 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)  	rwlock_init(&ndev->lock);  	ndev->dev = dev;  	INIT_LIST_HEAD(&ndev->addr_list); - +	setup_timer(&ndev->rs_timer, addrconf_rs_timer, +		    (unsigned long)ndev);  	memcpy(&ndev->cnf, dev_net(dev)->ipv6.devconf_dflt, sizeof(ndev->cnf));  	ndev->cnf.mtu6 = dev->mtu;  	ndev->cnf.sysctl = NULL; @@ -776,7 +773,7 @@ void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp)  	in6_dev_put(ifp->idev); -	if (del_timer(&ifp->timer)) +	if (del_timer(&ifp->dad_timer))  		pr_notice("Timer is still running, when freeing ifa=%p\n", ifp);  	if (ifp->state != INET6_IFADDR_STATE_DEAD) { @@ -816,8 +813,9 @@ static u32 inet6_addr_hash(const struct in6_addr *addr)  /* On success it returns ifp with increased reference count */  static struct inet6_ifaddr * -ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen, -	      int scope, u32 flags) +ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, +	      const struct in6_addr *peer_addr, int pfxlen, +	      int scope, u32 flags, u32 valid_lft, u32 prefered_lft)  {  	struct inet6_ifaddr *ifa = NULL;  	struct rt6_info *rt; @@ -866,15 +864,19 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,  	}  	ifa->addr = *addr; +	if (peer_addr) +		ifa->peer_addr = *peer_addr;  	spin_lock_init(&ifa->lock);  	spin_lock_init(&ifa->state_lock); -	init_timer(&ifa->timer); +	setup_timer(&ifa->dad_timer, addrconf_dad_timer, +		    (unsigned long)ifa);  	INIT_HLIST_NODE(&ifa->addr_lst); -	ifa->timer.data = (unsigned long) ifa;  	ifa->scope = scope;  	ifa->prefix_len = pfxlen;  	ifa->flags = flags | IFA_F_TENTATIVE; +	ifa->valid_lft = valid_lft; +	ifa->prefered_lft = prefered_lft;  	ifa->cstamp = ifa->tstamp = jiffies;  	ifa->tokenized = false; @@ -994,7 +996,7 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)  	}  	write_unlock_bh(&idev->lock); -	addrconf_del_timer(ifp); +	addrconf_del_dad_timer(ifp);  	ipv6_ifa_notify(RTM_DELADDR, ifp); @@ -1126,9 +1128,9 @@ retry:  	ift = !max_addresses ||  	      ipv6_count_addresses(idev) < max_addresses ? -		ipv6_add_addr(idev, &addr, tmp_plen, -			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, -			      addr_flags) : NULL; +		ipv6_add_addr(idev, &addr, NULL, tmp_plen, +			      ipv6_addr_scope(&addr), addr_flags, +			      tmp_valid_lft, tmp_prefered_lft) : NULL;  	if (IS_ERR_OR_NULL(ift)) {  		in6_ifa_put(ifp);  		in6_dev_put(idev); @@ -1140,8 +1142,6 @@ retry:  	spin_lock_bh(&ift->lock);  	ift->ifpub = ifp; -	ift->valid_lft = tmp_valid_lft; -	ift->prefered_lft = tmp_prefered_lft;  	ift->cstamp = now;  	ift->tstamp = tmp_tstamp;  	spin_unlock_bh(&ift->lock); @@ -1448,6 +1448,23 @@ try_nextdev:  }  EXPORT_SYMBOL(ipv6_dev_get_saddr); +int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr, +		      unsigned char banned_flags) +{ +	struct inet6_ifaddr *ifp; +	int err = -EADDRNOTAVAIL; + +	list_for_each_entry(ifp, &idev->addr_list, if_list) { +		if (ifp->scope == IFA_LINK && +		    !(ifp->flags & banned_flags)) { +			*addr = ifp->addr; +			err = 0; +			break; +		} +	} +	return err; +} +  int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr,  		    unsigned char banned_flags)  { @@ -1457,17 +1474,8 @@ int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr,  	rcu_read_lock();  	idev = __in6_dev_get(dev);  	if (idev) { -		struct inet6_ifaddr *ifp; -  		read_lock_bh(&idev->lock); -		list_for_each_entry(ifp, &idev->addr_list, if_list) { -			if (ifp->scope == IFA_LINK && -			    !(ifp->flags & banned_flags)) { -				*addr = ifp->addr; -				err = 0; -				break; -			} -		} +		err = __ipv6_get_lladdr(idev, addr, banned_flags);  		read_unlock_bh(&idev->lock);  	}  	rcu_read_unlock(); @@ -1581,7 +1589,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_timer(ifp); +		addrconf_del_dad_timer(ifp);  		ifp->flags |= IFA_F_TENTATIVE;  		if (dad_failed)  			ifp->flags |= IFA_F_DADFAILED; @@ -2175,16 +2183,19 @@ ok:  			 */  			if (!max_addresses ||  			    ipv6_count_addresses(in6_dev) < max_addresses) -				ifp = ipv6_add_addr(in6_dev, &addr, pinfo->prefix_len, +				ifp = ipv6_add_addr(in6_dev, &addr, NULL, +						    pinfo->prefix_len,  						    addr_type&IPV6_ADDR_SCOPE_MASK, -						    addr_flags); +						    addr_flags, valid_lft, +						    prefered_lft);  			if (IS_ERR_OR_NULL(ifp)) {  				in6_dev_put(in6_dev);  				return;  			} -			update_lft = create = 1; +			update_lft = 0; +			create = 1;  			ifp->cstamp = jiffies;  			ifp->tokenized = tokenized;  			addrconf_dad_start(ifp); @@ -2205,7 +2216,7 @@ ok:  				stored_lft = ifp->valid_lft - (now - ifp->tstamp) / HZ;  			else  				stored_lft = 0; -			if (!update_lft && stored_lft) { +			if (!update_lft && !create && stored_lft) {  				if (valid_lft > MIN_VALID_LIFETIME ||  				    valid_lft > stored_lft)  					update_lft = 1; @@ -2402,6 +2413,7 @@ err_exit:   *	Manual configuration of address on an interface   */  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)  { @@ -2450,15 +2462,10 @@ static int inet6_addr_add(struct net *net, int ifindex, const struct in6_addr *p  		prefered_lft = timeout;  	} -	ifp = ipv6_add_addr(idev, pfx, plen, scope, ifa_flags); +	ifp = ipv6_add_addr(idev, pfx, peer_pfx, plen, scope, ifa_flags, +			    valid_lft, prefered_lft);  	if (!IS_ERR(ifp)) { -		spin_lock_bh(&ifp->lock); -		ifp->valid_lft = valid_lft; -		ifp->prefered_lft = prefered_lft; -		ifp->tstamp = jiffies; -		spin_unlock_bh(&ifp->lock); -  		addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev,  				      expires, flags);  		/* @@ -2500,12 +2507,6 @@ static int inet6_addr_del(struct net *net, int ifindex, const struct in6_addr *p  			read_unlock_bh(&idev->lock);  			ipv6_del_addr(ifp); - -			/* If the last address is deleted administratively, -			   disable IPv6 on this interface. -			 */ -			if (list_empty(&idev->addr_list)) -				addrconf_ifdown(idev->dev, 1);  			return 0;  		}  	} @@ -2526,7 +2527,7 @@ int addrconf_add_ifaddr(struct net *net, void __user *arg)  		return -EFAULT;  	rtnl_lock(); -	err = inet6_addr_add(net, ireq.ifr6_ifindex, &ireq.ifr6_addr, +	err = inet6_addr_add(net, ireq.ifr6_ifindex, &ireq.ifr6_addr, NULL,  			     ireq.ifr6_prefixlen, IFA_F_PERMANENT,  			     INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);  	rtnl_unlock(); @@ -2556,7 +2557,8 @@ static void add_addr(struct inet6_dev *idev, const struct in6_addr *addr,  {  	struct inet6_ifaddr *ifp; -	ifp = ipv6_add_addr(idev, addr, plen, scope, IFA_F_PERMANENT); +	ifp = ipv6_add_addr(idev, addr, NULL, plen, +			    scope, IFA_F_PERMANENT, 0, 0);  	if (!IS_ERR(ifp)) {  		spin_lock_bh(&ifp->lock);  		ifp->flags &= ~IFA_F_TENTATIVE; @@ -2682,7 +2684,7 @@ static void addrconf_add_linklocal(struct inet6_dev *idev, const struct in6_addr  #endif -	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, addr_flags); +	ifp = ipv6_add_addr(idev, addr, NULL, 64, IFA_LINK, addr_flags, 0, 0);  	if (!IS_ERR(ifp)) {  		addrconf_prefix_route(&ifp->addr, ifp->prefix_len, idev->dev, 0, 0);  		addrconf_dad_start(ifp); @@ -2761,8 +2763,6 @@ static void addrconf_gre_config(struct net_device *dev)  	struct inet6_dev *idev;  	struct in6_addr addr; -	pr_info("%s(%s)\n", __func__, dev->name); -  	ASSERT_RTNL();  	if ((idev = ipv6_find_idev(dev)) == NULL) { @@ -2829,9 +2829,9 @@ static void addrconf_ip6_tnl_config(struct net_device *dev)  }  static int addrconf_notify(struct notifier_block *this, unsigned long event, -			   void *data) +			   void *ptr)  { -	struct net_device *dev = (struct net_device *) data; +	struct net_device *dev = netdev_notifier_info_to_dev(ptr);  	struct inet6_dev *idev = __in6_dev_get(dev);  	int run_pending = 0;  	int err; @@ -3039,7 +3039,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_timer(ifa); +				addrconf_del_dad_timer(ifa);  				goto restart;  			}  		} @@ -3048,6 +3048,8 @@ static int addrconf_ifdown(struct net_device *dev, int how)  	write_lock_bh(&idev->lock); +	addrconf_del_rs_timer(idev); +  	/* Step 2: clear flags for stateless addrconf */  	if (!how)  		idev->if_flags &= ~(IF_RS_SENT|IF_RA_RCVD|IF_READY); @@ -3077,7 +3079,7 @@ static int addrconf_ifdown(struct net_device *dev, int how)  	while (!list_empty(&idev->addr_list)) {  		ifa = list_first_entry(&idev->addr_list,  				       struct inet6_ifaddr, if_list); -		addrconf_del_timer(ifa); +		addrconf_del_dad_timer(ifa);  		list_del(&ifa->if_list); @@ -3119,10 +3121,10 @@ static int addrconf_ifdown(struct net_device *dev, int how)  static void addrconf_rs_timer(unsigned long data)  { -	struct inet6_ifaddr *ifp = (struct inet6_ifaddr *) data; -	struct inet6_dev *idev = ifp->idev; +	struct inet6_dev *idev = (struct inet6_dev *)data; +	struct in6_addr lladdr; -	read_lock(&idev->lock); +	write_lock(&idev->lock);  	if (idev->dead || !(idev->if_flags & IF_READY))  		goto out; @@ -3133,18 +3135,19 @@ static void addrconf_rs_timer(unsigned long data)  	if (idev->if_flags & IF_RA_RCVD)  		goto out; -	spin_lock(&ifp->lock); -	if (ifp->probes++ < idev->cnf.rtr_solicits) { -		/* The wait after the last probe can be shorter */ -		addrconf_mod_timer(ifp, AC_RS, -				   (ifp->probes == idev->cnf.rtr_solicits) ? -				   idev->cnf.rtr_solicit_delay : -				   idev->cnf.rtr_solicit_interval); -		spin_unlock(&ifp->lock); +	if (idev->rs_probes++ < idev->cnf.rtr_solicits) { +		if (!__ipv6_get_lladdr(idev, &lladdr, IFA_F_TENTATIVE)) +			ndisc_send_rs(idev->dev, &lladdr, +				      &in6addr_linklocal_allrouters); +		else +			goto out; -		ndisc_send_rs(idev->dev, &ifp->addr, &in6addr_linklocal_allrouters); +		/* The wait after the last probe can be shorter */ +		addrconf_mod_rs_timer(idev, (idev->rs_probes == +					     idev->cnf.rtr_solicits) ? +				      idev->cnf.rtr_solicit_delay : +				      idev->cnf.rtr_solicit_interval);  	} else { -		spin_unlock(&ifp->lock);  		/*  		 * Note: we do not support deprecated "all on-link"  		 * assumption any longer. @@ -3153,8 +3156,8 @@ static void addrconf_rs_timer(unsigned long data)  	}  out: -	read_unlock(&idev->lock); -	in6_ifa_put(ifp); +	write_unlock(&idev->lock); +	in6_dev_put(idev);  }  /* @@ -3170,8 +3173,8 @@ static void addrconf_dad_kick(struct inet6_ifaddr *ifp)  	else  		rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1); -	ifp->probes = idev->cnf.dad_transmits; -	addrconf_mod_timer(ifp, AC_DAD, rand_num); +	ifp->dad_probes = idev->cnf.dad_transmits; +	addrconf_mod_dad_timer(ifp, rand_num);  }  static void addrconf_dad_start(struct inet6_ifaddr *ifp) @@ -3232,40 +3235,40 @@ static void addrconf_dad_timer(unsigned long data)  	struct inet6_dev *idev = ifp->idev;  	struct in6_addr mcaddr; -	if (!ifp->probes && addrconf_dad_end(ifp)) +	if (!ifp->dad_probes && addrconf_dad_end(ifp))  		goto out; -	read_lock(&idev->lock); +	write_lock(&idev->lock);  	if (idev->dead || !(idev->if_flags & IF_READY)) { -		read_unlock(&idev->lock); +		write_unlock(&idev->lock);  		goto out;  	}  	spin_lock(&ifp->lock);  	if (ifp->state == INET6_IFADDR_STATE_DEAD) {  		spin_unlock(&ifp->lock); -		read_unlock(&idev->lock); +		write_unlock(&idev->lock);  		goto out;  	} -	if (ifp->probes == 0) { +	if (ifp->dad_probes == 0) {  		/*  		 * DAD was successful  		 */  		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED);  		spin_unlock(&ifp->lock); -		read_unlock(&idev->lock); +		write_unlock(&idev->lock);  		addrconf_dad_completed(ifp);  		goto out;  	} -	ifp->probes--; -	addrconf_mod_timer(ifp, AC_DAD, ifp->idev->nd_parms->retrans_time); +	ifp->dad_probes--; +	addrconf_mod_dad_timer(ifp, ifp->idev->nd_parms->retrans_time);  	spin_unlock(&ifp->lock); -	read_unlock(&idev->lock); +	write_unlock(&idev->lock);  	/* send a neighbour solicitation for our addr */  	addrconf_addr_solict_mult(&ifp->addr, &mcaddr); @@ -3277,6 +3280,10 @@ out:  static void addrconf_dad_completed(struct inet6_ifaddr *ifp)  {  	struct net_device *dev = ifp->idev->dev; +	struct in6_addr lladdr; +	bool send_rs, send_mld; + +	addrconf_del_dad_timer(ifp);  	/*  	 *	Configure the address for reception. Now it is valid. @@ -3288,22 +3295,41 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp)  	   router advertisements, start sending router solicitations.  	 */ -	if (ipv6_accept_ra(ifp->idev) && -	    ifp->idev->cnf.rtr_solicits > 0 && -	    (dev->flags&IFF_LOOPBACK) == 0 && -	    (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL)) { +	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_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. +	 * Resend with proper ll now. +	 */ +	if (send_mld) +		ipv6_mc_dad_complete(ifp->idev); + +	if (send_rs) {  		/*  		 *	If a host as already performed a random delay  		 *	[...] as part of DAD [...] there is no need  		 *	to delay again before sending the first RS  		 */ -		ndisc_send_rs(ifp->idev->dev, &ifp->addr, &in6addr_linklocal_allrouters); +		if (ipv6_get_lladdr(dev, &lladdr, IFA_F_TENTATIVE)) +			return; +		ndisc_send_rs(dev, &lladdr, &in6addr_linklocal_allrouters); -		spin_lock_bh(&ifp->lock); -		ifp->probes = 1; +		write_lock_bh(&ifp->idev->lock); +		spin_lock(&ifp->lock); +		ifp->idev->rs_probes = 1;  		ifp->idev->if_flags |= IF_RS_SENT; -		addrconf_mod_timer(ifp, AC_RS, ifp->idev->cnf.rtr_solicit_interval); -		spin_unlock_bh(&ifp->lock); +		addrconf_mod_rs_timer(ifp->idev, +				      ifp->idev->cnf.rtr_solicit_interval); +		spin_unlock(&ifp->lock); +		write_unlock_bh(&ifp->idev->lock);  	}  } @@ -3615,18 +3641,20 @@ restart:  	rcu_read_unlock_bh();  } -static struct in6_addr *extract_addr(struct nlattr *addr, struct nlattr *local) +static struct in6_addr *extract_addr(struct nlattr *addr, struct nlattr *local, +				     struct in6_addr **peer_pfx)  {  	struct in6_addr *pfx = NULL; +	*peer_pfx = NULL; +  	if (addr)  		pfx = nla_data(addr);  	if (local) {  		if (pfx && nla_memcmp(local, pfx, sizeof(*pfx))) -			pfx = NULL; -		else -			pfx = nla_data(local); +			*peer_pfx = pfx; +		pfx = nla_data(local);  	}  	return pfx; @@ -3644,7 +3672,7 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh)  	struct net *net = sock_net(skb->sk);  	struct ifaddrmsg *ifm;  	struct nlattr *tb[IFA_MAX+1]; -	struct in6_addr *pfx; +	struct in6_addr *pfx, *peer_pfx;  	int err;  	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy); @@ -3652,7 +3680,7 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh)  		return err;  	ifm = nlmsg_data(nlh); -	pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL]); +	pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer_pfx);  	if (pfx == NULL)  		return -EINVAL; @@ -3710,7 +3738,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)  	struct net *net = sock_net(skb->sk);  	struct ifaddrmsg *ifm;  	struct nlattr *tb[IFA_MAX+1]; -	struct in6_addr *pfx; +	struct in6_addr *pfx, *peer_pfx;  	struct inet6_ifaddr *ifa;  	struct net_device *dev;  	u32 valid_lft = INFINITY_LIFE_TIME, preferred_lft = INFINITY_LIFE_TIME; @@ -3722,7 +3750,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)  		return err;  	ifm = nlmsg_data(nlh); -	pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL]); +	pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer_pfx);  	if (pfx == NULL)  		return -EINVAL; @@ -3750,7 +3778,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)  		 * It would be best to check for !NLM_F_CREATE here but  		 * userspace alreay relies on not having to provide this.  		 */ -		return inet6_addr_add(net, ifm->ifa_index, pfx, +		return inet6_addr_add(net, ifm->ifa_index, pfx, peer_pfx,  				      ifm->ifa_prefixlen, ifa_flags,  				      preferred_lft, valid_lft);  	} @@ -3807,6 +3835,7 @@ static inline int rt_scope(int ifa_scope)  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));  } @@ -3845,13 +3874,22 @@ static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa,  		valid = INFINITY_LIFE_TIME;  	} -	if (nla_put(skb, IFA_ADDRESS, 16, &ifa->addr) < 0 || -	    put_cacheinfo(skb, ifa->cstamp, ifa->tstamp, preferred, valid) < 0) { -		nlmsg_cancel(skb, nlh); -		return -EMSGSIZE; -	} +	if (!ipv6_addr_any(&ifa->peer_addr)) { +		if (nla_put(skb, IFA_LOCAL, 16, &ifa->addr) < 0 || +		    nla_put(skb, IFA_ADDRESS, 16, &ifa->peer_addr) < 0) +			goto error; +	} else +		if (nla_put(skb, IFA_ADDRESS, 16, &ifa->addr) < 0) +			goto error; + +	if (put_cacheinfo(skb, ifa->cstamp, ifa->tstamp, preferred, valid) < 0) +		goto error;  	return nlmsg_end(skb, nlh); + +error: +	nlmsg_cancel(skb, nlh); +	return -EMSGSIZE;  }  static int inet6_fill_ifmcaddr(struct sk_buff *skb, struct ifmcaddr6 *ifmca, @@ -4051,7 +4089,7 @@ static int inet6_rtm_getaddr(struct sk_buff *in_skb, struct nlmsghdr *nlh)  	struct net *net = sock_net(in_skb->sk);  	struct ifaddrmsg *ifm;  	struct nlattr *tb[IFA_MAX+1]; -	struct in6_addr *addr = NULL; +	struct in6_addr *addr = NULL, *peer;  	struct net_device *dev = NULL;  	struct inet6_ifaddr *ifa;  	struct sk_buff *skb; @@ -4061,7 +4099,7 @@ static int inet6_rtm_getaddr(struct sk_buff *in_skb, struct nlmsghdr *nlh)  	if (err < 0)  		goto errout; -	addr = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL]); +	addr = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer);  	if (addr == NULL) {  		err = -EINVAL;  		goto errout; @@ -4339,8 +4377,11 @@ static int inet6_set_iftoken(struct inet6_dev *idev, struct in6_addr *token)  	write_lock_bh(&idev->lock); -	if (update_rs) +	if (update_rs) {  		idev->if_flags |= IF_RS_SENT; +		idev->rs_probes = 1; +		addrconf_mod_rs_timer(idev, idev->cnf.rtr_solicit_interval); +	}  	/* Well, that's kinda nasty ... */  	list_for_each_entry(ifp, &idev->addr_list, if_list) { @@ -4353,6 +4394,7 @@ static int inet6_set_iftoken(struct inet6_dev *idev, struct in6_addr *token)  	}  	write_unlock_bh(&idev->lock); +	addrconf_verify(0);  	return 0;  } @@ -4550,6 +4592,19 @@ 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); @@ -4558,6 +4613,8 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *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 @@ -4568,11 +4625,28 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)  			ip6_ins_rt(ifp->rt);  		if (ifp->idev->cnf.forwarding)  			addrconf_join_anycast(ifp); +		if (!ipv6_addr_any(&ifp->peer_addr)) +			addrconf_prefix_route(&ifp->peer_addr, 128, +					      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); +		if (!ipv6_addr_any(&ifp->peer_addr)) { +			struct rt6_info *rt; +			struct net_device *dev = ifp->idev->dev; + +			rt = rt6_lookup(dev_net(dev), &ifp->peer_addr, NULL, +					dev->ifindex, 1); +			if (rt) { +				dst_hold(&rt->dst); +				if (ip6_del_rt(rt)) +					dst_free(&rt->dst); +			} +		}  		dst_hold(&ifp->rt->dst);  		if (ip6_del_rt(ifp->rt)) @@ -4593,13 +4667,13 @@ static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)  #ifdef CONFIG_SYSCTL  static -int addrconf_sysctl_forward(ctl_table *ctl, int write, +int addrconf_sysctl_forward(struct ctl_table *ctl, int write,  			   void __user *buffer, size_t *lenp, loff_t *ppos)  {  	int *valp = ctl->data;  	int val = *valp;  	loff_t pos = *ppos; -	ctl_table lctl; +	struct ctl_table lctl;  	int ret;  	/* @@ -4620,13 +4694,16 @@ int addrconf_sysctl_forward(ctl_table *ctl, int write,  static void dev_disable_change(struct inet6_dev *idev)  { +	struct netdev_notifier_info info; +  	if (!idev || !idev->dev)  		return; +	netdev_notifier_info_init(&info, idev->dev);  	if (idev->cnf.disable_ipv6) -		addrconf_notify(NULL, NETDEV_DOWN, idev->dev); +		addrconf_notify(NULL, NETDEV_DOWN, &info);  	else -		addrconf_notify(NULL, NETDEV_UP, idev->dev); +		addrconf_notify(NULL, NETDEV_UP, &info);  }  static void addrconf_disable_change(struct net *net, __s32 newf) @@ -4675,13 +4752,13 @@ static int addrconf_disable_ipv6(struct ctl_table *table, int *p, int newf)  }  static -int addrconf_sysctl_disable(ctl_table *ctl, int write, +int addrconf_sysctl_disable(struct ctl_table *ctl, int write,  			    void __user *buffer, size_t *lenp, loff_t *ppos)  {  	int *valp = ctl->data;  	int val = *valp;  	loff_t pos = *ppos; -	ctl_table lctl; +	struct ctl_table lctl;  	int ret;  	/* @@ -4703,7 +4780,7 @@ int addrconf_sysctl_disable(ctl_table *ctl, int write,  static struct addrconf_sysctl_table  {  	struct ctl_table_header *sysctl_header; -	ctl_table addrconf_vars[DEVCONF_MAX+1]; +	struct ctl_table addrconf_vars[DEVCONF_MAX+1];  } addrconf_sysctl __read_mostly = {  	.sysctl_header = NULL,  	.addrconf_vars = {  | 
