From: Roy Marples Date: Mon, 1 Apr 2013 12:15:47 +0000 (+0000) Subject: Add DHCPv6 Prefix Delegation support, RFC3633. X-Git-Tag: v5.99.6~32 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=00ababe4a0fd5d15325d17b80b070cc8a0afa398;p=thirdparty%2Fdhcpcd.git Add DHCPv6 Prefix Delegation support, RFC3633. Add DHCPv6 Temporary Address support, RFC3315. --- diff --git a/defs.h b/defs.h index 3fa8f97a..ca782e72 100644 --- 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 5d09236b..5d0df8e9 100644 --- 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 65dfd09d..217c4cd2 100644 --- 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) diff --git a/dhcpcd.8.in b/dhcpcd.8.in index 121a5640..c3b6c0b8 100644 --- a/dhcpcd.8.in +++ b/dhcpcd.8.in @@ -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 diff --git a/dhcpcd.c b/dhcpcd.c index 2e121d5c..49e0ab10 100644 --- 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 diff --git a/dhcpcd.conf.5.in b/dhcpcd.conf.5.in index de0b97fc..f869481e 100644 --- a/dhcpcd.conf.5.in +++ b/dhcpcd.conf.5.in @@ -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 diff --git a/if-options.c b/if-options.c index f058636b..b93f6c66 100644 --- a/if-options.c +++ b/if-options.c @@ -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); } } diff --git a/if-options.h b/if-options.h index 81715d2c..ea14a5d3 100644 --- a/if-options.h +++ b/if-options.h @@ -1,6 +1,6 @@ /* * dhcpcd - DHCP client daemon - * 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 @@ -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) @@ -89,9 +89,22 @@ #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 f7209f6c..70f085d1 100644 --- 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 5ab9ff51..bbc0e191 100644 --- 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);