]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-netlink/netlink-socket.c
Merge pull request #11827 from keszybz/pkgconfig-variables
[thirdparty/systemd.git] / src / libsystemd / sd-netlink / netlink-socket.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
89489ef7
TG
2
3#include <netinet/in.h>
4#include <stdbool.h>
5#include <unistd.h>
6
07630cea
LP
7#include "sd-netlink.h"
8
b5efdb8a 9#include "alloc-util.h"
7fe2903c 10#include "fd-util.h"
f97b34a6 11#include "format-util.h"
cb310866 12#include "io-util.h"
89489ef7 13#include "missing.h"
89489ef7
TG
14#include "netlink-internal.h"
15#include "netlink-types.h"
07630cea
LP
16#include "netlink-util.h"
17#include "refcnt.h"
18#include "socket-util.h"
19#include "util.h"
89489ef7 20
b95cc756
TG
21int 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
7fe2903c 28 return fd_move_above_stdio(fd);
b95cc756
TG
29}
30
9c5a882b
TG
31static 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);
f78bc916 38 assert(nl->fd >= 0);
9c5a882b
TG
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++) {
313cefa1 70 for (j = 0; j < sizeof(uint32_t) * 8; j++) {
9c5a882b
TG
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
b95cc756
TG
90int socket_bind(sd_netlink *nl) {
91 socklen_t addrlen;
6d5e65f6 92 int r;
b95cc756 93
2ff48e98 94 r = setsockopt_int(nl->fd, SOL_NETLINK, NETLINK_PKTINFO, true);
b95cc756 95 if (r < 0)
2ff48e98 96 return r;
b95cc756
TG
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
9c5a882b
TG
109 r = broadcast_groups_get(nl);
110 if (r < 0)
111 return r;
112
b95cc756
TG
113 return 0;
114}
115
9c5a882b
TG
116static 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
122static 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}
b95cc756 133
9c5a882b 134static int broadcast_group_join(sd_netlink *nl, unsigned group) {
b95cc756
TG
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
9c5a882b
TG
148int 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
313cefa1 156 n_ref++;
9c5a882b
TG
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
177static 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
194int 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
313cefa1 204 n_ref--;
9c5a882b
TG
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
89489ef7
TG
221/* returns the number of bytes sent, or a negative error code */
222int 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
243static 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;
f1dd72c2 256 ssize_t n;
89489ef7
TG
257
258 assert(fd >= 0);
259 assert(iov);
260
f1dd72c2
LP
261 n = recvmsg(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0));
262 if (n < 0) {
89489ef7
TG
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
71994cff 269 return IN_SET(errno, EAGAIN, EINTR) ? 0 : -errno;
89489ef7
TG
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 */
f1dd72c2
LP
278 n = recvmsg(fd, &msg, 0);
279 if (n < 0)
71994cff 280 return IN_SET(errno, EAGAIN, EINTR) ? 0 : -errno;
89489ef7
TG
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
f1dd72c2 300 return (int) n;
89489ef7
TG
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 */
308int socket_read_message(sd_netlink *rtnl) {
4afd3348 309 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *first = NULL;
89489ef7
TG
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;
05d0c2e3 317 const NLTypeSystem *type_system_root;
89489ef7
TG
318
319 assert(rtnl);
320 assert(rtnl->rbuffer);
321 assert(rtnl->rbuffer_allocated >= sizeof(struct nlmsghdr));
322
05d0c2e3
JT
323 type_system_root = type_system_get_root(rtnl->protocol);
324
89489ef7
TG
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
f1dd72c2 330 len = (size_t) r;
89489ef7
TG
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
cb310866 338 iov = IOVEC_MAKE(rtnl->rbuffer, rtnl->rbuffer_allocated);
89489ef7
TG
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
f1dd72c2 345 len = (size_t) r;
89489ef7
TG
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)) {
4afd3348 364 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
89489ef7
TG
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 */
05d0c2e3 385 r = type_system_get_type(type_system_root, &nl_type, new_msg->nlmsg_type);
89489ef7
TG
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 */
817d1cd8 395 if (new_msg->nlmsg_len < NLMSG_LENGTH(type_get_size(nl_type))) {
89489ef7
TG
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;
1cc6c93a 418 first = TAKE_PTR(m);
89489ef7
TG
419 }
420
f1dd72c2 421 if (len > 0)
89489ef7
TG
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
1cc6c93a 433 rtnl->rqueue[rtnl->rqueue_size++] = TAKE_PTR(first);
89489ef7
TG
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));
313cefa1 439 rtnl->rqueue_partial_size--;
89489ef7
TG
440 }
441
442 return 1;
443 } else {
444 /* we only got a partial multi-part message, push it on the
445 partial read queue */
f1dd72c2 446 if (i < rtnl->rqueue_partial_size)
1cc6c93a 447 rtnl->rqueue_partial[i] = TAKE_PTR(first);
f1dd72c2 448 else {
89489ef7
TG
449 r = rtnl_rqueue_partial_make_room(rtnl);
450 if (r < 0)
451 return r;
452
1cc6c93a 453 rtnl->rqueue_partial[rtnl->rqueue_partial_size++] = TAKE_PTR(first);
89489ef7 454 }
89489ef7
TG
455
456 return 0;
457 }
458}