net neigh: Decouple per interface neighbour table controls from binary sysctls
[linux-2.6-block.git] / net / ipv6 / addrconf.c
index de7a194a64ab4fa0cf864d1fe3f83a3974e3b727..c79cbff543700a9b7dae91a91bac55fbbbb458e8 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);
 
@@ -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);
@@ -3027,14 +3041,14 @@ static const struct file_operations if6_fops = {
        .release        = seq_release_net,
 };
 
-static int if6_proc_net_init(struct net *net)
+static int __net_init if6_proc_net_init(struct net *net)
 {
        if (!proc_net_fops_create(net, "if_inet6", S_IRUGO, &if6_fops))
                return -ENOMEM;
        return 0;
 }
 
-static void if6_proc_net_exit(struct net *net)
+static void __net_exit if6_proc_net_exit(struct net *net)
 {
        proc_net_remove(net, "if_inet6");
 }
@@ -4402,8 +4416,7 @@ static void __addrconf_sysctl_unregister(struct ipv6_devconf *p)
 
 static void addrconf_sysctl_register(struct inet6_dev *idev)
 {
-       neigh_sysctl_register(idev->dev, idev->nd_parms, NET_IPV6,
-                             NET_IPV6_NEIGH, "ipv6",
+       neigh_sysctl_register(idev->dev, idev->nd_parms, "ipv6",
                              &ndisc_ifinfo_sysctl_change);
        __addrconf_sysctl_register(dev_net(idev->dev), idev->dev->name,
                                        idev, &idev->cnf);
@@ -4418,7 +4431,7 @@ static void addrconf_sysctl_unregister(struct inet6_dev *idev)
 
 #endif
 
-static int addrconf_init_net(struct net *net)
+static int __net_init addrconf_init_net(struct net *net)
 {
        int err;
        struct ipv6_devconf *all, *dflt;
@@ -4467,7 +4480,7 @@ err_alloc_all:
        return err;
 }
 
-static void addrconf_exit_net(struct net *net)
+static void __net_exit addrconf_exit_net(struct net *net)
 {
 #ifdef CONFIG_SYSCTL
        __addrconf_sysctl_unregister(net->ipv6.devconf_dflt);