diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 1b34000052..452d2835b0 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -1969,6 +1969,14 @@ allow my_server_t localnet_peer_t:peer recv;
+
+ UseCaptivePortal=
+
+ When true (the default), the captive portal advertised by the DHCP server will be recorded
+ and made available to client programs and displayed in the networkctl status output per-link.
+
+
+
UseMTU=
diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c
index dd440a5d17..cf3c400dbc 100644
--- a/src/libsystemd/sd-network/sd-network.c
+++ b/src/libsystemd/sd-network/sd-network.c
@@ -258,6 +258,10 @@ int sd_network_link_get_sip(int ifindex, char ***ret) {
return network_link_get_strv(ifindex, "SIP", ret);
}
+int sd_network_link_get_captive_portal(int ifindex, char **ret) {
+ return network_link_get_string(ifindex, "CAPTIVE_PORTAL", ret);
+}
+
int sd_network_link_get_search_domains(int ifindex, char ***ret) {
return network_link_get_strv(ifindex, "DOMAINS", ret);
}
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index d4b4942173..ce6ce230bc 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -1426,6 +1426,11 @@ static int dhcp4_configure(Link *link) {
if (r < 0)
return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for SIP server: %m");
}
+ if (link->network->dhcp_use_captive_portal) {
+ r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_DHCP_CAPTIVE_PORTAL);
+ if (r < 0)
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for captive portal: %m");
+ }
if (link->network->dhcp_use_timezone) {
r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_TZDB_TIMEZONE);
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 716904cc34..92ea9eaeef 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -216,6 +216,7 @@ DHCPv4.RoutesToDNS, config_parse_bool,
DHCPv4.UseNTP, config_parse_dhcp_use_ntp, AF_INET, 0
DHCPv4.RoutesToNTP, config_parse_bool, 0, offsetof(Network, dhcp_routes_to_ntp)
DHCPv4.UseSIP, config_parse_bool, 0, offsetof(Network, dhcp_use_sip)
+DHCPv4.UseCaptivePortal, config_parse_bool, 0, offsetof(Network, dhcp_use_captive_portal)
DHCPv4.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_use_mtu)
DHCPv4.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_use_hostname)
DHCPv4.UseDomains, config_parse_dhcp_use_domains, AF_INET, 0
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 2423d891d3..b2a2d8052c 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -395,6 +395,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
.dhcp_use_ntp = true,
.dhcp_routes_to_ntp = true,
.dhcp_use_sip = true,
+ .dhcp_use_captive_portal = true,
.dhcp_use_dns = true,
.dhcp_routes_to_dns = true,
.dhcp_use_hostname = true,
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index c692fad991..b4666f1d63 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -143,6 +143,7 @@ struct Network {
bool dhcp_use_ntp_set;
bool dhcp_routes_to_ntp;
bool dhcp_use_sip;
+ bool dhcp_use_captive_portal;
bool dhcp_use_mtu;
bool dhcp_use_routes;
int dhcp_use_gateway;
diff --git a/src/network/networkd-state-file.c b/src/network/networkd-state-file.c
index 6e962c03f6..e313d9e918 100644
--- a/src/network/networkd-state-file.c
+++ b/src/network/networkd-state-file.c
@@ -464,7 +464,8 @@ static void link_save_domains(Link *link, FILE *f, OrderedSet *static_domains, D
}
int link_save(Link *link) {
- const char *admin_state, *oper_state, *carrier_state, *address_state, *ipv4_address_state, *ipv6_address_state;
+ const char *admin_state, *oper_state, *carrier_state, *address_state, *ipv4_address_state, *ipv6_address_state,
+ *dhcp_captive_portal = NULL;
_cleanup_(unlink_and_freep) char *temp_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
int r;
@@ -606,6 +607,17 @@ int link_save(Link *link) {
/************************************************************/
+ if (link->dhcp_lease && link->network->dhcp_use_captive_portal) {
+ r = sd_dhcp_lease_get_captive_portal(link->dhcp_lease, &dhcp_captive_portal);
+ if (r < 0 && r != -ENODATA)
+ return r;
+ }
+
+ if (dhcp_captive_portal)
+ fprintf(f, "CAPTIVE_PORTAL=%s\n", dhcp_captive_portal);
+
+ /************************************************************/
+
fputs("DOMAINS=", f);
if (link->search_domains)
link_save_domains(link, f, link->search_domains, DHCP_USE_DOMAINS_NO);
diff --git a/src/systemd/sd-network.h b/src/systemd/sd-network.h
index 9cc2cbaa6e..d292719a3e 100644
--- a/src/systemd/sd-network.h
+++ b/src/systemd/sd-network.h
@@ -134,6 +134,9 @@ int sd_network_link_get_ntp(int ifindex, char ***ret);
* representations of IP addresses */
int sd_network_link_get_sip(int ifindex, char ***ret);
+/* Get the captive portal address for a given link. */
+int sd_network_link_get_captive_portal(int ifindex, char **ret);
+
/* Indicates whether or not LLMNR should be enabled for the link
* Possible levels of support: yes, no, resolve
* Possible return codes: