Module Name:    src
Committed By:   roy
Date:           Wed Jul 24 09:57:43 UTC 2019

Modified Files:
        src/external/bsd/dhcpcd/dist/hooks: 20-resolv.conf 50-ntp.conf
        src/external/bsd/dhcpcd/dist/src: bpf.c dhcp.c dhcp6.c dhcpcd.c
            if-bsd.c if-options.c ipv6nd.c

Log Message:
Sync


To generate a diff of this commit:
cvs rdiff -u -r1.2 -r1.3 src/external/bsd/dhcpcd/dist/hooks/20-resolv.conf \
    src/external/bsd/dhcpcd/dist/hooks/50-ntp.conf
cvs rdiff -u -r1.9 -r1.10 src/external/bsd/dhcpcd/dist/src/bpf.c
cvs rdiff -u -r1.19 -r1.20 src/external/bsd/dhcpcd/dist/src/dhcp.c
cvs rdiff -u -r1.8 -r1.9 src/external/bsd/dhcpcd/dist/src/dhcp6.c \
    src/external/bsd/dhcpcd/dist/src/if-bsd.c
cvs rdiff -u -r1.20 -r1.21 src/external/bsd/dhcpcd/dist/src/dhcpcd.c
cvs rdiff -u -r1.14 -r1.15 src/external/bsd/dhcpcd/dist/src/if-options.c
cvs rdiff -u -r1.7 -r1.8 src/external/bsd/dhcpcd/dist/src/ipv6nd.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/external/bsd/dhcpcd/dist/hooks/20-resolv.conf
diff -u src/external/bsd/dhcpcd/dist/hooks/20-resolv.conf:1.2 src/external/bsd/dhcpcd/dist/hooks/20-resolv.conf:1.3
--- src/external/bsd/dhcpcd/dist/hooks/20-resolv.conf:1.2	Sat Sep 22 13:17:46 2018
+++ src/external/bsd/dhcpcd/dist/hooks/20-resolv.conf	Wed Jul 24 09:57:43 2019
@@ -19,6 +19,7 @@ build_resolv_conf()
 	interfaces=$(list_interfaces "$resolv_conf_dir")
 
 	# Build the resolv.conf
+	header=
 	if [ -n "$interfaces" ]; then
 		# Build the header
 		for x in ${interfaces}; do
@@ -69,30 +70,26 @@ build_resolv_conf()
 }
 
 # Extract any ND DNS options from the RA
-# For now, we ignore the lifetime of the DNS options unless they
-# are absent or zero.
-# In this case they are removed from consideration.
-# See draft-gont-6man-slaac-dns-config-issues-01 for issues
-# regarding DNS option lifetime in ND messages.
+# Obey the lifetimes
 eval_nd_dns()
 {
-	eval ltime=\$nd${i}_rdnss${j}_lifetime
-	if [ -z "$ltime" ] || [ "$ltime" = 0 ]; then
-		rdnss=
-	else
+
+	eval rdnsstime=\$nd${i}_rdnss${j}_lifetime
+	[ -z "$rdnsstime" ] && return 1
+	ltime=$(($rdnsstime - $offset))
+	if [ "$ltime" -gt 0 ]; then
 		eval rdnss=\$nd${i}_rdnss${j}_servers
+		[ -n "$rdnss" ] && new_rdnss="$new_rdnss${new_rdnss:+ }$rdnss"
 	fi
-	eval ltime=\$nd${i}_dnssl${j}_lifetime
-	if [ -z "$ltime" ] || [ "$ltime" = 0 ]; then
-		dnssl=
-	else
+
+	eval dnssltime=\$nd${i}_dnssl${j}_lifetime
+	[ -z "$dnssltime" ] && return 1
+	ltime=$(($dnssltime - $offset))
+	if [ "$ltime" -gt 0 ]; then
 		eval dnssl=\$nd${i}_dnssl${j}_search
+		[ -n "$dnssl" ] && new_dnssl="$new_dnssl${new_dnssl:+ }$dnssl"
 	fi
 
-	[ -z "${rdnss}${dnssl}" ] && return 1
-
-	[ -n "$rdnss" ] && new_rdnss="$new_rdnss${new_rdnss:+ }$rdnss"
-	[ -n "$dnssl" ] && new_dnssl="$new_dnssl${new_dnssl:+ }$dnssl"
 	j=$(($j + 1))
 	return 0
 }
@@ -106,12 +103,16 @@ add_resolv_conf()
 	i=1
 	j=1
 	while true; do
+		eval acquired=\$nd${i}_acquired
+		[ -z "$acquired" ] && break
+		eval now=\$nd${i}_now
+		[ -z "$now" ] && break
+		offset=$(($now - $acquired))
 		while true; do
 			eval_nd_dns || break
 		done
 		i=$(($i + 1))
 		j=1
-		eval_nd_dns || break
 	done
 	[ -n "$new_rdnss" ] && \
 	    new_domain_name_servers="$new_domain_name_servers${new_domain_name_servers:+ }$new_rdnss"
Index: src/external/bsd/dhcpcd/dist/hooks/50-ntp.conf
diff -u src/external/bsd/dhcpcd/dist/hooks/50-ntp.conf:1.2 src/external/bsd/dhcpcd/dist/hooks/50-ntp.conf:1.3
--- src/external/bsd/dhcpcd/dist/hooks/50-ntp.conf:1.2	Sat Sep 22 13:17:46 2018
+++ src/external/bsd/dhcpcd/dist/hooks/50-ntp.conf	Wed Jul 24 09:57:43 2019
@@ -62,6 +62,7 @@ build_ntp_conf()
 	# Build a list of interfaces
 	interfaces=$(list_interfaces "$ntp_conf_dir")
 
+	header=
 	servers=
 	if [ -n "$interfaces" ]; then
 		# Build the header

Index: src/external/bsd/dhcpcd/dist/src/bpf.c
diff -u src/external/bsd/dhcpcd/dist/src/bpf.c:1.9 src/external/bsd/dhcpcd/dist/src/bpf.c:1.10
--- src/external/bsd/dhcpcd/dist/src/bpf.c:1.9	Sat May  4 09:42:15 2019
+++ src/external/bsd/dhcpcd/dist/src/bpf.c	Wed Jul 24 09:57:43 2019
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
 /*
  * dhcpcd: BPF arp and bootp filtering
  * Copyright (c) 2006-2019 Roy Marples <r...@marples.name>
@@ -84,7 +85,7 @@ size_t
 bpf_frame_header_len(const struct interface *ifp)
 {
 
-	switch(ifp->family) {
+	switch (ifp->family) {
 	case ARPHRD_ETHER:
 		return sizeof(struct ether_header);
 	default:
@@ -92,6 +93,23 @@ bpf_frame_header_len(const struct interf
 	}
 }
 
+static const uint8_t etherbroadcastaddr[] =
+    { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+int
+bpf_frame_bcast(const struct interface *ifp, const char *frame)
+{
+
+	switch (ifp->family) {
+	case ARPHRD_ETHER:
+		return memcmp(frame +
+		    offsetof(struct ether_header, ether_dhost),
+		    etherbroadcastaddr, sizeof(etherbroadcastaddr));
+	default:
+		return -1;
+	}
+}
+
 #ifndef __linux__
 /* Linux is a special snowflake for opening, attaching and reading BPF.
  * See if-linux.c for the Linux specific BPF functions. */
@@ -227,8 +245,12 @@ bpf_read(struct interface *ifp, int fd, 
 		if (state->buffer_pos + packet.bh_caplen + packet.bh_hdrlen >
 		    state->buffer_len)
 			goto next; /* Packet beyond buffer, drop. */
-		payload = state->buffer + state->buffer_pos +
-		    packet.bh_hdrlen + fl;
+		payload = state->buffer + state->buffer_pos + packet.bh_hdrlen;
+		if (bpf_frame_bcast(ifp, payload) == 0)
+			*flags |= BPF_BCAST;
+		else
+			*flags &= ~BPF_BCAST;
+		payload += fl;
 		bytes = (ssize_t)packet.bh_caplen - fl;
 		if ((size_t)bytes > len)
 			bytes = (ssize_t)len;

Index: src/external/bsd/dhcpcd/dist/src/dhcp.c
diff -u src/external/bsd/dhcpcd/dist/src/dhcp.c:1.19 src/external/bsd/dhcpcd/dist/src/dhcp.c:1.20
--- src/external/bsd/dhcpcd/dist/src/dhcp.c:1.19	Sat May  4 09:42:15 2019
+++ src/external/bsd/dhcpcd/dist/src/dhcp.c	Wed Jul 24 09:57:43 2019
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
 /*
  * dhcpcd - DHCP client daemon
  * Copyright (c) 2006-2019 Roy Marples <r...@marples.name>
@@ -47,6 +48,7 @@
 #include <inttypes.h>
 #include <stdbool.h>
 #include <stddef.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
@@ -124,8 +126,9 @@ static const char * const dhcp_params[] 
 };
 
 static int dhcp_openbpf(struct interface *);
+static void dhcp_start1(void *);
 #ifdef ARP
-static void dhcp_arp_conflicted(struct arp_state *, const struct arp_msg *);
+static void dhcp_arp_found(struct arp_state *, const struct arp_msg *);
 #endif
 static void dhcp_handledhcp(struct interface *, struct bootp *, size_t,
     const struct in_addr *);
@@ -339,23 +342,25 @@ get_option_uint8(struct dhcpcd_ctx *ctx,
 }
 
 ssize_t
-decode_rfc3442(char *out, size_t len, const uint8_t *p, size_t pl)
+print_rfc3442(FILE *fp, const uint8_t *data, size_t data_len)
 {
-	const uint8_t *e;
-	size_t bytes = 0, ocets;
-	int b;
+	const uint8_t *p = data, *e;
+	size_t ocets;
 	uint8_t cidr;
 	struct in_addr addr;
-	char *o = out;
 
 	/* Minimum is 5 -first is CIDR and a router length of 4 */
-	if (pl < 5) {
+	if (data_len < 5) {
 		errno = EINVAL;
 		return -1;
 	}
 
-	e = p + pl;
+	e = p + data_len;
 	while (p < e) {
+		if (p != data) {
+			if (fputc(' ', fp) == EOF)
+				return -1;
+		}
 		cidr = *p++;
 		if (cidr > 32) {
 			errno = EINVAL;
@@ -366,45 +371,29 @@ decode_rfc3442(char *out, size_t len, co
 			errno = ERANGE;
 			return -1;
 		}
-		if (!out) {
-			p += 4 + ocets;
-			bytes += ((4 * 4) * 2) + 4;
-			continue;
-		}
-		if ((((4 * 4) * 2) + 4) > len) {
-			errno = ENOBUFS;
-			return -1;
-		}
-		if (o != out) {
-			*o++ = ' ';
-			len--;
-		}
 		/* If we have ocets then we have a destination and netmask */
+		addr.s_addr = 0;
 		if (ocets > 0) {
-			addr.s_addr = 0;
 			memcpy(&addr.s_addr, p, ocets);
-			b = snprintf(o, len, "%s/%d", inet_ntoa(addr), cidr);
 			p += ocets;
-		} else
-			b = snprintf(o, len, "0.0.0.0/0");
-		o += b;
-		len -= (size_t)b;
+		}
+		if (fprintf(fp, "%s/%d", inet_ntoa(addr), cidr) == -1)
+			return -1;
 
 		/* Finally, snag the router */
 		memcpy(&addr.s_addr, p, 4);
 		p += 4;
-		b = snprintf(o, len, " %s", inet_ntoa(addr));
-		o += b;
-		len -= (size_t)b;
+		if (fprintf(fp, " %s", inet_ntoa(addr)) == -1)
+			return -1;
 	}
 
-	if (out)
-		return o - out;
-	return (ssize_t)bytes;
+	if (fputc('\0', fp) == EOF)
+		return -1;
+	return 1;
 }
 
 static int
-decode_rfc3442_rt(struct rt_head *routes, struct interface *ifp,
+decode_rfc3442_rt(rb_tree_t *routes, struct interface *ifp,
     const uint8_t *data, size_t dl, const struct bootp *bootp)
 {
 	const uint8_t *p = data;
@@ -467,22 +456,18 @@ decode_rfc3442_rt(struct rt_head *routes
 		sa_in_init(&rt->rt_dest, &dest);
 		sa_in_init(&rt->rt_netmask, &netmask);
 		sa_in_init(&rt->rt_gateway, &gateway);
-
-		TAILQ_INSERT_TAIL(routes, rt, rt_next);
-		n++;
+		if (rt_proto_add(routes, rt))
+			n = 1;
 	}
 	return n;
 }
 
-char *
-decode_rfc3361(const uint8_t *data, size_t dl)
+ssize_t
+print_rfc3361(FILE *fp, const uint8_t *data, size_t dl)
 {
 	uint8_t enc;
-	size_t l;
-	ssize_t r;
-	char *sip = NULL;
+	char sip[NS_MAXDNAME];
 	struct in_addr addr;
-	char *p;
 
 	if (dl < 2) {
 		errno = EINVAL;
@@ -493,13 +478,10 @@ decode_rfc3361(const uint8_t *data, size
 	dl--;
 	switch (enc) {
 	case 0:
-		if ((r = decode_rfc1035(NULL, 0, data, dl)) > 0) {
-			l = (size_t)r + 1;
-			sip = malloc(l);
-			if (sip == NULL)
-				return 0;
-			decode_rfc1035(sip, l, data, dl);
-		}
+		if (decode_rfc1035(sip, sizeof(sip), data, dl) == -1)
+			return -1;
+		if (efprintf(fp, "%s", sip) == -1)
+			return -1;
 		break;
 	case 1:
 		if (dl == 0 || dl % 4 != 0) {
@@ -507,25 +489,27 @@ decode_rfc3361(const uint8_t *data, size
 			break;
 		}
 		addr.s_addr = INADDR_BROADCAST;
-		l = ((dl / sizeof(addr.s_addr)) * ((4 * 4) + 1)) + 1;
-		sip = p = malloc(l);
-		if (sip == NULL)
-			return 0;
-		while (dl != 0) {
+		for (;
+		    dl != 0;
+		    data += sizeof(addr.s_addr), dl -= sizeof(addr.s_addr))
+		{
 			memcpy(&addr.s_addr, data, sizeof(addr.s_addr));
-			data += sizeof(addr.s_addr);
-			p += snprintf(p, l - (size_t)(p - sip),
-			    "%s ", inet_ntoa(addr));
-			dl -= sizeof(addr.s_addr);
+			if (fprintf(fp, "%s", inet_ntoa(addr)) == -1)
+				return -1;
+			if (dl != 0) {
+				if (fputc(' ', fp) == EOF)
+					return -1;
+			}
 		}
-		*--p = '\0';
+		if (fputc('\0', fp) == EOF)
+			return -1;
 		break;
 	default:
 		errno = EINVAL;
 		return 0;
 	}
 
-	return sip;
+	return 1;
 }
 
 static char *
@@ -581,7 +565,7 @@ route_netmask(uint32_t ip_in)
  * If we have a CSR then we only use that.
  * Otherwise we add static routes and then routers. */
 static int
-get_option_routes(struct rt_head *routes, struct interface *ifp,
+get_option_routes(rb_tree_t *routes, struct interface *ifp,
     const struct bootp *bootp, size_t bootp_len)
 {
 	struct if_options *ifo = ifp->options;
@@ -656,9 +640,8 @@ get_option_routes(struct rt_head *routes
 			sa_in_init(&rt->rt_dest, &dest);
 			sa_in_init(&rt->rt_netmask, &netmask);
 			sa_in_init(&rt->rt_gateway, &gateway);
-
-			TAILQ_INSERT_TAIL(routes, rt, rt_next);
-			n++;
+			if (rt_proto_add(routes, rt))
+				n++;
 		}
 	}
 
@@ -667,7 +650,7 @@ get_option_routes(struct rt_head *routes
 		p = get_option(ifp->ctx, bootp, bootp_len, DHO_ROUTER, &len);
 	else
 		p = NULL;
-	if (p) {
+	if (p && len % 4 == 0) {
 		e = p + len;
 		dest.s_addr = INADDR_ANY;
 		netmask.s_addr = INADDR_ANY;
@@ -679,8 +662,8 @@ get_option_routes(struct rt_head *routes
 			sa_in_init(&rt->rt_dest, &dest);
 			sa_in_init(&rt->rt_netmask, &netmask);
 			sa_in_init(&rt->rt_gateway, &gateway);
-			TAILQ_INSERT_TAIL(routes, rt, rt_next);
-			n++;
+			if (rt_proto_add(routes, rt))
+				n++;
 		}
 	}
 
@@ -707,7 +690,7 @@ dhcp_get_mtu(const struct interface *ifp
 /* Grab our routers from the DHCP message and apply any MTU value
  * the message contains */
 int
-dhcp_get_routes(struct rt_head *routes, struct interface *ifp)
+dhcp_get_routes(rb_tree_t *routes, struct interface *ifp)
 {
 	const struct dhcp_state *state;
 
@@ -1299,9 +1282,8 @@ dhcp_getoption(struct dhcpcd_ctx *ctx,
 }
 
 ssize_t
-dhcp_env(char **env, const char *prefix,
-    const struct bootp *bootp, size_t bootp_len,
-    const struct interface *ifp)
+dhcp_env(FILE *fenv, const char *prefix, const struct interface *ifp,
+    const struct bootp *bootp, size_t bootp_len)
 {
 	const struct if_options *ifo;
 	const uint8_t *p;
@@ -1309,109 +1291,73 @@ dhcp_env(char **env, const char *prefix,
 	struct in_addr net;
 	struct in_addr brd;
 	struct dhcp_opt *opt, *vo;
-	size_t e, i, pl;
-	char **ep;
-	char cidr[4], safe[(BOOTP_FILE_LEN * 4) + 1];
+	size_t i, pl;
+	char safe[(BOOTP_FILE_LEN * 4) + 1];
 	uint8_t overl = 0;
 	uint32_t en;
 
-	e = 0;
 	ifo = ifp->options;
 	if (get_option_uint8(ifp->ctx, &overl, bootp, bootp_len,
 	    DHO_OPTSOVERLOADED) == -1)
 		overl = 0;
 
-	if (env == NULL) {
-		if (bootp->yiaddr || bootp->ciaddr)
-			e += 5;
-		if (*bootp->file && !(overl & 1))
-			e++;
-		if (*bootp->sname && !(overl & 2))
-			e++;
-		for (i = 0, opt = ifp->ctx->dhcp_opts;
-		    i < ifp->ctx->dhcp_opts_len;
-		    i++, opt++)
-		{
-			if (has_option_mask(ifo->nomask, opt->option))
-				continue;
-			if (dhcp_getoverride(ifo, opt->option))
-				continue;
-			p = get_option(ifp->ctx, bootp, bootp_len,
-			    opt->option, &pl);
-			if (!p)
-				continue;
-			e += dhcp_envoption(ifp->ctx, NULL, NULL, ifp->name,
-			    opt, dhcp_getoption, p, pl);
-		}
-		for (i = 0, opt = ifo->dhcp_override;
-		    i < ifo->dhcp_override_len;
-		    i++, opt++)
-		{
-			if (has_option_mask(ifo->nomask, opt->option))
-				continue;
-			p = get_option(ifp->ctx, bootp, bootp_len,
-			    opt->option, &pl);
-			if (!p)
-				continue;
-			e += dhcp_envoption(ifp->ctx, NULL, NULL, ifp->name,
-			    opt, dhcp_getoption, p, pl);
-		}
-		return (ssize_t)e;
-	}
-
-	ep = env;
 	if (bootp->yiaddr || bootp->ciaddr) {
 		/* Set some useful variables that we derive from the DHCP
 		 * message but are not necessarily in the options */
 		addr.s_addr = bootp->yiaddr ? bootp->yiaddr : bootp->ciaddr;
-		addvar(&ep, prefix, "ip_address", inet_ntoa(addr));
+		if (efprintf(fenv, "%s_ip_address=%s",
+		    prefix, inet_ntoa(addr)) == -1)
+			return -1;
 		if (get_option_addr(ifp->ctx, &net,
-		    bootp, bootp_len, DHO_SUBNETMASK) == -1)
-		{
+		    bootp, bootp_len, DHO_SUBNETMASK) == -1) {
 			net.s_addr = ipv4_getnetmask(addr.s_addr);
-			addvar(&ep, prefix,
-			    "subnet_mask", inet_ntoa(net));
+			if (efprintf(fenv, "%s_subnet_mask=%s",
+			    prefix, inet_ntoa(net)) == -1)
+				return -1;
 		}
-		snprintf(cidr, sizeof(cidr), "%d", inet_ntocidr(net));
-		addvar(&ep, prefix, "subnet_cidr", cidr);
+		if (efprintf(fenv, "%s_subnet_cidr=%d",
+		    prefix, inet_ntocidr(net))== -1)
+			return -1;
 		if (get_option_addr(ifp->ctx, &brd,
 		    bootp, bootp_len, DHO_BROADCAST) == -1)
 		{
 			brd.s_addr = addr.s_addr | ~net.s_addr;
-			addvar(&ep, prefix,
-			    "broadcast_address", inet_ntoa(brd));
+			if (efprintf(fenv, "%s_broadcast_address=%s",
+			    prefix, inet_ntoa(brd)) == -1)
+				return -1;
 		}
 		addr.s_addr = bootp->yiaddr & net.s_addr;
-		addvar(&ep, prefix,
-		    "network_number", inet_ntoa(addr));
+		if (efprintf(fenv, "%s_network_number=%s",
+		    prefix, inet_ntoa(addr)) == -1)
+			return -1;
 	}
 
 	if (*bootp->file && !(overl & 1)) {
 		print_string(safe, sizeof(safe), OT_STRING,
 		    bootp->file, sizeof(bootp->file));
-		addvar(&ep, prefix, "filename", safe);
+		if (efprintf(fenv, "%s_filename=%s", prefix, safe) == -1)
+			return -1;
 	}
 	if (*bootp->sname && !(overl & 2)) {
 		print_string(safe, sizeof(safe), OT_STRING | OT_DOMAIN,
 		    bootp->sname, sizeof(bootp->sname));
-		addvar(&ep, prefix, "server_name", safe);
+		if (efprintf(fenv, "%s_server_name=%s", prefix, safe) == -1)
+			return -1;
 	}
 
 	/* Zero our indexes */
-	if (env) {
-		for (i = 0, opt = ifp->ctx->dhcp_opts;
-		    i < ifp->ctx->dhcp_opts_len;
-		    i++, opt++)
-			dhcp_zero_index(opt);
-		for (i = 0, opt = ifp->options->dhcp_override;
-		    i < ifp->options->dhcp_override_len;
-		    i++, opt++)
-			dhcp_zero_index(opt);
-		for (i = 0, opt = ifp->ctx->vivso;
-		    i < ifp->ctx->vivso_len;
-		    i++, opt++)
-			dhcp_zero_index(opt);
-	}
+	for (i = 0, opt = ifp->ctx->dhcp_opts;
+	    i < ifp->ctx->dhcp_opts_len;
+	    i++, opt++)
+		dhcp_zero_index(opt);
+	for (i = 0, opt = ifp->options->dhcp_override;
+	    i < ifp->options->dhcp_override_len;
+	    i++, opt++)
+		dhcp_zero_index(opt);
+	for (i = 0, opt = ifp->ctx->vivso;
+	    i < ifp->ctx->vivso_len;
+	    i++, opt++)
+		dhcp_zero_index(opt);
 
 	for (i = 0, opt = ifp->ctx->dhcp_opts;
 	    i < ifp->ctx->dhcp_opts_len;
@@ -1424,7 +1370,7 @@ dhcp_env(char **env, const char *prefix,
 		p = get_option(ifp->ctx, bootp, bootp_len, opt->option, &pl);
 		if (p == NULL)
 			continue;
-		ep += dhcp_envoption(ifp->ctx, ep, prefix, ifp->name,
+		dhcp_envoption(ifp->ctx, fenv, prefix, ifp->name,
 		    opt, dhcp_getoption, p, pl);
 
 		if (opt->option != DHO_VIVSO || pl <= (int)sizeof(uint32_t))
@@ -1437,7 +1383,7 @@ dhcp_env(char **env, const char *prefix,
 		/* Skip over en + total size */
 		p += sizeof(en) + 1;
 		pl -= sizeof(en) + 1;
-		ep += dhcp_envoption(ifp->ctx, ep, prefix, ifp->name,
+		dhcp_envoption(ifp->ctx, fenv, prefix, ifp->name,
 		    vo, dhcp_getoption, p, pl);
 	}
 
@@ -1450,11 +1396,11 @@ dhcp_env(char **env, const char *prefix,
 		p = get_option(ifp->ctx, bootp, bootp_len, opt->option, &pl);
 		if (p == NULL)
 			continue;
-		ep += dhcp_envoption(ifp->ctx, ep, prefix, ifp->name,
+		dhcp_envoption(ifp->ctx, fenv, prefix, ifp->name,
 		    opt, dhcp_getoption, p, pl);
 	}
 
-	return ep - env;
+	return 1;
 }
 
 static void
@@ -1497,7 +1443,7 @@ get_lease(struct interface *ifp,
 	}
 	if (get_option_uint32(ctx, &lease->leasetime,
 	    bootp, len, DHO_LEASETIME) != 0)
-		lease->leasetime = ~0U; /* Default to infinite lease */
+		lease->leasetime = DHCP_INFINITE_LIFETIME;
 	if (get_option_uint32(ctx, &lease->renewaltime,
 	    bootp, len, DHO_RENEWALTIME) != 0)
 		lease->renewaltime = 0;
@@ -1923,35 +1869,6 @@ dhcp_request(void *arg)
 	send_request(ifp);
 }
 
-static int
-dhcp_leaseextend(struct interface *ifp)
-{
-
-#ifdef ARP
-	if (ifp->options->options & DHCPCD_ARP) {
-		const struct dhcp_state *state;
-		struct arp_state *astate;
-
-		state = D_CSTATE(ifp);
-		if ((astate = arp_new(ifp, &state->lease.addr)) == NULL)
-			return -1;
-		astate->conflicted_cb = dhcp_arp_conflicted;
-
-#ifndef KERNEL_RFC5227
-		if (arp_open(ifp) == -1)
-			return -1;
-#endif
-
-		logwarnx("%s: extending lease until DaD failure or DHCP",
-		    ifp->name);
-		return 0;
-	}
-#endif
-
-	logwarnx("%s: extending lease", ifp->name);
-	return 0;
-}
-
 static void
 dhcp_expire1(struct interface *ifp)
 {
@@ -1970,12 +1887,12 @@ dhcp_expire(void *arg)
 {
 	struct interface *ifp = arg;
 
-	logerrx("%s: DHCP lease expired", ifp->name);
 	if (ifp->options->options & DHCPCD_LASTLEASE_EXTEND) {
-		if (dhcp_leaseextend(ifp) == 0)
-			return;
-		logerr(__func__);
+		logwarnx("%s: DHCP lease expired, extending lease", ifp->name);
+		return;
 	}
+
+	logerrx("%s: DHCP lease expired", ifp->name);
 	dhcp_expire1(ifp);
 }
 
@@ -2041,41 +1958,18 @@ dhcp_rebind(void *arg)
 	send_rebind(ifp);
 }
 
-#ifdef ARP
 static void
-dhcp_arp_probed(struct arp_state *astate)
+dhcp_finish_dad(struct interface *ifp, struct in_addr *ia)
 {
-	struct interface *ifp;
-	struct dhcp_state *state;
-	struct if_options *ifo;
+	struct dhcp_state *state = D_STATE(ifp);
 
-	ifp = astate->iface;
-	state = D_STATE(ifp);
-	ifo = ifp->options;
-#ifdef ARPING
-	if (ifo->arping_len && state->arping_index < ifo->arping_len) {
-		/* We didn't find a profile for this
-		 * address or hwaddr, so move to the next
-		 * arping profile */
-		if (++state->arping_index < ifo->arping_len) {
-			astate->addr.s_addr =
-			    ifo->arping[state->arping_index];
-			arp_probe(astate);
-			return;
-		}
-		arp_free(astate);
-		dhcpcd_startinterface(ifp);
+	if (state->state != DHS_PROBE)
 		return;
-	}
-#endif
-
-	/* Already bound so DAD has worked */
-	if (state->state == DHS_BOUND)
+	if (state->offer == NULL || state->offer->yiaddr != ia->s_addr)
 		return;
 
-	logdebugx("%s: DAD completed for %s",
-	    ifp->name, inet_ntoa(astate->addr));
-	if (!(ifo->options & DHCPCD_INFORM))
+	logdebugx("%s: DAD completed for %s", ifp->name, inet_ntoa(*ia));
+	if (!(ifp->options->options & DHCPCD_INFORM))
 		dhcp_bind(ifp);
 #ifndef IN_IFF_DUPLICATED
 	else {
@@ -2087,7 +1981,7 @@ dhcp_arp_probed(struct arp_state *astate
 		state->new = state->offer;
 		state->new_len = state->offer_len;
 		get_lease(ifp, &state->lease, state->new, state->new_len);
-		ipv4_applyaddr(astate->iface);
+		ipv4_applyaddr(ifp);
 		state->new = bootp;
 		state->new_len = len;
 	}
@@ -2102,23 +1996,80 @@ dhcp_arp_probed(struct arp_state *astate
 	ipv4ll_drop(ifp);
 #endif
 
-	if (ifo->options & DHCPCD_INFORM)
+	if (ifp->options->options & DHCPCD_INFORM)
 		dhcp_inform(ifp);
 }
 
+
+static void
+dhcp_addr_duplicated(struct interface *ifp, struct in_addr *ia)
+{
+	struct dhcp_state *state = D_STATE(ifp);
+#ifdef IN_IFF_DUPLICATED
+	struct ipv4_addr *iap;
+#endif
+
+	if ((state->offer == NULL || state->offer->yiaddr != ia->s_addr) &&
+	    !IN_ARE_ADDR_EQUAL(ia, &state->lease.addr))
+		return;
+
+	/* RFC 2131 3.1.5, Client-server interaction */
+	logerrx("%s: DAD detected %s", ifp->name, inet_ntoa(*ia));
+	unlink(state->leasefile);
+	if (!(ifp->options->options & DHCPCD_STATIC) && !state->lease.frominfo)
+		dhcp_decline(ifp);
+#ifdef IN_IFF_DUPLICATED
+	if ((iap = ipv4_iffindaddr(ifp, ia, NULL)) != NULL)
+		ipv4_deladdr(iap, 0);
+#endif
+	eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
+	eloop_timeout_add_sec(ifp->ctx->eloop,
+	    DHCP_RAND_MAX, dhcp_discover, ifp);
+}
+
+#ifdef ARP
 static void
-dhcp_arp_conflicted(struct arp_state *astate, const struct arp_msg *amsg)
+dhcp_arp_not_found(struct arp_state *astate)
 {
 	struct interface *ifp;
 	struct dhcp_state *state;
-#ifdef ARPING
 	struct if_options *ifo;
-#endif
 
 	ifp = astate->iface;
 	state = D_STATE(ifp);
+	ifo = ifp->options;
+#ifdef ARPING
+	if (ifo->arping_len && state->arping_index < ifo->arping_len) {
+		/* We didn't find a profile for this
+		 * address or hwaddr, so move to the next
+		 * arping profile */
+		if (++state->arping_index < ifo->arping_len) {
+			astate->addr.s_addr =
+			    ifo->arping[state->arping_index];
+			arp_probe(astate);
+			return;
+		}
+		arp_free(astate);
+		dhcpcd_startinterface(ifp);
+		return;
+	}
+#endif
 
+	dhcp_finish_dad(ifp, &astate->addr);
+}
+
+static void
+dhcp_arp_found(struct arp_state *astate, const struct arp_msg *amsg)
+{
+	struct in_addr addr;
 #ifdef ARPING
+	struct interface *ifp;
+	struct dhcp_state *state;
+	struct if_options *ifo;
+
+	ifp = astate->iface;
+	state = D_STATE(ifp);
+
 	ifo = ifp->options;
 	if (state->arping_index != -1 &&
 	    state->arping_index < ifo->arping_len &&
@@ -2127,17 +2078,14 @@ dhcp_arp_conflicted(struct arp_state *as
 	{
 		char buf[HWADDR_LEN * 3];
 
-		astate->failed.s_addr = ifo->arping[state->arping_index];
-		arp_report_conflicted(astate, amsg);
 		hwaddr_ntoa(amsg->sha, ifp->hwlen, buf, sizeof(buf));
 		if (dhcpcd_selectprofile(ifp, buf) == -1 &&
-		    dhcpcd_selectprofile(ifp,
-		        inet_ntoa(astate->failed)) == -1)
+		    dhcpcd_selectprofile(ifp, inet_ntoa(amsg->sip)) == -1)
 		{
 			/* We didn't find a profile for this
 			 * address or hwaddr, so move to the next
 			 * arping profile */
-			dhcp_arp_probed(astate);
+			dhcp_arp_not_found(astate);
 			return;
 		}
 		arp_free(astate);
@@ -2147,65 +2095,20 @@ dhcp_arp_conflicted(struct arp_state *as
 	}
 #endif
 
-	/* RFC 2131 3.1.5, Client-server interaction
-	 * NULL amsg means IN_IFF_DUPLICATED */
-	if (amsg == NULL || (state->offer &&
-	    (amsg->sip.s_addr == state->offer->yiaddr ||
-	    (amsg->sip.s_addr == 0 &&
-	    amsg->tip.s_addr == state->offer->yiaddr))))
-	{
-#ifdef IN_IFF_DUPLICATED
-		struct ipv4_addr *ia;
-#endif
-
-		if (amsg)
-			astate->failed.s_addr = state->offer->yiaddr;
-		else
-			astate->failed = astate->addr;
-		arp_report_conflicted(astate, amsg);
-		unlink(state->leasefile);
-#ifdef ARP
-		if (!(ifp->options->options & DHCPCD_STATIC) &&
-		    !state->lease.frominfo)
-			dhcp_decline(ifp);
-#endif
-#ifdef IN_IFF_DUPLICATED
-		if ((ia = ipv4_iffindaddr(ifp, &astate->addr, NULL)) != NULL)
-			ipv4_deladdr(ia, 1);
-#endif
-		arp_free(astate);
-		eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
-		eloop_timeout_add_sec(ifp->ctx->eloop,
-		    DHCP_RAND_MAX, dhcp_discover, ifp);
-		return;
-	}
-
-	/* Bound address */
-	if (amsg && state->addr &&
-	    amsg->sip.s_addr == state->addr->addr.s_addr)
-	{
-		astate->failed = state->addr->addr;
-		arp_report_conflicted(astate, amsg);
-		if (state->state == DHS_BOUND) {
-			/* For now, just report the duplicated address */
-		} else {
-			arp_free(astate);
-			dhcp_expire1(ifp);
-		}
-		return;
-	}
+	addr = astate->addr;
+	arp_free(astate);
+	dhcp_addr_duplicated(ifp, &addr);
 }
 
+#ifdef KERNEL_RFC5227
 static void
 dhcp_arp_announced(struct arp_state *state)
 {
 
-// TODO: DHCP addresses handle ACD?
-//#ifdef KERNEL_RFC5227
 	arp_free(state);
-//#endif
 }
-#endif
+#endif /* KERNEL_RFC5227 */
+#endif /* ARP */
 
 void
 dhcp_bind(struct interface *ifp)
@@ -2232,17 +2135,17 @@ dhcp_bind(struct interface *ifp)
 		loginfox("%s: using static address %s/%d",
 		    ifp->name, inet_ntoa(lease->addr),
 		    inet_ntocidr(lease->mask));
-		lease->leasetime = ~0U;
+		lease->leasetime = DHCP_INFINITE_LIFETIME;
 		state->reason = "STATIC";
 	} else if (ifo->options & DHCPCD_INFORM) {
 		loginfox("%s: received approval for %s",
 		    ifp->name, inet_ntoa(lease->addr));
-		lease->leasetime = ~0U;
+		lease->leasetime = DHCP_INFINITE_LIFETIME;
 		state->reason = "INFORM";
 	} else {
 		if (lease->frominfo)
 			state->reason = "TIMEOUT";
-		if (lease->leasetime == ~0U) {
+		if (lease->leasetime == DHCP_INFINITE_LIFETIME) {
 			lease->renewaltime =
 			    lease->rebindtime =
 			    lease->leasetime;
@@ -2305,7 +2208,7 @@ dhcp_bind(struct interface *ifp)
 		else
 			state->reason = "BOUND";
 	}
-	if (lease->leasetime == ~0U)
+	if (lease->leasetime == DHCP_INFINITE_LIFETIME)
 		lease->renewaltime = lease->rebindtime = lease->leasetime;
 	else {
 		eloop_timeout_add_sec(ctx->eloop,
@@ -2359,12 +2262,6 @@ dhcp_lastlease(void *arg)
 	if (ifp->ctx->options & DHCPCD_FORKED)
 		return;
 	state->interval = 0;
-	if (ifp->options->options & DHCPCD_LASTLEASE_EXTEND &&
-	    dhcp_leaseextend(ifp) == -1)
-	{
-		logerr("%s: %s", ifp->name, __func__);
-		dhcp_expire(ifp);
-	}
 	dhcp_discover(ifp);
 }
 
@@ -2397,17 +2294,32 @@ dhcp_message_new(struct bootp **bootp,
 }
 
 #ifdef ARP
+#ifndef KERNEL_RFC5227
+static void
+dhcp_arp_defend_failed(struct arp_state *astate)
+{
+
+	dhcp_drop(astate->iface, "EXPIRED");
+	dhcp_start1(astate->iface);
+}
+#endif
+
 static struct arp_state *
 dhcp_arp_new(struct interface *ifp, struct in_addr *addr)
 {
 	struct arp_state *astate;
+
 	astate = arp_new(ifp, addr);
 	if (astate == NULL)
 		return NULL;
 
-	astate->probed_cb = dhcp_arp_probed;
-	astate->conflicted_cb = dhcp_arp_conflicted;
+	astate->found_cb = dhcp_arp_found;
+	astate->not_found_cb = dhcp_arp_not_found;
+#ifdef KERNEL_RFC5227
 	astate->announced_cb = dhcp_arp_announced;
+#else
+	astate->defend_failed_cb = dhcp_arp_defend_failed;
+#endif
 	return astate;
 }
 
@@ -2417,7 +2329,6 @@ dhcp_arp_address(struct interface *ifp)
 	struct dhcp_state *state;
 	struct in_addr addr;
 	struct ipv4_addr *ia;
-	struct arp_state *astate;
 
 	eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
 
@@ -2427,10 +2338,6 @@ dhcp_arp_address(struct interface *ifp)
 	/* If the interface already has the address configured
 	 * then we can't ARP for duplicate detection. */
 	ia = ipv4_iffindaddr(ifp, &addr, NULL);
-	astate = dhcp_arp_new(ifp, &addr);
-	if (astate == NULL)
-		return -1;
-
 #ifdef IN_IFF_NOTUSEABLE
 	if (ia == NULL || ia->addr_flags & IN_IFF_NOTUSEABLE) {
 		state->state = DHS_PROBE;
@@ -2439,7 +2346,8 @@ dhcp_arp_address(struct interface *ifp)
 
 			get_lease(ifp, &l, state->offer, state->offer_len);
 			/* Add the address now, let the kernel handle DAD. */
-			ipv4_addaddr(ifp, &l.addr, &l.mask, &l.brd);
+			ipv4_addaddr(ifp, &l.addr, &l.mask, &l.brd,
+			    l.leasetime, l.rebindtime);
 		} else
 			loginfox("%s: waiting for DAD on %s",
 			    ifp->name, inet_ntoa(addr));
@@ -2447,8 +2355,13 @@ dhcp_arp_address(struct interface *ifp)
 	}
 #else
 	if (ifp->options->options & DHCPCD_ARP && ia == NULL) {
+		struct arp_state *astate;
 		struct dhcp_lease l;
 
+		astate = dhcp_arp_new(ifp, &addr);
+		if (astate == NULL)
+			return -1;
+
 		state->state = DHS_PROBE;
 		get_lease(ifp, &l, state->offer, state->offer_len);
 		loginfox("%s: probing address %s/%d",
@@ -2705,9 +2618,14 @@ dhcp_drop(struct interface *ifp, const c
 		return;
 	}
 
+#ifdef ARP
+	if (state->addr != NULL)
+		arp_freeaddr(ifp, &state->addr->addr);
+#endif
 #ifdef ARPING
 	state->arping_index = -1;
 #endif
+
 	if (ifp->options->options & DHCPCD_RELEASE &&
 	    !(ifp->options->options & DHCPCD_INFORM))
 	{
@@ -3653,15 +3571,9 @@ dhcp_init(struct interface *ifp)
 	const struct if_options *ifo;
 	uint8_t len;
 	char buf[(sizeof(ifo->clientid) - 1) * 3];
-	int r;
 
-	r = dhcp_initstate(ifp);
-	if (r == -1)
+	if (dhcp_initstate(ifp) == -1)
 		return -1;
-	else if (r == 1) {
-		/* Now is a good time to find IPv4 routes */
-		if_initrt(ifp->ctx, AF_INET);
-	}
 
 	state = D_STATE(ifp);
 	state->state = DHS_INIT;
@@ -3771,7 +3683,7 @@ dhcp_start1(void *arg)
 
 		astate = dhcp_arp_new(ifp, NULL);
 		if (astate)
-			dhcp_arp_probed(astate);
+			dhcp_arp_not_found(astate);
 		return;
 	}
 #endif
@@ -3848,7 +3760,7 @@ dhcp_start1(void *arg)
 			state->offer = NULL;
 			state->offer_len = 0;
 		} else if (!(ifo->options & DHCPCD_LASTLEASE_EXTEND) &&
-		    state->lease.leasetime != ~0U &&
+		    state->lease.leasetime != DHCP_INFINITE_LIFETIME &&
 		    stat(state->leasefile, &st) == 0)
 		{
 			time_t now;
@@ -4023,8 +3935,10 @@ dhcp_handleifa(int cmd, struct ipv4_addr
 		return;
 
 #ifdef IN_IFF_NOTUSEABLE
-	if (ia->addr_flags & IN_IFF_NOTUSEABLE)
-		return;
+	if (!(ia->addr_flags & IN_IFF_NOTUSEABLE))
+		dhcp_finish_dad(ifp, &ia->addr);
+	else if (ia->addr_flags & IN_IFF_DUPLICATED)
+		dhcp_addr_duplicated(ifp, &ia->addr);
 #endif
 
 	ifo = ifp->options;

Index: src/external/bsd/dhcpcd/dist/src/dhcp6.c
diff -u src/external/bsd/dhcpcd/dist/src/dhcp6.c:1.8 src/external/bsd/dhcpcd/dist/src/dhcp6.c:1.9
--- src/external/bsd/dhcpcd/dist/src/dhcp6.c:1.8	Wed Jun 26 17:47:47 2019
+++ src/external/bsd/dhcpcd/dist/src/dhcp6.c	Wed Jul 24 09:57:43 2019
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
 /*
  * dhcpcd - DHCP client daemon
  * Copyright (c) 2006-2019 Roy Marples <r...@marples.name>
@@ -582,10 +583,14 @@ dhcp6_delegateaddr(struct in6_addr *addr
 
 #define BIT(n) (1UL << (n))
 #define BIT_MASK(len) (BIT(len) - 1)
-		if (ia->sla_max == 0)
+		if (ia->sla_max == 0) {
 			/* Work out the real sla_max from our bits used */
-			ia->sla_max = (uint32_t)BIT_MASK(asla.prefix_len -
-			    prefix->prefix_len);
+			bits = asla.prefix_len - prefix->prefix_len;
+			/* Make static analysis happy.
+			 * Bits cannot be bigger than 32 thanks to fls32. */
+			assert(bits <= 32);
+			ia->sla_max = (uint32_t)BIT_MASK(bits);
+		}
 	}
 
 	if (ipv6_userprefix(&prefix->prefix, prefix->prefix_len,
@@ -2845,26 +2850,11 @@ dhcp6_delegate_prefix(struct interface *
 			struct dhcp6_state *s = D6_STATE(ifd);
 
 			ipv6_addaddrs(&s->addrs);
-
-			/*
-			 * Can't add routes here because that will trigger
-			 * interface sorting which may break the current
-			 * enumeration.
-			 * This doesn't really matter thanks to DaD because
-			 * calling the script will be delayed and routes
-			 * will get re-built if needed first.
-			 * This only cause minor confusion when dhcpcd is
-			 * restarted and confirms a lease where prior delegation
-			 * has already been assigned, because it will log it
-			 * added routes after the script has run.
-			 * The routes should still be there and fine though.
-			 */
 			dhcp6_script_try_run(ifd, 1);
 		}
 	}
 
 	/* Now all addresses have been added, rebuild the routing table. */
-	if_initrt(ifp->ctx, AF_INET6);
 	rt_build(ifp->ctx, AF_INET6);
 }
 
@@ -2929,7 +2919,6 @@ dhcp6_find_delegates(struct interface *i
 		state = D6_STATE(ifp);
 		state->state = DH6S_DELEGATED;
 		ipv6_addaddrs(&state->addrs);
-		if_initrt(ifp->ctx, AF_INET6);
 		rt_build(ifp->ctx, AF_INET6);
 		dhcp6_script_try_run(ifp, 1);
 	}
@@ -3167,7 +3156,6 @@ dhcp6_bind(struct interface *ifp, const 
 		else
 			lognewinfo("%s: expire in %"PRIu32" seconds",
 			    ifp->name, state->expire);
-		if_initrt(ifp->ctx, AF_INET6);
 		rt_build(ifp->ctx, AF_INET6);
 		if (!timed_out)
 			dhcp6_writelease(ifp);
@@ -3971,24 +3959,22 @@ dhcp6_handleifa(int cmd, struct ipv6_add
 }
 
 ssize_t
-dhcp6_env(char **env, const char *prefix, const struct interface *ifp,
+dhcp6_env(FILE *fp, const char *prefix, const struct interface *ifp,
     const struct dhcp6_message *m, size_t len)
 {
 	const struct if_options *ifo;
 	struct dhcp_opt *opt, *vo;
 	const uint8_t *p;
 	struct dhcp6_option o;
-	size_t i, n;
+	size_t i;
 	char *pfx;
 	uint32_t en;
 	const struct dhcpcd_ctx *ctx;
 #ifndef SMALL
 	const struct dhcp6_state *state;
 	const struct ipv6_addr *ap;
-	char *v, *val;
 #endif
 
-	n = 0;
 	if (m == NULL)
 		goto delegated;
 
@@ -4003,28 +3989,20 @@ dhcp6_env(char **env, const char *prefix
 	ctx = ifp->ctx;
 
 	/* Zero our indexes */
-	if (env) {
-		for (i = 0, opt = ctx->dhcp6_opts;
-		    i < ctx->dhcp6_opts_len;
-		    i++, opt++)
-			dhcp_zero_index(opt);
-		for (i = 0, opt = ifp->options->dhcp6_override;
-		    i < ifp->options->dhcp6_override_len;
-		    i++, opt++)
-			dhcp_zero_index(opt);
-		for (i = 0, opt = ctx->vivso;
-		    i < ctx->vivso_len;
-		    i++, opt++)
-			dhcp_zero_index(opt);
-		i = strlen(prefix) + strlen("_dhcp6") + 1;
-		pfx = malloc(i);
-		if (pfx == NULL) {
-			logerr(__func__);
-			return -1;
-		}
-		snprintf(pfx, i, "%s_dhcp6", prefix);
-	} else
-		pfx = NULL;
+	for (i = 0, opt = ctx->dhcp6_opts;
+	    i < ctx->dhcp6_opts_len;
+	    i++, opt++)
+		dhcp_zero_index(opt);
+	for (i = 0, opt = ifp->options->dhcp6_override;
+	    i < ifp->options->dhcp6_override_len;
+	    i++, opt++)
+		dhcp_zero_index(opt);
+	for (i = 0, opt = ctx->vivso;
+	    i < ctx->vivso_len;
+	    i++, opt++)
+		dhcp_zero_index(opt);
+	if (asprintf(&pfx, "%s_dhcp6", prefix) == -1)
+		return -1;
 
 	/* Unlike DHCP, DHCPv6 options *may* occur more than once.
 	 * There is also no provision for option concatenation unlike DHCP. */
@@ -4070,15 +4048,13 @@ dhcp6_env(char **env, const char *prefix
 				opt = NULL;
 		}
 		if (opt) {
-			n += dhcp_envoption(ifp->ctx,
-			    env == NULL ? NULL : &env[n],
-			    pfx, ifp->name,
+			dhcp_envoption(ifp->ctx,
+			    fp, pfx, ifp->name,
 			    opt, dhcp6_getoption, p, o.len);
 		}
 		if (vo) {
-			n += dhcp_envoption(ifp->ctx,
-			    env == NULL ? NULL : &env[n],
-			    pfx, ifp->name,
+			dhcp_envoption(ifp->ctx,
+			    fp, pfx, ifp->name,
 			    vo, dhcp6_getoption,
 			    p + sizeof(en),
 			    o.len - sizeof(en));
@@ -4090,38 +4066,29 @@ delegated:
 #ifndef SMALL
         /* Needed for Delegated Prefixes */
 	state = D6_CSTATE(ifp);
-	i = 0;
 	TAILQ_FOREACH(ap, &state->addrs, next) {
-		if (ap->delegating_prefix) {
-			i += strlen(ap->saddr) + 1;
-		}
+		if (ap->delegating_prefix)
+			break;
 	}
-	if (env && i) {
-		i += strlen(prefix) + strlen("_delegated_dhcp6_prefix=");
-                v = val = env[n] = malloc(i);
-		if (v == NULL) {
-			logerr(__func__);
-			return -1;
-		}
-		v += snprintf(val, i, "%s_delegated_dhcp6_prefix=", prefix);
-		TAILQ_FOREACH(ap, &state->addrs, next) {
-			if (ap->delegating_prefix) {
-				/* Can't use stpcpy(3) due to "security" */
-				const char *sap = ap->saddr;
-
-				do
-					*v++ = *sap;
-				while (*++sap != '\0');
-				*v++ = ' ';
-			}
+	if (ap == NULL)
+		return 1;
+	if (fprintf(fp, "%s_delegated_dhcp6_prefix=", prefix) == -1)
+		return -1;
+	TAILQ_FOREACH(ap, &state->addrs, next) {
+		if (ap->delegating_prefix == NULL)
+			continue;
+		if (ap != TAILQ_FIRST(&state->addrs)) {
+			if (fputc(' ', fp) == EOF)
+				return -1;
 		}
-		*--v = '\0';
+		if (fprintf(fp, "%s", ap->saddr) == -1)
+			return -1;
         }
-	if (i)
-		n++;
+	if (fputc('\0', fp) == EOF)
+		return -1;
 #endif
 
-	return (ssize_t)n;
+	return 1;
 }
 
 int
Index: src/external/bsd/dhcpcd/dist/src/if-bsd.c
diff -u src/external/bsd/dhcpcd/dist/src/if-bsd.c:1.8 src/external/bsd/dhcpcd/dist/src/if-bsd.c:1.9
--- src/external/bsd/dhcpcd/dist/src/if-bsd.c:1.8	Wed Jun 26 17:47:47 2019
+++ src/external/bsd/dhcpcd/dist/src/if-bsd.c	Wed Jul 24 09:57:43 2019
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
 /*
  * BSD interface driver for dhcpcd
  * Copyright (c) 2006-2019 Roy Marples <r...@marples.name>
@@ -496,6 +497,8 @@ if_route(unsigned char cmd, const struct
 	bool gateway_unspec;
 
 	assert(rt != NULL);
+	assert(rt->rt_ifp != NULL);
+	assert(rt->rt_ifp->ctx != NULL);
 	ctx = rt->rt_ifp->ctx;
 
 #define ADDSA(sa) do {							      \
@@ -695,15 +698,13 @@ if_copyrt(struct dhcpcd_ctx *ctx, struct
 }
 
 int
-if_initrt(struct dhcpcd_ctx *ctx, int af)
+if_initrt(struct dhcpcd_ctx *ctx, rb_tree_t *kroutes, int af)
 {
 	struct rt_msghdr *rtm;
 	int mib[6];
 	size_t needed;
 	char *buf, *p, *end;
-	struct rt rt;
-
-	rt_headclear(&ctx->kroutes, af);
+	struct rt rt, *rtn;
 
 	mib[0] = CTL_NET;
 	mib[1] = PF_ROUTE;
@@ -730,10 +731,15 @@ if_initrt(struct dhcpcd_ctx *ctx, int af
 			errno = EINVAL;
 			break;
 		}
-		if (if_copyrt(ctx, &rt, rtm) == 0) {
-			rt.rt_dflags |= RTDF_INIT;
-			rt_recvrt(RTM_ADD, &rt, rtm->rtm_pid);
+		if (if_copyrt(ctx, &rt, rtm) != 0)
+			continue;
+		if ((rtn = rt_new(rt.rt_ifp)) == NULL) {
+			logerr(__func__);
+			break;
 		}
+		memcpy(rtn, &rt, sizeof(*rtn));
+		if (rb_tree_insert_node(kroutes, rtn) != rtn)
+			rt_free(rtn);
 	}
 	free(buf);
 	return p == end ? 0 : -1;
@@ -1063,7 +1069,7 @@ if_rtm(struct dhcpcd_ctx *ctx, const str
 		return 0;
 
 	if (if_copyrt(ctx, &rt, rtm) == -1)
-		return errno == ENOTSUP ? 0 : -1;
+		return -1;
 
 #ifdef INET6
 	/*
@@ -1306,7 +1312,6 @@ if_dispatch(struct dhcpcd_ctx *ctx, cons
 #ifdef RTM_DESYNC
 	case RTM_DESYNC:
 		dhcpcd_linkoverflow(ctx);
-		return 0;
 #endif
 	}
 
@@ -1326,12 +1331,13 @@ if_handlelink(struct dhcpcd_ctx *ctx)
 		return -1;
 	if (len == 0)
 		return 0;
-	if ((size_t)len < offsetof(struct rt_msghdr, rtm_index) ||
-	    len < rtm.hdr.rtm_msglen)
-	{
+	if (len < rtm.hdr.rtm_msglen) {
 		errno = EINVAL;
 		return -1;
 	}
+	/* We generally treat rtm.hdr has an array so we can easily
+	 * access the following data. */
+	/* coverity[callee_ptr_arith] */
 	return if_dispatch(ctx, &rtm.hdr);
 }
 

Index: src/external/bsd/dhcpcd/dist/src/dhcpcd.c
diff -u src/external/bsd/dhcpcd/dist/src/dhcpcd.c:1.20 src/external/bsd/dhcpcd/dist/src/dhcpcd.c:1.21
--- src/external/bsd/dhcpcd/dist/src/dhcpcd.c:1.20	Wed Jun 26 17:47:47 2019
+++ src/external/bsd/dhcpcd/dist/src/dhcpcd.c	Wed Jul 24 09:57:43 2019
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
 /*
  * dhcpcd - DHCP client daemon
  * Copyright (c) 2006-2019 Roy Marples <r...@marples.name>
@@ -738,9 +739,6 @@ dhcpcd_handlecarrier(struct dhcpcd_ctx *
 #ifdef INET
 				dhcp_abort(ifp);
 #endif
-#ifdef INET6
-				ipv6nd_expire(ifp, 0);
-#endif
 #ifdef DHCP6
 				dhcp6_abort(ifp);
 #endif
@@ -785,7 +783,7 @@ dhcpcd_handlecarrier(struct dhcpcd_ctx *
 			/* Set any IPv6 Routers we remembered to expire
 			 * faster than they would normally as we
 			 * maybe on a new network. */
-			ipv6nd_expire(ifp, RTR_CARRIER_EXPIRE);
+			ipv6nd_startexpire(ifp);
 #endif
 			/* RFC4941 Section 3.5 */
 			ipv6_gentempifid(ifp);
@@ -1003,6 +1001,7 @@ dhcpcd_handleinterface(void *arg, int ac
 	struct if_head *ifs;
 	struct interface *ifp, *iff;
 	const char * const argv[] = { ifname };
+	int e;
 
 	ctx = arg;
 	if (action == -1) {
@@ -1026,13 +1025,17 @@ dhcpcd_handleinterface(void *arg, int ac
 		logerr(__func__);
 		return -1;
 	}
+
 	ifp = if_find(ifs, ifname);
 	if (ifp == NULL) {
 		/* This can happen if an interface is quickly added
 		 * and then removed. */
 		errno = ENOENT;
-		return -1;
+		e = -1;
+		goto out;
 	}
+	e = 1;
+
 	/* Check if we already have the interface */
 	iff = if_find(ctx->ifaces, ifp->name);
 
@@ -1061,6 +1064,7 @@ dhcpcd_handleinterface(void *arg, int ac
 			dhcpcd_prestartinterface(iff);
 	}
 
+out:
 	/* Free our discovered list */
 	while ((ifp = TAILQ_FIRST(ifs))) {
 		TAILQ_REMOVE(ifs, ifp, next);
@@ -1068,7 +1072,7 @@ dhcpcd_handleinterface(void *arg, int ac
 	}
 	free(ifs);
 
-	return 1;
+	return e;
 }
 
 static void
@@ -1081,7 +1085,8 @@ dhcpcd_handlelink(void *arg)
 			dhcpcd_linkoverflow(ctx);
 			return;
 		}
-		logerr(__func__);
+		if (errno != ENOTSUP)
+			logerr(__func__);
 	}
 }
 
@@ -1093,6 +1098,22 @@ dhcpcd_checkcarrier(void *arg)
 	dhcpcd_handlecarrier(ifp->ctx, LINK_UNKNOWN, ifp->flags, ifp->name);
 }
 
+#ifndef SMALL
+static void
+dhcpcd_setlinkrcvbuf(struct dhcpcd_ctx *ctx)
+{
+	socklen_t socklen;
+
+	if (ctx->link_rcvbuf == 0)
+		return;
+
+	socklen = sizeof(ctx->link_rcvbuf);
+	if (setsockopt(ctx->link_fd, SOL_SOCKET,
+	    SO_RCVBUF, &ctx->link_rcvbuf, socklen) == -1)
+		logerr(__func__);
+}
+#endif
+
 void
 dhcpcd_linkoverflow(struct dhcpcd_ctx *ctx)
 {
@@ -1113,10 +1134,17 @@ dhcpcd_linkoverflow(struct dhcpcd_ctx *c
 		eloop_exit(ctx->eloop, EXIT_FAILURE);
 		return;
 	}
+#ifndef SMALL
+	dhcpcd_setlinkrcvbuf(ctx);
+#endif
 	eloop_event_add(ctx->eloop, ctx->link_fd, dhcpcd_handlelink, ctx);
 
 	/* Work out the current interfaces. */
 	ifaces = if_discover(ctx, &ifaddrs, ctx->ifc, ctx->ifv);
+	if (ifaces == NULL) {
+		logerr(__func__);
+		return;
+	}
 
 	/* Punt departed interfaces */
 	TAILQ_FOREACH_SAFE(ifp, ctx->ifaces, next, ifn) {
@@ -1126,21 +1154,23 @@ dhcpcd_linkoverflow(struct dhcpcd_ctx *c
 	}
 
 	/* Add new interfaces */
-	TAILQ_FOREACH_SAFE(ifp, ifaces, next, ifn) {
+	while ((ifp = TAILQ_FIRST(ifaces)) != NULL ) {
+		TAILQ_REMOVE(ifaces, ifp, next);
 		ifp1 = if_find(ctx->ifaces, ifp->name);
 		if (ifp1 != NULL) {
 			/* If the interface already exists,
 			 * check carrier state. */
 			eloop_timeout_add_sec(ctx->eloop, 0,
 			    dhcpcd_checkcarrier, ifp1);
+			if_free(ifp);
 			continue;
 		}
-		TAILQ_REMOVE(ifaces, ifp, next);
 		TAILQ_INSERT_TAIL(ctx->ifaces, ifp, next);
 		if (ifp->active)
 			eloop_timeout_add_sec(ctx->eloop, 0,
 			    dhcpcd_prestartinterface, ifp);
 	}
+	free(ifaces);
 
 	/* Update address state. */
 	if_markaddrsstale(ctx->ifaces);
@@ -1958,6 +1988,9 @@ printpidfile:
 		logerr("%s: if_opensockets", __func__);
 		goto exit_failure;
 	}
+#ifndef SMALL
+	dhcpcd_setlinkrcvbuf(&ctx);
+#endif
 
 	/* When running dhcpcd against a single interface, we need to retain
 	 * the old behaviour of waiting for an IP address */
@@ -2057,7 +2090,6 @@ printpidfile:
 	free_options(&ctx, ifo);
 	ifo = NULL;
 
-	if_sortinterfaces(&ctx);
 	TAILQ_FOREACH(ifp, ctx.ifaces, next) {
 		if (ifp->active)
 			eloop_timeout_add_sec(ctx.eloop, 0,
@@ -2114,5 +2146,11 @@ exit1:
 	if (ctx.options & DHCPCD_FORKED)
 		_exit(i); /* so atexit won't remove our pidfile */
 #endif
+#ifdef HAVE_OPEN_MEMSTREAM
+	if (ctx.script_fp)
+		fclose(ctx.script_fp);
+#endif
+	free(ctx.script_buf);
+	free(ctx.script_env);
 	return i;
 }

Index: src/external/bsd/dhcpcd/dist/src/if-options.c
diff -u src/external/bsd/dhcpcd/dist/src/if-options.c:1.14 src/external/bsd/dhcpcd/dist/src/if-options.c:1.15
--- src/external/bsd/dhcpcd/dist/src/if-options.c:1.14	Sat May  4 09:42:15 2019
+++ src/external/bsd/dhcpcd/dist/src/if-options.c	Wed Jul 24 09:57:43 2019
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
 /*
  * dhcpcd - DHCP client daemon
  * Copyright (c) 2006-2019 Roy Marples <r...@marples.name>
@@ -64,7 +65,7 @@
 #define O_IPV6RS		O_BASE + 4
 #define O_NOIPV6RS		O_BASE + 5
 #define O_IPV6RA_FORK		O_BASE + 6
-// unused			O_BASE + 7
+#define O_LINK_RCVBUF		O_BASE + 7
 // unused			O_BASE + 8
 #define O_NOALIAS		O_BASE + 9
 #define O_IA_NA			O_BASE + 10
@@ -204,6 +205,7 @@ const struct option cf_options[] = {
 	{"lastleaseextend", no_argument,       NULL, O_LASTLEASE_EXTEND},
 	{"inactive",        no_argument,       NULL, O_INACTIVE},
 	{"mudurl",          required_argument, NULL, O_MUDURL},
+	{"link_rcvbuf",     required_argument, NULL, O_LINK_RCVBUF},
 	{NULL,              0,                 NULL, '\0'}
 };
 
@@ -724,8 +726,9 @@ parse_option(struct dhcpcd_ctx *ctx, con
 			logerr(__func__);
 			return -1;
 		}
-		parse_str(ifo->script, dl, arg, PARSE_STRING_NULL);
-		if (ifo->script[0] == '\0' ||
+		s = parse_str(ifo->script, dl, arg, PARSE_STRING_NULL);
+		if (s == -1 ||
+		    ifo->script[0] == '\0' ||
 		    strcmp(ifo->script, "/dev/null") == 0)
 		{
 			free(ifo->script);
@@ -1122,16 +1125,14 @@ parse_option(struct dhcpcd_ctx *ctx, con
 				*fp = ' ';
 				return -1;
 			}
-			if ((rt = rt_new0(ctx)) == NULL) {
-				*fp = ' ';
+			*fp = ' ';
+			if ((rt = rt_new0(ctx)) == NULL)
 				return -1;
-			}
 			sa_in_init(&rt->rt_dest, &addr);
 			sa_in_init(&rt->rt_netmask, &addr2);
 			sa_in_init(&rt->rt_gateway, &addr3);
-			TAILQ_INSERT_TAIL(&ifo->routes, rt, rt_next);
-			*fp = ' ';
-			add_environ(&ifo->config, arg, 0);
+			if (rt_proto_add_ctx(&ifo->routes, rt, ctx))
+				add_environ(&ifo->config, arg, 0);
 		} else if (strncmp(arg, "routers=", strlen("routers=")) == 0) {
 			if (parse_addr(&addr, NULL, p) == -1)
 				return -1;
@@ -1141,8 +1142,8 @@ parse_option(struct dhcpcd_ctx *ctx, con
 			sa_in_init(&rt->rt_dest, &addr2);
 			sa_in_init(&rt->rt_netmask, &addr2);
 			sa_in_init(&rt->rt_gateway, &addr);
-			TAILQ_INSERT_TAIL(&ifo->routes, rt, rt_next);
-			add_environ(&ifo->config, arg, 0);
+			if (rt_proto_add_ctx(&ifo->routes, rt, ctx))
+				add_environ(&ifo->config, arg, 0);
 		} else if (strncmp(arg, "interface_mtu=",
 		    strlen("interface_mtu=")) == 0 ||
 		    strncmp(arg, "mtu=", strlen("mtu=")) == 0)
@@ -1868,6 +1869,7 @@ err_sla:
 		    ifo->vivco_len + 1, sizeof(*ifo->vivco));
 		if (vivco == NULL) {
 			logerr( __func__);
+			free(np);
 			return -1;
 		}
 		ifo->vivco = vivco;
@@ -2160,6 +2162,16 @@ err_sla:
 		}
 		*ifo->mudurl = (uint8_t)s;
 		break;
+	case O_LINK_RCVBUF:
+#ifndef SMALL
+		ARG_REQUIRED;
+		ctx->link_rcvbuf = (int)strtoi(arg, NULL, 0, 0, INT32_MAX, &e);
+		if (e) {
+			logerrx("failed to convert link_rcvbuf %s", arg);
+			return -1;
+		}
+#endif
+		break;
 	default:
 		return 0;
 	}
@@ -2269,7 +2281,7 @@ default_config(struct dhcpcd_ctx *ctx)
 	ifo->script = UNCONST(default_script);
 	ifo->metric = -1;
 	ifo->auth.options |= DHCPCD_AUTH_REQUIRE;
-	TAILQ_INIT(&ifo->routes);
+	rb_tree_init(&ifo->routes, &rt_compare_proto_ops);
 #ifdef AUTH
 	TAILQ_INIT(&ifo->auth.tokens);
 #endif
@@ -2327,6 +2339,9 @@ read_config(struct dhcpcd_ctx *ctx,
 	buf = NULL;
 	buflen = 0;
 
+	/* Reset route order */
+	ctx->rt_order = 0;
+
 	/* Parse our embedded options file */
 	if (ifname == NULL && !(ctx->options & DHCPCD_PRINT_PIDFILE)) {
 		/* Space for initial estimates */

Index: src/external/bsd/dhcpcd/dist/src/ipv6nd.c
diff -u src/external/bsd/dhcpcd/dist/src/ipv6nd.c:1.7 src/external/bsd/dhcpcd/dist/src/ipv6nd.c:1.8
--- src/external/bsd/dhcpcd/dist/src/ipv6nd.c:1.7	Wed Jun 26 17:47:47 2019
+++ src/external/bsd/dhcpcd/dist/src/ipv6nd.c	Wed Jul 24 09:57:43 2019
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
 /*
  * dhcpcd - IPv6 ND handling
  * Copyright (c) 2006-2019 Roy Marples <r...@marples.name>
@@ -105,6 +106,9 @@ __CTASSERT(sizeof(struct nd_opt_rdnss) =
 #define RTPREF_RESERVED	(-2)
 #define RTPREF_INVALID	(-3)	/* internal */
 
+#define	EXPIRED_MAX	5	/* Remember 5 expired routers to avoid
+				   logspam. */
+
 #define MIN_RANDOM_FACTOR	500				/* millisecs */
 #define MAX_RANDOM_FACTOR	1500				/* millisecs */
 #define MIN_RANDOM_FACTOR_U	MIN_RANDOM_FACTOR * 1000	/* usecs */
@@ -462,7 +466,6 @@ ipv6nd_advertise(struct ipv6_addr *ia)
 		return;
 
 	ctx = ia->iface->ctx;
-	if_sortinterfaces(ctx);
 	/* Find the most preferred address to advertise. */
 	iaf = NULL;
 	TAILQ_FOREACH(ifp, ctx->ifaces, next) {
@@ -483,7 +486,8 @@ ipv6nd_advertise(struct ipv6_addr *ia)
 			    iap->addr_flags & IN6_IFF_NOTUSEABLE)
 				continue;
 
-			if (iaf == NULL)
+			if (iaf == NULL ||
+			    iaf->iface->metric > iap->iface->metric)
 				iaf = iap;
 		}
 	}
@@ -523,46 +527,34 @@ ipv6nd_advertise(struct ipv6_addr *ia)
 	ipv6nd_sendadvertisement(iaf);
 }
 
-void
-ipv6nd_expire(struct interface *ifp, uint32_t seconds)
+static void
+ipv6nd_expire(void *arg)
 {
+	struct interface *ifp = arg;
 	struct ra *rap;
-	struct timespec now;
-	uint32_t vltime = seconds;
-	uint32_t pltime = seconds / 2;
+	struct ipv6_addr *ia;
+	struct timespec now = { .tv_sec = 1 };
 
 	if (ifp->ctx->ra_routers == NULL)
 		return;
 
-	clock_gettime(CLOCK_MONOTONIC, &now);
-
 	TAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) {
-		if (rap->iface == ifp) {
-			rap->acquired = now;
-			rap->expired = seconds ? 0 : 1;
-			if (seconds) {
-				struct ipv6_addr *ia;
-
-				rap->lifetime = seconds;
-				TAILQ_FOREACH(ia, &rap->addrs, next) {
-					if (ia->prefix_pltime > pltime ||
-					    ia->prefix_vltime > vltime)
-					{
-						ia->acquired = now;
-						if (ia->prefix_pltime != 0)
-							ia->prefix_pltime =
-							    pltime;
-						ia->prefix_vltime = vltime;
-					}
-				}
-				ipv6_addaddrs(&rap->addrs);
-			}
+		if (rap->iface == ifp)
+			continue;
+		rap->acquired = now;
+		TAILQ_FOREACH(ia, &rap->addrs, next) {
+			ia->acquired = now;
 		}
 	}
-	if (seconds)
-		ipv6nd_expirera(ifp);
-	else
-		rt_build(ifp->ctx, AF_INET6);
+	ipv6nd_expirera(ifp);
+}
+
+void
+ipv6nd_startexpire(struct interface *ifp)
+{
+
+	eloop_timeout_add_sec(ifp->ctx->eloop, RTR_CARRIER_EXPIRE,
+	    ipv6nd_expire, ifp);
 }
 
 static void
@@ -1228,6 +1220,10 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx,
 			break;
 
 		case ND_OPT_MTU:
+			if (len < sizeof(mtu)) {
+				logerrx("%s: short MTU option", ifp->name);
+				break;
+			}
 			memcpy(&mtu, p, sizeof(mtu));
 			mtu.nd_opt_mtu_mtu = ntohl(mtu.nd_opt_mtu_mtu);
 			if (mtu.nd_opt_mtu_mtu < IPV6_MMTU) {
@@ -1239,6 +1235,10 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx,
 			break;
 
 		case ND_OPT_RDNSS:
+			if (len < sizeof(rdnss)) {
+				logerrx("%s: short RDNSS option", ifp->name);
+				break;
+			}
 			memcpy(&rdnss, p, sizeof(rdnss));
 			if (rdnss.nd_opt_rdnss_lifetime &&
 			    rdnss.nd_opt_rdnss_len > 1)
@@ -1278,11 +1278,6 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx,
 	ipv6_addtempaddrs(ifp, &rap->acquired);
 #endif
 
-	/* Find any freshly added routes, such as the subnet route.
-	 * We do this because we cannot rely on recieving the kernel
-	 * notification right now via our link socket. */
-	if_initrt(ifp->ctx, AF_INET6);
-
 	rt_build(ifp->ctx, AF_INET6);
 	if (ipv6nd_scriptrun(rap))
 		return;
@@ -1390,11 +1385,11 @@ ipv6nd_getoption(struct dhcpcd_ctx *ctx,
 }
 
 ssize_t
-ipv6nd_env(char **env, const char *prefix, const struct interface *ifp)
+ipv6nd_env(FILE *fp, const struct interface *ifp)
 {
 	size_t i, j, n, len, olen;
 	struct ra *rap;
-	char ndprefix[32], abuf[24];
+	char ndprefix[32];
 	struct dhcp_opt *opt;
 	uint8_t *p;
 	struct nd_opt_hdr ndo;
@@ -1407,34 +1402,25 @@ ipv6nd_env(char **env, const char *prefi
 		if (rap->iface != ifp)
 			continue;
 		i++;
-		if (prefix != NULL)
-			snprintf(ndprefix, sizeof(ndprefix),
-			    "%s_nd%zu", prefix, i);
-		else
-			snprintf(ndprefix, sizeof(ndprefix),
-			    "nd%zu", i);
-		if (env)
-			setvar(&env[n], ndprefix, "from", rap->sfrom);
-		n++;
-		if (env)
-			setvard(&env[n], ndprefix, "acquired",
-			    (size_t)rap->acquired.tv_sec);
-		n++;
-		if (env)
-			setvard(&env[n], ndprefix, "now", (size_t)now.tv_sec);
-		n++;
+		snprintf(ndprefix, sizeof(ndprefix), "nd%zu", i);
+		if (efprintf(fp, "%s_from=%s", ndprefix, rap->sfrom) == -1)
+			return -1;
+		if (efprintf(fp, "%s_acquired=%lld", ndprefix,
+		    (long long)rap->acquired.tv_sec) == -1)
+			return -1;
+		if (efprintf(fp, "%s_now=%lld", ndprefix,
+		    (long long)now.tv_sec) == -1)
+			return -1;
 
 		/* Zero our indexes */
-		if (env) {
-			for (j = 0, opt = rap->iface->ctx->nd_opts;
-			    j < rap->iface->ctx->nd_opts_len;
-			    j++, opt++)
-				dhcp_zero_index(opt);
-			for (j = 0, opt = rap->iface->options->nd_override;
-			    j < rap->iface->options->nd_override_len;
-			    j++, opt++)
-				dhcp_zero_index(opt);
-		}
+		for (j = 0, opt = rap->iface->ctx->nd_opts;
+		    j < rap->iface->ctx->nd_opts_len;
+		    j++, opt++)
+			dhcp_zero_index(opt);
+		for (j = 0, opt = rap->iface->options->nd_override;
+		    j < rap->iface->options->nd_override_len;
+		    j++, opt++)
+			dhcp_zero_index(opt);
 
 		/* Unlike DHCP, ND6 options *may* occur more than once.
 		 * There is also no provision for option concatenation
@@ -1467,34 +1453,31 @@ ipv6nd_env(char **env, const char *prefi
 				if (j == rap->iface->ctx->nd_opts_len)
 					opt = NULL;
 			}
-			if (opt) {
-				n += dhcp_envoption(rap->iface->ctx,
-				    env == NULL ? NULL : &env[n],
-				    ndprefix, rap->iface->name,
-				    opt, ipv6nd_getoption,
-				    p + sizeof(ndo), olen - sizeof(ndo));
-			}
+			if (opt == NULL)
+				continue;
+			dhcp_envoption(rap->iface->ctx, fp,
+			    ndprefix, rap->iface->name,
+			    opt, ipv6nd_getoption,
+			    p + sizeof(ndo), olen - sizeof(ndo));
 		}
 
 		/* We need to output the addresses we actually made
 		 * from the prefix information options as well. */
 		j = 0;
 		TAILQ_FOREACH(ia, &rap->addrs, next) {
-			if (!(ia->flags & IPV6_AF_AUTOCONF)
+			if (!(ia->flags & IPV6_AF_AUTOCONF) ||
 #ifdef IPV6_AF_TEMPORARY
-			    || ia->flags & IPV6_AF_TEMPORARY
+			    ia->flags & IPV6_AF_TEMPORARY ||
 #endif
-			    )
+			    !(ia->flags & IPV6_AF_ADDED) ||
+			    ia->prefix_vltime == 0)
 				continue;
-			j++;
-			if (env) {
-				snprintf(abuf, sizeof(abuf), "addr%zu", j);
-				setvar(&env[n], ndprefix, abuf, ia->saddr);
-			}
-			n++;
+			if (efprintf(fp, "%s_addr%zu=%s",
+			    ndprefix, j++, ia->saddr) == -1)
+				return -1;
 		}
 	}
-	return (ssize_t)n;
+	return 1;
 }
 
 void
@@ -1522,6 +1505,16 @@ ipv6nd_expirera(void *arg)
 	struct timespec now, lt, expire, next;
 	bool expired, valid, validone;
 	struct ipv6_addr *ia;
+	size_t len, olen;
+	uint8_t *p;
+	struct nd_opt_hdr ndo;
+#if 0
+	struct nd_opt_prefix_info pi;
+#endif
+	struct nd_opt_dnssl dnssl;
+	struct nd_opt_rdnss rdnss;
+	uint32_t ltime;
+	size_t nexpired = 0;
 
 	ifp = arg;
 	clock_gettime(CLOCK_MONOTONIC, &now);
@@ -1536,8 +1529,7 @@ ipv6nd_expirera(void *arg)
 			lt.tv_sec = (time_t)rap->lifetime;
 			lt.tv_nsec = 0;
 			timespecadd(&rap->acquired, &lt, &expire);
-			if (rap->lifetime == 0 || timespeccmp(&now, &expire, >))
-			{
+			if (timespeccmp(&now, &expire, >)) {
 				if (!rap->expired) {
 					logwarnx("%s: %s: router expired",
 					    ifp->name, rap->sfrom);
@@ -1588,14 +1580,79 @@ ipv6nd_expirera(void *arg)
 			}
 		}
 
-		/* XXX FixMe!
-		 * We need to extract the lifetime from each option and check
-		 * if that has expired or not.
-		 * If it has, zero the option out in the returned data. */
-
-		/* No valid lifetimes are left on the RA, so we might
-		 * as well punt it. */
-		if (!valid && !validone)
+		/* Work out expiry for ND options */
+		len = rap->data_len - sizeof(struct nd_router_advert);
+		for (p = rap->data + sizeof(struct nd_router_advert);
+		    len >= sizeof(ndo);
+		    p += olen, len -= olen)
+		{
+			memcpy(&ndo, p, sizeof(ndo));
+			olen = (size_t)(ndo.nd_opt_len * 8);
+			if (olen > len) {
+				errno =	EINVAL;
+				break;
+			}
+
+			if (has_option_mask(rap->iface->options->nomasknd,
+			    ndo.nd_opt_type))
+				continue;
+
+			switch (ndo.nd_opt_type) {
+			/* Prefix info is already checked in the above loop. */
+#if 0
+			case ND_OPT_PREFIX_INFORMATION:
+				if (len < sizeof(pi))
+					break;
+				memcpy(&pi, p, sizeof(pi));
+				ltime = pi.nd_opt_pi_valid_time;
+				break;
+#endif
+			case ND_OPT_DNSSL:
+				if (len < sizeof(dnssl))
+					continue;
+				memcpy(&dnssl, p, sizeof(dnssl));
+				ltime = dnssl.nd_opt_dnssl_lifetime;
+				break;
+			case ND_OPT_RDNSS:
+				if (len < sizeof(rdnss))
+					continue;
+				memcpy(&rdnss, p, sizeof(rdnss));
+				ltime = rdnss.nd_opt_rdnss_lifetime;
+				break;
+			default:
+				continue;
+			}
+
+			if (ltime == 0)
+				continue;
+			if (ltime == ND6_INFINITE_LIFETIME) {
+				validone = true;
+				continue;
+			}
+
+			lt.tv_sec = (time_t)ntohl(ltime);
+			lt.tv_nsec = 0;
+			timespecadd(&rap->acquired, &lt, &expire);
+			if (timespeccmp(&now, &expire, >)) {
+				expired = true;
+				continue;
+			}
+
+			timespecsub(&expire, &now, &lt);
+			if (!timespecisset(&next) ||
+			    timespeccmp(&next, &lt, >))
+			{
+				next = lt;
+				validone = true;
+			}
+		}
+
+		if (valid || validone)
+			continue;
+
+		/* Router has expired. Let's not keep a lot of them.
+		 * We should work out if all the options have expired .... */
+		if (++nexpired > EXPIRED_MAX)
 			ipv6nd_free_ra(rap);
 	}
 
@@ -1603,6 +1660,7 @@ ipv6nd_expirera(void *arg)
 		eloop_timeout_add_tv(ifp->ctx->eloop,
 		    &next, ipv6nd_expirera, ifp);
 	if (expired) {
+		logwarnx("%s: part of Router Advertisement expired", ifp->name);
 		rt_build(ifp->ctx, AF_INET6);
 		script_runreason(ifp, "ROUTERADVERT");
 	}

Reply via email to