tech-net archive

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

PATCH to mark IPv6 addresses DETACHED when down or link down



Hi List!

When a link status reports no carrier, or an interface is brought down the state of all IPv6 addresses remains the same. This means that when carrier is up or the interface is brought up no duplicate address checking is performed.

We currently have the rather unused IN6_IFF_DETACHED flag and the attached patch sets this when the interface is brought down or carrier lost. When it's brought up again with a carrier, the detached flag is cleared, the tentative flag is set and DAD starts. The routing socket is notified of all relevant changes via a RTM_NEWADDR message.

dhcpcd can now listen to this and knows that it can start dealing with IPv6 foo once the interface is up AND the locallink address is ready to be used. A nice side effect of this is that dhcpcd (latest git code) can now manipulate the routing table to prefer one interface over another on the same network and a ping6 command to the server works seamlessly while the wired and wireless interfaces are toggled up/down.

Comments welcome.

Thanks

Roy
Index: sys/net/if.c
===================================================================
RCS file: /cvsroot/src/sys/net/if.c,v
retrieving revision 1.262
diff -u -p -r1.262 if.c
--- sys/net/if.c        10 Mar 2013 19:46:12 -0000      1.262
+++ sys/net/if.c        30 May 2013 12:45:24 -0000
@@ -1342,6 +1342,12 @@ if_link_state_change(struct ifnet *ifp, 
        ifp->if_link_state = link_state;
        /* Notify that the link state has changed. */
        rt_ifmsg(ifp);
+#ifdef INET6
+       if (link_state == LINK_STATE_UP)
+               in6_if_up(ifp);
+       else if (link_state == LINK_STATE_DOWN)
+               in6_if_down(ifp);
+#endif
 #if NCARP > 0
        if (ifp->if_carp)
                carp_carpdev_state(ifp);
@@ -1363,11 +1369,14 @@ if_down(struct ifnet *ifp)
        IFADDR_FOREACH(ifa, ifp)
                pfctlinput(PRC_IFDOWN, ifa->ifa_addr);
        IFQ_PURGE(&ifp->if_snd);
+       rt_ifmsg(ifp);
 #if NCARP > 0
        if (ifp->if_carp)
                carp_carpdev_state(ifp);
 #endif
-       rt_ifmsg(ifp);
+#ifdef INET6
+       in6_if_down(ifp);
+#endif
 }
 
 /*
Index: sys/netinet6/in6.c
===================================================================
RCS file: /cvsroot/src/sys/netinet6/in6.c,v
retrieving revision 1.163
diff -u -p -r1.163 in6.c
--- sys/netinet6/in6.c  29 May 2013 12:07:58 -0000      1.163
+++ sys/netinet6/in6.c  30 May 2013 12:45:24 -0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: in6.c,v 1.163 2013/05/29 12:07:58 roy Exp $    */
+/*     $NetBSD: in6.c,v 1.162 2013/05/21 08:37:27 roy Exp $    */
 /*     $KAME: in6.c,v 1.198 2001/07/18 09:12:38 itojun Exp $   */
 
 /*
@@ -62,7 +62,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: in6.c,v 1.163 2013/05/29 12:07:58 roy Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in6.c,v 1.162 2013/05/21 08:37:27 roy Exp $");
 
 #include "opt_inet.h"
 #include "opt_pfil_hooks.h"
@@ -1004,6 +1004,9 @@ in6_update_ifa1(struct ifnet *ifp, struc
                ia->ia_ifa.ifa_netmask =
                    (struct sockaddr *)&ia->ia_prefixmask;
 
+               if (in6if_do_dad(ifp))
+                       ia->ia6_flags |= IN6_IFF_TENTATIVE;
+
                ia->ia_ifp = ifp;
                if ((oia = in6_ifaddr) != NULL) {
                        for ( ; oia->ia_next; oia = oia->ia_next)
@@ -1078,14 +1081,6 @@ in6_update_ifa1(struct ifnet *ifp, struc
         * configure address flags.
         */
        ia->ia6_flags = ifra->ifra_flags;
-       /*
-        * backward compatibility - if IN6_IFF_DEPRECATED is set from the
-        * userland, make it deprecated.
-        */
-       if ((ifra->ifra_flags & IN6_IFF_DEPRECATED) != 0) {
-               ia->ia6_lifetime.ia6t_pltime = 0;
-               ia->ia6_lifetime.ia6t_preferred = time_second;
-       }
 
        /*
         * Make the address tentative before joining multicast addresses,
@@ -1093,9 +1088,21 @@ in6_update_ifa1(struct ifnet *ifp, struc
         * source address.
         */
        ia->ia6_flags &= ~IN6_IFF_DUPLICATED;   /* safety */
-       if (hostIsNew && in6if_do_dad(ifp)) 
+       if (ifp->if_link_state == LINK_STATE_DOWN) {
+               ia->ia6_flags |= IN6_IFF_DETACHED;
+               ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
+       } else if (hostIsNew && in6if_do_dad(ifp))
                ia->ia6_flags |= IN6_IFF_TENTATIVE;
 
+       /*
+        * backward compatibility - if IN6_IFF_DEPRECATED is set from the
+        * userland, make it deprecated.
+        */
+       if ((ifra->ifra_flags & IN6_IFF_DEPRECATED) != 0) {
+               ia->ia6_lifetime.ia6t_pltime = 0;
+               ia->ia6_lifetime.ia6t_preferred = time_second;
+       }
+
        /* reset the interface and routing table appropriately. */
        if ((error = in6_ifinit(ifp, ia, &ifra->ifra_addr, hostIsNew)) != 0)
                goto unlink;
@@ -2167,10 +2174,25 @@ in6_if_up(struct ifnet *ifp)
        struct ifaddr *ifa;
        struct in6_ifaddr *ia;
 
+       /* Ensure it's sane to run DAD */
+       if (ifp->if_link_state == LINK_STATE_DOWN)
+               return;
+       if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
+               return;
+
        IFADDR_FOREACH(ifa, ifp) {
                if (ifa->ifa_addr->sa_family != AF_INET6)
                        continue;
                ia = (struct in6_ifaddr *)ifa;
+               if (ia->ia6_flags & IN6_IFF_DETACHED) {
+                       if (in6if_do_dad(ifp)) {
+                               ia->ia6_flags |= IN6_IFF_TENTATIVE;
+                               nd6log((LOG_ERR, "in6_if_up: "
+                                   "%s marked tentative\n",
+                                   ip6_sprintf(&ia->ia_addr.sin6_addr)));
+                       }
+                       ia->ia6_flags &= ~IN6_IFF_DETACHED;
+               }
                if (ia->ia6_flags & IN6_IFF_TENTATIVE) {
                        /*
                         * The TENTATIVE flag was likely set by hand
@@ -2190,6 +2212,29 @@ in6_if_up(struct ifnet *ifp)
        in6_ifattach(ifp, NULL);
 }
 
+/*
+ * Mark all addresses as detached.
+ */
+void
+in6_if_down(struct ifnet *ifp)
+{
+       struct ifaddr *ifa;
+       struct in6_ifaddr *ia;
+
+       IFADDR_FOREACH(ifa, ifp) {
+               if (ifa->ifa_addr->sa_family != AF_INET6)
+                       continue;
+               ia = (struct in6_ifaddr *)ifa;
+               if (!(ia->ia6_flags & IN6_IFF_DETACHED)) {
+                       nd6log((LOG_DEBUG, "in6_if_down: "
+                           "%s marked detached\n",
+                           ip6_sprintf(&ia->ia_addr.sin6_addr)));
+                       ia->ia6_flags |= IN6_IFF_DETACHED;
+                       nd6_newaddrmsg(ifa);
+               }
+       }
+}
+
 int
 in6if_do_dad(struct ifnet *ifp)
 {
Index: sys/netinet6/in6.h
===================================================================
RCS file: /cvsroot/src/sys/netinet6/in6.h,v
retrieving revision 1.71
diff -u -p -r1.71 in6.h
--- sys/netinet6/in6.h  27 Apr 2013 21:35:24 -0000      1.71
+++ sys/netinet6/in6.h  30 May 2013 12:45:24 -0000
@@ -701,6 +701,7 @@ int in6_localaddr(const struct in6_addr 
 int    in6_addrscope(const struct in6_addr *);
 struct in6_ifaddr *in6_ifawithifp(struct ifnet *, struct in6_addr *);
 extern void in6_if_up(struct ifnet *);
+extern void in6_if_down(struct ifnet *);
 #ifndef __FreeBSD__
 extern int in6_src_sysctl(void *, size_t *, void *, size_t);
 #endif


Home | Main Index | Thread Index | Old Index