From 9747955d2d60b818d008c7a3c255aedf8de1c673 Mon Sep 17 00:00:00 2001 From: Ronan Pigott Date: Thu, 29 Jun 2023 16:22:45 -0700 Subject: [PATCH] ndisc: parse RFC8910 captive portal ipv6ra option --- src/libsystemd-network/ndisc-router.c | 42 +++++++++++++++++++++++++++ src/network/networkd-link.c | 1 + src/network/networkd-link.h | 1 + src/network/networkd-ndisc.c | 40 +++++++++++++++++++++++++ src/network/networkd-network.c | 1 + src/network/networkd-network.h | 1 + src/systemd/sd-ndisc.h | 3 ++ 7 files changed, 89 insertions(+) diff --git a/src/libsystemd-network/ndisc-router.c b/src/libsystemd-network/ndisc-router.c index e4cbf714b97..997ac575862 100644 --- a/src/libsystemd-network/ndisc-router.c +++ b/src/libsystemd-network/ndisc-router.c @@ -715,3 +715,45 @@ int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router *rt, uint32_t *ret_sec) { *ret_sec = be32toh(*(uint32_t*) (ri + 4)); return 0; } + +int sd_ndisc_router_captive_portal_get_uri(sd_ndisc_router *rt, const char **ret_uri, size_t *ret_size) { + int r; + const char *nd_opt_captive_portal; + size_t length; + + assert_return(rt, -EINVAL); + assert_return(ret_uri, -EINVAL); + + r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_CAPTIVE_PORTAL); + if (r < 0) + return r; + if (r == 0) + return -EMEDIUMTYPE; + + r = sd_ndisc_router_option_get_raw(rt, (void *)&nd_opt_captive_portal, &length); + if (r < 0) + return r; + + /* The length field has units of 8 octets */ + assert(length % 8 == 0); + if (length == 0) + return -EBADMSG; + + /* Check that the message is not truncated by an embedded NUL. + * NUL padding to a multiple of 8 is expected. */ + size_t size = strnlen(nd_opt_captive_portal + 2, length - 2); + if (DIV_ROUND_UP(size + 2, 8) != length / 8) + return -EBADMSG; + + /* Let's not return an empty buffer */ + if (size == 0) { + *ret_uri = NULL; + *ret_size = 0; + return 0; + } + + *ret_uri = nd_opt_captive_portal + 2; + *ret_size = size; + + return 0; +} diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 62dd892afaa..7d9ea6f8e7b 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -196,6 +196,7 @@ 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 0d601ab5482..bc8fe083746 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -154,6 +154,7 @@ typedef struct Link { sd_event_source *ndisc_expire; Set *ndisc_rdnss; Set *ndisc_dnssl; + char *ndisc_captive_portal; unsigned ndisc_messages; bool ndisc_configured:1; diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 99a07e16fcf..da5312c5ff4 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -716,6 +716,43 @@ 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) { + 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 -EINVAL; + + 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; @@ -832,6 +869,9 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) { case SD_NDISC_OPTION_DNSSL: r = ndisc_router_process_dnssl(link, rt); break; + case SD_NDISC_OPTION_CAPTIVE_PORTAL: + r = ndisc_router_process_captive_portal(link, rt); + break; } if (r < 0 && r != -EBADMSG) return r; diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 9a0511eeef7..2423d891d3b 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -476,6 +476,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi .ipv6_accept_ra = -1, .ipv6_accept_ra_use_dns = true, .ipv6_accept_ra_use_gateway = true, + .ipv6_accept_ra_use_captive_portal = true, .ipv6_accept_ra_use_route_prefix = true, .ipv6_accept_ra_use_autonomous_prefix = true, .ipv6_accept_ra_use_onlink_prefix = true, diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 7685c98f652..c692fad991b 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -315,6 +315,7 @@ struct Network { bool ipv6_accept_ra_use_onlink_prefix; bool ipv6_accept_ra_use_mtu; bool ipv6_accept_ra_quickack; + bool ipv6_accept_ra_use_captive_portal; bool active_slave; bool primary_slave; DHCPUseDomains ipv6_accept_ra_use_domains; diff --git a/src/systemd/sd-ndisc.h b/src/systemd/sd-ndisc.h index ee309a4253e..b4faa4428e5 100644 --- a/src/systemd/sd-ndisc.h +++ b/src/systemd/sd-ndisc.h @@ -123,6 +123,9 @@ int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router *rt, uint32_t *ret); int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router *rt, char ***ret); int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router *rt, uint32_t *ret); +/* Specific option access: SD_NDISC_OPTION_CAPTIVE_PORTAL */ +int sd_ndisc_router_captive_portal_get_uri(sd_ndisc_router *rt, const char **uri, size_t *size); + _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc, sd_ndisc_unref); _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc_router, sd_ndisc_router_unref);