summaryrefslogtreecommitdiff
path: root/net/ipv6/addrconf.c
diff options
context:
space:
mode:
authorstephen hemminger <shemminger@vyatta.com>2010-02-08 22:48:05 +0300
committerDavid S. Miller <davem@davemloft.net>2010-02-12 23:28:01 +0300
commitdc2b99f71ef477a31020511876ab4403fb7c4420 (patch)
treecb30c11a7eb13d2ad004ee168135282aac434791 /net/ipv6/addrconf.c
parentc0ad98453f23b98f73a1f1be2a75303a6c0dee4c (diff)
downloadlinux-dc2b99f71ef477a31020511876ab4403fb7c4420.tar.xz
IPv6: keep permanent addresses on admin down
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>
Diffstat (limited to 'net/ipv6/addrconf.c')
-rw-r--r--net/ipv6/addrconf.c35
1 files changed, 24 insertions, 11 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 1593289155ff..b0e1430b64f1 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);