changeset 4511:a4f492ca06a6 draft

Merge branch 'master' into rbtree
author Roy Marples <roy@marples.name>
date Sat, 04 May 2019 11:05:17 +0100
parents 6fd556af42a4 (current diff) 6bf601e44f37 (diff)
children 011f49d4e4b8
files configure src/dhcp.c src/dhcp6.c src/dhcpcd.c src/dhcpcd.conf.5.in src/dhcpcd.h src/if-bsd.c src/if-linux.c src/if-options.c src/if-sun.c src/if.c src/if.h src/ipv4.c src/ipv4.h src/ipv4ll.c src/ipv4ll.h src/ipv6.c src/ipv6.h src/ipv6nd.c
diffstat 28 files changed, 1217 insertions(+), 867 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/compat/consttime_memequal.h	Sat May 04 11:05:17 2019 +0100
@@ -0,0 +1,28 @@
+/*
+ * Written by Matthias Drochner <drochner@NetBSD.org>.
+ * Public domain.
+ */
+
+#ifndef CONSTTIME_MEMEQUAL_H
+#define CONSTTIME_MEMEQUAL_H
+inline static int
+consttime_memequal(const void *b1, const void *b2, size_t len)
+{
+	const unsigned char *c1 = b1, *c2 = b2;
+	unsigned int res = 0;
+
+	while (len--)
+		res |= *c1++ ^ *c2++;
+
+	/*
+	 * Map 0 to 1 and [1, 256) to 0 using only constant-time
+	 * arithmetic.
+	 *
+	 * This is not simply `!res' because although many CPUs support
+	 * branchless conditional moves and many compilers will take
+	 * advantage of them, certain compilers generate branches on
+	 * certain CPUs for `!res'.
+	 */
+	return (1 & ((res - 1) >> 8));
+}
+#endif /* CONSTTIME_MEMEQUAL_H */
--- a/configure	Wed Apr 17 22:18:39 2019 +0000
+++ b/configure	Sat May 04 11:05:17 2019 +0100
@@ -14,6 +14,7 @@
 ARC4RANDOM=
 CLOSEFROM=
 RBTREE=
+CONSTTIME_MEMEQUAL=
 STRLCPY=
 UDEV=
 OS=
@@ -848,6 +849,27 @@
 	echo "#include			\"compat/strtoi.h\"" >>$CONFIG_H
 fi
 
+if [ -z "$CONSTTIME_MEMEQUAL" ]; then
+	printf "Testing for consttime_memequal ... "
+	cat <<EOF >_consttime_memequal.c
+#include <string.h>
+int main(void) {
+	return consttime_memequal("deadbeef", "deadbeef", 8);
+}
+EOF
+	if $XCC _consttime_memequal.c -o _consttime_memequal 2>&3; then
+		CONSTTIME_MEMEQUAL=yes
+	else
+		CONSTTIME_MEMEQUAL=no
+	fi
+	echo "$CONSTTIME_MEMEQUAL"
+	rm -f _consttime_memequal.c _consttime_memequal
+fi
+if [ "$CONSTTIME_MEMEQUAL" = no ]; then
+	echo "#include			\"compat/consttime_memequal.h\"" \
+	    >>$CONFIG_H
+fi
+
 if [ -z "$DPRINTF" ]; then
 	printf "Testing for dprintf ... "
 	cat <<EOF >_dprintf.c
--- a/src/arp.c	Wed Apr 17 22:18:39 2019 +0000
+++ b/src/arp.c	Sat May 04 11:05:17 2019 +0100
@@ -62,8 +62,9 @@
 /* Assert the correct structure size for on wire */
 __CTASSERT(sizeof(struct arphdr) == 8);
 
-ssize_t
-arp_request(const struct interface *ifp, in_addr_t sip, in_addr_t tip)
+static ssize_t
+arp_request(const struct interface *ifp,
+    const struct in_addr *sip, const struct in_addr *tip)
 {
 	uint8_t arp_buffer[ARP_LEN];
 	struct arphdr ar;
@@ -74,7 +75,7 @@
 	ar.ar_hrd = htons(ifp->family);
 	ar.ar_pro = htons(ETHERTYPE_IP);
 	ar.ar_hln = ifp->hwlen;
-	ar.ar_pln = sizeof(sip);
+	ar.ar_pln = sizeof(tip->s_addr);
 	ar.ar_op = htons(ARPOP_REQUEST);
 
 	p = arp_buffer;
@@ -93,9 +94,12 @@
 
 	APPEND(&ar, sizeof(ar));
 	APPEND(ifp->hwaddr, ifp->hwlen);
-	APPEND(&sip, sizeof(sip));
+	if (sip != NULL)
+		APPEND(&sip->s_addr, sizeof(sip->s_addr));
+	else
+		ZERO(sizeof(tip->s_addr));
 	ZERO(ifp->hwlen);
-	APPEND(&tip, sizeof(tip));
+	APPEND(&tip->s_addr, sizeof(tip->s_addr));
 
 	state = ARP_CSTATE(ifp);
 	return bpf_send(ifp, state->bpf_fd, ETHERTYPE_ARP, arp_buffer, len);
@@ -106,6 +110,77 @@
 }
 
 static void
+arp_report_conflicted(const struct arp_state *astate,
+    const struct arp_msg *amsg)
+{
+	char buf[HWADDR_LEN * 3];
+
+	if (amsg == NULL) {
+		logerrx("%s: DAD detected %s",
+		    astate->iface->name, inet_ntoa(astate->addr));
+		return;
+	}
+
+	logerrx("%s: hardware address %s claims %s",
+	    astate->iface->name,
+	    hwaddr_ntoa(amsg->sha, astate->iface->hwlen, buf, sizeof(buf)),
+	    inet_ntoa(astate->addr));
+}
+
+
+static void
+arp_found(struct arp_state *astate, const struct arp_msg *amsg)
+{
+	struct interface *ifp;
+	struct ivp4_addr *ia;
+#ifndef KERNEL_RFC5227
+	struct timespec now, defend;
+#endif
+
+	arp_report_conflicted(astate, amsg);
+	ifp = astate->iface;
+
+#pragma GCC diagnostic push /* GCC is clearly wrong about this warning. */
+#pragma GCC diagnostic ignored "-Wincompatible-pointer-types"
+	/* If we haven't added the address we're doing a probe. */
+	ia = ipv4_iffindaddr(ifp, &astate->addr, NULL);
+#pragma GCC diagnostic pop
+	if (ia == NULL) {
+		if (astate->found_cb != NULL)
+			astate->found_cb(astate, amsg);
+		return;
+	}
+
+#ifndef KERNEL_RFC5227
+	/* RFC 3927 Section 2.5 says a defence should
+	 * broadcast an ARP announcement.
+	 * Because the kernel will also unicast a reply to the
+	 * hardware address which requested the IP address
+	 * the other IPv4LL client will receieve two ARP
+	 * messages.
+	 * If another conflict happens within DEFEND_INTERVAL
+	 * then we must drop our address and negotiate a new one. */
+	defend.tv_sec = astate->defend.tv_sec + DEFEND_INTERVAL;
+	defend.tv_nsec = astate->defend.tv_nsec;
+	clock_gettime(CLOCK_MONOTONIC, &now);
+	if (timespeccmp(&defend, &now, >))
+		logwarnx("%s: %d second defence failed for %s",
+		    ifp->name, DEFEND_INTERVAL, inet_ntoa(astate->addr));
+	else if (arp_request(ifp, &astate->addr, &astate->addr) == -1)
+		logerr(__func__);
+	else {
+		logdebugx("%s: defended address %s",
+		    ifp->name, inet_ntoa(astate->addr));
+		astate->defend = now;
+		return;
+	}
+#endif
+
+	if (astate->defend_failed_cb != NULL)
+		astate->defend_failed_cb(astate);
+}
+
+static void
 arp_packet(struct interface *ifp, uint8_t *data, size_t len)
 {
 	const struct interface *ifn;
@@ -164,14 +239,15 @@
 	memcpy(&arm.tha, hw_t, ar.ar_hln);
 	memcpy(&arm.tip.s_addr, hw_t + ar.ar_hln, ar.ar_pln);
 
-	/* Run the conflicts */
+	/* Match the ARP probe to our states.
+	 * Ignore Unicast Poll, RFC1122. */
 	state = ARP_CSTATE(ifp);
 	TAILQ_FOREACH_SAFE(astate, &state->arp_states, next, astaten) {
-		if (arm.sip.s_addr != astate->addr.s_addr &&
-		    arm.tip.s_addr != astate->addr.s_addr)
-			continue;
-		if (astate->conflicted_cb)
-			astate->conflicted_cb(astate, &arm);
+		if (IN_ARE_ADDR_EQUAL(&arm.sip, &astate->addr) ||
+		    (IN_IS_ADDR_UNSPECIFIED(&arm.sip) &&
+		    IN_ARE_ADDR_EQUAL(&arm.tip, &astate->addr) &&
+		    state->bpf_flags & BPF_BCAST))
+			arp_found(astate, &arm);
 	}
 }
 
@@ -243,7 +319,7 @@
 	}
 }
 
-int
+static int
 arp_open(struct interface *ifp)
 {
 	struct iarp_state *state;
@@ -265,7 +341,8 @@
 {
 	struct arp_state *astate = arg;
 
-	astate->probed_cb(astate);
+	timespecclear(&astate->defend);
+	astate->not_found_cb(astate);
 }
 
 static void
@@ -290,7 +367,7 @@
 	    ifp->name, inet_ntoa(astate->addr),
 	    astate->probes ? astate->probes : PROBE_NUM, PROBE_NUM,
 	    timespec_to_double(&tv));
-	if (arp_request(ifp, 0, astate->addr.s_addr) == -1)
+	if (arp_request(ifp, NULL, &astate->addr) == -1)
 		logerr(__func__);
 }
 
@@ -314,6 +391,23 @@
 }
 #endif	/* ARP */
 
+static struct arp_state *
+arp_find(struct interface *ifp, const struct in_addr *addr)
+{
+	struct iarp_state *state;
+	struct arp_state *astate;
+
+	if ((state = ARP_STATE(ifp)) == NULL)
+		goto out;
+	TAILQ_FOREACH(astate, &state->arp_states, next) {
+		if (astate->addr.s_addr == addr->s_addr && astate->iface == ifp)
+			return astate;
+	}
+out:
+	errno = ESRCH;
+	return NULL;
+}
+
 static void
 arp_announced(void *arg)
 {
@@ -342,7 +436,7 @@
 		logdebugx("%s: ARP announcing %s (%d of %d)",
 		    ifp->name, inet_ntoa(astate->addr),
 		    astate->claims, ANNOUNCE_NUM);
-	if (arp_request(ifp, astate->addr.s_addr, astate->addr.s_addr) == -1)
+	if (arp_request(ifp, &astate->addr, &astate->addr) == -1)
 		logerr(__func__);
 	eloop_timeout_add_sec(ifp->ctx->eloop, ANNOUNCE_WAIT,
 	    astate->claims < ANNOUNCE_NUM ? arp_announce1 : arp_announced,
@@ -401,11 +495,25 @@
 }
 
 void
+arp_ifannounceaddr(struct interface *ifp, const struct in_addr *ia)
+{
+	struct arp_state *astate;
+
+	astate = arp_find(ifp, ia);
+	if (astate == NULL) {
+		astate = arp_new(ifp, ia);
+		if (astate == NULL)
+			return;
+		astate->announced_cb = arp_free;
+	}
+	arp_announce(astate);
+}
+
+void
 arp_announceaddr(struct dhcpcd_ctx *ctx, const struct in_addr *ia)
 {
 	struct interface *ifp;
 	struct ipv4_addr *iaf;
-	struct arp_state *astate;
 
 	TAILQ_FOREACH(ifp, ctx->ifaces, next) {
 		iaf = ipv4_iffindaddr(ifp, ia, NULL);
@@ -419,54 +527,7 @@
 	if (ifp == NULL)
 		return;
 
-	astate = arp_find(ifp, ia);
-	if (astate != NULL)
-		arp_announce(astate);
-}
-
-void
-arp_ifannounceaddr(struct interface *ifp, const struct in_addr *ia)
-{
-	struct arp_state *astate;
-
-	astate = arp_new(ifp, ia);
-	if (astate != NULL)
-		arp_announce(astate);
-}
-
-void
-arp_report_conflicted(const struct arp_state *astate,
-    const struct arp_msg *amsg)
-{
-
-	if (amsg != NULL) {
-		char buf[HWADDR_LEN * 3];
-
-		logerrx("%s: hardware address %s claims %s",
-		    astate->iface->name,
-		    hwaddr_ntoa(amsg->sha, astate->iface->hwlen,
-		    buf, sizeof(buf)),
-		    inet_ntoa(astate->failed));
-	} else
-		logerrx("%s: DAD detected %s",
-		    astate->iface->name, inet_ntoa(astate->failed));
-}
-
-struct arp_state *
-arp_find(struct interface *ifp, const struct in_addr *addr)
-{
-	struct iarp_state *state;
-	struct arp_state *astate;
-
-	if ((state = ARP_STATE(ifp)) == NULL)
-		goto out;
-	TAILQ_FOREACH(astate, &state->arp_states, next) {
-		if (astate->addr.s_addr == addr->s_addr && astate->iface == ifp)
-			return astate;
-	}
-out:
-	errno = ESRCH;
-	return NULL;
+	arp_ifannounceaddr(ifp, ia);
 }
 
 struct arp_state *
@@ -532,61 +593,28 @@
 	arp_tryfree(ifp);
 }
 
-static void
-arp_free_but1(struct interface *ifp, struct arp_state *astate)
+void
+arp_freeaddr(struct interface *ifp, const struct in_addr *ia)
 {
-	struct iarp_state *state;
-
-	if ((state = ARP_STATE(ifp)) != NULL) {
-		struct arp_state *p, *n;
+	struct arp_state *astate;
 
-		TAILQ_FOREACH_SAFE(p, &state->arp_states, next, n) {
-			if (p != astate)
-				arp_free(p);
-		}
-	}
-}
-
-void
-arp_free_but(struct arp_state *astate)
-{
-
-	arp_free_but1(astate->iface, astate);
+	astate = arp_find(ifp, ia);
+	arp_free(astate);
 }
 
 void
 arp_drop(struct interface *ifp)
 {
-
-	arp_free_but1(ifp, NULL);
-	arp_close(ifp);
-}
+	struct iarp_state *state;
+	struct arp_state *astate;
 
-void
-arp_handleifa(int cmd, struct ipv4_addr *addr)
-{
-	struct iarp_state *state;
-	struct arp_state *astate, *asn;
-
-	state = ARP_STATE(addr->iface);
+	state = ARP_STATE(ifp);
 	if (state == NULL)
 		return;
 
-	TAILQ_FOREACH_SAFE(astate, &state->arp_states, next, asn) {
-		if (astate->addr.s_addr != addr->addr.s_addr)
-			continue;
-		if (cmd == RTM_DELADDR)
-			arp_free(astate);
-#ifdef IN_IFF_DUPLICATED
-		if (cmd != RTM_NEWADDR)
-			continue;
-		if (addr->addr_flags & IN_IFF_DUPLICATED) {
-			if (astate->conflicted_cb)
-				astate->conflicted_cb(astate, NULL);
-		} else if (!(addr->addr_flags & IN_IFF_NOTUSEABLE)) {
-			if (astate->probed_cb)
-				astate->probed_cb(astate);
-		}
-#endif
+	while ((astate = TAILQ_FIRST(&state->arp_states)) != NULL) {
+		arp_free(astate);
 	}
+
+	/* No need to close because the last free will close */
 }
--- a/src/arp.h	Wed Apr 17 22:18:39 2019 +0000
+++ b/src/arp.h	Sat May 04 11:05:17 2019 +0100
@@ -63,15 +63,16 @@
 	TAILQ_ENTRY(arp_state) next;
 	struct interface *iface;
 
-	void (*probed_cb)(struct arp_state *);
+	void (*found_cb)(struct arp_state *, const struct arp_msg *);
+	void (*not_found_cb)(struct arp_state *);
 	void (*announced_cb)(struct arp_state *);
-	void (*conflicted_cb)(struct arp_state *, const struct arp_msg *);
+	void (*defend_failed_cb)(struct arp_state *);
 	void (*free_cb)(struct arp_state *);
 
 	struct in_addr addr;
 	int probes;
 	int claims;
-	struct in_addr failed;
+	struct timespec defend;
 };
 TAILQ_HEAD(arp_statehead, arp_state);
 
@@ -87,20 +88,14 @@
 	((const struct iarp_state *)(ifp)->if_data[IF_DATA_ARP])
 
 #ifdef ARP
-int arp_open(struct interface *);
-ssize_t arp_request(const struct interface *, in_addr_t, in_addr_t);
+struct arp_state *arp_new(struct interface *, const struct in_addr *);
 void arp_probe(struct arp_state *);
-void arp_report_conflicted(const struct arp_state *, const struct arp_msg *);
-struct arp_state *arp_new(struct interface *, const struct in_addr *);
-struct arp_state *arp_find(struct interface *, const struct in_addr *);
 void arp_announce(struct arp_state *);
 void arp_announceaddr(struct dhcpcd_ctx *, const struct in_addr *);
 void arp_ifannounceaddr(struct interface *, const struct in_addr *);
 void arp_cancel(struct arp_state *);
 void arp_free(struct arp_state *);
-void arp_free_but(struct arp_state *);
+void arp_freeaddr(struct interface *, const struct in_addr *);
 void arp_drop(struct interface *);
-
-void arp_handleifa(int, struct ipv4_addr *);
 #endif /* ARP */
 #endif /* ARP_H */
--- a/src/auth.c	Wed Apr 17 22:18:39 2019 +0000
+++ b/src/auth.c	Sat May 04 11:05:17 2019 +0100
@@ -117,7 +117,11 @@
 
 	m = vm;
 	data = vdata;
-	/* Ensure that d is inside m which *may* not be the case for DHPCPv4 */
+	/* Ensure that d is inside m which *may* not be the case for DHCPv4.
+	 * This can occur if the authentication option is split using
+	 * DHCP long option from RFC 3399. Section 9 which does infact note that
+	 * implementations should take this into account.
+	 * Fixing this would be problematic, patches welcome. */
 	if (data < m || data > m + mlen || data + dlen > m + mlen) {
 		errno = ERANGE;
 		return NULL;
@@ -354,7 +358,7 @@
 	}
 
 	free(mm);
-	if (memcmp(d, &hmac_code, dlen)) {
+	if (!consttime_memequal(d, &hmac_code, dlen)) {
 		errno = EPERM;
 		return NULL;
 	}
--- a/src/bpf.c	Wed Apr 17 22:18:39 2019 +0000
+++ b/src/bpf.c	Sat May 04 11:05:17 2019 +0100
@@ -84,7 +84,7 @@
 bpf_frame_header_len(const struct interface *ifp)
 {
 
-	switch(ifp->family) {
+	switch (ifp->family) {
 	case ARPHRD_ETHER:
 		return sizeof(struct ether_header);
 	default:
@@ -92,6 +92,23 @@
 	}
 }
 
+static const uint8_t etherbroadcastaddr[] =
+    { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+int
+bpf_frame_bcast(const struct interface *ifp, const char *frame)
+{
+
+	switch (ifp->family) {
+	case ARPHRD_ETHER:
+		return memcmp(frame +
+		    offsetof(struct ether_header, ether_dhost),
+		    etherbroadcastaddr, sizeof(etherbroadcastaddr));
+	default:
+		return -1;
+	}
+}
+
 #ifndef __linux__
 /* Linux is a special snowflake for opening, attaching and reading BPF.
  * See if-linux.c for the Linux specific BPF functions. */
@@ -227,8 +244,12 @@
 		if (state->buffer_pos + packet.bh_caplen + packet.bh_hdrlen >
 		    state->buffer_len)
 			goto next; /* Packet beyond buffer, drop. */
-		payload = state->buffer + state->buffer_pos +
-		    packet.bh_hdrlen + fl;
+		payload = state->buffer + state->buffer_pos + packet.bh_hdrlen;
+		if (bpf_frame_bcast(ifp, payload) == 0)
+			*flags |= BPF_BCAST;
+		else
+			*flags &= ~BPF_BCAST;
+		payload += fl;
 		bytes = (ssize_t)packet.bh_caplen - fl;
 		if ((size_t)bytes > len)
 			bytes = (ssize_t)len;
@@ -301,6 +322,7 @@
 /* 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)
@@ -414,7 +436,7 @@
 	         sizeof(((struct ether_arp *)0)->arp_sha), 1, 0),
 	BPF_STMT(BPF_RET + BPF_K, 0),
 };
-#define bpf_arp_ether_len	__arraycount(bpf_arp_ether)
+#define BPF_ARP_ETHER_LEN	__arraycount(bpf_arp_ether)
 
 static const struct bpf_insn bpf_arp_filter [] = {
 	/* Make sure this is for IP. */
@@ -425,21 +447,25 @@
 	BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct arphdr, ar_op)),
 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 2, 0),
 	/* or ARP REPLY. */
-	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 1),
+	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 0),
 	BPF_STMT(BPF_RET + BPF_K, 0),
 	/* Make sure the protocol length matches. */
 	BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(struct arphdr, ar_pln)),
 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(in_addr_t), 1, 0),
 	BPF_STMT(BPF_RET + BPF_K, 0),
 };
-#define bpf_arp_filter_len	__arraycount(bpf_arp_filter)
-#define bpf_arp_extra		((((ARP_ADDRS_MAX + 1) * 2) * 2) + 2)
-#define bpf_arp_hw		((((HWADDR_LEN / 4) + 2) * 2) + 1)
+#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
+
+#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)
 {
-	struct bpf_insn bpf[3+ bpf_arp_filter_len + bpf_arp_hw + bpf_arp_extra];
+	struct bpf_insn bpf[BPF_ARP_LEN];
 	struct bpf_insn *bp;
 	struct iarp_state *state;
 	uint16_t arp_len;
@@ -452,7 +478,7 @@
 	switch(ifp->family) {
 	case ARPHRD_ETHER:
 		memcpy(bp, bpf_arp_ether, sizeof(bpf_arp_ether));
-		bp += bpf_arp_ether_len;
+		bp += BPF_ARP_ETHER_LEN;
 		arp_len = sizeof(struct ether_header)+sizeof(struct ether_arp);
 		break;
 	default:
@@ -462,10 +488,10 @@
 
 	/* Copy in the main filter. */
 	memcpy(bp, bpf_arp_filter, sizeof(bpf_arp_filter));
-	bp += bpf_arp_filter_len;
+	bp += BPF_ARP_FILTER_LEN;
 
 	/* Ensure it's not from us. */
-	bp += bpf_cmp_hwaddr(bp, bpf_arp_hw, sizeof(struct arphdr),
+	bp += bpf_cmp_hwaddr(bp, BPF_CMP_HWADDR_LEN, sizeof(struct arphdr),
 	                     false, ifp->hwaddr, ifp->hwlen);
 
 	state = ARP_STATE(ifp);
--- a/src/bpf.h	Wed Apr 17 22:18:39 2019 +0000
+++ b/src/bpf.h	Sat May 04 11:05:17 2019 +0100
@@ -31,11 +31,13 @@
 #define	BPF_READING		(1U << 0)
 #define	BPF_EOF			(1U << 1)
 #define	BPF_PARTIALCSUM		(1U << 2)
+#define	BPF_BCAST		(1U << 3)
 
 #include "dhcpcd.h"
 
 extern const char *bpf_name;
 size_t bpf_frame_header_len(const struct interface *);
+int bpf_frame_bcast(const struct interface *, const char *frame);
 int bpf_open(struct interface *, int (*)(struct interface *, int));
 int bpf_close(struct interface *, int);
 int bpf_attach(int, void *, unsigned int);
--- a/src/control.c	Wed Apr 17 22:18:39 2019 +0000
+++ b/src/control.c	Sat May 04 11:05:17 2019 +0100
@@ -318,7 +318,7 @@
 
 	if ((fd = make_sock(&sa, ifname, 0)) != -1) {
 		socklen_t len;
-		
+
 		len = (socklen_t)SUN_LEN(&sa);
 		if (connect(fd, (struct sockaddr *)&sa, len) == -1) {
 			close(fd);
--- a/src/defs.h	Wed Apr 17 22:18:39 2019 +0000
+++ b/src/defs.h	Sat May 04 11:05:17 2019 +0100
@@ -28,7 +28,7 @@
 #define CONFIG_H
 
 #define PACKAGE			"dhcpcd"
-#define VERSION			"7.1.1"
+#define VERSION			"7.99.0"
 
 #ifndef CONFIG
 # define CONFIG			SYSCONFDIR "/" PACKAGE ".conf"
--- a/src/dhcp.c	Wed Apr 17 22:18:39 2019 +0000
+++ b/src/dhcp.c	Sat May 04 11:05:17 2019 +0100
@@ -124,8 +124,9 @@
 };
 
 static int dhcp_openbpf(struct interface *);
+static void dhcp_start1(void *);
 #ifdef ARP
-static void dhcp_arp_conflicted(struct arp_state *, const struct arp_msg *);
+static void dhcp_arp_found(struct arp_state *, const struct arp_msg *);
 #endif
 static void dhcp_handledhcp(struct interface *, struct bootp *, size_t,
     const struct in_addr *);
@@ -215,6 +216,12 @@
 		}
 		l = *p++;
 
+		/* Check we can read the option data, if present */
+		if (p + l > e) {
+			errno = EINVAL;
+			return NULL;
+		}
+
 		if (o == DHO_OPTSOVERLOADED) {
 			/* Ensure we only get this option once by setting
 			 * the last bit as well as the value.
@@ -249,10 +256,6 @@
 				bp += ol;
 			}
 			ol = l;
-			if (p + ol >= e) {
-				errno = EINVAL;
-				return NULL;
-			}
 			op = p;
 			bl += ol;
 		}
@@ -1925,35 +1928,6 @@
 	send_request(ifp);
 }
 
-static int
-dhcp_leaseextend(struct interface *ifp)
-{
-
-#ifdef ARP
-	if (ifp->options->options & DHCPCD_ARP) {
-		const struct dhcp_state *state;
-		struct arp_state *astate;
-
-		state = D_CSTATE(ifp);
-		if ((astate = arp_new(ifp, &state->lease.addr)) == NULL)
-			return -1;
-		astate->conflicted_cb = dhcp_arp_conflicted;
-
-#ifndef KERNEL_RFC5227
-		if (arp_open(ifp) == -1)
-			return -1;
-#endif
-
-		logwarnx("%s: extending lease until DaD failure or DHCP",
-		    ifp->name);
-		return 0;
-	}
-#endif
-
-	logwarnx("%s: extending lease", ifp->name);
-	return 0;
-}
-
 static void
 dhcp_expire1(struct interface *ifp)
 {
@@ -1972,12 +1946,12 @@
 {
 	struct interface *ifp = arg;
 
+	if (ifp->options->options & DHCPCD_LASTLEASE_EXTEND) {
+		logwarnx("%s: DHCP lease expired, extending lease", ifp->name);
+		return;
+	}
+
 	logerrx("%s: DHCP lease expired", ifp->name);
-	if (ifp->options->options & DHCPCD_LASTLEASE_EXTEND) {
-		if (dhcp_leaseextend(ifp) == 0)
-			return;
-		logerr(__func__);
-	}
 	dhcp_expire1(ifp);
 }
 
@@ -2043,9 +2017,78 @@
 	send_rebind(ifp);
 }
 
+static void
+dhcp_finish_dad(struct interface *ifp, struct in_addr *ia)
+{
+	struct dhcp_state *state = D_STATE(ifp);
+
+	if (state->state != DHS_PROBE)
+		return;
+	if (state->offer == NULL || state->offer->yiaddr != ia->s_addr)
+		return;
+
+	logdebugx("%s: DAD completed for %s", ifp->name, inet_ntoa(*ia));
+	if (!(ifp->options->options & DHCPCD_INFORM))
+		dhcp_bind(ifp);
+#ifndef IN_IFF_DUPLICATED
+	else {
+		struct bootp *bootp;
+		size_t len;
+
+		bootp = state->new;
+		len = state->new_len;
+		state->new = state->offer;
+		state->new_len = state->offer_len;
+		get_lease(ifp, &state->lease, state->new, state->new_len);
+		ipv4_applyaddr(ifp);
+		state->new = bootp;
+		state->new_len = len;
+	}
+#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);
+#endif
+
+	if (ifp->options->options & DHCPCD_INFORM)
+		dhcp_inform(ifp);
+}
+
+
+static void
+dhcp_addr_duplicated(struct interface *ifp, struct in_addr *ia)
+{
+	struct dhcp_state *state = D_STATE(ifp);
+#ifdef IN_IFF_DUPLICATED
+	struct ipv4_addr *iap;
+#endif
+
+	if ((state->offer == NULL || state->offer->yiaddr != ia->s_addr) &&
+	    !IN_ARE_ADDR_EQUAL(ia, &state->lease.addr))
+		return;
+
+	/* RFC 2131 3.1.5, Client-server interaction */
+	logerrx("%s: DAD detected %s", ifp->name, inet_ntoa(*ia));
+	unlink(state->leasefile);
+	if (!(ifp->options->options & DHCPCD_STATIC) && !state->lease.frominfo)
+		dhcp_decline(ifp);
+#ifdef IN_IFF_DUPLICATED
+	if ((iap = ipv4_iffindaddr(ifp, ia, NULL)) != NULL)
+		ipv4_deladdr(iap, 0);
+#endif
+	eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
+	eloop_timeout_add_sec(ifp->ctx->eloop,
+	    DHCP_RAND_MAX, dhcp_discover, ifp);
+}
+
 #ifdef ARP
 static void
-dhcp_arp_probed(struct arp_state *astate)
+dhcp_arp_not_found(struct arp_state *astate)
 {
 	struct interface *ifp;
 	struct dhcp_state *state;
@@ -2071,56 +2114,21 @@
 	}
 #endif
 
-	/* Already bound so DAD has worked */
-	if (state->state == DHS_BOUND)
-		return;
-
-	logdebugx("%s: DAD completed for %s",
-	    ifp->name, inet_ntoa(astate->addr));
-	if (!(ifo->options & DHCPCD_INFORM))
-		dhcp_bind(ifp);
-#ifndef IN_IFF_TENTATIVE
-	else {
-		struct bootp *bootp;
-		size_t len;
-
-		bootp = state->new;
-		len = state->new_len;
-		state->new = state->offer;
-		state->new_len = state->offer_len;
-		get_lease(ifp, &state->lease, state->new, state->new_len);
-		ipv4_applyaddr(astate->iface);
-		state->new = bootp;
-		state->new_len = len;
-	}
-#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);
-#endif
-
-	if (ifo->options & DHCPCD_INFORM)
-		dhcp_inform(ifp);
+	dhcp_finish_dad(ifp, &astate->addr);
 }
 
 static void
-dhcp_arp_conflicted(struct arp_state *astate, const struct arp_msg *amsg)
+dhcp_arp_found(struct arp_state *astate, const struct arp_msg *amsg)
 {
+	struct in_addr addr;
+#ifdef ARPING
 	struct interface *ifp;
 	struct dhcp_state *state;
-#ifdef ARPING
 	struct if_options *ifo;
-#endif
 
 	ifp = astate->iface;
 	state = D_STATE(ifp);
 
-#ifdef ARPING
 	ifo = ifp->options;
 	if (state->arping_index != -1 &&
 	    state->arping_index < ifo->arping_len &&
@@ -2129,17 +2137,14 @@
 	{
 		char buf[HWADDR_LEN * 3];
 
-		astate->failed.s_addr = ifo->arping[state->arping_index];
-		arp_report_conflicted(astate, amsg);
 		hwaddr_ntoa(amsg->sha, ifp->hwlen, buf, sizeof(buf));
 		if (dhcpcd_selectprofile(ifp, buf) == -1 &&
-		    dhcpcd_selectprofile(ifp,
-		        inet_ntoa(astate->failed)) == -1)
+		    dhcpcd_selectprofile(ifp, inet_ntoa(amsg->sip)) == -1)
 		{
 			/* We didn't find a profile for this
 			 * address or hwaddr, so move to the next
 			 * arping profile */
-			dhcp_arp_probed(astate);
+			dhcp_arp_not_found(astate);
 			return;
 		}
 		arp_free(astate);
@@ -2149,65 +2154,20 @@
 	}
 #endif
 
-	/* RFC 2131 3.1.5, Client-server interaction
-	 * NULL amsg means IN_IFF_DUPLICATED */
-	if (amsg == NULL || (state->offer &&
-	    (amsg->sip.s_addr == state->offer->yiaddr ||
-	    (amsg->sip.s_addr == 0 &&
-	    amsg->tip.s_addr == state->offer->yiaddr))))
-	{
-#ifdef IN_IFF_DUPLICATED
-		struct ipv4_addr *ia;
-#endif
-
-		if (amsg)
-			astate->failed.s_addr = state->offer->yiaddr;
-		else
-			astate->failed = astate->addr;
-		arp_report_conflicted(astate, amsg);
-		unlink(state->leasefile);
-#ifdef ARP
-		if (!(ifp->options->options & DHCPCD_STATIC) &&
-		    !state->lease.frominfo)
-			dhcp_decline(ifp);
-#endif
-#ifdef IN_IFF_DUPLICATED
-		if ((ia = ipv4_iffindaddr(ifp, &astate->addr, NULL)) != NULL)
-			ipv4_deladdr(ia, 1);
-#endif
-		arp_free(astate);
-		eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
-		eloop_timeout_add_sec(ifp->ctx->eloop,
-		    DHCP_RAND_MAX, dhcp_discover, ifp);
-		return;
-	}
-
-	/* Bound address */
-	if (amsg && state->addr &&
-	    amsg->sip.s_addr == state->addr->addr.s_addr)
-	{
-		astate->failed = state->addr->addr;
-		arp_report_conflicted(astate, amsg);
-		if (state->state == DHS_BOUND) {
-			/* For now, just report the duplicated address */
-		} else {
-			arp_free(astate);
-			dhcp_expire1(ifp);
-		}
-		return;
-	}
+	addr = astate->addr;
+	arp_free(astate);
+	dhcp_addr_duplicated(astate->iface, &addr);
 }
 
+#ifdef KERNEL_RFC5227
 static void
 dhcp_arp_announced(struct arp_state *state)
 {
 
-// TODO: DHCP addresses handle ACD?
-//#ifdef KERNEL_RFC5227
 	arp_free(state);
-//#endif
 }
-#endif
+#endif /* KERNEL_RFC5227 */
+#endif /* ARP */
 
 void
 dhcp_bind(struct interface *ifp)
@@ -2361,12 +2321,6 @@
 	if (ifp->ctx->options & DHCPCD_FORKED)
 		return;
 	state->interval = 0;
-	if (ifp->options->options & DHCPCD_LASTLEASE_EXTEND &&
-	    dhcp_leaseextend(ifp) == -1)
-	{
-		logerr("%s: %s", ifp->name, __func__);
-		dhcp_expire(ifp);
-	}
 	dhcp_discover(ifp);
 }
 
@@ -2399,17 +2353,32 @@
 }
 
 #ifdef ARP
+#ifndef KERNEL_RFC5227
+static void
+dhcp_arp_defend_failed(struct arp_state *astate)
+{
+
+	dhcp_drop(astate->iface, "EXPIRED");
+	dhcp_start1(astate->iface);
+}
+#endif
+
 static struct arp_state *
 dhcp_arp_new(struct interface *ifp, struct in_addr *addr)
 {
 	struct arp_state *astate;
+
 	astate = arp_new(ifp, addr);
 	if (astate == NULL)
 		return NULL;
 
-	astate->probed_cb = dhcp_arp_probed;
-	astate->conflicted_cb = dhcp_arp_conflicted;
+	astate->found_cb = dhcp_arp_found;
+	astate->not_found_cb = dhcp_arp_not_found;
+#ifdef KERNEL_RFC5227
 	astate->announced_cb = dhcp_arp_announced;
+#else
+	astate->defend_failed_cb = dhcp_arp_defend_failed;
+#endif
 	return astate;
 }
 
@@ -2419,7 +2388,6 @@
 	struct dhcp_state *state;
 	struct in_addr addr;
 	struct ipv4_addr *ia;
-	struct arp_state *astate;
 
 	eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
 
@@ -2429,11 +2397,7 @@
 	/* If the interface already has the address configured
 	 * then we can't ARP for duplicate detection. */
 	ia = ipv4_iffindaddr(ifp, &addr, NULL);
-	astate = dhcp_arp_new(ifp, &addr);
-	if (astate == NULL)
-		return -1;
-
-#ifdef IN_IFF_TENTATIVE
+#ifdef IN_IFF_NOTUSEABLE
 	if (ia == NULL || ia->addr_flags & IN_IFF_NOTUSEABLE) {
 		state->state = DHS_PROBE;
 		if (ia == NULL) {
@@ -2449,8 +2413,13 @@
 	}
 #else
 	if (ifp->options->options & DHCPCD_ARP && ia == NULL) {
+		struct arp_state *astate;
 		struct dhcp_lease l;
 
+		astate = dhcp_arp_new(ifp, &addr);
+		if (astate == NULL)
+			return -1;
+
 		state->state = DHS_PROBE;
 		get_lease(ifp, &l, state->offer, state->offer_len);
 		loginfox("%s: probing address %s/%d",
@@ -2707,9 +2676,14 @@
 		return;
 	}
 
+#ifdef ARP
+	if (state->addr != NULL)
+		arp_freeaddr(ifp, &state->addr->addr);
+#endif
 #ifdef ARPING
 	state->arping_index = -1;
 #endif
+
 	if (ifp->options->options & DHCPCD_RELEASE &&
 	    !(ifp->options->options & DHCPCD_INFORM))
 	{
@@ -3501,9 +3475,14 @@
 			logerr(__func__);
 			return;
 		}
+		if (D_CSTATE(ifp) == NULL) {
+			logdebugx("%s: received BOOTP for inactive interface",
+			    ifp->name);
+			return;
+		}
 	}
 
-	dhcp_handlebootp(ifp, (struct bootp *)buf, (size_t)bytes,
+	dhcp_handlebootp(ifp, (struct bootp *)(void *)buf, (size_t)bytes,
 	    &from.sin_addr);
 #endif
 }
@@ -3762,7 +3741,7 @@
 
 		astate = dhcp_arp_new(ifp, NULL);
 		if (astate)
-			dhcp_arp_probed(astate);
+			dhcp_arp_not_found(astate);
 		return;
 	}
 #endif
@@ -4014,8 +3993,10 @@
 		return;
 
 #ifdef IN_IFF_NOTUSEABLE
-	if (ia->addr_flags & IN_IFF_NOTUSEABLE)
-		return;
+	if (!(ia->addr_flags & IN_IFF_NOTUSEABLE))
+		dhcp_finish_dad(ifp, &ia->addr);
+	else if (ia->addr_flags & IN_IFF_DUPLICATED)
+		dhcp_addr_duplicated(ifp, &ia->addr);
 #endif
 
 	ifo = ifp->options;
--- a/src/dhcp6.c	Wed Apr 17 22:18:39 2019 +0000
+++ b/src/dhcp6.c	Sat May 04 11:05:17 2019 +0100
@@ -2029,12 +2029,12 @@
 		nd = o + ol;
 		l -= (size_t)(nd - d);
 		d = nd;
-		if (ol < 24) {
+		if (ol < sizeof(ia)) {
 			errno = EINVAL;
 			logerrx("%s: IA Address option truncated", ifp->name);
 			continue;
 		}
-		memcpy(&ia, o, ol);
+		memcpy(&ia, o, sizeof(ia));
 		ia.pltime = ntohl(ia.pltime);
 		ia.vltime = ntohl(ia.vltime);
 		/* RFC 3315 22.6 */
@@ -2166,40 +2166,38 @@
 			state->expire = a->prefix_vltime;
 		i++;
 
-		o = dhcp6_findoption(o, ol, D6_OPTION_PD_EXCLUDE, &ol);
 		a->prefix_exclude_len = 0;
 		memset(&a->prefix_exclude, 0, sizeof(a->prefix_exclude));
-#if 0
-		if (ex == NULL) {
-			struct dhcp6_option *w;
-			uint8_t *wp;
-
-			w = calloc(1, 128);
-			w->len = htons(2);
-			wp = D6_OPTION_DATA(w);
-			*wp++ = 64;
-			*wp++ = 0x78;
-			ex = w;
-		}
-#endif
+		o = dhcp6_findoption(o, ol, D6_OPTION_PD_EXCLUDE, &ol);
 		if (o == NULL)
 			continue;
-		if (ol < 2) {
-			logerrx("%s: truncated PD Exclude", ifp->name);
+
+		/* RFC 6603 4.2 says option length MUST be between 2 and 17.
+		 * This allows 1 octet for prefix length and 16 for the
+		 * subnet ID. */
+		if (ol < 2 || ol > 17) {
+			logerrx("%s: invalid PD Exclude option", ifp->name);
+			continue;
+		}
+
+		/* RFC 6603 4.2 says prefix length MUST be between the
+		 * length of the IAPREFIX prefix length + 1 and 128. */
+		if (*o < a->prefix_len + 1 || *o > 128) {
+			logerrx("%s: invalid PD Exclude length", ifp->name);
+			continue;
+		}
+
+		ol--;
+		/* Check option length matches prefix length. */
+		if (((*o - a->prefix_len - 1) / NBBY) + 1 != ol) {
+			logerrx("%s: PD Exclude length mismatch", ifp->name);
 			continue;
 		}
 		a->prefix_exclude_len = *o++;
-		ol--;
-		if (((a->prefix_exclude_len - a->prefix_len - 1) / NBBY) + 1
-		    != ol)
-		{
-			logerrx("%s: PD Exclude length mismatch", ifp->name);
-			a->prefix_exclude_len = 0;
-			continue;
-		}
-		nb = a->prefix_len % NBBY;
+
 		memcpy(&a->prefix_exclude, &a->prefix,
 		    sizeof(a->prefix_exclude));
+		nb = a->prefix_len % NBBY;
 		if (nb)
 			ol--;
 		pw = a->prefix_exclude.s6_addr +
@@ -3019,7 +3017,7 @@
 				 * unless those values in those fields are 0.
 				 */
 				logwarnx("%s: ignoring T1 %"PRIu32
-				    " to due address expiry",
+				    " due to address expiry",
 				    ifp->name, state->renew);
 				state->renew = state->rebind = 0;
 			}
--- a/src/dhcpcd.c	Wed Apr 17 22:18:39 2019 +0000
+++ b/src/dhcpcd.c	Sat May 04 11:05:17 2019 +0100
@@ -84,6 +84,9 @@
 const size_t dhcpcd_signals_len = __arraycount(dhcpcd_signals);
 #endif
 
+#define IF_UPANDRUNNING(a) \
+	(((a)->flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING))
+
 static void
 usage(void)
 {
@@ -653,25 +656,6 @@
 }
 
 static void
-dhcpcd_pollup(void *arg)
-{
-	struct interface *ifp = arg;
-	int carrier;
-
-	carrier = if_carrier(ifp); /* will set ifp->flags */
-	if (carrier == LINK_UP && !(ifp->flags & IFF_UP)) {
-		struct timespec tv;
-
-		tv.tv_sec = 0;
-		tv.tv_nsec = IF_POLL_UP * NSEC_PER_MSEC;
-		eloop_timeout_add_tv(ifp->ctx->eloop, &tv, dhcpcd_pollup, ifp);
-		return;
-	}
-
-	dhcpcd_handlecarrier(ifp->ctx, carrier, ifp->flags, ifp->name);
-}
-
-static void
 dhcpcd_initstate2(struct interface *ifp, unsigned long long options)
 {
 	struct if_options *ifo;
@@ -724,35 +708,18 @@
 	    !ifp->active)
 		return;
 
-	switch(carrier) {
-	case LINK_UNKNOWN:
-		carrier = if_carrier(ifp); /* will set ifp->flags */
-		break;
-	case LINK_UP:
-		/* we have a carrier! Still need to check for IFF_UP */
-		if (flags & IFF_UP)
+	if (carrier == LINK_UNKNOWN) {
+		if (ifp->wireless) {
+			carrier = LINK_DOWN;
 			ifp->flags = flags;
-		else {
-			/* So we need to poll for IFF_UP as there is no
-			 * kernel notification when it's set. */
-			dhcpcd_pollup(ifp);
-			return;
-		}
-		break;
-	default:
+		} else
+			carrier = if_carrier(ifp);
+	} else
 		ifp->flags = flags;
-	}
+	if (carrier == LINK_UNKNOWN)
+		carrier = IF_UPANDRUNNING(ifp) ? LINK_UP : LINK_DOWN;
 
-	/* If we here, we don't need to poll for IFF_UP any longer
-	 * if generated by a kernel event. */
-	eloop_timeout_delete(ifp->ctx->eloop, dhcpcd_pollup, ifp);
-
-	if (carrier == LINK_UNKNOWN) {
-		if (errno != ENOTTY && errno != ENXIO) {
-			/* Don't log an error if interface departed */
-			logerr("%s: %s", ifp->name, __func__);
-		}
-	} else if (carrier == LINK_DOWN || (ifp->flags & IFF_UP) == 0) {
+	if (carrier == LINK_DOWN || (ifp->flags & IFF_UP) == 0) {
 		if (ifp->carrier != LINK_DOWN) {
 			if (ifp->carrier == LINK_UP)
 				loginfox("%s: carrier lost", ifp->name);
@@ -867,7 +834,6 @@
 	struct if_options *ifo = ifp->options;
 	char buf[DUID_LEN * 3];
 	int carrier;
-	struct timespec tv;
 
 	if (ifo->options & DHCPCD_LINK) {
 		switch (ifp->carrier) {
@@ -879,14 +845,22 @@
 		case LINK_UNKNOWN:
 			/* No media state available.
 			 * Loop until both IFF_UP and IFF_RUNNING are set */
-			if ((carrier = if_carrier(ifp)) == LINK_UNKNOWN) {
-				tv.tv_sec = 0;
-				tv.tv_nsec = IF_POLL_UP * NSEC_PER_MSEC;
-				eloop_timeout_add_tv(ifp->ctx->eloop,
-				    &tv, dhcpcd_startinterface, ifp);
-			} else
-				dhcpcd_handlecarrier(ifp->ctx, carrier,
-				    ifp->flags, ifp->name);
+			carrier = if_carrier(ifp);
+			if (carrier == LINK_UNKNOWN) {
+				if (IF_UPANDRUNNING(ifp))
+					carrier = LINK_UP;
+				else {
+					struct timespec tv;
+
+					tv.tv_sec = 0;
+					tv.tv_nsec = IF_POLL_UP * NSEC_PER_MSEC;
+					eloop_timeout_add_tv(ifp->ctx->eloop,
+					    &tv, dhcpcd_startinterface, ifp);
+					return;
+				}
+			}
+			dhcpcd_handlecarrier(ifp->ctx, carrier,
+			    ifp->flags, ifp->name);
 			return;
 		}
 	}
@@ -987,20 +961,6 @@
 	    )
 		logerr("%s: %s", __func__, ifp->name);
 
-	if (ifp->options->options & DHCPCD_LINK &&
-	    ifp->carrier == LINK_UNKNOWN)
-	{
-		int carrier;
-
-		if ((carrier = if_carrier(ifp)) != LINK_UNKNOWN) {
-			dhcpcd_handlecarrier(ifp->ctx, carrier,
-			    ifp->flags, ifp->name);
-			return;
-		}
-		loginfox("%s: unknown carrier, waiting for interface flags",
-		    ifp->name);
-	}
-
 	dhcpcd_startinterface(ifp);
 }
 
@@ -1121,7 +1081,8 @@
 			dhcpcd_linkoverflow(ctx);
 			return;
 		}
-		logerr(__func__);
+		if (errno != ENOTSUP)
+			logerr(__func__);
 	}
 }
 
--- a/src/dhcpcd.conf.5.in	Wed Apr 17 22:18:39 2019 +0000
+++ b/src/dhcpcd.conf.5.in	Sat May 04 11:05:17 2019 +0100
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd March 8, 2019
+.Dd April 24, 2019
 .Dt DHCPCD.CONF 5
 .Os
 .Sh NAME
@@ -376,8 +376,7 @@
 noipv6rs                 # disable routing solicitation
 denyinterfaces eth2      # Don't touch eth2 at all
 interface eth0
-  ipv6rs                 # enable routing solicitation get the
-                         # default IPv6 route
+  ipv6rs                 # enable routing solicitation for eth0
   ia_na 1                # request an IPv6 address
   ia_pd 2 eth1/0         # request a PD and assign it to eth1
   ia_pd 3 eth2/1 eth3/2  # req a PD and assign it to eth2 and eth3
--- a/src/dhcpcd.h	Wed Apr 17 22:18:39 2019 +0000
+++ b/src/dhcpcd.h	Sat May 04 11:05:17 2019 +0100
@@ -85,7 +85,6 @@
 	unsigned short vlanid;
 	unsigned int metric;
 	int carrier;
-	bool media_valid;
 	bool wireless;
 	uint8_t ssid[IF_SSIDLEN];
 	unsigned int ssid_len;
@@ -184,7 +183,9 @@
 	uint8_t *secret;
 	size_t secret_len;
 
+#ifndef __sun
 	int nd_fd;
+#endif
 	struct ra_head *ra_routers;
 
 	int dhcp6_fd;
--- a/src/if-bsd.c	Wed Apr 17 22:18:39 2019 +0000
+++ b/src/if-bsd.c	Sat May 04 11:05:17 2019 +0100
@@ -203,6 +203,19 @@
 		close(priv->pf_inet6_fd);
 }
 
+int
+if_carrier(struct interface *ifp)
+{
+	struct ifmediareq ifmr = { .ifm_status = 0 };
+
+	strlcpy(ifmr.ifm_name, ifp->name, sizeof(ifmr.ifm_name));
+	if (ioctl(ifp->ctx->pf_inet_fd, SIOCGIFMEDIA, &ifmr) == -1 ||
+	    !(ifmr.ifm_status & IFM_AVALID))
+		return LINK_UNKNOWN;
+
+	return (ifmr.ifm_status & IFM_ACTIVE) ? LINK_UP : LINK_DOWN;
+}
+
 static void
 if_linkaddr(struct sockaddr_dl *sdl, const struct interface *ifp)
 {
@@ -347,20 +360,28 @@
 #endif
 }
 
-static void
-get_addrs(int type, const void *data, const struct sockaddr **sa)
+static int
+get_addrs(int type, const void *data, size_t data_len,
+    const struct sockaddr **sa)
 {
-	const char *cp;
+	const char *cp, *ep;
 	int i;
 
 	cp = data;
+	ep = cp + data_len;
 	for (i = 0; i < RTAX_MAX; i++) {
 		if (type & (1 << i)) {
+			if (cp >= ep) {
+				errno = EINVAL;
+				return -1;
+			}
 			sa[i] = (const struct sockaddr *)cp;
 			RT_ADVANCE(cp, sa[i]);
 		} else
 			sa[i] = NULL;
 	}
+
+	return 0;
 }
 
 static struct interface *
@@ -636,7 +657,12 @@
 	}
 #endif
 
-	get_addrs(rtm->rtm_addrs, rtm + 1, rti_info);
+	/* We have already checked that at least one address must be
+	 * present after the rtm structure. */
+	/* coverity[ptr_arith] */
+	if (get_addrs(rtm->rtm_addrs, rtm + 1,
+		      rtm->rtm_msglen - sizeof(*rtm), rti_info) == -1)
+		return -1;
 	memset(rt, 0, sizeof(*rt));
 
 	rt->rt_flags = (unsigned int)rtm->rtm_flags;
@@ -700,6 +726,10 @@
 	end = buf + needed;
 	for (p = buf; p < end; p += rtm->rtm_msglen) {
 		rtm = (void *)p;
+		if (p + rtm->rtm_msglen >= end) {
+			errno = EINVAL;
+			break;
+		}
 		if (if_copyrt(ctx, &rt, rtm) != 0)
 			continue;
 		if ((rtn = rt_new(rt.rt_ifp)) == NULL) {
@@ -711,7 +741,7 @@
 			rt_free(rtn);
 	}
 	free(buf);
-	return 0;
+	return p == end ? 0 : -1;
 }
 
 #ifdef INET
@@ -948,10 +978,15 @@
 	priv = (struct priv *)ia->iface->ctx->priv;
 	if (ioctl(priv->pf_inet6_fd, SIOCGIFALIFETIME_IN6, &ifr6) == -1)
 		return -1;
+	clock_gettime(CLOCK_MONOTONIC, &ia->created);
 
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+	t = ia->created.tv_sec;
+#else
 	t = time(NULL);
+#endif
+
 	lifetime = &ifr6.ifr_ifru.ifru_lifetime;
-
 	if (lifetime->ia6t_preferred)
 		ia->prefix_pltime = (uint32_t)(lifetime->ia6t_preferred -
 		    MIN(t, lifetime->ia6t_preferred));
@@ -961,7 +996,6 @@
 		ia->prefix_vltime = (uint32_t)(lifetime->ia6t_expire -
 		    MIN(t, lifetime->ia6t_expire));
 		/* Calculate the created time */
-		clock_gettime(CLOCK_MONOTONIC, &ia->created);
 		ia->created.tv_sec -= lifetime->ia6t_vltime - ia->prefix_vltime;
 	} else
 		ia->prefix_vltime = ND6_INFINITE_LIFETIME;
@@ -969,40 +1003,45 @@
 }
 #endif
 
-static void
+static int
 if_announce(struct dhcpcd_ctx *ctx, const struct if_announcemsghdr *ifan)
 {
 
+	if (ifan->ifan_msglen < sizeof(*ifan)) {
+		errno = EINVAL;
+		return -1;
+	}
+
 	switch(ifan->ifan_what) {
 	case IFAN_ARRIVAL:
-		dhcpcd_handleinterface(ctx, 1, ifan->ifan_name);
-		break;
+		return dhcpcd_handleinterface(ctx, 1, ifan->ifan_name);
 	case IFAN_DEPARTURE:
-		dhcpcd_handleinterface(ctx, -1, ifan->ifan_name);
-		break;
+		return dhcpcd_handleinterface(ctx, -1, ifan->ifan_name);
 	}
+
+	return 0;
 }
 
-static void
+static int
 if_ifinfo(struct dhcpcd_ctx *ctx, const struct if_msghdr *ifm)
 {
 	struct interface *ifp;
 	int link_state;
 
+	if (ifm->ifm_msglen < sizeof(*ifm)) {
+		errno = EINVAL;
+		return -1;
+	}
+
 	if ((ifp = if_findindex(ctx->ifaces, ifm->ifm_index)) == NULL)
-		return;
+		return 0;
 
 	switch (ifm->ifm_data.ifi_link_state) {
 	case LINK_STATE_UNKNOWN:
-		if (ifp->media_valid) {
-			link_state = LINK_DOWN;
-			break;
-		}
-		/* Interface does not report media state, so we have
-		 * to rely on IFF_UP. */
-		/* FALLTHROUGH */
+		link_state = LINK_UNKNOWN;
+		break;
 	case LINK_STATE_UP:
-		link_state = ifm->ifm_flags & IFF_UP ? LINK_UP : LINK_DOWN;
+		link_state = LINK_UP;
 		break;
 	default:
 		link_state = LINK_DOWN;
@@ -1011,19 +1050,25 @@
 
 	dhcpcd_handlecarrier(ctx, link_state,
 	    (unsigned int)ifm->ifm_flags, ifp->name);
+	return 0;
 }
 
-static void
+static int
 if_rtm(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)
 {
 	struct rt rt;
 
+	if (rtm->rtm_msglen < sizeof(*rtm)) {
+		errno = EINVAL;
+		return -1;
+	}
+
 	/* Ignore errors. */
 	if (rtm->rtm_errno != 0)
-		return;
+		return 0;
 
 	if (if_copyrt(ctx, &rt, rtm) == -1)
-		return;
+		return -1;
 
 #ifdef INET6
 	/*
@@ -1047,9 +1092,10 @@
 #endif
 
 	rt_recvrt(rtm->rtm_type, &rt, rtm->rtm_pid);
+	return 0;
 }
 
-static void
+static int
 if_ifa(struct dhcpcd_ctx *ctx, const struct ifa_msghdr *ifam)
 {
 	struct interface *ifp;
@@ -1057,11 +1103,21 @@
 	int addrflags;
 	pid_t pid;
 
+	if (ifam->ifam_msglen < sizeof(*ifam)) {
+		errno = EINVAL;
+		return -1;
+	}
+	if (~ifam->ifam_addrs & RTA_IFA)
+		return 0;
 	if ((ifp = if_findindex(ctx->ifaces, ifam->ifam_index)) == NULL)
-		return;
-	get_addrs(ifam->ifam_addrs, ifam + 1, rti_info);
-	if (rti_info[RTAX_IFA] == NULL)
-		return;
+		return 0;
+
+	/* We have already checked that at least one address must be
+	 * present after the ifam structure. */
+	/* coverity[ptr_arith] */
+	if (get_addrs(ifam->ifam_addrs, ifam + 1,
+		      ifam->ifam_msglen - sizeof(*ifam), rti_info) == -1)
+		return -1;
 
 #ifdef HAVE_IFAM_PID
 	pid = ifam->ifam_pid;
@@ -1161,7 +1217,7 @@
 			}
 			freeifaddrs(ifaddrs);
 			if (ifa != NULL)
-				return;
+				return 0;
 #endif
 		}
 
@@ -1224,42 +1280,41 @@
 	}
 #endif
 	}
+
+	return 0;
 }
 
-static void
+static int
 if_dispatch(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)
 {
 
 	if (rtm->rtm_version != RTM_VERSION)
-		return;
+		return 0;
 
 	switch(rtm->rtm_type) {
 #ifdef RTM_IFANNOUNCE
 	case RTM_IFANNOUNCE:
-		if_announce(ctx, (const void *)rtm);
-		break;
+		return if_announce(ctx, (const void *)rtm);
 #endif
 	case RTM_IFINFO:
-		if_ifinfo(ctx, (const void *)rtm);
-		break;
+		return if_ifinfo(ctx, (const void *)rtm);
 	case RTM_ADD:		/* FALLTHROUGH */
 	case RTM_CHANGE:	/* FALLTHROUGH */
 	case RTM_DELETE:
-		if_rtm(ctx, (const void *)rtm);
-		break;
+		return if_rtm(ctx, (const void *)rtm);
 #ifdef RTM_CHGADDR
 	case RTM_CHGADDR:	/* FALLTHROUGH */
 #endif
 	case RTM_DELADDR:	/* FALLTHROUGH */
 	case RTM_NEWADDR:
-		if_ifa(ctx, (const void *)rtm);
-		break;
+		return if_ifa(ctx, (const void *)rtm);
 #ifdef RTM_DESYNC
 	case RTM_DESYNC:
-		dhcpcd_linkoverflow(ctx);
-		break;
+		return dhcpcd_linkoverflow(ctx);
 #endif
 	}
+
+	return 0;
 }
 
 int
@@ -1273,9 +1328,13 @@
 	len = recvmsg(ctx->link_fd, &msg, 0);
 	if (len == -1)
 		return -1;
-	if (len != 0)
-		if_dispatch(ctx, &rtm.hdr);
-	return 0;
+	if (len == 0)
+		return 0;
+	if (len < rtm.hdr.rtm_msglen) {
+		errno = EINVAL;
+		return -1;
+	}
+	return if_dispatch(ctx, &rtm.hdr);
 }
 
 #ifndef SYS_NMLN	/* OSX */
--- a/src/if-linux.c	Wed Apr 17 22:18:39 2019 +0000
+++ b/src/if-linux.c	Sat May 04 11:05:17 2019 +0100
@@ -349,6 +349,15 @@
 	}
 }
 
+int
+if_carrier(struct interface *ifp)
+{
+
+	if (if_getflags(ifp) == -1)
+		return LINK_UNKNOWN;
+	return ifp->flags & IFF_RUNNING ? LINK_UP : LINK_DOWN;
+}
+
 static int
 get_netlink(struct dhcpcd_ctx *ctx, struct iovec *iov,
     void *arg, int fd, int flags,
@@ -1418,6 +1427,10 @@
 	if (bytes) {
 		ssize_t fl = (ssize_t)bpf_frame_header_len(ifp);
 
+		if (bpf_frame_bcast(ifp, state->buffer) == 0)
+			*flags |= BPF_BCAST;
+		else
+			*flags &= ~BPF_BCAST;
 		bytes -= fl;
 		if ((size_t)bytes > len)
 			bytes = (ssize_t)len;
--- a/src/if-options.c	Wed Apr 17 22:18:39 2019 +0000
+++ b/src/if-options.c	Sat May 04 11:05:17 2019 +0100
@@ -1323,7 +1323,7 @@
 			return -1;
 #else
 			if (ifname == NULL) {
-				logerr("IA PD must belong in an "
+				logerrx("IA PD must belong in an "
 				    "interface block");
 				return -1;
 			}
--- a/src/if-sun.c	Wed Apr 17 22:18:39 2019 +0000
+++ b/src/if-sun.c	Sat May 04 11:05:17 2019 +0100
@@ -74,9 +74,9 @@
 #endif
 
 #ifndef RT_ROUNDUP
-#define RT_ROUNDUP(a)							      \
-	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
-#define RT_ADVANCE(x, n) (x += RT_ROUNDUP(salen(n)))
+#define RT_ROUNDUP(a)                                                        \
+       ((a) > 0 ? (1 + (((a) - 1) | (sizeof(int32_t) - 1))) : sizeof(int32_t))
+#define RT_ADVANCE(x, n) ((x) += RT_ROUNDUP(salen((n))))
 #endif
 
 #define COPYOUT(sin, sa) do {						      \
@@ -171,13 +171,16 @@
 }
 
 int
-if_carrier_os(struct interface *ifp)
+if_carrier(struct interface *ifp)
 {
 	kstat_ctl_t		*kcp;
 	kstat_t			*ksp;
 	kstat_named_t		*knp;
 	link_state_t		linkstate;
 
+	if (if_getflags(ifp) == -1)
+		return LINK_UNKNOWN;
+
 	kcp = kstat_open();
 	if (kcp == NULL)
 		goto err;
@@ -268,6 +271,7 @@
 if_newaddr(const char *ifname, void *arg)
 {
 	struct linkwalk		*lw = arg;
+	int error;
 	struct ifaddrs		*ifa;
 	dlpi_handle_t		dh;
 	dlpi_info_t		dlinfo;
@@ -276,7 +280,10 @@
 	struct sockaddr_dl	*sdl;
 
 	ifa = NULL;
-	if (dlpi_open(ifname, &dh, 0) != DLPI_SUCCESS)
+	error = dlpi_open(ifname, &dh, 0);
+	if (error == DLPI_ENOLINK) /* Just vanished or in global zone */
+		return B_FALSE;
+	if (error != DLPI_SUCCESS)
 		goto failed1;
 	if (dlpi_info(dh, &dlinfo, 0) != DLPI_SUCCESS)
 		goto failed;
@@ -317,7 +324,7 @@
 	ifa->ifa_next = lw->lw_ifa;
 	lw->lw_ifa = ifa;
 	dlpi_close(dh);
-	return (B_FALSE);
+	return B_FALSE;
 
 failed:
 	dlpi_close(dh);
@@ -328,7 +335,7 @@
 	}
 failed1:
 	lw->lw_error = errno;
-	return (B_TRUE);
+	return B_TRUE;
 }
 
 /* Creates an empty sockaddr_dl for lo0. */
@@ -419,19 +426,26 @@
 }
 
 static int
-get_addrs(int type, const void *data, const struct sockaddr **sa)
+get_addrs(int type, const void *data, size_t data_len,
+    const struct sockaddr **sa)
 {
-	const char *cp;
+	const char *cp, *ep;
 	int i;
 
 	cp = data;
+	ep = cp + data_len;
 	for (i = 0; i < RTAX_MAX; i++) {
 		if (type & (1 << i)) {
+			if (cp >= ep) {
+				errno = EINVAL;
+				return -1;
+			}
 			sa[i] = (const struct sockaddr *)cp;
 			RT_ADVANCE(cp, sa[i]);
 		} else
 			sa[i] = NULL;
 	}
+
 	return 0;
 }
 
@@ -520,6 +534,7 @@
 	struct rt_msghdr *rtm;
 	char *bp = rtmsg->buffer;
 	socklen_t sl;
+	bool gateway_unspec;
 
 	/* WARNING: Solaris will not allow you to delete RTF_KERNEL routes.
 	 * This includes subnet/prefix routes. */
@@ -538,25 +553,28 @@
 	rtm->rtm_flags = rt->rt_flags;
 	rtm->rtm_addrs = RTA_DST | RTA_GATEWAY;
 
+	gateway_unspec = sa_is_unspecified(&rt->rt_gateway);
+
 	if (cmd == RTM_ADD || cmd == RTM_CHANGE) {
 		bool netmask_bcast = sa_is_allones(&rt->rt_netmask);
 
 		rtm->rtm_flags |= RTF_UP;
 		if (!(rtm->rtm_flags & RTF_REJECT) &&
-		    !sa_is_loopback(&rt->rt_gateway) &&
-		    /* Solaris doesn't like interfaces on default routes. */
-		    !sa_is_unspecified(&rt->rt_dest))
+		    !sa_is_loopback(&rt->rt_gateway))
 		{
 			rtm->rtm_addrs |= RTA_IFP;
-#if 0
+			/* RTA_IFA is currently ignored by the kernel.
+			 * RTA_SRC and RTF_SETSRC look like what we want,
+			 * but they don't work with RTF_GATEWAY.
+			 * We set RTA_IFA just in the hope that the
+			 * kernel will one day support this. */
 			if (!sa_is_unspecified(&rt->rt_ifa))
 				rtm->rtm_addrs |= RTA_IFA;
-#endif
 		}
 
 		if (netmask_bcast)
 			rtm->rtm_flags |= RTF_HOST;
-		else
+		else if (!gateway_unspec)
 			rtm->rtm_flags |= RTF_GATEWAY;
 
 		/* Emulate the kernel by marking address generated
@@ -575,7 +593,7 @@
 
 	ADDSA(&rt->rt_dest);
 
-	if (sa_is_unspecified(&rt->rt_gateway))
+	if (gateway_unspec)
 		ADDSA(&rt->rt_ifa);
 	else
 		ADDSA(&rt->rt_gateway);
@@ -590,14 +608,13 @@
 		ADDSA((struct sockaddr *)&sdl);
 	}
 
-	if (rtm->rtm_addrs & RTA_IFA) {
+	if (rtm->rtm_addrs & RTA_IFA)
 		ADDSA(&rt->rt_ifa);
-		rtm->rtm_addrs |= RTA_SRC;
-	}
+
+#if 0
 	if (rtm->rtm_addrs & RTA_SRC)
 		ADDSA(&rt->rt_ifa);
-
-#undef ADDSA
+#endif
 
 	rtm->rtm_msglen = (unsigned short)(bp - (char *)rtm);
 }
@@ -620,10 +637,16 @@
 {
 	const struct sockaddr *rti_info[RTAX_MAX];
 
-	if (~rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY))
+	if (~rtm->rtm_addrs & RTA_DST)
 		return -1;
 
-	get_addrs(rtm->rtm_addrs, rtm + 1, rti_info);
+	/* We have already checked that at least one address must be
+	 * present after the rtm structure. */
+	/* coverity[ptr_arith] */
+	if (get_addrs(rtm->rtm_addrs, rtm + 1,
+		      rtm->rtm_msglen - sizeof(*rtm), rti_info) == -1)
+		return -1;
+
 	memset(rt, 0, sizeof(*rt));
 
 	rt->rt_flags = (unsigned int)rtm->rtm_flags;
@@ -631,7 +654,7 @@
 	if (rtm->rtm_addrs & RTA_NETMASK)
 		COPYSA(&rt->rt_netmask, rti_info[RTAX_NETMASK]);
 	/* dhcpcd likes an unspecified gateway to indicate via the link. */
-	if (rt->rt_flags & RTF_GATEWAY &&
+	if (rtm->rtm_addrs & RTA_GATEWAY &&
 	    rti_info[RTAX_GATEWAY]->sa_family != AF_LINK)
 		COPYSA(&rt->rt_gateway, rti_info[RTAX_GATEWAY]);
 	if (rtm->rtm_addrs & RTA_SRC)
@@ -687,7 +710,7 @@
 	return rt;
 }
 
-static void
+static int
 if_finishrt(struct dhcpcd_ctx *ctx, struct rt *rt)
 {
 	int mtu;
@@ -731,10 +754,8 @@
 	if (rt->rt_ifp == NULL) {
 		if (if_route_get(ctx, rt) == NULL) {
 			rt->rt_ifp = if_loopback(ctx);
-			if (rt->rt_ifp == NULL) {
-				logerr(__func__);
-				return;
-			}
+			if (rt->rt_ifp == NULL)
+				return - 1;
 		}
 	}
 
@@ -743,29 +764,42 @@
 	 * This confuses dhcpcd as it expects MTU to be 0
 	 * when no explicit MTU has been set. */
 	mtu = if_getmtu(rt->rt_ifp);
+	if (mtu == -1)
+		return -1;
 	if (rt->rt_mtu == (unsigned int)mtu)
 		rt->rt_mtu = 0;
+
+	return 0;
 }
 
-static int
-if_addrflags0(int fd, const char *ifname)
+static uint64_t
+if_addrflags0(int fd, const char *ifname, const struct sockaddr *sa)
 {
 	struct lifreq		lifr;
 
 	memset(&lifr, 0, sizeof(lifr));
 	strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1)
-		return -1;
+		return 0;
+	if (ioctl(fd, SIOCGLIFADDR, &lifr) == -1)
+		return 0;
+	if (sa_cmp(sa, (struct sockaddr *)&lifr.lifr_addr) != 0)
+		return 0;
 
 	return lifr.lifr_flags;
 }
 
-static void
+static int
 if_rtm(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)
 {
 	const struct sockaddr *sa;
 	struct rt rt;
 
+	if (rtm->rtm_msglen < sizeof(*rtm) + sizeof(*sa)) {
+		errno = EINVAL;
+		return -1;
+	}
+
 	sa = (const void *)(rtm + 1);
 	switch (sa->sa_family) {
 #ifdef INET6
@@ -783,7 +817,9 @@
 			struct in6_addr dst6;
 			struct sockaddr_dl sdl;
 
-			get_addrs(rtm->rtm_addrs, sa, rti_info);
+			if (get_addrs(rtm->rtm_addrs, sa,
+			    rtm->rtm_msglen - sizeof(*rtm), rti_info) == -1)
+				return -1;
 			COPYOUT6(dst6, rti_info[RTAX_DST]);
 			if (rti_info[RTAX_GATEWAY]->sa_family == AF_LINK)
 				memcpy(&sdl, rti_info[RTAX_GATEWAY],
@@ -798,29 +834,85 @@
 	}
 #endif
 
-	if (if_copyrt(ctx, &rt, rtm) == 0) {
-		if_finishrt(ctx, &rt);
-		rt_recvrt(rtm->rtm_type, &rt, rtm->rtm_pid);
-	}
+	if (if_copyrt(ctx, &rt, rtm) == -1 && errno != ESRCH)
+		return -1;
+	if (if_finishrt(ctx, &rt) == -1)
+		return -1;
+	rt_recvrt(rtm->rtm_type, &rt, rtm->rtm_pid);
+	return 0;
 }
 
-static void
+static bool
+if_getalias(struct interface *ifp, const struct sockaddr *sa, char *alias)
+{
+	struct ifaddrs		*ifaddrs, *ifa;
+	struct interface	*ifpx;
+	bool			found;
+
+	ifaddrs = NULL;
+	if (getallifaddrs(sa->sa_family, &ifaddrs, 0) == -1)
+		return false;
+	found = false;
+	for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
+		if (ifa->ifa_addr == NULL)
+			continue;
+		if (sa_cmp(sa, ifa->ifa_addr) != 0)
+			continue;
+		/* Check it's for the right interace. */
+		ifpx = if_find(ifp->ctx->ifaces, ifa->ifa_name);
+		if (ifp == ifpx) {
+			strlcpy(alias, ifa->ifa_name, IF_NAMESIZE);
+			found = true;
+			break;
+		}
+	}
+	freeifaddrs(ifaddrs);
+	return found;
+}
+
+static int
+if_getbrdaddr(struct dhcpcd_ctx *ctx, const char *ifname, struct in_addr *brd)
+{
+	struct lifreq lifr = { 0 };
+	int r;
+
+	memset(&lifr, 0, sizeof(lifr));
+	strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
+	errno = 0;
+	r = ioctl(ctx->pf_inet_fd, SIOCGLIFBRDADDR, &lifr, sizeof(lifr));
+	if (r != -1)
+		COPYOUT(*brd, (struct sockaddr *)&lifr.lifr_broadaddr);
+	return r;
+}
+
+static int
 if_ifa(struct dhcpcd_ctx *ctx, const struct ifa_msghdr *ifam)
 {
 	struct interface	*ifp;
 	const struct sockaddr	*sa, *rti_info[RTAX_MAX];
 	int			flags;
-	const char		*ifalias;
+	char			ifalias[IF_NAMESIZE];
+
+	if (ifam->ifam_msglen < sizeof(*ifam)) {
+		errno = EINVAL;
+		return -1;
+	}
+	if (~ifam->ifam_addrs & RTA_IFA)
+		return 0;
+
+	/* We have already checked that at least one address must be
+	 * present after the ifam structure. */
+	/* coverity[ptr_arith] */
+	if (get_addrs(ifam->ifam_addrs, ifam + 1,
+		      ifam->ifam_msglen - sizeof(*ifam), rti_info) == -1)
+		return -1;
+	sa = rti_info[RTAX_IFA];
 
 	/* XXX We have no way of knowing who generated these
 	 * messages wich truely sucks because we want to
 	 * avoid listening to our own delete messages. */
 	if ((ifp = if_findindex(ctx->ifaces, ifam->ifam_index)) == NULL)
-		return;
-	sa = (const void *)(ifam + 1);
-	get_addrs(ifam->ifam_addrs, sa, rti_info);
-	if ((sa = rti_info[RTAX_IFA]) == NULL)
-		return;
+		return 0;
 
 	/*
 	 * ifa_msghdr does not supply the alias, just the interface index.
@@ -835,31 +927,8 @@
 	 *   ifam_alias
 	 *   ifam_pid
 	 */
-
-	ifalias = ifp->name;
-	if (ifam->ifam_type != RTM_DELADDR && sa->sa_family != AF_LINK) {
-		struct ifaddrs	*ifaddrs, *ifa;
-
-		ifaddrs = NULL;
-		if (getallifaddrs(sa->sa_family, &ifaddrs, 0) == -1)
-			return;
-		for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
-			if (ifa->ifa_addr != NULL) {
-				if (sa_cmp(sa, ifa->ifa_addr) == 0) {
-					/* Check it's for the right interace. */
-					struct interface	*ifpx;
-
-					ifpx = if_find(ctx->ifaces,
-					    ifa->ifa_name);
-					if (ifp == ifpx) {
-						ifalias = ifa->ifa_name;
-						break;
-					}
-				}
-			}
-		}
-		freeifaddrs(ifaddrs);
-	}
+	if (ifam->ifam_type != RTM_DELADDR && !if_getalias(ifp, sa, ifalias))
+		return 0;
 
 	switch (sa->sa_family) {
 	case AF_LINK:
@@ -882,12 +951,25 @@
 		COPYOUT(mask, rti_info[RTAX_NETMASK]);
 		COPYOUT(bcast, rti_info[RTAX_BRD]);
 
-		if (ifam->ifam_type != RTM_DELADDR) {
-			flags = if_addrflags0(ctx->pf_inet_fd, ifalias);
-			if (flags == -1)
-				break;
-		} else
-			flags = 0;
+		if (ifam->ifam_type == RTM_DELADDR) {
+			struct ipv4_addr *ia;
+
+			ia = ipv4_iffindaddr(ifp, &addr, &mask);
+			if (ia == NULL)
+				return 0;
+			strlcpy(ifalias, ia->alias, sizeof(ifalias));
+		} else if (bcast.s_addr == INADDR_ANY) {
+			/* Work around a bug where broadcast
+			 * address is not correctly reported. */
+			if (if_getbrdaddr(ctx, ifalias, &bcast) == -1)
+				return 0;
+		}
+		flags = if_addrflags(ifp, &addr, ifalias);
+		if (ifam->ifam_type == RTM_DELADDR) {
+			if (flags != -1)
+				return 0;
+		} else if (flags == -1)
+			return 0;
 
 		ipv4_handleifa(ctx,
 		    ifam->ifam_type == RTM_CHGADDR ?
@@ -907,15 +989,20 @@
 		sin6 = (const void *)rti_info[RTAX_NETMASK];
 		mask6 = sin6->sin6_addr;
 
-		if (ifam->ifam_type != RTM_DELADDR) {
-			const struct priv	 *priv;
+		if (ifam->ifam_type == RTM_DELADDR) {
+			struct ipv6_addr	*ia;
 
-			priv = (struct priv *)ctx->priv;
-			flags = if_addrflags0(priv->pf_inet6_fd, ifalias);
-			if (flags == -1)
-				break;
-		} else
-			flags = 0;
+			ia = ipv6_iffindaddr(ifp, &addr6, 0);
+			if (ia == NULL)
+				return 0;
+			strlcpy(ifalias, ia->alias, sizeof(ifalias));
+		}
+		flags = if_addrflags6(ifp, &addr6, ifalias);
+		if (ifam->ifam_type == RTM_DELADDR) {
+			if (flags != -1)
+				return 0;
+		} else if (flags == -1)
+			return 0;
 
 		ipv6_handleifa(ctx,
 		    ifam->ifam_type == RTM_CHGADDR ?
@@ -925,17 +1012,24 @@
 	}
 #endif
 	}
+
+	return 0;
 }
 
-static void
+static int
 if_ifinfo(struct dhcpcd_ctx *ctx, const struct if_msghdr *ifm)
 {
 	struct interface *ifp;
 	int state;
 	unsigned int flags;
 
+	if (ifm->ifm_msglen < sizeof(*ifm)) {
+		errno = EINVAL;
+		return -1;
+	}
+
 	if ((ifp = if_findindex(ctx->ifaces, ifm->ifm_index)) == NULL)
-		return;
+		return 0;
 	flags = (unsigned int)ifm->ifm_flags;
 	if (ifm->ifm_flags & IFF_OFFLINE)
 		state = LINK_DOWN;
@@ -944,30 +1038,30 @@
 		flags |= IFF_UP;
 	}
 	dhcpcd_handlecarrier(ctx, state, flags, ifp->name);
+	return 0;
 }
 
-static void
+static int
 if_dispatch(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)
 {
 
 	if (rtm->rtm_version != RTM_VERSION)
-		return;
+		return 0;
 
 	switch(rtm->rtm_type) {
 	case RTM_IFINFO:
-		if_ifinfo(ctx, (const void *)rtm);
-		break;
+		return if_ifinfo(ctx, (const void *)rtm);
 	case RTM_ADD:		/* FALLTHROUGH */
 	case RTM_CHANGE:	/* FALLTHROUGH */
 	case RTM_DELETE:
-		if_rtm(ctx, (const void *)rtm);
-		break;
+		return if_rtm(ctx, (const void *)rtm);
 	case RTM_CHGADDR:	/* FALLTHROUGH */
 	case RTM_DELADDR:	/* FALLTHROUGH */
 	case RTM_NEWADDR:
-		if_ifa(ctx, (const void *)rtm);
-		break;
+		return if_ifa(ctx, (const void *)rtm);
 	}
+
+	return 0;
 }
 
 int
@@ -980,9 +1074,15 @@
 
 	if ((len = recvmsg(ctx->link_fd, &msg, 0)) == -1)
 		return -1;
-	if (len != 0)
-		if_dispatch(ctx, &rtm.hdr);
-	return 0;
+	if (len == -1)
+		return -1;
+	if (len == 0)
+		return 0;
+	if (len < rtm.hdr.rtm_msglen) {
+		errno = EINVAL;
+		return -1;
+	}
+	return if_dispatch(ctx, &rtm.hdr);
 }
 
 static void
@@ -1003,7 +1103,8 @@
 
 static int
 if_addaddr(int fd, const char *ifname,
-    struct sockaddr_storage *addr, struct sockaddr_storage *mask)
+    struct sockaddr_storage *addr, struct sockaddr_storage *mask,
+    struct sockaddr_storage *brd)
 {
 	struct lifreq		lifr;
 
@@ -1020,6 +1121,13 @@
 	if (ioctl(fd, SIOCSLIFADDR, &lifr) == -1)
 		return -1;
 
+	/* Then assign the broadcast address. */
+	if (brd != NULL) {
+		lifr.lifr_broadaddr = *brd;
+		if (ioctl(fd, SIOCSLIFBRDADDR, &lifr) == -1)
+			return -1;
+	}
+
 	/* Now bring it up. */
 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1)
 		return -1;
@@ -1173,15 +1281,11 @@
 static int
 if_unplumbif(const struct dhcpcd_ctx *ctx, int af, const char *ifname)
 {
-	struct sockaddr_storage		addr, mask;
+	struct sockaddr_storage		addr = { .ss_family = af };
 	int				fd;
 
 	/* For the time being, don't unplumb the interface, just
 	 * set the address to zero. */
-	memset(&addr, 0, sizeof(addr));
-	addr.ss_family = af;
-	memset(&mask, 0, sizeof(mask));
-	mask.ss_family = af;
 	switch (af) {
 #ifdef INET
 	case AF_INET:
@@ -1202,7 +1306,8 @@
 		errno = EAFNOSUPPORT;
 		return -1;
 	}
-	return if_addaddr(fd, ifname, &addr, &mask);
+	return if_addaddr(fd, ifname, &addr, &addr,
+	    af == AF_INET ? &addr : NULL);
 }
 
 static int
@@ -1262,7 +1367,10 @@
 		rt.rt_mtu = re->ipRouteInfo.re_max_frag;
 		if_octetstr(ifname, &re->ipRouteIfIndex, sizeof(ifname));
 		rt.rt_ifp = if_find(ctx->ifaces, ifname);
-		if_finishrt(ctx, &rt);
+		if (if_finishrt(ctx, &rt) == -1) {
+			logerr(__func__);
+			continue;
+		}
 		if ((rtn = rt_new(rt.rt_ifp)) == NULL) {
 			logerr(__func__);
 			break;
@@ -1310,10 +1418,14 @@
 		ipv6_mask(&in6, re->ipv6RoutePfxLength);
 		sa_in6_init(&rt.rt_netmask, &in6);
 		sa_in6_init(&rt.rt_gateway, &re->ipv6RouteNextHop);
+		sa_in6_init(&rt.rt_ifa, &re->ipv6RouteInfo.re_src_addr);
 		rt.rt_mtu = re->ipv6RouteInfo.re_max_frag;
 		if_octetstr(ifname, &re->ipv6RouteIfIndex, sizeof(ifname));
 		rt.rt_ifp = if_find(ctx->ifaces, ifname);
-		if_finishrt(ctx, &rt);
+		if (if_finishrt(ctx, &rt) == -1) {
+			logerr(__func__);
+			continue;
+		}
 		if ((rtn = rt_new(rt.rt_ifp)) == NULL) {
 			logerr(__func__);
 			break;
@@ -1466,8 +1578,10 @@
 int
 if_address(unsigned char cmd, const struct ipv4_addr *ia)
 {
-	struct sockaddr_storage	ss_addr, ss_mask;
-	struct sockaddr_in	*sin_addr, *sin_mask;
+	union {
+		struct sockaddr		sa;
+		struct sockaddr_storage ss;
+	} addr, mask, brd;
 
 	/* Either remove the alias or ensure it exists. */
 	if (if_plumb(cmd, ia->iface->ctx, AF_INET, ia->alias) == -1)
@@ -1484,24 +1598,24 @@
 	/* We need to update the index now */
 	ia->iface->index = if_nametoindex(ia->alias);
 
-	sin_addr = (struct sockaddr_in *)&ss_addr;
-	sin_addr->sin_family = AF_INET;
-	sin_addr->sin_addr = ia->addr;
-	sin_mask = (struct sockaddr_in *)&ss_mask;
-	sin_mask->sin_family = AF_INET;
-	sin_mask->sin_addr = ia->mask;
-	return if_addaddr(ia->iface->ctx->pf_inet_fd,
-	    ia->alias, &ss_addr, &ss_mask);
+	sa_in_init(&addr.sa, &ia->addr);
+	sa_in_init(&mask.sa, &ia->mask);
+	sa_in_init(&brd.sa, &ia->brd);
+	return if_addaddr(ia->iface->ctx->pf_inet_fd, ia->alias,
+	    &addr.ss, &mask.ss, &brd.ss);
 }
 
 int
-if_addrflags(const struct interface *ifp, __unused const struct in_addr *addr,
+if_addrflags(const struct interface *ifp, const struct in_addr *addr,
     const char *alias)
 {
-	int		flags, aflags;
+	union sa_ss	ss;
+	uint64_t	aflags;
+	int		flags;
 
-	aflags = if_addrflags0(ifp->ctx->pf_inet_fd, alias);
-	if (aflags == -1)
+	sa_in_init(&ss.sa, addr);
+	aflags = if_addrflags0(ifp->ctx->pf_inet_fd, alias, &ss.sa);
+	if (aflags == 0)
 		return -1;
 	flags = 0;
 	if (aflags & IFF_DUPLICATE)
@@ -1515,9 +1629,12 @@
 int
 if_address6(unsigned char cmd, const struct ipv6_addr *ia)
 {
-	struct sockaddr_storage	ss_addr, ss_mask;
-	struct sockaddr_in6	*sin6_addr, *sin6_mask;
-	struct priv		*priv;
+	union {
+		struct sockaddr		sa;
+		struct sockaddr_in6	sin6;
+		struct sockaddr_storage ss;
+	} addr, mask;
+	const struct priv		*priv;
 	int			r;
 
 	/* Either remove the alias or ensure it exists. */
@@ -1532,29 +1649,30 @@
 		return -1;
 	}
 
-	priv = (struct priv *)ia->iface->ctx->priv;
-	sin6_addr = (struct sockaddr_in6 *)&ss_addr;
-	sin6_addr->sin6_family = AF_INET6;
-	sin6_addr->sin6_addr = ia->addr;
-	sin6_mask = (struct sockaddr_in6 *)&ss_mask;
-	sin6_mask->sin6_family = AF_INET6;
-	ipv6_mask(&sin6_mask->sin6_addr, ia->prefix_len);
-	r = if_addaddr(priv->pf_inet6_fd,
-	    ia->alias, &ss_addr, &ss_mask);
+	sa_in6_init(&addr.sa, &ia->addr);
+	mask.sin6.sin6_family = AF_INET6;
+	ipv6_mask(&mask.sin6.sin6_addr, ia->prefix_len);
+	priv = (const struct priv *)ia->iface->ctx->priv;
+	r = if_addaddr(priv->pf_inet6_fd, ia->alias, &addr.ss, &mask.ss, NULL);
 	if (r == -1 && errno == EEXIST)
 		return 0;
 	return r;
 }
 
 int
-if_addrflags6(const struct interface *ifp, __unused const struct in6_addr *addr,
+if_addrflags6(const struct interface *ifp, const struct in6_addr *addr,
     const char *alias)
 {
 	struct priv		*priv;
-	int			aflags, flags;
+	union sa_ss		ss;
+	uint64_t		aflags;
+	int			flags;
 
 	priv = (struct priv *)ifp->ctx->priv;
-	aflags = if_addrflags0(priv->pf_inet6_fd, alias);
+	sa_in6_init(&ss.sa, addr);
+	aflags = if_addrflags0(priv->pf_inet6_fd, alias, &ss.sa);
+	if (aflags == 0)
+		return -1;
 	flags = 0;
 	if (aflags & IFF_DUPLICATE)
 		flags |= IN6_IFF_DUPLICATED;
--- a/src/if.c	Wed Apr 17 22:18:39 2019 +0000
+++ b/src/if.c	Sat May 04 11:05:17 2019 +0100
@@ -126,65 +126,37 @@
 }
 
 int
-if_carrier(struct interface *ifp)
+if_getflags(struct interface *ifp)
 {
-	int r;
-	struct ifreq ifr;
-#ifdef SIOCGIFMEDIA
-	struct ifmediareq ifmr;
-#endif
-
-	memset(&ifr, 0, sizeof(ifr));
-	strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
-	r = ioctl(ifp->ctx->pf_inet_fd, SIOCGIFFLAGS, &ifr);
-	if (r != -1)
-		ifp->flags = (unsigned int)ifr.ifr_flags;
+	struct ifreq ifr = { .ifr_flags = 0 };
 
-#ifdef __sun
-	return if_carrier_os(ifp);
-#else
-	if (r == -1)
-		return LINK_UNKNOWN;
-
-#ifdef SIOCGIFMEDIA
-	memset(&ifmr, 0, sizeof(ifmr));
-	strlcpy(ifmr.ifm_name, ifp->name, sizeof(ifmr.ifm_name));
-	if (ioctl(ifp->ctx->pf_inet_fd, SIOCGIFMEDIA, &ifmr) != -1 &&
-	    ifmr.ifm_status & IFM_AVALID)
-	{
-		ifp->media_valid = true;
-		r = (ifmr.ifm_status & IFM_ACTIVE) ? LINK_UP : LINK_DOWN;
-	} else {
-		ifp->media_valid = false;
-		r = ifr.ifr_flags & IFF_RUNNING ? LINK_UP : LINK_UNKNOWN;
-	}
-#else
-	r = ifr.ifr_flags & IFF_RUNNING ? LINK_UP : LINK_DOWN;
-#endif
-#endif /* __sun */
-	return r;
+	strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
+	if (ioctl(ifp->ctx->pf_inet_fd, SIOCGIFFLAGS, &ifr) == -1)
+		return -1;
+	ifp->flags = (unsigned int)ifr.ifr_flags;
+	return 0;
 }
 
 int
 if_setflag(struct interface *ifp, short flag)
 {
-	struct ifreq ifr;
-	int r;
+	struct ifreq ifr = { .ifr_flags = 0 };
+	short f;
 
-	memset(&ifr, 0, sizeof(ifr));
+	if (if_getflags(ifp) == -1)
+		return -1;
+
+	f = (short)ifp->flags;
+	if ((f & flag) == flag)
+		return 0;
+
 	strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
-	r = -1;
-	if (ioctl(ifp->ctx->pf_inet_fd, SIOCGIFFLAGS, &ifr) == 0) {
-		if (flag == 0 || (ifr.ifr_flags & flag) == flag)
-			r = 0;
-		else {
-			ifr.ifr_flags |= flag;
-			if (ioctl(ifp->ctx->pf_inet_fd, SIOCSIFFLAGS, &ifr) ==0)
-				r = 0;
-		}
-		ifp->flags = (unsigned int)ifr.ifr_flags;
-	}
-	return r;
+	ifr.ifr_flags = f | flag;
+	if (ioctl(ifp->ctx->pf_inet_fd, SIOCSIFFLAGS, &ifr) == -1)
+		return -1;
+
+	ifp->flags = (unsigned int)ifr.ifr_flags;
+	return 0;
 }
 
 static int
--- a/src/if.h	Wed Apr 17 22:18:39 2019 +0000
+++ b/src/if.h	Sat May 04 11:05:17 2019 +0100
@@ -111,6 +111,7 @@
 #define	getifaddrs	if_getifaddrs
 #endif
 
+int if_getflags(struct interface *ifp);
 int if_setflag(struct interface *ifp, short flag);
 #define if_up(ifp) if_setflag((ifp), (IFF_UP | IFF_RUNNING))
 bool if_valid_hwaddr(const uint8_t *, size_t);
@@ -132,7 +133,6 @@
 int if_makealias(char *, size_t, const char *, int);
 #endif
 
-int if_carrier_os(struct interface *);
 int if_mtu_os(const struct interface *);
 
 /*
--- a/src/ipv4.c	Wed Apr 17 22:18:39 2019 +0000
+++ b/src/ipv4.c	Sat May 04 11:05:17 2019 +0100
@@ -482,11 +482,6 @@
 	int r;
 	struct ipv4_state *state;
 	struct ipv4_addr *ap;
-#ifdef ARP
-	struct arp_state *astate;
-#else
-	UNUSED(keeparp);
-#endif
 
 	logdebugx("%s: deleting IP address %s",
 	    addr->iface->name, addr->saddr);
@@ -498,8 +493,8 @@
 		logerr("%s: %s", addr->iface->name, __func__);
 
 #ifdef ARP
-	if (!keeparp && (astate = arp_find(addr->iface, &addr->addr)) != NULL)
-		arp_free(astate);
+	if (!keeparp)
+		arp_freeaddr(addr->iface, &addr->addr);
 #endif
 
 	state = IPV4_STATE(addr->iface);
@@ -537,6 +532,7 @@
 	    ifo->options & DHCPCD_INFORM ||
 	    (ifo->options & DHCPCD_STATIC && ifo->req_addr.s_addr == 0))
 		return 0;
+	arp_freeaddr(ifp, &state->addr->addr);
 	r = ipv4_deladdr(state->addr, 0);
 	return r;
 }
@@ -907,10 +903,10 @@
 	}
 
 	if (addr->s_addr != INADDR_ANY && addr->s_addr != INADDR_BROADCAST) {
-#ifdef ARP
-		arp_handleifa(cmd, ia);
+		dhcp_handleifa(cmd, ia, pid);
+#ifdef IPV4LL
+		ipv4ll_handleifa(cmd, ia, pid);
 #endif
-		dhcp_handleifa(cmd, ia, pid);
 	}
 
 	if (cmd == RTM_DELADDR)
--- a/src/ipv4.h	Wed Apr 17 22:18:39 2019 +0000
+++ b/src/ipv4.h	Sat May 04 11:05:17 2019 +0100
@@ -62,9 +62,8 @@
     * While it supports DaD, to seems to only expose IFF_DUPLICATE
     * so we have no way of knowing if it's tentative or not.
     * I don't even know if Solaris has any special treatment for tentative. */
-#  define IN_IFF_TENTATIVE	0
 #  define IN_IFF_DUPLICATED	0x02
-#  define IN_IFF_DETACHED	0
+#  define IN_IFF_NOTUSEABLE	IN_IFF_DUPLICATED
 #endif
 
 #ifdef IN_IFF_TENTATIVE
@@ -72,6 +71,9 @@
         (IN_IFF_TENTATIVE | IN_IFF_DUPLICATED | IN_IFF_DETACHED)
 #endif
 
+#define IN_ARE_ADDR_EQUAL(a, b)		((a)->s_addr == (b)->s_addr)
+#define IN_IS_ADDR_UNSPECIFIED(a)	((a)->s_addr == INADDR_ANY)
+
 struct ipv4_addr {
 	TAILQ_ENTRY(ipv4_addr) next;
 	struct in_addr addr;
--- a/src/ipv4ll.c	Wed Apr 17 22:18:39 2019 +0000
+++ b/src/ipv4ll.c	Sat May 04 11:05:17 2019 +0100
@@ -47,7 +47,6 @@
 #include "sa.h"
 #include "script.h"
 
-#ifdef IPV4LL
 static const struct in_addr inaddr_llmask = {
 	.s_addr = HTONL(LINKLOCAL_MASK)
 };
@@ -55,18 +54,21 @@
 	.s_addr = HTONL(LINKLOCAL_BCAST)
 };
 
+static void ipv4ll_start1(struct interface *, struct arp_state *);
+
 static in_addr_t
-ipv4ll_pickaddr(struct arp_state *astate)
+ipv4ll_pickaddr(struct interface *ifp)
 {
 	struct in_addr addr;
-	struct ipv4ll_state *istate;
+	struct ipv4ll_state *state;
 
-	istate = IPV4LL_STATE(astate->iface);
-	setstate(istate->randomstate);
+	state = IPV4LL_STATE(ifp);
+	setstate(state->randomstate);
 
 	do {
 		long r;
 
+again:
 		/* RFC 3927 Section 2.1 states that the first 256 and
 		 * last 256 addresses are reserved for future use.
 		 * See ipv4ll_start for why we don't use arc4random. */
@@ -76,13 +78,13 @@
 		    ((uint32_t)(r % 0xFD00) + 0x0100));
 
 		/* No point using a failed address */
-		if (addr.s_addr == astate->failed.s_addr)
-			continue;
+		if (IN_ARE_ADDR_EQUAL(&addr, &state->pickedaddr))
+			goto again;
 		/* Ensure we don't have the address on another interface */
-	} while (ipv4_findaddr(astate->iface->ctx, &addr) != NULL);
+	} while (ipv4_findaddr(ifp->ctx, &addr) != NULL);
 
 	/* Restore the original random state */
-	setstate(istate->arp->iface->ctx->randomstate);
+	setstate(ifp->ctx->randomstate);
 	return addr.s_addr;
 }
 
@@ -177,159 +179,14 @@
 }
 
 static void
-ipv4ll_probed(struct arp_state *astate)
-{
-	struct interface *ifp;
-	struct ipv4ll_state *state;
-	struct ipv4_addr *ia;
-
-	assert(astate != NULL);
-	assert(astate->iface != NULL);
-
-	ifp = astate->iface;
-	state = IPV4LL_STATE(ifp);
-	assert(state != NULL);
-
-	ia = ipv4_iffindaddr(ifp, &astate->addr, &inaddr_llmask);
-#ifdef IN_IFF_NOTREADY
-	if (ia == NULL || ia->addr_flags & IN_IFF_NOTREADY)
-#endif
-		loginfox("%s: using IPv4LL address %s",
-		  ifp->name, inet_ntoa(astate->addr));
-	if (ia == NULL) {
-		if (ifp->ctx->options & DHCPCD_TEST)
-			goto test;
-		ia = ipv4_addaddr(ifp, &astate->addr,
-		    &inaddr_llmask, &inaddr_llbcast);
-	}
-	if (ia == NULL)
-		return;
-#ifdef IN_IFF_NOTREADY
-	if (ia->addr_flags & IN_IFF_NOTREADY)
-		return;
-	logdebugx("%s: DAD completed for %s",
-	    ifp->name, inet_ntoa(astate->addr));
-#endif
-test:
-	state->addr = ia;
-	if (ifp->ctx->options & DHCPCD_TEST) {
-		script_runreason(ifp, "TEST");
-		eloop_exit(ifp->ctx->eloop, EXIT_SUCCESS);
-		return;
-	}
-	timespecclear(&state->defend);
-	rt_build(ifp->ctx, AF_INET);
-	arp_announce(astate);
-	script_runreason(ifp, "IPV4LL");
-	dhcpcd_daemonise(ifp->ctx);
-}
-
-static void
-ipv4ll_announced(struct arp_state *astate)
+ipv4ll_announced_arp(struct arp_state *astate)
 {
 	struct ipv4ll_state *state = IPV4LL_STATE(astate->iface);
 
 	state->conflicts = 0;
-	/* Need to keep the arp state so we can defend our IP. */
-}
-
-static void
-ipv4ll_probe(void *arg)
-{
-
-#ifdef IN_IFF_TENTATIVE
-	ipv4ll_probed(arg);
-#else
-	arp_probe(arg);
-#endif
-}
-
-static void
-ipv4ll_conflicted(struct arp_state *astate, const struct arp_msg *amsg)
-{
-	struct interface *ifp;
-	struct ipv4ll_state *state;
-#ifdef IN_IFF_DUPLICATED
-	struct ipv4_addr *ia;
+#ifdef KERNEL_RFC5227
+	arp_free(astate);
 #endif
-
-	assert(astate != NULL);
-	assert(astate->iface != NULL);
-	ifp = astate->iface;
-	state = IPV4LL_STATE(ifp);
-	assert(state != NULL);
-
-	/*
-	 * NULL amsg means kernel detected DAD.
-	 * We always fail on matching sip.
-	 * We only fail on matching tip and we haven't added that address yet.
-	 */
-	if (amsg == NULL ||
-	    amsg->sip.s_addr == astate->addr.s_addr ||
-	    (amsg->sip.s_addr == 0 && amsg->tip.s_addr == astate->addr.s_addr
-	     && ipv4_iffindaddr(ifp, &amsg->tip, NULL) == NULL))
-		astate->failed = astate->addr;
-	else
-		return;
-
-	arp_report_conflicted(astate, amsg);
-
-	if (state->addr != NULL &&
-	    astate->failed.s_addr == state->addr->addr.s_addr)
-	{
-#ifdef KERNEL_RFC5227
-		logwarnx("%s: IPv4LL defence failed for %s",
-		    ifp->name, state->addr->saddr);
-#else
-		struct timespec now, defend;
-
-		/* RFC 3927 Section 2.5 says a defence should
-		 * broadcast an ARP announcement.
-		 * Because the kernel will also unicast a reply to the
-		 * hardware address which requested the IP address
-		 * the other IPv4LL client will receieve two ARP
-		 * messages.
-		 * If another conflict happens within DEFEND_INTERVAL
-		 * then we must drop our address and negotiate a new one. */
-		defend.tv_sec = state->defend.tv_sec + DEFEND_INTERVAL;
-		defend.tv_nsec = state->defend.tv_nsec;
-		clock_gettime(CLOCK_MONOTONIC, &now);
-		if (timespeccmp(&defend, &now, >))
-			logwarnx("%s: IPv4LL %d second defence failed for %s",
-			    ifp->name, DEFEND_INTERVAL, state->addr->saddr);
-		else if (arp_request(ifp,
-		    state->addr->addr.s_addr, state->addr->addr.s_addr) == -1)
-			logerr(__func__);
-		else {
-			logdebugx("%s: defended IPv4LL address %s",
-			    ifp->name, state->addr->saddr);
-			state->defend = now;
-			return;
-		}
-#endif
-		ipv4_deladdr(state->addr, 1);
-		state->down = 1;
-		state->addr = NULL;
-		rt_build(ifp->ctx, AF_INET);
-		script_runreason(ifp, "IPV4LL");
-	}
-
-#ifdef IN_IFF_DUPLICATED
-	ia = ipv4_iffindaddr(ifp, &astate->addr, NULL);
-	if (ia != NULL && ia->addr_flags & IN_IFF_DUPLICATED)
-		ipv4_deladdr(ia, 1);
-#endif
-
-	arp_cancel(astate);
-	if (++state->conflicts == MAX_CONFLICTS)
-		logerr("%s: failed to acquire an IPv4LL address",
-		    ifp->name);
-	state->pickedaddr.s_addr = ipv4ll_pickaddr(astate);
-	astate->addr = state->pickedaddr;
-	eloop_timeout_add_sec(ifp->ctx->eloop,
-		state->conflicts >= MAX_CONFLICTS ?
-		RATE_LIMIT_INTERVAL : PROBE_WAIT,
-		ipv4ll_probe, astate);
 }
 
 static void
@@ -342,16 +199,144 @@
 		state->arp = NULL;
 }
 
-void
-ipv4ll_start(void *arg)
+static void
+ipv4ll_not_found(struct interface *ifp)
+{
+	struct ipv4ll_state *state;
+	struct ipv4_addr *ia;
+#ifdef KERNEL_RFC5227
+	struct arp_state *astate;
+#endif
+
+	state = IPV4LL_STATE(ifp);
+	assert(state != NULL);
+
+	ia = ipv4_iffindaddr(ifp, &state->pickedaddr, &inaddr_llmask);
+#ifdef IN_IFF_NOTREADY
+	if (ia == NULL || ia->addr_flags & IN_IFF_NOTREADY)
+#endif
+		loginfox("%s: using IPv4LL address %s",
+		  ifp->name, inet_ntoa(state->pickedaddr));
+	if (ia == NULL) {
+		if (ifp->ctx->options & DHCPCD_TEST)
+			goto test;
+		ia = ipv4_addaddr(ifp, &state->pickedaddr,
+		    &inaddr_llmask, &inaddr_llbcast);
+	}
+	if (ia == NULL)
+		return;
+#ifdef IN_IFF_NOTREADY
+	if (ia->addr_flags & IN_IFF_NOTREADY)
+		return;
+	logdebugx("%s: DAD completed for %s", ifp->name, ia->saddr);
+#endif
+test:
+	state->addr = ia;
+	state->down = false;
+	if (ifp->ctx->options & DHCPCD_TEST) {
+		script_runreason(ifp, "TEST");
+		eloop_exit(ifp->ctx->eloop, EXIT_SUCCESS);
+		return;
+	}
+	rt_build(ifp->ctx, AF_INET);
+#ifdef KERNEL_RFC5227
+	astate = arp_new(ifp, &ia->addr);
+	if (astate != NULL) {
+		astate->announced_cb = ipv4ll_announced_arp;
+		astate->free_cb = ipv4ll_arpfree;
+		arp_announce(astate);
+	}
+#else
+	arp_announce(state->arp);
+#endif
+	script_runreason(ifp, "IPV4LL");
+	dhcpcd_daemonise(ifp->ctx);
+}
+
+static void
+ipv4ll_startifp(void *arg)
+{
+	struct interface *ifp = arg;
+	struct ipv4ll_state *state;
+
+	state = IPV4LL_STATE(ifp);
+	ipv4ll_start1(ifp, state->arp);
+}
+
+static void
+ipv4ll_found(struct interface *ifp)
+{
+	struct ipv4ll_state *state = IPV4LL_STATE(ifp);
+
+	arp_cancel(state->arp);
+	if (++state->conflicts == MAX_CONFLICTS)
+		logerr("%s: failed to acquire an IPv4LL address",
+		    ifp->name);
+	eloop_timeout_add_sec(ifp->ctx->eloop,
+	    state->conflicts >= MAX_CONFLICTS ?
+	    RATE_LIMIT_INTERVAL : PROBE_WAIT,
+	    ipv4ll_startifp, ifp);
+}
+
+static void
+ipv4ll_defend_failed(struct interface *ifp)
+{
+	struct ipv4ll_state *state = IPV4LL_STATE(ifp);
+
+	ipv4_deladdr(state->addr, 1);
+	state->down = true;
+	state->addr = NULL;
+	if_initrt(ifp->ctx, AF_INET);
+	rt_build(ifp->ctx, AF_INET);
+	script_runreason(ifp, "IPV4LL");
+	ipv4ll_start1(ifp, state->arp);
+}
+
+#ifndef KERNEL_RFC5227
+static void
+ipv4ll_not_found_arp(struct arp_state *astate)
 {
 	struct interface *ifp;
 	struct ipv4ll_state *state;
-	struct arp_state *astate;
-	struct ipv4_addr *ia;
+
+	assert(astate != NULL);
+	assert(astate->iface != NULL);
+
+	ifp = astate->iface;
+	state = IPV4LL_STATE(ifp);
+	assert(state != NULL);
+	assert(state->arp == astate);
+	ipv4ll_not_found_arp(astate);
+}
+
+static void
+ipv4ll_found_arp(struct arp_state *astate, __unused const struct arp_msg *amsg)
+{
+	struct interface *ifp = astate->iface;
+	struct ipv4ll_state *state = IPV4LL_STATE(ifp);
 
-	assert(arg != NULL);
-	ifp = arg;
+	assert(state->arp == astate);
+	ipv4ll_found(ifp);
+}
+
+static void
+ipv4ll_defend_failed_arp(struct arp_state *astate)
+{
+	struct ipv4ll_state *state = IPV4LL_STATE(astate->iface);
+
+	assert(state->arp == astate);
+	ipv4ll_defend_failed(astate->iface);
+}
+#endif
+
+static void
+ipv4ll_start1(struct interface *ifp, struct arp_state *astate)
+{
+	struct ipv4ll_state *state;
+	struct ipv4_addr *ia;
+	bool repick;
+
+	assert(ifp != NULL);
 	if ((state = IPV4LL_STATE(ifp)) == NULL) {
 		ifp->if_data[IF_DATA_IPV4LL] = calloc(1, sizeof(*state));
 		if ((state = IPV4LL_STATE(ifp)) == NULL) {
@@ -387,16 +372,25 @@
 		state->seeded = true;
 	}
 
-	if (state->arp != NULL)
-		return;
-	if ((astate = arp_new(ifp, NULL)) == NULL)
-		return;
+#ifndef KERNEL_RFC5227
+	if (astate == NULL) {
+		if (state->arp != NULL)
+			return;
+		if ((astate = arp_new(ifp, NULL)) == NULL)
+			return;
+		astate->found_cb = ipv4ll_found_arp;
+		astate->not_found_cb = ipv4ll_not_found_arp;
+		astate->announced_cb = ipv4ll_announced_arp;
+		astate->defend_failed_cb = ipv4ll_defend_failed_arp;
+		astate->free_cb = ipv4ll_arpfree;
+		state->arp = astate;
+	} else
+		assert(state->arp == astate);
+#else
+	UNUSED(astate);
+#endif
 
-	state->arp = astate;
-	astate->probed_cb = ipv4ll_probed;
-	astate->announced_cb = ipv4ll_announced;
-	astate->conflicted_cb = ipv4ll_conflicted;
-	astate->free_cb = ipv4ll_arpfree;
+	state->down = true;
 
 	/* Find the previosuly used address. */
 	if (state->pickedaddr.s_addr != INADDR_ANY)
@@ -408,38 +402,56 @@
 	if (ia == NULL)
 		ia = ipv4_iffindlladdr(ifp);
 
-#ifdef IN_IFF_TENTATIVE
+	repick = false;
+#ifdef IN_IFF_DUPLICATED
 	if (ia != NULL && ia->addr_flags & IN_IFF_DUPLICATED) {
+		state->pickedaddr = ia->addr; /* So it's not picked again. */
+		repick = true;
 		ipv4_deladdr(ia, 0);
 		ia = NULL;
 	}
 #endif
 
+	state->addr = ia;
 	if (ia != NULL) {
-		state->pickedaddr = astate->addr = ia->addr;
+		state->pickedaddr = ia->addr;
+#ifndef KERNEL_RFC5227
+		astate->addr = ia->addr;
+#endif
 #ifdef IN_IFF_TENTATIVE
 		if (ia->addr_flags & (IN_IFF_TENTATIVE | IN_IFF_DETACHED)) {
 			loginfox("%s: waiting for DAD to complete on %s",
 			    ifp->name, inet_ntoa(ia->addr));
 			return;
 		}
+#endif
+#ifdef IN_IFF_DUPLICATED
 		loginfox("%s: using IPv4LL address %s", ifp->name, ia->saddr);
 #endif
-		ipv4ll_probed(astate);
+		ipv4ll_not_found(ifp);
 		return;
 	}
 
 	loginfox("%s: probing for an IPv4LL address", ifp->name);
-	if (state->pickedaddr.s_addr == INADDR_ANY)
-		state->pickedaddr.s_addr = ipv4ll_pickaddr(astate);
+	if (repick || state->pickedaddr.s_addr == INADDR_ANY)
+		state->pickedaddr.s_addr = ipv4ll_pickaddr(ifp);
+#ifndef KERNEL_RFC5227
 	astate->addr = state->pickedaddr;
-#ifdef IN_IFF_TENTATIVE
-	ipv4ll_probed(astate);
+#endif
+#ifdef IN_IFF_DUPLICATED
+	ipv4ll_not_found(ifp);
 #else
 	arp_probe(astate);
 #endif
 }
 
+void
+ipv4ll_start(void *arg)
+{
+
+	ipv4ll_start1(arg, NULL);
+}
+
 static void
 ipv4ll_freearp(struct interface *ifp)
 {
@@ -451,7 +463,6 @@
 
 	eloop_timeout_delete(ifp->ctx->eloop, NULL, state->arp);
 	arp_free(state->arp);
-	state->arp = NULL;
 }
 
 void
@@ -465,9 +476,7 @@
 
 	ipv4ll_freearp(ifp);
 
-#ifndef IN_IFF_TENATIVE
 	if ((ifp->options->options & DHCPCD_NODROP) == DHCPCD_NODROP)
-#endif
 		return;
 
 	state = IPV4LL_STATE(ifp);
@@ -542,4 +551,34 @@
 	return 0;
 }
 #endif
+
+void
+ipv4ll_handleifa(int cmd, struct ipv4_addr *ia, pid_t pid)
+{
+	struct interface *ifp;
+	struct ipv4ll_state *state;
+
+	ifp = ia->iface;
+	state = IPV4LL_STATE(ifp);
+	if (state == NULL || state->addr == NULL ||
+	    !IN_ARE_ADDR_EQUAL(&state->addr->addr, &ia->addr))
+		return;
+
+	if (cmd == RTM_DELADDR) {
+		loginfox("%s: pid %d deleted IP address %s",
+		    ifp->name, pid, ia->saddr);
+		ipv4ll_defend_failed(ifp);
+	}
+
+#ifdef IN_IFF_DUPLICATED
+	if (cmd != RTM_NEWADDR)
+		return;
+	if (!(ia->addr_flags & IN_IFF_NOTUSEABLE))
+		ipv4ll_not_found(ifp);
+	else if (ia->addr_flags & IN_IFF_DUPLICATED) {
+		logerrx("%s: DAD detected %s", ifp->name, ia->saddr);
+		ipv4_deladdr(state->addr, 1);
+		ipv4ll_found(ifp);
+	}
 #endif
+}
--- a/src/ipv4ll.h	Wed Apr 17 22:18:39 2019 +0000
+++ b/src/ipv4ll.h	Sat May 04 11:05:17 2019 +0100
@@ -43,11 +43,10 @@
 	struct in_addr pickedaddr;
 	struct ipv4_addr *addr;
 	struct arp_state *arp;
-	unsigned int conflicts;
-	struct timespec defend;
 	char randomstate[128];
 	bool seeded;
-	uint8_t down;
+	bool down;
+	size_t conflicts;
 };
 
 #define	IPV4LL_STATE(ifp)						       \
@@ -64,6 +63,7 @@
 void ipv4ll_start(void *);
 void ipv4ll_claimed(void *);
 void ipv4ll_handle_failure(void *);
+void ipv4ll_handleifa(int, struct ipv4_addr *, pid_t pid);
 #ifdef HAVE_ROUTE_METRIC
 int ipv4ll_recvrt(int, const struct rt *);
 #endif
--- a/src/ipv6.c	Wed Apr 17 22:18:39 2019 +0000
+++ b/src/ipv6.c	Sat May 04 11:05:17 2019 +0100
@@ -137,7 +137,9 @@
 		return -1;
 	TAILQ_INIT(ctx->ra_routers);
 
+#ifndef __sun
 	ctx->nd_fd = -1;
+#endif
 	ctx->dhcp6_fd = -1;
 	return 0;
 }
--- a/src/ipv6.h	Wed Apr 17 22:18:39 2019 +0000
+++ b/src/ipv6.h	Sat May 04 11:05:17 2019 +0100
@@ -44,9 +44,6 @@
 #  endif
 #endif
 
-#define	ALLNODES		"ff02::1"
-#define	ALLROUTERS		"ff02::2"
-
 #define EUI64_GBIT		0x01
 #define EUI64_UBIT		0x02
 #define EUI64_TO_IFID(in6)	do {(in6)->s6_addr[8] ^= EUI64_UBIT; } while (0)
@@ -77,6 +74,17 @@
 	(((d)->s6_addr32[3] ^ (a)->s6_addr32[3]) & (m)->s6_addr32[3]) == 0 )
 #endif
 
+#ifndef IN6ADDR_LINKLOCAL_ALLNODES_INIT
+#define	IN6ADDR_LINKLOCAL_ALLNODES_INIT				\
+	{{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	\
+	    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }}}
+#endif
+#ifndef IN6ADDR_LINKLOCAL_ALLROUTERS_INIT
+#define	IN6ADDR_LINKLOCAL_ALLROUTERS_INIT			\
+	{{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	\
+	    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }}}
+#endif
+
 /*
  * BSD kernels don't inform userland of DAD results.
  * See the discussion here:
--- a/src/ipv6nd.c	Wed Apr 17 22:18:39 2019 +0000
+++ b/src/ipv6nd.c	Sat May 04 11:05:17 2019 +0100
@@ -190,54 +190,106 @@
 }
 
 static int
-ipv6nd_open(struct dhcpcd_ctx *ctx)
+ipv6nd_open0(void)
 {
-	int on;
+	int s, on;
 	struct icmp6_filter filt;
 
-	if (ctx->nd_fd != -1)
-		return ctx->nd_fd;
 #define SOCK_FLAGS	SOCK_CLOEXEC | SOCK_NONBLOCK
-	ctx->nd_fd = xsocket(PF_INET6, SOCK_RAW | SOCK_FLAGS, IPPROTO_ICMPV6);
+	s = xsocket(PF_INET6, SOCK_RAW | SOCK_FLAGS, IPPROTO_ICMPV6);
 #undef SOCK_FLAGS
-	if (ctx->nd_fd == -1)
+	if (s == -1)
 		return -1;
 
 	/* RFC4861 4.1 */
 	on = 255;
-	if (setsockopt(ctx->nd_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+	if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
 	    &on, sizeof(on)) == -1)
 		goto eexit;
 
 	on = 1;
-	if (setsockopt(ctx->nd_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
-	    &on, sizeof(on)) == -1)
-		goto eexit;
-
-	on = 1;
-	if (setsockopt(ctx->nd_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
+	if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
 	    &on, sizeof(on)) == -1)
 		goto eexit;
 
 	ICMP6_FILTER_SETBLOCKALL(&filt);
 	ICMP6_FILTER_SETPASS(ND_NEIGHBOR_ADVERT, &filt);
 	ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt);
-	if (setsockopt(ctx->nd_fd, IPPROTO_ICMPV6, ICMP6_FILTER,
+	if (setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER,
 	    &filt, sizeof(filt)) == -1)
 		goto eexit;
 
-	eloop_event_add(ctx->eloop, ctx->nd_fd,  ipv6nd_handledata, ctx);
-	return ctx->nd_fd;
+	return s;
 
 eexit:
-	if (ctx->nd_fd != -1) {
-		eloop_event_delete(ctx->eloop, ctx->nd_fd);
-		close(ctx->nd_fd);
-		ctx->nd_fd = -1;
-	}
+	close(s);
 	return -1;
 }
 
+#ifdef __sun
+static int
+ipv6nd_open(struct interface *ifp)
+{
+	int s;
+	struct ipv6_mreq mreq = {
+	    .ipv6mr_multiaddr = IN6ADDR_LINKLOCAL_ALLNODES_INIT,
+	    .ipv6mr_interface = ifp->index
+	};
+	struct rs_state *state = RS_STATE(ifp);
+	uint_t ifindex = ifp->index;
+
+	if (state->nd_fd != -1)
+		return state->nd_fd;
+
+	s = ipv6nd_open0();
+	if (s == -1)
+		return -1;
+
+	if (setsockopt(s, IPPROTO_IPV6, IPV6_BOUND_IF,
+	    &ifindex, sizeof(ifindex)) == -1)
+	{
+		close(s);
+		return -1;
+	}
+
+	if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+	    &mreq, sizeof(mreq)) == -1)
+	{
+		close(s);
+		return -1;
+	}
+
+	state->nd_fd = s;
+	eloop_event_add(ifp->ctx->eloop, s, ipv6nd_handledata, ifp);
+	return s;
+}
+#else
+static int
+ipv6nd_open(struct dhcpcd_ctx *ctx)
+{
+	int s, on;
+
+	if (ctx->nd_fd != -1)
+		return ctx->nd_fd;
+
+	s = ipv6nd_open0();
+	if (s == -1)
+		return -1;
+
+	on = 1;
+	if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO,
+	    &on, sizeof(on)) == -1)
+	{
+		close(s);
+		return -1;
+	}
+
+	ctx->nd_fd = s;
+	eloop_event_add(ctx->eloop, s, ipv6nd_handledata, ctx);
+	return s;
+}
+#endif
+
 static int
 ipv6nd_makersprobe(struct interface *ifp)
 {
@@ -273,9 +325,12 @@
 ipv6nd_sendrsprobe(void *arg)
 {
 	struct interface *ifp = arg;
-	struct dhcpcd_ctx *ctx;
 	struct rs_state *state = RS_STATE(ifp);
-	struct sockaddr_in6 dst = { .sin6_family = AF_INET6 };
+	struct sockaddr_in6 dst = {
+		.sin6_family = AF_INET6,
+		.sin6_addr = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT,
+		.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 };
 	struct msghdr msg = {
@@ -285,6 +340,7 @@
 	};
 	struct cmsghdr *cm;
 	struct in6_pktinfo pi = { .ipi6_ifindex = ifp->index };
+	int s;
 
 	if (ipv6_linklocal(ifp) == NULL) {
 		logdebugx("%s: delaying Router Solicitation for LL address",
@@ -296,13 +352,6 @@
 #ifdef HAVE_SA_LEN
 	dst.sin6_len = sizeof(dst);
 #endif
-	dst.sin6_scope_id = ifp->index;
-	if (inet_pton(AF_INET6, ALLROUTERS, &dst.sin6_addr) != 1) {
-		logerr(__func__);
-		return;
-	}
-
-	ctx = ifp->ctx;
 
 	/* Set the outbound interface */
 	cm = CMSG_FIRSTHDR(&msg);
@@ -314,7 +363,12 @@
 	memcpy(CMSG_DATA(cm), &pi, sizeof(pi));
 
 	logdebugx("%s: sending Router Solicitation", ifp->name);
-	if (sendmsg(ctx->nd_fd, &msg, 0) == -1) {
+#ifdef __sun
+	s = state->nd_fd;
+#else
+	s = ifp->ctx->nd_fd;
+#endif
+	if (sendmsg(s, &msg, 0) == -1) {
 		logerr(__func__);
 		/* Allow IPv6ND to continue .... at most a few errors
 		 * would be logged.
@@ -342,6 +396,7 @@
 	struct dhcpcd_ctx *ctx = ifp->ctx;
 	struct sockaddr_in6 dst = {
 	    .sin6_family = AF_INET6,
+	    .sin6_addr = IN6ADDR_LINKLOCAL_ALLNODES_INIT,
 	    .sin6_scope_id = ifp->index,
 	};
 	struct iovec iov = { .iov_base = ia->na, .iov_len = ia->na_len };
@@ -354,6 +409,7 @@
 	struct cmsghdr *cm;
 	struct in6_pktinfo pi = { .ipi6_ifindex = ifp->index };
 	const struct rs_state *state = RS_CSTATE(ifp);
+	int s;
 
 	if (state == NULL || ifp->carrier <= LINK_DOWN)
 		goto freeit;
@@ -361,10 +417,6 @@
 #ifdef SIN6_LEN
 	dst.sin6_len = sizeof(dst);
 #endif
-	if (inet_pton(AF_INET6, ALLNODES, &dst.sin6_addr) != 1) {
-		logerr(__func__);
-		return;
-	}
 
 	/* Set the outbound interface. */
 	cm = CMSG_FIRSTHDR(&msg);
@@ -373,10 +425,20 @@
 	cm->cmsg_type = IPV6_PKTINFO;
 	cm->cmsg_len = CMSG_LEN(sizeof(pi));
 	memcpy(CMSG_DATA(cm), &pi, sizeof(pi));
-
 	logdebugx("%s: sending NA for %s", ifp->name, ia->saddr);
-	if (sendmsg(ctx->nd_fd, &msg, 0) == -1)
+#ifdef __sun
+	s = state->nd_fd;
+#else
+	s = ctx->nd_fd;
+#endif
+	if (sendmsg(s, &msg, 0) == -1)
+#ifdef __OpenBSD__
+/* This isn't too critical as they don't support IPv6 address sharing */
+#warning Cannot send NA messages on OpenBSD
+		logdebug(__func__);
+#else
 		logerr(__func__);
+#endif
 
 	if (++ia->na_count < MAX_NEIGHBOR_ADVERTISEMENT) {
 		eloop_timeout_add_sec(ctx->eloop,
@@ -619,6 +681,11 @@
 	if (state == NULL)
 		return 0;
 
+	ctx = ifp->ctx;
+#ifdef __sun
+	eloop_event_delete(ctx->eloop, state->nd_fd);
+	close(state->nd_fd);
+#endif
 	free(state->rs);
 	free(state);
 	ifp->if_data[IF_DATA_IPV6ND] = NULL;
@@ -630,9 +697,9 @@
 		}
 	}
 
+#ifndef __sun
 	/* If we don't have any more IPv6 enabled interfaces,
 	 * close the global socket and release resources */
-	ctx = ifp->ctx;
 	TAILQ_FOREACH(ifp, ctx->ifaces, next) {
 		if (RS_STATE(ifp))
 			break;
@@ -644,6 +711,7 @@
 			ctx->nd_fd = -1;
 		}
 	}
+#endif
 
 	return n;
 }
@@ -1654,6 +1722,7 @@
 ipv6nd_handledata(void *arg)
 {
 	struct dhcpcd_ctx *ctx;
+	int s;
 	struct sockaddr_in6 from;
 	unsigned char buf[64 * 1024]; /* Maximum ICMPv6 size */
 	struct iovec iov = {
@@ -1672,8 +1741,18 @@
 	struct icmp6_hdr *icp;
 	struct interface *ifp;
 
+#ifdef __sun
+	struct rs_state *state;
+
+	ifp = arg;
+	state = RS_STATE(ifp);
+	ctx = ifp->ctx;
+	s = state->nd_fd;
+#else
 	ctx = arg;
-	len = recvmsg(ctx->nd_fd, &msg, 0);
+	s = ctx->nd_fd;
+#endif
+	len = recvmsg(s, &msg, 0);
 	if (len == -1) {
 		logerr(__func__);
 		return;
@@ -1684,11 +1763,15 @@
 		return;
 	}
 
+#ifdef __sun
+	if_findifpfromcmsg(ctx, &msg, &hoplimit);
+#else
 	ifp = if_findifpfromcmsg(ctx, &msg, &hoplimit);
 	if (ifp == NULL) {
 		logerr(__func__);
 		return;
 	}
+#endif
 
 	/* Don't do anything if the user hasn't configured it. */
 	if (ifp->active != IF_ACTIVE_USER ||
@@ -1720,11 +1803,6 @@
 	struct rs_state *state;
 
 	loginfox("%s: soliciting an IPv6 router", ifp->name);
-	if (ipv6nd_open(ifp->ctx) == -1) {
-		logerr(__func__);
-		return;
-	}
-
 	state = RS_STATE(ifp);
 	if (state == NULL) {
 		ifp->if_data[IF_DATA_IPV6ND] = calloc(1, sizeof(*state));
@@ -1733,8 +1811,23 @@
 			logerr(__func__);
 			return;
 		}
+#ifdef __sun
+		state->nd_fd = -1;
+#endif
 	}
 
+#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);
--- a/src/ipv6nd.h	Wed Apr 17 22:18:39 2019 +0000
+++ b/src/ipv6nd.h	Sat May 04 11:05:17 2019 +0100
@@ -61,6 +61,9 @@
 	size_t rslen;
 	int rsprobes;
 	uint32_t retrans;
+#ifdef __sun
+	int nd_fd;
+#endif
 };
 
 #define	RS_STATE(a) ((struct rs_state *)(ifp)->if_data[IF_DATA_IPV6ND])