[packages/dhcpcd] - rel 2; dhcpcd can become cpu hog on ipv4 renewal - fix that

arekm arekm at pld-linux.org
Wed Nov 6 14:02:05 CET 2019


commit 3f1392780d88daffa88a8a772186fc8884fe45c3
Author: Arkadiusz Miśkiewicz <arekm at maven.pl>
Date:   Wed Nov 6 14:01:57 2019 +0100

    - rel 2; dhcpcd can become cpu hog on ipv4 renewal - fix that

 dhcpcd-git.patch | 3014 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 dhcpcd.spec      |    4 +-
 2 files changed, 3017 insertions(+), 1 deletion(-)
---
diff --git a/dhcpcd.spec b/dhcpcd.spec
index f6b0652..c5651ae 100644
--- a/dhcpcd.spec
+++ b/dhcpcd.spec
@@ -7,11 +7,12 @@ Summary(pt_BR.UTF-8):	Servidor DHCPC
 Summary(tr.UTF-8):	DHCPC sunucu süreçi (daemon)
 Name:		dhcpcd
 Version:	8.1.1
-Release:	1
+Release:	2
 License:	BSD
 Group:		Networking/Daemons
 Source0:	http://roy.marples.name/downloads/dhcpcd/%{name}-%{version}.tar.xz
 # Source0-md5:	dc4f29a62afc53cdac311e925cfd1bc7
+Patch0:		cpuhog.patch
 URL:		http://roy.marples.name/projects/dhcpcd
 BuildRoot:	%{tmpdir}/%{name}-%{version}-root-%(id -u -n)
 
@@ -86,6 +87,7 @@ kira zamanını (lease time) yenilemeye çalışır.
 
 %prep
 %setup -q
+%patch0 -p1
 
 %build
 %configure \
diff --git a/dhcpcd-git.patch b/dhcpcd-git.patch
new file mode 100644
index 0000000..9a77b9c
--- /dev/null
+++ b/dhcpcd-git.patch
@@ -0,0 +1,3014 @@
+diff -urN dhcpcd-6.4.0.org/configure dhcpcd-6.4.0/configure
+--- dhcpcd-6.4.0.org/configure	2014-06-14 22:13:12.000000000 +0200
++++ dhcpcd-6.4.0/configure	2014-07-05 21:47:22.000000000 +0200
+@@ -272,8 +272,18 @@
+ fi
+ 
+ echo "Using compiler .. $CC"
+-if ! type "$CC" >/dev/null 2>&1; then
+-	echo "$CC is not an executable"
++cat <<EOF >_test.c
++int main(void) {
++	return 0;
++}
++EOF
++_CC=false
++if $CC _test.c -o _test >/dev/null 2>&1; then
++	[ -x _test ] && _CC=true
++fi
++rm -f _test.c _test
++if ! $_CC; then
++	echo "$CC does not create executables"
+ 	exit 1
+ fi
+ [ "$CC" != cc ] && echo "CC=	$CC" >>$CONFIG_MK
+@@ -422,7 +432,7 @@
+ EOF
+ if $XCC _getifaddrs.c -o _getifaddrs 2>/dev/null; then
+ 	echo "yes"
+-elif $XCC _getifaddrs.c -o _getifaddrs -lsocket >/dev/null; then
++elif $XCC _getifaddrs.c -o _getifaddrs -lsocket 2>/dev/null; then
+ 	echo "yes (-lsocket)"
+ 	echo "LDADD+=		-lsocket" >>$CONFIG_MK
+ else
+diff -urN dhcpcd-6.4.0.org/control.c dhcpcd-6.4.0/control.c
+--- dhcpcd-6.4.0.org/control.c	2014-06-14 22:13:12.000000000 +0200
++++ dhcpcd-6.4.0/control.c	2014-07-05 21:47:22.000000000 +0200
+@@ -52,8 +52,9 @@
+ control_handle_data(void *arg)
+ {
+ 	struct fd_list *l = arg, *lp, *last;
+-	char buffer[1024], *e, *p, *argvp[255], **ap;
++	char buffer[1024], *e, *p, *argvp[255], **ap, *a;
+ 	ssize_t bytes;
++	size_t len;
+ 	int argc;
+ 
+ 	bytes = read(l->fd, buffer, sizeof(buffer) - 1);
+@@ -79,14 +80,28 @@
+ 	buffer[bytes] = '\0';
+ 	p = buffer;
+ 	e = buffer + bytes;
+-	argc = 0;
+-	ap = argvp;
+-	while (p < e && (size_t)argc < sizeof(argvp)) {
+-		argc++;
+-		*ap++ = p;
+-		p += strlen(p) + 1;
++
++	/* Each command is \n terminated
++	 * Each argument is NULL separated */
++	while (p < e) {
++		argc = 0;
++		ap = argvp;
++		while (p < e) {
++			argc++;
++			if ((size_t)argc > sizeof(argvp)) {
++				errno = ENOBUFS;
++				return;
++			}
++			a = *ap++ = p;
++			len = strlen(p);
++			p += len + 1;
++			if (a[len - 1] == '\n') {
++				a[len - 1] = '\0';
++				break;
++			}
++		}
++		dhcpcd_handleargs(l->ctx, l, argc, argvp);
+ 	}
+-	dhcpcd_handleargs(l->ctx, l, argc, argvp);
+ }
+ 
+ static void
+diff -urN dhcpcd-6.4.0.org/defs.h dhcpcd-6.4.0/defs.h
+--- dhcpcd-6.4.0.org/defs.h	2014-06-14 22:13:12.000000000 +0200
++++ dhcpcd-6.4.0/defs.h	2014-07-05 21:47:22.000000000 +0200
+@@ -49,7 +49,7 @@
+ # define LEASEFILE		DBDIR "/" PACKAGE "-%s.lease"
+ #endif
+ #ifndef LEASEFILE6
+-# define LEASEFILE6		DBDIR "/" PACKAGE "-%s.lease6"
++# define LEASEFILE6		DBDIR "/" PACKAGE "-%s%s.lease6"
+ #endif
+ #ifndef PIDFILE
+ # define PIDFILE		RUNDIR "/" PACKAGE "%s%s%s.pid"
+diff -urN dhcpcd-6.4.0.org/dhcp6.c dhcpcd-6.4.0/dhcp6.c
+--- dhcpcd-6.4.0.org/dhcp6.c	2014-06-14 22:13:12.000000000 +0200
++++ dhcpcd-6.4.0/dhcp6.c	2014-07-05 21:47:22.000000000 +0200
+@@ -121,13 +121,22 @@
+ };
+ 
+ void
+-dhcp6_printoptions(const struct dhcpcd_ctx *ctx)
++dhcp6_printoptions(const struct dhcpcd_ctx *ctx,
++    const struct dhcp_opt *opts, size_t opts_len)
+ {
+-	size_t i;
+-	const struct dhcp_opt *opt;
++	size_t i, j;
++	const struct dhcp_opt *opt, *opt2;
+ 
+ 	for (i = 0, opt = ctx->dhcp6_opts;
+ 	    i < ctx->dhcp6_opts_len; i++, opt++)
++	{
++		for (j = 0, opt2 = opts; j < opts_len; j++, opt2++)
++			if (opt2->option == opt->option)
++				break;
++		if (j == opts_len)
++			printf("%05d %s\n", opt->option, opt->var);
++	}
++	for (i = 0, opt = opts; i < opts_len; i++, opt++)
+ 		printf("%05d %s\n", opt->option, opt->var);
+ }
+ 
+@@ -304,24 +313,112 @@
+ 	m->xid[2] = xid & 0xff;
+ }
+ 
++static const struct if_sla *
++dhcp6_findselfsla(struct interface *ifp, const uint8_t *iaid)
++{
++	size_t i, j;
++
++	for (i = 0; i < ifp->options->ia_len; i++) {
++		if (iaid == NULL ||
++		    memcmp(&ifp->options->ia[i].iaid, iaid,
++		    sizeof(ifp->options->ia[i].iaid)) == 0)
++		{
++			for (j = 0; j < ifp->options->ia[i].sla_len; j++) {
++				if (strcmp(ifp->options->ia[i].sla[j].ifname,
++				    ifp->name) == 0)
++					return &ifp->options->ia[i].sla[j];
++			}
++		}
++	}
++	return NULL;
++}
++
++static int
++dhcp6_delegateaddr(struct in6_addr *addr, struct interface *ifp,
++    const struct ipv6_addr *prefix, const struct if_sla *sla)
++{
++	struct dhcp6_state *state;
++	struct if_sla asla;
++	char iabuf[INET6_ADDRSTRLEN];
++	const char *ia;
++
++	state = D6_STATE(ifp);
++	if (state == NULL) {
++		ifp->if_data[IF_DATA_DHCP6] = calloc(1, sizeof(*state));
++		state = D6_STATE(ifp);
++		if (state == NULL) {
++			syslog(LOG_ERR, "%s: %m", __func__);
++			return -1;
++		}
++
++		TAILQ_INIT(&state->addrs);
++		state->state = DH6S_DELEGATED;
++		state->reason = "DELEGATED6";
++	}
++
++	if (sla == NULL || sla->sla_set == 0) {
++		struct interface *ifi;
++		unsigned int idx;
++		int bits;
++
++		asla.sla = ifp->index;
++		/* Work out our largest index delegating to. */
++		idx = 0;
++		TAILQ_FOREACH(ifi, ifp->ctx->ifaces, next) {
++			if (ifi != ifp && ifi->index > idx)
++				idx = ifi->index;
++		}
++		bits = ffs((int)idx);
++		if (prefix->prefix_len + bits > UINT8_MAX)
++			asla.prefix_len = UINT8_MAX;
++		else {
++			asla.prefix_len = prefix->prefix_len + (uint8_t)bits;
++
++			/* Make a 64 prefix by default, as this maks SLAAC
++			 * possible. Otherwise round up to the nearest octet. */
++			if (asla.prefix_len <= 64)
++				asla.prefix_len = 64;
++			else
++				asla.prefix_len = ROUNDUP8(asla.prefix_len);
++
++		}
++		sla = &asla;
++	}
++
++	if (ipv6_userprefix(&prefix->prefix, prefix->prefix_len,
++		sla->sla, addr, sla->prefix_len) == -1)
++	{
++		ia = inet_ntop(AF_INET6, &prefix->prefix.s6_addr,
++		    iabuf, sizeof(iabuf));
++		syslog(LOG_ERR, "%s: invalid prefix %s/%d + %d/%d: %m",
++			ifp->name, ia, prefix->prefix_len,
++			sla->sla, sla->prefix_len);
++		return -1;
++	}
++
++	return 0;
++}
++
+ static int
+ dhcp6_makemessage(struct interface *ifp)
+ {
+ 	struct dhcp6_state *state;
+ 	struct dhcp6_message *m;
+-	struct dhcp6_option *o, *so;
++	struct dhcp6_option *o, *so, *eo;
+ 	const struct dhcp6_option *si, *unicast;
+-	size_t l, len, ml, auth_len;
++	size_t l, n, len, ml, auth_len;
+ 	uint8_t u8, type;
+ 	uint16_t *u16, n_options;
+ 	struct if_options *ifo;
+-	const struct dhcp_opt *opt;
++	const struct dhcp_opt *opt, *opt2;
+ 	uint8_t IA, *p;
+ 	uint32_t u32;
+ 	const struct ipv6_addr *ap;
+ 	char hbuf[HOSTNAME_MAX_LEN + 1];
+ 	const char *hostname;
+ 	int fqdn;
++	const struct if_sla *sla;
++	struct in6_addr addr;
+ 
+ 	state = D6_STATE(ifp);
+ 	if (state->send) {
+@@ -356,6 +453,15 @@
+ 		    l < ifp->ctx->dhcp6_opts_len;
+ 		    l++, opt++)
+ 		{
++			for (n = 0, opt2 = ifo->dhcp6_override;
++			    n < ifo->dhcp6_override_len;
++			    n++, opt2++)
++			{
++				if (opt->option == opt2->option)
++					break;
++			}
++			if (n < ifo->dhcp6_override_len)
++			    continue;
+ 			if (!(opt->type & NOREQ) &&
+ 			    (opt->type & REQUEST ||
+ 			    has_option_mask(ifo->requestmask6, opt->option)))
+@@ -364,6 +470,22 @@
+ 				len += sizeof(*u16);
+ 			}
+ 		}
++		for (l = 0, opt = ifo->dhcp6_override;
++		    l < ifo->dhcp6_override_len;
++		    l++, opt++)
++		{
++			if (!(opt->type & NOREQ) &&
++			    (opt->type & REQUEST ||
++			    has_option_mask(ifo->requestmask6, opt->option)))
++			{
++				n_options++;
++				len += sizeof(*u16);
++			}
++		}
++		if (dhcp6_findselfsla(ifp, NULL)) {
++			n_options++;
++			len += sizeof(*u16);
++		}
+ 		if (len)
+ 			len += sizeof(*o);
+ 
+@@ -405,25 +527,43 @@
+ 	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) +
+-				    sizeof(u32) + sizeof(u32) +
+-				    sizeof(ap->prefix.s6_addr);
+-			else
++			if (ap->ia_type == D6_OPTION_IA_PD) {
++				if (!(ifo->options & DHCPCD_NOPFXDLG)) {
++					len += sizeof(*o) + sizeof(u8) +
++					    sizeof(u32) + sizeof(u32) +
++					    sizeof(ap->prefix.s6_addr);
++					sla = dhcp6_findselfsla(ifp, ap->iaid);
++					if (sla)
++						len += sizeof(*o) + 1 +
++						    ((sla->prefix_len -
++						    ap->prefix_len - 1) / NBBY)
++						    + 1;
++
++				}
++			} else if (!(ifo->options & DHCPCD_PFXDLGONLY))
+ 				len += sizeof(*o) + sizeof(ap->addr.s6_addr) +
+ 				    sizeof(u32) + sizeof(u32);
+ 		}
+ 		/* FALLTHROUGH */
+-	case DH6S_INIT: /* FALLTHROUGH */
+-	case DH6S_DISCOVER:
+-		len += ifo->ia_len * (sizeof(*o) + (sizeof(u32) * 3));
++	case DH6S_INIT:
++		for (l = 0; l < ifo->ia_len; l++) {
++			if (ifo->ia[l].ia_type == D6_OPTION_IA_PD) {
++				if (ifo->options & DHCPCD_NOPFXDLG)
++					continue;
++			} else if (ifo->options & DHCPCD_PFXDLGONLY)
++				continue;
++			len += sizeof(*o) + (sizeof(u32) * 3);
++		}
+ 		IA = 1;
+ 		break;
+ 	default:
+@@ -528,20 +668,26 @@
+ 	}
+ 
+ 	for (l = 0; IA && l < ifo->ia_len; l++) {
++		if (ifo->ia[l].ia_type == D6_OPTION_IA_PD) {
++			if (ifo->options & DHCPCD_NOPFXDLG)
++				continue;
++		} else if (ifo->options & DHCPCD_PFXDLGONLY)
++			continue;
+ 		o = D6_NEXT_OPTION(o);
+-		o->code = htons(ifo->ia_type);
++		o->code = htons(ifo->ia[l].ia_type);
+ 		o->len = htons(sizeof(u32) + sizeof(u32) + sizeof(u32));
+ 		p = D6_OPTION_DATA(o);
+ 		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)
++			if (ap->prefix_vltime == 0 &&
++			    !(ap->flags & IPV6_AF_REQUEST))
+ 				continue;
+ 			if (memcmp(ifo->ia[l].iaid, ap->iaid, sizeof(u32)))
+ 				continue;
+ 			so = D6_NEXT_OPTION(o);
+-			if (ifo->ia_type == D6_OPTION_IA_PD) {
++			if (ap->ia_type == D6_OPTION_IA_PD) {
+ 				so->code = htons(D6_OPTION_IAPREFIX);
+ 				so->len = htons(sizeof(ap->prefix.s6_addr) +
+ 				    sizeof(u32) + sizeof(u32) + sizeof(u8));
+@@ -557,16 +703,45 @@
+ 				p += sizeof(u8);
+ 				memcpy(p, &ap->prefix.s6_addr,
+ 				    sizeof(ap->prefix.s6_addr));
+-				/* Avoid a shadowed declaration warning by
+-				 * moving our addition outside of the htons
+-				 * macro */
++
++				/* RFC6603 Sectio 4.2 */
++				sla = dhcp6_findselfsla(ifp, ap->iaid);
++				if (sla &&
++				    dhcp6_delegateaddr(&addr, ifp, ap, sla) ==0)
++				{
++					uint16_t el;
++					uint8_t *pp;
++
++					el = ((sla->prefix_len -
++					    ap->prefix_len - 1) / NBBY) + 1;
++					eo = D6_NEXT_OPTION(so);
++					eo->code = htons(D6_OPTION_PD_EXCLUDE);
++					eo->len = el + 1;
++					p = D6_OPTION_DATA(eo);
++					*p++ = (uint8_t)sla->prefix_len;
++					pp = addr.s6_addr;
++					pp += (ap->prefix_len - 1) / NBBY;
++					u8 = ap->prefix_len % NBBY;
++					if (u8 % NBBY == 0)
++						pp++;
++					else {
++						*p = (uint8_t)(*pp++ << u8);
++						el--;
++					}
++					memcpy(p, pp, el);
++					u32 = ntohs(so->len) +
++					    sizeof(*eo) + eo->len;
++					so->len = htons(u32);
++					eo->len = htons(eo->len);
++				}
++
+ 				u32 = ntohs(o->len) + sizeof(*so)
+ 				    + ntohs(so->len);
+ 				o->len = htons(u32);
+ 			} else {
+ 				so->code = htons(D6_OPTION_IA_ADDR);
+-				so->len = htons(sizeof(ap->addr.s6_addr) +
+-				    sizeof(u32) + sizeof(u32));
++				so->len = sizeof(ap->addr.s6_addr) +
++				    sizeof(u32) + sizeof(u32);
+ 				p = D6_OPTION_DATA(so);
+ 				memcpy(p, &ap->addr.s6_addr,
+ 				    sizeof(ap->addr.s6_addr));
+@@ -576,11 +751,9 @@
+ 				p += sizeof(u32);
+ 				u32 = htonl(ap->prefix_vltime);
+ 				memcpy(p, &u32, sizeof(u32));
+-				/* Avoid a shadowed declaration warning by
+-				 * moving our addition outside of the htons
+-				 * macro */
+ 				u32 = ntohs(o->len) + sizeof(*so)
+-				    + ntohs(so->len);
++				    + so->len;
++				so->len = htons(so->len);
+ 				o->len = htons(u32);
+ 			}
+ 		}
+@@ -625,6 +798,28 @@
+ 			    l < ifp->ctx->dhcp6_opts_len;
+ 			    l++, opt++)
+ 			{
++				for (n = 0, opt2 = ifo->dhcp6_override;
++				    n < ifo->dhcp6_override_len;
++				    n++, opt2++)
++				{
++					if (opt->option == opt2->option)
++						break;
++				}
++				if (n < ifo->dhcp6_override_len)
++				    continue;
++				if (!(opt->type & NOREQ) &&
++				    (opt->type & REQUEST ||
++				    has_option_mask(ifo->requestmask6,
++				        opt->option)))
++				{
++					*u16++ = htons(opt->option);
++					o->len += sizeof(*u16);
++				}
++			}
++			for (l = 0, opt = ifo->dhcp6_override;
++			    l < ifo->dhcp6_override_len;
++			    l++, opt++)
++			{
+ 				if (!(opt->type & NOREQ) &&
+ 				    (opt->type & REQUEST ||
+ 				    has_option_mask(ifo->requestmask6,
+@@ -634,6 +829,10 @@
+ 					o->len += sizeof(*u16);
+ 				}
+ 			}
++			if (dhcp6_findselfsla(ifp, NULL)) {
++				*u16++ = htons(D6_OPTION_PD_EXCLUDE);
++				o->len += sizeof(*u16);
++			}
+ 			o->len = htons(o->len);
+ 		}
+ 	}
+@@ -949,6 +1148,99 @@
+ }
+ 
+ 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_addrequestedaddrs(struct interface *ifp)
++{
++	struct dhcp6_state *state;
++	size_t i;
++	struct if_ia *ia;
++	struct ipv6_addr *a;
++	char iabuf[INET6_ADDRSTRLEN];
++	const char *iap;
++
++	state = D6_STATE(ifp);
++	/* Add any requested prefixes / addresses */
++	for (i = 0; i < ifp->options->ia_len; i++) {
++		ia = &ifp->options->ia[i];
++		if (!((ia->ia_type == D6_OPTION_IA_PD && ia->prefix_len) ||
++		    !IN6_IS_ADDR_UNSPECIFIED(&ia->addr)))
++			continue;
++		a = calloc(1, sizeof(*a));
++		if (a == NULL) {
++			syslog(LOG_ERR, "%s: %m", __func__);
++			return;
++		}
++		a->flags = IPV6_AF_REQUEST;
++		a->iface = ifp;
++		a->dadcallback = dhcp6_dadcallback;
++		memcpy(&a->iaid, &ia->iaid, sizeof(a->iaid));
++		a->ia_type = ia->ia_type;
++		//a->prefix_pltime = 0;
++		//a->prefix_vltime = 0;
++
++		if (ia->ia_type == D6_OPTION_IA_PD) {
++			memcpy(&a->prefix, &ia->addr, sizeof(a->addr));
++			a->prefix_len = ia->prefix_len;
++			iap = inet_ntop(AF_INET6, &a->prefix.s6_addr,
++			    iabuf, sizeof(iabuf));
++		} else {
++			memcpy(&a->addr, &ia->addr, sizeof(a->addr));
++			/*
++			 * RFC 5942 Section 5
++			 * We cannot assume any prefix length, nor tie the
++			 * address to an existing one as it could expire
++			 * before the address.
++			 * As such we just give it a 128 prefix.
++			 */
++			a->prefix_len = 128;
++			ipv6_makeprefix(&a->prefix, &a->addr, a->prefix_len);
++			iap = inet_ntop(AF_INET6, &a->addr.s6_addr,
++			    iabuf, sizeof(iabuf));
++		}
++		snprintf(a->saddr, sizeof(a->saddr),
++		    "%s/%d", iap, a->prefix_len);
++		TAILQ_INSERT_TAIL(&state->addrs, a, next);
++	}
++}
++
++static void
+ dhcp6_startdiscover(void *arg)
+ {
+ 	struct interface *ifp;
+@@ -974,6 +1266,8 @@
+ 	dhcp6_freedrop_addrs(ifp, 0, NULL);
+ 	unlink(state->leasefile);
+ 
++	dhcp6_addrequestedaddrs(ifp);
++
+ 	if (dhcp6_makemessage(ifp) == -1)
+ 		syslog(LOG_ERR, "%s: dhcp6_makemessage: %m", ifp->name);
+ 	else
+@@ -1021,11 +1315,25 @@
+ 	dhcp6_startdiscover(ifp);
+ }
+ 
++
++static int
++dhcp6_hasprefixdelegation(struct interface *ifp)
++{
++	size_t i;
++
++	for (i = 0; i < ifp->options->ia_len; i++) {
++		if (ifp->options->ia[i].ia_type == D6_OPTION_IA_PD)
++			return 1;
++	}
++	return 0;
++}
++
+ static void
+ dhcp6_startrebind(void *arg)
+ {
+ 	struct interface *ifp;
+ 	struct dhcp6_state *state;
++	int pd;
+ 
+ 	ifp = arg;
+ 	eloop_timeout_delete(ifp->ctx->eloop, dhcp6_sendrenew, ifp);
+@@ -1033,13 +1341,16 @@
+ 	if (state->state == DH6S_RENEW)
+ 		syslog(LOG_WARNING, "%s: failed to renew DHCPv6, rebinding",
+ 		    ifp->name);
++	else
++		syslog(LOG_INFO, "%s: rebinding prior DHCPc6 lease",
++		    ifp->name);
+ 	state->state = DH6S_REBIND;
+ 	state->RTC = 0;
+ 	state->MRC = 0;
+ 
+ 	/* RFC 3633 section 12.1 */
+-	if (ifp->options->ia_type == D6_OPTION_IA_PD) {
+-		syslog(LOG_INFO, "%s: confirming Prefix Delegation", ifp->name);
++	pd = dhcp6_hasprefixdelegation(ifp);
++	if (pd) {
+ 		state->IMD = CNF_MAX_DELAY;
+ 		state->IRT = CNF_TIMEOUT;
+ 		state->MRT = CNF_MAX_RT;
+@@ -1054,7 +1365,7 @@
+ 		dhcp6_sendrebind(ifp);
+ 
+ 	/* RFC 3633 section 12.1 */
+-	if (ifp->options->ia_type == D6_OPTION_IA_PD)
++	if (pd)
+ 		eloop_timeout_add_sec(ifp->ctx->eloop,
+ 		    CNF_MAX_RD, dhcp6_failrebind, ifp);
+ }
+@@ -1264,46 +1575,8 @@
+ 	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,
++dhcp6_findna(struct interface *ifp, uint16_t ot, const uint8_t *iaid,
+     const uint8_t *d, size_t l)
+ {
+ 	struct dhcp6_state *state;
+@@ -1345,6 +1618,7 @@
+ 			a->iface = ifp;
+ 			a->flags = IPV6_AF_NEW | IPV6_AF_ONLINK;
+ 			a->dadcallback = dhcp6_dadcallback;
++			a->ia_type = ot;
+ 			memcpy(a->iaid, iaid, sizeof(a->iaid));
+ 			memcpy(&a->addr.s6_addr, &in6.s6_addr,
+ 			    sizeof(in6.s6_addr));
+@@ -1357,20 +1631,18 @@
+ 			 * As such we just give it a 128 prefix.
+ 			 */
+ 			a->prefix_len = 128;
+-			if (ipv6_makeprefix(&a->prefix, &a->addr,
+-			    a->prefix_len) == -1)
+-			{
+-				syslog(LOG_ERR, "%s: %m", __func__);
+-				free(a);
+-				continue;
+-			}
++			ipv6_makeprefix(&a->prefix, &a->addr, a->prefix_len);
+ 			ia = inet_ntop(AF_INET6, &a->addr.s6_addr,
+ 			    iabuf, sizeof(iabuf));
+ 			snprintf(a->saddr, sizeof(a->saddr),
+ 			    "%s/%d", ia, a->prefix_len);
++
+ 			TAILQ_INSERT_TAIL(&state->addrs, a, next);
+-		} else
++		} else {
++			if (!(a->flags & IPV6_AF_ONLINK))
++				a->flags |= IPV6_AF_ONLINK | IPV6_AF_NEW;
+ 			a->flags &= ~IPV6_AF_STALE;
++		}
+ 		memcpy(&u32, p, sizeof(u32));
+ 		a->prefix_pltime = ntohl(u32);
+ 		p += sizeof(u32);
+@@ -1394,8 +1666,8 @@
+     const uint8_t *d, size_t l)
+ {
+ 	struct dhcp6_state *state;
+-	const struct dhcp6_option *o;
+-	const uint8_t *p;
++	const struct dhcp6_option *o, *ex;
++	const uint8_t *p, *ps, *pe, *op;
+ 	struct ipv6_addr *a;
+ 	char iabuf[INET6_ADDRSTRLEN];
+ 	const char *ia;
+@@ -1420,7 +1692,8 @@
+ 			    ifp->name);
+ 			continue;
+ 		}
+-		p = D6_COPTION_DATA(o);
++		ps = p = D6_COPTION_DATA(o);
++		pe = ps + u32;
+ 		memcpy(&u32, p, sizeof(u32));
+ 		pltime = ntohl(u32);
+ 		p += sizeof(u32);
+@@ -1431,6 +1704,7 @@
+ 		p += sizeof(u8);
+ 		len = u8;
+ 		memcpy(&prefix.s6_addr, p, sizeof(prefix.s6_addr));
++		p += sizeof(prefix.s6_addr);
+ 
+ 		TAILQ_FOREACH(a, &state->addrs, next) {
+ 			if (IN6_ARE_ADDR_EQUAL(&a->prefix, &prefix))
+@@ -1445,6 +1719,7 @@
+ 			a->iface = ifp;
+ 			a->flags = IPV6_AF_NEW | IPV6_AF_DELEGATEDPFX;
+ 			a->dadcallback = dhcp6_dadcallback;
++			a->ia_type = D6_OPTION_IA_PD;
+ 			memcpy(a->iaid, iaid, sizeof(a->iaid));
+ 			memcpy(&a->prefix.s6_addr,
+ 			    &prefix.s6_addr, sizeof(a->prefix.s6_addr));
+@@ -1455,24 +1730,58 @@
+ 			    "%s/%d", ia, a->prefix_len);
+ 			TAILQ_INSERT_TAIL(&state->addrs, a, next);
+ 		} else {
+-			a->flags &= ~IPV6_AF_STALE;
++			if (!(a->flags & IPV6_AF_DELEGATEDPFX))
++				a->flags |= IPV6_AF_NEW | IPV6_AF_DELEGATEDPFX;
++			a->flags &= ~(IPV6_AF_STALE | IPV6_AF_REQUEST);
+ 			if (a->prefix_vltime != vltime)
+ 				a->flags |= IPV6_AF_NEW;
+ 		}
+ 
+ 		a->prefix_pltime = pltime;
+ 		a->prefix_vltime = vltime;
++
+ 		if (a->prefix_pltime && a->prefix_pltime < state->lowpl)
+ 			state->lowpl = a->prefix_pltime;
+ 		if (a->prefix_vltime && a->prefix_vltime > state->expire)
+ 			state->expire = a->prefix_vltime;
+ 		i++;
++
++		off = (size_t)(pe - p);
++		ex = dhcp6_findoption(D6_OPTION_PD_EXCLUDE, p, off);
++
++		if (ex) {
++			off = ntohs(ex->len);
++			if (off < 2) {
++				syslog(LOG_ERR, "%s: truncated PD Exclude",
++				    ifp->name);
++				ex = NULL;
++			}
++		}
++		if (ex) {
++			int bytelen, bitlen;
++
++			op = D6_COPTION_DATA(ex);
++			a->prefix_exclude_len = *op++;
++			memcpy(&a->prefix_exclude, &a->prefix,
++			    sizeof(a->prefix_exclude));
++			bytelen = a->prefix_len / NBBY;
++			bitlen = a->prefix_len % NBBY;
++			if (bitlen != 0)
++				a->prefix_exclude.s6_addr[bytelen] |=
++				    *op++ >> bitlen;
++			memcpy(a->prefix_exclude.s6_addr + bytelen + 1,
++			    op, off - 2);
++		} else {
++			a->prefix_exclude_len = 0;
++			memset(&a->prefix_exclude, 0,
++			    sizeof(a->prefix_exclude));
++		}
+ 	}
+ 	return i;
+ }
+ 
+ static int
+-dhcp6_findia(struct interface *ifp, const uint8_t *d, size_t l,
++dhcp6_findia(struct interface *ifp, const struct dhcp6_message *m, size_t l,
+     const char *sfrom)
+ {
+ 	struct dhcp6_state *state;
+@@ -1480,25 +1789,47 @@
+ 	const struct dhcp6_option *o;
+ 	const uint8_t *p;
+ 	int i, e;
++	size_t j;
+ 	uint32_t u32, renew, rebind;
++	uint16_t code, ol;
+ 	uint8_t iaid[4];
+-	size_t ol;
++	char buf[sizeof(iaid) * 3];
+ 	struct ipv6_addr *ap, *nap;
+ 
++	if (l < sizeof(*m)) {
++		syslog(LOG_ERR, "%s: message truncated", ifp->name);
++		errno = EINVAL;
++		return -1;
++	}
++
+ 	ifo = ifp->options;
+ 	i = e = 0;
+ 	state = D6_STATE(ifp);
+ 	TAILQ_FOREACH(ap, &state->addrs, next) {
+ 		ap->flags |= IPV6_AF_STALE;
+ 	}
+-	while ((o = dhcp6_findoption(ifo->ia_type, d, l))) {
+-		ol = (size_t)((const uint8_t *)o - d);
+-		l -= ol;
+-		d += ol;
++	l -= sizeof(*m);
++	for (o = D6_FIRST_OPTION(m); l > sizeof(*o); o = D6_NEXT_OPTION(o)) {
+ 		ol = ntohs(o->len);
++		if (sizeof(*o) + ol > l) {
++			errno = EINVAL;
++			syslog(LOG_ERR, "%s: option overflow", ifp->name);
++			break;
++		}
+ 		l -= sizeof(*o) + ol;
+-		d += sizeof(*o) + ol;
+-		u32 = ifo->ia_type == D6_OPTION_IA_TA ? 4 : 12;
++
++		code = ntohs(o->code);
++		switch(code) {
++		case D6_OPTION_IA_TA:
++			u32 = 4;
++			break;
++		case D6_OPTION_IA_NA:
++		case D6_OPTION_IA_PD:
++			u32 = 12;
++			break;
++		default:
++			continue;
++		}
+ 		if (ol < u32) {
+ 			errno = EINVAL;
+ 			syslog(LOG_ERR, "%s: IA option truncated", ifp->name);
+@@ -1509,7 +1840,25 @@
+ 		memcpy(iaid, p, sizeof(iaid));
+ 		p += sizeof(iaid);
+ 		ol -= sizeof(iaid);
+-		if (ifo->ia_type != D6_OPTION_IA_TA) {
++
++		for (j = 0; j < ifo->ia_len; j++) {
++			if (memcmp(&ifo->ia[j].iaid, iaid, sizeof(iaid)) == 0)
++				break;
++		}
++		if (j == ifo->ia_len) {
++			syslog(LOG_DEBUG, "%s: ignoring unrequested IAID %s",
++			    ifp->name,
++			    hwaddr_ntoa(iaid, sizeof(iaid), buf, sizeof(buf)));
++			continue;
++		}
++		if (ifo->ia[j].ia_type != code) {
++			syslog(LOG_ERR, "%s: IAID %s: option type mismatch",
++			    ifp->name,
++			    hwaddr_ntoa(iaid, sizeof(iaid), buf, sizeof(buf)));
++			continue;
++		}
++
++		if (code != D6_OPTION_IA_TA) {
+ 			memcpy(&u32, p, sizeof(u32));
+ 			renew = ntohl(u32);
+ 			p += sizeof(u32);
+@@ -1524,22 +1873,24 @@
+ 			e = 1;
+ 			continue;
+ 		}
+-		if (ifo->ia_type == D6_OPTION_IA_PD) {
+-			if (dhcp6_findpd(ifp, iaid, p, ol) == 0) {
++		if (code == D6_OPTION_IA_PD) {
++			if (!(ifo->options & DHCPCD_NOPFXDLG) &&
++			    dhcp6_findpd(ifp, iaid, p, ol) == 0)
++			{
+ 				syslog(LOG_WARNING,
+ 				    "%s: %s: DHCPv6 REPLY missing Prefix",
+ 				    ifp->name, sfrom);
+ 				continue;
+ 			}
+-		} else {
+-			if (dhcp6_findna(ifp, iaid, p, ol) == 0) {
++		} else if (!(ifo->options & DHCPCD_PFXDLGONLY)) {
++			if (dhcp6_findna(ifp, code, iaid, p, ol) == 0) {
+ 				syslog(LOG_WARNING,
+ 				    "%s: %s: DHCPv6 REPLY missing IA Address",
+ 				    ifp->name, sfrom);
+ 				continue;
+ 			}
+ 		}
+-		if (ifo->ia_type != D6_OPTION_IA_TA) {
++		if (code != D6_OPTION_IA_TA) {
+ 			if (renew > rebind && rebind > 0) {
+ 				if (sfrom)
+ 				    syslog(LOG_WARNING,
+@@ -1559,11 +1910,15 @@
+ 	}
+ 	TAILQ_FOREACH_SAFE(ap, &state->addrs, next, nap) {
+ 		if (ap->flags & IPV6_AF_STALE) {
+-			TAILQ_REMOVE(&state->addrs, ap, next);
+ 			if (ap->dadcallback)
+ 				eloop_q_timeout_delete(ap->iface->ctx->eloop,
+ 				    0, NULL, ap);
+-			free(ap);
++			if (ap->flags & IPV6_AF_REQUEST) {
++				ap->prefix_vltime = ap->prefix_pltime = 0;
++			} else {
++				TAILQ_REMOVE(&state->addrs, ap, next);
++				free(ap);
++			}
+ 		}
+ 	}
+ 	if (i == 0 && e)
+@@ -1577,25 +1932,14 @@
+     const char *sfrom)
+ {
+ 	struct dhcp6_state *state;
+-	const struct dhcp6_option *o;
+ 
+ 	state = D6_STATE(ifp);
+-	o = dhcp6_getmoption(ifp->options->ia_type, m, len);
+-	if (o == NULL) {
+-		if (sfrom &&
+-		    dhcp6_checkstatusok(ifp, m, NULL, len) != -1)
+-			syslog(LOG_ERR, "%s: no IA in REPLY from %s",
+-			    ifp->name, sfrom);
+-		return -1;
+-	}
+-
+ 	if (dhcp6_checkstatusok(ifp, m, NULL, len) == -1)
+ 		return -1;
+ 
+ 	state->renew = state->rebind = state->expire = 0;
+ 	state->lowpl = ND6_INFINITE_LIFETIME;
+-	len -= (size_t)((const char *)o - (const char *)m);
+-	return dhcp6_findia(ifp, (const uint8_t *)o, len, sfrom);
++	return dhcp6_findia(ifp, m, len, sfrom);
+ }
+ 
+ static ssize_t
+@@ -1633,6 +1977,7 @@
+ 	if (stat(state->leasefile, &st) == -1) {
+ 		if (errno == ENOENT)
+ 			return 0;
++		syslog(LOG_ERR, "%s: %s: %m", ifp->name, __func__);
+ 		return -1;
+ 	}
+ 	syslog(LOG_DEBUG, "%s: reading lease `%s'",
+@@ -1642,12 +1987,17 @@
+ 		return -1;
+ 	}
+ 	state->new = malloc((size_t)st.st_size);
+-	if (state->new == NULL)
++	if (state->new == NULL) {
++		syslog(LOG_ERR, "%s: %m", __func__);
+ 		return -1;
++	}
+ 	state->new_len = (size_t)st.st_size;
+ 	fd = open(state->leasefile, O_RDONLY);
+-	if (fd == -1)
++	if (fd == -1) {
++		syslog(LOG_ERR, "%s: %s: %s: %m", ifp->name, __func__,
++		    state->leasefile);
+ 		return -1;
++	}
+ 	bytes = read(fd, state->new, state->new_len);
+ 	close(fd);
+ 	if (bytes < (ssize_t)state->new_len) {
+@@ -1660,7 +2010,7 @@
+ 	if (fd == -1)
+ 		goto ex;
+ 	if (fd == 0) {
+-		syslog(LOG_INFO, "%s: lease was for different IA type",
++		syslog(LOG_INFO, "%s: no useable IA found in lease",
+ 		    ifp->name);
+ 		goto ex;
+ 	}
+@@ -1705,22 +2055,39 @@
+ 	free(state->new);
+ 	state->new = NULL;
+ 	state->new_len = 0;
+-	unlink(state->leasefile);
++	if (!(ifp->ctx->options & DHCPCD_DUMPLEASE))
++		unlink(state->leasefile);
+ 	return 0;
+ }
+ 
++
+ static void
+ dhcp6_startinit(struct interface *ifp)
+ {
+ 	struct dhcp6_state *state;
+ 	int r;
++	uint8_t has_ta, has_non_ta;
++	size_t i;
+ 
+ 	state = D6_STATE(ifp);
+ 	state->state = DH6S_INIT;
+ 	state->expire = ND6_INFINITE_LIFETIME;
+ 	state->lowpl = ND6_INFINITE_LIFETIME;
++
++	dhcp6_addrequestedaddrs(ifp);
++	has_ta = has_non_ta = 0;
++	for (i = 0; i < ifp->options->ia_len; i++) {
++		switch (ifp->options->ia[i].ia_type) {
++		case D6_OPTION_IA_TA:
++			has_ta = 1;
++			break;
++		default:
++			has_non_ta = 1;
++		}
++	}
++
+ 	if (!(ifp->ctx->options & DHCPCD_TEST) &&
+-	    ifp->options->ia_type != D6_OPTION_IA_TA &&
++	    !(has_ta && !has_non_ta) &&
+ 	    ifp->options->reboot != 0)
+ 	{
+ 		r = dhcp6_readlease(ifp);
+@@ -1729,7 +2096,7 @@
+ 					ifp->name, state->leasefile);
+ 		else if (r != 0) {
+ 			/* RFC 3633 section 12.1 */
+-			if (ifp->options->ia_type == D6_OPTION_IA_PD)
++			if (dhcp6_hasprefixdelegation(ifp))
+ 				dhcp6_startrebind(ifp);
+ 			else
+ 				dhcp6_startconfirm(ifp);
+@@ -1740,69 +2107,28 @@
+ }
+ 
+ static struct ipv6_addr *
+-dhcp6_delegate_addr(struct interface *ifp, struct ipv6_addr *prefix,
++dhcp6_ifdelegateaddr(struct interface *ifp, struct ipv6_addr *prefix,
+     const struct if_sla *sla, struct interface *ifs)
+ {
+ 	struct dhcp6_state *state;
+-	struct if_sla asla;
+ 	struct in6_addr addr;
+ 	struct ipv6_addr *a, *ap, *apn;
+ 	char iabuf[INET6_ADDRSTRLEN];
+ 	const char *ia;
+ 
+-	state = D6_STATE(ifp);
+-	if (state == NULL) {
+-		ifp->if_data[IF_DATA_DHCP6] = calloc(1, sizeof(*state));
+-		state = D6_STATE(ifp);
+-		if (state == NULL) {
+-			syslog(LOG_ERR, "%s: %m", __func__);
++	/* RFC6603 Section 4.2 */
++	if (ifp == ifs) {
++		if (prefix->prefix_exclude_len == 0) {
++			syslog(LOG_WARNING,
++			    "%s: DHCPv6 server does not support "
++			    "OPTION_PD_EXCLUDE",
++			    ifp->name);
+ 			return NULL;
+ 		}
+-
+-		TAILQ_INIT(&state->addrs);
+-		state->state = DH6S_DELEGATED;
+-		state->reason = "DELEGATED6";
+-	}
+-
+-	if (sla == NULL || sla->sla_set == 0) {
+-		struct interface *ifi;
+-		unsigned int idx;
+-		int bits;
+-
+-		asla.sla = ifp->index;
+-		/* Work out our largest index delegating to. */
+-		idx = 0;
+-		TAILQ_FOREACH(ifi, ifp->ctx->ifaces, next) {
+-			if (ifi != ifp && ifi->index > idx)
+-				idx = ifi->index;
+-		}
+-		bits = ffs((int)idx);
+-		if (prefix->prefix_len + bits > UINT8_MAX)
+-			asla.prefix_len = UINT8_MAX;
+-		else {
+-			asla.prefix_len = prefix->prefix_len + (uint8_t)bits;
+-
+-			/* Make a 64 prefix by default, as this maks SLAAC
+-			 * possible. Otherwise round up to the nearest octet. */
+-			if (asla.prefix_len <= 64)
+-				asla.prefix_len = 64;
+-			else
+-				asla.prefix_len = ROUNDUP8(asla.prefix_len);
+-
+-		}
+-		sla = &asla;
+-	}
+-
+-	if (ipv6_userprefix(&prefix->prefix, prefix->prefix_len,
+-		sla->sla, &addr, sla->prefix_len) == -1)
+-	{
+-		ia = inet_ntop(AF_INET6, &prefix->prefix.s6_addr,
+-		    iabuf, sizeof(iabuf));
+-		syslog(LOG_ERR, "%s: invalid prefix %s/%d + %d/%d: %m",
+-			ifp->name, ia, prefix->prefix_len,
+-			sla->sla, sla->prefix_len);
++		memcpy(&addr, &prefix->prefix_exclude, sizeof(addr));
++	} else if (dhcp6_delegateaddr(&addr, ifp, prefix, sla) == -1)
+ 		return NULL;
+-	}
++
+ 
+ 	a = calloc(1, sizeof(*a));
+ 	if (a == NULL) {
+@@ -1824,6 +2150,7 @@
+ 	memcpy(&a->addr.s6_addr, &a->prefix.s6_addr, sizeof(a->addr.s6_addr));
+ 	a->addr.s6_addr[sizeof(a->addr.s6_addr) - 1] += 1;
+ 
++	state = D6_STATE(ifp);
+ 	/* Remove any exiting address */
+ 	TAILQ_FOREACH_SAFE(ap, &state->addrs, next, apn) {
+ 		if (IN6_ARE_ADDR_EQUAL(&ap->addr, &a->addr)) {
+@@ -1916,7 +2243,7 @@
+ 						    abrt = 1;
+ 						break;
+ 					}
+-					if (dhcp6_delegate_addr(ifd, ap,
++					if (dhcp6_ifdelegateaddr(ifd, ap,
+ 					    NULL, ifp))
+ 						k++;
+ 				}
+@@ -1935,7 +2262,7 @@
+ 						carrier_warned = 1;
+ 						break;
+ 					}
+-					if (dhcp6_delegate_addr(ifd, ap,
++					if (dhcp6_ifdelegateaddr(ifd, ap,
+ 					    sla, ifp))
+ 						k++;
+ 				}
+@@ -1998,7 +2325,7 @@
+ 						    dhcp6_find_delegates1, ifp);
+ 						return 1;
+ 					}
+-					if (dhcp6_delegate_addr(ifp, ap,
++					if (dhcp6_ifdelegateaddr(ifp, ap,
+ 					    sla, ifd))
+ 					    k++;
+ 				}
+@@ -2225,7 +2552,7 @@
+ 				/* PD doesn't use CONFIRM, so REBIND could
+ 				 * throw up an invalid prefix if we
+ 				 * changed link */
+-				if (ifp->options->ia_type == D6_OPTION_IA_PD)
++				if (dhcp6_hasprefixdelegation(ifp))
+ 					dhcp6_startdiscover(ifp);
+ 				return;
+ 			}
+@@ -2437,10 +2764,10 @@
+ 		if (state->expire && state->expire != ND6_INFINITE_LIFETIME)
+ 			eloop_timeout_add_sec(ifp->ctx->eloop,
+ 			    (time_t)state->expire, dhcp6_startexpire, ifp);
+-		if (ifp->options->ia_type == D6_OPTION_IA_PD)
+-			dhcp6_delegate_prefix(ifp);
+ 
+ 		ipv6_addaddrs(&state->addrs);
++		dhcp6_delegate_prefix(ifp);
++
+ 		if (state->state == DH6S_INFORMED)
+ 			syslog(has_new ? LOG_INFO : LOG_DEBUG,
+ 			    "%s: refresh in %"PRIu32" seconds",
+@@ -2584,6 +2911,10 @@
+ 			add_option_mask(ifo->requestmask6, D6_OPTION_FQDN);
+ 	}
+ 
++	/* Rapid commit won't wor with Prefix Delegation Exclusion */
++	if (dhcp6_findselfsla(ifp, NULL))
++		del_option_mask(ifo->requestmask6, D6_OPTION_RAPID_COMMIT);
++
+ 	if (state->state == DH6S_INFORM) {
+ 		add_option_mask(ifo->requestmask6, D6_OPTION_INFO_REFRESH_TIME);
+ 		dhcp6_startinform(ifp);
+@@ -2635,7 +2966,8 @@
+ 
+ 	state->state = init_state;
+ 	snprintf(state->leasefile, sizeof(state->leasefile),
+-	    LEASEFILE6, ifp->name);
++	    LEASEFILE6, ifp->name,
++	    ifp->options->options & DHCPCD_PFXDLGONLY ? ".pd" : "");
+ 
+ 	if (ipv6_linklocal(ifp) == NULL) {
+ 		syslog(LOG_DEBUG,
+@@ -2790,18 +3122,21 @@
+ dhcp6_env(char **env, const char *prefix, const struct interface *ifp,
+     const struct dhcp6_message *m, size_t len)
+ {
+-	const struct dhcp6_state *state;
+ 	const struct if_options *ifo;
+ 	struct dhcp_opt *opt, *vo;
+ 	const struct dhcp6_option *o;
+-	size_t i, l, n;
++	size_t i, n;
+ 	uint16_t ol, oc;
+-	char *v, *val, *pfx;
+-	const struct ipv6_addr *ap;
++	char *pfx;
+ 	uint32_t en;
+ 	const struct dhcpcd_ctx *ctx;
+ 
+-	state = D6_CSTATE(ifp);
++	if (len < sizeof(*m)) {
++		syslog(LOG_ERR, "%s: message truncated", ifp->name);
++		errno = EINVAL;
++		return -1;
++	}
++
+ 	n = 0;
+ 	ifo = ifp->options;
+ 	ctx = ifp->ctx;
+@@ -2885,64 +3220,38 @@
+ 	}
+ 	free(pfx);
+ 
+-	/* It is tempting to remove this section.
+-	 * However, we need it at least for Delegated Prefixes
+-	 * (they don't have a DHCPv6 message to parse to get the addressses)
+-	 * and it's easier for shell scripts to see which addresses have
+-	 * been added */
+-	if (TAILQ_FIRST(&state->addrs)) {
+-		if (env) {
+-			if (ifo->ia_type == D6_OPTION_IA_PD) {
+-				l = strlen(prefix) +
+-				    strlen("_dhcp6_prefix=");
+-				TAILQ_FOREACH(ap, &state->addrs, next) {
+-					l += strlen(ap->saddr) + 1;
+-				}
+-				v = val = env[n] = malloc(l);
+-				if (v == NULL) {
+-					syslog(LOG_ERR, "%s: %m", __func__);
+-					return -1;
+-				}
+-				i = (size_t)snprintf(v, l, "%s_dhcp6_prefix=",
+-				    prefix);
+-				v += i;
+-				l -= i;
+-				TAILQ_FOREACH(ap, &state->addrs, next) {
+-					i = strlen(ap->saddr);
+-					strlcpy(v, ap->saddr, l);
+-					v += i;
+-					l -= i;
+-					*v++ = ' ';
+-				}
+-				*--v = '\0';
+-			} else {
+-				l = strlen(prefix) +
+-				    strlen("_dhcp6_ip_address=");
+-				TAILQ_FOREACH(ap, &state->addrs, next) {
+-					l += strlen(ap->saddr) + 1;
+-				}
+-				v = val = env[n] = malloc(l);
+-				if (v == NULL) {
+-					syslog(LOG_ERR, "%s: %m", __func__);
+-					return -1;
+-				}
+-				i = (size_t)snprintf(v, l,
+-				    "%s_dhcp6_ip_address=",
+-				    prefix);
+-				v += i;
+-				l -= i;
+-				TAILQ_FOREACH(ap, &state->addrs, next) {
+-					i = strlen(ap->saddr);
+-					strlcpy(v, ap->saddr, l);
+-					v += i;
+-					l -= i;
+-					*v++ = ' ';
+-				}
+-				*--v = '\0';
+-			}
+-		}
+-		n++;
++	return (ssize_t)n;
++}
++
++int
++dhcp6_dump(struct interface *ifp)
++{
++	struct dhcp6_state *state;
++	int r;
++
++	ifp->if_data[IF_DATA_DHCP6] = state = calloc(1, sizeof(*state));
++	if (state == NULL)
++		goto eexit;
++	TAILQ_INIT(&state->addrs);
++	snprintf(state->leasefile, sizeof(state->leasefile),
++	    LEASEFILE6, ifp->name,
++	    ifp->options->options & DHCPCD_PFXDLGONLY ? ".pd" : "");
++	r = dhcp6_readlease(ifp);
++	if (r == -1 && errno == ENOENT) {
++		strlcpy(state->leasefile, ifp->name, sizeof(state->leasefile));
++		r = dhcp6_readlease(ifp);
+ 	}
++	if (r == -1) {
++		if (errno == ENOENT)
++			syslog(LOG_ERR, "%s: no lease to dump", ifp->name);
++		else
++			syslog(LOG_ERR, "%s: dhcp6_readlease: %m", ifp->name);
++		return -1;
++	}
++	state->reason = "DUMP6";
++	return script_runreason(ifp, state->reason);
+ 
+-	return (ssize_t)n;
++eexit:
++	syslog(LOG_ERR, "%s: %m", __func__);
++	return -1;
+ }
+diff -urN dhcpcd-6.4.0.org/dhcp6.h dhcpcd-6.4.0/dhcp6.h
+--- dhcpcd-6.4.0.org/dhcp6.h	2014-06-14 22:13:12.000000000 +0200
++++ dhcpcd-6.4.0/dhcp6.h	2014-07-05 21:47:22.000000000 +0200
+@@ -89,6 +89,7 @@
+ #define D6_OPTION_FQDN			39
+ #define D6_OPTION_POSIX_TIMEZONE	41
+ #define D6_OPTION_TZDB_TIMEZONE		42
++#define D6_OPTION_PD_EXCLUDE		67
+ #define D6_OPTION_SOL_MAX_RT		82
+ #define D6_OPTION_INF_MAX_RT		83
+ 
+@@ -225,7 +226,8 @@
+     ((const uint8_t *)(o) + sizeof(struct dhcp6_option))
+ 
+ #ifdef INET6
+-void dhcp6_printoptions(const struct dhcpcd_ctx *);
++void dhcp6_printoptions(const struct dhcpcd_ctx *,
++    const struct dhcp_opt *, size_t);
+ int dhcp6_addrexists(struct dhcpcd_ctx *, const struct ipv6_addr *);
+ size_t dhcp6_find_delegates(struct interface *);
+ int dhcp6_start(struct interface *, enum DH6S);
+@@ -236,15 +238,16 @@
+ void dhcp6_handleifa(struct dhcpcd_ctx *, int, const char *,
+     const struct in6_addr *addr, int);
+ void dhcp6_drop(struct interface *, const char *);
++int dhcp6_dump(struct interface *);
+ #else
+-#define dhcp6_printoptions()
+ #define dhcp6_addrexists(a, b) (0)
+-#define dhcp6_find_delegates(a) (0)
++#define dhcp6_find_delegates(a)
+ #define dhcp6_start(a, b) (0)
+ #define dhcp6_reboot(a)
+ #define dhcp6_env(a, b, c, d, e)
+ #define dhcp6_free(a)
+ #define dhcp6_drop(a, b)
++#define dhcp6_dump(a) -1
+ #endif
+ 
+ #endif
+diff -urN dhcpcd-6.4.0.org/dhcp.c dhcpcd-6.4.0/dhcp.c
+--- dhcpcd-6.4.0.org/dhcp.c	2014-06-14 22:13:12.000000000 +0200
++++ dhcpcd-6.4.0/dhcp.c	2014-07-05 21:47:22.000000000 +0200
+@@ -125,16 +125,24 @@
+ static int dhcp_open(struct interface *);
+ 
+ void
+-dhcp_printoptions(const struct dhcpcd_ctx *ctx)
++dhcp_printoptions(const struct dhcpcd_ctx *ctx,
++    const struct dhcp_opt *opts, size_t opts_len)
+ {
+ 	const char * const *p;
+-	size_t i;
+-	const struct dhcp_opt *opt;
++	size_t i, j;
++	const struct dhcp_opt *opt, *opt2;
+ 
+ 	for (p = dhcp_params; *p; p++)
+ 		printf("    %s\n", *p);
+ 
+-	for (i = 0, opt = ctx->dhcp_opts; i < ctx->dhcp_opts_len; i++, opt++)
++	for (i = 0, opt = ctx->dhcp_opts; i < ctx->dhcp_opts_len; i++, opt++) {
++		for (j = 0, opt2 = opts; j < opts_len; j++, opt2++)
++			if (opt->option == opt2->option)
++				break;
++		if (j == opts_len)
++			printf("%03d %s\n", opt->option, opt->var);
++	}
++	for (i = 0, opt = opts; i < opts_len; i++, opt++)
+ 		printf("%03d %s\n", opt->option, opt->var);
+ }
+ 
+@@ -286,7 +294,7 @@
+ 			errno = EINVAL;
+ 			return -1;
+ 		}
+-		ocets = (cidr + 7) / 8;
++		ocets = (cidr + 7) / NBBY;
+ 		if (!out) {
+ 			p += 4 + ocets;
+ 			bytes += ((4 * 4) * 2) + 4;
+@@ -357,7 +365,7 @@
+ 		}
+ 		TAILQ_INSERT_TAIL(routes, rt, next);
+ 
+-		ocets = (cidr + 7) / 8;
++		ocets = (cidr + 7) / NBBY;
+ 		/* If we have ocets then we have a destination and netmask */
+ 		if (ocets > 0) {
+ 			memcpy(&rt->dest.s_addr, p, ocets);
+@@ -943,6 +951,30 @@
+ 				goto toobig;
+ 			*p++ = (uint8_t)opt->option;
+ 		}
++		for (i = 0, opt = ifo->dhcp_override;
++		    i < ifo->dhcp_override_len;
++		    i++, opt++)
++		{
++			/* Check if added above */
++			for (lp = n_params + 1; lp < p; lp++)
++				if (*lp == (uint8_t)opt->option)
++					break;
++			if (lp < p)
++				continue;
++			if (!(opt->type & REQUEST ||
++				has_option_mask(ifo->requestmask, opt->option)))
++				continue;
++			if (opt->type & NOREQ)
++				continue;
++			if (type == DHCP_INFORM &&
++			    (opt->option == DHO_RENEWALTIME ||
++				opt->option == DHO_REBINDTIME))
++				continue;
++			len = (size_t)((p - m) + 2);
++			if (len > sizeof(*dhcp))
++				goto toobig;
++			*p++ = (uint8_t)opt->option;
++		}
+ 		*n_params = (uint8_t)(p - n_params - 1);
+ 	}
+ 
+@@ -2733,40 +2765,23 @@
+ }
+ 
+ int
+-dhcp_dump(struct dhcpcd_ctx *ctx, const char *ifname)
++dhcp_dump(struct interface *ifp)
+ {
+-	struct interface *ifp;
+ 	struct dhcp_state *state;
+ 
+-	if (ctx->ifaces == NULL) {
+-		ctx->ifaces = malloc(sizeof(*ctx->ifaces));
+-		if (ctx->ifaces == NULL)
+-			return -1;
+-		TAILQ_INIT(ctx->ifaces);
+-	}
+-	state = NULL;
+-	ifp = calloc(1, sizeof(*ifp));
+-	if (ifp == NULL)
+-		goto eexit;
+-	ifp->ctx = ctx;
+-	TAILQ_INSERT_HEAD(ctx->ifaces, ifp, next);
+ 	ifp->if_data[IF_DATA_DHCP] = state = calloc(1, sizeof(*state));
+ 	if (state == NULL)
+ 		goto eexit;
+-	ifp->options = calloc(1, sizeof(*ifp->options));
+-	if (ifp->options == NULL)
+-		goto eexit;
+-	strlcpy(ifp->name, ifname, sizeof(ifp->name));
+ 	snprintf(state->leasefile, sizeof(state->leasefile),
+ 	    LEASEFILE, ifp->name);
+ 	state->new = read_lease(ifp);
+ 	if (state->new == NULL && errno == ENOENT) {
+-		strlcpy(state->leasefile, ifname, sizeof(state->leasefile));
++		strlcpy(state->leasefile, ifp->name, sizeof(state->leasefile));
+ 		state->new = read_lease(ifp);
+ 	}
+ 	if (state->new == NULL) {
+ 		if (errno == ENOENT)
+-			syslog(LOG_ERR, "%s: no lease to dump", ifname);
++			syslog(LOG_ERR, "%s: no lease to dump", ifp->name);
+ 		return -1;
+ 	}
+ 	state->reason = "DUMP";
+@@ -3015,13 +3030,22 @@
+ 	if (!(ifp->options->options & DHCPCD_IPV4))
+ 		return;
+ 
+-	tv.tv_sec = DHCP_MIN_DELAY;
+-	tv.tv_usec = (suseconds_t)arc4random_uniform(
+-	    (DHCP_MAX_DELAY - DHCP_MIN_DELAY) * 1000000);
+-	timernorm(&tv);
+-	syslog(LOG_DEBUG,
+-	    "%s: delaying DHCP for %0.1f seconds",
+-	    ifp->name, timeval_to_double(&tv));
++	/* No point in delaying a static configuration */
++	if (ifp->options->options & DHCPCD_STATIC &&
++	    !(ifp->options->options & DHCPCD_INFORM))
++	{
++		tv.tv_sec = 0;
++		tv.tv_usec = 0;
++	} else {
++		tv.tv_sec = DHCP_MIN_DELAY;
++		tv.tv_usec = (suseconds_t)arc4random_uniform(
++		    (DHCP_MAX_DELAY - DHCP_MIN_DELAY) * 1000000);
++		timernorm(&tv);
++		syslog(LOG_DEBUG,
++		    "%s: delaying DHCP for %0.1f seconds",
++		    ifp->name, timeval_to_double(&tv));
++	}
++
+ 	eloop_timeout_add_tv(ifp->ctx->eloop, &tv, dhcp_start1, ifp);
+ }
+ 
+diff -urN dhcpcd-6.4.0.org/dhcpcd.8.in dhcpcd-6.4.0/dhcpcd.8.in
+--- dhcpcd-6.4.0.org/dhcpcd.8.in	2014-06-14 22:13:12.000000000 +0200
++++ dhcpcd-6.4.0/dhcpcd.8.in	2014-07-05 21:47:22.000000000 +0200
+@@ -22,7 +22,7 @@
+ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ .\" SUCH DAMAGE.
+ .\"
+-.Dd June 2, 2014
++.Dd July 4, 2014
+ .Dt DHCPCD 8
+ .Os
+ .Sh NAME
+@@ -68,6 +68,10 @@
+ .Fl U, Fl Fl dumplease
+ .Ar interface
+ .Nm
++.Fl Fl pfxdlgonly
++.Nm
++.Fl Fl nopfxdlg
++.Nm
+ .Fl Fl version
+ .Nm
+ .Fl x , Fl Fl exit
+@@ -75,7 +79,7 @@
+ .Sh DESCRIPTION
+ .Nm
+ is an implementation of the DHCP client specified in
+-.%R RFC 2131 .
++.Li RFC 2131 .
+ .Nm
+ gets the host information
+ .Po
+@@ -103,17 +107,17 @@
+ .Pp
+ .Nm
+ is also an implementation of the BOOTP client specified in
+-.%R RFC 951 .
++.Li RFC 951 .
+ .Pp
+ .Nm
+ is also an implementation of the IPv6 Router Solicitor as specified in
+-.%R RFC 4861
++.Li RFC 4861
+ and
+-.%R RFC 6106 .
++.Li RFC 6106 .
+ .Pp
+ .Nm
+ is also an implemenation of the DHCPv6 client as specified in
+-.%R RFC 3315 .
++.Li RFC 3315 .
+ By default,
+ .Nm
+ only starts DHCPv6 when instructed to do so by an IPV6 Router Advertisement.
+@@ -211,7 +215,7 @@
+ .Pa @SCRIPT@ .
+ .It Fl D , Fl Fl duid
+ Generate an
+-.%R RFC 4361
++.Li RFC 4361
+ compliant clientid.
+ This requires persistent storage and not all DHCP servers work with it so it
+ is not enabled by default.
+@@ -259,7 +263,7 @@
+ itself never does any DNS updates.
+ .Nm
+ encodes the FQDN hostname as specified in
+-.%R RFC1035 .
++.Li RFC1035 .
+ .It Fl f , Fl Fl config Ar file
+ Specify a config to load instead of
+ .Pa @SYSCONFDIR@/dhcpcd.conf .
+@@ -614,6 +618,30 @@
+ so that
+ .Nm
+ knows which process to signal.
++.Pp
++.Li RFC3633
++DHCPv6 Prefix Delegation does not work in the same state or session as
++Normal or Temporary Addresses.
++.Nm
++is designed for a single state per protocol and as such
++.Li draft-ietf-dhc-dhcpv6-stateful-issues-06
++is supported by default, but that is not a published RFC yet.
++To allow RFC conformance,
++.Nm
++supports the
++.Fl Fl pfxdlgonly
++and
++.Fl Fl nopfxdlg
++options which allow the spawning of two
++.Nm
++instances to separate the Prefix Delegation state from the others.
++You may wish to disable
++.Xr dhcpcd-run-hooks 8
++on the
++.Fl Fl pfxdlgonly
++instance using the
++.Fl Fl script \"\"
++option.
+ .Sh FILES
+ .Bl -ohang
+ .It Pa @SYSCONFDIR@/dhcpcd.conf
+@@ -671,7 +699,8 @@
+ RFC\ 3004, RFC\ 3118, RFC\ 3203, RFC\ 3315, RFC\ 3361, RFC\ 3633, RFC\ 3396,
+ RFC\ 3397, RFC\ 3442, RFC\ 3495, RFC\ 3925, RFC\ 3927, RFC\ 4039, RFC\ 4075,
+ RFC\ 4242, RFC\ 4361, RFC\ 4390, RFC\ 4702, RFC\ 4074, RFC\ 4861, RFC\ 4833,
+-RFC\ 5227, RFC\ 5942, RFC\ 5969, RFC\ 6106, RFC\ 6334, RFC\ 6704, RFC\ 7217.
++RFC\ 5227, RFC\ 5942, RFC\ 5969, RFC\ 6106, RFC\ 6334, RFC\ 6603, RFC\ 6704,
++RFC\ 7217.
+ .Sh AUTHORS
+ .An Roy Marples Aq Mt roy at marples.name
+ .Sh BUGS
+diff -urN dhcpcd-6.4.0.org/dhcpcd.c dhcpcd-6.4.0/dhcpcd.c
+--- dhcpcd-6.4.0.org/dhcpcd.c	2014-06-14 22:13:12.000000000 +0200
++++ dhcpcd-6.4.0/dhcpcd.c	2014-07-05 21:47:22.000000000 +0200
+@@ -139,14 +139,14 @@
+ 	struct dhcp_opt *opt;
+ 
+ 	if (ctx->ifac) {
+-		for (ctx->ifac--; ctx->ifac >= 0; ctx->ifac--)
+-			free(ctx->ifav[ctx->ifac]);
++		for (; ctx->ifac > 0; ctx->ifac--)
++			free(ctx->ifav[ctx->ifac - 1]);
+ 		free(ctx->ifav);
+ 		ctx->ifav = NULL;
+ 	}
+ 	if (ctx->ifdc) {
+-		for (ctx->ifdc--; ctx->ifdc >= 0; ctx->ifdc--)
+-			free(ctx->ifdv[ctx->ifdc]);
++		for (; ctx->ifdc > 0; ctx->ifdc--)
++			free(ctx->ifdv[ctx->ifdc - 1]);
+ 		free(ctx->ifdv);
+ 		ctx->ifdv = NULL;
+ 	}
+@@ -323,6 +323,9 @@
+ {
+ 	struct if_options *ifo = ifp->options;
+ 	int ra_global, ra_iface;
++#ifdef INET6
++	size_t i;
++#endif
+ 
+ 	/* Do any platform specific configuration */
+ 	if_conf(ifp);
+@@ -339,7 +342,7 @@
+ 		ifo->options &= ~(DHCPCD_ARP | DHCPCD_IPV4LL);
+ 	if (!(ifp->flags & (IFF_POINTOPOINT | IFF_LOOPBACK | IFF_MULTICAST)))
+ 		ifo->options &= ~DHCPCD_IPV6RS;
+-	if (ifo->options & DHCPCD_LINK && if_carrier(ifp) == LINK_UNKNOWN)
++	if (ifo->options & DHCPCD_LINK && ifp->carrier == LINK_UNKNOWN)
+ 		ifo->options &= ~DHCPCD_LINK;
+ 
+ 	if (ifo->metric != -1)
+@@ -431,17 +434,27 @@
+ 	}
+ 
+ #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;
++	if (ifo->options & DHCPCD_IPV6) {
++		if (ifo->ia == NULL) {
++			ifo->ia = malloc(sizeof(*ifo->ia));
++			if (ifo->ia == NULL)
++				syslog(LOG_ERR, "%s: %m", __func__);
++			else {
++				ifo->ia_len = 1;
++				ifo->ia->ia_type = D6_OPTION_IA_NA;
++				memcpy(ifo->ia->iaid, ifo->iaid,
++				    sizeof(ifo->iaid));
++				memset(&ifo->ia->addr, 0,
++				    sizeof(ifo->ia->addr));
++				ifo->ia->sla = NULL;
++				ifo->ia->sla_len = 0;
++			}
++		} else {
++			for (i = 0; i < ifo->ia_len; i++) {
++				if (!ifo->ia[i].iaid_set)
++					memcpy(ifo->ia->iaid, ifo->iaid,
++					    sizeof(ifo->iaid));
++			}
+ 		}
+ 	}
+ #endif
+@@ -493,10 +506,23 @@
+ 	if (ifp == NULL || !(ifp->options->options & DHCPCD_LINK))
+ 		return;
+ 
+-	if (carrier == LINK_UNKNOWN)
++	switch(carrier) {
++	case LINK_UNKNOWN:
+ 		carrier = if_carrier(ifp); /* will set ifp->flags */
+-	else
++		break;
++	case LINK_UP:
++		/* we have a carrier! however, we need to ignore the flags
++		 * set in the kernel message as sometimes this message is
++		 * reported before IFF_UP is set by the kernel even though
++		 * dhcpcd has already set it.
++		 *
++		 * So we check the flags now. If IFF_UP is still not set
++		 * then we should expect an accompanying link_down message */
++		if_setflag(ifp, 0); /* will set ifp->flags */
++		break;
++	default:
+ 		ifp->flags = flags;
++	}
+ 
+ 	if (carrier == LINK_UNKNOWN)
+ 		syslog(LOG_ERR, "%s: carrier_status: %m", ifname);
+@@ -561,32 +587,33 @@
+ 		    ifp->name, ifn->name);
+ }
+ 
+-void
+-dhcpcd_startinterface(void *arg)
++static void
++pre_start(struct interface *ifp)
+ {
+-	struct interface *ifp = arg;
+-	struct if_options *ifo = ifp->options;
+-	size_t i;
+-	char buf[DUID_LEN * 3];
+ 
+ 	/* Add our link-local address before upping the interface
+ 	 * so our RFC7217 address beats the hwaddr based one.
+ 	 * This is also a safety check incase it was ripped out
+ 	 * from under us. */
+-	if (ifo->options & DHCPCD_IPV6 && ipv6_start(ifp) == -1) {
++	if (ifp->options->options & DHCPCD_IPV6 && ipv6_start(ifp) == -1) {
+ 		syslog(LOG_ERR, "%s: ipv6_start: %m", ifp->name);
+-		ifo->options &= DHCPCD_IPV6;
++		ifp->options->options &= DHCPCD_IPV6;
+ 	}
++}
+ 
+-	if (!(ifp->flags & IFF_UP) && if_carrier(ifp) != LINK_UNKNOWN) {
+-		if (if_up(ifp) == -1)
+-			syslog(LOG_ERR, "%s: if_up: %m",
+-			    ifp->name);
+-	}
++void
++dhcpcd_startinterface(void *arg)
++{
++	struct interface *ifp = arg;
++	struct if_options *ifo = ifp->options;
++	size_t i;
++	char buf[DUID_LEN * 3];
+ 
+-	if (ifp->carrier == LINK_UNKNOWN)
+-		dhcpcd_handlecarrier(ifp->ctx, LINK_UNKNOWN, 0, ifp->name);
+-	if (ifp->carrier == LINK_DOWN) {
++	pre_start(ifp);
++	if (if_up(ifp) == -1)
++		syslog(LOG_ERR, "%s: if_up: %m", ifp->name);
++
++	if (ifp->carrier == LINK_DOWN && ifo->options & DHCPCD_LINK) {
+ 		syslog(LOG_INFO, "%s: waiting for carrier", ifp->name);
+ 		return;
+ 	}
+@@ -621,10 +648,12 @@
+ 
+ 	if (ifo->options & DHCPCD_IPV6) {
+ 		if (ifo->options & DHCPCD_IPV6RS &&
+-		    !(ifo->options & DHCPCD_INFORM))
++		    !(ifo->options & (DHCPCD_INFORM | DHCPCD_PFXDLGONLY)))
+ 			ipv6nd_startrs(ifp);
+ 
+-		if (!(ifo->options & DHCPCD_IPV6RS)) {
++		if (!(ifo->options & DHCPCD_IPV6RS) ||
++		    ifo->options & DHCPCD_IA_FORCED)
++		{
+ 			ssize_t nolease;
+ 
+ 			if (ifo->options & DHCPCD_IA_FORCED)
+@@ -651,6 +680,8 @@
+ 				    "%s: dhcp6_start: %m", ifp->name);
+ 		}
+ 	}
++	if (ifo->options & DHCPCD_PFXDLGONLY)
++		return;
+ 
+ 	if (ifo->options & DHCPCD_IPV4)
+ 		dhcp_start(ifp);
+@@ -700,31 +731,17 @@
+ static void
+ run_preinit(struct interface *ifp)
+ {
+-	const char *reason;
+ 
+-	reason = NULL; /* appease gcc */
+-	if (ifp->options->options & DHCPCD_LINK) {
+-		switch (if_carrier(ifp)) {
+-		case LINK_DOWN:
+-			ifp->carrier = LINK_DOWN;
+-			reason = "NOCARRIER";
+-			break;
+-		case LINK_UP:
+-			ifp->carrier = LINK_UP;
+-			reason = "CARRIER";
+-			break;
+-		default:
+-			ifp->carrier = LINK_UNKNOWN;
+-			return;
+-		}
+-	} else
+-		ifp->carrier = LINK_UNKNOWN;
++	pre_start(ifp);
++	if (ifp->ctx->options & DHCPCD_TEST)
++		return;
+ 
+-	if (!(ifp->ctx->options & DHCPCD_TEST))
+-		script_runreason(ifp, "PREINIT");
++	script_runreason(ifp, "PREINIT");
+ 
+-	if (ifp->carrier != LINK_UNKNOWN && !(ifp->ctx->options & DHCPCD_TEST))
+-		script_runreason(ifp, reason);
++	if (ifp->carrier != LINK_UNKNOWN &&
++	    ifp->options->options & DHCPCD_LINK)
++		script_runreason(ifp,
++		    ifp->carrier == LINK_UP ? "CARRIER" : "NOCARRIER");
+ }
+ 
+ int
+@@ -732,7 +749,7 @@
+ {
+ 	struct dhcpcd_ctx *ctx;
+ 	struct if_head *ifs;
+-	struct interface *ifp, *ifn, *ifl = NULL;
++	struct interface *ifp, *iff, *ifn;
+ 	const char * const argv[] = { ifname };
+ 	int i;
+ 
+@@ -743,6 +760,7 @@
+ 			errno = ESRCH;
+ 			return -1;
+ 		}
++		syslog(LOG_DEBUG, "%s: interface departed", ifp->name);
+ 		ifp->options->options |= DHCPCD_DEPARTED;
+ 		stop_interface(ifp);
+ 		return 0;
+@@ -764,22 +782,24 @@
+ 			continue;
+ 		i = 0;
+ 		/* Check if we already have the interface */
+-		ifl = if_find(ctx, ifp->name);
+-		if (ifl) {
++		iff = if_find(ctx, ifp->name);
++		if (iff) {
++			syslog(LOG_DEBUG, "%s: interface updated", iff->name);
+ 			/* The flags and hwaddr could have changed */
+-			ifl->flags = ifp->flags;
+-			ifl->hwlen = ifp->hwlen;
++			iff->flags = ifp->flags;
++			iff->hwlen = ifp->hwlen;
+ 			if (ifp->hwlen != 0)
+-				memcpy(ifl->hwaddr, ifp->hwaddr, ifl->hwlen);
++				memcpy(iff->hwaddr, ifp->hwaddr, iff->hwlen);
+ 		} else {
++			syslog(LOG_DEBUG, "%s: interface added", ifp->name);
+ 			TAILQ_REMOVE(ifs, ifp, next);
+ 			TAILQ_INSERT_TAIL(ctx->ifaces, ifp, next);
+-		}
+-		if (action > 0) {
+ 			init_state(ifp, ctx->argc, ctx->argv);
+ 			run_preinit(ifp);
+-			dhcpcd_startinterface(ifp);
++			iff = ifp;
+ 		}
++		if (action > 0)
++			dhcpcd_startinterface(iff);
+ 	}
+ 
+ 	/* Free our discovered list */
+@@ -1264,6 +1284,8 @@
+ 	ctx.ifv = argv + optind;
+ 
+ 	ifo = read_config(&ctx, NULL, NULL, NULL);
++	if (ifo == NULL)
++		goto exit_failure;
+ 	opt = add_options(&ctx, NULL, ifo, argc, argv);
+ 	if (opt != 1) {
+ 		if (opt == 0)
+@@ -1272,17 +1294,26 @@
+ 	}
+ 	if (i == 3) {
+ 		printf("Interface options:\n");
++		if (optind == argc - 1) {
++			free_options(ifo);
++			ifo = read_config(&ctx, argv[optind], NULL, NULL);
++			if (ifo == NULL)
++				goto exit_failure;
++			add_options(&ctx, NULL, ifo, argc, argv);
++		}
+ 		if_printoptions();
+ #ifdef INET
+ 		if (family == 0 || family == AF_INET) {
+ 			printf("\nDHCPv4 options:\n");
+-			dhcp_printoptions(&ctx);
++			dhcp_printoptions(&ctx,
++			    ifo->dhcp_override, ifo->dhcp_override_len);
+ 		}
+ #endif
+ #ifdef INET6
+ 		if (family == 0 || family == AF_INET6) {
+ 			printf("\nDHCPv6 options:\n");
+-			dhcp6_printoptions(&ctx);
++			dhcp6_printoptions(&ctx,
++			    ifo->dhcp6_override, ifo->dhcp6_override_len);
+ 		}
+ #endif
+ 		goto exit_success;
+@@ -1341,9 +1372,10 @@
+ 			}
+ 			snprintf(pidfile, sizeof(pidfile),
+ 			    PIDFILE, "-", argv[optind], per);
+-		}
+-		else {
+-			snprintf(pidfile, sizeof(pidfile), PIDFILE, "", "", "");
++		} else {
++			snprintf(pidfile, sizeof(pidfile), PIDFILE,
++			    ctx.options & DHCPCD_PFXDLGONLY ? ".pd" : "",
++			    "", "");
+ 			ctx.options |= DHCPCD_MASTER;
+ 		}
+ 	}
+@@ -1351,18 +1383,52 @@
+ 	if (chdir("/") == -1)
+ 		syslog(LOG_ERR, "chdir `/': %m");
+ 
++	/* Freeing allocated addresses from dumping leases can trigger
++	 * eloop removals as well, so init here. */
++	ctx.eloop = eloop_init();
++	if (ctx.eloop == NULL) {
++		syslog(LOG_ERR, "%s: %m", __func__);
++		goto exit_failure;
++	}
++
+ 	if (ctx.options & DHCPCD_DUMPLEASE) {
+ 		if (optind != argc - 1) {
+ 			syslog(LOG_ERR, "dumplease requires an interface");
+ 			goto exit_failure;
+ 		}
+-		if (dhcp_dump(&ctx, argv[optind]) == -1)
++		i = 0;
++		/* We need to try and find the interface so we can
++		 * load the hardware address to compare automated IAID */
++		ctx.ifaces = if_discover(&ctx, 1, argv + optind);
++		if (ctx.ifaces == NULL)
++			goto exit_failure;
++		ifp = TAILQ_FIRST(ctx.ifaces);
++		if (ifp == NULL) {
++			ifp = calloc(1, sizeof(*ifp));
++			if (ifp == NULL) {
++				syslog(LOG_ERR, "%s: %m", __func__);
++				goto exit_failure;
++			}
++			strlcpy(ifp->name, argv[optind], sizeof(ifp->name));
++			ifp->ctx = &ctx;
++			TAILQ_INSERT_HEAD(ctx.ifaces, ifp, next);
++		}
++		configure_interface(ifp, ctx.argc, ctx.argv);
++		if (family == 0 || family == AF_INET) {
++			if (dhcp_dump(ifp) == -1)
++				i = 1;
++		}
++		if (family == 0 || family == AF_INET6) {
++			if (dhcp6_dump(ifp) == -1)
++				i = 1;
++		}
++		if (i == -1)
+ 			goto exit_failure;
+ 		goto exit_success;
+ 	}
+ 
+ #ifdef USE_SIGNALS
+-	if (!(ctx.options & DHCPCD_TEST) &&
++	if (!(ctx.options & (DHCPCD_TEST | DHCPCD_PFXDLGONLY)) &&
+ 	    (sig == 0 || ctx.ifc != 0))
+ 	{
+ #endif
+@@ -1396,12 +1462,6 @@
+ 		syslog(LOG_WARNING,
+ 		    PACKAGE " will not work correctly unless run as root");
+ 
+-	ctx.eloop = eloop_init();
+-	if (ctx.eloop == NULL) {
+-		syslog(LOG_ERR, "%s: %m", __func__);
+-		goto exit_failure;
+-	}
+-
+ #ifdef USE_SIGNALS
+ 	if (sig != 0) {
+ 		pid = read_pid(pidfile);
+@@ -1484,7 +1544,7 @@
+ 	}
+ 
+ 
+-	if (ctx.options & DHCPCD_MASTER) {
++	if (ctx.options & DHCPCD_MASTER && !(ctx.options & DHCPCD_PFXDLGONLY)) {
+ 		if (control_start(&ctx, NULL) == -1)
+ 			syslog(LOG_ERR, "control_start: %m");
+ 	}
+@@ -1622,7 +1682,6 @@
+ 
+ 	free_options(ifo);
+ 	free_globals(&ctx);
+-	if_rarestore(&ctx);
+ 	ipv4_ctxfree(&ctx);
+ 	ipv6_ctxfree(&ctx);
+ 	dev_stop(&ctx, !(ctx.options & DHCPCD_FORKED));
+diff -urN dhcpcd-6.4.0.org/dhcpcd.conf.5.in dhcpcd-6.4.0/dhcpcd.conf.5.in
+--- dhcpcd-6.4.0.org/dhcpcd.conf.5.in	2014-06-14 22:13:12.000000000 +0200
++++ dhcpcd-6.4.0/dhcpcd.conf.5.in	2014-07-05 21:47:22.000000000 +0200
+@@ -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 July 4, 2014
+ .Dt DHCPCD.CONF 5
+ .Os
+ .Sh NAME
+@@ -214,7 +214,7 @@
+ 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
+ Request a DHCPv6 Normal Address for
+ .Ar iaid .
+ .Ar iaid
+@@ -230,7 +230,7 @@
+ 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
+@@ -250,7 +250,10 @@
+ and
+ .Ar sla_id .
+ Each assigned address will have a suffix of 1.
+-You cannot assign a prefix to the requesting interface.
++You cannot assign a prefix to the requesting interface unless the
++DHCPv6 server supports
++.Li RFC6603
++Prefix Exclude Option.
+ .Nm dhcpcd
+ has to be running for all the interfaces it is delegating to.
+ A default
+@@ -290,6 +293,12 @@
+ .D1 interface eth1
+ .D1 ipv4
+ .D1 ipv6rs
++.Pp
++Using this option with other IA options in the same interface block is not
++currently RFC conformant.
++Please see the
++.Li BUGS
++section below.
+ .It Ic ipv4only
+ Only configure IPv4.
+ .It Ic ipv6only
+@@ -457,6 +466,9 @@
+ .Ar ssid .
+ .It Ic slaac Op Ar hwaddr | Ar private
+ Selects the interface identifier used for SLAAC generated IPv6 addresses.
++If
++.Ar private
++is used, a RFC7217 address is generated.
+ .It Ic static Ar value
+ Configures a static
+ .Ar value .
+@@ -739,11 +751,21 @@
+ .Sh AUTHORS
+ .An Roy Marples Aq Mt roy at marples.name
+ .Sh BUGS
+-When configuring DHCPv6 you can only select one IA type.
+-I can't think of a use case where you would want different types,
+-so if you have one then please bring it up for discussion on the
+-.Aq Mt  dhcpcd-discuss at marples.name
+-mailing list.
++Combining different DHCPv6 IA options in the same interface block is not
++currently RFC conformant.
++See
++.Lk http://datatracker.ietf.org/doc/draft-ietf-dhc-dhcpv6-stateful-issues
++for details.
++.Nm dhcpcd
++strives to comply with this document and will be updated when finally published.
++To enable RFC conformance, spawn separate
++.Nm dhcpcd
++instances using the
++.Fl Fl pfxdlgonly
++and
++.Fl Fl nopfxdlg
++options as described in
++.Xr dhcpcd 8 .
+ .Pp
+ Please report them to
+ .Lk http://roy.marples.name/projects/dhcpcd
+diff -urN dhcpcd-6.4.0.org/dhcpcd-definitions.conf dhcpcd-6.4.0/dhcpcd-definitions.conf
+--- dhcpcd-6.4.0.org/dhcpcd-definitions.conf	2014-06-14 22:13:12.000000000 +0200
++++ dhcpcd-6.4.0/dhcpcd-definitions.conf	2014-07-05 21:47:22.000000000 +0200
+@@ -360,6 +360,7 @@
+ embed		uint32			vltime
+ embed		ip6address		prefix
+ encap 13	option
++encap 67	option
+ 
+ # DHCPv6 Network Information Service Options, RFC3898
+ define6 27	array ip6address	nis_servers
+@@ -463,6 +464,11 @@
+ # DHCPv6 AFTR-Name, RFC6334
+ define6 64	domain			aftr_name
+ 
++# DHCPv6 Prefix Exclude Option, RFC6603
++define6 67	embed			pd_exclude
++embed		byte			prefix_len
++embed		binhex			subnetID
++
+ # DHCPv6 Home Info Discovery in MIPv6, RFC6610
+ define6 69	encap			mip6_idinf
+ encap 71	option
+diff -urN dhcpcd-6.4.0.org/dhcpcd.h dhcpcd-6.4.0/dhcpcd.h
+--- dhcpcd-6.4.0.org/dhcpcd.h	2014-06-14 22:13:12.000000000 +0200
++++ dhcpcd-6.4.0/dhcpcd.h	2014-07-05 21:47:22.000000000 +0200
+@@ -123,11 +123,8 @@
+ 	struct dhcp_opt *dhcp6_opts;
+ 	size_t dhcp6_opts_len;
+ 	struct ipv6_ctx *ipv6;
+-	char **ra_restore;
+-	size_t ra_restore_len;
+ #ifndef __linux__
+ 	int ra_global;
+-	int ra_kernel_set;
+ #endif
+ #endif /* INET6 */
+ 
+diff -urN dhcpcd-6.4.0.org/dhcpcd-hooks/02-dump dhcpcd-6.4.0/dhcpcd-hooks/02-dump
+--- dhcpcd-6.4.0.org/dhcpcd-hooks/02-dump	2014-06-14 22:13:12.000000000 +0200
++++ dhcpcd-6.4.0/dhcpcd-hooks/02-dump	2014-07-05 21:47:22.000000000 +0200
+@@ -1,6 +1,8 @@
+ # Just echo our DHCP options we have
+ 
+-if [ "$reason" = "DUMP" ]; then
++case "$reason" in
++DUMP|DUMP6)
+ 	set | sed -ne 's/^new_//p' | sort
+ 	exit 0
+-fi
++	;;
++esac
+diff -urN dhcpcd-6.4.0.org/dhcpcd-hooks/20-resolv.conf dhcpcd-6.4.0/dhcpcd-hooks/20-resolv.conf
+--- dhcpcd-6.4.0.org/dhcpcd-hooks/20-resolv.conf	2014-06-14 22:13:12.000000000 +0200
++++ dhcpcd-6.4.0/dhcpcd-hooks/20-resolv.conf	2014-07-05 21:47:22.000000000 +0200
+@@ -70,7 +70,7 @@
+ 
+ add_resolv_conf()
+ {
+-	local x= conf="$signature$NL" i=${ra_count:-0} ra=
++	local x= conf="$signature$NL" i=${ra_count:-0} ra= warn=true
+ 
+ 	while [ $i -ne 0 ]; do
+ 		eval ra=\$ra${i}_rdnss
+@@ -101,24 +101,23 @@
+ 
+ 	if [ -n "$new_domain_name" ]; then
+ 		set -- $new_domain_name
+-		new_domain_name="$1"
+-		if valid_domainname "$new_domain_name"; then
+-			conf="${conf}domain $new_domain_name$NL"
++		if valid_domainname "$1"; then
++			conf="${conf}domain $1$NL"
+ 		else
+-			syslog err "Invalid domain name: $new_domain_name"
++			syslog err "Invalid domain name: $1"
+ 		fi
+-		# Support RFC violating search in domain
+-		if [ -z "$new_domain_search" -a -n "$2" ]; then
+-			new_domain_search="$*"
++		# If there is no search this, make this one
++		if [ -z "$new_domain_search" ]; then
++			new_domain_search="$new_domain_name"
++			[ "$new_domain_name" = "$1" ] && warn=true
+ 		fi
+ 	fi
+-	if [ -n "$new_domain_search" -a \
+-	    "$new_domain_search" != "$new_domain_name" ]
+-	then
++	if [ -n "$new_domain_search" ]; then
+ 		if valid_domainname_list $new_domain_search; then
+ 			conf="${conf}search $new_domain_search$NL"
+-		else
+-			syslog err "Invalid domain name in list: $new_domain_search"
++		elif ! $warn; then
++			syslog err "Invalid domain name in list:" \
++			    "$new_domain_search"
+ 		fi
+ 	fi
+ 	for x in ${new_domain_name_servers}; do
+diff -urN dhcpcd-6.4.0.org/dhcpcd-hooks/Makefile dhcpcd-6.4.0/dhcpcd-hooks/Makefile
+--- dhcpcd-6.4.0.org/dhcpcd-hooks/Makefile	2014-06-14 22:13:12.000000000 +0200
++++ dhcpcd-6.4.0/dhcpcd-hooks/Makefile	2014-07-05 21:47:22.000000000 +0200
+@@ -25,7 +25,7 @@
+ 
+ install: proginstall
+ 
+-import:
++import: ${HOOKSCRIPTS}
+ 	${INSTALL} -d /tmp/${DISTPREFIX}/dhcpcd-hooks
+ 	for x in ${SCRIPTS}; do \
+ 		t="/tmp/${DISTPREFIX}/dhcpcd-hooks/$$x"; \
+diff -urN dhcpcd-6.4.0.org/dhcp-common.c dhcpcd-6.4.0/dhcp-common.c
+--- dhcpcd-6.4.0.org/dhcp-common.c	2014-06-14 22:13:12.000000000 +0200
++++ dhcpcd-6.4.0/dhcp-common.c	2014-07-05 21:47:22.000000000 +0200
+@@ -86,6 +86,7 @@
+ 
+ int
+ make_option_mask(const struct dhcp_opt *dopts, size_t dopts_len,
++    const struct dhcp_opt *odopts, size_t odopts_len,
+     uint8_t *mask, const char *opts, int add)
+ {
+ 	char *token, *o, *p, *t;
+@@ -94,14 +95,14 @@
+ 	unsigned int n;
+ 	size_t i;
+ 
+-	o = p = strdup(opts);
+ 	if (opts == NULL)
+ 		return -1;
++	o = p = strdup(opts);
+ 	while ((token = strsep(&p, ", "))) {
+ 		if (*token == '\0')
+ 			continue;
+-		for (i = 0, opt = dopts; i < dopts_len; i++, opt++) {
+-			match = 0;
++		match = 0;
++		for (i = 0, opt = odopts; i < odopts_len; i++, opt++) {
+ 			if (strcmp(opt->var, token) == 0)
+ 				match = 1;
+ 			else {
+@@ -111,26 +112,40 @@
+ 					if (opt->option == n)
+ 						match = 1;
+ 			}
+-			if (match) {
+-				if (add == 2 && !(opt->type & ADDRIPV4)) {
+-					free(o);
+-					errno = EINVAL;
+-					return -1;
+-				}
+-				if (add == 1 || add == 2)
+-					add_option_mask(mask,
+-					    opt->option);
+-				else
+-					del_option_mask(mask,
+-					    opt->option);
++			if (match)
+ 				break;
++		}
++		if (match == 0) {
++			for (i = 0, opt = dopts; i < dopts_len; i++, opt++) {
++				if (strcmp(opt->var, token) == 0)
++				        match = 1;
++				else {
++					errno = 0;
++					n = (unsigned int)strtol(token, &t, 0);
++					if (errno == 0 && !*t)
++						if (opt->option == n)
++							match = 1;
++				}
++				if (match)
++					break;
+ 			}
+ 		}
+-		if (!opt->option) {
++		if (!match || !opt->option) {
+ 			free(o);
+ 			errno = ENOENT;
+ 			return -1;
+ 		}
++		if (add == 2 && !(opt->type & ADDRIPV4)) {
++			free(o);
++			errno = EINVAL;
++			return -1;
++		}
++		if (add == 1 || add == 2)
++			add_option_mask(mask,
++			    opt->option);
++		else
++			del_option_mask(mask,
++			    opt->option);
+ 	}
+ 	free(o);
+ 	return 0;
+diff -urN dhcpcd-6.4.0.org/dhcp-common.h dhcpcd-6.4.0/dhcp-common.h
+--- dhcpcd-6.4.0.org/dhcp-common.h	2014-06-14 22:13:12.000000000 +0200
++++ dhcpcd-6.4.0/dhcp-common.h	2014-07-05 21:47:22.000000000 +0200
+@@ -88,6 +88,7 @@
+ #define del_option_mask(var, val) (var[val >> 3] &= ~(1 << (val & 7)))
+ #define has_option_mask(var, val) (var[val >>3] & (1 << (val & 7)))
+ int make_option_mask(const struct dhcp_opt *, size_t,
++    const struct dhcp_opt *, size_t,
+     uint8_t *, const char *, int);
+ 
+ size_t encode_rfc1035(const char *src, uint8_t *dst);
+diff -urN dhcpcd-6.4.0.org/dhcp.h dhcpcd-6.4.0/dhcp.h
+--- dhcpcd-6.4.0.org/dhcp.h	2014-06-14 22:13:12.000000000 +0200
++++ dhcpcd-6.4.0/dhcp.h	2014-07-05 21:47:22.000000000 +0200
+@@ -250,7 +250,8 @@
+ ssize_t decode_rfc3442(char *, size_t, const uint8_t *p, size_t);
+ ssize_t decode_rfc5969(char *, size_t, const uint8_t *p, size_t);
+ 
+-void dhcp_printoptions(const struct dhcpcd_ctx *);
++void dhcp_printoptions(const struct dhcpcd_ctx *,
++    const struct dhcp_opt *, size_t);
+ int get_option_addr(struct dhcpcd_ctx *,struct in_addr *,
+     const struct dhcp_message *, uint8_t);
+ #define is_bootp(i, m) ((m) &&						\
+@@ -285,16 +286,15 @@
+ void dhcp_reboot_newopts(struct interface *, unsigned long long);
+ void dhcp_close(struct interface *);
+ void dhcp_free(struct interface *);
+-int dhcp_dump(struct dhcpcd_ctx *, const char *);
++int dhcp_dump(struct interface *);
+ #else
+-#define dhcp_printoptions
+ #define dhcp_drop(a, b)
+ #define dhcp_start(a) {}
+ #define dhcp_reboot(a, b) b = b
+ #define dhcp_reboot_newopts(a, b)
+ #define dhcp_close(a)
+ #define dhcp_free(a)
+-#define dhcp_dump(a, b) -1
++#define dhcp_dump(a) -1
+ #endif
+ 
+ #endif
+diff -urN dhcpcd-6.4.0.org/if-bsd.c dhcpcd-6.4.0/if-bsd.c
+--- dhcpcd-6.4.0.org/if-bsd.c	2014-06-14 22:13:12.000000000 +0200
++++ dhcpcd-6.4.0/if-bsd.c	2014-07-05 21:47:22.000000000 +0200
+@@ -1019,45 +1019,6 @@
+ 	return flags;
+ }
+ 
+-void
+-if_rarestore(struct dhcpcd_ctx *ctx)
+-{
+-
+-	if (ctx->options & DHCPCD_FORKED)
+-		return;
+-
+-	for (; ctx->ra_restore_len > 0; ctx->ra_restore_len--) {
+-#ifdef ND6_IFF_ACCEPT_RTADV
+-		if (!(ctx->options & DHCPCD_FORKED)) {
+-			syslog(LOG_DEBUG,
+-			    "%s: restoring kernel IPv6 RA support",
+-			    ctx->ra_restore[ctx->ra_restore_len - 1]);
+-			if (set_if_nd6_flag(
+-			    ctx->ra_restore[ctx->ra_restore_len -1],
+-			    ND6_IFF_ACCEPT_RTADV) == -1)
+-				syslog(LOG_ERR, "%s: set_if_nd6_flag: %m",
+-				    ctx->ra_restore[ctx->ra_restore_len - 1]);
+-#ifdef ND6_IFF_OVERRIDE_RTADV
+-			if (ctx->ra_kernel_set == 0 && del_if_nd6_flag(
+-			    ctx->ra_restore[ctx->ra_restore_len -1],
+-			    ND6_IFF_OVERRIDE_RTADV) == -1)
+-				syslog(LOG_ERR, "%s: del_if_nd6_flag: %m",
+-				    ctx->ra_restore[ctx->ra_restore_len - 1]);
+-#endif
+-		}
+-#endif
+-		free(ctx->ra_restore[ctx->ra_restore_len - 1]);
+-	}
+-	free(ctx->ra_restore);
+-	ctx->ra_restore = NULL;
+-
+-	if (ctx->ra_kernel_set) {
+-		syslog(LOG_DEBUG, "restoring kernel IPv6 RA support");
+-		if (set_inet6_sysctl(IPV6CTL_ACCEPT_RTADV, 1) == -1)
+-			syslog(LOG_ERR, "IPV6CTL_ACCEPT_RTADV: %m");
+-	}
+-}
+-
+ static int
+ if_raflush(void)
+ {
+@@ -1085,10 +1046,6 @@
+ #ifdef ND6_IFF_OVERRIDE_RTADV
+ 		int override;
+ #endif
+-#ifdef ND6_IFF_ACCEPT_RTADV
+-		size_t i;
+-		char *p, **nrest;
+-#endif
+ 
+ #ifdef ND6_IFF_IFDISABLED
+ 		if (del_if_nd6_flag(ifname, ND6_IFF_IFDISABLED) == -1) {
+@@ -1166,7 +1123,7 @@
+ 				return ra;
+ 			}
+ #ifdef ND6_IFF_OVERRIDE_RTADV
+-			if (override == 0 && ctx->ra_kernel_set == 0 &&
++			if (override == 0 &&
+ 			    set_if_nd6_flag(ifname, ND6_IFF_OVERRIDE_RTADV)
+ 			    == -1)
+ 			{
+@@ -1177,25 +1134,6 @@
+ 				return ra;
+ 			}
+ #endif
+-			for (i = 0; i < ctx->ra_restore_len; i++)
+-				if (strcmp(ctx->ra_restore[i], ifname) == 0)
+-					break;
+-			if (i == ctx->ra_restore_len) {
+-				p = strdup(ifname);
+-				if (p == NULL) {
+-					syslog(LOG_ERR, "%s: %m", __func__);
+-					return 0;
+-				}
+-				nrest = realloc(ctx->ra_restore,
+-				    (ctx->ra_restore_len + 1) * sizeof(char *));
+-				if (nrest == NULL) {
+-					syslog(LOG_ERR, "%s: %m", __func__);
+-					free(p);
+-					return 0;
+-				}
+-				ctx->ra_restore = nrest;
+-				ctx->ra_restore[ctx->ra_restore_len++] = p;
+-			}
+ 			return 0;
+ 		}
+ 		return ra;
+@@ -1217,7 +1155,6 @@
+ 			return ra;
+ 		}
+ 		ra = 0;
+-		ctx->ra_kernel_set = 1;
+ 
+ 		/* Flush the kernel knowledge of advertised routers
+ 		 * and prefixes so the kernel does not expire prefixes
+diff -urN dhcpcd-6.4.0.org/if.c dhcpcd-6.4.0/if.c
+--- dhcpcd-6.4.0.org/if.c	2014-06-14 22:13:12.000000000 +0200
++++ dhcpcd-6.4.0/if.c	2014-07-05 21:47:22.000000000 +0200
+@@ -133,7 +133,7 @@
+ }
+ 
+ int
+-if_up(struct interface *ifp)
++if_setflag(struct interface *ifp, short flag)
+ {
+ 	struct ifreq ifr;
+ 	int s, r;
+@@ -152,10 +152,10 @@
+ #endif
+ 	r = -1;
+ 	if (ioctl(s, SIOCGIFFLAGS, &ifr) == 0) {
+-		if ((ifr.ifr_flags & IFF_UP))
++		if (flag == 0 || ifr.ifr_flags & flag)
+ 			r = 0;
+ 		else {
+-			ifr.ifr_flags |= IFF_UP;
++			ifr.ifr_flags |= flag;
+ 			if (ioctl(s, SIOCSIFFLAGS, &ifr) == 0)
+ 				r = 0;
+ 		}
+@@ -295,6 +295,7 @@
+ 		ifp->ctx = ctx;
+ 		strlcpy(ifp->name, p, sizeof(ifp->name));
+ 		ifp->flags = ifa->ifa_flags;
++		ifp->carrier = if_carrier(ifp);
+ 
+ 		sdl_type = 0;
+ 		/* Don't allow loopback unless explicit */
+diff -urN dhcpcd-6.4.0.org/if.h dhcpcd-6.4.0/if.h
+--- dhcpcd-6.4.0.org/if.h	2014-06-14 22:13:12.000000000 +0200
++++ dhcpcd-6.4.0/if.h	2014-07-05 21:47:22.000000000 +0200
+@@ -90,7 +90,8 @@
+ #define RAW_EOF			1 << 0
+ #define RAW_PARTIALCSUM		2 << 0
+ 
+-int if_up(struct interface *ifp);
++int if_setflag(struct interface *ifp, short flag);
++#define if_up(ifp) if_setflag((ifp), IFF_UP)
+ struct if_head *if_discover(struct dhcpcd_ctx *, int, char * const *);
+ struct interface *if_find(struct dhcpcd_ctx *, const char *);
+ void if_free(struct interface *);
+@@ -131,7 +132,6 @@
+ 
+ #ifdef INET6
+ int if_checkipv6(struct dhcpcd_ctx *ctx, const char *, int);
+-void if_rarestore(struct dhcpcd_ctx *);
+ int if_nd6reachable(const char *ifname, struct in6_addr *addr);
+ 
+ int if_address6(const struct ipv6_addr *, int);
+@@ -145,7 +145,6 @@
+ #define if_delroute6(rt) if_route6(rt, -1)
+ #else
+ #define if_checkipv6(a, b, c) (-1)
+-#define if_rarestore(a)
+ #endif
+ 
+ int if_machinearch(char *, size_t);
+diff -urN dhcpcd-6.4.0.org/if-linux.c dhcpcd-6.4.0/if-linux.c
+--- dhcpcd-6.4.0.org/if-linux.c	2014-06-14 22:13:12.000000000 +0200
++++ dhcpcd-6.4.0/if-linux.c	2014-07-05 21:47:22.000000000 +0200
+@@ -1223,33 +1223,11 @@
+ 
+ static const char *prefix = "/proc/sys/net/ipv6/conf";
+ 
+-void
+-if_rarestore(struct dhcpcd_ctx *ctx)
+-{
+-	char path[256];
+-
+-	for (; ctx->ra_restore_len > 0; ctx->ra_restore_len--) {
+-		if (!(ctx->options & DHCPCD_FORKED)) {
+-			syslog(LOG_DEBUG,
+-			    "%s: restoring kernel IPv6 RA support",
+-			    ctx->ra_restore[ctx->ra_restore_len - 1]);
+-			snprintf(path, sizeof(path), "%s/%s/accept_ra",
+-			    prefix, ctx->ra_restore[ctx->ra_restore_len - 1]);
+-			if (write_path(path, "1") == -1 && errno != ENOENT)
+-			    syslog(LOG_ERR, "write_path: %s: %m", path);
+-		}
+-		free(ctx->ra_restore[ctx->ra_restore_len - 1]);
+-	}
+-	free(ctx->ra_restore);
+-	ctx->ra_restore = NULL;
+-}
+-
+ int
+-if_checkipv6(struct dhcpcd_ctx *ctx, const char *ifname, int own)
++if_checkipv6(__unused struct dhcpcd_ctx *ctx, const char *ifname, int own)
+ {
+ 	int ra;
+-	size_t i;
+-	char path[256], *p, **nrest;
++	char path[256];
+ 
+ 	if (ifname == NULL)
+ 		ifname = "all";
+@@ -1283,25 +1261,6 @@
+ 			syslog(LOG_ERR, "write_path: %s: %m", path);
+ 			return ra;
+ 		}
+-		for (i = 0; i < ctx->ra_restore_len; i++)
+-			if (strcmp(ctx->ra_restore[i], ifname) == 0)
+-				break;
+-		if (i == ctx->ra_restore_len) {
+-			p = strdup(ifname);
+-			if (p == NULL) {
+-				syslog(LOG_ERR, "%s: %m", __func__);
+-				return 0;
+-			}
+-			nrest = realloc(ctx->ra_restore,
+-			    (ctx->ra_restore_len + 1) * sizeof(char *));
+-			if (nrest == NULL) {
+-				syslog(LOG_ERR, "%s: %m", __func__);
+-				free(p);
+-				return 0;
+-			}
+-			ctx->ra_restore = nrest;
+-			ctx->ra_restore[ctx->ra_restore_len++] = p;
+-		}
+ 		return 0;
+ 	}
+ 
+diff -urN dhcpcd-6.4.0.org/if-options.c dhcpcd-6.4.0/if-options.c
+--- dhcpcd-6.4.0.org/if-options.c	2014-06-14 22:13:12.000000000 +0200
++++ dhcpcd-6.4.0/if-options.c	2014-07-05 21:47:22.000000000 +0200
+@@ -93,6 +93,8 @@
+ #define O_CONTROLGRP		O_BASE + 34
+ #define O_SLAAC			O_BASE + 35
+ #define O_GATEWAY		O_BASE + 36
++#define O_NOPFXDLG		O_BASE + 37
++#define O_PFXDLGONLY		O_BASE + 38
+ 
+ const struct option cf_options[] = {
+ 	{"background",      no_argument,       NULL, 'b'},
+@@ -179,6 +181,8 @@
+ 	{"controlgroup",    required_argument, NULL, O_CONTROLGRP},
+ 	{"slaac",           required_argument, NULL, O_SLAAC},
+ 	{"gateway",         no_argument,       NULL, O_GATEWAY},
++	{"nopfxdlg",        no_argument,       NULL, O_NOPFXDLG},
++	{"pfxdlgonly",      no_argument,       NULL, O_PFXDLGONLY},
+ 	{NULL,              0,                 NULL, '\0'}
+ };
+ 
+@@ -518,7 +522,9 @@
+ 
+ static const char *
+ set_option_space(struct dhcpcd_ctx *ctx,
+-    const char *arg, const struct dhcp_opt **d, size_t *dl,
++    const char *arg,
++    const struct dhcp_opt **d, size_t *dl,
++    const struct dhcp_opt **od, size_t *odl,
+     struct if_options *ifo,
+     uint8_t *request[], uint8_t *require[], uint8_t *no[])
+ {
+@@ -527,6 +533,8 @@
+ 	if (strncmp(arg, "dhcp6_", strlen("dhcp6_")) == 0) {
+ 		*d = ctx->dhcp6_opts;
+ 		*dl = ctx->dhcp6_opts_len;
++		*od = ifo->dhcp6_override;
++		*odl = ifo->dhcp6_override_len;
+ 		*request = ifo->requestmask6;
+ 		*require = ifo->requiremask6;
+ 		*no = ifo->nomask6;
+@@ -537,9 +545,13 @@
+ #ifdef INET
+ 	*d = ctx->dhcp_opts;
+ 	*dl = ctx->dhcp_opts_len;
++	*od = ifo->dhcp_override;
++	*odl = ifo->dhcp_override_len;
+ #else
+ 	*d = NULL;
+ 	*dl = 0;
++	*od = NULL;
++	*odl = 0;
+ #endif
+ 	*request = ifo->requestmask;
+ 	*require = ifo->requiremask;
+@@ -630,10 +642,10 @@
+ 	struct in_addr addr, addr2;
+ 	in_addr_t *naddr;
+ 	struct rt *rt;
+-	const struct dhcp_opt *d;
++	const struct dhcp_opt *d, *od;
+ 	uint8_t *request, *require, *no;
+ 	struct dhcp_opt **dop, *ndop;
+-	size_t *dop_len, dl;
++	size_t *dop_len, dl, odl;
+ 	struct vivco *vivco;
+ 	struct token *token;
+ 	struct group *grp;
+@@ -733,10 +745,10 @@
+ 		}
+ 		break;
+ 	case 'o':
+-		arg = set_option_space(ctx, arg, &d, &dl, ifo,
++		arg = set_option_space(ctx, arg, &d, &dl, &od, &odl, ifo,
+ 		    &request, &require, &no);
+-		if (make_option_mask(d, dl, request, arg, 1) != 0 ||
+-		    make_option_mask(d, dl, no, arg, -1) != 0)
++		if (make_option_mask(d, dl, od, odl, request, arg, 1) != 0 ||
++		    make_option_mask(d, dl, od, odl, no, arg, -1) != 0)
+ 		{
+ 			syslog(LOG_ERR, "unknown option `%s'", arg);
+ 			return -1;
+@@ -952,22 +964,22 @@
+ 		ifo->options |= DHCPCD_MASTER;
+ 		break;
+ 	case 'O':
+-		arg = set_option_space(ctx, arg, &d, &dl, ifo,
++		arg = set_option_space(ctx, arg, &d, &dl, &od, &odl, ifo,
+ 		    &request, &require, &no);
+-		if (make_option_mask(d, dl, request, arg, -1) != 0 ||
+-		    make_option_mask(d, dl, require, arg, -1) != 0 ||
+-		    make_option_mask(d, dl, no, arg, 1) != 0)
++		if (make_option_mask(d, dl, od, odl, request, arg, -1) != 0 ||
++		    make_option_mask(d, dl, od, odl, require, arg, -1) != 0 ||
++		    make_option_mask(d, dl, od, odl, no, arg, 1) != 0)
+ 		{
+ 			syslog(LOG_ERR, "unknown option `%s'", arg);
+ 			return -1;
+ 		}
+ 		break;
+ 	case 'Q':
+-		arg = set_option_space(ctx, arg, &d, &dl, ifo,
++		arg = set_option_space(ctx, arg, &d, &dl, &od, &odl, ifo,
+ 		    &request, &require, &no);
+-		if (make_option_mask(d, dl, require, arg, 1) != 0 ||
+-		    make_option_mask(d, dl, request, arg, 1) != 0 ||
+-		    make_option_mask(d, dl, no, arg, -1) != 0)
++		if (make_option_mask(d, dl, od, odl, require, arg, 1) != 0 ||
++		    make_option_mask(d, dl, od, odl, request, arg, 1) != 0 ||
++		    make_option_mask(d, dl, od, odl, no, arg, -1) != 0)
+ 		{
+ 			syslog(LOG_ERR, "unknown option `%s'", arg);
+ 			return -1;
+@@ -1156,8 +1168,11 @@
+ 		}
+ 		break;
+ 	case O_DESTINATION:
+-		if (make_option_mask(ctx->dhcp_opts, ctx->dhcp_opts_len,
+-		    ifo->dstmask, arg, 2) != 0) {
++		arg = set_option_space(ctx, arg, &d, &dl, &od, &odl, ifo,
++		    &request, &require, &no);
++		if (make_option_mask(d, dl, od, odl,
++		    ifo->dstmask, arg, 2) != 0)
++		{
+ 			if (errno == EINVAL)
+ 				syslog(LOG_ERR, "option `%s' does not take"
+ 				    " an IPv4 address", arg);
+@@ -1220,35 +1235,40 @@
+ 			}
+ 			i = D6_OPTION_IA_PD;
+ 		}
+-		if (arg != NULL && ifname == NULL) {
++		if (ifname == NULL && arg) {
+ 			syslog(LOG_ERR,
+ 			    "IA with IAID must belong in an interface block");
+ 			return -1;
+ 		}
+ 		ifo->options |= DHCPCD_IA_FORCED;
+-		if (ifo->ia_type != 0 && ifo->ia_type != i) {
+-			syslog(LOG_ERR, "cannot specify a different IA type");
+-			return -1;
+-		}
+-		ifo->ia_type = (uint16_t)i;
+-		if (arg == NULL)
+-			break;
+ 		fp = strwhite(arg);
+-		if (fp)
++		if (fp) {
+ 			*fp++ = '\0';
+-		if (parse_iaid(iaid, arg, sizeof(iaid)) == -1)
+-			return -1;
++			fp = strskipwhite(fp);
++		}
++		if (arg) {
++			p = strchr(arg, '/');
++			if (p)
++				*p++ = '\0';
++			if (parse_iaid(iaid, arg, sizeof(iaid)) == -1)
++				return -1;
++		}
+ 		ia = NULL;
+ 		for (sl = 0; sl < ifo->ia_len; sl++) {
+-			if (ifo->ia[sl].iaid[0] == iaid[0] &&
++			if ((arg == NULL && !ifo->ia[sl].iaid_set) ||
++			    (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])
++			    ifo->ia[sl].iaid[3] == iaid[3]))
+ 			{
+ 			        ia = &ifo->ia[sl];
+ 				break;
+ 			}
+ 		}
++		if (ia && ia->ia_type != (uint16_t)i) {
++			syslog(LOG_ERR, "Cannot mix IA for the same IAID");
++			break;
++		}
+ 		if (ia == NULL) {
+ 			ia = realloc(ifo->ia,
+ 			    sizeof(*ifo->ia) * (ifo->ia_len + 1));
+@@ -1258,14 +1278,47 @@
+ 			}
+ 			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->ia_type = (uint16_t)i;
++			if (arg) {
++				ia->iaid[0] = iaid[0];
++				ia->iaid[1] = iaid[1];
++				ia->iaid[2] = iaid[2];
++				ia->iaid[3] = iaid[3];
++				ia->iaid_set = 1;
++			} else
++				ia->iaid_set = 0;
++			if (!ia->iaid_set ||
++			    p == NULL ||
++			    ia->ia_type == D6_OPTION_IA_TA)
++			{
++				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 && ia->ia_type == D6_OPTION_IA_PD) {
++					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)
++		if (ia->ia_type != D6_OPTION_IA_PD)
+ 			break;
+ 		for (p = fp; p; p = fp) {
+ 			fp = strwhite(p);
+@@ -1284,12 +1337,6 @@
+ 			np = strchr(p, '/');
+ 			if (np)
+ 				*np++ = '\0';
+-			if (strcmp(ifname, p) == 0) {
+-				syslog(LOG_ERR,
+-				    "%s: cannot assign IA_PD to itself",
+-				    ifname);
+-				goto err_sla;
+-			}
+ 			if (strlcpy(sla->ifname, p,
+ 			    sizeof(sla->ifname)) >= sizeof(sla->ifname))
+ 			{
+@@ -1853,6 +1900,12 @@
+ 		else
+ 			ifo->options &= ~DHCPCD_SLAACPRIVATE;
+ 		break;
++	case O_NOPFXDLG:
++		ifo->options |= DHCPCD_NOPFXDLG;
++		break;
++	case O_PFXDLGONLY:
++		ifo->options |= DHCPCD_PFXDLGONLY;
++		break;
+ 	default:
+ 		return 0;
+ 	}
+@@ -2124,6 +2177,10 @@
+ 				skip = 1;
+ 			continue;
+ 		}
++		/* Skip arping if we have selected a profile but not parsing
++		 * one. */
++		if (profile && !have_profile && strcmp(option, "arping") == 0)
++			continue;
+ 		if (skip)
+ 			continue;
+ 		parse_config_line(ctx, ifname, ifo, option, line, &ldop, &edop);
+diff -urN dhcpcd-6.4.0.org/if-options.h dhcpcd-6.4.0/if-options.h
+--- dhcpcd-6.4.0.org/if-options.h	2014-06-14 22:13:12.000000000 +0200
++++ dhcpcd-6.4.0/if-options.h	2014-07-05 21:47:22.000000000 +0200
+@@ -28,6 +28,7 @@
+ #ifndef IF_OPTIONS_H
+ #define IF_OPTIONS_H
+ 
++#include <sys/param.h>
+ #include <sys/socket.h>
+ #include <net/if.h>
+ #include <netinet/in.h>
+@@ -102,6 +103,8 @@
+ #define DHCPCD_IAID			(1ULL << 48)
+ #define DHCPCD_DHCP			(1ULL << 49)
+ #define DHCPCD_DHCP6			(1ULL << 50)
++#define DHCPCD_NOPFXDLG			(1ULL << 51)
++#define DHCPCD_PFXDLGONLY		(1ULL << 52)
+ 
+ extern const struct option cf_options[];
+ 
+@@ -115,6 +118,10 @@
+ struct if_ia {
+ 	uint8_t iaid[4];
+ #ifdef INET6
++	uint16_t ia_type;
++	uint8_t iaid_set;
++	struct in6_addr addr;
++	uint8_t prefix_len;
+ 	size_t sla_len;
+ 	struct if_sla *sla;
+ #endif
+@@ -128,13 +135,13 @@
+ struct if_options {
+ 	uint8_t iaid[4];
+ 	int metric;
+-	uint8_t requestmask[256 / 8];
+-	uint8_t requiremask[256 / 8];
+-	uint8_t nomask[256 / 8];
+-	uint8_t requestmask6[(UINT16_MAX + 1) / 8];
+-	uint8_t requiremask6[(UINT16_MAX + 1) / 8];
+-	uint8_t nomask6[(UINT16_MAX + 1) / 8];
+-	uint8_t dstmask[256 / 8];
++	uint8_t requestmask[256 / NBBY];
++	uint8_t requiremask[256 / NBBY];
++	uint8_t nomask[256 / NBBY];
++	uint8_t requestmask6[(UINT16_MAX + 1) / NBBY];
++	uint8_t requiremask6[(UINT16_MAX + 1) / NBBY];
++	uint8_t nomask6[(UINT16_MAX + 1) / NBBY];
++	uint8_t dstmask[256 / NBBY];
+ 	uint32_t leasetime;
+ 	time_t timeout;
+ 	time_t reboot;
+@@ -163,7 +170,6 @@
+ 	in_addr_t *arping;
+ 	char *fallback;
+ 
+-	uint16_t ia_type;
+ 	struct if_ia *ia;
+ 	size_t ia_len;
+ 
+diff -urN dhcpcd-6.4.0.org/ipv4.c dhcpcd-6.4.0/ipv4.c
+--- dhcpcd-6.4.0.org/ipv4.c	2014-06-14 22:13:12.000000000 +0200
++++ dhcpcd-6.4.0/ipv4.c	2014-07-05 21:47:22.000000000 +0200
+@@ -82,13 +82,13 @@
+ 		errno = EINVAL;
+ 		return -1;
+ 	}
+-	ocets = (cidr + 7) / 8;
++	ocets = (cidr + 7) / NBBY;
+ 
+ 	addr->s_addr = 0;
+ 	if (ocets > 0) {
+ 		memset(&addr->s_addr, 255, (size_t)ocets - 1);
+ 		memset((unsigned char *)&addr->s_addr + (ocets - 1),
+-		    (256 - (1 << (32 - cidr) % 8)), 1);
++		    (256 - (1 << (32 - cidr) % NBBY)), 1);
+ 	}
+ 
+ 	return 0;
+diff -urN dhcpcd-6.4.0.org/ipv6.c dhcpcd-6.4.0/ipv6.c
+--- dhcpcd-6.4.0.org/ipv6.c	2014-06-14 22:13:12.000000000 +0200
++++ dhcpcd-6.4.0/ipv6.c	2014-07-05 21:47:22.000000000 +0200
+@@ -652,7 +652,6 @@
+ 	i = 0;
+ 	TAILQ_FOREACH_SAFE(ap, addrs, next, apn) {
+ 		if (ap->prefix_vltime == 0) {
+-			TAILQ_REMOVE(addrs, ap, next);
+ 			if (ap->flags & IPV6_AF_ADDED) {
+ 				syslog(LOG_INFO, "%s: deleting address %s",
+ 				    ap->iface->name, ap->saddr);
+@@ -664,7 +663,12 @@
+ 			}
+ 			eloop_q_timeout_delete(ap->iface->ctx->eloop,
+ 			    0, NULL, ap);
+-			free(ap);
++			if (ap->flags & IPV6_AF_REQUEST) {
++				ap->flags &= ~IPV6_AF_ADDED;
++			} else {
++				TAILQ_REMOVE(addrs, ap, next);
++				free(ap);
++			}
+ 		} else if (!IN6_IS_ADDR_UNSPECIFIED(&ap->addr)) {
+ 			if (ap->flags & IPV6_AF_NEW)
+ 				i++;
+diff -urN dhcpcd-6.4.0.org/ipv6.h dhcpcd-6.4.0/ipv6.h
+--- dhcpcd-6.4.0.org/ipv6.h	2014-06-14 22:13:12.000000000 +0200
++++ dhcpcd-6.4.0/ipv6.h	2014-07-05 21:47:22.000000000 +0200
+@@ -90,7 +90,10 @@
+ 	short flags;
+ 	char saddr[INET6_ADDRSTRLEN];
+ 	uint8_t iaid[4];
++	uint16_t ia_type;
+ 	struct interface *delegating_iface;
++	uint8_t prefix_exclude_len;
++	struct in6_addr prefix_exclude;
+ 
+ 	void (*dadcallback)(void *);
+ 	int dadcounter;
+@@ -109,7 +112,8 @@
+ #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;
================================================================

---- gitweb:

http://git.pld-linux.org/gitweb.cgi/packages/dhcpcd.git/commitdiff/3f1392780d88daffa88a8a772186fc8884fe45c3



More information about the pld-cvs-commit mailing list