From c0edd6b3b304acc0a26a1e7e1592f6dc0bd8507a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 29 Feb 2024 12:42:16 +0900 Subject: [PATCH] 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 | 725 ++---------------- 2 files changed, 93 insertions(+), 665 deletions(-) diff --git a/src/libsystemd-network/ndisc-router-internal.h b/src/libsystemd-network/ndisc-router-internal.h index f1a03bc0ab5..6f133508351 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 309ef10195c..46baa4fc8ea 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_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; - }} - } + 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"); - 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_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; -} - -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; -} - -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); -} - -int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type) { - uint8_t k; - int r; - - assert_return(rt, -EINVAL); - - r = sd_ndisc_router_option_get_type(rt, &k); - if (r < 0) - return r; - - return type == k; -} - -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->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; -} - -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; - +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); - r = get_prefix_info(rt, &pi); - if (r < 0) - return r; + sd_ndisc_option *p = ndisc_option_get(rt->options, SD_NDISC_OPTION_CAPTIVE_PORTAL); + if (!p) + return -ENODATA; - *ret = be32_sec_to_usec(pi->nd_opt_pi_preferred_time, /* max_as_infinity = */ true); + *ret = p->captive_portal; + *ret_size = strlen(p->captive_portal); 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; - +int sd_ndisc_router_option_rewind(sd_ndisc_router *rt) { 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; - } - - *ret = flags; - return 0; + rt->iterator = ITERATOR_FIRST; + return sd_ndisc_router_option_next(rt); } -int sd_ndisc_router_prefix_get_address(sd_ndisc_router *rt, struct in6_addr *ret) { - struct nd_opt_prefix_info *pi; - int r; - +int sd_ndisc_router_option_next(sd_ndisc_router *rt) { 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; + return set_iterate(rt->options, &rt->iterator, (void**) &rt->current_option); } -int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) { - struct nd_opt_prefix_info *pi; - int r; - +int sd_ndisc_router_option_get_type(sd_ndisc_router *rt, uint8_t *ret) { 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; + if (!rt->current_option) + return -ENODATA; - *ret = ri; + *ret = rt->current_option->type; return 0; } -int sd_ndisc_router_route_get_lifetime(sd_ndisc_router *rt, uint64_t *ret) { - uint8_t *ri; +int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type) { + uint8_t t; int r; assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - r = get_route_info(rt, &ri); + r = sd_ndisc_router_option_get_type(rt, &t); if (r < 0) return r; - *ret = unaligned_be32_sec_to_usec(ri + 4, /* max_as_infinity = */ true); - return 0; + return t == type; } -int sd_ndisc_router_route_get_address(sd_ndisc_router *rt, struct in6_addr *ret) { - uint8_t *ri; - int r; - +int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t *ret_size) { 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); + if (!rt->current_option) + return -ENODATA; - return 0; + return ndisc_option_parse(rt->packet, rt->current_option->offset, NULL, ret_size, (const uint8_t**) ret); } -int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) { - uint8_t *ri; - int r; +#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; \ + } - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); +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); - r = get_route_info(rt, &ri); - if (r < 0) - return r; +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); - *ret = ri[2]; - return 0; -} +DEFINE_GETTER(rdnss, SD_NDISC_OPTION_RDNSS, lifetime, uint64_t); -int sd_ndisc_router_route_get_preference(sd_ndisc_router *rt, unsigned *ret) { - uint8_t *ri; +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_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; -} - -int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **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 = (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; + *ret = rt->current_option->rdnss.addresses; + return (int) rt->current_option->rdnss.n_addresses; } -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; - - *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex; - return 0; -} +DEFINE_GETTER(dnssl, SD_NDISC_OPTION_DNSSL, lifetime, uint64_t); 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); + 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 != 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; + char **q = strv_copy(rt->current_option->dnssl.domains); + if (!q) + return -ENOMEM; - *ret = ri; - return 0; + *ret = q; + return (int) strv_length(q); } -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); -- 2.47.3