.Sh DESCRIPTION
.Nm
is an implementation of the DHCP client specified in
-.Li RFC 2131 .
+.%R RFC 2131 .
.Nm
gets the host information
.Po
.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.
.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.
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 .
if (!(ifo->auth.options & DHCPCD_AUTH_SEND))
ifo->auth.options &= ~DHCPCD_AUTH_REQUIRE;
+ if (ifo->options & DHCPCD_SLAACPRIVATE)
+ ifo->options |= DHCPCD_IPV6RA_OWN;
}
int
}
}
+ 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))
.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 .
#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'},
{"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'}
};
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;
#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;
#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[];
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;
}
}
+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)
{
#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
#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 *);
#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)
* 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",