]> git.pld-linux.org Git - packages/dhcpcd.git/commitdiff
- rel 2; dhcpcd can become cpu hog on ipv4 renewal - fix that
authorArkadiusz Miśkiewicz <arekm@maven.pl>
Wed, 6 Nov 2019 13:01:57 +0000 (14:01 +0100)
committerArkadiusz Miśkiewicz <arekm@maven.pl>
Wed, 6 Nov 2019 13:01:57 +0000 (14:01 +0100)
dhcpcd-git.patch [new file with mode: 0644]
dhcpcd.spec

diff --git a/dhcpcd-git.patch b/dhcpcd-git.patch
new file mode 100644 (file)
index 0000000..9a77b9c
--- /dev/null
@@ -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@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@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@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;
index f6b06520e7bf3763496fd1fda8a8314df053a266..c5651ae420b8f15fae35a1d7047a0bdae931b716 100644 (file)
@@ -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 \
This page took 0.178767 seconds and 4 git commands to generate.