]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-netlink/sd-netlink.c
Merge pull request #29332 from esposem/ukify_simplify
[thirdparty/systemd.git] / src / libsystemd / sd-netlink / sd-netlink.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <poll.h>
4
5 #include "sd-netlink.h"
6
7 #include "alloc-util.h"
8 #include "fd-util.h"
9 #include "hashmap.h"
10 #include "io-util.h"
11 #include "macro.h"
12 #include "netlink-genl.h"
13 #include "netlink-internal.h"
14 #include "netlink-slot.h"
15 #include "netlink-util.h"
16 #include "process-util.h"
17 #include "socket-util.h"
18 #include "string-util.h"
19
20 /* Some really high limit, to catch programming errors */
21 #define REPLY_CALLBACKS_MAX UINT16_MAX
22
23 static int netlink_new(sd_netlink **ret) {
24 _cleanup_(sd_netlink_unrefp) sd_netlink *nl = NULL;
25
26 assert_return(ret, -EINVAL);
27
28 nl = new(sd_netlink, 1);
29 if (!nl)
30 return -ENOMEM;
31
32 *nl = (sd_netlink) {
33 .n_ref = 1,
34 .fd = -EBADF,
35 .sockaddr.nl.nl_family = AF_NETLINK,
36 .original_pid = getpid_cached(),
37 .protocol = -1,
38
39 /* Kernel change notification messages have sequence number 0. We want to avoid that with our
40 * own serials, in order not to get confused when matching up kernel replies to our earlier
41 * requests.
42 *
43 * Moreover, when using netlink socket activation (i.e. where PID 1 binds an AF_NETLINK
44 * socket for us and passes it to us across execve()) and we get restarted multiple times
45 * while the socket sticks around we might get confused by replies from earlier runs coming
46 * in late — which is pretty likely if we'd start our sequence numbers always from 1. Hence,
47 * let's start with a value based on the system clock. This should make collisions much less
48 * likely (though still theoretically possible). We use a 32 bit μs counter starting at boot
49 * for this (and explicitly exclude the zero, see above). This counter will wrap around after
50 * a bit more than 1h, but that's hopefully OK as the kernel shouldn't take that long to
51 * reply to our requests.
52 *
53 * We only pick the initial start value this way. For each message we simply increase the
54 * sequence number by 1. This means we could enqueue 1 netlink message per μs without risking
55 * collisions, which should be OK.
56 *
57 * Note this means the serials will be in the range 1…UINT32_MAX here.
58 *
59 * (In an ideal world we'd attach the current serial counter to the netlink socket itself
60 * somehow, to avoid all this, but I couldn't come up with a nice way to do this) */
61 .serial = (uint32_t) (now(CLOCK_MONOTONIC) % UINT32_MAX) + 1,
62 };
63
64 *ret = TAKE_PTR(nl);
65 return 0;
66 }
67
68 int sd_netlink_open_fd(sd_netlink **ret, int fd) {
69 _cleanup_(sd_netlink_unrefp) sd_netlink *nl = NULL;
70 int r, protocol;
71
72 assert_return(ret, -EINVAL);
73 assert_return(fd >= 0, -EBADF);
74
75 r = netlink_new(&nl);
76 if (r < 0)
77 return r;
78
79 r = getsockopt_int(fd, SOL_SOCKET, SO_PROTOCOL, &protocol);
80 if (r < 0)
81 return r;
82
83 nl->fd = fd;
84 nl->protocol = protocol;
85
86 r = setsockopt_int(fd, SOL_NETLINK, NETLINK_EXT_ACK, true);
87 if (r < 0)
88 log_debug_errno(r, "sd-netlink: Failed to enable NETLINK_EXT_ACK option, ignoring: %m");
89
90 r = setsockopt_int(fd, SOL_NETLINK, NETLINK_GET_STRICT_CHK, true);
91 if (r < 0)
92 log_debug_errno(r, "sd-netlink: Failed to enable NETLINK_GET_STRICT_CHK option, ignoring: %m");
93
94 r = socket_bind(nl);
95 if (r < 0) {
96 nl->fd = -EBADF; /* on failure, the caller remains owner of the fd, hence don't close it here */
97 nl->protocol = -1;
98 return r;
99 }
100
101 *ret = TAKE_PTR(nl);
102
103 return 0;
104 }
105
106 int sd_netlink_open(sd_netlink **ret) {
107 return netlink_open_family(ret, NETLINK_ROUTE);
108 }
109
110 int sd_netlink_increase_rxbuf(sd_netlink *nl, size_t size) {
111 assert_return(nl, -EINVAL);
112 assert_return(!netlink_pid_changed(nl), -ECHILD);
113
114 return fd_increase_rxbuf(nl->fd, size);
115 }
116
117 static sd_netlink *netlink_free(sd_netlink *nl) {
118 sd_netlink_slot *s;
119
120 assert(nl);
121
122 ordered_set_free(nl->rqueue);
123 hashmap_free(nl->rqueue_by_serial);
124 hashmap_free(nl->rqueue_partial_by_serial);
125 free(nl->rbuffer);
126
127 while ((s = nl->slots)) {
128 assert(s->floating);
129 netlink_slot_disconnect(s, true);
130 }
131 hashmap_free(nl->reply_callbacks);
132 prioq_free(nl->reply_callbacks_prioq);
133
134 sd_event_source_unref(nl->io_event_source);
135 sd_event_source_unref(nl->time_event_source);
136 sd_event_unref(nl->event);
137
138 hashmap_free(nl->broadcast_group_refs);
139
140 genl_clear_family(nl);
141
142 safe_close(nl->fd);
143 return mfree(nl);
144 }
145
146 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_netlink, sd_netlink, netlink_free);
147
148 int sd_netlink_send(
149 sd_netlink *nl,
150 sd_netlink_message *message,
151 uint32_t *serial) {
152
153 int r;
154
155 assert_return(nl, -EINVAL);
156 assert_return(!netlink_pid_changed(nl), -ECHILD);
157 assert_return(message, -EINVAL);
158 assert_return(!message->sealed, -EPERM);
159
160 netlink_seal_message(nl, message);
161
162 r = socket_write_message(nl, message);
163 if (r < 0)
164 return r;
165
166 if (serial)
167 *serial = message_get_serial(message);
168
169 return 1;
170 }
171
172 static int dispatch_rqueue(sd_netlink *nl, sd_netlink_message **ret) {
173 sd_netlink_message *m;
174 int r;
175
176 assert(nl);
177 assert(ret);
178
179 if (ordered_set_size(nl->rqueue) <= 0) {
180 /* Try to read a new message */
181 r = socket_read_message(nl);
182 if (r == -ENOBUFS) /* FIXME: ignore buffer overruns for now */
183 log_debug_errno(r, "sd-netlink: Got ENOBUFS from netlink socket, ignoring.");
184 else if (r < 0)
185 return r;
186 }
187
188 /* Dispatch a queued message */
189 m = ordered_set_steal_first(nl->rqueue);
190 if (m)
191 sd_netlink_message_unref(hashmap_remove_value(nl->rqueue_by_serial, UINT32_TO_PTR(message_get_serial(m)), m));
192 *ret = m;
193 return !!m;
194 }
195
196 static int process_timeout(sd_netlink *nl) {
197 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
198 struct reply_callback *c;
199 sd_netlink_slot *slot;
200 usec_t n;
201 int r;
202
203 assert(nl);
204
205 c = prioq_peek(nl->reply_callbacks_prioq);
206 if (!c)
207 return 0;
208
209 n = now(CLOCK_MONOTONIC);
210 if (c->timeout > n)
211 return 0;
212
213 r = message_new_synthetic_error(nl, -ETIMEDOUT, c->serial, &m);
214 if (r < 0)
215 return r;
216
217 assert_se(prioq_pop(nl->reply_callbacks_prioq) == c);
218 hashmap_remove(nl->reply_callbacks, UINT32_TO_PTR(c->serial));
219
220 slot = container_of(c, sd_netlink_slot, reply_callback);
221
222 r = c->callback(nl, m, slot->userdata);
223 if (r < 0)
224 log_debug_errno(r, "sd-netlink: timedout callback %s%s%sfailed: %m",
225 slot->description ? "'" : "",
226 strempty(slot->description),
227 slot->description ? "' " : "");
228
229 if (slot->floating)
230 netlink_slot_disconnect(slot, true);
231
232 return 1;
233 }
234
235 static int process_reply(sd_netlink *nl, sd_netlink_message *m) {
236 struct reply_callback *c;
237 sd_netlink_slot *slot;
238 uint32_t serial;
239 uint16_t type;
240 int r;
241
242 assert(nl);
243 assert(m);
244
245 serial = message_get_serial(m);
246 c = hashmap_remove(nl->reply_callbacks, UINT32_TO_PTR(serial));
247 if (!c)
248 return 0;
249
250 if (c->timeout != USEC_INFINITY)
251 prioq_remove(nl->reply_callbacks_prioq, c, &c->prioq_idx);
252
253 r = sd_netlink_message_get_type(m, &type);
254 if (r < 0)
255 return r;
256
257 if (type == NLMSG_DONE)
258 m = NULL;
259
260 slot = container_of(c, sd_netlink_slot, reply_callback);
261
262 r = c->callback(nl, m, slot->userdata);
263 if (r < 0)
264 log_debug_errno(r, "sd-netlink: reply callback %s%s%sfailed: %m",
265 slot->description ? "'" : "",
266 strempty(slot->description),
267 slot->description ? "' " : "");
268
269 if (slot->floating)
270 netlink_slot_disconnect(slot, true);
271
272 return 1;
273 }
274
275 static int process_match(sd_netlink *nl, sd_netlink_message *m) {
276 uint16_t type;
277 uint8_t cmd;
278 int r;
279
280 assert(nl);
281 assert(m);
282
283 r = sd_netlink_message_get_type(m, &type);
284 if (r < 0)
285 return r;
286
287 if (m->protocol == NETLINK_GENERIC) {
288 r = sd_genl_message_get_command(nl, m, &cmd);
289 if (r < 0)
290 return r;
291 } else
292 cmd = 0;
293
294 LIST_FOREACH(match_callbacks, c, nl->match_callbacks) {
295 sd_netlink_slot *slot;
296 bool found = false;
297
298 if (c->type != type)
299 continue;
300 if (c->cmd != 0 && c->cmd != cmd)
301 continue;
302
303 for (size_t i = 0; i < c->n_groups; i++)
304 if (c->groups[i] == m->multicast_group) {
305 found = true;
306 break;
307 }
308
309 if (!found)
310 continue;
311
312 slot = container_of(c, sd_netlink_slot, match_callback);
313
314 r = c->callback(nl, m, slot->userdata);
315 if (r < 0)
316 log_debug_errno(r, "sd-netlink: match callback %s%s%sfailed: %m",
317 slot->description ? "'" : "",
318 strempty(slot->description),
319 slot->description ? "' " : "");
320 if (r != 0)
321 break;
322 }
323
324 return 1;
325 }
326
327 static int process_running(sd_netlink *nl, sd_netlink_message **ret) {
328 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
329 int r;
330
331 assert(nl);
332
333 r = process_timeout(nl);
334 if (r != 0)
335 goto null_message;
336
337 r = dispatch_rqueue(nl, &m);
338 if (r < 0)
339 return r;
340 if (!m)
341 goto null_message;
342
343 if (sd_netlink_message_is_broadcast(m))
344 r = process_match(nl, m);
345 else
346 r = process_reply(nl, m);
347 if (r != 0)
348 goto null_message;
349
350 if (ret) {
351 *ret = TAKE_PTR(m);
352
353 return 1;
354 }
355
356 return 1;
357
358 null_message:
359 if (r >= 0 && ret)
360 *ret = NULL;
361
362 return r;
363 }
364
365 int sd_netlink_process(sd_netlink *nl, sd_netlink_message **ret) {
366 NETLINK_DONT_DESTROY(nl);
367 int r;
368
369 assert_return(nl, -EINVAL);
370 assert_return(!netlink_pid_changed(nl), -ECHILD);
371 assert_return(!nl->processing, -EBUSY);
372
373 nl->processing = true;
374 r = process_running(nl, ret);
375 nl->processing = false;
376
377 return r;
378 }
379
380 static usec_t timespan_to_timestamp(usec_t usec) {
381 static bool default_timeout_set = false;
382 static usec_t default_timeout;
383 int r;
384
385 if (usec == 0) {
386 if (!default_timeout_set) {
387 const char *e;
388
389 default_timeout_set = true;
390 default_timeout = NETLINK_DEFAULT_TIMEOUT_USEC;
391
392 e = secure_getenv("SYSTEMD_NETLINK_DEFAULT_TIMEOUT");
393 if (e) {
394 r = parse_sec(e, &default_timeout);
395 if (r < 0)
396 log_debug_errno(r, "sd-netlink: Failed to parse $SYSTEMD_NETLINK_DEFAULT_TIMEOUT environment variable, ignoring: %m");
397 }
398 }
399
400 usec = default_timeout;
401 }
402
403 return usec_add(now(CLOCK_MONOTONIC), usec);
404 }
405
406 static int netlink_poll(sd_netlink *nl, bool need_more, usec_t timeout_usec) {
407 usec_t m = USEC_INFINITY;
408 int r, e;
409
410 assert(nl);
411
412 e = sd_netlink_get_events(nl);
413 if (e < 0)
414 return e;
415
416 if (need_more)
417 /* Caller wants more data, and doesn't care about
418 * what's been read or any other timeouts. */
419 e |= POLLIN;
420 else {
421 usec_t until;
422
423 /* Caller wants to process if there is something to
424 * process, but doesn't care otherwise */
425
426 r = sd_netlink_get_timeout(nl, &until);
427 if (r < 0)
428 return r;
429
430 m = usec_sub_unsigned(until, now(CLOCK_MONOTONIC));
431 }
432
433 r = fd_wait_for_event(nl->fd, e, MIN(m, timeout_usec));
434 if (r <= 0)
435 return r;
436
437 return 1;
438 }
439
440 int sd_netlink_wait(sd_netlink *nl, uint64_t timeout_usec) {
441 int r;
442
443 assert_return(nl, -EINVAL);
444 assert_return(!netlink_pid_changed(nl), -ECHILD);
445
446 if (ordered_set_size(nl->rqueue) > 0)
447 return 0;
448
449 r = netlink_poll(nl, false, timeout_usec);
450 if (ERRNO_IS_NEG_TRANSIENT(r)) /* Convert EINTR to "something happened" and give user a chance to run some code before calling back into us */
451 return 1;
452 return r;
453 }
454
455 static int timeout_compare(const void *a, const void *b) {
456 const struct reply_callback *x = a, *y = b;
457
458 return CMP(x->timeout, y->timeout);
459 }
460
461 int sd_netlink_call_async(
462 sd_netlink *nl,
463 sd_netlink_slot **ret_slot,
464 sd_netlink_message *m,
465 sd_netlink_message_handler_t callback,
466 sd_netlink_destroy_t destroy_callback,
467 void *userdata,
468 uint64_t usec,
469 const char *description) {
470
471 _cleanup_free_ sd_netlink_slot *slot = NULL;
472 int r, k;
473
474 assert_return(nl, -EINVAL);
475 assert_return(m, -EINVAL);
476 assert_return(callback, -EINVAL);
477 assert_return(!netlink_pid_changed(nl), -ECHILD);
478
479 if (hashmap_size(nl->reply_callbacks) >= REPLY_CALLBACKS_MAX)
480 return -ERANGE;
481
482 r = hashmap_ensure_allocated(&nl->reply_callbacks, &trivial_hash_ops);
483 if (r < 0)
484 return r;
485
486 if (usec != UINT64_MAX) {
487 r = prioq_ensure_allocated(&nl->reply_callbacks_prioq, timeout_compare);
488 if (r < 0)
489 return r;
490 }
491
492 r = netlink_slot_allocate(nl, !ret_slot, NETLINK_REPLY_CALLBACK, sizeof(struct reply_callback), userdata, description, &slot);
493 if (r < 0)
494 return r;
495
496 slot->reply_callback.callback = callback;
497 slot->reply_callback.timeout = timespan_to_timestamp(usec);
498
499 k = sd_netlink_send(nl, m, &slot->reply_callback.serial);
500 if (k < 0)
501 return k;
502
503 r = hashmap_put(nl->reply_callbacks, UINT32_TO_PTR(slot->reply_callback.serial), &slot->reply_callback);
504 if (r < 0)
505 return r;
506
507 if (slot->reply_callback.timeout != USEC_INFINITY) {
508 r = prioq_put(nl->reply_callbacks_prioq, &slot->reply_callback, &slot->reply_callback.prioq_idx);
509 if (r < 0) {
510 (void) hashmap_remove(nl->reply_callbacks, UINT32_TO_PTR(slot->reply_callback.serial));
511 return r;
512 }
513 }
514
515 /* Set this at last. Otherwise, some failures in above would call destroy_callback but some would not. */
516 slot->destroy_callback = destroy_callback;
517
518 if (ret_slot)
519 *ret_slot = slot;
520
521 TAKE_PTR(slot);
522
523 return k;
524 }
525
526 int sd_netlink_read(
527 sd_netlink *nl,
528 uint32_t serial,
529 uint64_t usec,
530 sd_netlink_message **ret) {
531
532 usec_t timeout;
533 int r;
534
535 assert_return(nl, -EINVAL);
536 assert_return(!netlink_pid_changed(nl), -ECHILD);
537
538 timeout = timespan_to_timestamp(usec);
539
540 for (;;) {
541 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
542 usec_t left;
543
544 m = hashmap_remove(nl->rqueue_by_serial, UINT32_TO_PTR(serial));
545 if (m) {
546 uint16_t type;
547
548 /* found a match, remove from rqueue and return it */
549 sd_netlink_message_unref(ordered_set_remove(nl->rqueue, m));
550
551 r = sd_netlink_message_get_errno(m);
552 if (r < 0)
553 return r;
554
555 r = sd_netlink_message_get_type(m, &type);
556 if (r < 0)
557 return r;
558
559 if (type == NLMSG_DONE) {
560 if (ret)
561 *ret = NULL;
562 return 0;
563 }
564
565 if (ret)
566 *ret = TAKE_PTR(m);
567 return 1;
568 }
569
570 r = socket_read_message(nl);
571 if (r < 0)
572 return r;
573 if (r > 0)
574 /* received message, so try to process straight away */
575 continue;
576
577 if (timeout != USEC_INFINITY) {
578 usec_t n;
579
580 n = now(CLOCK_MONOTONIC);
581 if (n >= timeout)
582 return -ETIMEDOUT;
583
584 left = usec_sub_unsigned(timeout, n);
585 } else
586 left = USEC_INFINITY;
587
588 r = netlink_poll(nl, true, left);
589 if (r < 0)
590 return r;
591 if (r == 0)
592 return -ETIMEDOUT;
593 }
594 }
595
596 int sd_netlink_call(
597 sd_netlink *nl,
598 sd_netlink_message *message,
599 uint64_t usec,
600 sd_netlink_message **ret) {
601
602 uint32_t serial;
603 int r;
604
605 assert_return(nl, -EINVAL);
606 assert_return(!netlink_pid_changed(nl), -ECHILD);
607 assert_return(message, -EINVAL);
608
609 r = sd_netlink_send(nl, message, &serial);
610 if (r < 0)
611 return r;
612
613 return sd_netlink_read(nl, serial, usec, ret);
614 }
615
616 int sd_netlink_get_events(sd_netlink *nl) {
617 assert_return(nl, -EINVAL);
618 assert_return(!netlink_pid_changed(nl), -ECHILD);
619
620 return ordered_set_size(nl->rqueue) == 0 ? POLLIN : 0;
621 }
622
623 int sd_netlink_get_timeout(sd_netlink *nl, uint64_t *timeout_usec) {
624 struct reply_callback *c;
625
626 assert_return(nl, -EINVAL);
627 assert_return(timeout_usec, -EINVAL);
628 assert_return(!netlink_pid_changed(nl), -ECHILD);
629
630 if (ordered_set_size(nl->rqueue) > 0) {
631 *timeout_usec = 0;
632 return 1;
633 }
634
635 c = prioq_peek(nl->reply_callbacks_prioq);
636 if (!c) {
637 *timeout_usec = UINT64_MAX;
638 return 0;
639 }
640
641 *timeout_usec = c->timeout;
642 return 1;
643 }
644
645 static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
646 sd_netlink *nl = ASSERT_PTR(userdata);
647 int r;
648
649 r = sd_netlink_process(nl, NULL);
650 if (r < 0)
651 return r;
652
653 return 1;
654 }
655
656 static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) {
657 sd_netlink *nl = ASSERT_PTR(userdata);
658 int r;
659
660 r = sd_netlink_process(nl, NULL);
661 if (r < 0)
662 return r;
663
664 return 1;
665 }
666
667 static int prepare_callback(sd_event_source *s, void *userdata) {
668 sd_netlink *nl = ASSERT_PTR(userdata);
669 int r, enabled;
670 usec_t until;
671
672 assert(s);
673
674 r = sd_netlink_get_events(nl);
675 if (r < 0)
676 return r;
677
678 r = sd_event_source_set_io_events(nl->io_event_source, r);
679 if (r < 0)
680 return r;
681
682 enabled = sd_netlink_get_timeout(nl, &until);
683 if (enabled < 0)
684 return enabled;
685 if (enabled > 0) {
686 r = sd_event_source_set_time(nl->time_event_source, until);
687 if (r < 0)
688 return r;
689 }
690
691 r = sd_event_source_set_enabled(nl->time_event_source,
692 enabled > 0 ? SD_EVENT_ONESHOT : SD_EVENT_OFF);
693 if (r < 0)
694 return r;
695
696 return 1;
697 }
698
699 int sd_netlink_attach_event(sd_netlink *nl, sd_event *event, int64_t priority) {
700 int r;
701
702 assert_return(nl, -EINVAL);
703 assert_return(!nl->event, -EBUSY);
704
705 assert(!nl->io_event_source);
706 assert(!nl->time_event_source);
707
708 if (event)
709 nl->event = sd_event_ref(event);
710 else {
711 r = sd_event_default(&nl->event);
712 if (r < 0)
713 return r;
714 }
715
716 r = sd_event_add_io(nl->event, &nl->io_event_source, nl->fd, 0, io_callback, nl);
717 if (r < 0)
718 goto fail;
719
720 r = sd_event_source_set_priority(nl->io_event_source, priority);
721 if (r < 0)
722 goto fail;
723
724 r = sd_event_source_set_description(nl->io_event_source, "netlink-receive-message");
725 if (r < 0)
726 goto fail;
727
728 r = sd_event_source_set_prepare(nl->io_event_source, prepare_callback);
729 if (r < 0)
730 goto fail;
731
732 r = sd_event_add_time(nl->event, &nl->time_event_source, CLOCK_MONOTONIC, 0, 0, time_callback, nl);
733 if (r < 0)
734 goto fail;
735
736 r = sd_event_source_set_priority(nl->time_event_source, priority);
737 if (r < 0)
738 goto fail;
739
740 r = sd_event_source_set_description(nl->time_event_source, "netlink-timer");
741 if (r < 0)
742 goto fail;
743
744 return 0;
745
746 fail:
747 sd_netlink_detach_event(nl);
748 return r;
749 }
750
751 int sd_netlink_detach_event(sd_netlink *nl) {
752 assert_return(nl, -EINVAL);
753 assert_return(nl->event, -ENXIO);
754
755 nl->io_event_source = sd_event_source_unref(nl->io_event_source);
756
757 nl->time_event_source = sd_event_source_unref(nl->time_event_source);
758
759 nl->event = sd_event_unref(nl->event);
760
761 return 0;
762 }
763
764 int netlink_add_match_internal(
765 sd_netlink *nl,
766 sd_netlink_slot **ret_slot,
767 const uint32_t *groups,
768 size_t n_groups,
769 uint16_t type,
770 uint8_t cmd,
771 sd_netlink_message_handler_t callback,
772 sd_netlink_destroy_t destroy_callback,
773 void *userdata,
774 const char *description) {
775
776 _cleanup_free_ sd_netlink_slot *slot = NULL;
777 int r;
778
779 assert(groups);
780 assert(n_groups > 0);
781
782 for (size_t i = 0; i < n_groups; i++) {
783 r = socket_broadcast_group_ref(nl, groups[i]);
784 if (r < 0)
785 return r;
786 }
787
788 r = netlink_slot_allocate(nl, !ret_slot, NETLINK_MATCH_CALLBACK, sizeof(struct match_callback),
789 userdata, description, &slot);
790 if (r < 0)
791 return r;
792
793 slot->match_callback.groups = newdup(uint32_t, groups, n_groups);
794 if (!slot->match_callback.groups)
795 return -ENOMEM;
796
797 slot->match_callback.n_groups = n_groups;
798 slot->match_callback.callback = callback;
799 slot->match_callback.type = type;
800 slot->match_callback.cmd = cmd;
801
802 LIST_PREPEND(match_callbacks, nl->match_callbacks, &slot->match_callback);
803
804 /* Set this at last. Otherwise, some failures in above call the destroy callback but some do not. */
805 slot->destroy_callback = destroy_callback;
806
807 if (ret_slot)
808 *ret_slot = slot;
809
810 TAKE_PTR(slot);
811 return 0;
812 }
813
814 int sd_netlink_add_match(
815 sd_netlink *rtnl,
816 sd_netlink_slot **ret_slot,
817 uint16_t type,
818 sd_netlink_message_handler_t callback,
819 sd_netlink_destroy_t destroy_callback,
820 void *userdata,
821 const char *description) {
822
823 static const uint32_t
824 address_groups[] = { RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR, },
825 link_groups[] = { RTNLGRP_LINK, },
826 neighbor_groups[] = { RTNLGRP_NEIGH, },
827 nexthop_groups[] = { RTNLGRP_NEXTHOP, },
828 route_groups[] = { RTNLGRP_IPV4_ROUTE, RTNLGRP_IPV6_ROUTE, },
829 rule_groups[] = { RTNLGRP_IPV4_RULE, RTNLGRP_IPV6_RULE, },
830 tc_groups[] = { RTNLGRP_TC };
831 const uint32_t *groups;
832 size_t n_groups;
833
834 assert_return(rtnl, -EINVAL);
835 assert_return(callback, -EINVAL);
836 assert_return(!netlink_pid_changed(rtnl), -ECHILD);
837
838 switch (type) {
839 case RTM_NEWLINK:
840 case RTM_DELLINK:
841 groups = link_groups;
842 n_groups = ELEMENTSOF(link_groups);
843 break;
844 case RTM_NEWADDR:
845 case RTM_DELADDR:
846 groups = address_groups;
847 n_groups = ELEMENTSOF(address_groups);
848 break;
849 case RTM_NEWNEIGH:
850 case RTM_DELNEIGH:
851 groups = neighbor_groups;
852 n_groups = ELEMENTSOF(neighbor_groups);
853 break;
854 case RTM_NEWROUTE:
855 case RTM_DELROUTE:
856 groups = route_groups;
857 n_groups = ELEMENTSOF(route_groups);
858 break;
859 case RTM_NEWRULE:
860 case RTM_DELRULE:
861 groups = rule_groups;
862 n_groups = ELEMENTSOF(rule_groups);
863 break;
864 case RTM_NEWNEXTHOP:
865 case RTM_DELNEXTHOP:
866 groups = nexthop_groups;
867 n_groups = ELEMENTSOF(nexthop_groups);
868 break;
869 case RTM_NEWQDISC:
870 case RTM_DELQDISC:
871 case RTM_NEWTCLASS:
872 case RTM_DELTCLASS:
873 groups = tc_groups;
874 n_groups = ELEMENTSOF(tc_groups);
875 break;
876 default:
877 return -EOPNOTSUPP;
878 }
879
880 return netlink_add_match_internal(rtnl, ret_slot, groups, n_groups, type, 0, callback,
881 destroy_callback, userdata, description);
882 }
883
884 int sd_netlink_attach_filter(sd_netlink *nl, size_t len, const struct sock_filter *filter) {
885 assert_return(nl, -EINVAL);
886 assert_return(len == 0 || filter, -EINVAL);
887
888 if (setsockopt(nl->fd, SOL_SOCKET,
889 len == 0 ? SO_DETACH_FILTER : SO_ATTACH_FILTER,
890 &(struct sock_fprog) {
891 .len = len,
892 .filter = (struct sock_filter*) filter,
893 }, sizeof(struct sock_fprog)) < 0)
894 return -errno;
895
896 return 0;
897 }