summaryrefslogtreecommitdiffstats
path: root/src/libsystemd-network/sd-ndisc-neighbor.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libsystemd-network/sd-ndisc-neighbor.c')
-rw-r--r--src/libsystemd-network/sd-ndisc-neighbor.c126
1 files changed, 126 insertions, 0 deletions
diff --git a/src/libsystemd-network/sd-ndisc-neighbor.c b/src/libsystemd-network/sd-ndisc-neighbor.c
new file mode 100644
index 0000000..1bb6ebf
--- /dev/null
+++ b/src/libsystemd-network/sd-ndisc-neighbor.c
@@ -0,0 +1,126 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <netinet/icmp6.h>
+
+#include "sd-ndisc.h"
+
+#include "alloc-util.h"
+#include "in-addr-util.h"
+#include "ndisc-internal.h"
+#include "ndisc-neighbor-internal.h"
+#include "ndisc-option.h"
+
+static sd_ndisc_neighbor* ndisc_neighbor_free(sd_ndisc_neighbor *na) {
+ if (!na)
+ return NULL;
+
+ icmp6_packet_unref(na->packet);
+ set_free(na->options);
+ return mfree(na);
+}
+
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc_neighbor, sd_ndisc_neighbor, ndisc_neighbor_free);
+
+sd_ndisc_neighbor* ndisc_neighbor_new(ICMP6Packet *packet) {
+ sd_ndisc_neighbor *na;
+
+ assert(packet);
+
+ na = new(sd_ndisc_neighbor, 1);
+ if (!na)
+ return NULL;
+
+ *na = (sd_ndisc_neighbor) {
+ .n_ref = 1,
+ .packet = icmp6_packet_ref(packet),
+ };
+
+ return na;
+}
+
+int ndisc_neighbor_parse(sd_ndisc *nd, sd_ndisc_neighbor *na) {
+ int r;
+
+ assert(na);
+
+ if (na->packet->raw_size < sizeof(struct nd_neighbor_advert))
+ return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
+ "Too small to be a neighbor advertisement, ignoring datagram.");
+
+ /* Neighbor advertisement packets are neatly aligned to 64-bit boundaries, hence we can access them directly */
+ const struct nd_neighbor_advert *a = (const struct nd_neighbor_advert*) na->packet->raw_packet;
+ assert(a->nd_na_type == ND_NEIGHBOR_ADVERT);
+ assert(a->nd_na_code == 0);
+
+ na->flags = a->nd_na_flags_reserved; /* the first 3 bits */
+ na->target_address = a->nd_na_target;
+
+ /* RFC 4861 section 4.4:
+ * For solicited advertisements, the Target Address field in the Neighbor Solicitation message that
+ * prompted this advertisement. For an unsolicited advertisement, the address whose link-layer
+ * address has changed. The Target Address MUST NOT be a multicast address.
+ *
+ * Here, we only check if the target address is a link-layer address (or a null address, for safety)
+ * when the message is an unsolicited neighbor advertisement. */
+ if (!FLAGS_SET(na->flags, ND_NA_FLAG_SOLICITED))
+ if (!in6_addr_is_link_local(&na->target_address) && !in6_addr_is_null(&na->target_address))
+ return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
+ "Received ND packet with an invalid target address (%s), ignoring datagram.",
+ IN6_ADDR_TO_STRING(&na->target_address));
+
+ r = ndisc_parse_options(na->packet, &na->options);
+ if (r < 0)
+ return log_ndisc_errno(nd, r, "Failed to parse NDisc options in neighbor advertisement message, ignoring: %m");
+
+ return 0;
+}
+
+int sd_ndisc_neighbor_get_sender_address(sd_ndisc_neighbor *na, struct in6_addr *ret) {
+ assert_return(na, -EINVAL);
+
+ return icmp6_packet_get_sender_address(na->packet, ret);
+}
+
+int sd_ndisc_neighbor_get_target_address(sd_ndisc_neighbor *na, struct in6_addr *ret) {
+ assert_return(na, -EINVAL);
+
+ if (in6_addr_is_null(&na->target_address))
+ /* fall back to the sender address, for safety. */
+ return sd_ndisc_neighbor_get_sender_address(na, ret);
+
+ if (ret)
+ *ret = na->target_address;
+ return 0;
+}
+
+int sd_ndisc_neighbor_get_target_mac(sd_ndisc_neighbor *na, struct ether_addr *ret) {
+ assert_return(na, -EINVAL);
+
+ return ndisc_option_get_mac(na->options, SD_NDISC_OPTION_TARGET_LL_ADDRESS, ret);
+}
+
+int sd_ndisc_neighbor_get_flags(sd_ndisc_neighbor *na, uint32_t *ret) {
+ assert_return(na, -EINVAL);
+
+ if (ret)
+ *ret = na->flags;
+ return 0;
+}
+
+int sd_ndisc_neighbor_is_router(sd_ndisc_neighbor *na) {
+ assert_return(na, -EINVAL);
+
+ return FLAGS_SET(na->flags, ND_NA_FLAG_ROUTER);
+}
+
+int sd_ndisc_neighbor_is_solicited(sd_ndisc_neighbor *na) {
+ assert_return(na, -EINVAL);
+
+ return FLAGS_SET(na->flags, ND_NA_FLAG_SOLICITED);
+}
+
+int sd_ndisc_neighbor_is_override(sd_ndisc_neighbor *na) {
+ assert_return(na, -EINVAL);
+
+ return FLAGS_SET(na->flags, ND_NA_FLAG_OVERRIDE);
+}