From: Roy Marples Date: Wed, 4 Jun 2014 20:27:40 +0000 (+0000) Subject: Default SLAAC to use RFC7271 addresses. X-Git-Tag: v6.4.0~21 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=1aeaf0e7517692a31fe347ea4cedc44409f021dd;p=thirdparty%2Fdhcpcd.git Default SLAAC to use RFC7271 addresses. Create an IPv6 link-local address if non exists at startup using the configured SLAAC method. --- diff --git a/dhcpcd.8.in b/dhcpcd.8.in index 29285f1b..de2a827b 100644 --- a/dhcpcd.8.in +++ b/dhcpcd.8.in @@ -75,7 +75,7 @@ .Sh DESCRIPTION .Nm is an implementation of the DHCP client specified in -.Li RFC 2131 . +.%R RFC 2131 . .Nm gets the host information .Po @@ -103,27 +103,20 @@ to renew early. .Pp .Nm is also an implementation of the BOOTP client specified in -.Li RFC 951 . +.%R RFC 951 . .Pp .Nm is also an implementation of the IPv6 Router Solicitor as specified in -.Li RFC 4861 +.%R RFC 4861 and -.Li RFC 6106 . -.Nm -can optionally handle address and route management itself, -and will do so by default if Router Solicitation is disabled in the kernel -or if stable private addresses are enabled in the configuration. -If -.Nm -is managing routes, -.Nm -sends Neighbor Solicitions to each advertising router periodically and will -expire the ones that do not respond. +.%R RFC 6106 +and will generate stable private Interface Identifiers for SLAAC +as specified in +.%R RFC 7212 . .Pp .Nm is also an implemenation of the DHCPv6 client as specified in -.Li RFC 3315 . +.%R RFC 3315 . By default, .Nm only starts DHCPv6 when instructed to do so by an IPV6 Router Advertisement. @@ -221,7 +214,7 @@ instead of the default .Pa @SCRIPT@ . .It Fl D , Fl Fl duid Generate an -.Li RFC 4361 +.%R RFC 4361 compliant clientid. This requires persistent storage and not all DHCP servers work with it so it is not enabled by default. @@ -269,7 +262,7 @@ are disable, none, ptr and both. itself never does any DNS updates. .Nm encodes the FQDN hostname as specified in -.Li RFC1035 . +.%R RFC1035 . .It Fl f , Fl Fl config Ar file Specify a config to load instead of .Pa @SYSCONFDIR@/dhcpcd.conf . diff --git a/dhcpcd.c b/dhcpcd.c index 482764d4..c89922a3 100644 --- a/dhcpcd.c +++ b/dhcpcd.c @@ -447,6 +447,8 @@ configure_interface1(struct interface *ifp) if (!(ifo->auth.options & DHCPCD_AUTH_SEND)) ifo->auth.options &= ~DHCPCD_AUTH_REQUIRE; + if (ifo->options & DHCPCD_SLAACPRIVATE) + ifo->options |= DHCPCD_IPV6RA_OWN; } int @@ -602,6 +604,10 @@ dhcpcd_startinterface(void *arg) } } + if (ifo->options & DHCPCD_IPV6 && ipv6_start(ifp) == -1) { + syslog(LOG_ERR, "%s: ipv6_start: %m", ifp->name); + ifo->options &= DHCPCD_IPV6; + } if (ifo->options & DHCPCD_IPV6) { if (ifo->options & DHCPCD_IPV6RS && !(ifo->options & DHCPCD_INFORM)) diff --git a/dhcpcd.conf.5.in b/dhcpcd.conf.5.in index e61b771a..845bce43 100644 --- a/dhcpcd.conf.5.in +++ b/dhcpcd.conf.5.in @@ -453,9 +453,11 @@ instead of the default .It Ic ssid Ar ssid Subsequent options are only parsed for this wireless .Ar ssid . -.It Ic stableprivate -Configure stable private IPv6 addresses as per RFC7217 for SLAAC instead of -ones generated from hardware addresses for SLAAC. +.It Ic slaac Op Ar hwaddr | Ar private +Selects the interface identifier used for SLAAC generated IPv6 addresses. +.Ar private +is the default and complies with +.%R RFC 7217. .It Ic static Ar value Configures a static .Ar value . diff --git a/if-options.c b/if-options.c index b3f255b0..34476088 100644 --- a/if-options.c +++ b/if-options.c @@ -91,7 +91,7 @@ #define O_IPV4 O_BASE + 32 #define O_IPV6 O_BASE + 33 #define O_CONTROLGRP O_BASE + 34 -#define O_STABLEPRIVATE O_BASE + 35 +#define O_SLAAC O_BASE + 35 const struct option cf_options[] = { {"background", no_argument, NULL, 'b'}, @@ -176,7 +176,7 @@ const struct option cf_options[] = { {"dhcp6", no_argument, NULL, O_DHCP6}, {"nodhcp6", no_argument, NULL, O_NODHCP6}, {"controlgroup", required_argument, NULL, O_CONTROLGRP}, - {"stableprivate", no_argument, NULL, O_STABLEPRIVATE}, + {"slaac", required_argument, NULL, O_SLAAC}, {NULL, 0, NULL, '\0'} }; @@ -1837,10 +1837,13 @@ err_sla: ctx->control_group = grp->gr_gid; #endif break; - case O_STABLEPRIVATE: - ifo->options |= DHCPCD_STABLEPRIVATE; - /* This option implies that we steal SLAAC from the kernel */ - ifo->options |= DHCPCD_IPV6RA_OWN; + case O_SLAAC: + if (strcmp(arg, "private") == 0 || + strcmp(arg, "stableprivate") == 0 || + strcmp(arg, "stable") == 0) + ifo->options |= DHCPCD_SLAACPRIVATE; + else + ifo->options &= ~DHCPCD_SLAACPRIVATE; break; default: return 0; @@ -1949,6 +1952,7 @@ read_config(struct dhcpcd_ctx *ctx, #endif #ifdef INET6 ifo->options |= DHCPCD_IPV6 | DHCPCD_IPV6RS | DHCPCD_IPV6RA_REQRDNSS; + ifo->options |= DHCPCD_SLAACPRIVATE; ifo->options |= DHCPCD_DHCP6; #endif ifo->timeout = DEFAULT_TIMEOUT; diff --git a/if-options.h b/if-options.h index 4288479b..b5cf156a 100644 --- a/if-options.h +++ b/if-options.h @@ -102,7 +102,7 @@ #define DHCPCD_IAID (1ULL << 48) #define DHCPCD_DHCP (1ULL << 49) #define DHCPCD_DHCP6 (1ULL << 50) -#define DHCPCD_STABLEPRIVATE (1ULL << 51) +#define DHCPCD_SLAACPRIVATE (1ULL << 51) extern const struct option cf_options[]; diff --git a/ipv6.c b/ipv6.c index 25ebd65a..f9a79ae8 100644 --- a/ipv6.c +++ b/ipv6.c @@ -393,7 +393,7 @@ ipv6_makeaddr(struct in6_addr *addr, const struct interface *ifp, return -1; } - if (ifp->options->options & DHCPCD_STABLEPRIVATE) { + if (ifp->options->options & DHCPCD_SLAACPRIVATE) { if (ifp->ctx->secret_len == 0) { if (ipv6_readsecret(ifp->ctx) == -1) return -1; @@ -878,6 +878,122 @@ ipv6_free_ll_callbacks(struct interface *ifp) } } +static struct ipv6_addr * +ipv6_newlinklocal(struct interface *ifp) +{ + struct ipv6_addr *ap; + + ap = calloc(1, sizeof(*ap)); + if (ap != NULL) { + ap->iface = ifp; + ap->prefix.s6_addr32[0] = htonl(0xfe800000); + ap->prefix.s6_addr32[1] = 0; + ap->prefix_len = 64; + ap->dadcounter = 0; + ap->prefix_pltime = ND6_INFINITE_LIFETIME; + ap->prefix_vltime = ND6_INFINITE_LIFETIME; + ap->flags = IPV6_AF_NEW; + ap->addr_flags = IN6_IFF_TENTATIVE; + } + return ap; +} + +static const uint8_t allzero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; +static const uint8_t allone[8] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +static int +ipv6_addlinklocal(struct interface *ifp) +{ + struct ipv6_state *state; + struct ipv6_addr *ap; + int dadcounter; + + if (ipv6_linklocal(ifp)) + return 0; + + /* Check sanity before malloc */ + if (!(ifp->options->options & DHCPCD_SLAACPRIVATE)) { + switch (ifp->family) { + case ARPHRD_ETHER: + /* Check for a valid hardware address */ + if (ifp->hwlen != 6 & ifp->hwlen != 8) { + errno = ENOTSUP; + return -1; + } + if (memcmp(ifp->hwaddr, allzero, ifp->hwlen) == 0 || + memcmp(ifp->hwaddr, allone, ifp->hwlen) == 0) + { + errno = EINVAL; + return -1; + } + break; + default: + errno = ENOTSUP; + return -1; + } + } + + state = ipv6_getstate(ifp); + if (state == NULL) + return -1; + + ap = ipv6_newlinklocal(ifp); + if (ap == NULL) + return -1; + + if (ifp->options->options & DHCPCD_SLAACPRIVATE) { + dadcounter = 0; + if (ipv6_makestableprivate(&ap->addr, + &ap->prefix, ap->prefix_len, ifp, &dadcounter) == -1) + { + free(ap); + return -1; + } + ap->dadcounter = dadcounter; + } else { + memcpy(ap->addr.s6_addr, ap->prefix.s6_addr, ap->prefix_len); + switch (ifp->family) { + case ARPHRD_ETHER: + if (ifp->hwlen == 6) { + ap->addr.s6_addr[ 8] = ifp->hwaddr[0]; + ap->addr.s6_addr[ 9] = ifp->hwaddr[1]; + ap->addr.s6_addr[10] = ifp->hwaddr[2]; + ap->addr.s6_addr[11] = 0xff; + ap->addr.s6_addr[12] = 0xfe; + ap->addr.s6_addr[13] = ifp->hwaddr[3]; + ap->addr.s6_addr[14] = ifp->hwaddr[4]; + ap->addr.s6_addr[15] = ifp->hwaddr[5]; + } else if (ifp->hwlen == 8) + memcpy(&ap->addr.s6_addr[8], ifp->hwaddr, 8); + break; + } + + /* Sanity check: g bit must not indciate "group" */ + if (EUI64_GROUP(&ap->addr)) { + free(ap); + errno = EINVAL; + return -1; + } + EUI64_TO_IFID(&ap->addr); + } + + inet_ntop(AF_INET6, &ap->addr, ap->saddr, sizeof(ap->saddr)); + TAILQ_INSERT_TAIL(&state->addrs, ap, next); + ipv6_addaddr(ap); + return 1; +} + +/* Ensure the interface has a link-local address */ +int +ipv6_start(struct interface *ifp) +{ + + if (ipv6_linklocal(ifp) == NULL && ipv6_addlinklocal(ifp) == -1) + return -1; + return 0; +} + void ipv6_free(struct interface *ifp) { diff --git a/ipv6.h b/ipv6.h index 82a74b37..0b860e2f 100644 --- a/ipv6.h +++ b/ipv6.h @@ -45,6 +45,11 @@ #define ROUNDUP8(a) (1 + (((a) - 1) | 7)) #define ROUNDUP16(a) (1 + (((a) - 1) | 16)) +#define EUI64_GBIT 0x01 +#define EUI64_UBIT 0x02 +#define EUI64_TO_IFID(in6) do {(in6)->s6_addr[8] ^= EUI64_UBIT; } while (0) +#define EUI64_GROUP(in6) ((in6)->s6_addr[8] & EUI64_GBIT) + #ifndef ND6_INFINITE_LIFETIME # define ND6_INFINITE_LIFETIME ((uint32_t)~0) #endif @@ -189,6 +194,7 @@ const struct ipv6_addr *ipv6_findaddr(const struct interface *, #define ipv6_linklocal(ifp) (ipv6_findaddr((ifp), NULL)) int ipv6_addlinklocalcallback(struct interface *, void (*)(void *), void *); void ipv6_free_ll_callbacks(struct interface *); +int ipv6_start(struct interface *); void ipv6_free(struct interface *); void ipv6_ctxfree(struct dhcpcd_ctx *); int ipv6_removesubnet(struct interface *, struct ipv6_addr *); @@ -196,6 +202,7 @@ void ipv6_buildroutes(struct dhcpcd_ctx *); #else #define ipv6_init(a) NULL +#define ipv6_start(a) (-1) #define ipv6_free_ll_callbacks(a) #define ipv6_free(a) #define ipv6_ctxfree(a) diff --git a/ipv6nd.c b/ipv6nd.c index 0ed14304..f7511529 100644 --- a/ipv6nd.c +++ b/ipv6nd.c @@ -603,7 +603,7 @@ ipv6nd_dadcallback(void *arg) * Because ap->dadcounter is always increamented, * a different address is generated. */ /* XXX Cache DAD counter per prefix/id/ssid? */ - if (ifp->options->options & DHCPCD_STABLEPRIVATE && + if (ifp->options->options & DHCPCD_SLAACPRIVATE && ap->dadcounter < IDGEN_RETRIES) { syslog(LOG_INFO, "%s: deleting address %s",