diff --git a/src/network/networkctl.c b/src/network/networkctl.c index ec60c5e6b8..ffbda5dc0e 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -2359,8 +2359,15 @@ static int link_status_one( return table_log_add_error(r); } - if (captive_portal) - table_add_string_line(table, "Captive Portal:", captive_portal); + if (captive_portal) { + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "Captive Portal:", + TABLE_STRING, captive_portal, + TABLE_SET_URL, captive_portal); + if (r < 0) + return table_log_add_error(r); + } if (lease) { const void *client_id; diff --git a/src/network/networkd-dhcp-common.c b/src/network/networkd-dhcp-common.c index ca9a825e7b..5f9ef80e3e 100644 --- a/src/network/networkd-dhcp-common.c +++ b/src/network/networkd-dhcp-common.c @@ -259,6 +259,74 @@ bool address_is_filtered(int family, const union in_addr_union *address, uint8_t return false; } +int link_get_captive_portal(Link *link, const char **ret) { + const char *dhcp4_cp = NULL, *dhcp6_cp = NULL, *ndisc_cp = NULL; + int r; + + assert(link); + + if (!link->network) { + *ret = NULL; + return 0; + } + + if (link->network->dhcp_use_captive_portal && link->dhcp_lease) { + r = sd_dhcp_lease_get_captive_portal(link->dhcp_lease, &dhcp4_cp); + if (r < 0 && r != -ENODATA) + return r; + } + + if (link->network->dhcp6_use_captive_portal && link->dhcp6_lease) { + r = sd_dhcp6_lease_get_captive_portal(link->dhcp6_lease, &dhcp6_cp); + if (r < 0 && r != -ENODATA) + return r; + } + + if (link->network->ipv6_accept_ra_use_captive_portal) { + NDiscCaptivePortal *cp; + usec_t usec = 0; + + /* Use the captive portal with the longest lifetime. */ + + SET_FOREACH(cp, link->ndisc_captive_portals) { + if (cp->lifetime_usec < usec) + continue; + + ndisc_cp = cp->captive_portal; + usec = cp->lifetime_usec; + } + + if (set_size(link->ndisc_captive_portals) > 1) + log_link_debug(link, "Multiple captive portals obtained by IPv6RA, using \"%s\" and ignoring others.", + ndisc_cp); + } + + if (dhcp4_cp) { + if (dhcp6_cp && !streq(dhcp4_cp, dhcp6_cp)) + log_link_debug(link, "DHCPv6 captive portal (%s) does not match DHCPv4 (%s), ignoring DHCPv6 captive portal.", + dhcp6_cp, dhcp4_cp); + + if (ndisc_cp && !streq(dhcp4_cp, ndisc_cp)) + log_link_debug(link, "IPv6RA captive portal (%s) does not match DHCPv4 (%s), ignoring IPv6RA captive portal.", + ndisc_cp, dhcp4_cp); + + *ret = dhcp4_cp; + return 1; + } + + if (dhcp6_cp) { + if (ndisc_cp && !streq(dhcp6_cp, ndisc_cp)) + log_link_debug(link, "IPv6RA captive portal (%s) does not match DHCPv6 (%s), ignoring IPv6RA captive portal.", + ndisc_cp, dhcp6_cp); + + *ret = dhcp6_cp; + return 1; + } + + *ret = ndisc_cp; + return !!ndisc_cp; +} + int config_parse_dhcp( const char* unit, const char *filename, diff --git a/src/network/networkd-dhcp-common.h b/src/network/networkd-dhcp-common.h index 6f57b78a7f..07367d4d24 100644 --- a/src/network/networkd-dhcp-common.h +++ b/src/network/networkd-dhcp-common.h @@ -85,6 +85,8 @@ static inline bool in6_prefix_is_filtered(const struct in6_addr *prefix, uint8_t return address_is_filtered(AF_INET6, &(union in_addr_union) { .in6 = *prefix }, prefixlen, allow_list, deny_list); } +int link_get_captive_portal(Link *link, const char **ret); + const char* dhcp_use_domains_to_string(DHCPUseDomains p) _const_; DHCPUseDomains dhcp_use_domains_from_string(const char *s) _pure_; diff --git a/src/network/networkd-json.c b/src/network/networkd-json.c index 31ccb9679a..da26f797fe 100644 --- a/src/network/networkd-json.c +++ b/src/network/networkd-json.c @@ -7,6 +7,7 @@ #include "ip-protocol-list.h" #include "netif-util.h" #include "networkd-address.h" +#include "networkd-dhcp-common.h" #include "networkd-json.h" #include "networkd-link.h" #include "networkd-manager.h" @@ -874,31 +875,15 @@ finalize: } static int captive_portal_build_json(Link *link, JsonVariant **ret) { + const char *captive_portal; int r; - const char *captive_portal = NULL; assert(link); assert(ret); - if (!link->network) { - *ret = NULL; - return 0; - } - - if (link->network->dhcp_use_captive_portal && link->dhcp_lease) { - r = sd_dhcp_lease_get_captive_portal(link->dhcp_lease, &captive_portal); - if (r < 0 && r != -ENODATA) - return r; - } - - if (link->network->dhcp6_use_captive_portal && link->dhcp6_lease && !captive_portal) { - r = sd_dhcp6_lease_get_captive_portal(link->dhcp6_lease, &captive_portal); - if (r < 0 && r != -ENODATA) - return r; - } - - if (link->network->ipv6_accept_ra_use_captive_portal && !captive_portal) - captive_portal = link->ndisc_captive_portal; + r = link_get_captive_portal(link, &captive_portal); + if (r < 0) + return r; if (!captive_portal) { *ret = NULL; diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 7d9ea6f8e7..62dd892afa 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -196,7 +196,6 @@ static Link *link_free(Link *link) { free(link->ssid); free(link->previous_ssid); free(link->driver); - free(link->ndisc_captive_portal); unlink_and_free(link->lease_file); unlink_and_free(link->lldp_file); diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index bc8fe08374..b91259c487 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -154,7 +154,7 @@ typedef struct Link { sd_event_source *ndisc_expire; Set *ndisc_rdnss; Set *ndisc_dnssl; - char *ndisc_captive_portal; + Set *ndisc_captive_portals; unsigned ndisc_messages; bool ndisc_configured:1; diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 8115595dc5..64c192a539 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -269,17 +269,17 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { r = sd_ndisc_router_get_lifetime(rt, &lifetime_sec); if (r < 0) - return log_link_error_errno(link, r, "Failed to get gateway lifetime from RA: %m"); + return log_link_warning_errno(link, r, "Failed to get gateway lifetime from RA: %m"); r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, ×tamp_usec); if (r < 0) - return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); + return log_link_warning_errno(link, r, "Failed to get RA timestamp: %m"); lifetime_usec = sec16_to_usec(lifetime_sec, timestamp_usec); r = sd_ndisc_router_get_address(rt, &gateway); if (r < 0) - return log_link_error_errno(link, r, "Failed to get gateway address from RA: %m"); + return log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m"); if (link_get_ipv6_address(link, &gateway, 0, NULL) >= 0) { if (DEBUG_LOGGING) @@ -290,12 +290,12 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { r = sd_ndisc_router_get_preference(rt, &preference); if (r < 0) - return log_link_error_errno(link, r, "Failed to get default router preference from RA: %m"); + return log_link_warning_errno(link, r, "Failed to get default router preference from RA: %m"); if (link->network->ipv6_accept_ra_use_mtu) { r = sd_ndisc_router_get_mtu(rt, &mtu); if (r < 0 && r != -ENODATA) - return log_link_error_errno(link, r, "Failed to get default router MTU from RA: %m"); + return log_link_warning_errno(link, r, "Failed to get default router MTU from RA: %m"); } if (link->network->ipv6_accept_ra_use_gateway) { @@ -314,7 +314,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { r = ndisc_request_route(TAKE_PTR(route), link, rt); if (r < 0) - return log_link_error_errno(link, r, "Could not request default route: %m"); + return log_link_warning_errno(link, r, "Could not request default route: %m"); } Route *route_gw; @@ -340,7 +340,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { r = ndisc_request_route(TAKE_PTR(route), link, rt); if (r < 0) - return log_link_error_errno(link, r, "Could not request gateway: %m"); + return log_link_warning_errno(link, r, "Could not request gateway: %m"); } return 0; @@ -363,15 +363,15 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, ×tamp_usec); if (r < 0) - return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); + return log_link_warning_errno(link, r, "Failed to get RA timestamp: %m"); r = sd_ndisc_router_prefix_get_address(rt, &prefix); if (r < 0) - return log_link_error_errno(link, r, "Failed to get prefix address: %m"); + return log_link_warning_errno(link, r, "Failed to get prefix address: %m"); r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen); if (r < 0) - return log_link_error_errno(link, r, "Failed to get prefix length: %m"); + return log_link_warning_errno(link, r, "Failed to get prefix length: %m"); /* ndisc_generate_addresses() below requires the prefix length <= 64. */ if (prefixlen > 64) { @@ -382,11 +382,11 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_valid_sec); if (r < 0) - return log_link_error_errno(link, r, "Failed to get prefix valid lifetime: %m"); + return log_link_warning_errno(link, r, "Failed to get prefix valid lifetime: %m"); r = sd_ndisc_router_prefix_get_preferred_lifetime(rt, &lifetime_preferred_sec); if (r < 0) - return log_link_error_errno(link, r, "Failed to get prefix preferred lifetime: %m"); + return log_link_warning_errno(link, r, "Failed to get prefix preferred lifetime: %m"); /* The preferred lifetime is never greater than the valid lifetime */ if (lifetime_preferred_sec > lifetime_valid_sec) @@ -397,7 +397,7 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r r = ndisc_generate_addresses(link, &prefix, prefixlen, &addresses); if (r < 0) - return log_link_error_errno(link, r, "Failed to generate SLAAC addresses: %m"); + return log_link_warning_errno(link, r, "Failed to generate SLAAC addresses: %m"); SET_FOREACH(a, addresses) { _cleanup_(address_freep) Address *address = NULL; @@ -415,7 +415,7 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r r = ndisc_request_address(TAKE_PTR(address), link, rt); if (r < 0) - return log_link_error_errno(link, r, "Could not request SLAAC address: %m"); + return log_link_warning_errno(link, r, "Could not request SLAAC address: %m"); } return 0; @@ -438,19 +438,19 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) { r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_sec); if (r < 0) - return log_link_error_errno(link, r, "Failed to get prefix lifetime: %m"); + return log_link_warning_errno(link, r, "Failed to get prefix lifetime: %m"); r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, ×tamp_usec); if (r < 0) - return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); + return log_link_warning_errno(link, r, "Failed to get RA timestamp: %m"); r = sd_ndisc_router_prefix_get_address(rt, &prefix); if (r < 0) - return log_link_error_errno(link, r, "Failed to get prefix address: %m"); + return log_link_warning_errno(link, r, "Failed to get prefix address: %m"); r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen); if (r < 0) - return log_link_error_errno(link, r, "Failed to get prefix length: %m"); + return log_link_warning_errno(link, r, "Failed to get prefix length: %m"); r = route_new(&route); if (r < 0) @@ -463,7 +463,7 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) { r = ndisc_request_route(TAKE_PTR(route), link, rt); if (r < 0) - return log_link_error_errno(link, r, "Could not request prefix route: %m"); + return log_link_warning_errno(link, r, "Could not request prefix route: %m"); return 0; } @@ -480,7 +480,7 @@ static int ndisc_router_process_prefix(Link *link, sd_ndisc_router *rt) { r = sd_ndisc_router_prefix_get_address(rt, &a); if (r < 0) - return log_link_error_errno(link, r, "Failed to get prefix address: %m"); + return log_link_warning_errno(link, r, "Failed to get prefix address: %m"); /* RFC 4861 Section 4.6.2: * A router SHOULD NOT send a prefix option for the link-local prefix and a host SHOULD ignore such @@ -492,7 +492,7 @@ static int ndisc_router_process_prefix(Link *link, sd_ndisc_router *rt) { r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen); if (r < 0) - return log_link_error_errno(link, r, "Failed to get prefix length: %m"); + return log_link_warning_errno(link, r, "Failed to get prefix length: %m"); if (in6_prefix_is_filtered(&a, prefixlen, link->network->ndisc_allow_listed_prefix, link->network->ndisc_deny_listed_prefix)) { if (DEBUG_LOGGING) @@ -505,7 +505,7 @@ static int ndisc_router_process_prefix(Link *link, sd_ndisc_router *rt) { r = sd_ndisc_router_prefix_get_flags(rt, &flags); if (r < 0) - return log_link_error_errno(link, r, "Failed to get RA prefix flags: %m"); + return log_link_warning_errno(link, r, "Failed to get RA prefix flags: %m"); if (FLAGS_SET(flags, ND_OPT_PI_FLAG_ONLINK)) { r = ndisc_router_process_onlink_prefix(link, rt); @@ -537,15 +537,15 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) { r = sd_ndisc_router_route_get_lifetime(rt, &lifetime_sec); if (r < 0) - return log_link_error_errno(link, r, "Failed to get route lifetime from RA: %m"); + return log_link_warning_errno(link, r, "Failed to get route lifetime from RA: %m"); r = sd_ndisc_router_route_get_address(rt, &dst); if (r < 0) - return log_link_error_errno(link, r, "Failed to get route destination address: %m"); + return log_link_warning_errno(link, r, "Failed to get route destination address: %m"); r = sd_ndisc_router_route_get_prefixlen(rt, &prefixlen); if (r < 0) - return log_link_error_errno(link, r, "Failed to get route prefix length: %m"); + return log_link_warning_errno(link, r, "Failed to get route prefix length: %m"); if (in6_addr_is_null(&dst) && prefixlen == 0) { log_link_debug(link, "Route prefix is ::/0, ignoring"); @@ -566,7 +566,7 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) { r = sd_ndisc_router_get_address(rt, &gateway); if (r < 0) - return log_link_error_errno(link, r, "Failed to get gateway address from RA: %m"); + return log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m"); if (link_get_ipv6_address(link, &gateway, 0, NULL) >= 0) { if (DEBUG_LOGGING) @@ -577,11 +577,11 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) { r = sd_ndisc_router_route_get_preference(rt, &preference); if (r < 0) - return log_link_error_errno(link, r, "Failed to get default router preference from RA: %m"); + return log_link_warning_errno(link, r, "Failed to get default router preference from RA: %m"); r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, ×tamp_usec); if (r < 0) - return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); + return log_link_warning_errno(link, r, "Failed to get RA timestamp: %m"); r = route_new(&route); if (r < 0) @@ -597,7 +597,7 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) { r = ndisc_request_route(TAKE_PTR(route), link, rt); if (r < 0) - return log_link_error_errno(link, r, "Could not request additional route: %m"); + return log_link_warning_errno(link, r, "Could not request additional route: %m"); return 0; } @@ -634,21 +634,21 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) { r = sd_ndisc_router_get_address(rt, &router); if (r < 0) - return log_link_error_errno(link, r, "Failed to get router address from RA: %m"); + return log_link_warning_errno(link, r, "Failed to get router address from RA: %m"); r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, ×tamp_usec); if (r < 0) - return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); + return log_link_warning_errno(link, r, "Failed to get RA timestamp: %m"); r = sd_ndisc_router_rdnss_get_lifetime(rt, &lifetime_sec); if (r < 0) - return log_link_error_errno(link, r, "Failed to get RDNSS lifetime: %m"); + return log_link_warning_errno(link, r, "Failed to get RDNSS lifetime: %m"); lifetime_usec = sec_to_usec(lifetime_sec, timestamp_usec); n = sd_ndisc_router_rdnss_get_addresses(rt, &a); if (n < 0) - return log_link_error_errno(link, n, "Failed to get RDNSS addresses: %m"); + return log_link_warning_errno(link, n, "Failed to get RDNSS addresses: %m"); for (int j = 0; j < n; j++) { _cleanup_free_ NDiscRDNSS *x = NULL; @@ -716,43 +716,6 @@ DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( ndisc_dnssl_compare_func, free); -static int ndisc_router_process_captive_portal(Link *link, sd_ndisc_router *rt) { - const char *uri; - _cleanup_free_ char *captive_portal = NULL; - size_t len; - int r; - - assert(link); - assert(link->network); - assert(rt); - - if (!link->network->ipv6_accept_ra_use_captive_portal) - return 0; - - r = sd_ndisc_router_captive_portal_get_uri(rt, &uri, &len); - if (r < 0) - return r; - - if (len == 0) { - link->ndisc_captive_portal = mfree(link->ndisc_captive_portal); - return 0; - } - - r = make_cstring(uri, len, MAKE_CSTRING_REFUSE_TRAILING_NUL, &captive_portal); - if (r < 0) - return r; - - if (!in_charset(captive_portal, URI_VALID)) - return -EBADMSG; - - if (!streq_ptr(link->ndisc_captive_portal, captive_portal)) { - free_and_replace(link->ndisc_captive_portal, captive_portal); - link_dirty(link); - } - - return 0; -} - static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) { _cleanup_strv_free_ char **l = NULL; usec_t lifetime_usec, timestamp_usec; @@ -770,21 +733,21 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) { r = sd_ndisc_router_get_address(rt, &router); if (r < 0) - return log_link_error_errno(link, r, "Failed to get router address from RA: %m"); + return log_link_warning_errno(link, r, "Failed to get router address from RA: %m"); r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, ×tamp_usec); if (r < 0) - return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); + return log_link_warning_errno(link, r, "Failed to get RA timestamp: %m"); r = sd_ndisc_router_dnssl_get_lifetime(rt, &lifetime_sec); if (r < 0) - return log_link_error_errno(link, r, "Failed to get DNSSL lifetime: %m"); + return log_link_warning_errno(link, r, "Failed to get DNSSL lifetime: %m"); lifetime_usec = sec_to_usec(lifetime_sec, timestamp_usec); r = sd_ndisc_router_dnssl_get_domains(rt, &l); if (r < 0) - return log_link_error_errno(link, r, "Failed to get DNSSL addresses: %m"); + return log_link_warning_errno(link, r, "Failed to get DNSSL addresses: %m"); STRV_FOREACH(j, l) { _cleanup_free_ NDiscDNSSL *s = NULL; @@ -834,6 +797,108 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) { return 0; } +static NDiscCaptivePortal* ndisc_captive_portal_free(NDiscCaptivePortal *x) { + if (!x) + return NULL; + + free(x->captive_portal); + return mfree(x); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(NDiscCaptivePortal*, ndisc_captive_portal_free); + +static void ndisc_captive_portal_hash_func(const NDiscCaptivePortal *x, struct siphash *state) { + assert(x); + siphash24_compress_string(x->captive_portal, state); +} + +static int ndisc_captive_portal_compare_func(const NDiscCaptivePortal *a, const NDiscCaptivePortal *b) { + assert(a); + assert(b); + return strcmp_ptr(a->captive_portal, b->captive_portal); +} + +DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( + ndisc_captive_portal_hash_ops, + NDiscCaptivePortal, + ndisc_captive_portal_hash_func, + ndisc_captive_portal_compare_func, + free); + +static int ndisc_router_process_captive_portal(Link *link, sd_ndisc_router *rt) { + _cleanup_(ndisc_captive_portal_freep) NDiscCaptivePortal *new_entry = NULL; + _cleanup_free_ char *captive_portal = NULL; + usec_t lifetime_usec, timestamp_usec; + NDiscCaptivePortal *exist; + struct in6_addr router; + uint16_t lifetime_sec; + const char *uri; + size_t len; + int r; + + assert(link); + assert(link->network); + assert(rt); + + if (!link->network->ipv6_accept_ra_use_captive_portal) + return 0; + + r = sd_ndisc_router_get_address(rt, &router); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to get router address from RA: %m"); + + r = sd_ndisc_router_get_lifetime(rt, &lifetime_sec); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to get lifetime of RA message: %m"); + + r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, ×tamp_usec); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to get RA timestamp: %m"); + + lifetime_usec = sec16_to_usec(lifetime_sec, timestamp_usec); + + r = sd_ndisc_router_captive_portal_get_uri(rt, &uri, &len); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to get captive portal from RA: %m"); + + if (len == 0) + return 0; + + r = make_cstring(uri, len, MAKE_CSTRING_REFUSE_TRAILING_NUL, &captive_portal); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to convert captive portal URI: %m"); + + if (!in_charset(captive_portal, URI_VALID)) + return log_link_debug_errno(link, SYNTHETIC_ERRNO(EBADMSG), "Received invalid captive portal, ignoring."); + + exist = set_get(link->ndisc_captive_portals, &(NDiscCaptivePortal) { .captive_portal = captive_portal }); + if (exist) { + /* update existing entry */ + exist->router = router; + exist->lifetime_usec = lifetime_usec; + return 0; + } + + new_entry = new(NDiscCaptivePortal, 1); + if (!new_entry) + return log_oom(); + + *new_entry = (NDiscCaptivePortal) { + .router = router, + .lifetime_usec = lifetime_usec, + .captive_portal = TAKE_PTR(captive_portal), + }; + + r = set_ensure_put(&link->ndisc_captive_portals, &ndisc_captive_portal_hash_ops, new_entry); + if (r < 0) + return log_oom(); + assert(r > 0); + TAKE_PTR(new_entry); + + link_dirty(link); + return 0; +} + static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) { int r; @@ -845,13 +910,13 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) { uint8_t type; if (r < 0) - return log_link_error_errno(link, r, "Failed to iterate through options: %m"); + return log_link_warning_errno(link, r, "Failed to iterate through options: %m"); if (r == 0) /* EOF */ return 0; r = sd_ndisc_router_option_get_type(rt, &type); if (r < 0) - return log_link_error_errno(link, r, "Failed to get RA option type: %m"); + return log_link_warning_errno(link, r, "Failed to get RA option type: %m"); switch (type) { case SD_NDISC_OPTION_PREFIX_INFORMATION: @@ -882,6 +947,7 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) { bool updated = false; NDiscDNSSL *dnssl; NDiscRDNSS *rdnss; + NDiscCaptivePortal *cp; Address *address; Route *route; int r = 0, k; @@ -934,6 +1000,14 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) { updated = true; } + SET_FOREACH(cp, link->ndisc_captive_portals) { + if (cp->lifetime_usec >= timestamp_usec) + continue; /* the captive portal is still valid */ + + free(set_remove(link->ndisc_captive_portals, cp)); + updated = true; + } + if (updated) link_dirty(link); @@ -957,6 +1031,7 @@ static int ndisc_expire_handler(sd_event_source *s, uint64_t usec, void *userdat static int ndisc_setup_expire(Link *link) { usec_t lifetime_usec = USEC_INFINITY; + NDiscCaptivePortal *cp; NDiscDNSSL *dnssl; NDiscRDNSS *rdnss; Address *address; @@ -992,6 +1067,9 @@ static int ndisc_setup_expire(Link *link) { SET_FOREACH(dnssl, link->ndisc_dnssl) lifetime_usec = MIN(lifetime_usec, dnssl->lifetime_usec); + SET_FOREACH(cp, link->ndisc_captive_portals) + lifetime_usec = MIN(lifetime_usec, cp->lifetime_usec); + if (lifetime_usec == USEC_INFINITY) return 0; @@ -1040,7 +1118,7 @@ static int ndisc_start_dhcp6_client(Link *link, sd_ndisc_router *rt) { } if (r < 0) - return log_link_error_errno(link, r, "Could not acquire DHCPv6 lease on NDisc request: %m"); + return log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease on NDisc request: %m"); log_link_debug(link, "Acquiring DHCPv6 lease on NDisc request"); return 0; @@ -1062,7 +1140,7 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { return 0; } if (r < 0) - return log_link_error_errno(link, r, "Failed to get router address from RA: %m"); + return log_link_warning_errno(link, r, "Failed to get router address from RA: %m"); if (in6_prefix_is_filtered(&router, 128, link->network->ndisc_allow_listed_router, link->network->ndisc_deny_listed_router)) { if (DEBUG_LOGGING) { @@ -1254,10 +1332,11 @@ int ndisc_stop(Link *link) { void ndisc_flush(Link *link) { assert(link); - /* Removes all RDNSS and DNSSL entries, without exception */ + /* Remove all RDNSS, DNSSL, and Captive Portal entries, without exception. */ link->ndisc_rdnss = set_free(link->ndisc_rdnss); link->ndisc_dnssl = set_free(link->ndisc_dnssl); + link->ndisc_captive_portals = set_free(link->ndisc_captive_portals); } static const char* const ipv6_accept_ra_start_dhcp6_client_table[_IPV6_ACCEPT_RA_START_DHCP6_CLIENT_MAX] = { diff --git a/src/network/networkd-ndisc.h b/src/network/networkd-ndisc.h index 6ccc1295d0..267f7d4a02 100644 --- a/src/network/networkd-ndisc.h +++ b/src/network/networkd-ndisc.h @@ -18,7 +18,7 @@ typedef enum IPv6AcceptRAStartDHCP6Client { typedef struct NDiscRDNSS { struct in6_addr router; /* This is an absolute point in time, and NOT a timespan/duration. - * Must be specified with clock_boottime_or_monotonic(). */ + * Must be specified with CLOCK_BOOTTIME. */ usec_t lifetime_usec; struct in6_addr address; } NDiscRDNSS; @@ -26,11 +26,19 @@ typedef struct NDiscRDNSS { typedef struct NDiscDNSSL { struct in6_addr router; /* This is an absolute point in time, and NOT a timespan/duration. - * Must be specified with clock_boottime_or_monotonic(). */ + * Must be specified with CLOCK_BOOTTIME. */ usec_t lifetime_usec; /* The domain name follows immediately. */ } NDiscDNSSL; +typedef struct NDiscCaptivePortal { + struct in6_addr router; + /* This is an absolute point in time, and NOT a timespan/duration. + * Must be specified with CLOCK_BOOTTIME. */ + usec_t lifetime_usec; + char *captive_portal; +} NDiscCaptivePortal; + static inline char* NDISC_DNSSL_DOMAIN(const NDiscDNSSL *n) { return ((char*) n) + ALIGN(sizeof(NDiscDNSSL)); } diff --git a/src/network/networkd-state-file.c b/src/network/networkd-state-file.c index c9366e97d8..85c9d21082 100644 --- a/src/network/networkd-state-file.c +++ b/src/network/networkd-state-file.c @@ -10,6 +10,7 @@ #include "fileio.h" #include "fs-util.h" #include "network-internal.h" +#include "networkd-dhcp-common.h" #include "networkd-link.h" #include "networkd-manager-bus.h" #include "networkd-manager.h" @@ -465,7 +466,7 @@ 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, - *dhcp_captive_portal = NULL, *dhcp6_captive_portal = NULL; + *captive_portal; _cleanup_(unlink_and_freep) char *temp_path = NULL; _cleanup_fclose_ FILE *f = NULL; int r; @@ -607,37 +608,12 @@ 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; - } + r = link_get_captive_portal(link, &captive_portal); + if (r < 0) + return r; - if (link->dhcp6_lease && link->network->dhcp6_use_captive_portal) { - r = sd_dhcp6_lease_get_captive_portal(link->dhcp6_lease, &dhcp6_captive_portal); - if (r < 0 && r != -ENODATA) - return r; - } - - if (dhcp6_captive_portal && dhcp_captive_portal && !streq(dhcp_captive_portal, dhcp6_captive_portal)) - log_link_warning(link, "DHCPv6 Captive Portal (%s) does not match DHCPv4 (%s). Ignoring DHCPv6 portal.", - dhcp6_captive_portal, dhcp_captive_portal); - - if (link->network->ipv6_accept_ra_use_captive_portal && link->ndisc_captive_portal) { - if (dhcp_captive_portal && !streq(dhcp_captive_portal, link->ndisc_captive_portal)) - log_link_warning(link, "IPv6RA captive portal (%s) does not match DHCPv4 (%s). Ignorning IPv6RA portal.", - link->ndisc_captive_portal, dhcp_captive_portal); - if (dhcp6_captive_portal && !streq(dhcp6_captive_portal, link->ndisc_captive_portal)) - log_link_warning(link, "IPv6RA captive portal (%s) does not match DHCPv6 (%s). Ignorning IPv6RA portal.", - link->ndisc_captive_portal, dhcp6_captive_portal); - } - - if (dhcp_captive_portal) - fprintf(f, "CAPTIVE_PORTAL=%s\n", dhcp_captive_portal); - else if (dhcp6_captive_portal) - fprintf(f, "CAPTIVE_PORTAL=%s\n", dhcp6_captive_portal); - else if (link->ndisc_captive_portal) - fprintf(f, "CAPTIVE_PORTAL=%s\n", link->ndisc_captive_portal); + if (captive_portal) + fprintf(f, "CAPTIVE_PORTAL=%s\n", captive_portal); /************************************************************/ diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index be3ac67216..3138a102b7 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -357,17 +357,12 @@ def create_unit_dropin(unit, contents): with open(f'/run/systemd/system/{unit}.d/00-override.conf', mode='w', encoding='utf-8') as f: f.write('\n'.join(contents)) -def create_service_dropin(service, command, reload_command=None, additional_settings=None): +def create_service_dropin(service, command, additional_settings=None): drop_in = [ '[Service]', 'ExecStart=', f'ExecStart=!!{valgrind_cmd}{command}', ] - if reload_command: - drop_in += [ - 'ExecReload=', - f'ExecReload={valgrind_cmd}{reload_command}', - ] if enable_debug: drop_in += ['Environment=SYSTEMD_LOG_LEVEL=debug'] if asan_options: @@ -688,7 +683,6 @@ def setUpModule(): save_timezone() create_service_dropin('systemd-networkd', networkd_bin, - f'{networkctl_bin} reload', ['[Service]', 'Restart=no', '[Unit]', 'StartLimitIntervalSec=0']) create_service_dropin('systemd-resolved', resolved_bin) create_service_dropin('systemd-timesyncd', timesyncd_bin) @@ -702,8 +696,6 @@ def setUpModule(): '[Service]', 'ExecStart=', f'ExecStart=!!{udevd_bin}', - 'ExecReload=', - f'ExecReload={udevadm_bin} control --reload --timeout 0', ] ) create_unit_dropin(