summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Donald2025-11-17 19:43:15 +0000
committerÁlvaro Fernández Rojas2025-11-18 10:53:02 +0000
commit9830e5e2bf375a7b74fa4ffc72330fccf0da6097 (patch)
tree2a45fa08011d96bbb4d7c98b832a1179fe2df64a
parentd1500bb5d64d60e3fa4ec7ddb60501162002db3e (diff)
downloadodhcpd-9830e5e2bf375a7b74fa4ffc72330fccf0da6097.tar.gz
all: implement RFC8910 captive portal (CP) option for DHCPv4
https://www.rfc-editor.org/rfc/rfc8910.html Signed-off-by: Paul Donald <newtwen+github@gmail.com> Link: https://github.com/openwrt/odhcpd/pull/315 Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
-rw-r--r--README.md2
-rw-r--r--src/config.c5
-rw-r--r--src/dhcpv4.c19
-rw-r--r--src/dhcpv4.h1
4 files changed, 26 insertions, 1 deletions
diff --git a/README.md b/README.md
index 1ac7742..2d8c3f2 100644
--- a/README.md
+++ b/README.md
@@ -121,7 +121,7 @@ and may also receive information from ubus
| prefix_filter |string |`::/0` | Only advertise on-link prefixes within the provided IPv6 prefix; others are filtered out. [IPv6 prefix] |
| 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 DHCPv6 and ICMPv6 RA. |
+| captive_portal_uri |string | no | The API URI to be sent in RFC8910 captive portal options, via DHCPv4, DHCPv6, and ICMPv6 RA. |
[//]: # "dhcpv6_raw - string - not documented, may change when generic DHCPv4/DHCPv6 options are added"
diff --git a/src/config.c b/src/config.c
index b2991ef..865dadf 100644
--- a/src/config.c
+++ b/src/config.c
@@ -1310,6 +1310,11 @@ int config_parse_interface(void *data, size_t len, const char *name, bool overwr
if ((c = tb[IFACE_ATTR_CAPTIVE_PORTAL_URI])) {
iface->captive_portal_uri = strdup(blobmsg_get_string(c));
iface->captive_portal_uri_len = strlen(iface->captive_portal_uri);
+ if (iface->captive_portal_uri_len > UINT8_MAX) {
+ warn("RFC8910 captive portal URI > %d characters for interface '%s': option via DHCPv4 not possible",
+ UINT8_MAX,
+ iface->name);
+ }
debug("Set RFC8910 captive portal URI: '%s' for interface '%s'",
iface->captive_portal_uri, iface->name);
}
diff --git a/src/dhcpv4.c b/src/dhcpv4.c
index cf5abf5..9ab7869 100644
--- a/src/dhcpv4.c
+++ b/src/dhcpv4.c
@@ -779,6 +779,7 @@ enum {
IOV_FR_NONCE_CAP,
IOV_DNR,
IOV_DNR_BODY,
+ IOV_CAPTIVE_PORTAL,
IOV_END,
IOV_PADDING,
IOV_TOTAL
@@ -920,6 +921,7 @@ void dhcpv4_handle_msg(void *src_addr, void *data, size_t len,
[IOV_FR_NONCE_CAP] = { &reply_fr_nonce_cap, 0 },
[IOV_DNR] = { &reply_dnr, 0 },
[IOV_DNR_BODY] = { NULL, 0 },
+ [IOV_CAPTIVE_PORTAL] = { NULL, 0 },
[IOV_END] = { &reply_end, sizeof(reply_end) },
[IOV_PADDING] = { NULL, 0 },
};
@@ -938,6 +940,7 @@ void dhcpv4_handle_msg(void *src_addr, void *data, size_t len,
DHCPV4_OPT_CLIENTID, // Must be in reply if present in req, RFC6842, §3
DHCPV4_OPT_AUTHENTICATION,
DHCPV4_OPT_SEARCH_DOMAIN,
+ DHCPV4_OPT_CAPTIVE_PORTAL,
DHCPV4_OPT_FORCERENEW_NONCE_CAPABLE,
};
@@ -1296,6 +1299,22 @@ void dhcpv4_handle_msg(void *src_addr, void *data, size_t len,
iov[IOV_DNR_BODY].iov_base = dnrs;
iov[IOV_DNR_BODY].iov_len = dnrs_len;
break;
+
+ case DHCPV4_OPT_CAPTIVE_PORTAL:
+ size_t uri_len = iface->captive_portal_uri_len;
+ if (uri_len == 0 || uri_len > UINT8_MAX)
+ break;
+
+ uint8_t *buf = alloca(2 + uri_len);
+ struct dhcpv4_option *opt = (struct dhcpv4_option *)buf;
+
+ opt->code = DHCPV4_OPT_CAPTIVE_PORTAL;
+ opt->len = uri_len;
+ memcpy(opt->data, iface->captive_portal_uri, uri_len);
+
+ iov[IOV_CAPTIVE_PORTAL].iov_base = opt;
+ iov[IOV_CAPTIVE_PORTAL].iov_len = 2 + uri_len;
+ break;
}
}
diff --git a/src/dhcpv4.h b/src/dhcpv4.h
index d2b31a3..f428022 100644
--- a/src/dhcpv4.h
+++ b/src/dhcpv4.h
@@ -78,6 +78,7 @@ enum dhcpv4_opt {
DHCPV4_OPT_CLIENTID = 61,
DHCPV4_OPT_USER_CLASS = 77,
DHCPV4_OPT_AUTHENTICATION = 90,
+ DHCPV4_OPT_CAPTIVE_PORTAL = 114, // RFC8910
DHCPV4_OPT_SEARCH_DOMAIN = 119,
DHCPV4_OPT_FORCERENEW_NONCE_CAPABLE = 145,
DHCPV4_OPT_DNR = 162,