mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 00:14:32 +09:00
Merge pull request #31584 from yuwata/sd-ndisc-option-parser-cleanups
sd-ndisc: rewrite option parser
This commit is contained in:
@@ -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',
|
||||
|
||||
855
src/libsystemd-network/ndisc-option.c
Normal file
855
src/libsystemd-network/ndisc-option.c
Normal file
@@ -0,0 +1,855 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <netinet/icmp6.h>
|
||||
|
||||
#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,
|
||||
size_t offset,
|
||||
uint8_t *ret_type,
|
||||
size_t *ret_len,
|
||||
const uint8_t **ret_opt) {
|
||||
|
||||
assert(p);
|
||||
|
||||
if (offset == p->raw_size)
|
||||
return -ESPIPE; /* end of the packet */
|
||||
|
||||
if (offset > p->raw_size)
|
||||
return -EBADMSG;
|
||||
|
||||
if (p->raw_size - offset < sizeof(struct nd_opt_hdr))
|
||||
return -EBADMSG;
|
||||
|
||||
assert_cc(alignof(struct nd_opt_hdr) == 1);
|
||||
const struct nd_opt_hdr *hdr = (const struct nd_opt_hdr*) (p->raw_packet + offset);
|
||||
if (hdr->nd_opt_len == 0)
|
||||
return -EBADMSG;
|
||||
|
||||
size_t len = hdr->nd_opt_len * 8;
|
||||
if (p->raw_size - offset < len)
|
||||
return -EBADMSG;
|
||||
|
||||
if (ret_type)
|
||||
*ret_type = hdr->nd_opt_type;
|
||||
if (ret_len)
|
||||
*ret_len = len;
|
||||
if (ret_opt)
|
||||
*ret_opt = p->raw_packet + offset;
|
||||
|
||||
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,
|
||||
[PREFIX_LENGTH_CODE_56] = 56,
|
||||
[PREFIX_LENGTH_CODE_48] = 48,
|
||||
[PREFIX_LENGTH_CODE_40] = 40,
|
||||
[PREFIX_LENGTH_CODE_32] = 32,
|
||||
};
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
162
src/libsystemd-network/ndisc-option.h
Normal file
162
src/libsystemd-network/ndisc-option.h
Normal file
@@ -0,0 +1,162 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip6.h>
|
||||
|
||||
#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
|
||||
#define PREF64_MAX_LIFETIME_USEC (65528 * USEC_PER_SEC)
|
||||
|
||||
typedef enum PrefixLengthCode {
|
||||
PREFIX_LENGTH_CODE_96,
|
||||
PREFIX_LENGTH_CODE_64,
|
||||
PREFIX_LENGTH_CODE_56,
|
||||
PREFIX_LENGTH_CODE_48,
|
||||
PREFIX_LENGTH_CODE_40,
|
||||
PREFIX_LENGTH_CODE_32,
|
||||
_PREFIX_LENGTH_CODE_MAX,
|
||||
_PREFIX_LENGTH_CODE_INVALID = -EINVAL,
|
||||
} PrefixLengthCode;
|
||||
|
||||
/* rfc8781: section 4 - Scaled Lifetime: 13-bit unsigned integer. PREFIX_LEN (Prefix Length Code): 3-bit unsigned integer */
|
||||
struct nd_opt_prefix64_info {
|
||||
uint8_t type;
|
||||
uint8_t length;
|
||||
uint16_t lifetime_and_plc;
|
||||
uint8_t prefix[12];
|
||||
} _packed_;
|
||||
|
||||
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);
|
||||
@@ -1,73 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <netinet/icmp6.h>
|
||||
|
||||
#include "ndisc-protocol.h"
|
||||
|
||||
int ndisc_option_parse(
|
||||
ICMP6Packet *p,
|
||||
size_t offset,
|
||||
uint8_t *ret_type,
|
||||
size_t *ret_len,
|
||||
const uint8_t **ret_opt) {
|
||||
|
||||
assert(p);
|
||||
|
||||
if (offset == p->raw_size)
|
||||
return -ESPIPE; /* end of the packet */
|
||||
|
||||
if (offset > p->raw_size)
|
||||
return -EBADMSG;
|
||||
|
||||
if (p->raw_size - offset < sizeof(struct nd_opt_hdr))
|
||||
return -EBADMSG;
|
||||
|
||||
assert_cc(alignof(struct nd_opt_hdr) == 1);
|
||||
const struct nd_opt_hdr *hdr = (const struct nd_opt_hdr*) (p->raw_packet + offset);
|
||||
if (hdr->nd_opt_len == 0)
|
||||
return -EBADMSG;
|
||||
|
||||
size_t len = hdr->nd_opt_len * 8;
|
||||
if (p->raw_size - offset < len)
|
||||
return -EBADMSG;
|
||||
|
||||
if (ret_type)
|
||||
*ret_type = hdr->nd_opt_type;
|
||||
if (ret_len)
|
||||
*ret_len = len;
|
||||
if (ret_opt)
|
||||
*ret_opt = p->raw_packet + offset;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const uint8_t prefix_length_code_to_prefix_length[_PREFIX_LENGTH_CODE_MAX] = {
|
||||
[PREFIX_LENGTH_CODE_96] = 96,
|
||||
[PREFIX_LENGTH_CODE_64] = 64,
|
||||
[PREFIX_LENGTH_CODE_56] = 56,
|
||||
[PREFIX_LENGTH_CODE_48] = 48,
|
||||
[PREFIX_LENGTH_CODE_40] = 40,
|
||||
[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) {
|
||||
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;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "icmp6-packet.h"
|
||||
#include "time-util.h"
|
||||
|
||||
/* RFC 8781: PREF64 or (NAT64 prefix) */
|
||||
#define PREF64_SCALED_LIFETIME_MASK 0xfff8
|
||||
#define PREF64_PLC_MASK 0x0007
|
||||
#define PREF64_MAX_LIFETIME_USEC (65528 * USEC_PER_SEC)
|
||||
|
||||
typedef enum PrefixLengthCode {
|
||||
PREFIX_LENGTH_CODE_96,
|
||||
PREFIX_LENGTH_CODE_64,
|
||||
PREFIX_LENGTH_CODE_56,
|
||||
PREFIX_LENGTH_CODE_48,
|
||||
PREFIX_LENGTH_CODE_40,
|
||||
PREFIX_LENGTH_CODE_32,
|
||||
_PREFIX_LENGTH_CODE_MAX,
|
||||
_PREFIX_LENGTH_CODE_INVALID = -EINVAL,
|
||||
} PrefixLengthCode;
|
||||
|
||||
/* rfc8781: section 4 - Scaled Lifetime: 13-bit unsigned integer. PREFIX_LEN (Prefix Length Code): 3-bit unsigned integer */
|
||||
struct nd_opt_prefix64_info {
|
||||
uint8_t type;
|
||||
uint8_t length;
|
||||
uint16_t lifetime_and_plc;
|
||||
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);
|
||||
|
||||
int ndisc_option_parse(
|
||||
ICMP6Packet *p,
|
||||
size_t offset,
|
||||
uint8_t *ret_type,
|
||||
size_t *ret_len,
|
||||
const uint8_t **ret_opt);
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -8,21 +8,15 @@
|
||||
#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-protocol.h"
|
||||
#include "ndisc-router-internal.h"
|
||||
#include "strv.h"
|
||||
|
||||
static sd_ndisc_router* ndisc_router_free(sd_ndisc_router *rt) {
|
||||
if (!rt)
|
||||
return NULL;
|
||||
|
||||
icmp6_packet_unref(rt->packet);
|
||||
set_free(rt->options);
|
||||
return mfree(rt);
|
||||
}
|
||||
|
||||
@@ -40,12 +34,13 @@ 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;
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -90,24 +85,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 +106,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 +151,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;
|
||||
}
|
||||
|
||||
@@ -272,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);
|
||||
|
||||
@@ -280,602 +174,134 @@ 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);
|
||||
|
||||
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_get_captive_portal(sd_ndisc_router *rt, const char **ret) {
|
||||
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;
|
||||
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;
|
||||
|
||||
int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const uint8_t **ret, size_t *ret_size) {
|
||||
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, 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, 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);
|
||||
|
||||
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, 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);
|
||||
|
||||
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;
|
||||
int r;
|
||||
DEFINE_GETTER(dnssl, SD_NDISC_OPTION_DNSSL, domains, char**);
|
||||
DEFINE_GETTER(dnssl, SD_NDISC_OPTION_DNSSL, lifetime, uint64_t);
|
||||
|
||||
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;
|
||||
|
||||
*ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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, uint8_t);
|
||||
DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, prefix, struct in6_addr);
|
||||
DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, lifetime, uint64_t);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
***/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
@@ -32,24 +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);
|
||||
@@ -58,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);
|
||||
@@ -77,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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user