sd-ndisc: split sd-ndisc.h into small pieces
authorYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 14 Feb 2024 09:11:06 +0000 (18:11 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 20 Feb 2024 06:30:49 +0000 (15:30 +0900)
src/libsystemd-network/meson.build
src/libsystemd-network/ndisc-router-internal.h [new file with mode: 0644]
src/libsystemd-network/ndisc-router.c [deleted file]
src/libsystemd-network/ndisc-router.h [deleted file]
src/libsystemd-network/sd-ndisc-router.c [new file with mode: 0644]
src/libsystemd-network/sd-ndisc.c
src/systemd/meson.build
src/systemd/sd-ndisc-protocol.h [new file with mode: 0644]
src/systemd/sd-ndisc-router.h [new file with mode: 0644]
src/systemd/sd-ndisc.h

index 277935296fba923602dfd007d23f15767f835a8e..301a4d5c5cb911755ca383ed0c78bb342198a9ca 100644 (file)
@@ -12,7 +12,6 @@ sources = files(
         'lldp-neighbor.c',
         'lldp-network.c',
         'ndisc-protocol.c',
-        'ndisc-router.c',
         'network-common.c',
         'network-internal.c',
         'sd-dhcp-client-id.c',
@@ -28,6 +27,7 @@ sources = files(
         'sd-lldp-rx.c',
         'sd-lldp-tx.c',
         'sd-ndisc.c',
+        'sd-ndisc-router.c',
         'sd-radv.c',
 )
 
diff --git a/src/libsystemd-network/ndisc-router-internal.h b/src/libsystemd-network/ndisc-router-internal.h
new file mode 100644 (file)
index 0000000..63d4f90
--- /dev/null
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+/***
+  Copyright © 2014 Intel Corporation. All rights reserved.
+***/
+
+#include "sd-ndisc.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;
+
+        /* The current read index for the iterative option interface */
+        size_t rindex;
+
+        uint64_t flags;
+        unsigned preference;
+        uint64_t lifetime_usec;
+        usec_t retransmission_time_usec;
+
+        uint8_t hop_limit;
+        uint32_t mtu;
+        uint64_t icmp6_ratelimit_usec;
+};
+
+static inline void* NDISC_ROUTER_RAW(const sd_ndisc_router *rt) {
+        return (uint8_t*) rt + ALIGN(sizeof(sd_ndisc_router));
+}
+
+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(size_t raw_size);
+int ndisc_router_parse(sd_ndisc *nd, sd_ndisc_router *rt);
diff --git a/src/libsystemd-network/ndisc-router.c b/src/libsystemd-network/ndisc-router.c
deleted file mode 100644 (file)
index e50a0b9..0000000
+++ /dev/null
@@ -1,931 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/***
-  Copyright © 2014 Intel Corporation. All rights reserved.
-***/
-
-#include <netinet/icmp6.h>
-
-#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.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);
-        if (!rt)
-                return NULL;
-
-        rt->raw_size = raw_size;
-        rt->n_ref = 1;
-
-        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;
-}
-
-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;
-}
-
-#define DEFINE_GET_TIMESTAMP(name)                                      \
-        int sd_ndisc_router_##name##_timestamp(                         \
-                        sd_ndisc_router *rt,                            \
-                        clockid_t clock,                                \
-                        uint64_t *ret) {                                \
-                                                                        \
-                usec_t s, t;                                            \
-                int r;                                                  \
-                                                                        \
-                assert_return(rt, -EINVAL);                             \
-                assert_return(ret, -EINVAL);                            \
-                                                                        \
-                r = sd_ndisc_router_##name(rt, &s);                     \
-                if (r < 0)                                              \
-                        return r;                                       \
-                                                                        \
-                r = sd_ndisc_router_get_timestamp(rt, clock, &t);       \
-                if (r < 0)                                              \
-                        return r;                                       \
-                                                                        \
-                *ret = time_span_to_stamp(s, t);                        \
-                return 0;                                               \
-        }
-
-DEFINE_GET_TIMESTAMP(get_lifetime);
-DEFINE_GET_TIMESTAMP(prefix_get_valid_lifetime);
-DEFINE_GET_TIMESTAMP(prefix_get_preferred_lifetime);
-DEFINE_GET_TIMESTAMP(route_get_lifetime);
-DEFINE_GET_TIMESTAMP(rdnss_get_lifetime);
-DEFINE_GET_TIMESTAMP(dnssl_get_lifetime);
-DEFINE_GET_TIMESTAMP(prefix64_get_lifetime);
-
-int sd_ndisc_router_get_raw(sd_ndisc_router *rt, const void **ret, size_t *ret_size) {
-        assert_return(rt, -EINVAL);
-        assert_return(ret, -EINVAL);
-        assert_return(ret_size, -EINVAL);
-
-        *ret = NDISC_ROUTER_RAW(rt);
-        *ret_size = rt->raw_size;
-
-        return 0;
-}
-
-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) {
-        struct nd_router_advert *a;
-        const uint8_t *p;
-        bool has_mtu = false, has_flag_extension = false;
-        size_t left;
-
-        assert(rt);
-
-        if (rt->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.");
-
-        rt->hop_limit = a->nd_ra_curhoplimit;
-        rt->flags = a->nd_ra_flags_reserved; /* the first 8 bits */
-        rt->lifetime_usec = be16_sec_to_usec(a->nd_ra_router_lifetime, /* max_as_infinity = */ false);
-        rt->icmp6_ratelimit_usec = be32_msec_to_usec(a->nd_ra_retransmit, /* max_as_infinity = */ false);
-        rt->retransmission_time_usec = be32_msec_to_usec(a->nd_ra_retransmit, /* max_as_infinity = */ false);
-
-        rt->preference = (rt->flags >> 3) & 3;
-        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 (;;) {
-                uint8_t type;
-                size_t length;
-
-                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.");
-
-                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;
-                }}
-
-                p += length, left -= length;
-        }
-
-        rt->rindex = sizeof(struct nd_router_advert);
-        return 0;
-}
-
-int sd_ndisc_router_get_hop_limit(sd_ndisc_router *rt, uint8_t *ret) {
-        assert_return(rt, -EINVAL);
-        assert_return(ret, -EINVAL);
-
-        *ret = rt->hop_limit;
-        return 0;
-}
-
-int sd_ndisc_router_get_retransmission_time(sd_ndisc_router *rt, uint64_t *ret) {
-        assert_return(rt, -EINVAL);
-        assert_return(ret, -EINVAL);
-
-        *ret = rt->retransmission_time_usec;
-        return 0;
-}
-
-int sd_ndisc_router_get_icmp6_ratelimit(sd_ndisc_router *rt, uint64_t *ret) {
-        assert_return(rt, -EINVAL);
-        assert_return(ret, -EINVAL);
-
-        *ret = rt->icmp6_ratelimit_usec;
-        return 0;
-}
-
-int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret) {
-        assert_return(rt, -EINVAL);
-        assert_return(ret, -EINVAL);
-
-        *ret = rt->flags;
-        return 0;
-}
-
-int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint64_t *ret) {
-        assert_return(rt, -EINVAL);
-        assert_return(ret, -EINVAL);
-
-        *ret = rt->lifetime_usec;
-        return 0;
-}
-
-int sd_ndisc_router_get_preference(sd_ndisc_router *rt, unsigned *ret) {
-        assert_return(rt, -EINVAL);
-        assert_return(ret, -EINVAL);
-
-        *ret = rt->preference;
-        return 0;
-}
-
-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)
-                return -ENODATA;
-
-        *ret = rt->mtu;
-        return 0;
-}
-
-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);
-
-        return rt->rindex < rt->raw_size;
-}
-
-int sd_ndisc_router_option_next(sd_ndisc_router *rt) {
-        size_t length;
-
-        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;
-
-        rt->rindex += length;
-        return rt->rindex < rt->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;
-}
-
-int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type) {
-        uint8_t k;
-        int r;
-
-        assert_return(rt, -EINVAL);
-
-        r = sd_ndisc_router_option_get_type(rt, &k);
-        if (r < 0)
-                return r;
-
-        return type == k;
-}
-
-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->rindex + 2 > rt->raw_size)
-                return -EBADMSG;
-
-        length = NDISC_ROUTER_OPTION_LENGTH(rt);
-        if (rt->rindex + length > rt->raw_size)
-                return -EBADMSG;
-
-        *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
-        *ret_size = length;
-
-        return 0;
-}
-
-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;
-        }
-
-        *ret = flags;
-        return 0;
-}
-
-int sd_ndisc_router_prefix_get_address(sd_ndisc_router *rt, struct in6_addr *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 = 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;
-}
-
-int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **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 = (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;
-}
diff --git a/src/libsystemd-network/ndisc-router.h b/src/libsystemd-network/ndisc-router.h
deleted file mode 100644 (file)
index 63d4f90..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-/***
-  Copyright © 2014 Intel Corporation. All rights reserved.
-***/
-
-#include "sd-ndisc.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;
-
-        /* The current read index for the iterative option interface */
-        size_t rindex;
-
-        uint64_t flags;
-        unsigned preference;
-        uint64_t lifetime_usec;
-        usec_t retransmission_time_usec;
-
-        uint8_t hop_limit;
-        uint32_t mtu;
-        uint64_t icmp6_ratelimit_usec;
-};
-
-static inline void* NDISC_ROUTER_RAW(const sd_ndisc_router *rt) {
-        return (uint8_t*) rt + ALIGN(sizeof(sd_ndisc_router));
-}
-
-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(size_t raw_size);
-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
new file mode 100644 (file)
index 0000000..fa22368
--- /dev/null
@@ -0,0 +1,931 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/***
+  Copyright © 2014 Intel Corporation. All rights reserved.
+***/
+
+#include <netinet/icmp6.h>
+
+#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"
+
+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);
+        if (!rt)
+                return NULL;
+
+        rt->raw_size = raw_size;
+        rt->n_ref = 1;
+
+        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;
+}
+
+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;
+}
+
+#define DEFINE_GET_TIMESTAMP(name)                                      \
+        int sd_ndisc_router_##name##_timestamp(                         \
+                        sd_ndisc_router *rt,                            \
+                        clockid_t clock,                                \
+                        uint64_t *ret) {                                \
+                                                                        \
+                usec_t s, t;                                            \
+                int r;                                                  \
+                                                                        \
+                assert_return(rt, -EINVAL);                             \
+                assert_return(ret, -EINVAL);                            \
+                                                                        \
+                r = sd_ndisc_router_##name(rt, &s);                     \
+                if (r < 0)                                              \
+                        return r;                                       \
+                                                                        \
+                r = sd_ndisc_router_get_timestamp(rt, clock, &t);       \
+                if (r < 0)                                              \
+                        return r;                                       \
+                                                                        \
+                *ret = time_span_to_stamp(s, t);                        \
+                return 0;                                               \
+        }
+
+DEFINE_GET_TIMESTAMP(get_lifetime);
+DEFINE_GET_TIMESTAMP(prefix_get_valid_lifetime);
+DEFINE_GET_TIMESTAMP(prefix_get_preferred_lifetime);
+DEFINE_GET_TIMESTAMP(route_get_lifetime);
+DEFINE_GET_TIMESTAMP(rdnss_get_lifetime);
+DEFINE_GET_TIMESTAMP(dnssl_get_lifetime);
+DEFINE_GET_TIMESTAMP(prefix64_get_lifetime);
+
+int sd_ndisc_router_get_raw(sd_ndisc_router *rt, const void **ret, size_t *ret_size) {
+        assert_return(rt, -EINVAL);
+        assert_return(ret, -EINVAL);
+        assert_return(ret_size, -EINVAL);
+
+        *ret = NDISC_ROUTER_RAW(rt);
+        *ret_size = rt->raw_size;
+
+        return 0;
+}
+
+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) {
+        struct nd_router_advert *a;
+        const uint8_t *p;
+        bool has_mtu = false, has_flag_extension = false;
+        size_t left;
+
+        assert(rt);
+
+        if (rt->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.");
+
+        rt->hop_limit = a->nd_ra_curhoplimit;
+        rt->flags = a->nd_ra_flags_reserved; /* the first 8 bits */
+        rt->lifetime_usec = be16_sec_to_usec(a->nd_ra_router_lifetime, /* max_as_infinity = */ false);
+        rt->icmp6_ratelimit_usec = be32_msec_to_usec(a->nd_ra_retransmit, /* max_as_infinity = */ false);
+        rt->retransmission_time_usec = be32_msec_to_usec(a->nd_ra_retransmit, /* max_as_infinity = */ false);
+
+        rt->preference = (rt->flags >> 3) & 3;
+        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 (;;) {
+                uint8_t type;
+                size_t length;
+
+                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.");
+
+                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;
+                }}
+
+                p += length, left -= length;
+        }
+
+        rt->rindex = sizeof(struct nd_router_advert);
+        return 0;
+}
+
+int sd_ndisc_router_get_hop_limit(sd_ndisc_router *rt, uint8_t *ret) {
+        assert_return(rt, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        *ret = rt->hop_limit;
+        return 0;
+}
+
+int sd_ndisc_router_get_retransmission_time(sd_ndisc_router *rt, uint64_t *ret) {
+        assert_return(rt, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        *ret = rt->retransmission_time_usec;
+        return 0;
+}
+
+int sd_ndisc_router_get_icmp6_ratelimit(sd_ndisc_router *rt, uint64_t *ret) {
+        assert_return(rt, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        *ret = rt->icmp6_ratelimit_usec;
+        return 0;
+}
+
+int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret) {
+        assert_return(rt, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        *ret = rt->flags;
+        return 0;
+}
+
+int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint64_t *ret) {
+        assert_return(rt, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        *ret = rt->lifetime_usec;
+        return 0;
+}
+
+int sd_ndisc_router_get_preference(sd_ndisc_router *rt, unsigned *ret) {
+        assert_return(rt, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        *ret = rt->preference;
+        return 0;
+}
+
+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)
+                return -ENODATA;
+
+        *ret = rt->mtu;
+        return 0;
+}
+
+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);
+
+        return rt->rindex < rt->raw_size;
+}
+
+int sd_ndisc_router_option_next(sd_ndisc_router *rt) {
+        size_t length;
+
+        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;
+
+        rt->rindex += length;
+        return rt->rindex < rt->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;
+}
+
+int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type) {
+        uint8_t k;
+        int r;
+
+        assert_return(rt, -EINVAL);
+
+        r = sd_ndisc_router_option_get_type(rt, &k);
+        if (r < 0)
+                return r;
+
+        return type == k;
+}
+
+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->rindex + 2 > rt->raw_size)
+                return -EBADMSG;
+
+        length = NDISC_ROUTER_OPTION_LENGTH(rt);
+        if (rt->rindex + length > rt->raw_size)
+                return -EBADMSG;
+
+        *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
+        *ret_size = length;
+
+        return 0;
+}
+
+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;
+        }
+
+        *ret = flags;
+        return 0;
+}
+
+int sd_ndisc_router_prefix_get_address(sd_ndisc_router *rt, struct in6_addr *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 = 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;
+}
+
+int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **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 = (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;
+}
index 1beed5d0ce29f995950c02fb63b94ea971314b0b..fe81794e3dfedb1717cad830d7fbc271521a3d55 100644 (file)
@@ -15,7 +15,7 @@
 #include "in-addr-util.h"
 #include "memory-util.h"
 #include "ndisc-internal.h"
-#include "ndisc-router.h"
+#include "ndisc-router-internal.h"
 #include "network-common.h"
 #include "random-util.h"
 #include "socket-util.h"
index 76d87cc9d4c5064f445895148e80d568bab8e019..d50edb413fcb6fedae536236bacb14e8755023cf 100644 (file)
@@ -38,6 +38,8 @@ _not_installed_headers = [
         'sd-lldp-tx.h',
         'sd-lldp.h',
         'sd-ndisc.h',
+        'sd-ndisc-protocol.h',
+        'sd-ndisc-router.h',
         'sd-netlink.h',
         'sd-network.h',
         'sd-radv.h',
diff --git a/src/systemd/sd-ndisc-protocol.h b/src/systemd/sd-ndisc-protocol.h
new file mode 100644 (file)
index 0000000..93c5d20
--- /dev/null
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#ifndef foosdndiscprotocolfoo
+#define foosdndiscprotocolfoo
+
+/***
+  Copyright © 2014 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <https://www.gnu.org/licenses/>.
+***/
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+/* Neighbor Discovery Options, RFC 4861, Section 4.6 and
+ * https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-5 */
+enum {
+        SD_NDISC_OPTION_SOURCE_LL_ADDRESS  = 1,
+        SD_NDISC_OPTION_TARGET_LL_ADDRESS  = 2,
+        SD_NDISC_OPTION_PREFIX_INFORMATION = 3,
+        SD_NDISC_OPTION_MTU                = 5,
+        SD_NDISC_OPTION_ROUTE_INFORMATION  = 24,
+        SD_NDISC_OPTION_RDNSS              = 25,
+        SD_NDISC_OPTION_FLAGS_EXTENSION    = 26,
+        SD_NDISC_OPTION_DNSSL              = 31,
+        SD_NDISC_OPTION_CAPTIVE_PORTAL     = 37,
+        SD_NDISC_OPTION_PREF64             = 38
+};
+
+/* Route preference, RFC 4191, Section 2.1 */
+enum {
+        SD_NDISC_PREFERENCE_LOW    = 3U,
+        SD_NDISC_PREFERENCE_MEDIUM = 0U,
+        SD_NDISC_PREFERENCE_HIGH   = 1U
+};
+
+_SD_END_DECLARATIONS;
+
+#endif
diff --git a/src/systemd/sd-ndisc-router.h b/src/systemd/sd-ndisc-router.h
new file mode 100644 (file)
index 0000000..1445939
--- /dev/null
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#ifndef foosdndiscrouterfoo
+#define foosdndiscrouterfoo
+
+/***
+  Copyright © 2014 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <https://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+typedef struct sd_ndisc_router sd_ndisc_router;
+
+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_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
+int sd_ndisc_router_get_raw(sd_ndisc_router *rt, const void **ret, size_t *ret_size);
+
+int sd_ndisc_router_get_hop_limit(sd_ndisc_router *rt, uint8_t *ret);
+int sd_ndisc_router_get_icmp6_ratelimit(sd_ndisc_router *rt, uint64_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_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_retransmission_time(sd_ndisc_router *rt, uint64_t *ret);
+int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *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);
+
+/* Specific option access: SD_NDISC_OPTION_PREFIX_INFORMATION */
+int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router *rt, uint64_t *ret);
+int sd_ndisc_router_prefix_get_valid_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
+int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router *rt, uint64_t *ret);
+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);
+
+/* 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);
+
+/* Specific option access: SD_NDISC_OPTION_RDNSS */
+int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **ret);
+int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router *rt, uint64_t *ret);
+int sd_ndisc_router_rdnss_get_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
+
+/* Specific option access: SD_NDISC_OPTION_DNSSL */
+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_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);
+
+_SD_END_DECLARATIONS;
+
+#endif
index a5ccd5f6449107585406e701f63833366bee28ed..c348e505ede3dc1d529d56c00cc115a27dca284b 100644 (file)
 #include <sys/types.h>
 
 #include "sd-event.h"
+#include "sd-ndisc-protocol.h"
+#include "sd-ndisc-router.h"
 
 #include "_sd-common.h"
 
 _SD_BEGIN_DECLARATIONS;
 
-/* Neighbor Discovery Options, RFC 4861, Section 4.6 and
- * https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-5 */
-enum {
-        SD_NDISC_OPTION_SOURCE_LL_ADDRESS  = 1,
-        SD_NDISC_OPTION_TARGET_LL_ADDRESS  = 2,
-        SD_NDISC_OPTION_PREFIX_INFORMATION = 3,
-        SD_NDISC_OPTION_MTU                = 5,
-        SD_NDISC_OPTION_ROUTE_INFORMATION  = 24,
-        SD_NDISC_OPTION_RDNSS              = 25,
-        SD_NDISC_OPTION_FLAGS_EXTENSION    = 26,
-        SD_NDISC_OPTION_DNSSL              = 31,
-        SD_NDISC_OPTION_CAPTIVE_PORTAL     = 37,
-        SD_NDISC_OPTION_PREF64             = 38
-};
-
-/* Route preference, RFC 4191, Section 2.1 */
-enum {
-        SD_NDISC_PREFERENCE_LOW    = 3U,
-        SD_NDISC_PREFERENCE_MEDIUM = 0U,
-        SD_NDISC_PREFERENCE_HIGH   = 1U
-};
-
 typedef struct sd_ndisc sd_ndisc;
-typedef struct sd_ndisc_router sd_ndisc_router;
 
 __extension__ typedef enum sd_ndisc_event_t {
         SD_NDISC_EVENT_TIMEOUT,
@@ -69,6 +48,7 @@ typedef void (*sd_ndisc_callback_t)(sd_ndisc *nd, sd_ndisc_event_t event, sd_ndi
 int sd_ndisc_new(sd_ndisc **ret);
 sd_ndisc *sd_ndisc_ref(sd_ndisc *nd);
 sd_ndisc *sd_ndisc_unref(sd_ndisc *nd);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc, sd_ndisc_unref);
 
 int sd_ndisc_start(sd_ndisc *nd);
 int sd_ndisc_stop(sd_ndisc *nd);
@@ -83,67 +63,6 @@ int sd_ndisc_set_ifname(sd_ndisc *nd, const char *interface_name);
 int sd_ndisc_get_ifname(sd_ndisc *nd, const char **ret);
 int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr);
 
-sd_ndisc_router *sd_ndisc_router_ref(sd_ndisc_router *rt);
-sd_ndisc_router *sd_ndisc_router_unref(sd_ndisc_router *rt);
-
-int sd_ndisc_router_get_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_raw(sd_ndisc_router *rt, const void **ret, size_t *ret_size);
-
-int sd_ndisc_router_get_hop_limit(sd_ndisc_router *rt, uint8_t *ret);
-int sd_ndisc_router_get_icmp6_ratelimit(sd_ndisc_router *rt, uint64_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_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_retransmission_time(sd_ndisc_router *rt, uint64_t *ret);
-int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *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);
-
-/* Specific option access: SD_NDISC_OPTION_PREFIX_INFORMATION */
-int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router *rt, uint64_t *ret);
-int sd_ndisc_router_prefix_get_valid_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
-int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router *rt, uint64_t *ret);
-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);
-
-/* 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);
-
-/* Specific option access: SD_NDISC_OPTION_RDNSS */
-int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **ret);
-int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router *rt, uint64_t *ret);
-int sd_ndisc_router_rdnss_get_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
-
-/* Specific option access: SD_NDISC_OPTION_DNSSL */
-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_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);
-
-_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc, sd_ndisc_unref);
-_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc_router, sd_ndisc_router_unref);
-
 _SD_END_DECLARATIONS;
 
 #endif