#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)) && \
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 &&
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
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 &&
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);
}
+ }
+
+ AREA_CHECK(3);
+ *p++ = DHO_MESSAGETYPE;
+ *p++ = 1;
+ *p++ = type;
- if (type == DHCP_RELEASE) {
+ 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);
}
}
}
- 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 (DHCP_DIR(type)) {
+ if (type != DHCP_INFORM) {
+ if (ifo->leasetime != 0) {
+ AREA_CHECK(4);
+ *p++ = DHO_LEASETIME;
+ *p++ = 4;
+ ul = htonl(ifo->leasetime);
+ memcpy(p, &ul, 4);
+ p += 4;
+ }
+ }
- if (type == DHCP_DISCOVER ||
- type == DHCP_INFORM ||
- type == DHCP_REQUEST)
- {
- if (mtu != -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);
+
+ if (mtu != -1 &&
+ !(has_option_mask(ifo->nomask, DHO_MAXMESSAGESIZE)))
+ {
AREA_CHECK(2);
*p++ = DHO_MAXMESSAGESIZE;
*p++ = 2;
p += 2;
}
- if (ifo->userclass[0]) {
+ 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 (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 (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 (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) &&
+ !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_INFORM) {
- if (ifo->leasetime != 0) {
- AREA_CHECK(4);
- *p++ = DHO_LEASETIME;
- *p++ = 4;
- ul = htonl(ifo->leasetime);
- memcpy(p, &ul, 4);
- p += 4;
- }
- }
+ 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);
/*
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;
+ auth = NULL; /* appease GCC */
+ auth_len = 0;
+ if (ifo->auth.options & DHCPCD_AUTH_SEND) {
+ ssize_t alen = dhcp_auth_encode(&ifo->auth,
+ state->auth.token,
+ NULL, 0, 4, type, NULL, 0);
+ if (alen != -1 && alen > UINT8_MAX) {
+ errno = ERANGE;
+ alen = -1;
+ }
+ if (alen == -1)
+ logerr("%s: dhcp_auth_encode", ifp->name);
+ else if (alen != 0) {
+ auth_len = (uint8_t)alen;
+ AREA_CHECK(auth_len);
+ *p++ = DHO_AUTHENTICATION;
+ *p++ = auth_len;
+ auth = p;
+ p += auth_len;
}
+ }
#endif
- if (ifo->vivco_len) {
+ /* 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++;
}
}
- 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
- auth = NULL; /* appease GCC */
- auth_len = 0;
- if (ifo->auth.options & DHCPCD_AUTH_SEND) {
- ssize_t alen = dhcp_auth_encode(&ifo->auth,
- state->auth.token,
- NULL, 0, 4, type, NULL, 0);
- if (alen != -1 && alen > UINT8_MAX) {
- errno = ERANGE;
- alen = -1;
- }
- if (alen == -1)
- logerr("%s: dhcp_auth_encode", ifp->name);
- else if (alen != 0) {
- auth_len = (uint8_t)alen;
- AREA_CHECK(auth_len);
- *p++ = DHO_AUTHENTICATION;
- *p++ = auth_len;
- auth = p;
- p += auth_len;
+ 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);
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;
}
#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);
#ifdef AUTH
uint16_t auth_len;
#endif
+ uint8_t duid[DUID_LEN];
+ size_t duid_len = 0;
state = D6_STATE(ifp);
if (state->send) {
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))
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
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) {
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;
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;
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;
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
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))
}
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;
}
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);
}
# 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
# 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
# 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 2 uint32 time_offset
+define 3 request array ipaddress routers
define 4 array ipaddress time_servers
define 5 array ipaddress ien116_name_servers
define 6 array ipaddress domain_name_servers
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
# 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
.\" 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
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
* 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));
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
} 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) {
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);
}
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__);
.\" 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
.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.
#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>
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
#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
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
#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
{"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},
break;
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:
#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 */
}
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;
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;
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)
{
#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 *);
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,
#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
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:
}
}
-ssize_t
-ps_root_ioctl6(struct dhcpcd_ctx *ctx, unsigned long request, void *data, size_t len)
+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, PS_IOCTL6,
+ 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)
+{
+
+ return ps_root_ioctldom(ctx, PS_IOCTL6, request, data, len);
+}
+
ssize_t
ps_root_route(struct dhcpcd_ctx *ctx, void *data, size_t len)
{
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__
#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