[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