Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[safe/jmp/linux-2.6] / net / ipv6 / addrconf.c
index 9be6be3..e83852a 100644 (file)
@@ -119,6 +119,7 @@ static void ipv6_regen_rndid(unsigned long data);
 static int desync_factor = MAX_DESYNC_FACTOR * HZ;
 #endif
 
+static int ipv6_generate_eui64(u8 *eui, struct net_device *dev);
 static int ipv6_count_addresses(struct inet6_dev *idev);
 
 /*
@@ -152,7 +153,7 @@ static int ipv6_chk_same_addr(struct net *net, const struct in6_addr *addr,
 
 static ATOMIC_NOTIFIER_HEAD(inet6addr_chain);
 
-struct ipv6_devconf ipv6_devconf __read_mostly = {
+static struct ipv6_devconf ipv6_devconf __read_mostly = {
        .forwarding             = 0,
        .hop_limit              = IPV6_DEFAULT_HOPLIMIT,
        .mtu6                   = IPV6_MIN_MTU,
@@ -183,6 +184,8 @@ struct ipv6_devconf ipv6_devconf __read_mostly = {
 #endif
        .proxy_ndp              = 0,
        .accept_source_route    = 0,    /* we do not accept RH0 by default. */
+       .disable_ipv6           = 0,
+       .accept_dad             = 1,
 };
 
 static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
@@ -215,6 +218,8 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
 #endif
        .proxy_ndp              = 0,
        .accept_source_route    = 0,    /* we do not accept RH0 by default. */
+       .disable_ipv6           = 0,
+       .accept_dad             = 1,
 };
 
 /* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */
@@ -224,9 +229,9 @@ const struct in6_addr in6addr_linklocal_allnodes = IN6ADDR_LINKLOCAL_ALLNODES_IN
 const struct in6_addr in6addr_linklocal_allrouters = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT;
 
 /* Check if a valid qdisc is available */
-static inline int addrconf_qdisc_ok(struct net_device *dev)
+static inline bool addrconf_qdisc_ok(const struct net_device *dev)
 {
-       return (dev->qdisc != &noop_qdisc);
+       return !qdisc_tx_is_noop(dev);
 }
 
 /* Check if a route is valid prefix route */
@@ -308,8 +313,10 @@ static void in6_dev_finish_destroy_rcu(struct rcu_head *head)
 void in6_dev_finish_destroy(struct inet6_dev *idev)
 {
        struct net_device *dev = idev->dev;
-       BUG_TRAP(idev->addr_list==NULL);
-       BUG_TRAP(idev->mc_list==NULL);
+
+       WARN_ON(idev->addr_list != NULL);
+       WARN_ON(idev->mc_list != NULL);
+
 #ifdef NET_REFCNT_DEBUG
        printk(KERN_DEBUG "in6_dev_finish_destroy: %s\n", dev ? dev->name : "NIL");
 #endif
@@ -348,6 +355,8 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev)
                kfree(ndev);
                return NULL;
        }
+       if (ndev->cnf.forwarding)
+               dev_disable_lro(dev);
        /* We refer to the device */
        dev_hold(dev);
 
@@ -376,6 +385,9 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev)
         */
        in6_dev_hold(ndev);
 
+       if (dev->flags & (IFF_NOARP | IFF_LOOPBACK))
+               ndev->cnf.accept_dad = -1;
+
 #if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE)
        if (dev->type == ARPHRD_SIT && (dev->priv_flags & IFF_ISATAP)) {
                printk(KERN_INFO
@@ -442,6 +454,8 @@ static void dev_forward_change(struct inet6_dev *idev)
        if (!idev)
                return;
        dev = idev->dev;
+       if (idev->cnf.forwarding)
+               dev_disable_lro(dev);
        if (dev && (dev->flags & IFF_MULTICAST)) {
                if (idev->cnf.forwarding)
                        ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters);
@@ -479,13 +493,16 @@ static void addrconf_forward_change(struct net *net, __s32 newf)
        read_unlock(&dev_base_lock);
 }
 
-static void addrconf_fixup_forwarding(struct ctl_table *table, int *p, int old)
+static int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int old)
 {
        struct net *net;
 
        net = (struct net *)table->extra2;
        if (p == &net->ipv6.devconf_dflt->forwarding)
-               return;
+               return 0;
+
+       if (!rtnl_trylock())
+               return -ERESTARTSYS;
 
        if (p == &net->ipv6.devconf_all->forwarding) {
                __s32 newf = net->ipv6.devconf_all->forwarding;
@@ -493,9 +510,11 @@ static void addrconf_fixup_forwarding(struct ctl_table *table, int *p, int old)
                addrconf_forward_change(net, newf);
        } else if ((!*p) ^ (!old))
                dev_forward_change((struct inet6_dev *)table->extra1);
+       rtnl_unlock();
 
        if (*p)
                rt6_purge_dflt_routers(net);
+       return 1;
 }
 #endif
 
@@ -503,8 +522,9 @@ static void addrconf_fixup_forwarding(struct ctl_table *table, int *p, int old)
 
 void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp)
 {
-       BUG_TRAP(ifp->if_next==NULL);
-       BUG_TRAP(ifp->lst_next==NULL);
+       WARN_ON(ifp->if_next != NULL);
+       WARN_ON(ifp->lst_next != NULL);
+
 #ifdef NET_REFCNT_DEBUG
        printk(KERN_DEBUG "inet6_ifa_finish_destroy\n");
 #endif
@@ -572,6 +592,13 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
        struct rt6_info *rt;
        int hash;
        int err = 0;
+       int addr_type = ipv6_addr_type(addr);
+
+       if (addr_type == IPV6_ADDR_ANY ||
+           addr_type & IPV6_ADDR_MULTICAST ||
+           (!(idev->dev->flags & IFF_LOOPBACK) &&
+            addr_type & IPV6_ADDR_LOOPBACK))
+               return ERR_PTR(-EADDRNOTAVAIL);
 
        rcu_read_lock_bh();
        if (idev->dead) {
@@ -753,12 +780,12 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
        }
        write_unlock_bh(&idev->lock);
 
+       addrconf_del_timer(ifp);
+
        ipv6_ifa_notify(RTM_DELADDR, ifp);
 
        atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifp);
 
-       addrconf_del_timer(ifp);
-
        /*
         * Purge or update corresponding prefix
         *
@@ -1082,13 +1109,12 @@ out:
        return ret;
 }
 
-int ipv6_dev_get_saddr(struct net_device *dst_dev,
+int ipv6_dev_get_saddr(struct net *net, struct net_device *dst_dev,
                       const struct in6_addr *daddr, unsigned int prefs,
                       struct in6_addr *saddr)
 {
        struct ipv6_saddr_score scores[2],
                                *score = &scores[0], *hiscore = &scores[1];
-       struct net *net = dev_net(dst_dev);
        struct ipv6_saddr_dst dst;
        struct net_device *dev;
        int dst_type;
@@ -1406,6 +1432,20 @@ static void addrconf_dad_stop(struct inet6_ifaddr *ifp)
 
 void addrconf_dad_failure(struct inet6_ifaddr *ifp)
 {
+       struct inet6_dev *idev = ifp->idev;
+       if (idev->cnf.accept_dad > 1 && !idev->cnf.disable_ipv6) {
+               struct in6_addr addr;
+
+               addr.s6_addr32[0] = htonl(0xfe800000);
+               addr.s6_addr32[1] = 0;
+
+               if (!ipv6_generate_eui64(addr.s6_addr + 8, idev->dev) &&
+                   ipv6_addr_equal(&ifp->addr, &addr)) {
+                       /* DAD failed for link-local based on MAC address */
+                       idev->cnf.disable_ipv6 = 1;
+               }
+       }
+
        if (net_ratelimit())
                printk(KERN_INFO "%s: duplicate address detected!\n", ifp->idev->dev->name);
        addrconf_dad_stop(ifp);
@@ -1651,6 +1691,7 @@ addrconf_prefix_route(struct in6_addr *pfx, int plen, struct net_device *dev,
                .fc_dst_len = plen,
                .fc_flags = RTF_UP | flags,
                .fc_nlinfo.nl_net = dev_net(dev),
+               .fc_protocol = RTPROT_KERNEL,
        };
 
        ipv6_addr_copy(&cfg.fc_dst, pfx);
@@ -1828,6 +1869,7 @@ 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);
@@ -1846,7 +1888,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len)
 
 ok:
 
-               ifp = ipv6_get_ifaddr(dev_net(dev), &addr, dev, 1);
+               ifp = ipv6_get_ifaddr(net, &addr, dev, 1);
 
                if (ifp == NULL && valid_lft) {
                        int max_addresses = in6_dev->cnf.max_addresses;
@@ -1854,7 +1896,7 @@ ok:
 
 #ifdef CONFIG_IPV6_OPTIMISTIC_DAD
                        if (in6_dev->cnf.optimistic_dad &&
-                           !ipv6_devconf.forwarding)
+                           !net->ipv6.devconf_all->forwarding)
                                addr_flags = IFA_F_OPTIMISTIC;
 #endif
 
@@ -1992,8 +2034,8 @@ int addrconf_set_dstaddr(struct net *net, void __user *arg)
 
 #if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE)
        if (dev->type == ARPHRD_SIT) {
+               const struct net_device_ops *ops = dev->netdev_ops;
                struct ifreq ifr;
-               mm_segment_t    oldfs;
                struct ip_tunnel_parm p;
 
                err = -EADDRNOTAVAIL;
@@ -2009,9 +2051,14 @@ int addrconf_set_dstaddr(struct net *net, void __user *arg)
                p.iph.ttl = 64;
                ifr.ifr_ifru.ifru_data = (__force void __user *)&p;
 
-               oldfs = get_fs(); set_fs(KERNEL_DS);
-               err = dev->do_ioctl(dev, &ifr, SIOCADDTUNNEL);
-               set_fs(oldfs);
+               if (ops->ndo_do_ioctl) {
+                       mm_segment_t oldfs = get_fs();
+
+                       set_fs(KERNEL_DS);
+                       err = ops->ndo_do_ioctl(dev, &ifr, SIOCADDTUNNEL);
+                       set_fs(oldfs);
+               } else
+                       err = -EOPNOTSUPP;
 
                if (err == 0) {
                        err = -ENOBUFS;
@@ -2180,10 +2227,24 @@ int addrconf_del_ifaddr(struct net *net, void __user *arg)
        return err;
 }
 
+static void add_addr(struct inet6_dev *idev, const struct in6_addr *addr,
+                    int plen, int scope)
+{
+       struct inet6_ifaddr *ifp;
+
+       ifp = ipv6_add_addr(idev, addr, plen, scope, IFA_F_PERMANENT);
+       if (!IS_ERR(ifp)) {
+               spin_lock_bh(&ifp->lock);
+               ifp->flags &= ~IFA_F_TENTATIVE;
+               spin_unlock_bh(&ifp->lock);
+               ipv6_ifa_notify(RTM_NEWADDR, ifp);
+               in6_ifa_put(ifp);
+       }
+}
+
 #if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE)
 static void sit_add_v4_addrs(struct inet6_dev *idev)
 {
-       struct inet6_ifaddr * ifp;
        struct in6_addr addr;
        struct net_device *dev;
        struct net *net = dev_net(idev->dev);
@@ -2202,14 +2263,7 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)
        }
 
        if (addr.s6_addr32[3]) {
-               ifp = ipv6_add_addr(idev, &addr, 128, scope, IFA_F_PERMANENT);
-               if (!IS_ERR(ifp)) {
-                       spin_lock_bh(&ifp->lock);
-                       ifp->flags &= ~IFA_F_TENTATIVE;
-                       spin_unlock_bh(&ifp->lock);
-                       ipv6_ifa_notify(RTM_NEWADDR, ifp);
-                       in6_ifa_put(ifp);
-               }
+               add_addr(idev, &addr, 128, scope);
                return;
        }
 
@@ -2237,15 +2291,7 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)
                                else
                                        plen = 96;
 
-                               ifp = ipv6_add_addr(idev, &addr, plen, flag,
-                                                   IFA_F_PERMANENT);
-                               if (!IS_ERR(ifp)) {
-                                       spin_lock_bh(&ifp->lock);
-                                       ifp->flags &= ~IFA_F_TENTATIVE;
-                                       spin_unlock_bh(&ifp->lock);
-                                       ipv6_ifa_notify(RTM_NEWADDR, ifp);
-                                       in6_ifa_put(ifp);
-                               }
+                               add_addr(idev, &addr, plen, flag);
                        }
                }
        }
@@ -2255,7 +2301,6 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)
 static void init_loopback(struct net_device *dev)
 {
        struct inet6_dev  *idev;
-       struct inet6_ifaddr * ifp;
 
        /* ::1 */
 
@@ -2266,14 +2311,7 @@ static void init_loopback(struct net_device *dev)
                return;
        }
 
-       ifp = ipv6_add_addr(idev, &in6addr_loopback, 128, IFA_HOST, IFA_F_PERMANENT);
-       if (!IS_ERR(ifp)) {
-               spin_lock_bh(&ifp->lock);
-               ifp->flags &= ~IFA_F_TENTATIVE;
-               spin_unlock_bh(&ifp->lock);
-               ipv6_ifa_notify(RTM_NEWADDR, ifp);
-               in6_ifa_put(ifp);
-       }
+       add_addr(idev, &in6addr_loopback, 128, IFA_HOST);
 }
 
 static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr)
@@ -2283,7 +2321,7 @@ static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr
 
 #ifdef CONFIG_IPV6_OPTIMISTIC_DAD
        if (idev->cnf.optimistic_dad &&
-           !ipv6_devconf.forwarding)
+           !dev_net(idev->dev)->ipv6.devconf_all->forwarding)
                addr_flags |= IFA_F_OPTIMISTIC;
 #endif
 
@@ -2444,8 +2482,10 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
                        if (!idev && dev->mtu >= IPV6_MIN_MTU)
                                idev = ipv6_add_dev(dev);
 
-                       if (idev)
+                       if (idev) {
                                idev->if_flags |= IF_READY;
+                               run_pending = 1;
+                       }
                } else {
                        if (!addrconf_qdisc_ok(dev)) {
                                /* device is still not ready. */
@@ -2562,9 +2602,6 @@ static int addrconf_ifdown(struct net_device *dev, int how)
 
        ASSERT_RTNL();
 
-       if ((dev->flags & IFF_LOOPBACK) && how == 1)
-               how = 0;
-
        rt6_ifdown(net, dev);
        neigh_ifdown(&nd_tbl, dev);
 
@@ -2738,6 +2775,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
        spin_lock_bh(&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);
@@ -2785,6 +2823,11 @@ static void addrconf_dad_timer(unsigned long data)
                read_unlock_bh(&idev->lock);
                goto out;
        }
+       if (idev->cnf.accept_dad > 1 && idev->cnf.disable_ipv6) {
+               read_unlock_bh(&idev->lock);
+               addrconf_dad_failure(ifp);
+               return;
+       }
        spin_lock_bh(&ifp->lock);
        if (ifp->probes == 0) {
                /*
@@ -2941,9 +2984,8 @@ static void if6_seq_stop(struct seq_file *seq, void *v)
 static int if6_seq_show(struct seq_file *seq, void *v)
 {
        struct inet6_ifaddr *ifp = (struct inet6_ifaddr *)v;
-       seq_printf(seq,
-                  NIP6_SEQFMT " %02x %02x %02x %02x %8s\n",
-                  NIP6(ifp->addr),
+       seq_printf(seq, "%pi6 %02x %02x %02x %02x %8s\n",
+                  &ifp->addr,
                   ifp->idev->dev->ifindex,
                   ifp->prefix_len,
                   ifp->scope,
@@ -3596,7 +3638,8 @@ static void inet6_ifa_notify(int event, struct inet6_ifaddr *ifa)
                kfree_skb(skb);
                goto errout;
        }
-       err = rtnl_notify(skb, net, 0, RTNLGRP_IPV6_IFADDR, NULL, GFP_ATOMIC);
+       rtnl_notify(skb, net, 0, RTNLGRP_IPV6_IFADDR, NULL, GFP_ATOMIC);
+       return;
 errout:
        if (err < 0)
                rtnl_set_sk_err(net, RTNLGRP_IPV6_IFADDR, err);
@@ -3644,6 +3687,8 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
 #ifdef CONFIG_IPV6_MROUTE
        array[DEVCONF_MC_FORWARDING] = cnf->mc_forwarding;
 #endif
+       array[DEVCONF_DISABLE_IPV6] = cnf->disable_ipv6;
+       array[DEVCONF_ACCEPT_DAD] = cnf->accept_dad;
 }
 
 static inline size_t inet6_if_nlmsg_size(void)
@@ -3805,7 +3850,8 @@ void inet6_ifinfo_notify(int event, struct inet6_dev *idev)
                kfree_skb(skb);
                goto errout;
        }
-       err = rtnl_notify(skb, net, 0, RTNLGRP_IPV6_IFADDR, NULL, GFP_ATOMIC);
+       rtnl_notify(skb, net, 0, RTNLGRP_IPV6_IFADDR, NULL, GFP_ATOMIC);
+       return;
 errout:
        if (err < 0)
                rtnl_set_sk_err(net, RTNLGRP_IPV6_IFADDR, err);
@@ -3875,7 +3921,8 @@ static void inet6_prefix_notify(int event, struct inet6_dev *idev,
                kfree_skb(skb);
                goto errout;
        }
-       err = rtnl_notify(skb, net, 0, RTNLGRP_IPV6_PREFIX, NULL, GFP_ATOMIC);
+       rtnl_notify(skb, net, 0, RTNLGRP_IPV6_PREFIX, NULL, GFP_ATOMIC);
+       return;
 errout:
        if (err < 0)
                rtnl_set_sk_err(net, RTNLGRP_IPV6_PREFIX, err);
@@ -3930,12 +3977,11 @@ int addrconf_sysctl_forward(ctl_table *ctl, int write, struct file * filp,
        ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
 
        if (write)
-               addrconf_fixup_forwarding(ctl, valp, val);
+               ret = addrconf_fixup_forwarding(ctl, valp, val);
        return ret;
 }
 
 static int addrconf_sysctl_forward_strategy(ctl_table *table,
-                                           int __user *name, int nlen,
                                            void __user *oldval,
                                            size_t __user *oldlenp,
                                            void __user *newval, size_t newlen)
@@ -3967,8 +4013,7 @@ static int addrconf_sysctl_forward_strategy(ctl_table *table,
        }
 
        *valp = new;
-       addrconf_fixup_forwarding(table, valp, val);
-       return 1;
+       return addrconf_fixup_forwarding(table, valp, val);
 }
 
 static struct addrconf_sysctl_table
@@ -3985,8 +4030,8 @@ static struct addrconf_sysctl_table
                        .data           =       &ipv6_devconf.forwarding,
                        .maxlen         =       sizeof(int),
                        .mode           =       0644,
-                       .proc_handler   =       &addrconf_sysctl_forward,
-                       .strategy       =       &addrconf_sysctl_forward_strategy,
+                       .proc_handler   =       addrconf_sysctl_forward,
+                       .strategy       =       addrconf_sysctl_forward_strategy,
                },
                {
                        .ctl_name       =       NET_IPV6_HOP_LIMIT,
@@ -4002,7 +4047,7 @@ static struct addrconf_sysctl_table
                        .data           =       &ipv6_devconf.mtu6,
                        .maxlen         =       sizeof(int),
                        .mode           =       0644,
-                       .proc_handler   =       &proc_dointvec,
+                       .proc_handler   =       proc_dointvec,
                },
                {
                        .ctl_name       =       NET_IPV6_ACCEPT_RA,
@@ -4010,7 +4055,7 @@ static struct addrconf_sysctl_table
                        .data           =       &ipv6_devconf.accept_ra,
                        .maxlen         =       sizeof(int),
                        .mode           =       0644,
-                       .proc_handler   =       &proc_dointvec,
+                       .proc_handler   =       proc_dointvec,
                },
                {
                        .ctl_name       =       NET_IPV6_ACCEPT_REDIRECTS,
@@ -4018,7 +4063,7 @@ static struct addrconf_sysctl_table
                        .data           =       &ipv6_devconf.accept_redirects,
                        .maxlen         =       sizeof(int),
                        .mode           =       0644,
-                       .proc_handler   =       &proc_dointvec,
+                       .proc_handler   =       proc_dointvec,
                },
                {
                        .ctl_name       =       NET_IPV6_AUTOCONF,
@@ -4026,7 +4071,7 @@ static struct addrconf_sysctl_table
                        .data           =       &ipv6_devconf.autoconf,
                        .maxlen         =       sizeof(int),
                        .mode           =       0644,
-                       .proc_handler   =       &proc_dointvec,
+                       .proc_handler   =       proc_dointvec,
                },
                {
                        .ctl_name       =       NET_IPV6_DAD_TRANSMITS,
@@ -4034,7 +4079,7 @@ static struct addrconf_sysctl_table
                        .data           =       &ipv6_devconf.dad_transmits,
                        .maxlen         =       sizeof(int),
                        .mode           =       0644,
-                       .proc_handler   =       &proc_dointvec,
+                       .proc_handler   =       proc_dointvec,
                },
                {
                        .ctl_name       =       NET_IPV6_RTR_SOLICITS,
@@ -4042,7 +4087,7 @@ static struct addrconf_sysctl_table
                        .data           =       &ipv6_devconf.rtr_solicits,
                        .maxlen         =       sizeof(int),
                        .mode           =       0644,
-                       .proc_handler   =       &proc_dointvec,
+                       .proc_handler   =       proc_dointvec,
                },
                {
                        .ctl_name       =       NET_IPV6_RTR_SOLICIT_INTERVAL,
@@ -4050,8 +4095,8 @@ static struct addrconf_sysctl_table
                        .data           =       &ipv6_devconf.rtr_solicit_interval,
                        .maxlen         =       sizeof(int),
                        .mode           =       0644,
-                       .proc_handler   =       &proc_dointvec_jiffies,
-                       .strategy       =       &sysctl_jiffies,
+                       .proc_handler   =       proc_dointvec_jiffies,
+                       .strategy       =       sysctl_jiffies,
                },
                {
                        .ctl_name       =       NET_IPV6_RTR_SOLICIT_DELAY,
@@ -4059,8 +4104,8 @@ static struct addrconf_sysctl_table
                        .data           =       &ipv6_devconf.rtr_solicit_delay,
                        .maxlen         =       sizeof(int),
                        .mode           =       0644,
-                       .proc_handler   =       &proc_dointvec_jiffies,
-                       .strategy       =       &sysctl_jiffies,
+                       .proc_handler   =       proc_dointvec_jiffies,
+                       .strategy       =       sysctl_jiffies,
                },
                {
                        .ctl_name       =       NET_IPV6_FORCE_MLD_VERSION,
@@ -4068,7 +4113,7 @@ static struct addrconf_sysctl_table
                        .data           =       &ipv6_devconf.force_mld_version,
                        .maxlen         =       sizeof(int),
                        .mode           =       0644,
-                       .proc_handler   =       &proc_dointvec,
+                       .proc_handler   =       proc_dointvec,
                },
 #ifdef CONFIG_IPV6_PRIVACY
                {
@@ -4077,7 +4122,7 @@ static struct addrconf_sysctl_table
                        .data           =       &ipv6_devconf.use_tempaddr,
                        .maxlen         =       sizeof(int),
                        .mode           =       0644,
-                       .proc_handler   =       &proc_dointvec,
+                       .proc_handler   =       proc_dointvec,
                },
                {
                        .ctl_name       =       NET_IPV6_TEMP_VALID_LFT,
@@ -4085,7 +4130,7 @@ static struct addrconf_sysctl_table
                        .data           =       &ipv6_devconf.temp_valid_lft,
                        .maxlen         =       sizeof(int),
                        .mode           =       0644,
-                       .proc_handler   =       &proc_dointvec,
+                       .proc_handler   =       proc_dointvec,
                },
                {
                        .ctl_name       =       NET_IPV6_TEMP_PREFERED_LFT,
@@ -4093,7 +4138,7 @@ static struct addrconf_sysctl_table
                        .data           =       &ipv6_devconf.temp_prefered_lft,
                        .maxlen         =       sizeof(int),
                        .mode           =       0644,
-                       .proc_handler   =       &proc_dointvec,
+                       .proc_handler   =       proc_dointvec,
                },
                {
                        .ctl_name       =       NET_IPV6_REGEN_MAX_RETRY,
@@ -4101,7 +4146,7 @@ static struct addrconf_sysctl_table
                        .data           =       &ipv6_devconf.regen_max_retry,
                        .maxlen         =       sizeof(int),
                        .mode           =       0644,
-                       .proc_handler   =       &proc_dointvec,
+                       .proc_handler   =       proc_dointvec,
                },
                {
                        .ctl_name       =       NET_IPV6_MAX_DESYNC_FACTOR,
@@ -4109,7 +4154,7 @@ static struct addrconf_sysctl_table
                        .data           =       &ipv6_devconf.max_desync_factor,
                        .maxlen         =       sizeof(int),
                        .mode           =       0644,
-                       .proc_handler   =       &proc_dointvec,
+                       .proc_handler   =       proc_dointvec,
                },
 #endif
                {
@@ -4118,7 +4163,7 @@ static struct addrconf_sysctl_table
                        .data           =       &ipv6_devconf.max_addresses,
                        .maxlen         =       sizeof(int),
                        .mode           =       0644,
-                       .proc_handler   =       &proc_dointvec,
+                       .proc_handler   =       proc_dointvec,
                },
                {
                        .ctl_name       =       NET_IPV6_ACCEPT_RA_DEFRTR,
@@ -4126,7 +4171,7 @@ static struct addrconf_sysctl_table
                        .data           =       &ipv6_devconf.accept_ra_defrtr,
                        .maxlen         =       sizeof(int),
                        .mode           =       0644,
-                       .proc_handler   =       &proc_dointvec,
+                       .proc_handler   =       proc_dointvec,
                },
                {
                        .ctl_name       =       NET_IPV6_ACCEPT_RA_PINFO,
@@ -4134,7 +4179,7 @@ static struct addrconf_sysctl_table
                        .data           =       &ipv6_devconf.accept_ra_pinfo,
                        .maxlen         =       sizeof(int),
                        .mode           =       0644,
-                       .proc_handler   =       &proc_dointvec,
+                       .proc_handler   =       proc_dointvec,
                },
 #ifdef CONFIG_IPV6_ROUTER_PREF
                {
@@ -4143,7 +4188,7 @@ static struct addrconf_sysctl_table
                        .data           =       &ipv6_devconf.accept_ra_rtr_pref,
                        .maxlen         =       sizeof(int),
                        .mode           =       0644,
-                       .proc_handler   =       &proc_dointvec,
+                       .proc_handler   =       proc_dointvec,
                },
                {
                        .ctl_name       =       NET_IPV6_RTR_PROBE_INTERVAL,
@@ -4151,8 +4196,8 @@ static struct addrconf_sysctl_table
                        .data           =       &ipv6_devconf.rtr_probe_interval,
                        .maxlen         =       sizeof(int),
                        .mode           =       0644,
-                       .proc_handler   =       &proc_dointvec_jiffies,
-                       .strategy       =       &sysctl_jiffies,
+                       .proc_handler   =       proc_dointvec_jiffies,
+                       .strategy       =       sysctl_jiffies,
                },
 #ifdef CONFIG_IPV6_ROUTE_INFO
                {
@@ -4161,7 +4206,7 @@ static struct addrconf_sysctl_table
                        .data           =       &ipv6_devconf.accept_ra_rt_info_max_plen,
                        .maxlen         =       sizeof(int),
                        .mode           =       0644,
-                       .proc_handler   =       &proc_dointvec,
+                       .proc_handler   =       proc_dointvec,
                },
 #endif
 #endif
@@ -4171,7 +4216,7 @@ static struct addrconf_sysctl_table
                        .data           =       &ipv6_devconf.proxy_ndp,
                        .maxlen         =       sizeof(int),
                        .mode           =       0644,
-                       .proc_handler   =       &proc_dointvec,
+                       .proc_handler   =       proc_dointvec,
                },
                {
                        .ctl_name       =       NET_IPV6_ACCEPT_SOURCE_ROUTE,
@@ -4179,7 +4224,7 @@ static struct addrconf_sysctl_table
                        .data           =       &ipv6_devconf.accept_source_route,
                        .maxlen         =       sizeof(int),
                        .mode           =       0644,
-                       .proc_handler   =       &proc_dointvec,
+                       .proc_handler   =       proc_dointvec,
                },
 #ifdef CONFIG_IPV6_OPTIMISTIC_DAD
                {
@@ -4188,7 +4233,7 @@ static struct addrconf_sysctl_table
                        .data           =       &ipv6_devconf.optimistic_dad,
                        .maxlen         =       sizeof(int),
                        .mode           =       0644,
-                       .proc_handler   =       &proc_dointvec,
+                       .proc_handler   =       proc_dointvec,
 
                },
 #endif
@@ -4198,11 +4243,27 @@ static struct addrconf_sysctl_table
                        .procname       =       "mc_forwarding",
                        .data           =       &ipv6_devconf.mc_forwarding,
                        .maxlen         =       sizeof(int),
-                       .mode           =       0644,
-                       .proc_handler   =       &proc_dointvec,
+                       .mode           =       0444,
+                       .proc_handler   =       proc_dointvec,
                },
 #endif
                {
+                       .ctl_name       =       CTL_UNNUMBERED,
+                       .procname       =       "disable_ipv6",
+                       .data           =       &ipv6_devconf.disable_ipv6,
+                       .maxlen         =       sizeof(int),
+                       .mode           =       0644,
+                       .proc_handler   =       proc_dointvec,
+               },
+               {
+                       .ctl_name       =       CTL_UNNUMBERED,
+                       .procname       =       "accept_dad",
+                       .data           =       &ipv6_devconf.accept_dad,
+                       .maxlen         =       sizeof(int),
+                       .mode           =       0644,
+                       .proc_handler   =       proc_dointvec,
+               },
+               {
                        .ctl_name       =       0,      /* sentinel */
                }
        },
@@ -4378,25 +4439,6 @@ int unregister_inet6addr_notifier(struct notifier_block *nb)
 
 EXPORT_SYMBOL(unregister_inet6addr_notifier);
 
-static void addrconf_net_exit(struct net *net)
-{
-       struct net_device *dev;
-
-       rtnl_lock();
-       /* clean dev list */
-       for_each_netdev(net, dev) {
-               if (__in6_dev_get(dev) == NULL)
-                       continue;
-               addrconf_ifdown(dev, 1);
-       }
-       addrconf_ifdown(net->loopback_dev, 2);
-       rtnl_unlock();
-}
-
-static struct pernet_operations addrconf_net_ops = {
-       .exit = addrconf_net_exit,
-};
-
 /*
  *     Init / cleanup code
  */
@@ -4438,10 +4480,6 @@ int __init addrconf_init(void)
        if (err)
                goto errlo;
 
-       err = register_pernet_device(&addrconf_net_ops);
-       if (err)
-               return err;
-
        register_netdevice_notifier(&ipv6_dev_notf);
 
        addrconf_verify(0);
@@ -4471,15 +4509,22 @@ errlo:
 void addrconf_cleanup(void)
 {
        struct inet6_ifaddr *ifa;
+       struct net_device *dev;
        int i;
 
        unregister_netdevice_notifier(&ipv6_dev_notf);
-       unregister_pernet_device(&addrconf_net_ops);
-
        unregister_pernet_subsys(&addrconf_ops);
 
        rtnl_lock();
 
+       /* clean dev list */
+       for_each_netdev(&init_net, dev) {
+               if (__in6_dev_get(dev) == NULL)
+                       continue;
+               addrconf_ifdown(dev, 1);
+       }
+       addrconf_ifdown(init_net.loopback_dev, 2);
+
        /*
         *      Check hash table.
         */
@@ -4500,6 +4545,4 @@ void addrconf_cleanup(void)
 
        del_timer(&addr_chk_timer);
        rtnl_unlock();
-
-       unregister_pernet_subsys(&addrconf_net_ops);
 }