]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-netlink/netlink-socket.c
ssh-generator: generate /etc/issue.d/ with VSOCK ssh info data (#37819)
[thirdparty/systemd.git] / src / libsystemd / sd-netlink / netlink-socket.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
89489ef7 2
07630cea
LP
3#include "sd-netlink.h"
4
b5efdb8a 5#include "alloc-util.h"
5cdf13c7
DDM
6#include "errno-util.h"
7#include "hashmap.h"
bd1ae178 8#include "iovec-util.h"
93a1f792 9#include "log.h"
89489ef7
TG
10#include "netlink-internal.h"
11#include "netlink-types.h"
5cdf13c7 12#include "ordered-set.h"
07630cea 13#include "socket-util.h"
89489ef7 14
9c5a882b
TG
15static int broadcast_groups_get(sd_netlink *nl) {
16 _cleanup_free_ uint32_t *groups = NULL;
029709f9 17 size_t len;
9c5a882b
TG
18 int r;
19
20 assert(nl);
f78bc916 21 assert(nl->fd >= 0);
9c5a882b 22
029709f9 23 r = netlink_socket_get_multicast_groups(nl->fd, &len, &groups);
029709f9
YW
24 if (r < 0)
25 return r;
9c5a882b 26
029709f9 27 for (size_t i = 0; i < len; i++)
c7209bcf
ZJS
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;
9c5a882b 31
db4735b7 32 r = hashmap_ensure_put(&nl->broadcast_group_refs, NULL, UINT_TO_PTR(group), UINT_TO_PTR(1));
c7209bcf
ZJS
33 if (r < 0)
34 return r;
35 }
9c5a882b
TG
36
37 return 0;
38}
39
b95cc756
TG
40int socket_bind(sd_netlink *nl) {
41 socklen_t addrlen;
6d5e65f6 42 int r;
b95cc756 43
2ff48e98 44 r = setsockopt_int(nl->fd, SOL_NETLINK, NETLINK_PKTINFO, true);
b95cc756 45 if (r < 0)
2ff48e98 46 return r;
b95cc756
TG
47
48 addrlen = sizeof(nl->sockaddr);
49
db4735b7 50 /* ignore EINVAL to allow binding an already bound socket */
52888279 51 if (bind(nl->fd, &nl->sockaddr.sa, addrlen) < 0 && errno != EINVAL)
b95cc756
TG
52 return -errno;
53
52888279 54 if (getsockname(nl->fd, &nl->sockaddr.sa, &addrlen) < 0)
b95cc756
TG
55 return -errno;
56
c7209bcf 57 return broadcast_groups_get(nl);
b95cc756
TG
58}
59
9c5a882b
TG
60static 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
66static int broadcast_group_set_ref(sd_netlink *nl, unsigned group, unsigned n_ref) {
9c5a882b
TG
67 assert(nl);
68
e1226a9b 69 return hashmap_ensure_replace(&nl->broadcast_group_refs, NULL, UINT_TO_PTR(group), UINT_TO_PTR(n_ref));
9c5a882b 70}
b95cc756 71
9c5a882b 72static int broadcast_group_join(sd_netlink *nl, unsigned group) {
b95cc756
TG
73 assert(nl);
74 assert(nl->fd >= 0);
75 assert(group > 0);
76
4fa02468
ZJS
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);
b95cc756
TG
79}
80
9c5a882b
TG
81int 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
313cefa1 89 n_ref++;
9c5a882b 90
9c5a882b
TG
91 r = broadcast_group_set_ref(nl, group, n_ref);
92 if (r < 0)
93 return r;
94
95 if (n_ref > 1)
db4735b7 96 /* already in the group */
9c5a882b
TG
97 return 0;
98
db4735b7 99 return broadcast_group_join(nl, group);
9c5a882b
TG
100}
101
9c5a882b
TG
102int 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);
eee15fff
YW
109 if (n_ref == 0)
110 return 0;
9c5a882b 111
313cefa1 112 n_ref--;
9c5a882b
TG
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
6dbf2c1b
YW
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);
9c5a882b
TG
124}
125
89489ef7
TG
126/* returns the number of bytes sent, or a negative error code */
127int socket_write_message(sd_netlink *nl, sd_netlink_message *m) {
db4735b7 128 union sockaddr_union addr = {
89489ef7
TG
129 .nl.nl_family = AF_NETLINK,
130 };
131 ssize_t k;
132
133 assert(nl);
134 assert(m);
135 assert(m->hdr);
136
db4735b7 137 k = sendto(nl->fd, m->hdr, m->hdr->nlmsg_len, 0, &addr.sa, sizeof(addr));
89489ef7
TG
138 if (k < 0)
139 return -errno;
140
141 return k;
142}
143
61d95dc0
YW
144static 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);
89489ef7 146 union sockaddr_union sender;
43007b30 147 CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct nl_pktinfo))) control;
89489ef7 148 struct msghdr msg = {
61d95dc0 149 .msg_iov = &iov,
89489ef7
TG
150 .msg_iovlen = 1,
151 .msg_name = &sender,
152 .msg_namelen = sizeof(sender),
9e45fb09
LP
153 .msg_control = &control,
154 .msg_controllen = sizeof(control),
89489ef7 155 };
f1dd72c2 156 ssize_t n;
89489ef7
TG
157
158 assert(fd >= 0);
61d95dc0 159 assert(peek || (buf && buf_size > 0));
89489ef7 160
a035eaa2
LP
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
ad501930 165 n = recvmsg_safe(fd, &msg, peek ? (MSG_PEEK|MSG_TRUNC) : 0);
90755dac
LP
166 if (ERRNO_IS_NEG_TRANSIENT(n))
167 goto transient;
ad501930
MY
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)
9e45fb09 175 return (int) n;
89489ef7
TG
176
177 if (sender.nl.nl_pid != 0) {
178 /* not from the kernel, ignore */
409856d3 179 log_debug("sd-netlink: ignoring message from PID %"PRIu32, sender.nl.nl_pid);
89489ef7
TG
180
181 if (peek) {
90755dac 182 /* Drop the message. Note that we ignore ECHRNG/EXFULL errors here, which
a035eaa2
LP
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. */
9e45fb09 186 n = recvmsg_safe(fd, &msg, 0);
90755dac 187 if (n < 0 && !IN_SET(n, -ECHRNG, -EXFULL))
9e45fb09 188 return (int) n;
89489ef7
TG
189 }
190
90755dac 191 goto transient;
89489ef7
TG
192 }
193
dac556fa 194 if (ret_mcast_group) {
9e45fb09 195 struct nl_pktinfo *pi;
89489ef7 196
9e45fb09
LP
197 pi = CMSG_FIND_DATA(&msg, SOL_NETLINK, NETLINK_PKTINFO, struct nl_pktinfo);
198 if (pi)
199 *ret_mcast_group = pi->group;
dac556fa
LP
200 else
201 *ret_mcast_group = 0;
89489ef7
TG
202 }
203
f1dd72c2 204 return (int) n;
90755dac
LP
205
206transient:
207 if (ret_mcast_group)
208 *ret_mcast_group = 0;
209
210 return 0;
89489ef7
TG
211}
212
e417c4ac
YW
213DEFINE_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
2ea465ef 218static int netlink_queue_received_message(sd_netlink *nl, sd_netlink_message *m) {
7b34bae3 219 uint32_t serial;
e417c4ac
YW
220 int r;
221
2ea465ef
YW
222 assert(nl);
223 assert(m);
224
e417c4ac 225 if (ordered_set_size(nl->rqueue) >= NETLINK_RQUEUE_MAX)
2ea465ef 226 return log_debug_errno(SYNTHETIC_ERRNO(ENOBUFS),
e417c4ac 227 "sd-netlink: exhausted the read queue size (%d)", NETLINK_RQUEUE_MAX);
2ea465ef 228
e417c4ac
YW
229 r = ordered_set_ensure_put(&nl->rqueue, &netlink_message_hash_ops, m);
230 if (r < 0)
231 return r;
2ea465ef 232
7b34bae3
YW
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
e417c4ac 263 sd_netlink_message_ref(m);
2ea465ef
YW
264 return 0;
265}
266
267static int netlink_queue_partially_received_message(sd_netlink *nl, sd_netlink_message *m) {
e417c4ac
YW
268 uint32_t serial;
269 int r;
270
2ea465ef
YW
271 assert(nl);
272 assert(m);
273 assert(m->hdr->nlmsg_flags & NLM_F_MULTI);
274
e417c4ac 275 if (hashmap_size(nl->rqueue_partial_by_serial) >= NETLINK_RQUEUE_MAX)
2ea465ef 276 return log_debug_errno(SYNTHETIC_ERRNO(ENOBUFS),
e417c4ac 277 "sd-netlink: exhausted the partial read queue size (%d)", NETLINK_RQUEUE_MAX);
2ea465ef 278
e417c4ac
YW
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;
2ea465ef 283
e417c4ac 284 sd_netlink_message_ref(m);
2ea465ef
YW
285 return 0;
286}
287
9482429a
YW
288static 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 */
6e196011 306 r = netlink_get_policy_set_and_header_size(nl, hdr->nlmsg_type, hdr->nlmsg_flags, NULL, &size);
9482429a
YW
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
337finalize:
338 *ret = NULL;
339 return 0;
340}
341
89489ef7
TG
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 */
409856d3 347int socket_read_message(sd_netlink *nl) {
a8ac0526 348 bool done = false;
027193db 349 uint32_t group;
a8ac0526 350 size_t len;
319a4f4b 351 int r;
89489ef7 352
409856d3 353 assert(nl);
89489ef7
TG
354
355 /* read nothing, just get the pending message size */
61d95dc0 356 r = socket_recv_message(nl->fd, NULL, 0, NULL, true);
89489ef7
TG
357 if (r <= 0)
358 return r;
b374397e 359 len = (size_t) r;
89489ef7
TG
360
361 /* make room for the pending message */
409856d3 362 if (!greedy_realloc((void**) &nl->rbuffer, len, sizeof(uint8_t)))
89489ef7
TG
363 return -ENOMEM;
364
89489ef7 365 /* read the pending message */
61d95dc0 366 r = socket_recv_message(nl->fd, nl->rbuffer, MALLOC_SIZEOF_SAFE(nl->rbuffer), &group, false);
89489ef7
TG
367 if (r <= 0)
368 return r;
b374397e 369 len = (size_t) r;
89489ef7 370
b491454d
YW
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
a8ac0526 376 for (struct nlmsghdr *hdr = nl->rbuffer; NLMSG_OK(hdr, len); hdr = NLMSG_NEXT(hdr, len)) {
4afd3348 377 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
89489ef7 378
9482429a
YW
379 r = parse_message_one(nl, group, hdr, &m);
380 if (r < 0)
381 return r;
382 if (r == 0)
89489ef7
TG
383 continue;
384
a8ac0526
YW
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;
89489ef7 388
a8ac0526
YW
389 /* finished reading multi-part message */
390 existing = hashmap_remove(nl->rqueue_partial_by_serial, UINT32_TO_PTR(hdr->nlmsg_seq));
89489ef7 391
a8ac0526
YW
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
52ceba53
YW
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 }
a8ac0526
YW
415 }
416
417 } else {
418 r = netlink_queue_received_message(nl, m);
419 if (r < 0)
420 return r;
421
422 done = true;
423 }
89489ef7
TG
424 }
425
f1dd72c2 426 if (len > 0)
b491454d 427 log_debug("sd-netlink: discarding trailing %zu bytes of incoming message", len);
89489ef7 428
2ea465ef 429 return done;
89489ef7 430}