From: Roy Marples Date: Sun, 9 Jun 2013 07:31:08 +0000 (+0000) Subject: Because not all OS's send RTM_NEWADDR for a refreshed RA we need X-Git-Tag: v6.0.0~36 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d5690e937bb0b6c4f451d3248ee66f4a1460e17c;p=thirdparty%2Fdhcpcd.git Because not all OS's send RTM_NEWADDR for a refreshed RA we need 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. --- diff --git a/dhcp6.c b/dhcp6.c index 7604de81..6e038ef1 100644 --- 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)) diff --git a/if-linux.c b/if-linux.c index 0277f075..13411518 100644 --- a/if-linux.c +++ b/if-linux.c @@ -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 5a219d30..c295d3e5 100644 --- 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 190f056f..ac427407 100644 --- 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 *); diff --git a/ipv6rs.c b/ipv6rs.c index 3130297f..5652cd47 100644 --- 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 ae932243..d8a8c9c8 100644 --- 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