]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Use the kernel DAD for IPv6 addresses and finish the action once each
authorRoy Marples <roy@marples.name>
Wed, 15 May 2013 10:27:36 +0000 (10:27 +0000)
committerRoy Marples <roy@marples.name>
Wed, 15 May 2013 10:27:36 +0000 (10:27 +0000)
address DAD completes. BSD kernels will require a patch as noted within
the README. The linux netlink part still needs to be written.

README
defs.h
dhcp6.c
dhcp6.h
dhcpcd.c
if-bsd.c
ipv6.c
ipv6.h
ipv6ns.c
ipv6rs.c
ipv6rs.h

diff --git a/README b/README
index c85432a2860325bfee42966093361802777f3d12..17eb5108f5d039770b06cb74999d7b71e9427184 100644 (file)
--- a/README
+++ b/README
@@ -38,6 +38,16 @@ routes. You can find discussion here:
 BSD systems where this has been fixed are:
     NetBSD-5.0
 
+Some BSD systems announce IPv6 addresses to userland when the address has
+been added, not when it's actually ready to use. This is important because
+no kernel allows to send from the unspecified address which means userland
+cannot be RFC conformation when creating DAD messages so we need to rely on
+the kernel implementation.
+You can find the discussion here:
+    http://mail-index.netbsd.org/tech-net/2013/03/15/msg004019.html
+BSD systems where this will be fixed are:
+    NetBSD-7.0
+
 We try and detect how dhcpcd should interact with system services during the
 configure stage. If we cannot auto-detect how do to this, or it is wrong then
 you can change this by passing shell commands to --service-exists,
@@ -67,11 +77,6 @@ Compatibility
 dhcpcd-5.0 is only fully command line compatible with dhcpcd-4.0
 For compatibility with older versions, use dhcpcd-4.0
 
-dhcpcd no longer sends a default ClientID for ethernet interfaces.
-This is so we can re-use the address the kernel DHCP client found.
-To retain the old behaviour of sending a default ClientID based on the
-hardware address for interface, simply add the keyword clientid to dhcpcd.conf.
-
 
 ChangeLog
 ---------
diff --git a/defs.h b/defs.h
index 47f20533496e94f8a79d18b8b404e828e6fa9c4b..e606056cdfc46d34837541b71d669628dba9e421 100644 (file)
--- a/defs.h
+++ b/defs.h
@@ -28,7 +28,7 @@
 #define CONFIG_H
 
 #define PACKAGE                        "dhcpcd"
-#define VERSION                        "5.99.5"
+#define VERSION                        "5.99.6"
 
 #ifndef CONFIG
 # define CONFIG                        SYSCONFDIR "/" PACKAGE ".conf"
diff --git a/dhcp6.c b/dhcp6.c
index dc1d47701cfe6a2b833884501edc881e42b83c16..6d8d3bef6f444859eb9f4d2db248782198e23fef 100644 (file)
--- a/dhcp6.c
+++ b/dhcp6.c
@@ -1559,6 +1559,7 @@ dhcp6_find_delegates(struct interface *ifp)
        if (k) {
                syslog(LOG_INFO, "%s: adding delegated prefixes", ifp->name);
                state = D6_STATE(ifp);
+               state->state = DH6S_DELEGATED;
                ipv6ns_probeaddrs(&state->addrs);
                ipv6_buildroutes();
        }
@@ -1577,7 +1578,6 @@ dhcp6_handledata(__unused void *arg)
        struct dhcp6_message *r;
        struct dhcp6_state *state;
        const struct dhcp6_option *o;
-       const char *reason;
        const struct dhcp_opt *opt;
        const struct if_options *ifo;
        const struct ipv6_addr *ap;
@@ -1638,7 +1638,8 @@ dhcp6_handledata(__unused void *arg)
            r->xid[2] != state->send->xid[2])
        {
                syslog(LOG_ERR,
-                   "%s: wrong xid 0x%02x%02x%02x (expecting 0x%02x%02x%02x) from %s",
+                   "%s: wrong xid 0x%02x%02x%02x"
+                   " (expecting 0x%02x%02x%02x) from %s",
                    ifp->name,
                    r->xid[0], r->xid[1], r->xid[2],
                    state->send->xid[0], state->send->xid[1],
@@ -1746,7 +1747,7 @@ replyok:
 recv:
        syslog(LOG_INFO, "%s: %s received from %s", ifp->name, op, sfrom);
 
-       reason = NULL;
+       state->reason = NULL;
        eloop_timeout_delete(NULL, ifp);
        switch(state->state) {
        case DH6S_INFORM:
@@ -1754,22 +1755,22 @@ recv:
                state->rebind = 0;
                state->expire = ~0U;
                state->lowpl = ~0U;
-               reason = "INFORM6";
+               state->reason = "INFORM6";
                break;
        case DH6S_REQUEST:
-               if (reason == NULL)
-                       reason = "BOUND6";
+               if (state->reason == NULL)
+                       state->reason = "BOUND6";
                /* FALLTHROUGH */
        case DH6S_RENEW:
-               if (reason == NULL)
-                       reason = "RENEW6";
+               if (state->reason == NULL)
+                       state->reason = "RENEW6";
                /* FALLTHROUGH */
        case DH6S_REBIND:
-               if (reason == NULL)
-                       reason = "REBIND6";
+               if (state->reason == NULL)
+                       state->reason = "REBIND6";
        case DH6S_CONFIRM:
-               if (reason == NULL)
-                       reason = "REBOOT6";
+               if (state->reason == NULL)
+                       state->reason = "REBOOT6";
                if (state->renew == 0) {
                        if (state->expire == ~0U)
                                state->renew = ~0U;
@@ -1784,7 +1785,7 @@ recv:
                }
                break;
        default:
-               reason = "UNKNOWN6";
+               state->reason = "UNKNOWN6";
                break;
        }
 
@@ -1798,7 +1799,11 @@ recv:
                state->recv_len = 0;
        }
 
-       if (!(options & DHCPCD_TEST)) {
+       if (options & DHCPCD_TEST)
+               script_runreason(ifp, "TEST");
+       else {
+               if (state->state == DH6S_INFORM)
+                       script_runreason(ifp, state->reason);
                state->state = DH6S_BOUND;
                if (state->renew)
                        eloop_timeout_add_sec(state->renew,
@@ -1818,9 +1823,23 @@ recv:
                            ifp->name, state->renew, state->rebind);
                ipv6_buildroutes();
                dhcp6_writelease(ifp);
+
+               len = 1;
+               /* If all addresses have completed DAD run the script */
+               TAILQ_FOREACH(ap, &state->addrs, next) {
+                       if (ap->dadcompleted == 0) {
+                               len = 0;
+                               break;
+                       }
+               }
+               if (len) {
+                       script_runreason(ifp, state->reason);
+                       daemonise();
+               } else
+                       syslog(LOG_DEBUG, "%s: waiting for RA DAD to complete",
+                           ifp->name);
        }
 
-       script_runreason(ifp, options & DHCPCD_TEST ? "TEST" : reason);
        if (options & DHCPCD_TEST ||
            (ifp->options->options & DHCPCD_INFORM &&
            !(options & DHCPCD_MASTER)))
@@ -1830,7 +1849,6 @@ recv:
 #endif
                exit(EXIT_SUCCESS);
        }
-       daemonise();
 }
 
 static int
@@ -1920,10 +1938,8 @@ dhcp6_start(struct interface *ifp, int manage)
                return -1;
 
        TAILQ_INIT(&state->addrs);
-       if (dhcp6_find_delegates(ifp)) {
-               state->state = DH6S_DELEGATED;
+       if (dhcp6_find_delegates(ifp))
                return 0;
-       }
 
        syslog(LOG_INFO, "%s: %s", ifp->name,
            manage ? "soliciting DHCPv6 address" :
@@ -1997,6 +2013,27 @@ dhcp6_free(struct interface *ifp)
        dhcp6_freedrop(ifp, 0, NULL);
 }
 
+void
+dhcp6_handleifa(int cmd, const char *ifname, const struct in6_addr *addr)
+{
+       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();
+               }
+       }
+}
+
 ssize_t
 dhcp6_env(char **env, const char *prefix, const struct interface *ifp,
     const struct dhcp6_message *m, ssize_t mlen)
diff --git a/dhcp6.h b/dhcp6.h
index 282b8375ef1065362e8d685cf240533ca5bfbb44..c7df8c257fad0ca4957c0189471be9092575c526 100644 (file)
--- a/dhcp6.h
+++ b/dhcp6.h
@@ -1,4 +1,4 @@
-/* 
+/*
  * dhcpcd - DHCP client daemon
  * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
  * All rights reserved
@@ -180,6 +180,7 @@ struct dhcp6_state {
        struct ipv6_addrhead addrs;
        uint32_t lowpl;
        char leasefile[PATH_MAX];
+       const char *reason;
 };
 
 #define D6_STATE(ifp)                                                         \
@@ -212,6 +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_drop(struct interface *, const char *);
 #else
 #define dhcp6_printoptions()
index 2aac91daba7e26bd96d6a7646d16b3543d85aaee..5fd2b4320469e01f64e928f49cac28d1feb708f4 100644 (file)
--- a/dhcpcd.c
+++ b/dhcpcd.c
@@ -457,7 +457,10 @@ init_state(struct interface *ifp, int argc, char **argv)
        configure_interface(ifp, argc, argv);
        ifo = ifp->options;
 
-       if (ifo->options & DHCPCD_LINK && linkfd == -1) {
+       /* RTM_NEWADDR goes through the link socket as well which we
+        * need for IPv6 DAD, so we check for DHCPCD_LINK in handle_carrier
+        * instead */
+       if (linkfd == -1) {
                linkfd = open_link_socket();
                if (linkfd == -1) {
                        syslog(LOG_ERR, "open_link_socket: %m");
index 78425d0fa2bade407ceb6682f8b81d45c56272b3..b647e0775acda3b32ce147c96d5afa9677731a3a 100644 (file)
--- a/if-bsd.c
+++ b/if-bsd.c
 #define RT_ADVANCE(x, n) (x += RT_ROUNDUP((n)->sa_len))
 #endif
 
-/* FIXME: Why do we need to check for sa_family 255 */
 #define COPYOUT(sin, sa)                                                     \
        sin.s_addr = ((sa) != NULL) ?                                         \
            (((struct sockaddr_in *)(void *)sa)->sin_addr).s_addr : 0
 
+#define COPYOUT6(sin, sa)                                                    \
+       sin.s6_addr = ((sa) != NULL) ?                                        \
+           (((struct sockaddr_in6 *)(void *)sa)->sin6_addr).s6_addr : 0
+
 static int r_fd = -1;
 static char *link_buf;
 static ssize_t link_buflen;
@@ -478,6 +481,10 @@ manage_link(int fd)
        struct sockaddr_dl sdl;
        unsigned char *hwaddr;
 #endif
+#ifdef INET6
+       struct in6_addr ia6;
+       struct sockaddr_in6 *sin6;
+#endif
 
        for (;;) {
                if (ioctl(fd, FIONREAD, &len) == -1)
@@ -598,6 +605,17 @@ manage_link(int fd)
                                        ipv4_handleifa(rtm->rtm_type, ifname,
                                            &rt.dest, &rt.net, &rt.gate);
                                        break;
+#endif
+#ifdef INET6
+                               case AF_INET6:
+                                       sin6 = (struct sockaddr_in6*)
+                                           rti_info[RTAX_IFA];
+                                       memcpy(ia6.s6_addr,
+                                           sin6->sin6_addr.s6_addr,
+                                           sizeof(ia6.s6_addr));
+                                       ipv6_handleifa(rtm->rtm_type, ifname,
+                                           &ia6);
+                                       break;
 #endif
                                }
                                break;
diff --git a/ipv6.c b/ipv6.c
index e8752815f9e99d0b8e4ae7b994b434858150ab55..5e201b1904c80e93e004e361ad538d6947d98f1f 100644 (file)
--- a/ipv6.c
+++ b/ipv6.c
 #include <sys/types.h>
 #include <sys/socket.h>
 
+#include <net/route.h>
 #include <netinet/in.h>
 
+#ifdef __linux__
+#  include <asm/types.h> /* for systems with broken headers */
+#  include <linux/rtnetlink.h>
+#endif
+
 #include <errno.h>
 #include <ifaddrs.h>
 #include <stdlib.h>
@@ -285,6 +291,49 @@ ipv6_addaddrs(struct ipv6_addrhead *addrs)
        return i;
 }
 
+void
+ipv6_handleifa(int cmd, const char *ifname, const struct in6_addr *addr)
+{
+
+       ipv6rs_handleifa(cmd, ifname, addr);
+       dhcp6_handleifa(cmd, ifname, addr);
+}
+
+int
+ipv6_handleifa_addrs(int cmd,
+    struct ipv6_addrhead *addrs, const struct in6_addr *addr)
+{
+       struct ipv6_addr *ap, *apn;
+       uint8_t found, alldadcompleted;
+
+       alldadcompleted = 1;
+       found = 0;
+       TAILQ_FOREACH_SAFE(ap, addrs, next, apn) {
+               if (memcmp(addr->s6_addr, ap->addr.s6_addr,
+                   sizeof(addr->s6_addr)))
+               {
+                       if (ap->dadcompleted == 0)
+                               alldadcompleted = 0;
+                       continue;
+               }
+               switch (cmd) {
+               case RTM_DELADDR:
+                       syslog(LOG_INFO, "%s: deleted address %s",
+                           ap->iface->name, ap->saddr);
+                       TAILQ_REMOVE(addrs, ap, next);
+                       free(ap);
+                       break;
+               case RTM_NEWADDR:
+                       if (!ap->dadcompleted) {
+                               found++;
+                               ap->dadcompleted = 1;
+                       }
+               }
+       }
+
+       return alldadcompleted ? found : 0;
+}
+
 static struct rt6 *
 find_route6(struct rt6head *rts, const struct rt6 *r)
 {
diff --git a/ipv6.h b/ipv6.h
index ad4b91a84c02111978b55816b4b050cc1ab94970..6b037e38930c8dbc1f25707d24799d8368bba4c9 100644 (file)
--- a/ipv6.h
+++ b/ipv6.h
@@ -84,6 +84,8 @@ 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 *);
 int ipv6_removesubnet(const struct interface *, struct ipv6_addr *);
 void ipv6_buildroutes(void);
 void ipv6_drop(struct interface *);
index 09a37e55a23ba0b133f19f15090d4b8a964a2bef..bb24162b0adc81f1091aa1129e9af022559882d0 100644 (file)
--- a/ipv6ns.c
+++ b/ipv6ns.c
@@ -55,8 +55,8 @@
 #include "ipv6ns.h"
 #include "script.h"
 
-#define MIN_RANDOM_FACTOR      500                             /* milliseconds */
-#define MAX_RANDOM_FACTOR      1500                            /* milliseconds */
+#define MIN_RANDOM_FACTOR      500                             /* millisecs */
+#define MAX_RANDOM_FACTOR      1500                            /* millisecs */
 #define MIN_RANDOM_FACTOR_U    MIN_RANDOM_FACTOR * 1000        /* usecs */
 #define MAX_RANDOM_FACTOR_U    MAX_RANDOM_FACTOR * 1000        /* usecs */
 
index 79c646e5fdffdc5179a4d6df45435f5c38048661..705364d928e99e88b7e213b8be76eb260ca9c8b5 100644 (file)
--- a/ipv6rs.c
+++ b/ipv6rs.c
@@ -410,6 +410,50 @@ ipv6rs_dadcallback(void *arg)
 #endif
 }
 
+static void
+ipv6rs_scriptrun(const struct ra *rap)
+{
+       int alldadcomplete, hasdns;
+       const struct ipv6_addr *ap;
+       const struct ra_opt *rao;
+
+       /* If all addresses have completed DAD run the script */
+       alldadcomplete = 1;
+       TAILQ_FOREACH(ap, &rap->addrs, next) {
+               if (ap->dadcompleted == 0)
+                       return;
+       }
+
+       /* If we don't require RDNSS then set hasdns = 1 so we fork */
+       if (!(rap->iface->options->options & DHCPCD_IPV6RA_REQRDNSS))
+               hasdns = 1;
+       else {
+               hasdns = 0;
+               TAILQ_FOREACH(rao, &rap->options, next) {
+                       if (rao->type == ND_OPT_RDNSS &&
+                           rao->option &&
+                           timerisset(&rao->expire))
+                       {
+                               hasdns = 1;
+                               break;
+                       }
+               }
+       }
+
+       script_runreason(rap->iface, "ROUTERADVERT");
+       if (hasdns)
+               daemonise();
+#if 0
+       else if (options & DHCPCD_DAEMONISE &&
+           !(options & DHCPCD_DAEMONISED) && new_data)
+               syslog(LOG_WARNING,
+                   "%s: did not fork due to an absent"
+                   " RDNSS option in the RA",
+                   ifp->name);
+}
+#endif
+}
+
 /* ARGSUSED */
 static void
 ipv6rs_handledata(__unused void *arg)
@@ -437,7 +481,7 @@ ipv6rs_handledata(__unused void *arg)
        struct ipv6_addr *ap;
        char *opt, *tmp;
        struct timeval expire;
-       uint8_t has_dns, new_rap, new_data;
+       uint8_t new_rap, new_data;
 
        len = recvmsg(sock, &rcvhdr, 0);
        if (len == -1) {
@@ -578,7 +622,6 @@ ipv6rs_handledata(__unused void *arg)
        p = ((uint8_t *)icp) + sizeof(struct nd_router_advert);
        olen = 0;
        lifetime = ~0U;
-       has_dns = 0;
        for (olen = 0; len > 0; p += olen, len -= olen) {
                if ((size_t)len < sizeof(struct nd_opt_hdr)) {
                        syslog(LOG_ERR, "%s: Short option", ifp->name);
@@ -746,8 +789,6 @@ ipv6rs_handledata(__unused void *arg)
                                                l -= (m + 1);
                                                tmp += m;
                                                *tmp++ = ' ';
-                                               if (lifetime > 0)
-                                                       has_dns = 1;
                                        }
                                }
                                if (tmp != opt)
@@ -836,23 +877,13 @@ ipv6rs_handledata(__unused void *arg)
        if (options & DHCPCD_IPV6RA_OWN)
                ipv6ns_probeaddrs(&rap->addrs);
        ipv6_buildroutes();
+
        /* We will get run by the expire function */
        if (rap->lifetime)
-               script_runreason(ifp, "ROUTERADVERT");
-
-       /* If we don't require RDNSS then set has_dns = 1 so we fork */
-       if (!(ifp->options->options & DHCPCD_IPV6RA_REQRDNSS))
-               has_dns = 1;
+               ipv6rs_scriptrun(rap);
 
        eloop_timeout_delete(NULL, ifp);
        eloop_timeout_delete(NULL, rap); /* reachable timer */
-       if (has_dns)
-               daemonise();
-       else if (options & DHCPCD_DAEMONISE &&
-           !(options & DHCPCD_DAEMONISED) && new_data)
-               syslog(LOG_WARNING,
-                   "%s: did not fork due to an absent RDNSS option in the RA",
-                   ifp->name);
 
        /* If we're owning the RA then we need to try and ensure the
         * router is actually reachable */
@@ -1050,6 +1081,25 @@ ipv6rs_findsameaddr(const struct ipv6_addr *ap)
        return NULL;
 }
 
+void
+ipv6rs_handleifa(int cmd, const char *ifname, const struct in6_addr *addr)
+{
+       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);
+               }
+       }
+}
+
 void
 ipv6rs_expire(void *arg)
 {
index 2b53d0cf9fad687cab68f3ddc466f099213afeff..bb08a60ebfe2fc59b8501fdd6c0ffa5c76bf2a6f 100644 (file)
--- a/ipv6rs.h
+++ b/ipv6rs.h
@@ -86,6 +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_drop(struct interface *);
 #else
 #define ipv6rs_start(a) {}