]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/libsystemd/sd-netlink/sd-netlink.c
Merge pull request #1668 from ssahani/net1
[thirdparty/systemd.git] / src / libsystemd / sd-netlink / sd-netlink.c
... / ...
CommitLineData
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2013 Tom Gundersen <teg@jklm.no>
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <poll.h>
23#include <sys/socket.h>
24
25#include "sd-netlink.h"
26
27#include "fd-util.h"
28#include "hashmap.h"
29#include "macro.h"
30#include "missing.h"
31#include "netlink-internal.h"
32#include "netlink-util.h"
33#include "util.h"
34
35static int sd_netlink_new(sd_netlink **ret) {
36 _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
37
38 assert_return(ret, -EINVAL);
39
40 rtnl = new0(sd_netlink, 1);
41 if (!rtnl)
42 return -ENOMEM;
43
44 rtnl->n_ref = REFCNT_INIT;
45
46 rtnl->fd = -1;
47
48 rtnl->sockaddr.nl.nl_family = AF_NETLINK;
49
50 rtnl->original_pid = getpid();
51
52 LIST_HEAD_INIT(rtnl->match_callbacks);
53
54 /* We guarantee that the read buffer has at least space for
55 * a message header */
56 if (!greedy_realloc((void**)&rtnl->rbuffer, &rtnl->rbuffer_allocated,
57 sizeof(struct nlmsghdr), sizeof(uint8_t)))
58 return -ENOMEM;
59
60 /* Change notification responses have sequence 0, so we must
61 * start our request sequence numbers at 1, or we may confuse our
62 * responses with notifications from the kernel */
63 rtnl->serial = 1;
64
65 *ret = rtnl;
66 rtnl = NULL;
67
68 return 0;
69}
70
71int sd_netlink_new_from_netlink(sd_netlink **ret, int fd) {
72 _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
73 socklen_t addrlen;
74 int r;
75
76 assert_return(ret, -EINVAL);
77
78 r = sd_netlink_new(&rtnl);
79 if (r < 0)
80 return r;
81
82 addrlen = sizeof(rtnl->sockaddr);
83
84 r = getsockname(fd, &rtnl->sockaddr.sa, &addrlen);
85 if (r < 0)
86 return -errno;
87
88 rtnl->fd = fd;
89
90 *ret = rtnl;
91 rtnl = NULL;
92
93 return 0;
94}
95
96static bool rtnl_pid_changed(sd_netlink *rtnl) {
97 assert(rtnl);
98
99 /* We don't support people creating an rtnl connection and
100 * keeping it around over a fork(). Let's complain. */
101
102 return rtnl->original_pid != getpid();
103}
104
105int sd_netlink_open_fd(sd_netlink **ret, int fd) {
106 _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
107 int r;
108
109 assert_return(ret, -EINVAL);
110 assert_return(fd >= 0, -EBADF);
111
112 r = sd_netlink_new(&rtnl);
113 if (r < 0)
114 return r;
115
116 rtnl->fd = fd;
117
118 r = socket_bind(rtnl);
119 if (r < 0)
120 return r;
121
122 *ret = rtnl;
123 rtnl = NULL;
124
125 return 0;
126}
127
128int sd_netlink_open(sd_netlink **ret) {
129 _cleanup_close_ int fd = -1;
130 int r;
131
132 fd = socket_open(NETLINK_ROUTE);
133 if (fd < 0)
134 return fd;
135
136 r = sd_netlink_open_fd(ret, fd);
137 if (r < 0)
138 return r;
139
140 fd = -1;
141
142 return 0;
143}
144
145int sd_netlink_inc_rcvbuf(const sd_netlink *const rtnl, const int size) {
146 return fd_inc_rcvbuf(rtnl->fd, size);
147}
148
149sd_netlink *sd_netlink_ref(sd_netlink *rtnl) {
150 assert_return(rtnl, NULL);
151 assert_return(!rtnl_pid_changed(rtnl), NULL);
152
153 if (rtnl)
154 assert_se(REFCNT_INC(rtnl->n_ref) >= 2);
155
156 return rtnl;
157}
158
159sd_netlink *sd_netlink_unref(sd_netlink *rtnl) {
160 if (!rtnl)
161 return NULL;
162
163 assert_return(!rtnl_pid_changed(rtnl), NULL);
164
165 if (REFCNT_DEC(rtnl->n_ref) == 0) {
166 struct match_callback *f;
167 unsigned i;
168
169 for (i = 0; i < rtnl->rqueue_size; i++)
170 sd_netlink_message_unref(rtnl->rqueue[i]);
171 free(rtnl->rqueue);
172
173 for (i = 0; i < rtnl->rqueue_partial_size; i++)
174 sd_netlink_message_unref(rtnl->rqueue_partial[i]);
175 free(rtnl->rqueue_partial);
176
177 free(rtnl->rbuffer);
178
179 hashmap_free_free(rtnl->reply_callbacks);
180 prioq_free(rtnl->reply_callbacks_prioq);
181
182 sd_event_source_unref(rtnl->io_event_source);
183 sd_event_source_unref(rtnl->time_event_source);
184 sd_event_unref(rtnl->event);
185
186 while ((f = rtnl->match_callbacks)) {
187 sd_netlink_remove_match(rtnl, f->type, f->callback, f->userdata);
188 }
189
190 hashmap_free(rtnl->broadcast_group_refs);
191
192 safe_close(rtnl->fd);
193 free(rtnl);
194 }
195
196 return NULL;
197}
198
199static void rtnl_seal_message(sd_netlink *rtnl, sd_netlink_message *m) {
200 assert(rtnl);
201 assert(!rtnl_pid_changed(rtnl));
202 assert(m);
203 assert(m->hdr);
204
205 /* don't use seq == 0, as that is used for broadcasts, so we
206 would get confused by replies to such messages */
207 m->hdr->nlmsg_seq = rtnl->serial++ ? : rtnl->serial++;
208
209 rtnl_message_seal(m);
210
211 return;
212}
213
214int sd_netlink_send(sd_netlink *nl,
215 sd_netlink_message *message,
216 uint32_t *serial) {
217 int r;
218
219 assert_return(nl, -EINVAL);
220 assert_return(!rtnl_pid_changed(nl), -ECHILD);
221 assert_return(message, -EINVAL);
222 assert_return(!message->sealed, -EPERM);
223
224 rtnl_seal_message(nl, message);
225
226 r = socket_write_message(nl, message);
227 if (r < 0)
228 return r;
229
230 if (serial)
231 *serial = rtnl_message_get_serial(message);
232
233 return 1;
234}
235
236int rtnl_rqueue_make_room(sd_netlink *rtnl) {
237 assert(rtnl);
238
239 if (rtnl->rqueue_size >= RTNL_RQUEUE_MAX) {
240 log_debug("rtnl: exhausted the read queue size (%d)", RTNL_RQUEUE_MAX);
241 return -ENOBUFS;
242 }
243
244 if (!GREEDY_REALLOC(rtnl->rqueue, rtnl->rqueue_allocated, rtnl->rqueue_size + 1))
245 return -ENOMEM;
246
247 return 0;
248}
249
250int rtnl_rqueue_partial_make_room(sd_netlink *rtnl) {
251 assert(rtnl);
252
253 if (rtnl->rqueue_partial_size >= RTNL_RQUEUE_MAX) {
254 log_debug("rtnl: exhausted the partial read queue size (%d)", RTNL_RQUEUE_MAX);
255 return -ENOBUFS;
256 }
257
258 if (!GREEDY_REALLOC(rtnl->rqueue_partial, rtnl->rqueue_partial_allocated,
259 rtnl->rqueue_partial_size + 1))
260 return -ENOMEM;
261
262 return 0;
263}
264
265static int dispatch_rqueue(sd_netlink *rtnl, sd_netlink_message **message) {
266 int r;
267
268 assert(rtnl);
269 assert(message);
270
271 if (rtnl->rqueue_size <= 0) {
272 /* Try to read a new message */
273 r = socket_read_message(rtnl);
274 if (r <= 0)
275 return r;
276 }
277
278 /* Dispatch a queued message */
279 *message = rtnl->rqueue[0];
280 rtnl->rqueue_size --;
281 memmove(rtnl->rqueue, rtnl->rqueue + 1, sizeof(sd_netlink_message*) * rtnl->rqueue_size);
282
283 return 1;
284}
285
286static int process_timeout(sd_netlink *rtnl) {
287 _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
288 struct reply_callback *c;
289 usec_t n;
290 int r;
291
292 assert(rtnl);
293
294 c = prioq_peek(rtnl->reply_callbacks_prioq);
295 if (!c)
296 return 0;
297
298 n = now(CLOCK_MONOTONIC);
299 if (c->timeout > n)
300 return 0;
301
302 r = rtnl_message_new_synthetic_error(-ETIMEDOUT, c->serial, &m);
303 if (r < 0)
304 return r;
305
306 assert_se(prioq_pop(rtnl->reply_callbacks_prioq) == c);
307 hashmap_remove(rtnl->reply_callbacks, &c->serial);
308
309 r = c->callback(rtnl, m, c->userdata);
310 if (r < 0)
311 log_debug_errno(r, "sd-netlink: timedout callback failed: %m");
312
313 free(c);
314
315 return 1;
316}
317
318static int process_reply(sd_netlink *rtnl, sd_netlink_message *m) {
319 _cleanup_free_ struct reply_callback *c = NULL;
320 uint64_t serial;
321 uint16_t type;
322 int r;
323
324 assert(rtnl);
325 assert(m);
326
327 serial = rtnl_message_get_serial(m);
328 c = hashmap_remove(rtnl->reply_callbacks, &serial);
329 if (!c)
330 return 0;
331
332 if (c->timeout != 0)
333 prioq_remove(rtnl->reply_callbacks_prioq, c, &c->prioq_idx);
334
335 r = sd_netlink_message_get_type(m, &type);
336 if (r < 0)
337 return 0;
338
339 if (type == NLMSG_DONE)
340 m = NULL;
341
342 r = c->callback(rtnl, m, c->userdata);
343 if (r < 0)
344 log_debug_errno(r, "sd-netlink: callback failed: %m");
345
346 return 1;
347}
348
349static int process_match(sd_netlink *rtnl, sd_netlink_message *m) {
350 struct match_callback *c;
351 uint16_t type;
352 int r;
353
354 assert(rtnl);
355 assert(m);
356
357 r = sd_netlink_message_get_type(m, &type);
358 if (r < 0)
359 return r;
360
361 LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks) {
362 if (type == c->type) {
363 r = c->callback(rtnl, m, c->userdata);
364 if (r != 0) {
365 if (r < 0)
366 log_debug_errno(r, "sd-netlink: match callback failed: %m");
367
368 break;
369 }
370 }
371 }
372
373 return 1;
374}
375
376static int process_running(sd_netlink *rtnl, sd_netlink_message **ret) {
377 _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
378 int r;
379
380 assert(rtnl);
381
382 r = process_timeout(rtnl);
383 if (r != 0)
384 goto null_message;
385
386 r = dispatch_rqueue(rtnl, &m);
387 if (r < 0)
388 return r;
389 if (!m)
390 goto null_message;
391
392 if (sd_netlink_message_is_broadcast(m)) {
393 r = process_match(rtnl, m);
394 if (r != 0)
395 goto null_message;
396 } else {
397 r = process_reply(rtnl, m);
398 if (r != 0)
399 goto null_message;
400 }
401
402 if (ret) {
403 *ret = m;
404 m = NULL;
405
406 return 1;
407 }
408
409 return 1;
410
411null_message:
412 if (r >= 0 && ret)
413 *ret = NULL;
414
415 return r;
416}
417
418int sd_netlink_process(sd_netlink *rtnl, sd_netlink_message **ret) {
419 RTNL_DONT_DESTROY(rtnl);
420 int r;
421
422 assert_return(rtnl, -EINVAL);
423 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
424 assert_return(!rtnl->processing, -EBUSY);
425
426 rtnl->processing = true;
427 r = process_running(rtnl, ret);
428 rtnl->processing = false;
429
430 return r;
431}
432
433static usec_t calc_elapse(uint64_t usec) {
434 if (usec == (uint64_t) -1)
435 return 0;
436
437 if (usec == 0)
438 usec = RTNL_DEFAULT_TIMEOUT;
439
440 return now(CLOCK_MONOTONIC) + usec;
441}
442
443static int rtnl_poll(sd_netlink *rtnl, bool need_more, uint64_t timeout_usec) {
444 struct pollfd p[1] = {};
445 struct timespec ts;
446 usec_t m = USEC_INFINITY;
447 int r, e;
448
449 assert(rtnl);
450
451 e = sd_netlink_get_events(rtnl);
452 if (e < 0)
453 return e;
454
455 if (need_more)
456 /* Caller wants more data, and doesn't care about
457 * what's been read or any other timeouts. */
458 e |= POLLIN;
459 else {
460 usec_t until;
461 /* Caller wants to process if there is something to
462 * process, but doesn't care otherwise */
463
464 r = sd_netlink_get_timeout(rtnl, &until);
465 if (r < 0)
466 return r;
467 if (r > 0) {
468 usec_t nw;
469 nw = now(CLOCK_MONOTONIC);
470 m = until > nw ? until - nw : 0;
471 }
472 }
473
474 if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m))
475 m = timeout_usec;
476
477 p[0].fd = rtnl->fd;
478 p[0].events = e;
479
480 r = ppoll(p, 1, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL);
481 if (r < 0)
482 return -errno;
483
484 return r > 0 ? 1 : 0;
485}
486
487int sd_netlink_wait(sd_netlink *nl, uint64_t timeout_usec) {
488 assert_return(nl, -EINVAL);
489 assert_return(!rtnl_pid_changed(nl), -ECHILD);
490
491 if (nl->rqueue_size > 0)
492 return 0;
493
494 return rtnl_poll(nl, false, timeout_usec);
495}
496
497static int timeout_compare(const void *a, const void *b) {
498 const struct reply_callback *x = a, *y = b;
499
500 if (x->timeout != 0 && y->timeout == 0)
501 return -1;
502
503 if (x->timeout == 0 && y->timeout != 0)
504 return 1;
505
506 if (x->timeout < y->timeout)
507 return -1;
508
509 if (x->timeout > y->timeout)
510 return 1;
511
512 return 0;
513}
514
515int sd_netlink_call_async(sd_netlink *nl,
516 sd_netlink_message *m,
517 sd_netlink_message_handler_t callback,
518 void *userdata,
519 uint64_t usec,
520 uint32_t *serial) {
521 struct reply_callback *c;
522 uint32_t s;
523 int r, k;
524
525 assert_return(nl, -EINVAL);
526 assert_return(m, -EINVAL);
527 assert_return(callback, -EINVAL);
528 assert_return(!rtnl_pid_changed(nl), -ECHILD);
529
530 r = hashmap_ensure_allocated(&nl->reply_callbacks, &uint64_hash_ops);
531 if (r < 0)
532 return r;
533
534 if (usec != (uint64_t) -1) {
535 r = prioq_ensure_allocated(&nl->reply_callbacks_prioq, timeout_compare);
536 if (r < 0)
537 return r;
538 }
539
540 c = new0(struct reply_callback, 1);
541 if (!c)
542 return -ENOMEM;
543
544 c->callback = callback;
545 c->userdata = userdata;
546 c->timeout = calc_elapse(usec);
547
548 k = sd_netlink_send(nl, m, &s);
549 if (k < 0) {
550 free(c);
551 return k;
552 }
553
554 c->serial = s;
555
556 r = hashmap_put(nl->reply_callbacks, &c->serial, c);
557 if (r < 0) {
558 free(c);
559 return r;
560 }
561
562 if (c->timeout != 0) {
563 r = prioq_put(nl->reply_callbacks_prioq, c, &c->prioq_idx);
564 if (r > 0) {
565 c->timeout = 0;
566 sd_netlink_call_async_cancel(nl, c->serial);
567 return r;
568 }
569 }
570
571 if (serial)
572 *serial = s;
573
574 return k;
575}
576
577int sd_netlink_call_async_cancel(sd_netlink *nl, uint32_t serial) {
578 struct reply_callback *c;
579 uint64_t s = serial;
580
581 assert_return(nl, -EINVAL);
582 assert_return(serial != 0, -EINVAL);
583 assert_return(!rtnl_pid_changed(nl), -ECHILD);
584
585 c = hashmap_remove(nl->reply_callbacks, &s);
586 if (!c)
587 return 0;
588
589 if (c->timeout != 0)
590 prioq_remove(nl->reply_callbacks_prioq, c, &c->prioq_idx);
591
592 free(c);
593 return 1;
594}
595
596int sd_netlink_call(sd_netlink *rtnl,
597 sd_netlink_message *message,
598 uint64_t usec,
599 sd_netlink_message **ret) {
600 usec_t timeout;
601 uint32_t serial;
602 int r;
603
604 assert_return(rtnl, -EINVAL);
605 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
606 assert_return(message, -EINVAL);
607
608 r = sd_netlink_send(rtnl, message, &serial);
609 if (r < 0)
610 return r;
611
612 timeout = calc_elapse(usec);
613
614 for (;;) {
615 usec_t left;
616 unsigned i;
617
618 for (i = 0; i < rtnl->rqueue_size; i++) {
619 uint32_t received_serial;
620
621 received_serial = rtnl_message_get_serial(rtnl->rqueue[i]);
622
623 if (received_serial == serial) {
624 _cleanup_netlink_message_unref_ sd_netlink_message *incoming = NULL;
625 uint16_t type;
626
627 incoming = rtnl->rqueue[i];
628
629 /* found a match, remove from rqueue and return it */
630 memmove(rtnl->rqueue + i,rtnl->rqueue + i + 1,
631 sizeof(sd_netlink_message*) * (rtnl->rqueue_size - i - 1));
632 rtnl->rqueue_size--;
633
634 r = sd_netlink_message_get_errno(incoming);
635 if (r < 0)
636 return r;
637
638 r = sd_netlink_message_get_type(incoming, &type);
639 if (r < 0)
640 return r;
641
642 if (type == NLMSG_DONE) {
643 *ret = NULL;
644 return 0;
645 }
646
647 if (ret) {
648 *ret = incoming;
649 incoming = NULL;
650 }
651
652 return 1;
653 }
654 }
655
656 r = socket_read_message(rtnl);
657 if (r < 0)
658 return r;
659 if (r > 0)
660 /* received message, so try to process straight away */
661 continue;
662
663 if (timeout > 0) {
664 usec_t n;
665
666 n = now(CLOCK_MONOTONIC);
667 if (n >= timeout)
668 return -ETIMEDOUT;
669
670 left = timeout - n;
671 } else
672 left = (uint64_t) -1;
673
674 r = rtnl_poll(rtnl, true, left);
675 if (r < 0)
676 return r;
677 else if (r == 0)
678 return -ETIMEDOUT;
679 }
680}
681
682int sd_netlink_get_events(sd_netlink *rtnl) {
683 assert_return(rtnl, -EINVAL);
684 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
685
686 if (rtnl->rqueue_size == 0)
687 return POLLIN;
688 else
689 return 0;
690}
691
692int sd_netlink_get_timeout(sd_netlink *rtnl, uint64_t *timeout_usec) {
693 struct reply_callback *c;
694
695 assert_return(rtnl, -EINVAL);
696 assert_return(timeout_usec, -EINVAL);
697 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
698
699 if (rtnl->rqueue_size > 0) {
700 *timeout_usec = 0;
701 return 1;
702 }
703
704 c = prioq_peek(rtnl->reply_callbacks_prioq);
705 if (!c) {
706 *timeout_usec = (uint64_t) -1;
707 return 0;
708 }
709
710 *timeout_usec = c->timeout;
711
712 return 1;
713}
714
715static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
716 sd_netlink *rtnl = userdata;
717 int r;
718
719 assert(rtnl);
720
721 r = sd_netlink_process(rtnl, NULL);
722 if (r < 0)
723 return r;
724
725 return 1;
726}
727
728static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) {
729 sd_netlink *rtnl = userdata;
730 int r;
731
732 assert(rtnl);
733
734 r = sd_netlink_process(rtnl, NULL);
735 if (r < 0)
736 return r;
737
738 return 1;
739}
740
741static int prepare_callback(sd_event_source *s, void *userdata) {
742 sd_netlink *rtnl = userdata;
743 int r, e;
744 usec_t until;
745
746 assert(s);
747 assert(rtnl);
748
749 e = sd_netlink_get_events(rtnl);
750 if (e < 0)
751 return e;
752
753 r = sd_event_source_set_io_events(rtnl->io_event_source, e);
754 if (r < 0)
755 return r;
756
757 r = sd_netlink_get_timeout(rtnl, &until);
758 if (r < 0)
759 return r;
760 if (r > 0) {
761 int j;
762
763 j = sd_event_source_set_time(rtnl->time_event_source, until);
764 if (j < 0)
765 return j;
766 }
767
768 r = sd_event_source_set_enabled(rtnl->time_event_source, r > 0);
769 if (r < 0)
770 return r;
771
772 return 1;
773}
774
775int sd_netlink_attach_event(sd_netlink *rtnl, sd_event *event, int priority) {
776 int r;
777
778 assert_return(rtnl, -EINVAL);
779 assert_return(!rtnl->event, -EBUSY);
780
781 assert(!rtnl->io_event_source);
782 assert(!rtnl->time_event_source);
783
784 if (event)
785 rtnl->event = sd_event_ref(event);
786 else {
787 r = sd_event_default(&rtnl->event);
788 if (r < 0)
789 return r;
790 }
791
792 r = sd_event_add_io(rtnl->event, &rtnl->io_event_source, rtnl->fd, 0, io_callback, rtnl);
793 if (r < 0)
794 goto fail;
795
796 r = sd_event_source_set_priority(rtnl->io_event_source, priority);
797 if (r < 0)
798 goto fail;
799
800 r = sd_event_source_set_description(rtnl->io_event_source, "rtnl-receive-message");
801 if (r < 0)
802 goto fail;
803
804 r = sd_event_source_set_prepare(rtnl->io_event_source, prepare_callback);
805 if (r < 0)
806 goto fail;
807
808 r = sd_event_add_time(rtnl->event, &rtnl->time_event_source, CLOCK_MONOTONIC, 0, 0, time_callback, rtnl);
809 if (r < 0)
810 goto fail;
811
812 r = sd_event_source_set_priority(rtnl->time_event_source, priority);
813 if (r < 0)
814 goto fail;
815
816 r = sd_event_source_set_description(rtnl->time_event_source, "rtnl-timer");
817 if (r < 0)
818 goto fail;
819
820 return 0;
821
822fail:
823 sd_netlink_detach_event(rtnl);
824 return r;
825}
826
827int sd_netlink_detach_event(sd_netlink *rtnl) {
828 assert_return(rtnl, -EINVAL);
829 assert_return(rtnl->event, -ENXIO);
830
831 rtnl->io_event_source = sd_event_source_unref(rtnl->io_event_source);
832
833 rtnl->time_event_source = sd_event_source_unref(rtnl->time_event_source);
834
835 rtnl->event = sd_event_unref(rtnl->event);
836
837 return 0;
838}
839
840int sd_netlink_add_match(sd_netlink *rtnl,
841 uint16_t type,
842 sd_netlink_message_handler_t callback,
843 void *userdata) {
844 _cleanup_free_ struct match_callback *c = NULL;
845 int r;
846
847 assert_return(rtnl, -EINVAL);
848 assert_return(callback, -EINVAL);
849 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
850
851 c = new0(struct match_callback, 1);
852 if (!c)
853 return -ENOMEM;
854
855 c->callback = callback;
856 c->type = type;
857 c->userdata = userdata;
858
859 switch (type) {
860 case RTM_NEWLINK:
861 case RTM_DELLINK:
862 r = socket_broadcast_group_ref(rtnl, RTNLGRP_LINK);
863 if (r < 0)
864 return r;
865
866 break;
867 case RTM_NEWADDR:
868 case RTM_DELADDR:
869 r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_IFADDR);
870 if (r < 0)
871 return r;
872
873 r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_IFADDR);
874 if (r < 0)
875 return r;
876
877 break;
878 case RTM_NEWROUTE:
879 case RTM_DELROUTE:
880 r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_ROUTE);
881 if (r < 0)
882 return r;
883
884 r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_ROUTE);
885 if (r < 0)
886 return r;
887 break;
888 default:
889 return -EOPNOTSUPP;
890 }
891
892 LIST_PREPEND(match_callbacks, rtnl->match_callbacks, c);
893
894 c = NULL;
895
896 return 0;
897}
898
899int sd_netlink_remove_match(sd_netlink *rtnl,
900 uint16_t type,
901 sd_netlink_message_handler_t callback,
902 void *userdata) {
903 struct match_callback *c;
904 int r;
905
906 assert_return(rtnl, -EINVAL);
907 assert_return(callback, -EINVAL);
908 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
909
910 LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks)
911 if (c->callback == callback && c->type == type && c->userdata == userdata) {
912 LIST_REMOVE(match_callbacks, rtnl->match_callbacks, c);
913 free(c);
914
915 switch (type) {
916 case RTM_NEWLINK:
917 case RTM_DELLINK:
918 r = socket_broadcast_group_unref(rtnl, RTNLGRP_LINK);
919 if (r < 0)
920 return r;
921
922 break;
923 case RTM_NEWADDR:
924 case RTM_DELADDR:
925 r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV4_IFADDR);
926 if (r < 0)
927 return r;
928
929 r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV6_IFADDR);
930 if (r < 0)
931 return r;
932
933 break;
934 case RTM_NEWROUTE:
935 case RTM_DELROUTE:
936 r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV4_ROUTE);
937 if (r < 0)
938 return r;
939
940 r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV6_ROUTE);
941 if (r < 0)
942 return r;
943 break;
944 default:
945 return -EOPNOTSUPP;
946 }
947
948 return 1;
949 }
950
951 return 0;
952}