]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/icmp6-packet.c
machine: resolve race condition in TEST-13-NSPAWN.machinectl.sh
[thirdparty/systemd.git] / src / libsystemd-network / icmp6-packet.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <netinet/icmp6.h>
4
5 #include "icmp6-packet.h"
6 #include "icmp6-util.h"
7 #include "in-addr-util.h"
8 #include "iovec-util.h"
9 #include "network-common.h"
10 #include "socket-util.h"
11
12 DEFINE_TRIVIAL_REF_UNREF_FUNC(ICMP6Packet, icmp6_packet, mfree);
13
14 static ICMP6Packet* icmp6_packet_new(size_t size) {
15 ICMP6Packet *p;
16
17 if (size > SIZE_MAX - offsetof(ICMP6Packet, raw_packet))
18 return NULL;
19
20 p = malloc0(offsetof(ICMP6Packet, raw_packet) + size);
21 if (!p)
22 return NULL;
23
24 p->raw_size = size;
25 p->n_ref = 1;
26
27 return p;
28 }
29
30 int icmp6_packet_set_sender_address(ICMP6Packet *p, const struct in6_addr *addr) {
31 assert(p);
32
33 if (addr)
34 p->sender_address = *addr;
35 else
36 p->sender_address = (const struct in6_addr) {};
37
38 return 0;
39 }
40
41 int icmp6_packet_get_sender_address(ICMP6Packet *p, struct in6_addr *ret) {
42 assert(p);
43
44 if (in6_addr_is_null(&p->sender_address))
45 return -ENODATA;
46
47 if (ret)
48 *ret = p->sender_address;
49 return 0;
50 }
51
52 int icmp6_packet_get_timestamp(ICMP6Packet *p, clockid_t clock, usec_t *ret) {
53 assert(p);
54 assert(ret);
55
56 if (!TRIPLE_TIMESTAMP_HAS_CLOCK(clock) || !clock_supported(clock))
57 return -EOPNOTSUPP;
58
59 if (!triple_timestamp_is_set(&p->timestamp))
60 return -ENODATA;
61
62 *ret = triple_timestamp_by_clock(&p->timestamp, clock);
63 return 0;
64 }
65
66 const struct icmp6_hdr* icmp6_packet_get_header(ICMP6Packet *p) {
67 assert(p);
68
69 if (p->raw_size < sizeof(struct icmp6_hdr))
70 return NULL;
71
72 return (const struct icmp6_hdr*) p->raw_packet;
73 }
74
75 int icmp6_packet_get_type(ICMP6Packet *p) {
76 const struct icmp6_hdr *hdr = icmp6_packet_get_header(p);
77 if (!hdr)
78 return -EBADMSG;
79
80 return hdr->icmp6_type;
81 }
82
83 static int icmp6_packet_verify(ICMP6Packet *p) {
84 const struct icmp6_hdr *hdr = icmp6_packet_get_header(p);
85 if (!hdr)
86 return -EBADMSG;
87
88 if (hdr->icmp6_code != 0)
89 return -EBADMSG;
90
91 /* Drop any overly large packets early. We are not interested in jumbograms,
92 * which could cause excessive processing. */
93 if (p->raw_size > ICMP6_MAX_NORMAL_PAYLOAD_SIZE)
94 return -EMSGSIZE;
95
96 return 0;
97 }
98
99 int icmp6_packet_receive(int fd, ICMP6Packet **ret) {
100 _cleanup_(icmp6_packet_unrefp) ICMP6Packet *p = NULL;
101 ssize_t len;
102 int r;
103
104 assert(fd >= 0);
105 assert(ret);
106
107 len = next_datagram_size_fd(fd);
108 if (len < 0)
109 return (int) len;
110
111 p = icmp6_packet_new(len);
112 if (!p)
113 return -ENOMEM;
114
115 r = icmp6_receive(fd, p->raw_packet, p->raw_size, &p->sender_address, &p->timestamp);
116 if (r == -EADDRNOTAVAIL)
117 return log_debug_errno(r, "ICMPv6: Received a packet from neither link-local nor null address.");
118 if (r == -EMULTIHOP)
119 return log_debug_errno(r, "ICMPv6: Received a packet with an invalid hop limit.");
120 if (r == -EPFNOSUPPORT)
121 return log_debug_errno(r, "ICMPv6: Received a packet with an invalid source address.");
122 if (r < 0)
123 return log_debug_errno(r, "ICMPv6: Unexpected error while receiving a packet: %m");
124
125 r = icmp6_packet_verify(p);
126 if (r < 0)
127 return r;
128
129 *ret = TAKE_PTR(p);
130 return 0;
131 }