]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Implement the core DHCPv6 client for SOLICIT, REQUEST, RENEW, CONFIRM.
authorRoy Marples <roy@marples.name>
Tue, 6 Nov 2012 23:40:15 +0000 (23:40 +0000)
committerRoy Marples <roy@marples.name>
Tue, 6 Nov 2012 23:40:15 +0000 (23:40 +0000)
16 files changed:
bind.c
common.h
defs.h
dhcp6.c
dhcp6.h
dhcpcd-hooks/20-resolv.conf
dhcpcd-run-hooks.8.in
dhcpcd-run-hooks.in
dhcpcd.8.in
dhcpcd.c
if-bsd.c
ipv6.c
ipv6.h
ipv6ns.c
ipv6rs.c
ipv6rs.h

diff --git a/bind.c b/bind.c
index 30e639acb46170e61cc0f86c893e66fc2341c4f8..3a00465a8ce376283b72ca76b0eb1f830eba7645 100644 (file)
--- a/bind.c
+++ b/bind.c
@@ -64,6 +64,7 @@ daemonise(void)
        char buf = '\0';
        int sidpipe[2], fd;
 
+       delete_timeout(handle_exit_timeout, NULL);
        if (options & DHCPCD_DAEMONISED || !(options & DHCPCD_DAEMONISE))
                return 0;
        sigfillset(&full);
@@ -130,7 +131,6 @@ bind_interface(void *arg)
        /* We're binding an address now - ensure that sockets are closed */
        close_sockets(iface);
        state->reason = NULL;
-       delete_timeout(handle_exit_timeout, NULL);
        if (clock_monotonic)
                get_monotonic(&lease->boundtime);
        state->xid = 0;
index e11cc5110a7c34b34c8cff52d1072e94213cb620..f2a894ef817bbe959571ac887bfc2f504bf9616f 100644 (file)
--- a/common.h
+++ b/common.h
 #define UNCONST(a)             ((void *)(unsigned long)(const void *)(a))
 
 #define timeval_to_double(tv) ((tv)->tv_sec * 1.0 + (tv)->tv_usec * 1.0e-6)
+#define tv_to_ms(ms, tv)                                               \
+       do {                                                            \
+               ms = (tv)->tv_sec * 1000;                               \
+               ms += (tv)->tv_usec / 1000;                             \
+       } while (0 /* CONSTCOND */);
 #define ms_to_tv(tv, ms)                                               \
        do {                                                            \
-               (tv)->tv_sec = (ms / 1000);                             \
-               (tv)->tv_usec = ((ms % 1000) * 1000);                   \
+               (tv)->tv_sec = ms / 1000;                               \
+               (tv)->tv_usec = (ms - (tv)->tv_sec * 1000) * 1000;      \
        } while (0 /* CONSTCOND */);
 #define timernorm(tvp)                                                 \
        do {                                                            \
diff --git a/defs.h b/defs.h
index dc92c6518910a3ea00c020f6a8ad44ee848d7a39..db315377e05315c67303d4aedabf3314ff0a726a 100644 (file)
--- a/defs.h
+++ b/defs.h
@@ -28,7 +28,7 @@
 #define CONFIG_H
 
 #define PACKAGE                        "dhcpcd"
-#define VERSION                        "5.99.1"
+#define VERSION                        "5.99.2"
 
 #ifndef CONFIG
 # define CONFIG                        SYSCONFDIR "/" PACKAGE ".conf"
@@ -42,6 +42,9 @@
 #ifndef LEASEFILE
 # define LEASEFILE             DBDIR "/" PACKAGE "-%s.lease"
 #endif
+#ifndef LEASEFILE6
+# define LEASEFILE6            DBDIR "/" PACKAGE "-%s.lease6"
+#endif
 #ifndef PIDFILE
 # define PIDFILE               RUNDIR "/" PACKAGE "%s%s.pid"
 #endif
diff --git a/dhcp6.c b/dhcp6.c
index 96e48143ee1687445eee2723d9b48ec0bf689208..bf2f7736c7c580837f85e4e0bbf9b64871a61e42 100644 (file)
--- a/dhcp6.c
+++ b/dhcp6.c
@@ -25,6 +25,7 @@
  * SUCH DAMAGE.
  */
 
+#include <sys/stat.h>
 #include <sys/utsname.h>
 
 #include <netinet/in.h>
@@ -43,6 +44,7 @@
 
 #define ELOOP_QUEUE 2
 
+#include "bind.h"
 #include "config.h"
 #include "common.h"
 #include "configure.h"
@@ -50,6 +52,7 @@
 #include "dhcp6.h"
 #include "duid.h"
 #include "eloop.h"
+#include "ipv6rs.h"
 #include "platform.h"
 
 #ifndef __UNCONST
@@ -74,6 +77,7 @@ static unsigned char ansbuf[1500];
 static unsigned char *duid;
 static uint16_t duid_len;
 static char ntopbuf[INET6_ADDRSTRLEN];
+static char *status;
 
 struct dhcp6_op {
        uint16_t type;
@@ -82,8 +86,11 @@ struct dhcp6_op {
 
 static const struct dhcp6_op dhcp6_ops[] = {
        { DHCP6_SOLICIT, "SOLICIT6" },
+       { DHCP6_ADVERTISE, "ADVERTISE6" },
        { DHCP6_REQUEST, "REQUEST6" },
        { DHCP6_REPLY, "REPLY6" },
+       { DHCP6_RENEW, "RENEW6" },
+       { DHCP6_CONFIRM, "CONFIRM6" },
        { DHCP6_INFORMATION_REQ, "INFORM6" },
        { 0, NULL }
 };
@@ -119,6 +126,7 @@ dhcp6_cleanup(void)
        free(sndbuf);
        free(rcvbuf);
        free(duid);
+       free(status);
 }
 #endif
 
@@ -263,13 +271,12 @@ dhcp6_makevendor(struct dhcp6_option *o)
 #endif
 
 static const struct dhcp6_option *
-dhcp6_getoption(int code, const struct dhcp6_message *m, ssize_t len)
+dhcp6_findoption(int code, const uint8_t *d, ssize_t len)
 {
        const struct dhcp6_option *o;
 
        code = htons(code);
-       len -= sizeof(*m);
-       for (o = D6_CFIRST_OPTION(m);
+       for (o = (const struct dhcp6_option *)d;
            len > (ssize_t)sizeof(*o);
            o = D6_CNEXT_OPTION(o))
        {
@@ -288,6 +295,14 @@ dhcp6_getoption(int code, const struct dhcp6_message *m, ssize_t len)
        return NULL;
 }
 
+static const struct dhcp6_option *
+dhcp6_getoption(int code, const struct dhcp6_message *m, ssize_t len)
+{
+
+       len -= sizeof(*m);
+       return dhcp6_findoption(code,
+           (const uint8_t *)D6_CFIRST_OPTION(m), len);
+}
 
 static int
 dhcp6_updateelapsed(struct interface *ifp, struct dhcp6_message *m, ssize_t len)
@@ -312,16 +327,31 @@ dhcp6_updateelapsed(struct interface *ifp, struct dhcp6_message *m, ssize_t len)
        return 0;
 }
 
+static void
+dhcp6_newxid(struct dhcp6_message *m)
+{
+       uint32_t xid;
+
+       xid = arc4random();
+       m->xid[0] = (xid >> 16) & 0xff;
+       m->xid[1] = (xid >> 8) & 0xff;
+       m->xid[2] = xid & 0xff;
+}
+
 static int
 dhcp6_makemessage(struct interface *ifp)
 {
        struct dhcp6_state *state;
-       struct dhcp6_option *o;
-       int xid;
-       ssize_t len;
+       struct dhcp6_message *m;
+       struct dhcp6_option *o, *so;
+       const struct dhcp6_option *si;
+       ssize_t len, ml;
        uint16_t *u16;
        const struct if_options *ifo;
        const struct dhcp_opt *opt;
+       uint8_t IA_NA, *p;
+       uint32_t u32;
+       const struct ipv6_addr *ap;
 
        state = D6_STATE(ifp);
        if (state->send) {
@@ -332,6 +362,7 @@ dhcp6_makemessage(struct interface *ifp)
        /* Work out option size first */
        ifo = ifp->state->options;
        len = 0;
+       si = NULL;
        for (opt = dhcp6_opts; opt->option; opt++) {
                if (!(opt->type & REQUEST ||
                    has_option_mask(ifo->requestmask6, opt->option)))
@@ -342,42 +373,135 @@ dhcp6_makemessage(struct interface *ifp)
                len = sizeof(*u16) * 2;
        len += sizeof(*o);
 
-       len += sizeof(state->send);
+       len += sizeof(*state->send);
        len += sizeof(*o) + 14; /* clientid */ 
        len += sizeof(*o) + sizeof(uint16_t); /* elapsed */
 #ifdef DHCPCD_IANA_PEN
        len += sizeof(*o) + dhcp6_makevendor(NULL);
 #endif
+       /* IA_NA */
+       m = NULL;
+       ml = 0;
+       switch(state->state) {
+       case DH6S_REQUEST:
+               m = state->recv;
+               ml = state->recv_len;
+               /* FALLTHROUGH */
+       case DH6S_RENEW:
+               if (m == NULL) {
+                       m = state->new;
+                       ml = state->new_len;
+               }
+               si = dhcp6_getoption(D6_OPTION_SERVERID, m, ml);
+               len += sizeof(*si) + ntohs(si->len);
+               /* FALLTHROUGH */
+       case DH6S_REBOOT:
+               if (m == NULL) {
+                       m = state->new;
+                       ml = state->new_len;
+               }
+               TAILQ_FOREACH(ap, &state->addrs, next) {
+                       len += sizeof(*o) + sizeof(ap->addr.s6_addr) +
+                               sizeof(u32) + sizeof(u32);
+               }
+               /* FALLTHROUGH */
+       case DH6S_INIT: /* FALLTHROUGH */
+       case DH6S_DISCOVER:
+               len += sizeof(*o) + sizeof(u32) + sizeof(u32) + sizeof(u32);
+               IA_NA = 1;
+               break;
+       default:
+               IA_NA = 0;
+       }
+
+       if (m == NULL) {
+               m = state->new;
+               ml = state->new_len;
+       }
 
-       state->send = calloc(1, len);
+       state->send = malloc(len);
        if (state->send == NULL)
                return -1;
 
        state->send_len = len;
-       if (state->state == DH6S_INFORM)
-               state->send->type = DHCP6_INFORMATION_REQ;
-       else
+       switch(state->state) {
+               break;
+       case DH6S_INIT: /* FALLTHROUGH */
+       case DH6S_DISCOVER:
                state->send->type = DHCP6_SOLICIT;
-       xid = arc4random();
-       state->send->xid[0] = (xid >> 16) & 0xff;
-       state->send->xid[1] = (xid >> 8) & 0xff;
-       state->send->xid[2] = xid & 0xff;
+               break;
+       case DH6S_REQUEST: /* FALLTHROUGH */
+       case DH6S_REBIND:
+               state->send->type = DHCP6_REQUEST;
+               break;
+       case DH6S_RENEW:
+               state->send->type = DHCP6_RENEW;
+               break;
+       case DH6S_REBOOT:
+               state->send->type = DHCP6_CONFIRM;
+               break;
+       case DH6S_INFORM:
+               state->send->type = DHCP6_INFORMATION_REQ;
+               break;
+       default:
+               printf ("state %d\n", state->state);
+               errno = EINVAL;
+               free(state->send);
+               state->send = NULL;
+               return -1;
+       }
+
+       dhcp6_newxid(state->send);
 
        o = D6_FIRST_OPTION(state->send);
        o->code = htons(D6_OPTION_CLIENTID);
        o->len = htons(duid_len);
        memcpy(D6_OPTION_DATA(o), duid, duid_len);
 
+       if (si) {
+               o = D6_NEXT_OPTION(o);
+               memcpy(o, si, sizeof(*si) + ntohs(si->len));
+       }
+
        o = D6_NEXT_OPTION(o);
        o->code = htons(D6_OPTION_ELAPSED);
        o->len = htons(sizeof(uint16_t));
-       dhcp6_updateelapsed(ifp, state->send, state->send_len);
+       p = D6_OPTION_DATA(o);
+       memset(p, 0, sizeof(u16));
 
 #ifdef DHCPCD_IANA_PEN
        o = D6_NEXT_OPTION(o);
        dhcp6_makevendor(o);
 #endif
 
+       if (IA_NA) {
+               o = D6_NEXT_OPTION(o);
+               o->code = htons(D6_OPTION_IA_NA);
+               o->len = htons(sizeof(u32) + sizeof(u32) + sizeof(u32));
+               p = D6_OPTION_DATA(o);
+               memcpy(p, state->iaid, sizeof(u32));
+               p += sizeof(u32);
+               memset(p, 0, sizeof(u32) + sizeof(u32));
+               TAILQ_FOREACH(ap, &state->addrs, next) {
+                       so = D6_NEXT_OPTION(o);
+                       so->code = htons(D6_OPTION_IA_ADDR);
+                       so->len = htons(sizeof(ap->addr.s6_addr) +
+                           sizeof(u32) + sizeof(u32));
+                       p = D6_OPTION_DATA(so);
+                       memcpy(p, &ap->addr.s6_addr, sizeof(ap->addr.s6_addr));
+                       p += sizeof(ap->addr.s6_addr);
+                       u32 = htonl(ap->prefix_pltime);
+                       memcpy(p, &u32, sizeof(u32));
+                       p += sizeof(u32);
+                       u32 = htonl(ap->prefix_vltime);
+                       memcpy(p, &u32, sizeof(u32));
+                       /* Avoid a shadowed declaration warning by
+                        * moving our addition outside of the htons macro */
+                       u32 = ntohs(o->len) + sizeof(*so) + ntohs(so->len);
+                       o->len = htons(u32);
+               }
+       }
+
        o = D6_NEXT_OPTION(o);
        o->code = htons(D6_OPTION_ORO);
        o->len = 0;
@@ -410,14 +534,45 @@ dhcp6_get_op(uint16_t type)
        return NULL;
 }
 
+
+static void
+dhcp6_freedrop_addrs(struct interface *ifp, int drop)
+{
+       struct dhcp6_state *state;
+       struct ipv6_addr *ap;
+
+       state = D6_STATE(ifp);
+       while ((ap = TAILQ_FIRST(&state->addrs))) {
+               TAILQ_REMOVE(&state->addrs, ap, next);
+               /* Only drop the address if no other RAs have assigned it.
+                * This is safe because the RA is removed from the list
+                * before we are called. */
+               if (drop &&
+                   !dhcp6_addrexists(ap) &&
+                   !ipv6rs_addrexists(ap))
+               {
+                       syslog(LOG_INFO, "%s: deleting address %s",
+                           ifp->name, ap->saddr);
+                       if (del_address6(ifp, ap) == -1)
+                               syslog(LOG_ERR, "del_address6 %m");
+               }
+               free(ap);
+       }
+       if (drop)
+               ipv6_buildroutes();
+}
+
 static void
 dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *))
 {
        struct dhcp6_state *state;
-       struct timeval tv;
        struct sockaddr_in6 to;
        struct cmsghdr *cm;
        struct in6_pktinfo pi;
+       struct timeval RTprev;
+       double rnd;
+       suseconds_t ms;
+       uint8_t neg;
 
        state = D6_STATE(ifp);
        if (!callback)
@@ -428,24 +583,56 @@ dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *))
                    state->send->xid[1],
                    state->send->xid[2]);
        else {
-               if (state->interval == 0)
-                       state->interval = 4;
-               else {
-                       state->interval *= 2;
-                       if (state->interval > 64)
-                               state->interval = 64;
+               if (state->RTC == 0) {
+                       RTprev.tv_sec = state->IRT;
+                       RTprev.tv_usec = 0;
+                       state->RT.tv_sec = state->IRT;
+                       state->RT.tv_usec = 0;
+               } else {
+                       RTprev = state->RT;
+                       timeradd(&state->RT, &state->RT, &state->RT);
                }
-               tv.tv_sec = state->interval + DHCP_RAND_MIN;
-               tv.tv_usec = arc4random() % (DHCP_RAND_MAX_U - DHCP_RAND_MIN_U);
+
+               rnd = DHCP6_RAND_MIN;
+               rnd += arc4random() % (DHCP6_RAND_MAX - DHCP6_RAND_MIN);
+               rnd /= 1000;
+               neg = (rnd < 0.0);
+               if (neg)
+                       rnd = -rnd;
+               tv_to_ms(ms, &RTprev);
+               ms *= rnd;
+               ms_to_tv(&RTprev, ms);
+               if (neg)
+                       timersub(&state->RT, &RTprev, &state->RT);
+               else
+                       timeradd(&state->RT, &RTprev, &state->RT);
+
+               if (state->RT.tv_sec > state->MRT) {
+                       RTprev.tv_sec = state->MRT;
+                       RTprev.tv_usec = 0;
+                       state->RT.tv_sec = state->MRT;
+                       state->RT.tv_usec = 0;
+                       tv_to_ms(ms, &RTprev);
+                       ms *= rnd;
+                       ms_to_tv(&RTprev, ms);
+                       if (neg)
+                               timersub(&state->RT, &RTprev, &state->RT);
+                       else
+                               timeradd(&state->RT, &RTprev, &state->RT);
+               }
+
                syslog(LOG_DEBUG,
                    "%s: sending %s (xid 0x%02x%02x%02x), next in %0.2f seconds",
                    ifp->name, dhcp6_get_op(state->send->type),
                    state->send->xid[0],
                    state->send->xid[1],
                    state->send->xid[2],
-                   timeval_to_double(&tv));
+                   timeval_to_double(&state->RT));
        }
 
+       /* Update the elapsed time */
+       dhcp6_updateelapsed(ifp, state->send, state->send_len);
+
        to = allrouters;
        sndhdr.msg_name = (caddr_t)&to;
        sndhdr.msg_iov[0].iov_base = (caddr_t)state->send;
@@ -463,8 +650,16 @@ dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *))
        if (sendmsg(sock, &sndhdr, 0) == -1)
                syslog(LOG_ERR, "%s: sendmsg: %m", ifp->name);
 
-       if (callback)
-               add_timeout_tv(&tv, callback, ifp);
+       state->RTC++;
+       if (callback) {
+               if (state->MRC == 0 || state->RTC < state->MRC)
+                       add_timeout_tv(&state->RT, callback, ifp);
+               else if (state->MRC != 0 && state->MRCcallback)
+                       add_timeout_tv(&state->RT, state->MRCcallback, ifp);
+               else
+                       syslog(LOG_WARNING, "%s: sent %d times with no reply",
+                           ifp->name, state->RTC);
+       }
 }
 
 static void
@@ -474,6 +669,469 @@ dhcp6_sendinform(void *arg)
        dhcp6_sendmessage(arg, dhcp6_sendinform);
 }
 
+static void
+dhcp6_senddiscover(void *arg)
+{
+
+       dhcp6_sendmessage(arg, dhcp6_senddiscover);
+}
+
+static void
+dhcp6_sendrequest(void *arg)
+{
+
+       dhcp6_sendmessage(arg, dhcp6_sendrequest);
+}
+
+static void
+dhcp6_sendrebind(void *arg)
+{
+
+       dhcp6_sendmessage(arg, dhcp6_sendrebind);
+}
+
+static void
+dhcp6_sendrenew(void *arg)
+{
+
+       dhcp6_sendmessage(arg, dhcp6_sendrenew);
+}
+
+static void
+dhcp6_sendconfirm(void *arg)
+{
+
+       dhcp6_sendmessage(arg, dhcp6_sendconfirm);
+}
+
+static void
+dhcp6_startrenew(void *arg)
+{
+       struct interface *ifp;
+       struct dhcp6_state *state;
+
+       ifp = arg;
+       state = D6_STATE(ifp);
+       state->state = DH6S_RENEW;
+       state->start_uptime = uptime();
+       state->RTC = 0;
+       state->IRT = REN_TIMEOUT;
+       state->MRT = REN_MAX_RT;
+       state->MRC = 0;
+
+       if (dhcp6_makemessage(ifp) == -1)
+               syslog(LOG_ERR, "%s: dhcp6_makemessage: %m", ifp->name);
+       else
+               dhcp6_sendrenew(ifp);
+}
+
+static void
+dhcp6_startrebind(void *arg)
+{
+       struct interface *ifp;
+       struct dhcp6_state *state;
+
+       ifp = arg;
+       delete_timeout(dhcp6_sendrenew, ifp);
+       state = D6_STATE(ifp);
+       state->state = DH6S_REBIND;
+       state->RTC = 0;
+       state->IRT = REB_TIMEOUT;
+       state->MRT = REB_MAX_RT;
+       state->MRC = 0;
+
+       if (dhcp6_makemessage(ifp) == -1)
+               syslog(LOG_ERR, "%s: dhcp6_makemessage: %m", ifp->name);
+       else
+               dhcp6_sendrebind(ifp);
+}
+
+static void
+dhcp6_startdiscover(void *arg)
+{
+       struct interface *ifp;
+       struct dhcp6_state *state;
+
+       ifp = arg;
+       state = D6_STATE(ifp);
+       state->state = DH6S_DISCOVER;
+       state->start_uptime = uptime();
+       state->RTC = 0;
+       state->IRT = SOL_TIMEOUT;
+       state->MRT = SOL_MAX_RT;
+       state->MRC = 0;
+
+       delete_timeout(NULL, ifp);
+       free(state->new);
+       state->new = NULL;
+       state->new_len = 0;
+
+       if (dhcp6_makemessage(ifp) == -1)
+               syslog(LOG_ERR, "%s: dhcp6_makemessage: %m", ifp->name);
+       else
+               dhcp6_senddiscover(ifp);
+}
+
+static void
+dhcp6_failconfirm(void *arg)
+{
+       struct interface *ifp;
+
+       ifp = arg;
+       syslog(LOG_ERR, "%s: failed to confirm prior address", ifp->name);
+       /* Section 18.1.2 says that we SHOULD use the last known
+        * IP address(s) and lifetimes if we didn't get a reply.
+        * I disagree with this. */
+       dhcp6_startdiscover(ifp);
+}
+
+static void
+dhcp6_failrequest(void *arg)
+{
+       struct interface *ifp;
+
+       ifp = arg;
+       syslog(LOG_ERR, "%s: failed to request address", ifp->name);
+       /* Section 18.1.1 says that client local policy dictates
+        * what happens if a REQUEST fails.
+        * Of the possible scenarios listed, moving back to the
+        * DISCOVER phase makes more sense for us. */
+       dhcp6_startdiscover(ifp);
+}
+
+static void
+dhcp6_startrequest(struct interface *ifp)
+{
+       struct dhcp6_state *state;
+
+       delete_timeout(dhcp6_senddiscover, ifp);
+       state = D6_STATE(ifp);
+       state->state = DH6S_REQUEST;
+       state->RTC = 0;
+       state->IRT = REQ_TIMEOUT;
+       state->MRT = REQ_MAX_RT;
+       state->MRC = REQ_MAX_RC;
+       state->MRCcallback = dhcp6_failrequest;
+
+       if (dhcp6_makemessage(ifp) == -1) {
+               syslog(LOG_ERR, "%s: dhcp6_makemessage: %m", ifp->name);
+               return;
+       }
+       dhcp6_sendrequest(ifp);
+}
+
+static void
+dhcp6_startconfirm(struct interface *ifp)
+{
+       struct dhcp6_state *state;
+
+       state = D6_STATE(ifp);
+       state->state = DH6S_REBOOT;
+       state->start_uptime = uptime();
+       state->RTC = 0;
+       state->IRT = CNF_TIMEOUT;
+       state->MRT = CNF_MAX_RT;
+       state->MRC = 0;
+
+       if (dhcp6_makemessage(ifp) == -1) {
+               syslog(LOG_ERR, "%s: dhcp6_makemessage: %m", ifp->name);
+               return;
+       }
+       dhcp6_sendconfirm(ifp);
+       add_timeout_sec(CNF_MAX_RD, dhcp6_failconfirm, ifp);
+}
+
+static void
+dhcp6_startinform(struct interface *ifp)
+{
+       struct dhcp6_state *state;
+
+       state = D6_STATE(ifp);
+       state->state = DH6S_INFORM;
+       state->start_uptime = uptime();
+       state->RTC = 0;
+       state->IRT = INF_TIMEOUT;
+       state->MRT = INF_MAX_RT;
+       state->MRC = 0;
+
+       if (dhcp6_makemessage(ifp) == -1)
+               syslog(LOG_ERR, "%s: dhcp6_makemessage: %m", ifp->name);
+       else
+               dhcp6_sendinform(ifp);
+}
+
+static void
+dhcp6_startexpire(void *arg)
+{
+       struct interface *ifp;
+       const struct dhcp6_state *state;
+
+       ifp = arg;
+       delete_timeout(dhcp6_sendrebind, ifp);
+
+       syslog(LOG_ERR, "%s: DHCPv6 lease expired", ifp->name);
+       dhcp6_freedrop_addrs(ifp, 1);
+       run_script_reason(ifp, "EXPIRE6");
+       state = D6_CSTATE(ifp);
+       unlink(state->leasefile);
+       dhcp6_startdiscover(ifp);
+}
+
+static int dhcp6_getstatus(const struct dhcp6_option *o)
+{
+       const struct dhcp6_status *s;
+       size_t len;
+
+       len = ntohs(o->len);
+       if (len < sizeof(uint16_t)) {
+               syslog(LOG_ERR, "status truncated");
+               return -1;
+       }
+       if (ntohs(o->code) != D6_OPTION_STATUS_CODE) {
+               /* unlikely */
+               syslog(LOG_ERR, "not a status");
+               return -1;
+       }
+       s = (const struct dhcp6_status *)o;
+       len = ntohs(s->len) - sizeof(s->len);
+       if (status == NULL || len > strlen(status)) {
+               free(status);
+               status = malloc(len + 1);
+       }
+       memcpy(status, (const char *)s + sizeof(*s), len);
+       status[len] = '\0';
+       return ntohs(s->status);
+}
+
+int
+dhcp6_addrexists(const struct ipv6_addr *a)
+{
+       const struct interface *ifp;
+       const struct dhcp6_state *state;
+       const struct ipv6_addr *ap;
+
+       for (ifp = ifaces; ifp; ifp = ifp->next) {
+               state = D6_CSTATE(ifp);
+               if (state == NULL)
+                       continue;
+               TAILQ_FOREACH(ap, &state->addrs, next) {
+                       if (memcmp(&ap->addr, &a->addr, sizeof(a->addr)) == 0)
+                               return 1;
+               }
+       }
+       return 0;
+}
+
+static int
+dhcp6_findia(struct interface *ifp, const uint8_t *d, size_t l)
+{
+       struct dhcp6_state *state;
+       const struct dhcp6_option *o;
+       const uint8_t *p;
+       struct ipv6_addr *a;
+       const struct ipv6_addr *pa;
+       char iabuf[INET6_ADDRSTRLEN];
+       const char *ia;
+       int i;
+       uint32_t u32;
+
+       i = 0;
+       dhcp6_freedrop_addrs(ifp, 0);
+       state = D6_STATE(ifp);
+       while ((o = dhcp6_findoption(D6_OPTION_IA_ADDR, d, l))) {
+               d += ntohs(o->len);
+               l -= ntohs(o->len);
+               a = malloc(sizeof(*a));
+               if (a) {
+                       a->new = 1;
+                       p = D6_COPTION_DATA(o);
+                       memcpy(&a->addr.s6_addr, p,
+                           sizeof(a->addr.s6_addr));
+                       p += sizeof(a->addr.s6_addr);
+                       pa = ipv6rs_findprefix(a);
+                       if (pa) {
+                               memcpy(&a->prefix, &pa->prefix,
+                                   sizeof(a->prefix));
+                               a->prefix_len = pa->prefix_len;
+                       } else {
+                               a->prefix_len = 64;
+                               ipv6_makeprefix(&a->prefix, &a->addr, 64);
+                       }
+                       memcpy(&u32, p, sizeof(u32));
+                       a->prefix_pltime = ntohl(u32);
+                       p += sizeof(u32);
+                       memcpy(&u32, p, sizeof(u32));
+                       a->prefix_vltime = ntohl(u32);
+                       if (a->prefix_pltime < state->lowpl)
+                               state->lowpl = a->prefix_pltime;
+                       if (a->prefix_vltime > state->expire)
+                               state->expire = a->prefix_vltime;
+                       ia = inet_ntop(AF_INET6, &a->addr.s6_addr,
+                           iabuf, sizeof(iabuf));
+                       snprintf(a->saddr, sizeof(a->saddr),
+                           "%s/%d", ia, a->prefix_len);
+                       TAILQ_INSERT_TAIL(&state->addrs, a, next);
+                       i++;
+               }
+       }
+       return i;
+}
+
+
+static int
+dhcp6_validatelease(struct interface *ifp,
+    const struct dhcp6_message *m, size_t len,
+    const char *sfrom)
+{
+       struct dhcp6_state *state;
+       const struct dhcp6_option *o;
+       size_t l, ol;
+       const uint8_t *p;
+       uint32_t u32;
+
+       state = D6_STATE(ifp);
+       o = dhcp6_getoption(D6_OPTION_IA_NA, m, len);
+       if (o == NULL) {
+               if (sfrom)
+                       syslog(LOG_ERR, "%s: no IA_NA in REPLY from %s",
+                           ifp->name, sfrom);
+               return -1;
+       }
+       ol = ntohs(o->len);
+       l = sizeof(state->iaid) + sizeof(uint32_t) + sizeof(uint32_t);
+       if (ol < l + sizeof(struct dhcp6_status)) {
+               if (sfrom)
+                       syslog(LOG_ERR, "%s: truncated IA NA from %s",
+                           ifp->name, sfrom);
+               return -1;
+       }
+       p = D6_COPTION_DATA(o);
+       if (memcmp(p, state->iaid, sizeof(state->iaid)) != 0) {
+               syslog(LOG_ERR, "%s: IAID mismatch from %s",
+                   ifp->name, sfrom ? sfrom : "lease");
+               return -1;
+       }
+       p += sizeof(state->iaid);
+       memcpy(&u32, p, sizeof(u32));
+       state->renew = ntohl(u32);
+       p += sizeof(u32);
+       memcpy(&u32, p, sizeof(u32));
+       state->rebind = ntohl(u32);
+       if (state->renew > state->rebind && state->rebind > 0) {
+               if (sfrom)
+                       syslog(LOG_WARNING, "%s: T1 (%d) > T2 (%d) from %s",
+                           ifp->name, state->renew, state->rebind, sfrom);
+               state->renew = 0;
+               state->rebind = 0;
+       }
+       p += sizeof(u32);
+       state->expire = 0;
+       state->lowpl = ~0U;
+       ol -= l;
+       o = dhcp6_findoption(D6_OPTION_STATUS_CODE, p, ol);
+       if (o && dhcp6_getstatus(o) != D6_STATUS_OK) {
+               syslog(LOG_ERR, "%s: DHCPv6 REPLY: %s", ifp->name, status);
+               return -1;
+       }
+       if (dhcp6_findia(ifp, p, ol) == 0) {
+               syslog(LOG_ERR, "%s: %s: DHCPv6 REPLY missing IA ADDR",
+                   ifp->name, sfrom);
+               return -1;
+       }
+       return 0;
+}
+
+static ssize_t
+dhcp6_writelease(const struct interface *ifp)
+{
+       const struct dhcp6_state *state;
+       int fd;
+       ssize_t bytes;
+
+       state = D6_CSTATE(ifp);
+       syslog(LOG_DEBUG, "%s: writing lease `%s'",
+           ifp->name, state->leasefile);
+
+       fd = open(state->leasefile, O_WRONLY | O_CREAT | O_TRUNC, 0444);
+       if (fd == -1) {
+               syslog(LOG_ERR, "%s: dhcp6_writelease: %m", ifp->name);
+               return -1;
+       }
+       bytes = write(fd, state->new, state->new_len);
+       close(fd);
+       return bytes;
+}
+
+static ssize_t
+dhcp6_readlease(struct interface *ifp)
+{
+       struct dhcp6_state *state;
+       struct stat st;
+       int fd;
+       ssize_t bytes;
+       struct timeval now;
+
+       state = D6_STATE(ifp);
+       if (stat(state->leasefile, &st) == -1) {
+               if (errno == ENOENT)
+                       return 0;
+               return -1;
+       }
+       syslog(LOG_DEBUG, "%s: reading lease `%s'",
+           ifp->name, state->leasefile);
+       state->new = malloc(st.st_size);
+       if (state->new == NULL)
+               return -1;
+       state->new_len = st.st_size;
+       fd = open(state->leasefile, O_RDONLY);
+       if (fd == -1)
+               return -1;
+       bytes = read(fd, state->new, state->new_len);
+       close(fd);
+
+       /* Check to see if the lease is still valid */
+       if (dhcp6_validatelease(ifp, state->new, state->new_len, NULL) == -1)
+               goto ex;
+
+       gettimeofday(&now, NULL);
+       if ((time_t)state->expire < now.tv_sec - st.st_mtime) {
+               syslog(LOG_DEBUG, "%s: discarding expired lease", ifp->name);
+               goto ex;
+       }
+
+       return bytes;
+
+ex:
+       free(state->new);
+       state->new = NULL;
+       state->new_len = 0;
+       unlink(state->leasefile);
+       return 0;
+}
+
+static void
+dhcp6_startinit(struct interface *ifp)
+{
+       struct dhcp6_state *state;
+       int r;
+
+       state = D6_STATE(ifp);
+       state->state = DH6S_INIT;
+       state->expire = ~0U;
+       state->lowpl = ~0U;
+       if (!(options & DHCPCD_TEST)) {
+               r = dhcp6_readlease(ifp);
+               if (r == -1)
+                       syslog(LOG_ERR, "%s: dhcp6_readlease: %s: %m",
+                                       ifp->name, state->leasefile);
+               else if (r != 0) {
+                       dhcp6_startconfirm(ifp);
+                       return;
+               }
+       }
+       dhcp6_startdiscover(ifp);
+}
+
 /* ARGSUSED */
 static void
 dhcp6_handledata(_unused void *arg)
@@ -483,12 +1141,13 @@ dhcp6_handledata(_unused void *arg)
        struct in6_pktinfo pkt;
        struct interface *ifp;
        const char *sfrom, *op;
-       struct dhcp6_message *m, *r;
+       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;
 
        len = recvmsg(sock, &rcvhdr, 0);
        if (len == -1) {
@@ -524,7 +1183,6 @@ dhcp6_handledata(_unused void *arg)
                return;
        }
 
-
        for (ifp = ifaces; ifp; ifp = ifp->next)
                if (ifp->index == (unsigned int)pkt.ipi6_ifindex)
                        break;
@@ -540,17 +1198,17 @@ dhcp6_handledata(_unused void *arg)
                return;
        }
 
-       m = state->send;
        r = (struct dhcp6_message *)rcvhdr.msg_iov[0].iov_base;
-       if (r->xid[0] != m->xid[0] ||
-           r->xid[1] != m->xid[1] ||
-           r->xid[2] != m->xid[2])
+       if (r->xid[0] != state->send->xid[0] ||
+           r->xid[1] != state->send->xid[1] ||
+           r->xid[2] != state->send->xid[2])
        {
                syslog(LOG_ERR,
                    "%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],
-                   r->xid[0], r->xid[1], r->xid[2],
+                   state->send->xid[0], state->send->xid[1],
+                   state->send->xid[2],
                    sfrom);
                return;
        }
@@ -562,7 +1220,7 @@ dhcp6_handledata(_unused void *arg)
        }
 
        o = dhcp6_getoption(D6_OPTION_CLIENTID, r, len);
-       if (o && ntohs(o->len) != duid_len &&
+       if (o == NULL || ntohs(o->len) != duid_len || 
            memcmp(D6_COPTION_DATA(o), duid, duid_len) != 0)
        {
                syslog(LOG_ERR, "%s: incorrect client ID from %s",
@@ -582,35 +1240,147 @@ dhcp6_handledata(_unused void *arg)
                }
        }
 
-       m = malloc(len);
-       if (m == NULL) {
-               syslog(LOG_ERR, "%s: malloc DHCPv6 reply: %m", ifp->name);
-               return;
-       }
-
-       free(state->old);
-       state->old = state->new;
-       state->old_len = state->new_len;
-       state->new = m;
-       memcpy(m, r, len);
-       state->new_len = len;
-
        op = dhcp6_get_op(r->type);
-       if (r->type != DHCP6_REPLY) {
+       switch(r->type) {
+       case DHCP6_REPLY:
+               if (state->state == DH6S_INFORM)
+                       break;
+               switch(state->state) {
+               case DH6S_REBOOT:
+                       o = dhcp6_getoption(D6_OPTION_STATUS_CODE, r, len);
+                       if (o == NULL) {
+                               syslog(LOG_ERR,
+                                   "%s: no status code in reply from %s",
+                                   ifp->name, sfrom);
+                               return;
+                       }
+                       if (dhcp6_getstatus(o) != D6_STATUS_OK) {
+                               syslog(LOG_ERR, "%s: DHCPv6 REPLY: %s",
+                                   ifp->name, status);
+                               dhcp6_startdiscover(ifp);
+                               return;
+                       }
+                       goto recv;
+               case DH6S_REQUEST: /* FALLTHROUGH */
+               case DH6S_RENEW: /* FALLTHROUGH */
+               case DH6S_REBIND:
+                       goto replyok;
+               default:
+                       op = NULL;
+               }
+               break;
+       case DHCP6_ADVERTISE:
+               if (state->state != DH6S_DISCOVER) {
+                       op = NULL;
+                       break;
+               }
+replyok:
+               if (dhcp6_validatelease(ifp, r, len, sfrom) == -1)
+                       return;
+               break;
+       default:
                syslog(LOG_ERR, "%s: invalid DHCP6 type %s (%d)",
                    ifp->name, op, r->type);
                return;
        }
+       if (op == NULL) {
+               syslog(LOG_WARNING, "%s: invalid state for DHCP6 type %s (%d)",
+                   ifp->name, op, r->type);
+               return;
+       }
+
+       if (state->recv_len < (size_t)len) {
+               free(state->recv);
+               state->recv = malloc(len);
+               if (state->recv == NULL) {
+                       syslog(LOG_ERR, "%s: malloc recv: %m", ifp->name);
+                       return;
+               }
+       }
+       memcpy(state->recv, r, len);
+       state->recv_len = len;
+
+       switch(r->type) {
+       case DHCP6_ADVERTISE:
+               ap = TAILQ_FIRST(&state->addrs);
+               syslog(LOG_INFO, "%s: ADV %s from %s",
+                   ifp->name, ap->saddr, sfrom);
+               dhcp6_startrequest(ifp);
+               return;
+       }
 
+recv:
        syslog(LOG_INFO, "%s: %s received from %s", ifp->name, op, sfrom);
+
+       reason = NULL; 
+       delete_timeout(NULL, ifp);
        switch(state->state) {
        case DH6S_INFORM:
+               state->renew = 0;
+               state->rebind = 0;
+               state->expire = ~0U;
+               state->lowpl = ~0U;
                reason = "INFORM6";
                break;
+       case DH6S_REQUEST:
+               if (reason == NULL)
+                       reason = "BOUND6";
+               /* FALLTHROUGH */
+       case DH6S_RENEW:
+               if (reason == NULL)
+                       reason = "RENEW6";
+               /* FALLTHROUGH */
+       case DH6S_REBIND:
+               if (reason == NULL)
+                       reason = "REBIND6";
+       case DH6S_REBOOT:
+               if (reason == NULL)
+                       reason = "REBOOT6";
+               if (state->renew == 0) {
+                       if (state->expire == ~0U)
+                               state->renew = ~0U;
+                       else
+                               state->renew = state->lowpl * 0.5;
+               }
+               if (state->rebind == 0) {
+                       if (state->expire == ~0U)
+                               state->rebind = ~0U;
+                       else
+                               state->rebind = state->lowpl * 0.8;
+               }
+               break;
        default:
                reason = "UNKNOWN6";
                break;
        }
+
+       if (state->state != DH6S_REBOOT) {
+               free(state->old);
+               state->old = state->new;
+               state->old_len = state->new_len;
+               state->new = state->recv;
+               state->new_len = state->recv_len;
+               state->recv = NULL;
+               state->recv_len = 0;
+       }
+
+       if (!(options & DHCPCD_TEST)) {
+               state->state = DH6S_BOUND;
+               if (state->renew)
+                       add_timeout_sec(state->renew, dhcp6_startrenew, ifp);
+               if (state->rebind)
+                       add_timeout_sec(state->rebind, dhcp6_startrebind, ifp);
+               if (state->expire != ~0U)
+                       add_timeout_sec(state->expire, dhcp6_startexpire, ifp);
+               ipv6_addaddrs(ifp, &state->addrs);
+               if (state->renew || state->rebind)
+                       syslog(LOG_INFO,
+                           "%s: renew in %u seconds, rebind in %u seconds",
+                           ifp->name, state->renew, state->rebind);
+               ipv6_buildroutes();
+               dhcp6_writelease(ifp);
+       }
+
        run_script_reason(ifp, options & DHCPCD_TEST ? "TEST" : reason);
        if (options & DHCPCD_TEST ||
            (ifp->state->options->options & DHCPCD_INFORM &&
@@ -621,10 +1391,9 @@ dhcp6_handledata(_unused void *arg)
 #endif
                exit(EXIT_SUCCESS);
        }
-       delete_timeout(NULL, ifp);
+       daemonise();
 }
 
-
 static int
 dhcp6_open(void)
 {
@@ -686,6 +1455,7 @@ int
 dhcp6_start(struct interface *ifp, int manage)
 {
        struct dhcp6_state *state;
+       uint32_t u32;
 
        state = D6_STATE(ifp);
        if (state) {
@@ -714,13 +1484,24 @@ dhcp6_start(struct interface *ifp, int manage)
                return -1;
 
        state->state = manage ? DH6S_INIT : DH6S_INFORM;
-       state->start_uptime = uptime();
-
-       if (dhcp6_makemessage(ifp) == -1)
-               return -1;
+       TAILQ_INIT(&state->addrs);
+       snprintf(state->leasefile, sizeof(state->leasefile),
+           LEASEFILE6, ifp->name);
+
+       u32 = strlen(ifp->name);
+       if (u32 < 5) {
+               memcpy(state->iaid, ifp->name, u32);
+               if (u32 < 4)
+                       memset(state->iaid + u32, 0, 4 - u32);
+       } else {
+               u32 = htonl(ifp->index);
+               memcpy(state->iaid, &u32, 4);
+       }
 
        if (state->state == DH6S_INFORM)
-               dhcp6_sendinform(ifp);
+               dhcp6_startinform(ifp);
+       else
+               dhcp6_startinit(ifp);
 
        return 1;
 }
@@ -733,9 +1514,11 @@ dhcp6_freedrop(struct interface *ifp, int drop)
        delete_timeout(NULL, ifp);
        state = D6_STATE(ifp);
        if (state) {
+               dhcp6_freedrop_addrs(ifp, drop);
                if (drop && state->new)
                        run_script_reason(ifp, "STOP6");
                free(state->send);
+               free(state->recv);
                free(state->new);
                free(state->old);
                free(state);
@@ -772,6 +1555,7 @@ ssize_t
 dhcp6_env(char **env, const char *prefix, const struct interface *ifp,
     const struct dhcp6_message *m, ssize_t mlen)
 {
+       const struct dhcp6_state *state;
        const struct if_options *ifo;
        const struct dhcp_opt *opt;
        const struct dhcp6_option *o;
@@ -779,7 +1563,9 @@ dhcp6_env(char **env, const char *prefix, const struct interface *ifp,
        uint16_t ol;
        const uint8_t *od;
        char **ep, *v, *val;
+       const struct ipv6_addr *ap;
 
+       state = D6_CSTATE(ifp);
        e = 0;
        ep = env;
        ifo = ifp->state->options;
@@ -808,6 +1594,25 @@ dhcp6_env(char **env, const char *prefix, const struct interface *ifp,
 
        }
 
+       if (TAILQ_FIRST(&state->addrs)) {
+               if (env == NULL)
+                       e++;
+               else {
+                       e = strlen(prefix) + strlen("_dhcp6_ip_address=");
+                       TAILQ_FOREACH(ap, &state->addrs, next) {
+                               e += strlen(ap->saddr) + 1;
+                       }
+                       v = val = *ep++ = xmalloc(e);
+                       v += snprintf(val, e, "%s_dhcp6_ip_address=", prefix);
+                       TAILQ_FOREACH(ap, &state->addrs, next) {
+                               strcpy(v, ap->saddr);
+                               v += strlen(ap->saddr);
+                               *v++ = ' ';
+                       }
+                       *--v = '\0';
+               }
+       }
+
        if (env == NULL)
                return e;
        return ep - env;
diff --git a/dhcp6.h b/dhcp6.h
index f6af057c826254a77a747409a209d53c62473894..3a1b023bfc2f53d08c212a1ae9802619103560a8 100644 (file)
--- a/dhcp6.h
+++ b/dhcp6.h
@@ -51,6 +51,7 @@
 
 #define D6_OPTION_CLIENTID             1
 #define D6_OPTION_SERVERID             2
+#define D6_OPTION_IA_NA                        3
 #define D6_OPTION_ORO                  6
 #define D6_OPTION_IA_ADDR              5
 #define D6_OPTION_PREFERENCE           7
@@ -73,6 +74,7 @@
 #define D6_OPTION_BCMS_SERVER_A                34
 
 #include "dhcp.h"
+#include "ipv6.h"
 extern const struct dhcp_opt const dhcp6_opts[];
 
 struct dhcp6_message {
@@ -87,6 +89,48 @@ struct dhcp6_option {
        /* followed by data */
 } _packed;
 
+struct dhcp6_status {
+       uint16_t code;
+       uint16_t len;
+       uint16_t status;
+       /* followed by message */
+} _packed;
+
+#define D6_STATUS_OK           0
+#define D6_STATUS_FAIL         1
+#define D6_STATUS_NOADDR       2
+#define D6_STATUS_NOBINDING    3
+#define D6_STATUS_NOTONLINK    4
+#define D6_STATUS_USEMULTICAST 5
+
+#define SOL_MAX_DELAY          1
+#define SOL_TIMEOUT            1
+#define SOL_MAX_RT             120
+#define REQ_TIMEOUT            1
+#define REQ_MAX_RT             30
+#define REQ_MAX_RC             10
+#define CNF_MAX_DELAY          1
+#define CNF_TIMEOUT            1
+#define CNF_MAX_RT             4
+#define CNF_MAX_RD             10
+#define REN_TIMEOUT            10
+#define REN_MAX_RT             600
+#define REB_TIMEOUT            10
+#define REB_MAX_RT             600
+#define INF_MAX_DELAY          1
+#define INF_TIMEOUT            1
+#define INF_MAX_RT             120
+#define REL_TIMEOUT            1
+#define REL_MAX_RC             5
+#define DEC_TIMEOUT            1
+#define DEC_MAX_RC             5
+#define REC_TIMEOUT            2
+#define REC_MAX_RC             8
+#define HOP_COUNT_LIMIT                32
+
+#define DHCP6_RAND_MIN         -100
+#define DHCP6_RAND_MAX         100
 enum DH6S {
        DH6S_INIT,
        DH6S_DISCOVER,
@@ -102,17 +146,38 @@ enum DH6S {
 
 struct dhcp6_state {
        enum DH6S state;
+       uint8_t iaid[4];
        time_t start_uptime;
-       int interval;
+
+       /* Message retransmission timings */
+       struct timeval RT;
+       int RTC;
+       int IRT;
+       int MRC;
+       int MRT;
+       void (*MRCcallback)(void *);
+
        struct dhcp6_message *send;
        size_t send_len;
+       struct dhcp6_message *recv;
+       size_t recv_len;
        struct dhcp6_message *new;
        size_t new_len;
        struct dhcp6_message *old;
        size_t old_len;
+
+       uint32_t renew;
+       uint32_t rebind;
+       uint32_t expire;
+       struct ipv6_addrhead addrs;
+       uint32_t lowpl;
+       char leasefile[PATH_MAX];
 };
 
-#define D6_STATE(ifp) ((struct dhcp6_state *)(ifp)->if_data[IF_DATA_DHCP6])
+#define D6_STATE(ifp)                                                         \
+       ((struct dhcp6_state *)(ifp)->if_data[IF_DATA_DHCP6])
+#define D6_CSTATE(ifp)                                                        \
+       ((const struct dhcp6_state *)(ifp)->if_data[IF_DATA_DHCP6])
 #define D6_STATE_RUNNING(ifp) (D6_STATE((ifp)) && D6_STATE((ifp))->new)
 #define D6_FIRST_OPTION(m)                                                    \
     ((struct dhcp6_option *)                                                  \
@@ -132,6 +197,7 @@ struct dhcp6_state {
     ((const uint8_t *)(o) + sizeof(struct dhcp6_option))
 
 void dhcp6_printoptions(void);
+int dhcp6_addrexists(const struct ipv6_addr *);
 int dhcp6_start(struct interface *, int);
 ssize_t dhcp6_env(char **, const char *, const struct interface *,
     const struct dhcp6_message *, ssize_t);
index 719202de30c10f8b786b1d84294c8d6daf057011..4610079d56e3e4fcfba30453d6075f51fbe5085e 100644 (file)
@@ -140,7 +140,7 @@ remove_resolv_conf()
 
 # For ease of use, map DHCP6 names onto our DHCP4 names
 case "$reason" in
-INFORM6)
+BOUND6|RENEW6|REBIND6|REBOOT6|INFORM6)
        new_domain_name_servers="$new_dhcp6_name_servers"
        new_domain_search="$new_dhcp6_domain_search"
        ;;
index 789171aa7cdd2685b1e3e4e88474b44f74ee1c52..49a2962a8435afcaedca3b39f8bbd1d2bc833735 100644 (file)
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd October 11, 2012
+.Dd November 6, 2012
 .Dt DHCPCD-RUN-HOOKS 8
 .Os
 .Sh NAME
@@ -78,13 +78,13 @@ This is generally just a notification and no action need be taken.
 .It Dv INFORM | Dv INFORM6
 dhcpcd informed a DHCP server about it's address and obtained other
 configuration details.
-.It Dv BOUND
+.It Dv BOUND | Dv BOUND6
 dhcpcd obtained a new lease from a DHCP server.
-.It Dv RENEW
+.It Dv RENEW | Dv RENEW6
 dhcpcd renewed it's lease.
-.It Dv REBIND
+.It Dv REBIND | Dv REBIND6
 dhcpcd has rebound to a new DHCP server.
-.It Dv REBOOT
+.It Dv REBOOT | Dv REBOOT6
 dhcpcd successfully requested a lease from a DHCP server.
 .It Dv IPV4LL
 dhcpcd failed to contact any DHCP servers but did obtain an IPV4LL address.
index 7f46cd623d6c101f25501d1a0786fc0564d6c44d..6210eed8b53cf920eec932b1f945eb0a5d5c8ddc 100644 (file)
@@ -5,7 +5,7 @@
 case "$reason" in
        ROUTERADVERT)
                ifsuffix=":ra";;
-       INFORM6|BOUND6|RENEW6|REBIND6|EXPIRE6|RELEASE6|STOP6)
+       INFORM6|BOUND6|RENEW6|REBIND6|REBOOT6|EXPIRE6|RELEASE6|STOP6)
                ifsuffix=":dhcp6";;
        *)
                ifsuffix=;;
index 22197c2cd5786f0180cd61eb1b8b82f6eeab2e3b..121a5640213141bd62500dd970e5a3c4dbeb3954 100644 (file)
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd October 11, 2012
+.Dd November 6, 2012
 .Dt DHCPCD 8
 .Os
 .Sh NAME
@@ -101,7 +101,7 @@ is also an implementation of the BOOTP client specified in
 .Li RFC 951 .
 .Pp
 .Nm
-is also an implementation of an IPv6 Router Solicitor as specified in
+is also an implementation of the IPv6 Router Solicitor as specified in
 .Li RFC 4861
 and
 .Li RFC 6106 .
@@ -116,10 +116,11 @@ sends Neighbor Solicitions to each advertising router periodically and will
 expire the ones that do not respond.
 .Pp
 .Nm
-is also an implemenation of a DHCPv6 client as specified in
-.Li RFC 3315
-but only for sending Information Request messages when instructed to do so
-by an IPv6 Router Advertisment or manually.
+is also an implemenation of the DHCPv6 client as specified in
+.Li RFC 3315 .
+By default,
+.Nm
+only starts DHCPv6 when instructed to do so by an IPV6 Router Advertisement.
 .Ss Local Link configuration
 If
 .Nm
@@ -564,7 +565,8 @@ unicasts INFORM to the destination, otherwise it defaults to STATIC.
 .Sh NOTES
 .Nm
 requires a Berkley Packet Filter, or BPF device on BSD based systems and a
-Linux Socket Filter, or LPF device on Linux based systems.
+Linux Socket Filter, or LPF device on Linux based systems for all IPv4
+configuration.
 .Sh FILES
 .Bl -ohang
 .It Pa @SYSCONFDIR@/dhcpcd.conf
index 0c62a43357564a7dcf864e2ef10668e7d65991a0..a1071b04e64d9aeb9442cf127d869ce384972289 100644 (file)
--- a/dhcpcd.c
+++ b/dhcpcd.c
@@ -217,7 +217,7 @@ handle_exit_timeout(_unused void *arg)
        int timeout;
 
        syslog(LOG_ERR, "timed out");
-       if (!(options & DHCPCD_TIMEOUT_IPV4LL)) {
+       if (!(options & DHCPCD_IPV4) || !(options & DHCPCD_TIMEOUT_IPV4LL)) {
                if (options & DHCPCD_MASTER) {
                        daemonise();
                        return;
@@ -1216,8 +1216,12 @@ start_interface(void *arg)
                return;
        }
 
-       if (ifo->options & DHCPCD_INFORM && ifo->options & DHCPCD_IPV6)
-               dhcp6_start(iface, 0);
+       if (ifo->options & DHCPCD_IPV6) {
+               if (ifo->options & DHCPCD_INFORM)
+                       dhcp6_start(iface, 0);
+               else if (!(ifo->options & DHCPCD_IPV6RS))
+                       dhcp6_start(iface, 1);
+       }
 
        if (!(ifo->options & DHCPCD_IPV4))
                return;
@@ -2081,12 +2085,12 @@ main(int argc, char **argv)
        }
 #endif
 
+       if (options & DHCPCD_IPV6 && ipv6_init() == -1) {
+               options &= ~DHCPCD_IPV6;
+               syslog(LOG_ERR, "ipv6_init: %m");
+       }
        if (options & DHCPCD_IPV6RS && !check_ipv6(NULL))
                options &= ~DHCPCD_IPV6RS;
-       if (options & DHCPCD_IPV6RS && ipv6_open() == -1) {
-               options &= ~DHCPCD_IPV6RS;
-               syslog(LOG_ERR, "ipv6_open: %m");
-       }
        if (options & DHCPCD_IPV6RS) {
                ipv6rsfd = ipv6rs_open();
                if (ipv6rsfd == -1) {
index 1a35ab768e2d78eed0f95491c5eaad9e113c9103..10cf1c3bba08abd4f6ea0bcd86af1af80e2097e5 100644 (file)
--- a/if-bsd.c
+++ b/if-bsd.c
@@ -276,9 +276,13 @@ if_route(const struct rt *rt, int action)
 int
 if_address6(const struct interface *ifp, const struct ipv6_addr *a, int action)
 {
+       int s, r;
        struct in6_aliasreq ifa;
        struct in6_addr mask;
 
+       s = socket(AF_INET6, SOCK_DGRAM, 0);
+       if (s == -1)
+               return -1;
        memset(&ifa, 0, sizeof(ifa));
        strlcpy(ifa.ifra_name, ifp->name, sizeof(ifa.ifra_name));
 
@@ -295,8 +299,9 @@ if_address6(const struct interface *ifp, const struct ipv6_addr *a, int action)
        ifa.ifra_lifetime.ia6t_pltime = a->prefix_pltime;
 #undef ADDADDR
 
-       return ioctl(socket_afnet6,
-           action < 0 ? SIOCDIFADDR_IN6 : SIOCAIFADDR_IN6, &ifa);
+       r = ioctl(s, action < 0 ? SIOCDIFADDR_IN6 : SIOCAIFADDR_IN6, &ifa);
+       close(s);
+       return r;
 }
 
 int
diff --git a/ipv6.c b/ipv6.c
index e50a7484cb83c87046b1a914287ba41d8271fcd8..7a4a166db98626c774e23f7b9f3aa85c9981b4bc 100644 (file)
--- a/ipv6.c
+++ b/ipv6.c
@@ -39,6 +39,7 @@
 #include "common.h"
 #include "configure.h"
 #include "dhcpcd.h"
+#include "dhcp6.h"
 #include "ipv6.h"
 #include "ipv6rs.h"
 
@@ -47,7 +48,6 @@
 #  define s6_addr32 __u6_addr.__u6_addr32
 #endif
 
-int socket_afnet6;
 static struct rt6head *routes;
 
 #ifdef DEBUG_MEMORY
@@ -64,19 +64,18 @@ ipv6_cleanup()
 }
 #endif
 
-int
-ipv6_open(void)
+int ipv6_init(void)
 {
-       socket_afnet6 = socket(AF_INET6, SOCK_DGRAM, 0);
-       if (socket_afnet6 == -1)
+
+       routes = malloc(sizeof(*routes));
+       if (routes == NULL)
                return -1;
-       set_cloexec(socket_afnet6);
-       routes = xmalloc(sizeof(*routes));
+
        TAILQ_INIT(routes);
 #ifdef DEBUG_MEMORY
        atexit(ipv6_cleanup);
 #endif
-       return socket_afnet6;
+       return 0;
 }
 
 struct in6_addr *
@@ -116,7 +115,7 @@ ipv6_makeaddr(struct in6_addr *addr, const char *ifname,
 {
        struct in6_addr *lla;
 
-       if (prefix_len > 64) {
+       if (prefix_len < 0 || prefix_len > 64) {
                errno = EINVAL;
                return -1;
        }
@@ -134,6 +133,26 @@ ipv6_makeaddr(struct in6_addr *addr, const char *ifname,
        return 0;
 }
 
+int
+ipv6_makeprefix(struct in6_addr *prefix, const struct in6_addr *addr, int len)
+{
+       int bytelen, bitlen;
+
+       if (len < 0 || len > 128) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       bytelen = len / NBBY;
+       bitlen = len % NBBY;
+       memcpy(&prefix->s6_addr, &addr->s6_addr, bytelen);
+       if (bitlen != 0)
+               prefix->s6_addr[bytelen] >>= NBBY - bitlen;
+       memset((char *)prefix->s6_addr + bytelen, 0,
+           sizeof(prefix->s6_addr) - bytelen);
+       return 0;
+}
+
 int
 ipv6_mask(struct in6_addr *mask, int len)
 {
@@ -190,6 +209,36 @@ ipv6_prefixlen(const struct in6_addr *mask)
        return x * NBBY + y;
 }
 
+ssize_t
+ipv6_addaddrs(const struct interface *ifp, struct ipv6_addrhead *addrs)
+{
+       struct ipv6_addr *ap;
+       ssize_t i;
+
+       i = 0;
+       TAILQ_FOREACH(ap, addrs, next) {
+               if (ap->prefix_vltime == 0 ||
+                   IN6_IS_ADDR_UNSPECIFIED(&ap->addr))
+                       continue;
+               syslog(ap->new ? LOG_INFO : LOG_DEBUG,
+                   "%s: adding address %s",
+                   ifp->name, ap->saddr);
+               if (add_address6(ifp, ap) == -1)
+                       syslog(LOG_ERR, "add_address6 %m");
+               else {
+                       i++;
+                       if (ipv6_removesubnet(ifp, ap) == -1)
+                               syslog(LOG_ERR,"ipv6_removesubnet %m");
+                       syslog(LOG_DEBUG,
+                           "%s: pltime %d seconds, vltime %d seconds",
+                           ifp->name, ap->prefix_pltime,
+                           ap->prefix_vltime);
+               }
+       }
+
+       return i;
+}
+
 static struct rt6 *
 find_route6(struct rt6head *rts, const struct rt6 *r)
 {
@@ -284,39 +333,43 @@ d_route(struct rt6 *rt)
 }
 
 static struct rt6 *
-make_route(struct ra *rap)
+make_route(const struct interface *ifp, struct ra *rap)
 {
        struct rt6 *r;
 
        r = xzalloc(sizeof(*r));
        r->ra = rap;
-       r->iface = rap->iface;
-       r->metric = rap->iface->metric;
-       r->mtu = rap->mtu;
+       r->iface = ifp;
+       r->metric = ifp->metric;
+       if (rap)
+               r->mtu = rap->mtu;
+       else
+               r->mtu = 0;
        return r;
 }
 
 static struct rt6 *
-make_prefix(struct ra *rap, struct ipv6_addr *addr)
+make_prefix(const struct interface * ifp,struct ra *rap, struct ipv6_addr *addr)
 {
        struct rt6 *r;
 
        if (addr == NULL || addr->prefix_len > 128)
                return NULL;
 
-       r = make_route(rap);
+       r = make_route(ifp, rap);
        r->dest = addr->prefix;
        ipv6_mask(&r->net, addr->prefix_len);
        r->gate = in6addr_any;
        return r;
 }
 
+
 static struct rt6 *
 make_router(struct ra *rap)
 {
        struct rt6 *r;
 
-       r = make_route(rap);
+       r = make_route(rap->iface, rap);
        r->dest = in6addr_any;
        r->net = in6addr_any;
        r->gate = rap->from;
@@ -324,7 +377,7 @@ make_router(struct ra *rap)
 }
 
 int
-ipv6_remove_subnet(struct ra *rap, struct ipv6_addr *addr)
+ipv6_removesubnet(const struct interface *ifp, struct ipv6_addr *addr)
 {
        struct rt6 *rt;
 #if HAVE_ROUTE_METRIC
@@ -335,9 +388,9 @@ ipv6_remove_subnet(struct ra *rap, struct ipv6_addr *addr)
        /* We need to delete the subnet route to have our metric or
         * prefer the interface. */
        r = 0;
-       rt = make_prefix(rap, addr);
+       rt = make_prefix(ifp, NULL, addr);
        if (rt) {
-               rt->iface = rap->iface;
+               rt->iface = ifp;
 #ifdef __linux__
                rt->metric = 256;
 #else
@@ -370,22 +423,34 @@ ipv6_remove_subnet(struct ra *rap, struct ipv6_addr *addr)
            IN6_ARE_ADDR_EQUAL(&((rtp)->net), &in6addr_any))
 
 void
-ipv6_build_routes(void)
+ipv6_buildroutes(void)
 {
        struct rt6head dnr, *nrs;
        struct rt6 *rt, *rtn, *or;
        struct ra *rap;
        struct ipv6_addr *addr;
+       const struct interface *ifp;
+       const struct dhcp6_state *d6_state;
        int have_default;
 
        if (!(options & (DHCPCD_IPV6RA_OWN | DHCPCD_IPV6RA_OWN_DEFAULT)))
                return;
 
        TAILQ_INIT(&dnr);
+       for (ifp = ifaces; ifp; ifp = ifp->next) {
+               d6_state = D6_CSTATE(ifp);
+               if (d6_state && d6_state->state == DH6S_BOUND) {
+                       TAILQ_FOREACH(addr, &d6_state->addrs, next) {
+                               rt = make_prefix(ifp, NULL, addr);
+                               if (rt)
+                                       TAILQ_INSERT_TAIL(&dnr, rt, next);
+                       }
+               }
+       }
        TAILQ_FOREACH(rap, &ipv6_routers, next) {
                if (options & DHCPCD_IPV6RA_OWN) {
                        TAILQ_FOREACH(addr, &rap->addrs, next) {
-                               rt = make_prefix(rap, addr);
+                               rt = make_prefix(rap->iface, rap, addr);
                                if (rt)
                                        TAILQ_INSERT_TAIL(&dnr, rt, next);
                        }
diff --git a/ipv6.h b/ipv6.h
index 8f6ef27f8da6584d933e0bd1412132c17221f363..55be21681f52055ce333cc6959ed3a6ff77468bf 100644 (file)
--- a/ipv6.h
+++ b/ipv6.h
@@ -32,8 +32,6 @@
 
 #include <netinet/in.h>
 
-#include "ipv6rs.h"
-
 #define ALLROUTERS "ff02::2"
 #define HOPLIMIT 255
 
@@ -49,6 +47,7 @@ struct ipv6_addr {
        int new;
        char saddr[INET6_ADDRSTRLEN];
 };
+TAILQ_HEAD(ipv6_addrhead, ipv6_addr);
 
 struct rt6 {
        TAILQ_ENTRY(rt6) next;
@@ -62,15 +61,15 @@ struct rt6 {
 };
 TAILQ_HEAD(rt6head, rt6);
 
-extern int socket_afnet6;
-
-int ipv6_open(void);
+int ipv6_init(void);
 struct in6_addr *ipv6_linklocal(const char *);
 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_remove_subnet(struct ra *, struct ipv6_addr *);
-void ipv6_build_routes(void);
+ssize_t ipv6_addaddrs(const struct interface *, struct ipv6_addrhead *);
+int ipv6_removesubnet(const struct interface *, struct ipv6_addr *);
+void ipv6_buildroutes(void);
 void ipv6_drop(struct interface *);
 
 #endif
index c66770c258d5939d68ebae94faa413668a649b59..9a2ac2495900d1f2095436ccd432e1a4d9f8b615 100644 (file)
--- a/ipv6ns.c
+++ b/ipv6ns.c
@@ -168,7 +168,7 @@ ipv6ns_unreachable(void *arg)
        syslog(LOG_WARNING, "%s: %s is unreachable, expiring it",
            rap->iface->name, rap->sfrom);
        rap->expired = 1;
-       ipv6_build_routes();
+       ipv6_buildroutes();
        run_script_reason(rap->iface, "ROUTERADVERT"); /* XXX not RA */
 }
 
@@ -341,7 +341,7 @@ ipv6ns_handledata(_unused void *arg)
                syslog(LOG_INFO, "%s: %s is no longer a router",
                    ifp->name, sfrom);
                rap->expired = 1;
-               ipv6_build_routes();
+               ipv6_buildroutes();
                run_script_reason(ifp, "ROUTERADVERT");
                return;
        }
index deb2a968f11cbdee2af9f3e9a04f7cb12fed2d52..62bc34a0ca13a3884487c84665111a0522325ef5 100644 (file)
--- a/ipv6rs.c
+++ b/ipv6rs.c
@@ -269,8 +269,8 @@ ipv6rs_free_opts(struct ra *rap)
        }
 }
 
-static int
-ipv6rs_addrexists(struct ipv6_addr *a)
+int
+ipv6rs_addrexists(const struct ipv6_addr *a)
 {
        struct ra *rap;
        struct ipv6_addr *ap;
@@ -295,7 +295,7 @@ ipv6rs_freedrop_addrs(struct ra *rap, int drop)
                 * This is safe because the RA is removed from the list
                 * before we are called. */
                if (drop && (options & DHCPCD_IPV6RA_OWN) &&
-                   !ipv6rs_addrexists(ap))
+                   !ipv6rs_addrexists(ap) && !dhcp6_addrexists(ap))
                {
                        syslog(LOG_INFO, "%s: deleting address %s",
                            rap->iface->name, ap->saddr);
@@ -566,6 +566,13 @@ ipv6rs_handledata(_unused void *arg)
                                    "%s: invalid prefix in RA", ifp->name);
                                break;
                        }
+                       if (ntohl(pi->nd_opt_pi_preferred_time) >
+                           ntohl(pi->nd_opt_pi_valid_time))
+                       {
+                               syslog(LOG_ERR,
+                                   "%s: pltime > vltime", ifp->name);
+                               break;
+                       }
                        TAILQ_FOREACH(ap, &rap->addrs, next)
                                if (ap->prefix_len ==pi->nd_opt_pi_prefix_len &&
                                    memcmp(ap->prefix.s6_addr,
@@ -579,15 +586,26 @@ ipv6rs_handledata(_unused void *arg)
                                memcpy(ap->prefix.s6_addr,
                                   pi->nd_opt_pi_prefix.s6_addr,
                                   sizeof(ap->prefix.s6_addr));
-                               ipv6_makeaddr(&ap->addr, ifp->name,
-                                   &ap->prefix, pi->nd_opt_pi_prefix_len);
-                               cbp = inet_ntop(AF_INET6, ap->addr.s6_addr,
-                                   ntopbuf, INET6_ADDRSTRLEN);
-                               if (cbp)
-                                       snprintf(ap->saddr, sizeof(ap->saddr),
-                                           "%s/%d", cbp, ap->prefix_len);
-                               else
+                               if (pi->nd_opt_pi_flags_reserved &
+                                   ND_OPT_PI_FLAG_AUTO)
+                               {
+                                       ipv6_makeaddr(&ap->addr, ifp->name,
+                                           &ap->prefix,
+                                           pi->nd_opt_pi_prefix_len);
+                                       cbp = inet_ntop(AF_INET6,
+                                           ap->addr.s6_addr,
+                                           ntopbuf, INET6_ADDRSTRLEN);
+                                       if (cbp)
+                                               snprintf(ap->saddr,
+                                                   sizeof(ap->saddr),
+                                                   "%s/%d",
+                                                   cbp, ap->prefix_len);
+                                       else
+                                               ap->saddr[0] = '\0';
+                               } else {
+                                       memset(&ap->addr, 0, sizeof(ap->addr));
                                        ap->saddr[0] = '\0';
+                               }
                                TAILQ_INSERT_TAIL(&rap->addrs, ap, next);
                        } else if (ap->prefix_vltime !=
                            ntohl(pi->nd_opt_pi_valid_time) ||
@@ -711,27 +729,10 @@ ipv6rs_handledata(_unused void *arg)
 
        if (new_rap)
                add_router(rap);
-       if (options & DHCPCD_IPV6RA_OWN && !(options & DHCPCD_TEST)) {
-               TAILQ_FOREACH(ap, &rap->addrs, next) {
-                       if (ap->prefix_vltime == 0)
-                               continue;
-                       syslog(ap->new ? LOG_INFO : LOG_DEBUG,
-                           "%s: adding address %s",
-                           ifp->name, ap->saddr);
-                       if (add_address6(ifp, ap) == -1)
-                               syslog(LOG_ERR, "add_address6 %m");
-                       else {
-                               if (ipv6_remove_subnet(rap, ap) == -1)
-                                       syslog(LOG_ERR,"ipv6_remove_subnet %m");
-                               syslog(LOG_DEBUG,
-                                   "%s: vltime %d seconds, pltime %d seconds",
-                                   ifp->name, ap->prefix_vltime,
-                                   ap->prefix_pltime);
-                       }
-               }
-       }
+       if (options & DHCPCD_IPV6RA_OWN && !(options & DHCPCD_TEST))
+               ipv6_addaddrs(ifp, &rap->addrs);
        if (!(options & DHCPCD_TEST))
-               ipv6_build_routes();
+               ipv6_buildroutes();
        run_script_reason(ifp, options & DHCPCD_TEST ? "TEST" : "ROUTERADVERT");
        if (options & DHCPCD_TEST)
                goto handle_flag;
@@ -740,8 +741,6 @@ ipv6rs_handledata(_unused void *arg)
        if (!(ifp->state->options->options & DHCPCD_IPV6RA_REQRDNSS))
                has_dns = 1;
 
-       if (has_dns)
-               delete_q_timeout(0, handle_exit_timeout, NULL);
        delete_timeout(NULL, ifp);
        delete_timeout(NULL, rap); /* reachable timer */
        if (has_dns)
@@ -763,13 +762,8 @@ ipv6rs_handledata(_unused void *arg)
 
 handle_flag:
        if (rap->flags & ND_RA_FLAG_MANAGED) {
-               if (new_data)
-                       syslog(LOG_WARNING, "%s: no support for DHCPv6 management",
-                           ifp->name);
-               if (options & DHCPCD_TEST)
-                       exit(EXIT_SUCCESS);
-//             if (dhcp6_start(ifp, 1) == -1)
-//                     syslog(LOG_ERR, "dhcp6_start: %s: %m", ifp->name);
+               if (dhcp6_start(ifp, 1) == -1)
+                       syslog(LOG_ERR, "dhcp6_start: %s: %m", ifp->name);
        } else if (rap->flags & ND_RA_FLAG_OTHER) {
                if (dhcp6_start(ifp, 0) == -1)
                        syslog(LOG_ERR, "dhcp6_start: %s: %m", ifp->name);
@@ -861,7 +855,8 @@ ipv6rs_env(char **env, const char *prefix, const struct interface *ifp)
                                                continue;
                                        } else
                                                new++;
-                                       len = (new - **var) + strlen(rao->option) + 1;
+                                       len = (new - **var) +
+                                           strlen(rao->option) + 1;
                                        if (len > strlen(**var))
                                                new = realloc(**var, len);
                                        else
@@ -870,9 +865,11 @@ ipv6rs_env(char **env, const char *prefix, const struct interface *ifp)
                                                **var = new;
                                                new = strchr(**var, '=');
                                                if (new)
-                                                       strcpy(new + 1, rao->option);
+                                                       strcpy(new + 1,
+                                                           rao->option);
                                                else
-                                                       syslog(LOG_ERR, "new is null");
+                                                       syslog(LOG_ERR,
+                                                           "new is null");
                                        }
                                        continue;
                                }
@@ -901,6 +898,34 @@ ipv6rs_env(char **env, const char *prefix, const struct interface *ifp)
        return l;
 }
 
+const struct ipv6_addr *
+ipv6rs_findprefix(const struct ipv6_addr *a)
+{
+       const struct ra *rap;
+       const struct ipv6_addr *ap;
+       const struct in6_addr *p1, *p2;
+       int bytelen, bitlen;
+
+       p1 = &a->addr;
+       TAILQ_FOREACH(rap, &ipv6_routers, next) {
+               TAILQ_FOREACH(ap, &rap->addrs, next) {
+                       p2 = & ap->prefix;
+                       bytelen = ap->prefix_len / NBBY;
+                       bitlen = ap->prefix_len % NBBY;
+                       if (memcmp(&p1->s6_addr, &p2->s6_addr, bytelen))
+                               continue;
+                       if (bitlen != 0) {
+                               bitlen = NBBY - bitlen;
+                               if (p1->s6_addr[bytelen] >> bitlen !=
+                                   p2->s6_addr[bytelen] >> bitlen)
+                               continue;
+                       }
+                       return ap;
+               }
+       }
+       return NULL;    
+}
+
 static const struct ipv6_addr *
 ipv6rs_findsameaddr(const struct ipv6_addr *ap)
 {
@@ -1017,7 +1042,7 @@ ipv6rs_expire(void *arg)
        if (timerisset(&next))
                add_timeout_tv(&next, ipv6rs_expire, ifp);
        if (expired) {
-               ipv6_build_routes();
+               ipv6_buildroutes();
                run_script_reason(ifp, "ROUTERADVERT");
        }
 }
@@ -1065,7 +1090,7 @@ ipv6rs_drop(struct interface *ifp)
                }
        }
        if (expired) {
-               ipv6_build_routes();
+               ipv6_buildroutes();
                TAILQ_FOREACH_SAFE(rap, &ipv6_routers, next, ran) {
                        if (rap->iface == ifp) {
                                TAILQ_CONCAT(&rap->addrs, &addrs, next);
index 7fa73fde7da2bce8a4300ccd54a56b4d0b137668..eeff3ce79b40982e873261018a8a231d97bf87ea 100644 (file)
--- a/ipv6rs.h
+++ b/ipv6rs.h
@@ -55,7 +55,7 @@ struct ra {
        uint32_t reachable;
        uint32_t retrans;
        uint32_t mtu;
-       TAILQ_HEAD(, ipv6_addr) addrs;
+       struct ipv6_addrhead addrs;
        TAILQ_HEAD(, ra_opt) options;
        
        unsigned char *ns;
@@ -79,6 +79,8 @@ int ipv6rs_open(void);
 void ipv6rs_handledata(void *);
 int ipv6rs_start(struct interface *);
 ssize_t ipv6rs_env(char **, const char *, const struct interface *);
+const struct ipv6_addr * ipv6rs_findprefix(const struct ipv6_addr *);
+int ipv6rs_addrexists(const struct ipv6_addr *);
 void ipv6rs_freedrop_ra(struct ra *, int);
 #define ipv6rs_free_ra(ra) ipv6rs_freedrop_ra((ra),  0)
 #define ipv6rs_drop_ra(ra) ipv6rs_freedrop_ra((ra),  1)