summaryrefslogtreecommitdiffstats
path: root/src/libsystemd-network/sd-ndisc-router.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libsystemd-network/sd-ndisc-router.c')
-rw-r--r--src/libsystemd-network/sd-ndisc-router.c344
1 files changed, 344 insertions, 0 deletions
diff --git a/src/libsystemd-network/sd-ndisc-router.c b/src/libsystemd-network/sd-ndisc-router.c
new file mode 100644
index 0000000..9ca737d
--- /dev/null
+++ b/src/libsystemd-network/sd-ndisc-router.c
@@ -0,0 +1,344 @@
+/* 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 "ndisc-internal.h"
+#include "ndisc-router-internal.h"
+#include "string-table.h"
+
+static sd_ndisc_router* ndisc_router_free(sd_ndisc_router *rt) {
+ if (!rt)
+ return NULL;
+
+ icmp6_packet_unref(rt->packet);
+ set_free(rt->options);
+ return mfree(rt);
+}
+
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc_router, sd_ndisc_router, ndisc_router_free);
+
+sd_ndisc_router* ndisc_router_new(ICMP6Packet *packet) {
+ sd_ndisc_router *rt;
+
+ assert(packet);
+
+ rt = new(sd_ndisc_router, 1);
+ if (!rt)
+ return NULL;
+
+ *rt = (sd_ndisc_router) {
+ .n_ref = 1,
+ .packet = icmp6_packet_ref(packet),
+ .iterator = ITERATOR_FIRST,
+ };
+
+ return rt;
+}
+
+int sd_ndisc_router_set_sender_address(sd_ndisc_router *rt, const struct in6_addr *addr) {
+ assert_return(rt, -EINVAL);
+
+ return icmp6_packet_set_sender_address(rt->packet, addr);
+}
+
+int sd_ndisc_router_get_sender_address(sd_ndisc_router *rt, struct in6_addr *ret) {
+ assert_return(rt, -EINVAL);
+
+ 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(ret, -EINVAL);
+
+ return icmp6_packet_get_timestamp(rt->packet, clock, ret);
+}
+
+#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 ndisc_router_parse(sd_ndisc *nd, sd_ndisc_router *rt) {
+ const struct nd_router_advert *a;
+ int r;
+
+ assert(rt);
+ assert(rt->packet);
+
+ 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.");
+
+ 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 */
+ rt->lifetime_usec = be16_sec_to_usec(a->nd_ra_router_lifetime, /* max_as_infinity = */ false);
+ rt->reachable_time_usec = be32_msec_to_usec(a->nd_ra_reachable, /* mas_as_infinity = */ false);
+ rt->retransmission_time_usec = be32_msec_to_usec(a->nd_ra_retransmit, /* max_as_infinity = */ false);
+
+ /* RFC 4191 section 2.2
+ * Prf (Default Router Preference)
+ * 2-bit signed integer. Indicates whether to prefer this router over other default routers. If the
+ * Router Lifetime is zero, the preference value MUST be set to (00) by the sender and MUST be
+ * ignored by the receiver. If the Reserved (10) value is received, the receiver MUST treat the value
+ * as if it were (00). */
+ rt->preference = (rt->flags >> 3) & 3;
+ if (rt->preference == SD_NDISC_PREFERENCE_RESERVED)
+ rt->preference = SD_NDISC_PREFERENCE_MEDIUM;
+
+ r = ndisc_parse_options(rt->packet, &rt->options);
+ if (r < 0)
+ return log_ndisc_errno(nd, r, "Failed to parse NDisc options in router advertisement message, ignoring: %m");
+
+ 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_reachable_time(sd_ndisc_router *rt, uint64_t *ret) {
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ *ret = rt->reachable_time_usec;
+ 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_flags(sd_ndisc_router *rt, uint64_t *ret) {
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ sd_ndisc_option *p = ndisc_option_get_by_type(rt->options, SD_NDISC_OPTION_FLAGS_EXTENSION);
+
+ *ret = rt->flags | (p ? p->extended_flags : 0);
+ return 0;
+}
+
+int ndisc_router_flags_to_string(uint64_t flags, char **ret) {
+ _cleanup_free_ char *s = NULL;
+
+ assert(ret);
+
+ if (FLAGS_SET(flags, ND_RA_FLAG_MANAGED) &&
+ !strextend_with_separator(&s, ", ", "managed"))
+ return -ENOMEM;
+
+ if (FLAGS_SET(flags, ND_RA_FLAG_OTHER) &&
+ !strextend_with_separator(&s, ", ", "other"))
+ return -ENOMEM;
+
+ if (FLAGS_SET(flags, ND_RA_FLAG_HOME_AGENT) &&
+ !strextend_with_separator(&s, ", ", "home-agent"))
+ return -ENOMEM;
+
+ *ret = TAKE_PTR(s);
+ return 0;
+}
+
+int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint64_t *ret) {
+ assert_return(rt, -EINVAL);
+
+ if (ret)
+ *ret = rt->lifetime_usec;
+
+ return rt->lifetime_usec > 0; /* Indicate if the router is still valid or not. */
+}
+
+int sd_ndisc_router_get_preference(sd_ndisc_router *rt, uint8_t *ret) {
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ *ret = rt->preference;
+ return 0;
+}
+
+static const char* const ndisc_router_preference_table[] = {
+ [SD_NDISC_PREFERENCE_LOW] = "low",
+ [SD_NDISC_PREFERENCE_MEDIUM] = "medium",
+ [SD_NDISC_PREFERENCE_HIGH] = "high",
+ [SD_NDISC_PREFERENCE_RESERVED] = "reserved",
+};
+
+DEFINE_STRING_TABLE_LOOKUP_TO_STRING(ndisc_router_preference, int);
+
+int sd_ndisc_router_get_sender_mac(sd_ndisc_router *rt, struct ether_addr *ret) {
+ assert_return(rt, -EINVAL);
+
+ return ndisc_option_get_mac(rt->options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, ret);
+}
+
+int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret) {
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ sd_ndisc_option *p = ndisc_option_get_by_type(rt->options, SD_NDISC_OPTION_MTU);
+ if (!p)
+ return -ENODATA;
+
+ *ret = p->mtu;
+ return 0;
+}
+
+int sd_ndisc_router_get_captive_portal(sd_ndisc_router *rt, const char **ret) {
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ sd_ndisc_option *p = ndisc_option_get_by_type(rt->options, SD_NDISC_OPTION_CAPTIVE_PORTAL);
+ if (!p)
+ return -ENODATA;
+
+ *ret = p->captive_portal;
+ return 0;
+}
+
+int sd_ndisc_router_option_rewind(sd_ndisc_router *rt) {
+ assert_return(rt, -EINVAL);
+
+ rt->iterator = ITERATOR_FIRST;
+ return sd_ndisc_router_option_next(rt);
+}
+
+int sd_ndisc_router_option_next(sd_ndisc_router *rt) {
+ assert_return(rt, -EINVAL);
+
+ return set_iterate(rt->options, &rt->iterator, (void**) &rt->current_option);
+}
+
+int sd_ndisc_router_option_get_type(sd_ndisc_router *rt, uint8_t *ret) {
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if (!rt->current_option)
+ return -ENODATA;
+
+ *ret = rt->current_option->type;
+ return 0;
+}
+
+int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type) {
+ uint8_t t;
+ int r;
+
+ assert_return(rt, -EINVAL);
+
+ r = sd_ndisc_router_option_get_type(rt, &t);
+ if (r < 0)
+ return r;
+
+ return t == type;
+}
+
+int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const uint8_t **ret, size_t *ret_size) {
+ assert_return(rt, -EINVAL);
+
+ if (!rt->current_option)
+ return -ENODATA;
+
+ return ndisc_option_parse(rt->packet, rt->current_option->offset, NULL, ret_size, ret);
+}
+
+#define DEFINE_GETTER(name, type, element, element_type) \
+ int sd_ndisc_router_##name##_get_##element( \
+ sd_ndisc_router *rt, \
+ element_type *ret) { \
+ \
+ int r; \
+ \
+ assert_return(rt, -EINVAL); \
+ assert_return(ret, -EINVAL); \
+ \
+ r = sd_ndisc_router_option_is_type(rt, type); \
+ if (r < 0) \
+ return r; \
+ if (r == 0) \
+ return -EMEDIUMTYPE; \
+ \
+ *ret = rt->current_option->name.element; \
+ return 0; \
+ }
+
+DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, flags, uint8_t);
+DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, prefixlen, uint8_t);
+DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, address, struct in6_addr);
+DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, valid_lifetime, uint64_t);
+DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, preferred_lifetime, uint64_t);
+
+DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, preference, uint8_t);
+DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, prefixlen, uint8_t);
+DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, address, struct in6_addr);
+DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, lifetime, uint64_t);
+
+DEFINE_GETTER(rdnss, SD_NDISC_OPTION_RDNSS, lifetime, uint64_t);
+
+int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **ret) {
+ int r;
+
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_RDNSS);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EMEDIUMTYPE;
+
+ *ret = rt->current_option->rdnss.addresses;
+ return (int) rt->current_option->rdnss.n_addresses;
+}
+
+DEFINE_GETTER(dnssl, SD_NDISC_OPTION_DNSSL, domains, char**);
+DEFINE_GETTER(dnssl, SD_NDISC_OPTION_DNSSL, lifetime, uint64_t);
+
+DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, prefixlen, uint8_t);
+DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, prefix, struct in6_addr);
+DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, lifetime, uint64_t);