net: spread __net_init, __net_exit
[safe/jmp/linux-2.6] / net / ipv6 / addrconf.c
index 717584b..1593289 100644 (file)
@@ -40,6 +40,7 @@
 
 #include <linux/errno.h>
 #include <linux/types.h>
+#include <linux/kernel.h>
 #include <linux/socket.h>
 #include <linux/sockios.h>
 #include <linux/net.h>
@@ -136,6 +137,8 @@ static DEFINE_SPINLOCK(addrconf_verify_lock);
 static void addrconf_join_anycast(struct inet6_ifaddr *ifp);
 static void addrconf_leave_anycast(struct inet6_ifaddr *ifp);
 
+static void addrconf_bonding_change(struct net_device *dev,
+                                   unsigned long event);
 static int addrconf_ifdown(struct net_device *dev, int how);
 
 static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags);
@@ -478,9 +481,8 @@ static void addrconf_forward_change(struct net *net, __s32 newf)
        struct net_device *dev;
        struct inet6_dev *idev;
 
-       read_lock(&dev_base_lock);
-       for_each_netdev(net, dev) {
-               rcu_read_lock();
+       rcu_read_lock();
+       for_each_netdev_rcu(net, dev) {
                idev = __in6_dev_get(dev);
                if (idev) {
                        int changed = (!idev->cnf.forwarding) ^ (!newf);
@@ -488,9 +490,8 @@ static void addrconf_forward_change(struct net *net, __s32 newf)
                        if (changed)
                                dev_forward_change(idev);
                }
-               rcu_read_unlock();
        }
-       read_unlock(&dev_base_lock);
+       rcu_read_unlock();
 }
 
 static int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int old)
@@ -502,7 +503,7 @@ static int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int old)
                return 0;
 
        if (!rtnl_trylock())
-               return -ERESTARTSYS;
+               return restart_syscall();
 
        if (p == &net->ipv6.devconf_all->forwarding) {
                __s32 newf = net->ipv6.devconf_all->forwarding;
@@ -590,7 +591,6 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
 {
        struct inet6_ifaddr *ifa = NULL;
        struct rt6_info *rt;
-       struct net *net = dev_net(idev->dev);
        int hash;
        int err = 0;
        int addr_type = ipv6_addr_type(addr);
@@ -607,7 +607,7 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
                goto out2;
        }
 
-       if (idev->cnf.disable_ipv6 || net->ipv6.devconf_all->disable_ipv6) {
+       if (idev->cnf.disable_ipv6) {
                err = -EACCES;
                goto out2;
        }
@@ -1135,10 +1135,9 @@ int ipv6_dev_get_saddr(struct net *net, struct net_device *dst_dev,
        hiscore->rule = -1;
        hiscore->ifa = NULL;
 
-       read_lock(&dev_base_lock);
        rcu_read_lock();
 
-       for_each_netdev(net, dev) {
+       for_each_netdev_rcu(net, dev) {
                struct inet6_dev *idev;
 
                /* Candidate Source Address (section 4)
@@ -1215,16 +1214,12 @@ int ipv6_dev_get_saddr(struct net *net, struct net_device *dst_dev,
                                        }
                                        break;
                                } else if (minihiscore < miniscore) {
-                                       struct ipv6_saddr_score *tmp;
-
                                        if (hiscore->ifa)
                                                in6_ifa_put(hiscore->ifa);
 
                                        in6_ifa_hold(score->ifa);
 
-                                       tmp = hiscore;
-                                       hiscore = score;
-                                       score = tmp;
+                                       swap(hiscore, score);
 
                                        /* restore our iterator */
                                        score->ifa = hiscore->ifa;
@@ -1237,7 +1232,6 @@ try_nextdev:
                read_unlock_bh(&idev->lock);
        }
        rcu_read_unlock();
-       read_unlock(&dev_base_lock);
 
        if (!hiscore->ifa)
                return -EADDRNOTAVAIL;
@@ -1373,48 +1367,16 @@ struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, const struct in6_addr *add
        return ifp;
 }
 
-int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2)
-{
-       const struct in6_addr *sk_rcv_saddr6 = &inet6_sk(sk)->rcv_saddr;
-       const struct in6_addr *sk2_rcv_saddr6 = inet6_rcv_saddr(sk2);
-       __be32 sk_rcv_saddr = inet_sk(sk)->rcv_saddr;
-       __be32 sk2_rcv_saddr = inet_rcv_saddr(sk2);
-       int sk_ipv6only = ipv6_only_sock(sk);
-       int sk2_ipv6only = inet_v6_ipv6only(sk2);
-       int addr_type = ipv6_addr_type(sk_rcv_saddr6);
-       int addr_type2 = sk2_rcv_saddr6 ? ipv6_addr_type(sk2_rcv_saddr6) : IPV6_ADDR_MAPPED;
-
-       if (!sk2_rcv_saddr && !sk_ipv6only)
-               return 1;
-
-       if (addr_type2 == IPV6_ADDR_ANY &&
-           !(sk2_ipv6only && addr_type == IPV6_ADDR_MAPPED))
-               return 1;
-
-       if (addr_type == IPV6_ADDR_ANY &&
-           !(sk_ipv6only && addr_type2 == IPV6_ADDR_MAPPED))
-               return 1;
-
-       if (sk2_rcv_saddr6 &&
-           ipv6_addr_equal(sk_rcv_saddr6, sk2_rcv_saddr6))
-               return 1;
-
-       if (addr_type == IPV6_ADDR_MAPPED &&
-           !sk2_ipv6only &&
-           (!sk2_rcv_saddr || !sk_rcv_saddr || sk_rcv_saddr == sk2_rcv_saddr))
-               return 1;
-
-       return 0;
-}
-
 /* Gets referenced address, destroys ifaddr */
 
-static void addrconf_dad_stop(struct inet6_ifaddr *ifp)
+static void addrconf_dad_stop(struct inet6_ifaddr *ifp, int dad_failed)
 {
        if (ifp->flags&IFA_F_PERMANENT) {
                spin_lock_bh(&ifp->lock);
                addrconf_del_timer(ifp);
                ifp->flags |= IFA_F_TENTATIVE;
+               if (dad_failed)
+                       ifp->flags |= IFA_F_DADFAILED;
                spin_unlock_bh(&ifp->lock);
                in6_ifa_put(ifp);
 #ifdef CONFIG_IPV6_PRIVACY
@@ -1441,8 +1403,8 @@ void addrconf_dad_failure(struct inet6_ifaddr *ifp)
        struct inet6_dev *idev = ifp->idev;
 
        if (net_ratelimit())
-               printk(KERN_INFO "%s: IPv6 duplicate address detected!\n",
-                       ifp->idev->dev->name);
+               printk(KERN_INFO "%s: IPv6 duplicate address %pI6c detected!\n",
+                       ifp->idev->dev->name, &ifp->addr);
 
        if (idev->cnf.accept_dad > 1 && !idev->cnf.disable_ipv6) {
                struct in6_addr addr;
@@ -1460,7 +1422,7 @@ void addrconf_dad_failure(struct inet6_ifaddr *ifp)
                }
        }
 
-       addrconf_dad_stop(ifp);
+       addrconf_dad_stop(ifp, 1);
 }
 
 /* Join to solicited addr multicast group. */
@@ -1557,6 +1519,8 @@ static int addrconf_ifid_infiniband(u8 *eui, struct net_device *dev)
 
 int __ipv6_isatap_ifid(u8 *eui, __be32 addr)
 {
+       if (addr == 0)
+               return -1;
        eui[0] = (ipv4_is_zeronet(addr) || ipv4_is_private_10(addr) ||
                  ipv4_is_loopback(addr) || ipv4_is_linklocal_169(addr) ||
                  ipv4_is_private_172(addr) || ipv4_is_test_192(addr) ||
@@ -1787,6 +1751,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len)
        __u32 prefered_lft;
        int addr_type;
        struct inet6_dev *in6_dev;
+       struct net *net = dev_net(dev);
 
        pinfo = (struct prefix_info *) opt;
 
@@ -1844,7 +1809,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len)
                if (addrconf_finite_timeout(rt_expires))
                        rt_expires *= HZ;
 
-               rt = rt6_lookup(dev_net(dev), &pinfo->prefix, NULL,
+               rt = rt6_lookup(net, &pinfo->prefix, NULL,
                                dev->ifindex, 1);
 
                if (rt && addrconf_is_prefix_route(rt)) {
@@ -1881,7 +1846,6 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len)
                struct inet6_ifaddr * ifp;
                struct in6_addr addr;
                int create = 0, update_lft = 0;
-               struct net *net = dev_net(dev);
 
                if (pinfo->prefix_len == 64) {
                        memcpy(&addr, &pinfo->prefix, 8);
@@ -1952,8 +1916,32 @@ ok:
                                        update_lft = 1;
                                else if (stored_lft <= MIN_VALID_LIFETIME) {
                                        /* valid_lft <= stored_lft is always true */
-                                       /* XXX: IPsec */
-                                       update_lft = 0;
+                                       /*
+                                        * RFC 4862 Section 5.5.3e:
+                                        * "Note that the preferred lifetime of
+                                        *  the corresponding address is always
+                                        *  reset to the Preferred Lifetime in
+                                        *  the received Prefix Information
+                                        *  option, regardless of whether the
+                                        *  valid lifetime is also reset or
+                                        *  ignored."
+                                        *
+                                        *  So if the preferred lifetime in
+                                        *  this advertisement is different
+                                        *  than what we have stored, but the
+                                        *  valid lifetime is invalid, just
+                                        *  reset prefered_lft.
+                                        *
+                                        *  We must set the valid lifetime
+                                        *  to the stored lifetime since we'll
+                                        *  be updating the timestamp below,
+                                        *  else we'll set it back to the
+                                        *  minumum.
+                                        */
+                                       if (prefered_lft != ifp->prefered_lft) {
+                                               valid_lft = stored_lft;
+                                               update_lft = 1;
+                                       }
                                } else {
                                        valid_lft = MIN_VALID_LIFETIME;
                                        if (valid_lft < prefered_lft)
@@ -2592,6 +2580,10 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
                                return notifier_from_errno(err);
                }
                break;
+       case NETDEV_BONDING_OLDTYPE:
+       case NETDEV_BONDING_NEWTYPE:
+               addrconf_bonding_change(dev, event);
+               break;
        }
 
        return NOTIFY_OK;
@@ -2605,6 +2597,19 @@ static struct notifier_block ipv6_dev_notf = {
        .priority = 0
 };
 
+static void addrconf_bonding_change(struct net_device *dev, unsigned long event)
+{
+       struct inet6_dev *idev;
+       ASSERT_RTNL();
+
+       idev = __in6_dev_get(dev);
+
+       if (event == NETDEV_BONDING_NEWTYPE)
+               ipv6_mc_remap(idev);
+       else if (event == NETDEV_BONDING_OLDTYPE)
+               ipv6_mc_unmap(idev);
+}
+
 static int addrconf_ifdown(struct net_device *dev, int how)
 {
        struct inet6_dev *idev;
@@ -2790,7 +2795,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
            idev->cnf.accept_dad < 1 ||
            !(ifp->flags&IFA_F_TENTATIVE) ||
            ifp->flags & IFA_F_NODAD) {
-               ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
+               ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED);
                spin_unlock_bh(&ifp->lock);
                read_unlock_bh(&idev->lock);
 
@@ -2802,12 +2807,12 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
                spin_unlock_bh(&ifp->lock);
                read_unlock_bh(&idev->lock);
                /*
-                * If the defice is not ready:
+                * If the device is not ready:
                 * - keep it tentative if it is a permanent address.
                 * - otherwise, kill it.
                 */
                in6_ifa_hold(ifp);
-               addrconf_dad_stop(ifp);
+               addrconf_dad_stop(ifp, 0);
                return;
        }
 
@@ -2841,7 +2846,7 @@ static void addrconf_dad_timer(unsigned long data)
                 * DAD was successful
                 */
 
-               ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
+               ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED);
                spin_unlock_bh(&ifp->lock);
                read_unlock_bh(&idev->lock);
 
@@ -3022,14 +3027,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");
 }
@@ -3121,7 +3126,7 @@ restart:
                                spin_unlock(&ifp->lock);
                                continue;
                        } else if (age >= ifp->prefered_lft) {
-                               /* jiffies - ifp->tsamp > age >= ifp->prefered_lft */
+                               /* jiffies - ifp->tstamp > age >= ifp->prefered_lft */
                                int deprecate = 0;
 
                                if (!(ifp->flags&IFA_F_DEPRECATED)) {
@@ -3398,7 +3403,10 @@ static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa,
                valid = ifa->valid_lft;
                if (preferred != INFINITY_LIFE_TIME) {
                        long tval = (jiffies - ifa->tstamp)/HZ;
-                       preferred -= tval;
+                       if (preferred > tval)
+                               preferred -= tval;
+                       else
+                               preferred = 0;
                        if (valid != INFINITY_LIFE_TIME)
                                valid -= tval;
                }
@@ -3473,85 +3481,114 @@ enum addr_type_t
        ANYCAST_ADDR,
 };
 
+/* called with rcu_read_lock() */
+static int in6_dump_addrs(struct inet6_dev *idev, struct sk_buff *skb,
+                         struct netlink_callback *cb, enum addr_type_t type,
+                         int s_ip_idx, int *p_ip_idx)
+{
+       struct inet6_ifaddr *ifa;
+       struct ifmcaddr6 *ifmca;
+       struct ifacaddr6 *ifaca;
+       int err = 1;
+       int ip_idx = *p_ip_idx;
+
+       read_lock_bh(&idev->lock);
+       switch (type) {
+       case UNICAST_ADDR:
+               /* unicast address incl. temp addr */
+               for (ifa = idev->addr_list; ifa;
+                    ifa = ifa->if_next, ip_idx++) {
+                       if (ip_idx < s_ip_idx)
+                               continue;
+                       err = inet6_fill_ifaddr(skb, ifa,
+                                               NETLINK_CB(cb->skb).pid,
+                                               cb->nlh->nlmsg_seq,
+                                               RTM_NEWADDR,
+                                               NLM_F_MULTI);
+                       if (err <= 0)
+                               break;
+               }
+               break;
+       case MULTICAST_ADDR:
+               /* multicast address */
+               for (ifmca = idev->mc_list; ifmca;
+                    ifmca = ifmca->next, ip_idx++) {
+                       if (ip_idx < s_ip_idx)
+                               continue;
+                       err = inet6_fill_ifmcaddr(skb, ifmca,
+                                                 NETLINK_CB(cb->skb).pid,
+                                                 cb->nlh->nlmsg_seq,
+                                                 RTM_GETMULTICAST,
+                                                 NLM_F_MULTI);
+                       if (err <= 0)
+                               break;
+               }
+               break;
+       case ANYCAST_ADDR:
+               /* anycast address */
+               for (ifaca = idev->ac_list; ifaca;
+                    ifaca = ifaca->aca_next, ip_idx++) {
+                       if (ip_idx < s_ip_idx)
+                               continue;
+                       err = inet6_fill_ifacaddr(skb, ifaca,
+                                                 NETLINK_CB(cb->skb).pid,
+                                                 cb->nlh->nlmsg_seq,
+                                                 RTM_GETANYCAST,
+                                                 NLM_F_MULTI);
+                       if (err <= 0)
+                               break;
+               }
+               break;
+       default:
+               break;
+       }
+       read_unlock_bh(&idev->lock);
+       *p_ip_idx = ip_idx;
+       return err;
+}
+
 static int inet6_dump_addr(struct sk_buff *skb, struct netlink_callback *cb,
                           enum addr_type_t type)
 {
+       struct net *net = sock_net(skb->sk);
+       int h, s_h;
        int idx, ip_idx;
        int s_idx, s_ip_idx;
-       int err = 1;
        struct net_device *dev;
-       struct inet6_dev *idev = NULL;
-       struct inet6_ifaddr *ifa;
-       struct ifmcaddr6 *ifmca;
-       struct ifacaddr6 *ifaca;
-       struct net *net = sock_net(skb->sk);
+       struct inet6_dev *idev;
+       struct hlist_head *head;
+       struct hlist_node *node;
 
-       s_idx = cb->args[0];
-       s_ip_idx = ip_idx = cb->args[1];
+       s_h = cb->args[0];
+       s_idx = idx = cb->args[1];
+       s_ip_idx = ip_idx = cb->args[2];
 
-       idx = 0;
-       for_each_netdev(net, dev) {
-               if (idx < s_idx)
-                       goto cont;
-               if (idx > s_idx)
-                       s_ip_idx = 0;
-               ip_idx = 0;
-               if ((idev = in6_dev_get(dev)) == NULL)
-                       goto cont;
-               read_lock_bh(&idev->lock);
-               switch (type) {
-               case UNICAST_ADDR:
-                       /* unicast address incl. temp addr */
-                       for (ifa = idev->addr_list; ifa;
-                            ifa = ifa->if_next, ip_idx++) {
-                               if (ip_idx < s_ip_idx)
-                                       continue;
-                               err = inet6_fill_ifaddr(skb, ifa,
-                                                       NETLINK_CB(cb->skb).pid,
-                                                       cb->nlh->nlmsg_seq,
-                                                       RTM_NEWADDR,
-                                                       NLM_F_MULTI);
-                       }
-                       break;
-               case MULTICAST_ADDR:
-                       /* multicast address */
-                       for (ifmca = idev->mc_list; ifmca;
-                            ifmca = ifmca->next, ip_idx++) {
-                               if (ip_idx < s_ip_idx)
-                                       continue;
-                               err = inet6_fill_ifmcaddr(skb, ifmca,
-                                                         NETLINK_CB(cb->skb).pid,
-                                                         cb->nlh->nlmsg_seq,
-                                                         RTM_GETMULTICAST,
-                                                         NLM_F_MULTI);
-                       }
-                       break;
-               case ANYCAST_ADDR:
-                       /* anycast address */
-                       for (ifaca = idev->ac_list; ifaca;
-                            ifaca = ifaca->aca_next, ip_idx++) {
-                               if (ip_idx < s_ip_idx)
-                                       continue;
-                               err = inet6_fill_ifacaddr(skb, ifaca,
-                                                         NETLINK_CB(cb->skb).pid,
-                                                         cb->nlh->nlmsg_seq,
-                                                         RTM_GETANYCAST,
-                                                         NLM_F_MULTI);
-                       }
-                       break;
-               default:
-                       break;
-               }
-               read_unlock_bh(&idev->lock);
-               in6_dev_put(idev);
-
-               if (err <= 0)
-                       break;
+       rcu_read_lock();
+       for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
+               idx = 0;
+               head = &net->dev_index_head[h];
+               hlist_for_each_entry_rcu(dev, node, head, index_hlist) {
+                       if (idx < s_idx)
+                               goto cont;
+                       if (idx > s_idx)
+                               s_ip_idx = 0;
+                       ip_idx = 0;
+                       if ((idev = __in6_dev_get(dev)) == NULL)
+                               goto cont;
+
+                       if (in6_dump_addrs(idev, skb, cb, type,
+                                          s_ip_idx, &ip_idx) <= 0)
+                               goto done;
 cont:
-               idx++;
+                       idx++;
+               }
        }
-       cb->args[0] = idx;
-       cb->args[1] = ip_idx;
+done:
+       rcu_read_unlock();
+       cb->args[0] = h;
+       cb->args[1] = idx;
+       cb->args[2] = ip_idx;
+
        return skb->len;
 }
 
@@ -3696,6 +3733,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
 #endif
        array[DEVCONF_DISABLE_IPV6] = cnf->disable_ipv6;
        array[DEVCONF_ACCEPT_DAD] = cnf->accept_dad;
+       array[DEVCONF_FORCE_TLLAO] = cnf->force_tllao;
 }
 
 static inline size_t inet6_if_nlmsg_size(void)
@@ -3814,28 +3852,39 @@ nla_put_failure:
 static int inet6_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
 {
        struct net *net = sock_net(skb->sk);
-       int idx, err;
-       int s_idx = cb->args[0];
+       int h, s_h;
+       int idx = 0, s_idx;
        struct net_device *dev;
        struct inet6_dev *idev;
+       struct hlist_head *head;
+       struct hlist_node *node;
 
-       read_lock(&dev_base_lock);
-       idx = 0;
-       for_each_netdev(net, dev) {
-               if (idx < s_idx)
-                       goto cont;
-               if ((idev = in6_dev_get(dev)) == NULL)
-                       goto cont;
-               err = inet6_fill_ifinfo(skb, idev, NETLINK_CB(cb->skb).pid,
-                               cb->nlh->nlmsg_seq, RTM_NEWLINK, NLM_F_MULTI);
-               in6_dev_put(idev);
-               if (err <= 0)
-                       break;
+       s_h = cb->args[0];
+       s_idx = cb->args[1];
+
+       rcu_read_lock();
+       for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
+               idx = 0;
+               head = &net->dev_index_head[h];
+               hlist_for_each_entry_rcu(dev, node, head, index_hlist) {
+                       if (idx < s_idx)
+                               goto cont;
+                       idev = __in6_dev_get(dev);
+                       if (!idev)
+                               goto cont;
+                       if (inet6_fill_ifinfo(skb, idev,
+                                             NETLINK_CB(cb->skb).pid,
+                                             cb->nlh->nlmsg_seq,
+                                             RTM_NEWLINK, NLM_F_MULTI) <= 0)
+                               goto out;
 cont:
-               idx++;
+                       idx++;
+               }
        }
-       read_unlock(&dev_base_lock);
-       cb->args[0] = idx;
+out:
+       rcu_read_unlock();
+       cb->args[1] = idx;
+       cb->args[0] = h;
 
        return skb->len;
 }
@@ -3974,53 +4023,85 @@ static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
 #ifdef CONFIG_SYSCTL
 
 static
-int addrconf_sysctl_forward(ctl_table *ctl, int write, struct file * filp,
+int addrconf_sysctl_forward(ctl_table *ctl, int write,
                           void __user *buffer, size_t *lenp, loff_t *ppos)
 {
        int *valp = ctl->data;
        int val = *valp;
        int ret;
 
-       ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
+       ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
 
        if (write)
                ret = addrconf_fixup_forwarding(ctl, valp, val);
        return ret;
 }
 
-static int addrconf_sysctl_forward_strategy(ctl_table *table,
-                                           void __user *oldval,
-                                           size_t __user *oldlenp,
-                                           void __user *newval, size_t newlen)
+static void dev_disable_change(struct inet6_dev *idev)
 {
-       int *valp = table->data;
-       int val = *valp;
-       int new;
+       if (!idev || !idev->dev)
+               return;
 
-       if (!newval || !newlen)
-               return 0;
-       if (newlen != sizeof(int))
-               return -EINVAL;
-       if (get_user(new, (int __user *)newval))
-               return -EFAULT;
-       if (new == *valp)
-               return 0;
-       if (oldval && oldlenp) {
-               size_t len;
-               if (get_user(len, oldlenp))
-                       return -EFAULT;
-               if (len) {
-                       if (len > table->maxlen)
-                               len = table->maxlen;
-                       if (copy_to_user(oldval, valp, len))
-                               return -EFAULT;
-                       if (put_user(len, oldlenp))
-                               return -EFAULT;
+       if (idev->cnf.disable_ipv6)
+               addrconf_notify(NULL, NETDEV_DOWN, idev->dev);
+       else
+               addrconf_notify(NULL, NETDEV_UP, idev->dev);
+}
+
+static void addrconf_disable_change(struct net *net, __s32 newf)
+{
+       struct net_device *dev;
+       struct inet6_dev *idev;
+
+       rcu_read_lock();
+       for_each_netdev_rcu(net, dev) {
+               idev = __in6_dev_get(dev);
+               if (idev) {
+                       int changed = (!idev->cnf.disable_ipv6) ^ (!newf);
+                       idev->cnf.disable_ipv6 = newf;
+                       if (changed)
+                               dev_disable_change(idev);
                }
        }
+       rcu_read_unlock();
+}
+
+static int addrconf_disable_ipv6(struct ctl_table *table, int *p, int old)
+{
+       struct net *net;
+
+       net = (struct net *)table->extra2;
+
+       if (p == &net->ipv6.devconf_dflt->disable_ipv6)
+               return 0;
+
+       if (!rtnl_trylock())
+               return restart_syscall();
+
+       if (p == &net->ipv6.devconf_all->disable_ipv6) {
+               __s32 newf = net->ipv6.devconf_all->disable_ipv6;
+               net->ipv6.devconf_dflt->disable_ipv6 = newf;
+               addrconf_disable_change(net, newf);
+       } else if ((!*p) ^ (!old))
+               dev_disable_change((struct inet6_dev *)table->extra1);
+
+       rtnl_unlock();
+       return 0;
+}
+
+static
+int addrconf_sysctl_disable(ctl_table *ctl, int write,
+                           void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+       int *valp = ctl->data;
+       int val = *valp;
+       int ret;
+
+       ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
 
-       *valp = new;
-       return addrconf_fixup_forwarding(table, valp, val);
+       if (write)
+               ret = addrconf_disable_ipv6(ctl, valp, val);
+       return ret;
 }
 
 static struct addrconf_sysctl_table
@@ -4032,16 +4113,13 @@ static struct addrconf_sysctl_table
        .sysctl_header = NULL,
        .addrconf_vars = {
                {
-                       .ctl_name       =       NET_IPV6_FORWARDING,
                        .procname       =       "forwarding",
                        .data           =       &ipv6_devconf.forwarding,
                        .maxlen         =       sizeof(int),
                        .mode           =       0644,
                        .proc_handler   =       addrconf_sysctl_forward,
-                       .strategy       =       addrconf_sysctl_forward_strategy,
                },
                {
-                       .ctl_name       =       NET_IPV6_HOP_LIMIT,
                        .procname       =       "hop_limit",
                        .data           =       &ipv6_devconf.hop_limit,
                        .maxlen         =       sizeof(int),
@@ -4049,7 +4127,6 @@ static struct addrconf_sysctl_table
                        .proc_handler   =       proc_dointvec,
                },
                {
-                       .ctl_name       =       NET_IPV6_MTU,
                        .procname       =       "mtu",
                        .data           =       &ipv6_devconf.mtu6,
                        .maxlen         =       sizeof(int),
@@ -4057,7 +4134,6 @@ static struct addrconf_sysctl_table
                        .proc_handler   =       proc_dointvec,
                },
                {
-                       .ctl_name       =       NET_IPV6_ACCEPT_RA,
                        .procname       =       "accept_ra",
                        .data           =       &ipv6_devconf.accept_ra,
                        .maxlen         =       sizeof(int),
@@ -4065,7 +4141,6 @@ static struct addrconf_sysctl_table
                        .proc_handler   =       proc_dointvec,
                },
                {
-                       .ctl_name       =       NET_IPV6_ACCEPT_REDIRECTS,
                        .procname       =       "accept_redirects",
                        .data           =       &ipv6_devconf.accept_redirects,
                        .maxlen         =       sizeof(int),
@@ -4073,7 +4148,6 @@ static struct addrconf_sysctl_table
                        .proc_handler   =       proc_dointvec,
                },
                {
-                       .ctl_name       =       NET_IPV6_AUTOCONF,
                        .procname       =       "autoconf",
                        .data           =       &ipv6_devconf.autoconf,
                        .maxlen         =       sizeof(int),
@@ -4081,7 +4155,6 @@ static struct addrconf_sysctl_table
                        .proc_handler   =       proc_dointvec,
                },
                {
-                       .ctl_name       =       NET_IPV6_DAD_TRANSMITS,
                        .procname       =       "dad_transmits",
                        .data           =       &ipv6_devconf.dad_transmits,
                        .maxlen         =       sizeof(int),
@@ -4089,7 +4162,6 @@ static struct addrconf_sysctl_table
                        .proc_handler   =       proc_dointvec,
                },
                {
-                       .ctl_name       =       NET_IPV6_RTR_SOLICITS,
                        .procname       =       "router_solicitations",
                        .data           =       &ipv6_devconf.rtr_solicits,
                        .maxlen         =       sizeof(int),
@@ -4097,25 +4169,20 @@ static struct addrconf_sysctl_table
                        .proc_handler   =       proc_dointvec,
                },
                {
-                       .ctl_name       =       NET_IPV6_RTR_SOLICIT_INTERVAL,
                        .procname       =       "router_solicitation_interval",
                        .data           =       &ipv6_devconf.rtr_solicit_interval,
                        .maxlen         =       sizeof(int),
                        .mode           =       0644,
                        .proc_handler   =       proc_dointvec_jiffies,
-                       .strategy       =       sysctl_jiffies,
                },
                {
-                       .ctl_name       =       NET_IPV6_RTR_SOLICIT_DELAY,
                        .procname       =       "router_solicitation_delay",
                        .data           =       &ipv6_devconf.rtr_solicit_delay,
                        .maxlen         =       sizeof(int),
                        .mode           =       0644,
                        .proc_handler   =       proc_dointvec_jiffies,
-                       .strategy       =       sysctl_jiffies,
                },
                {
-                       .ctl_name       =       NET_IPV6_FORCE_MLD_VERSION,
                        .procname       =       "force_mld_version",
                        .data           =       &ipv6_devconf.force_mld_version,
                        .maxlen         =       sizeof(int),
@@ -4124,7 +4191,6 @@ static struct addrconf_sysctl_table
                },
 #ifdef CONFIG_IPV6_PRIVACY
                {
-                       .ctl_name       =       NET_IPV6_USE_TEMPADDR,
                        .procname       =       "use_tempaddr",
                        .data           =       &ipv6_devconf.use_tempaddr,
                        .maxlen         =       sizeof(int),
@@ -4132,7 +4198,6 @@ static struct addrconf_sysctl_table
                        .proc_handler   =       proc_dointvec,
                },
                {
-                       .ctl_name       =       NET_IPV6_TEMP_VALID_LFT,
                        .procname       =       "temp_valid_lft",
                        .data           =       &ipv6_devconf.temp_valid_lft,
                        .maxlen         =       sizeof(int),
@@ -4140,7 +4205,6 @@ static struct addrconf_sysctl_table
                        .proc_handler   =       proc_dointvec,
                },
                {
-                       .ctl_name       =       NET_IPV6_TEMP_PREFERED_LFT,
                        .procname       =       "temp_prefered_lft",
                        .data           =       &ipv6_devconf.temp_prefered_lft,
                        .maxlen         =       sizeof(int),
@@ -4148,7 +4212,6 @@ static struct addrconf_sysctl_table
                        .proc_handler   =       proc_dointvec,
                },
                {
-                       .ctl_name       =       NET_IPV6_REGEN_MAX_RETRY,
                        .procname       =       "regen_max_retry",
                        .data           =       &ipv6_devconf.regen_max_retry,
                        .maxlen         =       sizeof(int),
@@ -4156,7 +4219,6 @@ static struct addrconf_sysctl_table
                        .proc_handler   =       proc_dointvec,
                },
                {
-                       .ctl_name       =       NET_IPV6_MAX_DESYNC_FACTOR,
                        .procname       =       "max_desync_factor",
                        .data           =       &ipv6_devconf.max_desync_factor,
                        .maxlen         =       sizeof(int),
@@ -4165,7 +4227,6 @@ static struct addrconf_sysctl_table
                },
 #endif
                {
-                       .ctl_name       =       NET_IPV6_MAX_ADDRESSES,
                        .procname       =       "max_addresses",
                        .data           =       &ipv6_devconf.max_addresses,
                        .maxlen         =       sizeof(int),
@@ -4173,7 +4234,6 @@ static struct addrconf_sysctl_table
                        .proc_handler   =       proc_dointvec,
                },
                {
-                       .ctl_name       =       NET_IPV6_ACCEPT_RA_DEFRTR,
                        .procname       =       "accept_ra_defrtr",
                        .data           =       &ipv6_devconf.accept_ra_defrtr,
                        .maxlen         =       sizeof(int),
@@ -4181,7 +4241,6 @@ static struct addrconf_sysctl_table
                        .proc_handler   =       proc_dointvec,
                },
                {
-                       .ctl_name       =       NET_IPV6_ACCEPT_RA_PINFO,
                        .procname       =       "accept_ra_pinfo",
                        .data           =       &ipv6_devconf.accept_ra_pinfo,
                        .maxlen         =       sizeof(int),
@@ -4190,7 +4249,6 @@ static struct addrconf_sysctl_table
                },
 #ifdef CONFIG_IPV6_ROUTER_PREF
                {
-                       .ctl_name       =       NET_IPV6_ACCEPT_RA_RTR_PREF,
                        .procname       =       "accept_ra_rtr_pref",
                        .data           =       &ipv6_devconf.accept_ra_rtr_pref,
                        .maxlen         =       sizeof(int),
@@ -4198,17 +4256,14 @@ static struct addrconf_sysctl_table
                        .proc_handler   =       proc_dointvec,
                },
                {
-                       .ctl_name       =       NET_IPV6_RTR_PROBE_INTERVAL,
                        .procname       =       "router_probe_interval",
                        .data           =       &ipv6_devconf.rtr_probe_interval,
                        .maxlen         =       sizeof(int),
                        .mode           =       0644,
                        .proc_handler   =       proc_dointvec_jiffies,
-                       .strategy       =       sysctl_jiffies,
                },
 #ifdef CONFIG_IPV6_ROUTE_INFO
                {
-                       .ctl_name       =       NET_IPV6_ACCEPT_RA_RT_INFO_MAX_PLEN,
                        .procname       =       "accept_ra_rt_info_max_plen",
                        .data           =       &ipv6_devconf.accept_ra_rt_info_max_plen,
                        .maxlen         =       sizeof(int),
@@ -4218,7 +4273,6 @@ static struct addrconf_sysctl_table
 #endif
 #endif
                {
-                       .ctl_name       =       NET_IPV6_PROXY_NDP,
                        .procname       =       "proxy_ndp",
                        .data           =       &ipv6_devconf.proxy_ndp,
                        .maxlen         =       sizeof(int),
@@ -4226,7 +4280,6 @@ static struct addrconf_sysctl_table
                        .proc_handler   =       proc_dointvec,
                },
                {
-                       .ctl_name       =       NET_IPV6_ACCEPT_SOURCE_ROUTE,
                        .procname       =       "accept_source_route",
                        .data           =       &ipv6_devconf.accept_source_route,
                        .maxlen         =       sizeof(int),
@@ -4235,7 +4288,6 @@ static struct addrconf_sysctl_table
                },
 #ifdef CONFIG_IPV6_OPTIMISTIC_DAD
                {
-                       .ctl_name       =       CTL_UNNUMBERED,
                        .procname       =       "optimistic_dad",
                        .data           =       &ipv6_devconf.optimistic_dad,
                        .maxlen         =       sizeof(int),
@@ -4246,7 +4298,6 @@ static struct addrconf_sysctl_table
 #endif
 #ifdef CONFIG_IPV6_MROUTE
                {
-                       .ctl_name       =       CTL_UNNUMBERED,
                        .procname       =       "mc_forwarding",
                        .data           =       &ipv6_devconf.mc_forwarding,
                        .maxlen         =       sizeof(int),
@@ -4255,15 +4306,13 @@ static struct addrconf_sysctl_table
                },
 #endif
                {
-                       .ctl_name       =       CTL_UNNUMBERED,
                        .procname       =       "disable_ipv6",
                        .data           =       &ipv6_devconf.disable_ipv6,
                        .maxlen         =       sizeof(int),
                        .mode           =       0644,
-                       .proc_handler   =       proc_dointvec,
+                       .proc_handler   =       addrconf_sysctl_disable,
                },
                {
-                       .ctl_name       =       CTL_UNNUMBERED,
                        .procname       =       "accept_dad",
                        .data           =       &ipv6_devconf.accept_dad,
                        .maxlen         =       sizeof(int),
@@ -4271,13 +4320,20 @@ static struct addrconf_sysctl_table
                        .proc_handler   =       proc_dointvec,
                },
                {
-                       .ctl_name       =       0,      /* sentinel */
+                       .procname       = "force_tllao",
+                       .data           = &ipv6_devconf.force_tllao,
+                       .maxlen         = sizeof(int),
+                       .mode           = 0644,
+                       .proc_handler   = proc_dointvec
+               },
+               {
+                       /* sentinel */
                }
        },
 };
 
 static int __addrconf_sysctl_register(struct net *net, char *dev_name,
-               int ctl_name, struct inet6_dev *idev, struct ipv6_devconf *p)
+               struct inet6_dev *idev, struct ipv6_devconf *p)
 {
        int i;
        struct addrconf_sysctl_table *t;
@@ -4285,9 +4341,9 @@ static int __addrconf_sysctl_register(struct net *net, char *dev_name,
 #define ADDRCONF_CTL_PATH_DEV  3
 
        struct ctl_path addrconf_ctl_path[] = {
-               { .procname = "net", .ctl_name = CTL_NET, },
-               { .procname = "ipv6", .ctl_name = NET_IPV6, },
-               { .procname = "conf", .ctl_name = NET_IPV6_CONF, },
+               { .procname = "net", },
+               { .procname = "ipv6", },
+               { .procname = "conf", },
                { /* to be set */ },
                { },
        };
@@ -4313,7 +4369,6 @@ static int __addrconf_sysctl_register(struct net *net, char *dev_name,
                goto free;
 
        addrconf_ctl_path[ADDRCONF_CTL_PATH_DEV].procname = t->dev_name;
-       addrconf_ctl_path[ADDRCONF_CTL_PATH_DEV].ctl_name = ctl_name;
 
        t->sysctl_header = register_net_sysctl_table(net, addrconf_ctl_path,
                        t->addrconf_vars);
@@ -4349,10 +4404,9 @@ static void addrconf_sysctl_register(struct inet6_dev *idev)
 {
        neigh_sysctl_register(idev->dev, idev->nd_parms, NET_IPV6,
                              NET_IPV6_NEIGH, "ipv6",
-                             &ndisc_ifinfo_sysctl_change,
-                             ndisc_ifinfo_sysctl_strategy);
+                             &ndisc_ifinfo_sysctl_change);
        __addrconf_sysctl_register(dev_net(idev->dev), idev->dev->name,
-                       idev->dev->ifindex, idev, &idev->cnf);
+                                       idev, &idev->cnf);
 }
 
 static void addrconf_sysctl_unregister(struct inet6_dev *idev)
@@ -4364,7 +4418,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;
@@ -4373,7 +4427,7 @@ static int addrconf_init_net(struct net *net)
        all = &ipv6_devconf;
        dflt = &ipv6_devconf_dflt;
 
-       if (net != &init_net) {
+       if (!net_eq(net, &init_net)) {
                all = kmemdup(all, sizeof(ipv6_devconf), GFP_KERNEL);
                if (all == NULL)
                        goto err_alloc_all;
@@ -4381,19 +4435,21 @@ static int addrconf_init_net(struct net *net)
                dflt = kmemdup(dflt, sizeof(ipv6_devconf_dflt), GFP_KERNEL);
                if (dflt == NULL)
                        goto err_alloc_dflt;
+       } else {
+               /* these will be inherited by all namespaces */
+               dflt->autoconf = ipv6_defaults.autoconf;
+               dflt->disable_ipv6 = ipv6_defaults.disable_ipv6;
        }
 
        net->ipv6.devconf_all = all;
        net->ipv6.devconf_dflt = dflt;
 
 #ifdef CONFIG_SYSCTL
-       err = __addrconf_sysctl_register(net, "all", NET_PROTO_CONF_ALL,
-                       NULL, all);
+       err = __addrconf_sysctl_register(net, "all", NULL, all);
        if (err < 0)
                goto err_reg_all;
 
-       err = __addrconf_sysctl_register(net, "default", NET_PROTO_CONF_DEFAULT,
-                       NULL, dflt);
+       err = __addrconf_sysctl_register(net, "default", NULL, dflt);
        if (err < 0)
                goto err_reg_dflt;
 #endif
@@ -4411,13 +4467,13 @@ 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);
        __addrconf_sysctl_unregister(net->ipv6.devconf_all);
 #endif
-       if (net != &init_net) {
+       if (!net_eq(net, &init_net)) {
                kfree(net->ipv6.devconf_dflt);
                kfree(net->ipv6.devconf_all);
        }