changeset 2155:4a46cea89a30 draft

Allow IAID to be configured in DHCPv4 clientid. Use this as the default for DHCPv6 IA's as well. Rename if_iaid to if_ia as it's really an IA containing an IAID.
author Roy Marples <roy@marples.name>
date Fri, 15 Nov 2013 13:43:41 +0000
parents c04ac211733e
children af00c6d25a45
files dhcp.c dhcp6.c dhcpcd.8.in dhcpcd.c dhcpcd.conf.5.in dhcpcd.h duid.c duid.h if-options.c if-options.h
diffstat 10 files changed, 231 insertions(+), 190 deletions(-) [+]
line wrap: on
line diff
--- a/dhcp.c	Thu Nov 14 15:48:07 2013 +0000
+++ b/dhcp.c	Fri Nov 15 13:43:41 2013 +0000
@@ -2512,8 +2512,7 @@
 {
 	struct dhcp_state *state;
 	const struct if_options *ifo;
-	unsigned char *duid;
-	size_t len, ifl;
+	size_t len;
 
 	state = D_STATE(ifp);
 	if (state == NULL) {
@@ -2546,33 +2545,15 @@
 			goto eexit;
 		memcpy(state->clientid, ifo->clientid, ifo->clientid[0] + 1);
 	} else if (ifo->options & DHCPCD_CLIENTID) {
-		len = 0;
 		if (ifo->options & DHCPCD_DUID) {
-			duid = malloc(DUID_LEN);
-			if (duid == NULL)
-				goto eexit;
-			if ((len = get_duid(duid, ifp)) == 0)
-				syslog(LOG_ERR, "get_duid: %m");
-		} else
-			duid = NULL;
-		if (len > 0) {
-			state->clientid = malloc(len + 6);
+			state->clientid = malloc(duid_len + 6);
 			if (state->clientid == NULL)
 				goto eexit;
-			state->clientid[0] = len + 5;
+			state->clientid[0] = duid_len + 5;
 			state->clientid[1] = 255; /* RFC 4361 */
-			ifl = strlen(ifp->name);
-			if (ifl < 5) {
-				memcpy(state->clientid + 2, ifp->name, ifl);
-				if (ifl < 4)
-					memset(state->clientid + 2 + ifl,
-					    0, 4 - ifl);
-			} else {
-				ifl = htonl(ifp->index);
-				memcpy(state->clientid + 2, &ifl, 4);
-			}
-			memcpy(state->clientid + 6, duid, len);
-		} else if (len == 0) {
+			memcpy(state->clientid + 2, ifo->iaid, 4);
+			memcpy(state->clientid + 6, duid, duid_len);
+		} else {
 			len = ifp->hwlen + 1;
 			state->clientid = malloc(len + 1);
 			if (state->clientid == NULL)
@@ -2582,7 +2563,6 @@
 			memcpy(state->clientid + 2, ifp->hwaddr,
 			    ifp->hwlen);
 		}
-		free(duid);
 	}
 	if (ifo->options & DHCPCD_CLIENTID)
 		syslog(LOG_DEBUG, "%s: using ClientID %s", ifp->name,
--- a/dhcp6.c	Thu Nov 14 15:48:07 2013 +0000
+++ b/dhcp6.c	Fri Nov 15 13:43:41 2013 +0000
@@ -78,8 +78,6 @@
 static struct iovec rcviov[2];
 static unsigned char *rcvbuf;
 static unsigned char ansbuf[1500];
-static unsigned char *duid;
-static uint16_t duid_len;
 static char ntopbuf[INET6_ADDRSTRLEN];
 static char *status;
 static size_t status_len;
@@ -153,7 +151,6 @@
 
 	free(sndbuf);
 	free(rcvbuf);
-	free(duid);
 	free(status);
 }
 #endif
@@ -470,7 +467,7 @@
 		/* FALLTHROUGH */
 	case DH6S_INIT: /* FALLTHROUGH */
 	case DH6S_DISCOVER:
-		len += ifo->iaid_len * (sizeof(*o) + (sizeof(u32) * 3));
+		len += ifo->ia_len * (sizeof(*o) + (sizeof(u32) * 3));
 		IA = 1;
 		break;
 	default:
@@ -567,18 +564,18 @@
 		o->len = 0;
 	}
 
-	for (l = 0; IA && l < ifo->iaid_len; l++) {
+	for (l = 0; IA && l < ifo->ia_len; l++) {
 		o = D6_NEXT_OPTION(o);
 		o->code = htons(ifo->ia_type);
 		o->len = htons(sizeof(u32) + sizeof(u32) + sizeof(u32));
 		p = D6_OPTION_DATA(o);
-		memcpy(p, ifo->iaid[l].iaid, sizeof(u32));
+		memcpy(p, ifo->ia[l].iaid, sizeof(u32));
 		p += sizeof(u32);
 		memset(p, 0, sizeof(u32) + sizeof(u32));
 		TAILQ_FOREACH(ap, &state->addrs, next) {
 			if (ap->prefix_vltime == 0)
 				continue;
-			if (memcmp(ifo->iaid[l].iaid, ap->iaid, sizeof(u32)))
+			if (memcmp(ifo->ia[l].iaid, ap->iaid, sizeof(u32)))
 				continue;
 			so = D6_NEXT_OPTION(o);
 			if (ifo->ia_type == D6_OPTION_IA_PD) {
@@ -1813,7 +1810,7 @@
 	struct dhcp6_state *state, *ifd_state;
 	struct ipv6_addr *ap;
 	size_t i, j, k;
-	struct if_iaid *iaid;
+	struct if_ia *ia;
 	struct if_sla *sla;
 	struct interface *ifd;
 	uint8_t carrier_warned;
@@ -1829,12 +1826,12 @@
 				syslog(LOG_DEBUG, "%s: delegated prefix %s",
 				    ifp->name, ap->saddr);
 			}
-			for (i = 0; i < ifo->iaid_len; i++) {
-				iaid = &ifo->iaid[i];
-				if (memcmp(iaid->iaid, ap->iaid,
-				    sizeof(iaid->iaid)))
+			for (i = 0; i < ifo->ia_len; i++) {
+				ia = &ifo->ia[i];
+				if (memcmp(ia->iaid, ap->iaid,
+				    sizeof(ia->iaid)))
 					continue;
-				if (iaid->sla_len == 0) {
+				if (ia->sla_len == 0) {
 					/* no SLA configured, so lets
 					 * automate it */
 					if (ifp == ifd)
@@ -1851,8 +1848,8 @@
 					    NULL, ifp))
 						k++;
 				}
-				for (j = 0; j < iaid->sla_len; j++) {
-					sla = &iaid->sla[j];
+				for (j = 0; j < ia->sla_len; j++) {
+					sla = &ia->sla[j];
 					if (strcmp(ifd->name, sla->ifname))
 						continue;
 					if (ifd->carrier == LINK_DOWN) {
@@ -1880,12 +1877,12 @@
 	}
 
 	/* Warn about configured interfaces for delegation that do not exist */
-	for (i = 0; i < ifo->iaid_len; i++) {
-		iaid = &ifo->iaid[i];
-		for (j = 0; j < iaid->sla_len; j++) {
-			sla = &iaid->sla[j];
+	for (i = 0; i < ifo->ia_len; i++) {
+		ia = &ifo->ia[i];
+		for (j = 0; j < ia->sla_len; j++) {
+			sla = &ia->sla[j];
 			for (k = 0; k < i; j++)
-				if (strcmp(sla->ifname, iaid->sla[j].ifname) == 0)
+				if (strcmp(sla->ifname, ia->sla[j].ifname) == 0)
 					break;
 			if (j >= i && find_interface(sla->ifname) == NULL)
 				syslog(LOG_ERR,
@@ -1910,7 +1907,7 @@
 	struct dhcp6_state *state;
 	struct ipv6_addr *ap;
 	size_t i, j, k;
-	struct if_iaid *iaid;
+	struct if_ia *ia;
 	struct if_sla *sla;
 	struct interface *ifd;
 
@@ -1923,13 +1920,13 @@
 		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)))
+			for (i = 0; i < ifo->ia_len; i++) {
+				ia = &ifo->ia[i];
+				if (memcmp(ia->iaid, ap->iaid,
+				    sizeof(ia->iaid)))
 					continue;
-				for (j = 0; j < iaid->sla_len; j++) {
-					sla = &iaid->sla[j];
+				for (j = 0; j < ia->sla_len; j++) {
+					sla = &ia->sla[j];
 					if (strcmp(ifp->name, sla->ifname))
 						continue;
 					if (ipv6_linklocal(ifp) == NULL) {
@@ -2417,13 +2414,6 @@
 	if (sock == -1 && dhcp6_open() == -1)
 		return -1;
 
-	if (duid == NULL) {
-		duid = malloc(DUID_LEN);
-		if (duid == NULL)
-			return -1;
-		duid_len = get_duid(duid, ifp);
-	}
-
 	ifp->if_data[IF_DATA_DHCP6] = calloc(1, sizeof(*state));
 	state = D6_STATE(ifp);
 	if (state == NULL)
--- a/dhcpcd.8.in	Thu Nov 14 15:48:07 2013 +0000
+++ b/dhcpcd.8.in	Fri Nov 15 13:43:41 2013 +0000
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd November 12, 2013
+.Dd November 15, 2013
 .Dt DHCPCD 8
 .Os
 .Sh NAME
@@ -125,6 +125,8 @@
 By default,
 .Nm
 only starts DHCPv6 when instructed to do so by an IPV6 Router Advertisement.
+If no Identity Association is configured,
+then a Non-temporary Address is requested.
 .Ss Local Link configuration
 If
 .Nm
--- a/dhcpcd.c	Thu Nov 14 15:48:07 2013 +0000
+++ b/dhcpcd.c	Fri Nov 15 13:43:41 2013 +0000
@@ -57,6 +57,7 @@
 #include "dev.h"
 #include "dhcpcd.h"
 #include "dhcp6.h"
+#include "duid.h"
 #include "eloop.h"
 #include "if-options.h"
 #include "if-pref.h"
@@ -141,6 +142,7 @@
 	struct interface *ifp;
 	int i;
 
+	free(duid);
 	free_options(if_options);
 
 	if (ifaces) {
@@ -319,7 +321,7 @@
 {
 	struct if_options *ifo = ifp->options;
 	int ra_global, ra_iface;
-
+	uint32_t len;
 	/* Do any platform specific configuration */
 	if_conf(ifp);
 
@@ -336,6 +338,9 @@
 	if (ifo->metric != -1)
 		ifp->metric = ifo->metric;
 
+	if (!(ifo->options & DHCPCD_IPV6))
+		ifo->options &= ~DHCPCD_IPV6RS;
+
 	/* We want to disable kernel interface RA as early as possible. */
 	if (ifo->options & DHCPCD_IPV6RS) {
 		ra_global = check_ipv6(NULL, options & DHCPCD_IPV6RA_OWN ? 1:0);
@@ -361,6 +366,35 @@
 		ifo->options |= DHCPCD_CLIENTID | DHCPCD_BROADCAST;
 		break;
 	}
+
+	if (!(ifo->options & DHCPCD_IAID)) {
+		len = strlen(ifp->name);
+		if (len <= sizeof(ifo->iaid)) {
+			memcpy(ifo->iaid, ifp->name, len);
+			memset(ifo->iaid + len, 0, sizeof(ifo->iaid) - len);
+		} else {
+			/* IAID is the same size as a uint32_t */
+			len = htonl(ifp->index);
+			memcpy(ifo->iaid, &len, sizeof(len));
+		}
+		ifo->options |= DHCPCD_IAID;
+	}
+
+#ifdef INET6
+	if (ifo->ia == NULL && ifo->options & DHCPCD_IPV6) {
+		ifo->ia = malloc(sizeof(*ifo->ia));
+		if (ifo->ia == NULL)
+			syslog(LOG_ERR, "%s: %m", __func__);
+		else {
+			if (ifo->ia_type == 0)
+				ifo->ia_type = D6_OPTION_IA_NA;
+			memcpy(ifo->ia->iaid, ifo->iaid, sizeof(ifo->iaid));
+			ifo->ia_len = 1;
+			ifo->ia->sla = NULL;
+			ifo->ia->sla_len = 0;
+		}
+	}
+#endif
 }
 
 int
@@ -457,6 +491,7 @@
 	struct interface *ifp = arg;
 	struct if_options *ifo = ifp->options;
 	int nolease;
+	size_t i;
 
 	handle_carrier(LINK_UNKNOWN, 0, ifp->name);
 	if (ifp->carrier == LINK_DOWN) {
@@ -464,6 +499,27 @@
 		return;
 	}
 
+	if (ifo->options & (DHCPCD_DUID | DHCPCD_IPV6)) {
+		/* Report client DUID */
+		if (duid == NULL) {
+			if (duid_init(ifp) == 0)
+				return;
+			syslog(LOG_INFO, "DUID %s",
+			    hwaddr_ntoa(duid, duid_len));
+		}
+
+		/* Report IAIDs */
+		syslog(LOG_INFO, "%s: IAID %s", ifp->name,
+		    hwaddr_ntoa(ifo->iaid, sizeof(ifo->iaid)));
+		for (i = 0; i < ifo->ia_len; i++) {
+			if (memcmp(ifo->iaid, ifo->ia[i].iaid,
+			    sizeof(ifo->iaid)))
+				syslog(LOG_INFO, "%s: IAID %s", ifp->name,
+				    hwaddr_ntoa(ifo->ia[i].iaid,
+				    sizeof(ifo->ia[i].iaid)));
+		}
+	}
+
 	if (ifo->options & DHCPCD_IPV6) {
 		if (ifo->options & DHCPCD_IPV6RS &&
 		    !(ifo->options & DHCPCD_INFORM))
--- a/dhcpcd.conf.5.in	Thu Nov 14 15:48:07 2013 +0000
+++ b/dhcpcd.conf.5.in	Fri Nov 15 13:43:41 2013 +0000
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd October 10, 2013
+.Dd November 15, 2013
 .Dt DHCPCD.CONF 5
 .Os
 .Sh NAME
@@ -135,12 +135,19 @@
 .Rs
 .%T "RFC 4361"
 .Re
-compliant clientid.
+compliant DHCP Unique Identifier.
 If persistent storage is available then a DUID-LLT (link local address + time)
 is generated, otherwise DUID-LL is generated (link local address).
+This, plus the IAID will be used as the
+.Ic clientid .
 The DUID-LLT generated will be held in
 .Pa @SYSCONFDIR@/dhcpcd.duid
 and should not be copied to other hosts.
+.It Ic iaid Ar iaid
+Set the Interface Association Identifier to
+.Ar iaid .
+This defaults to the interface name if 4 or less characters.
+If more then it defaults to the interface index.
 .It Ic persistent
 .Nm dhcpcd
 normally de-configures the interface and configuration when it exits.
@@ -172,16 +179,19 @@
 .It Ic ia_na Op 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.
-You can request more than one ia_na by specifying a unique iaid for each one.
+defaults to the
+.Ic iaid
+option as described above.
+You can request more than one ia_na by specifying a unique
+.Ar iaid
+for each one.
 .It Ic ia_ta Op Ar iaid
 Request a DHCPv6 Temporary Address for
 .Ar iaid .
-You can request more than one ia_ta by specifying a unique iaid for each one.
+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
 Request a DHCPv6 Delegated Prefix for
 .Ar iaid .
@@ -461,5 +471,7 @@
 .Sh AUTHORS
 .An Roy Marples Aq Mt roy@marples.name
 .Sh BUGS
+When configuring DHCPv6 you can only select one IA type.
+.Pp
 Please report them to
 .Lk http://roy.marples.name/projects/dhcpcd
--- a/dhcpcd.h	Thu Nov 14 15:48:07 2013 +0000
+++ b/dhcpcd.h	Fri Nov 15 13:43:41 2013 +0000
@@ -44,7 +44,7 @@
 #define LINK_DOWN	-1
 
 #define IF_DATA_IPV4	0
-#define IF_DATA_DHCP	1	
+#define IF_DATA_DHCP	1
 #define IF_DATA_IPV6	2
 #define IF_DATA_IPV6ND	3
 #define IF_DATA_DHCP6	4
--- a/duid.c	Thu Nov 14 15:48:07 2013 +0000
+++ b/duid.c	Fri Nov 15 13:43:41 2013 +0000
@@ -1,6 +1,6 @@
 /*
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2008 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
@@ -51,15 +51,18 @@
 #include "duid.h"
 #include "net.h"
 
+unsigned char *duid = NULL;
+size_t duid_len = 0;
+
 static size_t
-make_duid(unsigned char *duid, const struct interface *ifp, uint16_t type)
+duid_make(unsigned char *d, const struct interface *ifp, uint16_t type)
 {
 	unsigned char *p;
 	uint16_t u16;
 	time_t t;
 	uint32_t u32;
 
-	p = duid;
+	p = d;
 	u16 = htons(type);
 	memcpy(p, &u16, 2);
 	p += 2;
@@ -77,17 +80,17 @@
 	/* Finally, add the MAC address of the interface */
 	memcpy(p, ifp->hwaddr, ifp->hwlen);
 	p += ifp->hwlen;
-	return p - duid;
+	return p - d;
 }
 
-size_t
-get_duid(unsigned char *duid, const struct interface *iface)
+static size_t
+duid_get(unsigned char *d, const struct interface *ifp)
 {
 	FILE *f;
 	int x = 0;
 	size_t len = 0;
 	char *line;
-	const struct interface *ifp;
+	const struct interface *ifp2;
 
 	/* If we already have a DUID then use it as it's never supposed
 	 * to change once we have one even if the interfaces do */
@@ -95,7 +98,7 @@
 		while ((line = get_line(f))) {
 			len = hwaddr_aton(NULL, line);
 			if (len && len <= DUID_LEN) {
-				hwaddr_aton(duid, line);
+				hwaddr_aton(d, line);
 				break;
 			}
 			len = 0;
@@ -109,37 +112,51 @@
 	}
 
 	/* No file? OK, lets make one based on our interface */
-	if (iface->family == ARPHRD_NETROM) {
+	if (ifp->family == ARPHRD_NETROM) {
 		syslog(LOG_WARNING, "%s: is a NET/ROM psuedo interface",
-		    iface->name);
-		TAILQ_FOREACH(ifp, ifaces, next) {
-			if (ifp->family != ARPHRD_NETROM)
+		    ifp->name);
+		TAILQ_FOREACH(ifp2, ifaces, next) {
+			if (ifp2->family != ARPHRD_NETROM)
 				break;
 		}
-		if (ifp) {
-			iface = ifp;
+		if (ifp2) {
+			ifp = ifp2;
 			syslog(LOG_WARNING,
 			    "picked interface %s to generate a DUID",
-			    iface->name);
+			    ifp->name);
 		} else {
 			syslog(LOG_WARNING,
 			    "no interfaces have a fixed hardware address");
-			return make_duid(duid, iface, DUID_LL);
+			return duid_make(d, ifp, DUID_LL);
 		}
 	}
 
 	if (!(f = fopen(DUID, "w"))) {
 		syslog(LOG_ERR, "error writing DUID: %s: %m", DUID);
-		return make_duid(duid, iface, DUID_LL);
+		return duid_make(d, ifp, DUID_LL);
 	}
-	len = make_duid(duid, iface, DUID_LLT);
-	x = fprintf(f, "%s\n", hwaddr_ntoa(duid, len));
+	len = duid_make(d, ifp, DUID_LLT);
+	x = fprintf(f, "%s\n", hwaddr_ntoa(d, len));
 	fclose(f);
 	/* Failed to write the duid? scrub it, we cannot use it */
 	if (x < 1) {
 		syslog(LOG_ERR, "error writing DUID: %s: %m", DUID);
 		unlink(DUID);
-		return make_duid(duid, iface, DUID_LL);
+		return duid_make(d, ifp, DUID_LL);
 	}
 	return len;
 }
+
+size_t duid_init(const struct interface *ifp)
+{
+
+	if (duid == NULL) {
+		duid = malloc(DUID_LEN);
+		if (duid == NULL) {
+			syslog(LOG_ERR, "%s: %m", __func__);
+			return 0;
+		}
+		duid_len = duid_get(duid, ifp);
+	}
+	return duid_len;
+}
--- a/duid.h	Thu Nov 14 15:48:07 2013 +0000
+++ b/duid.h	Fri Nov 15 13:43:41 2013 +0000
@@ -1,6 +1,6 @@
 /*
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2008 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
@@ -30,6 +30,9 @@
 
 #include "net.h"
 
-size_t get_duid(unsigned char *duid, const struct interface *iface);
+extern unsigned char *duid;
+extern size_t duid_len;
+
+size_t duid_init(const struct interface *);
 
 #endif
--- a/if-options.c	Thu Nov 14 15:48:07 2013 +0000
+++ b/if-options.c	Fri Nov 15 13:43:41 2013 +0000
@@ -72,6 +72,7 @@
 #define O_NODEV			O_BASE + 15
 #define O_NOIPV4		O_BASE + 16
 #define O_NOIPV6		O_BASE + 17
+#define O_IAID			O_BASE + 18
 
 char *dev_load;
 
@@ -134,6 +135,7 @@
 	{"noipv4",          no_argument,       NULL, O_NOIPV4},
 	{"noipv6",          no_argument,       NULL, O_NOIPV6},
 	{"noalias",         no_argument,       NULL, O_NOALIAS},
+	{"iaid",            no_argument,       NULL, O_IAID},
 	{"ia_na",           no_argument,       NULL, O_IA_NA},
 	{"ia_ta",           no_argument,       NULL, O_IA_TA},
 	{"ia_pd",           no_argument,       NULL, O_IA_PD},
@@ -334,6 +336,35 @@
 	return l;
 }
 
+static int
+parse_iaid(uint8_t *iaid, const char *arg, size_t len)
+{
+	unsigned long l;
+	size_t s;
+	uint32_t u32;
+	char *np;
+
+	errno = 0;
+	l = strtoul(arg, &np, 0);
+	if (l <= (unsigned long)UINT32_MAX && errno == 0 && *np == '\0') {
+		u32 = htonl(l);
+		memcpy(iaid, &u32, sizeof(u32));
+		return 0;
+	}
+
+	if ((s = parse_string((char *)iaid, len, 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';
+	return 0;
+}
+
 static char **
 splitv(int *argc, char **argv, const char *arg)
 {
@@ -449,11 +480,9 @@
 	const struct dhcp_opt *d;
 	uint8_t *request, *require, *no;
 #ifdef INET6
-	long l;
-	uint32_t u32;
 	size_t sl;
-	struct if_iaid *iaid;
-	uint8_t _iaid[4];
+	struct if_ia *ia;
+	uint8_t iaid[4];
 	struct if_sla *sla, *slap;
 #endif
 
@@ -520,7 +549,7 @@
 			return -1;
 		}
 		errno = 0;
-		ifo->leasetime = (uint32_t)strtol(arg, NULL, 0);
+		ifo->leasetime = (uint32_t)strtoul(arg, NULL, 0);
 		if (errno == EINVAL || errno == ERANGE) {
 			syslog(LOG_ERR, "`%s' out of range", arg);
 			return -1;
@@ -954,6 +983,10 @@
 		}
 		break;
 #endif
+	case O_IAID:
+		if (parse_iaid(ifo->iaid, arg, sizeof(ifo->iaid)) == -1)
+			return -1;
+		ifo->options |= DHCPCD_IAID;
 	case O_IPV6RS:
 		ifo->options |= DHCPCD_IPV6RS;
 		break;
@@ -994,52 +1027,34 @@
 		fp = strchr(arg, ' ');
 		if (fp)
 			*fp++ = '\0';
-		errno = 0;
-		l = strtol(arg, &np, 0);
-		if (l >= 0 && l <= (long)UINT32_MAX &&
-		    errno == 0 && *np == '\0')
-		{
-			u32 = htonl(l);
-			memcpy(&_iaid, &u32, sizeof(_iaid));
-			goto got_iaid;
-		}
-		if ((s = parse_string((char *)_iaid, sizeof(_iaid), arg)) < 1) {
-			syslog(LOG_ERR, "%s: invalid IAID", arg);
+		if (parse_iaid(iaid, arg, sizeof(iaid)) == -1)
 			return -1;
-		}
-		if (s < 4)
-			_iaid[3] = '\0';
-		if (s < 3)
-			_iaid[2] = '\0';
-		if (s < 2)
-			_iaid[1] = '\0';
-got_iaid:
-		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])
+		ia = NULL;
+		for (sl = 0; sl < ifo->ia_len; sl++) {
+			if (ifo->ia[sl].iaid[0] == iaid[0] &&
+			    ifo->ia[sl].iaid[1] == iaid[1] &&
+			    ifo->ia[sl].iaid[2] == iaid[2] &&
+			    ifo->ia[sl].iaid[3] == iaid[3])
 			{
-			        iaid = &ifo->iaid[sl];
+			        ia = &ifo->ia[sl];
 				break;
 			}
 		}
-		if (iaid == NULL) {
-			iaid = realloc(ifo->iaid,
-			    sizeof(*ifo->iaid) * (ifo->iaid_len + 1));
-			if (iaid == NULL) {
+		if (ia == NULL) {
+			ia = realloc(ifo->ia,
+			    sizeof(*ifo->ia) * (ifo->ia_len + 1));
+			if (ia == 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;
+			ifo->ia = ia;
+			ia = &ifo->ia[ifo->ia_len++];
+			ia->iaid[0] = iaid[0];
+			ia->iaid[1] = iaid[1];
+			ia->iaid[2] = iaid[2];
+			ia->iaid[3] = iaid[3];
+			ia->sla = NULL;
+			ia->sla_len = 0;
 		}
 		if (ifo->ia_type != D6_OPTION_IA_PD)
 			break;
@@ -1047,14 +1062,14 @@
 			fp = strchr(p, ' ');
 			if (fp)
 				*fp++ = '\0';
-			sla = realloc(iaid->sla,
-			    sizeof(*iaid->sla) * (iaid->sla_len + 1));
+			sla = realloc(ia->sla,
+			    sizeof(*ia->sla) * (ia->sla_len + 1));
 			if (sla == NULL) {
 				syslog(LOG_ERR, "%s: %m", __func__);
 				return -1;
 			}
-			iaid->sla = sla;
-			sla = &iaid->sla[iaid->sla_len++];
+			ia->sla = sla;
+			sla = &ia->sla[ia->sla_len++];
 			np = strchr(p, '/');
 			if (np)
 				*np++ = '\0';
@@ -1090,8 +1105,8 @@
 				sla->sla_set = 0;
 				/* Sanity - check there are no more
 				 * unspecified SLA's */
-				for (sl = 0; sl < iaid->sla_len - 1; sl++) {
-					slap = &iaid->sla[sl];
+				for (sl = 0; sl < ia->sla_len - 1; sl++) {
+					slap = &ia->sla[sl];
 					if (slap->sla_set == 0 &&
 					    strcmp(slap->ifname, sla->ifname)
 					    == 0)
@@ -1101,14 +1116,14 @@
 						    "same interface twice with "
 						    "an automatic SLA",
 						    sla->ifname);
-						iaid->sla_len--;
+						ia->sla_len--;
 						break;
 					}
 				}
 			}
 		}
+#endif
 		break;
-#endif
 	case O_HOSTNAME_SHORT:
 		ifo->options |= DHCPCD_HOSTNAME | DHCPCD_HOSTNAME_SHORT;
 		break;
@@ -1166,39 +1181,6 @@
 	}
 }
 
-#ifdef INET6
-static void
-finish_config6(struct if_options *ifo, const char *ifname)
-{
-
-	if (!(ifo->options & DHCPCD_IPV6))
-		ifo->options &= ~DHCPCD_IPV6RS;
-
-	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;
-		}
-	}
-}
-#endif
-
 struct if_options *
 read_config(const char *file,
     const char *ifname, const char *ssid, const char *profile)
@@ -1290,9 +1272,6 @@
 	}
 
 	finish_config(ifo);
-#ifdef INET6
-	finish_config6(ifo, ifname);
-#endif
 	return ifo;
 }
 
@@ -1314,9 +1293,6 @@
 	}
 
 	finish_config(ifo);
-#ifdef INET6
-	finish_config6(ifo, NULL);
-#endif
 	return r;
 }
 
@@ -1343,10 +1319,11 @@
 		free(ifo->blacklist);
 		free(ifo->fallback);
 #ifdef INET6
-		for (i = 0; i < ifo->iaid_len; i++)
-			free(ifo->iaid[i].sla);
-		free(ifo->iaid);
+		for (i = 0; i < ifo->ia_len; i++)
+			free(ifo->ia[i].sla);
 #endif
+		free(ifo->ia);
+
 		free(ifo);
 	}
 }
--- a/if-options.h	Thu Nov 14 15:48:07 2013 +0000
+++ b/if-options.h	Fri Nov 15 13:43:41 2013 +0000
@@ -97,6 +97,7 @@
 #define DHCPCD_WAITIP4			(1ULL << 45)
 #define DHCPCD_WAITIP6			(1ULL << 46)
 #define DHCPCD_DEV			(1ULL << 47)
+#define DHCPCD_IAID			(1ULL << 48)
 
 extern const struct option cf_options[];
 
@@ -107,13 +108,16 @@
 	int8_t sla_set;
 };
 
-struct if_iaid {
+struct if_ia {
 	uint8_t iaid[4];
+#ifdef INET6
 	size_t sla_len;
 	struct if_sla *sla;
+#endif
 };
 
 struct if_options {
+	uint8_t iaid[4];
 	int metric;
 	uint8_t requestmask[256 / 8];
 	uint8_t requiremask[256 / 8];
@@ -150,10 +154,10 @@
 	in_addr_t *arping;
 	char *fallback;
 
+	uint16_t ia_type;
+	struct if_ia *ia;
+	size_t ia_len;
 #ifdef INET6
-	uint16_t ia_type;
-	size_t iaid_len;
-	struct if_iaid *iaid;
 	int dadtransmits;
 #endif
 };