aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorstephen hemminger <shemminger@vyatta.com>2010-03-02 08:32:46 -0500
committerDavid S. Miller <davem@davemloft.net>2010-03-04 03:39:33 -0500
commit84e8b803f1e16f3a2b8b80f80a63fa2f2f8a9be6 (patch)
treef7e5aec42f50c6e4751751cf422c7bf16bff69ce
parent5b2a19539c5f59c5a038d213ede723f0245d97cf (diff)
IPv6: addrconf notify when address is unavailable
My recent change in net-next to retain permanent addresses caused regression. Device refcount would not go to zero when device was unregistered because left over anycast reference would hold ipv6 dev reference which would hold device references... The correct procedure is to call notify chain when address is no longer available for use. When interface comes back DAD timer will notify back that address is available. Also, link local addresses should be purged when interface is brought down. The address might be changed. Signed-off-by: Stephen Hemminger <shemminger@vyatta.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/ipv6/addrconf.c46
1 files changed, 29 insertions, 17 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 5f582f385abb..7a4bf7671285 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -2649,11 +2649,11 @@ static int addrconf_ifdown(struct net_device *dev, int how)
2649 write_lock_bh(&addrconf_hash_lock); 2649 write_lock_bh(&addrconf_hash_lock);
2650 while ((ifa = *bifa) != NULL) { 2650 while ((ifa = *bifa) != NULL) {
2651 if (ifa->idev == idev && 2651 if (ifa->idev == idev &&
2652 (how || !(ifa->flags&IFA_F_PERMANENT))) { 2652 (how || !(ifa->flags&IFA_F_PERMANENT) ||
2653 ipv6_addr_type(&ifa->addr) & IPV6_ADDR_LINKLOCAL)) {
2653 *bifa = ifa->lst_next; 2654 *bifa = ifa->lst_next;
2654 ifa->lst_next = NULL; 2655 ifa->lst_next = NULL;
2655 addrconf_del_timer(ifa); 2656 __in6_ifa_put(ifa);
2656 in6_ifa_put(ifa);
2657 continue; 2657 continue;
2658 } 2658 }
2659 bifa = &ifa->lst_next; 2659 bifa = &ifa->lst_next;
@@ -2691,28 +2691,40 @@ static int addrconf_ifdown(struct net_device *dev, int how)
2691#endif 2691#endif
2692 bifa = &idev->addr_list; 2692 bifa = &idev->addr_list;
2693 while ((ifa = *bifa) != NULL) { 2693 while ((ifa = *bifa) != NULL) {
2694 if (how == 0 && (ifa->flags&IFA_F_PERMANENT)) { 2694 addrconf_del_timer(ifa);
2695 /* Retain permanent address on admin down */ 2695
2696 /* If just doing link down, and address is permanent
2697 and not link-local, then retain it. */
2698 if (how == 0 &&
2699 (ifa->flags&IFA_F_PERMANENT) &&
2700 !(ipv6_addr_type(&ifa->addr) & IPV6_ADDR_LINKLOCAL)) {
2696 bifa = &ifa->if_next; 2701 bifa = &ifa->if_next;
2697 2702
2698 /* Restart DAD if needed when link comes back up */ 2703 /* If not doing DAD on this address, just keep it. */
2699 if ( !((dev->flags&(IFF_NOARP|IFF_LOOPBACK)) || 2704 if ((dev->flags&(IFF_NOARP|IFF_LOOPBACK)) ||
2700 idev->cnf.accept_dad <= 0 || 2705 idev->cnf.accept_dad <= 0 ||
2701 (ifa->flags & IFA_F_NODAD))) 2706 (ifa->flags & IFA_F_NODAD))
2702 ifa->flags |= IFA_F_TENTATIVE; 2707 continue;
2708
2709 /* If it was tentative already, no need to notify */
2710 if (ifa->flags & IFA_F_TENTATIVE)
2711 continue;
2712
2713 /* Flag it for later restoration when link comes up */
2714 ifa->flags |= IFA_F_TENTATIVE;
2715 in6_ifa_hold(ifa);
2703 } else { 2716 } else {
2704 *bifa = ifa->if_next; 2717 *bifa = ifa->if_next;
2705 ifa->if_next = NULL; 2718 ifa->if_next = NULL;
2706
2707 ifa->dead = 1; 2719 ifa->dead = 1;
2708 write_unlock_bh(&idev->lock); 2720 }
2721 write_unlock_bh(&idev->lock);
2709 2722
2710 __ipv6_ifa_notify(RTM_DELADDR, ifa); 2723 __ipv6_ifa_notify(RTM_DELADDR, ifa);
2711 atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifa); 2724 atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifa);
2712 in6_ifa_put(ifa); 2725 in6_ifa_put(ifa);
2713 2726
2714 write_lock_bh(&idev->lock); 2727 write_lock_bh(&idev->lock);
2715 }
2716 } 2728 }
2717 write_unlock_bh(&idev->lock); 2729 write_unlock_bh(&idev->lock);
2718 2730