From c34cb1d6451dd9fcd36e1c08c553ca7f25e9d83b Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 27 Feb 2024 13:47:01 +0900 Subject: [PATCH] sd-ndisc: use ICMP6Packet and ndisc_option_parse() --- .../ndisc-router-internal.h | 11 +- src/libsystemd-network/sd-ndisc-router.c | 126 ++++++------------ src/libsystemd-network/sd-ndisc.c | 64 ++++----- 3 files changed, 74 insertions(+), 127 deletions(-) diff --git a/src/libsystemd-network/ndisc-router-internal.h b/src/libsystemd-network/ndisc-router-internal.h index 5bd9a65dd22..47b15e37245 100644 --- a/src/libsystemd-network/ndisc-router-internal.h +++ b/src/libsystemd-network/ndisc-router-internal.h @@ -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); diff --git a/src/libsystemd-network/sd-ndisc-router.c b/src/libsystemd-network/sd-ndisc-router.c index b90fb77cd82..f13d802509d 100644 --- a/src/libsystemd-network/sd-ndisc-router.c +++ b/src/libsystemd-network/sd-ndisc-router.c @@ -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); +static sd_ndisc_router* ndisc_router_free(sd_ndisc_router *rt) { + if (!rt) + return NULL; + + 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(size_t raw_size) { +sd_ndisc_router* ndisc_router_new(ICMP6Packet *packet) { sd_ndisc_router *rt; - if (raw_size > SIZE_MAX - ALIGN(sizeof(sd_ndisc_router))) - return NULL; + assert(packet); - rt = malloc0(ALIGN(sizeof(sd_ndisc_router)) + raw_size); + rt = new(sd_ndisc_router, 1); if (!rt) return NULL; - rt->raw_size = raw_size; - rt->n_ref = 1; + *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; diff --git a/src/libsystemd-network/sd-ndisc.c b/src/libsystemd-network/sd-ndisc.c index 1ebae2c2f3e..87f5e5693e7 100644 --- a/src/libsystemd-network/sd-ndisc.c +++ b/src/libsystemd-network/sd-ndisc.c @@ -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; } -- 2.47.3