--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <netinet/icmp6.h>
+
+#include "icmp6-packet.h"
+#include "icmp6-util.h"
+#include "in-addr-util.h"
+#include "iovec-util.h"
+#include "network-common.h"
+#include "socket-util.h"
+
+DEFINE_TRIVIAL_REF_UNREF_FUNC(ICMP6Packet, icmp6_packet, mfree);
+
+static ICMP6Packet* icmp6_packet_new(size_t size) {
+ ICMP6Packet *p;
+
+ if (size > SIZE_MAX - offsetof(ICMP6Packet, raw_packet))
+ return NULL;
+
+ p = malloc0(offsetof(ICMP6Packet, raw_packet) + size);
+ if (!p)
+ return NULL;
+
+ p->raw_size = size;
+ p->n_ref = 1;
+
+ return p;
+}
+
+int icmp6_packet_get_sender_address(ICMP6Packet *p, struct in6_addr *ret) {
+ assert(p);
+
+ if (in6_addr_is_null(&p->sender_address))
+ return -ENODATA;
+
+ if (ret)
+ *ret = p->sender_address;
+ return 0;
+}
+
+int icmp6_packet_get_timestamp(ICMP6Packet *p, clockid_t clock, usec_t *ret) {
+ assert(p);
+ assert(ret);
+
+ if (!TRIPLE_TIMESTAMP_HAS_CLOCK(clock) || !clock_supported(clock))
+ return -EOPNOTSUPP;
+
+ if (!triple_timestamp_is_set(&p->timestamp))
+ return -ENODATA;
+
+ *ret = triple_timestamp_by_clock(&p->timestamp, clock);
+ return 0;
+}
+
+static const struct icmp6_hdr* icmp6_packet_get_header(ICMP6Packet *p) {
+ assert(p);
+
+ if (p->raw_size < sizeof(struct icmp6_hdr))
+ return NULL;
+
+ return (const struct icmp6_hdr*) p->raw_packet;
+}
+
+int icmp6_packet_get_type(ICMP6Packet *p) {
+ const struct icmp6_hdr *hdr = icmp6_packet_get_header(p);
+ if (!hdr)
+ return -EBADMSG;
+
+ return hdr->icmp6_type;
+}
+
+static int icmp6_packet_verify(ICMP6Packet *p) {
+ const struct icmp6_hdr *hdr = icmp6_packet_get_header(p);
+ if (!hdr)
+ return -EBADMSG;
+
+ if (hdr->icmp6_code != 0)
+ return -EBADMSG;
+
+ return 0;
+}
+
+int icmp6_packet_receive(int fd, ICMP6Packet **ret) {
+ _cleanup_(icmp6_packet_unrefp) ICMP6Packet *p = NULL;
+ ssize_t len;
+ int r;
+
+ assert(fd >= 0);
+ assert(ret);
+
+ len = next_datagram_size_fd(fd);
+ if (len < 0)
+ return (int) len;
+
+ p = icmp6_packet_new(len);
+ if (!p)
+ return -ENOMEM;
+
+ r = icmp6_receive(fd, p->raw_packet, p->raw_size, &p->sender_address, &p->timestamp);
+ if (r < 0)
+ switch (r) {
+ case -EADDRNOTAVAIL:
+ return log_debug_errno(r, "ICMPv6: Received a packet from neither link-local nor null address.");
+
+ case -EMULTIHOP:
+ return log_debug_errno(r, "ICMPv6: Received a packet with an invalid hop limit.");
+
+ case -EPFNOSUPPORT:
+ return log_debug_errno(r, "ICMPv6: Received a packet with an invalid source address.");
+
+ default:
+ return log_debug_errno(r, "ICMPv6: Unexpected error while receiving a packet: %m");
+ }
+
+ r = icmp6_packet_verify(p);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(p);
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <inttypes.h>
+#include <netinet/in.h>
+
+#include "macro.h"
+#include "time-util.h"
+
+typedef struct ICMP6Pakcet {
+ unsigned n_ref;
+
+ struct in6_addr sender_address;
+ struct triple_timestamp timestamp;
+
+ size_t raw_size;
+ uint8_t raw_packet[];
+} ICMP6Packet;
+
+ICMP6Packet* icmp6_packet_ref(ICMP6Packet *p);
+ICMP6Packet* icmp6_packet_unref(ICMP6Packet *p);
+DEFINE_TRIVIAL_CLEANUP_FUNC(ICMP6Packet*, icmp6_packet_unref);
+
+int icmp6_packet_get_sender_address(ICMP6Packet *p, struct in6_addr *ret);
+int icmp6_packet_get_timestamp(ICMP6Packet *p, clockid_t clock, usec_t *ret);
+int icmp6_packet_get_type(ICMP6Packet *p);
+
+int icmp6_packet_receive(int fd, ICMP6Packet **ret);