IPv6: keep permanent addresses on admin down
authorstephen hemminger <shemminger@vyatta.com>
Mon, 8 Feb 2010 19:48:05 +0000 (19:48 +0000)
committerDavid S. Miller <davem@davemloft.net>
Fri, 12 Feb 2010 20:28:01 +0000 (12:28 -0800)
Permanent IPV6 addresses should not be removed when the link is
set to admin down, only when device is removed.

When link is lost permanent addresses should be marked as tentative
so that when link comes back they are subject to duplicate address
detection (if DAD was enabled for that address).

Other routing systems keep manually configured IPv6 addresses
when link is set down.

Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv6/addrconf.c

index 1593289155ff7996b79b41bc3dcf484000369db3..b0e1430b64f1c3714aa332d3f98682dde3481eac 100644 (file)
@@ -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);