changeset 4958:a120f447fe74 draft

Implement Anonymity Profiles for DHCP Clients, RFC 7844 This works by randomising the hardware address when carrier is down and using this to construct a DUID LL which is used over any saved DUID. IAID is defaulted to zero and hostname + FQDN are disabled. Then every possible option is masked out except for essential ones. It's possible to request options *after* anonymous option which will enable it. This is RFC compliant and allows 100% flexability in letting the user decide what, if any, details leek out. This is disabled by default. Only works on NetBSD, other OS coming shortly.
author Roy Marples <roy@marples.name>
date Wed, 15 Jan 2020 14:28:24 +0000
parents 2c96d2b00ed7
children 1b7325caa3ce
files src/dhcp-common.h src/dhcp.c src/dhcp6.c src/dhcpcd-definitions-small.conf src/dhcpcd-definitions.conf src/dhcpcd.8.in src/dhcpcd.c src/dhcpcd.conf.5.in src/duid.c src/duid.h src/if-bsd.c src/if-options.c src/if-options.h src/if.c src/if.h src/privsep-bsd.c src/privsep-root.h src/privsep.h
diffstat 18 files changed, 548 insertions(+), 293 deletions(-) [+]
line wrap: on
line diff
--- a/src/dhcp-common.h	Tue Jan 14 11:53:29 2020 +0000
+++ b/src/dhcp-common.h	Wed Jan 15 14:28:24 2020 +0000
@@ -81,6 +81,9 @@
 #define	OT_BITFLAG		(1 << 27)
 #define	OT_RESERVED		(1 << 28)
 
+#define	DHC_REQ(r, n, o) \
+	(has_option_mask((r), (o)) && !has_option_mask((n), (o)))
+
 #define DHC_REQOPT(o, r, n)						  \
 	(!((o)->type & OT_NOREQ) &&					  \
 	 ((o)->type & OT_REQUEST || has_option_mask((r), (o)->option)) && \
--- a/src/dhcp.c	Tue Jan 14 11:53:29 2020 +0000
+++ b/src/dhcp.c	Wed Jan 15 14:28:24 2020 +0000
@@ -782,12 +782,9 @@
 
 	bootp->op = BOOTREQUEST;
 	bootp->htype = (uint8_t)ifp->family;
-	switch (ifp->family) {
-	case ARPHRD_ETHER:
-	case ARPHRD_IEEE802:
+	if (ifp->hwlen != 0 && ifp->hwlen < sizeof(bootp->chaddr)) {
 		bootp->hlen = (uint8_t)ifp->hwlen;
 		memcpy(&bootp->chaddr, &ifp->hwaddr, ifp->hwlen);
-		break;
 	}
 
 	if (ifo->options & DHCPCD_BROADCAST &&
@@ -820,10 +817,6 @@
 	memcpy(p, &ul, sizeof(ul));
 	p += sizeof(ul);
 
-	*p++ = DHO_MESSAGETYPE;
-	*p++ = 1;
-	*p++ = type;
-
 #define AREA_LEFT	(size_t)(e - p)
 #define AREA_FIT(s)	if ((s) > AREA_LEFT) goto toobig
 #define AREA_CHECK(s)	if ((s) + 2UL > AREA_LEFT) goto toobig
@@ -835,13 +828,10 @@
 	p += 4;				\
 } while (0 /* CONSTCOND */)
 
-	if (state->clientid) {
-		AREA_CHECK(state->clientid[0]);
-		*p++ = DHO_CLIENTID;
-		memcpy(p, state->clientid, (size_t)state->clientid[0] + 1);
-		p += state->clientid[0] + 1;
-	}
-
+	/* Options are listed in numerical order as per RFC 7844 Section 3.1
+	 * XXX: They should be randomised. */
+
+	bool putip = false;
 	if (lease->addr.s_addr && lease->cookie == htonl(MAGIC_COOKIE)) {
 		if (type == DHCP_DECLINE ||
 		    (type == DHCP_REQUEST &&
@@ -849,12 +839,18 @@
 		    state->added & STATE_FAKE ||
 		    lease->addr.s_addr != state->addr->addr.s_addr)))
 		{
+			putip = true;
 			PUT_ADDR(DHO_IPADDRESS, &lease->addr);
-			if (lease->server.s_addr)
-				PUT_ADDR(DHO_SERVERID, &lease->server);
 		}
-
-		if (type == DHCP_RELEASE) {
+	}
+
+	AREA_CHECK(3);
+	*p++ = DHO_MESSAGETYPE;
+	*p++ = 1;
+	*p++ = type;
+
+	if (lease->addr.s_addr && lease->cookie == htonl(MAGIC_COOKIE)) {
+		if (type == DHCP_RELEASE || putip) {
 			if (lease->server.s_addr)
 				PUT_ADDR(DHO_SERVERID, &lease->server);
 		}
@@ -870,63 +866,23 @@
 		}
 	}
 
-	if (type == DHCP_DISCOVER &&
-	    !(ifp->ctx->options & DHCPCD_TEST) &&
-	    has_option_mask(ifo->requestmask, DHO_RAPIDCOMMIT))
-	{
-		/* RFC 4039 Section 3 */
-		AREA_CHECK(0);
-		*p++ = DHO_RAPIDCOMMIT;
-		*p++ = 0;
+#define	DHCP_DIR(type) ((type) == DHCP_DISCOVER || (type) == DHCP_INFORM || \
+    (type) == DHCP_REQUEST)
+
+	if (DHCP_DIR(type)) {
+		/* vendor is already encoded correctly, so just add it */
+		if (ifo->vendor[0]) {
+			AREA_CHECK(ifo->vendor[0]);
+			*p++ = DHO_VENDOR;
+			memcpy(p, ifo->vendor, (size_t)ifo->vendor[0] + 1);
+			p += ifo->vendor[0] + 1;
+		}
 	}
 
 	if (type == DHCP_DISCOVER && ifo->options & DHCPCD_REQUEST)
 		PUT_ADDR(DHO_IPADDRESS, &ifo->req_addr);
 
-	/* RFC 2563 Auto Configure */
-	if (type == DHCP_DISCOVER && ifo->options & DHCPCD_IPV4LL) {
-		AREA_CHECK(1);
-		*p++ = DHO_AUTOCONFIGURE;
-		*p++ = 1;
-		*p++ = 1;
-	}
-
-	if (type == DHCP_DISCOVER ||
-	    type == DHCP_INFORM ||
-	    type == DHCP_REQUEST)
-	{
-		if (mtu != -1) {
-			AREA_CHECK(2);
-			*p++ = DHO_MAXMESSAGESIZE;
-			*p++ = 2;
-			sz = htons((uint16_t)(mtu - IP_UDP_SIZE));
-			memcpy(p, &sz, 2);
-			p += 2;
-		}
-
-		if (ifo->userclass[0]) {
-			AREA_CHECK(ifo->userclass[0]);
-			*p++ = DHO_USERCLASS;
-			memcpy(p, ifo->userclass,
-			    (size_t)ifo->userclass[0] + 1);
-			p += ifo->userclass[0] + 1;
-		}
-
-		if (ifo->vendorclassid[0]) {
-			AREA_CHECK(ifo->vendorclassid[0]);
-			*p++ = DHO_VENDORCLASSID;
-			memcpy(p, ifo->vendorclassid,
-			    (size_t)ifo->vendorclassid[0] + 1);
-			p += ifo->vendorclassid[0] + 1;
-		}
-
-		if (ifo->mudurl[0]) {
-		       AREA_CHECK(ifo->mudurl[0]);
-		       *p++ = DHO_MUDURL;
-		       memcpy(p, ifo->mudurl, (size_t)ifo->mudurl[0] + 1);
-		       p += ifo->mudurl[0] + 1;
-		}
-
+	if (DHCP_DIR(type)) {
 		if (type != DHCP_INFORM) {
 			if (ifo->leasetime != 0) {
 				AREA_CHECK(4);
@@ -938,6 +894,94 @@
 			}
 		}
 
+		AREA_CHECK(0);
+		*p++ = DHO_PARAMETERREQUESTLIST;
+		n_params = p;
+		*p++ = 0;
+		for (i = 0, opt = ifp->ctx->dhcp_opts;
+		    i < ifp->ctx->dhcp_opts_len;
+		    i++, opt++)
+		{
+			if (!DHC_REQOPT(opt, ifo->requestmask, ifo->nomask))
+				continue;
+			if (type == DHCP_INFORM &&
+			    (opt->option == DHO_RENEWALTIME ||
+				opt->option == DHO_REBINDTIME))
+				continue;
+			AREA_FIT(1);
+			*p++ = (uint8_t)opt->option;
+		}
+		for (i = 0, opt = ifo->dhcp_override;
+		    i < ifo->dhcp_override_len;
+		    i++, opt++)
+		{
+			/* Check if added above */
+			for (lp = n_params + 1; lp < p; lp++)
+				if (*lp == (uint8_t)opt->option)
+					break;
+			if (lp < p)
+				continue;
+			if (!DHC_REQOPT(opt, ifo->requestmask, ifo->nomask))
+				continue;
+			if (type == DHCP_INFORM &&
+			    (opt->option == DHO_RENEWALTIME ||
+				opt->option == DHO_REBINDTIME))
+				continue;
+			AREA_FIT(1);
+			*p++ = (uint8_t)opt->option;
+		}
+		*n_params = (uint8_t)(p - n_params - 1);
+
+		if (mtu != -1 &&
+		    !(has_option_mask(ifo->nomask, DHO_MAXMESSAGESIZE)))
+		{
+			AREA_CHECK(2);
+			*p++ = DHO_MAXMESSAGESIZE;
+			*p++ = 2;
+			sz = htons((uint16_t)(mtu - IP_UDP_SIZE));
+			memcpy(p, &sz, 2);
+			p += 2;
+		}
+
+		if (ifo->userclass[0] &&
+		    !has_option_mask(ifo->nomask, DHO_USERCLASS))
+		{
+			AREA_CHECK(ifo->userclass[0]);
+			*p++ = DHO_USERCLASS;
+			memcpy(p, ifo->userclass,
+			    (size_t)ifo->userclass[0] + 1);
+			p += ifo->userclass[0] + 1;
+		}
+	}
+
+	if (state->clientid) {
+		AREA_CHECK(state->clientid[0]);
+		*p++ = DHO_CLIENTID;
+		memcpy(p, state->clientid, (size_t)state->clientid[0] + 1);
+		p += state->clientid[0] + 1;
+	}
+
+	if (DHCP_DIR(type) &&
+	    !has_option_mask(ifo->nomask, DHO_VENDORCLASSID) &&
+	    ifo->vendorclassid[0])
+	{
+		AREA_CHECK(ifo->vendorclassid[0]);
+		*p++ = DHO_VENDORCLASSID;
+		memcpy(p, ifo->vendorclassid, (size_t)ifo->vendorclassid[0]+1);
+		p += ifo->vendorclassid[0] + 1;
+	}
+
+	if (type == DHCP_DISCOVER &&
+	    !(ifp->ctx->options & DHCPCD_TEST) &&
+	    DHC_REQ(ifo->requestmask, ifo->nomask, DHO_RAPIDCOMMIT))
+	{
+		/* RFC 4039 Section 3 */
+		AREA_CHECK(0);
+		*p++ = DHO_RAPIDCOMMIT;
+		*p++ = 0;
+	}
+
+	if (DHCP_DIR(type)) {
 		hostname = dhcp_get_hostname(hbuf, sizeof(hbuf), ifo);
 
 		/*
@@ -984,91 +1028,6 @@
 			memcpy(p, hostname, len);
 			p += len;
 		}
-
-		/* vendor is already encoded correctly, so just add it */
-		if (ifo->vendor[0]) {
-			AREA_CHECK(ifo->vendor[0]);
-			*p++ = DHO_VENDOR;
-			memcpy(p, ifo->vendor, (size_t)ifo->vendor[0] + 1);
-			p += ifo->vendor[0] + 1;
-		}
-
-#ifdef AUTH
-		if ((ifo->auth.options & DHCPCD_AUTH_SENDREQUIRE) !=
-		    DHCPCD_AUTH_SENDREQUIRE &&
-		    !has_option_mask(ifo->nomask, DHO_FORCERENEW_NONCE))
-		{
-			/* We support HMAC-MD5 */
-			AREA_CHECK(1);
-			*p++ = DHO_FORCERENEW_NONCE;
-			*p++ = 1;
-			*p++ = AUTH_ALG_HMAC_MD5;
-		}
-#endif
-
-		if (ifo->vivco_len) {
-			AREA_CHECK(sizeof(ul));
-			*p++ = DHO_VIVCO;
-			lp = p++;
-			*lp = sizeof(ul);
-			ul = htonl(ifo->vivco_en);
-			memcpy(p, &ul, sizeof(ul));
-			p += sizeof(ul);
-			for (i = 0, vivco = ifo->vivco;
-			    i < ifo->vivco_len;
-			    i++, vivco++)
-			{
-				AREA_FIT(vivco->len);
-				if (vivco->len + 2 + *lp > 255) {
-					logerrx("%s: VIVCO option too big",
-					    ifp->name);
-					free(bootp);
-					return -1;
-				}
-				*p++ = (uint8_t)vivco->len;
-				memcpy(p, vivco->data, vivco->len);
-				p += vivco->len;
-				*lp = (uint8_t)(*lp + vivco->len + 1);
-			}
-		}
-
-		AREA_CHECK(0);
-		*p++ = DHO_PARAMETERREQUESTLIST;
-		n_params = p;
-		*p++ = 0;
-		for (i = 0, opt = ifp->ctx->dhcp_opts;
-		    i < ifp->ctx->dhcp_opts_len;
-		    i++, opt++)
-		{
-			if (!DHC_REQOPT(opt, ifo->requestmask, ifo->nomask))
-				continue;
-			if (type == DHCP_INFORM &&
-			    (opt->option == DHO_RENEWALTIME ||
-				opt->option == DHO_REBINDTIME))
-				continue;
-			AREA_FIT(1);
-			*p++ = (uint8_t)opt->option;
-		}
-		for (i = 0, opt = ifo->dhcp_override;
-		    i < ifo->dhcp_override_len;
-		    i++, opt++)
-		{
-			/* Check if added above */
-			for (lp = n_params + 1; lp < p; lp++)
-				if (*lp == (uint8_t)opt->option)
-					break;
-			if (lp < p)
-				continue;
-			if (!DHC_REQOPT(opt, ifo->requestmask, ifo->nomask))
-				continue;
-			if (type == DHCP_INFORM &&
-			    (opt->option == DHO_RENEWALTIME ||
-				opt->option == DHO_REBINDTIME))
-				continue;
-			AREA_FIT(1);
-			*p++ = (uint8_t)opt->option;
-		}
-		*n_params = (uint8_t)(p - n_params - 1);
 	}
 
 #ifdef AUTH
@@ -1095,6 +1054,66 @@
 	}
 #endif
 
+	/* RFC 2563 Auto Configure */
+	if (type == DHCP_DISCOVER && ifo->options & DHCPCD_IPV4LL &&
+	    !(has_option_mask(ifo->nomask, DHO_AUTOCONFIGURE)))
+	{
+		AREA_CHECK(1);
+		*p++ = DHO_AUTOCONFIGURE;
+		*p++ = 1;
+		*p++ = 1;
+	}
+
+	if (DHCP_DIR(type)) {
+		if (ifo->mudurl[0]) {
+		       AREA_CHECK(ifo->mudurl[0]);
+		       *p++ = DHO_MUDURL;
+		       memcpy(p, ifo->mudurl, (size_t)ifo->mudurl[0] + 1);
+		       p += ifo->mudurl[0] + 1;
+		}
+
+		if (ifo->vivco_len &&
+		    !has_option_mask(ifo->nomask, DHO_VIVCO))
+		{
+			AREA_CHECK(sizeof(ul));
+			*p++ = DHO_VIVCO;
+			lp = p++;
+			*lp = sizeof(ul);
+			ul = htonl(ifo->vivco_en);
+			memcpy(p, &ul, sizeof(ul));
+			p += sizeof(ul);
+			for (i = 0, vivco = ifo->vivco;
+			    i < ifo->vivco_len;
+			    i++, vivco++)
+			{
+				AREA_FIT(vivco->len);
+				if (vivco->len + 2 + *lp > 255) {
+					logerrx("%s: VIVCO option too big",
+					    ifp->name);
+					free(bootp);
+					return -1;
+				}
+				*p++ = (uint8_t)vivco->len;
+				memcpy(p, vivco->data, vivco->len);
+				p += vivco->len;
+				*lp = (uint8_t)(*lp + vivco->len + 1);
+			}
+		}
+
+#ifdef AUTH
+		if ((ifo->auth.options & DHCPCD_AUTH_SENDREQUIRE) !=
+		    DHCPCD_AUTH_SENDREQUIRE &&
+		    !has_option_mask(ifo->nomask, DHO_FORCERENEW_NONCE))
+		{
+			/* We support HMAC-MD5 */
+			AREA_CHECK(1);
+			*p++ = DHO_FORCERENEW_NONCE;
+			*p++ = 1;
+			*p++ = AUTH_ALG_HMAC_MD5;
+		}
+#endif
+	}
+
 	*p++ = DHO_END;
 	len = (size_t)(p - (uint8_t *)bootp);
 
@@ -3762,7 +3781,22 @@
 	free(state->clientid);
 	state->clientid = NULL;
 
-	if (*ifo->clientid) {
+	if (ifo->options & DHCPCD_ANONYMOUS) {
+		uint8_t duid[DUID_LEN];
+		uint8_t duid_len;
+
+		duid_len = (uint8_t)duid_make(duid, ifp, DUID_LL);
+		if (duid_len != 0) {
+			state->clientid = malloc((size_t)duid_len + 6);
+			if (state->clientid == NULL)
+				goto eexit;
+			state->clientid[0] =(uint8_t)(duid_len + 5);
+			state->clientid[1] = 255; /* RFC 4361 */
+			memcpy(state->clientid + 2, ifo->iaid, 4);
+			memset(state->clientid + 2, 0, 4); /* IAID */
+			memcpy(state->clientid + 6, duid, duid_len);
+		}
+	} else if (*ifo->clientid) {
 		state->clientid = malloc((size_t)(ifo->clientid[0] + 1));
 		if (state->clientid == NULL)
 			goto eexit;
@@ -3976,7 +4010,9 @@
 	}
 #endif
 
-	if (state->offer == NULL || !IS_DHCP(state->offer))
+	if (state->offer == NULL ||
+	    !IS_DHCP(state->offer) ||
+	    ifo->options & DHCPCD_ANONYMOUS)
 		dhcp_discover(ifp);
 	else
 		dhcp_reboot(ifp);
--- a/src/dhcp6.c	Tue Jan 14 11:53:29 2020 +0000
+++ b/src/dhcp6.c	Wed Jan 15 14:28:24 2020 +0000
@@ -637,6 +637,8 @@
 #ifdef AUTH
 	uint16_t auth_len;
 #endif
+	uint8_t duid[DUID_LEN];
+	size_t duid_len = 0;
 
 	state = D6_STATE(ifp);
 	if (state->send) {
@@ -705,19 +707,29 @@
 			len += sizeof(o) + 1 + hl;
 		}
 
-		if (ifo->mudurl[0])
+		if (!has_option_mask(ifo->nomask6, D6_OPTION_MUDURL) &&
+		    ifo->mudurl[0])
 			len += sizeof(o) + ifo->mudurl[0];
 
 #ifdef AUTH
 		if ((ifo->auth.options & DHCPCD_AUTH_SENDREQUIRE) !=
-		    DHCPCD_AUTH_SENDREQUIRE)
+		    DHCPCD_AUTH_SENDREQUIRE &&
+		    DHC_REQ(ifo->requestmask6, ifo->nomask6,
+		    D6_OPTION_RECONF_ACCEPT))
 			len += sizeof(o); /* Reconfigure Accept */
 #endif
 	}
 
 	len += sizeof(*state->send);
-	len += sizeof(o) + ifp->ctx->duid_len;
 	len += sizeof(o) + sizeof(uint16_t); /* elapsed */
+
+	if (ifo->options & DHCPCD_ANONYMOUS) {
+		duid_len = duid_make(duid, ifp, DUID_LL);
+		len += sizeof(o) + duid_len;
+	} else {
+		len += sizeof(o) + ifp->ctx->duid_len;
+	}
+
 	if (!has_option_mask(ifo->nomask6, D6_OPTION_USER_CLASS))
 		len += dhcp6_makeuser(NULL, ifp);
 	if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_CLASS))
@@ -755,8 +767,9 @@
 		TAILQ_FOREACH(ap, &state->addrs, next) {
 			if (ap->flags & IPV6_AF_STALE)
 				continue;
-			if (ap->prefix_vltime == 0 &&
-			    !(ap->flags & IPV6_AF_REQUEST))
+			if (!(ap->flags & IPV6_AF_REQUEST) &&
+			    (ap->prefix_vltime == 0 ||
+			    state->state == DH6S_DISCOVER))
 				continue;
 			if (ap->ia_type == D6_OPTION_IA_PD) {
 #ifndef SMALL
@@ -785,7 +798,7 @@
 
 	if (state->state == DH6S_DISCOVER &&
 	    !(ifp->ctx->options & DHCPCD_TEST) &&
-	    has_option_mask(ifo->requestmask6, D6_OPTION_RAPID_COMMIT))
+	    DHC_REQ(ifo->requestmask6, ifo->nomask6, D6_OPTION_RAPID_COMMIT))
 		len += sizeof(o);
 
 	if (m == NULL) {
@@ -884,35 +897,29 @@
 	memcpy(p, &o, sizeof(o));		\
 	p += sizeof(o);				\
 }
-#define COPYIN(_code, _data, _len)	{	\
+#define COPYIN(_code, _data, _len)	do {	\
 	COPYIN1((_code), (_len));		\
 	if ((_len) != 0) {			\
 		memcpy(p, (_data), (_len));	\
 		p += (_len);			\
 	}					\
-}
+} while (0 /* CONSTCOND */)
 #define NEXTLEN (p + offsetof(struct dhcp6_option, len))
 
+	/* Options are listed in numerical order as per RFC 7844 Section 4.1
+	 * XXX: They should be randomised. */
+
 	p = (uint8_t *)state->send + sizeof(*state->send);
-	COPYIN(D6_OPTION_CLIENTID, ifp->ctx->duid,
-	    (uint16_t)ifp->ctx->duid_len);
+	if (ifo->options & DHCPCD_ANONYMOUS)
+		COPYIN(D6_OPTION_CLIENTID, duid,
+		    (uint16_t)duid_len);
+	else
+		COPYIN(D6_OPTION_CLIENTID, ifp->ctx->duid,
+		    (uint16_t)ifp->ctx->duid_len);
 
 	if (si != NULL)
 		COPYIN(D6_OPTION_SERVERID, si, si_len);
 
-	si_len = 0;
-	COPYIN(D6_OPTION_ELAPSED, &si_len, sizeof(si_len));
-
-	if (!has_option_mask(ifo->nomask6, D6_OPTION_USER_CLASS))
-		p += dhcp6_makeuser(p, ifp);
-	if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_CLASS))
-		p += dhcp6_makevendor(p, ifp);
-
-	if (state->state == DH6S_DISCOVER &&
-	    !(ifp->ctx->options & DHCPCD_TEST) &&
-	    has_option_mask(ifo->requestmask6, D6_OPTION_RAPID_COMMIT))
-		COPYIN1(D6_OPTION_RAPID_COMMIT, 0);
-
 	for (l = 0; IA && l < ifo->ia_len; l++) {
 		ifia = &ifo->ia[l];
 		o_lenp = NEXTLEN;
@@ -931,8 +938,9 @@
 		TAILQ_FOREACH(ap, &state->addrs, next) {
 			if (ap->flags & IPV6_AF_STALE)
 				continue;
-			if (ap->prefix_vltime == 0 &&
-			    !(ap->flags & IPV6_AF_REQUEST))
+			if (!(ap->flags & IPV6_AF_REQUEST) &&
+			    (ap->prefix_vltime == 0 ||
+			    state->state == DH6S_DISCOVER))
 				continue;
 			if (ap->ia_type != ifia->ia_type)
 				continue;
@@ -995,6 +1003,68 @@
 		memcpy(o_lenp, &ia_na_len, sizeof(ia_na_len));
 	}
 
+	if (state->send->type != DHCP6_RELEASE && n_options) {
+		o_lenp = NEXTLEN;
+		o.len = 0;
+		COPYIN1(D6_OPTION_ORO, 0);
+		for (l = 0, opt = ifp->ctx->dhcp6_opts;
+		    l < ifp->ctx->dhcp6_opts_len;
+		    l++, opt++)
+		{
+#ifndef SMALL
+			for (n = 0, opt2 = ifo->dhcp6_override;
+			    n < ifo->dhcp6_override_len;
+			    n++, opt2++)
+			{
+				if (opt->option == opt2->option)
+					break;
+			}
+			if (n < ifo->dhcp6_override_len)
+			    continue;
+#endif
+			if (!DHC_REQOPT(opt, ifo->requestmask6, ifo->nomask6))
+				continue;
+			o.code = htons((uint16_t)opt->option);
+			memcpy(p, &o.code, sizeof(o.code));
+			p += sizeof(o.code);
+			o.len = (uint16_t)(o.len + sizeof(o.code));
+		}
+#ifndef SMALL
+		for (l = 0, opt = ifo->dhcp6_override;
+		    l < ifo->dhcp6_override_len;
+		    l++, opt++)
+		{
+			if (!DHC_REQOPT(opt, ifo->requestmask6, ifo->nomask6))
+				continue;
+			o.code = htons((uint16_t)opt->option);
+			memcpy(p, &o.code, sizeof(o.code));
+			p += sizeof(o.code);
+			o.len = (uint16_t)(o.len + sizeof(o.code));
+		}
+		if (dhcp6_findselfsla(ifp)) {
+			o.code = htons(D6_OPTION_PD_EXCLUDE);
+			memcpy(p, &o.code, sizeof(o.code));
+			p += sizeof(o.code);
+			o.len = (uint16_t)(o.len + sizeof(o.code));
+		}
+#endif
+		o.len = htons(o.len);
+		memcpy(o_lenp, &o.len, sizeof(o.len));
+	}
+
+	si_len = 0;
+	COPYIN(D6_OPTION_ELAPSED, &si_len, sizeof(si_len));
+
+	if (state->state == DH6S_DISCOVER &&
+	    !(ifp->ctx->options & DHCPCD_TEST) &&
+	    DHC_REQ(ifo->requestmask6, ifo->nomask6, D6_OPTION_RAPID_COMMIT))
+		COPYIN1(D6_OPTION_RAPID_COMMIT, 0);
+
+	if (!has_option_mask(ifo->nomask6, D6_OPTION_USER_CLASS))
+		p += dhcp6_makeuser(p, ifp);
+	if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_CLASS))
+		p += dhcp6_makevendor(p, ifp);
+
 	if (state->send->type != DHCP6_RELEASE) {
 		if (fqdn != FQDN_DISABLE) {
 			o_lenp = NEXTLEN;
@@ -1021,67 +1091,19 @@
 			memcpy(o_lenp, &o.len, sizeof(o.len));
 		}
 
-		if (ifo->mudurl[0])
+		if (!has_option_mask(ifo->nomask6, D6_OPTION_MUDURL &&
+		    ifo->mudurl[0]))
 			COPYIN(D6_OPTION_MUDURL,
 			    ifo->mudurl + 1, ifo->mudurl[0]);
 
 #ifdef AUTH
 		if ((ifo->auth.options & DHCPCD_AUTH_SENDREQUIRE) !=
 		    DHCPCD_AUTH_SENDREQUIRE &&
-		    !has_option_mask(ifo->nomask6, D6_OPTION_RECONF_ACCEPT))
+		    DHC_REQ(ifo->requestmask6, ifo->nomask6,
+		    D6_OPTION_RECONF_ACCEPT))
 			COPYIN1(D6_OPTION_RECONF_ACCEPT, 0);
 #endif
 
-		if (n_options) {
-			o_lenp = NEXTLEN;
-			o.len = 0;
-			COPYIN1(D6_OPTION_ORO, 0);
-			for (l = 0, opt = ifp->ctx->dhcp6_opts;
-			    l < ifp->ctx->dhcp6_opts_len;
-			    l++, opt++)
-			{
-#ifndef SMALL
-				for (n = 0, opt2 = ifo->dhcp6_override;
-				    n < ifo->dhcp6_override_len;
-				    n++, opt2++)
-				{
-					if (opt->option == opt2->option)
-						break;
-				}
-				if (n < ifo->dhcp6_override_len)
-				    continue;
-#endif
-				if (!DHC_REQOPT(opt, ifo->requestmask6,
-				    ifo->nomask6))
-					continue;
-				o.code = htons((uint16_t)opt->option);
-				memcpy(p, &o.code, sizeof(o.code));
-				p += sizeof(o.code);
-				o.len = (uint16_t)(o.len + sizeof(o.code));
-			}
-#ifndef SMALL
-			for (l = 0, opt = ifo->dhcp6_override;
-			    l < ifo->dhcp6_override_len;
-			    l++, opt++)
-			{
-				if (!DHC_REQOPT(opt, ifo->requestmask6,
-				    ifo->nomask6))
-					continue;
-				o.code = htons((uint16_t)opt->option);
-				memcpy(p, &o.code, sizeof(o.code));
-				p += sizeof(o.code);
-				o.len = (uint16_t)(o.len + sizeof(o.code));
-			}
-			if (dhcp6_findselfsla(ifp)) {
-				o.code = htons(D6_OPTION_PD_EXCLUDE);
-				memcpy(p, &o.code, sizeof(o.code));
-				p += sizeof(o.code);
-				o.len = (uint16_t)(o.len + sizeof(o.code));
-			}
-#endif
-			o.len = htons(o.len);
-			memcpy(o_lenp, &o.len, sizeof(o.len));
-		}
 	}
 
 #ifdef AUTH
@@ -2631,7 +2653,9 @@
 		if (r == -1) {
 			if (errno != ENOENT)
 				logerr("%s: %s", __func__, state->leasefile);
-		} else if (r != 0) {
+		} else if (r != 0 &&
+		    !(ifp->options->options & DHCPCD_ANONYMOUS))
+		{
 			/* RFC 3633 section 12.1 */
 #ifndef SMALL
 			if (dhcp6_hasprefixdelegation(ifp))
@@ -3520,10 +3544,18 @@
 	}
 
 	r = (struct dhcp6_message *)msg->msg_iov[0].iov_base;
+
+	uint8_t duid[DUID_LEN], *dp;
+	size_t duid_len;
 	o = dhcp6_findmoption(r, len, D6_OPTION_CLIENTID, &ol);
-	if (o == NULL || ol != ctx->duid_len ||
-	    memcmp(o, ctx->duid, ol) != 0)
-	{
+	if (ifp->options->options & DHCPCD_ANONYMOUS) {
+		duid_len = duid_make(duid, ifp, DUID_LL);
+		dp = duid;
+	} else {
+		duid_len = ctx->duid_len;
+		dp = ctx->duid;
+	}
+	if (o == NULL || ol != duid_len || memcmp(o, dp, ol) != 0) {
 		logdebugx("%s: incorrect client ID from %s",
 		    ifp->name, sfrom);
 		return;
@@ -3774,12 +3806,11 @@
 	}
 	if (i == sizeof(ifo->requestmask6)) {
 		for (dhc = dhcp_compats; dhc->dhcp_opt; dhc++) {
-			if (has_option_mask(ifo->requestmask, dhc->dhcp_opt))
+			if (DHC_REQ(ifo->requestmask, ifo->nomask, dhc->dhcp_opt))
 				add_option_mask(ifo->requestmask6,
 				    dhc->dhcp6_opt);
 		}
-		if (ifo->fqdn != FQDN_DISABLE ||
-		    ifo->options & DHCPCD_HOSTNAME)
+		if (ifo->fqdn != FQDN_DISABLE || ifo->options & DHCPCD_HOSTNAME)
 			add_option_mask(ifo->requestmask6, D6_OPTION_FQDN);
 	}
 
--- a/src/dhcpcd-definitions-small.conf	Tue Jan 14 11:53:29 2020 +0000
+++ b/src/dhcpcd-definitions-small.conf	Wed Jan 15 14:28:24 2020 +0000
@@ -9,16 +9,13 @@
 # RFC3442 states that the CSR has to come before all other routes
 # For completeness we also specify static routes then routers
 define 121	rfc3442			classless_static_routes
-# Option 249 is an IANA assigned private number used by Windows DHCP servers
-# to provide the exact same information as option 121, classless static routes
-define 249	rfc3442			ms_classless_static_routes
-define 33	request array ipaddress	static_routes
 define 3	request array ipaddress	routers
 define 6	array ipaddress		domain_name_servers
 define 12	dname			host_name
 define 15	array dname		domain_name
 define 26	uint16			interface_mtu
 define 28	request ipaddress	broadcast_address
+define 33	request array ipaddress	static_routes
 define 50	ipaddress		dhcp_requested_address
 define 51	request uint32		dhcp_lease_time
 define 52	byte			dhcp_option_overload
@@ -49,6 +46,10 @@
 # DHCP Domain Search, RFC3397
 define 119	array domain		domain_search
 
+# Option 249 is an IANA assigned private number used by Windows DHCP servers
+# to provide the exact same information as option 121, classless static routes
+define 249	rfc3442			ms_classless_static_routes
+
 ##############################################################################
 # ND6 options, RFC4861
 definend 1	binhex			source_address
--- a/src/dhcpcd-definitions.conf	Tue Jan 14 11:53:29 2020 +0000
+++ b/src/dhcpcd-definitions.conf	Wed Jan 15 14:28:24 2020 +0000
@@ -12,12 +12,8 @@
 # RFC3442 states that the CSR has to come before all other routes
 # For completeness we also specify static routes then routers
 define 121	rfc3442			classless_static_routes
-# Option 249 is an IANA assigned private number used by Windows DHCP servers
-# to provide the exact same information as option 121, classless static routes
-define 249	rfc3442			ms_classless_static_routes
-define 33	request array ipaddress	static_routes
+define 2	uint32			time_offset
 define 3	request array ipaddress	routers
-define 2	uint32			time_offset
 define 4	array ipaddress		time_servers
 define 5	array ipaddress		ien116_name_servers
 define 6	array ipaddress		domain_name_servers
@@ -49,6 +45,7 @@
 define 30	byte			mask_supplier
 define 31	byte			router_discovery
 define 32	ipaddress		router_solicitation_address
+define 33	request array ipaddress	static_routes
 define 34	byte			trailer_encapsulation
 define 35	uint32			arp_cache_timeout
 define 36	uint16			ieee802_3_encapsulation
@@ -311,7 +308,12 @@
 # Options 222 and 223 are unused, RFC3942
 
 # Options 224-254 are reserved for Private Use
-# However, an expired RFC for Web Proxy Auto Discovery Protocol does define
+
+# Option 249 is an IANA assigned private number used by Windows DHCP servers
+# to provide the exact same information as option 121, classless static routes
+define 249	rfc3442			ms_classless_static_routes
+
+# An expired RFC for Web Proxy Auto Discovery Protocol does define
 # Option 252 which is commonly used by major browsers.
 # Apparently the code was assigned by agreement of the DHC working group chair.
 define 252	string			wpad_url
--- a/src/dhcpcd.8.in	Tue Jan 14 11:53:29 2020 +0000
+++ b/src/dhcpcd.8.in	Wed Jan 15 14:28:24 2020 +0000
@@ -24,7 +24,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd January 3, 2020
+.Dd January 15, 2020
 .Dt DHCPCD 8
 .Os
 .Sh NAME
@@ -833,7 +833,7 @@
 RFC\ 3397, RFC\ 3442, RFC\ 3495, RFC\ 3925, RFC\ 3927, RFC\ 4039, RFC\ 4075,
 RFC\ 4242, RFC\ 4361, RFC\ 4390, RFC\ 4702, RFC\ 4074, RFC\ 4861, RFC\ 4833,
 RFC\ 4941, RFC\ 5227, RFC\ 5942, RFC\ 5969, RFC\ 6106, RFC\ 6334, RFC\ 6355,
-RFC\ 6603, RFC\ 6704, RFC\ 7217, RFC\ 7550.
+RFC\ 6603, RFC\ 6704, RFC\ 7217, RFC\ 7550, RFC\ 7844.
 .Sh AUTHORS
 .An Roy Marples Aq Mt roy@marples.name
 .Sh BUGS
--- a/src/dhcpcd.c	Tue Jan 14 11:53:29 2020 +0000
+++ b/src/dhcpcd.c	Wed Jan 15 14:28:24 2020 +0000
@@ -494,7 +494,9 @@
 			 * so we don't conflict with an interface index. */
 			vlanid = htonl(ifp->vlanid | 0xff000000);
 			memcpy(ifo->iaid, &vlanid, sizeof(vlanid));
-		} else if (ifp->hwlen >= sizeof(ifo->iaid)) {
+		} else if (ifo->options & DHCPCD_ANONYMOUS)
+			memset(ifo->iaid, 0, sizeof(ifo->iaid));
+		else if (ifp->hwlen >= sizeof(ifo->iaid)) {
 			memcpy(ifo->iaid,
 			    ifp->hwaddr + ifp->hwlen - sizeof(ifo->iaid),
 			    sizeof(ifo->iaid));
@@ -700,14 +702,17 @@
 			if (ifp->carrier == LINK_UP)
 				loginfox("%s: carrier lost", ifp->name);
 #ifdef NOCARRIER_PRESERVE_IP
-			if (ifp->flags & IFF_UP)
+			if (ifp->flags & IFF_UP &&
+			    !(ifp->options->options & DHCPCD_ANONYMOUS))
 				ifp->carrier = LINK_DOWN_IFFUP;
 			else
 #endif
 				ifp->carrier = LINK_DOWN;
 			script_runreason(ifp, "NOCARRIER");
 #ifdef NOCARRIER_PRESERVE_IP
-			if (ifp->flags & IFF_UP) {
+			if (ifp->flags & IFF_UP &&
+			    !(ifp->options->options & DHCPCD_ANONYMOUS))
+			{
 #ifdef ARP
 				arp_drop(ifp);
 #endif
@@ -720,6 +725,12 @@
 			} else
 #endif
 				dhcpcd_drop(ifp, 0);
+			if (ifp->options->options & DHCPCD_ANONYMOUS) {
+				if_down(ifp);
+				if (if_randomisemac(ifp) == -1 && errno != ENXIO)
+					logerr(__func__);
+				if_up(ifp);
+			}
 		}
 	} else if (carrier == LINK_UP && ifp->flags & IFF_UP) {
 		if (ifp->carrier != LINK_UP) {
@@ -924,8 +935,14 @@
 
 	if ((!(ifp->ctx->options & DHCPCD_MASTER) ||
 	    ifp->options->options & DHCPCD_IF_UP) &&
-	    if_up(ifp) == -1)
-		logerr("%s: %s", __func__, ifp->name);
+	    ifp->carrier != LINK_UP)
+	{
+		if (ifp->options->options & DHCPCD_ANONYMOUS &&
+		    if_randomisemac(ifp) == -1)
+			logerr(__func__);
+		if (if_up(ifp) == -1)
+			logerr(__func__);
+	}
 
 	dhcpcd_startinterface(ifp);
 }
@@ -1312,9 +1329,10 @@
 
 	if (ctx->options & DHCPCD_FORKED) {
 		pid_t pid = pidfile_read(ctx->pidfile);
-		if (pid == -1)
-			logerr("%s: pidfile_read",__func__);
-		else if (pid == 0)
+		if (pid == -1) {
+			if (errno != ENOENT)
+				logerr("%s: pidfile_read",__func__);
+		} else if (pid == 0)
 			logerr("%s: pid cannot be zero", __func__);
 		else if (kill(pid, sig) == -1)
 			logerr("%s: kill", __func__);
--- a/src/dhcpcd.conf.5.in	Tue Jan 14 11:53:29 2020 +0000
+++ b/src/dhcpcd.conf.5.in	Wed Jan 15 14:28:24 2020 +0000
@@ -24,7 +24,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd January 3, 2020
+.Dd January 15, 2020
 .Dt DHCPCD.CONF 5
 .Os
 .Sh NAME
@@ -59,6 +59,26 @@
 .Ar pattern
 which is a space or comma separated list of patterns passed to
 .Xr fnmatch 3 .
+.It Ic anonymous
+Enables Anonymity Profiles for DHCP, RFC 7844.
+This implementation forces a hardware address randomisaton when
+the interface link is down and that ClientID's are only LL.
+Any DUID is ignored.
+All non essential options are then masked at this point,
+but they could be unmasked by explicitly requesting the option
+.Sy after
+the
+.Ic anonymous
+option is processed.
+As such, the
+.Ic anonymous
+option
+.Sy should
+be the last option in the configuration unless you really want to
+send something which could identify you.
+.Nm dhcpcd
+will not try and reboot an old lease, it will go straight into
+DISCOVER/SOLICIT.
 .It Ic arping Ar address Op address
 .Nm dhcpcd
 will arping each address in order before attempting DHCP.
--- a/src/duid.c	Tue Jan 14 11:53:29 2020 +0000
+++ b/src/duid.c	Wed Jan 15 14:28:24 2020 +0000
@@ -28,9 +28,6 @@
 
 #define	UUID_LEN	36
 #define	DUID_TIME_EPOCH 946684800
-#define	DUID_LLT	1
-#define	DUID_LL		3
-#define	DUID_UUID	4
 
 #include <sys/param.h>
 #include <sys/socket.h>
@@ -118,33 +115,36 @@
 	return l;
 }
 
-static size_t
-duid_make(uint8_t *d, const struct interface *ifp, uint16_t type)
+size_t
+duid_make(void *d, const struct interface *ifp, uint16_t type)
 {
 	uint8_t *p;
 	uint16_t u16;
 	time_t t;
 	uint32_t u32;
 
+	if (ifp->hwlen == 0)
+		return 0;
+
 	p = d;
 	u16 = htons(type);
-	memcpy(p, &u16, 2);
-	p += 2;
+	memcpy(p, &u16, sizeof(u16));
+	p += sizeof(u16);
 	u16 = htons(ifp->family);
-	memcpy(p, &u16, 2);
-	p += 2;
+	memcpy(p, &u16, sizeof(u16));
+	p += sizeof(u16);
 	if (type == DUID_LLT) {
 		/* time returns seconds from jan 1 1970, but DUID-LLT is
 		 * seconds from jan 1 2000 modulo 2^32 */
 		t = time(NULL) - DUID_TIME_EPOCH;
 		u32 = htonl((uint32_t)t & 0xffffffff);
-		memcpy(p, &u32, 4);
-		p += 4;
+		memcpy(p, &u32, sizeof(u32));
+		p += sizeof(u32);
 	}
 	/* Finally, add the MAC address of the interface */
 	memcpy(p, ifp->hwaddr, ifp->hwlen);
 	p += ifp->hwlen;
-	return (size_t)(p - d);
+	return (size_t)(p - (uint8_t *)d);
 }
 
 #define DUID_STRLEN DUID_LEN * 3
--- a/src/duid.h	Tue Jan 14 11:53:29 2020 +0000
+++ b/src/duid.h	Wed Jan 15 14:28:24 2020 +0000
@@ -30,7 +30,11 @@
 #define DUID_H
 
 #define DUID_LEN	128 + 2
+#define	DUID_LLT	1
+#define	DUID_LL		3
+#define	DUID_UUID	4
 
+size_t duid_make(void *, const struct interface *, uint16_t);
 size_t duid_init(const struct interface *);
 
 #endif
--- a/src/if-bsd.c	Tue Jan 14 11:53:29 2020 +0000
+++ b/src/if-bsd.c	Wed Jan 15 14:28:24 2020 +0000
@@ -213,6 +213,55 @@
 	priv = (struct priv *)ctx->priv;
 	if (priv->pf_inet6_fd != -1)
 		close(priv->pf_inet6_fd);
+	free(priv);
+	ctx->priv = NULL;
+}
+
+static int
+if_ioctllink(struct dhcpcd_ctx *ctx, unsigned long req, void *data, size_t len)
+{
+	int s;
+	int retval;
+
+#ifdef PRIVSEP
+	if (ctx->options & DHCPCD_PRIVSEP)
+		return (int)ps_root_ioctllink(ctx, req, data, len);
+#else
+	UNUSED(ctx);
+#endif
+
+	s = socket(PF_LINK, SOCK_DGRAM, 0);
+	if (s == -1)
+		return -1;
+	retval = ioctl(s, req, data, len);
+	close(s);
+	return retval;
+}
+
+int
+if_setmac(struct interface *ifp, void *mac, uint8_t maclen)
+{
+	struct if_laddrreq iflr = { .flags = IFLR_ACTIVE };
+	struct sockaddr_dl *sdl = satosdl(&iflr.addr);
+	int retval;
+
+	if (ifp->hwlen != maclen) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	strlcpy(iflr.iflr_name, ifp->name, sizeof(iflr.iflr_name));
+	sdl->sdl_family = AF_LINK;
+	sdl->sdl_len = sizeof(*sdl);
+	sdl->sdl_alen = maclen;
+	memcpy(LLADDR(sdl), mac, maclen);
+	retval = if_ioctllink(ifp->ctx, SIOCALIFADDR, &iflr, sizeof(iflr));
+
+	/* Try and remove the old address */
+	memcpy(LLADDR(sdl), ifp->hwaddr, ifp->hwlen);
+	if_ioctllink(ifp->ctx, SIOCDLIFADDR, &iflr, sizeof(iflr));
+
+	return retval;
 }
 
 static bool
--- a/src/if-options.c	Tue Jan 14 11:53:29 2020 +0000
+++ b/src/if-options.c	Wed Jan 15 14:28:24 2020 +0000
@@ -66,7 +66,7 @@
 #define O_NOIPV6RS		O_BASE + 5
 #define O_IPV6RA_FORK		O_BASE + 6
 #define O_LINK_RCVBUF		O_BASE + 7
-// unused			O_BASE + 8
+#define O_ANONYMOUS		O_BASE + 8
 #define O_NOALIAS		O_BASE + 9
 #define O_IA_NA			O_BASE + 10
 #define O_IA_TA			O_BASE + 11
@@ -161,6 +161,7 @@
 	{"oneshot",         no_argument,       NULL, '1'},
 	{"ipv4only",        no_argument,       NULL, '4'},
 	{"ipv6only",        no_argument,       NULL, '6'},
+	{"anonymous",       no_argument,       NULL, O_ANONYMOUS},
 	{"arping",          required_argument, NULL, O_ARPING},
 	{"destination",     required_argument, NULL, O_DESTINATION},
 	{"fallback",        required_argument, NULL, O_FALLBACK},
@@ -1239,6 +1240,33 @@
 	case O_NOIPV6:
 		ifo->options &= ~DHCPCD_IPV6;
 		break;
+	case O_ANONYMOUS:
+		ifo->options |= DHCPCD_ANONYMOUS;
+		ifo->options &= ~DHCPCD_HOSTNAME;
+		ifo->fqdn = FQDN_DISABLE;
+
+		/* Block everything */
+		memset(ifo->nomask, 0xff, sizeof(ifo->nomask));
+		memset(ifo->nomask6, 0xff, sizeof(ifo->nomask6));
+
+		/* Allow the bare minimum through */
+		del_option_mask(ifo->nomask, DHO_SUBNETMASK);
+		del_option_mask(ifo->nomask, DHO_CSR);
+		del_option_mask(ifo->nomask, DHO_ROUTER);
+		del_option_mask(ifo->nomask, DHO_DNSSERVER);
+		del_option_mask(ifo->nomask, DHO_BROADCAST);
+		del_option_mask(ifo->nomask, DHO_STATICROUTE);
+		del_option_mask(ifo->nomask, DHO_SERVERID);
+		del_option_mask(ifo->nomask, DHO_RENEWALTIME);
+		del_option_mask(ifo->nomask, DHO_REBINDTIME);
+		del_option_mask(ifo->nomask, DHO_DNSSEARCH);
+
+		del_option_mask(ifo->nomask6, D6_OPTION_DNS_SERVERS);
+		del_option_mask(ifo->nomask6, D6_OPTION_DOMAIN_LIST);
+		del_option_mask(ifo->nomask6, D6_OPTION_SOL_MAX_RT);
+		del_option_mask(ifo->nomask6, D6_OPTION_INF_MAX_RT);
+
+		break;
 #ifdef INET
 	case O_ARPING:
 		while (arg != NULL) {
--- a/src/if-options.h	Tue Jan 14 11:53:29 2020 +0000
+++ b/src/if-options.h	Wed Jan 15 14:28:24 2020 +0000
@@ -78,7 +78,7 @@
 #define DHCPCD_HOSTNAME			(1ULL << 18)
 #define DHCPCD_CLIENTID			(1ULL << 19)
 #define DHCPCD_LINK			(1ULL << 20)
-// unused				(1ULL << 21)
+#define DHCPCD_ANONYMOUS		(1ULL << 21)
 #define DHCPCD_BACKGROUND		(1ULL << 22)
 #define DHCPCD_VENDORRAW		(1ULL << 23)
 #define DHCPCD_NOWAITIP			(1ULL << 24) /* To force daemonise */
--- a/src/if.c	Tue Jan 14 11:53:29 2020 +0000
+++ b/src/if.c	Wed Jan 15 14:28:24 2020 +0000
@@ -156,7 +156,7 @@
 }
 
 int
-if_setflag(struct interface *ifp, short flag)
+if_setflag(struct interface *ifp, short setflag, short unsetflag)
 {
 	struct ifreq ifr = { .ifr_flags = 0 };
 	short f;
@@ -165,11 +165,12 @@
 		return -1;
 
 	f = (short)ifp->flags;
-	if ((f & flag) == flag)
+	if ((f & setflag) == setflag && (f & unsetflag) == 0)
 		return 0;
 
 	strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
-	ifr.ifr_flags = f | flag;
+	ifr.ifr_flags |= setflag;
+	ifr.ifr_flags &= (short)~unsetflag;
 	if (if_ioctl(ifp->ctx, SIOCSIFFLAGS, &ifr, sizeof(ifr)) == -1)
 		return -1;
 
@@ -177,6 +178,46 @@
 	return 0;
 }
 
+int
+if_randomisemac(struct interface *ifp)
+{
+	uint32_t randnum;
+	size_t hwlen = ifp->hwlen, rlen = 0;
+	uint8_t buf[hwlen], *bp = buf, *rp = (uint8_t *)&randnum;
+	char sbuf[hwlen * 3];
+	int retval;
+
+	if (hwlen == 0) {
+		errno = ENOTSUP;
+		return -1;
+	}
+
+	for (; hwlen != 0; hwlen--) {
+		if (rlen == 0) {
+			randnum = arc4random();
+			rp = (uint8_t *)&randnum;
+			rlen = sizeof(randnum);
+		}
+		if (bp == buf) {
+			/* First octet is special. We need to preserve
+			 * bit 8 (unicast/multicast) and set
+			 * bit 7 (locally administered address) */
+			*bp = *rp++ & 0xFC;
+			*bp++ |= 2;
+		} else
+			*bp++ = *rp++;
+		rlen--;
+	}
+
+	logdebugx("%s: hardware address randomised to %s",
+	    ifp->name,
+	    hwaddr_ntoa(buf, sizeof(buf), sbuf, sizeof(sbuf)));
+	retval = if_setmac(ifp, buf, ifp->hwlen);
+	if (retval == 0)
+		memcpy(ifp->hwaddr, buf, sizeof(ifp->hwaddr));
+	return retval;
+}
+
 static int
 if_hasconf(struct dhcpcd_ctx *ctx, const char *ifname)
 {
--- a/src/if.h	Tue Jan 14 11:53:29 2020 +0000
+++ b/src/if.h	Wed Jan 15 14:28:24 2020 +0000
@@ -121,9 +121,10 @@
 #endif
 
 int if_ioctl(struct dhcpcd_ctx *, ioctl_request_t, void *, size_t);
-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))
+int if_getflags(struct interface *);
+int if_setflag(struct interface *, short, short);
+#define if_up(ifp) if_setflag((ifp), (IFF_UP | IFF_RUNNING), 0)
+#define if_down(ifp) if_setflag((ifp), 0, IFF_UP);
 bool if_valid_hwaddr(const uint8_t *, size_t);
 struct if_head *if_discover(struct dhcpcd_ctx *, struct ifaddrs **,
     int, char * const *);
@@ -171,6 +172,8 @@
 void if_closesockets(struct dhcpcd_ctx *);
 void if_closesockets_os(struct dhcpcd_ctx *);
 int if_handlelink(struct dhcpcd_ctx *);
+int if_randomisemac(struct interface *);
+int if_setmac(struct interface *ifp, void *, uint8_t);
 
 /* dhcpcd uses the same routing flags as BSD.
  * If the platform doesn't use these flags,
--- a/src/privsep-bsd.c	Tue Jan 14 11:53:29 2020 +0000
+++ b/src/privsep-bsd.c	Wed Jan 15 14:28:24 2020 +0000
@@ -36,11 +36,11 @@
 #include "privsep.h"
 
 static ssize_t
-ps_root_doioctl6(unsigned long req, void *data, size_t len)
+ps_root_doioctldom(int domain, unsigned long req, void *data, size_t len)
 {
 	int s, err;
 
-	s = socket(PF_INET6, SOCK_DGRAM, 0);
+	s = socket(domain, SOCK_DGRAM, 0);
 	if (s != -1)
 		err = ioctl(s, req, data, len);
 	else
@@ -74,8 +74,10 @@
 	size_t len = iov->iov_len;
 
 	switch (psm->ps_cmd) {
+	case PS_IOCTLLINK:
+		return ps_root_doioctldom(PF_LINK, psm->ps_flags, data, len);
 	case PS_IOCTL6:
-		return ps_root_doioctl6(psm->ps_flags, data, len);
+		return ps_root_doioctldom(PF_INET6, psm->ps_flags, data, len);
 	case PS_ROUTE:
 		return ps_root_doroute(data, len);
 	default:
@@ -84,14 +86,29 @@
 	}
 }
 
+static ssize_t
+ps_root_ioctldom(struct dhcpcd_ctx *ctx, uint8_t domain, unsigned long request,
+    void *data, size_t len)
+{
+
+	if (ps_sendcmd(ctx, ctx->ps_root_fd, domain,
+	    request, data, len) == -1)
+		return -1;
+	return ps_root_readerror(ctx);
+}
+
+ssize_t
+ps_root_ioctllink(struct dhcpcd_ctx *ctx, unsigned long request, void *data, size_t len)
+{
+
+	return ps_root_ioctldom(ctx, PS_IOCTLLINK, request, data, len);
+}
+
 ssize_t
 ps_root_ioctl6(struct dhcpcd_ctx *ctx, unsigned long request, void *data, size_t len)
 {
 
-	if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_IOCTL6,
-	    request, data, len) == -1)
-		return -1;
-	return ps_root_readerror(ctx);
+	return ps_root_ioctldom(ctx, PS_IOCTL6, request, data, len);
 }
 
 ssize_t
--- a/src/privsep-root.h	Tue Jan 14 11:53:29 2020 +0000
+++ b/src/privsep-root.h	Wed Jan 15 14:28:24 2020 +0000
@@ -39,6 +39,7 @@
 ssize_t ps_root_os(struct ps_msghdr *, struct msghdr *);
 #if defined(BSD) || defined(__sun)
 ssize_t ps_root_route(struct dhcpcd_ctx *, void *, size_t);
+ssize_t ps_root_ioctllink(struct dhcpcd_ctx *, unsigned long, void *, size_t);
 ssize_t ps_root_ioctl6(struct dhcpcd_ctx *, unsigned long, void *, size_t);
 #endif
 #ifdef __linux__
--- a/src/privsep.h	Tue Jan 14 11:53:29 2020 +0000
+++ b/src/privsep.h	Wed Jan 15 14:28:24 2020 +0000
@@ -44,9 +44,10 @@
 
 #define	PS_IOCTL		0x10
 #define	PS_SCRIPT		0x11
-#define	PS_IOCTL6		0x12
-#define	PS_ROUTE		0x13	/* Also used for NETLINK */
-#define	PS_WRITEPATHUINT	0x14
+#define	PS_IOCTLLINK		0x12
+#define	PS_IOCTL6		0x13
+#define	PS_ROUTE		0x14	/* Also used for NETLINK */
+#define	PS_WRITEPATHUINT	0x15
 
 #define	PS_DELETE		0x20
 #define	PS_START		0x40