| ntp |list |`<local address>`| NTP servers to announce accepts IPv4 and IPv6 |
| upstream |list | - | A list of interfaces which can be used as a source of configuration information (e.g. for NTP servers, if not set explicitly). |
| captive_portal_uri |string | no | The API URI to be sent in RFC8910 captive portal options, via DHCPv4, DHCPv6, and ICMPv6 RA. |
+| ipv6_only_preferred |integer| 0 | Indicate that IPv6-only mode is preferred (RFC8925) [V6ONLY_WAIT time in seconds] |
[//]: # "dhcpv6_raw - string - not documented, may change when generic DHCPv4/DHCPv6 options are added"
#include "odhcpd.h"
#include "router.h"
#include "dhcpv6-pxe.h"
+#include "dhcpv4.h"
static struct blob_buf b;
IFACE_ATTR_MAX_VALID_LIFETIME,
IFACE_ATTR_NTP,
IFACE_ATTR_CAPTIVE_PORTAL_URI,
+ IFACE_ATTR_IPV6_ONLY_PREFERRED,
IFACE_ATTR_MAX
};
[IFACE_ATTR_MAX_VALID_LIFETIME] = { .name = "max_valid_lifetime", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_NTP] = { .name = "ntp", .type = BLOBMSG_TYPE_ARRAY },
[IFACE_ATTR_CAPTIVE_PORTAL_URI] = { .name = "captive_portal_uri", .type = BLOBMSG_TYPE_STRING },
+ [IFACE_ATTR_IPV6_ONLY_PREFERRED] = { .name = "ipv6_only_preferred", .type = BLOBMSG_TYPE_INT32 },
};
const struct uci_blob_param_list interface_attr_list = {
}
}
+ if ((c = tb[IFACE_ATTR_IPV6_ONLY_PREFERRED])) {
+ uint32_t v6only_wait = blobmsg_get_u32(c);
+
+ if (v6only_wait > 0 && v6only_wait < DHCPV4_MIN_V6ONLY_WAIT) {
+ warn("Invalid %s value configured for interface '%s', clamped to %d",
+ iface_attrs[IFACE_ATTR_IPV6_ONLY_PREFERRED].name,
+ iface->name, DHCPV4_MIN_V6ONLY_WAIT);
+ v6only_wait = DHCPV4_MIN_V6ONLY_WAIT;
+ }
+
+ iface->dhcpv4_v6only_wait = v6only_wait;
+ }
+
if ((c = tb[IFACE_ATTR_RA_PREFERENCE])) {
const char *prio = blobmsg_get_string(c);
IOV_DNR,
IOV_DNR_BODY,
IOV_CAPTIVE_PORTAL,
+ IOV_IPV6_ONLY_PREF,
IOV_END,
IOV_PADDING,
IOV_TOTAL
uint8_t *req_clientid = NULL;
size_t req_clientid_len = 0;
bool req_accept_fr = false;
+ bool ipv6_only = false;
/* Reply variables */
struct dhcpv4_message reply = {
struct dhcpv4_option reply_dnr = {
.code = DHCPV4_OPT_DNR,
};
+ struct dhcpv4_option_u32 reply_ipv6_only = {
+ .code = DHCPV4_OPT_IPV6_ONLY_PREFERRED,
+ .len = sizeof(uint32_t),
+ .data = htonl(iface->dhcpv4_v6only_wait),
+ };
uint8_t reply_end = DHCPV4_OPT_END;
struct iovec iov[IOV_TOTAL] = {
[IOV_DNR] = { &reply_dnr, 0 },
[IOV_DNR_BODY] = { NULL, 0 },
[IOV_CAPTIVE_PORTAL] = { NULL, 0 },
+ [IOV_IPV6_ONLY_PREF] = { &reply_ipv6_only, 0 },
[IOV_END] = { &reply_end, sizeof(reply_end) },
[IOV_PADDING] = { NULL, 0 },
};
return;
break;
case DHCPV4_OPT_REQOPTS:
- if (opt->len > 0) {
- req_opts = opt->data;
- req_opts_len = opt->len;
- }
+ req_opts = opt->data;
+ req_opts_len = opt->len;
+ if (iface->dhcpv4_v6only_wait)
+ for (uint8_t i = 0; i < opt->len; i++)
+ if (opt->data[i] == DHCPV4_OPT_IPV6_ONLY_PREFERRED)
+ ipv6_only = true;
break;
case DHCPV4_OPT_CLIENTID:
if (opt->len >= 2) {
&reply_incl_fr, &fr_serverid);
return;
case DHCPV4_MSG_DISCOVER:
+ if (ipv6_only)
+ break;
+ _o_fallthrough;
case DHCPV4_MSG_REQUEST:
lease = dhcpv4_lease(iface, req_msg, req->chaddr, req_clientid,
req_clientid_len, req_addr, &req_leasetime,
/* We are at the point where we know the client expects a reply */
switch (req_msg) {
case DHCPV4_MSG_DISCOVER:
- if (!lease)
+ if (!lease && !ipv6_only)
return;
reply_msg.data = DHCPV4_MSG_OFFER;
break;
iov[IOV_DNR_BODY].iov_len = dnrs_len;
break;
+ case DHCPV4_OPT_IPV6_ONLY_PREFERRED:
+ iov[IOV_IPV6_ONLY_PREF].iov_len = sizeof(reply_ipv6_only);
+ break;
+
case DHCPV4_OPT_CAPTIVE_PORTAL:
size_t uri_len = iface->captive_portal_uri_len;
if (uri_len == 0 || uri_len > UINT8_MAX)
#define DHCPV4_FR_MIN_DELAY 500
#define DHCPV4_FR_MAX_FUZZ 500
+// RFC8925, §3.4
+#define DHCPV4_MIN_V6ONLY_WAIT 300
+
// RFC4361, §6.1
#define DHCPV4_CLIENTID_TYPE_DUID_IAID 255
DHCPV4_OPT_CLIENTID = 61,
DHCPV4_OPT_USER_CLASS = 77,
DHCPV4_OPT_AUTHENTICATION = 90,
+ DHCPV4_OPT_IPV6_ONLY_PREFERRED = 108, // RFC8925
DHCPV4_OPT_CAPTIVE_PORTAL = 114, // RFC8910
DHCPV4_OPT_DNS_DOMAIN_SEARCH = 119,
DHCPV4_OPT_FORCERENEW_NONCE_CAPABLE = 145,
struct in_addr *dhcpv4_routers; // IPv4 addresses for routers on this subnet
size_t dhcpv4_routers_cnt; // Count of router addresses
bool dhcpv4_forcereconf;
+ uint32_t dhcpv4_v6only_wait; // V6ONLY_WAIT for the IPv6-only preferred option (RFC8925)
// DNS
struct in_addr *dns_addrs4; // IPv4 DNS server addresses to announce