From dd6bbb302791b40d31ff0913cbc1a618595e605c Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 9 Mar 2024 00:08:27 +0900 Subject: [PATCH 1/6] sd-ndisc: rename ndisc-protocol.[ch] -> ndisc-option.[ch] --- src/libsystemd-network/meson.build | 2 +- src/libsystemd-network/{ndisc-protocol.c => ndisc-option.c} | 2 +- src/libsystemd-network/{ndisc-protocol.h => ndisc-option.h} | 0 src/libsystemd-network/radv-internal.h | 2 +- src/libsystemd-network/sd-ndisc-router.c | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename src/libsystemd-network/{ndisc-protocol.c => ndisc-option.c} (98%) rename src/libsystemd-network/{ndisc-protocol.h => ndisc-option.h} (100%) diff --git a/src/libsystemd-network/meson.build b/src/libsystemd-network/meson.build index a2e5106b42..e4340f9957 100644 --- a/src/libsystemd-network/meson.build +++ b/src/libsystemd-network/meson.build @@ -12,7 +12,7 @@ sources = files( 'icmp6-util.c', 'lldp-neighbor.c', 'lldp-network.c', - 'ndisc-protocol.c', + 'ndisc-option.c', 'network-common.c', 'network-internal.c', 'sd-dhcp-client-id.c', diff --git a/src/libsystemd-network/ndisc-protocol.c b/src/libsystemd-network/ndisc-option.c similarity index 98% rename from src/libsystemd-network/ndisc-protocol.c rename to src/libsystemd-network/ndisc-option.c index d1f0819e14..ad32129e46 100644 --- a/src/libsystemd-network/ndisc-protocol.c +++ b/src/libsystemd-network/ndisc-option.c @@ -2,7 +2,7 @@ #include -#include "ndisc-protocol.h" +#include "ndisc-option.h" int ndisc_option_parse( ICMP6Packet *p, diff --git a/src/libsystemd-network/ndisc-protocol.h b/src/libsystemd-network/ndisc-option.h similarity index 100% rename from src/libsystemd-network/ndisc-protocol.h rename to src/libsystemd-network/ndisc-option.h diff --git a/src/libsystemd-network/radv-internal.h b/src/libsystemd-network/radv-internal.h index d6cec904b0..8091a5d882 100644 --- a/src/libsystemd-network/radv-internal.h +++ b/src/libsystemd-network/radv-internal.h @@ -10,7 +10,7 @@ #include "sd-radv.h" #include "list.h" -#include "ndisc-protocol.h" +#include "ndisc-option.h" #include "network-common.h" #include "sparse-endian.h" #include "time-util.h" diff --git a/src/libsystemd-network/sd-ndisc-router.c b/src/libsystemd-network/sd-ndisc-router.c index 4a95d7dba7..309ef10195 100644 --- a/src/libsystemd-network/sd-ndisc-router.c +++ b/src/libsystemd-network/sd-ndisc-router.c @@ -14,7 +14,7 @@ #include "memory-util.h" #include "missing_network.h" #include "ndisc-internal.h" -#include "ndisc-protocol.h" +#include "ndisc-option.h" #include "ndisc-router-internal.h" #include "strv.h" From a163404cc88914142ef8bbfaab0eb39d1a990c02 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 29 Feb 2024 12:31:58 +0900 Subject: [PATCH 2/6] ndisc-option: introduce generic NDisc option parser It is not used in this commit, but will be used for parsing NDisc options in Router Advertisement message and friends. The parser does mostly equivalent to what currently we do in sd-ndisc-router.c. Several notable differences are: - also perse source and target link-layer address, - refuse multiple captive portals, - check if the captive portal is in safe characters, as previously we checked that in networkd-ndisc.c, - dedup prefixes, routes, and pref64, - limit the total number of options, for safety. --- src/libsystemd-network/ndisc-option.c | 798 +++++++++++++++++++++++++- src/libsystemd-network/ndisc-option.h | 124 ++++ src/systemd/sd-ndisc-protocol.h | 8 +- 3 files changed, 924 insertions(+), 6 deletions(-) diff --git a/src/libsystemd-network/ndisc-option.c b/src/libsystemd-network/ndisc-option.c index ad32129e46..cddde0cabf 100644 --- a/src/libsystemd-network/ndisc-option.c +++ b/src/libsystemd-network/ndisc-option.c @@ -2,7 +2,19 @@ #include +#include "dns-domain.h" +#include "ether-addr-util.h" +#include "hostname-util.h" +#include "in-addr-util.h" +#include "missing_network.h" #include "ndisc-option.h" +#include "network-common.h" +#include "strv.h" +#include "unaligned.h" + +/* RFC does not say anything about the maximum number of options, but let's limit the number of options for + * safety. Typically, the number of options in an ICMPv6 message should be only a few. */ +#define MAX_OPTIONS 128 int ndisc_option_parse( ICMP6Packet *p, @@ -41,6 +53,593 @@ int ndisc_option_parse( return 0; } +static sd_ndisc_option* ndisc_option_new(uint8_t type, size_t offset) { + sd_ndisc_option *p = new0(sd_ndisc_option, 1); /* use new0() here to make the fuzzers silent. */ + if (!p) + return NULL; + + /* As the same reason in the above, do not use the structured initializer here. */ + p->type = type; + p->offset = offset; + + return p; +} + +static void ndisc_rdnss_done(sd_ndisc_rdnss *rdnss) { + if (!rdnss) + return; + + free(rdnss->addresses); +} + +static void ndisc_dnssl_done(sd_ndisc_dnssl *dnssl) { + if (!dnssl) + return; + + strv_free(dnssl->domains); +} + +sd_ndisc_option* ndisc_option_free(sd_ndisc_option *option) { + if (!option) + return NULL; + + switch (option->type) { + case SD_NDISC_OPTION_RDNSS: + ndisc_rdnss_done(&option->rdnss); + break; + + case SD_NDISC_OPTION_DNSSL: + ndisc_dnssl_done(&option->dnssl); + break; + + case SD_NDISC_OPTION_CAPTIVE_PORTAL: + free(option->captive_portal); + break; + } + + return mfree(option); +} + +static int ndisc_option_compare_func(const sd_ndisc_option *x, const sd_ndisc_option *y) { + int r; + + assert(x); + assert(y); + + r = CMP(x->type, y->type); + if (r != 0) + return r; + + switch (x->type) { + case SD_NDISC_OPTION_SOURCE_LL_ADDRESS: + case SD_NDISC_OPTION_TARGET_LL_ADDRESS: + case SD_NDISC_OPTION_REDIRECTED_HEADER: + case SD_NDISC_OPTION_MTU: + case SD_NDISC_OPTION_FLAGS_EXTENSION: + case SD_NDISC_OPTION_CAPTIVE_PORTAL: + /* These options cannot be specified multiple times. */ + return 0; + + case SD_NDISC_OPTION_PREFIX_INFORMATION: + /* Should not specify the same prefix multiple times. */ + r = CMP(x->prefix.prefixlen, y->prefix.prefixlen); + if (r != 0) + return r; + + return memcmp(&x->prefix.address, &y->prefix.address, sizeof(struct in6_addr)); + + case SD_NDISC_OPTION_ROUTE_INFORMATION: + r = CMP(x->route.prefixlen, y->route.prefixlen); + if (r != 0) + return r; + + return memcmp(&x->route.address, &y->route.address, sizeof(struct in6_addr)); + + case SD_NDISC_OPTION_PREF64: + r = CMP(x->prefix64.prefixlen, y->prefix64.prefixlen); + if (r != 0) + return r; + + return memcmp(&x->prefix64.prefix, &y->prefix64.prefix, sizeof(struct in6_addr)); + + default: + /* DNSSL, RDNSS, and other unsupported options can be specified multiple times. */ + return trivial_compare_func(x, y); + } +} + +static void ndisc_option_hash_func(const sd_ndisc_option *option, struct siphash *state) { + assert(option); + assert(state); + + siphash24_compress_typesafe(option->type, state); + + switch (option->type) { + case SD_NDISC_OPTION_SOURCE_LL_ADDRESS: + case SD_NDISC_OPTION_TARGET_LL_ADDRESS: + case SD_NDISC_OPTION_REDIRECTED_HEADER: + case SD_NDISC_OPTION_MTU: + case SD_NDISC_OPTION_FLAGS_EXTENSION: + case SD_NDISC_OPTION_CAPTIVE_PORTAL: + break; + + case SD_NDISC_OPTION_PREFIX_INFORMATION: + siphash24_compress_typesafe(option->prefix.prefixlen, state); + siphash24_compress_typesafe(option->prefix.address, state); + break; + + case SD_NDISC_OPTION_ROUTE_INFORMATION: + siphash24_compress_typesafe(option->route.prefixlen, state); + siphash24_compress_typesafe(option->route.address, state); + break; + + case SD_NDISC_OPTION_PREF64: + siphash24_compress_typesafe(option->prefix64.prefixlen, state); + siphash24_compress_typesafe(option->prefix64.prefix, state); + break; + + default: + trivial_hash_func(option, state); + } +} + +DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( + ndisc_option_hash_ops, + sd_ndisc_option, + ndisc_option_hash_func, + ndisc_option_compare_func, + ndisc_option_free); + +static int ndisc_option_consume(Set **options, sd_ndisc_option *p) { + if (set_size(*options) >= MAX_OPTIONS) { + ndisc_option_free(p); + return -ETOOMANYREFS; /* recognizable error code */ + } + + return set_ensure_consume(options, &ndisc_option_hash_ops, p); +} + +int ndisc_option_add_link_layer_address(Set **options, uint8_t opt, size_t offset, const struct ether_addr *mac) { + assert(options); + assert(IN_SET(opt, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, SD_NDISC_OPTION_TARGET_LL_ADDRESS)); + assert(mac); + + if (ether_addr_is_null(mac)) + return -EINVAL; + + sd_ndisc_option *p = ndisc_option_new(opt, offset); + if (!p) + return -ENOMEM; + + p->mac = *mac; + + return set_ensure_consume(options, &ndisc_option_hash_ops, p); +} + +static int ndisc_option_parse_link_layer_address(Set **options, size_t offset, size_t len, const uint8_t *opt) { + assert(options); + assert(opt); + + if (len != sizeof(struct ether_addr) + 2) + return -EBADMSG; + + if (!IN_SET(opt[0], SD_NDISC_OPTION_SOURCE_LL_ADDRESS, SD_NDISC_OPTION_TARGET_LL_ADDRESS)) + return -EBADMSG; + + struct ether_addr mac; + memcpy(&mac, opt + 2, sizeof(struct ether_addr)); + + return ndisc_option_add_link_layer_address(options, opt[0], offset, &mac); +} + +int ndisc_option_add_prefix( + Set **options, + size_t offset, + uint8_t flags, + uint8_t prefixlen, + const struct in6_addr *address, + usec_t valid_lifetime, + usec_t preferred_lifetime) { + + assert(options); + assert(address); + + if (prefixlen > 128) + return -EINVAL; + + if (in6_addr_is_link_local(address)) + return -EINVAL; + + if (preferred_lifetime > valid_lifetime) + return -EINVAL; + + sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_PREFIX_INFORMATION, offset); + if (!p) + return -ENOMEM; + + p->prefix = (sd_ndisc_prefix) { + .flags = flags, + .prefixlen = prefixlen, + .address = *address, + .valid_lifetime = valid_lifetime, + .preferred_lifetime = preferred_lifetime, + }; + + in6_addr_mask(&p->prefix.address, p->prefix.prefixlen); + + return ndisc_option_consume(options, p); +} + +static int ndisc_option_parse_prefix(Set **options, size_t offset, size_t len, const uint8_t *opt) { + const struct nd_opt_prefix_info *pi = (const struct nd_opt_prefix_info*) ASSERT_PTR(opt); + + assert(options); + + if (len != sizeof(struct nd_opt_prefix_info)) + return -EBADMSG; + + if (pi->nd_opt_pi_type != SD_NDISC_OPTION_PREFIX_INFORMATION) + return -EBADMSG; + + usec_t valid = be32_sec_to_usec(pi->nd_opt_pi_valid_time, /* max_as_infinity = */ true); + usec_t pref = be32_sec_to_usec(pi->nd_opt_pi_preferred_time, /* max_as_infinity = */ true); + + /* We only support 64 bits interface identifier for addrconf. */ + uint8_t flags = pi->nd_opt_pi_flags_reserved; + if (FLAGS_SET(flags, ND_OPT_PI_FLAG_AUTO) && pi->nd_opt_pi_prefix_len != 64) + flags &= ~ND_OPT_PI_FLAG_AUTO; + + return ndisc_option_add_prefix(options, offset, flags, pi->nd_opt_pi_prefix_len, &pi->nd_opt_pi_prefix, valid, pref); +} + +int ndisc_option_add_redirected_header(Set **options, size_t offset, const struct ip6_hdr *hdr) { + assert(options); + assert(hdr); + + sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_REDIRECTED_HEADER, offset); + if (!p) + return -ENOMEM; + + /* For safety, here we copy only IPv6 header. */ + memcpy(&p->hdr, hdr, sizeof(struct ip6_hdr)); + + return ndisc_option_consume(options, p); +} + +static int ndisc_option_parse_redirected_header(Set **options, size_t offset, size_t len, const uint8_t *opt) { + assert(options); + assert(opt); + + if (len < sizeof(struct nd_opt_rd_hdr) + sizeof(struct ip6_hdr)) + return -EBADMSG; + + if (opt[0] != SD_NDISC_OPTION_REDIRECTED_HEADER) + return -EBADMSG; + + return ndisc_option_add_redirected_header(options, offset, (const struct ip6_hdr*) (opt + sizeof(struct nd_opt_rd_hdr))); +} + +int ndisc_option_add_mtu(Set **options, size_t offset, uint32_t mtu) { + assert(options); + + if (mtu < IPV6_MIN_MTU) + return -EINVAL; + + sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_MTU, offset); + if (!p) + return -ENOMEM; + + p->mtu = mtu; + + return ndisc_option_consume(options, p); +} + +static int ndisc_option_parse_mtu(Set **options, size_t offset, size_t len, const uint8_t *opt) { + const struct nd_opt_mtu *pm = (const struct nd_opt_mtu*) ASSERT_PTR(opt); + + assert(options); + + if (len != sizeof(struct nd_opt_mtu)) + return -EBADMSG; + + if (pm->nd_opt_mtu_type != SD_NDISC_OPTION_MTU) + return -EBADMSG; + + return ndisc_option_add_mtu(options, offset, be32toh(pm->nd_opt_mtu_mtu)); +} + +int ndisc_option_add_route( + Set **options, + size_t offset, + uint8_t preference, + uint8_t prefixlen, + const struct in6_addr *prefix, + usec_t lifetime) { + + assert(options); + assert(prefix); + + if (prefixlen > 128) + return -EINVAL; + + /* RFC 4191 section 2.3 + * Prf (Route Preference) + * 2-bit signed integer. The Route Preference indicates whether to prefer the router associated with + * this prefix over others, when multiple identical prefixes (for different routers) have been + * received. If the Reserved (10) value is received, the Route Information Option MUST be ignored. */ + if (!IN_SET(preference, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_MEDIUM, SD_NDISC_PREFERENCE_HIGH)) + return -EINVAL; + + sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_ROUTE_INFORMATION, offset); + if (!p) + return -ENOMEM; + + p->route = (sd_ndisc_route) { + .preference = preference, + .prefixlen = prefixlen, + .address = *prefix, + .lifetime = lifetime, + }; + + in6_addr_mask(&p->route.address, p->route.prefixlen); + + return ndisc_option_consume(options, p); +} + +static int ndisc_option_parse_route(Set **options, size_t offset, size_t len, const uint8_t *opt) { + assert(options); + assert(opt); + + if (!IN_SET(len, 1*8, 2*8, 3*8)) + return -EBADMSG; + + if (opt[0] != SD_NDISC_OPTION_ROUTE_INFORMATION) + return -EBADMSG; + + uint8_t prefixlen = opt[2]; + if (prefixlen > 128) + return -EBADMSG; + + if (len < (size_t) (DIV_ROUND_UP(prefixlen, 64) + 1) * 8) + return -EBADMSG; + + uint8_t preference = (opt[3] >> 3) & 0x03; + usec_t lifetime = unaligned_be32_sec_to_usec(opt + 4, /* max_as_infinity = */ true); + + struct in6_addr prefix; + memcpy(&prefix, opt + 8, len - 8); + in6_addr_mask(&prefix, prefixlen); + + return ndisc_option_add_route(options, offset, preference, prefixlen, &prefix, lifetime); +} + +int ndisc_option_add_rdnss( + Set **options, + size_t offset, + size_t n_addresses, + const struct in6_addr *addresses, + usec_t lifetime) { + + assert(options); + assert(addresses); + + if (n_addresses == 0) + return -EINVAL; + + _cleanup_free_ struct in6_addr *addrs = newdup(struct in6_addr, addresses, n_addresses); + if (!addrs) + return -ENOMEM; + + sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_RDNSS, offset); + if (!p) + return -ENOMEM; + + p->rdnss = (sd_ndisc_rdnss) { + .n_addresses = n_addresses, + .addresses = TAKE_PTR(addrs), + .lifetime = lifetime, + }; + + return ndisc_option_consume(options, p); +} + +static int ndisc_option_parse_rdnss(Set **options, size_t offset, size_t len, const uint8_t *opt) { + assert(options); + assert(opt); + + if (len < 8 + sizeof(struct in6_addr) || (len % sizeof(struct in6_addr)) != 8) + return -EBADMSG; + + if (opt[0] != SD_NDISC_OPTION_RDNSS) + return -EBADMSG; + + usec_t lifetime = unaligned_be32_sec_to_usec(opt + 4, /* max_as_infinity = */ true); + size_t n_addrs = len / sizeof(struct in6_addr); + + return ndisc_option_add_rdnss(options, offset, n_addrs, (const struct in6_addr*) (opt + 8), lifetime); +} + +int ndisc_option_add_flags_extension(Set **options, size_t offset, uint64_t flags) { + assert(options); + + if ((flags & UINT64_C(0x00ffffffffffff00)) != flags) + return -EINVAL; + + sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_FLAGS_EXTENSION, offset); + if (!p) + return -ENOMEM; + + p->extended_flags = flags; + + return ndisc_option_consume(options, p); +} + +static int ndisc_option_parse_flags_extension(Set **options, size_t offset, size_t len, const uint8_t *opt) { + assert(options); + assert(opt); + + if (len != 8) + return -EBADMSG; + + if (opt[0] != SD_NDISC_OPTION_FLAGS_EXTENSION) + return -EBADMSG; + + uint64_t flags = (unaligned_read_be64(opt) & UINT64_C(0xffffffffffff0000)) >> 8; + return ndisc_option_add_flags_extension(options, offset, flags); +} + +int ndisc_option_add_dnssl(Set **options, size_t offset, char * const *domains, usec_t lifetime) { + int r; + + assert(options); + + if (strv_isempty(domains)) + return -EINVAL; + + STRV_FOREACH(s, domains) { + r = dns_name_is_valid(*s); + if (r < 0) + return r; + + if (is_localhost(*s) || dns_name_is_root(*s)) + return -EINVAL; + } + + _cleanup_strv_free_ char **copy = strv_copy(domains); + if (!copy) + return -ENOMEM; + + sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_DNSSL, offset); + if (!p) + return -ENOMEM; + + p->dnssl = (sd_ndisc_dnssl) { + .domains = TAKE_PTR(copy), + .lifetime = lifetime, + }; + + return ndisc_option_consume(options, p); +} + +static int ndisc_option_parse_dnssl(Set **options, size_t offset, size_t len, const uint8_t *opt) { + int r; + + assert(options); + assert(opt); + + if (len < 2*8) + return -EBADMSG; + + if (opt[0] != SD_NDISC_OPTION_DNSSL) + return -EBADMSG; + + usec_t lifetime = unaligned_be32_sec_to_usec(opt + 4, /* max_as_infinity = */ true); + + _cleanup_strv_free_ char **l = NULL; + _cleanup_free_ char *e = NULL; + size_t n = 0; + for (size_t c, pos = 8; pos < len; pos += c) { + + c = opt[pos]; + pos++; + + if (c == 0) { + /* Found NUL termination */ + + if (n > 0) { + _cleanup_free_ char *normalized = NULL; + + e[n] = 0; + r = dns_name_normalize(e, 0, &normalized); + if (r < 0) + return r; + + /* Ignore the root domain name or "localhost" and friends */ + if (!is_localhost(normalized) && !dns_name_is_root(normalized)) { + r = strv_consume(&l, TAKE_PTR(normalized)); + if (r < 0) + return r; + } + } + + n = 0; + continue; + } + + /* Check for compression (which is not allowed) */ + if (c > 63) + return -EBADMSG; + + if (pos + c >= len) + return -EBADMSG; + + if (!GREEDY_REALLOC(e, n + (n != 0) + DNS_LABEL_ESCAPED_MAX + 1U)) + return -ENOMEM; + + if (n != 0) + e[n++] = '.'; + + r = dns_label_escape((const char*) (opt + pos), c, e + n, DNS_LABEL_ESCAPED_MAX); + if (r < 0) + return r; + + n += r; + } + + if (n > 0) /* Not properly NUL terminated */ + return -EBADMSG; + + return ndisc_option_add_dnssl(options, offset, l, lifetime); +} + +int ndisc_option_add_captive_portal(Set **options, size_t offset, const char *portal) { + assert(options); + + if (isempty(portal)) + return -EINVAL; + + if (!in_charset(portal, URI_VALID)) + return -EINVAL; + + _cleanup_free_ char *copy = strdup(portal); + if (!copy) + return -ENOMEM; + + sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_CAPTIVE_PORTAL, offset); + if (!p) + return -ENOMEM; + + p->captive_portal = TAKE_PTR(copy); + + return ndisc_option_consume(options, p); +} + +static int ndisc_option_parse_captive_portal(Set **options, size_t offset, size_t len, const uint8_t *opt) { + assert(options); + assert(opt); + + if (len < 8) + return -EBADMSG; + + if (opt[0] != SD_NDISC_OPTION_CAPTIVE_PORTAL) + return -EBADMSG; + + _cleanup_free_ char *portal = memdup_suffix0(opt + 2, len - 2); + if (!portal) + return -ENOMEM; + + size_t size = strlen(portal); + if (size == 0) + return -EBADMSG; + + /* Check that the message is not truncated by an embedded NUL. + * NUL padding to a multiple of 8 is expected. */ + if (DIV_ROUND_UP(size + 2, 8) * 8 != len && DIV_ROUND_UP(size + 3, 8) * 8 != len) + return -EBADMSG; + + return ndisc_option_add_captive_portal(options, offset, portal); +} + static const uint8_t prefix_length_code_to_prefix_length[_PREFIX_LENGTH_CODE_MAX] = { [PREFIX_LENGTH_CODE_96] = 96, [PREFIX_LENGTH_CODE_64] = 64, @@ -61,13 +660,206 @@ int pref64_plc_to_prefix_length(uint16_t plc, uint8_t *ret) { } int pref64_prefix_length_to_plc(uint8_t prefixlen, uint8_t *ret) { - assert(ret); - for (size_t i = 0; i < ELEMENTSOF(prefix_length_code_to_prefix_length); i++) if (prefix_length_code_to_prefix_length[i] == prefixlen) { - *ret = i; + if (ret) + *ret = i; return 0; } return -EINVAL; } + +static int pref64_lifetime_and_plc_parse(uint16_t lifetime_and_plc, uint8_t *ret_prefixlen, usec_t *ret_lifetime) { + uint16_t plc = lifetime_and_plc & PREF64_PLC_MASK; + if (plc >= _PREFIX_LENGTH_CODE_MAX) + return -EINVAL; + + if (ret_prefixlen) + *ret_prefixlen = prefix_length_code_to_prefix_length[plc]; + if (ret_lifetime) + *ret_lifetime = (lifetime_and_plc & PREF64_SCALED_LIFETIME_MASK) * USEC_PER_SEC; + return 0; +} + +int ndisc_option_add_prefix64( + Set **options, + size_t offset, + uint8_t prefixlen, + const struct in6_addr *prefix, + usec_t lifetime) { + + int r; + + assert(options); + assert(prefix); + + r = pref64_prefix_length_to_plc(prefixlen, NULL); + if (r < 0) + return r; + + if (lifetime > PREF64_MAX_LIFETIME_USEC) + return -EINVAL; + + sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_PREF64, offset); + if (!p) + return -ENOMEM; + + p->prefix64 = (sd_ndisc_prefix64) { + .prefixlen = prefixlen, + .prefix = *prefix, + .lifetime = lifetime, + }; + + in6_addr_mask(&p->prefix64.prefix, p->prefix64.prefixlen); + + return ndisc_option_consume(options, p); +} + +static int ndisc_option_parse_prefix64(Set **options, size_t offset, size_t len, const uint8_t *opt) { + int r; + + assert(options); + assert(opt); + + if (len != 2*8) + return -EBADMSG; + + if (opt[0] != SD_NDISC_OPTION_PREF64) + return -EBADMSG; + + uint8_t prefixlen; + usec_t lifetime; + r = pref64_lifetime_and_plc_parse(unaligned_read_be16(opt + 2), &prefixlen, &lifetime); + if (r < 0) + return r; + + struct in6_addr prefix; + memcpy(&prefix, opt + 4, len - 4); + in6_addr_mask(&prefix, prefixlen); + + return ndisc_option_add_prefix64(options, offset, prefixlen, &prefix, lifetime); +} + +static int ndisc_option_parse_default(Set **options, size_t offset, size_t len, const uint8_t *opt) { + assert(options); + assert(opt); + assert(len > 0); + + sd_ndisc_option *p = ndisc_option_new(opt[0], offset); + if (!p) + return -ENOMEM; + + return ndisc_option_consume(options, p); +} + +static int ndisc_header_size(uint8_t icmp6_type) { + switch (icmp6_type) { + case ND_ROUTER_SOLICIT: + return sizeof(struct nd_router_solicit); + case ND_ROUTER_ADVERT: + return sizeof(struct nd_router_advert); + case ND_NEIGHBOR_SOLICIT: + return sizeof(struct nd_neighbor_solicit); + case ND_NEIGHBOR_ADVERT: + return sizeof(struct nd_neighbor_advert); + case ND_REDIRECT: + return sizeof(struct nd_redirect); + default: + return -EINVAL; + } +} + +int ndisc_parse_options(ICMP6Packet *packet, Set **ret_options) { + _cleanup_set_free_ Set *options = NULL; + int r; + + assert(packet); + assert(ret_options); + + r = icmp6_packet_get_type(packet); + if (r < 0) + return r; + + r = ndisc_header_size(r); + if (r < 0) + return -EBADMSG; + size_t header_size = r; + + if (packet->raw_size < header_size) + return -EBADMSG; + + for (size_t length, offset = header_size; offset < packet->raw_size; offset += length) { + uint8_t type; + const uint8_t *opt; + + r = ndisc_option_parse(packet, offset, &type, &length, &opt); + if (r < 0) + return log_debug_errno(r, "Failed to parse NDisc option header: %m"); + + switch (type) { + case SD_NDISC_OPTION_SOURCE_LL_ADDRESS: + case SD_NDISC_OPTION_TARGET_LL_ADDRESS: + r = ndisc_option_parse_link_layer_address(&options, offset, length, opt); + break; + + case SD_NDISC_OPTION_PREFIX_INFORMATION: + r = ndisc_option_parse_prefix(&options, offset, length, opt); + break; + + case SD_NDISC_OPTION_REDIRECTED_HEADER: + r = ndisc_option_parse_redirected_header(&options, offset, length, opt); + break; + + case SD_NDISC_OPTION_MTU: + r = ndisc_option_parse_mtu(&options, offset, length, opt); + break; + + case SD_NDISC_OPTION_ROUTE_INFORMATION: + r = ndisc_option_parse_route(&options, offset, length, opt); + break; + + case SD_NDISC_OPTION_RDNSS: + r = ndisc_option_parse_rdnss(&options, offset, length, opt); + break; + + case SD_NDISC_OPTION_FLAGS_EXTENSION: + r = ndisc_option_parse_flags_extension(&options, offset, length, opt); + break; + + case SD_NDISC_OPTION_DNSSL: + r = ndisc_option_parse_dnssl(&options, offset, length, opt); + break; + + case SD_NDISC_OPTION_CAPTIVE_PORTAL: + r = ndisc_option_parse_captive_portal(&options, offset, length, opt); + break; + + case SD_NDISC_OPTION_PREF64: + r = ndisc_option_parse_prefix64(&options, offset, length, opt); + break; + + default: + r = ndisc_option_parse_default(&options, offset, length, opt); + } + if (r == -ENOMEM) + return log_oom_debug(); + if (r < 0) + log_debug_errno(r, "Failed to parse NDisc option %u, ignoring: %m", type); + } + + *ret_options = TAKE_PTR(options); + return 0; +} + +int ndisc_option_get_mac(Set *options, uint8_t type, struct ether_addr *ret) { + assert(IN_SET(type, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, SD_NDISC_OPTION_TARGET_LL_ADDRESS)); + + sd_ndisc_option *p = ndisc_option_get(options, type); + if (!p) + return -ENODATA; + + if (ret) + *ret = p->mac; + return 0; +} diff --git a/src/libsystemd-network/ndisc-option.h b/src/libsystemd-network/ndisc-option.h index dcb1b191c7..6e6a1ced63 100644 --- a/src/libsystemd-network/ndisc-option.h +++ b/src/libsystemd-network/ndisc-option.h @@ -1,9 +1,69 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include +#include +#include +#include + +#include "sd-ndisc-protocol.h" + #include "icmp6-packet.h" +#include "macro.h" +#include "set.h" #include "time-util.h" +/* Mostly equivalent to struct nd_opt_prefix_info, but using usec_t. */ +typedef struct sd_ndisc_prefix { + uint8_t flags; + uint8_t prefixlen; + struct in6_addr address; + usec_t valid_lifetime; + usec_t preferred_lifetime; +} sd_ndisc_prefix; + +typedef struct sd_ndisc_route { + uint8_t preference; + uint8_t prefixlen; + struct in6_addr address; + usec_t lifetime; +} sd_ndisc_route; + +typedef struct sd_ndisc_rdnss { + size_t n_addresses; + struct in6_addr *addresses; + usec_t lifetime; +} sd_ndisc_rdnss; + +typedef struct sd_ndisc_dnssl { + char **domains; + usec_t lifetime; +} sd_ndisc_dnssl; + +typedef struct sd_ndisc_prefix64 { + uint8_t prefixlen; + struct in6_addr prefix; + usec_t lifetime; +} sd_ndisc_prefix64; + +typedef struct sd_ndisc_option { + uint8_t type; + size_t offset; + + union { + struct ether_addr mac; /* SD_NDISC_OPTION_SOURCE_LL_ADDRESS or SD_NDISC_OPTION_TARGET_LL_ADDRESS */ + sd_ndisc_prefix prefix; /* SD_NDISC_OPTION_PREFIX_INFORMATION */ + struct ip6_hdr hdr; /* SD_NDISC_OPTION_REDIRECTED_HEADER */ + uint32_t mtu; /* SD_NDISC_OPTION_MTU */ + sd_ndisc_route route; /* SD_NDISC_OPTION_ROUTE_INFORMATION */ + sd_ndisc_rdnss rdnss; /* SD_NDISC_OPTION_RDNSS */ + uint64_t extended_flags; /* SD_NDISC_OPTION_FLAGS_EXTENSION */ + sd_ndisc_dnssl dnssl; /* SD_NDISC_OPTION_DNSSL */ + char *captive_portal; /* SD_NDISC_OPTION_CAPTIVE_PORTAL */ + sd_ndisc_prefix64 prefix64; /* SD_NDISC_OPTION_PREF64 */ + }; +} sd_ndisc_option; + /* RFC 8781: PREF64 or (NAT64 prefix) */ #define PREF64_SCALED_LIFETIME_MASK 0xfff8 #define PREF64_PLC_MASK 0x0007 @@ -31,9 +91,73 @@ struct nd_opt_prefix64_info { int pref64_plc_to_prefix_length(uint16_t plc, uint8_t *ret); int pref64_prefix_length_to_plc(uint8_t prefixlen, uint8_t *ret); +sd_ndisc_option* ndisc_option_free(sd_ndisc_option *option); + int ndisc_option_parse( ICMP6Packet *p, size_t offset, uint8_t *ret_type, size_t *ret_len, const uint8_t **ret_opt); + +int ndisc_parse_options(ICMP6Packet *p, Set **ret_options); + +static inline sd_ndisc_option* ndisc_option_get(Set *options, uint8_t type) { + return set_get(options, &(sd_ndisc_option) { .type = type, }); +} + +int ndisc_option_get_mac(Set *options, uint8_t type, struct ether_addr *ret); + +int ndisc_option_add_link_layer_address( + Set **options, + uint8_t opt, + size_t offset, + const struct ether_addr *mac); +int ndisc_option_add_prefix( + Set **options, + size_t offset, + uint8_t flags, + uint8_t prefixlen, + const struct in6_addr *address, + usec_t valid_lifetime, + usec_t preferred_lifetime); +int ndisc_option_add_redirected_header( + Set **options, + size_t offset, + const struct ip6_hdr *hdr); +int ndisc_option_add_mtu( + Set **options, + size_t offset, + uint32_t mtu); +int ndisc_option_add_route( + Set **options, + size_t offset, + uint8_t preference, + uint8_t prefixlen, + const struct in6_addr *prefix, + usec_t lifetime); +int ndisc_option_add_rdnss( + Set **options, + size_t offset, + size_t n_addresses, + const struct in6_addr *addresses, + usec_t lifetime); +int ndisc_option_add_flags_extension( + Set **options, + size_t offset, + uint64_t flags); +int ndisc_option_add_dnssl( + Set **options, + size_t offset, + char * const *domains, + usec_t lifetime); +int ndisc_option_add_captive_portal( + Set **options, + size_t offset, + const char *portal); +int ndisc_option_add_prefix64( + Set **options, + size_t offset, + uint8_t prefixlen, + const struct in6_addr *prefix, + usec_t lifetime); diff --git a/src/systemd/sd-ndisc-protocol.h b/src/systemd/sd-ndisc-protocol.h index c6064b6795..3104ad42a3 100644 --- a/src/systemd/sd-ndisc-protocol.h +++ b/src/systemd/sd-ndisc-protocol.h @@ -27,6 +27,7 @@ enum { SD_NDISC_OPTION_SOURCE_LL_ADDRESS = 1, SD_NDISC_OPTION_TARGET_LL_ADDRESS = 2, SD_NDISC_OPTION_PREFIX_INFORMATION = 3, + SD_NDISC_OPTION_REDIRECTED_HEADER = 4, SD_NDISC_OPTION_MTU = 5, SD_NDISC_OPTION_ROUTE_INFORMATION = 24, SD_NDISC_OPTION_RDNSS = 25, @@ -38,9 +39,10 @@ enum { /* Route preference, RFC 4191, Section 2.1 */ enum { - SD_NDISC_PREFERENCE_LOW = 3U, - SD_NDISC_PREFERENCE_MEDIUM = 0U, - SD_NDISC_PREFERENCE_HIGH = 1U + SD_NDISC_PREFERENCE_MEDIUM = 0U, + SD_NDISC_PREFERENCE_HIGH = 1U, + SD_NDISC_PREFERENCE_RESERVED = 2U, + SD_NDISC_PREFERENCE_LOW = 3U }; _SD_END_DECLARATIONS; From c0edd6b3b304acc0a26a1e7e1592f6dc0bd8507a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 29 Feb 2024 12:42:16 +0900 Subject: [PATCH 3/6] sd-ndisc-router: use ndisc_parse_options() and friends to parse Router Advertisement --- .../ndisc-router-internal.h | 33 +- src/libsystemd-network/sd-ndisc-router.c | 741 +++--------------- 2 files changed, 101 insertions(+), 673 deletions(-) diff --git a/src/libsystemd-network/ndisc-router-internal.h b/src/libsystemd-network/ndisc-router-internal.h index f1a03bc0ab..6f13350835 100644 --- a/src/libsystemd-network/ndisc-router-internal.h +++ b/src/libsystemd-network/ndisc-router-internal.h @@ -8,6 +8,7 @@ #include "sd-ndisc.h" #include "icmp6-packet.h" +#include "ndisc-option.h" #include "time-util.h" struct sd_ndisc_router { @@ -15,33 +16,19 @@ struct sd_ndisc_router { ICMP6Packet *packet; - /* The current read index for the iterative option interface */ - size_t rindex; - - uint64_t flags; - unsigned preference; - uint64_t lifetime_usec; + /* From RA header */ + uint8_t hop_limit; + uint8_t flags; + uint8_t preference; + usec_t lifetime_usec; usec_t reachable_time_usec; usec_t retransmission_time_usec; - uint8_t hop_limit; - uint32_t mtu; + /* Options */ + Set *options; + Iterator iterator; + sd_ndisc_option *current_option; }; -static inline void* NDISC_ROUTER_RAW(const sd_ndisc_router *rt) { - return ASSERT_PTR(ASSERT_PTR(rt)->packet)->raw_packet; -} - -static inline void *NDISC_ROUTER_OPTION_DATA(const sd_ndisc_router *rt) { - return ((uint8_t*) NDISC_ROUTER_RAW(rt)) + rt->rindex; -} - -static inline uint8_t NDISC_ROUTER_OPTION_TYPE(const sd_ndisc_router *rt) { - return ((uint8_t*) NDISC_ROUTER_OPTION_DATA(rt))[0]; -} -static inline size_t NDISC_ROUTER_OPTION_LENGTH(const sd_ndisc_router *rt) { - return ((uint8_t*) NDISC_ROUTER_OPTION_DATA(rt))[1] * 8; -} - sd_ndisc_router* ndisc_router_new(ICMP6Packet *packet); int ndisc_router_parse(sd_ndisc *nd, sd_ndisc_router *rt); diff --git a/src/libsystemd-network/sd-ndisc-router.c b/src/libsystemd-network/sd-ndisc-router.c index 309ef10195..46baa4fc8e 100644 --- a/src/libsystemd-network/sd-ndisc-router.c +++ b/src/libsystemd-network/sd-ndisc-router.c @@ -8,13 +8,7 @@ #include "sd-ndisc.h" #include "alloc-util.h" -#include "dns-domain.h" -#include "escape.h" -#include "hostname-util.h" -#include "memory-util.h" -#include "missing_network.h" #include "ndisc-internal.h" -#include "ndisc-option.h" #include "ndisc-router-internal.h" #include "strv.h" @@ -23,6 +17,7 @@ static sd_ndisc_router* ndisc_router_free(sd_ndisc_router *rt) { return NULL; icmp6_packet_unref(rt->packet); + set_free(rt->options); return mfree(rt); } @@ -40,6 +35,7 @@ sd_ndisc_router* ndisc_router_new(ICMP6Packet *packet) { *rt = (sd_ndisc_router) { .n_ref = 1, .packet = icmp6_packet_ref(packet), + .iterator = ITERATOR_FIRST, }; return rt; @@ -90,24 +86,8 @@ DEFINE_GET_TIMESTAMP(rdnss_get_lifetime); DEFINE_GET_TIMESTAMP(dnssl_get_lifetime); DEFINE_GET_TIMESTAMP(prefix64_get_lifetime); -static bool pref64_option_verify(const struct nd_opt_prefix64_info *p, size_t length) { - uint16_t lifetime_and_plc; - - assert(p); - - if (length != sizeof(struct nd_opt_prefix64_info)) - return false; - - lifetime_and_plc = be16toh(p->lifetime_and_plc); - if (pref64_plc_to_prefix_length(lifetime_and_plc, NULL) < 0) - return false; - - return true; -} - int ndisc_router_parse(sd_ndisc *nd, sd_ndisc_router *rt) { const struct nd_router_advert *a; - bool has_mtu = false, has_flag_extension = false; int r; assert(rt); @@ -127,107 +107,20 @@ int ndisc_router_parse(sd_ndisc *nd, sd_ndisc_router *rt) { rt->reachable_time_usec = be32_msec_to_usec(a->nd_ra_reachable, /* mas_as_infinity = */ false); rt->retransmission_time_usec = be32_msec_to_usec(a->nd_ra_retransmit, /* max_as_infinity = */ false); + /* RFC 4191 section 2.2 + * Prf (Default Router Preference) + * 2-bit signed integer. Indicates whether to prefer this router over other default routers. If the + * Router Lifetime is zero, the preference value MUST be set to (00) by the sender and MUST be + * ignored by the receiver. If the Reserved (10) value is received, the receiver MUST treat the value + * as if it were (00). */ rt->preference = (rt->flags >> 3) & 3; - if (!IN_SET(rt->preference, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_HIGH)) + if (rt->preference == SD_NDISC_PREFERENCE_RESERVED) rt->preference = SD_NDISC_PREFERENCE_MEDIUM; - for (size_t offset = sizeof(struct nd_router_advert), length; offset < rt->packet->raw_size; offset += length) { - uint8_t type; - const uint8_t *p; + r = ndisc_parse_options(rt->packet, &rt->options); + if (r < 0) + return log_ndisc_errno(nd, r, "Failed to parse NDisc options in router advertisement message, ignoring: %m"); - r = ndisc_option_parse(rt->packet, offset, &type, &length, &p); - if (r < 0) - return log_ndisc_errno(nd, r, "Failed to parse NDisc option header, ignoring: %m"); - - switch (type) { - - case SD_NDISC_OPTION_PREFIX_INFORMATION: - - if (length != 4*8) - return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), - "Prefix option of invalid size, ignoring datagram."); - - if (p[2] > 128) - return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), - "Bad prefix length, ignoring datagram."); - - break; - - case SD_NDISC_OPTION_MTU: { - uint32_t m; - - if (has_mtu) { - log_ndisc(nd, "MTU option specified twice, ignoring."); - break; - } - - if (length != 8) - return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), - "MTU option of invalid size, ignoring datagram."); - - m = be32toh(*(uint32_t*) (p + 4)); - if (m >= IPV6_MIN_MTU) /* ignore invalidly small MTUs */ - rt->mtu = m; - - has_mtu = true; - break; - } - - case SD_NDISC_OPTION_ROUTE_INFORMATION: - if (length < 1*8 || length > 3*8) - return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), - "Route information option of invalid size, ignoring datagram."); - - if (p[2] > 128) - return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), - "Bad route prefix length, ignoring datagram."); - - break; - - case SD_NDISC_OPTION_RDNSS: - if (length < 3*8 || (length % (2*8)) != 1*8) - return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), "RDNSS option has invalid size."); - - break; - - case SD_NDISC_OPTION_FLAGS_EXTENSION: - - if (has_flag_extension) { - log_ndisc(nd, "Flags extension option specified twice, ignoring."); - break; - } - - if (length < 1*8) - return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), - "Flags extension option has invalid size."); - - /* Add in the additional flags bits */ - rt->flags |= - ((uint64_t) p[2] << 8) | - ((uint64_t) p[3] << 16) | - ((uint64_t) p[4] << 24) | - ((uint64_t) p[5] << 32) | - ((uint64_t) p[6] << 40) | - ((uint64_t) p[7] << 48); - - has_flag_extension = true; - break; - - case SD_NDISC_OPTION_DNSSL: - if (length < 2*8) - return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), - "DNSSL option has invalid size."); - - break; - case SD_NDISC_OPTION_PREF64: { - if (!pref64_option_verify((struct nd_opt_prefix64_info *) p, length)) - log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), - "PREF64 prefix has invalid prefix length."); - break; - }} - } - - rt->rindex = sizeof(struct nd_router_advert); return 0; } @@ -259,7 +152,9 @@ int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret) { assert_return(rt, -EINVAL); assert_return(ret, -EINVAL); - *ret = rt->flags; + sd_ndisc_option *p = ndisc_option_get(rt->options, SD_NDISC_OPTION_FLAGS_EXTENSION); + + *ret = rt->flags | (p ? p->extended_flags : 0); return 0; } @@ -284,598 +179,144 @@ int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret) { assert_return(rt, -EINVAL); assert_return(ret, -EINVAL); - if (rt->mtu <= 0) + sd_ndisc_option *p = ndisc_option_get(rt->options, SD_NDISC_OPTION_MTU); + if (!p) return -ENODATA; - *ret = rt->mtu; + *ret = p->mtu; + return 0; +} + +int sd_ndisc_router_captive_portal_get_uri(sd_ndisc_router *rt, const char **ret, size_t *ret_size) { + assert_return(rt, -EINVAL); + assert_return(ret, -EINVAL); + + sd_ndisc_option *p = ndisc_option_get(rt->options, SD_NDISC_OPTION_CAPTIVE_PORTAL); + if (!p) + return -ENODATA; + + *ret = p->captive_portal; + *ret_size = strlen(p->captive_portal); return 0; } int sd_ndisc_router_option_rewind(sd_ndisc_router *rt) { assert_return(rt, -EINVAL); - assert(rt->packet); - assert(rt->packet->raw_size >= sizeof(struct nd_router_advert)); - - rt->rindex = sizeof(struct nd_router_advert); - return rt->rindex < rt->packet->raw_size; + rt->iterator = ITERATOR_FIRST; + return sd_ndisc_router_option_next(rt); } int sd_ndisc_router_option_next(sd_ndisc_router *rt) { - size_t length; - int r; - assert_return(rt, -EINVAL); - r = ndisc_option_parse(rt->packet, rt->rindex, NULL, &length, NULL); - if (r < 0) - return r; - - rt->rindex += length; - return rt->rindex < rt->packet->raw_size; + return set_iterate(rt->options, &rt->iterator, (void**) &rt->current_option); } int sd_ndisc_router_option_get_type(sd_ndisc_router *rt, uint8_t *ret) { assert_return(rt, -EINVAL); - return ndisc_option_parse(rt->packet, rt->rindex, ret, NULL, NULL); + assert_return(ret, -EINVAL); + + if (!rt->current_option) + return -ENODATA; + + *ret = rt->current_option->type; + return 0; } int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type) { - uint8_t k; + uint8_t t; int r; assert_return(rt, -EINVAL); - r = sd_ndisc_router_option_get_type(rt, &k); + r = sd_ndisc_router_option_get_type(rt, &t); if (r < 0) return r; - return type == k; + return t == type; } int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t *ret_size) { - size_t length; - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - assert_return(ret_size, -EINVAL); - /* Note that this returns the full option, including the option header */ + if (!rt->current_option) + return -ENODATA; - if (rt->rindex + 2 > rt->packet->raw_size) - return -EBADMSG; - - length = NDISC_ROUTER_OPTION_LENGTH(rt); - if (rt->rindex + length > rt->packet->raw_size) - return -EBADMSG; - - *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex; - *ret_size = length; - - return 0; + return ndisc_option_parse(rt->packet, rt->current_option->offset, NULL, ret_size, (const uint8_t**) ret); } -static int get_prefix_info(sd_ndisc_router *rt, struct nd_opt_prefix_info **ret) { - struct nd_opt_prefix_info *ri; - size_t length; - int r; - - assert(rt); - assert(ret); - - r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_PREFIX_INFORMATION); - if (r < 0) - return r; - if (r == 0) - return -EMEDIUMTYPE; - - length = NDISC_ROUTER_OPTION_LENGTH(rt); - if (length != sizeof(struct nd_opt_prefix_info)) - return -EBADMSG; - - ri = (struct nd_opt_prefix_info*) ((uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex); - if (ri->nd_opt_pi_prefix_len > 128) - return -EBADMSG; - - *ret = ri; - return 0; -} - -int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router *rt, uint64_t *ret) { - struct nd_opt_prefix_info *ri; - int r; - - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - r = get_prefix_info(rt, &ri); - if (r < 0) - return r; - - *ret = be32_sec_to_usec(ri->nd_opt_pi_valid_time, /* max_as_infinity = */ true); - return 0; -} - -int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router *rt, uint64_t *ret) { - struct nd_opt_prefix_info *pi; - int r; - - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - r = get_prefix_info(rt, &pi); - if (r < 0) - return r; - - *ret = be32_sec_to_usec(pi->nd_opt_pi_preferred_time, /* max_as_infinity = */ true); - return 0; -} - -int sd_ndisc_router_prefix_get_flags(sd_ndisc_router *rt, uint8_t *ret) { - struct nd_opt_prefix_info *pi; - uint8_t flags; - int r; - - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - r = get_prefix_info(rt, &pi); - if (r < 0) - return r; - - flags = pi->nd_opt_pi_flags_reserved; - - if ((flags & ND_OPT_PI_FLAG_AUTO) && (pi->nd_opt_pi_prefix_len != 64)) { - log_ndisc(NULL, "Invalid prefix length, ignoring prefix for stateless autoconfiguration."); - flags &= ~ND_OPT_PI_FLAG_AUTO; +#define DEFINE_GETTER(name, type, element, element_type) \ + int sd_ndisc_router_##name##_get_##element( \ + sd_ndisc_router *rt, \ + element_type *ret) { \ + \ + int r; \ + \ + assert_return(rt, -EINVAL); \ + assert_return(ret, -EINVAL); \ + \ + r = sd_ndisc_router_option_is_type(rt, type); \ + if (r < 0) \ + return r; \ + if (r == 0) \ + return -EMEDIUMTYPE; \ + \ + *ret = rt->current_option->name.element; \ + return 0; \ } - *ret = flags; - return 0; -} +DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, flags, uint8_t); +DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, prefixlen, unsigned); +DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, address, struct in6_addr); +DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, valid_lifetime, uint64_t); +DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, preferred_lifetime, uint64_t); -int sd_ndisc_router_prefix_get_address(sd_ndisc_router *rt, struct in6_addr *ret) { - struct nd_opt_prefix_info *pi; +DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, preference, unsigned); +DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, prefixlen, unsigned); +DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, address, struct in6_addr); +DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, lifetime, uint64_t); + +DEFINE_GETTER(rdnss, SD_NDISC_OPTION_RDNSS, lifetime, uint64_t); + +int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **ret) { int r; assert_return(rt, -EINVAL); assert_return(ret, -EINVAL); - r = get_prefix_info(rt, &pi); - if (r < 0) - return r; - - *ret = pi->nd_opt_pi_prefix; - return 0; -} - -int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) { - struct nd_opt_prefix_info *pi; - int r; - - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - r = get_prefix_info(rt, &pi); - if (r < 0) - return r; - - if (pi->nd_opt_pi_prefix_len > 128) - return -EBADMSG; - - *ret = pi->nd_opt_pi_prefix_len; - return 0; -} - -static int get_route_info(sd_ndisc_router *rt, uint8_t **ret) { - uint8_t *ri; - size_t length; - int r; - - assert(rt); - assert(ret); - - r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_ROUTE_INFORMATION); - if (r < 0) - return r; - if (r == 0) - return -EMEDIUMTYPE; - - length = NDISC_ROUTER_OPTION_LENGTH(rt); - if (length < 1*8 || length > 3*8) - return -EBADMSG; - - ri = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex; - - if (ri[2] > 128) - return -EBADMSG; - - *ret = ri; - return 0; -} - -int sd_ndisc_router_route_get_lifetime(sd_ndisc_router *rt, uint64_t *ret) { - uint8_t *ri; - int r; - - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - r = get_route_info(rt, &ri); - if (r < 0) - return r; - - *ret = unaligned_be32_sec_to_usec(ri + 4, /* max_as_infinity = */ true); - return 0; -} - -int sd_ndisc_router_route_get_address(sd_ndisc_router *rt, struct in6_addr *ret) { - uint8_t *ri; - int r; - - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - r = get_route_info(rt, &ri); - if (r < 0) - return r; - - zero(*ret); - memcpy(ret, ri + 8, NDISC_ROUTER_OPTION_LENGTH(rt) - 8); - - return 0; -} - -int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) { - uint8_t *ri; - int r; - - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - r = get_route_info(rt, &ri); - if (r < 0) - return r; - - *ret = ri[2]; - return 0; -} - -int sd_ndisc_router_route_get_preference(sd_ndisc_router *rt, unsigned *ret) { - uint8_t *ri; - int r; - - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - r = get_route_info(rt, &ri); - if (r < 0) - return r; - - if (!IN_SET((ri[3] >> 3) & 3, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_MEDIUM, SD_NDISC_PREFERENCE_HIGH)) - return -EOPNOTSUPP; - - *ret = (ri[3] >> 3) & 3; - return 0; -} - -static int get_rdnss_info(sd_ndisc_router *rt, uint8_t **ret) { - size_t length; - int r; - - assert(rt); - assert(ret); - r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_RDNSS); if (r < 0) return r; if (r == 0) return -EMEDIUMTYPE; - length = NDISC_ROUTER_OPTION_LENGTH(rt); - if (length < 3*8 || (length % (2*8)) != 1*8) - return -EBADMSG; - - *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex; - return 0; + *ret = rt->current_option->rdnss.addresses; + return (int) rt->current_option->rdnss.n_addresses; } -int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **ret) { - uint8_t *ri; +DEFINE_GETTER(dnssl, SD_NDISC_OPTION_DNSSL, lifetime, uint64_t); + +int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router *rt, char ***ret) { int r; assert_return(rt, -EINVAL); assert_return(ret, -EINVAL); - r = get_rdnss_info(rt, &ri); - if (r < 0) - return r; - - *ret = (const struct in6_addr*) (ri + 8); - return (NDISC_ROUTER_OPTION_LENGTH(rt) - 8) / 16; -} - -int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router *rt, uint64_t *ret) { - uint8_t *ri; - int r; - - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - r = get_rdnss_info(rt, &ri); - if (r < 0) - return r; - - *ret = unaligned_be32_sec_to_usec(ri + 4, /* max_as_infinity = */ true); - return 0; -} - -static int get_dnssl_info(sd_ndisc_router *rt, uint8_t **ret) { - size_t length; - int r; - - assert(rt); - assert(ret); - r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_DNSSL); if (r < 0) return r; if (r == 0) return -EMEDIUMTYPE; - length = NDISC_ROUTER_OPTION_LENGTH(rt); - if (length < 2*8) - return -EBADMSG; + char **q = strv_copy(rt->current_option->dnssl.domains); + if (!q) + return -ENOMEM; - *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex; - return 0; + *ret = q; + return (int) strv_length(q); } -int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router *rt, char ***ret) { - _cleanup_strv_free_ char **l = NULL; - _cleanup_free_ char *e = NULL; - size_t n = 0, left; - uint8_t *ri, *p; - bool first = true; - int r; - unsigned k = 0; - - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - r = get_dnssl_info(rt, &ri); - if (r < 0) - return r; - - p = ri + 8; - left = NDISC_ROUTER_OPTION_LENGTH(rt) - 8; - - for (;;) { - if (left == 0) { - - if (n > 0) /* Not properly NUL terminated */ - return -EBADMSG; - - break; - } - - if (*p == 0) { - /* Found NUL termination */ - - if (n > 0) { - _cleanup_free_ char *normalized = NULL; - - e[n] = 0; - r = dns_name_normalize(e, 0, &normalized); - if (r < 0) { - _cleanup_free_ char *escaped = cescape(e); - log_debug_errno(r, "Failed to normalize advertised domain name \"%s\": %m", strna(escaped)); - /* Here, do not propagate error code from dns_name_normalize() except for ENOMEM. */ - return r == -ENOMEM ? -ENOMEM : -EBADMSG; - } - - /* Ignore the root domain name or "localhost" and friends */ - if (!is_localhost(normalized) && - !dns_name_is_root(normalized)) { - - if (strv_push(&l, normalized) < 0) - return -ENOMEM; - - normalized = NULL; - k++; - } - } - - n = 0; - first = true; - p++, left--; - continue; - } - - /* Check for compression (which is not allowed) */ - if (*p > 63) - return -EBADMSG; - - if (1U + *p + 1U > left) - return -EBADMSG; - - if (!GREEDY_REALLOC(e, n + !first + DNS_LABEL_ESCAPED_MAX + 1U)) - return -ENOMEM; - - if (first) - first = false; - else - e[n++] = '.'; - - r = dns_label_escape((char*) p+1, *p, e + n, DNS_LABEL_ESCAPED_MAX); - if (r < 0) { - _cleanup_free_ char *escaped = cescape_length((const char*) p+1, *p); - log_debug_errno(r, "Failed to escape advertised domain name \"%s\": %m", strna(escaped)); - /* Here, do not propagate error code from dns_label_escape() except for ENOMEM. */ - return r == -ENOMEM ? -ENOMEM : -EBADMSG; - } - - n += r; - - left -= 1 + *p; - p += 1 + *p; - } - - if (strv_isempty(l)) { - *ret = NULL; - return 0; - } - - *ret = TAKE_PTR(l); - - return k; -} - -int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router *rt, uint64_t *ret) { - uint8_t *ri; - int r; - - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - r = get_dnssl_info(rt, &ri); - if (r < 0) - return r; - - *ret = unaligned_be32_sec_to_usec(ri + 4, /* max_as_infinity = */ true); - return 0; -} - -int sd_ndisc_router_captive_portal_get_uri(sd_ndisc_router *rt, const char **ret, size_t *ret_size) { - int r; - const char *nd_opt_captive_portal; - size_t length; - - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - assert_return(ret_size, -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 = NULL; - *ret_size = 0; - return 0; - } - - *ret = nd_opt_captive_portal + 2; - *ret_size = size; - - return 0; -} - -static int get_pref64_prefix_info(sd_ndisc_router *rt, struct nd_opt_prefix64_info **ret) { - struct nd_opt_prefix64_info *ri; - size_t length; - int r; - - assert(rt); - assert(ret); - - r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_PREF64); - if (r < 0) - return r; - if (r == 0) - return -EMEDIUMTYPE; - - length = NDISC_ROUTER_OPTION_LENGTH(rt); - if (length != sizeof(struct nd_opt_prefix64_info)) - return -EBADMSG; - - ri = (struct nd_opt_prefix64_info *) ((uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex); - if (!pref64_option_verify(ri, length)) - return -EBADMSG; - - *ret = ri; - return 0; -} - -int sd_ndisc_router_prefix64_get_prefix(sd_ndisc_router *rt, struct in6_addr *ret) { - struct nd_opt_prefix64_info *pi; - struct in6_addr a = {}; - unsigned prefixlen; - int r; - - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - r = get_pref64_prefix_info(rt, &pi); - if (r < 0) - return r; - - r = sd_ndisc_router_prefix64_get_prefixlen(rt, &prefixlen); - if (r < 0) - return r; - - memcpy(&a, pi->prefix, sizeof(pi->prefix)); - in6_addr_mask(&a, prefixlen); - /* extra safety check for refusing malformed prefix. */ - if (memcmp(&a, pi->prefix, sizeof(pi->prefix)) != 0) - return -EBADMSG; - - *ret = a; - return 0; -} - -int sd_ndisc_router_prefix64_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) { - struct nd_opt_prefix64_info *pi; - uint16_t lifetime_prefix_len; - uint8_t prefix_len; - int r; - - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - r = get_pref64_prefix_info(rt, &pi); - if (r < 0) - return r; - - lifetime_prefix_len = be16toh(pi->lifetime_and_plc); - pref64_plc_to_prefix_length(lifetime_prefix_len, &prefix_len); - - *ret = prefix_len; - return 0; -} - -int sd_ndisc_router_prefix64_get_lifetime(sd_ndisc_router *rt, uint64_t *ret) { - struct nd_opt_prefix64_info *pi; - uint16_t lifetime_prefix_len; - int r; - - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - r = get_pref64_prefix_info(rt, &pi); - if (r < 0) - return r; - - lifetime_prefix_len = be16toh(pi->lifetime_and_plc); - - *ret = (lifetime_prefix_len & PREF64_SCALED_LIFETIME_MASK) * USEC_PER_SEC; - return 0; -} +DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, prefixlen, unsigned); +DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, prefix, struct in6_addr); +DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, lifetime, uint64_t); From 0216c672691168bbafb6ddd21af2f48c0e3520de Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 29 Feb 2024 12:43:03 +0900 Subject: [PATCH 4/6] ndisc-option: drop unused function --- src/libsystemd-network/ndisc-option.c | 10 ---------- src/libsystemd-network/ndisc-option.h | 1 - 2 files changed, 11 deletions(-) diff --git a/src/libsystemd-network/ndisc-option.c b/src/libsystemd-network/ndisc-option.c index cddde0cabf..380e71e764 100644 --- a/src/libsystemd-network/ndisc-option.c +++ b/src/libsystemd-network/ndisc-option.c @@ -649,16 +649,6 @@ static const uint8_t prefix_length_code_to_prefix_length[_PREFIX_LENGTH_CODE_MAX [PREFIX_LENGTH_CODE_32] = 32, }; -int pref64_plc_to_prefix_length(uint16_t plc, uint8_t *ret) { - plc &= PREF64_PLC_MASK; - if (plc >= _PREFIX_LENGTH_CODE_MAX) - return -EINVAL; - - if (ret) - *ret = prefix_length_code_to_prefix_length[plc]; - return 0; -} - int pref64_prefix_length_to_plc(uint8_t prefixlen, uint8_t *ret) { for (size_t i = 0; i < ELEMENTSOF(prefix_length_code_to_prefix_length); i++) if (prefix_length_code_to_prefix_length[i] == prefixlen) { diff --git a/src/libsystemd-network/ndisc-option.h b/src/libsystemd-network/ndisc-option.h index 6e6a1ced63..45108ee1aa 100644 --- a/src/libsystemd-network/ndisc-option.h +++ b/src/libsystemd-network/ndisc-option.h @@ -88,7 +88,6 @@ struct nd_opt_prefix64_info { uint8_t prefix[12]; } _packed_; -int pref64_plc_to_prefix_length(uint16_t plc, uint8_t *ret); int pref64_prefix_length_to_plc(uint8_t prefixlen, uint8_t *ret); sd_ndisc_option* ndisc_option_free(sd_ndisc_option *option); From b43c2221f6df23770ff1b53679a330c39025afa9 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 29 Feb 2024 12:45:51 +0900 Subject: [PATCH 5/6] sd-ndisc-router: introduce sd_ndisc_router_get_sender_mac() The Router Advertisement option can take the MAC address of the sender. Let's introduce a function to get it from the parsed options. --- src/libsystemd-network/sd-ndisc-router.c | 6 ++++++ src/systemd/sd-ndisc-router.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/src/libsystemd-network/sd-ndisc-router.c b/src/libsystemd-network/sd-ndisc-router.c index 46baa4fc8e..fcc2bbce6e 100644 --- a/src/libsystemd-network/sd-ndisc-router.c +++ b/src/libsystemd-network/sd-ndisc-router.c @@ -175,6 +175,12 @@ int sd_ndisc_router_get_preference(sd_ndisc_router *rt, unsigned *ret) { return 0; } +int sd_ndisc_router_get_sender_mac(sd_ndisc_router *rt, struct ether_addr *ret) { + assert_return(rt, -EINVAL); + + return ndisc_option_get_mac(rt->options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, ret); +} + int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret) { assert_return(rt, -EINVAL); assert_return(ret, -EINVAL); diff --git a/src/systemd/sd-ndisc-router.h b/src/systemd/sd-ndisc-router.h index 57dbb12817..dde37bcfd7 100644 --- a/src/systemd/sd-ndisc-router.h +++ b/src/systemd/sd-ndisc-router.h @@ -18,6 +18,7 @@ ***/ #include +#include #include #include #include @@ -42,6 +43,7 @@ int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint64_t *ret); int sd_ndisc_router_get_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret); int sd_ndisc_router_get_reachable_time(sd_ndisc_router *rt, uint64_t *ret); int sd_ndisc_router_get_retransmission_time(sd_ndisc_router *rt, uint64_t *ret); +int sd_ndisc_router_get_sender_mac(sd_ndisc_router *rt, struct ether_addr *ret); int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret); /* Generic option access */ From 9ca04752cb1de91d8656990bbc7d6d0fd732ddcc Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 29 Feb 2024 13:06:31 +0900 Subject: [PATCH 6/6] sd-ndisc-router: adjust function names and type of returned value - prefix length and preference should be fit in uint8_t, and actually the kernel and networkd uses uint8_t to store them. - captive portal is now stored as a NUL-terminated string. Hence, it is not necessary to also provide its length. --- src/libsystemd-network/sd-ndisc-router.c | 41 ++++------------- src/libsystemd-network/test-ndisc-rs.c | 25 ++++------ src/network/networkd-ndisc.c | 58 ++++++++++-------------- src/systemd/sd-ndisc-router.h | 18 ++++---- 4 files changed, 52 insertions(+), 90 deletions(-) diff --git a/src/libsystemd-network/sd-ndisc-router.c b/src/libsystemd-network/sd-ndisc-router.c index fcc2bbce6e..bf7e185ee6 100644 --- a/src/libsystemd-network/sd-ndisc-router.c +++ b/src/libsystemd-network/sd-ndisc-router.c @@ -10,7 +10,6 @@ #include "alloc-util.h" #include "ndisc-internal.h" #include "ndisc-router-internal.h" -#include "strv.h" static sd_ndisc_router* ndisc_router_free(sd_ndisc_router *rt) { if (!rt) @@ -41,7 +40,7 @@ sd_ndisc_router* ndisc_router_new(ICMP6Packet *packet) { return rt; } -int sd_ndisc_router_get_address(sd_ndisc_router *rt, struct in6_addr *ret) { +int sd_ndisc_router_get_sender_address(sd_ndisc_router *rt, struct in6_addr *ret) { assert_return(rt, -EINVAL); return icmp6_packet_get_sender_address(rt->packet, ret); @@ -167,7 +166,7 @@ int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint64_t *ret) { return rt->lifetime_usec > 0; /* Indicate if the router is still valid or not. */ } -int sd_ndisc_router_get_preference(sd_ndisc_router *rt, unsigned *ret) { +int sd_ndisc_router_get_preference(sd_ndisc_router *rt, uint8_t *ret) { assert_return(rt, -EINVAL); assert_return(ret, -EINVAL); @@ -193,7 +192,7 @@ int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret) { return 0; } -int sd_ndisc_router_captive_portal_get_uri(sd_ndisc_router *rt, const char **ret, size_t *ret_size) { +int sd_ndisc_router_get_captive_portal(sd_ndisc_router *rt, const char **ret) { assert_return(rt, -EINVAL); assert_return(ret, -EINVAL); @@ -202,7 +201,6 @@ int sd_ndisc_router_captive_portal_get_uri(sd_ndisc_router *rt, const char **ret return -ENODATA; *ret = p->captive_portal; - *ret_size = strlen(p->captive_portal); return 0; } @@ -243,13 +241,13 @@ int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type) { return t == type; } -int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t *ret_size) { +int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const uint8_t **ret, size_t *ret_size) { assert_return(rt, -EINVAL); if (!rt->current_option) return -ENODATA; - return ndisc_option_parse(rt->packet, rt->current_option->offset, NULL, ret_size, (const uint8_t**) ret); + return ndisc_option_parse(rt->packet, rt->current_option->offset, NULL, ret_size, ret); } #define DEFINE_GETTER(name, type, element, element_type) \ @@ -273,13 +271,13 @@ int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t } DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, flags, uint8_t); -DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, prefixlen, unsigned); +DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, prefixlen, uint8_t); DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, address, struct in6_addr); DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, valid_lifetime, uint64_t); DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, preferred_lifetime, uint64_t); -DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, preference, unsigned); -DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, prefixlen, unsigned); +DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, preference, uint8_t); +DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, prefixlen, uint8_t); DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, address, struct in6_addr); DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, lifetime, uint64_t); @@ -301,28 +299,9 @@ int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_ad return (int) rt->current_option->rdnss.n_addresses; } +DEFINE_GETTER(dnssl, SD_NDISC_OPTION_DNSSL, domains, char**); DEFINE_GETTER(dnssl, SD_NDISC_OPTION_DNSSL, lifetime, uint64_t); -int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router *rt, char ***ret) { - int r; - - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_DNSSL); - if (r < 0) - return r; - if (r == 0) - return -EMEDIUMTYPE; - - char **q = strv_copy(rt->current_option->dnssl.domains); - if (!q) - return -ENOMEM; - - *ret = q; - return (int) strv_length(q); -} - -DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, prefixlen, unsigned); +DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, prefixlen, uint8_t); DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, prefix, struct in6_addr); DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, lifetime, uint64_t); diff --git a/src/libsystemd-network/test-ndisc-rs.c b/src/libsystemd-network/test-ndisc-rs.c index e37dfeeded..4541605940 100644 --- a/src/libsystemd-network/test-ndisc-rs.c +++ b/src/libsystemd-network/test-ndisc-rs.c @@ -31,13 +31,13 @@ static void router_dump(sd_ndisc_router *rt) { usec_t t, lifetime, retrans_time; uint64_t flags; uint32_t mtu; - unsigned preference; + uint8_t preference; int r; assert_se(rt); log_info("--"); - assert_se(sd_ndisc_router_get_address(rt, &addr) >= 0); + assert_se(sd_ndisc_router_get_sender_address(rt, &addr) >= 0); log_info("Sender: %s", IN6_ADDR_TO_STRING(&addr)); assert_se(sd_ndisc_router_get_timestamp(rt, CLOCK_REALTIME, &t) >= 0); @@ -91,20 +91,19 @@ static void router_dump(sd_ndisc_router *rt) { case SD_NDISC_OPTION_SOURCE_LL_ADDRESS: case SD_NDISC_OPTION_TARGET_LL_ADDRESS: { _cleanup_free_ char *c = NULL; - const void *p; + const uint8_t *p; size_t n; assert_se(sd_ndisc_router_option_get_raw(rt, &p, &n) >= 0); assert_se(n > 2); - assert_se(c = hexmem((uint8_t*) p + 2, n - 2)); + assert_se(c = hexmem(p + 2, n - 2)); log_info("Address: %s", c); break; } case SD_NDISC_OPTION_PREFIX_INFORMATION: { - unsigned prefix_len; - uint8_t pfl; + uint8_t prefix_len, pfl; struct in6_addr a; assert_se(sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime) >= 0); @@ -146,18 +145,12 @@ static void router_dump(sd_ndisc_router *rt) { } case SD_NDISC_OPTION_DNSSL: { - _cleanup_strv_free_ char **l = NULL; - int n, i; + char **l; - n = sd_ndisc_router_dnssl_get_domains(rt, &l); - if (n == -EBADMSG) { - log_info("Invalid domain(s)."); - break; - } - assert_se(n > 0); + assert_se(sd_ndisc_router_dnssl_get_domains(rt, &l) >= 0); - for (i = 0; i < n; i++) - log_info("Domain: %s", l[i]); + STRV_FOREACH(s, l) + log_info("Domain: %s", *s); assert_se(sd_ndisc_router_dnssl_get_lifetime(rt, &lifetime) >= 0); assert_se(sd_ndisc_router_dnssl_get_lifetime_timestamp(rt, CLOCK_REALTIME, &t) >= 0); diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index b3da0c2a31..61d591a2af 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -190,7 +190,7 @@ static int ndisc_request_route(Route *route, Link *link, sd_ndisc_router *rt) { assert(link->network); assert(rt); - r = sd_ndisc_router_get_address(rt, &router); + r = sd_ndisc_router_get_sender_address(rt, &router); if (r < 0) return r; @@ -336,7 +336,7 @@ static int ndisc_request_address(Address *address, Link *link, sd_ndisc_router * assert(link); assert(rt); - r = sd_ndisc_router_get_address(rt, &router); + r = sd_ndisc_router_get_sender_address(rt, &router); if (r < 0) return r; @@ -387,7 +387,7 @@ static int ndisc_router_drop_default(Link *link, sd_ndisc_router *rt) { assert(link->network); assert(rt); - r = sd_ndisc_router_get_address(rt, &gateway); + r = sd_ndisc_router_get_sender_address(rt, &gateway); if (r < 0) return log_link_warning_errno(link, r, "Failed to get router address from RA: %m"); @@ -430,7 +430,7 @@ static int ndisc_router_drop_default(Link *link, sd_ndisc_router *rt) { static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { usec_t lifetime_usec; struct in6_addr gateway; - unsigned preference; + uint8_t preference; int r; assert(link); @@ -452,7 +452,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_link_warning_errno(link, r, "Failed to get gateway lifetime from RA: %m"); - r = sd_ndisc_router_get_address(rt, &gateway); + r = sd_ndisc_router_get_sender_address(rt, &gateway); if (r < 0) return log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m"); @@ -625,7 +625,7 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r usec_t lifetime_valid_usec, lifetime_preferred_usec; _cleanup_set_free_ Set *addresses = NULL; struct in6_addr prefix, *a; - unsigned prefixlen; + uint8_t prefixlen; int r; assert(link); @@ -703,7 +703,7 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) { _cleanup_(route_unrefp) Route *route = NULL; - unsigned prefixlen, preference; + uint8_t prefixlen, preference; usec_t lifetime_usec; struct in6_addr prefix; int r; @@ -751,7 +751,7 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) { static int ndisc_router_drop_onlink_prefix(Link *link, sd_ndisc_router *rt) { _cleanup_(route_unrefp) Route *route = NULL; - unsigned prefixlen; + uint8_t prefixlen; struct in6_addr prefix; usec_t lifetime_usec; int r; @@ -800,9 +800,8 @@ static int ndisc_router_drop_onlink_prefix(Link *link, sd_ndisc_router *rt) { } static int ndisc_router_process_prefix(Link *link, sd_ndisc_router *rt) { - unsigned prefixlen; + uint8_t flags, prefixlen; struct in6_addr a; - uint8_t flags; int r; assert(link); @@ -856,7 +855,7 @@ static int ndisc_router_process_prefix(Link *link, sd_ndisc_router *rt) { static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) { _cleanup_(route_unrefp) Route *route = NULL; - unsigned preference, prefixlen; + uint8_t preference, prefixlen; struct in6_addr gateway, dst; usec_t lifetime_usec; int r; @@ -895,7 +894,7 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) { return 0; } - r = sd_ndisc_router_get_address(rt, &gateway); + r = sd_ndisc_router_get_sender_address(rt, &gateway); if (r < 0) return log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m"); @@ -962,7 +961,7 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) { if (!link->network->ndisc_use_dns) return 0; - r = sd_ndisc_router_get_address(rt, &router); + r = sd_ndisc_router_get_sender_address(rt, &router); if (r < 0) return log_link_warning_errno(link, r, "Failed to get router address from RA: %m"); @@ -1041,7 +1040,7 @@ DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( free); static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) { - _cleanup_strv_free_ char **l = NULL; + char **l; usec_t lifetime_usec; struct in6_addr router; bool updated = false, logged_about_too_many = false; @@ -1054,7 +1053,7 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) { if (link->network->ndisc_use_domains == DHCP_USE_DOMAINS_NO) return 0; - r = sd_ndisc_router_get_address(rt, &router); + r = sd_ndisc_router_get_sender_address(rt, &router); if (r < 0) return log_link_warning_errno(link, r, "Failed to get router address from RA: %m"); @@ -1145,11 +1144,10 @@ DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( 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; + const char *uri; usec_t lifetime_usec; NDiscCaptivePortal *exist; struct in6_addr router; - const char *uri; - size_t len; int r; assert(link); @@ -1159,7 +1157,7 @@ static int ndisc_router_process_captive_portal(Link *link, sd_ndisc_router *rt) if (!link->network->ndisc_use_captive_portal) return 0; - r = sd_ndisc_router_get_address(rt, &router); + r = sd_ndisc_router_get_sender_address(rt, &router); if (r < 0) return log_link_warning_errno(link, r, "Failed to get router address from RA: %m"); @@ -1170,31 +1168,25 @@ static int ndisc_router_process_captive_portal(Link *link, sd_ndisc_router *rt) if (r < 0) return log_link_warning_errno(link, r, "Failed to get lifetime of RA message: %m"); - r = sd_ndisc_router_captive_portal_get_uri(rt, &uri, &len); + r = sd_ndisc_router_get_captive_portal(rt, &uri); if (r < 0) return log_link_warning_errno(link, r, "Failed to get captive portal from RA: %m"); - if (len == 0) - return log_link_warning_errno(link, SYNTHETIC_ERRNO(EBADMSG), "Received empty captive portal, ignoring."); - - 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_warning_errno(link, SYNTHETIC_ERRNO(EBADMSG), "Received invalid captive portal, ignoring."); + captive_portal = strdup(uri); + if (!captive_portal) + return log_oom(); if (lifetime_usec == 0) { /* Drop the portal with zero lifetime. */ ndisc_captive_portal_free(set_remove(link->ndisc_captive_portals, - &(NDiscCaptivePortal) { + &(const NDiscCaptivePortal) { .captive_portal = captive_portal, })); return 0; } exist = set_get(link->ndisc_captive_portals, - &(NDiscCaptivePortal) { + &(const NDiscCaptivePortal) { .captive_portal = captive_portal, }); if (exist) { @@ -1268,7 +1260,7 @@ static int ndisc_router_process_pref64(Link *link, sd_ndisc_router *rt) { _cleanup_free_ NDiscPREF64 *new_entry = NULL; usec_t lifetime_usec; struct in6_addr a, router; - unsigned prefix_len; + uint8_t prefix_len; NDiscPREF64 *exist; int r; @@ -1279,7 +1271,7 @@ static int ndisc_router_process_pref64(Link *link, sd_ndisc_router *rt) { if (!link->network->ndisc_use_pref64) return 0; - r = sd_ndisc_router_get_address(rt, &router); + r = sd_ndisc_router_get_sender_address(rt, &router); if (r < 0) return log_link_warning_errno(link, r, "Failed to get router address from RA: %m"); @@ -1617,7 +1609,7 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { assert(link->manager); assert(rt); - r = sd_ndisc_router_get_address(rt, &router); + r = sd_ndisc_router_get_sender_address(rt, &router); if (r == -ENODATA) { log_link_debug(link, "Received RA without router address, ignoring."); return 0; diff --git a/src/systemd/sd-ndisc-router.h b/src/systemd/sd-ndisc-router.h index dde37bcfd7..32ee5796e6 100644 --- a/src/systemd/sd-ndisc-router.h +++ b/src/systemd/sd-ndisc-router.h @@ -33,25 +33,26 @@ sd_ndisc_router *sd_ndisc_router_ref(sd_ndisc_router *rt); sd_ndisc_router *sd_ndisc_router_unref(sd_ndisc_router *rt); _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc_router, sd_ndisc_router_unref); -int sd_ndisc_router_get_address(sd_ndisc_router *rt, struct in6_addr *ret); +int sd_ndisc_router_get_sender_address(sd_ndisc_router *rt, struct in6_addr *ret); int sd_ndisc_router_get_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret); int sd_ndisc_router_get_hop_limit(sd_ndisc_router *rt, uint8_t *ret); int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret); -int sd_ndisc_router_get_preference(sd_ndisc_router *rt, unsigned *ret); +int sd_ndisc_router_get_preference(sd_ndisc_router *rt, uint8_t *ret); int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint64_t *ret); int sd_ndisc_router_get_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret); int sd_ndisc_router_get_reachable_time(sd_ndisc_router *rt, uint64_t *ret); int sd_ndisc_router_get_retransmission_time(sd_ndisc_router *rt, uint64_t *ret); int sd_ndisc_router_get_sender_mac(sd_ndisc_router *rt, struct ether_addr *ret); int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret); +int sd_ndisc_router_get_captive_portal(sd_ndisc_router *rt, const char **ret); /* Generic option access */ int sd_ndisc_router_option_rewind(sd_ndisc_router *rt); int sd_ndisc_router_option_next(sd_ndisc_router *rt); int sd_ndisc_router_option_get_type(sd_ndisc_router *rt, uint8_t *ret); int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type); -int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t *ret_size); +int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const uint8_t **ret, size_t *ret_size); /* Specific option access: SD_NDISC_OPTION_PREFIX_INFORMATION */ int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router *rt, uint64_t *ret); @@ -60,14 +61,14 @@ int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router *rt, uint64_t int sd_ndisc_router_prefix_get_preferred_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret); int sd_ndisc_router_prefix_get_flags(sd_ndisc_router *rt, uint8_t *ret); int sd_ndisc_router_prefix_get_address(sd_ndisc_router *rt, struct in6_addr *ret); -int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router *rt, unsigned *ret); +int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router *rt, uint8_t *ret); /* Specific option access: SD_NDISC_OPTION_ROUTE_INFORMATION */ int sd_ndisc_router_route_get_lifetime(sd_ndisc_router *rt, uint64_t *ret); int sd_ndisc_router_route_get_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret); int sd_ndisc_router_route_get_address(sd_ndisc_router *rt, struct in6_addr *ret); -int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router *rt, unsigned *ret); -int sd_ndisc_router_route_get_preference(sd_ndisc_router *rt, unsigned *ret); +int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router *rt, uint8_t *ret); +int sd_ndisc_router_route_get_preference(sd_ndisc_router *rt, uint8_t *ret); /* Specific option access: SD_NDISC_OPTION_RDNSS */ int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **ret); @@ -79,12 +80,9 @@ int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router *rt, char ***ret); int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router *rt, uint64_t *ret); int sd_ndisc_router_dnssl_get_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret); -/* Specific option access: SD_NDISC_OPTION_CAPTIVE_PORTAL */ -int sd_ndisc_router_captive_portal_get_uri(sd_ndisc_router *rt, const char **ret, size_t *ret_size); - /* Specific option access: SD_NDISC_OPTION_PREF64 */ int sd_ndisc_router_prefix64_get_prefix(sd_ndisc_router *rt, struct in6_addr *ret); -int sd_ndisc_router_prefix64_get_prefixlen(sd_ndisc_router *rt, unsigned *ret); +int sd_ndisc_router_prefix64_get_prefixlen(sd_ndisc_router *rt, uint8_t *ret); int sd_ndisc_router_prefix64_get_lifetime(sd_ndisc_router *rt, uint64_t *ret); int sd_ndisc_router_prefix64_get_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);