]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Tentative does not mean RTM_DELADDR.
authorRoy Marples <roy@marples.name>
Thu, 6 Mar 2014 19:11:55 +0000 (19:11 +0000)
committerRoy Marples <roy@marples.name>
Thu, 6 Mar 2014 19:11:55 +0000 (19:11 +0000)
This fixes RTM address handling on FreeBSD and OpenBSD.

Instead of listening for duplicate address messages for
FreeBSD and OpenBSD simply poll for the address flag changing.
This means less code to maintain and hopefully less error-prone.

Remove dead code which send a DAD as this is, and should,
be done entirely in the kernel.

configure
dhcp6.c
if-bsd.c
ipv4.c
ipv6.c
ipv6.h
ipv6nd.c
ipv6nd.h

index 2c7640be78fdbd81b55024c993b3b3d27e167c59..57258b30d0bcba94a577ad631418f26989eb010f 100755 (executable)
--- a/configure
+++ b/configure
@@ -215,11 +215,11 @@ if [ -z "$OS" ]; then
 
         # Work with cpu-kernel-os, ie Debian
        case "$VENDOR" in
-       linux|kfreebsd) OS=$VENDOR; VENDOR= ;;
+       linux*|kfreebsd*) OS=$VENDOR; VENDOR= ;;
        esac
        # Special case
        case "$OS" in
-       gnu) OS=hurd;; # No HURD support as yet
+       gnu*) OS=hurd;; # No HURD support as yet
        esac
 fi
 
@@ -286,7 +286,7 @@ CFLAGS+=    -Wpointer-arith -Wstrict-overflow
 CFLAGS+=       -Wdeclaration-after-statement
 EOF
        case "$OS" in
-       openbsd       ;; # OpenBSD has many redundant decs in system headers
+       openbsd*)       ;; # OpenBSD has many redundant decs in system headers
        *)              echo "CFLAGS+=  -Wredundant-decls" >>$CONFIG_MK;;
        esac
 fi
@@ -306,7 +306,7 @@ if [ -n "$FORK" -a "$FORK" != yes -a "$FORK" != true ]; then
 fi
 
 case "$OS" in
-linux)
+linux*)
        echo "CPPFLAGS+=        -D_BSD_SOURCE -D_XOPEN_SOURCE=700" >>$CONFIG_MK
        if [ -z "$INET" -o "$INET" = yes ]; then
                echo "DHCPCD_SRCS+=     lpf.c" >>$CONFIG_MK
@@ -315,7 +315,7 @@ linux)
        echo "DHCPCD_SRCS+=     platform-linux.c" >>$CONFIG_MK
        echo "LDADD+=           -lrt -ldl" >>$CONFIG_MK
        ;;
-kfreebsd)
+kfreebsd*)
        echo "CPPFLAGS+=        -D_GNU_SOURCE" >>$CONFIG_MK
        if [ -z "$INET" -o "$INET" = yes ]; then
                echo "DHCPCD_SRCS+=     bpf.c" >>$CONFIG_MK
@@ -543,7 +543,7 @@ int main(void) {
 EOF
        if $XCC _posix_spawn.c -o _posix_spawn 2>/dev/null; then
                case "$OS" in
-               openbsd) printf "broken OpenBSD ... "; POSIX_SPAWN=no;;
+               openbsd*) printf "broken OpenBSD ... "; POSIX_SPAWN=no;;
                *) POSIX_SPAWN=yes;;
                esac
        else
diff --git a/dhcp6.c b/dhcp6.c
index eab245401ee2825052a90872fffb13ef89a63403..b13720b8306905a38556d0758fb3574a6cb56c8a 100644 (file)
--- a/dhcp6.c
+++ b/dhcp6.c
@@ -1258,17 +1258,12 @@ dhcp6_dadcallback(void *arg)
        int wascompleted;
 
        wascompleted = (ap->flags & IPV6_AF_DADCOMPLETED);
-       ipv6nd_cancelprobeaddr(ap);
        ap->flags |= IPV6_AF_DADCOMPLETED;
        if (ap->flags & IPV6_AF_DUPLICATED)
                /* XXX FIXME
                 * We should decline the address */
                syslog(LOG_WARNING, "%s: DAD detected %s",
                    ap->iface->name, ap->saddr);
-#ifdef IPV6_SEND_DAD
-       else
-               ipv6_addaddr(ap);
-#endif
 
        if (!wascompleted) {
                ifp = ap->iface;
@@ -1899,7 +1894,7 @@ dhcp6_delegate_prefix(struct interface *ifp)
                }
                if (k && !carrier_warned) {
                        ifd_state = D6_STATE(ifd);
-                       ipv6nd_probeaddrs(&ifd_state->addrs);
+                       ipv6_addaddrs(&ifd_state->addrs);
                }
        }
 
@@ -1979,7 +1974,7 @@ dhcp6_find_delegates(struct interface *ifp)
                syslog(LOG_INFO, "%s: adding delegated prefixes", ifp->name);
                state = D6_STATE(ifp);
                state->state = DH6S_DELEGATED;
-               ipv6nd_probeaddrs(&state->addrs);
+               ipv6_addaddrs(&state->addrs);
                ipv6_buildroutes(ifp->ctx);
        }
        return k;
@@ -2393,7 +2388,7 @@ recv:
                if (ifp->options->ia_type == D6_OPTION_IA_PD)
                        dhcp6_delegate_prefix(ifp);
 
-               ipv6nd_probeaddrs(&state->addrs);
+               ipv6_addaddrs(&state->addrs);
                if (state->state == DH6S_INFORMED)
                        syslog(has_new ? LOG_INFO : LOG_DEBUG,
                            "%s: refresh in %"PRIu32" seconds",
index de25b28d6b15bd3f7f16dca8e43c26ac66d02e66..b027315beb726a710b3c1c23a038987ae3dd0169 100644 (file)
--- a/if-bsd.c
+++ b/if-bsd.c
@@ -412,7 +412,7 @@ if_route6(const struct rt6 *rt, int action)
        char *bp = rtm.buffer;
        size_t l;
        int s, retval;
-       const struct ipv6_addr_l *lla;
+       const struct ipv6_addr *lla;
 
        if ((s = socket(PF_ROUTE, SOCK_RAW, 0)) == -1)
                return -1;
@@ -586,7 +586,7 @@ manage_link(struct dhcpcd_ctx *ctx)
 #ifdef INET
        struct rt rt;
 #endif
-#if defined(INET6) && !defined(LISTEN_DAD)
+#ifdef INET6
        struct in6_addr ia6;
        struct sockaddr_in6 *sin6;
        int ifa_flags;
@@ -705,7 +705,7 @@ manage_link(struct dhcpcd_ctx *ctx)
                                            &rt.dest, &rt.net, &rt.gate);
                                        break;
 #endif
-#if defined(INET6) && !defined(LISTEN_DAD)
+#ifdef INET6
                                case AF_INET6:
                                        sin6 = (struct sockaddr_in6*)(void *)
                                            rti_info[RTAX_IFA];
diff --git a/ipv4.c b/ipv4.c
index 2f81b1f33659f7029e43b05ece96aec5f414da27..4f1c54039c5ba7dbd85e9c705a5aac3d19388aa8 100644 (file)
--- a/ipv4.c
+++ b/ipv4.c
@@ -722,21 +722,29 @@ ipv4_handleifa(struct dhcpcd_ctx *ctx,
 
        if (ifs == NULL)
                ifs = ctx->ifaces;
-       if (ifs == NULL)
+       if (ifs == NULL) {
+               errno = ESRCH;
                return;
-       if (addr->s_addr == INADDR_ANY)
+       }
+       if (addr->s_addr == INADDR_ANY) {
+               errno = EINVAL;
                return;
+       }
 
        TAILQ_FOREACH(ifp, ifs, next) {
                if (strcmp(ifp->name, ifname) == 0)
                        break;
        }
-       if (ifp == NULL)
+       if (ifp == NULL) {
+               errno = ESRCH;
                return;
-
+       }
        state = ipv4_getstate(ifp);
-       if (state == NULL)
+       if (state == NULL) {
+               errno = ENOENT;
                return;
+       }
+
        ap = ipv4_findaddr(ifp, addr, net);
        if (type == RTM_NEWADDR && ap == NULL) {
                ap = malloc(sizeof(*ap));
diff --git a/ipv6.c b/ipv6.c
index 018a62a7ee18f9e4aa026098efbc5d68471d86a9..ed5882e0bcc7e78831df43bd4beb6d4c2985ab00 100644 (file)
--- a/ipv6.c
+++ b/ipv6.c
@@ -38,6 +38,7 @@
    /* Match Linux defines to BSD */
 #  define IN6_IFF_TENTATIVE    (IFA_F_TENTATIVE | IFA_F_OPTIMISTIC)
 #  define IN6_IFF_DUPLICATED   IFA_F_DADFAILED
+#  define IN6_IFF_DETACHED     0
 #else
 #  include <net/if.h>
 #ifdef __FreeBSD__ /* Needed so that including netinet6/in6_var.h works */
 #include "ipv6.h"
 #include "ipv6nd.h"
 
+#ifdef IPV6_POLLADDRFLAG
+#  warning kernel does not report IPv6 address flag changes
+#  warning polling tentative address flags periodically instead
+#endif
+
+#define IN6_IFF_NOTUSEABLE \
+       (IN6_IFF_TENTATIVE | IN6_IFF_DUPLICATED | IN6_IFF_DETACHED)
+
 /* Hackery at it's finest. */
 #ifndef s6_addr32
 #  define s6_addr32 __u6_addr.__u6_addr32
 #endif
 
-#define EUI64_GBIT     0x01
-#define EUI64_UBIT     0x02
-#define EUI64_TO_IFID(in6)     do {(in6)->s6_addr[8] ^= EUI64_UBIT; } \
-                                   while (/*CONSTCOND*/ 0)
-#define EUI64_GROUP(in6)       ((in6)->s6_addr[8] & EUI64_GBIT)
-
-
 struct ipv6_ctx *
 ipv6_init(struct dhcpcd_ctx *dhcpcd_ctx)
 {
@@ -117,6 +119,16 @@ ipv6_init(struct dhcpcd_ctx *dhcpcd_ctx)
        ctx->nd_fd = -1;
        ctx->dhcp_fd = -1;
 
+#ifdef IPV6_POLLADDRFLAG
+       if (!ctx->polladdr_warned) {
+               syslog(LOG_WARNING, 
+                   "kernel does not report IPv6 address flag changes");
+               syslog(LOG_WARNING,
+                   "polling tentative address flags periodically instead");
+               ctx->polladdr_warned = 1;
+       }
+#endif
+
        dhcpcd_ctx->ipv6 = ctx;
        return ctx;
 }
@@ -157,91 +169,24 @@ int
 ipv6_makeaddr(struct in6_addr *addr, const struct interface *ifp,
     const struct in6_addr *prefix, int prefix_len)
 {
-       const struct ipv6_addr_l *ap;
-#if 0
-       static u_int8_t allzero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
-       static u_int8_t allone[8] =
-           { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
-#endif
+       const struct ipv6_addr *ap;
 
        if (prefix_len < 0 || prefix_len > 64) {
                errno = EINVAL;
                return -1;
        }
-
-       memcpy(addr, prefix, sizeof(*prefix));
-
-       /* Try and make the address from the first local-link address */
-       ap = ipv6_linklocal(ifp);
-       if (ap) {
-               addr->s6_addr32[2] = ap->addr.s6_addr32[2];
-               addr->s6_addr32[3] = ap->addr.s6_addr32[3];
-               return 0;
-       }
-
-       /* Because we delay a few functions until we get a local-link address
-        * there is little point in the below code.
-        * It exists in-case we need to create local-link addresses
-        * ourselves, but then we would need to be able to send RFC
-        * conformant DAD requests.
-        * See ipv6ns.c for why we need the kernel to do this. */
-       errno = ENOENT;
-       return -1;
-
-#if 0
-       /* Make an EUI64 based off our hardware address */
-       switch (ifp->family) {
-       case ARPHRD_ETHER:
-               /* Check for a valid hardware address */
-               if (ifp->hwlen != 8 && ifp->hwlen != 6) {
-                       errno = ENOTSUP;
-                       return -1;
-               }
-               if (memcmp(ifp->hwaddr, allzero, ifp->hwlen) == 0 ||
-                   memcmp(ifp->hwaddr, allone, ifp->hwlen) == 0)
-               {
-                       errno = EINVAL;
-                       return -1;
-               }
-
-               /* make a EUI64 address */
-               if (ifp->hwlen == 8)
-                       memcpy(&addr->s6_addr[8], ifp->hwaddr, 8);
-               else if (ifp->hwlen == 6) {
-                       addr->s6_addr[8] = ifp->hwaddr[0];
-                       addr->s6_addr[9] = ifp->hwaddr[1];
-                       addr->s6_addr[10] = ifp->hwaddr[2];
-                       addr->s6_addr[11] = 0xff;
-                       addr->s6_addr[12] = 0xfe;
-                       addr->s6_addr[13] = ifp->hwaddr[3];
-                       addr->s6_addr[14] = ifp->hwaddr[4];
-                       addr->s6_addr[15] = ifp->hwaddr[5];
-               }
-               break;
-       default:
-               errno = ENOTSUP;
-               return -1;
-       }
-
-       /* sanity check: g bit must not indicate "group" */
-       if (EUI64_GROUP(addr)) {
-               errno = EINVAL;
-               return -1;
-       }
-
-       EUI64_TO_IFID(addr);
-
-       /* sanity check: ifid must not be all zero, avoid conflict with
-        * subnet router anycast */
-       if ((addr->s6_addr[8] & ~(EUI64_GBIT | EUI64_UBIT)) == 0x00 &&
-               memcmp(&addr->s6_addr[9], allzero, 7) == 0)
-       {
-               errno = EINVAL;
+       if ((ap = ipv6_linklocal(ifp)) == NULL) {
+               /* We delay a few functions until we get a local-link address
+                * so this should never be hit. */
+               errno = ENOENT;
                return -1;
        }
 
+       /* Make the address from the first local-link address */
+       memcpy(addr, prefix, sizeof(*prefix));
+       addr->s6_addr32[2] = ap->addr.s6_addr32[2];
+       addr->s6_addr32[3] = ap->addr.s6_addr32[3];
        return 0;
-#endif
 }
 
 int
@@ -418,7 +363,7 @@ ipv6_userprefix(
        return 0;
 }
 
-#ifdef LISTEN_DAD
+#ifdef IPV6_POLLADDRFLAG
 void
 ipv6_checkaddrflags(void *arg)
 {
@@ -480,18 +425,60 @@ ipv6_addaddr(struct ipv6_addr *ap)
                syslog(LOG_DEBUG,
                    "%s: vltime %"PRIu32" seconds, pltime %"PRIu32" seconds",
                    ap->iface->name, ap->prefix_vltime, ap->prefix_pltime);
+
+#ifdef IPV6_POLLADDRFLAG
+       eloop_timeout_delete(ap->iface->ctx->eloop,
+               ipv6_checkaddrflags, ap);
+       if (!(ap->flags & IPV6_AF_DADCOMPLETED)) {
+               struct timeval tv;
+
+               ms_to_tv(&tv, RETRANS_TIMER / 2);
+               eloop_timeout_add_tv(ap->iface->ctx->eloop,
+                   &tv, ipv6_checkaddrflags, ap);
+       }
+#endif
+
        return 0;
 }
 
+ssize_t
+ipv6_addaddrs(struct ipv6_addrhead *addrs)
+{
+       struct ipv6_addr *ap, *apn;
+       ssize_t i;
+
+       i = 0;
+       TAILQ_FOREACH_SAFE(ap, addrs, next, apn) {
+               if (ap->prefix_vltime == 0) {
+                       TAILQ_REMOVE(addrs, ap, next);
+                       if (ap->flags & IPV6_AF_ADDED) {
+                               syslog(LOG_INFO, "%s: deleting address %s",
+                                   ap->iface->name, ap->saddr);
+                               i++;
+                               if (!IN6_IS_ADDR_UNSPECIFIED(&ap->addr) &&
+                                   del_address6(ap) == -1 &&
+                                   errno != EADDRNOTAVAIL && errno != ENXIO)
+                                       syslog(LOG_ERR, "del_address6 %m");
+                       }
+                       eloop_q_timeout_delete(ap->iface->ctx->eloop,
+                           0, NULL, ap);
+                       free(ap);
+               } else if (!IN6_IS_ADDR_UNSPECIFIED(&ap->addr)) {
+                       if (ap->flags & IPV6_AF_NEW)
+                               i++;
+                       ipv6_addaddr(ap);
+               }
+       }
+
+       return i;
+}
+
+
 void
 ipv6_freedrop_addrs(struct ipv6_addrhead *addrs, int drop,
     const struct interface *ifd)
 {
        struct ipv6_addr *ap, *apn;
-#ifdef LISTEN_DAD
-       struct ipv6_state *state;
-       struct ipv6_addr_l *lap;
-#endif
 
        TAILQ_FOREACH_SAFE(ap, addrs, next, apn) {
                if (ifd && ap->delegating_iface != ifd)
@@ -513,24 +500,6 @@ ipv6_freedrop_addrs(struct ipv6_addrhead *addrs, int drop,
                        if (del_address6(ap) == -1 &&
                            errno != EADDRNOTAVAIL && errno != ENXIO)
                                syslog(LOG_ERR, "del_address6 %m");
-
-#ifdef LISTEN_DAD
-                       /* Remove it from our internal state as we cannot
-                        * reliably listen to RTM messages */
-                       state = IPV6_STATE(ap->iface);
-                       if (state) {
-                               TAILQ_FOREACH(lap, &state->addrs, next) {
-                                       if (IN6_ARE_ADDR_EQUAL(&lap->addr,
-                                           &ap->addr))
-                                       {
-                                               TAILQ_REMOVE(&state->addrs,
-                                                   lap, next);
-                                               free(lap);
-                                               break;
-                                       }
-                               }
-                       }
-#endif
                }
                free(ap);
        }
@@ -562,7 +531,7 @@ ipv6_handleifa(struct dhcpcd_ctx *ctx,
 {
        struct interface *ifp;
        struct ipv6_state *state;
-       struct ipv6_addr_l *ap;
+       struct ipv6_addr *ap;
        struct ll_callback *cb;
 
 #if 0
@@ -573,16 +542,6 @@ ipv6_handleifa(struct dhcpcd_ctx *ctx,
            ifname, cmd, buf, flags);
 #endif
 
-       /* Safety, remove tentative addresses */
-       if (cmd == RTM_NEWADDR) {
-               if (flags & (IN6_IFF_TENTATIVE | IN6_IFF_DUPLICATED))
-                       cmd = RTM_DELADDR;
-#ifdef IN6_IFF_DETACHED
-               if (flags & IN6_IFF_DETACHED)
-                       cmd = RTM_DELADDR;
-#endif
-       }
-
        if (ifs == NULL)
                ifs = ctx->ifaces;
        if (ifs == NULL) {
@@ -597,20 +556,17 @@ ipv6_handleifa(struct dhcpcd_ctx *ctx,
                errno = ESRCH;
                return;
        }
-
        state = ipv6_getstate(ifp);
-       if (state == NULL)
+       if (state == NULL) {
+               errno = ENOENT;
                return;
+       }
 
        if (!IN6_IS_ADDR_LINKLOCAL(addr)) {
                ipv6nd_handleifa(ctx, cmd, ifname, addr, flags);
                dhcp6_handleifa(ctx, cmd, ifname, addr, flags);
        }
 
-       /* We don't care about duplicated addresses, so remove them */
-       if (flags & IN6_IFF_DUPLICATED)
-               cmd = RTM_DELADDR;
-
        TAILQ_FOREACH(ap, &state->addrs, next) {
                if (IN6_ARE_ADDR_EQUAL(&ap->addr, addr))
                        break;
@@ -626,16 +582,30 @@ ipv6_handleifa(struct dhcpcd_ctx *ctx,
        case RTM_NEWADDR:
                if (ap == NULL) {
                        ap = calloc(1, sizeof(*ap));
+                       ap->iface = ifp;
                        memcpy(ap->addr.s6_addr, addr->s6_addr,
                            sizeof(ap->addr.s6_addr));
                        TAILQ_INSERT_TAIL(&state->addrs,
                            ap, next);
+               }
+               ap->addr_flags = flags;
+
+               if (IN6_IS_ADDR_LINKLOCAL(&ap->addr)) {
+#ifdef IPV6_POLLADDRFLAG
+                       if (ap->addr_flags & IN6_IFF_TENTATIVE) {
+                               struct timeval tv;
 
-                       if (IN6_IS_ADDR_LINKLOCAL(&ap->addr)) {
+                               ms_to_tv(&tv, RETRANS_TIMER / 2);
+                               eloop_timeout_add_tv(ap->iface->ctx->eloop,
+                                   &tv, ipv6_checkaddrflags, ap);
+                               break;
+                       }
+#endif
+
+                       if (!(ap->addr_flags & IN6_IFF_NOTUSEABLE)) {
                                /* Now run any callbacks.
                                 * Typically IPv6RS or DHCPv6 */
-                               while ((cb =
-                                   TAILQ_FIRST(&state->ll_callbacks)))
+                               while ((cb = TAILQ_FIRST(&state->ll_callbacks)))
                                {
                                        TAILQ_REMOVE(&state->ll_callbacks,
                                            cb, next);
@@ -648,33 +618,23 @@ ipv6_handleifa(struct dhcpcd_ctx *ctx,
        }
 }
 
-const struct ipv6_addr_l *
-ipv6_linklocal(const struct interface *ifp)
-{
-       const struct ipv6_state *state;
-       const struct ipv6_addr_l *ap;
-
-       state = IPV6_CSTATE(ifp);
-       if (state) {
-               TAILQ_FOREACH(ap, &state->addrs, next) {
-                       if (IN6_IS_ADDR_LINKLOCAL(&ap->addr))
-                               return ap;
-               }
-       }
-       return NULL;
-}
-
-const struct ipv6_addr_l *
+const struct ipv6_addr *
 ipv6_findaddr(const struct interface *ifp, const struct in6_addr *addr)
 {
        const struct ipv6_state *state;
-       const struct ipv6_addr_l *ap;
+       const struct ipv6_addr *ap;
 
        state = IPV6_CSTATE(ifp);
        if (state) {
                TAILQ_FOREACH(ap, &state->addrs, next) {
-                       if (IN6_ARE_ADDR_EQUAL(&ap->addr, addr))
-                               return ap;
+                       if (addr == NULL) {
+                               if (IN6_IS_ADDR_LINKLOCAL(&ap->addr) &&
+                                   !(ap->addr_flags & IN6_IFF_NOTUSEABLE))
+                                       return ap;
+                       } else {
+                               if (IN6_ARE_ADDR_EQUAL(&ap->addr, addr))
+                                       return ap;
+                       }
                }
        }
        return NULL;
@@ -723,7 +683,7 @@ void
 ipv6_free(struct interface *ifp)
 {
        struct ipv6_state *state;
-       struct ipv6_addr_l *ap;
+       struct ipv6_addr *ap;
 
        if (ifp) {
                ipv6_free_ll_callbacks(ifp);
diff --git a/ipv6.h b/ipv6.h
index 12367ad9e1974cebf211c44c5f1327f70916c1e5..2e9f7f9d792cd6e405e9f6c0e1c1ff1bdc99b6c7 100644 (file)
--- a/ipv6.h
+++ b/ipv6.h
 
 /*
  * BSD kernels don't inform userland of DAD results.
- * Also, for RTM_NEWADDR messages the address flags could be
- * undefined leading to false positive duplicate address errors.
- * As such we listen for duplicate addresses on the wire and
- * wait the maxium possible length of time as dictated by the DAD transmission
- * counter and RFC timings.
  * See the discussion here:
  *    http://mail-index.netbsd.org/tech-net/2013/03/15/msg004019.html
  */
 #  include <sys/param.h>
 #endif
 #ifdef BSD
-#  define LISTEN_DAD
+#  define IPV6_POLLADDRFLAG
 #endif
 
 /* This was fixed in NetBSD */
 #ifdef __NetBSD_Prereq__
 #  if __NetBSD_Prereq__(6, 99, 20)
-#    undef LISTEN_DAD
+#    undef IPV6_POLLADDRFLAG
 #  endif
 #endif
 
@@ -82,6 +77,7 @@ struct ipv6_addr {
        uint32_t prefix_vltime;
        uint32_t prefix_pltime;
        struct in6_addr addr;
+       int addr_flags;
        short flags;
        char saddr[INET6_ADDRSTRLEN];
        uint8_t iaid[4];
@@ -114,13 +110,6 @@ struct rt6 {
 };
 TAILQ_HEAD(rt6_head, rt6);
 
-struct ipv6_addr_l {
-       TAILQ_ENTRY(ipv6_addr_l) next;
-       struct in6_addr addr;
-};
-
-TAILQ_HEAD(ipv6_addr_l_head, ipv6_addr_l);
-
 struct ll_callback {
        TAILQ_ENTRY(ll_callback) next;
        void (*callback)(void *);
@@ -129,7 +118,7 @@ struct ll_callback {
 TAILQ_HEAD(ll_callback_head, ll_callback);
 
 struct ipv6_state {
-       struct ipv6_addr_l_head addrs;
+       struct ipv6_addrhead addrs;
        struct ll_callback_head ll_callbacks;
 };
 
@@ -155,11 +144,8 @@ struct ipv6_ctx {
        const char *sfrom;
 
        int nd_fd;
-#ifdef IPV6_SEND_DAD
-       int unspec_fd;
-#endif
-#ifdef LISTEN_DAD
-       uint8_t dad_warned;
+#ifdef IPV6_POLLADDRFLAG
+       uint8_t polladdr_warned;
 #endif
        struct ra_head *ra_routers;
        struct rt6_head *routes;
@@ -180,15 +166,16 @@ int ipv6_userprefix( const struct in6_addr *, short prefix_len,
     uint64_t user_number, struct in6_addr *result, short result_len);
 void ipv6_checkaddrflags(void *);
 int ipv6_addaddr(struct ipv6_addr *);
+ssize_t ipv6_addaddrs(struct ipv6_addrhead *addrs);
 void ipv6_freedrop_addrs(struct ipv6_addrhead *, int,
     const struct interface *);
 void ipv6_handleifa(struct dhcpcd_ctx *ctx, int, struct if_head *,
     const char *, const struct in6_addr *, int);
 int ipv6_handleifa_addrs(int, struct ipv6_addrhead *,
     const struct in6_addr *, int);
-const struct ipv6_addr_l *ipv6_linklocal(const struct interface *);
-const struct ipv6_addr_l *ipv6_findaddr(const struct interface *,
+const struct ipv6_addr *ipv6_findaddr(const struct interface *,
     const struct in6_addr *);
+#define ipv6_linklocal(ifp) (ipv6_findaddr((ifp), NULL))
 int ipv6_addlinklocalcallback(struct interface *, void (*)(void *), void *);
 void ipv6_free_ll_callbacks(struct interface *);
 void ipv6_free(struct interface *);
index 3a182a429a99fcb6d9abdee614d2d2993a242997..7eccdad78134c7bb935baad9eeec214c05663dec 100644 (file)
--- a/ipv6nd.c
+++ b/ipv6nd.c
 #include "ipv6nd.h"
 #include "script.h"
 
-#if defined(LISTEN_DAD) && defined(INET6)
-#  warning kernel does not report DAD results to userland
-#  warning listening to duplicated addresses on the wire
-#endif
-
 /* Debugging Router Solicitations is a lot of spam, so disable it */
 //#define DEBUG_RS
 
@@ -267,16 +262,6 @@ unspec:
                goto eexit;
 #endif
 
-#ifdef LISTEN_DAD
-       if (!ctx->dad_warned) {
-               syslog(LOG_WARNING, 
-                   "kernel does not report DAD results to userland");
-               syslog(LOG_WARNING,
-                   "warning listening to duplicated addresses on the wire");
-               ctx->dad_warned = 1;
-       }
-#endif
-
        return ctx->nd_fd;
 
 eexit:
@@ -578,16 +563,11 @@ ipv6nd_dadcallback(void *arg)
        int wascompleted, found;
 
        wascompleted = (ap->flags & IPV6_AF_DADCOMPLETED);
-       ipv6nd_cancelprobeaddr(ap);
        ap->flags |= IPV6_AF_DADCOMPLETED;
        if (ap->flags & IPV6_AF_DUPLICATED)
                /* No idea what how to try and make another address :( */
                syslog(LOG_WARNING, "%s: DAD detected %s",
                    ap->iface->name, ap->saddr);
-#ifdef IPV6_SEND_DAD
-       else
-               ipv6_addaddr(ap);
-#endif
 
        if (!wascompleted) {
                ifp = ap->iface;
@@ -998,7 +978,7 @@ ipv6nd_handlera(struct ipv6_ctx *ctx, struct interface *ifp,
                script_runreason(ifp, "TEST");
                goto handle_flag;
        }
-       ipv6nd_probeaddrs(&rap->addrs);
+       ipv6_addaddrs(&rap->addrs);
        ipv6_buildroutes(ifp->ctx);
 
        /* We will get run by the expire function */
@@ -1219,7 +1199,7 @@ ipv6nd_expirera(void *arg)
                                next = lt;
                }
 
-               /* Addresses are expired in ipv6ns_probeaddrs
+               /* Addresses are expired in ipv6_addaddrs
                 * so that DHCPv6 addresses can be removed also. */
                TAILQ_FOREACH_SAFE(rao, &rap->options, next, raon) {
                        if (rap->expired) {
@@ -1330,193 +1310,6 @@ ipv6nd_unreachable(void *arg)
            &tv, ipv6nd_proberouter, rap);
 }
 
-#ifdef LISTEN_DAD
-void
-ipv6nd_cancelprobeaddr(struct ipv6_addr *ap)
-{
-
-       eloop_timeout_delete(ap->iface->ctx->eloop, ipv6nd_probeaddr, ap);
-       if (ap->dadcallback)
-               eloop_timeout_delete(ap->iface->ctx->eloop, ap->dadcallback,ap);
-}
-
-#endif
-
-void
-ipv6nd_probeaddr(void *arg)
-{
-       struct ipv6_addr *ap = arg;
-#ifdef IPV6_SEND_DAD
-       struct nd_neighbor_solicit *ns;
-       struct nd_opt_hdr *nd;
-       struct sockaddr_in6 dst;
-       struct cmsghdr *cm;
-       struct in6_pktinfo pi;
-       int hoplimit = HOPLIMIT;
-       struct timeval tv, rtv;
-#else
-#ifdef LISTEN_DAD
-       struct timeval tv;
-#endif
-#endif
-
-       if (ap->dadcallback &&
-           ((ap->flags & IPV6_AF_NEW) == 0 ||
-           ap->nsprobes >= ap->iface->options->dadtransmits))
-       {
-#ifdef IPV6_SEND_DAD
-               ap->dadcallback(ap);
-#else
-               if (!(ap->flags & IPV6_AF_AUTOCONF) ||
-                   ap->iface->options->options & DHCPCD_IPV6RA_OWN)
-                       ipv6_addaddr(ap);
-#endif
-               return;
-       }
-
-       if (ipv6nd_open(ap->iface->ctx) == -1) {
-               syslog(LOG_ERR, "%s: ipv6nd_open: %m", __func__);
-               return;
-       }
-
-       ap->flags &= ~IPV6_AF_DADCOMPLETED;
-
-#ifdef IPV6_SEND_DAD
-       if (!ap->ns) {
-               ap->nslen = sizeof(*ns) + ROUNDUP8(ap->iface->hwlen + 2);
-               ap->ns = calloc(1, ap->nslen);
-               if (ap->ns == NULL) {
-                       syslog(LOG_ERR, "%s: %m", __func__);
-                       return;
-               }
-               ns = (struct nd_neighbor_solicit *)(void *)ap->ns;
-               ns->nd_ns_type = ND_NEIGHBOR_SOLICIT;
-               //ns->nd_ns_cksum = 0;
-               //ns->nd_ns_code = 0;
-               //ns->nd_ns_reserved = 0;
-               ns->nd_ns_target = ap->addr;
-               nd = (struct nd_opt_hdr *)(ap->ns + sizeof(*ns));
-               nd->nd_opt_type = ND_OPT_SOURCE_LINKADDR;
-               nd->nd_opt_len = (ROUNDUP8(ap->iface->hwlen + 2)) >> 3;
-               memcpy(nd + 1, ap->iface->hwaddr, ap->iface->hwlen);
-       }
-
-       memset(&dst, 0, sizeof(dst));
-       dst.sin6_family = AF_INET6;
-#ifdef SIN6_LEN
-       dst.sin6_len = sizeof(dst);
-#endif
-       dst.sin6_addr.s6_addr16[0] = IPV6_ADDR_INT16_MLL;
-       dst.sin6_addr.s6_addr16[1] = 0;
-       dst.sin6_addr.s6_addr32[1] = 0;
-       dst.sin6_addr.s6_addr32[2] = IPV6_ADDR_INT32_ONE;
-       dst.sin6_addr.s6_addr32[3] = ap->addr.s6_addr32[3];
-       dst.sin6_addr.s6_addr[12] = 0xff;
-
-       //memcpy(&dst.sin6_addr, &ap->addr, sizeof(dst.sin6_addr));
-       dst.sin6_scope_id = ap->iface->index;
-
-       ctx = ap->iface->ctx->ipv6;
-       ctx->sndhdr.msg_name = (caddr_t)&dst;
-       ctx->sndhdr.msg_iov[0].iov_base = ap->ns;
-       ctx->sndhdr.msg_iov[0].iov_len = ap->nslen;
-
-       /* Set the outbound interface */
-       cm = CMSG_FIRSTHDR(&ctx->sndhdr);
-       if (cm == NULL) /* unlikely */
-               return;
-       cm->cmsg_level = IPPROTO_IPV6;
-       cm->cmsg_type = IPV6_PKTINFO;
-       cm->cmsg_len = CMSG_LEN(sizeof(pi));
-       memset(&pi, 0, sizeof(pi));
-       pi.ipi6_ifindex = ap->iface->index;
-       memcpy(CMSG_DATA(cm), &pi, sizeof(pi));
-
-       /* Hop limit */
-       cm = CMSG_NXTHDR(&sndhdr, cm);
-       if (cm == NULL) /* unlikely */
-               return;
-       cm->cmsg_level = IPPROTO_IPV6;
-       cm->cmsg_type = IPV6_HOPLIMIT;
-       cm->cmsg_len = CMSG_LEN(sizeof(hoplimit));
-       memcpy(CMSG_DATA(cm), &hoplimit, sizeof(hoplimit));
-
-#ifdef DEBUG_NS
-       syslog(LOG_INFO, "%s: sending IPv6 NS for %s",
-           ap->iface->name, ap->saddr);
-       if (ap->dadcallback == NULL)
-               syslog(LOG_WARNING, "%s: no callback!", ap->iface->name);
-#endif
-       if (sendmsg(unspec_sock, &sndhdr, 0) == -1) {
-               syslog(LOG_ERR, "%s: %s: sendmsg: %m",
-                   ap->iface->name, __func__);
-               return;
-       }
-
-       if (ap->dadcallback) {
-               ms_to_tv(&tv, RETRANS_TIMER);
-               ms_to_tv(&rtv, MIN_RANDOM_FACTOR);
-               timeradd(&tv, &rtv, &tv);
-               rtv.tv_sec = 0;
-               rtv.tv_usec = arc4random() %
-                   (MAX_RANDOM_FACTOR_U - MIN_RANDOM_FACTOR_U);
-               timeradd(&tv, &rtv, &tv);
-
-               eloop_timeout_add_tv(ap->iface->ctx->eloop, &tv,
-                   ++(ap->nsprobes) < ap->iface->options->dadtransmits ?
-                   ipv6nd_probeaddr : ap->dadcallback,
-                   ap);
-       }
-#else /* IPV6_SEND_DAD */
-
-       if (!(ap->flags & IPV6_AF_AUTOCONF) ||
-           ap->iface->options->options & DHCPCD_IPV6RA_OWN)
-               ipv6_addaddr(ap);
-
-#ifdef LISTEN_DAD
-       /* Let the kernel handle DAD.
-        * We don't know the timings, so just poll the address flags */
-       if (ap->dadcallback) {
-               ms_to_tv(&tv, RETRANS_TIMER / 2);
-               eloop_timeout_add_tv(ap->iface->ctx->eloop,
-                   &tv, ipv6_checkaddrflags, ap);
-       }
-#endif
-#endif /* IPV6_SEND_DAD */
-}
-
-ssize_t
-ipv6nd_probeaddrs(struct ipv6_addrhead *addrs)
-{
-       struct ipv6_addr *ap, *apn;
-       ssize_t i;
-
-       i = 0;
-       TAILQ_FOREACH_SAFE(ap, addrs, next, apn) {
-               if (ap->prefix_vltime == 0) {
-                       TAILQ_REMOVE(addrs, ap, next);
-                       if (ap->flags & IPV6_AF_ADDED) {
-                               syslog(LOG_INFO, "%s: deleting address %s",
-                                   ap->iface->name, ap->saddr);
-                               i++;
-                               if (!IN6_IS_ADDR_UNSPECIFIED(&ap->addr) &&
-                                   del_address6(ap) == -1 &&
-                                   errno != EADDRNOTAVAIL && errno != ENXIO)
-                                       syslog(LOG_ERR, "del_address6 %m");
-                       }
-                       eloop_q_timeout_delete(ap->iface->ctx->eloop,
-                           0, NULL, ap);
-                       free(ap);
-               } else if (!IN6_IS_ADDR_UNSPECIFIED(&ap->addr)) {
-                       ipv6nd_probeaddr(ap);
-                       if (ap->flags & IPV6_AF_NEW)
-                               i++;
-               }
-       }
-
-       return i;
-}
-
 void
 ipv6nd_proberouter(void *arg)
 {
@@ -1626,16 +1419,8 @@ ipv6nd_handlena(struct ipv6_ctx *ctx, struct interface *ifp,
        struct nd_neighbor_advert *nd_na;
        struct ra *rap;
        int is_router, is_solicited;
-#ifdef DEBUG_NS
-       int found;
-#endif
        struct timeval tv;
 
-#ifdef LISTEN_DAD
-       struct dhcp6_state *d6state;
-       struct ipv6_addr *ap;
-#endif
-
        if ((size_t)len < sizeof(struct nd_neighbor_advert)) {
                syslog(LOG_ERR, "IPv6 NA packet too short from %s", ctx->sfrom);
                return;
@@ -1659,55 +1444,16 @@ ipv6nd_handlena(struct ipv6_ctx *ctx, struct interface *ifp,
                return;
        }
 
-#ifdef DEBUG_NS
-       found = 0;
-#endif
        TAILQ_FOREACH(rap, ctx->ra_routers, next) {
-               if (rap->iface != ifp)
-                       continue;
-               if (memcmp(rap->from.s6_addr, nd_na->nd_na_target.s6_addr,
+               if (rap->iface == ifp &&
+                   memcmp(rap->from.s6_addr, nd_na->nd_na_target.s6_addr,
                    sizeof(rap->from.s6_addr)) == 0)
                        break;
-#ifdef LISTEN_DAD
-               TAILQ_FOREACH(ap, &rap->addrs, next) {
-                       if (memcmp(ap->addr.s6_addr,
-                           nd_na->nd_na_target.s6_addr,
-                           sizeof(ap->addr.s6_addr)) == 0)
-                       {
-                               ap->flags |= IPV6_AF_DUPLICATED;
-                               if (ap->dadcallback)
-                                       ap->dadcallback(ap);
-#ifdef DEBUG_NS
-                               found++;
-#endif
-                       }
-               }
-#endif
        }
        if (rap == NULL) {
-#ifdef LISTEN_DAD
-               d6state = D6_STATE(ifp);
-               if (d6state) {
-                       TAILQ_FOREACH(ap, &d6state->addrs, next) {
-                               if (memcmp(ap->addr.s6_addr,
-                                   nd_na->nd_na_target.s6_addr,
-                                   sizeof(ap->addr.s6_addr)) == 0)
-                               {
-                                       ap->flags |= IPV6_AF_DUPLICATED;
-                                       if (ap->dadcallback)
-                                               ap->dadcallback(ap);
-#ifdef DEBUG_NS
-                                       found++;
-#endif
-                               }
-                       }
-               }
-#endif
-
 #ifdef DEBUG_NS
-               if (found == 0)
-                       syslog(LOG_DEBUG, "%s: unexpected NA from %s",
-                           ifp->name, ctx->sfrom);
+               syslog(LOG_DEBUG, "%s: unexpected NA from s",
+                   ifp->name, ctx->sfrom);
 #endif
                return;
        }
index 31252e84b28b6b369c61475b7746348f10b0b064..30691c8bd422ade20992e625cc97b7fbd6038c66 100644 (file)
--- a/ipv6nd.h
+++ b/ipv6nd.h
@@ -94,17 +94,9 @@ void ipv6nd_handleifa(struct dhcpcd_ctx *, int,
     const char *, const struct in6_addr *, int);
 void ipv6nd_drop(struct interface *);
 
-void ipv6nd_probeaddr(void *);
-ssize_t ipv6nd_probeaddrs(struct ipv6_addrhead *);
 void ipv6nd_proberouter(void *);
 void ipv6nd_cancelproberouter(struct ra *);
 
-#ifdef LISTEN_DAD
-void ipv6nd_cancelprobeaddr(struct ipv6_addr *);
-#else
-#define ipv6nd_cancelprobeaddr(a)
-#endif
-
 #else
 #define ipv6nd_startrs(a) {}
 #define ipv6nd_addrexists(a, b) (0)