From: Roy Marples Date: Sat, 21 Jun 2014 11:42:36 +0000 (+0000) Subject: Allow the request of a DHCPv6 address or prefix, a prefix length must be specified. X-Git-Tag: v6.4.1~55 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=4f94ed51b4681796c14b26aff3b7a7e321c72ae3;p=thirdparty%2Fdhcpcd.git Allow the request of a DHCPv6 address or prefix, a prefix length must be specified. --- diff --git a/dhcp6.c b/dhcp6.c index ad439fd2..96fcd417 100644 --- a/dhcp6.c +++ b/dhcp6.c @@ -405,12 +405,15 @@ dhcp6_makemessage(struct interface *ifp) case DH6S_REBIND: /* FALLTHROUGH */ case DH6S_CONFIRM: + /* FALLTHROUGH */ + case DH6S_DISCOVER: if (m == NULL) { m = state->new; ml = state->new_len; } TAILQ_FOREACH(ap, &state->addrs, next) { - if (ap->prefix_vltime == 0) + if (ap->prefix_vltime == 0 && + !(ap->flags & IPV6_AF_REQUEST)) continue; if (ifo->ia_type == D6_OPTION_IA_PD) len += sizeof(*o) + sizeof(u8) + @@ -421,8 +424,7 @@ dhcp6_makemessage(struct interface *ifp) sizeof(u32) + sizeof(u32); } /* FALLTHROUGH */ - case DH6S_INIT: /* FALLTHROUGH */ - case DH6S_DISCOVER: + case DH6S_INIT: len += ifo->ia_len * (sizeof(*o) + (sizeof(u32) * 3)); IA = 1; break; @@ -536,7 +538,8 @@ dhcp6_makemessage(struct interface *ifp) p += sizeof(u32); memset(p, 0, sizeof(u32) + sizeof(u32)); TAILQ_FOREACH(ap, &state->addrs, next) { - if (ap->prefix_vltime == 0) + if (ap->prefix_vltime == 0 && + !(ap->flags & IPV6_AF_REQUEST)) continue; if (memcmp(ifo->ia[l].iaid, ap->iaid, sizeof(u32))) continue; @@ -948,11 +951,52 @@ dhcp6_startrenew(void *arg) dhcp6_sendrenew(ifp); } +static void +dhcp6_dadcallback(void *arg) +{ + struct ipv6_addr *ap = arg; + struct interface *ifp; + struct dhcp6_state *state; + int wascompleted; + + wascompleted = (ap->flags & IPV6_AF_DADCOMPLETED); + ap->flags |= IPV6_AF_DADCOMPLETED; + if (ap->flags & IPV6_AF_DUPLICATED) + /* XXX FIXME + * We should decline the address */ + syslog(LOG_WARNING, "%s: DAD detected %s", + ap->iface->name, ap->saddr); + + if (!wascompleted) { + ifp = ap->iface; + state = D6_STATE(ifp); + if (state->state == DH6S_BOUND || + state->state == DH6S_DELEGATED) + { + TAILQ_FOREACH(ap, &state->addrs, next) { + if ((ap->flags & IPV6_AF_DADCOMPLETED) == 0) { + wascompleted = 1; + break; + } + } + if (!wascompleted) { + syslog(LOG_DEBUG, "%s: DHCPv6 DAD completed", + ifp->name); + script_runreason(ifp, state->reason); + dhcpcd_daemonise(ifp->ctx); + } + } + } +} + static void dhcp6_startdiscover(void *arg) { struct interface *ifp; struct dhcp6_state *state; + size_t i; + struct if_ia *ia; + struct ipv6_addr *a; ifp = arg; dhcp6_delete_delegates(ifp); @@ -974,6 +1018,30 @@ dhcp6_startdiscover(void *arg) dhcp6_freedrop_addrs(ifp, 0, NULL); unlink(state->leasefile); + /* Add any requested prefixes / addresses */ + for (i = 0; i < ifp->options->ia_len; i++) { + ia = &ifp->options->ia[i]; + if (ia->prefix_len) { + a = calloc(1, sizeof(*a)); + if (a == NULL) { + syslog(LOG_ERR, "%s: %m", __func__); + return NULL; + } + a->flags = IPV6_AF_REQUEST; + a->iface = ifp; + a->dadcallback = dhcp6_dadcallback; + memcpy(&a->iaid, &ia->iaid, sizeof(a->iaid)); + //a->prefix_pltime = 0; + //a->prefix_vltime = 0; + if (ifp->options->ia_type == D6_OPTION_IA_PD) + memcpy(&a->prefix, &ia->addr, sizeof(a->addr)); + else + memcpy(&a->addr, &ia->addr, sizeof(a->addr)); + a->prefix_len = ia->prefix_len; + TAILQ_INSERT_TAIL(&state->addrs, a, next); + } + } + if (dhcp6_makemessage(ifp) == -1) syslog(LOG_ERR, "%s: dhcp6_makemessage: %m", ifp->name); else @@ -1264,44 +1332,6 @@ dhcp6_addrexists(struct dhcpcd_ctx *ctx, const struct ipv6_addr *addr) return 0; } -static void -dhcp6_dadcallback(void *arg) -{ - struct ipv6_addr *ap = arg; - struct interface *ifp; - struct dhcp6_state *state; - int wascompleted; - - wascompleted = (ap->flags & IPV6_AF_DADCOMPLETED); - ap->flags |= IPV6_AF_DADCOMPLETED; - if (ap->flags & IPV6_AF_DUPLICATED) - /* XXX FIXME - * We should decline the address */ - syslog(LOG_WARNING, "%s: DAD detected %s", - ap->iface->name, ap->saddr); - - if (!wascompleted) { - ifp = ap->iface; - state = D6_STATE(ifp); - if (state->state == DH6S_BOUND || - state->state == DH6S_DELEGATED) - { - TAILQ_FOREACH(ap, &state->addrs, next) { - if ((ap->flags & IPV6_AF_DADCOMPLETED) == 0) { - wascompleted = 1; - break; - } - } - if (!wascompleted) { - syslog(LOG_DEBUG, "%s: DHCPv6 DAD completed", - ifp->name); - script_runreason(ifp, state->reason); - dhcpcd_daemonise(ifp->ctx); - } - } - } -} - static int dhcp6_findna(struct interface *ifp, const uint8_t *iaid, const uint8_t *d, size_t l) @@ -1370,7 +1400,7 @@ dhcp6_findna(struct interface *ifp, const uint8_t *iaid, "%s/%d", ia, a->prefix_len); TAILQ_INSERT_TAIL(&state->addrs, a, next); } else - a->flags &= ~IPV6_AF_STALE; + a->flags &= ~(IPV6_AF_STALE | IPV6_AF_REQUEST); memcpy(&u32, p, sizeof(u32)); a->prefix_pltime = ntohl(u32); p += sizeof(u32); @@ -1455,7 +1485,7 @@ dhcp6_findpd(struct interface *ifp, const uint8_t *iaid, "%s/%d", ia, a->prefix_len); TAILQ_INSERT_TAIL(&state->addrs, a, next); } else { - a->flags &= ~IPV6_AF_STALE; + a->flags &= ~(IPV6_AF_STALE | IPV6_AF_REQUEST); if (a->prefix_vltime != vltime) a->flags |= IPV6_AF_NEW; } diff --git a/dhcpcd.conf.5.in b/dhcpcd.conf.5.in index d53e8285..d755e678 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 June 6, 2014 +.Dd June 20, 2014 .Dt DHCPCD.CONF 5 .Os .Sh NAME @@ -214,7 +214,7 @@ DNS if the domain part does not match theirs. Also, see the .Ic env option above to control how the hostname is set on the host. -.It Ic ia_na Op Ar iaid +.It Ic ia_na Op Ar iaid Op / address Op / prefix_len Request a DHCPv6 Normal Address for .Ar iaid . .Ar iaid @@ -230,7 +230,7 @@ Request a DHCPv6 Temporary Address for You can request more than one ia_ta by specifying a unique .Ar iaid for each one. -.It Ic ia_pd Op Ar iaid Op Ar interface Op / Ar sla_id Op / Ar prefix_len +.It Ic ia_pd Op Ar iaid Oo / Ar prefix / Ar prefix_len Oc Op Ar interface Op / Ar sla_id Op / Ar prefix_len Request a DHCPv6 Delegated Prefix for .Ar iaid . This option must be used in an diff --git a/if-options.c b/if-options.c index 7dd2c9b0..dec0f46d 100644 --- a/if-options.c +++ b/if-options.c @@ -1236,6 +1236,9 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, fp = strwhite(arg); if (fp) *fp++ = '\0'; + p = strchr(arg, '/'); + if (p) + *p++ = '\0'; if (parse_iaid(iaid, arg, sizeof(iaid)) == -1) return -1; ia = NULL; @@ -1262,8 +1265,33 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, ia->iaid[1] = iaid[1]; ia->iaid[2] = iaid[2]; ia->iaid[3] = iaid[3]; - ia->sla = NULL; + if (p == NULL) { + memset(&ia->addr, 0, sizeof(ia->addr)); + ia->prefix_len = 0; + } else { + arg = p; + p = strchr(arg, '/'); + if (p) + *p++ = '\0'; + if (inet_pton(AF_INET6, arg, &ia->addr) == -1) { + syslog(LOG_ERR, "%s: %m", arg); + memset(&ia->addr, 0, sizeof(ia->addr)); + } + if (p) { + i = atoint(p); + if (i != -1 && (i < 8 || i > 120)) { + errno = EINVAL; + i = -1; + } + if (i == -1) { + syslog(LOG_ERR, "%s: %m", p); + ia->prefix_len = 0; + } else + ia->prefix_len = (uint8_t)i; + } + } ia->sla_len = 0; + ia->sla = NULL; } if (ifo->ia_type != D6_OPTION_IA_PD) break; diff --git a/if-options.h b/if-options.h index f90e428e..bfcca817 100644 --- a/if-options.h +++ b/if-options.h @@ -115,6 +115,8 @@ struct if_sla { struct if_ia { uint8_t iaid[4]; #ifdef INET6 + struct in6_addr addr; + uint8_t prefix_len; size_t sla_len; struct if_sla *sla; #endif diff --git a/ipv6.h b/ipv6.h index 0b860e2f..a1259413 100644 --- a/ipv6.h +++ b/ipv6.h @@ -109,7 +109,8 @@ TAILQ_HEAD(ipv6_addrhead, ipv6_addr); #define IPV6_AF_DADCOMPLETED 0x0040 #define IPV6_AF_DELEGATED 0x0080 #define IPV6_AF_DELEGATEDPFX 0x0100 -#define IPV6_AF_DELEGATEDZERO 0X0200 +#define IPV6_AF_DELEGATEDZERO 0x0200 +#define IPV6_AF_REQUEST 0x0400 struct rt6 { TAILQ_ENTRY(rt6) next;