]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Because not all OS's send RTM_NEWADDR for a refreshed RA we need
authorRoy Marples <roy@marples.name>
Sun, 9 Jun 2013 07:31:08 +0000 (07:31 +0000)
committerRoy Marples <roy@marples.name>
Sun, 9 Jun 2013 07:31:08 +0000 (07:31 +0000)
to manage a list of all IPv6 addresses on an interface so that we
can know if we need to wait for DAD to complete or not.

dhcp6.c
if-linux.c
ipv6.c
ipv6.h
ipv6rs.c
net.c

diff --git a/dhcp6.c b/dhcp6.c
index 7604de812ca58416ef31205bed656eb973745d16..6e038ef101ab6a7b677a20fc3570119d64c6d2bf 100644 (file)
--- a/dhcp6.c
+++ b/dhcp6.c
@@ -1860,7 +1860,7 @@ dhcp6_handledata(__unused void *arg)
        const struct dhcp6_option *o;
        const struct dhcp_opt *opt;
        const struct if_options *ifo;
-       const struct ipv6_addr *ap;
+       struct ipv6_addr *ap;
        uint8_t has_new;
        int error;
 
@@ -2129,11 +2129,14 @@ recv:
                len = 1;
                /* If all addresses have completed DAD run the script */
                TAILQ_FOREACH(ap, &state->addrs, next) {
-                       if (ap->flags & IPV6_AF_ONLINK &&
-                           (ap->flags & IPV6_AF_DADCOMPLETED) == 0)
-                       {
-                               len = 0;
-                               break;
+                       if (ap->flags & IPV6_AF_ONLINK) {
+                               if (!(ap->flags & IPV6_AF_DADCOMPLETED) &&
+                                   ipv6_findaddr(ap->iface, &ap->addr))
+                                       ap->flags |= IPV6_AF_DADCOMPLETED;
+                               if ((ap->flags & IPV6_AF_DADCOMPLETED) == 0) {
+                                       len = 0;
+                                       break;
+                               }
                        }
                }
                if (len) {
@@ -2373,6 +2376,9 @@ dhcp6_handleifa(int cmd, const char *ifname,
        struct interface *ifp;
        struct dhcp6_state *state;
 
+       if (ifaces == NULL)
+               return;
+
        TAILQ_FOREACH(ifp, ifaces, next) {
                state = D6_STATE(ifp);
                if (state == NULL || strcmp(ifp->name, ifname))
index 0277f07526eb1df993535b849ca804866b0f2d87..13411518f363f0def8c2b547caf716f6b4055f0e 100644 (file)
@@ -332,8 +332,8 @@ link_addr(struct nlmsghdr *nlm)
                errno = EBADMSG;
                return -1;
        }
-       if (nlm->nlmsg_pid == (uint32_t)getpid())
-               return 1;
+//     if (nlm->nlmsg_pid == (uint32_t)getpid())
+//             return 1;
        ifa = NLMSG_DATA(nlm);
        if (if_indextoname(ifa->ifa_index, ifn) == NULL)
                return -1;
diff --git a/ipv6.c b/ipv6.c
index 5a219d303534c8737abd0dc29ce4b624703a1cbc..c295d3e511061d208c16a8d9e30db37365760bbe 100644 (file)
--- a/ipv6.c
+++ b/ipv6.c
@@ -141,8 +141,7 @@ int
 ipv6_makeaddr(struct in6_addr *addr, const struct interface *ifp,
     const struct in6_addr *prefix, int prefix_len)
 {
-       const struct ipv6_state *state;
-       const struct ll_addr *ap;
+       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] =
@@ -157,14 +156,11 @@ ipv6_makeaddr(struct in6_addr *addr, const struct interface *ifp,
        memcpy(addr, prefix, sizeof(*prefix));
 
        /* Try and make the address from the first local-link address */
-       state = IPV6_CSTATE(ifp);
-       if (state) {
-               ap = TAILQ_FIRST(&state->ll_addrs);
-               if (ap) {
-                       addr->s6_addr32[2] = ap->addr.s6_addr32[2];
-                       addr->s6_addr32[3] = ap->addr.s6_addr32[3];
-                       return 0;
-               }
+       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
@@ -412,6 +408,9 @@ ipv6_addaddr(struct ipv6_addr *ap)
 
        syslog(ap->flags & IPV6_AF_NEW ? LOG_INFO : LOG_DEBUG,
            "%s: adding address %s", ap->iface->name, ap->saddr);
+       if (!(ap->flags & IPV6_AF_DADCOMPLETED) &&
+           ipv6_findaddr(ap->iface, &ap->addr))
+               ap->flags |= IPV6_AF_DADCOMPLETED;
        if (add_address6(ap) == -1) {
                syslog(LOG_ERR, "add_address6 %m");
                return -1;
@@ -455,7 +454,7 @@ ipv6_getstate(struct interface *ifp)
                        syslog(LOG_ERR, "%s: %m", __func__);
                        return NULL;
                }
-               TAILQ_INIT(&state->ll_addrs);
+               TAILQ_INIT(&state->addrs);
                TAILQ_INIT(&state->ll_callbacks);
        }
        return state;
@@ -467,7 +466,7 @@ ipv6_handleifa(int cmd, struct if_head *ifs, const char *ifname,
 {
        struct interface *ifp;
        struct ipv6_state *state;
-       struct ll_addr *ap;
+       struct ipv6_addr_l *ap;
        struct ll_callback *cb;
 
 #if 0
@@ -503,21 +502,20 @@ ipv6_handleifa(int cmd, struct if_head *ifs, const char *ifname,
                return;
        }
 
+       state = ipv6_getstate(ifp);
+       if (state == NULL)
+               return;
+
        if (!IN6_IS_ADDR_LINKLOCAL(addr)) {
                ipv6rs_handleifa(cmd, ifname, addr, flags);
                dhcp6_handleifa(cmd, ifname, addr, flags);
-               return;
        }
 
-       state = ipv6_getstate(ifp);
-       if (state == NULL)
-               return;
-
-       /* We don't care about duplicated LL addresses, so remove them */
+       /* We don't care about duplicated addresses, so remove them */
        if (flags & IN6_IFF_DUPLICATED)
                cmd = RTM_DELADDR;
 
-       TAILQ_FOREACH(ap, &state->ll_addrs, next) {
+       TAILQ_FOREACH(ap, &state->addrs, next) {
                if (IN6_ARE_ADDR_EQUAL(&ap->addr, addr))
                        break;
        }
@@ -525,7 +523,7 @@ ipv6_handleifa(int cmd, struct if_head *ifs, const char *ifname,
        switch (cmd) {
        case RTM_DELADDR:
                if (ap) {
-                       TAILQ_REMOVE(&state->ll_addrs, ap, next);
+                       TAILQ_REMOVE(&state->addrs, ap, next);
                        free(ap);
                }
                break;
@@ -534,7 +532,7 @@ ipv6_handleifa(int cmd, struct if_head *ifs, const char *ifname,
                        ap = calloc(1, sizeof(*ap));
                        memcpy(ap->addr.s6_addr, addr->s6_addr,
                            sizeof(ap->addr.s6_addr));
-                       TAILQ_INSERT_TAIL(&state->ll_addrs,
+                       TAILQ_INSERT_TAIL(&state->addrs,
                            ap, next);
 
                        /* Now run any callbacks.
@@ -551,14 +549,35 @@ ipv6_handleifa(int cmd, struct if_head *ifs, const char *ifname,
        }
 }
 
-const struct ll_addr *
+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)
-               return TAILQ_FIRST(&state->ll_addrs);
+       if (state) {
+               TAILQ_FOREACH(ap, &state->addrs, next) {
+                       if (IN6_IS_ADDR_LINKLOCAL(&ap->addr))
+                               return ap;
+               }
+       }
+       return NULL;
+}
+
+const struct ipv6_addr_l *
+ipv6_findaddr(const struct interface *ifp, const struct in6_addr *addr)
+{
+       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_ARE_ADDR_EQUAL(&ap->addr, addr))
+                               return ap;
+               }
+       }
        return NULL;
 }
 
@@ -600,17 +619,18 @@ ipv6_free_ll_callbacks(struct interface *ifp)
                }
        }
 }
+
 void
 ipv6_free(struct interface *ifp)
 {
        struct ipv6_state *state;
-       struct ll_addr *ap;
+       struct ipv6_addr_l *ap;
 
        ipv6_free_ll_callbacks(ifp);
        state = IPV6_STATE(ifp);
        if (state) {
-               while ((ap = TAILQ_FIRST(&state->ll_addrs))) {
-                       TAILQ_REMOVE(&state->ll_addrs, ap, next);
+               while ((ap = TAILQ_FIRST(&state->addrs))) {
+                       TAILQ_REMOVE(&state->addrs, ap, next);
                        free(ap);
                }
                free(state);
diff --git a/ipv6.h b/ipv6.h
index 190f056f8199c5657511a6f7b2350ec29d8a27de..ac427407d27207ed7d97a1e343aa2f824fcf8e41 100644 (file)
--- a/ipv6.h
+++ b/ipv6.h
@@ -113,12 +113,12 @@ struct rt6 {
 };
 TAILQ_HEAD(rt6head, rt6);
 
-struct ll_addr {
-       TAILQ_ENTRY(ll_addr) next;
+struct ipv6_addr_l {
+       TAILQ_ENTRY(ipv6_addr_l) next;
        struct in6_addr addr;
 };
 
-TAILQ_HEAD(ll_addr_head, ll_addr);
+TAILQ_HEAD(ipv6_addr_l_head, ipv6_addr_l);
 
 struct ll_callback {
        TAILQ_ENTRY(ll_callback) next;
@@ -128,7 +128,7 @@ struct ll_callback {
 TAILQ_HEAD(ll_callback_head, ll_callback);
 
 struct ipv6_state {
-       struct ll_addr_head ll_addrs;
+       struct ipv6_addr_l_head addrs;
        struct ll_callback_head ll_callbacks;
 };
 
@@ -151,7 +151,9 @@ void ipv6_handleifa(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 ll_addr *ipv6_linklocal(const struct interface *);
+const struct ipv6_addr_l *ipv6_linklocal(const struct interface *);
+const struct ipv6_addr_l *ipv6_findaddr(const struct interface *,
+    const struct in6_addr *);
 int ipv6_addlinklocalcallback(struct interface *, void (*)(void *), void *);
 void ipv6_free_ll_callbacks(struct interface *);
 void ipv6_free(struct interface *);
index 3130297fa348352c3847f676edfe68085b1ac548..5652cd47981241e9ece9172f21475560073c8391 100644 (file)
--- a/ipv6rs.c
+++ b/ipv6rs.c
@@ -406,20 +406,25 @@ add_router(struct ra *router)
 }
 
 static void
-ipv6rs_scriptrun(const struct ra *rap)
+ipv6rs_scriptrun(struct ra *rap)
 {
        int hasdns;
-       const struct ipv6_addr *ap;
+       struct ipv6_addr *ap;
        const struct ra_opt *rao;
 
        /* If all addresses have completed DAD run the script */
        TAILQ_FOREACH(ap, &rap->addrs, next) {
-               if ((ap->flags & IPV6_AF_DADCOMPLETED) == 0) {
-                       syslog(LOG_DEBUG,
-                           "%s: waiting for Router Advertisement"
-                           " DAD to complete",
-                           rap->iface->name);
-                       return;
+               if (ap->flags & IPV6_AF_ONLINK) {
+                       if (!(ap->flags & IPV6_AF_DADCOMPLETED) &&
+                           ipv6_findaddr(ap->iface, &ap->addr))
+                               ap->flags |= IPV6_AF_DADCOMPLETED;
+                       if ((ap->flags & IPV6_AF_DADCOMPLETED) == 0) {
+                               syslog(LOG_DEBUG,
+                                   "%s: waiting for Router Advertisement"
+                                   " DAD to complete",
+                                   rap->iface->name);
+                               return;
+                       }
                }
        }
 
diff --git a/net.c b/net.c
index ae932243454f5f43e7569839599e35342ed1f40e..d8a8c9c815af505197b97cd897ad65ca8e64bf1e 100644 (file)
--- a/net.c
+++ b/net.c
@@ -441,21 +441,18 @@ discover_interfaces(int argc, char * const *argv)
        }
 
 #ifdef INET6
-       /* Capture local link addresses */
        for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) {
                if (ifa->ifa_addr != NULL &&
                    ifa->ifa_addr->sa_family == AF_INET6)
                {
                        sin6 = (const struct sockaddr_in6 *)
                            (void *)ifa->ifa_addr;
-                       if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
-                               ifa_flags = in6_addr_flags(ifa->ifa_name,
-                                               &sin6->sin6_addr);
-                               if (ifa_flags != -1)
-                                       ipv6_handleifa(RTM_NEWADDR, ifs,
-                                           ifa->ifa_name,
-                                           &sin6->sin6_addr, ifa_flags);
-                       }
+                       ifa_flags = in6_addr_flags(ifa->ifa_name,
+                           &sin6->sin6_addr);
+                       if (ifa_flags != -1)
+                               ipv6_handleifa(RTM_NEWADDR, ifs,
+                                   ifa->ifa_name,
+                                   &sin6->sin6_addr, ifa_flags);
                }
        }
 #endif