1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Tom Gundersen <teg@jklm.no>
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.
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.
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/>.
22 #include <sys/socket.h>
31 #include "rtnl-internal.h"
32 #include "rtnl-util.h"
34 static int sd_rtnl_new(sd_rtnl **ret) {
35 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
37 assert_return(ret, -EINVAL);
39 rtnl = new0(sd_rtnl, 1);
43 rtnl->n_ref = REFCNT_INIT;
47 rtnl->sockaddr.nl.nl_family = AF_NETLINK;
49 rtnl->original_pid = getpid();
51 LIST_HEAD_INIT(rtnl->match_callbacks);
53 /* We guarantee that wqueue always has space for at least
55 if (!GREEDY_REALLOC(rtnl->wqueue, rtnl->wqueue_allocated, 1))
58 /* We guarantee that the read buffer has at least space for
60 if (!greedy_realloc((void**)&rtnl->rbuffer, &rtnl->rbuffer_allocated,
61 sizeof(struct nlmsghdr), sizeof(uint8_t)))
64 /* Change notification responses have sequence 0, so we must
65 * start our request sequence numbers at 1, or we may confuse our
66 * responses with notifications from the kernel */
75 int sd_rtnl_new_from_netlink(sd_rtnl **ret, int fd) {
76 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
80 assert_return(ret, -EINVAL);
82 r = sd_rtnl_new(&rtnl);
86 addrlen = sizeof(rtnl->sockaddr);
88 r = getsockname(fd, &rtnl->sockaddr.sa, &addrlen);
100 static bool rtnl_pid_changed(sd_rtnl *rtnl) {
103 /* We don't support people creating an rtnl connection and
104 * keeping it around over a fork(). Let's complain. */
106 return rtnl->original_pid != getpid();
109 static int rtnl_compute_groups_ap(uint32_t *_groups, unsigned n_groups, va_list ap) {
113 for (i = 0; i < n_groups; i++) {
116 group = va_arg(ap, unsigned);
117 assert_return(group < 32, -EINVAL);
119 groups |= group ? (1 << (group - 1)) : 0;
127 static int rtnl_open_fd_ap(sd_rtnl **ret, int fd, unsigned n_groups, va_list ap) {
128 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
132 assert_return(ret, -EINVAL);
133 assert_return(fd >= 0, -EINVAL);
135 r = sd_rtnl_new(&rtnl);
139 r = setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
143 r = setsockopt(fd, SOL_NETLINK, NETLINK_PKTINFO, &one, sizeof(one));
147 r = rtnl_compute_groups_ap(&rtnl->sockaddr.nl.nl_groups, n_groups, ap);
151 addrlen = sizeof(rtnl->sockaddr);
153 r = bind(fd, &rtnl->sockaddr.sa, addrlen);
154 /* ignore EINVAL to allow opening an already bound socket */
155 if (r < 0 && errno != EINVAL)
158 r = getsockname(fd, &rtnl->sockaddr.sa, &addrlen);
170 int sd_rtnl_open_fd(sd_rtnl **ret, int fd, unsigned n_groups, ...) {
174 va_start(ap, n_groups);
175 r = rtnl_open_fd_ap(ret, fd, n_groups, ap);
181 int sd_rtnl_open(sd_rtnl **ret, unsigned n_groups, ...) {
185 fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE);
189 va_start(ap, n_groups);
190 r = rtnl_open_fd_ap(ret, fd, n_groups, ap);
201 int sd_rtnl_inc_rcvbuf(const sd_rtnl *const rtnl, const int size) {
202 return fd_inc_rcvbuf(rtnl->fd, size);
205 sd_rtnl *sd_rtnl_ref(sd_rtnl *rtnl) {
206 assert_return(rtnl, NULL);
207 assert_return(!rtnl_pid_changed(rtnl), NULL);
210 assert_se(REFCNT_INC(rtnl->n_ref) >= 2);
215 sd_rtnl *sd_rtnl_unref(sd_rtnl *rtnl) {
219 assert_return(!rtnl_pid_changed(rtnl), NULL);
221 if (REFCNT_DEC(rtnl->n_ref) == 0) {
222 struct match_callback *f;
225 for (i = 0; i < rtnl->rqueue_size; i++)
226 sd_rtnl_message_unref(rtnl->rqueue[i]);
229 for (i = 0; i < rtnl->rqueue_partial_size; i++)
230 sd_rtnl_message_unref(rtnl->rqueue_partial[i]);
231 free(rtnl->rqueue_partial);
233 for (i = 0; i < rtnl->wqueue_size; i++)
234 sd_rtnl_message_unref(rtnl->wqueue[i]);
239 hashmap_free_free(rtnl->reply_callbacks);
240 prioq_free(rtnl->reply_callbacks_prioq);
242 sd_event_source_unref(rtnl->io_event_source);
243 sd_event_source_unref(rtnl->time_event_source);
244 sd_event_source_unref(rtnl->exit_event_source);
245 sd_event_unref(rtnl->event);
247 while ((f = rtnl->match_callbacks)) {
248 LIST_REMOVE(match_callbacks, rtnl->match_callbacks, f);
252 safe_close(rtnl->fd);
259 static void rtnl_seal_message(sd_rtnl *rtnl, sd_rtnl_message *m) {
261 assert(!rtnl_pid_changed(rtnl));
265 m->hdr->nlmsg_seq = rtnl->serial++;
267 rtnl_message_seal(m);
272 int sd_rtnl_send(sd_rtnl *nl,
273 sd_rtnl_message *message,
277 assert_return(nl, -EINVAL);
278 assert_return(!rtnl_pid_changed(nl), -ECHILD);
279 assert_return(message, -EINVAL);
280 assert_return(!message->sealed, -EPERM);
282 rtnl_seal_message(nl, message);
284 if (nl->wqueue_size <= 0) {
286 r = socket_write_message(nl, message);
290 /* nothing was sent, so let's put it on
292 nl->wqueue[0] = sd_rtnl_message_ref(message);
296 /* append to queue */
297 if (nl->wqueue_size >= RTNL_WQUEUE_MAX) {
298 log_debug("rtnl: exhausted the write queue size (%d)", RTNL_WQUEUE_MAX);
302 if (!GREEDY_REALLOC(nl->wqueue, nl->wqueue_allocated, nl->wqueue_size + 1))
305 nl->wqueue[nl->wqueue_size ++] = sd_rtnl_message_ref(message);
309 *serial = rtnl_message_get_serial(message);
314 int rtnl_rqueue_make_room(sd_rtnl *rtnl) {
317 if (rtnl->rqueue_size >= RTNL_RQUEUE_MAX) {
318 log_debug("rtnl: exhausted the read queue size (%d)", RTNL_RQUEUE_MAX);
322 if (!GREEDY_REALLOC(rtnl->rqueue, rtnl->rqueue_allocated, rtnl->rqueue_size + 1))
328 int rtnl_rqueue_partial_make_room(sd_rtnl *rtnl) {
331 if (rtnl->rqueue_partial_size >= RTNL_RQUEUE_MAX) {
332 log_debug("rtnl: exhausted the partial read queue size (%d)", RTNL_RQUEUE_MAX);
336 if (!GREEDY_REALLOC(rtnl->rqueue_partial, rtnl->rqueue_partial_allocated,
337 rtnl->rqueue_partial_size + 1))
343 static int dispatch_rqueue(sd_rtnl *rtnl, sd_rtnl_message **message) {
349 if (rtnl->rqueue_size <= 0) {
350 /* Try to read a new message */
351 r = socket_read_message(rtnl);
356 /* Dispatch a queued message */
357 *message = rtnl->rqueue[0];
358 rtnl->rqueue_size --;
359 memmove(rtnl->rqueue, rtnl->rqueue + 1, sizeof(sd_rtnl_message*) * rtnl->rqueue_size);
364 static int dispatch_wqueue(sd_rtnl *rtnl) {
369 while (rtnl->wqueue_size > 0) {
370 r = socket_write_message(rtnl, rtnl->wqueue[0]);
374 /* Didn't do anything this time */
377 /* see equivalent in sd-bus.c */
378 sd_rtnl_message_unref(rtnl->wqueue[0]);
379 rtnl->wqueue_size --;
380 memmove(rtnl->wqueue, rtnl->wqueue + 1, sizeof(sd_rtnl_message*) * rtnl->wqueue_size);
389 static int process_timeout(sd_rtnl *rtnl) {
390 _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
391 struct reply_callback *c;
397 c = prioq_peek(rtnl->reply_callbacks_prioq);
401 n = now(CLOCK_MONOTONIC);
405 r = rtnl_message_new_synthetic_error(-ETIMEDOUT, c->serial, &m);
409 assert_se(prioq_pop(rtnl->reply_callbacks_prioq) == c);
410 hashmap_remove(rtnl->reply_callbacks, &c->serial);
412 r = c->callback(rtnl, m, c->userdata);
414 log_debug_errno(r, "sd-rtnl: timedout callback failed: %m");
421 static int process_reply(sd_rtnl *rtnl, sd_rtnl_message *m) {
422 struct reply_callback *c;
429 if (sd_rtnl_message_is_broadcast(m))
432 serial = rtnl_message_get_serial(m);
433 c = hashmap_remove(rtnl->reply_callbacks, &serial);
438 prioq_remove(rtnl->reply_callbacks_prioq, c, &c->prioq_idx);
440 r = c->callback(rtnl, m, c->userdata);
442 log_debug_errno(r, "sd-rtnl: callback failed: %m");
449 static int process_match(sd_rtnl *rtnl, sd_rtnl_message *m) {
450 struct match_callback *c;
457 r = sd_rtnl_message_get_type(m, &type);
461 LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks) {
462 if (type == c->type) {
463 r = c->callback(rtnl, m, c->userdata);
466 log_debug_errno(r, "sd-rtnl: match callback failed: %m");
476 static int process_running(sd_rtnl *rtnl, sd_rtnl_message **ret) {
477 _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
482 r = process_timeout(rtnl);
486 r = dispatch_wqueue(rtnl);
490 r = dispatch_rqueue(rtnl, &m);
496 r = process_reply(rtnl, m);
500 r = process_match(rtnl, m);
520 int sd_rtnl_process(sd_rtnl *rtnl, sd_rtnl_message **ret) {
521 RTNL_DONT_DESTROY(rtnl);
524 assert_return(rtnl, -EINVAL);
525 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
526 assert_return(!rtnl->processing, -EBUSY);
528 rtnl->processing = true;
529 r = process_running(rtnl, ret);
530 rtnl->processing = false;
535 static usec_t calc_elapse(uint64_t usec) {
536 if (usec == (uint64_t) -1)
540 usec = RTNL_DEFAULT_TIMEOUT;
542 return now(CLOCK_MONOTONIC) + usec;
545 static int rtnl_poll(sd_rtnl *rtnl, bool need_more, uint64_t timeout_usec) {
546 struct pollfd p[1] = {};
548 usec_t m = USEC_INFINITY;
553 e = sd_rtnl_get_events(rtnl);
558 /* Caller wants more data, and doesn't care about
559 * what's been read or any other timeouts. */
563 /* Caller wants to process if there is something to
564 * process, but doesn't care otherwise */
566 r = sd_rtnl_get_timeout(rtnl, &until);
571 nw = now(CLOCK_MONOTONIC);
572 m = until > nw ? until - nw : 0;
576 if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m))
582 r = ppoll(p, 1, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL);
586 return r > 0 ? 1 : 0;
589 int sd_rtnl_wait(sd_rtnl *nl, uint64_t timeout_usec) {
590 assert_return(nl, -EINVAL);
591 assert_return(!rtnl_pid_changed(nl), -ECHILD);
593 if (nl->rqueue_size > 0)
596 return rtnl_poll(nl, false, timeout_usec);
599 static int timeout_compare(const void *a, const void *b) {
600 const struct reply_callback *x = a, *y = b;
602 if (x->timeout != 0 && y->timeout == 0)
605 if (x->timeout == 0 && y->timeout != 0)
608 if (x->timeout < y->timeout)
611 if (x->timeout > y->timeout)
617 int sd_rtnl_call_async(sd_rtnl *nl,
619 sd_rtnl_message_handler_t callback,
623 struct reply_callback *c;
627 assert_return(nl, -EINVAL);
628 assert_return(m, -EINVAL);
629 assert_return(callback, -EINVAL);
630 assert_return(!rtnl_pid_changed(nl), -ECHILD);
632 r = hashmap_ensure_allocated(&nl->reply_callbacks, &uint64_hash_ops);
636 if (usec != (uint64_t) -1) {
637 r = prioq_ensure_allocated(&nl->reply_callbacks_prioq, timeout_compare);
642 c = new0(struct reply_callback, 1);
646 c->callback = callback;
647 c->userdata = userdata;
648 c->timeout = calc_elapse(usec);
650 k = sd_rtnl_send(nl, m, &s);
658 r = hashmap_put(nl->reply_callbacks, &c->serial, c);
664 if (c->timeout != 0) {
665 r = prioq_put(nl->reply_callbacks_prioq, c, &c->prioq_idx);
668 sd_rtnl_call_async_cancel(nl, c->serial);
679 int sd_rtnl_call_async_cancel(sd_rtnl *nl, uint32_t serial) {
680 struct reply_callback *c;
683 assert_return(nl, -EINVAL);
684 assert_return(serial != 0, -EINVAL);
685 assert_return(!rtnl_pid_changed(nl), -ECHILD);
687 c = hashmap_remove(nl->reply_callbacks, &s);
692 prioq_remove(nl->reply_callbacks_prioq, c, &c->prioq_idx);
698 int sd_rtnl_call(sd_rtnl *rtnl,
699 sd_rtnl_message *message,
701 sd_rtnl_message **ret) {
707 assert_return(rtnl, -EINVAL);
708 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
709 assert_return(message, -EINVAL);
711 r = sd_rtnl_send(rtnl, message, &serial);
715 timeout = calc_elapse(usec);
720 while (i < rtnl->rqueue_size) {
721 sd_rtnl_message *incoming;
722 uint32_t received_serial;
724 incoming = rtnl->rqueue[i];
725 received_serial = rtnl_message_get_serial(incoming);
727 if (received_serial == serial) {
728 /* found a match, remove from rqueue and return it */
729 memmove(rtnl->rqueue + i,rtnl->rqueue + i + 1,
730 sizeof(sd_rtnl_message*) * (rtnl->rqueue_size - i - 1));
733 r = sd_rtnl_message_get_errno(incoming);
735 sd_rtnl_message_unref(incoming);
742 sd_rtnl_message_unref(incoming);
747 /* Try to read more, right away */
751 r = socket_read_message(rtnl);
755 /* received message, so try to process straight away */
761 n = now(CLOCK_MONOTONIC);
767 left = (uint64_t) -1;
769 r = rtnl_poll(rtnl, true, left);
775 r = dispatch_wqueue(rtnl);
781 int sd_rtnl_flush(sd_rtnl *rtnl) {
784 assert_return(rtnl, -EINVAL);
785 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
787 if (rtnl->wqueue_size <= 0)
791 r = dispatch_wqueue(rtnl);
795 if (rtnl->wqueue_size <= 0)
798 r = rtnl_poll(rtnl, false, (uint64_t) -1);
804 int sd_rtnl_get_events(sd_rtnl *rtnl) {
807 assert_return(rtnl, -EINVAL);
808 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
810 if (rtnl->rqueue_size <= 0)
812 if (rtnl->wqueue_size > 0)
818 int sd_rtnl_get_timeout(sd_rtnl *rtnl, uint64_t *timeout_usec) {
819 struct reply_callback *c;
821 assert_return(rtnl, -EINVAL);
822 assert_return(timeout_usec, -EINVAL);
823 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
825 if (rtnl->rqueue_size > 0) {
830 c = prioq_peek(rtnl->reply_callbacks_prioq);
832 *timeout_usec = (uint64_t) -1;
836 *timeout_usec = c->timeout;
841 static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
842 sd_rtnl *rtnl = userdata;
847 r = sd_rtnl_process(rtnl, NULL);
854 static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) {
855 sd_rtnl *rtnl = userdata;
860 r = sd_rtnl_process(rtnl, NULL);
867 static int prepare_callback(sd_event_source *s, void *userdata) {
868 sd_rtnl *rtnl = userdata;
875 e = sd_rtnl_get_events(rtnl);
879 r = sd_event_source_set_io_events(rtnl->io_event_source, e);
883 r = sd_rtnl_get_timeout(rtnl, &until);
889 j = sd_event_source_set_time(rtnl->time_event_source, until);
894 r = sd_event_source_set_enabled(rtnl->time_event_source, r > 0);
901 static int exit_callback(sd_event_source *event, void *userdata) {
902 sd_rtnl *rtnl = userdata;
911 int sd_rtnl_attach_event(sd_rtnl *rtnl, sd_event *event, int priority) {
914 assert_return(rtnl, -EINVAL);
915 assert_return(!rtnl->event, -EBUSY);
917 assert(!rtnl->io_event_source);
918 assert(!rtnl->time_event_source);
921 rtnl->event = sd_event_ref(event);
923 r = sd_event_default(&rtnl->event);
928 r = sd_event_add_io(rtnl->event, &rtnl->io_event_source, rtnl->fd, 0, io_callback, rtnl);
932 r = sd_event_source_set_priority(rtnl->io_event_source, priority);
936 r = sd_event_source_set_description(rtnl->io_event_source, "rtnl-receive-message");
940 r = sd_event_source_set_prepare(rtnl->io_event_source, prepare_callback);
944 r = sd_event_add_time(rtnl->event, &rtnl->time_event_source, CLOCK_MONOTONIC, 0, 0, time_callback, rtnl);
948 r = sd_event_source_set_priority(rtnl->time_event_source, priority);
952 r = sd_event_source_set_description(rtnl->time_event_source, "rtnl-timer");
956 r = sd_event_add_exit(rtnl->event, &rtnl->exit_event_source, exit_callback, rtnl);
960 r = sd_event_source_set_description(rtnl->exit_event_source, "rtnl-exit");
967 sd_rtnl_detach_event(rtnl);
971 int sd_rtnl_detach_event(sd_rtnl *rtnl) {
972 assert_return(rtnl, -EINVAL);
973 assert_return(rtnl->event, -ENXIO);
975 if (rtnl->io_event_source)
976 rtnl->io_event_source = sd_event_source_unref(rtnl->io_event_source);
978 if (rtnl->time_event_source)
979 rtnl->time_event_source = sd_event_source_unref(rtnl->time_event_source);
981 if (rtnl->exit_event_source)
982 rtnl->exit_event_source = sd_event_source_unref(rtnl->exit_event_source);
985 rtnl->event = sd_event_unref(rtnl->event);
990 int sd_rtnl_add_match(sd_rtnl *rtnl,
992 sd_rtnl_message_handler_t callback,
994 struct match_callback *c;
996 assert_return(rtnl, -EINVAL);
997 assert_return(callback, -EINVAL);
998 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
999 assert_return(rtnl_message_type_is_link(type) ||
1000 rtnl_message_type_is_addr(type) ||
1001 rtnl_message_type_is_route(type), -ENOTSUP);
1003 c = new0(struct match_callback, 1);
1007 c->callback = callback;
1009 c->userdata = userdata;
1011 LIST_PREPEND(match_callbacks, rtnl->match_callbacks, c);
1016 int sd_rtnl_remove_match(sd_rtnl *rtnl,
1018 sd_rtnl_message_handler_t callback,
1020 struct match_callback *c;
1022 assert_return(rtnl, -EINVAL);
1023 assert_return(callback, -EINVAL);
1024 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
1026 LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks)
1027 if (c->callback == callback && c->type == type && c->userdata == userdata) {
1028 LIST_REMOVE(match_callbacks, rtnl->match_callbacks, c);