]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-netlink/netlink-socket.c
Merge pull request #10797 from poettering/run-generator
[thirdparty/systemd.git] / src / libsystemd / sd-netlink / netlink-socket.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <netinet/in.h>
4 #include <stdbool.h>
5 #include <unistd.h>
6
7 #include "sd-netlink.h"
8
9 #include "alloc-util.h"
10 #include "fd-util.h"
11 #include "format-util.h"
12 #include "io-util.h"
13 #include "missing.h"
14 #include "netlink-internal.h"
15 #include "netlink-types.h"
16 #include "netlink-util.h"
17 #include "refcnt.h"
18 #include "socket-util.h"
19 #include "util.h"
20
21 int socket_open(int family) {
22 int fd;
23
24 fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, family);
25 if (fd < 0)
26 return -errno;
27
28 return fd_move_above_stdio(fd);
29 }
30
31 static int broadcast_groups_get(sd_netlink *nl) {
32 _cleanup_free_ uint32_t *groups = NULL;
33 socklen_t len = 0, old_len;
34 unsigned i, j;
35 int r;
36
37 assert(nl);
38 assert(nl->fd >= 0);
39
40 r = getsockopt(nl->fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, NULL, &len);
41 if (r < 0) {
42 if (errno == ENOPROTOOPT) {
43 nl->broadcast_group_dont_leave = true;
44 return 0;
45 } else
46 return -errno;
47 }
48
49 if (len == 0)
50 return 0;
51
52 groups = new0(uint32_t, len);
53 if (!groups)
54 return -ENOMEM;
55
56 old_len = len;
57
58 r = getsockopt(nl->fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, groups, &len);
59 if (r < 0)
60 return -errno;
61
62 if (old_len != len)
63 return -EIO;
64
65 r = hashmap_ensure_allocated(&nl->broadcast_group_refs, NULL);
66 if (r < 0)
67 return r;
68
69 for (i = 0; i < len; i++) {
70 for (j = 0; j < sizeof(uint32_t) * 8; j++) {
71 uint32_t offset;
72 unsigned group;
73
74 offset = 1U << j;
75
76 if (!(groups[i] & offset))
77 continue;
78
79 group = i * sizeof(uint32_t) * 8 + j + 1;
80
81 r = hashmap_put(nl->broadcast_group_refs, UINT_TO_PTR(group), UINT_TO_PTR(1));
82 if (r < 0)
83 return r;
84 }
85 }
86
87 return 0;
88 }
89
90 int socket_bind(sd_netlink *nl) {
91 socklen_t addrlen;
92 int r;
93
94 r = setsockopt_int(nl->fd, SOL_NETLINK, NETLINK_PKTINFO, true);
95 if (r < 0)
96 return r;
97
98 addrlen = sizeof(nl->sockaddr);
99
100 r = bind(nl->fd, &nl->sockaddr.sa, addrlen);
101 /* ignore EINVAL to allow opening an already bound socket */
102 if (r < 0 && errno != EINVAL)
103 return -errno;
104
105 r = getsockname(nl->fd, &nl->sockaddr.sa, &addrlen);
106 if (r < 0)
107 return -errno;
108
109 r = broadcast_groups_get(nl);
110 if (r < 0)
111 return r;
112
113 return 0;
114 }
115
116 static unsigned broadcast_group_get_ref(sd_netlink *nl, unsigned group) {
117 assert(nl);
118
119 return PTR_TO_UINT(hashmap_get(nl->broadcast_group_refs, UINT_TO_PTR(group)));
120 }
121
122 static int broadcast_group_set_ref(sd_netlink *nl, unsigned group, unsigned n_ref) {
123 int r;
124
125 assert(nl);
126
127 r = hashmap_replace(nl->broadcast_group_refs, UINT_TO_PTR(group), UINT_TO_PTR(n_ref));
128 if (r < 0)
129 return r;
130
131 return 0;
132 }
133
134 static int broadcast_group_join(sd_netlink *nl, unsigned group) {
135 int r;
136
137 assert(nl);
138 assert(nl->fd >= 0);
139 assert(group > 0);
140
141 r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group));
142 if (r < 0)
143 return -errno;
144
145 return 0;
146 }
147
148 int socket_broadcast_group_ref(sd_netlink *nl, unsigned group) {
149 unsigned n_ref;
150 int r;
151
152 assert(nl);
153
154 n_ref = broadcast_group_get_ref(nl, group);
155
156 n_ref++;
157
158 r = hashmap_ensure_allocated(&nl->broadcast_group_refs, NULL);
159 if (r < 0)
160 return r;
161
162 r = broadcast_group_set_ref(nl, group, n_ref);
163 if (r < 0)
164 return r;
165
166 if (n_ref > 1)
167 /* not yet in the group */
168 return 0;
169
170 r = broadcast_group_join(nl, group);
171 if (r < 0)
172 return r;
173
174 return 0;
175 }
176
177 static int broadcast_group_leave(sd_netlink *nl, unsigned group) {
178 int r;
179
180 assert(nl);
181 assert(nl->fd >= 0);
182 assert(group > 0);
183
184 if (nl->broadcast_group_dont_leave)
185 return 0;
186
187 r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP, &group, sizeof(group));
188 if (r < 0)
189 return -errno;
190
191 return 0;
192 }
193
194 int socket_broadcast_group_unref(sd_netlink *nl, unsigned group) {
195 unsigned n_ref;
196 int r;
197
198 assert(nl);
199
200 n_ref = broadcast_group_get_ref(nl, group);
201
202 assert(n_ref > 0);
203
204 n_ref--;
205
206 r = broadcast_group_set_ref(nl, group, n_ref);
207 if (r < 0)
208 return r;
209
210 if (n_ref > 0)
211 /* still refs left */
212 return 0;
213
214 r = broadcast_group_leave(nl, group);
215 if (r < 0)
216 return r;
217
218 return 0;
219 }
220
221 /* returns the number of bytes sent, or a negative error code */
222 int socket_write_message(sd_netlink *nl, sd_netlink_message *m) {
223 union {
224 struct sockaddr sa;
225 struct sockaddr_nl nl;
226 } addr = {
227 .nl.nl_family = AF_NETLINK,
228 };
229 ssize_t k;
230
231 assert(nl);
232 assert(m);
233 assert(m->hdr);
234
235 k = sendto(nl->fd, m->hdr, m->hdr->nlmsg_len,
236 0, &addr.sa, sizeof(addr));
237 if (k < 0)
238 return -errno;
239
240 return k;
241 }
242
243 static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool peek) {
244 union sockaddr_union sender;
245 uint8_t cmsg_buffer[CMSG_SPACE(sizeof(struct nl_pktinfo))];
246 struct msghdr msg = {
247 .msg_iov = iov,
248 .msg_iovlen = 1,
249 .msg_name = &sender,
250 .msg_namelen = sizeof(sender),
251 .msg_control = cmsg_buffer,
252 .msg_controllen = sizeof(cmsg_buffer),
253 };
254 struct cmsghdr *cmsg;
255 uint32_t group = 0;
256 ssize_t n;
257
258 assert(fd >= 0);
259 assert(iov);
260
261 n = recvmsg(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0));
262 if (n < 0) {
263 /* no data */
264 if (errno == ENOBUFS)
265 log_debug("rtnl: kernel receive buffer overrun");
266 else if (errno == EAGAIN)
267 log_debug("rtnl: no data in socket");
268
269 return IN_SET(errno, EAGAIN, EINTR) ? 0 : -errno;
270 }
271
272 if (sender.nl.nl_pid != 0) {
273 /* not from the kernel, ignore */
274 log_debug("rtnl: ignoring message from portid %"PRIu32, sender.nl.nl_pid);
275
276 if (peek) {
277 /* drop the message */
278 n = recvmsg(fd, &msg, 0);
279 if (n < 0)
280 return IN_SET(errno, EAGAIN, EINTR) ? 0 : -errno;
281 }
282
283 return 0;
284 }
285
286 CMSG_FOREACH(cmsg, &msg) {
287 if (cmsg->cmsg_level == SOL_NETLINK &&
288 cmsg->cmsg_type == NETLINK_PKTINFO &&
289 cmsg->cmsg_len == CMSG_LEN(sizeof(struct nl_pktinfo))) {
290 struct nl_pktinfo *pktinfo = (void *)CMSG_DATA(cmsg);
291
292 /* multi-cast group */
293 group = pktinfo->group;
294 }
295 }
296
297 if (_group)
298 *_group = group;
299
300 return (int) n;
301 }
302
303 /* On success, the number of bytes received is returned and *ret points to the received message
304 * which has a valid header and the correct size.
305 * If nothing useful was received 0 is returned.
306 * On failure, a negative error code is returned.
307 */
308 int socket_read_message(sd_netlink *rtnl) {
309 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *first = NULL;
310 struct iovec iov = {};
311 uint32_t group = 0;
312 bool multi_part = false, done = false;
313 struct nlmsghdr *new_msg;
314 size_t len;
315 int r;
316 unsigned i = 0;
317 const NLTypeSystem *type_system_root;
318
319 assert(rtnl);
320 assert(rtnl->rbuffer);
321 assert(rtnl->rbuffer_allocated >= sizeof(struct nlmsghdr));
322
323 type_system_root = type_system_get_root(rtnl->protocol);
324
325 /* read nothing, just get the pending message size */
326 r = socket_recv_message(rtnl->fd, &iov, NULL, true);
327 if (r <= 0)
328 return r;
329 else
330 len = (size_t) r;
331
332 /* make room for the pending message */
333 if (!greedy_realloc((void **)&rtnl->rbuffer,
334 &rtnl->rbuffer_allocated,
335 len, sizeof(uint8_t)))
336 return -ENOMEM;
337
338 iov = IOVEC_MAKE(rtnl->rbuffer, rtnl->rbuffer_allocated);
339
340 /* read the pending message */
341 r = socket_recv_message(rtnl->fd, &iov, &group, false);
342 if (r <= 0)
343 return r;
344 else
345 len = (size_t) r;
346
347 if (len > rtnl->rbuffer_allocated)
348 /* message did not fit in read buffer */
349 return -EIO;
350
351 if (NLMSG_OK(rtnl->rbuffer, len) && rtnl->rbuffer->nlmsg_flags & NLM_F_MULTI) {
352 multi_part = true;
353
354 for (i = 0; i < rtnl->rqueue_partial_size; i++) {
355 if (rtnl_message_get_serial(rtnl->rqueue_partial[i]) ==
356 rtnl->rbuffer->nlmsg_seq) {
357 first = rtnl->rqueue_partial[i];
358 break;
359 }
360 }
361 }
362
363 for (new_msg = rtnl->rbuffer; NLMSG_OK(new_msg, len) && !done; new_msg = NLMSG_NEXT(new_msg, len)) {
364 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
365 const NLType *nl_type;
366
367 if (!group && new_msg->nlmsg_pid != rtnl->sockaddr.nl.nl_pid)
368 /* not broadcast and not for us */
369 continue;
370
371 if (new_msg->nlmsg_type == NLMSG_NOOP)
372 /* silently drop noop messages */
373 continue;
374
375 if (new_msg->nlmsg_type == NLMSG_DONE) {
376 /* finished reading multi-part message */
377 done = true;
378
379 /* if first is not defined, put NLMSG_DONE into the receive queue. */
380 if (first)
381 continue;
382 }
383
384 /* check that we support this message type */
385 r = type_system_get_type(type_system_root, &nl_type, new_msg->nlmsg_type);
386 if (r < 0) {
387 if (r == -EOPNOTSUPP)
388 log_debug("sd-netlink: ignored message with unknown type: %i",
389 new_msg->nlmsg_type);
390
391 continue;
392 }
393
394 /* check that the size matches the message type */
395 if (new_msg->nlmsg_len < NLMSG_LENGTH(type_get_size(nl_type))) {
396 log_debug("sd-netlink: message larger than expected, dropping");
397 continue;
398 }
399
400 r = message_new_empty(rtnl, &m);
401 if (r < 0)
402 return r;
403
404 m->broadcast = !!group;
405
406 m->hdr = memdup(new_msg, new_msg->nlmsg_len);
407 if (!m->hdr)
408 return -ENOMEM;
409
410 /* seal and parse the top-level message */
411 r = sd_netlink_message_rewind(m);
412 if (r < 0)
413 return r;
414
415 /* push the message onto the multi-part message stack */
416 if (first)
417 m->next = first;
418 first = TAKE_PTR(m);
419 }
420
421 if (len > 0)
422 log_debug("sd-netlink: discarding %zu bytes of incoming message", len);
423
424 if (!first)
425 return 0;
426
427 if (!multi_part || done) {
428 /* we got a complete message, push it on the read queue */
429 r = rtnl_rqueue_make_room(rtnl);
430 if (r < 0)
431 return r;
432
433 rtnl->rqueue[rtnl->rqueue_size++] = TAKE_PTR(first);
434
435 if (multi_part && (i < rtnl->rqueue_partial_size)) {
436 /* remove the message form the partial read queue */
437 memmove(rtnl->rqueue_partial + i,rtnl->rqueue_partial + i + 1,
438 sizeof(sd_netlink_message*) * (rtnl->rqueue_partial_size - i - 1));
439 rtnl->rqueue_partial_size--;
440 }
441
442 return 1;
443 } else {
444 /* we only got a partial multi-part message, push it on the
445 partial read queue */
446 if (i < rtnl->rqueue_partial_size)
447 rtnl->rqueue_partial[i] = TAKE_PTR(first);
448 else {
449 r = rtnl_rqueue_partial_make_room(rtnl);
450 if (r < 0)
451 return r;
452
453 rtnl->rqueue_partial[rtnl->rqueue_partial_size++] = TAKE_PTR(first);
454 }
455
456 return 0;
457 }
458 }