changeset 4485:9ba7f650a8f7 draft

Merge branch 'dhcpcd-7'
author Roy Marples <roy@marples.name>
date Fri, 26 Apr 2019 14:07:35 +0100
parents 0dcdae5e7954 (current diff) 0f490b4cde2a (diff)
children 6635eac6eb05
files src/dhcp.c src/ipv4.h src/ipv4ll.c
diffstat 14 files changed, 347 insertions(+), 161 deletions(-) [+]
line wrap: on
line diff
--- a/src/auth.c	Fri Apr 19 21:54:19 2019 +0100
+++ b/src/auth.c	Fri Apr 26 14:07:35 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;
--- a/src/control.c	Fri Apr 19 21:54:19 2019 +0100
+++ b/src/control.c	Fri Apr 26 14:07:35 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/dhcp.c	Fri Apr 19 21:54:19 2019 +0100
+++ b/src/dhcp.c	Fri Apr 26 14:07:35 2019 +0100
@@ -2026,7 +2026,7 @@
 	logdebugx("%s: DAD completed for %s", ifp->name, inet_ntoa(*ia));
 	if (!(ifp->options->options & DHCPCD_INFORM))
 		dhcp_bind(ifp);
-#ifndef IN_IFF_TENTATIVE
+#ifndef IN_IFF_DUPLICATED
 	else {
 		struct bootp *bootp;
 		size_t len;
@@ -2393,7 +2393,7 @@
 	/* If the interface already has the address configured
 	 * then we can't ARP for duplicate detection. */
 	ia = ipv4_iffindaddr(ifp, &addr, NULL);
-#ifdef IN_IFF_TENTATIVE
+#ifdef IN_IFF_NOTUSEABLE
 	if (ia == NULL || ia->addr_flags & IN_IFF_NOTUSEABLE) {
 		state->state = DHS_PROBE;
 		if (ia == NULL) {
--- a/src/dhcp6.c	Fri Apr 19 21:54:19 2019 +0100
+++ b/src/dhcp6.c	Fri Apr 26 14:07:35 2019 +0100
@@ -3035,7 +3035,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.conf.5.in	Fri Apr 19 21:54:19 2019 +0100
+++ b/src/dhcpcd.conf.5.in	Fri Apr 26 14:07:35 2019 +0100
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd September 15, 2018
+.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	Fri Apr 19 21:54:19 2019 +0100
+++ b/src/dhcpcd.h	Fri Apr 26 14:07:35 2019 +0100
@@ -180,7 +180,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	Fri Apr 19 21:54:19 2019 +0100
+++ b/src/if-bsd.c	Fri Apr 26 14:07:35 2019 +0100
@@ -943,10 +943,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));
@@ -956,7 +961,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;
--- a/src/if-sun.c	Fri Apr 19 21:54:19 2019 +0100
+++ b/src/if-sun.c	Fri Apr 26 14:07:35 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 {						      \
@@ -268,6 +268,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 +277,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 +321,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 +332,7 @@
 	}
 failed1:
 	lw->lw_error = errno;
-	return (B_TRUE);
+	return B_TRUE;
 }
 
 /* Creates an empty sockaddr_dl for lo0. */
@@ -423,14 +427,16 @@
 {
 	const char *cp;
 	int i;
+	const struct sockaddr **sap;
 
 	cp = data;
 	for (i = 0; i < RTAX_MAX; i++) {
+		sap = &sa[i];
 		if (type & (1 << i)) {
-			sa[i] = (const struct sockaddr *)cp;
-			RT_ADVANCE(cp, sa[i]);
+			*sap = (const struct sockaddr *)cp;
+			RT_ADVANCE(cp, *sap);
 		} else
-			sa[i] = NULL;
+			*sap = NULL;
 	}
 	return 0;
 }
@@ -520,6 +526,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 +545,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 +585,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 +600,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);
 }
@@ -747,15 +756,19 @@
 		rt->rt_mtu = 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;
 }
@@ -804,13 +817,56 @@
 	}
 }
 
+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 void
 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];
 
 	/* XXX We have no way of knowing who generated these
 	 * messages wich truely sucks because we want to
@@ -819,6 +875,7 @@
 		return;
 	sa = (const void *)(ifam + 1);
 	get_addrs(ifam->ifam_addrs, sa, rti_info);
+
 	if ((sa = rti_info[RTAX_IFA]) == NULL)
 		return;
 
@@ -835,31 +892,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;
 
 	switch (sa->sa_family) {
 	case AF_LINK:
@@ -882,12 +916,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;
+			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;
+		}
+		flags = if_addrflags(ifp, &addr, ifalias);
+		if (ifam->ifam_type == RTM_DELADDR) {
+			if (flags != -1)
+				return;
+		} else if (flags == -1)
+			return;
 
 		ipv4_handleifa(ctx,
 		    ifam->ifam_type == RTM_CHGADDR ?
@@ -907,15 +954,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;
+			strlcpy(ifalias, ia->alias, sizeof(ifalias));
+		}
+		flags = if_addrflags6(ifp, &addr6, ifalias);
+		if (ifam->ifam_type == RTM_DELADDR) {
+			if (flags != -1)
+				return;
+		} else if (flags == -1)
+			return;
 
 		ipv6_handleifa(ctx,
 		    ifam->ifam_type == RTM_CHGADDR ?
@@ -1003,7 +1055,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 +1073,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 +1233,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 +1258,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
@@ -1306,6 +1363,7 @@
 		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);
@@ -1456,8 +1514,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)
@@ -1474,24 +1534,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)
@@ -1505,9 +1565,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. */
@@ -1522,29 +1585,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/ipv4.h	Fri Apr 19 21:54:19 2019 +0100
+++ b/src/ipv4.h	Fri Apr 26 14:07:35 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
--- a/src/ipv4ll.c	Fri Apr 19 21:54:19 2019 +0100
+++ b/src/ipv4ll.c	Fri Apr 26 14:07:35 2019 +0100
@@ -398,7 +398,7 @@
 		ia = ipv4_iffindlladdr(ifp);
 
 	repick = false;
-#ifdef IN_IFF_TENTATIVE
+#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;
@@ -419,6 +419,8 @@
 			    ifp->name, inet_ntoa(ia->addr));
 			return;
 		}
+#endif
+#ifdef IN_IFF_DUPLICATED
 		loginfox("%s: using IPv4LL address %s", ifp->name, ia->saddr);
 #endif
 		ipv4ll_not_found(ifp);
@@ -431,7 +433,7 @@
 #ifndef KERNEL_RFC5227
 	astate->addr = state->pickedaddr;
 #endif
-#ifdef IN_IFF_TENTATIVE
+#ifdef IN_IFF_DUPLICATED
 	ipv4ll_not_found(ifp);
 #else
 	arp_probe(astate);
--- a/src/ipv6.c	Fri Apr 19 21:54:19 2019 +0100
+++ b/src/ipv6.c	Fri Apr 26 14:07:35 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	Fri Apr 19 21:54:19 2019 +0100
+++ b/src/ipv6.h	Fri Apr 26 14:07:35 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	Fri Apr 19 21:54:19 2019 +0100
+++ b/src/ipv6nd.c	Fri Apr 26 14:07:35 2019 +0100
@@ -160,6 +160,12 @@
 #define IPV6_RECVPKTINFO IPV6_PKTINFO
 #endif
 
+#ifdef __sun
+struct in6_addr all_nodes_mcast = { .s6_addr = {
+    0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } };
+#endif
+
 /* Handy defines */
 #define ipv6nd_free_ra(ra) ipv6nd_freedrop_ra((ra),  0)
 #define ipv6nd_drop_ra(ra) ipv6nd_freedrop_ra((ra),  1)
@@ -190,54 +196,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 = all_nodes_mcast,
+	    .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 +331,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 +346,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 +358,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 +369,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 +402,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 +415,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 +423,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 +431,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 +687,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 +703,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 +717,7 @@
 			ctx->nd_fd = -1;
 		}
 	}
+#endif
 
 	return n;
 }
@@ -1659,6 +1733,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 = {
@@ -1677,8 +1752,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;
@@ -1689,11 +1774,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 ||
@@ -1725,11 +1814,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));
@@ -1738,8 +1822,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	Fri Apr 19 21:54:19 2019 +0100
+++ b/src/ipv6nd.h	Fri Apr 26 14:07:35 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])