From: Roy Marples Date: Thu, 16 May 2013 10:31:21 +0000 (+0000) Subject: Check address flags for tentative and duplicates bits for sanity. X-Git-Tag: v5.99.7~70 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=d8194bcd39a1dbb7230a5e7ea7af4c7f72899b42;p=thirdparty%2Fdhcpcd.git Check address flags for tentative and duplicates bits for sanity. Handle the actual trigger to callout dhcpcd-run-hooks in the DAD callback instead of on receipt of RTM_NEWADDR directly. This is more code, but it allows us to use our own DAD engine if we need to. --- diff --git a/dhcp6.c b/dhcp6.c index 6d8d3bef..00d069d7 100644 --- a/dhcp6.c +++ b/dhcp6.c @@ -1039,7 +1039,11 @@ static void dhcp6_dadcallback(void *arg) { struct ipv6_addr *ap = arg; + struct interface *ifp; + struct dhcp6_state *state; + int wascompleted; + wascompleted = ap->dadcompleted; ipv6ns_cancelprobeaddr(ap); ap->dadcompleted = 1; if (ap->dad) @@ -1047,10 +1051,29 @@ dhcp6_dadcallback(void *arg) * We should decline the address */ syslog(LOG_WARNING, "%s: DAD detected %s", ap->iface->name, ap->saddr); -#ifdef IPV6_SEND_DAD else +#ifdef IPV6_SEND_DAD ipv6_addaddr(ap); #endif + + if (!wascompleted) { + ifp = ap->iface; + state = D6_STATE(ifp); + if (state->state == DH6S_BOUND) { + TAILQ_FOREACH(ap, &state->addrs, next) { + if (!ap->dadcompleted) { + wascompleted = 1; + break; + } + } + if (!wascompleted) { + syslog(LOG_DEBUG, "%s: DHCPv6 DAD completed", + ifp->name); + script_runreason(ifp, state->reason); + daemonise(); + } + } + } } static int @@ -1836,7 +1859,8 @@ recv: script_runreason(ifp, state->reason); daemonise(); } else - syslog(LOG_DEBUG, "%s: waiting for RA DAD to complete", + syslog(LOG_DEBUG, "%s: waiting for DHCPv6 DAD" + " to complete", ifp->name); } @@ -2014,23 +2038,17 @@ dhcp6_free(struct interface *ifp) } void -dhcp6_handleifa(int cmd, const char *ifname, const struct in6_addr *addr) +dhcp6_handleifa(int cmd, const char *ifname, + const struct in6_addr *addr, int flags) { struct interface *ifp; struct dhcp6_state *state; - int found; TAILQ_FOREACH(ifp, ifaces, next) { state = D6_STATE(ifp); if (state == NULL || strcmp(ifp->name, ifname)) continue; - found = ipv6_handleifa_addrs(cmd, &state->addrs, addr); - if (found && state->state == DH6S_BOUND) { - syslog(LOG_DEBUG, "%s: DHCPv6 DAD completed", - ifp->name); - script_runreason(ifp, state->reason); - daemonise(); - } + ipv6_handleifa_addrs(cmd, &state->addrs, addr, flags); } } diff --git a/dhcp6.h b/dhcp6.h index c7df8c25..c47e23a1 100644 --- a/dhcp6.h +++ b/dhcp6.h @@ -213,7 +213,7 @@ int dhcp6_start(struct interface *, int); ssize_t dhcp6_env(char **, const char *, const struct interface *, const struct dhcp6_message *, ssize_t); void dhcp6_free(struct interface *); -void dhcp6_handleifa(int, const char *, const struct in6_addr *addr); +void dhcp6_handleifa(int, const char *, const struct in6_addr *addr, int); void dhcp6_drop(struct interface *, const char *); #else #define dhcp6_printoptions() diff --git a/if-bsd.c b/if-bsd.c index b647e077..d664d4d1 100644 --- a/if-bsd.c +++ b/if-bsd.c @@ -614,7 +614,7 @@ manage_link(int fd) sin6->sin6_addr.s6_addr, sizeof(ia6.s6_addr)); ipv6_handleifa(rtm->rtm_type, ifname, - &ia6); + &ia6, ifam->ifam_flags); break; #endif } diff --git a/ipv6.c b/ipv6.c index 5e201b19..4a6a57e2 100644 --- a/ipv6.c +++ b/ipv6.c @@ -34,6 +34,11 @@ #ifdef __linux__ # include /* for systems with broken headers */ # include + /* Match Linux defines to BSD */ +# define IN6_IFF_TENTATIVE IFA_F_TENTATIVE | IFA_F_OPTIMISTIC +# define IN6_IFF_DUPLICATED IFA_F_DADFAILED +#else +# include #endif #include @@ -292,16 +297,17 @@ ipv6_addaddrs(struct ipv6_addrhead *addrs) } void -ipv6_handleifa(int cmd, const char *ifname, const struct in6_addr *addr) +ipv6_handleifa(int cmd, const char *ifname, + const struct in6_addr *addr, int flags) { - ipv6rs_handleifa(cmd, ifname, addr); - dhcp6_handleifa(cmd, ifname, addr); + ipv6rs_handleifa(cmd, ifname, addr, flags); + dhcp6_handleifa(cmd, ifname, addr, flags); } int ipv6_handleifa_addrs(int cmd, - struct ipv6_addrhead *addrs, const struct in6_addr *addr) + struct ipv6_addrhead *addrs, const struct in6_addr *addr, int flags) { struct ipv6_addr *ap, *apn; uint8_t found, alldadcompleted; @@ -324,8 +330,17 @@ ipv6_handleifa_addrs(int cmd, free(ap); break; case RTM_NEWADDR: + /* Safety - ignore tentative announcements */ + if (flags & IN6_IFF_TENTATIVE) + break; if (!ap->dadcompleted) { found++; + if (flags & IN6_IFF_DUPLICATED && ap->dad == 0) + ap->dad = 1; + if (ap->dadcallback) + ap->dadcallback(ap); + /* We need to set this here in-case the + * dadcallback function checks it */ ap->dadcompleted = 1; } } diff --git a/ipv6.h b/ipv6.h index 6b037e38..870cb0e4 100644 --- a/ipv6.h +++ b/ipv6.h @@ -78,14 +78,16 @@ TAILQ_HEAD(rt6head, rt6); int ipv6_init(void); ssize_t ipv6_printaddr(char *, ssize_t, const uint8_t *, const char *); struct in6_addr *ipv6_linklocal(const char *); -int ipv6_makeaddr(struct in6_addr *, const char *, const struct in6_addr *, int); +int ipv6_makeaddr(struct in6_addr *, const char *, + const struct in6_addr *, int); int ipv6_makeprefix(struct in6_addr *, const struct in6_addr *, int); int ipv6_mask(struct in6_addr *, int); int ipv6_prefixlen(const struct in6_addr *); int ipv6_addaddr(struct ipv6_addr *); ssize_t ipv6_addaddrs(struct ipv6_addrhead *); -void ipv6_handleifa(int, const char *, const struct in6_addr *); -int ipv6_handleifa_addrs(int, struct ipv6_addrhead *, const struct in6_addr *); +void ipv6_handleifa(int, const char *, const struct in6_addr *, int); +int ipv6_handleifa_addrs(int, struct ipv6_addrhead *, + const struct in6_addr *, int); int ipv6_removesubnet(const struct interface *, struct ipv6_addr *); void ipv6_buildroutes(void); void ipv6_drop(struct interface *); diff --git a/ipv6ns.c b/ipv6ns.c index bb24162b..16cc15ec 100644 --- a/ipv6ns.c +++ b/ipv6ns.c @@ -213,6 +213,7 @@ ipv6ns_unreachable(void *arg) script_runreason(rap->iface, "ROUTERADVERT"); /* XXX not RA */ } +#ifdef LISTEN_DAD void ipv6ns_cancelprobeaddr(struct ipv6_addr *ap) { @@ -221,6 +222,7 @@ ipv6ns_cancelprobeaddr(struct ipv6_addr *ap) if (ap->dadcallback) eloop_timeout_delete(ap->dadcallback, ap); } +#endif void ipv6ns_probeaddr(void *arg) @@ -234,10 +236,12 @@ ipv6ns_probeaddr(void *arg) struct in6_pktinfo pi; int hoplimit = HOPLIMIT; #else +#ifdef LISTEN_DAD + struct timeval tv, rtv; struct timeval mtv; int i; #endif - struct timeval tv, rtv; +#endif if (ap->dadcallback && (ap->new == 0 || ap->nsprobes >= ap->iface->options->dadtransmits)) @@ -335,9 +339,10 @@ ipv6ns_probeaddr(void *arg) ap); } #else /* IPV6_SEND_DAD */ + ipv6_addaddr(ap); +#ifdef LISTEN_DAD /* Let the kernel handle DAD. * We don't know the timings, so just wait for the max */ - ipv6_addaddr(ap); if (ap->dadcallback) { mtv.tv_sec = 0; mtv.tv_usec = 0; @@ -349,6 +354,7 @@ ipv6ns_probeaddr(void *arg) } eloop_timeout_add_tv(&mtv, ap->dadcallback, ap); } +#endif #endif /* IPV6_SEND_DAD */ } @@ -472,8 +478,11 @@ ipv6ns_handledata(__unused void *arg) int found; #endif struct timeval tv; + +#ifdef LISTEN_DAD struct dhcp6_state *d6state; struct ipv6_addr *ap; +#endif len = recvmsg(sock, &rcvhdr, 0); if (len == -1) { @@ -551,6 +560,7 @@ ipv6ns_handledata(__unused void *arg) if (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, @@ -564,8 +574,10 @@ ipv6ns_handledata(__unused void *arg) #endif } } +#endif } if (rap == NULL) { +#ifdef LISTEN_DAD d6state = D6_STATE(ifp); if (d6state) { TAILQ_FOREACH(ap, &d6state->addrs, next) { @@ -582,6 +594,7 @@ ipv6ns_handledata(__unused void *arg) } } } +#endif #ifdef DEBUG_NS if (found == 0) diff --git a/ipv6ns.h b/ipv6ns.h index 7b0a9e56..54ee87f1 100644 --- a/ipv6ns.h +++ b/ipv6ns.h @@ -38,6 +38,11 @@ void ipv6ns_probeaddr(void *); ssize_t ipv6ns_probeaddrs(struct ipv6_addrhead *); -void ipv6ns_cancelprobeaddr(struct ipv6_addr *); void ipv6ns_proberouter(void *); + +#ifdef LISTEN_DAD +void ipv6ns_cancelprobeaddr(struct ipv6_addr *); +#else +#define ipv6ns_cancelprobeaddr(a) +#endif #endif diff --git a/ipv6rs.c b/ipv6rs.c index b57592c5..1688ac91 100644 --- a/ipv6rs.c +++ b/ipv6rs.c @@ -256,7 +256,7 @@ ipv6rs_sendprobe(void *arg) cm->cmsg_len = CMSG_LEN(sizeof(hoplimit)); memcpy(CMSG_DATA(cm), &hoplimit, sizeof(hoplimit)); - syslog(LOG_INFO, "%s: sending IPv6 Router Solicitation", ifp->name); + syslog(LOG_INFO, "%s: sending Router Solicitation", ifp->name); if (sendmsg(sock, &sndhdr, 0) == -1) { syslog(LOG_ERR, "%s: sendmsg: %m", ifp->name); ipv6rs_drop(ifp); @@ -393,23 +393,6 @@ add_router(struct ra *router) TAILQ_INSERT_HEAD(&ipv6_routers, router, next); } -static void -ipv6rs_dadcallback(void *arg) -{ - struct ipv6_addr *ap = arg; - - ipv6ns_cancelprobeaddr(ap); - ap->dadcompleted = 1; - if (ap->dad) - /* 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 -} - static void ipv6rs_scriptrun(const struct ra *rap) { @@ -419,8 +402,12 @@ ipv6rs_scriptrun(const struct ra *rap) /* If all addresses have completed DAD run the script */ TAILQ_FOREACH(ap, &rap->addrs, next) { - if (ap->dadcompleted == 0) + if (ap->dadcompleted == 0) { + syslog(LOG_DEBUG, "%s: waiting for Router Advertisement" + " DAD to complete", + rap->iface->name); return; + } } /* If we don't require RDNSS then set hasdns = 1 so we fork */ @@ -453,6 +440,52 @@ ipv6rs_scriptrun(const struct ra *rap) #endif } +static void +ipv6rs_dadcallback(void *arg) +{ + struct ipv6_addr *ap = arg, *rapap; + struct interface *ifp; + struct ra *rap; + int wascompleted, found; + + wascompleted = ap->dadcompleted; + ipv6ns_cancelprobeaddr(ap); + ap->dadcompleted = 1; + if (ap->dad) + /* 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; + + TAILQ_FOREACH(rap, &ipv6_routers, next) { + if (rap->iface != ifp) + continue; + wascompleted = 1; + TAILQ_FOREACH(rapap, &rap->addrs, next) { + if (!rapap->dadcompleted) { + wascompleted = 0; + break; + } + if (rapap == ap) + found = 1; + } + + if (wascompleted && found && rap->lifetime) { + syslog(LOG_DEBUG, + "%s: Router Advertisement DAD completed", + rap->iface->name); + ipv6rs_scriptrun(rap); + } + } + } +} + /* ARGSUSED */ static void ipv6rs_handledata(__unused void *arg) @@ -1081,21 +1114,15 @@ ipv6rs_findsameaddr(const struct ipv6_addr *ap) } void -ipv6rs_handleifa(int cmd, const char *ifname, const struct in6_addr *addr) +ipv6rs_handleifa(int cmd, const char *ifname, + const struct in6_addr *addr, int flags) { struct ra *rap; - int found; TAILQ_FOREACH(rap, &ipv6_routers, next) { if (strcmp(rap->iface->name, ifname)) continue; - found = ipv6_handleifa_addrs(cmd, &rap->addrs, addr); - if (found && rap->lifetime) { - syslog(LOG_DEBUG, - "%s: IPv6 Router Advertisement DAD completed", - rap->iface->name); - ipv6rs_scriptrun(rap); - } + ipv6_handleifa_addrs(cmd, &rap->addrs, addr, flags); } } diff --git a/ipv6rs.h b/ipv6rs.h index bb08a60e..4aa75216 100644 --- a/ipv6rs.h +++ b/ipv6rs.h @@ -86,7 +86,7 @@ void ipv6rs_freedrop_ra(struct ra *, int); ssize_t ipv6rs_free(struct interface *); void ipv6rs_expire(void *arg); int ipv6rs_has_ra(const struct interface *); -void ipv6rs_handleifa(int, const char *, const struct in6_addr *); +void ipv6rs_handleifa(int, const char *, const struct in6_addr *, int); void ipv6rs_drop(struct interface *); #else #define ipv6rs_start(a) {}