[IPV6] ADDRCONF: Optimistic Duplicate Address Detection (RFC 4429) Support.
authorNeil Horman <nhorman@tuxdriver.com>
Thu, 26 Apr 2007 00:08:10 +0000 (17:08 -0700)
committerDavid S. Miller <davem@sunset.davemloft.net>
Thu, 26 Apr 2007 05:23:43 +0000 (22:23 -0700)
Nominally an autoconfigured IPv6 address is added to an interface in the
Tentative state (as per RFC 2462).  Addresses in this state remain in this
state while the Duplicate Address Detection process operates on them to
determine their uniqueness on the network.  During this period, these
tentative addresses may not be used for communication, increasing the time
before a node may be able to communicate on a network.  Using Optimistic
Duplicate Address Detection, autoconfigured addresses may be used
immediately for communication on the network, as long as certain rules are
followed to avoid conflicts with other nodes during the Duplicate Address
Detection process.

Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/if_addr.h
include/linux/ipv6.h
include/net/addrconf.h
net/ipv6/Kconfig
net/ipv6/addrconf.c
net/ipv6/ip6_output.c
net/ipv6/mcast.c
net/ipv6/ndisc.c

index d557e4c..43f3bed 100644 (file)
@@ -39,6 +39,7 @@ enum
 #define IFA_F_TEMPORARY                IFA_F_SECONDARY
 
 #define        IFA_F_NODAD             0x02
+#define IFA_F_OPTIMISTIC       0x04
 #define        IFA_F_HOMEADDRESS       0x10
 #define IFA_F_DEPRECATED       0x20
 #define IFA_F_TENTATIVE                0x40
index 713eb5e..e046b22 100644 (file)
@@ -178,6 +178,9 @@ struct ipv6_devconf {
 #endif
        __s32           proxy_ndp;
        __s32           accept_source_route;
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+       __s32           optimistic_dad;
+#endif
        void            *sysctl;
 };
 
@@ -208,6 +211,7 @@ enum {
        DEVCONF_PROXY_NDP,
        __DEVCONF_OPTIMISTIC_DAD,
        DEVCONF_ACCEPT_SOURCE_ROUTE,
+       DEVCONF_OPTIMISTIC_DAD,
        DEVCONF_MAX
 };
 
index 88df8fc..f3531d0 100644 (file)
@@ -73,7 +73,9 @@ extern int                    ipv6_get_saddr(struct dst_entry *dst,
 extern int                     ipv6_dev_get_saddr(struct net_device *dev, 
                                               struct in6_addr *daddr,
                                               struct in6_addr *saddr);
-extern int                     ipv6_get_lladdr(struct net_device *dev, struct in6_addr *);
+extern int                     ipv6_get_lladdr(struct net_device *dev,
+                                               struct in6_addr *addr,
+                                               unsigned char banned_flags);
 extern int                     ipv6_rcv_saddr_equal(const struct sock *sk, 
                                                      const struct sock *sk2);
 extern void                    addrconf_join_solict(struct net_device *dev,
index 79682ef..8e5d54f 100644 (file)
@@ -57,6 +57,16 @@ config IPV6_ROUTE_INFO
 
          If unsure, say N.
 
+config IPV6_OPTIMISTIC_DAD
+       bool "IPv6: Enable RFC 4429 Optimistic DAD (EXPERIMENTAL)"
+       depends on IPV6 && EXPERIMENTAL
+       ---help---
+         This is experimental support for optimistic Duplicate
+         Address Detection.  It allows for autoconfigured addresses
+         to be used more quickly.
+
+         If unsure, say N.
+
 config INET6_AH
        tristate "IPv6: AH transformation"
        depends on IPV6
index e035896..38274c2 100644 (file)
@@ -530,6 +530,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 */
@@ -706,6 +716,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) {
@@ -763,10 +774,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);
@@ -898,13 +916,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)) {
@@ -963,15 +982,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;
@@ -1111,7 +1132,8 @@ int ipv6_get_saddr(struct dst_entry *dst,
 
 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;
@@ -1122,7 +1144,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;
@@ -1674,6 +1696,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.
@@ -1681,7 +1710,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);
@@ -1889,6 +1919,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);
@@ -2065,8 +2100,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);
@@ -2134,7 +2177,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;
        }
@@ -2479,7 +2522,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);
 }
@@ -2501,7 +2548,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
        if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) ||
            !(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);
 
@@ -2521,6 +2568,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:
@@ -2545,7 +2600,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);
 
@@ -3364,6 +3419,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)
@@ -3578,7 +3636,14 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *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;
@@ -3899,6 +3964,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 */
                }
index 0d60fbc..7e25043 100644 (file)
@@ -863,6 +863,41 @@ static int ip6_dst_lookup_tail(struct sock *sk,
                        goto out_err_release;
        }
 
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+               /*
+                * Here if the dst entry we've looked up
+                * has a neighbour entry that is in the INCOMPLETE
+                * state and the src address from the flow is
+                * marked as OPTIMISTIC, we release the found
+                * dst entry and replace it instead with the
+                * dst entry of the nexthop router
+                */
+               if (!((*dst)->neighbour->nud_state & NUD_VALID)) {
+                       struct inet6_ifaddr *ifp;
+                       struct flowi fl_gw;
+                       int redirect;
+
+                       ifp = ipv6_get_ifaddr(&fl->fl6_src, (*dst)->dev, 1);
+
+                       redirect = (ifp && ifp->flags & IFA_F_OPTIMISTIC);
+                       if (ifp)
+                               in6_ifa_put(ifp);
+
+                       if (redirect) {
+                               /*
+                                * We need to get the dst entry for the
+                                * default router instead
+                                */
+                               dst_release(*dst);
+                               memcpy(&fl_gw, fl, sizeof(struct flowi));
+                               memset(&fl_gw.fl6_dst, 0, sizeof(struct in6_addr));
+                               *dst = ip6_route_output(sk, &fl_gw);
+                               if ((err = (*dst)->error))
+                                       goto out_err_release;
+                       }
+               }
+#endif
+
        return 0;
 
 out_err_release:
index a8d6625..924e249 100644 (file)
@@ -1411,7 +1411,7 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size)
 
        skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-       if (ipv6_get_lladdr(dev, &addr_buf)) {
+       if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
                /* <draft-ietf-magma-mld-source-05.txt>:
                 * use unspecified address as the source address
                 * when a valid link-local address is not available.
@@ -1791,7 +1791,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
 
        skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-       if (ipv6_get_lladdr(dev, &addr_buf)) {
+       if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
                /* <draft-ietf-magma-mld-source-05.txt>:
                 * use unspecified address as the source address
                 * when a valid link-local address is not available.
index 886c5be..b79b000 100644 (file)
@@ -449,6 +449,8 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
        ifp = ipv6_get_ifaddr(solicited_addr, dev, 1);
        if (ifp) {
                src_addr = solicited_addr;
+               if (ifp->flags & IFA_F_OPTIMISTIC)
+                       override = 0;
                in6_ifa_put(ifp);
        } else {
                if (ipv6_dev_get_saddr(dev, daddr, &tmpaddr))
@@ -544,7 +546,8 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
        int send_llinfo;
 
        if (saddr == NULL) {
-               if (ipv6_get_lladdr(dev, &addr_buf))
+               if (ipv6_get_lladdr(dev, &addr_buf,
+                                  (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
                        return;
                saddr = &addr_buf;
        }
@@ -624,9 +627,33 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
        struct sk_buff *skb;
        struct icmp6hdr *hdr;
        __u8 * opt;
+       struct inet6_ifaddr *ifp;
+       int send_sllao = dev->addr_len;
        int len;
        int err;
 
+
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+       /*
+        * According to section 2.2 of RFC 4429, we must not
+        * send router solicitations with a sllao from
+        * optimistic addresses, but we may send the solicitation
+        * if we don't include the sllao.  So here we check
+        * if our address is optimistic, and if so, we
+        * supress the inclusion of the sllao.
+        */
+       if (send_sllao) {
+               ifp = ipv6_get_ifaddr(saddr, dev, 1);
+               if (ifp) {
+                       if (ifp->flags & IFA_F_OPTIMISTIC)  {
+                               send_sllao=0;
+                               in6_ifa_put(ifp);
+                       }
+               } else {
+                       send_sllao = 0;
+               }
+       }
+#endif
        ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr,
                        dev->ifindex);
 
@@ -639,7 +666,7 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
                return;
 
        len = sizeof(struct icmp6hdr);
-       if (dev->addr_len)
+       if (send_sllao)
                len += ndisc_opt_addr_space(dev);
 
        skb = sock_alloc_send_skb(sk,
@@ -666,7 +693,7 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
 
        opt = (u8*) (hdr + 1);
 
-       if (dev->addr_len)
+       if (send_sllao)
                ndisc_fill_addr_option(opt, ND_OPT_SOURCE_LL_ADDR, dev->dev_addr,
                                       dev->addr_len, dev->type);
 
@@ -798,28 +825,39 @@ static void ndisc_recv_ns(struct sk_buff *skb)
        inc = ipv6_addr_is_multicast(daddr);
 
        if ((ifp = ipv6_get_ifaddr(&msg->target, dev, 1)) != NULL) {
-               if (ifp->flags & IFA_F_TENTATIVE) {
-                       /* Address is tentative. If the source
-                          is unspecified address, it is someone
-                          does DAD, otherwise we ignore solicitations
-                          until DAD timer expires.
-                        */
-                       if (!dad)
+
+               if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
+                       if (dad) {
+                               if (dev->type == ARPHRD_IEEE802_TR) {
+                                       unsigned char *sadr = skb->mac.raw;
+                                       if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
+                                           sadr[9] == dev->dev_addr[1] &&
+                                           sadr[10] == dev->dev_addr[2] &&
+                                           sadr[11] == dev->dev_addr[3] &&
+                                           sadr[12] == dev->dev_addr[4] &&
+                                           sadr[13] == dev->dev_addr[5]) {
+                                               /* looped-back to us */
+                                               goto out;
+                                       }
+                               }
+
+                               /*
+                                * We are colliding with another node
+                                * who is doing DAD
+                                * so fail our DAD process
+                                */
+                               addrconf_dad_failure(ifp);
                                goto out;
-                       if (dev->type == ARPHRD_IEEE802_TR) {
-                               unsigned char *sadr = skb->mac.raw;
-                               if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
-                                   sadr[9] == dev->dev_addr[1] &&
-                                   sadr[10] == dev->dev_addr[2] &&
-                                   sadr[11] == dev->dev_addr[3] &&
-                                   sadr[12] == dev->dev_addr[4] &&
-                                   sadr[13] == dev->dev_addr[5]) {
-                                       /* looped-back to us */
+                       } else {
+                               /*
+                                * This is not a dad solicitation.
+                                * If we are an optimistic node,
+                                * we should respond.
+                                * Otherwise, we should ignore it.
+                                */
+                               if (!(ifp->flags & IFA_F_OPTIMISTIC))
                                        goto out;
-                               }
                        }
-                       addrconf_dad_failure(ifp);
-                       return;
                }
 
                idev = ifp->idev;
@@ -1408,7 +1446,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
 
        dev = skb->dev;
 
-       if (ipv6_get_lladdr(dev, &saddr_buf)) {
+       if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
                ND_PRINTK2(KERN_WARNING
                           "ICMPv6 Redirect: no link-local address on %s\n",
                           dev->name);