diff options
Diffstat (limited to 'net/ipv6/addrconf.c')
-rw-r--r-- | net/ipv6/addrconf.c | 46 |
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 | ||