]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Check address flags for tentative and duplicates bits for sanity.
authorRoy Marples <roy@marples.name>
Thu, 16 May 2013 10:31:21 +0000 (10:31 +0000)
committerRoy Marples <roy@marples.name>
Thu, 16 May 2013 10:31:21 +0000 (10:31 +0000)
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.

dhcp6.c
dhcp6.h
if-bsd.c
ipv6.c
ipv6.h
ipv6ns.c
ipv6ns.h
ipv6rs.c
ipv6rs.h

diff --git a/dhcp6.c b/dhcp6.c
index 6d8d3bef6f444859eb9f4d2db248782198e23fef..00d069d7785534df8a5b10b8dacda6f456b16b91 100644 (file)
--- 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 c7df8c257fad0ca4957c0189471be9092575c526..c47e23a154117a1b6501d9d6ac5ce1a8d3817cd9 100644 (file)
--- 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()
index b647e0775acda3b32ce147c96d5afa9677731a3a..d664d4d1a68abe2aff15404a272267760ad514c9 100644 (file)
--- 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 5e201b1904c80e93e004e361ad538d6947d98f1f..4a6a57e274c4f209e53bba55fb4e05eec792cfc7 100644 (file)
--- a/ipv6.c
+++ b/ipv6.c
 #ifdef __linux__
 #  include <asm/types.h> /* for systems with broken headers */
 #  include <linux/rtnetlink.h>
+   /* Match Linux defines to BSD */
+#  define IN6_IFF_TENTATIVE    IFA_F_TENTATIVE | IFA_F_OPTIMISTIC
+#  define IN6_IFF_DUPLICATED   IFA_F_DADFAILED
+#else
+#  include <netinet6/in6_var.h>
 #endif
 
 #include <errno.h>
@@ -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 6b037e38930c8dbc1f25707d24799d8368bba4c9..870cb0e4728cddd6368ff6df5312f944721e3db8 100644 (file)
--- 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 *);
index bb24162b0adc81f1091aa1129e9af022559882d0..16cc15ece569dfdac31112b50a1188bd311ee5c3 100644 (file)
--- 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)
index 7b0a9e56975f0b99222024e69a31373cedcff41e..54ee87f1da25d4093d5e128ce2884d4c4594673f 100644 (file)
--- a/ipv6ns.h
+++ b/ipv6ns.h
 
 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
index b57592c53218eb203ad90b220da3166d7861363a..1688ac913e3fae33da750c74f7c1fd07c4adf370 100644 (file)
--- 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);
        }
 }
 
index bb08a60ebfe2fc59b8501fdd6c0ffa5c76bf2a6f..4aa752161f87dacd6914ccc760e40e38b9eda9e3 100644 (file)
--- 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) {}