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