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