diff options
Diffstat (limited to 'net/ipv6/addrconf.c')
-rw-r--r-- | net/ipv6/addrconf.c | 56 |
1 files changed, 35 insertions, 21 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 1593289155ff..764ad37ca070 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -2646,7 +2646,8 @@ static int addrconf_ifdown(struct net_device *dev, int how) write_lock_bh(&addrconf_hash_lock); while ((ifa = *bifa) != NULL) { - if (ifa->idev == idev) { + if (ifa->idev == idev && + (how || !(ifa->flags&IFA_F_PERMANENT))) { *bifa = ifa->lst_next; ifa->lst_next = NULL; addrconf_del_timer(ifa); @@ -2686,18 +2687,30 @@ static int addrconf_ifdown(struct net_device *dev, int how) write_lock_bh(&idev->lock); } #endif - while ((ifa = idev->addr_list) != NULL) { - idev->addr_list = ifa->if_next; - ifa->if_next = NULL; - ifa->dead = 1; - addrconf_del_timer(ifa); - write_unlock_bh(&idev->lock); + bifa = &idev->addr_list; + while ((ifa = *bifa) != NULL) { + if (how == 0 && (ifa->flags&IFA_F_PERMANENT)) { + /* Retain permanent address on admin down */ + bifa = &ifa->if_next; + + /* Restart DAD if needed when link comes back up */ + if ( !((dev->flags&(IFF_NOARP|IFF_LOOPBACK)) || + idev->cnf.accept_dad <= 0 || + (ifa->flags & IFA_F_NODAD))) + ifa->flags |= IFA_F_TENTATIVE; + } else { + *bifa = ifa->if_next; + ifa->if_next = NULL; - __ipv6_ifa_notify(RTM_DELADDR, ifa); - atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifa); - in6_ifa_put(ifa); + ifa->dead = 1; + write_unlock_bh(&idev->lock); - write_lock_bh(&idev->lock); + __ipv6_ifa_notify(RTM_DELADDR, ifa); + atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifa); + in6_ifa_put(ifa); + + write_lock_bh(&idev->lock); + } } write_unlock_bh(&idev->lock); @@ -2789,14 +2802,14 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags) read_lock_bh(&idev->lock); if (ifp->dead) goto out; - spin_lock_bh(&ifp->lock); + spin_lock(&ifp->lock); if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) || idev->cnf.accept_dad < 1 || !(ifp->flags&IFA_F_TENTATIVE) || ifp->flags & IFA_F_NODAD) { ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED); - spin_unlock_bh(&ifp->lock); + spin_unlock(&ifp->lock); read_unlock_bh(&idev->lock); addrconf_dad_completed(ifp); @@ -2804,7 +2817,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags) } if (!(idev->if_flags & IF_READY)) { - spin_unlock_bh(&ifp->lock); + spin_unlock(&ifp->lock); read_unlock_bh(&idev->lock); /* * If the device is not ready: @@ -2824,7 +2837,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags) ip6_ins_rt(ifp->rt); addrconf_dad_kick(ifp); - spin_unlock_bh(&ifp->lock); + spin_unlock(&ifp->lock); out: read_unlock_bh(&idev->lock); } @@ -2840,14 +2853,15 @@ static void addrconf_dad_timer(unsigned long data) read_unlock_bh(&idev->lock); goto out; } - spin_lock_bh(&ifp->lock); + + spin_lock(&ifp->lock); if (ifp->probes == 0) { /* * DAD was successful */ ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED); - spin_unlock_bh(&ifp->lock); + spin_unlock(&ifp->lock); read_unlock_bh(&idev->lock); addrconf_dad_completed(ifp); @@ -2857,7 +2871,7 @@ static void addrconf_dad_timer(unsigned long data) ifp->probes--; addrconf_mod_timer(ifp, AC_DAD, ifp->idev->nd_parms->retrans_time); - spin_unlock_bh(&ifp->lock); + spin_unlock(&ifp->lock); read_unlock_bh(&idev->lock); /* send a neighbour solicitation for our addr */ @@ -2905,12 +2919,12 @@ static void addrconf_dad_run(struct inet6_dev *idev) { read_lock_bh(&idev->lock); for (ifp = idev->addr_list; ifp; ifp = ifp->if_next) { - spin_lock_bh(&ifp->lock); + spin_lock(&ifp->lock); if (!(ifp->flags & IFA_F_TENTATIVE)) { - spin_unlock_bh(&ifp->lock); + spin_unlock(&ifp->lock); continue; } - spin_unlock_bh(&ifp->lock); + spin_unlock(&ifp->lock); addrconf_dad_kick(ifp); } read_unlock_bh(&idev->lock); |