- patches.apparmor/remove_suid_new_case_in_2.6.22.diff: Merge fix.
[linux-flexiantxendom0-3.2.10.git] / net / ipv6 / addrconf.c
index ac83b7e..9d0e006 100644 (file)
@@ -81,6 +81,7 @@
 #endif
 
 #include <asm/uaccess.h>
+#include <asm/unaligned.h>
 
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
@@ -208,9 +209,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
 };
 
 /* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */
-#if 0
 const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
-#endif
 const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
 
 static void addrconf_del_timer(struct inet6_ifaddr *ifp)
@@ -246,6 +245,37 @@ static void addrconf_mod_timer(struct inet6_ifaddr *ifp,
        add_timer(&ifp->timer);
 }
 
+static int snmp6_alloc_dev(struct inet6_dev *idev)
+{
+       int err = -ENOMEM;
+
+       if (!idev || !idev->dev)
+               return -EINVAL;
+
+       if (snmp_mib_init((void **)idev->stats.ipv6,
+                         sizeof(struct ipstats_mib),
+                         __alignof__(struct ipstats_mib)) < 0)
+               goto err_ip;
+       if (snmp_mib_init((void **)idev->stats.icmpv6,
+                         sizeof(struct icmpv6_mib),
+                         __alignof__(struct icmpv6_mib)) < 0)
+               goto err_icmp;
+
+       return 0;
+
+err_icmp:
+       snmp_mib_free((void **)idev->stats.ipv6);
+err_ip:
+       return err;
+}
+
+static int snmp6_free_dev(struct inet6_dev *idev)
+{
+       snmp_mib_free((void **)idev->stats.icmpv6);
+       snmp_mib_free((void **)idev->stats.ipv6);
+       return 0;
+}
+
 /* Nobody refers to this device, we may destroy it. */
 
 static void in6_dev_finish_destroy_rcu(struct rcu_head *head)
@@ -271,6 +301,8 @@ void in6_dev_finish_destroy(struct inet6_dev *idev)
        call_rcu(&idev->rcu, in6_dev_finish_destroy_rcu);
 }
 
+EXPORT_SYMBOL(in6_dev_finish_destroy);
+
 static struct inet6_dev * ipv6_add_dev(struct net_device *dev)
 {
        struct inet6_dev *ndev;
@@ -417,7 +449,7 @@ static void addrconf_forward_change(void)
        struct inet6_dev *idev;
 
        read_lock(&dev_base_lock);
-       for (dev=dev_base; dev; dev=dev->next) {
+       for_each_netdev(dev) {
                rcu_read_lock();
                idev = __in6_dev_get(dev);
                if (idev) {
@@ -528,6 +560,16 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
 
        ifa->rt = rt;
 
+       /*
+        * part one of RFC 4429, section 3.3
+        * We should not configure an address as
+        * optimistic if we do not yet know the link
+        * layer address of our nexhop router
+        */
+
+       if (rt->rt6i_nexthop == NULL)
+               ifa->flags &= ~IFA_F_OPTIMISTIC;
+
        ifa->idev = idev;
        in6_dev_hold(idev);
        /* For caller */
@@ -704,6 +746,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *i
        int tmp_plen;
        int ret = 0;
        int max_addresses;
+       u32 addr_flags;
 
        write_lock(&idev->lock);
        if (ift) {
@@ -761,10 +804,17 @@ retry:
        spin_unlock_bh(&ifp->lock);
 
        write_unlock(&idev->lock);
+
+       addr_flags = IFA_F_TEMPORARY;
+       /* set in addrconf_prefix_rcv() */
+       if (ifp->flags & IFA_F_OPTIMISTIC)
+               addr_flags |= IFA_F_OPTIMISTIC;
+
        ift = !max_addresses ||
              ipv6_count_addresses(idev) < max_addresses ?
                ipv6_add_addr(idev, &addr, tmp_plen,
-                             ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, IFA_F_TEMPORARY) : NULL;
+                             ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK,
+                             addr_flags) : NULL;
        if (!ift || IS_ERR(ift)) {
                in6_ifa_put(ifp);
                in6_dev_put(idev);
@@ -861,7 +911,7 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
        read_lock(&dev_base_lock);
        rcu_read_lock();
 
-       for (dev = dev_base; dev; dev=dev->next) {
+       for_each_netdev(dev) {
                struct inet6_dev *idev;
                struct inet6_ifaddr *ifa;
 
@@ -896,13 +946,14 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
                         * - Tentative Address (RFC2462 section 5.4)
                         *  - A tentative address is not considered
                         *    "assigned to an interface" in the traditional
-                        *    sense.
+                        *    sense, unless it is also flagged as optimistic.
                         * - Candidate Source Address (section 4)
                         *  - In any case, anycast addresses, multicast
                         *    addresses, and the unspecified address MUST
                         *    NOT be included in a candidate set.
                         */
-                       if (ifa->flags & IFA_F_TENTATIVE)
+                       if ((ifa->flags & IFA_F_TENTATIVE) &&
+                           (!(ifa->flags & IFA_F_OPTIMISTIC)))
                                continue;
                        if (unlikely(score.addr_type == IPV6_ADDR_ANY ||
                                     score.addr_type & IPV6_ADDR_MULTICAST)) {
@@ -961,15 +1012,17 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
                                }
                        }
 
-                       /* Rule 3: Avoid deprecated address */
+                       /* Rule 3: Avoid deprecated and optimistic addresses */
                        if (hiscore.rule < 3) {
                                if (ipv6_saddr_preferred(hiscore.addr_type) ||
-                                   !(ifa_result->flags & IFA_F_DEPRECATED))
+                                  (((ifa_result->flags &
+                                   (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)) == 0)))
                                        hiscore.attrs |= IPV6_SADDR_SCORE_PREFERRED;
                                hiscore.rule++;
                        }
                        if (ipv6_saddr_preferred(score.addr_type) ||
-                           !(ifa->flags & IFA_F_DEPRECATED)) {
+                          (((ifa_result->flags &
+                           (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)) == 0))) {
                                score.attrs |= IPV6_SADDR_SCORE_PREFERRED;
                                if (!(hiscore.attrs & IPV6_SADDR_SCORE_PREFERRED)) {
                                        score.rule = 3;
@@ -1107,8 +1160,10 @@ int ipv6_get_saddr(struct dst_entry *dst,
        return ipv6_dev_get_saddr(dst ? ip6_dst_idev(dst)->dev : NULL, daddr, saddr);
 }
 
+EXPORT_SYMBOL(ipv6_get_saddr);
 
-int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
+int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr,
+                   unsigned char banned_flags)
 {
        struct inet6_dev *idev;
        int err = -EADDRNOTAVAIL;
@@ -1119,7 +1174,7 @@ int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
 
                read_lock_bh(&idev->lock);
                for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {
-                       if (ifp->scope == IFA_LINK && !(ifp->flags&IFA_F_TENTATIVE)) {
+                       if (ifp->scope == IFA_LINK && !(ifp->flags & banned_flags)) {
                                ipv6_addr_copy(addr, &ifp->addr);
                                err = 0;
                                break;
@@ -1161,6 +1216,8 @@ int ipv6_chk_addr(struct in6_addr *addr, struct net_device *dev, int strict)
        return ifp != NULL;
 }
 
+EXPORT_SYMBOL(ipv6_chk_addr);
+
 static
 int ipv6_chk_same_addr(const struct in6_addr *addr, struct net_device *dev)
 {
@@ -1669,6 +1726,13 @@ ok:
 
                if (ifp == NULL && valid_lft) {
                        int max_addresses = in6_dev->cnf.max_addresses;
+                       u32 addr_flags = 0;
+
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+                       if (in6_dev->cnf.optimistic_dad &&
+                           !ipv6_devconf.forwarding)
+                               addr_flags = IFA_F_OPTIMISTIC;
+#endif
 
                        /* Do not allow to create too much of autoconfigured
                         * addresses; this would be too easy way to crash kernel.
@@ -1676,7 +1740,8 @@ ok:
                        if (!max_addresses ||
                            ipv6_count_addresses(in6_dev) < max_addresses)
                                ifp = ipv6_add_addr(in6_dev, &addr, pinfo->prefix_len,
-                                                   addr_type&IPV6_ADDR_SCOPE_MASK, 0);
+                                                   addr_type&IPV6_ADDR_SCOPE_MASK,
+                                                   addr_flags);
 
                        if (!ifp || IS_ERR(ifp)) {
                                in6_dev_put(in6_dev);
@@ -1884,6 +1949,11 @@ static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen,
 
                addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev,
                                      jiffies_to_clock_t(valid_lft * HZ), flags);
+               /*
+                * Note that section 3.1 of RFC 4429 indicates
+                * that the Optimistic flag should not be set for
+                * manually configured addresses
+                */
                addrconf_dad_start(ifp, 0);
                in6_ifa_put(ifp);
                addrconf_verify(0);
@@ -1994,7 +2064,7 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)
                return;
        }
 
-       for (dev = dev_base; dev != NULL; dev = dev->next) {
+       for_each_netdev(dev) {
                struct in_device * in_dev = __in_dev_get_rtnl(dev);
                if (in_dev && (dev->flags & IFF_UP)) {
                        struct in_ifaddr * ifa;
@@ -2060,8 +2130,16 @@ static void init_loopback(struct net_device *dev)
 static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr)
 {
        struct inet6_ifaddr * ifp;
+       u32 addr_flags = IFA_F_PERMANENT;
+
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+       if (idev->cnf.optimistic_dad &&
+           !ipv6_devconf.forwarding)
+               addr_flags |= IFA_F_OPTIMISTIC;
+#endif
+
 
-       ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, IFA_F_PERMANENT);
+       ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, addr_flags);
        if (!IS_ERR(ifp)) {
                addrconf_prefix_route(&ifp->addr, ifp->prefix_len, idev->dev, 0, 0);
                addrconf_dad_start(ifp, 0);
@@ -2076,15 +2154,6 @@ static void addrconf_dev_config(struct net_device *dev)
 
        ASSERT_RTNL();
 
-       if ((dev->type != ARPHRD_ETHER) &&
-           (dev->type != ARPHRD_FDDI) &&
-           (dev->type != ARPHRD_IEEE802_TR) &&
-           (dev->type != ARPHRD_ARCNET) &&
-           (dev->type != ARPHRD_INFINIBAND)) {
-               /* Alas, we support only Ethernet autoconfiguration. */
-               return;
-       }
-
        idev = addrconf_add_dev(dev);
        if (idev == NULL)
                return;
@@ -2129,7 +2198,7 @@ ipv6_inherit_linklocal(struct inet6_dev *idev, struct net_device *link_dev)
 {
        struct in6_addr lladdr;
 
-       if (!ipv6_get_lladdr(link_dev, &lladdr)) {
+       if (!ipv6_get_lladdr(link_dev, &lladdr, IFA_F_TENTATIVE)) {
                addrconf_add_linklocal(idev, &lladdr);
                return 0;
        }
@@ -2147,7 +2216,7 @@ static void ip6_tnl_add_linklocal(struct inet6_dev *idev)
                        return;
        }
        /* then try to inherit it from any device */
-       for (link_dev = dev_base; link_dev; link_dev = link_dev->next) {
+       for_each_netdev(link_dev) {
                if (!ipv6_inherit_linklocal(idev, link_dev))
                        return;
        }
@@ -2172,13 +2241,33 @@ static void addrconf_ip6_tnl_config(struct net_device *dev)
        ip6_tnl_add_linklocal(idev);
 }
 
+static int ipv6_hwtype(struct net_device *dev)
+{
+       if ((dev->type == ARPHRD_ETHER) ||
+           (dev->type == ARPHRD_LOOPBACK) ||
+           (dev->type == ARPHRD_SIT) ||
+           (dev->type == ARPHRD_TUNNEL6) ||
+           (dev->type == ARPHRD_FDDI) ||
+           (dev->type == ARPHRD_IEEE802_TR) ||
+           (dev->type == ARPHRD_ARCNET) ||
+           (dev->type == ARPHRD_INFINIBAND))
+               return 1;
+
+       return 0;
+}
+
 static int addrconf_notify(struct notifier_block *this, unsigned long event,
                           void * data)
 {
        struct net_device *dev = (struct net_device *) data;
-       struct inet6_dev *idev = __in6_dev_get(dev);
+       struct inet6_dev *idev;
        int run_pending = 0;
 
+       if (!ipv6_hwtype(dev))
+               return NOTIFY_OK;
+
+       idev = __in6_dev_get(dev);
+
        switch(event) {
        case NETDEV_REGISTER:
                if (!idev) {
@@ -2240,7 +2329,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
                default:
                        addrconf_dev_config(dev);
                        break;
-               };
+               }
                if (idev) {
                        if (run_pending)
                                addrconf_dad_run(idev);
@@ -2281,8 +2370,9 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
                break;
 
        case NETDEV_CHANGENAME:
-#ifdef CONFIG_SYSCTL
                if (idev) {
+                       snmp6_unregister_dev(idev);
+#ifdef CONFIG_SYSCTL
                        addrconf_sysctl_unregister(&idev->cnf);
                        neigh_sysctl_unregister(idev->nd_parms);
                        neigh_sysctl_register(dev, idev->nd_parms,
@@ -2290,10 +2380,11 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
                                              &ndisc_ifinfo_sysctl_change,
                                              NULL);
                        addrconf_sysctl_register(idev, &idev->cnf);
-               }
 #endif
+                       snmp6_register_dev(idev);
+               }
                break;
-       };
+       }
 
        return NOTIFY_OK;
 }
@@ -2474,7 +2565,11 @@ static void addrconf_dad_kick(struct inet6_ifaddr *ifp)
        unsigned long rand_num;
        struct inet6_dev *idev = ifp->idev;
 
-       rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
+       if (ifp->flags & IFA_F_OPTIMISTIC)
+               rand_num = 0;
+       else
+               rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
+
        ifp->probes = idev->cnf.dad_transmits;
        addrconf_mod_timer(ifp, AC_DAD, rand_num);
 }
@@ -2497,7 +2592,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
            !(dev->flags&IFF_MULTICAST) ||
            !(ifp->flags&IFA_F_TENTATIVE) ||
            ifp->flags & IFA_F_NODAD) {
-               ifp->flags &= ~IFA_F_TENTATIVE;
+               ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
                spin_unlock_bh(&ifp->lock);
                read_unlock_bh(&idev->lock);
 
@@ -2517,6 +2612,14 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
                addrconf_dad_stop(ifp);
                return;
        }
+
+       /*
+        * Optimistic nodes can start receiving
+        * Frames right away
+        */
+       if(ifp->flags & IFA_F_OPTIMISTIC)
+               ip6_ins_rt(ifp->rt);
+
        addrconf_dad_kick(ifp);
        spin_unlock_bh(&ifp->lock);
 out:
@@ -2541,7 +2644,7 @@ static void addrconf_dad_timer(unsigned long data)
                 * DAD was successful
                 */
 
-               ifp->flags &= ~IFA_F_TENTATIVE;
+               ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
                spin_unlock_bh(&ifp->lock);
                read_unlock_bh(&idev->lock);
 
@@ -3166,16 +3269,16 @@ static int inet6_dump_addr(struct sk_buff *skb, struct netlink_callback *cb,
 
        s_idx = cb->args[0];
        s_ip_idx = ip_idx = cb->args[1];
-       read_lock(&dev_base_lock);
 
-       for (dev = dev_base, idx = 0; dev; dev = dev->next, idx++) {
+       idx = 0;
+       for_each_netdev(dev) {
                if (idx < s_idx)
-                       continue;
+                       goto cont;
                if (idx > s_idx)
                        s_ip_idx = 0;
                ip_idx = 0;
                if ((idev = in6_dev_get(dev)) == NULL)
-                       continue;
+                       goto cont;
                read_lock_bh(&idev->lock);
                switch (type) {
                case UNICAST_ADDR:
@@ -3222,13 +3325,14 @@ static int inet6_dump_addr(struct sk_buff *skb, struct netlink_callback *cb,
                }
                read_unlock_bh(&idev->lock);
                in6_dev_put(idev);
+cont:
+               idx++;
        }
 done:
        if (err <= 0) {
                read_unlock_bh(&idev->lock);
                in6_dev_put(idev);
        }
-       read_unlock(&dev_base_lock);
        cb->args[0] = idx;
        cb->args[1] = ip_idx;
        return skb->len;
@@ -3361,6 +3465,9 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
 #endif
        array[DEVCONF_PROXY_NDP] = cnf->proxy_ndp;
        array[DEVCONF_ACCEPT_SOURCE_ROUTE] = cnf->accept_source_route;
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+       array[DEVCONF_OPTIMISTIC_DAD] = cnf->optimistic_dad;
+#endif
 }
 
 static inline size_t inet6_if_nlmsg_size(void)
@@ -3374,14 +3481,44 @@ static inline size_t inet6_if_nlmsg_size(void)
                        nla_total_size(4) /* IFLA_INET6_FLAGS */
                        + nla_total_size(sizeof(struct ifla_cacheinfo))
                        + nla_total_size(DEVCONF_MAX * 4) /* IFLA_INET6_CONF */
+                       + nla_total_size(IPSTATS_MIB_MAX * 8) /* IFLA_INET6_STATS */
+                       + nla_total_size(ICMP6_MIB_MAX * 8) /* IFLA_INET6_ICMP6STATS */
                 );
 }
 
+static inline void __snmp6_fill_stats(u64 *stats, void **mib, int items,
+                                     int bytes)
+{
+       int i;
+       int pad = bytes - sizeof(u64) * items;
+       BUG_ON(pad < 0);
+
+       /* Use put_unaligned() because stats may not be aligned for u64. */
+       put_unaligned(items, &stats[0]);
+       for (i = 1; i < items; i++)
+               put_unaligned(snmp_fold_field(mib, i), &stats[i]);
+
+       memset(&stats[items], 0, pad);
+}
+
+static void snmp6_fill_stats(u64 *stats, struct inet6_dev *idev, int attrtype,
+                            int bytes)
+{
+       switch(attrtype) {
+       case IFLA_INET6_STATS:
+               __snmp6_fill_stats(stats, (void **)idev->stats.ipv6, IPSTATS_MIB_MAX, bytes);
+               break;
+       case IFLA_INET6_ICMP6STATS:
+               __snmp6_fill_stats(stats, (void **)idev->stats.icmpv6, ICMP6_MIB_MAX, bytes);
+               break;
+       }
+}
+
 static int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev,
                             u32 pid, u32 seq, int event, unsigned int flags)
 {
        struct net_device *dev = idev->dev;
-       struct nlattr *conf;
+       struct nlattr *nla;
        struct ifinfomsg *hdr;
        struct nlmsghdr *nlh;
        void *protoinfo;
@@ -3421,12 +3558,22 @@ static int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev,
        ci.retrans_time = idev->nd_parms->retrans_time;
        NLA_PUT(skb, IFLA_INET6_CACHEINFO, sizeof(ci), &ci);
 
-       conf = nla_reserve(skb, IFLA_INET6_CONF, DEVCONF_MAX * sizeof(s32));
-       if (conf == NULL)
+       nla = nla_reserve(skb, IFLA_INET6_CONF, DEVCONF_MAX * sizeof(s32));
+       if (nla == NULL)
+               goto nla_put_failure;
+       ipv6_store_devconf(&idev->cnf, nla_data(nla), nla_len(nla));
+
+       /* XXX - MC not implemented */
+
+       nla = nla_reserve(skb, IFLA_INET6_STATS, IPSTATS_MIB_MAX * sizeof(u64));
+       if (nla == NULL)
                goto nla_put_failure;
-       ipv6_store_devconf(&idev->cnf, nla_data(conf), nla_len(conf));
+       snmp6_fill_stats(nla_data(nla), idev, IFLA_INET6_STATS, nla_len(nla));
 
-       /* XXX - Statistics/MC not implemented */
+       nla = nla_reserve(skb, IFLA_INET6_ICMP6STATS, ICMP6_MIB_MAX * sizeof(u64));
+       if (nla == NULL)
+               goto nla_put_failure;
+       snmp6_fill_stats(nla_data(nla), idev, IFLA_INET6_ICMP6STATS, nla_len(nla));
 
        nla_nest_end(skb, protoinfo);
        return nlmsg_end(skb, nlh);
@@ -3444,16 +3591,19 @@ static int inet6_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
        struct inet6_dev *idev;
 
        read_lock(&dev_base_lock);
-       for (dev=dev_base, idx=0; dev; dev = dev->next, idx++) {
+       idx = 0;
+       for_each_netdev(dev) {
                if (idx < s_idx)
-                       continue;
+                       goto cont;
                if ((idev = in6_dev_get(dev)) == NULL)
-                       continue;
+                       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;
+cont:
+               idx++;
        }
        read_unlock(&dev_base_lock);
        cb->args[0] = idx;
@@ -3552,30 +3702,20 @@ errout:
                rtnl_set_sk_err(RTNLGRP_IPV6_PREFIX, err);
 }
 
-static struct rtnetlink_link inet6_rtnetlink_table[RTM_NR_MSGTYPES] = {
-       [RTM_GETLINK - RTM_BASE] = { .dumpit    = inet6_dump_ifinfo, },
-       [RTM_NEWADDR - RTM_BASE] = { .doit      = inet6_rtm_newaddr, },
-       [RTM_DELADDR - RTM_BASE] = { .doit      = inet6_rtm_deladdr, },
-       [RTM_GETADDR - RTM_BASE] = { .doit      = inet6_rtm_getaddr,
-                                    .dumpit    = inet6_dump_ifaddr, },
-       [RTM_GETMULTICAST - RTM_BASE] = { .dumpit = inet6_dump_ifmcaddr, },
-       [RTM_GETANYCAST - RTM_BASE] = { .dumpit = inet6_dump_ifacaddr, },
-       [RTM_NEWROUTE - RTM_BASE] = { .doit     = inet6_rtm_newroute, },
-       [RTM_DELROUTE - RTM_BASE] = { .doit     = inet6_rtm_delroute, },
-       [RTM_GETROUTE - RTM_BASE] = { .doit     = inet6_rtm_getroute,
-                                     .dumpit   = inet6_dump_fib, },
-#ifdef CONFIG_IPV6_MULTIPLE_TABLES
-       [RTM_GETRULE  - RTM_BASE] = { .dumpit   = fib6_rules_dump,   },
-#endif
-};
-
 static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
 {
        inet6_ifa_notify(event ? : RTM_NEWADDR, ifp);
 
        switch (event) {
        case RTM_NEWADDR:
-               ip6_ins_rt(ifp->rt);
+               /*
+                * If the address was optimistic
+                * we inserted the route at the start of
+                * our DAD process, so we don't need
+                * to do it again
+                */
+               if (!(ifp->rt->rt6i_node))
+                       ip6_ins_rt(ifp->rt);
                if (ifp->idev->cnf.forwarding)
                        addrconf_join_anycast(ifp);
                break;
@@ -3896,6 +4036,17 @@ static struct addrconf_sysctl_table
                        .mode           =       0644,
                        .proc_handler   =       &proc_dointvec,
                },
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+               {
+                       .ctl_name       =       CTL_UNNUMBERED,
+                       .procname       =       "optimistic_dad",
+                       .data           =       &ipv6_devconf.optimistic_dad,
+                       .maxlen         =       sizeof(int),
+                       .mode           =       0644,
+                       .proc_handler   =       &proc_dointvec,
+
+               },
+#endif
                {
                        .ctl_name       =       0,      /* sentinel */
                }
@@ -4023,11 +4174,15 @@ int register_inet6addr_notifier(struct notifier_block *nb)
        return atomic_notifier_chain_register(&inet6addr_chain, nb);
 }
 
+EXPORT_SYMBOL(register_inet6addr_notifier);
+
 int unregister_inet6addr_notifier(struct notifier_block *nb)
 {
        return atomic_notifier_chain_unregister(&inet6addr_chain,nb);
 }
 
+EXPORT_SYMBOL(unregister_inet6addr_notifier);
+
 /*
  *     Init / cleanup code
  */
@@ -4062,11 +4217,26 @@ int __init addrconf_init(void)
                return err;
 
        ip6_null_entry.rt6i_idev = in6_dev_get(&loopback_dev);
+#ifdef CONFIG_IPV6_MULTIPLE_TABLES
+       ip6_prohibit_entry.rt6i_idev = in6_dev_get(&loopback_dev);
+       ip6_blk_hole_entry.rt6i_idev = in6_dev_get(&loopback_dev);
+#endif
 
        register_netdevice_notifier(&ipv6_dev_notf);
 
        addrconf_verify(0);
-       rtnetlink_links[PF_INET6] = inet6_rtnetlink_table;
+
+       err = __rtnl_register(PF_INET6, RTM_GETLINK, NULL, inet6_dump_ifinfo);
+       if (err < 0)
+               goto errout;
+
+       /* Only the first call to __rtnl_register can fail */
+       __rtnl_register(PF_INET6, RTM_NEWADDR, inet6_rtm_newaddr, NULL);
+       __rtnl_register(PF_INET6, RTM_DELADDR, inet6_rtm_deladdr, NULL);
+       __rtnl_register(PF_INET6, RTM_GETADDR, inet6_rtm_getaddr, inet6_dump_ifaddr);
+       __rtnl_register(PF_INET6, RTM_GETMULTICAST, NULL, inet6_dump_ifmcaddr);
+       __rtnl_register(PF_INET6, RTM_GETANYCAST, NULL, inet6_dump_ifacaddr);
+
 #ifdef CONFIG_SYSCTL
        addrconf_sysctl.sysctl_header =
                register_sysctl_table(addrconf_sysctl.addrconf_root_dir);
@@ -4074,6 +4244,10 @@ int __init addrconf_init(void)
 #endif
 
        return 0;
+errout:
+       unregister_netdevice_notifier(&ipv6_dev_notf);
+
+       return err;
 }
 
 void __exit addrconf_cleanup(void)
@@ -4085,7 +4259,6 @@ void __exit addrconf_cleanup(void)
 
        unregister_netdevice_notifier(&ipv6_dev_notf);
 
-       rtnetlink_links[PF_INET6] = NULL;
 #ifdef CONFIG_SYSCTL
        addrconf_sysctl_unregister(&ipv6_devconf_dflt);
        addrconf_sysctl_unregister(&ipv6_devconf);
@@ -4097,7 +4270,7 @@ void __exit addrconf_cleanup(void)
         *      clean dev list.
         */
 
-       for (dev=dev_base; dev; dev=dev->next) {
+       for_each_netdev(dev) {
                if ((idev = __in6_dev_get(dev)) == NULL)
                        continue;
                addrconf_ifdown(dev, 1);