]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Add DHCPv6 Prefix Delegation support, RFC3633.
authorRoy Marples <roy@marples.name>
Mon, 1 Apr 2013 12:15:47 +0000 (12:15 +0000)
committerRoy Marples <roy@marples.name>
Mon, 1 Apr 2013 12:15:47 +0000 (12:15 +0000)
Add DHCPv6 Temporary Address support, RFC3315.

defs.h
dhcp6.c
dhcp6.h
dhcpcd.8.in
dhcpcd.c
dhcpcd.conf.5.in
if-options.c
if-options.h
ipv6.c
ipv6.h

diff --git a/defs.h b/defs.h
index 3fa8f97a9ab22c22441e8fb24b311abe1b6f7c49..ca782e725fe7043f07ee4c35a6d327259adef010 100644 (file)
--- a/defs.h
+++ b/defs.h
@@ -28,7 +28,7 @@
 #define CONFIG_H
 
 #define PACKAGE                        "dhcpcd"
-#define VERSION                        "5.99.3"
+#define VERSION                        "5.99.4"
 
 #ifndef CONFIG
 # define CONFIG                        SYSCONFDIR "/" PACKAGE ".conf"
diff --git a/dhcp6.c b/dhcp6.c
index 5d09236bdc8ab115ced943a60b5c6d7021d4654d..5d0df8e932c074f2ac1c01c1e92ac3a7d15a2751 100644 (file)
--- a/dhcp6.c
+++ b/dhcp6.c
@@ -89,6 +89,7 @@ static const struct dhcp6_op dhcp6_ops[] = {
        { DHCP6_REQUEST, "REQUEST6" },
        { DHCP6_REPLY, "REPLY6" },
        { DHCP6_RENEW, "RENEW6" },
+       { DHCP6_REBIND, "REBIND6" },
        { DHCP6_CONFIRM, "CONFIRM6" },
        { DHCP6_INFORMATION_REQ, "INFORM6" },
        { 0, NULL }
@@ -333,7 +334,7 @@ dhcp6_newxid(const struct interface *ifp, struct dhcp6_message *m)
        uint32_t xid;
 
        if (ifp->options->options & DHCPCD_XID_HWADDR &&
-           ifp->hwlen >= sizeof(xid)) 
+           ifp->hwlen >= sizeof(xid))
                /* The lower bits are probably more unique on the network */
                memcpy(&xid, (ifp->hwaddr + ifp->hwlen) - sizeof(xid),
                    sizeof(xid));
@@ -353,10 +354,12 @@ dhcp6_makemessage(struct interface *ifp)
        struct dhcp6_option *o, *so;
        const struct dhcp6_option *si;
        ssize_t len, ml;
+       size_t l;
+       uint8_t u8;
        uint16_t *u16;
        const struct if_options *ifo;
        const struct dhcp_opt *opt;
-       uint8_t IA_NA, *p;
+       uint8_t IA, *p;
        uint32_t u32;
        const struct ipv6_addr *ap;
 
@@ -381,12 +384,13 @@ dhcp6_makemessage(struct interface *ifp)
        len += sizeof(*o);
 
        len += sizeof(*state->send);
-       len += sizeof(*o) + 14; /* clientid */ 
+       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 */
+
+       /* IA */
        m = NULL;
        ml = 0;
        switch(state->state) {
@@ -402,23 +406,30 @@ dhcp6_makemessage(struct interface *ifp)
                si = dhcp6_getoption(D6_OPTION_SERVERID, m, ml);
                len += sizeof(*si) + ntohs(si->len);
                /* FALLTHROUGH */
-       case DH6S_REBOOT:
+       case DH6S_REBIND:
+               /* FALLTHROUGH */
+       case DH6S_CONFIRM:
                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);
+                       if (ifo->ia_type == D6_OPTION_IA_PD)
+                               len += sizeof(*o) + sizeof(u8) +
+                                   sizeof(u32) + sizeof(u32) + 
+                                   sizeof(ap->prefix.s6_addr);
+                       else
+                               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;
+               len += ifo->iaid_len * (sizeof(*o) + (sizeof(u32) * 3));
+               IA = 1;
                break;
        default:
-               IA_NA = 0;
+               IA = 0;
        }
 
        if (m == NULL) {
@@ -437,21 +448,22 @@ dhcp6_makemessage(struct interface *ifp)
        case DH6S_DISCOVER:
                state->send->type = DHCP6_SOLICIT;
                break;
-       case DH6S_REQUEST: /* FALLTHROUGH */
-       case DH6S_REBIND:
+       case DH6S_REQUEST:
                state->send->type = DHCP6_REQUEST;
                break;
+       case DH6S_CONFIRM:
+               state->send->type = DHCP6_CONFIRM;
+               break;
+       case DH6S_REBIND:
+               state->send->type = DHCP6_REBIND;
+               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;
@@ -481,31 +493,60 @@ dhcp6_makemessage(struct interface *ifp)
        dhcp6_makevendor(o);
 #endif
 
-       if (IA_NA) {
+       for (l = 0; IA && l < ifo->iaid_len; l++) {
                o = D6_NEXT_OPTION(o);
-               o->code = htons(D6_OPTION_IA_NA);
+               o->code = htons(ifo->ia_type);
                o->len = htons(sizeof(u32) + sizeof(u32) + sizeof(u32));
                p = D6_OPTION_DATA(o);
-               memcpy(p, state->iaid, sizeof(u32));
+               memcpy(p, ifo->iaid[l].iaid, sizeof(u32));
                p += sizeof(u32);
                memset(p, 0, sizeof(u32) + sizeof(u32));
                TAILQ_FOREACH(ap, &state->addrs, next) {
+                       if (memcmp(ifo->iaid[l].iaid, ap->iaid, sizeof(u32)))
+                               continue;
                        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);
+                       if (ifo->ia_type == D6_OPTION_IA_PD) {
+                               so->code = htons(D6_OPTION_IAPREFIX);
+                               so->len = htons(sizeof(ap->prefix.s6_addr) +
+                                   sizeof(u32) + sizeof(u32) + sizeof(u8));
+                               p = D6_OPTION_DATA(so);
+                               u32 = htonl(ap->prefix_pltime);
+                               memcpy(p, &u32, sizeof(u32));
+                               p += sizeof(u32);
+                               u32 = htonl(ap->prefix_vltime);
+                               memcpy(p, &u32, sizeof(u32));
+                               p += sizeof(u32);
+                               u8 = ap->prefix_len;
+                               memcpy(p, &u8, sizeof(u8));
+                               p += sizeof(u8);
+                               memcpy(p, &ap->prefix.s6_addr,
+                                   sizeof(ap->prefix.s6_addr));
+                               /* 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);
+                       } else {
+                               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);
+                       }
                }
        }
 
@@ -554,7 +595,7 @@ dhcp6_freedrop_addrs(struct interface *ifp, int drop)
                /* 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 &&
+               if (drop && ap->onlink &&
                    !dhcp6_addrexists(ap) &&
                    !ipv6rs_addrexists(ap))
                {
@@ -653,7 +694,7 @@ dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *))
        memset(&pi, 0, sizeof(pi));
        pi.ipi6_ifindex = ifp->index;
        memcpy(CMSG_DATA(cm), &pi, sizeof(pi));
-       
+
        if (sendmsg(sock, &sndhdr, 0) == -1) {
                syslog(LOG_ERR, "%s: sendmsg: %m", ifp->name);
                ifp->options->options &= ~DHCPCD_IPV6;
@@ -749,10 +790,17 @@ dhcp6_startrebind(void *arg)
        state = D6_STATE(ifp);
        state->state = DH6S_REBIND;
        state->RTC = 0;
-       state->IRT = REB_TIMEOUT;
-       state->MRT = REB_MAX_RT;
        state->MRC = 0;
 
+       /* RFC 3633 section 12.1 */
+       if (ifp->options->ia_type == D6_OPTION_IA_PD) {
+           state->IRT = CNF_TIMEOUT;
+           state->MRT = CNF_MAX_RT;
+       } else {
+           state->IRT = REB_TIMEOUT;
+           state->MRT = REB_MAX_RT;
+       }
+
        if (dhcp6_makemessage(ifp) == -1)
                syslog(LOG_ERR, "%s: dhcp6_makemessage: %m", ifp->name);
        else
@@ -843,7 +891,7 @@ dhcp6_startconfirm(struct interface *ifp)
        struct dhcp6_state *state;
 
        state = D6_STATE(ifp);
-       state->state = DH6S_REBOOT;
+       state->state = DH6S_CONFIRM;
        state->start_uptime = uptime();
        state->RTC = 0;
        state->IRT = CNF_TIMEOUT;
@@ -940,7 +988,8 @@ dhcp6_addrexists(const struct ipv6_addr *a)
 }
 
 static int
-dhcp6_findia(struct interface *ifp, const uint8_t *d, size_t l)
+dhcp6_findna(struct interface *ifp, const uint8_t *iaid,
+    const uint8_t *d, size_t l)
 {
        struct dhcp6_state *state;
        const struct dhcp6_option *o;
@@ -953,49 +1002,185 @@ dhcp6_findia(struct interface *ifp, const uint8_t *d, size_t l)
        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;
-                       a->onlink = 1; /* XXX: suprised no DHCP opt for this */
-                       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);
+               if (a == NULL) {
+                       syslog(LOG_ERR, "%s: %m", __func__);
+                       break;
+               }
+               a->new = 1;
+               a->onlink = 1; /* XXX: suprised no DHCP opt for this */
+               memcpy(a->iaid, iaid, sizeof(a->iaid));
+               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;
+                       if (ipv6_makeprefix(&a->prefix, &a->addr, 64) == -1) {
+                               syslog(LOG_ERR, "%s: %m", __func__);
+                               free(a);
+                               continue;
                        }
+               }
+               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_findpd(struct interface *ifp, const uint8_t *iaid,
+    const uint8_t *d, size_t l)
+{
+       struct dhcp6_state *state;
+       const struct dhcp6_option *o;
+       const uint8_t *p;
+       struct ipv6_addr *a;
+       char iabuf[INET6_ADDRSTRLEN];
+       const char *ia;
+       int i;
+       uint8_t u8;
+       uint32_t u32;
+
+       i = 0;
+       state = D6_STATE(ifp);
+       while ((o = dhcp6_findoption(D6_OPTION_IAPREFIX, d, l))) {
+               u32 = ntohs(o->len);
+               d += u32;
+               l -= u32;
+               a = malloc(sizeof(*a));
+               if (a == NULL) {
+                       syslog(LOG_ERR, "%s: %m", __func__);
+                       break;
+               }
+               a->new = 1;
+               a->onlink = 0;
+               memcpy(a->iaid, iaid, sizeof(a->iaid));
+               p = D6_COPTION_DATA(o);
+               memcpy(&u32, p, sizeof(u32));
+               a->prefix_pltime = ntohl(u32);
+               p += sizeof(u32);
+               memcpy(&u32, p, sizeof(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;
+               memcpy(&u8, p, sizeof(u8));
+               p += sizeof(u8);
+               a->prefix_len = u8;
+               memcpy(&a->prefix.s6_addr, p, sizeof(a->prefix.s6_addr));
+               p += sizeof(a->prefix.s6_addr);
+               ia = inet_ntop(AF_INET6, &a->prefix.s6_addr,
+                   iabuf, sizeof(iabuf));
+               snprintf(a->saddr, sizeof(a->saddr),
+                   "%s/%d", ia, a->prefix_len);
+               memset(a->addr.s6_addr, 0, sizeof(a->addr.s6_addr));
+               TAILQ_INSERT_TAIL(&state->addrs, a, next);
+               i++;
+       }
+       return i;
+}
+
+static int
+dhcp6_findia(struct interface *ifp, const uint8_t *d, size_t l,
+    const char *sfrom)
+{
+       struct dhcp6_state *state;
+       const struct if_options *ifo;
+       const struct dhcp6_option *o;
+       const uint8_t *p;
+       int i;
+       uint32_t u32, renew, rebind;
+       uint8_t iaid[4];
+       size_t ol;
+
+       ifo = ifp->options;
+       i = 0;
+       dhcp6_freedrop_addrs(ifp, 0);
+       state = D6_STATE(ifp);
+       while ((o = dhcp6_findoption(ifo->ia_type, d, l))) {
+               ol = sizeof(*o) + ntohs(o->len);
+               d += ol;
+               l -= ol;
+
+               p = D6_COPTION_DATA(o);
+               memcpy(iaid, p, sizeof(iaid));
+               p += sizeof(iaid);
+               ol -= sizeof(iaid);
+               if (ifo->ia_type == D6_OPTION_IA_NA) {
                        memcpy(&u32, p, sizeof(u32));
-                       a->prefix_pltime = ntohl(u32);
+                       renew = ntohl(u32);
                        p += sizeof(u32);
+                       ol -= 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++;
+                       rebind = ntohl(u32);
+                       p += sizeof(u32);
+                       ol -= sizeof(u32);
+                       if (renew > rebind && rebind > 0) {
+                               if (sfrom)
+                                   syslog(LOG_WARNING,
+                                       "%s: T1 (%d) > T2 (%d) from %s",
+                                       ifp->name, renew, rebind, sfrom);
+                               renew = 0;
+                               rebind = 0;
+                       }
+                       if (renew != 0 &&
+                           (renew < state->renew || state->renew == 0))
+                               state->renew = renew;
+                       if (rebind != 0 &&
+                           (rebind < state->rebind || state->rebind == 0))
+                               state->rebind = rebind;
+               }
+               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 (ifo->ia_type == D6_OPTION_IA_PD) {
+                       if (dhcp6_findpd(ifp, iaid, p, ol) == 0) {
+                               syslog(LOG_ERR,
+                                   "%s: %s: DHCPv6 REPLY missing Prefix",
+                                   ifp->name, sfrom);
+                               return -1;
+                       }
+               } else {
+                       if (dhcp6_findna(ifp, iaid, p, ol) == 0) {
+                               syslog(LOG_ERR,
+                                   "%s: %s: DHCPv6 REPLY missing IA Address",
+                                   ifp->name, sfrom);
+                               return -1;
+                       }
+               }
+               i++;
        }
        return i;
 }
 
-
 static int
 dhcp6_validatelease(struct interface *ifp,
     const struct dhcp6_message *m, size_t len,
@@ -1003,60 +1188,20 @@ dhcp6_validatelease(struct interface *ifp,
 {
        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);
+       o = dhcp6_getoption(ifp->options->ia_type, m, len);
        if (o == NULL) {
                if (sfrom)
-                       syslog(LOG_ERR, "%s: no IA_NA in REPLY from %s",
+                       syslog(LOG_ERR, "%s: no IA 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->renew = state->rebind = 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;
+       len -= (const char *)o - (const char *)m;
+       return dhcp6_findia(ifp, (const uint8_t *)o, len, sfrom);
 }
 
 static ssize_t
@@ -1138,19 +1283,187 @@ dhcp6_startinit(struct interface *ifp)
        state->state = DH6S_INIT;
        state->expire = ~0U;
        state->lowpl = ~0U;
-       if (!(options & DHCPCD_TEST)) {
+       if (!(options & DHCPCD_TEST) &&
+           ifp->options->ia_type != D6_OPTION_IA_TA)
+       {
                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);
+                       /* RFC 3633 section 12.1 */
+                       if (ifp->options->ia_type == D6_OPTION_IA_PD)
+                               dhcp6_startrebind(ifp);
+                       else
+                               dhcp6_startconfirm(ifp);
                        return;
                }
        }
        dhcp6_startdiscover(ifp);
 }
 
+static struct ipv6_addr *
+dhcp6_delegate_addr(struct interface *ifp, const struct ipv6_addr *prefix,
+    const struct if_sla *sla, struct interface *ifs)
+{
+       struct dhcp6_state *state;
+       struct ipv6_addr *a, *ap;
+       int b, i, l;
+       const uint8_t *p;
+       char iabuf[INET6_ADDRSTRLEN];
+       const char *ia;
+
+       state = D6_STATE(ifp);
+
+       l = prefix->prefix_len + sla->sla_len;
+       if (l < 0 || l > 128) {
+               syslog(LOG_ERR, "%s: invalid prefix length (%d + %d = %d)",
+                   ifs->name, prefix->prefix_len, sla->sla_len, l);
+               return NULL;
+       }
+
+       if (state == NULL) {
+               ifp->if_data[IF_DATA_DHCP6] = calloc(1, sizeof(*state));
+               state = D6_STATE(ifp);
+               if (state == NULL) {
+                       syslog(LOG_ERR, "%s: %m", __func__);
+                       return NULL;
+               }
+
+               TAILQ_INIT(&state->addrs);
+               state->state = DH6S_DELEGATED;
+       }
+
+       a = malloc(sizeof(*a));
+       if (a == NULL) {
+               syslog(LOG_ERR, "%s: %m", __func__);
+               return NULL;
+       }
+
+       a->new = 1;
+       a->onlink = 1;
+       a->delegating_iface = ifs;
+       memcpy(&a->iaid, &prefix->iaid, sizeof(a->iaid));
+       a->prefix_pltime = prefix->prefix_pltime;
+       a->prefix_vltime = prefix->prefix_vltime;
+       a->prefix_len = l;
+
+       memset(&a->prefix.s6_addr, 0, sizeof(a->prefix.s6_addr));
+       for (i = 0, b = prefix->prefix_len; b > 0; b -= 8, i++)
+               a->prefix.s6_addr[i] = prefix->prefix.s6_addr[i];
+       p = &sla->sla[3];
+       i = (128 - 64) / 8;
+       for (b = sla->sla_len; b > 7; b -= 8, p--)
+               a->prefix.s6_addr[--i] = *p;
+       if (b)
+               a->prefix.s6_addr[--i] |= *p;
+
+       if (ipv6_makeaddr(&a->addr, ifp->name, &a->prefix, a->prefix_len) == -1)
+       {
+               syslog(LOG_ERR, "%s: %m", __func__);
+               free(a);
+               return NULL;
+       }
+
+       /* Remove any exiting address */
+       TAILQ_FOREACH(ap, &state->addrs, next) {
+               if (memcmp(&ap->addr, &a->addr, sizeof(ap->addr)) == 0) {
+                       TAILQ_REMOVE(&state->addrs, ap, next);
+                       free(ap);
+                       break;
+               }
+       }
+
+       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);
+       return a;
+}
+
+static void
+dhcp6_delegate_prefix(struct interface *ifp)
+{
+       struct if_options *ifo;
+       struct dhcp6_state *state, *ifd_state;
+       struct ipv6_addr *ap;
+       size_t i, j, k;
+       struct if_iaid *iaid;
+       struct if_sla *sla;
+       struct interface *ifd;
+
+       ifo = ifp->options;
+       state = D6_STATE(ifp);
+       TAILQ_FOREACH(ifd, ifaces, next) {
+               k = 0;
+               TAILQ_FOREACH(ap, &state->addrs, next) {
+                       for (i = 0; i < ifo->iaid_len; i++) {
+                               iaid = &ifo->iaid[i];
+                               if (memcmp(iaid->iaid, ap->iaid,
+                                   sizeof(iaid->iaid)))
+                                       continue;
+                               for (j = 0; j < iaid->sla_len; j++) {
+                                       sla = &iaid->sla[j];
+                                       if (strcmp(ifd->name, sla->ifname))
+                                               continue;
+                                       if (dhcp6_delegate_addr(ifd, ap,
+                                           sla, ifp))
+                                               k++;
+                               }
+                       }
+               }
+               if (k) {
+                       ifd_state = D6_STATE(ifd);
+                       ipv6_addaddrs(ifd, &ifd_state->addrs);
+               }
+       }
+}
+
+int
+dhcp6_find_delegates(struct interface *ifp)
+{
+       struct if_options *ifo;
+       struct dhcp6_state *state;
+       struct ipv6_addr *ap;
+       size_t i, j, k;
+       struct if_iaid *iaid;
+       struct if_sla *sla;
+       struct interface *ifd;
+
+       k = 0;
+       TAILQ_FOREACH(ifd, ifaces, next) {
+               ifo = ifd->options;
+               if (ifo->ia_type != D6_OPTION_IA_PD)
+                       continue;
+               state = D6_STATE(ifd);
+               if (state == NULL || state->state != DH6S_BOUND)
+                       continue;
+               TAILQ_FOREACH(ap, &state->addrs, next) {
+                       for (i = 0; i < ifo->iaid_len; i++) {
+                               iaid = &ifo->iaid[i];
+                               if (memcmp(iaid->iaid, ap->iaid,
+                                   sizeof(iaid->iaid)))
+                                       continue;
+                               for (j = 0; j < iaid->sla_len; j++) {
+                                       sla = &iaid->sla[j];
+                                       if (strcmp(ifp->name, sla->ifname))
+                                               continue;
+                                       if (dhcp6_delegate_addr(ifp, ap,
+                                           sla, ifd))
+                                           k++;
+                               }
+                       }
+               }
+       }
+
+       if (k) {
+               syslog(LOG_INFO, "%s: adding delegated prefixes", ifp->name);
+               state = D6_STATE(ifp);
+               ipv6_addaddrs(ifp, &state->addrs);
+               ipv6_buildroutes();
+       }
+       return k;
+}
+
 /* ARGSUSED */
 static void
 dhcp6_handledata(_unused void *arg)
@@ -1240,7 +1553,7 @@ dhcp6_handledata(_unused void *arg)
        }
 
        o = dhcp6_getoption(D6_OPTION_CLIENTID, r, len);
-       if (o == NULL || 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",
@@ -1266,7 +1579,7 @@ dhcp6_handledata(_unused void *arg)
                if (state->state == DH6S_INFORM)
                        break;
                switch(state->state) {
-               case DH6S_REBOOT:
+               case DH6S_CONFIRM:
                        o = dhcp6_getoption(D6_OPTION_STATUS_CODE, r, len);
                        if (o == NULL) {
                                syslog(LOG_ERR,
@@ -1332,7 +1645,7 @@ replyok:
 recv:
        syslog(LOG_INFO, "%s: %s received from %s", ifp->name, op, sfrom);
 
-       reason = NULL; 
+       reason = NULL;
        eloop_timeout_delete(NULL, ifp);
        switch(state->state) {
        case DH6S_INFORM:
@@ -1353,7 +1666,7 @@ recv:
        case DH6S_REBIND:
                if (reason == NULL)
                        reason = "REBIND6";
-       case DH6S_REBOOT:
+       case DH6S_CONFIRM:
                if (reason == NULL)
                        reason = "REBOOT6";
                if (state->renew == 0) {
@@ -1374,7 +1687,7 @@ recv:
                break;
        }
 
-       if (state->state != DH6S_REBOOT) {
+       if (state->state != DH6S_CONFIRM) {
                free(state->old);
                state->old = state->new;
                state->old_len = state->new_len;
@@ -1395,6 +1708,8 @@ recv:
                if (state->expire != ~0U)
                        eloop_timeout_add_sec(state->expire,
                            dhcp6_startexpire, ifp);
+               if (ifp->options->ia_type == D6_OPTION_IA_PD)
+                       dhcp6_delegate_prefix(ifp);
                ipv6_addaddrs(ifp, &state->addrs);
                if (state->renew || state->rebind)
                        syslog(LOG_INFO,
@@ -1478,19 +1793,16 @@ int
 dhcp6_start(struct interface *ifp, int manage)
 {
        struct dhcp6_state *state;
-       uint32_t u32;
 
        state = D6_STATE(ifp);
        if (state) {
+               if (state->state == DH6S_DELEGATED)
+                       return dhcp6_find_delegates(ifp);
                /* We're already running DHCP6 */
                /* XXX: What if the managed flag changes? */
                return 0;
        }
 
-       syslog(LOG_INFO, "%s: %s", ifp->name,
-           manage ? "soliciting DHCPv6 address" :
-           "requesting DHCPv6 information");
-
        if (sock == -1 && dhcp6_open() == -1)
                return -1;
 
@@ -1506,21 +1818,20 @@ dhcp6_start(struct interface *ifp, int manage)
        if (state == NULL)
                return -1;
 
-       state->state = manage ? DH6S_INIT : DH6S_INFORM;
        TAILQ_INIT(&state->addrs);
+       if (dhcp6_find_delegates(ifp)) {
+               state->state = DH6S_DELEGATED;
+               return 0;
+       }
+
+       syslog(LOG_INFO, "%s: %s", ifp->name,
+           manage ? "soliciting DHCPv6 address" :
+           "requesting DHCPv6 information");
+
+       state->state = manage ? DH6S_INIT : DH6S_INFORM;
        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_startinform(ifp);
        else
@@ -1547,6 +1858,8 @@ dhcp6_freedrop(struct interface *ifp, int drop, const char *reason)
                free(state->recv);
                free(state->new);
                free(state->old);
+//             if (state->state == DH6S_DELEGATED)
+//                     return;
                free(state);
                ifp->if_data[IF_DATA_DHCP6] = NULL;
        }
@@ -1603,7 +1916,7 @@ dhcp6_env(char **env, const char *prefix, const struct interface *ifp,
                        continue;
                if (has_option_mask(ifo->nomask6, opt->option))
                        continue;
-               o = dhcp6_getoption(opt->option, m, mlen);
+               o = dhcp6_getoption(opt->option, m, mlen);
                if (o == NULL)
                        continue;
                if (env == NULL) {
@@ -1631,22 +1944,45 @@ dhcp6_env(char **env, const char *prefix, const struct interface *ifp,
                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++ = malloc(e);
-                       if (v == NULL) {
-                               syslog(LOG_ERR, "%s: %m", __func__);
-                               return -1;
-                       }
-                       v += snprintf(val, e, "%s_dhcp6_ip_address=", prefix);
-                       TAILQ_FOREACH(ap, &state->addrs, next) {
-                               strcpy(v, ap->saddr);
-                               v += strlen(ap->saddr);
-                               *v++ = ' ';
+                       if (ifo->ia_type == D6_OPTION_IA_PD) {
+                               e = strlen(prefix) +
+                                   strlen("_dhcp6_prefix=");
+                               TAILQ_FOREACH(ap, &state->addrs, next) {
+                                       e += strlen(ap->saddr) + 1;
+                               }
+                               v = val = *ep++ = malloc(e);
+                               if (v == NULL) {
+                                       syslog(LOG_ERR, "%s: %m", __func__);
+                                       return -1;
+                               }
+                               v += snprintf(val, e, "%s_dhcp6_prefix=",
+                                       prefix);
+                               TAILQ_FOREACH(ap, &state->addrs, next) {
+                                       strcpy(v, ap->saddr);
+                                       v += strlen(ap->saddr);
+                                       *v++ = ' ';
+                               }
+                               *--v = '\0';
+                       } else {
+                               e = strlen(prefix) +
+                                   strlen("_dhcp6_ip_address=");
+                               TAILQ_FOREACH(ap, &state->addrs, next) {
+                                       e += strlen(ap->saddr) + 1;
+                               }
+                               v = val = *ep++ = malloc(e);
+                               if (v == NULL) {
+                                       syslog(LOG_ERR, "%s: %m", __func__);
+                                       return -1;
+                               }
+                               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';
                        }
-                       *--v = '\0';
                }
        }
 
diff --git a/dhcp6.h b/dhcp6.h
index 65dfd09de72df203fff7093c87c3e89c67245e27..217c4cd2a797e072a63e7534b602d676fd74a414 100644 (file)
--- a/dhcp6.h
+++ b/dhcp6.h
@@ -54,6 +54,7 @@
 #define D6_OPTION_CLIENTID             1
 #define D6_OPTION_SERVERID             2
 #define D6_OPTION_IA_NA                        3
+#define D6_OPTION_IA_TA                        4
 #define D6_OPTION_ORO                  6
 #define D6_OPTION_IA_ADDR              5
 #define D6_OPTION_PREFERENCE           7
@@ -66,6 +67,8 @@
 #define D6_OPTION_SIP_SERVERS_ADDRESS  22
 #define D6_OPTION_DNS_SERVERS          23
 #define D6_OPTION_DOMAIN_LIST          24
+#define D6_OPTION_IA_PD                        25
+#define D6_OPTION_IAPREFIX             26
 #define D6_OPTION_NIS_SERVERS          27
 #define D6_OPTION_NISP_SERVERS         28
 #define D6_OPTION_NIS_DOMAIN_NAME      29
@@ -140,15 +143,15 @@ enum DH6S {
        DH6S_BOUND,
        DH6S_RENEW,
        DH6S_REBIND,
-       DH6S_REBOOT,
+       DH6S_CONFIRM,
        DH6S_INFORM,
        DH6S_RENEW_REQUESTED,
-       DH6S_PROBE
+       DH6S_PROBE,
+       DH6S_DELEGATED
 };
 
 struct dhcp6_state {
        enum DH6S state;
-       uint8_t iaid[4];
        time_t start_uptime;
 
        /* Message retransmission timings */
@@ -201,6 +204,7 @@ struct dhcp6_state {
 #ifdef INET6
 void dhcp6_printoptions(void);
 int dhcp6_addrexists(const struct ipv6_addr *);
+int dhcp6_find_delegates(struct interface *);
 int dhcp6_start(struct interface *, int);
 ssize_t dhcp6_env(char **, const char *, const struct interface *,
     const struct dhcp6_message *, ssize_t);
@@ -209,6 +213,7 @@ void dhcp6_drop(struct interface *, const char *);
 #else
 #define dhcp6_printoptions()
 #define dhcp6_addrexists(a)
+#define dhcp6_find_delegates(a);
 #define dhcp6_start(a, b) 0
 #define dhcp6_env(a, b, c, d, e)
 #define dhcp6_free(a)
index 121a5640213141bd62500dd970e5a3c4dbeb3954..c3b6c0b8df767fe33935857f7fd06f10bed83e17 100644 (file)
@@ -1,4 +1,4 @@
-.\" Copyright (c) 2006-2012 Roy Marples
+.\" Copyright (c) 2006-2013 Roy Marples
 .\" All rights reserved
 .\"
 .\" Redistribution and use in source and binary forms, with or without
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd November 6, 2012
+.Dd April 1, 2013
 .Dt DHCPCD 8
 .Os
 .Sh NAME
@@ -604,8 +604,8 @@ running on the
 .Xr resolvconf 8
 .Sh STANDARDS
 RFC 951, RFC 1534, RFC 2131, RFC 2132, RFC 2855, RFC 3004, RFC 3315,RFC 3361,
-RFC 3396, RFC 3397, RFC 3442, RFC 3927, RFC 4361, RFC 4390, RFC 4702, RFC 4861,
-RFC 5969, RFC 6106.
+RFC 3633, RFC 3396, RFC 3397, RFC 3442, RFC 3927, RFC 4361, RFC 4390, RFC 4702, 
+RFC 4861, RFC 5969, RFC 6106.
 .Sh AUTHORS
 .An Roy Marples Aq roy@marples.name
 .Sh BUGS
index 2e121d5c97dec26578407f9f3679fe54c0dc2bfb..49e0ab1096d55eb02e8bbb33a48943aca9f0528e 100644 (file)
--- a/dhcpcd.c
+++ b/dhcpcd.c
@@ -302,7 +302,7 @@ configure_interface1(struct interface *ifp)
                ifo->options &= ~DHCPCD_IPV6RS;
        if (ifo->options & DHCPCD_LINK && carrier_status(ifp) == -1)
                ifo->options &= ~DHCPCD_LINK;
-       
+
        if (ifo->metric != -1)
                ifp->metric = ifo->metric;
 
@@ -430,12 +430,13 @@ start_interface(void *arg)
                ipv6rs_start(ifp);
 
        if (ifo->options & DHCPCD_IPV6) {
-               if (ifo->options & DHCPCD_INFORM)
+               if (ifo->options & DHCPCD_IPV6RS)
                        nolease = dhcp6_start(ifp, 0);
-               else if (!(ifo->options & DHCPCD_IPV6RS))
+               else if (ifo->options & DHCPCD_IA_FORCED)
                        nolease = dhcp6_start(ifp, 1);
-               else
-                       nolease = 0;
+               else {
+                       nolease = dhcp6_find_delegates(ifp);;
+               }
                if (nolease == -1)
                        syslog(LOG_ERR, "%s: dhcp6_start: %m", ifp->name);
        }
@@ -475,7 +476,7 @@ init_state(struct interface *ifp, int argc, char **argv)
                syslog(LOG_ERR, "ipv4_init: %m");
                ifo->options &= ~DHCPCD_IPV4;
        }
-       if (ifo->options & DHCPCD_IPV6RS && ipv6_init() == -1) {
+       if (ifo->options & DHCPCD_IPV6 && ipv6_init() == -1) {
                syslog(LOG_ERR, "ipv6_init: %m");
                ifo->options &= ~DHCPCD_IPV6RS;
        }
@@ -508,7 +509,7 @@ handle_interface(int action, const char *ifname)
 {
        struct if_head *ifs;
        struct interface *ifp, *ifn, *ifl = NULL;
-       const char * const argv[] = { ifname }; 
+       const char * const argv[] = { ifname };
        int i;
 
        if (action == -1) {
@@ -571,7 +572,7 @@ handle_hwaddr(const char *ifname, unsigned char *hwaddr, size_t hwlen)
                        ifo = ifp->options;
                        if (!(ifo->options &
                            (DHCPCD_INFORM | DHCPCD_STATIC | DHCPCD_CLIENTID))
-                           && state->new != NULL &&
+                           && state->new != NULL &&
                            state->new->cookie == htonl(MAGIC_COOKIE))
                        {
                                syslog(LOG_INFO,
@@ -586,7 +587,7 @@ handle_hwaddr(const char *ifname, unsigned char *hwaddr, size_t hwlen)
                        {
                                syslog(LOG_DEBUG, "%s: using hwaddr %s",
                                    ifp->name,
-                                   hwaddr_ntoa(ifp->hwaddr, ifp->hwlen));
+                                   hwaddr_ntoa(ifp->hwaddr, ifp->hwlen));
                                state->interval = 0;
                                state->nakoff = 0;
                                start_interface(ifp);
@@ -613,7 +614,7 @@ reconf_reboot(int action, int argc, char **argv, int oi)
 {
        struct if_head *ifs;
        struct interface *ifn, *ifp;
-       
+
        ifs = discover_interfaces(argc - oi, argv + oi);
        if (ifs == NULL)
                return;
@@ -982,7 +983,7 @@ main(int argc, char **argv)
                options |= DHCPCD_PERSISTENT;
                options &= ~DHCPCD_DAEMONISE;
        }
-       
+
 #ifdef THERE_IS_NO_FORK
        options &= ~DHCPCD_DAEMONISE;
 #endif
index de0b97fcd81c47e449c75912f901fb8cdbc92b6b..f869481eaf934219a83be294cad2a95915f0d6dc 100644 (file)
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd March 27, 2013
+.Dd April 1, 2013
 .Dt DHCPCD.CONF 5 SMM
 .Os
 .Sh NAME
@@ -133,6 +133,36 @@ is an empty string then the current system hostname is sent.
 If
 .Ar hostname
 is a FQDN (ie, contains a .) then it will be encoded as such.
+.It Ic ia_na Ar iaid
+Request a DHCPv6 Normal Address for
+.Ar iaid .
+If none is specified, a default
+.Ar iaid
+is used.
+If the interface name is 4 characters or less then that is used,
+otherwise the interface index is used.
+.It Ic ia_ta Ar iaid
+Request a DHCPv6 Temporary Address for
+.Ar iaid .
+.It Ic ia_pd Ar iaid Op Ar interface / Ar sla_id Op / Ar sla_len
+Request a DHCPv6 Delegated Prefix for
+.Ar iaid .
+If an
+.Ar interface
+and
+.Ar sla_id
+is given,
+then an address is also assigned to that
+.Ar interface .
+A default
+.Ar sla_len
+of 16 is assumed if not given.
+IPv6RS should be disabled globally when requesting a Prefix Delegation like so:
+.Pp
+.D1 noipv6rs
+.Pp
+.D1 interface eth0
+.D1 ia_pd 00:11:22:33 eth1/33:44:55:66
 .It Ic ipv4only
 Only configure IPv4.
 .It Ic ipv6only
index f058636b2bcc2373cd3c1c2856beba7f2ed22b00..b93f6c66acd39a07ba40f6a66cd6ebbdffd03ba3 100644 (file)
@@ -57,11 +57,14 @@ unsigned long long options = 0;
 #define O_FALLBACK             O_BASE + 2
 #define O_DESTINATION          O_BASE + 3
 #define O_IPV6RS               O_BASE + 4
-#define O_NOIPV6RS             O_BASE + 5 
-#define O_IPV6RA_FORK          O_BASE + 6 
+#define O_NOIPV6RS             O_BASE + 5
+#define O_IPV6RA_FORK          O_BASE + 6
 #define O_IPV6RA_OWN           O_BASE + 7
 #define O_IPV6RA_OWN_D         O_BASE + 8
 #define O_NOALIAS              O_BASE + 9
+#define O_IA_NA                        O_BASE + 10
+#define O_IA_TA                        O_BASE + 11
+#define O_IA_PD                        O_BASE + 12
 
 const struct option cf_options[] = {
        {"background",      no_argument,       NULL, 'b'},
@@ -95,7 +98,7 @@ const struct option cf_options[] = {
        {"lastlease",       no_argument,       NULL, 'E'},
        {"fqdn",            optional_argument, NULL, 'F'},
        {"nogateway",       no_argument,       NULL, 'G'},
-       {"xidhwaddr",       no_argument,       NULL, 'H'}, 
+       {"xidhwaddr",       no_argument,       NULL, 'H'},
        {"clientid",        optional_argument, NULL, 'I'},
        {"broadcast",       no_argument,       NULL, 'J'},
        {"nolink",          no_argument,       NULL, 'K'},
@@ -120,6 +123,9 @@ const struct option cf_options[] = {
        {"ipv4only",        no_argument,       NULL, '4'},
        {"ipv6only",        no_argument,       NULL, '6'},
        {"noalias",         no_argument,       NULL, O_NOALIAS},
+       {"ia_na",           no_argument,       NULL, O_IA_NA},
+       {"ia_ta",           no_argument,       NULL, O_IA_TA},
+       {"ia_pd",           no_argument,       NULL, O_IA_PD},
        {NULL,              0,                 NULL, '\0'}
 };
 
@@ -141,7 +147,7 @@ atoint(const char *s)
        return (int)n;
 }
 
-static char * 
+static char *
 add_environ(struct if_options *ifo, const char *value, int uniq)
 {
        char **newlist;
@@ -339,7 +345,7 @@ splitv(int *argc, char **argv, const char *arg)
                v[(*argc) - 1] = nt;
        }
        free(o);
-       return v;       
+       return v;
 }
 
 static int
@@ -365,7 +371,7 @@ parse_addr(struct in_addr *addr, struct in_addr *net, const char *arg)
                        syslog(LOG_ERR, "`%s' is not a valid CIDR", p);
                        return -1;
                }
-       } 
+       }
 
        if (addr != NULL && inet_aton(arg, addr) == 0) {
                syslog(LOG_ERR, "`%s' is not a valid IP address", arg);
@@ -382,7 +388,7 @@ parse_addr(struct in_addr *addr, struct in_addr *net, const char *arg)
 #endif
 }
 
-static const char * 
+static const char *
 set_option_space(const char *arg, const struct dhcp_opt **d,
     struct if_options *ifo,
     uint8_t *request[], uint8_t *require[], uint8_t *no[])
@@ -415,12 +421,17 @@ parse_option(struct if_options *ifo, int opt, const char *arg)
        int i;
        char *p = NULL, *fp, *np, **nconf;
        ssize_t s;
+       size_t sl;
        struct in_addr addr, addr2;
        in_addr_t *naddr;
        struct rt *rt;
        const struct dhcp_opt const *d;
        uint8_t *request, *require, *no;
+       struct if_iaid *iaid;
+       uint8_t _iaid[4];
+       struct if_sla *sla;
 
+       i = 0;
        switch(opt) {
        case 'f': /* FALLTHROUGH */
        case 'g': /* FALLTHROUGH */
@@ -780,7 +791,7 @@ parse_option(struct if_options *ifo, int opt, const char *arg)
                                        syslog(LOG_ERR, "%s: %m", __func__);
                                        return -1;
                                }
-                               TAILQ_INIT(ifo->routes);
+                               TAILQ_INIT(ifo->routes);
                        }
                        rt = malloc(sizeof(*rt));
                        if (rt == NULL) {
@@ -898,7 +909,7 @@ parse_option(struct if_options *ifo, int opt, const char *arg)
                ifo->fallback = strdup(arg);
                if (ifo->fallback == NULL) {
                        syslog(LOG_ERR, "%s: %m", __func__);
-                       return -1;
+                       return -1;
                }
                break;
 #endif
@@ -920,6 +931,116 @@ parse_option(struct if_options *ifo, int opt, const char *arg)
        case O_NOALIAS:
                ifo->options |= DHCPCD_NOALIAS;
                break;
+#ifdef INET6
+       case O_IA_NA:
+               i = D6_OPTION_IA_NA;
+               /* FALLTHROUGH */
+       case O_IA_TA:
+               if (i == 0)
+                       i = D6_OPTION_IA_TA;
+               /* FALLTHROUGH */
+       case O_IA_PD:
+               if (i == 0)
+                       i = D6_OPTION_IA_PD;
+               ifo->options |= DHCPCD_IA_FORCED;
+               if (ifo->ia_type != 0 && ifo->ia_type != i) {
+                       syslog(LOG_ERR, "cannot specify a different IA type");
+                       return -1;
+               }
+               ifo->ia_type = i;
+               if (arg == NULL)
+                       break;
+               fp = strchr(arg, ' ');
+               if (fp == NULL) {
+                       syslog(LOG_ERR, "%s: invalid syntax", arg);
+                       return -1;
+               }
+               *fp++ = '\0';
+               if ((s = parse_string((char *)_iaid, sizeof(_iaid), arg)) < 1) {
+                       syslog(LOG_ERR, "%s: invalid IAID", arg);
+                       return -1;
+               }
+               if (s < 4)
+                       _iaid[3] = '\0';
+               if (s < 3)
+                       _iaid[2] = '\0';
+               if (s < 2)
+                       _iaid[1] = '\0';
+               iaid = NULL;
+               for (sl = 0; sl < ifo->iaid_len; sl++) {
+                       if (ifo->iaid[sl].iaid[0] == _iaid[0] &&
+                           ifo->iaid[sl].iaid[1] == _iaid[1] &&
+                           ifo->iaid[sl].iaid[2] == _iaid[2] &&
+                           ifo->iaid[sl].iaid[3] == _iaid[3])
+                       {
+                               iaid = &ifo->iaid[sl];
+                               break;
+                       }
+               }
+               if (iaid == NULL) {
+                       iaid = realloc(ifo->iaid,
+                           sizeof(*ifo->iaid) * (ifo->iaid_len + 1));
+                       if (iaid == NULL) {
+                               syslog(LOG_ERR, "%s: %m", __func__);
+                               return -1;
+                       }
+                       ifo->iaid = iaid;
+                       iaid = &ifo->iaid[ifo->iaid_len++];
+                       iaid->iaid[0] = _iaid[0];
+                       iaid->iaid[1] = _iaid[1];
+                       iaid->iaid[2] = _iaid[2];
+                       iaid->iaid[3] = _iaid[3];
+                       iaid->sla = NULL;
+                       iaid->sla_len = 0;
+               }
+               for (p = fp; p; p = fp) {
+                       fp = strchr(p, ' ');
+                       if (fp)
+                               *fp++ = '\0';
+                       sla = realloc(iaid->sla,
+                           sizeof(*iaid->sla) * (iaid->sla_len + 1));
+                       if (sla == NULL) {
+                               syslog(LOG_ERR, "%s: %m", __func__);
+                               return -1;
+                       }
+                       iaid->sla = sla;
+                       sla = &iaid->sla[iaid->sla_len++];
+                       np = strchr(p, '/');
+                       if (np)
+                               *np++ = '\0';
+                       else {
+                               syslog(LOG_ERR, "%s: missing sla", arg);
+                               return -1;
+                       }
+                       if (strlcpy(sla->ifname, p,
+                           sizeof(sla->ifname)) >= sizeof(sla->ifname))
+                       {
+                               syslog(LOG_ERR, "%s: interface name too long",
+                                   arg);
+                               return -1;
+                       }
+                       p = np;
+                       np = strchr(p, '/');
+                       if (np)
+                               *np++ = '\0';
+                       if (parse_string((char *)sla->sla,
+                           sizeof(sla->sla), p) == -1)
+                       {
+                               syslog(LOG_ERR, "%s: sla: %m", arg);
+                               return -1;
+                       }
+                       if (np) {
+                               sla->sla_len = atoint(np);
+                               if (sla->sla_len < 0 || sla->sla_len > 128) {
+                                       syslog(LOG_ERR, "%s: sla len: range",
+                                           arg);
+                                       return -1;
+                               }
+                       } else
+                               sla->sla_len = 8;
+               }
+               break;
+#endif
        default:
                return 0;
        }
@@ -969,7 +1090,7 @@ read_config(const char *file,
        ifo->options |= DHCPCD_DAEMONISE | DHCPCD_LINK;
 #ifdef INET
        ifo->options |= DHCPCD_IPV4 | DHCPCD_IPV4LL;
-       ifo->options |= DHCPCD_GATEWAY | DHCPCD_ARP; 
+       ifo->options |= DHCPCD_GATEWAY | DHCPCD_ARP;
 #endif
 #ifdef INET6
        ifo->options |= DHCPCD_IPV6 | DHCPCD_IPV6RS | DHCPCD_IPV6RA_REQRDNSS;
@@ -1048,6 +1169,33 @@ read_config(const char *file,
                ifo->vendor[0]++;
                ifo->vendor[ifo->vendor[0]] = DHO_END;
        }
+
+#ifdef INET6
+       if (ifname && ifo->iaid_len == 0 && ifo->options & DHCPCD_IPV6) {
+               ifo->iaid = malloc(sizeof(*ifo->iaid));
+               if (ifo->iaid == NULL)
+                       syslog(LOG_ERR, "%s: %m", __func__);
+               else {
+                       if (ifo->ia_type == 0)
+                               ifo->ia_type = D6_OPTION_IA_NA;
+                       ifo->iaid_len = strlen(ifname);
+                       if (ifo->iaid_len <= sizeof(ifo->iaid->iaid)) {
+                               strncpy((char *)ifo->iaid->iaid, ifname,
+                                       sizeof(ifo->iaid->iaid));
+                               memset(ifo->iaid->iaid + ifo->iaid_len, 0,
+                                       sizeof(ifo->iaid->iaid) -ifo->iaid_len);
+                       } else {
+                               uint32_t idx = if_nametoindex(ifname);
+                               memcpy(ifo->iaid->iaid, &idx, sizeof(idx));
+                       }
+                       ifo->iaid_len = 1;
+                       ifo->iaid->sla = NULL;
+                       ifo->iaid->sla_len = 0;
+               }
+       } else
+               ifo->options |= DHCPCD_IA_FORCED;
+#endif
+
        return ifo;
 }
 
@@ -1093,6 +1241,11 @@ free_options(struct if_options *ifo)
                free(ifo->arping);
                free(ifo->blacklist);
                free(ifo->fallback);
+#ifdef INET6
+               for (i = 0; i < ifo->iaid_len; i++)
+                       free(ifo->iaid[i].sla);
+               free(ifo->iaid);
+#endif
                free(ifo);
        }
 }
index 81715d2cb4e6e67128ff5a7dbd50de645d5741eb..ea14a5d359055c64786e074dad61fb6cdc04c9b9 100644 (file)
@@ -1,6 +1,6 @@
 /* 
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
@@ -70,7 +70,7 @@
 #define DHCPCD_HOSTNAME                        (1ULL << 18)
 #define DHCPCD_CLIENTID                        (1ULL << 19)
 #define DHCPCD_LINK                    (1ULL << 20)
-#define DHCPCD_QUIET                   (1ULL << 21) 
+#define DHCPCD_QUIET                   (1ULL << 21)
 #define DHCPCD_BACKGROUND              (1ULL << 22)
 #define DHCPCD_VENDORRAW               (1ULL << 23)
 #define DHCPCD_TIMEOUT_IPV4LL          (1ULL << 24)
 #define DHCPCD_IPV6                    (1ULL << 37)
 #define DHCPCD_STARTED                 (1ULL << 38)
 #define DHCPCD_NOALIAS                 (1ULL << 39)
+#define DHCPCD_IA_FORCED               (1ULL << 40)
 
 extern const struct option cf_options[];
 
+struct if_sla {
+       char ifname[IF_NAMESIZE];
+       uint8_t sla[16];
+       short sla_len;
+};
+
+struct if_iaid {
+       uint8_t iaid[4];
+       size_t sla_len;
+       struct if_sla *sla;
+};
+
 struct if_options {
        int metric;
        uint8_t requestmask[256 / 8];
@@ -113,7 +126,7 @@ struct if_options {
 
        char **environ;
        char script[PATH_MAX];
-       
+
        char hostname[HOSTNAME_MAX_LEN + 1]; /* We don't store the length */
        int fqdn;
        uint8_t vendorclassid[VENDORCLASSID_MAX_LEN + 2];
@@ -128,6 +141,12 @@ struct if_options {
        size_t arping_len;
        in_addr_t *arping;
        char *fallback;
+
+#ifdef INET6
+       uint16_t ia_type;
+       size_t iaid_len;
+       struct if_iaid *iaid;
+#endif
 };
 
 extern unsigned long long options;
diff --git a/ipv6.c b/ipv6.c
index f7209f6cb19cd9faa5b7c8f2755d6c9d1e7f0bbf..70f085d1aa92c39d651237cf6c70155dd47e71e3 100644 (file)
--- a/ipv6.c
+++ b/ipv6.c
@@ -482,7 +482,10 @@ ipv6_buildroutes(void)
        TAILQ_INIT(&dnr);
        TAILQ_FOREACH(ifp, ifaces, next) {
                d6_state = D6_CSTATE(ifp);
-               if (d6_state && d6_state->state == DH6S_BOUND) {
+               if (d6_state &&
+                   (d6_state->state == DH6S_BOUND ||
+                    d6_state->state == DH6S_DELEGATED))
+               {
                        TAILQ_FOREACH(addr, &d6_state->addrs, next) {
                                if (!addr->onlink)
                                        continue;
diff --git a/ipv6.h b/ipv6.h
index 5ab9ff5196d640c62e4554e7a4be1ae40b791523..bbc0e1919dfd5dc3cf08736f65594f68e5bd2d8c 100644 (file)
--- a/ipv6.h
+++ b/ipv6.h
@@ -47,6 +47,8 @@ struct ipv6_addr {
        uint8_t onlink;
        uint8_t new;
        char saddr[INET6_ADDRSTRLEN];
+       uint8_t iaid[4];
+       struct interface *delegating_iface;
 };
 TAILQ_HEAD(ipv6_addrhead, ipv6_addr);