tech-net archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

PATCH to only announce RTM_NEWADDR once IPv6 DAD completes



Hi List

Old discussion references:
http://mail-index4.netbsd.org/tech-net/2012/10/19/msg003676.html
http://mail-index.netbsd.org/current-users/2010/05/12/msg013334.html
http://mail-index.netbsd.org/current-users/2010/05/25/msg013529.html
http://mail-index.netbsd.org/tech-net/2010/05/25/msg002094.html

I agree with the latest proposal and attach a patch for review to address it. I've been testing this with a new dhcpcd build which listens for RTM_NEWADDR or a duplicate NA message and reacts accordingly. This is important as not only daemons fail to bind with a tentative address from a RTM_NEWADDR message, but one shot things normally run after successful address configuration, such as ntpdate also fail.

Although dhcpcd does have it's own DAD engine, it's not RFC conformation (no kernel allows it to be), so getting kernels to correctly announce RTM_NEWADDR when the address is ready does at least allow userland applications such as dhcpcd to take advantage of the kernel DAD.

Comments are welcome, probably comitting it over the weekend with hopefully a new dhcpcd shortly afterwards.

Thanks

Roy
Index: sys/netinet6/in6.c
===================================================================
RCS file: /cvsroot/src/sys/netinet6/in6.c,v
retrieving revision 1.161
diff -u -p -r1.161 in6.c
--- sys/netinet6/in6.c  23 Jun 2012 03:14:03 -0000      1.161
+++ sys/netinet6/in6.c  15 May 2013 08:14:30 -0000
@@ -157,11 +157,13 @@ in6_ifloop_request(int cmd, struct ifadd
        struct sockaddr_in6 lo_sa;
        struct sockaddr_in6 all1_sa;
        struct rtentry *nrt = NULL;
+       struct in6_ifaddr *ia;
        int e;
 
        sockaddr_in6_init(&all1_sa, &in6mask128, 0, 0, 0);
        sockaddr_in6_init(&lo_sa, &in6addr_loopback, 0, 0, 0);
 
+       ia = (struct in6_ifaddr *)ifa;
        /*
         * We specify the address itself as the gateway, and set the
         * RTF_LLINFO flag, so that the corresponding host route would have
@@ -176,7 +178,7 @@ in6_ifloop_request(int cmd, struct ifadd
                log(LOG_ERR, "in6_ifloop_request: "
                    "%s operation failed for %s (errno=%d)\n",
                    cmd == RTM_ADD ? "ADD" : "DELETE",
-                   ip6_sprintf(&((struct in6_ifaddr *)ifa)->ia_addr.sin6_addr),
+                   ip6_sprintf(&ia->ia_addr.sin6_addr),
                    e);
        }
 
@@ -191,13 +193,26 @@ in6_ifloop_request(int cmd, struct ifadd
                rt_replace_ifa(nrt, ifa);
 
        /*
-        * Report the addition/removal of the address to the routing socket.
+        * Report the addition/removal of the address to the routing socket
+        * unless the address is marked tentative, where it will be reported
+        * once DAD completes.
         * XXX: since we called rtinit for a p2p interface with a destination,
         *      we end up reporting twice in such a case.  Should we rather
         *      omit the second report?
         */
        if (nrt) {
-               rt_newaddrmsg(cmd, ifa, e, nrt);
+               if (cmd != RTM_ADD ||
+                   !(ia->ia6_flags & IN6_IFF_TENTATIVE))
+               {
+#if 0
+                       log(LOG_DEBUG,
+                           "in6_ifloop_request: announced %s (%s %d)\n",
+                           ip6_sprintf(&ia->ia_addr.sin6_addr),
+                           cmd == RTM_ADD ? "RTM_ADD" : "RTM_DELETE",
+                           ia->ia6_flags);
+#endif
+                       rt_newaddrmsg(cmd, ifa, e, nrt);
+               }
                if (cmd == RTM_DELETE) {
                        if (nrt->rt_refcnt <= 0) {
                                /* XXX: we should free the entry ourselves. */
@@ -1058,10 +1073,6 @@ in6_update_ifa1(struct ifnet *ifp, struc
        } else
                ia->ia6_lifetime.ia6t_preferred = 0;
 
-       /* reset the interface and routing table appropriately. */
-       if ((error = in6_ifinit(ifp, ia, &ifra->ifra_addr, hostIsNew)) != 0)
-               goto unlink;
-
        /*
         * configure address flags.
         */
@@ -1084,6 +1095,10 @@ in6_update_ifa1(struct ifnet *ifp, struc
        if (hostIsNew && in6if_do_dad(ifp)) 
                ia->ia6_flags |= IN6_IFF_TENTATIVE;
 
+       /* reset the interface and routing table appropriately. */
+       if ((error = in6_ifinit(ifp, ia, &ifra->ifra_addr, hostIsNew)) != 0)
+               goto unlink;
+
        /*
         * We are done if we have simply modified an existing address.
         */
Index: sys/netinet6/nd6_nbr.c
===================================================================
RCS file: /cvsroot/src/sys/netinet6/nd6_nbr.c,v
retrieving revision 1.96
diff -u -p -r1.96 nd6_nbr.c
--- sys/netinet6/nd6_nbr.c      22 Mar 2012 20:34:41 -0000      1.96
+++ sys/netinet6/nd6_nbr.c      15 May 2013 08:14:30 -0000
@@ -1013,6 +1013,31 @@ nd6_ifptomac(const struct ifnet *ifp)
        }
 }
 
+static void
+nd6_dad_announce(struct ifaddr *ifa)
+{
+       struct sockaddr_in6 all1_sa;
+       struct rtentry *nrt = NULL;
+       int e;
+       const char *ias;
+
+       sockaddr_in6_init(&all1_sa, &in6mask128, 0, 0, 0);
+       ias = ip6_sprintf(&((struct in6_ifaddr *)ifa)->ia_addr.sin6_addr);
+
+       e = rtrequest(RTM_GET, ifa->ifa_addr, ifa->ifa_addr,
+           (struct sockaddr *)&all1_sa, RTF_UP|RTF_HOST|RTF_LLINFO, &nrt);
+       if (e != 0) {
+               log(LOG_ERR, "nd6_dad_announce:"
+                   "RTM_GET operation failed for %s (errno=%d)\n", ias, e);
+       }
+
+       if (nrt) {
+               rt_newaddrmsg(RTM_ADD, ifa, e, nrt);
+               //log(LOG_DEBUG, "nd6_dad_announce: announced %s\n", ias);
+               nrt->rt_refcnt--;
+       }
+}
+
 TAILQ_HEAD(dadq_head, dadq);
 struct dadq {
        TAILQ_ENTRY(dadq) dad_list;
@@ -1085,12 +1110,9 @@ nd6_dad_start(struct ifaddr *ifa, int xt
                        ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???");
                return;
        }
-       if (ia->ia6_flags & IN6_IFF_ANYCAST) {
-               ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
-               return;
-       }
-       if (!ip6_dad_count) {
+       if (ia->ia6_flags & IN6_IFF_ANYCAST || !ip6_dad_count) {
                ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
+               nd6_dad_announce(ifa);
                return;
        }
        if (ifa->ifa_ifp == NULL)
@@ -1246,6 +1268,7 @@ nd6_dad_timer(struct ifaddr *ifa)
                         * No duplicate address found.
                         */
                        ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
+                       nd6_dad_announce(ifa);
 
                        nd6log((LOG_DEBUG,
                            "%s: DAD complete for %s - no duplicates found\n",


Home | Main Index | Thread Index | Old Index