]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-netlink/netlink-socket.c
28fd2bc3d9b03b537dadf552019fd497091c04c4
[thirdparty/systemd.git] / src / libsystemd / sd-netlink / netlink-socket.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "sd-netlink.h"
4
5 #include "alloc-util.h"
6 #include "errno-util.h"
7 #include "hashmap.h"
8 #include "iovec-util.h"
9 #include "log.h"
10 #include "netlink-internal.h"
11 #include "netlink-types.h"
12 #include "ordered-set.h"
13 #include "socket-util.h"
14
15 static int broadcast_groups_get(sd_netlink *nl) {
16 _cleanup_free_ uint32_t *groups = NULL;
17 size_t len;
18 int r;
19
20 assert(nl);
21 assert(nl->fd >= 0);
22
23 r = netlink_socket_get_multicast_groups(nl->fd, &len, &groups);
24 if (r < 0)
25 return r;
26
27 for (size_t i = 0; i < len; i++)
28 for (unsigned j = 0; j < sizeof(uint32_t) * 8; j++)
29 if (groups[i] & (1U << j)) {
30 unsigned group = i * sizeof(uint32_t) * 8 + j + 1;
31
32 r = hashmap_ensure_put(&nl->broadcast_group_refs, NULL, UINT_TO_PTR(group), UINT_TO_PTR(1));
33 if (r < 0)
34 return r;
35 }
36
37 return 0;
38 }
39
40 int socket_bind(sd_netlink *nl) {
41 socklen_t addrlen;
42 int r;
43
44 r = setsockopt_int(nl->fd, SOL_NETLINK, NETLINK_PKTINFO, true);
45 if (r < 0)
46 return r;
47
48 addrlen = sizeof(nl->sockaddr);
49
50 /* ignore EINVAL to allow binding an already bound socket */
51 if (bind(nl->fd, &nl->sockaddr.sa, addrlen) < 0 && errno != EINVAL)
52 return -errno;
53
54 if (getsockname(nl->fd, &nl->sockaddr.sa, &addrlen) < 0)
55 return -errno;
56
57 return broadcast_groups_get(nl);
58 }
59
60 static unsigned broadcast_group_get_ref(sd_netlink *nl, unsigned group) {
61 assert(nl);
62
63 return PTR_TO_UINT(hashmap_get(nl->broadcast_group_refs, UINT_TO_PTR(group)));
64 }
65
66 static int broadcast_group_set_ref(sd_netlink *nl, unsigned group, unsigned n_ref) {
67 assert(nl);
68
69 return hashmap_ensure_replace(&nl->broadcast_group_refs, NULL, UINT_TO_PTR(group), UINT_TO_PTR(n_ref));
70 }
71
72 static int broadcast_group_join(sd_netlink *nl, unsigned group) {
73 assert(nl);
74 assert(nl->fd >= 0);
75 assert(group > 0);
76
77 /* group is "unsigned", but netlink(7) says the argument for NETLINK_ADD_MEMBERSHIP is "int" */
78 return setsockopt_int(nl->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, group);
79 }
80
81 int socket_broadcast_group_ref(sd_netlink *nl, unsigned group) {
82 unsigned n_ref;
83 int r;
84
85 assert(nl);
86
87 n_ref = broadcast_group_get_ref(nl, group);
88
89 n_ref++;
90
91 r = broadcast_group_set_ref(nl, group, n_ref);
92 if (r < 0)
93 return r;
94
95 if (n_ref > 1)
96 /* already in the group */
97 return 0;
98
99 return broadcast_group_join(nl, group);
100 }
101
102 int socket_broadcast_group_unref(sd_netlink *nl, unsigned group) {
103 unsigned n_ref;
104 int r;
105
106 assert(nl);
107
108 n_ref = broadcast_group_get_ref(nl, group);
109 if (n_ref == 0)
110 return 0;
111
112 n_ref--;
113
114 r = broadcast_group_set_ref(nl, group, n_ref);
115 if (r < 0)
116 return r;
117
118 if (n_ref > 0)
119 /* still refs left */
120 return 0;
121
122 /* group is "unsigned", but netlink(7) says the argument for NETLINK_DROP_MEMBERSHIP is "int" */
123 return setsockopt_int(nl->fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP, group);
124 }
125
126 /* returns the number of bytes sent, or a negative error code */
127 int socket_write_message(sd_netlink *nl, sd_netlink_message *m) {
128 union sockaddr_union addr = {
129 .nl.nl_family = AF_NETLINK,
130 };
131 ssize_t k;
132
133 assert(nl);
134 assert(m);
135 assert(m->hdr);
136
137 k = sendto(nl->fd, m->hdr, m->hdr->nlmsg_len, 0, &addr.sa, sizeof(addr));
138 if (k < 0)
139 return -errno;
140
141 return k;
142 }
143
144 static int socket_recv_message(int fd, void *buf, size_t buf_size, uint32_t *ret_mcast_group, bool peek) {
145 struct iovec iov = IOVEC_MAKE(buf, buf_size);
146 union sockaddr_union sender;
147 CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct nl_pktinfo))) control;
148 struct msghdr msg = {
149 .msg_iov = &iov,
150 .msg_iovlen = 1,
151 .msg_name = &sender,
152 .msg_namelen = sizeof(sender),
153 .msg_control = &control,
154 .msg_controllen = sizeof(control),
155 };
156 ssize_t n;
157
158 assert(fd >= 0);
159 assert(peek || (buf && buf_size > 0));
160
161 /* Note: this might return successfully, but with a zero size under some transient conditions, such
162 * as the reception of a non-kernel message. In such a case the passed buffer might or might not be
163 * modified. Caller must treat a zero return as "no message, but also not an error". */
164
165 n = recvmsg_safe(fd, &msg, peek ? (MSG_PEEK|MSG_TRUNC) : 0);
166 if (ERRNO_IS_NEG_TRANSIENT(n))
167 goto transient;
168 if (n == -ENOBUFS)
169 return log_debug_errno(n, "sd-netlink: kernel receive buffer overrun");
170 if (n == -ECHRNG)
171 return log_debug_errno(n, "sd-netlink: got truncated control message");
172 if (n == -EXFULL)
173 return log_debug_errno(n, "sd-netlink: got truncated payload message");
174 if (n < 0)
175 return (int) n;
176
177 if (sender.nl.nl_pid != 0) {
178 /* not from the kernel, ignore */
179 log_debug("sd-netlink: ignoring message from PID %"PRIu32, sender.nl.nl_pid);
180
181 if (peek) {
182 /* Drop the message. Note that we ignore ECHRNG/EXFULL errors here, which
183 * recvmsg_safe() returns in case the payload or cdata is truncated. Given we just
184 * want to drop the message we also don't care if its payload or cdata was
185 * truncated. */
186 n = recvmsg_safe(fd, &msg, 0);
187 if (n < 0 && !IN_SET(n, -ECHRNG, -EXFULL))
188 return (int) n;
189 }
190
191 goto transient;
192 }
193
194 if (ret_mcast_group) {
195 struct nl_pktinfo *pi;
196
197 pi = CMSG_FIND_DATA(&msg, SOL_NETLINK, NETLINK_PKTINFO, struct nl_pktinfo);
198 if (pi)
199 *ret_mcast_group = pi->group;
200 else
201 *ret_mcast_group = 0;
202 }
203
204 return (int) n;
205
206 transient:
207 if (ret_mcast_group)
208 *ret_mcast_group = 0;
209
210 return 0;
211 }
212
213 DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
214 netlink_message_hash_ops,
215 void, trivial_hash_func, trivial_compare_func,
216 sd_netlink_message, sd_netlink_message_unref);
217
218 static int netlink_queue_received_message(sd_netlink *nl, sd_netlink_message *m) {
219 uint32_t serial;
220 int r;
221
222 assert(nl);
223 assert(m);
224
225 if (ordered_set_size(nl->rqueue) >= NETLINK_RQUEUE_MAX)
226 return log_debug_errno(SYNTHETIC_ERRNO(ENOBUFS),
227 "sd-netlink: exhausted the read queue size (%d)", NETLINK_RQUEUE_MAX);
228
229 r = ordered_set_ensure_put(&nl->rqueue, &netlink_message_hash_ops, m);
230 if (r < 0)
231 return r;
232
233 sd_netlink_message_ref(m);
234
235 if (sd_netlink_message_is_broadcast(m))
236 return 0;
237
238 serial = message_get_serial(m);
239 if (serial == 0)
240 return 0;
241
242 if (sd_netlink_message_get_errno(m) < 0) {
243 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *old = NULL;
244
245 old = hashmap_remove(nl->rqueue_by_serial, UINT32_TO_PTR(serial));
246 if (old)
247 log_debug("sd-netlink: received error message with serial %"PRIu32", but another message with "
248 "the same serial is already stored in the read queue, replacing.", serial);
249 }
250
251 r = hashmap_ensure_put(&nl->rqueue_by_serial, &netlink_message_hash_ops, UINT32_TO_PTR(serial), m);
252 if (r == -EEXIST) {
253 if (!sd_netlink_message_is_error(m))
254 log_debug("sd-netlink: received message with serial %"PRIu32", but another message with "
255 "the same serial is already stored in the read queue, ignoring.", serial);
256 return 0;
257 }
258 if (r < 0) {
259 sd_netlink_message_unref(ordered_set_remove(nl->rqueue, m));
260 return r;
261 }
262
263 sd_netlink_message_ref(m);
264 return 0;
265 }
266
267 static int netlink_queue_partially_received_message(sd_netlink *nl, sd_netlink_message *m) {
268 uint32_t serial;
269 int r;
270
271 assert(nl);
272 assert(m);
273 assert(m->hdr->nlmsg_flags & NLM_F_MULTI);
274
275 if (hashmap_size(nl->rqueue_partial_by_serial) >= NETLINK_RQUEUE_MAX)
276 return log_debug_errno(SYNTHETIC_ERRNO(ENOBUFS),
277 "sd-netlink: exhausted the partial read queue size (%d)", NETLINK_RQUEUE_MAX);
278
279 serial = message_get_serial(m);
280 r = hashmap_ensure_put(&nl->rqueue_partial_by_serial, &netlink_message_hash_ops, UINT32_TO_PTR(serial), m);
281 if (r < 0)
282 return r;
283
284 sd_netlink_message_ref(m);
285 return 0;
286 }
287
288 static int parse_message_one(sd_netlink *nl, uint32_t group, const struct nlmsghdr *hdr, sd_netlink_message **ret) {
289 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
290 size_t size;
291 int r;
292
293 assert(nl);
294 assert(hdr);
295 assert(ret);
296
297 /* not broadcast and not for us */
298 if (group == 0 && hdr->nlmsg_pid != nl->sockaddr.nl.nl_pid)
299 goto finalize;
300
301 /* silently drop noop messages */
302 if (hdr->nlmsg_type == NLMSG_NOOP)
303 goto finalize;
304
305 /* check that we support this message type */
306 r = netlink_get_policy_set_and_header_size(nl, hdr->nlmsg_type, hdr->nlmsg_flags, NULL, &size);
307 if (r == -EOPNOTSUPP) {
308 log_debug("sd-netlink: ignored message with unknown type: %i", hdr->nlmsg_type);
309 goto finalize;
310 }
311 if (r < 0)
312 return r;
313
314 /* check that the size matches the message type */
315 if (hdr->nlmsg_len < NLMSG_LENGTH(size)) {
316 log_debug("sd-netlink: message is shorter than expected, dropping.");
317 goto finalize;
318 }
319
320 r = message_new_empty(nl, &m);
321 if (r < 0)
322 return r;
323
324 m->multicast_group = group;
325 m->hdr = memdup(hdr, hdr->nlmsg_len);
326 if (!m->hdr)
327 return -ENOMEM;
328
329 /* seal and parse the top-level message */
330 r = sd_netlink_message_rewind(m, nl);
331 if (r < 0)
332 return r;
333
334 *ret = TAKE_PTR(m);
335 return 1;
336
337 finalize:
338 *ret = NULL;
339 return 0;
340 }
341
342 /* On success, the number of bytes received is returned and *ret points to the received message
343 * which has a valid header and the correct size.
344 * If nothing useful was received 0 is returned.
345 * On failure, a negative error code is returned.
346 */
347 int socket_read_message(sd_netlink *nl) {
348 bool done = false;
349 uint32_t group;
350 size_t len;
351 int r;
352
353 assert(nl);
354
355 /* read nothing, just get the pending message size */
356 r = socket_recv_message(nl->fd, NULL, 0, NULL, true);
357 if (r <= 0)
358 return r;
359 len = (size_t) r;
360
361 /* make room for the pending message */
362 if (!greedy_realloc((void**) &nl->rbuffer, len, sizeof(uint8_t)))
363 return -ENOMEM;
364
365 /* read the pending message */
366 r = socket_recv_message(nl->fd, nl->rbuffer, MALLOC_SIZEOF_SAFE(nl->rbuffer), &group, false);
367 if (r <= 0)
368 return r;
369 len = (size_t) r;
370
371 if (!NLMSG_OK(nl->rbuffer, len)) {
372 log_debug("sd-netlink: received invalid message, discarding %zu bytes of incoming message", len);
373 return 0;
374 }
375
376 for (struct nlmsghdr *hdr = nl->rbuffer; NLMSG_OK(hdr, len); hdr = NLMSG_NEXT(hdr, len)) {
377 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
378
379 r = parse_message_one(nl, group, hdr, &m);
380 if (r < 0)
381 return r;
382 if (r == 0)
383 continue;
384
385 if (hdr->nlmsg_flags & NLM_F_MULTI) {
386 if (hdr->nlmsg_type == NLMSG_DONE) {
387 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *existing = NULL;
388
389 /* finished reading multi-part message */
390 existing = hashmap_remove(nl->rqueue_partial_by_serial, UINT32_TO_PTR(hdr->nlmsg_seq));
391
392 /* if we receive only NLMSG_DONE, put it into the receive queue. */
393 r = netlink_queue_received_message(nl, existing ?: m);
394 if (r < 0)
395 return r;
396
397 done = true;
398 } else {
399 sd_netlink_message *existing;
400
401 existing = hashmap_get(nl->rqueue_partial_by_serial, UINT32_TO_PTR(hdr->nlmsg_seq));
402 if (existing) {
403 /* This is the continuation of the previously read messages.
404 * Let's append this message at the end. */
405 while (existing->next)
406 existing = existing->next;
407 existing->next = TAKE_PTR(m);
408 } else {
409 /* This is the first message. Put it into the queue for partially
410 * received messages. */
411 r = netlink_queue_partially_received_message(nl, m);
412 if (r < 0)
413 return r;
414 }
415 }
416
417 } else {
418 r = netlink_queue_received_message(nl, m);
419 if (r < 0)
420 return r;
421
422 done = true;
423 }
424 }
425
426 if (len > 0)
427 log_debug("sd-netlink: discarding trailing %zu bytes of incoming message", len);
428
429 return done;
430 }