diff options
Diffstat (limited to 'src/libsystemd-network/ndisc-router.c')
-rw-r--r-- | src/libsystemd-network/ndisc-router.c | 778 |
1 files changed, 0 insertions, 778 deletions
diff --git a/src/libsystemd-network/ndisc-router.c b/src/libsystemd-network/ndisc-router.c deleted file mode 100644 index 41ff2b353a..0000000000 --- a/src/libsystemd-network/ndisc-router.c +++ /dev/null @@ -1,778 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2014 Intel Corporation. All rights reserved. - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <netinet/icmp6.h> - -#include "sd-ndisc.h" - -#include "alloc-util.h" -#include "dns-domain.h" -#include "hostname-util.h" -#include "missing.h" -#include "ndisc-internal.h" -#include "ndisc-router.h" -#include "strv.h" - -_public_ sd_ndisc_router* sd_ndisc_router_ref(sd_ndisc_router *rt) { - if (!rt) - return NULL; - - assert(rt->n_ref > 0); - rt->n_ref++; - - return rt; -} - -_public_ sd_ndisc_router* sd_ndisc_router_unref(sd_ndisc_router *rt) { - if (!rt) - return NULL; - - assert(rt->n_ref > 0); - rt->n_ref--; - - if (rt->n_ref > 0) - return NULL; - - return mfree(rt); -} - -sd_ndisc_router *ndisc_router_new(size_t raw_size) { - sd_ndisc_router *rt; - - rt = malloc0(ALIGN(sizeof(sd_ndisc_router)) + raw_size); - if (!rt) - return NULL; - - rt->raw_size = raw_size; - rt->n_ref = 1; - - return rt; -} - -_public_ int sd_ndisc_router_from_raw(sd_ndisc_router **ret, const void *raw, size_t raw_size) { - _cleanup_(sd_ndisc_router_unrefp) sd_ndisc_router *rt = NULL; - int r; - - assert_return(ret, -EINVAL); - assert_return(raw || raw_size <= 0, -EINVAL); - - rt = ndisc_router_new(raw_size); - if (!rt) - return -ENOMEM; - - memcpy(NDISC_ROUTER_RAW(rt), raw, raw_size); - r = ndisc_router_parse(rt); - if (r < 0) - return r; - - *ret = rt; - rt = NULL; - - return r; -} - -_public_ int sd_ndisc_router_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr) { - assert_return(rt, -EINVAL); - assert_return(ret_addr, -EINVAL); - - if (in6_addr_is_null(&rt->address)) - return -ENODATA; - - *ret_addr = rt->address; - return 0; -} - -_public_ int sd_ndisc_router_get_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret) { - assert_return(rt, -EINVAL); - assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP); - assert_return(clock_supported(clock), -EOPNOTSUPP); - assert_return(ret, -EINVAL); - - if (!triple_timestamp_is_set(&rt->timestamp)) - return -ENODATA; - - *ret = triple_timestamp_by_clock(&rt->timestamp, clock); - return 0; -} - -_public_ int sd_ndisc_router_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size) { - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - assert_return(size, -EINVAL); - - *ret = NDISC_ROUTER_RAW(rt); - *size = rt->raw_size; - - return 0; -} - -int ndisc_router_parse(sd_ndisc_router *rt) { - struct nd_router_advert *a; - const uint8_t *p; - bool has_mtu = false, has_flag_extension = false; - size_t left; - - assert(rt); - - if (rt->raw_size < sizeof(struct nd_router_advert)) { - log_ndisc("Too small to be a router advertisement, ignoring."); - return -EBADMSG; - } - - /* Router advertisement packets are neatly aligned to 64bit boundaries, hence we can access them directly */ - a = NDISC_ROUTER_RAW(rt); - - if (a->nd_ra_type != ND_ROUTER_ADVERT) { - log_ndisc("Received ND packet that is not a router advertisement, ignoring."); - return -EBADMSG; - } - - if (a->nd_ra_code != 0) { - log_ndisc("Received ND packet with wrong RA code, ignoring."); - return -EBADMSG; - } - - rt->hop_limit = a->nd_ra_curhoplimit; - rt->flags = a->nd_ra_flags_reserved; /* the first 8bit */ - rt->lifetime = be16toh(a->nd_ra_router_lifetime); - - rt->preference = (rt->flags >> 3) & 3; - if (!IN_SET(rt->preference, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_HIGH)) - rt->preference = SD_NDISC_PREFERENCE_MEDIUM; - - p = (const uint8_t*) NDISC_ROUTER_RAW(rt) + sizeof(struct nd_router_advert); - left = rt->raw_size - sizeof(struct nd_router_advert); - - for (;;) { - uint8_t type; - size_t length; - - if (left == 0) - break; - - if (left < 2) { - log_ndisc("Option lacks header, ignoring datagram."); - return -EBADMSG; - } - - type = p[0]; - length = p[1] * 8; - - if (length == 0) { - log_ndisc("Zero-length option, ignoring datagram."); - return -EBADMSG; - } - if (left < length) { - log_ndisc("Option truncated, ignoring datagram."); - return -EBADMSG; - } - - switch (type) { - - case SD_NDISC_OPTION_PREFIX_INFORMATION: - - if (length != 4*8) { - log_ndisc("Prefix option of invalid size, ignoring datagram."); - return -EBADMSG; - } - - if (p[2] > 128) { - log_ndisc("Bad prefix length, ignoring datagram."); - return -EBADMSG; - } - - break; - - case SD_NDISC_OPTION_MTU: { - uint32_t m; - - if (has_mtu) { - log_ndisc("MTU option specified twice, ignoring."); - continue; - } - - if (length != 8) { - log_ndisc("MTU option of invalid size, ignoring datagram."); - return -EBADMSG; - } - - 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) { - log_ndisc("Route information option of invalid size, ignoring datagram."); - return -EBADMSG; - } - - if (p[2] > 128) { - log_ndisc("Bad route prefix length, ignoring datagram."); - return -EBADMSG; - } - - break; - - case SD_NDISC_OPTION_RDNSS: - if (length < 3*8 || (length % (2*8)) != 1*8) { - log_ndisc("RDNSS option has invalid size."); - return -EBADMSG; - } - - break; - - case SD_NDISC_OPTION_FLAGS_EXTENSION: - - if (has_flag_extension) { - log_ndisc("Flags extension option specified twice, ignoring."); - continue; - } - - if (length < 1*8) { - log_ndisc("Flags extension option has invalid size."); - return -EBADMSG; - } - - /* 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) { - log_ndisc("DNSSL option has invalid size."); - return -EBADMSG; - } - - break; - } - - p += length, left -= length; - } - - rt->rindex = sizeof(struct nd_router_advert); - return 0; -} - -_public_ int sd_ndisc_router_get_hop_limit(sd_ndisc_router *rt, uint8_t *ret) { - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - *ret = rt->hop_limit; - return 0; -} - -_public_ int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret_flags) { - assert_return(rt, -EINVAL); - assert_return(ret_flags, -EINVAL); - - *ret_flags = rt->flags; - return 0; -} - -_public_ int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint16_t *ret_lifetime) { - assert_return(rt, -EINVAL); - assert_return(ret_lifetime, -EINVAL); - - *ret_lifetime = rt->lifetime; - return 0; -} - -_public_ int sd_ndisc_router_get_preference(sd_ndisc_router *rt, unsigned *ret) { - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - *ret = rt->preference; - return 0; -} - -_public_ 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) - return -ENODATA; - - *ret = rt->mtu; - return 0; -} - -_public_ int sd_ndisc_router_option_rewind(sd_ndisc_router *rt) { - assert_return(rt, -EINVAL); - - assert(rt->raw_size >= sizeof(struct nd_router_advert)); - rt->rindex = sizeof(struct nd_router_advert); - - return rt->rindex < rt->raw_size; -} - -_public_ int sd_ndisc_router_option_next(sd_ndisc_router *rt) { - size_t length; - - assert_return(rt, -EINVAL); - - if (rt->rindex == rt->raw_size) /* EOF */ - return -ESPIPE; - - if (rt->rindex + 2 > rt->raw_size) /* Truncated message */ - return -EBADMSG; - - length = NDISC_ROUTER_OPTION_LENGTH(rt); - if (rt->rindex + length > rt->raw_size) - return -EBADMSG; - - rt->rindex += length; - return rt->rindex < rt->raw_size; -} - -_public_ int sd_ndisc_router_option_get_type(sd_ndisc_router *rt, uint8_t *ret) { - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - if (rt->rindex == rt->raw_size) /* EOF */ - return -ESPIPE; - - if (rt->rindex + 2 > rt->raw_size) /* Truncated message */ - return -EBADMSG; - - *ret = NDISC_ROUTER_OPTION_TYPE(rt); - return 0; -} - -_public_ 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; -} - -_public_ int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size) { - size_t length; - - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - assert_return(size, -EINVAL); - - /* Note that this returns the full option, including the option header */ - - if (rt->rindex + 2 > rt->raw_size) - return -EBADMSG; - - length = NDISC_ROUTER_OPTION_LENGTH(rt); - if (rt->rindex + length > rt->raw_size) - return -EBADMSG; - - *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex; - *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; -} - -_public_ int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router *rt, uint32_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 = be32toh(ri->nd_opt_pi_valid_time); - return 0; -} - -_public_ int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router *rt, uint32_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 = be32toh(pi->nd_opt_pi_preferred_time); - return 0; -} - -_public_ int sd_ndisc_router_prefix_get_flags(sd_ndisc_router *rt, uint8_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 = pi->nd_opt_pi_flags_reserved; - return 0; -} - -_public_ int sd_ndisc_router_prefix_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr) { - struct nd_opt_prefix_info *pi; - int r; - - assert_return(rt, -EINVAL); - assert_return(ret_addr, -EINVAL); - - r = get_prefix_info(rt, &pi); - if (r < 0) - return r; - - *ret_addr = pi->nd_opt_pi_prefix; - return 0; -} - -_public_ 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; -} - -_public_ int sd_ndisc_router_route_get_lifetime(sd_ndisc_router *rt, uint32_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 = be32toh(*(uint32_t*) (ri + 4)); - return 0; -} - -_public_ int sd_ndisc_router_route_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr) { - uint8_t *ri; - int r; - - assert_return(rt, -EINVAL); - assert_return(ret_addr, -EINVAL); - - r = get_route_info(rt, &ri); - if (r < 0) - return r; - - zero(*ret_addr); - memcpy(ret_addr, ri + 8, NDISC_ROUTER_OPTION_LENGTH(rt) - 8); - - return 0; -} - -_public_ 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; -} - -_public_ 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; - - *ret = (ri[3] >> 3) & 3; - if (!IN_SET(*ret, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_HIGH)) - *ret = SD_NDISC_PREFERENCE_MEDIUM; - - 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; -} - -_public_ 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; -} - -_public_ int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router *rt, uint32_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 = be32toh(*(uint32_t*) (ri + 4)); - 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; - - *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex; - return 0; -} - -_public_ 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 allocated = 0, 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, &normalized); - if (r < 0) - return r; - - /* 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, allocated, 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) - return r; - - n += r; - - left -= 1 + *p; - p += 1 + *p; - } - - if (strv_isempty(l)) { - *ret = NULL; - return 0; - } - - *ret = l; - l = NULL; - - return k; -} - -_public_ int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router *rt, uint32_t *ret_sec) { - uint8_t *ri; - int r; - - assert_return(rt, -EINVAL); - assert_return(ret_sec, -EINVAL); - - r = get_dnssl_info(rt, &ri); - if (r < 0) - return r; - - *ret_sec = be32toh(*(uint32_t*) (ri + 4)); - return 0; -} |