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