changeset 4442:52d9fd948974 draft

Merge branch 'master' into rbtree
author Roy Marples <roy@marples.name>
date Tue, 16 Apr 2019 20:02:36 +0100
parents 11b2ea4f8036 (current diff) 116946128ea1 (diff)
children ec7ded7718ae
files configure src/dhcp.c src/dhcp6.c src/dhcpcd.c src/if-bsd.c src/if-linux.c src/if-options.c src/if.c src/if.h src/ipv4.c src/ipv4ll.c src/ipv6.c src/route.c src/route.h
diffstat 18 files changed, 569 insertions(+), 312 deletions(-) [+]
line wrap: on
line diff
--- a/configure	Tue Apr 16 19:48:46 2019 +0100
+++ b/configure	Tue Apr 16 20:02:36 2019 +0100
@@ -455,7 +455,7 @@
 	echo "CPPFLAGS+=	-D_XPG4_2 -D__EXTENSIONS__ -DBSD_COMP" \
 	    >>$CONFIG_MK
 	echo "DHCPCD_SRCS+=	if-sun.c" >>$CONFIG_MK
-	echo "LDADD+=		-ldlpi" >>$CONFIG_MK
+	echo "LDADD+=		-ldlpi -lkstat" >>$CONFIG_MK
 	;;
 *)
 	echo "DHCPCD_SRCS+=	if-bsd.c" >>$CONFIG_MK
--- a/src/dhcp.c	Tue Apr 16 19:48:46 2019 +0100
+++ b/src/dhcp.c	Tue Apr 16 20:02:36 2019 +0100
@@ -2468,7 +2468,8 @@
 dhcp_arp_bind(struct interface *ifp)
 {
 
-	if (dhcp_arp_address(ifp) == 1)
+	if (ifp->ctx->options & DHCPCD_TEST ||
+	    dhcp_arp_address(ifp) == 1)
 		dhcp_bind(ifp);
 }
 #endif
@@ -3215,6 +3216,7 @@
 			state->reason = "TEST";
 			script_runreason(ifp, state->reason);
 			eloop_exit(ifp->ctx->eloop, EXIT_SUCCESS);
+			state->bpf_flags |= BPF_EOF;
 			return;
 		}
 		eloop_timeout_delete(ifp->ctx->eloop, send_discover, ifp);
--- a/src/dhcp6.c	Tue Apr 16 19:48:46 2019 +0100
+++ b/src/dhcp6.c	Tue Apr 16 20:02:36 2019 +0100
@@ -797,8 +797,7 @@
 		m = state->new;
 		ml = state->new_len;
 	}
-	unicast = NULL;
-	/* Depending on state, get the unicast address */
+
 	switch(state->state) {
 	case DH6S_INIT: /* FALLTHROUGH */
 	case DH6S_DISCOVER:
@@ -806,7 +805,6 @@
 		break;
 	case DH6S_REQUEST:
 		type = DHCP6_REQUEST;
-		unicast = dhcp6_findmoption(m, ml, D6_OPTION_UNICAST, &uni_len);
 		break;
 	case DH6S_CONFIRM:
 		type = DHCP6_CONFIRM;
@@ -816,20 +814,33 @@
 		break;
 	case DH6S_RENEW:
 		type = DHCP6_RENEW;
-		unicast = dhcp6_findmoption(m, ml, D6_OPTION_UNICAST, &uni_len);
 		break;
 	case DH6S_INFORM:
 		type = DHCP6_INFORMATION_REQ;
 		break;
 	case DH6S_RELEASE:
 		type = DHCP6_RELEASE;
-		unicast = dhcp6_findmoption(m, ml, D6_OPTION_UNICAST, &uni_len);
 		break;
 	default:
 		errno = EINVAL;
 		return -1;
 	}
 
+	switch(state->state) {
+	case DH6S_REQUEST: /* FALLTHROUGH */
+	case DH6S_RENEW:   /* FALLTHROUGH */
+	case DH6S_RELEASE:
+		if (has_option_mask(ifo->nomask6, D6_OPTION_UNICAST)) {
+			unicast = NULL;
+			break;
+		}
+		unicast = dhcp6_findmoption(m, ml, D6_OPTION_UNICAST, &uni_len);
+		break;
+	default:
+		unicast = NULL;
+		break;
+	}
+
 	/* In non master mode we listen and send from fixed addresses.
 	 * We should try and match an address we have to unicast to,
 	 * but for now this is the safest policy. */
@@ -1902,13 +1913,16 @@
 dhcp6_checkstatusok(const struct interface *ifp,
     struct dhcp6_message *m, uint8_t *p, size_t len)
 {
+	struct dhcp6_state *state;
 	uint8_t *opt;
 	uint16_t opt_len, code;
 	size_t mlen;
 	void * (*f)(void *, size_t, uint16_t, uint16_t *), *farg;
 	char buf[32], *sbuf;
 	const char *status;
-
+	logfunc_t *logfunc;
+
+	state = D6_STATE(ifp);
 	f = p ? dhcp6_findoption : dhcp6_findmoption;
 	if (p)
 		farg = p;
@@ -1916,6 +1930,7 @@
 		farg = m;
 	if ((opt = f(farg, len, D6_OPTION_STATUS_CODE, &opt_len)) == NULL) {
 		//logdebugx("%s: no status", ifp->name);
+		state->lerror = 0;
 		return 0;
 	}
 
@@ -1925,8 +1940,10 @@
 	}
 	memcpy(&code, opt, sizeof(code));
 	code = ntohs(code);
-	if (code == D6_STATUS_OK)
+	if (code == D6_STATUS_OK) {
+		state->lerror = 0;
 		return 1;
+	}
 
 	/* Anything after the code is a message. */
 	opt += sizeof(code);
@@ -1949,8 +1966,13 @@
 		status = sbuf;
 	}
 
-	logerrx("%s: DHCPv6 REPLY: %s", ifp->name, status);
+	if (state->lerror == code || state->state == DH6S_INIT)
+		logfunc = logdebugx;
+	else
+		logfunc = logerrx;
+	logfunc("%s: DHCPv6 REPLY: %s", ifp->name, status);
 	free(sbuf);
+	state->lerror = code;
 	return -1;
 }
 
@@ -3766,6 +3788,7 @@
 
 gogogo:
 	state->state = init_state;
+	state->lerror = 0;
 	dhcp_set_leasefile(state->leasefile, sizeof(state->leasefile),
 	    AF_INET6, ifp);
 	if (ipv6_linklocal(ifp) == NULL) {
@@ -3785,18 +3808,20 @@
 	struct dhcp6_state *state;
 
 	state = D6_STATE(ifp);
-	if (state) {
-		switch (state->state) {
-		case DH6S_BOUND:
-			dhcp6_startrebind(ifp);
-			break;
-		case DH6S_INFORMED:
-			dhcp6_startinform(ifp);
-			break;
-		default:
-			dhcp6_startdiscover(ifp);
-			break;
-		}
+	if (state == NULL)
+		return;
+
+	state->lerror = 0;
+	switch (state->state) {
+	case DH6S_BOUND:
+		dhcp6_startrebind(ifp);
+		break;
+	case DH6S_INFORMED:
+		dhcp6_startinform(ifp);
+		break;
+	default:
+		dhcp6_startdiscover(ifp);
+		break;
 	}
 }
 
--- a/src/dhcp6.h	Tue Apr 16 19:48:46 2019 +0100
+++ b/src/dhcp6.h	Tue Apr 16 20:02:36 2019 +0100
@@ -206,7 +206,7 @@
 	/* The +3 is for the possible .pd extension for prefix delegation */
 	char leasefile[sizeof(LEASEFILE6) + IF_NAMESIZE + (IF_SSIDLEN * 4) +3];
 	const char *reason;
-
+	uint16_t lerror; /* Last error received from DHCPv6 reply. */
 	struct authstate auth;
 };
 
--- a/src/dhcpcd.c	Tue Apr 16 19:48:46 2019 +0100
+++ b/src/dhcpcd.c	Tue Apr 16 20:02:36 2019 +0100
@@ -979,7 +979,12 @@
 
 	if ((!(ifp->ctx->options & DHCPCD_MASTER) ||
 	    ifp->options->options & DHCPCD_IF_UP) &&
-	    if_up(ifp) == -1)
+	    if_up(ifp) == -1
+#ifdef __sun
+	    /* Interface could not yet be plumbed. */
+	    && errno != ENXIO
+#endif
+	    )
 		logerr("%s: %s", __func__, ifp->name);
 
 	if (ifp->options->options & DHCPCD_LINK &&
--- a/src/duid.c	Tue Apr 16 19:48:46 2019 +0100
+++ b/src/duid.c	Tue Apr 16 20:02:36 2019 +0100
@@ -90,6 +90,7 @@
 	fclose(fp);
 	r = len == 1 ? -1 : 0;
 #else
+	UNUSED(uuid);
 	r = -1;
 	errno = ENOSYS;
 #endif
--- a/src/if-bsd.c	Tue Apr 16 19:48:46 2019 +0100
+++ b/src/if-bsd.c	Tue Apr 16 20:02:36 2019 +0100
@@ -99,14 +99,20 @@
 #endif
 
 #ifdef INET6
-static void
-ifa_scope(struct sockaddr_in6 *, unsigned int);
+static void ifa_setscope(struct sockaddr_in6 *, unsigned int);
+static unsigned int ifa_getscope(const struct sockaddr_in6 *);
 #endif
 
 struct priv {
 	int pf_inet6_fd;
 };
 
+struct rtm
+{
+	struct rt_msghdr hdr;
+	char buffer[sizeof(struct sockaddr_storage) * RTAX_MAX];
+};
+
 int
 if_init(__unused struct interface *iface)
 {
@@ -418,9 +424,13 @@
 	case AF_INET6:
 	{
 		const struct sockaddr_in6 *sin;
+		unsigned int scope;
 		struct ipv6_addr *ia;
 
 		sin = (const void *)sa;
+		scope = ifa_getscope(sin);
+		if (scope != 0)
+			return if_findindex(ctx->ifaces, scope);
 		if ((ia = ipv6_findmaskaddr(ctx, &sin->sin6_addr)))
 			return ia->iface;
 		break;
@@ -458,11 +468,7 @@
 if_route(unsigned char cmd, const struct rt *rt)
 {
 	struct dhcpcd_ctx *ctx;
-	struct rtm
-	{
-		struct rt_msghdr hdr;
-		char buffer[sizeof(struct sockaddr_storage) * RTAX_MAX];
-	} rtmsg;
+	struct rtm rtmsg;
 	struct rt_msghdr *rtm = &rtmsg.hdr;
 	char *bp = rtmsg.buffer;
 	struct sockaddr_dl sdl;
@@ -579,7 +585,7 @@
 			if_copysa(&gateway.sa, &rt->rt_gateway);
 #ifdef INET6
 			if (gateway.sa.sa_family == AF_INET6)
-				ifa_scope(&gateway.sin6, rt->rt_ifp->index);
+				ifa_setscope(&gateway.sin6, rt->rt_ifp->index);
 #endif
 			ADDSA(&gateway.sa);
 		}
@@ -607,19 +613,27 @@
 {
 	const struct sockaddr *rti_info[RTAX_MAX];
 
-	if (~rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY))
+	if (~rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY)) {
+		errno = EINVAL;
 		return -1;
+	}
 #ifdef RTF_CLONED
-	if (rtm->rtm_flags & RTF_CLONED)
+	if (rtm->rtm_flags & RTF_CLONED) {
+		errno = ENOTSUP;
 		return -1;
+	}
 #endif
 #ifdef RTF_LOCAL
-	if (rtm->rtm_flags & RTF_LOCAL)
+	if (rtm->rtm_flags & RTF_LOCAL) {
+		errno = ENOTSUP;
 		return -1;
+	}
 #endif
 #ifdef RTF_BROADCAST
-	if (rtm->rtm_flags & RTF_BROADCAST)
+	if (rtm->rtm_flags & RTF_BROADCAST) {
+		errno = ENOTSUP;
 		return -1;
+	}
 #endif
 
 	get_addrs(rtm->rtm_addrs, rtm + 1, rti_info);
@@ -756,7 +770,7 @@
 
 #ifdef INET6
 static void
-ifa_scope(struct sockaddr_in6 *sin, unsigned int ifindex)
+ifa_setscope(struct sockaddr_in6 *sin, unsigned int ifindex)
 {
 
 #ifdef __KAME__
@@ -776,6 +790,23 @@
 #endif
 }
 
+static unsigned int
+ifa_getscope(const struct sockaddr_in6 *sin)
+{
+#ifdef __KAME__
+	uint16_t scope;
+#endif
+
+	if (!IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr))
+		return 0;
+#ifdef __KAME__
+	memcpy(&scope, &sin->sin6_addr.s6_addr[2], sizeof(scope));
+	return (unsigned int)ntohs(scope);
+#else
+	return (unsigned int)sin->sin6_scope_id;
+#endif
+}
+
 int
 if_address6(unsigned char cmd, const struct ipv6_addr *ia)
 {
@@ -815,7 +846,7 @@
 	}
 
 	ADDADDR(&ifa.ifra_addr, &ia->addr);
-	ifa_scope(&ifa.ifra_addr, ia->iface->index);
+	ifa_setscope(&ifa.ifra_addr, ia->iface->index);
 	ipv6_mask(&mask, ia->prefix_len);
 	ADDADDR(&ifa.ifra_prefixmask, &mask);
 
@@ -892,7 +923,7 @@
 	strlcpy(ifr6.ifr_name, ifp->name, sizeof(ifr6.ifr_name));
 	ifr6.ifr_addr.sin6_family = AF_INET6;
 	ifr6.ifr_addr.sin6_addr = *addr;
-	ifa_scope(&ifr6.ifr_addr, ifp->index);
+	ifa_setscope(&ifr6.ifr_addr, ifp->index);
 	priv = (struct priv *)ifp->ctx->priv;
 	if (ioctl(priv->pf_inet6_fd, SIOCGIFAFLAG_IN6, &ifr6) != -1)
 		flags = ifr6.ifr_ifru.ifru_flags6;
@@ -913,7 +944,7 @@
 	strlcpy(ifr6.ifr_name, ia->iface->name, sizeof(ifr6.ifr_name));
 	ifr6.ifr_addr.sin6_family = AF_INET6;
 	ifr6.ifr_addr.sin6_addr = ia->addr;
-	ifa_scope(&ifr6.ifr_addr, ia->iface->index);
+	ifa_setscope(&ifr6.ifr_addr, ia->iface->index);
 	priv = (struct priv *)ia->iface->ctx->priv;
 	if (ioctl(priv->pf_inet6_fd, SIOCGIFALIFETIME_IN6, &ifr6) == -1)
 		return -1;
@@ -1015,7 +1046,7 @@
 	}
 #endif
 
-	rt_recvrt(rtm->rtm_type, &rt);
+	rt_recvrt(rtm->rtm_type, &rt, rtm->rtm_pid);
 }
 
 static void
@@ -1234,11 +1265,8 @@
 int
 if_handlelink(struct dhcpcd_ctx *ctx)
 {
-	union {
-		struct rt_msghdr rtmsg;
-		unsigned char buf[2048];
-	} u;
-	struct iovec iov = { .iov_base = u.buf, .iov_len = sizeof(u) };
+	struct rtm rtm;
+	struct iovec iov = { .iov_base = &rtm, .iov_len = sizeof(rtm) };
 	struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1 };
 	ssize_t len;
 
@@ -1246,7 +1274,7 @@
 	if (len == -1)
 		return -1;
 	if (len != 0)
-		if_dispatch(ctx, &u.rtmsg);
+		if_dispatch(ctx, &rtm.hdr);
 	return 0;
 }
 
--- a/src/if-linux.c	Tue Apr 16 19:48:46 2019 +0100
+++ b/src/if-linux.c	Tue Apr 16 20:02:36 2019 +0100
@@ -550,7 +550,7 @@
 		return 0;
 
 	if (if_copyrt(ctx, &rt, nlm) == 0)
-		rt_recvrt(cmd, &rt);
+		rt_recvrt(cmd, &rt, (pid_t)nlm->nlmsg_pid);
 
 	return 0;
 }
--- a/src/if-options.c	Tue Apr 16 19:48:46 2019 +0100
+++ b/src/if-options.c	Tue Apr 16 20:02:36 2019 +0100
@@ -208,11 +208,12 @@
 	{NULL,              0,                 NULL, '\0'}
 };
 
+static const char *default_script = SCRIPT;
+
 static char *
-add_environ(struct if_options *ifo, const char *value, int uniq)
+add_environ(char ***array, const char *value, int uniq)
 {
-	char **newlist;
-	char **lst = ifo->environ;
+	char **newlist, **list = *array;
 	size_t i = 0, l, lv;
 	char *match = NULL, *p, *n;
 
@@ -230,8 +231,8 @@
 	*p++ = '\0';
 	l = strlen(match);
 
-	while (lst && lst[i]) {
-		if (match && strncmp(lst[i], match, l) == 0) {
+	while (list && list[i]) {
+		if (match && strncmp(list[i], match, l) == 0) {
 			if (uniq) {
 				n = strdup(value);
 				if (n == NULL) {
@@ -239,25 +240,25 @@
 					free(match);
 					return NULL;
 				}
-				free(lst[i]);
-				lst[i] = n;
+				free(list[i]);
+				list[i] = n;
 			} else {
 				/* Append a space and the value to it */
-				l = strlen(lst[i]);
+				l = strlen(list[i]);
 				lv = strlen(p);
-				n = realloc(lst[i], l + lv + 2);
+				n = realloc(list[i], l + lv + 2);
 				if (n == NULL) {
 					logerr(__func__);
 					free(match);
 					return NULL;
 				}
-				lst[i] = n;
-				lst[i][l] = ' ';
-				memcpy(lst[i] + l + 1, p, lv);
-				lst[i][l + lv + 1] = '\0';
+				list[i] = n;
+				list[i][l] = ' ';
+				memcpy(list[i] + l + 1, p, lv);
+				list[i][l + lv + 1] = '\0';
 			}
 			free(match);
-			return lst[i];
+			return list[i];
 		}
 		i++;
 	}
@@ -268,7 +269,7 @@
 		logerr(__func__);
 		return NULL;
 	}
-	newlist = reallocarray(lst, i + 2, sizeof(char *));
+	newlist = reallocarray(list, i + 2, sizeof(char *));
 	if (newlist == NULL) {
 		logerr(__func__);
 		free(n);
@@ -276,26 +277,31 @@
 	}
 	newlist[i] = n;
 	newlist[i + 1] = NULL;
-	ifo->environ = newlist;
+	*array = newlist;
 	return newlist[i];
 }
 
-#define parse_string(buf, len, arg) parse_string_hwaddr(buf, len, arg, 0)
+#define PARSE_STRING		0
+#define PARSE_STRING_NULL	1
+#define PARSE_HWADDR		2
+#define parse_string(a, b, c) parse_str((a), (b), (c), PARSE_STRING)
+#define parse_hwaddr(a, b, c) parse_str((a), (b), (c), PARSE_HWADDR)
 static ssize_t
-parse_string_hwaddr(char *sbuf, size_t slen, const char *str, int clid)
+parse_str(char *sbuf, size_t slen, const char *str, int flags)
 {
 	size_t l;
-	const char *p;
-	int i, punt_last = 0;
+	const char *p, *end;
+	int i;
 	char c[4], cmd;
 
+	end = str + strlen(str);
 	/* If surrounded by quotes then it's a string */
 	if (*str == '"') {
-		str++;
-		l = strlen(str);
-		p = str + l - 1;
-		if (*p == '"')
-			punt_last = 1;
+		p = end - 1;
+		if (*p == '"') {
+			str++;
+			end = p;
+		}
 	} else {
 		l = (size_t)hwaddr_aton(NULL, str);
 		if ((ssize_t) l != -1 && l > 1) {
@@ -312,13 +318,13 @@
 	l = 0;
 	/* If processing a string on the clientid, first byte should be
 	 * 0 to indicate a non hardware type */
-	if (clid && *str) {
+	if (flags == PARSE_HWADDR && *str) {
 		if (sbuf)
 			*sbuf++ = 0;
 		l++;
 	}
 	c[3] = '\0';
-	while (*str) {
+	while (str < end) {
 		if (++l > slen && sbuf) {
 			errno = ENOBUFS;
 			return -1;
@@ -386,11 +392,8 @@
 			str++;
 		}
 	}
-	if (punt_last) {
-		if (sbuf)
-			*--sbuf = '\0';
-		l--;
-	}
+	if (flags == PARSE_STRING_NULL && sbuf)
+		*sbuf = '\0';
 	return (ssize_t)l;
 }
 
@@ -655,7 +658,7 @@
 	int e, i, t;
 	long l;
 	unsigned long u;
-	char *p = NULL, *bp, *fp, *np, **nconf;
+	char *p = NULL, *bp, *fp, *np;
 	ssize_t s;
 	struct in_addr addr, addr2;
 	in_addr_t *naddr;
@@ -709,17 +712,33 @@
 		break;
 	case 'c':
 		ARG_REQUIRED;
-		free(ifo->script);
-		ifo->script = strdup(arg);
-		if (ifo->script == NULL)
+		if (ifo->script != default_script)
+			free(ifo->script);
+		s = parse_str(NULL, 0, arg, PARSE_STRING_NULL);
+		if (s == 0) {
+			ifo->script = NULL;
+			break;
+		}
+		dl = (size_t)s;
+		if (s == -1 || (ifo->script = malloc(dl)) == NULL) {
+			ifo->script = NULL;
 			logerr(__func__);
+			return -1;
+		}
+		parse_str(ifo->script, dl, arg, PARSE_STRING_NULL);
+		if (ifo->script[0] == '\0' ||
+		    strcmp(ifo->script, "/dev/null") == 0)
+		{
+			free(ifo->script);
+			ifo->script = NULL;
+		}
 		break;
 	case 'd':
 		ifo->options |= DHCPCD_DEBUG;
 		break;
 	case 'e':
 		ARG_REQUIRED;
-		add_environ(ifo, arg, 1);
+		add_environ(&ifo->environ, arg, 1);
 		break;
 	case 'h':
 		if (!arg) {
@@ -970,7 +989,7 @@
 			return -1;
 		}
 		snprintf(p, dl, "skip_hooks=%s", arg);
-		add_environ(ifo, p, 0);
+		add_environ(&ifo->environ, p, 0);
 		free(p);
 		break;
 	case 'D':
@@ -1007,8 +1026,8 @@
 		/* Strings have a type of 0 */;
 		ifo->clientid[1] = 0;
 		if (arg)
-			s = parse_string_hwaddr((char *)ifo->clientid + 1,
-			    CLIENTID_MAX_LEN, arg, 1);
+			s = parse_hwaddr((char *)ifo->clientid + 1,
+			    CLIENTID_MAX_LEN, arg);
 		else
 			s = 0;
 		if (s == -1) {
@@ -1104,16 +1123,16 @@
 				*fp = ' ';
 				return -1;
 			}
-			if ((rt = rt_new0(ctx)) == NULL) {
-				*fp = ' ';
+			*fp = ' ';
+			if ((rt = rt_new0(ctx)) == NULL)
 				return -1;
-			}
 			sa_in_init(&rt->rt_dest, &addr);
 			sa_in_init(&rt->rt_netmask, &addr2);
 			sa_in_init(&rt->rt_gateway, &addr3);
 			if (rb_tree_insert_node(&ifo->routes, rt) != rt)
 				rt_free(rt);
-			*fp = ' ';
+			else
+				add_environ(&ifo->config, arg, 0);
 		} else if (strncmp(arg, "routers=", strlen("routers=")) == 0) {
 			if (parse_addr(&addr, NULL, p) == -1)
 				return -1;
@@ -1125,6 +1144,8 @@
 			sa_in_init(&rt->rt_gateway, &addr);
 			if (rb_tree_insert_node(&ifo->routes, rt) != rt)
 				rt_free(rt);
+			else
+				add_environ(&ifo->config, arg, 0);
 		} else if (strncmp(arg, "interface_mtu=",
 		    strlen("interface_mtu=")) == 0 ||
 		    strncmp(arg, "mtu=", strlen("mtu=")) == 0)
@@ -1152,40 +1173,8 @@
 				} else
 					ifo->req_prefix_len = 128;
 			}
-		} else {
-			dl = 0;
-			if (ifo->config != NULL) {
-				while (ifo->config[dl] != NULL) {
-					if (strncmp(ifo->config[dl], arg,
-						(size_t)(p - arg)) == 0)
-					{
-						p = strdup(arg);
-						if (p == NULL) {
-							logerr(__func__);
-							return -1;
-						}
-						free(ifo->config[dl]);
-						ifo->config[dl] = p;
-						return 1;
-					}
-					dl++;
-				}
-			}
-			p = strdup(arg);
-			if (p == NULL) {
-				logerr(__func__);
-				return -1;
-			}
-			nconf = reallocarray(ifo->config, dl+2, sizeof(char *));
-			if (nconf == NULL) {
-				logerr(__func__);
-				free(p);
-				return -1;
-			}
-			ifo->config = nconf;
-			ifo->config[dl] = p;
-			ifo->config[dl + 1] = NULL;
-		}
+		} else
+			add_environ(&ifo->config, arg, 1);
 		break;
 	case 'W':
 		if (parse_addr(&addr, &addr2, arg) != 0)
@@ -2290,6 +2279,7 @@
 	ifo->options |= DHCPCD_IF_UP | DHCPCD_LINK | DHCPCD_INITIAL_DELAY;
 	ifo->timeout = DEFAULT_TIMEOUT;
 	ifo->reboot = DEFAULT_REBOOT;
+	ifo->script = UNCONST(default_script);
 	ifo->metric = -1;
 	ifo->auth.options |= DHCPCD_AUTH_REQUIRE;
 	rb_tree_init(&ifo->routes, &rt_compare_list_ops);
@@ -2639,7 +2629,8 @@
 			free(ifo->config);
 		}
 		rt_headclear0(ctx, &ifo->routes, AF_UNSPEC);
-		free(ifo->script);
+		if (ifo->script != default_script)
+			free(ifo->script);
 		free(ifo->arping);
 		free(ifo->blacklist);
 		free(ifo->fallback);
--- a/src/if-sun.c	Tue Apr 16 19:48:46 2019 +0100
+++ b/src/if-sun.c	Tue Apr 16 20:02:36 2019 +0100
@@ -30,6 +30,7 @@
 #include <fcntl.h>
 #include <ifaddrs.h>
 #include <libdlpi.h>
+#include <kstat.h>
 #include <stddef.h>
 #include <stdlib.h>
 #include <stropts.h>
@@ -45,6 +46,7 @@
 #include <netinet/udp.h>
 
 #include <sys/ioctl.h>
+#include <sys/mac.h>
 #include <sys/pfmod.h>
 #include <sys/tihdr.h>
 #include <sys/utsname.h>
@@ -63,6 +65,7 @@
 #include "ipv4.h"
 #include "ipv6.h"
 #include "ipv6nd.h"
+#include "logerr.h"
 #include "route.h"
 #include "sa.h"
 
@@ -96,6 +99,12 @@
 #endif
 };
 
+struct rtm
+{
+	struct rt_msghdr hdr;
+	char buffer[sizeof(struct sockaddr_storage) * RTAX_MAX];
+};
+
 int
 if_init(__unused struct interface *ifp)
 {
@@ -162,6 +171,63 @@
 }
 
 int
+if_carrier_os(struct interface *ifp)
+{
+	kstat_ctl_t		*kcp;
+	kstat_t			*ksp;
+	kstat_named_t		*knp;
+	link_state_t		linkstate;
+
+	kcp = kstat_open();
+	if (kcp == NULL)
+		goto err;
+	ksp = kstat_lookup(kcp, UNCONST("link"), 0, ifp->name);
+	if (ksp == NULL)
+		goto err;
+	if (kstat_read(kcp, ksp, NULL) == -1)
+		goto err;
+	knp = kstat_data_lookup(ksp, UNCONST("link_state"));
+	if (knp == NULL)
+		goto err;
+	if (knp->data_type != KSTAT_DATA_UINT32)
+		goto err;
+	linkstate = (link_state_t)knp->value.ui32;
+	kstat_close(kcp);
+
+	switch (linkstate) {
+	case LINK_STATE_UP:
+		ifp->flags |= IFF_UP;
+		return LINK_UP;
+	case LINK_STATE_DOWN:
+		return LINK_DOWN;
+	default:
+		return LINK_UNKNOWN;
+	}
+
+err:
+	if (kcp != NULL)
+		kstat_close(kcp);
+	return LINK_UNKNOWN;
+}
+
+int
+if_mtu_os(const struct interface *ifp)
+{
+	dlpi_handle_t		dh;
+	dlpi_info_t		dlinfo;
+	int			mtu;
+
+	if (dlpi_open(ifp->name, &dh, 0) != DLPI_SUCCESS)
+		return -1;
+	if (dlpi_info(dh, &dlinfo, 0) == DLPI_SUCCESS)
+		mtu = dlinfo.di_max_sdu;
+	else
+		mtu = -1;
+	dlpi_close(dh);
+	return mtu;
+}
+
+int
 if_getssid(struct interface *ifp)
 {
 
@@ -448,46 +514,105 @@
 }
 
 static void
-if_finishrt(struct rt *rt)
+if_route0(struct dhcpcd_ctx *ctx, struct rtm *rtmsg,
+    unsigned char cmd, const struct rt *rt)
 {
+	struct rt_msghdr *rtm;
+	char *bp = rtmsg->buffer;
+	socklen_t sl;
+
+	/* WARNING: Solaris will not allow you to delete RTF_KERNEL routes.
+	 * This includes subnet/prefix routes. */
 
-	/* Solaris has a subnet route with the gateway
-	 * of the owning address.
-	 * dhcpcd has a blank gateway here to indicate a
-	 * subnet route. */
-	if (!sa_is_unspecified(&rt->rt_gateway)) {
-		switch(rt->rt_gateway.sa_family) {
-#ifdef INET
-		case AF_INET:
-		{
-			struct in_addr *in;
+#define ADDSA(sa) do {							\
+		sl = salen((sa));					\
+		memcpy(bp, (sa), sl);					\
+		bp += RT_ROUNDUP(sl);					\
+	} while (/* CONSTCOND */ 0)
+
+	memset(rtmsg, 0, sizeof(*rtmsg));
+	rtm = &rtmsg->hdr;
+	rtm->rtm_version = RTM_VERSION;
+	rtm->rtm_type = cmd;
+	rtm->rtm_seq = ++ctx->seq;
+	rtm->rtm_flags = rt->rt_flags;
+	rtm->rtm_addrs = RTA_DST | RTA_GATEWAY;
+
+	if (cmd == RTM_ADD || cmd == RTM_CHANGE) {
+		bool netmask_bcast = sa_is_allones(&rt->rt_netmask);
 
-			in = &satosin(&rt->rt_gateway)->sin_addr;
-			if (ipv4_iffindaddr(rt->rt_ifp, in, NULL))
-				in->s_addr = INADDR_ANY;
-			break;
-		}
-#endif
-#ifdef INET6
-		case AF_INET6:
+		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))
 		{
-			struct in6_addr *in6;
+			rtm->rtm_addrs |= RTA_IFP;
+#if 0
+			if (!sa_is_unspecified(&rt->rt_ifa))
+				rtm->rtm_addrs |= RTA_IFA;
+#endif
+		}
 
-			in6 = &satosin6(&rt->rt_gateway)->sin6_addr;
-			if (ipv6_iffindaddr(rt->rt_ifp, in6, 0))
-				*in6 = in6addr_any;
-			break;
-		}
-#endif
+		if (netmask_bcast)
+			rtm->rtm_flags |= RTF_HOST;
+		else
+			rtm->rtm_flags |= RTF_GATEWAY;
+
+		/* Emulate the kernel by marking address generated
+		 * network routes non-static. */
+		if (!(rt->rt_dflags & RTDF_IFA_ROUTE))
+			rtm->rtm_flags |= RTF_STATIC;
+
+		if (rt->rt_mtu != 0) {
+			rtm->rtm_inits |= RTV_MTU;
+			rtm->rtm_rmx.rmx_mtu = rt->rt_mtu;
 		}
 	}
 
-	/* Solaris likes to set route MTU to match
-	 * interface MTU when adding routes.
-	 * This confuses dhcpcd as it expects MTU to be 0
-	 * when no explicit MTU has been set. */
-	if (rt->rt_mtu == (unsigned int)if_getmtu(rt->rt_ifp))
-		rt->rt_mtu = 0;
+	if (!(rtm->rtm_flags & RTF_HOST))
+		rtm->rtm_addrs |= RTA_NETMASK;
+
+	ADDSA(&rt->rt_dest);
+
+	if (sa_is_unspecified(&rt->rt_gateway))
+		ADDSA(&rt->rt_ifa);
+	else
+		ADDSA(&rt->rt_gateway);
+
+	if (rtm->rtm_addrs & RTA_NETMASK)
+		ADDSA(&rt->rt_netmask);
+
+	if (rtm->rtm_addrs & RTA_IFP) {
+		struct sockaddr_dl sdl;
+
+		if_linkaddr(&sdl, rt->rt_ifp);
+		ADDSA((struct sockaddr *)&sdl);
+	}
+
+	if (rtm->rtm_addrs & RTA_IFA) {
+		ADDSA(&rt->rt_ifa);
+		rtm->rtm_addrs |= RTA_SRC;
+	}
+	if (rtm->rtm_addrs & RTA_SRC)
+		ADDSA(&rt->rt_ifa);
+
+#undef ADDSA
+
+	rtm->rtm_msglen = (unsigned short)(bp - (char *)rtm);
+}
+
+int
+if_route(unsigned char cmd, const struct rt *rt)
+{
+	struct rtm rtm;
+	struct dhcpcd_ctx *ctx = rt->rt_ifp->ctx;
+
+	if_route0(ctx, &rtm, cmd, rt);
+
+	if (write(ctx->link_fd, &rtm, rtm.hdr.rtm_msglen) == -1)
+		return -1;
+	return 0;
 }
 
 static int
@@ -509,7 +634,8 @@
 	if (rt->rt_flags & RTF_GATEWAY &&
 	    rti_info[RTAX_GATEWAY]->sa_family != AF_LINK)
 		COPYSA(&rt->rt_gateway, rti_info[RTAX_GATEWAY]);
-	COPYSA(&rt->rt_ifa, rti_info[RTAX_SRC]);
+	if (rtm->rtm_addrs & RTA_SRC)
+		COPYSA(&rt->rt_ifa, rti_info[RTAX_SRC]);
 	rt->rt_mtu = (unsigned int)rtm->rtm_rmx.rmx_mtu;
 
 	if (rtm->rtm_index)
@@ -529,6 +655,98 @@
 	return 0;
 }
 
+static struct rt *
+if_route_get(struct dhcpcd_ctx *ctx, struct rt *rt)
+{
+	struct rtm rtm;
+	int s;
+	struct iovec iov = { .iov_base = &rtm, .iov_len = sizeof(rtm) };
+	struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1 };
+	ssize_t len;
+	struct rt *rtw = rt;
+
+	if_route0(ctx, &rtm, RTM_GET, rt);
+	rt = NULL;
+	s = socket(PF_ROUTE, SOCK_RAW | SOCK_CLOEXEC, 0);
+	if (s == -1)
+		return NULL;
+	if (write(s, &rtm, rtm.hdr.rtm_msglen) == -1)
+		goto out;
+	if ((len = recvmsg(s, &msg, 0)) == -1)
+		goto out;
+	if ((size_t)len < sizeof(rtm.hdr) || len < rtm.hdr.rtm_msglen) {
+		errno = EINVAL;
+		goto out;
+	}
+	if (if_copyrt(ctx, rtw, &rtm.hdr) == -1)
+		goto out;
+	rt = rtw;
+
+out:
+	close(s);
+	return rt;
+}
+
+static void
+if_finishrt(struct dhcpcd_ctx *ctx, struct rt *rt)
+{
+	int mtu;
+
+	/* Solaris has a subnet route with the gateway
+	 * of the owning address.
+	 * dhcpcd has a blank gateway here to indicate a
+	 * subnet route. */
+	if (!sa_is_unspecified(&rt->rt_dest) &&
+	    !sa_is_unspecified(&rt->rt_gateway))
+	{
+		switch(rt->rt_gateway.sa_family) {
+#ifdef INET
+		case AF_INET:
+		{
+			struct in_addr *in;
+
+			in = &satosin(&rt->rt_gateway)->sin_addr;
+			if (ipv4_findaddr(ctx, in))
+				in->s_addr = INADDR_ANY;
+			break;
+		}
+#endif
+#ifdef INET6
+		case AF_INET6:
+		{
+			struct in6_addr *in6;
+
+			in6 = &satosin6(&rt->rt_gateway)->sin6_addr;
+			if (ipv6_findaddr(ctx, in6, 0))
+				*in6 = in6addr_any;
+			break;
+		}
+#endif
+		}
+	}
+
+	/* Solaris doesn't set interfaces for some routes.
+	 * This sucks, so we need to call RTM_GET to
+	 * work out the interface. */
+	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;
+			}
+		}
+	}
+
+	/* Solaris likes to set route MTU to match
+	 * interface MTU when adding routes.
+	 * This confuses dhcpcd as it expects MTU to be 0
+	 * when no explicit MTU has been set. */
+	mtu = if_getmtu(rt->rt_ifp);
+	if (rt->rt_mtu == (unsigned int)mtu)
+		rt->rt_mtu = 0;
+}
+
 static int
 if_addrflags0(int fd, const char *ifname)
 {
@@ -581,8 +799,8 @@
 #endif
 
 	if (if_copyrt(ctx, &rt, rtm) == 0) {
-		if_finishrt(&rt);
-		rt_recvrt(rtm->rtm_type, &rt);
+		if_finishrt(ctx, &rt);
+		rt_recvrt(rtm->rtm_type, &rt, rtm->rtm_pid);
 	}
 }
 
@@ -714,15 +932,18 @@
 {
 	struct interface *ifp;
 	int state;
+	unsigned int flags;
 
 	if ((ifp = if_findindex(ctx->ifaces, ifm->ifm_index)) == NULL)
 		return;
-	if (ifm->ifm_flags & IFF_OFFLINE || !(ifm->ifm_flags & IFF_UP))
+	flags = (unsigned int)ifm->ifm_flags;
+	if (ifm->ifm_flags & IFF_OFFLINE)
 		state = LINK_DOWN;
-	else
+	else {
 		state = LINK_UP;
-	dhcpcd_handlecarrier(ctx, state,
-	    (unsigned int)ifm->ifm_flags, ifp->name);
+		flags |= IFF_UP;
+	}
+	dhcpcd_handlecarrier(ctx, state, flags, ifp->name);
 }
 
 static void
@@ -752,18 +973,15 @@
 int
 if_handlelink(struct dhcpcd_ctx *ctx)
 {
-	union {
-		struct rt_msghdr rtmsg;
-		unsigned char buf[2048];
-	} u;
-	struct iovec iov = { .iov_base = u.buf, .iov_len = sizeof(u) };
+	struct rtm rtm;
+	struct iovec iov = { .iov_base = &rtm, .iov_len = sizeof(rtm) };
 	struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1 };
 	ssize_t len;
 
 	if ((len = recvmsg(ctx->link_fd, &msg, 0)) == -1)
 		return -1;
 	if (len != 0)
-		if_dispatch(ctx, &u.rtmsg);
+		if_dispatch(ctx, &rtm.hdr);
 	return 0;
 }
 
@@ -810,6 +1028,7 @@
 		if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1)
 			return -1;
 	}
+
 	return 0;
 }
 
@@ -838,15 +1057,20 @@
 static int
 if_plumbif(const struct dhcpcd_ctx *ctx, int af, const char *ifname)
 {
-	dlpi_handle_t		dh;
-	int			fd, af_fd, mux_fd, retval;
+	dlpi_handle_t		dh, dh_arp = NULL;
+	int			fd, af_fd, mux_fd, arp_fd = -1, mux_id, retval;
+	uint64_t		flags;
 	struct lifreq		lifr;
 	const char		*udp_dev;
+	struct strioctl		ioc;
+	struct if_spec		spec;
 
-	memset(&lifr, 0, sizeof(lifr));
+	if (if_nametospec(ifname, &spec) == -1)
+		return -1;
+
 	switch (af) {
 	case AF_INET:
-		lifr.lifr_flags = IFF_IPV4;
+		flags = IFF_IPV4;
 		af_fd = ctx->pf_inet_fd;
 		udp_dev = UDP_DEV_NAME;
 		break;
@@ -855,7 +1079,7 @@
 		struct priv *priv;
 
 		/* We will take care of setting the link local address. */
-		lifr.lifr_flags = IFF_IPV6 | IFF_NOLINKLOCAL;
+		flags = IFF_IPV6 | IFF_NOLINKLOCAL;
 		priv = (struct priv *)ctx->priv;
 		af_fd = priv->pf_inet6_fd;
 		udp_dev = UDP6_DEV_NAME;
@@ -876,13 +1100,17 @@
 	mux_fd = -1;
 	if (ioctl(fd, I_PUSH, IP_MOD_NAME) == -1)
 		goto out;
+	memset(&lifr, 0, sizeof(lifr));
 	strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
+	lifr.lifr_ppa = spec.ppa;
+	lifr.lifr_flags = flags;
 	if (ioctl(fd, SIOCSLIFNAME, &lifr) == -1)
 		goto out;
 
 	/* Get full flags. */
 	if (ioctl(af_fd, SIOCGLIFFLAGS, &lifr) == -1)
 		goto out;
+	flags = lifr.lifr_flags;
 
 	/* Open UDP as a multiplexor to PLINK the interface stream.
 	 * UDP is used because STREAMS will not let you PLINK a driver
@@ -894,20 +1122,49 @@
 		;
 	if (errno != EINVAL)
 		goto out;
+	if(ioctl(mux_fd, I_PUSH, ARP_MOD_NAME) == -1)
+		goto out;
 
-	if (lifr.lifr_flags & IFF_IPV4 && !(lifr.lifr_flags & IFF_NOARP)) {
-		if (ioctl(mux_fd, I_PUSH, ARP_MOD_NAME) == -1)
+	if (flags & (IFF_NOARP | IFF_IPV6)) {
+		/* PLINK the interface stream so it persists. */
+		if (ioctl(mux_fd, I_PLINK, fd) == -1)
 			goto out;
+		goto done;
 	}
 
-	/* PLINK the interface stream so it persists. */
-	if (ioctl(mux_fd, I_PLINK, fd) == -1)
+	if (dlpi_open(ifname, &dh_arp, DLPI_NOATTACH) != DLPI_SUCCESS)
+		goto out;
+	arp_fd = dlpi_fd(dh_arp);
+	if (ioctl(arp_fd, I_PUSH, ARP_MOD_NAME) == -1)
 		goto out;
 
+	memset(&lifr, 0, sizeof(lifr));
+	strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
+	lifr.lifr_ppa = spec.ppa;
+	lifr.lifr_flags = flags;
+	memset(&ioc, 0, sizeof(ioc));
+	ioc.ic_cmd = SIOCSLIFNAME;
+	ioc.ic_dp = (char *)&lifr;
+	ioc.ic_len = sizeof(lifr);
+	if (ioctl(arp_fd, I_STR, &ioc) == -1)
+		goto out;
+
+	/* PLINK the interface stream so it persists. */
+	mux_id = ioctl(mux_fd, I_PLINK, fd);
+	if (mux_id == -1)
+		goto out;
+	if (ioctl(mux_fd, I_PLINK, arp_fd) == -1) {
+		ioctl(mux_fd, I_PUNLINK, mux_id);
+		goto out;
+	}
+
+done:
 	retval = 0;
 
 out:
 	dlpi_close(dh);
+	if (dh_arp != NULL)
+		dlpi_close(dh_arp);
 	if (mux_fd != -1)
 		close(mux_fd);
 	return retval;
@@ -963,102 +1220,6 @@
 		return if_unplumbif(ctx, af, ifname);
 }
 
-int
-if_route(unsigned char cmd, const struct rt *rt)
-{
-	struct dhcpcd_ctx *ctx;
-	struct rtm
-	{
-		struct rt_msghdr hdr;
-		char buffer[sizeof(struct sockaddr_storage) * RTAX_MAX];
-	} rtmsg;
-	struct rt_msghdr *rtm;
-	char *bp = rtmsg.buffer;
-	size_t l;
-
-	/* WARNING: Solaris will not allow you to delete RTF_KERNEL routes.
-	 * This includes subnet/prefix routes. */
-
-	ctx = rt->rt_ifp->ctx;
-
-#define ADDSA(sa) do {							      \
-		l = RT_ROUNDUP(salen((sa)));				      \
-		memcpy(bp, (sa), l);					      \
-		bp += l;						      \
-	} while (/* CONSTCOND */ 0)
-
-	memset(&rtmsg, 0, sizeof(rtmsg));
-	rtm = &rtmsg.hdr;
-	rtm->rtm_version = RTM_VERSION;
-	rtm->rtm_type = cmd;
-	rtm->rtm_seq = ++ctx->seq;
-	rtm->rtm_flags = rt->rt_flags;
-	rtm->rtm_addrs = RTA_DST | RTA_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))
-		{
-			rtm->rtm_addrs |= RTA_IFP;
-			if (!sa_is_unspecified(&rt->rt_ifa))
-				rtm->rtm_addrs |= RTA_IFA;
-		}
-		if (netmask_bcast)
-			rtm->rtm_flags |= RTF_HOST;
-		else
-			rtm->rtm_flags |= RTF_GATEWAY;
-
-		/* Emulate the kernel by marking address generated
-		 * network routes non-static. */
-		if (!(rt->rt_dflags & RTDF_IFA_ROUTE))
-			rtm->rtm_flags |= RTF_STATIC;
-
-		if (rt->rt_mtu != 0) {
-			rtm->rtm_inits |= RTV_MTU;
-			rtm->rtm_rmx.rmx_mtu = rt->rt_mtu;
-		}
-	}
-
-	ADDSA(&rt->rt_dest);
-
-	if (sa_is_unspecified(&rt->rt_gateway))
-		ADDSA(&rt->rt_ifa);
-	else
-		ADDSA(&rt->rt_gateway);
-
-	if (rtm->rtm_addrs & RTA_NETMASK)
-		ADDSA(&rt->rt_netmask);
-
-	if (rtm->rtm_addrs & RTA_IFP) {
-		struct sockaddr_dl sdl;
-
-		if_linkaddr(&sdl, rt->rt_ifp);
-		ADDSA((struct sockaddr *)&sdl);
-	}
-
-/* This no workie :/ */
-#if 1
-	/* route(1M) says RTA_IFA is accepted but ignored
-	 * it's unclear how RTA_SRC is different. */
-	if (rtm->rtm_addrs & RTA_IFA) {
-		rtm->rtm_addrs &= ~RTA_IFA;
-		rtm->rtm_addrs |= RTA_SRC;
-	}
-	if (rtm->rtm_addrs & RTA_SRC)
-		ADDSA(&rt->rt_ifa);
-#endif
-
-#undef ADDSA
-
-	rtm->rtm_msglen = (unsigned short)(bp - (char *)rtm);
-	if (write(ctx->link_fd, rtm, rtm->rtm_msglen) == -1)
-		return -1;
-	return 0;
-}
-
 #ifdef INET
 static int
 if_walkrt(struct dhcpcd_ctx *ctx, char *data, size_t len)
@@ -1087,6 +1248,7 @@
 		default:
 			break;
 		}
+
 		memset(&rt, 0, sizeof(rt));
 		rt.rt_dflags |= RTDF_INIT;
 		in.s_addr = re->ipRouteDest;
@@ -1099,13 +1261,10 @@
 		in.s_addr = re->ipRouteInfo.re_src_addr;
 		sa_in_init(&rt.rt_ifa, &in);
 		rt.rt_mtu = re->ipRouteInfo.re_max_frag;
-
 		if_octetstr(ifname, &re->ipRouteIfIndex, sizeof(ifname));
 		rt.rt_ifp = if_find(ctx->ifaces, ifname);
-		if (rt.rt_ifp != NULL) {
-			if_finishrt(&rt);
-			rt_recvrt(RTM_ADD, &rt);
-		}
+		if_finishrt(ctx, &rt);
+		rt_recvrt(RTM_ADD, &rt, 0);
 	} while (++re < e);
 	return 0;
 }
@@ -1140,6 +1299,7 @@
 		default:
 			break;
 		}
+
 		memset(&rt, 0, sizeof(rt));
 		rt.rt_dflags |= RTDF_INIT;
 		sa_in6_init(&rt.rt_dest, &re->ipv6RouteDest);
@@ -1147,13 +1307,10 @@
 		sa_in6_init(&rt.rt_netmask, &in6);
 		sa_in6_init(&rt.rt_gateway, &re->ipv6RouteNextHop);
 		rt.rt_mtu = re->ipv6RouteInfo.re_max_frag;
-
 		if_octetstr(ifname, &re->ipv6RouteIfIndex, sizeof(ifname));
 		rt.rt_ifp = if_find(ctx->ifaces, ifname);
-		if (rt.rt_ifp != NULL) {
-			if_finishrt(&rt);
-			rt_recvrt(RTM_ADD, &rt);
-		}
+		if_finishrt(ctx, &rt);
+		rt_recvrt(RTM_ADD, &rt, 0);
 	} while (++re < e);
 	return 0;
 }
@@ -1314,6 +1471,9 @@
 		return -1;
 	}
 
+	/* 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;
@@ -1405,4 +1565,11 @@
 {
 
 }
+
+int
+ip6_forwarding(__unused const char *ifname)
+{
+
+	return 1;
+}
 #endif
--- a/src/if.c	Tue Apr 16 19:48:46 2019 +0100
+++ b/src/if.c	Tue Apr 16 20:02:36 2019 +0100
@@ -136,9 +136,15 @@
 
 	memset(&ifr, 0, sizeof(ifr));
 	strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
-	if (ioctl(ifp->ctx->pf_inet_fd, SIOCGIFFLAGS, &ifr) == -1)
+	r = ioctl(ifp->ctx->pf_inet_fd, SIOCGIFFLAGS, &ifr);
+	if (r != -1)
+		ifp->flags = (unsigned int)ifr.ifr_flags;
+
+#ifdef __sun
+	return if_carrier_os(ifp);
+#else
+	if (r == -1)
 		return LINK_UNKNOWN;
-	ifp->flags = (unsigned int)ifr.ifr_flags;
 
 #ifdef SIOCGIFMEDIA
 	memset(&ifmr, 0, sizeof(ifmr));
@@ -155,6 +161,7 @@
 #else
 	r = ifr.ifr_flags & IFF_RUNNING ? LINK_UP : LINK_DOWN;
 #endif
+#endif /* __sun */
 	return r;
 }
 
@@ -711,6 +718,11 @@
 	int r;
 	struct ifreq ifr;
 
+#ifdef __sun
+	if (mtu == 0)
+		return if_mtu_os(ifp);
+#endif
+
 	memset(&ifr, 0, sizeof(ifr));
 	strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
 	ifr.ifr_mtu = mtu;
@@ -720,6 +732,17 @@
 	return ifr.ifr_mtu;
 }
 
+#ifdef ALIAS_ADDR
+int
+if_makealias(char *alias, size_t alias_len, const char *ifname, int lun)
+{
+
+	if (lun == 0)
+		return strlcpy(alias, ifname, alias_len);
+	return snprintf(alias, alias_len, "%s:%u", ifname, lun);
+}
+#endif
+
 struct interface *
 if_findifpfromcmsg(struct dhcpcd_ctx *ctx, struct msghdr *msg, int *hoplimit)
 {
--- a/src/if.h	Tue Apr 16 19:48:46 2019 +0100
+++ b/src/if.h	Tue Apr 16 20:02:36 2019 +0100
@@ -128,6 +128,13 @@
 #define if_setmtu(ifp, mtu) if_domtu((ifp), (mtu))
 int if_carrier(struct interface *);
 
+#ifdef ALIAS_ADDR
+int if_makealias(char *, size_t, const char *, int);
+#endif
+
+int if_carrier_os(struct interface *);
+int if_mtu_os(const struct interface *);
+
 /*
  * Helper to decode an interface name of bge0:1 to
  * devname = bge0, drvname = bge0, ppa = 0, lun = 1.
--- a/src/ipv4.c	Tue Apr 16 19:48:46 2019 +0100
+++ b/src/ipv4.c	Tue Apr 16 20:02:36 2019 +0100
@@ -571,10 +571,12 @@
 	lun = 0;
 	state = IPV4_STATE(ia->iface);
 find_lun:
-	if (lun == 0)
-		strlcpy(alias, ia->iface->name, sizeof(alias));
-	else
-		snprintf(alias, sizeof(alias), "%s:%u", ia->iface->name, lun);
+	if (if_makealias(alias, IF_NAMESIZE, ia->iface->name, lun) >=
+	    IF_NAMESIZE)
+	{
+		errno = ENOMEM;
+		return -1;
+	}
 	TAILQ_FOREACH(iap, &state->addrs, next) {
 		if (iap->alias[0] != '\0' && iap->addr.s_addr == INADDR_ANY) {
 			/* No address assigned? Lets use it. */
--- a/src/ipv4ll.c	Tue Apr 16 19:48:46 2019 +0100
+++ b/src/ipv4ll.c	Tue Apr 16 20:02:36 2019 +0100
@@ -310,6 +310,7 @@
 		ipv4_deladdr(state->addr, 1);
 		state->down = 1;
 		state->addr = NULL;
+		rt_build(ifp->ctx, AF_INET);
 		script_runreason(ifp, "IPV4LL");
 	}
 
--- a/src/ipv6.c	Tue Apr 16 19:48:46 2019 +0100
+++ b/src/ipv6.c	Tue Apr 16 20:02:36 2019 +0100
@@ -624,6 +624,10 @@
 	uint32_t pltime, vltime;
 	bool vltime_was_zero;
 	__printflike(1, 2) void (*logfunc)(const char *, ...);
+#ifdef __sun
+	struct ipv6_state *state;
+	struct ipv6_addr *ia2;
+#endif
 
 	/* Remember the interface of the address. */
 	ifp = ia->iface;
@@ -759,13 +763,13 @@
 }
 
 #ifdef ALIAS_ADDR
-/* Find the next logical aliase address we can use. */
+/* Find the next logical alias address we can use. */
 static int
 ipv6_aliasaddr(struct ipv6_addr *ia, struct ipv6_addr **repl)
 {
 	struct ipv6_state *state;
 	struct ipv6_addr *iap;
-	unsigned int unit;
+	unsigned int lun;
 	char alias[IF_NAMESIZE];
 
 	if (ia->alias[0] != '\0')
@@ -784,12 +788,14 @@
 		}
 	}
 
-	unit = 0;
+	lun = 0;
 find_unit:
-	if (unit == 0)
-		strlcpy(alias, ia->iface->name, sizeof(alias));
-	else
-		snprintf(alias, sizeof(alias), "%s:%u", ia->iface->name, unit);
+	if (if_makealias(alias, IF_NAMESIZE, ia->iface->name, lun) >=
+	    IF_NAMESIZE)
+	{
+		errno = ENOMEM;
+		return -1;
+	}
 	TAILQ_FOREACH(iap, &state->addrs, next) {
 		if (iap->alias[0] == '\0')
 			continue;
@@ -805,11 +811,11 @@
 	}
 
 	if (iap != NULL) {
-		if (unit == UINT_MAX) {
+		if (lun == UINT_MAX) {
 			errno = ERANGE;
 			return -1;
 		}
-		unit++;
+		lun++;
 		goto find_unit;
 	}
 
--- a/src/route.c	Tue Apr 16 19:48:46 2019 +0100
+++ b/src/route.c	Tue Apr 16 20:02:36 2019 +0100
@@ -366,7 +366,7 @@
 /* If something other than dhcpcd removes a route,
  * we need to remove it from our internal table. */
 void
-rt_recvrt(int cmd, const struct rt *rt)
+rt_recvrt(int cmd, const struct rt *rt, pid_t pid)
 {
 	struct dhcpcd_ctx *ctx;
 	struct rt *f;
@@ -381,8 +381,11 @@
 	case RTM_DELETE:
 		f = rb_tree_find_node(&ctx->routes, rt);
 		if (f != NULL) {
+			char buf[32];
+
 			rb_tree_remove_node(&ctx->routes, f);
-			rt_desc("deleted", f);
+			snprintf(buf, sizeof(buf), "pid %d deleted", pid);
+			rt_desc(buf, f);
 			rt_free(f);
 		}
 		break;
--- a/src/route.h	Tue Apr 16 19:48:46 2019 +0100
+++ b/src/route.h	Tue Apr 16 20:02:36 2019 +0100
@@ -106,7 +106,7 @@
 struct rt * rt_new0(struct dhcpcd_ctx *);
 void rt_setif(struct rt *, struct interface *);
 struct rt * rt_new(struct interface *);
-void rt_recvrt(int, const struct rt *);
+void rt_recvrt(int, const struct rt *, pid_t);
 void rt_build(struct dhcpcd_ctx *, int);
 
 #endif
--- a/src/script.c	Tue Apr 16 19:48:46 2019 +0100
+++ b/src/script.c	Tue Apr 16 20:02:36 2019 +0100
@@ -704,9 +704,7 @@
 	int status = 0;
 	struct fd_list *fd;
 
-	if (ifp->options->script &&
-	    (ifp->options->script[0] == '\0' ||
-	    strcmp(ifp->options->script, "/dev/null") == 0) &&
+	if (ifp->options->script == NULL &&
 	    TAILQ_FIRST(&ifp->ctx->control_fds) == NULL)
 		return 0;
 
@@ -717,12 +715,10 @@
 		return -1;
 	}
 
-	if (ifp->options->script &&
-	    (ifp->options->script[0] == '\0' ||
-	    strcmp(ifp->options->script, "/dev/null") == 0))
+	if (ifp->options->script == NULL)
 		goto send_listeners;
 
-	argv[0] = ifp->options->script ? ifp->options->script : UNCONST(SCRIPT);
+	argv[0] = ifp->options->script;
 	argv[1] = NULL;
 	logdebugx("%s: executing `%s' %s", ifp->name, argv[0], reason);