changeset 2516:88bbfcbd9ec4 draft

Default SLAAC to use RFC7271 addresses. Create an IPv6 link-local address if non exists at startup using the configured SLAAC method.
author Roy Marples <roy@marples.name>
date Wed, 04 Jun 2014 20:27:40 +0000
parents 1f5fc94ae1cf
children 1d4beca0771f
files dhcpcd.8.in dhcpcd.c dhcpcd.conf.5.in if-options.c if-options.h ipv6.c ipv6.h ipv6nd.c
diffstat 8 files changed, 157 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/dhcpcd.8.in	Tue Jun 03 21:20:49 2014 +0000
+++ b/dhcpcd.8.in	Wed Jun 04 20:27:40 2014 +0000
@@ -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 @@
 .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 @@
 .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 @@
 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 .
--- a/dhcpcd.c	Tue Jun 03 21:20:49 2014 +0000
+++ b/dhcpcd.c	Wed Jun 04 20:27:40 2014 +0000
@@ -447,6 +447,8 @@
 	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 @@
 		}
 	}
 
+	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))
--- a/dhcpcd.conf.5.in	Tue Jun 03 21:20:49 2014 +0000
+++ b/dhcpcd.conf.5.in	Wed Jun 04 20:27:40 2014 +0000
@@ -453,9 +453,11 @@
 .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 .
--- a/if-options.c	Tue Jun 03 21:20:49 2014 +0000
+++ b/if-options.c	Wed Jun 04 20:27:40 2014 +0000
@@ -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 @@
 	{"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 @@
 		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 @@
 #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;
--- a/if-options.h	Tue Jun 03 21:20:49 2014 +0000
+++ b/if-options.h	Wed Jun 04 20:27:40 2014 +0000
@@ -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[];
 
--- a/ipv6.c	Tue Jun 03 21:20:49 2014 +0000
+++ b/ipv6.c	Wed Jun 04 20:27:40 2014 +0000
@@ -393,7 +393,7 @@
 		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 @@
 	}
 }
 
+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)
 {
--- a/ipv6.h	Tue Jun 03 21:20:49 2014 +0000
+++ b/ipv6.h	Wed Jun 04 20:27:40 2014 +0000
@@ -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 @@
 #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 @@
 
 #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)
--- a/ipv6nd.c	Tue Jun 03 21:20:49 2014 +0000
+++ b/ipv6nd.c	Wed Jun 04 20:27:40 2014 +0000
@@ -603,7 +603,7 @@
 		 * 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",