Module Name:    src
Committed By:   roy
Date:           Sun May 31 12:52:11 UTC 2020

Modified Files:
        src/external/bsd/dhcpcd/dist/hooks: dhcpcd-run-hooks.in
        src/external/bsd/dhcpcd/dist/src: bpf.c dhcp.c dhcp6.c dhcpcd.8.in
            dhcpcd.c if-bsd.c if-options.c ipv6.c ipv6.h ipv6nd.c logerr.c
            privsep.c script.c

Log Message:
Sync


To generate a diff of this commit:
cvs rdiff -u -r1.5 -r1.6 \
    src/external/bsd/dhcpcd/dist/hooks/dhcpcd-run-hooks.in
cvs rdiff -u -r1.14 -r1.15 src/external/bsd/dhcpcd/dist/src/bpf.c
cvs rdiff -u -r1.36 -r1.37 src/external/bsd/dhcpcd/dist/src/dhcp.c \
    src/external/bsd/dhcpcd/dist/src/dhcpcd.c
cvs rdiff -u -r1.18 -r1.19 src/external/bsd/dhcpcd/dist/src/dhcp6.c
cvs rdiff -u -r1.5 -r1.6 src/external/bsd/dhcpcd/dist/src/dhcpcd.8.in \
    src/external/bsd/dhcpcd/dist/src/script.c
cvs rdiff -u -r1.19 -r1.20 src/external/bsd/dhcpcd/dist/src/if-bsd.c \
    src/external/bsd/dhcpcd/dist/src/ipv6nd.c
cvs rdiff -u -r1.23 -r1.24 src/external/bsd/dhcpcd/dist/src/if-options.c
cvs rdiff -u -r1.11 -r1.12 src/external/bsd/dhcpcd/dist/src/ipv6.c
cvs rdiff -u -r1.10 -r1.11 src/external/bsd/dhcpcd/dist/src/ipv6.h
cvs rdiff -u -r1.3 -r1.4 src/external/bsd/dhcpcd/dist/src/logerr.c
cvs rdiff -u -r1.2 -r1.3 src/external/bsd/dhcpcd/dist/src/privsep.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/dhcpcd-run-hooks.in
diff -u src/external/bsd/dhcpcd/dist/hooks/dhcpcd-run-hooks.in:1.5 src/external/bsd/dhcpcd/dist/hooks/dhcpcd-run-hooks.in:1.6
--- src/external/bsd/dhcpcd/dist/hooks/dhcpcd-run-hooks.in:1.5	Thu Apr  2 12:41:48 2020
+++ src/external/bsd/dhcpcd/dist/hooks/dhcpcd-run-hooks.in	Sun May 31 12:52:11 2020
@@ -211,7 +211,7 @@ valid_domainname_list()
 }
 
 # With the advent of alternative init systems, it's possible to have
-# more than one installed. So we need to try and guess what one we're
+# more than one installed. So we need to try to guess what one we're
 # using unless overridden by configure.
 detect_init()
 {

Index: src/external/bsd/dhcpcd/dist/src/bpf.c
diff -u src/external/bsd/dhcpcd/dist/src/bpf.c:1.14 src/external/bsd/dhcpcd/dist/src/bpf.c:1.15
--- src/external/bsd/dhcpcd/dist/src/bpf.c:1.14	Thu Apr  2 12:41:48 2020
+++ src/external/bsd/dhcpcd/dist/src/bpf.c	Sun May 31 12:52:11 2020
@@ -57,8 +57,6 @@
 #include "if.h"
 #include "logerr.h"
 
-#define	ARP_ADDRS_MAX	3
-
 /* BPF helper macros */
 #ifdef __linux__
 #define	BPF_WHOLEPACKET		0x7fffffff /* work around buggy LPF filters */
@@ -85,7 +83,7 @@ size_t
 bpf_frame_header_len(const struct interface *ifp)
 {
 
-	switch (ifp->family) {
+	switch (ifp->hwtype) {
 	case ARPHRD_ETHER:
 		return sizeof(struct ether_header);
 	default:
@@ -98,7 +96,7 @@ bpf_frame_header_src(const struct interf
 {
 	uint8_t *f = fh;
 
-	switch (ifp->family) {
+	switch (ifp->hwtype) {
 	case ARPHRD_ETHER:
 		*len = sizeof(((struct ether_header *)0)->ether_shost);
 		return f + offsetof(struct ether_header, ether_shost);
@@ -114,7 +112,7 @@ bpf_frame_header_dst(const struct interf
 {
 	uint8_t *f = fh;
 
-	switch (ifp->family) {
+	switch (ifp->hwtype) {
 	case ARPHRD_ETHER:
 		*len = sizeof(((struct ether_header *)0)->ether_dhost);
 		return f + offsetof(struct ether_header, ether_dhost);
@@ -129,12 +127,12 @@ static const uint8_t etherbcastaddr[] =
     { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
 
 int
-bpf_frame_bcast(const struct interface *ifp, const char *frame)
+bpf_frame_bcast(const struct interface *ifp, const void *frame)
 {
 
-	switch (ifp->family) {
+	switch (ifp->hwtype) {
 	case ARPHRD_ETHER:
-		return memcmp(frame +
+		return memcmp((const char *)frame +
 		    offsetof(struct ether_header, ether_dhost),
 		    etherbcastaddr, sizeof(etherbcastaddr));
 	default:
@@ -148,15 +146,15 @@ bpf_frame_bcast(const struct interface *
 
 const char *bpf_name = "Berkley Packet Filter";
 
-int
-bpf_open(struct interface *ifp, int (*filter)(struct interface *, int))
-{
-	struct ipv4_state *state;
-	int fd = -1;
-	struct ifreq ifr;
+struct bpf *
+bpf_open(const struct interface *ifp,
+    int (*filter)(const struct bpf *, const struct in_addr *),
+    const struct in_addr *ia)
+{
+	struct bpf *bpf;
+	struct bpf_version pv = { .bv_major = 0, .bv_minor = 0 };
+	struct ifreq ifr = { .ifr_flags = 0 };
 	int ibuf_len = 0;
-	size_t buf_len;
-	struct bpf_version pv;
 #ifdef BIOCIMMEDIATE
 	unsigned int flags;
 #endif
@@ -164,8 +162,13 @@ bpf_open(struct interface *ifp, int (*fi
 	int fd_opts;
 #endif
 
+	bpf = calloc(1, sizeof(*bpf));
+	if (bpf == NULL)
+		return NULL;
+	bpf->bpf_ifp = ifp;
+
 #ifdef _PATH_BPF
-	fd = open(_PATH_BPF, O_RDWR | O_NONBLOCK
+	bpf->bpf_fd = open(_PATH_BPF, O_RDWR | O_NONBLOCK
 #ifdef O_CLOEXEC
 		| O_CLOEXEC
 #endif
@@ -176,27 +179,24 @@ bpf_open(struct interface *ifp, int (*fi
 
 	do {
 		snprintf(device, sizeof(device), "/dev/bpf%d", n++);
-		fd = open(device, O_RDWR | O_NONBLOCK
+		bpf->bpf_fd = open(device, O_RDWR | O_NONBLOCK
 #ifdef O_CLOEXEC
 				| O_CLOEXEC
 #endif
 		);
-	} while (fd == -1 && errno == EBUSY);
+	} while (bpf->bpf_fd == -1 && errno == EBUSY);
 #endif
 
-	if (fd == -1)
-		return -1;
+	if (bpf->bpf_fd == -1)
+		goto eexit;
 
 #ifndef O_CLOEXEC
-	if ((fd_opts = fcntl(fd, F_GETFD)) == -1 ||
-	    fcntl(fd, F_SETFD, fd_opts | FD_CLOEXEC) == -1) {
-		close(fd);
-		return -1;
-	}
+	if ((fd_opts = fcntl(bpf->bpf_fd, F_GETFD)) == -1 ||
+	    fcntl(bpf->bpf_fd, F_SETFD, fd_opts | FD_CLOEXEC) == -1)
+		goto eexit;
 #endif
 
-	memset(&pv, 0, sizeof(pv));
-	if (ioctl(fd, BIOCVERSION, &pv) == -1)
+	if (ioctl(bpf->bpf_fd, BIOCVERSION, &pv) == -1)
 		goto eexit;
 	if (pv.bv_major != BPF_MAJOR_VERSION ||
 	    pv.bv_minor < BPF_MINOR_VERSION) {
@@ -204,94 +204,84 @@ bpf_open(struct interface *ifp, int (*fi
 		goto eexit;
 	}
 
-	if (filter(ifp, fd) != 0)
-		goto eexit;
-
-	memset(&ifr, 0, sizeof(ifr));
 	strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
-	if (ioctl(fd, BIOCSETIF, &ifr) == -1)
+	if (ioctl(bpf->bpf_fd, BIOCSETIF, &ifr) == -1)
 		goto eexit;
 
-	/* Get the required BPF buffer length from the kernel. */
-	if (ioctl(fd, BIOCGBLEN, &ibuf_len) == -1)
-		goto eexit;
-	buf_len = (size_t)ibuf_len;
-	state = ipv4_getstate(ifp);
-	if (state == NULL)
-		goto eexit;
-	if (state->buffer_size != buf_len) {
-		void *nb;
-
-		if ((nb = realloc(state->buffer, buf_len)) == NULL)
-			goto eexit;
-		state->buffer = nb;
-		state->buffer_size = buf_len;
-	}
-
 #ifdef BIOCIMMEDIATE
 	flags = 1;
-	if (ioctl(fd, BIOCIMMEDIATE, &flags) == -1)
+	if (ioctl(bpf->bpf_fd, BIOCIMMEDIATE, &flags) == -1)
 		goto eexit;
 #endif
 
-	return fd;
+	if (filter(bpf, ia) != 0)
+		goto eexit;
+
+	/* Get the required BPF buffer length from the kernel. */
+	if (ioctl(bpf->bpf_fd, BIOCGBLEN, &ibuf_len) == -1)
+		goto eexit;
+	bpf->bpf_size = (size_t)ibuf_len;
+	bpf->bpf_buffer = malloc(bpf->bpf_size);
+	if (bpf->bpf_buffer == NULL)
+		goto eexit;
+	return bpf;
 
 eexit:
-	close(fd);
-	return -1;
+	if (bpf->bpf_fd != -1)
+		close(bpf->bpf_fd);
+	free(bpf);
+	return NULL;
 }
 
 /* BPF requires that we read the entire buffer.
  * So we pass the buffer in the API so we can loop on >1 packet. */
 ssize_t
-bpf_read(struct interface *ifp, int fd, void *data, size_t len,
-    unsigned int *flags)
+bpf_read(struct bpf *bpf, void *data, size_t len)
 {
 	ssize_t bytes;
-	struct ipv4_state *state = IPV4_STATE(ifp);
-
 	struct bpf_hdr packet;
 	const char *payload;
 
-	*flags &= ~BPF_EOF;
+	bpf->bpf_flags &= ~BPF_EOF;
 	for (;;) {
-		if (state->buffer_len == 0) {
-			bytes = read(fd, state->buffer, state->buffer_size);
+		if (bpf->bpf_len == 0) {
+			bytes = read(bpf->bpf_fd, bpf->bpf_buffer,
+			    bpf->bpf_size);
 #if defined(__sun)
 			/* After 2^31 bytes, the kernel offset overflows.
 			 * To work around this bug, lseek 0. */
 			if (bytes == -1 && errno == EINVAL) {
-				lseek(fd, 0, SEEK_SET);
+				lseek(bpf->bpf_fd, 0, SEEK_SET);
 				continue;
 			}
 #endif
 			if (bytes == -1 || bytes == 0)
 				return bytes;
-			state->buffer_len = (size_t)bytes;
-			state->buffer_pos = 0;
+			bpf->bpf_len = (size_t)bytes;
+			bpf->bpf_pos = 0;
 		}
 		bytes = -1;
-		memcpy(&packet, state->buffer + state->buffer_pos,
-		    sizeof(packet));
-		if (state->buffer_pos + packet.bh_caplen + packet.bh_hdrlen >
-		    state->buffer_len)
+		payload = (const char *)bpf->bpf_buffer + bpf->bpf_pos;
+		memcpy(&packet, payload, sizeof(packet));
+		if (bpf->bpf_pos + packet.bh_caplen + packet.bh_hdrlen >
+		    bpf->bpf_len)
 			goto next; /* Packet beyond buffer, drop. */
-		payload = state->buffer + state->buffer_pos + packet.bh_hdrlen;
-		if (bpf_frame_bcast(ifp, payload) == 0)
-			*flags |= BPF_BCAST;
-		else
-			*flags &= ~BPF_BCAST;
+		payload += packet.bh_hdrlen;
 		if (packet.bh_caplen > len)
 			bytes = (ssize_t)len;
 		else
 			bytes = (ssize_t)packet.bh_caplen;
+		if (bpf_frame_bcast(bpf->bpf_ifp, payload) == 0)
+			bpf->bpf_flags |= BPF_BCAST;
+		else
+			bpf->bpf_flags &= ~BPF_BCAST;
 		memcpy(data, payload, (size_t)bytes);
 next:
-		state->buffer_pos += BPF_WORDALIGN(packet.bh_hdrlen +
+		bpf->bpf_pos += BPF_WORDALIGN(packet.bh_hdrlen +
 		    packet.bh_caplen);
-		if (state->buffer_pos >= state->buffer_len) {
-			state->buffer_len = state->buffer_pos = 0;
-			*flags |= BPF_EOF;
+		if (bpf->bpf_pos >= bpf->bpf_len) {
+			bpf->bpf_len = bpf->bpf_pos = 0;
+			bpf->bpf_flags |= BPF_EOF;
 		}
 		if (bytes != -1)
 			return bytes;
@@ -303,29 +293,38 @@ next:
 int
 bpf_attach(int fd, void *filter, unsigned int filter_len)
 {
-	struct bpf_program pf;
+	struct bpf_program pf = { .bf_insns = filter, .bf_len = filter_len };
 
 	/* Install the filter. */
-	memset(&pf, 0, sizeof(pf));
-	pf.bf_insns = filter;
-	pf.bf_len = filter_len;
 	return ioctl(fd, BIOCSETF, &pf);
 }
+
+#ifdef BIOCSETWF
+static int
+bpf_wattach(int fd, void *filter, unsigned int filter_len)
+{
+	struct bpf_program pf = { .bf_insns = filter, .bf_len = filter_len };
+
+	/* Install the filter. */
+	return ioctl(fd, BIOCSETWF, &pf);
+}
+#endif
 #endif
 
 #ifndef __sun
 /* SunOS is special too - sending via BPF goes nowhere. */
 ssize_t
-bpf_send(const struct interface *ifp, int fd, uint16_t protocol,
+bpf_send(const struct bpf *bpf, uint16_t protocol,
     const void *data, size_t len)
 {
 	struct iovec iov[2];
 	struct ether_header eh;
 
-	switch(ifp->family) {
+	switch(bpf->bpf_ifp->hwtype) {
 	case ARPHRD_ETHER:
 		memset(&eh.ether_dhost, 0xff, sizeof(eh.ether_dhost));
-		memcpy(&eh.ether_shost, ifp->hwaddr, sizeof(eh.ether_shost));
+		memcpy(&eh.ether_shost, bpf->bpf_ifp->hwaddr,
+		    sizeof(eh.ether_shost));
 		eh.ether_type = htons(protocol);
 		iov[0].iov_base = &eh;
 		iov[0].iov_len = sizeof(eh);
@@ -337,27 +336,24 @@ bpf_send(const struct interface *ifp, in
 	}
 	iov[1].iov_base = UNCONST(data);
 	iov[1].iov_len = len;
-	return writev(fd, iov, 2);
+	return writev(bpf->bpf_fd, iov, 2);
 }
 #endif
 
-int
-bpf_close(struct interface *ifp, int fd)
+void
+bpf_close(struct bpf *bpf)
 {
-	struct ipv4_state *state = IPV4_STATE(ifp);
 
-	/* Rewind the buffer on closing. */
-	state->buffer_len = state->buffer_pos = 0;
-	return close(fd);
+	close(bpf->bpf_fd);
+	free(bpf->bpf_buffer);
+	free(bpf);
 }
 
-/* Normally this is needed by bootp.
- * Once that uses this again, the ARP guard here can be removed. */
 #ifdef ARP
 #define BPF_CMP_HWADDR_LEN	((((HWADDR_LEN / 4) + 2) * 2) + 1)
 static unsigned int
 bpf_cmp_hwaddr(struct bpf_insn *bpf, size_t bpf_len, size_t off,
-    bool equal, uint8_t *hwaddr, size_t hwaddr_len)
+    bool equal, const uint8_t *hwaddr, size_t hwaddr_len)
 {
 	struct bpf_insn *bp;
 	size_t maclen, nlft, njmps;
@@ -451,7 +447,7 @@ static const struct bpf_insn bpf_arp_eth
 	/* Load frame header length into X */
 	BPF_STMT(BPF_LDX + BPF_W + BPF_IMM, sizeof(struct ether_header)),
 
-	/* Make sure the hardware family matches. */
+	/* Make sure the hardware type matches. */
 	BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct arphdr, ar_hrd)),
 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0),
 	BPF_STMT(BPF_RET + BPF_K, 0),
@@ -482,28 +478,24 @@ static const struct bpf_insn bpf_arp_fil
 };
 #define BPF_ARP_FILTER_LEN	__arraycount(bpf_arp_filter)
 
-#define BPF_ARP_ADDRS_LEN	1 + (ARP_ADDRS_MAX * 2) + 3 + \
-				(ARP_ADDRS_MAX * 2) + 1
+/* One address is two checks of two statements. */
+#define BPF_NADDRS		1
+#define BPF_ARP_ADDRS_LEN	5 + ((BPF_NADDRS * 2) * 2)
 
 #define BPF_ARP_LEN		BPF_ARP_ETHER_LEN + BPF_ARP_FILTER_LEN + \
 				BPF_CMP_HWADDR_LEN + BPF_ARP_ADDRS_LEN
 
-int
-bpf_arp(struct interface *ifp, int fd)
+static int
+bpf_arp_rw(const struct bpf *bpf, const struct in_addr *ia, bool recv)
 {
-	struct bpf_insn bpf[BPF_ARP_LEN];
+	const struct interface *ifp = bpf->bpf_ifp;
+	struct bpf_insn buf[BPF_ARP_LEN + 1];
 	struct bpf_insn *bp;
-	struct iarp_state *state;
 	uint16_t arp_len;
-	struct arp_state *astate;
-	size_t naddrs;
-
-	if (fd == -1)
-		return 0;
 
-	bp = bpf;
+	bp = buf;
 	/* Check frame header. */
-	switch(ifp->family) {
+	switch(ifp->hwtype) {
 	case ARPHRD_ETHER:
 		memcpy(bp, bpf_arp_ether, sizeof(bpf_arp_ether));
 		bp += BPF_ARP_ETHER_LEN;
@@ -520,32 +512,16 @@ bpf_arp(struct interface *ifp, int fd)
 
 	/* Ensure it's not from us. */
 	bp += bpf_cmp_hwaddr(bp, BPF_CMP_HWADDR_LEN, sizeof(struct arphdr),
-	                     false, ifp->hwaddr, ifp->hwlen);
-
-	state = ARP_STATE(ifp);
-	/* privsep may not have an initial state yet. */
-	if (state == NULL || TAILQ_FIRST(&state->arp_states) == NULL)
-		goto noaddrs;
+	                     !recv, ifp->hwaddr, ifp->hwlen);
 
 	/* Match sender protocol address */
 	BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND,
-	             sizeof(struct arphdr) + ifp->hwlen);
+	    sizeof(struct arphdr) + ifp->hwlen);
+	bp++;
+	BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K, htonl(ia->s_addr), 0, 1);
+	bp++;
+	BPF_SET_STMT(bp, BPF_RET + BPF_K, arp_len);
 	bp++;
-	naddrs = 0;
-	TAILQ_FOREACH(astate, &state->arp_states, next) {
-		if (IN_IS_ADDR_UNSPECIFIED(&astate->addr))
-			continue;
-		if (++naddrs > ARP_ADDRS_MAX) {
-			errno = ENOBUFS;
-			logerr(__func__);
-			break;
-		}
-		BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
-		             htonl(astate->addr.s_addr), 0, 1);
-		bp++;
-		BPF_SET_STMT(bp, BPF_RET + BPF_K, arp_len);
-		bp++;
-	}
 
 	/* If we didn't match sender, then we're only interested in
 	 * ARP probes to us, so check the null host sender. */
@@ -555,29 +531,39 @@ bpf_arp(struct interface *ifp, int fd)
 	bp++;
 
 	/* Match target protocol address */
-	BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND,
-	             (sizeof(struct arphdr)
-		     + (size_t)(ifp->hwlen * 2) + sizeof(in_addr_t)));
+	BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND, (sizeof(struct arphdr) +
+	    (size_t)(ifp->hwlen * 2) + sizeof(in_addr_t)));
+	bp++;
+	BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K, htonl(ia->s_addr), 0, 1);
+	bp++;
+	BPF_SET_STMT(bp, BPF_RET + BPF_K, arp_len);
 	bp++;
-	naddrs = 0;
-	TAILQ_FOREACH(astate, &state->arp_states, next) {
-		if (++naddrs > ARP_ADDRS_MAX) {
-			/* Already logged error above. */
-			break;
-		}
-		BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
-		             htonl(astate->addr.s_addr), 0, 1);
-		bp++;
-		BPF_SET_STMT(bp, BPF_RET + BPF_K, arp_len);
-		bp++;
-	}
 
-noaddrs:
-	/* Return nothing, no protocol address match. */
+	/* No match, drop it */
 	BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
 	bp++;
 
-	return bpf_attach(fd, bpf, (unsigned int)(bp - bpf));
+#ifdef BIOCSETWF
+	if (!recv)
+		return bpf_wattach(bpf->bpf_fd, buf, (unsigned int)(bp - buf));
+#endif
+
+	return bpf_attach(bpf->bpf_fd, buf, (unsigned int)(bp - buf));
+}
+
+int
+bpf_arp(const struct bpf *bpf, const struct in_addr *ia)
+{
+
+#ifdef BIOCSETWF
+	if (bpf_arp_rw(bpf, ia, true) == -1 ||
+	    bpf_arp_rw(bpf, ia, false) == -1 ||
+	    ioctl(bpf->bpf_fd, BIOCLOCK) == -1)
+		return -1;
+	return 0;
+#else
+	return bpf_arp_rw(bpf, ia, true);
+#endif
 }
 #endif
 
@@ -602,7 +588,7 @@ static const struct bpf_insn bpf_bootp_e
 #define BOOTP_MIN_SIZE		sizeof(struct ip) + sizeof(struct udphdr) + \
 				sizeof(struct bootp)
 
-static const struct bpf_insn bpf_bootp_filter[] = {
+static const struct bpf_insn bpf_bootp_base[] = {
 	/* Make sure it's an IPv4 packet. */
 	BPF_STMT(BPF_LD + BPF_B + BPF_IND, 0),
 	BPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0xf0),
@@ -625,35 +611,43 @@ static const struct bpf_insn bpf_bootp_f
 	BPF_STMT(BPF_ALU + BPF_MUL + BPF_K, 4),
 	BPF_STMT(BPF_ALU + BPF_ADD + BPF_X, 0),
 	BPF_STMT(BPF_MISC + BPF_TAX, 0),
+};
+#define BPF_BOOTP_BASE_LEN	__arraycount(bpf_bootp_base)
 
+static const struct bpf_insn bpf_bootp_read[] = {
 	/* Make sure it's from and to the right port. */
 	BPF_STMT(BPF_LD + BPF_W + BPF_IND, 0),
 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (BOOTPS << 16) + BOOTPC, 1, 0),
 	BPF_STMT(BPF_RET + BPF_K, 0),
 };
+#define BPF_BOOTP_READ_LEN	__arraycount(bpf_bootp_read)
+
+#ifdef BIOCSETWF
+static const struct bpf_insn bpf_bootp_write[] = {
+	/* Make sure it's from and to the right port. */
+	BPF_STMT(BPF_LD + BPF_W + BPF_IND, 0),
+	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (BOOTPC << 16) + BOOTPS, 1, 0),
+	BPF_STMT(BPF_RET + BPF_K, 0),
+};
+#define BPF_BOOTP_WRITE_LEN	__arraycount(bpf_bootp_write)
+#endif
 
-#define BPF_BOOTP_FILTER_LEN	__arraycount(bpf_bootp_filter)
 #define BPF_BOOTP_CHADDR_LEN	((BOOTP_CHADDR_LEN / 4) * 3)
 #define	BPF_BOOTP_XID_LEN	4 /* BOUND check is 4 instructions */
 
-#define BPF_BOOTP_LEN		BPF_BOOTP_ETHER_LEN + BPF_BOOTP_FILTER_LEN \
-				+ BPF_BOOTP_XID_LEN + BPF_BOOTP_CHADDR_LEN + 4
+#define BPF_BOOTP_LEN		BPF_BOOTP_ETHER_LEN + \
+				BPF_BOOTP_BASE_LEN + BPF_BOOTP_READ_LEN + \
+				BPF_BOOTP_XID_LEN + BPF_BOOTP_CHADDR_LEN + 4
 
-int
-bpf_bootp(struct interface *ifp, int fd)
+static int
+bpf_bootp_rw(const struct bpf *bpf, bool read)
 {
-#if 0
-	const struct dhcp_state *state = D_CSTATE(ifp);
-#endif
-	struct bpf_insn bpf[BPF_BOOTP_LEN];
+	struct bpf_insn buf[BPF_BOOTP_LEN + 1];
 	struct bpf_insn *bp;
 
-	if (fd == -1)
-		return 0;
-
-	bp = bpf;
+	bp = buf;
 	/* Check frame header. */
-	switch(ifp->family) {
+	switch(bpf->bpf_ifp->hwtype) {
 #ifdef ARPHRD_NONE
 	case ARPHRD_NONE:
 		memcpy(bp, bpf_bootp_none, sizeof(bpf_bootp_none));
@@ -670,48 +664,45 @@ bpf_bootp(struct interface *ifp, int fd)
 	}
 
 	/* Copy in the main filter. */
-	memcpy(bp, bpf_bootp_filter, sizeof(bpf_bootp_filter));
-	bp += BPF_BOOTP_FILTER_LEN;
+	memcpy(bp, bpf_bootp_base, sizeof(bpf_bootp_base));
+	bp += BPF_BOOTP_BASE_LEN;
 
-	/* These checks won't work when same IP exists on other interfaces. */
-#if 0
-	if (ifp->hwlen <= sizeof(((struct bootp *)0)->chaddr))
-		bp += bpf_cmp_hwaddr(bp, BPF_BOOTP_CHADDR_LEN,
-		                     offsetof(struct bootp, chaddr),
-				     true, ifp->hwaddr, ifp->hwlen);
-
-	/* Make sure the BOOTP packet is for us. */
-	if (state->state == DHS_BOUND) {
-		/* If bound, we only expect FORCERENEW messages
-		 * and they need to be unicast to us.
-		 * Move back to the IP header in M0 and check dst. */
-		BPF_SET_STMT(bp, BPF_LDX + BPF_W + BPF_MEM, 0);
-		bp++;
-		BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND,
-		             offsetof(struct ip, ip_dst));
-		bp++;
-		BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
-		             htonl(state->lease.addr.s_addr), 1, 0);
-		bp++;
-		BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
-		bp++;
-	} else {
-		/* As we're not bound, we need to check xid to ensure
-		 * it's a reply to our transaction. */
-		BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND,
-		             offsetof(struct bootp, xid));
-		bp++;
-		BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
-		             state->xid, 1, 0);
-		bp++;
-		BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
+#ifdef BIOCSETWF
+	if (!read) {
+		memcpy(bp, bpf_bootp_write, sizeof(bpf_bootp_write));
+		bp += BPF_BOOTP_WRITE_LEN;
+
+		/* All passed, return the packet. */
+		BPF_SET_STMT(bp, BPF_RET + BPF_K, BPF_WHOLEPACKET);
 		bp++;
+
+		return bpf_wattach(bpf->bpf_fd, buf, (unsigned int)(bp - buf));
 	}
+#else
+	UNUSED(read);
 #endif
 
+	memcpy(bp, bpf_bootp_read, sizeof(bpf_bootp_read));
+	bp += BPF_BOOTP_READ_LEN;
+
 	/* All passed, return the packet. */
 	BPF_SET_STMT(bp, BPF_RET + BPF_K, BPF_WHOLEPACKET);
 	bp++;
 
-	return bpf_attach(fd, bpf, (unsigned int)(bp - bpf));
+	return bpf_attach(bpf->bpf_fd, buf, (unsigned int)(bp - buf));
+}
+
+int
+bpf_bootp(const struct bpf *bpf, __unused const struct in_addr *ia)
+{
+
+#ifdef BIOCSETWF
+	if (bpf_bootp_rw(bpf, true) == -1 ||
+	    bpf_bootp_rw(bpf, false) == -1 ||
+	    ioctl(bpf->bpf_fd, BIOCLOCK) == -1)
+		return -1;
+	return 0;
+#else
+	return bpf_bootp_rw(bpf, true);
+#endif
 }

Index: src/external/bsd/dhcpcd/dist/src/dhcp.c
diff -u src/external/bsd/dhcpcd/dist/src/dhcp.c:1.36 src/external/bsd/dhcpcd/dist/src/dhcp.c:1.37
--- src/external/bsd/dhcpcd/dist/src/dhcp.c:1.36	Tue Apr 21 09:55:33 2020
+++ src/external/bsd/dhcpcd/dist/src/dhcp.c	Sun May 31 12:52:11 2020
@@ -28,7 +28,6 @@
 
 #include <sys/param.h>
 #include <sys/socket.h>
-#include <sys/stat.h>
 
 #include <arpa/inet.h>
 #include <net/if.h>
@@ -503,7 +502,7 @@ print_rfc3361(FILE *fp, const uint8_t *d
 			memcpy(&addr.s_addr, data, sizeof(addr.s_addr));
 			if (fprintf(fp, "%s", inet_ntoa(addr)) == -1)
 				return -1;
-			if (dl != 0) {
+			if (dl != sizeof(addr.s_addr)) {
 				if (fputc(' ', fp) == EOF)
 					return -1;
 			}
@@ -782,7 +781,7 @@ make_message(struct bootp **bootpm, cons
 		bootp->ciaddr = state->addr->addr.s_addr;
 
 	bootp->op = BOOTREQUEST;
-	bootp->htype = (uint8_t)ifp->family;
+	bootp->htype = (uint8_t)ifp->hwtype;
 	if (ifp->hwlen != 0 && ifp->hwlen < sizeof(bootp->chaddr)) {
 		bootp->hlen = (uint8_t)ifp->hwlen;
 		memcpy(&bootp->chaddr, &ifp->hwaddr, ifp->hwlen);
@@ -1142,30 +1141,15 @@ toobig:
 	return -1;
 }
 
-static ssize_t
-write_lease(const struct interface *ifp, const struct bootp *bootp, size_t len)
-{
-	int fd;
-	ssize_t bytes;
-	const struct dhcp_state *state = D_CSTATE(ifp);
-
-	logdebugx("%s: writing lease `%s'", ifp->name, state->leasefile);
-
-	fd = open(state->leasefile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
-	if (fd == -1)
-		return -1;
-	bytes = write(fd, bootp, len);
-	close(fd);
-	return bytes;
-}
-
 static size_t
 read_lease(struct interface *ifp, struct bootp **bootp)
 {
-	int fd;
-	bool fd_opened;
+	union {
+		struct bootp bootp;
+		uint8_t buf[FRAMELEN_MAX];
+	} buf;
 	struct dhcp_state *state = D_STATE(ifp);
-	struct bootp *lease;
+	ssize_t sbytes;
 	size_t bytes;
 	uint8_t type;
 #ifdef AUTH
@@ -1177,29 +1161,20 @@ read_lease(struct interface *ifp, struct
 	*bootp = NULL;
 
 	if (state->leasefile[0] == '\0') {
-		fd = fileno(stdin);
-		fd_opened = false;
+		logdebugx("reading standard input");
+		sbytes = read(fileno(stdin), buf.buf, sizeof(buf.buf));
 	} else {
-		fd = open(state->leasefile, O_RDONLY);
-		fd_opened = true;
+		logdebugx("%s: reading lease `%s'",
+		    ifp->name, state->leasefile);
+		sbytes = dhcp_readfile(ifp->ctx, state->leasefile,
+		    buf.buf, sizeof(buf.buf));
 	}
-	if (fd == -1) {
+	if (sbytes == -1) {
 		if (errno != ENOENT)
-			logerr("%s: open `%s'",
-			    ifp->name, state->leasefile);
+			logerr("%s: %s", ifp->name, state->leasefile);
 		return 0;
 	}
-	if (state->leasefile[0] == '\0')
-		logdebugx("reading standard input");
-	else
-		logdebugx("%s: reading lease `%s'",
-		    ifp->name, state->leasefile);
-
-	bytes = dhcp_read_lease_fd(fd, (void **)&lease);
-	if (fd_opened)
-		close(fd);
-	if (bytes == 0)
-		return 0;
+	bytes = (size_t)sbytes;
 
 	/* Ensure the packet is at lease BOOTP sized
 	 * with a vendor area of 4 octets
@@ -1207,7 +1182,6 @@ read_lease(struct interface *ifp, struct
 	 * code should not be needed, but of course people could
 	 * scribble whatever in the stored lease file. */
 	if (bytes < DHCP_MIN_LEN) {
-		free(lease);
 		logerrx("%s: %s: truncated lease", ifp->name, __func__);
 		return 0;
 	}
@@ -1216,20 +1190,19 @@ read_lease(struct interface *ifp, struct
 		goto out;
 
 	/* We may have found a BOOTP server */
-	if (get_option_uint8(ifp->ctx, &type, (struct bootp *)lease, bytes,
+	if (get_option_uint8(ifp->ctx, &type, &buf.bootp, bytes,
 	    DHO_MESSAGETYPE) == -1)
 		type = 0;
 
 #ifdef AUTH
 	/* Authenticate the message */
-	auth = get_option(ifp->ctx, (struct bootp *)lease, bytes,
+	auth = get_option(ifp->ctx, &buf.bootp, bytes,
 	    DHO_AUTHENTICATION, &auth_len);
 	if (auth) {
 		if (dhcp_auth_validate(&state->auth, &ifp->options->auth,
-		    lease, bytes, 4, type, auth, auth_len) == NULL)
+		    &buf.bootp, bytes, 4, type, auth, auth_len) == NULL)
 		{
 			logerr("%s: authentication failed", ifp->name);
-			free(lease);
 			return 0;
 		}
 		if (state->auth.token)
@@ -1241,13 +1214,17 @@ read_lease(struct interface *ifp, struct
 	    DHCPCD_AUTH_SENDREQUIRE)
 	{
 		logerrx("%s: authentication now required", ifp->name);
-		free(lease);
 		return 0;
 	}
 #endif
 
 out:
-	*bootp = (struct bootp *)lease;
+	*bootp = malloc(bytes);
+	if (*bootp == NULL) {
+		logerr(__func__);
+		return 0;
+	}
+	memcpy(*bootp, buf.buf, bytes);
 	return bytes;
 }
 
@@ -1560,16 +1537,15 @@ dhcp_close(struct interface *ifp)
 	}
 #endif
 
-	if (state->bpf_fd != -1) {
-		eloop_event_delete(ctx->eloop, state->bpf_fd);
-		bpf_close(ifp, state->bpf_fd);
-		state->bpf_fd = -1;
-		state->bpf_flags |= BPF_EOF;
-	}
-	if (state->udp_fd != -1) {
-		eloop_event_delete(ctx->eloop, state->udp_fd);
-		close(state->udp_fd);
-		state->udp_fd = -1;
+	if (state->bpf != NULL) {
+		eloop_event_delete(ctx->eloop, state->bpf->bpf_fd);
+		bpf_close(state->bpf);
+		state->bpf = NULL;
+	}
+	if (state->udp_rfd != -1) {
+		eloop_event_delete(ctx->eloop, state->udp_rfd);
+		close(state->udp_rfd);
+		state->udp_rfd = -1;
 	}
 
 	state->interval = 0;
@@ -1582,7 +1558,7 @@ dhcp_openudp(struct in_addr *ia)
 	struct sockaddr_in sin;
 	int n;
 
-	if ((s = xsocket(PF_INET, SOCK_DGRAM|SOCK_CLOEXEC, IPPROTO_UDP)) == -1)
+	if ((s = xsocket(PF_INET, SOCK_DGRAM | SOCK_CXNB, IPPROTO_UDP)) == -1)
 		return -1;
 
 	n = 1;
@@ -1595,6 +1571,11 @@ dhcp_openudp(struct in_addr *ia)
 	if (setsockopt(s, IPPROTO_IP, IP_RECVPKTINFO, &n, sizeof(n)) == -1)
 		goto errexit;
 #endif
+#ifdef SO_RERROR
+	if (setsockopt(s, SOL_SOCKET, SO_RERROR, &n, sizeof(n)) == -1)
+		goto errexit;
+#endif
+
 	memset(&sin, 0, sizeof(sin));
 	sin.sin_family = AF_INET;
 	sin.sin_port = htons(BOOTPC);
@@ -1692,33 +1673,28 @@ dhcp_sendudp(struct interface *ifp, stru
 		.sin_len = sizeof(sin),
 #endif
 	};
+	struct udphdr udp = {
+	    .uh_sport = htons(BOOTPC),
+	    .uh_dport = htons(BOOTPS),
+	    .uh_ulen = htons((uint16_t)(sizeof(udp) + len)),
+	};
 	struct iovec iov[] = {
-		{ .iov_base = data, .iov_len = len }
+	    { .iov_base = &udp, .iov_len = sizeof(udp), },
+	    { .iov_base = data, .iov_len = len, },
 	};
 	struct msghdr msg = {
 		.msg_name = (void *)&sin,
 		.msg_namelen = sizeof(sin),
 		.msg_iov = iov,
-		.msg_iovlen = 1,
+		.msg_iovlen = __arraycount(iov),
 	};
-	struct dhcp_state *state = D_STATE(ifp);
-	ssize_t r;
-	int fd;
+	struct dhcpcd_ctx *ctx = ifp->ctx;
 
 #ifdef PRIVSEP
-	if (ifp->ctx->options & DHCPCD_PRIVSEP)
-		return ps_inet_sendbootp(state->addr, &msg);
+	if (ctx->options & DHCPCD_PRIVSEP)
+		return ps_inet_sendbootp(ifp, &msg);
 #endif
-	fd = state->udp_fd;
-	if (fd == -1) {
-		fd = dhcp_openudp(&state->addr->addr);
-		if (fd == -1)
-			return -1;
-	}
-	r = sendmsg(fd, &msg, 0);
-	if (state->udp_fd == -1)
-		close(fd);
-	return r;
+	return sendmsg(ctx->udp_wfd, &msg, 0);
 }
 
 static void
@@ -1794,8 +1770,7 @@ send_message(struct interface *ifp, uint
 	 * interface we want to configure because we can't dictate the
 	 * interface via IP_PKTINFO unlike for IPv6.
 	 */
-	if (to.s_addr != INADDR_BROADCAST)
-	{
+	if (to.s_addr != INADDR_BROADCAST) {
 		if (dhcp_sendudp(ifp, &to, bootp, len) != -1)
 			goto out;
 		logerr("%s: dhcp_sendudp", ifp->name);
@@ -1814,8 +1789,7 @@ send_message(struct interface *ifp, uint
 		free(udp);
 #endif
 	} else {
-		r = bpf_send(ifp, state->bpf_fd,
-		    ETHERTYPE_IP, (uint8_t *)udp, ulen);
+		r = bpf_send(state->bpf, ETHERTYPE_IP, udp, ulen);
 		free(udp);
 	}
 	/* If we failed to send a raw packet this normally means
@@ -1929,7 +1903,7 @@ dhcp_expire1(struct interface *ifp)
 
 	eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
 	dhcp_drop(ifp, "EXPIRE");
-	unlink(state->leasefile);
+	dhcp_unlink(ifp->ctx, state->leasefile);
 	state->interval = 0;
 	if (!(ifp->options->options & DHCPCD_LINK) || ifp->carrier > LINK_DOWN)
 		dhcp_discover(ifp);
@@ -2041,10 +2015,6 @@ dhcp_finish_dad(struct interface *ifp, s
 	}
 #endif
 
-	/* If we forked, stop here. */
-	if (ifp->ctx->options & DHCPCD_FORKED)
-		return;
-
 #ifdef IPV4LL
 	/* Stop IPv4LL now we have a working DHCP address */
 	ipv4ll_drop(ifp);
@@ -2072,7 +2042,7 @@ dhcp_addr_duplicated(struct interface *i
 
 	/* RFC 2131 3.1.5, Client-server interaction */
 	logerrx("%s: DAD detected %s", ifp->name, inet_ntoa(*ia));
-	unlink(state->leasefile);
+	dhcp_unlink(ifp->ctx, state->leasefile);
 	if (!(opts & DHCPCD_STATIC) && !state->lease.frominfo)
 		dhcp_decline(ifp);
 #ifdef IN_IFF_DUPLICATED
@@ -2096,35 +2066,91 @@ dhcp_addr_duplicated(struct interface *i
 }
 #endif
 
-#if defined(ARP) && (!defined(KERNEL_RFC5227) || defined(ARPING))
+#ifdef ARP
+#ifdef KERNEL_RFC5227
 static void
-dhcp_arp_not_found(struct arp_state *astate)
+dhcp_arp_announced(struct arp_state *state)
 {
-	struct interface *ifp;
+
+	arp_free(state);
+}
+#else
+static void
+dhcp_arp_defend_failed(struct arp_state *astate)
+{
+	struct interface *ifp = astate->iface;
+
+	dhcp_drop(ifp, "EXPIRED");
+	dhcp_start1(ifp);
+}
+#endif
+
+#if !defined(KERNEL_RFC5227) || defined(ARPING)
+static void dhcp_arp_not_found(struct arp_state *);
+
+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->found_cb = dhcp_arp_found;
+	astate->not_found_cb = dhcp_arp_not_found;
+#ifdef KERNEL_RFC5227
+	astate->announced_cb = dhcp_arp_announced;
+#else
+	astate->announced_cb = NULL;
+	astate->defend_failed_cb = dhcp_arp_defend_failed;
+#endif
+	return astate;
+}
+#endif
+
 #ifdef ARPING
+static int
+dhcp_arping(struct interface *ifp)
+{
 	struct dhcp_state *state;
 	struct if_options *ifo;
-#endif
+	struct arp_state *astate;
+	struct in_addr addr;
 
-	ifp = astate->iface;
-#ifdef ARPING
 	state = D_STATE(ifp);
 	ifo = ifp->options;
-	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) {
-			struct in_addr addr = {
-				.s_addr = ifo->arping[state->arping_index]
-			};
 
-			arp_change(astate, &addr);
-			arp_probe(astate);
-			return;
-		}
-		arp_free(astate);
+	if (ifo->arping_len == 0 || state->arping_index > ifo->arping_len)
+		return 0;
+
+	if (state->arping_index + 1 == ifo->arping_len) {
+		state->arping_index++;
 		dhcpcd_startinterface(ifp);
+		return 1;
+	}
+
+	addr.s_addr = ifo->arping[++state->arping_index];
+	astate = dhcp_arp_new(ifp, &addr);
+	if (astate == NULL) {
+		logerr(__func__);
+		return -1;
+	}
+	arp_probe(astate);
+	return 1;
+}
+#endif
+
+#if !defined(KERNEL_RFC5227) || defined(ARPING)
+static void
+dhcp_arp_not_found(struct arp_state *astate)
+{
+	struct interface *ifp;
+
+	ifp = astate->iface;
+#ifdef ARPING
+	if (dhcp_arping(ifp) == 1) {
+		arp_free(astate);
 		return;
 	}
 #endif
@@ -2174,15 +2200,8 @@ dhcp_arp_found(struct arp_state *astate,
 	arp_free(astate);
 	dhcp_addr_duplicated(ifp, &addr);
 }
+#endif
 
-#ifdef KERNEL_RFC5227
-static void
-dhcp_arp_announced(struct arp_state *state)
-{
-
-	arp_free(state);
-}
-#endif /* KERNEL_RFC5227 */
 #endif /* ARP */
 
 void
@@ -2253,7 +2272,7 @@ dhcp_bind(struct interface *ifp)
 				    "rebind time, forcing to %"PRIu32" seconds",
 				    ifp->name, lease->renewaltime);
 			}
-			if (state->addr &&
+			if (state->state == DHS_RENEW && state->addr &&
 			    lease->addr.s_addr == state->addr->addr.s_addr &&
 			    !(state->added & STATE_FAKE))
 				logdebugx("%s: leased %s for %"PRIu32" seconds",
@@ -2299,9 +2318,13 @@ dhcp_bind(struct interface *ifp)
 	}
 	state->state = DHS_BOUND;
 	if (!state->lease.frominfo &&
-	    !(ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC)))
-		if (write_lease(ifp, state->new, state->new_len) == -1)
-			logerr("write_lease: %s", state->leasefile);
+	    !(ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC))) {
+		logdebugx("%s: writing lease `%s'",
+		    ifp->name, state->leasefile);
+		if (dhcp_writefile(ifp->ctx, state->leasefile, 0640,
+		    state->new, state->new_len) == -1)
+			logerr("dhcp_writefile: %s", state->leasefile);
+	}
 
 	/* Close the BPF filter as we can now receive DHCP messages
 	 * on a UDP socket. */
@@ -2328,8 +2351,8 @@ dhcp_bind(struct interface *ifp)
 	}
 #endif
 
-	state->udp_fd = dhcp_openudp(&state->addr->addr);
-	if (state->udp_fd == -1) {
+	state->udp_rfd = dhcp_openudp(&state->addr->addr);
+	if (state->udp_rfd == -1) {
 		logerr(__func__);
 		/* Address sharing without master mode is not supported.
 		 * It's also possible another DHCP client could be running,
@@ -2338,7 +2361,7 @@ dhcp_bind(struct interface *ifp)
 		dhcp_openbpf(ifp);
 		return;
 	}
-	eloop_event_add(ctx->eloop, state->udp_fd, dhcp_handleifudp, ifp);
+	eloop_event_add(ctx->eloop, state->udp_rfd, dhcp_handleifudp, ifp);
 }
 
 static void
@@ -2350,9 +2373,6 @@ dhcp_lastlease(void *arg)
 	loginfox("%s: timed out contacting a DHCP server, using last lease",
 	    ifp->name);
 	dhcp_bind(ifp);
-	/* If we forked, stop here. */
-	if (ifp->ctx->options & DHCPCD_FORKED)
-		return;
 	state->interval = 0;
 	dhcp_discover(ifp);
 }
@@ -2385,41 +2405,6 @@ dhcp_message_new(struct bootp **bootp,
 	return sizeof(**bootp);
 }
 
-#ifdef ARP
-#ifndef KERNEL_RFC5227
-static void
-dhcp_arp_defend_failed(struct arp_state *astate)
-{
-	struct interface *ifp = astate->iface;
-
-	dhcp_drop(ifp, "EXPIRED");
-	dhcp_start1(ifp);
-}
-#endif
-
-#if !defined(KERNEL_RFC5227) || defined(ARPING)
-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->found_cb = dhcp_arp_found;
-	astate->not_found_cb = dhcp_arp_not_found;
-#ifdef KERNEL_RFC5227
-	astate->announced_cb = dhcp_arp_announced;
-#else
-	astate->announced_cb = NULL;
-	astate->defend_failed_cb = dhcp_arp_defend_failed;
-#endif
-	return astate;
-}
-#endif
-#endif /* ARP */
-
 #if defined(ARP) || defined(KERNEL_RFC5227)
 static int
 dhcp_arp_address(struct interface *ifp)
@@ -2686,11 +2671,7 @@ dhcp_reboot(struct interface *ifp)
 	    !(ia->addr_flags & IN_IFF_NOTUSEABLE) &&
 #endif
 	    dhcp_activeaddr(ifp, &state->lease.addr) == 0)
-	{
 		arp_ifannounceaddr(ifp, &state->lease.addr);
-		if (ifp->ctx->options & DHCPCD_FORKED)
-			return;
-	}
 #endif
 
 	dhcp_new_xid(ifp);
@@ -2749,7 +2730,7 @@ dhcp_drop(struct interface *ifp, const c
 			return;
 		state->state = DHS_RELEASE;
 
-		unlink(state->leasefile);
+		dhcp_unlink(ifp->ctx, state->leasefile);
 		if (ifp->carrier > LINK_DOWN &&
 		    state->new != NULL &&
 		    state->lease.server.s_addr != INADDR_ANY)
@@ -2772,6 +2753,10 @@ dhcp_drop(struct interface *ifp, const c
 	dhcp_auth_reset(&state->auth);
 #endif
 
+	/* Close DHCP ports so a changed interface family is picked
+	 * up by a new BPF state. */
+	dhcp_close(ifp);
+
 	state->state = DHS_NONE;
 	free(state->offer);
 	state->offer = NULL;
@@ -3110,7 +3095,7 @@ dhcp_handledhcp(struct interface *ifp, s
 			return;
 		if (!(ifp->ctx->options & DHCPCD_TEST)) {
 			dhcp_drop(ifp, "NAK");
-			unlink(state->leasefile);
+			dhcp_unlink(ifp->ctx, state->leasefile);
 		}
 
 		/* If we constantly get NAKS then we should slowly back off */
@@ -3251,7 +3236,7 @@ dhcp_handledhcp(struct interface *ifp, s
 			state->reason = "TEST";
 			script_runreason(ifp, state->reason);
 			eloop_exit(ifp->ctx->eloop, EXIT_SUCCESS);
-			state->bpf_flags |= BPF_EOF;
+			state->bpf->bpf_flags |= BPF_EOF;
 			return;
 		}
 		eloop_timeout_delete(ifp->ctx->eloop, send_discover, ifp);
@@ -3390,9 +3375,11 @@ checksums_valid(void *packet,
 		struct ip ip;
 		uint16_t w[sizeof(struct ip) / 2];
 	} pip = {
-		.ip.ip_p = IPPROTO_UDP,
-		.ip.ip_src = ip->ip_src,
-		.ip.ip_dst = ip->ip_dst,
+		.ip = {
+			.ip_p = IPPROTO_UDP,
+			.ip_src = ip->ip_src,
+			.ip_dst = ip->ip_dst,
+		}
 	};
 	size_t ip_hlen;
 	struct udphdr udp;
@@ -3466,15 +3453,16 @@ dhcp_handlebootp(struct interface *ifp, 
 }
 
 void
-dhcp_packet(struct interface *ifp, uint8_t *data, size_t len)
+dhcp_packet(struct interface *ifp, uint8_t *data, size_t len,
+    unsigned int bpf_flags)
 {
 	struct bootp *bootp;
 	struct in_addr from;
 	size_t udp_len;
-	const struct dhcp_state *state = D_CSTATE(ifp);
 	size_t fl = bpf_frame_header_len(ifp);
-
 #ifdef PRIVSEP
+	const struct dhcp_state *state = D_CSTATE(ifp);
+
 	/* Ignore double reads */
 	if (IN_PRIVSEP(ifp->ctx)) {
 		switch (state->state) {
@@ -3490,8 +3478,8 @@ dhcp_packet(struct interface *ifp, uint8
 	/* Trim frame header */
 	if (fl != 0) {
 		if (len < fl) {
-			logerrx("%s: %s: short frame header",
-			    __func__, ifp->name);
+			logerrx("%s: %s: short frame header %zu",
+			    __func__, ifp->name, len);
 			return;
 		}
 		len -= fl;
@@ -3507,7 +3495,7 @@ dhcp_packet(struct interface *ifp, uint8
 		return;
 	}
 
-	if (!checksums_valid(data, &from, state->bpf_flags)) {
+	if (!checksums_valid(data, &from, bpf_flags)) {
 		logerrx("%s: checksum failure from %s",
 		    ifp->name, inet_ntoa(from));
 		return;
@@ -3531,15 +3519,11 @@ dhcp_readbpf(void *arg)
 	uint8_t buf[FRAMELEN_MAX];
 	ssize_t bytes;
 	struct dhcp_state *state = D_STATE(ifp);
+	struct bpf *bpf = state->bpf;
 
-	/* Some RAW mechanisms are generic file descriptors, not sockets.
-	 * This means we have no kernel call to just get one packet,
-	 * so we have to process the entire buffer. */
-	state->bpf_flags &= ~BPF_EOF;
-	state->bpf_flags |= BPF_READING;
-	while (!(state->bpf_flags & BPF_EOF)) {
-		bytes = bpf_read(ifp, state->bpf_fd, buf, sizeof(buf),
-				 &state->bpf_flags);
+	bpf->bpf_flags &= ~BPF_EOF;
+	while (!(bpf->bpf_flags & BPF_EOF)) {
+		bytes = bpf_read(bpf, buf, sizeof(buf));
 		if (bytes == -1) {
 			if (state->state != DHS_NONE) {
 				logerr("%s: %s", __func__, ifp->name);
@@ -3547,13 +3531,13 @@ dhcp_readbpf(void *arg)
 			}
 			break;
 		}
-		dhcp_packet(ifp, buf, (size_t)bytes);
+		dhcp_packet(ifp, buf, (size_t)bytes, bpf->bpf_flags);
 		/* Check we still have a state after processing. */
 		if ((state = D_STATE(ifp)) == NULL)
 			break;
+		if ((bpf = state->bpf) == NULL)
+			break;
 	}
-	if (state != NULL)
-		state->bpf_flags &= ~BPF_READING;
 }
 
 void
@@ -3577,7 +3561,7 @@ dhcp_recvmsg(struct dhcpcd_ctx *ctx, str
 		return;
 	}
 
-	if (state->bpf_fd != -1) {
+	if (state->bpf != NULL) {
 		/* Avoid a duplicate read if BPF is open for the interface. */
 		return;
 	}
@@ -3595,7 +3579,7 @@ dhcp_recvmsg(struct dhcpcd_ctx *ctx, str
 	}
 #endif
 
-	dhcp_handlebootp(ifp, (struct bootp *)iov->iov_base, iov->iov_len,
+	dhcp_handlebootp(ifp, iov->iov_base, iov->iov_len,
 	    &from->sin_addr);
 }
 
@@ -3604,10 +3588,13 @@ dhcp_readudp(struct dhcpcd_ctx *ctx, str
 {
 	const struct dhcp_state *state;
 	struct sockaddr_in from;
-	unsigned char buf[10 * 1024]; /* Maximum MTU */
+	union {
+		struct bootp bootp;
+		uint8_t buf[10 * 1024]; /* Maximum MTU */
+	} iovbuf;
 	struct iovec iov = {
-		.iov_base = buf,
-		.iov_len = sizeof(buf),
+		.iov_base = iovbuf.buf,
+		.iov_len = sizeof(iovbuf.buf),
 	};
 	union {
 		struct cmsghdr hdr;
@@ -3620,16 +3607,16 @@ dhcp_readudp(struct dhcpcd_ctx *ctx, str
 	struct msghdr msg = {
 	    .msg_name = &from, .msg_namelen = sizeof(from),
 	    .msg_iov = &iov, .msg_iovlen = 1,
-	    .msg_control = buf, .msg_controllen = sizeof(cmsgbuf.buf),
+	    .msg_control = cmsgbuf.buf, .msg_controllen = sizeof(cmsgbuf.buf),
 	};
 	int s;
 	ssize_t bytes;
 
 	if (ifp != NULL) {
 		state = D_CSTATE(ifp);
-		s = state->udp_fd;
+		s = state->udp_rfd;
 	} else
-		s = ctx->udp_fd;
+		s = ctx->udp_rfd;
 
 	bytes = recvmsg(s, &msg, 0);
 	if (bytes == -1) {
@@ -3674,11 +3661,11 @@ dhcp_openbpf(struct interface *ifp)
 	}
 #endif
 
-	if (state->bpf_fd != -1)
+	if (state->bpf != NULL)
 		return 0;
 
-	state->bpf_fd = bpf_open(ifp, bpf_bootp);
-	if (state->bpf_fd == -1) {
+	state->bpf = bpf_open(ifp, bpf_bootp, NULL);
+	if (state->bpf == NULL) {
 		if (errno == ENOENT) {
 			logerrx("%s not found", bpf_name);
 			/* May as well disable IPv4 entirely at
@@ -3690,7 +3677,7 @@ dhcp_openbpf(struct interface *ifp)
 	}
 
 	eloop_event_add(ifp->ctx->eloop,
-	    state->bpf_fd, dhcp_readbpf, ifp);
+	    state->bpf->bpf_fd, dhcp_readbpf, ifp);
 	return 0;
 }
 
@@ -3724,10 +3711,14 @@ dhcp_free(struct interface *ifp)
 		}
 	}
 	if (ifp == NULL) {
-		if (ctx->udp_fd != -1) {
-			eloop_event_delete(ctx->eloop, ctx->udp_fd);
-			close(ctx->udp_fd);
-			ctx->udp_fd = -1;
+		if (ctx->udp_rfd != -1) {
+			eloop_event_delete(ctx->eloop, ctx->udp_rfd);
+			close(ctx->udp_rfd);
+			ctx->udp_rfd = -1;
+		}
+		if (ctx->udp_wfd != -1) {
+			close(ctx->udp_wfd);
+			ctx->udp_wfd = -1;
 		}
 
 		free(ctx->opt_buffer);
@@ -3751,8 +3742,7 @@ dhcp_initstate(struct interface *ifp)
 
 	state->state = DHS_NONE;
 	/* 0 is a valid fd, so init to -1 */
-	state->bpf_fd = -1;
-	state->udp_fd = -1;
+	state->udp_rfd = -1;
 #ifdef ARPING
 	state->arping_index = -1;
 #endif
@@ -3763,7 +3753,7 @@ static int
 dhcp_init(struct interface *ifp)
 {
 	struct dhcp_state *state;
-	const struct if_options *ifo;
+	struct if_options *ifo;
 	uint8_t len;
 	char buf[(sizeof(ifo->clientid) - 1) * 3];
 
@@ -3781,7 +3771,7 @@ dhcp_init(struct interface *ifp)
 	/* We need to drop the leasefile so that dhcp_start
 	 * doesn't load it. */
 	if (ifo->options & DHCPCD_REQUEST)
-		unlink(state->leasefile);
+		dhcp_unlink(ifp->ctx, state->leasefile);
 
 	free(state->clientid);
 	state->clientid = NULL;
@@ -3823,7 +3813,7 @@ dhcp_init(struct interface *ifp)
 			if (state->clientid == NULL)
 				goto eexit;
 			state->clientid[0] = len;
-			state->clientid[1] = (uint8_t)ifp->family;
+			state->clientid[1] = (uint8_t)ifp->hwtype;
 			memcpy(state->clientid + 2, ifp->hwaddr,
 			    ifp->hwlen);
 		}
@@ -3834,7 +3824,7 @@ dhcp_init(struct interface *ifp)
 		 * at device start. */
 		return 0;
 
-	if (ifo->options & DHCPCD_CLIENTID)
+	if (ifo->options & DHCPCD_CLIENTID && state->clientid != NULL)
 		logdebugx("%s: using ClientID %s", ifp->name,
 		    hwaddr_ntoa(state->clientid + 1, state->clientid[0],
 			buf, sizeof(buf)));
@@ -3855,7 +3845,6 @@ dhcp_start1(void *arg)
 	struct dhcpcd_ctx *ctx = ifp->ctx;
 	struct if_options *ifo = ifp->options;
 	struct dhcp_state *state;
-	struct stat st;
 	uint32_t l;
 	int nolease;
 
@@ -3867,14 +3856,21 @@ dhcp_start1(void *arg)
 	 * Only do this in master mode so we don't swallow messages
 	 * for dhcpcd running on another interface. */
 	if ((ctx->options & (DHCPCD_MASTER|DHCPCD_PRIVSEP)) == DHCPCD_MASTER
-	    && ctx->udp_fd == -1)
+	    && ctx->udp_rfd == -1)
 	{
-		ctx->udp_fd = dhcp_openudp(NULL);
-		if (ctx->udp_fd == -1) {
+		ctx->udp_rfd = dhcp_openudp(NULL);
+		if (ctx->udp_rfd == -1) {
+			logerr(__func__);
+			return;
+		}
+		eloop_event_add(ctx->eloop, ctx->udp_rfd, dhcp_handleudp, ctx);
+	}
+	if (!IN_PRIVSEP(ctx) && ctx->udp_wfd == -1) {
+		ctx->udp_wfd = xsocket(PF_INET, SOCK_RAW|SOCK_CXNB,IPPROTO_UDP);
+		if (ctx->udp_wfd == -1) {
 			logerr(__func__);
 			return;
 		}
-		eloop_event_add(ctx->eloop, ctx->udp_fd, dhcp_handleudp, ctx);
 	}
 
 	if (dhcp_init(ifp) == -1) {
@@ -3891,11 +3887,7 @@ dhcp_start1(void *arg)
 
 #ifdef ARPING
 	if (ifo->arping_len && state->arping_index < ifo->arping_len) {
-		struct arp_state *astate;
-
-		astate = dhcp_arp_new(ifp, NULL);
-		if (astate)
-			dhcp_arp_not_found(astate);
+		dhcp_arping(ifp);
 		return;
 	}
 #endif
@@ -3940,6 +3932,7 @@ dhcp_start1(void *arg)
 	}
 	if (state->offer) {
 		struct ipv4_addr *ia;
+		time_t mtime;
 
 		get_lease(ifp, &state->lease, state->offer, state->offer_len);
 		state->lease.frominfo = 1;
@@ -3967,14 +3960,14 @@ dhcp_start1(void *arg)
 			state->offer_len = 0;
 		} else if (!(ifo->options & DHCPCD_LASTLEASE_EXTEND) &&
 		    state->lease.leasetime != DHCP_INFINITE_LIFETIME &&
-		    stat(state->leasefile, &st) == 0)
+		    dhcp_filemtime(ifp->ctx, state->leasefile, &mtime) == 0)
 		{
 			time_t now;
 
 			/* Offset lease times and check expiry */
 			now = time(NULL);
 			if (now == -1 ||
-			    (time_t)state->lease.leasetime < now - st.st_mtime)
+			    (time_t)state->lease.leasetime < now - mtime)
 			{
 				logdebugx("%s: discarding expired lease",
 				    ifp->name);
@@ -3999,7 +3992,7 @@ dhcp_start1(void *arg)
 					dhcp_drop(ifp, "EXPIRE");
 #endif
 			} else {
-				l = (uint32_t)(now - st.st_mtime);
+				l = (uint32_t)(now - mtime);
 				state->lease.leasetime -= l;
 				state->lease.renewaltime -= l;
 				state->lease.rebindtime -= l;
@@ -4043,7 +4036,7 @@ dhcp_start(struct interface *ifp)
 
 	/* If we haven't specified a ClientID and our hardware address
 	 * length is greater than BOOTP CHADDR then we enforce a ClientID
-	 * of the hardware address family and the hardware address.
+	 * of the hardware address type and the hardware address.
 	 * If there is no hardware address and no ClientID set,
 	 * force a DUID based ClientID. */
 	if (ifp->hwlen > 16)
@@ -4053,7 +4046,7 @@ dhcp_start(struct interface *ifp)
 
 	/* Firewire and InfiniBand interfaces require ClientID and
 	 * the broadcast option being set. */
-	switch (ifp->family) {
+	switch (ifp->hwtype) {
 	case ARPHRD_IEEE1394:	/* FALLTHROUGH */
 	case ARPHRD_INFINIBAND:
 		ifp->options->options |= DHCPCD_CLIENTID | DHCPCD_BROADCAST;
@@ -4105,7 +4098,8 @@ dhcp_abort(struct interface *ifp)
 	if (state != NULL && state->added) {
 		rt_build(ifp->ctx, AF_INET);
 #ifdef ARP
-		arp_announceaddr(ifp->ctx, &state->addr->addr);
+		if (ifp->options->options & DHCPCD_ARP)
+			arp_announceaddr(ifp->ctx, &state->addr->addr);
 #endif
 	}
 }
Index: src/external/bsd/dhcpcd/dist/src/dhcpcd.c
diff -u src/external/bsd/dhcpcd/dist/src/dhcpcd.c:1.36 src/external/bsd/dhcpcd/dist/src/dhcpcd.c:1.37
--- src/external/bsd/dhcpcd/dist/src/dhcpcd.c:1.36	Sun May 10 17:58:16 2020
+++ src/external/bsd/dhcpcd/dist/src/dhcpcd.c	Sun May 31 12:52:11 2020
@@ -71,6 +71,9 @@ const char dhcpcd_copyright[] = "Copyrig
 #include "privsep.h"
 #include "script.h"
 
+#ifdef HAVE_CAPSICUM
+#include <sys/capsicum.h>
+#endif
 #ifdef HAVE_UTIL_H
 #include <util.h>
 #endif
@@ -80,17 +83,23 @@ const int dhcpcd_signals[] = {
 	SIGTERM,
 	SIGINT,
 	SIGALRM,
-	SIGPIPE,
 	SIGHUP,
 	SIGUSR1,
 	SIGUSR2,
 };
 const size_t dhcpcd_signals_len = __arraycount(dhcpcd_signals);
+
+const int dhcpcd_signals_ignore[] = {
+	SIGPIPE,
+};
+const size_t dhcpcd_signals_ignore_len = __arraycount(dhcpcd_signals_ignore);
 #endif
 
 #define IF_UPANDRUNNING(a) \
 	(((a)->flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING))
 
+const char *dhcpcd_default_script = SCRIPT;
+
 static void
 usage(void)
 {
@@ -349,7 +358,7 @@ dhcpcd_daemonise(struct dhcpcd_ctx *ctx)
 
 	/* Don't use loginfo because this makes no sense in a log. */
 	if (!(loggetopts() & LOGERR_QUIET))
-		fprintf(stderr, "forked to background, child pid %d\n",
+		(void)fprintf(stderr, "forked to background, child pid %d\n",
 		    getpid());
 	i = EXIT_SUCCESS;
 	if (write(ctx->fork_fd, &i, sizeof(i)) == -1)
@@ -358,8 +367,17 @@ dhcpcd_daemonise(struct dhcpcd_ctx *ctx)
 	eloop_event_delete(ctx->eloop, ctx->fork_fd);
 	close(ctx->fork_fd);
 	ctx->fork_fd = -1;
-	freopen(_PATH_DEVNULL, "w", stdout);
-	freopen(_PATH_DEVNULL, "w", stderr);
+#ifdef PRIVSEP
+	if (ctx->options & DHCPCD_PRIVSEP) {
+		/* Aside from Linux, we don't have access to /dev/null */
+		fclose(stdout);
+		fclose(stderr);
+	} else
+#endif
+	{
+		(void)freopen(_PATH_DEVNULL, "w", stdout);
+		(void)freopen(_PATH_DEVNULL, "w", stderr);
+	}
 #endif
 }
 
@@ -826,12 +844,27 @@ warn_iaid_conflict(struct interface *ifp
 		    ifp->name, ifn->name);
 }
 
+static void
+dhcpcd_initduid(struct dhcpcd_ctx *ctx, struct interface *ifp)
+{
+	char buf[DUID_LEN * 3];
+
+	if (ctx->duid != NULL)
+		return;
+
+	duid_init(ctx, ifp);
+	if (ctx->duid == NULL)
+		return;
+
+	loginfox("DUID %s",
+	    hwaddr_ntoa(ctx->duid, ctx->duid_len, buf, sizeof(buf)));
+}
+
 void
 dhcpcd_startinterface(void *arg)
 {
 	struct interface *ifp = arg;
 	struct if_options *ifo = ifp->options;
-	char buf[DUID_LEN * 3];
 	int carrier;
 
 	if (ifo->options & DHCPCD_LINK) {
@@ -864,26 +897,21 @@ dhcpcd_startinterface(void *arg)
 	if (ifo->options & (DHCPCD_DUID | DHCPCD_IPV6) &&
 	    !(ifo->options & DHCPCD_ANONYMOUS))
 	{
+		char buf[sizeof(ifo->iaid) * 3];
 #ifdef INET6
 		size_t i;
 		struct if_ia *ia;
 #endif
 
-		/* Report client DUID */
-		if (ifp->ctx->duid == NULL) {
-			if (duid_init(ifp) == 0)
-				return;
-			loginfox("DUID %s",
-			    hwaddr_ntoa(ifp->ctx->duid,
-			    ifp->ctx->duid_len,
-			    buf, sizeof(buf)));
-		}
+		/* Try and init DUID from the interface hardware address */
+		dhcpcd_initduid(ifp->ctx, ifp);
 
 		/* Report IAIDs */
 		loginfox("%s: IAID %s", ifp->name,
 		    hwaddr_ntoa(ifo->iaid, sizeof(ifo->iaid),
 		    buf, sizeof(buf)));
 		warn_iaid_conflict(ifp, 0, ifo->iaid);
+
 #ifdef INET6
 		for (i = 0; i < ifo->ia_len; i++) {
 			ia = &ifo->ia[i];
@@ -1137,6 +1165,9 @@ dhcpcd_linkoverflow(struct dhcpcd_ctx *c
 {
 	socklen_t socklen;
 	int rcvbuflen;
+	char buf[2048];
+	ssize_t rlen;
+	size_t rcnt;
 	struct if_head *ifaces;
 	struct ifaddrs *ifaddrs;
 	struct interface *ifp, *ifn, *ifp1;
@@ -1153,21 +1184,16 @@ dhcpcd_linkoverflow(struct dhcpcd_ctx *c
 	logerrx("route socket overflowed (rcvbuflen %d)"
 	    " - learning interface state", rcvbuflen);
 
-	/* Close the existing socket and open a new one.
-	 * This is easier than draining the kernel buffer of an
-	 * in-determinate size. */
-	eloop_event_delete(ctx->eloop, ctx->link_fd);
-	close(ctx->link_fd);
-	if_closesockets_os(ctx);
-	if (if_opensockets_os(ctx) == -1) {
-		logerr("%s: if_opensockets", __func__);
-		eloop_exit(ctx->eloop, EXIT_FAILURE);
-		return;
-	}
-#ifndef SMALL
-	dhcpcd_setlinkrcvbuf(ctx);
-#endif
-	eloop_event_add(ctx->eloop, ctx->link_fd, dhcpcd_handlelink, ctx);
+	/* Drain the socket.
+	 * We cannot open a new one due to privsep. */
+	rcnt = 0;
+	do {
+		rlen = read(ctx->link_fd, buf, sizeof(buf));
+		if (++rcnt % 1000 == 0)
+			logwarnx("drained %zu messages", rcnt);
+	} while (rlen != -1 || errno == ENOBUFS || errno == ENOMEM);
+	if (rcnt % 1000 != 0)
+		logwarnx("drained %zu messages", rcnt);
 
 	/* Work out the current interfaces. */
 	ifaces = if_discover(ctx, &ifaddrs, ctx->ifc, ctx->ifv);
@@ -1209,17 +1235,12 @@ dhcpcd_linkoverflow(struct dhcpcd_ctx *c
 }
 
 void
-dhcpcd_handlehwaddr(struct dhcpcd_ctx *ctx, const char *ifname,
-    const void *hwaddr, uint8_t hwlen)
+dhcpcd_handlehwaddr(struct interface *ifp,
+    uint16_t hwtype, const void *hwaddr, uint8_t hwlen)
 {
-	struct interface *ifp;
 	char buf[sizeof(ifp->hwaddr) * 3];
 
-	ifp = if_find(ctx->ifaces, ifname);
-	if (ifp == NULL)
-		return;
-
-	if (!if_valid_hwaddr(hwaddr, hwlen))
+	if (hwaddr == NULL || !if_valid_hwaddr(hwaddr, hwlen))
 		hwlen = 0;
 
 	if (hwlen > sizeof(ifp->hwaddr)) {
@@ -1228,13 +1249,21 @@ dhcpcd_handlehwaddr(struct dhcpcd_ctx *c
 		return;
 	}
 
-	if (ifp->hwlen == hwlen && memcmp(ifp->hwaddr, hwaddr, hwlen) == 0)
+	if (ifp->hwtype != hwtype) {
+		loginfox("%s: hardware address type changed from %d to %d",
+		    ifp->name, ifp->hwtype, hwtype);
+		ifp->hwtype = hwtype;
+	}
+
+	if (ifp->hwlen == hwlen &&
+	    (hwlen == 0 || memcmp(ifp->hwaddr, hwaddr, hwlen) == 0))
 		return;
 
 	loginfox("%s: new hardware address: %s", ifp->name,
 	    hwaddr_ntoa(hwaddr, hwlen, buf, sizeof(buf)));
 	ifp->hwlen = hwlen;
-	memcpy(ifp->hwaddr, hwaddr, hwlen);
+	if (hwaddr != NULL)
+		memcpy(ifp->hwaddr, hwaddr, hwlen);
 }
 
 static void
@@ -1313,13 +1342,13 @@ stop_all_interfaces(struct dhcpcd_ctx *c
 	ctx->options |= DHCPCD_EXITING;
 	/* Drop the last interface first */
 	TAILQ_FOREACH_REVERSE(ifp, ctx->ifaces, if_head, next) {
-		if (ifp->active) {
-			ifp->options->options |= opts;
-			if (ifp->options->options & DHCPCD_RELEASE)
-				ifp->options->options &= ~DHCPCD_PERSISTENT;
-			ifp->options->options |= DHCPCD_EXITING;
-			stop_interface(ifp);
-		}
+		if (!ifp->active)
+			continue;
+		ifp->options->options |= opts;
+		if (ifp->options->options & DHCPCD_RELEASE)
+			ifp->options->options &= ~DHCPCD_PERSISTENT;
+		ifp->options->options |= DHCPCD_EXITING;
+		stop_interface(ifp);
 	}
 }
 
@@ -1696,7 +1725,7 @@ again3:
 		}
 		dp = buf;
 		de = dp + dlen;
-		if (*(dp - 1) != '\0') {
+		if (*(de - 1) != '\0') {
 			errno = EINVAL;
 			error = -1;
 			goto out;
@@ -1750,7 +1779,7 @@ main(int argc, char **argv)
 	struct ifaddrs *ifaddrs = NULL;
 	struct if_options *ifo;
 	struct interface *ifp;
-	uint16_t family = 0;
+	sa_family_t family = AF_UNSPEC;
 	int opt, oi = 0, i;
 	unsigned int logopts, t;
 	ssize_t len;
@@ -1761,6 +1790,7 @@ main(int argc, char **argv)
 #ifdef USE_SIGNALS
 	int sig = 0;
 	const char *siga = NULL;
+	size_t si;
 #endif
 
 	/* Test for --help and --version */
@@ -1804,6 +1834,7 @@ main(int argc, char **argv)
 
 	ifo = NULL;
 	ctx.cffile = CONFIG;
+	ctx.script = UNCONST(dhcpcd_default_script);
 	ctx.control_fd = ctx.control_unpriv_fd = ctx.link_fd = -1;
 	ctx.pf_inet_fd = -1;
 
@@ -1815,13 +1846,15 @@ main(int argc, char **argv)
 	ctx.dev_fd = -1;
 #endif
 #ifdef INET
-	ctx.udp_fd = -1;
+	ctx.udp_rfd = -1;
+	ctx.udp_wfd = -1;
 #endif
 #if defined(INET6) && !defined(__sun)
 	ctx.nd_fd = -1;
 #endif
 #ifdef DHCP6
-	ctx.dhcp6_fd = -1;
+	ctx.dhcp6_rfd = -1;
+	ctx.dhcp6_wfd = -1;
 #endif
 #ifdef PRIVSEP
 	ctx.ps_root_fd = ctx.ps_data_fd = -1;
@@ -1865,7 +1898,14 @@ main(int argc, char **argv)
 			i = 4;
 			break;
 		case 'q':
-			logopts |= LOGERR_QUIET;
+			/* -qq disables console output entirely.
+			 * This is important for systemd because it logs
+			 * both console AND syslog to the same log
+			 * resulting in untold confusion. */
+			if (logopts & LOGERR_QUIET)
+				logopts &= ~LOGERR_ERR;
+			else
+				logopts |= LOGERR_QUIET;
 			break;
 		case 'x':
 			sig = SIGTERM;
@@ -2017,8 +2057,8 @@ printpidfile:
 	}
 
 #ifdef USE_SIGNALS
-	/* Ignore SIGPIPE, prefer EPIPE. */
-	signal(SIGPIPE, SIG_IGN);
+	for (si = 0; si < dhcpcd_signals_ignore_len; si++)
+		signal(dhcpcd_signals_ignore[si], SIG_IGN);
 
 	/* Save signal mask, block and redirect signals to our handler */
 	if (eloop_signal_set_cb(ctx.eloop,
@@ -2033,19 +2073,52 @@ printpidfile:
 		goto exit_failure;
 	}
 
+	if (sig != 0) {
+		pid = pidfile_read(ctx.pidfile);
+		if (pid != 0 && pid != -1)
+			loginfox("sending signal %s to pid %d", siga, pid);
+		if (pid == 0 || pid == -1 || kill(pid, sig) != 0) {
+			if (sig != SIGHUP && sig != SIGUSR1 && errno != EPERM)
+				logerrx(PACKAGE" not running");
+			if (pid != 0 && pid != -1 && errno != ESRCH) {
+				logerr("kill");
+				goto exit_failure;
+			}
+			unlink(ctx.pidfile);
+			if (sig != SIGHUP && sig != SIGUSR1)
+				goto exit_failure;
+		} else {
+			struct timespec ts;
+
+			if (sig == SIGHUP || sig == SIGUSR1)
+				goto exit_success;
+			/* Spin until it exits */
+			loginfox("waiting for pid %d to exit", pid);
+			ts.tv_sec = 0;
+			ts.tv_nsec = 100000000; /* 10th of a second */
+			for(i = 0; i < 100; i++) {
+				nanosleep(&ts, NULL);
+				if (pidfile_read(ctx.pidfile) == -1)
+					goto exit_success;
+			}
+			logerrx("pid %d failed to exit", pid);
+			goto exit_failure;
+		}
+	}
+#endif
+
 	/* Test against siga instead of sig to avoid gcc
 	 * warning about a bogus potential signed overflow.
 	 * The end result will be the same. */
 	if ((siga == NULL || i == 4 || ctx.ifc != 0) &&
 	    !(ctx.options & DHCPCD_TEST))
 	{
-#endif
 		ctx.options |= DHCPCD_FORKED; /* avoid socket unlink */
 		if (!(ctx.options & DHCPCD_MASTER))
-			ctx.control_fd = control_open(argv[optind],
+			ctx.control_fd = control_open(argv[optind], family,
 			    ctx.options & DHCPCD_DUMPLEASE);
 		if (ctx.control_fd == -1)
-			ctx.control_fd = control_open(NULL,
+			ctx.control_fd = control_open(NULL, AF_UNSPEC,
 			    ctx.options & DHCPCD_DUMPLEASE);
 		if (ctx.control_fd != -1) {
 			if (!(ctx.options & DHCPCD_DUMPLEASE))
@@ -2074,42 +2147,6 @@ printpidfile:
 			}
 		}
 		ctx.options &= ~DHCPCD_FORKED;
-#ifdef USE_SIGNALS
-	}
-#endif
-
-#ifdef USE_SIGNALS
-	if (sig != 0) {
-		pid = pidfile_read(ctx.pidfile);
-		if (pid != 0 && pid != -1)
-			loginfox("sending signal %s to pid %d", siga, pid);
-		if (pid == 0 || pid == -1 || kill(pid, sig) != 0) {
-			if (sig != SIGHUP && sig != SIGUSR1 && errno != EPERM)
-				logerrx(PACKAGE" not running");
-			if (pid != 0 && pid != -1 && errno != ESRCH) {
-				logerr("kill");
-				goto exit_failure;
-			}
-			unlink(ctx.pidfile);
-			if (sig != SIGHUP && sig != SIGUSR1)
-				goto exit_failure;
-		} else {
-			struct timespec ts;
-
-			if (sig == SIGHUP || sig == SIGUSR1)
-				goto exit_success;
-			/* Spin until it exits */
-			loginfox("waiting for pid %d to exit", pid);
-			ts.tv_sec = 0;
-			ts.tv_nsec = 100000000; /* 10th of a second */
-			for(i = 0; i < 100; i++) {
-				nanosleep(&ts, NULL);
-				if (pidfile_read(ctx.pidfile) == -1)
-					goto exit_success;
-			}
-			logerrx("pid %d failed to exit", pid);
-			goto exit_failure;
-		}
 	}
 
 	if (!(ctx.options & DHCPCD_TEST)) {
@@ -2127,14 +2164,12 @@ printpidfile:
 			goto exit_failure;
 		}
 	}
-#endif
 
 	loginfox(PACKAGE "-" VERSION " starting");
 	freopen(_PATH_DEVNULL, "r", stdin);
 
 #ifdef PRIVSEP
-	if (ps_init(&ctx) == 0)
-		script_runchroot(&ctx, ifo->script);
+	ps_init(&ctx);
 #endif
 
 #ifdef USE_SIGNALS
@@ -2210,23 +2245,19 @@ printpidfile:
 
 	if (!(ctx.options & DHCPCD_TEST) &&
 	    control_start(&ctx,
-	    ctx.options & DHCPCD_MASTER ? NULL : argv[optind]) == -1)
+	    ctx.options & DHCPCD_MASTER ? NULL : argv[optind], family) == -1)
 	{
 		logerr("%s: control_start", __func__);
 		goto exit_failure;
 	}
 
+#ifdef PLUGIN_DEV
 	/* Start any dev listening plugin which may want to
 	 * change the interface name provided by the kernel */
-	if ((ctx.options & (DHCPCD_MASTER | DHCPCD_DEV)) ==
+	if (!IN_PRIVSEP(&ctx) &&
+	    (ctx.options & (DHCPCD_MASTER | DHCPCD_DEV)) ==
 	    (DHCPCD_MASTER | DHCPCD_DEV))
-		dev_start(&ctx);
-
-#ifdef PRIVSEP
-	if (ctx.options & DHCPCD_PRIVSEP && ps_dropprivs(&ctx) == -1) {
-		logerr("ps_dropprivs");
-		goto exit_failure;
-	}
+		dev_start(&ctx, dhcpcd_handleinterface);
 #endif
 
 	setproctitle("%s%s%s",
@@ -2242,6 +2273,34 @@ printpidfile:
 	dhcpcd_setlinkrcvbuf(&ctx);
 #endif
 
+	/* Try and create DUID from the machine UUID. */
+	dhcpcd_initduid(&ctx, NULL);
+
+	/* Cache the default vendor option. */
+	if (dhcp_vendor(ctx.vendor, sizeof(ctx.vendor)) == -1)
+		logerrx("dhcp_vendor");
+
+#ifdef PRIVSEP
+	if (ctx.options & DHCPCD_PRIVSEP) {
+		if (ps_dropprivs(&ctx) == -1) {
+			logerr("ps_dropprivs");
+			goto exit_failure;
+		}
+#ifdef HAVE_CAPSICUM
+		if (cap_enter() == -1 && errno != ENOSYS) {
+			logerr("%s: cap_enter", __func__);
+			goto exit_failure;
+		}
+#endif
+#ifdef HAVE_PLEDGE
+		if (pledge("stdio inet route dns", NULL) == -1) {
+			logerr("%s: pledge", __func__);
+			goto exit_failure;
+		}
+#endif
+	}
+#endif
+
 	/* When running dhcpcd against a single interface, we need to retain
 	 * the old behaviour of waiting for an IP address */
 	if (ctx.ifc == 1 && !(ctx.options & DHCPCD_BACKGROUND))
@@ -2392,11 +2451,15 @@ exit1:
 #ifdef INET6
 	ipv6_ctxfree(&ctx);
 #endif
+#ifdef PLUGIN_DEV
 	dev_stop(&ctx);
+#endif
 #ifdef PRIVSEP
 	eloop_free(ctx.ps_eloop);
 #endif
 	eloop_free(ctx.eloop);
+	if (ctx.script != dhcpcd_default_script)
+		free(ctx.script);
 	if (ctx.options & DHCPCD_STARTED && !(ctx.options & DHCPCD_FORKED))
 		loginfox(PACKAGE " exited");
 	logclose();

Index: src/external/bsd/dhcpcd/dist/src/dhcp6.c
diff -u src/external/bsd/dhcpcd/dist/src/dhcp6.c:1.18 src/external/bsd/dhcpcd/dist/src/dhcp6.c:1.19
--- src/external/bsd/dhcpcd/dist/src/dhcp6.c:1.18	Tue Apr 21 09:55:33 2020
+++ src/external/bsd/dhcpcd/dist/src/dhcp6.c	Sun May 31 12:52:11 2020
@@ -26,12 +26,11 @@
  * SUCH DAMAGE.
  */
 
-/* TODO: We should decline dupliate addresses detected */
-
-#include <sys/stat.h>
 #include <sys/utsname.h>
+#include <sys/types.h>
 
 #include <netinet/in.h>
+#include <netinet/ip6.h>
 
 #include <assert.h>
 #include <ctype.h>
@@ -139,6 +138,7 @@ static const struct dhcp6_op dhcp6_ops[]
 	{ DHCP6_INFORMATION_REQ, "INFORM6" },
 	{ DHCP6_RELEASE, "RELEASE6" },
 	{ DHCP6_RECONFIGURE, "RECONFIGURE6" },
+	{ DHCP6_DECLINE, "DECLINE6" },
 	{ 0, NULL }
 };
 
@@ -174,6 +174,7 @@ static const char * const dhcp6_statuses
 static void dhcp6_bind(struct interface *, const char *, const char *);
 static void dhcp6_failinform(void *);
 static void dhcp6_recvaddr(void *);
+static void dhcp6_startdecline(struct interface *);
 
 #ifdef SMALL
 #define dhcp6_hasprefixdelegation(a)	(0)
@@ -181,6 +182,12 @@ static void dhcp6_recvaddr(void *);
 static int dhcp6_hasprefixdelegation(struct interface *);
 #endif
 
+#define DECLINE_IA(ia) \
+	((ia)->addr_flags & IN6_IFF_DUPLICATED && \
+	(ia)->ia_type != 0 && (ia)->ia_type != D6_OPTION_IA_PD && \
+	!((ia)->flags & IPV6_AF_STALE) && \
+	(ia)->prefix_vltime != 0)
+
 void
 dhcp6_printoptions(const struct dhcpcd_ctx *ctx,
     const struct dhcp_opt *opts, size_t opts_len)
@@ -252,28 +259,27 @@ static size_t
 dhcp6_makevendor(void *data, const struct interface *ifp)
 {
 	const struct if_options *ifo;
-	size_t len, i;
+	size_t len, vlen, i;
 	uint8_t *p;
-	ssize_t vlen;
 	const struct vivco *vivco;
-	char vendor[VENDORCLASSID_MAX_LEN];
 	struct dhcp6_option o;
 
 	ifo = ifp->options;
 	len = sizeof(uint32_t); /* IANA PEN */
 	if (ifo->vivco_en) {
+		vlen = 0;
 		for (i = 0, vivco = ifo->vivco;
 		    i < ifo->vivco_len;
 		    i++, vivco++)
-			len += sizeof(uint16_t) + vivco->len;
-		vlen = 0; /* silence bogus gcc warning */
-	} else {
-		vlen = dhcp_vendor(vendor, sizeof(vendor));
-		if (vlen == -1)
-			vlen = 0;
-		else
-			len += sizeof(uint16_t) + (size_t)vlen;
-	}
+			vlen += sizeof(uint16_t) + vivco->len;
+		len += vlen;
+	} else if (ifo->vendorclassid[0] != '\0') {
+		/* dhcpcd owns DHCPCD_IANA_PEN.
+		 * If you need your own string, get your own IANA PEN. */
+		vlen = strlen(ifp->ctx->vendor);
+		len += sizeof(uint16_t) + vlen;
+	} else
+		return 0;
 
 	if (len > UINT16_MAX) {
 		logerrx("%s: DHCPv6 Vendor Class too big", ifp->name);
@@ -304,11 +310,11 @@ dhcp6_makevendor(void *data, const struc
 				memcpy(p, vivco->data, vivco->len);
 				p += vivco->len;
 			}
-		} else if (vlen) {
+		} else if (ifo->vendorclassid[0] != '\0') {
 			hvlen = htons((uint16_t)vlen);
 			memcpy(p, &hvlen, sizeof(hvlen));
 			p += sizeof(hvlen);
-			memcpy(p, vendor, (size_t)vlen);
+			memcpy(p, ifp->ctx->vendor, vlen);
 		}
 	}
 
@@ -672,7 +678,7 @@ dhcp6_makemessage(struct interface *ifp)
 	len = 0;
 	si = NULL;
 	hl = 0; /* Appease gcc */
-	if (state->state != DH6S_RELEASE) {
+	if (state->state != DH6S_RELEASE && state->state != DH6S_DECLINE) {
 		for (l = 0, opt = ifp->ctx->dhcp6_opts;
 		    l < ifp->ctx->dhcp6_opts_len;
 		    l++, opt++)
@@ -750,6 +756,8 @@ dhcp6_makemessage(struct interface *ifp)
 		m = state->recv;
 		ml = state->recv_len;
 		/* FALLTHROUGH */
+	case DH6S_DECLINE:
+		/* FALLTHROUGH */
 	case DH6S_RELEASE:
 		/* FALLTHROUGH */
 	case DH6S_RENEW:
@@ -778,6 +786,8 @@ dhcp6_makemessage(struct interface *ifp)
 			    (ap->prefix_vltime == 0 ||
 			    state->state == DH6S_DISCOVER))
 				continue;
+			if (DECLINE_IA(ap) && state->state != DH6S_DECLINE)
+				continue;
 			if (ap->ia_type == D6_OPTION_IA_PD) {
 #ifndef SMALL
 				len += sizeof(o) + sizeof(struct dhcp6_pd_addr);
@@ -836,6 +846,9 @@ dhcp6_makemessage(struct interface *ifp)
 	case DH6S_RELEASE:
 		type = DHCP6_RELEASE;
 		break;
+	case DH6S_DECLINE:
+		type = DHCP6_DECLINE;
+		break;
 	default:
 		errno = EINVAL;
 		return -1;
@@ -949,6 +962,8 @@ dhcp6_makemessage(struct interface *ifp)
 			    (ap->prefix_vltime == 0 ||
 			    state->state == DH6S_DISCOVER))
 				continue;
+			if (DECLINE_IA(ap) && state->state != DH6S_DECLINE)
+				continue;
 			if (ap->ia_type != ifia->ia_type)
 				continue;
 			if (memcmp(ap->iaid, ifia->iaid, sizeof(ap->iaid)))
@@ -1010,7 +1025,10 @@ dhcp6_makemessage(struct interface *ifp)
 		memcpy(o_lenp, &ia_na_len, sizeof(ia_na_len));
 	}
 
-	if (state->send->type != DHCP6_RELEASE && n_options) {
+	if (state->send->type != DHCP6_RELEASE &&
+	    state->send->type != DHCP6_DECLINE &&
+	    n_options)
+	{
 		o_lenp = NEXTLEN;
 		o.len = 0;
 		COPYIN1(D6_OPTION_ORO, 0);
@@ -1072,7 +1090,9 @@ dhcp6_makemessage(struct interface *ifp)
 	if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_CLASS))
 		p += dhcp6_makevendor(p, ifp);
 
-	if (state->send->type != DHCP6_RELEASE) {
+	if (state->send->type != DHCP6_RELEASE &&
+	    state->send->type != DHCP6_DECLINE)
+	{
 		if (fqdn != FQDN_DISABLE) {
 			o_lenp = NEXTLEN;
 			COPYIN1(D6_OPTION_FQDN, 0);
@@ -1182,59 +1202,71 @@ dhcp6_update_auth(struct interface *ifp,
 }
 #endif
 
+static const struct in6_addr alldhcp = IN6ADDR_LINKLOCAL_ALLDHCP_INIT;
 static int
 dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *))
 {
 	struct dhcp6_state *state = D6_STATE(ifp);
 	struct dhcpcd_ctx *ctx = ifp->ctx;
+	unsigned int RT;
+	bool broadcast = true;
 	struct sockaddr_in6 dst = {
 	    .sin6_family = AF_INET6,
+	    /* Setting the port on Linux gives EINVAL when sending.
+	     * This looks like a kernel bug as the equivalent works
+	     * fine with the DHCP counterpart. */
+#ifndef __linux__
 	    .sin6_port = htons(DHCP6_SERVER_PORT),
+#endif
 	};
-	unsigned int RT;
-	const char *broad_uni;
-	const struct in6_addr alldhcp = IN6ADDR_LINKLOCAL_ALLDHCP_INIT;
-	struct ipv6_addr *lla;
-	int s;
-	struct iovec iov = {
-	    .iov_base = state->send, .iov_len = state->send_len,
+	struct udphdr udp = {
+	    .uh_sport = htons(DHCP6_CLIENT_PORT),
+	    .uh_dport = htons(DHCP6_SERVER_PORT),
+	    .uh_ulen = htons((uint16_t)(sizeof(udp) + state->send_len)),
 	};
-	unsigned char ctl[CMSG_SPACE(sizeof(struct in6_pktinfo))] = { 0 };
+	struct iovec iov[] = {
+	    { .iov_base = &udp, .iov_len = sizeof(udp), },
+	    { .iov_base = state->send, .iov_len = state->send_len, },
+	};
+	union {
+		struct cmsghdr hdr;
+		uint8_t buf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+	} cmsgbuf = { .buf = { 0 } };
 	struct msghdr msg = {
 	    .msg_name = &dst, .msg_namelen = sizeof(dst),
-	    .msg_iov = &iov, .msg_iovlen = 1,
+	    .msg_iov = iov, .msg_iovlen = __arraycount(iov),
 	};
+	char uaddr[INET6_ADDRSTRLEN];
 
 	if (!callback && ifp->carrier <= LINK_DOWN)
 		return 0;
 
-#ifdef HAVE_SA_LEN
-	dst.sin6_len = sizeof(dst);
-#endif
-
-	lla = ipv6_linklocal(ifp);
-	/* We need to ensure we have sufficient scope to unicast the address */
-	/* XXX FIXME: We should check any added addresses we have like from
-	 * a Router Advertisement */
-	if (IN6_IS_ADDR_UNSPECIFIED(&state->unicast) ||
-	    (state->state == DH6S_REQUEST &&
-	    (!IN6_IS_ADDR_LINKLOCAL(&state->unicast) || lla == NULL)))
-	{
-		dst.sin6_addr = alldhcp;
-		broad_uni = "broadcasting";
-	} else {
-		dst.sin6_addr = state->unicast;
-		broad_uni = "unicasting";
+	if (!IN6_IS_ADDR_UNSPECIFIED(&state->unicast)) {
+		switch (state->send->type) {
+		case DHCP6_SOLICIT:	/* FALLTHROUGH */
+		case DHCP6_CONFIRM:	/* FALLTHROUGH */
+		case DHCP6_REBIND:
+			/* Unicasting is denied for these types. */
+			break;
+		default:
+			broadcast = false;
+			inet_ntop(AF_INET6, &state->unicast, uaddr,
+			    sizeof(uaddr));
+			break;
+		}
 	}
+	dst.sin6_addr = broadcast ? alldhcp : state->unicast;
 
 	if (!callback) {
-		logdebugx("%s: %s %s with xid 0x%02x%02x%02x",
+		logdebugx("%s: %s %s with xid 0x%02x%02x%02x%s%s",
 		    ifp->name,
-		    broad_uni,
+		    broadcast ? "broadcasting" : "unicasting",
 		    dhcp6_get_op(state->send->type),
 		    state->send->xid[0],
 		    state->send->xid[1],
-		    state->send->xid[2]);
+		    state->send->xid[2],
+		    !broadcast ? " " : "",
+		    !broadcast ? uaddr : "");
 		RT = 0;
 	} else {
 		if (state->IMD &&
@@ -1248,7 +1280,6 @@ dhcp6_sendmessage(struct interface *ifp,
 			 * 1 second grace seems to be the sweet spot. */
 			if (ifp->flags & IFF_POINTOPOINT)
 				state->RT += MSEC_PER_SEC;
-			broad_uni = "delaying";
 		} else if (state->RTC == 0)
 			state->RT = state->IRT * MSEC_PER_SEC;
 
@@ -1269,14 +1300,17 @@ dhcp6_sendmessage(struct interface *ifp,
 		    * ((float)lr / DHCP6_RAND_DIV));
 
 		if (ifp->carrier > LINK_DOWN)
-			logdebugx("%s: %s %s (xid 0x%02x%02x%02x),"
+			logdebugx("%s: %s %s (xid 0x%02x%02x%02x)%s%s,"
 			    " next in %0.1f seconds",
 			    ifp->name,
-			    broad_uni,
+			    state->IMD != 0 ? "delaying" :
+			    broadcast ? "broadcasting" : "unicasting",
 			    dhcp6_get_op(state->send->type),
 			    state->send->xid[0],
 			    state->send->xid[1],
 			    state->send->xid[2],
+			    state->IMD == 0 && !broadcast ? " " : "",
+			    state->IMD == 0 && !broadcast ? uaddr : "",
 			    (float)RT / MSEC_PER_SEC);
 
 		/* Wait the initial delay */
@@ -1303,13 +1337,13 @@ dhcp6_sendmessage(struct interface *ifp,
 #endif
 
 	/* Set the outbound interface */
-	if (IN6_ARE_ADDR_EQUAL(&dst.sin6_addr, &alldhcp)) {
+	if (broadcast) {
 		struct cmsghdr *cm;
 		struct in6_pktinfo pi = { .ipi6_ifindex = ifp->index };
 
 		dst.sin6_scope_id = ifp->index;
-		msg.msg_control = ctl;
-		msg.msg_controllen = sizeof(ctl);
+		msg.msg_control = cmsgbuf.buf;
+		msg.msg_controllen = sizeof(cmsgbuf.buf);
 		cm = CMSG_FIRSTHDR(&msg);
 		if (cm == NULL) /* unlikely */
 			return -1;
@@ -1321,46 +1355,13 @@ dhcp6_sendmessage(struct interface *ifp,
 
 #ifdef PRIVSEP
 	if (IN_PRIVSEP(ifp->ctx)) {
-		struct ipv6_addr *ia;
-
-		if (IN6_ARE_ADDR_EQUAL(&dst.sin6_addr, &alldhcp))
-			ia = lla;
-		else {
-			/* Find an IA to send from */
-			TAILQ_FOREACH(ia, &state->addrs, next) {
-				if (ia->flags & IPV6_AF_STALE)
-					continue;
-				if (ia->addr_flags & IN6_IFF_NOTUSEABLE)
-					continue;
-				if (ia->ia_type == D6_OPTION_IA_PD)
-					continue;
-				break;
-			}
-		}
-		if (ia == NULL) {
-			if (lla == NULL) {
-				logerrx("%s: no address to send from",
-				    ifp->name);
-				return -1;
-			}
-			ia = lla;
-		}
-		if (ps_inet_senddhcp6(ia, &msg) == -1)
+		if (ps_inet_senddhcp6(ifp, &msg) == -1)
 			logerr(__func__);
 		goto sent;
 	}
 #endif
 
-	if (ctx->dhcp6_fd != -1)
-		s = ctx->dhcp6_fd;
-	else if (lla != NULL && lla->dhcp6_fd != -1)
-		s = lla->dhcp6_fd;
-	else {
-		logerrx("%s: no socket to send from", ifp->name);
-		return -1;
-	}
-
-	if (sendmsg(s, &msg, 0) == -1) {
+	if (sendmsg(ctx->dhcp6_wfd, &msg, 0) == -1) {
 		logerr("%s: %s: sendmsg", __func__, ifp->name);
 		/* Allow DHCPv6 to continue .... the errors
 		 * would be rate limited by the protocol.
@@ -1432,6 +1433,13 @@ dhcp6_sendconfirm(void *arg)
 }
 
 static void
+dhcp6_senddecline(void *arg)
+{
+
+	dhcp6_sendmessage(arg, dhcp6_senddecline);
+}
+
+static void
 dhcp6_sendrelease(void *arg)
 {
 
@@ -1496,54 +1504,60 @@ dhcp6_dadcallback(void *arg)
 	struct ipv6_addr *ia = arg;
 	struct interface *ifp;
 	struct dhcp6_state *state;
-	int wascompleted, valid;
+	struct ipv6_addr *ia2;
+	bool completed, valid, oneduplicated;
 
-	wascompleted = (ia->flags & IPV6_AF_DADCOMPLETED);
+	completed = (ia->flags & IPV6_AF_DADCOMPLETED);
 	ia->flags |= IPV6_AF_DADCOMPLETED;
-	if (ia->flags & IPV6_AF_DUPLICATED) {
-		/* XXX FIXME
-		 * We should decline the address */
+	if (ia->addr_flags & IN6_IFF_DUPLICATED)
 		logwarnx("%s: DAD detected %s", ia->iface->name, ia->saddr);
-	}
 
-	if (!wascompleted) {
-		ifp = ia->iface;
+#ifdef ND6_ADVERTISE
+	else
+		ipv6nd_advertise(ia);
+#endif
+	if (completed)
+		return;
 
-		state = D6_STATE(ifp);
-		if (state->state == DH6S_BOUND ||
-		    state->state == DH6S_DELEGATED)
-		{
-			struct ipv6_addr *ia2;
+	ifp = ia->iface;
+	state = D6_STATE(ifp);
+	if (state->state != DH6S_BOUND && state->state != DH6S_DELEGATED)
+		return;
 
 #ifdef SMALL
-			valid = true;
+	valid = true;
 #else
-			valid = (ia->delegating_prefix == NULL);
-#endif
-			TAILQ_FOREACH(ia2, &state->addrs, next) {
-				if (ia2->flags & IPV6_AF_ADDED &&
-				    !(ia2->flags & IPV6_AF_DADCOMPLETED))
-				{
-					wascompleted = 1;
-					break;
-				}
-			}
-			if (!wascompleted) {
-				logdebugx("%s: DHCPv6 DAD completed",
-				    ifp->name);
-				script_runreason(ifp,
-#ifndef SMALL
-				    ia->delegating_prefix ? "DELEGATED6" :
-#endif
-				    state->reason);
-				if (valid)
-					dhcpcd_daemonise(ifp->ctx);
-			}
-#ifdef ND6_ADVERTISE
-			ipv6nd_advertise(ia);
+	valid = (ia->delegating_prefix == NULL);
 #endif
+	completed = true;
+	oneduplicated = false;
+	TAILQ_FOREACH(ia2, &state->addrs, next) {
+		if (ia2->flags & IPV6_AF_ADDED &&
+		    !(ia2->flags & IPV6_AF_DADCOMPLETED))
+		{
+			completed = false;
+			break;
 		}
+		if (DECLINE_IA(ia))
+			oneduplicated = true;
+	}
+	if (!completed)
+		return;
+
+	logdebugx("%s: DHCPv6 DAD completed", ifp->name);
+
+	if (oneduplicated && state->state == DH6S_BOUND) {
+		dhcp6_startdecline(ifp);
+		return;
 	}
+
+	script_runreason(ifp,
+#ifndef SMALL
+	    ia->delegating_prefix ? "DELEGATED6" :
+#endif
+	    state->reason);
+	if (valid)
+		dhcpcd_daemonise(ifp->ctx);
 }
 
 static void
@@ -1682,7 +1696,7 @@ dhcp6_leaseextend(struct interface *ifp)
 }
 
 static void
-dhcp6_fail(struct interface* ifp)
+dhcp6_fail(struct interface *ifp)
 {
 	struct dhcp6_state *state = D6_STATE(ifp);
 
@@ -1712,7 +1726,7 @@ dhcp6_fail(struct interface* ifp)
 		state->new_len = 0;
 		if (state->old != NULL)
 			script_runreason(ifp, "EXPIRE6");
-		unlink(state->leasefile);
+		dhcp_unlink(ifp->ctx, state->leasefile);
 	}
 
 	if (!dhcp6_startdiscoinform(ifp)) {
@@ -1867,8 +1881,18 @@ static void
 dhcp6_startconfirm(struct interface *ifp)
 {
 	struct dhcp6_state *state;
+	struct ipv6_addr *ia;
 
 	state = D6_STATE(ifp);
+
+	TAILQ_FOREACH(ia, &state->addrs, next) {
+		if (!DECLINE_IA(ia))
+			continue;
+		logerrx("%s: prior DHCPv6 has a duplicated address", ifp->name);
+		dhcp6_startdecline(ifp);
+		return;
+	}
+
 	state->state = DH6S_CONFIRM;
 	state->RTC = 0;
 	state->IMD = CNF_MAX_DELAY;
@@ -1877,6 +1901,7 @@ dhcp6_startconfirm(struct interface *ifp
 	state->MRC = CNF_MAX_RC;
 
 	loginfox("%s: confirming prior DHCPv6 lease", ifp->name);
+
 	if (dhcp6_makemessage(ifp) == -1) {
 		logerr("%s: %s", __func__, ifp->name);
 		return;
@@ -1899,6 +1924,36 @@ dhcp6_startexpire(void *arg)
 }
 
 static void
+dhcp6_faildecline(void *arg)
+{
+	struct interface *ifp = arg;
+
+	logerrx("%s: failed to decline duplicated DHCPv6 addresses", ifp->name);
+	dhcp6_fail(ifp);
+}
+
+static void
+dhcp6_startdecline(struct interface *ifp)
+{
+	struct dhcp6_state *state;
+
+	state = D6_STATE(ifp);
+	loginfox("%s: declining failed DHCPv6 addresses", ifp->name);
+	state->state = DH6S_DECLINE;
+	state->RTC = 0;
+	state->IMD = 0;
+	state->IRT = DEC_TIMEOUT;
+	state->MRT = 0;
+	state->MRC = DEC_MAX_RC;
+	state->MRCcallback = dhcp6_faildecline;
+
+	if (dhcp6_makemessage(ifp) == -1)
+		logerr("%s: %s", __func__, ifp->name);
+	else
+		dhcp6_senddecline(ifp);
+}
+
+static void
 dhcp6_finishrelease(void *arg)
 {
 	struct interface *ifp;
@@ -2513,106 +2568,77 @@ dhcp6_validatelease(struct interface *if
 }
 
 static ssize_t
-dhcp6_writelease(const struct interface *ifp)
-{
-	const struct dhcp6_state *state;
-	int fd;
-	ssize_t bytes;
-
-	state = D6_CSTATE(ifp);
-	logdebugx("%s: writing lease `%s'", ifp->name, state->leasefile);
-
-	fd = open(state->leasefile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
-	if (fd == -1)
-		return -1;
-	bytes = write(fd, state->new, state->new_len);
-	close(fd);
-	return bytes;
-}
-
-static int
 dhcp6_readlease(struct interface *ifp, int validate)
 {
+	union {
+		struct dhcp6_message dhcp6;
+		uint8_t buf[UDPLEN_MAX];
+	} buf;
 	struct dhcp6_state *state;
-	struct stat st;
+	ssize_t bytes;
 	int fd;
-	time_t now;
-	int retval;
-	bool read_stdin, fd_opened;
+	time_t mtime, now;
 #ifdef AUTH
 	uint8_t *o;
 	uint16_t ol;
 #endif
 
 	state = D6_STATE(ifp);
-	read_stdin = state->leasefile[0] == '\0';
-	if (read_stdin) {
+	if (state->leasefile[0] == '\0') {
 		logdebugx("reading standard input");
-		fd = fileno(stdin);
-		fd_opened = false;
+		bytes = read(fileno(stdin), buf.buf, sizeof(buf.buf));
 	} else {
-		logdebugx("%s: reading lease `%s'", ifp->name,state->leasefile);
-		fd = open(state->leasefile, O_RDONLY);
-		if (fd != -1 && fstat(fd, &st) == -1) {
-			close(fd);
-			fd = -1;
-		}
-		fd_opened = true;
+		logdebugx("%s: reading lease `%s'",
+		    ifp->name, state->leasefile);
+		bytes = dhcp_readfile(ifp->ctx, state->leasefile,
+		    buf.buf, sizeof(buf.buf));
 	}
-	if (fd == -1)
-		return -1;
-	retval = -1;
-	free(state->new);
-	state->new_len = dhcp_read_lease_fd(fd, (void **)&state->new);
-	if (fd_opened)
-		close(fd);
+	if (bytes == -1)
+		goto ex;
 
-	if (ifp->ctx->options & DHCPCD_DUMPLEASE || read_stdin)
-		return 0;
+	if (ifp->ctx->options & DHCPCD_DUMPLEASE || state->leasefile[0] == '\0')
+		goto out;
 
-	if (state->new_len == 0) {
-		retval = 0;
+	if (bytes == 0)
 		goto ex;
-	}
 
 	/* If not validating IA's and if they have expired,
 	 * skip to the auth check. */
-	if (!validate) {
-		fd = 0;
+	if (!validate)
 		goto auth;
-	}
 
+	if (dhcp_filemtime(ifp->ctx, state->leasefile, &mtime) == -1)
+		goto ex;
 	clock_gettime(CLOCK_MONOTONIC, &state->acquired);
 	if ((now = time(NULL)) == -1)
 		goto ex;
-	state->acquired.tv_sec -= now - st.st_mtime;
+	state->acquired.tv_sec -= now - mtime;
 
 	/* Check to see if the lease is still valid */
-	fd = dhcp6_validatelease(ifp, state->new, state->new_len, NULL,
+	fd = dhcp6_validatelease(ifp, &buf.dhcp6, (size_t)bytes, NULL,
 	    &state->acquired);
 	if (fd == -1)
 		goto ex;
 
 	if (state->expire != ND6_INFINITE_LIFETIME &&
-	    (time_t)state->expire < now - st.st_mtime &&
+	    (time_t)state->expire < now - mtime &&
 	    !(ifp->options->options & DHCPCD_LASTLEASE_EXTEND))
 	{
 		logdebugx("%s: discarding expired lease", ifp->name);
-		retval = 0;
+		bytes = 0;
 		goto ex;
 	}
 
 auth:
-	retval = 0;
 #ifdef AUTH
 	/* Authenticate the message */
-	o = dhcp6_findmoption(state->new, state->new_len, D6_OPTION_AUTH, &ol);
+	o = dhcp6_findmoption(&buf.dhcp6, (size_t)bytes, D6_OPTION_AUTH, &ol);
 	if (o) {
 		if (dhcp_auth_validate(&state->auth, &ifp->options->auth,
-		    (uint8_t *)state->new, state->new_len, 6, state->new->type,
-		    o, ol) == NULL)
+		    buf.buf, (size_t)bytes, 6, buf.dhcp6.type, o, ol) == NULL)
 		{
 			logerr("%s: authentication failed", ifp->name);
+			bytes = 0;
 			goto ex;
 		}
 		if (state->auth.token)
@@ -2628,22 +2654,32 @@ auth:
 	}
 #endif
 
-	return fd;
+out:
+	free(state->new);
+	state->new = malloc((size_t)bytes);
+	if (state->new == NULL) {
+		logerr(__func__);
+		goto ex;
+	}
+
+	memcpy(state->new, buf.buf, (size_t)bytes);
+	state->new_len = (size_t)bytes;
+	return bytes;
 
 ex:
 	dhcp6_freedrop_addrs(ifp, 0, NULL);
-	unlink(state->leasefile);
+	dhcp_unlink(ifp->ctx, state->leasefile);
 	free(state->new);
 	state->new = NULL;
 	state->new_len = 0;
-	return retval;
+	return bytes == 0 ? 0 : -1;
 }
 
 static void
 dhcp6_startinit(struct interface *ifp)
 {
 	struct dhcp6_state *state;
-	int r;
+	ssize_t r;
 	uint8_t has_ta, has_non_ta;
 	size_t i;
 
@@ -2998,7 +3034,7 @@ dhcp6_bind(struct interface *ifp, const 
 			break;
 		}
 	}
-	loglevel = has_new ? LOG_INFO : LOG_DEBUG;
+	loglevel = has_new || state->state != DH6S_RENEW ? LOG_INFO : LOG_DEBUG;
 	if (!timedout) {
 		logmessage(loglevel, "%s: %s received from %s",
 		    ifp->name, op, sfrom);
@@ -3146,12 +3182,14 @@ dhcp6_bind(struct interface *ifp, const 
 			state->state = DH6S_BOUND;
 		state->failed = false;
 
-		if (state->renew && state->renew != ND6_INFINITE_LIFETIME)
+		if ((state->renew != 0 || state->rebind != 0) &&
+		    state->renew != ND6_INFINITE_LIFETIME)
 			eloop_timeout_add_sec(ifp->ctx->eloop,
 			    state->renew,
 			    state->state == DH6S_INFORMED ?
 			    dhcp6_startinform : dhcp6_startrenew, ifp);
-		if (state->rebind && state->rebind != ND6_INFINITE_LIFETIME)
+		if ((state->rebind != 0 || state->expire != 0) &&
+		    state->rebind != ND6_INFINITE_LIFETIME)
 			eloop_timeout_add_sec(ifp->ctx->eloop,
 			    state->rebind, dhcp6_startrebind, ifp);
 		if (state->expire != ND6_INFINITE_LIFETIME)
@@ -3180,9 +3218,13 @@ dhcp6_bind(struct interface *ifp, const 
 			logmessage(loglevel, "%s: expire in %"PRIu32" seconds",
 			    ifp->name, state->expire);
 		rt_build(ifp->ctx, AF_INET6);
-		if (!confirmed && !timedout)
-			if (dhcp6_writelease(ifp) == -1)
-				logerr("dhcp6_writelease: %s",state->leasefile);
+		if (!confirmed && !timedout) {
+			logdebugx("%s: writing lease `%s'",
+			    ifp->name, state->leasefile);
+			if (dhcp_writefile(ifp->ctx, state->leasefile, 0640,
+			    state->new, state->new_len) == -1)
+				logerr("dhcp_writefile: %s",state->leasefile);
+		}
 #ifndef SMALL
 		dhcp6_delegate_prefix(ifp);
 #endif
@@ -3350,6 +3392,13 @@ dhcp6_recvif(struct interface *ifp, cons
 			if (state->state == DH6S_DISCOVER)
 				state->state = DH6S_REQUEST;
 			break;
+		case DH6S_DECLINE:
+			/* This isnt really a failure, but an
+			 * acknowledgement of one. */
+			loginfox("%s: %s acknowledged DECLINE6",
+			    ifp->name, sfrom);
+			dhcp6_fail(ifp);
+			return;
 		default:
 			valid_op = false;
 			break;
@@ -3602,7 +3651,7 @@ dhcp6_recvmsg(struct dhcpcd_ctx *ctx, st
 	 * replay it in my code.
 	 */
 	static int replyn = 0;
-	char fname[PATH_MAX], tbuf[64 * 1024];
+	char fname[PATH_MAX], tbuf[UDPLEN_MAX];
 	int fd;
 	ssize_t tlen;
 	uint8_t *si1, *si2;
@@ -3637,10 +3686,12 @@ static void
 dhcp6_recv(struct dhcpcd_ctx *ctx, struct ipv6_addr *ia)
 {
 	struct sockaddr_in6 from;
-	unsigned char buf[64 * 1024]; /* Maximum UDP message size */
+	union {
+		struct dhcp6_message dhcp6;
+		uint8_t buf[UDPLEN_MAX]; /* Maximum UDP message size */
+	} iovbuf;
 	struct iovec iov = {
-		.iov_base = buf,
-		.iov_len = sizeof(buf),
+		.iov_base = iovbuf.buf, .iov_len = sizeof(iovbuf.buf),
 	};
 	union {
 		struct cmsghdr hdr;
@@ -3654,7 +3705,7 @@ dhcp6_recv(struct dhcpcd_ctx *ctx, struc
 	int s;
 	ssize_t bytes;
 
-	s = ia != NULL ? ia->dhcp6_fd : ctx->dhcp6_fd;
+	s = ia != NULL ? ia->dhcp6_fd : ctx->dhcp6_rfd;
 	bytes = recvmsg(s, &msg, 0);
 	if (bytes == -1) {
 		logerr(__func__);
@@ -3682,21 +3733,39 @@ dhcp6_recvctx(void *arg)
 }
 
 int
+dhcp6_openraw(void)
+{
+	int fd, v;
+
+	fd = socket(PF_INET6, SOCK_RAW | SOCK_CXNB, IPPROTO_UDP);
+	if (fd == -1)
+		return -1;
+
+	v = 1;
+	if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &v, sizeof(v)) == -1)
+		goto errexit;
+
+	v = offsetof(struct udphdr, uh_sum);
+	if (setsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &v, sizeof(v)) == -1)
+		goto errexit;
+
+	return fd;
+
+errexit:
+	close(fd);
+	return -1;
+}
+
+int
 dhcp6_openudp(unsigned int ifindex, struct in6_addr *ia)
 {
 	struct sockaddr_in6 sa;
 	int n, s;
 
-#define SOCK_FLAGS	SOCK_CLOEXEC | SOCK_NONBLOCK
-	s = xsocket(PF_INET6, SOCK_DGRAM | SOCK_FLAGS, IPPROTO_UDP);
-#undef SOCK_FLAGS
+	s = xsocket(PF_INET6, SOCK_DGRAM | SOCK_CXNB, IPPROTO_UDP);
 	if (s == -1)
 		goto errexit;
 
-	n = 1;
-	if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &n, sizeof(n)) == -1)
-		goto errexit;
-
 	memset(&sa, 0, sizeof(sa));
 	sa.sin6_family = AF_INET6;
 	sa.sin6_port = htons(DHCP6_CLIENT_PORT);
@@ -3706,7 +3775,7 @@ dhcp6_openudp(unsigned int ifindex, stru
 
 	if (ia != NULL) {
 		memcpy(&sa.sin6_addr, ia, sizeof(sa.sin6_addr));
-		sa.sin6_scope_id = ifindex;
+		ipv6_setscope(&sa, ifindex);
 	}
 
 	if (bind(s, (struct sockaddr *)&sa, sizeof(sa)) == -1)
@@ -3716,6 +3785,12 @@ dhcp6_openudp(unsigned int ifindex, stru
 	if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &n, sizeof(n)) == -1)
 		goto errexit;
 
+#ifdef SO_RERROR
+	n = 1;
+	if (setsockopt(s, SOL_SOCKET, SO_RERROR, &n, sizeof(n)) == -1)
+		goto errexit;
+#endif
+
 	return s;
 
 errexit:
@@ -3768,14 +3843,22 @@ dhcp6_start1(void *arg)
 	const struct dhcp_compat *dhc;
 
 	if ((ctx->options & (DHCPCD_MASTER|DHCPCD_PRIVSEP)) == DHCPCD_MASTER &&
-	    ctx->dhcp6_fd == -1)
+	    ctx->dhcp6_rfd == -1)
 	{
-		ctx->dhcp6_fd = dhcp6_openudp(0, NULL);
-		if (ctx->dhcp6_fd == -1) {
+		ctx->dhcp6_rfd = dhcp6_openudp(0, NULL);
+		if (ctx->dhcp6_rfd == -1) {
+			logerr(__func__);
+			return;
+		}
+		eloop_event_add(ctx->eloop, ctx->dhcp6_rfd, dhcp6_recvctx, ctx);
+	}
+
+	if (!IN_PRIVSEP(ctx) && ctx->dhcp6_wfd == -1) {
+		ctx->dhcp6_wfd = dhcp6_openraw();
+		if (ctx->dhcp6_wfd == -1) {
 			logerr(__func__);
 			return;
 		}
-		eloop_event_add(ctx->eloop, ctx->dhcp6_fd, dhcp6_recvctx, ctx);
 	}
 
 	state = D6_STATE(ifp);
@@ -3956,7 +4039,7 @@ dhcp6_freedrop(struct interface *ifp, in
 				dhcp6_startrelease(ifp);
 				return;
 			}
-			unlink(state->leasefile);
+			dhcp_unlink(ifp->ctx, state->leasefile);
 		}
 		dhcp6_freedrop_addrs(ifp, drop, NULL);
 		free(state->old);
@@ -3987,10 +4070,10 @@ dhcp6_freedrop(struct interface *ifp, in
 				break;
 		}
 	}
-	if (ifp == NULL && ctx->dhcp6_fd != -1) {
-		eloop_event_delete(ctx->eloop, ctx->dhcp6_fd);
-		close(ctx->dhcp6_fd);
-		ctx->dhcp6_fd = -1;
+	if (ifp == NULL && ctx->dhcp6_rfd != -1) {
+		eloop_event_delete(ctx->eloop, ctx->dhcp6_rfd);
+		close(ctx->dhcp6_rfd);
+		ctx->dhcp6_rfd = -1;
 	}
 }
 

Index: src/external/bsd/dhcpcd/dist/src/dhcpcd.8.in
diff -u src/external/bsd/dhcpcd/dist/src/dhcpcd.8.in:1.5 src/external/bsd/dhcpcd/dist/src/dhcpcd.8.in:1.6
--- src/external/bsd/dhcpcd/dist/src/dhcpcd.8.in:1.5	Thu Apr  2 12:41:48 2020
+++ src/external/bsd/dhcpcd/dist/src/dhcpcd.8.in	Sun May 31 12:52:11 2020
@@ -24,7 +24,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd March 27, 2020
+.Dd May 21, 2020
 .Dt DHCPCD 8
 .Os
 .Sh NAME
@@ -109,7 +109,7 @@ to renew early.
 .Pp
 If any interface reports a working carrier then
 .Nm
-will try and obtain a lease before forking to the background,
+will try to obtain a lease before forking to the background,
 otherwise it will fork right away.
 This behaviour can be modified with the
 .Fl b , Fl Fl background
@@ -669,7 +669,9 @@ only responds to DHCP servers and not BO
 Quiet
 .Nm
 on the command line, only warnings and errors will be displayed.
-The messages are still logged though.
+If this option is used another time then all console output is disabled.
+These messages are still logged via
+.Xr syslog 3 .
 .It Fl T , Fl Fl test
 On receipt of DHCP messages just call
 .Pa @SCRIPT@
Index: src/external/bsd/dhcpcd/dist/src/script.c
diff -u src/external/bsd/dhcpcd/dist/src/script.c:1.5 src/external/bsd/dhcpcd/dist/src/script.c:1.6
--- src/external/bsd/dhcpcd/dist/src/script.c:1.5	Mon Apr 13 15:46:26 2020
+++ src/external/bsd/dhcpcd/dist/src/script.c	Sun May 31 12:52:11 2020
@@ -108,6 +108,8 @@ script_exec(char *const *argv, char *con
 	posix_spawnattr_setsigmask(&attr, &defsigs);
 	for (i = 0; i < dhcpcd_signals_len; i++)
 		sigaddset(&defsigs, dhcpcd_signals[i]);
+	for (i = 0; i < dhcpcd_signals_ignore_len; i++)
+		sigaddset(&defsigs, dhcpcd_signals_ignore[i]);
 	posix_spawnattr_setsigdefault(&attr, &defsigs);
 #endif
 	errno = 0;
@@ -250,8 +252,10 @@ make_env(struct dhcpcd_ctx *ctx, const s
 
 	fp = NULL;
 	tmpfd = mkstemp(tmpfile);
-	if (tmpfd == -1)
-		goto eexit;
+	if (tmpfd == -1) {
+		logerr("%s: mkstemp", __func__);
+		return -1;
+	}
 	unlink(tmpfile);
 	fp = fdopen(tmpfd, "w+");
 	if (fp == NULL) {
@@ -689,7 +693,7 @@ script_runreason(const struct interface 
 	int status = 0;
 	struct fd_list *fd;
 
-	if (ifp->options->script == NULL &&
+	if (ctx->script == NULL &&
 	    TAILQ_FIRST(&ifp->ctx->control_fds) == NULL)
 		return 0;
 
@@ -699,23 +703,23 @@ script_runreason(const struct interface 
 		return -1;
 	}
 
-	if (ifp->options->script == NULL)
+	if (ctx->script == NULL)
 		goto send_listeners;
 
-	argv[0] = ifp->options->script;
+	argv[0] = ctx->script;
 	argv[1] = NULL;
 	logdebugx("%s: executing `%s' %s", ifp->name, argv[0], reason);
 
 #ifdef PRIVSEP
 	if (ctx->options & DHCPCD_PRIVSEP) {
-		if (ps_root_script(ifp,
+		if (ps_root_script(ctx,
 		    ctx->script_buf, ctx->script_buflen) == -1)
 			logerr(__func__);
 		goto send_listeners;
 	}
 #endif
 
-	status = script_run(ctx, argv);
+	script_run(ctx, argv);
 
 send_listeners:
 	/* Send to our listeners */
@@ -732,23 +736,3 @@ send_listeners:
 
 	return status;
 }
-
-#ifdef PRIVSEP
-int
-script_runchroot(struct dhcpcd_ctx *ctx, char *script)
-{
-	char *argv[2];
-
-	/* Make our env */
-	if (make_env(ctx, NULL, "CHROOT") == -1) {
-		logerr(__func__);
-		return -1;
-	}
-
-	argv[0] = script;
-	argv[1] = NULL;
-	logdebugx("executing `%s' %s", argv[0], "CHROOT");
-
-	return script_run(ctx, argv);
-}
-#endif

Index: src/external/bsd/dhcpcd/dist/src/if-bsd.c
diff -u src/external/bsd/dhcpcd/dist/src/if-bsd.c:1.19 src/external/bsd/dhcpcd/dist/src/if-bsd.c:1.20
--- src/external/bsd/dhcpcd/dist/src/if-bsd.c:1.19	Tue Apr 21 09:55:33 2020
+++ src/external/bsd/dhcpcd/dist/src/if-bsd.c	Sun May 31 12:52:11 2020
@@ -109,11 +109,6 @@ static const char * const ifnames_ignore
 	NULL
 };
 
-#ifdef INET6
-static void ifa_setscope(struct sockaddr_in6 *, unsigned int);
-static unsigned int ifa_getscope(const struct sockaddr_in6 *);
-#endif
-
 struct priv {
 	int pf_inet6_fd;
 };
@@ -173,12 +168,16 @@ if_opensockets_os(struct dhcpcd_ctx *ctx
 	priv->pf_inet6_fd = -1;
 #endif
 
-#define SOCK_FLAGS	(SOCK_CLOEXEC | SOCK_NONBLOCK)
-	ctx->link_fd = xsocket(PF_ROUTE, SOCK_RAW | SOCK_FLAGS, AF_UNSPEC);
-#undef SOCK_FLAGS
+	ctx->link_fd = xsocket(PF_ROUTE, SOCK_RAW | SOCK_CXNB, AF_UNSPEC);
 	if (ctx->link_fd == -1)
 		return -1;
 
+#ifdef SO_RERROR
+	n = 1;
+	if (setsockopt(ctx->link_fd, SOL_SOCKET, SO_RERROR, &n,sizeof(n)) == -1)
+		logerr("%s: SO_RERROR", __func__);
+#endif
+
 	/* Ignore our own route(4) messages.
 	 * Sadly there is no way of doing this for route(4) messages
 	 * generated from addresses we add/delete. */
@@ -303,6 +302,9 @@ if_ignore(struct dhcpcd_ctx *ctx, const 
 		return true;
 
 #ifdef SIOCGIFGROUP
+#ifdef HAVE_PLEDGE
+#warning Fix SIOCGIFGROUP to use privsep to remove inet pledge requirement
+#endif
 	struct ifgroupreq ifgr = { .ifgr_len = 0 };
 	struct ifg_req *ifg;
 	size_t ifg_len;
@@ -359,27 +361,26 @@ if_linkaddr(struct sockaddr_dl *sdl, con
 }
 
 #if defined(SIOCG80211NWID) || defined(SIOCGETVLAN)
-static int if_direct_ioctl(int s, const char *ifname,
-    unsigned long cmd, void *data)
+static int if_indirect_ioctl(struct dhcpcd_ctx *ctx,
+    const char *ifname, unsigned long cmd, void *data, size_t len)
 {
+	struct ifreq ifr = { .ifr_flags = 0 };
 
-	strlcpy(data, ifname, IFNAMSIZ);
-	return ioctl(s, cmd, data);
-}
-
-static int if_indirect_ioctl(int s, const char *ifname,
-    unsigned long cmd, void *data)
-{
-	struct ifreq ifr;
+#if defined(PRIVSEP) && defined(HAVE_PLEDGE)
+	if (IN_PRIVSEP(ctx))
+		return (int)ps_root_indirectioctl(ctx, cmd, ifname, data, len);
+#else
+	UNUSED(len);
+#endif
 
-	memset(&ifr, 0, sizeof(ifr));
+	strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
 	ifr.ifr_data = data;
-	return if_direct_ioctl(s, ifname, cmd, &ifr);
+	return ioctl(ctx->pf_inet_fd, cmd, &ifr);
 }
 #endif
 
 static int
-if_getssid1(int s, const char *ifname, void *ssid)
+if_getssid1(struct dhcpcd_ctx *ctx, const char *ifname, void *ssid)
 {
 	int retval = -1;
 #if defined(SIOCG80211NWID)
@@ -391,7 +392,9 @@ if_getssid1(int s, const char *ifname, v
 
 #if defined(SIOCG80211NWID) /* NetBSD */
 	memset(&nwid, 0, sizeof(nwid));
-	if (if_indirect_ioctl(s, ifname, SIOCG80211NWID, &nwid) == 0) {
+	if (if_indirect_ioctl(ctx, ifname, SIOCG80211NWID,
+	    &nwid, sizeof(nwid)) == 0)
+	{
 		if (ssid == NULL)
 			retval = nwid.i_len;
 		else if (nwid.i_len > IF_SSIDLEN)
@@ -408,7 +411,7 @@ if_getssid1(int s, const char *ifname, v
 	ireq.i_val = -1;
 	memset(nwid, 0, sizeof(nwid));
 	ireq.i_data = &nwid;
-	if (ioctl(s, SIOCG80211, &ireq) == 0) {
+	if (ioctl(ctx->pf_inet_fd, SIOCG80211, &ireq) == 0) {
 		if (ssid == NULL)
 			retval = ireq.i_len;
 		else if (ireq.i_len > IF_SSIDLEN)
@@ -430,7 +433,7 @@ if_getssid(struct interface *ifp)
 {
 	int r;
 
-	r = if_getssid1(ifp->ctx->pf_inet_fd, ifp->name, ifp->ssid);
+	r = if_getssid1(ifp->ctx, ifp->name, ifp->ssid);
 	if (r != -1)
 		ifp->ssid_len = (unsigned int)r;
 	else
@@ -447,12 +450,11 @@ if_getssid(struct interface *ifp)
  * returning the SSID gives an error.
  */
 int
-if_vimaster(const struct dhcpcd_ctx *ctx, const char *ifname)
+if_vimaster(struct dhcpcd_ctx *ctx, const char *ifname)
 {
 	int r;
-	struct ifmediareq ifmr;
+	struct ifmediareq ifmr = { .ifm_active = 0 };
 
-	memset(&ifmr, 0, sizeof(ifmr));
 	strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
 	r = ioctl(ctx->pf_inet_fd, SIOCGIFMEDIA, &ifmr);
 	if (r == -1)
@@ -460,7 +462,7 @@ if_vimaster(const struct dhcpcd_ctx *ctx
 	if (ifmr.ifm_status & IFM_AVALID &&
 	    IFM_TYPE(ifmr.ifm_active) == IFM_IEEE80211)
 	{
-		if (if_getssid1(ctx->pf_inet_fd, ifname, NULL) == -1)
+		if (if_getssid1(ctx, ifname, NULL) == -1)
 			return 1;
 	}
 	return 0;
@@ -470,17 +472,15 @@ unsigned short
 if_vlanid(const struct interface *ifp)
 {
 #ifdef SIOCGETVLAN
-	struct vlanreq vlr;
+	struct vlanreq vlr = { .vlr_tag = 0 };
 
-	memset(&vlr, 0, sizeof(vlr));
-	if (if_indirect_ioctl(ifp->ctx->pf_inet_fd,
-	    ifp->name, SIOCGETVLAN, &vlr) != 0)
+	if (if_indirect_ioctl(ifp->ctx, ifp->name, SIOCGETVLAN,
+	    &vlr, sizeof(vlr)) != 0)
 		return 0; /* 0 means no VLANID */
 	return vlr.vlr_tag;
 #elif defined(SIOCGVNETID)
-	struct ifreq ifr;
+	struct ifreq ifr = { .ifr_vnetid = 0 };
 
-	memset(&ifr, 0, sizeof(ifr));
 	strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
 	if (ioctl(ifp->ctx->pf_inet_fd, SIOCGVNETID, &ifr) != 0)
 		return 0; /* 0 means no VLANID */
@@ -580,7 +580,7 @@ if_findsa(struct dhcpcd_ctx *ctx, const 
 		struct ipv6_addr *ia;
 
 		sin = (const void *)sa;
-		scope = ifa_getscope(sin);
+		scope = ipv6_getscope(sin);
 		if (scope != 0)
 			return if_findindex(ctx->ifaces, scope);
 		if ((ia = ipv6_findmaskaddr(ctx, &sin->sin6_addr)))
@@ -737,7 +737,7 @@ if_route(unsigned char cmd, const struct
 			if_copysa(&gateway.sa, &rt->rt_gateway);
 #ifdef INET6
 			if (gateway.sa.sa_family == AF_INET6)
-				ifa_setscope(&gateway.sin6, rt->rt_ifp->index);
+				ipv6_setscope(&gateway.sin6, rt->rt_ifp->index);
 #endif
 			ADDSA(&gateway.sa);
 		}
@@ -960,44 +960,6 @@ if_addrflags(const struct interface *ifp
 #endif /* INET */
 
 #ifdef INET6
-static void
-ifa_setscope(struct sockaddr_in6 *sin, unsigned int ifindex)
-{
-
-#ifdef __KAME__
-	/* KAME based systems want to store the scope inside the sin6_addr
-	 * for link local addresses */
-	if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr)) {
-		uint16_t scope = htons((uint16_t)ifindex);
-		memcpy(&sin->sin6_addr.s6_addr[2], &scope,
-		    sizeof(scope));
-	}
-	sin->sin6_scope_id = 0;
-#else
-	if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr))
-		sin->sin6_scope_id = ifindex;
-	else
-		sin->sin6_scope_id = 0;
-#endif
-}
-
-static unsigned int
-ifa_getscope(const struct sockaddr_in6 *sin)
-{
-#ifdef __KAME__
-	uint16_t scope;
-#endif
-
-	if (!IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr))
-		return 0;
-#ifdef __KAME__
-	memcpy(&scope, &sin->sin6_addr.s6_addr[2], sizeof(scope));
-	return (unsigned int)ntohs(scope);
-#else
-	return (unsigned int)sin->sin6_scope_id;
-#endif
-}
-
 static int
 if_ioctl6(struct dhcpcd_ctx *ctx, unsigned long req, void *data, size_t len)
 {
@@ -1042,7 +1004,7 @@ if_address6(unsigned char cmd, const str
 	}
 
 	ADDADDR(&ifa.ifra_addr, &ia->addr);
-	ifa_setscope(&ifa.ifra_addr, ia->iface->index);
+	ipv6_setscope(&ifa.ifra_addr, ia->iface->index);
 	ipv6_mask(&mask, ia->prefix_len);
 	ADDADDR(&ifa.ifra_prefixmask, &mask);
 
@@ -1120,7 +1082,7 @@ if_addrflags6(const struct interface *if
 	strlcpy(ifr6.ifr_name, ifp->name, sizeof(ifr6.ifr_name));
 	ifr6.ifr_addr.sin6_family = AF_INET6;
 	ifr6.ifr_addr.sin6_addr = *addr;
-	ifa_setscope(&ifr6.ifr_addr, ifp->index);
+	ipv6_setscope(&ifr6.ifr_addr, ifp->index);
 	priv = (struct priv *)ifp->ctx->priv;
 	if (ioctl(priv->pf_inet6_fd, SIOCGIFAFLAG_IN6, &ifr6) != -1)
 		flags = ifr6.ifr_ifru.ifru_flags6;
@@ -1141,7 +1103,7 @@ if_getlifetime6(struct ipv6_addr *ia)
 	strlcpy(ifr6.ifr_name, ia->iface->name, sizeof(ifr6.ifr_name));
 	ifr6.ifr_addr.sin6_family = AF_INET6;
 	ifr6.ifr_addr.sin6_addr = ia->addr;
-	ifa_setscope(&ifr6.ifr_addr, ia->iface->index);
+	ipv6_setscope(&ifr6.ifr_addr, ia->iface->index);
 	priv = (struct priv *)ia->iface->ctx->priv;
 	if (ioctl(priv->pf_inet6_fd, SIOCGIFALIFETIME_IN6, &ifr6) == -1)
 		return -1;
@@ -1330,7 +1292,8 @@ if_ifa(struct dhcpcd_ctx *ctx, const str
 			break;
 #endif
 		memcpy(&sdl, rti_info[RTAX_IFA], rti_info[RTAX_IFA]->sa_len);
-		dhcpcd_handlehwaddr(ctx, ifp->name, CLLADDR(&sdl),sdl.sdl_alen);
+		dhcpcd_handlehwaddr(ifp, ifp->hwtype,
+		    CLLADDR(&sdl), sdl.sdl_alen);
 		break;
 	}
 #ifdef INET
@@ -1516,7 +1479,7 @@ if_missfilter0(struct dhcpcd_ctx *ctx, s
 
 #ifdef INET6
 	if (sa->sa_family == AF_INET6)
-		ifa_setscope(satosin6(sa), ifp->index);
+		ipv6_setscope(satosin6(sa), ifp->index);
 #else
 	UNUSED(ifp);
 #endif
@@ -1529,7 +1492,7 @@ if_missfilter0(struct dhcpcd_ctx *ctx, s
 
 #ifdef INET6
 	if (sa->sa_family == AF_INET6)
-		ifa_setscope(satosin6(sa), 0);
+		ipv6_setscope(satosin6(sa), 0);
 #endif
 
 	return 0;
@@ -1597,7 +1560,7 @@ if_handlelink(struct dhcpcd_ctx *ctx)
 }
 
 #ifndef SYS_NMLN	/* OSX */
-#  define SYS_NMLN 256
+#  define SYS_NMLN __SYS_NAMELEN
 #endif
 #ifndef HW_MACHINE_ARCH
 #  ifdef HW_MODEL	/* OpenBSD */
@@ -1608,13 +1571,8 @@ int
 if_machinearch(char *str, size_t len)
 {
 	int mib[2] = { CTL_HW, HW_MACHINE_ARCH };
-	char march[SYS_NMLN];
-	size_t marchlen = sizeof(march);
 
-	if (sysctl(mib, sizeof(mib) / sizeof(mib[0]),
-	    march, &marchlen, NULL, 0) != 0)
-		return -1;
-	return snprintf(str, len, ":%s", march);
+	return sysctl(mib, sizeof(mib) / sizeof(mib[0]), str, &len, NULL, 0);
 }
 
 #ifdef INET6
@@ -1648,18 +1606,30 @@ if_applyra(const struct ra *rap)
 #ifdef SIOCSIFINFO_IN6
 	struct in6_ndireq nd = { .ndi.chlim = 0 };
 	struct dhcpcd_ctx *ctx = rap->iface->ctx;
-	struct priv *priv = ctx->priv;
 	int error;
 
 	strlcpy(nd.ifname, rap->iface->name, sizeof(nd.ifname));
+
+#ifdef IPV6CTL_ACCEPT_RTADV
+	struct priv *priv = ctx->priv;
+
+	/*
+	 * NetBSD changed SIOCSIFINFO_IN6 to NOT set flags when kernel
+	 * RA was removed, however both FreeBSD and DragonFlyBSD still do.
+	 * linkmtu was also removed.
+	 * Hopefully this guard will still work if either remove kernel RA.
+	 */
 	if (ioctl(priv->pf_inet6_fd, SIOCGIFINFO_IN6, &nd, sizeof(nd)) == -1)
 		return -1;
 
 	nd.ndi.linkmtu = rap->mtu;
+#endif
+
 	nd.ndi.chlim = rap->hoplimit;
 	nd.ndi.retrans = rap->retrans;
 	nd.ndi.basereachable = rap->reachable;
 	error = if_ioctl6(ctx, SIOCSIFINFO_IN6, &nd, sizeof(nd));
+#ifdef IPV6CTL_ACCEPT_RTADV
 	if (error == -1 && errno == EINVAL) {
 		/*
 		 * Very likely that this is caused by a dodgy MTU
@@ -1671,6 +1641,7 @@ if_applyra(const struct ra *rap)
 		nd.ndi.linkmtu = 0;
 		error = if_ioctl6(ctx, SIOCSIFINFO_IN6, &nd, sizeof(nd));
 	}
+#endif
 	return error;
 #else
 #warning OS does not allow setting of RA bits hoplimit, retrans or reachable
@@ -1812,8 +1783,7 @@ if_setup_inet6(const struct interface *i
 #endif
 
 #ifdef ND6_IFF_AUTO_LINKLOCAL
-	/* Unlike the kernel,
-	 * dhcpcd make make a stable private address. */
+	/* Unlike the kernel, dhcpcd make make a stable private address. */
 	flags &= ~ND6_IFF_AUTO_LINKLOCAL;
 #endif
 
Index: src/external/bsd/dhcpcd/dist/src/ipv6nd.c
diff -u src/external/bsd/dhcpcd/dist/src/ipv6nd.c:1.19 src/external/bsd/dhcpcd/dist/src/ipv6nd.c:1.20
--- src/external/bsd/dhcpcd/dist/src/ipv6nd.c:1.19	Tue Apr 21 09:55:33 2020
+++ src/external/bsd/dhcpcd/dist/src/ipv6nd.c	Sun May 31 12:52:11 2020
@@ -188,36 +188,45 @@ ipv6nd_printoptions(const struct dhcpcd_
 	}
 }
 
-static int
-ipv6nd_open0(void)
+int
+ipv6nd_open(bool recv)
 {
 	int fd, on;
 	struct icmp6_filter filt;
 
-#define SOCK_FLAGS	SOCK_CLOEXEC | SOCK_NONBLOCK
-	fd = xsocket(PF_INET6, SOCK_RAW | SOCK_FLAGS, IPPROTO_ICMPV6);
-#undef SOCK_FLAGS
+	fd = xsocket(PF_INET6, SOCK_RAW | SOCK_CXNB, IPPROTO_ICMPV6);
 	if (fd == -1)
 		return -1;
 
+	ICMP6_FILTER_SETBLOCKALL(&filt);
+
 	/* RFC4861 4.1 */
 	on = 255;
 	if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
 	    &on, sizeof(on)) == -1)
 		goto eexit;
 
-	on = 1;
-	if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
-	    &on, sizeof(on)) == -1)
-		goto eexit;
-
-	on = 1;
-	if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
-	    &on, sizeof(on)) == -1)
-		goto eexit;
+	if (recv) {
+		on = 1;
+		if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
+		    &on, sizeof(on)) == -1)
+			goto eexit;
+
+		on = 1;
+		if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
+		    &on, sizeof(on)) == -1)
+			goto eexit;
+
+		ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt);
+
+#ifdef SO_RERROR
+		on = 1;
+		if (setsockopt(fd, SOL_SOCKET, SO_RERROR,
+		    &on, sizeof(on)) == -1)
+			goto eexit;
+#endif
+	}
 
-	ICMP6_FILTER_SETBLOCKALL(&filt);
-	ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt);
 	if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER,
 	    &filt, sizeof(filt)) == -1)
 		goto eexit;
@@ -231,7 +240,7 @@ eexit:
 
 #ifdef __sun
 int
-ipv6nd_open(struct interface *ifp)
+ipv6nd_openif(struct interface *ifp)
 {
 	int fd;
 	struct ipv6_mreq mreq = {
@@ -244,7 +253,7 @@ ipv6nd_open(struct interface *ifp)
 	if (state->nd_fd != -1)
 		return state->nd_fd;
 
-	fd = ipv6nd_open0();
+	fd = ipv6nd_open(true);
 	if (fd == -1)
 		return -1;
 
@@ -266,24 +275,6 @@ ipv6nd_open(struct interface *ifp)
 	eloop_event_add(ifp->ctx->eloop, fd, ipv6nd_handledata, ifp);
 	return fd;
 }
-#else
-int
-ipv6nd_open(struct dhcpcd_ctx *ctx)
-{
-	int fd;
-
-	if (ctx->nd_fd != -1)
-		return ctx->nd_fd;
-
-	fd = ipv6nd_open0();
-	if (fd == -1)
-		return -1;
-
-	ctx->nd_fd = fd;
-	if (!(IN_PRIVSEP(ctx)))
-		eloop_event_add(ctx->eloop, fd, ipv6nd_handledata, ctx);
-	return fd;
-}
 #endif
 
 static int
@@ -328,15 +319,21 @@ ipv6nd_sendrsprobe(void *arg)
 		.sin6_scope_id = ifp->index,
 	};
 	struct iovec iov = { .iov_base = state->rs, .iov_len = state->rslen };
-	unsigned char ctl[CMSG_SPACE(sizeof(struct in6_pktinfo))] = { 0 };
+	union {
+		struct cmsghdr hdr;
+		uint8_t buf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+	} cmsgbuf = { .buf = { 0 } };
 	struct msghdr msg = {
 	    .msg_name = &dst, .msg_namelen = sizeof(dst),
 	    .msg_iov = &iov, .msg_iovlen = 1,
-	    .msg_control = ctl, .msg_controllen = sizeof(ctl),
+	    .msg_control = cmsgbuf.buf, .msg_controllen = sizeof(cmsgbuf.buf),
 	};
 	struct cmsghdr *cm;
 	struct in6_pktinfo pi = { .ipi6_ifindex = ifp->index };
 	int s;
+#ifndef __sun
+	struct dhcpcd_ctx *ctx = ifp->ctx;
+#endif
 
 	if (ipv6_linklocal(ifp) == NULL) {
 		logdebugx("%s: delaying Router Solicitation for LL address",
@@ -367,8 +364,22 @@ ipv6nd_sendrsprobe(void *arg)
 	}
 #endif
 #ifdef __sun
+	if (state->nd_fd == -1) {
+		if (ipv6nd_openif(ifp) == -1) {
+			logerr(__func__);
+			return;
+		}
+	}
 	s = state->nd_fd;
 #else
+	if (ctx->nd_fd == -1) {
+		ctx->nd_fd = ipv6nd_open(true);
+		if (ctx->nd_fd == -1) {
+			logerr(__func__);
+			return;
+		}
+		eloop_event_add(ctx->eloop, ctx->nd_fd, ipv6nd_handledata, ctx);
+	}
 	s = ifp->ctx->nd_fd;
 #endif
 	if (sendmsg(s, &msg, 0) == -1) {
@@ -402,11 +413,14 @@ ipv6nd_sendadvertisement(void *arg)
 	    .sin6_scope_id = ifp->index,
 	};
 	struct iovec iov = { .iov_base = ia->na, .iov_len = ia->na_len };
-	unsigned char ctl[CMSG_SPACE(sizeof(struct in6_pktinfo))] = { 0 };
+	union {
+		struct cmsghdr hdr;
+		uint8_t buf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+	} cmsgbuf = { .buf = { 0 } };
 	struct msghdr msg = {
 	    .msg_name = &dst, .msg_namelen = sizeof(dst),
 	    .msg_iov = &iov, .msg_iovlen = 1,
-	    .msg_control = ctl, .msg_controllen = sizeof(ctl),
+	    .msg_control = cmsgbuf.buf, .msg_controllen = sizeof(cmsgbuf.buf),
 	};
 	struct cmsghdr *cm;
 	struct in6_pktinfo pi = { .ipi6_ifindex = ifp->index };
@@ -518,6 +532,12 @@ ipv6nd_advertise(struct ipv6_addr *ia)
 
 	na->nd_na_type = ND_NEIGHBOR_ADVERT;
 	na->nd_na_flags_reserved = ND_NA_FLAG_OVERRIDE;
+#if defined(PRIVSEP) && (defined(__linux__) || defined(HAVE_PLEDGE))
+	if (IN_PRIVSEP(ctx)) {
+		if (ps_root_ip6forwarding(ctx, ifp->name) == 1)
+			na->nd_na_flags_reserved |= ND_NA_FLAG_ROUTER;
+	} else
+#endif
 	if (ip6_forwarding(ifp->name) == 1)
 		na->nd_na_flags_reserved |= ND_NA_FLAG_ROUTER;
 	na->nd_na_target = ia->addr;
@@ -628,18 +648,25 @@ ipv6nd_sortrouters(struct dhcpcd_ctx *ct
 }
 
 static void
-ipv6nd_applyra(struct dhcpcd_ctx *ctx, struct interface *ifp)
+ipv6nd_applyra(struct interface *ifp)
 {
 	struct ra *rap;
 	struct rs_state *state = RS_STATE(ifp);
+	struct ra defra = {
+		.iface = ifp,
+		.hoplimit = IPV6_DEFHLIM ,
+		.reachable = REACHABLE_TIME,
+		.retrans = RETRANS_TIMER,
+	};
 
-	TAILQ_FOREACH(rap, ctx->ra_routers, next) {
+	TAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) {
 		if (rap->iface == ifp)
 			break;
 	}
 
-	if (rap == NULL)
-		return;
+	/* If we have no Router Advertisement, then set default values. */
+	if (rap == NULL || rap->expired || rap->willexpire)
+		rap = &defra;
 
 	state->retrans = rap->retrans;
 	if (if_applyra(rap) == -1 && errno != ENOENT)
@@ -681,7 +708,7 @@ ipv6nd_neighbour(struct dhcpcd_ctx *ctx,
 
 	/* See if we can install a reachable default router. */
 	ipv6nd_sortrouters(ctx);
-	ipv6nd_applyra(ctx, rap->iface);
+	ipv6nd_applyra(rap->iface);
 	rt_build(ctx, AF_INET6);
 
 	if (reachable)
@@ -927,7 +954,7 @@ ipv6nd_dadcallback(void *arg)
 	ifp = ia->iface;
 	wascompleted = (ia->flags & IPV6_AF_DADCOMPLETED);
 	ia->flags |= IPV6_AF_DADCOMPLETED;
-	if (ia->flags & IPV6_AF_DUPLICATED) {
+	if (ia->addr_flags & IN6_IFF_DUPLICATED) {
 		ia->dadcounter++;
 		logwarnx("%s: DAD detected %s", ifp->name, ia->saddr);
 
@@ -1117,6 +1144,23 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx,
 		return;
 	}
 
+#ifdef NOCARRIER_PRESERVE_IP
+	/*
+	 * Because we preserve RA's and expire them quickly after
+	 * carrier up, it's important to reset the kernels notion of
+	 * reachable timers back to default values before applying
+	 * new RA values.
+	 */
+	TAILQ_FOREACH(rap, ctx->ra_routers, next) {
+		if (ifp == rap->iface)
+			break;
+	}
+	if (rap != NULL && rap->willexpire) {
+		logerrx("settng def RA");
+		ipv6nd_applyra(ifp);
+	}
+#endif
+
 	TAILQ_FOREACH(rap, ctx->ra_routers, next) {
 		if (ifp == rap->iface &&
 		    IN6_ARE_ADDR_EQUAL(&rap->from, &from->sin6_addr))
@@ -1435,7 +1479,7 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx,
 		script_runreason(ifp, "TEST");
 		goto handle_flag;
 	}
-	ipv6nd_applyra(ifp->ctx, ifp);
+	ipv6nd_applyra(ifp);
 	ipv6_addaddrs(&rap->addrs);
 #ifdef IPV6_MANAGETEMPADDR
 	ipv6_addtempaddrs(ifp, &rap->acquired);
@@ -1693,7 +1737,7 @@ ipv6nd_expirera(void *arg)
 		if (rap->lifetime) {
 			elapsed = (uint32_t)eloop_timespec_diff(&now,
 			    &rap->acquired, NULL);
-			if (elapsed > rap->lifetime || rap->doexpire) {
+			if (elapsed >= rap->lifetime || rap->doexpire) {
 				if (!rap->expired) {
 					logwarnx("%s: %s: router expired",
 					    ifp->name, rap->sfrom);
@@ -1722,7 +1766,7 @@ ipv6nd_expirera(void *arg)
 			}
 			elapsed = (uint32_t)eloop_timespec_diff(&now,
 			    &ia->acquired, NULL);
-			if (elapsed > ia->prefix_vltime || rap->doexpire) {
+			if (elapsed >= ia->prefix_vltime || rap->doexpire) {
 				if (ia->flags & IPV6_AF_ADDED) {
 					logwarnx("%s: expired %s %s",
 					    ia->iface->name,
@@ -1803,7 +1847,7 @@ ipv6nd_expirera(void *arg)
 			}
 
 			ltime = ntohl(ltime);
-			if (elapsed > ltime) {
+			if (elapsed >= ltime) {
 				expired = true;
 				continue;
 			}
@@ -1829,6 +1873,8 @@ ipv6nd_expirera(void *arg)
 	if (expired) {
 		logwarnx("%s: part of a Router Advertisement expired",
 		    ifp->name);
+		ipv6nd_sortrouters(ifp->ctx);
+		ipv6nd_applyra(ifp);
 		rt_build(ifp->ctx, AF_INET6);
 		script_runreason(ifp, "ROUTERADVERT");
 	}
@@ -1851,6 +1897,7 @@ ipv6nd_drop(struct interface *ifp)
 		}
 	}
 	if (expired) {
+		ipv6nd_applyra(ifp);
 		rt_build(ifp->ctx, AF_INET6);
 		if ((ifp->options->options & DHCPCD_NODROP) != DHCPCD_NODROP)
 			script_runreason(ifp, "ROUTERADVERT");
@@ -1904,10 +1951,12 @@ ipv6nd_handledata(void *arg)
 	struct dhcpcd_ctx *ctx;
 	int fd;
 	struct sockaddr_in6 from;
-	unsigned char buf[64 * 1024]; /* Maximum ICMPv6 size */
+	union {
+		struct icmp6_hdr hdr;
+		uint8_t buf[64 * 1024]; /* Maximum ICMPv6 size */
+	} iovbuf;
 	struct iovec iov = {
-		.iov_base = buf,
-		.iov_len = sizeof(buf),
+		.iov_base = iovbuf.buf, .iov_len = sizeof(iovbuf.buf),
 	};
 	union {
 		struct cmsghdr hdr;
@@ -1963,20 +2012,6 @@ ipv6nd_startrs1(void *arg)
 #endif
 	}
 
-	if (!(IN_PRIVSEP(ifp->ctx))) {
-#ifdef __sun
-		if (ipv6nd_open(ifp) == -1) {
-			logerr(__func__);
-			return;
-		}
-#else
-		if (ipv6nd_open(ifp->ctx) == -1) {
-			logerr(__func__);
-			return;
-		}
-#endif
-	}
-
 	/* Always make a new probe as the underlying hardware
 	 * address could have changed. */
 	ipv6nd_makersprobe(ifp);

Index: src/external/bsd/dhcpcd/dist/src/if-options.c
diff -u src/external/bsd/dhcpcd/dist/src/if-options.c:1.23 src/external/bsd/dhcpcd/dist/src/if-options.c:1.24
--- src/external/bsd/dhcpcd/dist/src/if-options.c:1.23	Tue Apr 21 09:55:33 2020
+++ src/external/bsd/dhcpcd/dist/src/if-options.c	Sun May 31 12:52:11 2020
@@ -27,7 +27,6 @@
  */
 
 #include <sys/param.h>
-#include <sys/stat.h>
 #include <sys/types.h>
 
 #include <arpa/inet.h>
@@ -56,58 +55,6 @@
 #include "logerr.h"
 #include "sa.h"
 
-/* These options only make sense in the config file, so don't use any
-   valid short options for them */
-#define O_BASE			MAX('z', 'Z') + 1
-#define O_ARPING		O_BASE + 1
-#define O_FALLBACK		O_BASE + 2
-#define O_DESTINATION		O_BASE + 3
-#define O_IPV6RS		O_BASE + 4
-#define O_NOIPV6RS		O_BASE + 5
-#define O_IPV6RA_FORK		O_BASE + 6
-#define O_LINK_RCVBUF		O_BASE + 7
-#define O_ANONYMOUS		O_BASE + 8
-#define O_NOALIAS		O_BASE + 9
-#define O_IA_NA			O_BASE + 10
-#define O_IA_TA			O_BASE + 11
-#define O_IA_PD			O_BASE + 12
-#define O_HOSTNAME_SHORT	O_BASE + 13
-#define O_DEV			O_BASE + 14
-#define O_NODEV			O_BASE + 15
-#define O_NOIPV4		O_BASE + 16
-#define O_NOIPV6		O_BASE + 17
-#define O_IAID			O_BASE + 18
-#define O_DEFINE		O_BASE + 19
-#define O_DEFINE6		O_BASE + 20
-#define O_EMBED			O_BASE + 21
-#define O_ENCAP			O_BASE + 22
-#define O_VENDOPT		O_BASE + 23
-#define O_VENDCLASS		O_BASE + 24
-#define O_AUTHPROTOCOL		O_BASE + 25
-#define O_AUTHTOKEN		O_BASE + 26
-#define O_AUTHNOTREQUIRED	O_BASE + 27
-#define O_NODHCP		O_BASE + 28
-#define O_NODHCP6		O_BASE + 29
-#define O_DHCP			O_BASE + 30
-#define O_DHCP6			O_BASE + 31
-#define O_IPV4			O_BASE + 32
-#define O_IPV6			O_BASE + 33
-#define O_CONTROLGRP		O_BASE + 34
-#define O_SLAAC			O_BASE + 35
-#define O_GATEWAY		O_BASE + 36
-#define O_NOUP			O_BASE + 37
-#define O_IPV6RA_AUTOCONF	O_BASE + 38
-#define O_IPV6RA_NOAUTOCONF	O_BASE + 39
-#define O_REJECT		O_BASE + 40
-#define O_BOOTP			O_BASE + 42
-#define O_DEFINEND		O_BASE + 43
-#define O_NODELAY		O_BASE + 44
-#define O_INFORM6		O_BASE + 45
-#define O_LASTLEASE_EXTEND	O_BASE + 46
-#define O_INACTIVE		O_BASE + 47
-#define	O_MUDURL		O_BASE + 48
-#define	O_MSUSERCLASS		O_BASE + 49
-
 const struct option cf_options[] = {
 	{"background",      no_argument,       NULL, 'b'},
 	{"script",          required_argument, NULL, 'c'},
@@ -214,8 +161,6 @@ const struct option cf_options[] = {
 	{NULL,              0,                 NULL, '\0'}
 };
 
-static const char *default_script = SCRIPT;
-
 static char *
 add_environ(char ***array, const char *value, int uniq)
 {
@@ -728,26 +673,32 @@ parse_option(struct dhcpcd_ctx *ctx, con
 		break;
 	case 'c':
 		ARG_REQUIRED;
-		if (ifo->script != default_script)
-			free(ifo->script);
+		if (ifname != NULL) {
+			logerrx("%s: per interface scripts"
+			    " are no longer supported",
+			    ifname);
+			return -1;
+		}
+		if (ctx->script != dhcpcd_default_script)
+			free(ctx->script);
 		s = parse_nstring(NULL, 0, arg);
 		if (s == 0) {
-			ifo->script = NULL;
+			ctx->script = NULL;
 			break;
 		}
 		dl = (size_t)s;
-		if (s == -1 || (ifo->script = malloc(dl)) == NULL) {
-			ifo->script = NULL;
+		if (s == -1 || (ctx->script = malloc(dl)) == NULL) {
+			ctx->script = NULL;
 			logerr(__func__);
 			return -1;
 		}
-		s = parse_nstring(ifo->script, dl, arg);
+		s = parse_nstring(ctx->script, dl, arg);
 		if (s == -1 ||
-		    ifo->script[0] == '\0' ||
-		    strcmp(ifo->script, "/dev/null") == 0)
+		    ctx->script[0] == '\0' ||
+		    strcmp(ctx->script, "/dev/null") == 0)
 		{
-			free(ifo->script);
-			ifo->script = NULL;
+			free(ctx->script);
+			ctx->script = NULL;
 		}
 		break;
 	case 'd':
@@ -1291,7 +1242,7 @@ parse_option(struct dhcpcd_ctx *ctx, con
 		del_option_mask(ifo->nomask, DHO_DNSSEARCH);
 #endif
 
-#ifdef INET6
+#ifdef DHCP6
 		del_option_mask(ifo->nomask6, D6_OPTION_DNS_SERVERS);
 		del_option_mask(ifo->nomask6, D6_OPTION_DOMAIN_LIST);
 		del_option_mask(ifo->nomask6, D6_OPTION_SOL_MAX_RT);
@@ -2145,7 +2096,7 @@ invalid_token:
 			logerr(__func__);
 			return -1;
 		}
-		while ((i = getgrnam_r(arg, &grpbuf, p, (size_t)l, &grp)) ==
+		while ((i = getgrnam_r(arg, &grpbuf, p, dl, &grp)) ==
 		    ERANGE)
 		{
 			size_t nl = dl * 2;
@@ -2170,7 +2121,8 @@ invalid_token:
 			return -1;
 		}
 		if (grp == NULL) {
-			logerrx("controlgroup: %s: not found", arg);
+			if (!ctx->control_group)
+				logerrx("controlgroup: %s: not found", arg);
 			free(p);
 			return -1;
 		}
@@ -2179,7 +2131,8 @@ invalid_token:
 #else
 		grp = getgrnam(arg);
 		if (grp == NULL) {
-			logerrx("controlgroup: %s: not found", arg);
+			if (!ctx->control_group)
+				logerrx("controlgroup: %s: not found", arg);
 			return -1;
 		}
 		ctx->control_group = grp->gr_gid;
@@ -2308,45 +2261,6 @@ finish_config(struct if_options *ifo)
 		    ~(DHCPCD_IPV6RA_AUTOCONF | DHCPCD_IPV6RA_REQRDNSS);
 }
 
-/* Handy routine to read very long lines in text files.
- * This means we read the whole line and avoid any nasty buffer overflows.
- * We strip leading space and avoid comment lines, making the code that calls
- * us smaller. */
-static char *
-get_line(char ** __restrict buf, size_t * __restrict buflen,
-    FILE * __restrict fp)
-{
-	char *p, *c;
-	ssize_t bytes;
-	int quoted;
-
-	do {
-		bytes = getline(buf, buflen, fp);
-		if (bytes == -1)
-			return NULL;
-		for (p = *buf; *p == ' ' || *p == '\t'; p++)
-			;
-	} while (*p == '\0' || *p == '\n' || *p == '#' || *p == ';');
-	if ((*buf)[--bytes] == '\n')
-		(*buf)[bytes] = '\0';
-
-	/* Strip embedded comments unless in a quoted string or escaped */
-	quoted = 0;
-	for (c = p; *c != '\0'; c++) {
-		if (*c == '\\') {
-			c++; /* escaped */
-			continue;
-		}
-		if (*c == '"')
-			quoted = !quoted;
-		else if (*c == '#' && !quoted) {
-			*c = '\0';
-			break;
-		}
-	}
-	return p;
-}
-
 struct if_options *
 default_config(struct dhcpcd_ctx *ctx)
 {
@@ -2360,7 +2274,6 @@ default_config(struct dhcpcd_ctx *ctx)
 	ifo->options |= DHCPCD_IF_UP | DHCPCD_LINK | DHCPCD_INITIAL_DELAY;
 	ifo->timeout = DEFAULT_TIMEOUT;
 	ifo->reboot = DEFAULT_REBOOT;
-	ifo->script = UNCONST(default_script);
 	ifo->metric = -1;
 	ifo->auth.options |= DHCPCD_AUTH_REQUIRE;
 	rb_tree_init(&ifo->routes, &rt_compare_list_ops);
@@ -2382,16 +2295,11 @@ read_config(struct dhcpcd_ctx *ctx,
     const char *ifname, const char *ssid, const char *profile)
 {
 	struct if_options *ifo;
-	FILE *fp;
-	struct stat sb;
-	char *line, *buf, *option, *p;
-	size_t buflen;
-	ssize_t vlen;
+	char buf[UDPLEN_MAX], *bp; /* 64k max config file size */
+	char *line, *option, *p;
+	ssize_t buflen;
+	size_t vlen;
 	int skip, have_profile, new_block, had_block;
-#ifndef EMBEDDED_CONFIG
-	const char * const *e;
-	size_t ol;
-#endif
 #if !defined(INET) || !defined(INET6)
 	size_t i;
 	struct dhcp_opt *opt;
@@ -2414,12 +2322,9 @@ read_config(struct dhcpcd_ctx *ctx,
 	ifo->options |= DHCPCD_DHCP6;
 #endif
 
-	vlen = dhcp_vendor((char *)ifo->vendorclassid + 1,
-	            sizeof(ifo->vendorclassid) - 1);
-	ifo->vendorclassid[0] = (uint8_t)(vlen == -1 ? 0 : vlen);
-
-	buf = NULL;
-	buflen = 0;
+	vlen = strlcpy((char *)ifo->vendorclassid + 1, ctx->vendor,
+	    sizeof(ifo->vendorclassid) - 1);
+	ifo->vendorclassid[0] = (uint8_t)(vlen > 255 ? 0 : vlen);
 
 	/* Reset route order */
 	ctx->rt_order = 0;
@@ -2455,38 +2360,27 @@ read_config(struct dhcpcd_ctx *ctx,
 
 		/* Now load our embedded config */
 #ifdef EMBEDDED_CONFIG
-		fp = fopen(EMBEDDED_CONFIG, "r");
-		if (fp == NULL)
-			logerr("%s: fopen `%s'", __func__, EMBEDDED_CONFIG);
-
-		while (fp && (line = get_line(&buf, &buflen, fp))) {
+		buflen = dhcp_readfile(ctx, EMBEDDED_CONFIG, buf, sizeof(buf));
+		if (buflen == -1) {
+			logerr("%s: %s", __func__, EMBEDDED_CONFIG);
+			return ifo;
+		}
+		if (buf[buflen - 1] != '\0') {
+			if (buflen < sizeof(buf) - 1)
+				bulen++;
+			buf[buflen - 1] = '\0';
+		}
 #else
-		buflen = 80;
-		buf = malloc(buflen);
-		if (buf == NULL) {
-			logerr(__func__);
-			free_options(ctx, ifo);
-			return NULL;
+		buflen = (ssize_t)strlcpy(buf, dhcpcd_embedded_conf,
+		    sizeof(buf));
+		if ((size_t)buflen >= sizeof(buf)) {
+			logerrx("%s: embedded config too big", __func__);
+			return ifo;
 		}
-		ldop = edop = NULL;
-		for (e = dhcpcd_embedded_conf; *e; e++) {
-			ol = strlen(*e) + 1;
-			if (ol > buflen) {
-				char *nbuf;
-
-				buflen = ol;
-				nbuf = realloc(buf, buflen);
-				if (nbuf == NULL) {
-					logerr(__func__);
-					free(buf);
-					free_options(ctx, ifo);
-					return NULL;
-				}
-				buf = nbuf;
-			}
-			memcpy(buf, *e, ol);
-			line = buf;
+		/* Our embedded config is NULL terminated */
 #endif
+		bp = buf;
+		while ((line = get_line(&bp, &buflen)) != NULL) {
 			option = strsep(&line, " \t");
 			if (line)
 				line = strskipwhite(line);
@@ -2500,13 +2394,8 @@ read_config(struct dhcpcd_ctx *ctx,
 			}
 			parse_config_line(ctx, NULL, ifo, option, line,
 			    &ldop, &edop);
-
 		}
 
-#ifdef EMBEDDED_CONFIG
-		if (fp)
-			fclose(fp);
-#endif
 #ifdef INET
 		ctx->dhcp_opts = ifo->dhcp_override;
 		ctx->dhcp_opts_len = ifo->dhcp_override_len;
@@ -2551,26 +2440,25 @@ read_config(struct dhcpcd_ctx *ctx,
 	}
 
 	/* Parse our options file */
-#ifdef PRIVSEP
-	if (ctx->options & DHCPCD_PRIVSEP &&
-	    ps_root_copychroot(ctx, ctx->cffile) == -1)
-		logwarn("%s: ps_root_copychroot `%s'", __func__, ctx->cffile);
-#endif
-	fp = fopen(ctx->cffile, "r");
-	if (fp == NULL) {
+	buflen = dhcp_readfile(ctx, ctx->cffile, buf, sizeof(buf));
+	if (buflen == -1) {
 		/* dhcpcd can continue without it, but no DNS options
 		 * would be requested ... */
-		logwarn("%s: fopen `%s'", __func__, ctx->cffile);
-		free(buf);
+		logerr("%s: %s", __func__, ctx->cffile);
 		return ifo;
 	}
-	if (stat(ctx->cffile, &sb) == 0)
-		ifo->mtime = sb.st_mtime;
+	if (buf[buflen - 1] != '\0') {
+		if ((size_t)buflen < sizeof(buf) - 1)
+			buflen++;
+		buf[buflen - 1] = '\0';
+	}
+	dhcp_filemtime(ctx, ctx->cffile, &ifo->mtime);
 
 	ldop = edop = NULL;
 	skip = have_profile = new_block = 0;
 	had_block = ifname == NULL ? 1 : 0;
-	while ((line = get_line(&buf, &buflen, fp))) {
+	bp = buf;
+	while ((line = get_line(&bp, &buflen)) != NULL) {
 		option = strsep(&line, " \t");
 		if (line)
 			line = strskipwhite(line);
@@ -2644,10 +2532,9 @@ read_config(struct dhcpcd_ctx *ctx,
 			continue;
 		if (skip)
 			continue;
+
 		parse_config_line(ctx, ifname, ifo, option, line, &ldop, &edop);
 	}
-	fclose(fp);
-	free(buf);
 
 	if (profile && !have_profile) {
 		free_options(ctx, ifo);
@@ -2738,8 +2625,6 @@ free_options(struct dhcpcd_ctx *ctx, str
 #endif
 	rt_headclear0(ctx, &ifo->routes, AF_UNSPEC);
 
-	if (ifo->script != default_script)
-		free(ifo->script);
 	free(ifo->arping);
 	free(ifo->blacklist);
 	free(ifo->fallback);

Index: src/external/bsd/dhcpcd/dist/src/ipv6.c
diff -u src/external/bsd/dhcpcd/dist/src/ipv6.c:1.11 src/external/bsd/dhcpcd/dist/src/ipv6.c:1.12
--- src/external/bsd/dhcpcd/dist/src/ipv6.c:1.11	Tue Apr 21 09:55:33 2020
+++ src/external/bsd/dhcpcd/dist/src/ipv6.c	Sun May 31 12:52:11 2020
@@ -69,6 +69,7 @@
 #include "ipv6.h"
 #include "ipv6nd.h"
 #include "logerr.h"
+#include "privsep.h"
 #include "sa.h"
 #include "script.h"
 
@@ -139,25 +140,27 @@ ipv6_init(struct dhcpcd_ctx *ctx)
 #ifndef __sun
 	ctx->nd_fd = -1;
 #endif
-	ctx->dhcp6_fd = -1;
+#ifdef DHCP6
+	ctx->dhcp6_rfd = -1;
+	ctx->dhcp6_wfd = -1;
+#endif
 	return 0;
 }
 
 static ssize_t
 ipv6_readsecret(struct dhcpcd_ctx *ctx)
 {
-	FILE *fp;
 	char line[1024];
 	unsigned char *p;
 	size_t len;
 	uint32_t r;
-	int x;
 
-	if ((ctx->secret_len = read_hwaddr_aton(&ctx->secret, SECRET)) != 0)
+	ctx->secret_len = dhcp_read_hwaddr_aton(ctx, &ctx->secret, SECRET);
+	if (ctx->secret_len != 0)
 		return (ssize_t)ctx->secret_len;
 
 	if (errno != ENOENT)
-		logerr("%s: %s", __func__, SECRET);
+		logerr("%s: cannot read secret", __func__);
 
 	/* Chaining arc4random should be good enough.
 	 * RFC7217 section 5.1 states the key SHOULD be at least 128 bits.
@@ -177,27 +180,18 @@ ipv6_readsecret(struct dhcpcd_ctx *ctx)
 		p += sizeof(r);
 	}
 
-	/* Ensure that only the dhcpcd user can read the secret.
-	 * Write permission is also denied as changing it would remove
-	 * it's stability. */
-	if ((fp = fopen(SECRET, "w")) == NULL ||
-	    chmod(SECRET, S_IRUSR) == -1)
-		goto eexit;
-	x = fprintf(fp, "%s\n",
-	    hwaddr_ntoa(ctx->secret, ctx->secret_len, line, sizeof(line)));
-	if (fclose(fp) == EOF)
-		x = -1;
-	fp = NULL;
-	if (x > 0)
-		return (ssize_t)ctx->secret_len;
-
-eexit:
-	logerr("%s: %s", __func__, SECRET);
-	if (fp != NULL)
-		fclose(fp);
-	unlink(SECRET);
-	ctx->secret_len = 0;
-	return -1;
+	hwaddr_ntoa(ctx->secret, ctx->secret_len, line, sizeof(line));
+	len = strlen(line);
+	if (len < sizeof(line) - 2) {
+		line[len++] = '\n';
+		line[len] = '\0';
+	}
+	if (dhcp_writefile(ctx, SECRET, S_IRUSR, line, len) == -1) {
+		logerr("%s: cannot write secret", __func__);
+		ctx->secret_len = 0;
+		return -1;
+	}
+	return (ssize_t)ctx->secret_len;
 }
 
 /* http://www.iana.org/assignments/ipv6-interface-ids/ipv6-interface-ids.xhtml
@@ -600,6 +594,7 @@ static void
 ipv6_deletedaddr(struct ipv6_addr *ia)
 {
 
+#ifdef DHCP6
 #ifdef PRIVSEP
 	if (!(ia->iface->ctx->options & DHCPCD_MASTER))
 		ps_inet_closedhcp6(ia);
@@ -613,6 +608,9 @@ ipv6_deletedaddr(struct ipv6_addr *ia)
 	if (ia->delegating_prefix != NULL)
 		ia->delegating_prefix->flags &= ~IPV6_AF_NOREJECT;
 #endif
+#else
+	UNUSED(ia);
+#endif
 }
 
 void
@@ -1094,10 +1092,33 @@ ipv6_anyglobal(struct interface *sifp)
 	struct interface *ifp;
 	struct ipv6_state *state;
 	struct ipv6_addr *ia;
+#ifdef BSD
+	bool forwarding;
+
+#if defined(PRIVSEP) && defined(HAVE_PLEDGE)
+	if (IN_PRIVSEP(sifp->ctx))
+		forwarding = ps_root_ip6forwarding(sifp->ctx, NULL) == 1;
+	else
+#endif
+		forwarding = ip6_forwarding(NULL) == 1;
+#endif
+
 
 	TAILQ_FOREACH(ifp, sifp->ctx->ifaces, next) {
+#ifdef BSD
+		if (ifp != sifp && !forwarding)
+			continue;
+#else
+#if defined(PRIVSEP) && defined(__linux__)
+	if (IN_PRIVSEP(sifp->ctx)) {
+		if (ifp != sifp &&
+		    ps_root_ip6forwarding(sifp->ctx, ifp->name) != 1)
+			continue;
+	} else
+#endif
 		if (ifp != sifp && ip6_forwarding(ifp->name) != 1)
 			continue;
+#endif
 
 		state = IPV6_STATE(ifp);
 		if (state == NULL)
@@ -1227,11 +1248,8 @@ ipv6_handleifa(struct dhcpcd_ctx *ctx,
 			}
 #endif
 
-			if (ia->dadcallback) {
+			if (ia->dadcallback)
 				ia->dadcallback(ia);
-				if (ctx->options & DHCPCD_FORKED)
-					goto out;
-			}
 
 			if (IN6_IS_ADDR_LINKLOCAL(&ia->addr) &&
 			    !(ia->addr_flags & IN6_IFF_NOTUSEABLE))
@@ -1246,8 +1264,6 @@ ipv6_handleifa(struct dhcpcd_ctx *ctx,
 					    cb, next);
 					cb->callback(cb->arg);
 					free(cb);
-					if (ctx->options & DHCPCD_FORKED)
-						goto out;
 				}
 			}
 		}
@@ -1263,7 +1279,6 @@ ipv6_handleifa(struct dhcpcd_ctx *ctx,
 	dhcp6_handleifa(cmd, ia, pid);
 #endif
 
-out:
 	/* Done with the ia now, so free it. */
 	if (cmd == RTM_DELADDR)
 		ipv6_freeaddr(ia);
@@ -1406,7 +1421,7 @@ ipv6_addlinklocal(struct interface *ifp)
 
 	/* Check sanity before malloc */
 	if (!(ifp->options->options & DHCPCD_SLAACPRIVATE)) {
-		switch (ifp->family) {
+		switch (ifp->hwtype) {
 		case ARPHRD_ETHER:
 			/* Check for a valid hardware address */
 			if (ifp->hwlen != 6 && ifp->hwlen != 8) {
@@ -1446,7 +1461,7 @@ nextslaacprivate:
 		ap->dadcounter = dadcounter;
 	} else {
 		memcpy(ap->addr.s6_addr, ap->prefix.s6_addr, 8);
-		switch (ifp->family) {
+		switch (ifp->hwtype) {
 		case ARPHRD_ETHER:
 			if (ifp->hwlen == 6) {
 				ap->addr.s6_addr[ 8] = ifp->hwaddr[0];
@@ -1532,6 +1547,44 @@ ipv6_tryaddlinklocal(struct interface *i
 	return ipv6_addlinklocal(ifp);
 }
 
+void
+ipv6_setscope(struct sockaddr_in6 *sin, unsigned int ifindex)
+{
+
+#ifdef __KAME__
+	/* KAME based systems want to store the scope inside the sin6_addr
+	 * for link local addresses */
+	if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr)) {
+		uint16_t scope = htons((uint16_t)ifindex);
+		memcpy(&sin->sin6_addr.s6_addr[2], &scope,
+		    sizeof(scope));
+	}
+	sin->sin6_scope_id = 0;
+#else
+	if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr))
+		sin->sin6_scope_id = ifindex;
+	else
+		sin->sin6_scope_id = 0;
+#endif
+}
+
+unsigned int
+ipv6_getscope(const struct sockaddr_in6 *sin)
+{
+#ifdef __KAME__
+	uint16_t scope;
+#endif
+
+	if (!IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr))
+		return 0;
+#ifdef __KAME__
+	memcpy(&scope, &sin->sin6_addr.s6_addr[2], sizeof(scope));
+	return (unsigned int)ntohs(scope);
+#else
+	return (unsigned int)sin->sin6_scope_id;
+#endif
+}
+
 struct ipv6_addr *
 ipv6_newaddr(struct interface *ifp, const struct in6_addr *addr,
     uint8_t prefix_len, unsigned int flags)
@@ -1635,7 +1688,7 @@ ipv6_staticdadcallback(void *arg)
 
 	wascompleted = (ia->flags & IPV6_AF_DADCOMPLETED);
 	ia->flags |= IPV6_AF_DADCOMPLETED;
-	if (ia->flags & IPV6_AF_DUPLICATED)
+	if (ia->addr_flags & IN6_IFF_DUPLICATED)
 		logwarnx("%s: DAD detected %s", ia->iface->name,
 		    ia->saddr);
 	else if (!wascompleted) {
@@ -1838,16 +1891,13 @@ ipv6_handleifa_addrs(int cmd,
 			}
 			break;
 		case RTM_NEWADDR:
+			ia->addr_flags = addr->addr_flags;
 			/* Safety - ignore tentative announcements */
-			if (addr->addr_flags &
+			if (ia->addr_flags &
 			    (IN6_IFF_DETACHED | IN6_IFF_TENTATIVE))
 				break;
 			if ((ia->flags & IPV6_AF_DADCOMPLETED) == 0) {
 				found++;
-				if (addr->addr_flags & IN6_IFF_DUPLICATED)
-					ia->flags |= IPV6_AF_DUPLICATED;
-				else
-					ia->flags &= ~IPV6_AF_DUPLICATED;
 				if (ia->dadcallback)
 					ia->dadcallback(ia);
 				/* We need to set this here in-case the
@@ -1890,7 +1940,7 @@ ipv6_tempdadcallback(void *arg)
 {
 	struct ipv6_addr *ia = arg;
 
-	if (ia->flags & IPV6_AF_DUPLICATED) {
+	if (ia->addr_flags & IN6_IFF_DUPLICATED) {
 		struct ipv6_addr *ia1;
 		struct timespec tv;
 
@@ -2076,10 +2126,22 @@ ipv6_regentempaddrs(void *arg)
 	ipv6_regen_desync(ifp, true);
 
 	clock_gettime(CLOCK_MONOTONIC, &tv);
+
+	/* Mark addresses for regen so we don't infinite loop. */
 	TAILQ_FOREACH(ia, &state->addrs, next) {
 		if (ia->flags & IPV6_AF_TEMPORARY &&
 		    !(ia->flags & IPV6_AF_STALE))
+			ia->flags |= IPV6_AF_REGEN;
+		else
+			ia->flags &= ~IPV6_AF_REGEN;
+	}
+
+	/* Now regen temp addrs */
+	TAILQ_FOREACH(ia, &state->addrs, next) {
+		if (ia->flags & IPV6_AF_REGEN) {
 			ipv6_regentempaddr0(ia, &tv);
+			ia->flags &= ~IPV6_AF_REGEN;
+		}
 	}
 }
 #endif /* IPV6_MANAGETEMPADDR */

Index: src/external/bsd/dhcpcd/dist/src/ipv6.h
diff -u src/external/bsd/dhcpcd/dist/src/ipv6.h:1.10 src/external/bsd/dhcpcd/dist/src/ipv6.h:1.11
--- src/external/bsd/dhcpcd/dist/src/ipv6.h:1.10	Mon Apr 13 15:46:26 2020
+++ src/external/bsd/dhcpcd/dist/src/ipv6.h	Sun May 31 12:52:11 2020
@@ -216,16 +216,16 @@ struct ipv6_addr {
 #define	IPV6_AF_STALE		(1U << 2)
 #define	IPV6_AF_ADDED		(1U << 3)
 #define	IPV6_AF_AUTOCONF	(1U << 4)
-#define	IPV6_AF_DUPLICATED	(1U << 5)
-#define	IPV6_AF_DADCOMPLETED	(1U << 6)
-#define	IPV6_AF_DELEGATED	(1U << 7)
-#define	IPV6_AF_DELEGATEDPFX	(1U << 8)
-#define	IPV6_AF_NOREJECT	(1U << 9)
-#define	IPV6_AF_REQUEST		(1U << 10)
-#define	IPV6_AF_STATIC		(1U << 11)
-#define	IPV6_AF_DELEGATEDLOG	(1U << 12)
-#define	IPV6_AF_RAPFX		(1U << 13)
-#define	IPV6_AF_EXTENDED	(1U << 14)
+#define	IPV6_AF_DADCOMPLETED	(1U << 5)
+#define	IPV6_AF_DELEGATED	(1U << 6)
+#define	IPV6_AF_DELEGATEDPFX	(1U << 7)
+#define	IPV6_AF_NOREJECT	(1U << 8)
+#define	IPV6_AF_REQUEST		(1U << 9)
+#define	IPV6_AF_STATIC		(1U << 10)
+#define	IPV6_AF_DELEGATEDLOG	(1U << 11)
+#define	IPV6_AF_RAPFX		(1U << 12)
+#define	IPV6_AF_EXTENDED	(1U << 13)
+#define	IPV6_AF_REGEN		(1U << 14)
 #ifdef IPV6_MANAGETEMPADDR
 #define	IPV6_AF_TEMPORARY	(1U << 15)
 #endif
@@ -287,8 +287,10 @@ struct ipv6_addr *ipv6_findmaskaddr(stru
     const struct in6_addr *);
 #define ipv6_linklocal(ifp) ipv6_iffindaddr((ifp), NULL, IN6_IFF_NOTUSEABLE)
 int ipv6_addlinklocalcallback(struct interface *, void (*)(void *), void *);
-struct ipv6_addr *ipv6_newaddr(struct interface *, const struct in6_addr *, uint8_t,
-    unsigned int);
+void ipv6_setscope(struct sockaddr_in6 *, unsigned int);
+unsigned int ipv6_getscope(const struct sockaddr_in6 *);
+struct ipv6_addr *ipv6_newaddr(struct interface *, const struct in6_addr *,
+    uint8_t, unsigned int);
 void ipv6_freeaddr(struct ipv6_addr *);
 void ipv6_freedrop(struct interface *, int);
 #define ipv6_free(ifp) ipv6_freedrop((ifp), 0)

Index: src/external/bsd/dhcpcd/dist/src/logerr.c
diff -u src/external/bsd/dhcpcd/dist/src/logerr.c:1.3 src/external/bsd/dhcpcd/dist/src/logerr.c:1.4
--- src/external/bsd/dhcpcd/dist/src/logerr.c:1.3	Thu Apr  2 12:41:48 2020
+++ src/external/bsd/dhcpcd/dist/src/logerr.c	Sun May 31 12:52:11 2020
@@ -104,7 +104,6 @@ logprintdate(FILE *stream)
 		return -1;
 
 	now = tv.tv_sec;
-	tzset();
 	if (localtime_r(&now, &tmnow) == NULL)
 		return -1;
 	if (strftime(buf, sizeof(buf), "%b %d %T ", &tmnow) == 0)
@@ -238,6 +237,7 @@ vlogerrmessage(int pri, const char *fmt,
 
 	vsnprintf(buf, sizeof(buf), fmt, args);
 	logmessage(pri, "%s: %s", buf, strerror(_errno));
+	errno = _errno;
 }
 
 __printflike(2, 3) void
@@ -366,6 +366,9 @@ logopen(const char *path)
 {
 	struct logctx *ctx = &_logctx;
 
+	/* Cache timezone */
+	tzset();
+
 	if (path == NULL) {
 		int opts = 0;
 

Index: src/external/bsd/dhcpcd/dist/src/privsep.c
diff -u src/external/bsd/dhcpcd/dist/src/privsep.c:1.2 src/external/bsd/dhcpcd/dist/src/privsep.c:1.3
--- src/external/bsd/dhcpcd/dist/src/privsep.c:1.2	Wed Apr 15 15:55:24 2020
+++ src/external/bsd/dhcpcd/dist/src/privsep.c	Sun May 31 12:52:11 2020
@@ -63,6 +63,7 @@
 #include "arp.h"
 #include "common.h"
 #include "control.h"
+#include "dev.h"
 #include "dhcp.h"
 #include "dhcp6.h"
 #include "eloop.h"
@@ -70,36 +71,18 @@
 #include "logerr.h"
 #include "privsep.h"
 
+#ifdef HAVE_CAPSICUM
+#include <sys/capsicum.h>
+#endif
 #ifdef HAVE_UTIL_H
 #include <util.h>
 #endif
 
 int
-ps_mkdir(char *path)
-{
-	char *slash;
-	bool done;
-
-	slash = path;
-	for (;;) {
-		slash += strspn(slash, "/");
-		slash += strcspn(slash, "/");
-		done = (*slash == '\0');
-		*slash = '\0';
-		if (mkdir(path, 0755) == -1 && errno != EEXIST)
-			return -1;
-		if (done)
-			break;
-		*slash = '/';
-	}
-	return 0;
-}
-
-int
 ps_init(struct dhcpcd_ctx *ctx)
 {
-	char path[PATH_MAX];
-	struct passwd *pw = ctx->ps_user;
+	struct passwd *pw;
+	struct stat st;
 
 	errno = 0;
 	if ((ctx->ps_user = pw = getpwnam(PRIVSEP_USER)) == NULL) {
@@ -113,25 +96,14 @@ ps_init(struct dhcpcd_ctx *ctx)
 		return -1;
 	}
 
-	/* If we pickup the _dhcp user refuse the default directory */
-	if (strcmp(pw->pw_dir, "/var/empty") == 0) {
+	if (stat(pw->pw_dir, &st) == -1 || !S_ISDIR(st.st_mode)) {
 		ctx->options &= ~DHCPCD_PRIVSEP;
-		logerrx("refusing chroot: %s: %s", PRIVSEP_USER, pw->pw_dir);
+		logerrx("refusing chroot: %s: %s",
+		    PRIVSEP_USER, pw->pw_dir);
 		errno = 0;
 		return -1;
 	}
 
-	/* Create the database directory. */
-	if (snprintf(path, sizeof(path), "%s%s", pw->pw_dir, DBDIR) == -1 ||
-	    ps_mkdir(path) == -1 ||
-	    chown(path, pw->pw_uid, pw->pw_gid) == -1 ||
-	    chmod(path, 0755) == -1)
-		logerr("%s: %s", __func__, path);
-
-	/* Ensure we have a localtime to correctly format dates. */
-	if (ps_root_docopychroot(ctx, "/etc/localtime") == -1 && errno!=ENOENT)
-		logerr("%s: %s", __func__, "/etc/localtime");
-
 	ctx->options |= DHCPCD_PRIVSEP;
 	return 0;
 }
@@ -142,8 +114,7 @@ ps_dropprivs(struct dhcpcd_ctx *ctx)
 	struct passwd *pw = ctx->ps_user;
 
 	if (!(ctx->options & DHCPCD_FORKED))
-		logdebugx("chrooting to `%s'", pw->pw_dir);
-
+		logdebugx("chrooting to `%s' as %s", pw->pw_dir, pw->pw_name);
 	if (chroot(pw->pw_dir) == -1)
 		logerr("%s: chroot `%s'", __func__, pw->pw_dir);
 	if (chdir("/") == -1)
@@ -170,6 +141,11 @@ ps_dostart(struct dhcpcd_ctx *ctx,
 	int stype;
 	int fd[2];
 	pid_t pid;
+#ifdef HAVE_CAPSICUM
+	cap_rights_t rights;
+
+	cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT, CAP_SHUTDOWN);
+#endif
 
 	stype = SOCK_CLOEXEC | SOCK_NONBLOCK;
 	if (socketpair(AF_UNIX, SOCK_DGRAM | stype, 0, fd) == -1) {
@@ -189,8 +165,17 @@ ps_dostart(struct dhcpcd_ctx *ctx,
 		*priv_pid = pid;
 		*priv_fd = fd[0];
 		close(fd[1]);
-		if (recv_unpriv_msg != NULL &&
-		    eloop_event_add(ctx->eloop, *priv_fd,
+		if (recv_unpriv_msg == NULL)
+			;
+#ifdef HAVE_CAPSICUM
+		else if (cap_rights_limit(*priv_fd, &rights) == -1
+		    && errno != ENOSYS)
+		{
+			logerr("%s: cap_rights_limit", __func__);
+			return -1;
+		}
+#endif
+		else if (eloop_event_add(ctx->eloop, *priv_fd,
 		    recv_unpriv_msg, recv_ctx) == -1)
 		{
 			logerr("%s: eloop_event_add", __func__);
@@ -234,6 +219,11 @@ ps_dostart(struct dhcpcd_ctx *ctx,
 		goto errexit;
 	}
 
+#ifdef HAVE_CAPSICUM
+	if (cap_rights_limit(*priv_fd, &rights) == -1 && errno != ENOSYS)
+		goto errexit;
+#endif
+
 	if (eloop_event_add(ctx->eloop, *priv_fd, recv_msg, recv_ctx) == -1)
 	{
 		logerr("%s: eloop_event_add", __func__);
@@ -246,8 +236,8 @@ ps_dostart(struct dhcpcd_ctx *ctx,
 	if (!(ctx->options & DHCPCD_DEBUG) &&
 	   (!(ctx->options & DHCPCD_TEST) || loggetopts() & LOGERR_QUIET))
 	{
-		freopen(_PATH_DEVNULL, "w", stdout);
-		freopen(_PATH_DEVNULL, "w", stderr);
+		(void)freopen(_PATH_DEVNULL, "w", stdout);
+		(void)freopen(_PATH_DEVNULL, "w", stderr);
 	}
 
 	if (flags & PSF_DROPPRIVS)
@@ -258,7 +248,7 @@ ps_dostart(struct dhcpcd_ctx *ctx,
 errexit:
 	/* Failure to start root or inet processes is fatal. */
 	if (priv_fd == &ctx->ps_root_fd || priv_fd == &ctx->ps_inet_fd)
-		ps_sendcmd(ctx, *priv_fd, PS_STOP, 0, NULL, 0);
+		(void)ps_sendcmd(ctx, *priv_fd, PS_STOP, 0, NULL, 0);
 	shutdown(*priv_fd, SHUT_RDWR);
 	*priv_fd = -1;
 	return -1;
@@ -288,6 +278,18 @@ ps_dostop(struct dhcpcd_ctx *ctx, pid_t 
 		logerr(__func__);
 #endif
 	status = 0;
+
+#ifdef HAVE_CAPSICUM
+	unsigned int cap_mode = 0;
+	int cap_err = cap_getmode(&cap_mode);
+
+	if (cap_err == -1) {
+		if (errno != ENOSYS)
+			logerr("%s: cap_getmode", __func__);
+	} else if (cap_mode != 0)
+		goto nowait;
+#endif
+
 	/* Wait for the process to finish */
 	while (waitpid(*pid, &status, 0) == -1) {
 		if (errno != EINTR) {
@@ -300,6 +302,9 @@ ps_dostop(struct dhcpcd_ctx *ctx, pid_t 
 			logerr("%s: waitpid ", __func__);
 #endif
 	}
+#ifdef HAVE_CAPSICUM
+nowait:
+#endif
 	*pid = 0;
 
 #ifdef PRIVSEP_DEBUG
@@ -318,6 +323,7 @@ ps_start(struct dhcpcd_ctx *ctx)
 
 	switch (pid = ps_root_start(ctx)) {
 	case -1:
+		logerr("ps_root_start");
 		return -1;
 	case 0:
 		return 0;
@@ -327,7 +333,7 @@ ps_start(struct dhcpcd_ctx *ctx)
 
 	/* No point in spawning the generic network listener if we're
 	 * not going to use it. */
-	if (!(ctx->options & (DHCPCD_MASTER | DHCPCD_IPV6RS)))
+	if (!(ctx->options & (DHCPCD_MASTER | DHCPCD_IPV6)))
 		goto started;
 
 	switch (pid = ps_inet_start(ctx)) {
@@ -342,6 +348,13 @@ ps_start(struct dhcpcd_ctx *ctx)
 	}
 
 started:
+
+#ifdef ARC4RANDOM_H
+	/* Seed the random number generator early incase it needs /dev/urandom
+	 * which won't be available in the chroot. */
+	arc4random();
+#endif
+
 	return 1;
 }
 
@@ -374,14 +387,6 @@ ps_stop(struct dhcpcd_ctx *ctx)
 void
 ps_freeprocess(struct ps_process *psp)
 {
-#ifdef INET
-	struct ipv4_state *istate = IPV4_STATE(&psp->psp_ifp);
-
-	if (istate != NULL) {
-		free(istate->buffer);
-		free(istate);
-	}
-#endif
 
 	TAILQ_REMOVE(&psp->psp_ctx->ps_processes, psp, next);
 	if (psp->psp_fd != -1) {
@@ -392,6 +397,10 @@ ps_freeprocess(struct ps_process *psp)
 		eloop_event_delete(psp->psp_ctx->eloop, psp->psp_work_fd);
 		close(psp->psp_work_fd);
 	}
+#ifdef INET
+	if (psp->psp_bpf != NULL)
+		bpf_close(psp->psp_bpf);
+#endif
 	free(psp);
 }
 
@@ -452,26 +461,24 @@ ps_unrollmsg(struct msghdr *msg, struct 
 	return 0;
 }
 
-
 ssize_t
 ps_sendpsmmsg(struct dhcpcd_ctx *ctx, int fd,
     struct ps_msghdr *psm, const struct msghdr *msg)
 {
-	assert(msg == NULL || msg->msg_iovlen == 1);
-
 	struct iovec iov[] = {
 		{ .iov_base = UNCONST(psm), .iov_len = sizeof(*psm) },
 		{ .iov_base = NULL, },	/* name */
 		{ .iov_base = NULL, },	/* control */
-		{ .iov_base = NULL, },	/* payload */
+		{ .iov_base = NULL, },	/* payload 1 */
+		{ .iov_base = NULL, },	/* payload 2 */
+		{ .iov_base = NULL, },	/* payload 3 */
 	};
-	int iovlen = __arraycount(iov);
+	int iovlen;
 	ssize_t len;
 
 	if (msg != NULL) {
 		struct iovec *iovp = &iov[1];
-
-		assert(msg->msg_iovlen == 1);
+		int i;
 
 		psm->ps_namelen = msg->msg_namelen;
 		psm->ps_controllen = (socklen_t)msg->msg_controllen;
@@ -481,10 +488,18 @@ ps_sendpsmmsg(struct dhcpcd_ctx *ctx, in
 		iovp++;
 		iovp->iov_base = msg->msg_control;
 		iovp->iov_len = msg->msg_controllen;
-		iovp++;
-		iovp->iov_base = msg->msg_iov[0].iov_base;
-		iovp->iov_len = msg->msg_iov[0].iov_len;
-		iovlen = __arraycount(iov);
+		iovlen = 3;
+
+		for (i = 0; i < (int)msg->msg_iovlen; i++) {
+			if ((size_t)(iovlen + i) > __arraycount(iov)) {
+				errno =	ENOBUFS;
+				return -1;
+			}
+			iovp++;
+			iovp->iov_base = msg->msg_iov[i].iov_base;
+			iovp->iov_len = msg->msg_iov[i].iov_len;
+		}
+		iovlen += i;
 	} else
 		iovlen = 1;
 
@@ -513,18 +528,19 @@ ps_sendpsmdata(struct dhcpcd_ctx *ctx, i
 
 
 ssize_t
-ps_sendmsg(struct dhcpcd_ctx *ctx, int fd, uint8_t cmd, unsigned long flags,
+ps_sendmsg(struct dhcpcd_ctx *ctx, int fd, uint16_t cmd, unsigned long flags,
     const struct msghdr *msg)
 {
-	assert(msg->msg_iovlen == 1);
-
 	struct ps_msghdr psm = {
 		.ps_cmd = cmd,
 		.ps_flags = flags,
 		.ps_namelen = msg->msg_namelen,
 		.ps_controllen = (socklen_t)msg->msg_controllen,
-		.ps_datalen = msg->msg_iov[0].iov_len,
 	};
+	size_t i;
+
+	for (i = 0; i < (size_t)msg->msg_iovlen; i++)
+		psm.ps_datalen += msg->msg_iov[i].iov_len;
 
 #if 0	/* For debugging structure padding. */
 	logerrx("psa.family %lu %zu", offsetof(struct ps_addr, psa_family), sizeof(psm.ps_id.psi_addr.psa_family));
@@ -555,7 +571,7 @@ ps_sendmsg(struct dhcpcd_ctx *ctx, int f
 }
 
 ssize_t
-ps_sendcmd(struct dhcpcd_ctx *ctx, int fd, uint8_t cmd, unsigned long flags,
+ps_sendcmd(struct dhcpcd_ctx *ctx, int fd, uint16_t cmd, unsigned long flags,
     const void *data, size_t len)
 {
 	struct ps_msghdr psm = {
@@ -573,7 +589,7 @@ ps_sendcmd(struct dhcpcd_ctx *ctx, int f
 }
 
 static ssize_t
-ps_sendcmdmsg(int fd, uint8_t cmd, const struct msghdr *msg)
+ps_sendcmdmsg(int fd, uint16_t cmd, const struct msghdr *msg)
 {
 	struct ps_msghdr psm = { .ps_cmd = cmd };
 	uint8_t data[PS_BUFLEN], *p = data;
@@ -616,7 +632,7 @@ nobufs:
 }
 
 ssize_t
-ps_recvmsg(struct dhcpcd_ctx *ctx, int rfd, uint8_t cmd, int wfd)
+ps_recvmsg(struct dhcpcd_ctx *ctx, int rfd, uint16_t cmd, int wfd)
 {
 	struct sockaddr_storage ss = { .ss_family = AF_UNSPEC };
 	uint8_t controlbuf[sizeof(struct sockaddr_storage)] = { 0 };
@@ -688,6 +704,9 @@ ps_recvpsmsg(struct dhcpcd_ctx *ctx, int
 		logdebugx("process %d stopping", getpid());
 #endif
 		ps_free(ctx);
+#ifdef PLUGIN_DEV
+		dev_stop(ctx);
+#endif
 		eloop_exit(ctx->eloop, len != -1 ? EXIT_SUCCESS : EXIT_FAILURE);
 		return len;
 	}
@@ -696,6 +715,9 @@ ps_recvpsmsg(struct dhcpcd_ctx *ctx, int
 	if (ps_unrollmsg(&msg, &psm.psm_hdr, psm.psm_data, dlen) == -1)
 		return -1;
 
+	if (callback == NULL)
+		return 0;
+
 	errno = 0;
 	return callback(cbctx, &psm.psm_hdr, &msg);
 }

Reply via email to