sd-ndisc: use ICMP6Packet and ndisc_option_parse()
This commit is contained in:
@@ -7,16 +7,13 @@
|
||||
|
||||
#include "sd-ndisc.h"
|
||||
|
||||
#include "icmp6-packet.h"
|
||||
#include "time-util.h"
|
||||
|
||||
struct sd_ndisc_router {
|
||||
unsigned n_ref;
|
||||
|
||||
triple_timestamp timestamp;
|
||||
struct in6_addr address;
|
||||
|
||||
/* The raw packet size. The data is appended to the object, accessible via NDIS_ROUTER_RAW() */
|
||||
size_t raw_size;
|
||||
ICMP6Packet *packet;
|
||||
|
||||
/* The current read index for the iterative option interface */
|
||||
size_t rindex;
|
||||
@@ -33,7 +30,7 @@ struct sd_ndisc_router {
|
||||
};
|
||||
|
||||
static inline void* NDISC_ROUTER_RAW(const sd_ndisc_router *rt) {
|
||||
return (uint8_t*) rt + ALIGN(sizeof(sd_ndisc_router));
|
||||
return ASSERT_PTR(ASSERT_PTR(rt)->packet)->raw_packet;
|
||||
}
|
||||
|
||||
static inline void *NDISC_ROUTER_OPTION_DATA(const sd_ndisc_router *rt) {
|
||||
@@ -47,5 +44,5 @@ 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(size_t raw_size);
|
||||
sd_ndisc_router* ndisc_router_new(ICMP6Packet *packet);
|
||||
int ndisc_router_parse(sd_ndisc *nd, sd_ndisc_router *rt);
|
||||
|
||||
@@ -18,46 +18,44 @@
|
||||
#include "ndisc-router-internal.h"
|
||||
#include "strv.h"
|
||||
|
||||
DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc_router, sd_ndisc_router, mfree);
|
||||
|
||||
sd_ndisc_router *ndisc_router_new(size_t raw_size) {
|
||||
sd_ndisc_router *rt;
|
||||
|
||||
if (raw_size > SIZE_MAX - ALIGN(sizeof(sd_ndisc_router)))
|
||||
return NULL;
|
||||
|
||||
rt = malloc0(ALIGN(sizeof(sd_ndisc_router)) + raw_size);
|
||||
static sd_ndisc_router* ndisc_router_free(sd_ndisc_router *rt) {
|
||||
if (!rt)
|
||||
return NULL;
|
||||
|
||||
rt->raw_size = raw_size;
|
||||
rt->n_ref = 1;
|
||||
icmp6_packet_unref(rt->packet);
|
||||
return mfree(rt);
|
||||
}
|
||||
|
||||
DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc_router, sd_ndisc_router, ndisc_router_free);
|
||||
|
||||
sd_ndisc_router* ndisc_router_new(ICMP6Packet *packet) {
|
||||
sd_ndisc_router *rt;
|
||||
|
||||
assert(packet);
|
||||
|
||||
rt = new(sd_ndisc_router, 1);
|
||||
if (!rt)
|
||||
return NULL;
|
||||
|
||||
*rt = (sd_ndisc_router) {
|
||||
.n_ref = 1,
|
||||
.packet = icmp6_packet_ref(packet),
|
||||
};
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
int sd_ndisc_router_get_address(sd_ndisc_router *rt, struct in6_addr *ret) {
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
if (in6_addr_is_null(&rt->address))
|
||||
return -ENODATA;
|
||||
|
||||
*ret = rt->address;
|
||||
return 0;
|
||||
return icmp6_packet_get_sender_address(rt->packet, ret);
|
||||
}
|
||||
|
||||
int sd_ndisc_router_get_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret) {
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
|
||||
assert_return(clock_supported(clock), -EOPNOTSUPP);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
if (!triple_timestamp_is_set(&rt->timestamp))
|
||||
return -ENODATA;
|
||||
|
||||
*ret = triple_timestamp_by_clock(&rt->timestamp, clock);
|
||||
return 0;
|
||||
return icmp6_packet_get_timestamp(rt->packet, clock, ret);
|
||||
}
|
||||
|
||||
#define DEFINE_GET_TIMESTAMP(name) \
|
||||
@@ -98,7 +96,7 @@ int sd_ndisc_router_get_raw(sd_ndisc_router *rt, const void **ret, size_t *ret_s
|
||||
assert_return(ret_size, -EINVAL);
|
||||
|
||||
*ret = NDISC_ROUTER_RAW(rt);
|
||||
*ret_size = rt->raw_size;
|
||||
*ret_size = rt->packet->raw_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -119,27 +117,20 @@ static bool pref64_option_verify(const struct nd_opt_prefix64_info *p, size_t le
|
||||
}
|
||||
|
||||
int ndisc_router_parse(sd_ndisc *nd, sd_ndisc_router *rt) {
|
||||
struct nd_router_advert *a;
|
||||
const uint8_t *p;
|
||||
const struct nd_router_advert *a;
|
||||
bool has_mtu = false, has_flag_extension = false;
|
||||
size_t left;
|
||||
int r;
|
||||
|
||||
assert(rt);
|
||||
assert(rt->packet);
|
||||
|
||||
if (rt->raw_size < sizeof(struct nd_router_advert))
|
||||
if (rt->packet->raw_size < sizeof(struct nd_router_advert))
|
||||
return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Too small to be a router advertisement, ignoring.");
|
||||
|
||||
/* Router advertisement packets are neatly aligned to 64-bit boundaries, hence we can access them directly */
|
||||
a = NDISC_ROUTER_RAW(rt);
|
||||
|
||||
if (a->nd_ra_type != ND_ROUTER_ADVERT)
|
||||
return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Received ND packet that is not a router advertisement, ignoring.");
|
||||
|
||||
if (a->nd_ra_code != 0)
|
||||
return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Received ND packet with wrong RA code, ignoring.");
|
||||
a = (const struct nd_router_advert*) rt->packet->raw_packet;
|
||||
assert(a->nd_ra_type == ND_ROUTER_ADVERT);
|
||||
assert(a->nd_ra_code == 0);
|
||||
|
||||
rt->hop_limit = a->nd_ra_curhoplimit;
|
||||
rt->flags = a->nd_ra_flags_reserved; /* the first 8 bits */
|
||||
@@ -152,29 +143,13 @@ int ndisc_router_parse(sd_ndisc *nd, sd_ndisc_router *rt) {
|
||||
if (!IN_SET(rt->preference, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_HIGH))
|
||||
rt->preference = SD_NDISC_PREFERENCE_MEDIUM;
|
||||
|
||||
p = (const uint8_t*) NDISC_ROUTER_RAW(rt) + sizeof(struct nd_router_advert);
|
||||
left = rt->raw_size - sizeof(struct nd_router_advert);
|
||||
|
||||
for (;;) {
|
||||
for (size_t offset = sizeof(struct nd_router_advert), length; offset < rt->packet->raw_size; offset += length) {
|
||||
uint8_t type;
|
||||
size_t length;
|
||||
const uint8_t *p;
|
||||
|
||||
if (left == 0)
|
||||
break;
|
||||
|
||||
if (left < 2)
|
||||
return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Option lacks header, ignoring datagram.");
|
||||
|
||||
type = p[0];
|
||||
length = p[1] * 8;
|
||||
|
||||
if (length == 0)
|
||||
return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Zero-length option, ignoring datagram.");
|
||||
if (left < length)
|
||||
return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Option truncated, ignoring datagram.");
|
||||
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) {
|
||||
|
||||
@@ -262,8 +237,6 @@ int ndisc_router_parse(sd_ndisc *nd, sd_ndisc_router *rt) {
|
||||
"PREF64 prefix has invalid prefix length.");
|
||||
break;
|
||||
}}
|
||||
|
||||
p += length, left -= length;
|
||||
}
|
||||
|
||||
rt->rindex = sizeof(struct nd_router_advert);
|
||||
@@ -341,43 +314,30 @@ int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret) {
|
||||
int sd_ndisc_router_option_rewind(sd_ndisc_router *rt) {
|
||||
assert_return(rt, -EINVAL);
|
||||
|
||||
assert(rt->raw_size >= sizeof(struct nd_router_advert));
|
||||
rt->rindex = sizeof(struct nd_router_advert);
|
||||
assert(rt->packet);
|
||||
assert(rt->packet->raw_size >= sizeof(struct nd_router_advert));
|
||||
|
||||
return rt->rindex < rt->raw_size;
|
||||
rt->rindex = sizeof(struct nd_router_advert);
|
||||
return rt->rindex < rt->packet->raw_size;
|
||||
}
|
||||
|
||||
int sd_ndisc_router_option_next(sd_ndisc_router *rt) {
|
||||
size_t length;
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
|
||||
if (rt->rindex == rt->raw_size) /* EOF */
|
||||
return -ESPIPE;
|
||||
|
||||
if (rt->rindex + 2 > rt->raw_size) /* Truncated message */
|
||||
return -EBADMSG;
|
||||
|
||||
length = NDISC_ROUTER_OPTION_LENGTH(rt);
|
||||
if (rt->rindex + length > rt->raw_size)
|
||||
return -EBADMSG;
|
||||
r = ndisc_option_parse(rt->packet, rt->rindex, NULL, &length, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
rt->rindex += length;
|
||||
return rt->rindex < rt->raw_size;
|
||||
return rt->rindex < rt->packet->raw_size;
|
||||
}
|
||||
|
||||
int sd_ndisc_router_option_get_type(sd_ndisc_router *rt, uint8_t *ret) {
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
if (rt->rindex == rt->raw_size) /* EOF */
|
||||
return -ESPIPE;
|
||||
|
||||
if (rt->rindex + 2 > rt->raw_size) /* Truncated message */
|
||||
return -EBADMSG;
|
||||
|
||||
*ret = NDISC_ROUTER_OPTION_TYPE(rt);
|
||||
return 0;
|
||||
return ndisc_option_parse(rt->packet, rt->rindex, ret, NULL, NULL);
|
||||
}
|
||||
|
||||
int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type) {
|
||||
@@ -402,11 +362,11 @@ int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t
|
||||
|
||||
/* Note that this returns the full option, including the option header */
|
||||
|
||||
if (rt->rindex + 2 > rt->raw_size)
|
||||
if (rt->rindex + 2 > rt->packet->raw_size)
|
||||
return -EBADMSG;
|
||||
|
||||
length = NDISC_ROUTER_OPTION_LENGTH(rt);
|
||||
if (rt->rindex + length > rt->raw_size)
|
||||
if (rt->rindex + length > rt->packet->raw_size)
|
||||
return -EBADMSG;
|
||||
|
||||
*ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
|
||||
|
||||
@@ -186,11 +186,16 @@ int sd_ndisc_new(sd_ndisc **ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ndisc_handle_datagram(sd_ndisc *nd, sd_ndisc_router *rt) {
|
||||
static int ndisc_handle_router(sd_ndisc *nd, ICMP6Packet *packet) {
|
||||
_cleanup_(sd_ndisc_router_unrefp) sd_ndisc_router *rt = NULL;
|
||||
int r;
|
||||
|
||||
assert(nd);
|
||||
assert(rt);
|
||||
assert(packet);
|
||||
|
||||
rt = ndisc_router_new(packet);
|
||||
if (!rt)
|
||||
return -ENOMEM;
|
||||
|
||||
r = ndisc_router_parse(nd, rt);
|
||||
if (r < 0)
|
||||
@@ -208,56 +213,41 @@ static int ndisc_handle_datagram(sd_ndisc *nd, sd_ndisc_router *rt) {
|
||||
}
|
||||
|
||||
static int ndisc_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
||||
_cleanup_(sd_ndisc_router_unrefp) sd_ndisc_router *rt = NULL;
|
||||
_cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL;
|
||||
sd_ndisc *nd = ASSERT_PTR(userdata);
|
||||
ssize_t buflen;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(nd->event);
|
||||
|
||||
buflen = next_datagram_size_fd(fd);
|
||||
if (ERRNO_IS_NEG_TRANSIENT(buflen) || ERRNO_IS_NEG_DISCONNECT(buflen))
|
||||
return 0;
|
||||
if (buflen < 0) {
|
||||
log_ndisc_errno(nd, buflen, "Failed to determine datagram size to read, ignoring: %m");
|
||||
r = icmp6_packet_receive(fd, &packet);
|
||||
if (r < 0) {
|
||||
log_ndisc_errno(nd, r, "Failed to receive ICMPv6 packet, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
rt = ndisc_router_new(buflen);
|
||||
if (!rt)
|
||||
return -ENOMEM;
|
||||
|
||||
r = icmp6_receive(fd, NDISC_ROUTER_RAW(rt), rt->raw_size, &rt->address, &rt->timestamp);
|
||||
if (ERRNO_IS_NEG_TRANSIENT(r) || ERRNO_IS_NEG_DISCONNECT(r))
|
||||
return 0;
|
||||
if (r < 0)
|
||||
switch (r) {
|
||||
case -EADDRNOTAVAIL:
|
||||
log_ndisc(nd, "Received an ICMPv6 packet from neither link-local nor null address, ignoring.");
|
||||
return 0;
|
||||
|
||||
case -EMULTIHOP:
|
||||
log_ndisc(nd, "Received an ICMPv6 packet with an invalid hop limit, ignoring.");
|
||||
return 0;
|
||||
|
||||
case -EPFNOSUPPORT:
|
||||
log_ndisc(nd, "Received an ICMPv6 packet with an invalid source address, ignoring.");
|
||||
return 0;
|
||||
|
||||
default:
|
||||
log_ndisc_errno(nd, r, "Unexpected error while receiving an ICMPv6 packet, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The function icmp6_receive() accepts the null source address, but RFC 4861 Section 6.1.2 states
|
||||
* that hosts MUST discard messages with the null source address. */
|
||||
if (in6_addr_is_null(&rt->address)) {
|
||||
if (in6_addr_is_null(&packet->sender_address)) {
|
||||
log_ndisc(nd, "Received an ICMPv6 packet from null address, ignoring.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
(void) ndisc_handle_datagram(nd, rt);
|
||||
r = icmp6_packet_get_type(packet);
|
||||
if (r < 0) {
|
||||
log_ndisc_errno(nd, r, "Received an invalid ICMPv6 packet, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (r) {
|
||||
case ND_ROUTER_ADVERT:
|
||||
(void) ndisc_handle_router(nd, packet);
|
||||
break;
|
||||
|
||||
default:
|
||||
log_ndisc(nd, "Received an ICMPv6 packet with unexpected type %i, ignoring.", r);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user