]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-ndisc: use ICMP6Packet and ndisc_option_parse()
authorYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 27 Feb 2024 04:47:01 +0000 (13:47 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 28 Feb 2024 02:37:36 +0000 (11:37 +0900)
src/libsystemd-network/ndisc-router-internal.h
src/libsystemd-network/sd-ndisc-router.c
src/libsystemd-network/sd-ndisc.c

index 5bd9a65dd227948639dfc308861a493a9271689f..47b15e37245d520279b431d1c39fc1a789ebeb16 100644 (file)
@@ -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);
index b90fb77cd82792f46f68717301b6a59ca7892eee..f13d802509d479a0f27fa6481d9d069abd753154 100644 (file)
 #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;
index 1ebae2c2f3ed49b019fc491afc96d2d5d5b6f4fb..87f5e5693e76f353d694f5b7168eb9a0cc145fc4 100644 (file)
@@ -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;
 }