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 if (sd_rtnl_message_is_broadcast(m))
434 serial = rtnl_message_get_serial(m);
435 c = hashmap_remove(rtnl->reply_callbacks, &serial);
440 prioq_remove(rtnl->reply_callbacks_prioq, c, &c->prioq_idx);
442 r = c->callback(rtnl, m, c->userdata);
444 log_debug_errno(r, "sd-rtnl: callback failed: %m");
451 static int process_match(sd_rtnl *rtnl, sd_rtnl_message *m) {
452 struct match_callback *c;
459 r = sd_rtnl_message_get_type(m, &type);
463 LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks) {
464 if (type == c->type) {
465 r = c->callback(rtnl, m, c->userdata);
468 log_debug_errno(r, "sd-rtnl: match callback failed: %m");
478 static int process_running(sd_rtnl *rtnl, sd_rtnl_message **ret) {
479 _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
484 r = process_timeout(rtnl);
488 r = dispatch_wqueue(rtnl);
492 r = dispatch_rqueue(rtnl, &m);
498 r = process_reply(rtnl, m);
502 r = process_match(rtnl, m);
522 int sd_rtnl_process(sd_rtnl *rtnl, sd_rtnl_message **ret) {
523 RTNL_DONT_DESTROY(rtnl);
526 assert_return(rtnl, -EINVAL);
527 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
528 assert_return(!rtnl->processing, -EBUSY);
530 rtnl->processing = true;
531 r = process_running(rtnl, ret);
532 rtnl->processing = false;
537 static usec_t calc_elapse(uint64_t usec) {
538 if (usec == (uint64_t) -1)
542 usec = RTNL_DEFAULT_TIMEOUT;
544 return now(CLOCK_MONOTONIC) + usec;
547 static int rtnl_poll(sd_rtnl *rtnl, bool need_more, uint64_t timeout_usec) {
548 struct pollfd p[1] = {};
550 usec_t m = USEC_INFINITY;
555 e = sd_rtnl_get_events(rtnl);
560 /* Caller wants more data, and doesn't care about
561 * what's been read or any other timeouts. */
565 /* Caller wants to process if there is something to
566 * process, but doesn't care otherwise */
568 r = sd_rtnl_get_timeout(rtnl, &until);
573 nw = now(CLOCK_MONOTONIC);
574 m = until > nw ? until - nw : 0;
578 if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m))
584 r = ppoll(p, 1, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL);
588 return r > 0 ? 1 : 0;
591 int sd_rtnl_wait(sd_rtnl *nl, uint64_t timeout_usec) {
592 assert_return(nl, -EINVAL);
593 assert_return(!rtnl_pid_changed(nl), -ECHILD);
595 if (nl->rqueue_size > 0)
598 return rtnl_poll(nl, false, timeout_usec);
601 static int timeout_compare(const void *a, const void *b) {
602 const struct reply_callback *x = a, *y = b;
604 if (x->timeout != 0 && y->timeout == 0)
607 if (x->timeout == 0 && y->timeout != 0)
610 if (x->timeout < y->timeout)
613 if (x->timeout > y->timeout)
619 int sd_rtnl_call_async(sd_rtnl *nl,
621 sd_rtnl_message_handler_t callback,
625 struct reply_callback *c;
629 assert_return(nl, -EINVAL);
630 assert_return(m, -EINVAL);
631 assert_return(callback, -EINVAL);
632 assert_return(!rtnl_pid_changed(nl), -ECHILD);
634 r = hashmap_ensure_allocated(&nl->reply_callbacks, &uint64_hash_ops);
638 if (usec != (uint64_t) -1) {
639 r = prioq_ensure_allocated(&nl->reply_callbacks_prioq, timeout_compare);
644 c = new0(struct reply_callback, 1);
648 c->callback = callback;
649 c->userdata = userdata;
650 c->timeout = calc_elapse(usec);
652 k = sd_rtnl_send(nl, m, &s);
660 r = hashmap_put(nl->reply_callbacks, &c->serial, c);
666 if (c->timeout != 0) {
667 r = prioq_put(nl->reply_callbacks_prioq, c, &c->prioq_idx);
670 sd_rtnl_call_async_cancel(nl, c->serial);
681 int sd_rtnl_call_async_cancel(sd_rtnl *nl, uint32_t serial) {
682 struct reply_callback *c;
685 assert_return(nl, -EINVAL);
686 assert_return(serial != 0, -EINVAL);
687 assert_return(!rtnl_pid_changed(nl), -ECHILD);
689 c = hashmap_remove(nl->reply_callbacks, &s);
694 prioq_remove(nl->reply_callbacks_prioq, c, &c->prioq_idx);
700 int sd_rtnl_call(sd_rtnl *rtnl,
701 sd_rtnl_message *message,
703 sd_rtnl_message **ret) {
709 assert_return(rtnl, -EINVAL);
710 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
711 assert_return(message, -EINVAL);
713 r = sd_rtnl_send(rtnl, message, &serial);
717 timeout = calc_elapse(usec);
722 while (i < rtnl->rqueue_size) {
723 sd_rtnl_message *incoming;
724 uint32_t received_serial;
726 incoming = rtnl->rqueue[i];
727 received_serial = rtnl_message_get_serial(incoming);
729 if (received_serial == serial) {
730 /* found a match, remove from rqueue and return it */
731 memmove(rtnl->rqueue + i,rtnl->rqueue + i + 1,
732 sizeof(sd_rtnl_message*) * (rtnl->rqueue_size - i - 1));
735 r = sd_rtnl_message_get_errno(incoming);
737 sd_rtnl_message_unref(incoming);
744 sd_rtnl_message_unref(incoming);
749 /* Try to read more, right away */
753 r = socket_read_message(rtnl);
757 /* received message, so try to process straight away */
763 n = now(CLOCK_MONOTONIC);
769 left = (uint64_t) -1;
771 r = rtnl_poll(rtnl, true, left);
777 r = dispatch_wqueue(rtnl);
783 int sd_rtnl_flush(sd_rtnl *rtnl) {
786 assert_return(rtnl, -EINVAL);
787 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
789 if (rtnl->wqueue_size <= 0)
793 r = dispatch_wqueue(rtnl);
797 if (rtnl->wqueue_size <= 0)
800 r = rtnl_poll(rtnl, false, (uint64_t) -1);
806 int sd_rtnl_get_events(sd_rtnl *rtnl) {
809 assert_return(rtnl, -EINVAL);
810 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
812 if (rtnl->rqueue_size <= 0)
814 if (rtnl->wqueue_size > 0)
820 int sd_rtnl_get_timeout(sd_rtnl *rtnl, uint64_t *timeout_usec) {
821 struct reply_callback *c;
823 assert_return(rtnl, -EINVAL);
824 assert_return(timeout_usec, -EINVAL);
825 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
827 if (rtnl->rqueue_size > 0) {
832 c = prioq_peek(rtnl->reply_callbacks_prioq);
834 *timeout_usec = (uint64_t) -1;
838 *timeout_usec = c->timeout;
843 static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
844 sd_rtnl *rtnl = userdata;
849 r = sd_rtnl_process(rtnl, NULL);
856 static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) {
857 sd_rtnl *rtnl = userdata;
862 r = sd_rtnl_process(rtnl, NULL);
869 static int prepare_callback(sd_event_source *s, void *userdata) {
870 sd_rtnl *rtnl = userdata;
877 e = sd_rtnl_get_events(rtnl);
881 r = sd_event_source_set_io_events(rtnl->io_event_source, e);
885 r = sd_rtnl_get_timeout(rtnl, &until);
891 j = sd_event_source_set_time(rtnl->time_event_source, until);
896 r = sd_event_source_set_enabled(rtnl->time_event_source, r > 0);
903 static int exit_callback(sd_event_source *event, void *userdata) {
904 sd_rtnl *rtnl = userdata;
913 int sd_rtnl_attach_event(sd_rtnl *rtnl, sd_event *event, int priority) {
916 assert_return(rtnl, -EINVAL);
917 assert_return(!rtnl->event, -EBUSY);
919 assert(!rtnl->io_event_source);
920 assert(!rtnl->time_event_source);
923 rtnl->event = sd_event_ref(event);
925 r = sd_event_default(&rtnl->event);
930 r = sd_event_add_io(rtnl->event, &rtnl->io_event_source, rtnl->fd, 0, io_callback, rtnl);
934 r = sd_event_source_set_priority(rtnl->io_event_source, priority);
938 r = sd_event_source_set_description(rtnl->io_event_source, "rtnl-receive-message");
942 r = sd_event_source_set_prepare(rtnl->io_event_source, prepare_callback);
946 r = sd_event_add_time(rtnl->event, &rtnl->time_event_source, CLOCK_MONOTONIC, 0, 0, time_callback, rtnl);
950 r = sd_event_source_set_priority(rtnl->time_event_source, priority);
954 r = sd_event_source_set_description(rtnl->time_event_source, "rtnl-timer");
958 r = sd_event_add_exit(rtnl->event, &rtnl->exit_event_source, exit_callback, rtnl);
962 r = sd_event_source_set_description(rtnl->exit_event_source, "rtnl-exit");
969 sd_rtnl_detach_event(rtnl);
973 int sd_rtnl_detach_event(sd_rtnl *rtnl) {
974 assert_return(rtnl, -EINVAL);
975 assert_return(rtnl->event, -ENXIO);
977 if (rtnl->io_event_source)
978 rtnl->io_event_source = sd_event_source_unref(rtnl->io_event_source);
980 if (rtnl->time_event_source)
981 rtnl->time_event_source = sd_event_source_unref(rtnl->time_event_source);
983 if (rtnl->exit_event_source)
984 rtnl->exit_event_source = sd_event_source_unref(rtnl->exit_event_source);
987 rtnl->event = sd_event_unref(rtnl->event);
992 int sd_rtnl_add_match(sd_rtnl *rtnl,
994 sd_rtnl_message_handler_t callback,
996 struct match_callback *c;
998 assert_return(rtnl, -EINVAL);
999 assert_return(callback, -EINVAL);
1000 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
1001 assert_return(rtnl_message_type_is_link(type) ||
1002 rtnl_message_type_is_addr(type) ||
1003 rtnl_message_type_is_route(type), -EOPNOTSUPP);
1005 c = new0(struct match_callback, 1);
1009 c->callback = callback;
1011 c->userdata = userdata;
1013 LIST_PREPEND(match_callbacks, rtnl->match_callbacks, c);
1018 int sd_rtnl_remove_match(sd_rtnl *rtnl,
1020 sd_rtnl_message_handler_t callback,
1022 struct match_callback *c;
1024 assert_return(rtnl, -EINVAL);
1025 assert_return(callback, -EINVAL);
1026 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
1028 LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks)
1029 if (c->callback == callback && c->type == type && c->userdata == userdata) {
1030 LIST_REMOVE(match_callbacks, rtnl->match_callbacks, c);