sd-ndisc-router: use ndisc_parse_options() and friends to parse Router Advertisement
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -8,13 +8,7 @@
|
||||
#include "sd-ndisc.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "dns-domain.h"
|
||||
#include "escape.h"
|
||||
#include "hostname-util.h"
|
||||
#include "memory-util.h"
|
||||
#include "missing_network.h"
|
||||
#include "ndisc-internal.h"
|
||||
#include "ndisc-option.h"
|
||||
#include "ndisc-router-internal.h"
|
||||
#include "strv.h"
|
||||
|
||||
@@ -23,6 +17,7 @@ static sd_ndisc_router* ndisc_router_free(sd_ndisc_router *rt) {
|
||||
return NULL;
|
||||
|
||||
icmp6_packet_unref(rt->packet);
|
||||
set_free(rt->options);
|
||||
return mfree(rt);
|
||||
}
|
||||
|
||||
@@ -40,6 +35,7 @@ sd_ndisc_router* ndisc_router_new(ICMP6Packet *packet) {
|
||||
*rt = (sd_ndisc_router) {
|
||||
.n_ref = 1,
|
||||
.packet = icmp6_packet_ref(packet),
|
||||
.iterator = ITERATOR_FIRST,
|
||||
};
|
||||
|
||||
return rt;
|
||||
@@ -90,24 +86,8 @@ DEFINE_GET_TIMESTAMP(rdnss_get_lifetime);
|
||||
DEFINE_GET_TIMESTAMP(dnssl_get_lifetime);
|
||||
DEFINE_GET_TIMESTAMP(prefix64_get_lifetime);
|
||||
|
||||
static bool pref64_option_verify(const struct nd_opt_prefix64_info *p, size_t length) {
|
||||
uint16_t lifetime_and_plc;
|
||||
|
||||
assert(p);
|
||||
|
||||
if (length != sizeof(struct nd_opt_prefix64_info))
|
||||
return false;
|
||||
|
||||
lifetime_and_plc = be16toh(p->lifetime_and_plc);
|
||||
if (pref64_plc_to_prefix_length(lifetime_and_plc, NULL) < 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int ndisc_router_parse(sd_ndisc *nd, sd_ndisc_router *rt) {
|
||||
const struct nd_router_advert *a;
|
||||
bool has_mtu = false, has_flag_extension = false;
|
||||
int r;
|
||||
|
||||
assert(rt);
|
||||
@@ -127,107 +107,20 @@ int ndisc_router_parse(sd_ndisc *nd, sd_ndisc_router *rt) {
|
||||
rt->reachable_time_usec = be32_msec_to_usec(a->nd_ra_reachable, /* mas_as_infinity = */ false);
|
||||
rt->retransmission_time_usec = be32_msec_to_usec(a->nd_ra_retransmit, /* max_as_infinity = */ false);
|
||||
|
||||
/* RFC 4191 section 2.2
|
||||
* Prf (Default Router Preference)
|
||||
* 2-bit signed integer. Indicates whether to prefer this router over other default routers. If the
|
||||
* Router Lifetime is zero, the preference value MUST be set to (00) by the sender and MUST be
|
||||
* ignored by the receiver. If the Reserved (10) value is received, the receiver MUST treat the value
|
||||
* as if it were (00). */
|
||||
rt->preference = (rt->flags >> 3) & 3;
|
||||
if (!IN_SET(rt->preference, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_HIGH))
|
||||
if (rt->preference == SD_NDISC_PREFERENCE_RESERVED)
|
||||
rt->preference = SD_NDISC_PREFERENCE_MEDIUM;
|
||||
|
||||
for (size_t offset = sizeof(struct nd_router_advert), length; offset < rt->packet->raw_size; offset += length) {
|
||||
uint8_t type;
|
||||
const uint8_t *p;
|
||||
r = ndisc_parse_options(rt->packet, &rt->options);
|
||||
if (r < 0)
|
||||
return log_ndisc_errno(nd, r, "Failed to parse NDisc options in router advertisement message, ignoring: %m");
|
||||
|
||||
r = ndisc_option_parse(rt->packet, offset, &type, &length, &p);
|
||||
if (r < 0)
|
||||
return log_ndisc_errno(nd, r, "Failed to parse NDisc option header, ignoring: %m");
|
||||
|
||||
switch (type) {
|
||||
|
||||
case SD_NDISC_OPTION_PREFIX_INFORMATION:
|
||||
|
||||
if (length != 4*8)
|
||||
return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Prefix option of invalid size, ignoring datagram.");
|
||||
|
||||
if (p[2] > 128)
|
||||
return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Bad prefix length, ignoring datagram.");
|
||||
|
||||
break;
|
||||
|
||||
case SD_NDISC_OPTION_MTU: {
|
||||
uint32_t m;
|
||||
|
||||
if (has_mtu) {
|
||||
log_ndisc(nd, "MTU option specified twice, ignoring.");
|
||||
break;
|
||||
}
|
||||
|
||||
if (length != 8)
|
||||
return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"MTU option of invalid size, ignoring datagram.");
|
||||
|
||||
m = be32toh(*(uint32_t*) (p + 4));
|
||||
if (m >= IPV6_MIN_MTU) /* ignore invalidly small MTUs */
|
||||
rt->mtu = m;
|
||||
|
||||
has_mtu = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case SD_NDISC_OPTION_ROUTE_INFORMATION:
|
||||
if (length < 1*8 || length > 3*8)
|
||||
return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Route information option of invalid size, ignoring datagram.");
|
||||
|
||||
if (p[2] > 128)
|
||||
return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Bad route prefix length, ignoring datagram.");
|
||||
|
||||
break;
|
||||
|
||||
case SD_NDISC_OPTION_RDNSS:
|
||||
if (length < 3*8 || (length % (2*8)) != 1*8)
|
||||
return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), "RDNSS option has invalid size.");
|
||||
|
||||
break;
|
||||
|
||||
case SD_NDISC_OPTION_FLAGS_EXTENSION:
|
||||
|
||||
if (has_flag_extension) {
|
||||
log_ndisc(nd, "Flags extension option specified twice, ignoring.");
|
||||
break;
|
||||
}
|
||||
|
||||
if (length < 1*8)
|
||||
return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Flags extension option has invalid size.");
|
||||
|
||||
/* Add in the additional flags bits */
|
||||
rt->flags |=
|
||||
((uint64_t) p[2] << 8) |
|
||||
((uint64_t) p[3] << 16) |
|
||||
((uint64_t) p[4] << 24) |
|
||||
((uint64_t) p[5] << 32) |
|
||||
((uint64_t) p[6] << 40) |
|
||||
((uint64_t) p[7] << 48);
|
||||
|
||||
has_flag_extension = true;
|
||||
break;
|
||||
|
||||
case SD_NDISC_OPTION_DNSSL:
|
||||
if (length < 2*8)
|
||||
return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"DNSSL option has invalid size.");
|
||||
|
||||
break;
|
||||
case SD_NDISC_OPTION_PREF64: {
|
||||
if (!pref64_option_verify((struct nd_opt_prefix64_info *) p, length))
|
||||
log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"PREF64 prefix has invalid prefix length.");
|
||||
break;
|
||||
}}
|
||||
}
|
||||
|
||||
rt->rindex = sizeof(struct nd_router_advert);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -259,7 +152,9 @@ int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret) {
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
*ret = rt->flags;
|
||||
sd_ndisc_option *p = ndisc_option_get(rt->options, SD_NDISC_OPTION_FLAGS_EXTENSION);
|
||||
|
||||
*ret = rt->flags | (p ? p->extended_flags : 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -284,598 +179,144 @@ int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret) {
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
if (rt->mtu <= 0)
|
||||
sd_ndisc_option *p = ndisc_option_get(rt->options, SD_NDISC_OPTION_MTU);
|
||||
if (!p)
|
||||
return -ENODATA;
|
||||
|
||||
*ret = rt->mtu;
|
||||
*ret = p->mtu;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ndisc_router_captive_portal_get_uri(sd_ndisc_router *rt, const char **ret, size_t *ret_size) {
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
sd_ndisc_option *p = ndisc_option_get(rt->options, SD_NDISC_OPTION_CAPTIVE_PORTAL);
|
||||
if (!p)
|
||||
return -ENODATA;
|
||||
|
||||
*ret = p->captive_portal;
|
||||
*ret_size = strlen(p->captive_portal);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ndisc_router_option_rewind(sd_ndisc_router *rt) {
|
||||
assert_return(rt, -EINVAL);
|
||||
|
||||
assert(rt->packet);
|
||||
assert(rt->packet->raw_size >= sizeof(struct nd_router_advert));
|
||||
|
||||
rt->rindex = sizeof(struct nd_router_advert);
|
||||
return rt->rindex < rt->packet->raw_size;
|
||||
rt->iterator = ITERATOR_FIRST;
|
||||
return sd_ndisc_router_option_next(rt);
|
||||
}
|
||||
|
||||
int sd_ndisc_router_option_next(sd_ndisc_router *rt) {
|
||||
size_t length;
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
|
||||
r = ndisc_option_parse(rt->packet, rt->rindex, NULL, &length, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
rt->rindex += length;
|
||||
return rt->rindex < rt->packet->raw_size;
|
||||
return set_iterate(rt->options, &rt->iterator, (void**) &rt->current_option);
|
||||
}
|
||||
|
||||
int sd_ndisc_router_option_get_type(sd_ndisc_router *rt, uint8_t *ret) {
|
||||
assert_return(rt, -EINVAL);
|
||||
return ndisc_option_parse(rt->packet, rt->rindex, ret, NULL, NULL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
if (!rt->current_option)
|
||||
return -ENODATA;
|
||||
|
||||
*ret = rt->current_option->type;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type) {
|
||||
uint8_t k;
|
||||
uint8_t t;
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
|
||||
r = sd_ndisc_router_option_get_type(rt, &k);
|
||||
r = sd_ndisc_router_option_get_type(rt, &t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return type == k;
|
||||
return t == type;
|
||||
}
|
||||
|
||||
int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t *ret_size) {
|
||||
size_t length;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
assert_return(ret_size, -EINVAL);
|
||||
|
||||
/* Note that this returns the full option, including the option header */
|
||||
if (!rt->current_option)
|
||||
return -ENODATA;
|
||||
|
||||
if (rt->rindex + 2 > rt->packet->raw_size)
|
||||
return -EBADMSG;
|
||||
|
||||
length = NDISC_ROUTER_OPTION_LENGTH(rt);
|
||||
if (rt->rindex + length > rt->packet->raw_size)
|
||||
return -EBADMSG;
|
||||
|
||||
*ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
|
||||
*ret_size = length;
|
||||
|
||||
return 0;
|
||||
return ndisc_option_parse(rt->packet, rt->current_option->offset, NULL, ret_size, (const uint8_t**) ret);
|
||||
}
|
||||
|
||||
static int get_prefix_info(sd_ndisc_router *rt, struct nd_opt_prefix_info **ret) {
|
||||
struct nd_opt_prefix_info *ri;
|
||||
size_t length;
|
||||
int r;
|
||||
|
||||
assert(rt);
|
||||
assert(ret);
|
||||
|
||||
r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_PREFIX_INFORMATION);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -EMEDIUMTYPE;
|
||||
|
||||
length = NDISC_ROUTER_OPTION_LENGTH(rt);
|
||||
if (length != sizeof(struct nd_opt_prefix_info))
|
||||
return -EBADMSG;
|
||||
|
||||
ri = (struct nd_opt_prefix_info*) ((uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex);
|
||||
if (ri->nd_opt_pi_prefix_len > 128)
|
||||
return -EBADMSG;
|
||||
|
||||
*ret = ri;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router *rt, uint64_t *ret) {
|
||||
struct nd_opt_prefix_info *ri;
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = get_prefix_info(rt, &ri);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = be32_sec_to_usec(ri->nd_opt_pi_valid_time, /* max_as_infinity = */ true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router *rt, uint64_t *ret) {
|
||||
struct nd_opt_prefix_info *pi;
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = get_prefix_info(rt, &pi);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = be32_sec_to_usec(pi->nd_opt_pi_preferred_time, /* max_as_infinity = */ true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ndisc_router_prefix_get_flags(sd_ndisc_router *rt, uint8_t *ret) {
|
||||
struct nd_opt_prefix_info *pi;
|
||||
uint8_t flags;
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = get_prefix_info(rt, &pi);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
flags = pi->nd_opt_pi_flags_reserved;
|
||||
|
||||
if ((flags & ND_OPT_PI_FLAG_AUTO) && (pi->nd_opt_pi_prefix_len != 64)) {
|
||||
log_ndisc(NULL, "Invalid prefix length, ignoring prefix for stateless autoconfiguration.");
|
||||
flags &= ~ND_OPT_PI_FLAG_AUTO;
|
||||
#define DEFINE_GETTER(name, type, element, element_type) \
|
||||
int sd_ndisc_router_##name##_get_##element( \
|
||||
sd_ndisc_router *rt, \
|
||||
element_type *ret) { \
|
||||
\
|
||||
int r; \
|
||||
\
|
||||
assert_return(rt, -EINVAL); \
|
||||
assert_return(ret, -EINVAL); \
|
||||
\
|
||||
r = sd_ndisc_router_option_is_type(rt, type); \
|
||||
if (r < 0) \
|
||||
return r; \
|
||||
if (r == 0) \
|
||||
return -EMEDIUMTYPE; \
|
||||
\
|
||||
*ret = rt->current_option->name.element; \
|
||||
return 0; \
|
||||
}
|
||||
|
||||
*ret = flags;
|
||||
return 0;
|
||||
}
|
||||
DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, flags, uint8_t);
|
||||
DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, prefixlen, unsigned);
|
||||
DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, address, struct in6_addr);
|
||||
DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, valid_lifetime, uint64_t);
|
||||
DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, preferred_lifetime, uint64_t);
|
||||
|
||||
int sd_ndisc_router_prefix_get_address(sd_ndisc_router *rt, struct in6_addr *ret) {
|
||||
struct nd_opt_prefix_info *pi;
|
||||
DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, preference, unsigned);
|
||||
DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, prefixlen, unsigned);
|
||||
DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, address, struct in6_addr);
|
||||
DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, lifetime, uint64_t);
|
||||
|
||||
DEFINE_GETTER(rdnss, SD_NDISC_OPTION_RDNSS, lifetime, uint64_t);
|
||||
|
||||
int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **ret) {
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = get_prefix_info(rt, &pi);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = pi->nd_opt_pi_prefix;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) {
|
||||
struct nd_opt_prefix_info *pi;
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = get_prefix_info(rt, &pi);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (pi->nd_opt_pi_prefix_len > 128)
|
||||
return -EBADMSG;
|
||||
|
||||
*ret = pi->nd_opt_pi_prefix_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_route_info(sd_ndisc_router *rt, uint8_t **ret) {
|
||||
uint8_t *ri;
|
||||
size_t length;
|
||||
int r;
|
||||
|
||||
assert(rt);
|
||||
assert(ret);
|
||||
|
||||
r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_ROUTE_INFORMATION);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -EMEDIUMTYPE;
|
||||
|
||||
length = NDISC_ROUTER_OPTION_LENGTH(rt);
|
||||
if (length < 1*8 || length > 3*8)
|
||||
return -EBADMSG;
|
||||
|
||||
ri = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
|
||||
|
||||
if (ri[2] > 128)
|
||||
return -EBADMSG;
|
||||
|
||||
*ret = ri;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ndisc_router_route_get_lifetime(sd_ndisc_router *rt, uint64_t *ret) {
|
||||
uint8_t *ri;
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = get_route_info(rt, &ri);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = unaligned_be32_sec_to_usec(ri + 4, /* max_as_infinity = */ true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ndisc_router_route_get_address(sd_ndisc_router *rt, struct in6_addr *ret) {
|
||||
uint8_t *ri;
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = get_route_info(rt, &ri);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
zero(*ret);
|
||||
memcpy(ret, ri + 8, NDISC_ROUTER_OPTION_LENGTH(rt) - 8);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) {
|
||||
uint8_t *ri;
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = get_route_info(rt, &ri);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = ri[2];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ndisc_router_route_get_preference(sd_ndisc_router *rt, unsigned *ret) {
|
||||
uint8_t *ri;
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = get_route_info(rt, &ri);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!IN_SET((ri[3] >> 3) & 3, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_MEDIUM, SD_NDISC_PREFERENCE_HIGH))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
*ret = (ri[3] >> 3) & 3;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_rdnss_info(sd_ndisc_router *rt, uint8_t **ret) {
|
||||
size_t length;
|
||||
int r;
|
||||
|
||||
assert(rt);
|
||||
assert(ret);
|
||||
|
||||
r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_RDNSS);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -EMEDIUMTYPE;
|
||||
|
||||
length = NDISC_ROUTER_OPTION_LENGTH(rt);
|
||||
if (length < 3*8 || (length % (2*8)) != 1*8)
|
||||
return -EBADMSG;
|
||||
|
||||
*ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
|
||||
return 0;
|
||||
*ret = rt->current_option->rdnss.addresses;
|
||||
return (int) rt->current_option->rdnss.n_addresses;
|
||||
}
|
||||
|
||||
int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **ret) {
|
||||
uint8_t *ri;
|
||||
DEFINE_GETTER(dnssl, SD_NDISC_OPTION_DNSSL, lifetime, uint64_t);
|
||||
|
||||
int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router *rt, char ***ret) {
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = get_rdnss_info(rt, &ri);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = (const struct in6_addr*) (ri + 8);
|
||||
return (NDISC_ROUTER_OPTION_LENGTH(rt) - 8) / 16;
|
||||
}
|
||||
|
||||
int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router *rt, uint64_t *ret) {
|
||||
uint8_t *ri;
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = get_rdnss_info(rt, &ri);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = unaligned_be32_sec_to_usec(ri + 4, /* max_as_infinity = */ true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_dnssl_info(sd_ndisc_router *rt, uint8_t **ret) {
|
||||
size_t length;
|
||||
int r;
|
||||
|
||||
assert(rt);
|
||||
assert(ret);
|
||||
|
||||
r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_DNSSL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -EMEDIUMTYPE;
|
||||
|
||||
length = NDISC_ROUTER_OPTION_LENGTH(rt);
|
||||
if (length < 2*8)
|
||||
return -EBADMSG;
|
||||
char **q = strv_copy(rt->current_option->dnssl.domains);
|
||||
if (!q)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
|
||||
return 0;
|
||||
*ret = q;
|
||||
return (int) strv_length(q);
|
||||
}
|
||||
|
||||
int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router *rt, char ***ret) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
_cleanup_free_ char *e = NULL;
|
||||
size_t n = 0, left;
|
||||
uint8_t *ri, *p;
|
||||
bool first = true;
|
||||
int r;
|
||||
unsigned k = 0;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = get_dnssl_info(rt, &ri);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
p = ri + 8;
|
||||
left = NDISC_ROUTER_OPTION_LENGTH(rt) - 8;
|
||||
|
||||
for (;;) {
|
||||
if (left == 0) {
|
||||
|
||||
if (n > 0) /* Not properly NUL terminated */
|
||||
return -EBADMSG;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (*p == 0) {
|
||||
/* Found NUL termination */
|
||||
|
||||
if (n > 0) {
|
||||
_cleanup_free_ char *normalized = NULL;
|
||||
|
||||
e[n] = 0;
|
||||
r = dns_name_normalize(e, 0, &normalized);
|
||||
if (r < 0) {
|
||||
_cleanup_free_ char *escaped = cescape(e);
|
||||
log_debug_errno(r, "Failed to normalize advertised domain name \"%s\": %m", strna(escaped));
|
||||
/* Here, do not propagate error code from dns_name_normalize() except for ENOMEM. */
|
||||
return r == -ENOMEM ? -ENOMEM : -EBADMSG;
|
||||
}
|
||||
|
||||
/* Ignore the root domain name or "localhost" and friends */
|
||||
if (!is_localhost(normalized) &&
|
||||
!dns_name_is_root(normalized)) {
|
||||
|
||||
if (strv_push(&l, normalized) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
normalized = NULL;
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
n = 0;
|
||||
first = true;
|
||||
p++, left--;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check for compression (which is not allowed) */
|
||||
if (*p > 63)
|
||||
return -EBADMSG;
|
||||
|
||||
if (1U + *p + 1U > left)
|
||||
return -EBADMSG;
|
||||
|
||||
if (!GREEDY_REALLOC(e, n + !first + DNS_LABEL_ESCAPED_MAX + 1U))
|
||||
return -ENOMEM;
|
||||
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
e[n++] = '.';
|
||||
|
||||
r = dns_label_escape((char*) p+1, *p, e + n, DNS_LABEL_ESCAPED_MAX);
|
||||
if (r < 0) {
|
||||
_cleanup_free_ char *escaped = cescape_length((const char*) p+1, *p);
|
||||
log_debug_errno(r, "Failed to escape advertised domain name \"%s\": %m", strna(escaped));
|
||||
/* Here, do not propagate error code from dns_label_escape() except for ENOMEM. */
|
||||
return r == -ENOMEM ? -ENOMEM : -EBADMSG;
|
||||
}
|
||||
|
||||
n += r;
|
||||
|
||||
left -= 1 + *p;
|
||||
p += 1 + *p;
|
||||
}
|
||||
|
||||
if (strv_isempty(l)) {
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(l);
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router *rt, uint64_t *ret) {
|
||||
uint8_t *ri;
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = get_dnssl_info(rt, &ri);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = unaligned_be32_sec_to_usec(ri + 4, /* max_as_infinity = */ true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ndisc_router_captive_portal_get_uri(sd_ndisc_router *rt, const char **ret, size_t *ret_size) {
|
||||
int r;
|
||||
const char *nd_opt_captive_portal;
|
||||
size_t length;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
assert_return(ret_size, -EINVAL);
|
||||
|
||||
r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_CAPTIVE_PORTAL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -EMEDIUMTYPE;
|
||||
|
||||
r = sd_ndisc_router_option_get_raw(rt, (void *)&nd_opt_captive_portal, &length);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* The length field has units of 8 octets */
|
||||
assert(length % 8 == 0);
|
||||
if (length == 0)
|
||||
return -EBADMSG;
|
||||
|
||||
/* Check that the message is not truncated by an embedded NUL.
|
||||
* NUL padding to a multiple of 8 is expected. */
|
||||
size_t size = strnlen(nd_opt_captive_portal + 2, length - 2);
|
||||
if (DIV_ROUND_UP(size + 2, 8) != length / 8)
|
||||
return -EBADMSG;
|
||||
|
||||
/* Let's not return an empty buffer */
|
||||
if (size == 0) {
|
||||
*ret = NULL;
|
||||
*ret_size = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*ret = nd_opt_captive_portal + 2;
|
||||
*ret_size = size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_pref64_prefix_info(sd_ndisc_router *rt, struct nd_opt_prefix64_info **ret) {
|
||||
struct nd_opt_prefix64_info *ri;
|
||||
size_t length;
|
||||
int r;
|
||||
|
||||
assert(rt);
|
||||
assert(ret);
|
||||
|
||||
r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_PREF64);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -EMEDIUMTYPE;
|
||||
|
||||
length = NDISC_ROUTER_OPTION_LENGTH(rt);
|
||||
if (length != sizeof(struct nd_opt_prefix64_info))
|
||||
return -EBADMSG;
|
||||
|
||||
ri = (struct nd_opt_prefix64_info *) ((uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex);
|
||||
if (!pref64_option_verify(ri, length))
|
||||
return -EBADMSG;
|
||||
|
||||
*ret = ri;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ndisc_router_prefix64_get_prefix(sd_ndisc_router *rt, struct in6_addr *ret) {
|
||||
struct nd_opt_prefix64_info *pi;
|
||||
struct in6_addr a = {};
|
||||
unsigned prefixlen;
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = get_pref64_prefix_info(rt, &pi);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_ndisc_router_prefix64_get_prefixlen(rt, &prefixlen);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
memcpy(&a, pi->prefix, sizeof(pi->prefix));
|
||||
in6_addr_mask(&a, prefixlen);
|
||||
/* extra safety check for refusing malformed prefix. */
|
||||
if (memcmp(&a, pi->prefix, sizeof(pi->prefix)) != 0)
|
||||
return -EBADMSG;
|
||||
|
||||
*ret = a;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ndisc_router_prefix64_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) {
|
||||
struct nd_opt_prefix64_info *pi;
|
||||
uint16_t lifetime_prefix_len;
|
||||
uint8_t prefix_len;
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = get_pref64_prefix_info(rt, &pi);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
lifetime_prefix_len = be16toh(pi->lifetime_and_plc);
|
||||
pref64_plc_to_prefix_length(lifetime_prefix_len, &prefix_len);
|
||||
|
||||
*ret = prefix_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ndisc_router_prefix64_get_lifetime(sd_ndisc_router *rt, uint64_t *ret) {
|
||||
struct nd_opt_prefix64_info *pi;
|
||||
uint16_t lifetime_prefix_len;
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = get_pref64_prefix_info(rt, &pi);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
lifetime_prefix_len = be16toh(pi->lifetime_and_plc);
|
||||
|
||||
*ret = (lifetime_prefix_len & PREF64_SCALED_LIFETIME_MASK) * USEC_PER_SEC;
|
||||
return 0;
|
||||
}
|
||||
DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, prefixlen, unsigned);
|
||||
DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, prefix, struct in6_addr);
|
||||
DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, lifetime, uint64_t);
|
||||
|
||||
Reference in New Issue
Block a user