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 /* don't use seq == 0, as that is used for broadcasts, so we
266 would get confused by replies to such messages */
267 m->hdr->nlmsg_seq = rtnl->serial++ ? : rtnl->serial++;
269 rtnl_message_seal(m);
274 int sd_rtnl_send(sd_rtnl *nl,
275 sd_rtnl_message *message,
279 assert_return(nl, -EINVAL);
280 assert_return(!rtnl_pid_changed(nl), -ECHILD);
281 assert_return(message, -EINVAL);
282 assert_return(!message->sealed, -EPERM);
284 rtnl_seal_message(nl, message);
286 if (nl->wqueue_size <= 0) {
288 r = socket_write_message(nl, message);
292 /* nothing was sent, so let's put it on
294 nl->wqueue[0] = sd_rtnl_message_ref(message);
298 /* append to queue */
299 if (nl->wqueue_size >= RTNL_WQUEUE_MAX) {
300 log_debug("rtnl: exhausted the write queue size (%d)", RTNL_WQUEUE_MAX);
304 if (!GREEDY_REALLOC(nl->wqueue, nl->wqueue_allocated, nl->wqueue_size + 1))
307 nl->wqueue[nl->wqueue_size ++] = sd_rtnl_message_ref(message);
311 *serial = rtnl_message_get_serial(message);
316 int rtnl_rqueue_make_room(sd_rtnl *rtnl) {
319 if (rtnl->rqueue_size >= RTNL_RQUEUE_MAX) {
320 log_debug("rtnl: exhausted the read queue size (%d)", RTNL_RQUEUE_MAX);
324 if (!GREEDY_REALLOC(rtnl->rqueue, rtnl->rqueue_allocated, rtnl->rqueue_size + 1))
330 int rtnl_rqueue_partial_make_room(sd_rtnl *rtnl) {
333 if (rtnl->rqueue_partial_size >= RTNL_RQUEUE_MAX) {
334 log_debug("rtnl: exhausted the partial read queue size (%d)", RTNL_RQUEUE_MAX);
338 if (!GREEDY_REALLOC(rtnl->rqueue_partial, rtnl->rqueue_partial_allocated,
339 rtnl->rqueue_partial_size + 1))
345 static int dispatch_rqueue(sd_rtnl *rtnl, sd_rtnl_message **message) {
351 if (rtnl->rqueue_size <= 0) {
352 /* Try to read a new message */
353 r = socket_read_message(rtnl);
358 /* Dispatch a queued message */
359 *message = rtnl->rqueue[0];
360 rtnl->rqueue_size --;
361 memmove(rtnl->rqueue, rtnl->rqueue + 1, sizeof(sd_rtnl_message*) * rtnl->rqueue_size);
366 static int dispatch_wqueue(sd_rtnl *rtnl) {
371 while (rtnl->wqueue_size > 0) {
372 r = socket_write_message(rtnl, rtnl->wqueue[0]);
376 /* Didn't do anything this time */
379 /* see equivalent in sd-bus.c */
380 sd_rtnl_message_unref(rtnl->wqueue[0]);
381 rtnl->wqueue_size --;
382 memmove(rtnl->wqueue, rtnl->wqueue + 1, sizeof(sd_rtnl_message*) * rtnl->wqueue_size);
391 static int process_timeout(sd_rtnl *rtnl) {
392 _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
393 struct reply_callback *c;
399 c = prioq_peek(rtnl->reply_callbacks_prioq);
403 n = now(CLOCK_MONOTONIC);
407 r = rtnl_message_new_synthetic_error(-ETIMEDOUT, c->serial, &m);
411 assert_se(prioq_pop(rtnl->reply_callbacks_prioq) == c);
412 hashmap_remove(rtnl->reply_callbacks, &c->serial);
414 r = c->callback(rtnl, m, c->userdata);
416 log_debug_errno(r, "sd-rtnl: timedout callback failed: %m");
423 static int process_reply(sd_rtnl *rtnl, sd_rtnl_message *m) {
424 struct reply_callback *c;
431 serial = rtnl_message_get_serial(m);
432 c = hashmap_remove(rtnl->reply_callbacks, &serial);
437 prioq_remove(rtnl->reply_callbacks_prioq, c, &c->prioq_idx);
439 r = c->callback(rtnl, m, c->userdata);
441 log_debug_errno(r, "sd-rtnl: callback failed: %m");
448 static int process_match(sd_rtnl *rtnl, sd_rtnl_message *m) {
449 struct match_callback *c;
456 r = sd_rtnl_message_get_type(m, &type);
460 LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks) {
461 if (type == c->type) {
462 r = c->callback(rtnl, m, c->userdata);
465 log_debug_errno(r, "sd-rtnl: match callback failed: %m");
475 static int process_running(sd_rtnl *rtnl, sd_rtnl_message **ret) {
476 _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
481 r = process_timeout(rtnl);
485 r = dispatch_wqueue(rtnl);
489 r = dispatch_rqueue(rtnl, &m);
495 if (sd_rtnl_message_is_broadcast(m)) {
496 r = process_match(rtnl, m);
500 r = process_reply(rtnl, m);
521 int sd_rtnl_process(sd_rtnl *rtnl, sd_rtnl_message **ret) {
522 RTNL_DONT_DESTROY(rtnl);
525 assert_return(rtnl, -EINVAL);
526 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
527 assert_return(!rtnl->processing, -EBUSY);
529 rtnl->processing = true;
530 r = process_running(rtnl, ret);
531 rtnl->processing = false;
536 static usec_t calc_elapse(uint64_t usec) {
537 if (usec == (uint64_t) -1)
541 usec = RTNL_DEFAULT_TIMEOUT;
543 return now(CLOCK_MONOTONIC) + usec;
546 static int rtnl_poll(sd_rtnl *rtnl, bool need_more, uint64_t timeout_usec) {
547 struct pollfd p[1] = {};
549 usec_t m = USEC_INFINITY;
554 e = sd_rtnl_get_events(rtnl);
559 /* Caller wants more data, and doesn't care about
560 * what's been read or any other timeouts. */
564 /* Caller wants to process if there is something to
565 * process, but doesn't care otherwise */
567 r = sd_rtnl_get_timeout(rtnl, &until);
572 nw = now(CLOCK_MONOTONIC);
573 m = until > nw ? until - nw : 0;
577 if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m))
583 r = ppoll(p, 1, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL);
587 return r > 0 ? 1 : 0;
590 int sd_rtnl_wait(sd_rtnl *nl, uint64_t timeout_usec) {
591 assert_return(nl, -EINVAL);
592 assert_return(!rtnl_pid_changed(nl), -ECHILD);
594 if (nl->rqueue_size > 0)
597 return rtnl_poll(nl, false, timeout_usec);
600 static int timeout_compare(const void *a, const void *b) {
601 const struct reply_callback *x = a, *y = b;
603 if (x->timeout != 0 && y->timeout == 0)
606 if (x->timeout == 0 && y->timeout != 0)
609 if (x->timeout < y->timeout)
612 if (x->timeout > y->timeout)
618 int sd_rtnl_call_async(sd_rtnl *nl,
620 sd_rtnl_message_handler_t callback,
624 struct reply_callback *c;
628 assert_return(nl, -EINVAL);
629 assert_return(m, -EINVAL);
630 assert_return(callback, -EINVAL);
631 assert_return(!rtnl_pid_changed(nl), -ECHILD);
633 r = hashmap_ensure_allocated(&nl->reply_callbacks, &uint64_hash_ops);
637 if (usec != (uint64_t) -1) {
638 r = prioq_ensure_allocated(&nl->reply_callbacks_prioq, timeout_compare);
643 c = new0(struct reply_callback, 1);
647 c->callback = callback;
648 c->userdata = userdata;
649 c->timeout = calc_elapse(usec);
651 k = sd_rtnl_send(nl, m, &s);
659 r = hashmap_put(nl->reply_callbacks, &c->serial, c);
665 if (c->timeout != 0) {
666 r = prioq_put(nl->reply_callbacks_prioq, c, &c->prioq_idx);
669 sd_rtnl_call_async_cancel(nl, c->serial);
680 int sd_rtnl_call_async_cancel(sd_rtnl *nl, uint32_t serial) {
681 struct reply_callback *c;
684 assert_return(nl, -EINVAL);
685 assert_return(serial != 0, -EINVAL);
686 assert_return(!rtnl_pid_changed(nl), -ECHILD);
688 c = hashmap_remove(nl->reply_callbacks, &s);
693 prioq_remove(nl->reply_callbacks_prioq, c, &c->prioq_idx);
699 int sd_rtnl_call(sd_rtnl *rtnl,
700 sd_rtnl_message *message,
702 sd_rtnl_message **ret) {
708 assert_return(rtnl, -EINVAL);
709 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
710 assert_return(message, -EINVAL);
712 r = sd_rtnl_send(rtnl, message, &serial);
716 timeout = calc_elapse(usec);
721 while (i < rtnl->rqueue_size) {
722 sd_rtnl_message *incoming;
723 uint32_t received_serial;
725 incoming = rtnl->rqueue[i];
726 received_serial = rtnl_message_get_serial(incoming);
728 if (received_serial == serial) {
729 /* found a match, remove from rqueue and return it */
730 memmove(rtnl->rqueue + i,rtnl->rqueue + i + 1,
731 sizeof(sd_rtnl_message*) * (rtnl->rqueue_size - i - 1));
734 r = sd_rtnl_message_get_errno(incoming);
736 sd_rtnl_message_unref(incoming);
743 sd_rtnl_message_unref(incoming);
748 /* Try to read more, right away */
752 r = socket_read_message(rtnl);
756 /* received message, so try to process straight away */
762 n = now(CLOCK_MONOTONIC);
768 left = (uint64_t) -1;
770 r = rtnl_poll(rtnl, true, left);
776 r = dispatch_wqueue(rtnl);
782 int sd_rtnl_flush(sd_rtnl *rtnl) {
785 assert_return(rtnl, -EINVAL);
786 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
788 if (rtnl->wqueue_size <= 0)
792 r = dispatch_wqueue(rtnl);
796 if (rtnl->wqueue_size <= 0)
799 r = rtnl_poll(rtnl, false, (uint64_t) -1);
805 int sd_rtnl_get_events(sd_rtnl *rtnl) {
808 assert_return(rtnl, -EINVAL);
809 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
811 if (rtnl->rqueue_size <= 0)
813 if (rtnl->wqueue_size > 0)
819 int sd_rtnl_get_timeout(sd_rtnl *rtnl, uint64_t *timeout_usec) {
820 struct reply_callback *c;
822 assert_return(rtnl, -EINVAL);
823 assert_return(timeout_usec, -EINVAL);
824 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
826 if (rtnl->rqueue_size > 0) {
831 c = prioq_peek(rtnl->reply_callbacks_prioq);
833 *timeout_usec = (uint64_t) -1;
837 *timeout_usec = c->timeout;
842 static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
843 sd_rtnl *rtnl = userdata;
848 r = sd_rtnl_process(rtnl, NULL);
855 static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) {
856 sd_rtnl *rtnl = userdata;
861 r = sd_rtnl_process(rtnl, NULL);
868 static int prepare_callback(sd_event_source *s, void *userdata) {
869 sd_rtnl *rtnl = userdata;
876 e = sd_rtnl_get_events(rtnl);
880 r = sd_event_source_set_io_events(rtnl->io_event_source, e);
884 r = sd_rtnl_get_timeout(rtnl, &until);
890 j = sd_event_source_set_time(rtnl->time_event_source, until);
895 r = sd_event_source_set_enabled(rtnl->time_event_source, r > 0);
902 static int exit_callback(sd_event_source *event, void *userdata) {
903 sd_rtnl *rtnl = userdata;
912 int sd_rtnl_attach_event(sd_rtnl *rtnl, sd_event *event, int priority) {
915 assert_return(rtnl, -EINVAL);
916 assert_return(!rtnl->event, -EBUSY);
918 assert(!rtnl->io_event_source);
919 assert(!rtnl->time_event_source);
922 rtnl->event = sd_event_ref(event);
924 r = sd_event_default(&rtnl->event);
929 r = sd_event_add_io(rtnl->event, &rtnl->io_event_source, rtnl->fd, 0, io_callback, rtnl);
933 r = sd_event_source_set_priority(rtnl->io_event_source, priority);
937 r = sd_event_source_set_description(rtnl->io_event_source, "rtnl-receive-message");
941 r = sd_event_source_set_prepare(rtnl->io_event_source, prepare_callback);
945 r = sd_event_add_time(rtnl->event, &rtnl->time_event_source, CLOCK_MONOTONIC, 0, 0, time_callback, rtnl);
949 r = sd_event_source_set_priority(rtnl->time_event_source, priority);
953 r = sd_event_source_set_description(rtnl->time_event_source, "rtnl-timer");
957 r = sd_event_add_exit(rtnl->event, &rtnl->exit_event_source, exit_callback, rtnl);
961 r = sd_event_source_set_description(rtnl->exit_event_source, "rtnl-exit");
968 sd_rtnl_detach_event(rtnl);
972 int sd_rtnl_detach_event(sd_rtnl *rtnl) {
973 assert_return(rtnl, -EINVAL);
974 assert_return(rtnl->event, -ENXIO);
976 if (rtnl->io_event_source)
977 rtnl->io_event_source = sd_event_source_unref(rtnl->io_event_source);
979 if (rtnl->time_event_source)
980 rtnl->time_event_source = sd_event_source_unref(rtnl->time_event_source);
982 if (rtnl->exit_event_source)
983 rtnl->exit_event_source = sd_event_source_unref(rtnl->exit_event_source);
986 rtnl->event = sd_event_unref(rtnl->event);
991 int sd_rtnl_add_match(sd_rtnl *rtnl,
993 sd_rtnl_message_handler_t callback,
995 struct match_callback *c;
997 assert_return(rtnl, -EINVAL);
998 assert_return(callback, -EINVAL);
999 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
1000 assert_return(rtnl_message_type_is_link(type) ||
1001 rtnl_message_type_is_addr(type) ||
1002 rtnl_message_type_is_route(type), -EOPNOTSUPP);
1004 c = new0(struct match_callback, 1);
1008 c->callback = callback;
1010 c->userdata = userdata;
1012 LIST_PREPEND(match_callbacks, rtnl->match_callbacks, c);
1017 int sd_rtnl_remove_match(sd_rtnl *rtnl,
1019 sd_rtnl_message_handler_t callback,
1021 struct match_callback *c;
1023 assert_return(rtnl, -EINVAL);
1024 assert_return(callback, -EINVAL);
1025 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
1027 LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks)
1028 if (c->callback == callback && c->type == type && c->userdata == userdata) {
1029 LIST_REMOVE(match_callbacks, rtnl->match_callbacks, c);