]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/libsystemd-network/sd-lldp-rx.c
ci: enable arm64 runner for build/unit jobs
[thirdparty/systemd.git] / src / libsystemd-network / sd-lldp-rx.c
... / ...
CommitLineData
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include <linux/sockios.h>
4#include <sys/ioctl.h>
5
6#include "sd-json.h"
7#include "sd-lldp-rx.h"
8
9#include "alloc-util.h"
10#include "errno-util.h"
11#include "ether-addr-util.h"
12#include "event-util.h"
13#include "fd-util.h"
14#include "hashmap.h"
15#include "lldp-neighbor.h"
16#include "lldp-network.h"
17#include "lldp-rx-internal.h"
18#include "memory-util.h"
19#include "network-common.h"
20#include "prioq.h"
21#include "socket-util.h"
22#include "sort-util.h"
23#include "string-table.h"
24#include "string-util.h"
25
26#define LLDP_DEFAULT_NEIGHBORS_MAX 128U
27
28static const char * const lldp_rx_event_table[_SD_LLDP_RX_EVENT_MAX] = {
29 [SD_LLDP_RX_EVENT_ADDED] = "added",
30 [SD_LLDP_RX_EVENT_REMOVED] = "removed",
31 [SD_LLDP_RX_EVENT_UPDATED] = "updated",
32 [SD_LLDP_RX_EVENT_REFRESHED] = "refreshed",
33};
34
35DEFINE_STRING_TABLE_LOOKUP(lldp_rx_event, sd_lldp_rx_event_t);
36
37static void lldp_rx_flush_neighbors(sd_lldp_rx *lldp_rx) {
38 assert(lldp_rx);
39
40 hashmap_clear(lldp_rx->neighbor_by_id);
41}
42
43static void lldp_rx_callback(sd_lldp_rx *lldp_rx, sd_lldp_rx_event_t event, sd_lldp_neighbor *n) {
44 assert(lldp_rx);
45 assert(event >= 0 && event < _SD_LLDP_RX_EVENT_MAX);
46
47 if (!lldp_rx->callback)
48 return (void) log_lldp_rx(lldp_rx, "Received '%s' event.", lldp_rx_event_to_string(event));
49
50 log_lldp_rx(lldp_rx, "Invoking callback for '%s' event.", lldp_rx_event_to_string(event));
51 lldp_rx->callback(lldp_rx, event, n, lldp_rx->userdata);
52}
53
54static int lldp_rx_make_space(sd_lldp_rx *lldp_rx, size_t extra) {
55 usec_t t = USEC_INFINITY;
56 bool changed = false;
57
58 assert(lldp_rx);
59
60 /* Remove all entries that are past their TTL, and more until at least the specified number of extra entries
61 * are free. */
62
63 for (;;) {
64 _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
65
66 n = prioq_peek(lldp_rx->neighbor_by_expiry);
67 if (!n)
68 break;
69
70 sd_lldp_neighbor_ref(n);
71
72 if (hashmap_size(lldp_rx->neighbor_by_id) > LESS_BY(lldp_rx->neighbors_max, extra))
73 goto remove_one;
74
75 if (t == USEC_INFINITY)
76 t = now(CLOCK_BOOTTIME);
77
78 if (n->until > t)
79 break;
80
81 remove_one:
82 lldp_neighbor_unlink(n);
83 lldp_rx_callback(lldp_rx, SD_LLDP_RX_EVENT_REMOVED, n);
84 changed = true;
85 }
86
87 return changed;
88}
89
90static bool lldp_rx_keep_neighbor(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *n) {
91 assert(lldp_rx);
92 assert(n);
93
94 /* Don't keep data with a zero TTL */
95 if (n->ttl <= 0)
96 return false;
97
98 /* Filter out data from the filter address */
99 if (!ether_addr_is_null(&lldp_rx->filter_address) &&
100 ether_addr_equal(&lldp_rx->filter_address, &n->source_address))
101 return false;
102
103 /* Only add if the neighbor has a capability we are interested in. Note that we also store all neighbors with
104 * no caps field set. */
105 if (n->has_capabilities &&
106 (n->enabled_capabilities & lldp_rx->capability_mask) == 0)
107 return false;
108
109 /* Keep everything else */
110 return true;
111}
112
113static int lldp_rx_start_timer(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *neighbor);
114
115static int lldp_rx_add_neighbor(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *n) {
116 _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *old = NULL;
117 bool keep;
118 int r;
119
120 assert(lldp_rx);
121 assert(n);
122 assert(!n->lldp_rx);
123
124 keep = lldp_rx_keep_neighbor(lldp_rx, n);
125
126 /* First retrieve the old entry for this MSAP */
127 old = hashmap_get(lldp_rx->neighbor_by_id, &n->id);
128 if (old) {
129 sd_lldp_neighbor_ref(old);
130
131 if (!keep) {
132 lldp_neighbor_unlink(old);
133 lldp_rx_callback(lldp_rx, SD_LLDP_RX_EVENT_REMOVED, old);
134 return 0;
135 }
136
137 if (lldp_neighbor_equal(n, old)) {
138 /* Is this equal, then restart the TTL counter, but don't do anything else. */
139 old->timestamp = n->timestamp;
140 lldp_rx_start_timer(lldp_rx, old);
141 lldp_rx_callback(lldp_rx, SD_LLDP_RX_EVENT_REFRESHED, old);
142 return 0;
143 }
144
145 /* Data changed, remove the old entry, and add a new one */
146 lldp_neighbor_unlink(old);
147
148 } else if (!keep)
149 return 0;
150
151 /* Then, make room for at least one new neighbor */
152 lldp_rx_make_space(lldp_rx, 1);
153
154 r = hashmap_ensure_put(&lldp_rx->neighbor_by_id, &lldp_neighbor_hash_ops, &n->id, n);
155 if (r < 0)
156 goto finish;
157
158 r = prioq_ensure_put(&lldp_rx->neighbor_by_expiry, lldp_neighbor_prioq_compare_func, n, &n->prioq_idx);
159 if (r < 0) {
160 assert_se(hashmap_remove(lldp_rx->neighbor_by_id, &n->id) == n);
161 goto finish;
162 }
163
164 n->lldp_rx = lldp_rx;
165
166 lldp_rx_start_timer(lldp_rx, n);
167 lldp_rx_callback(lldp_rx, old ? SD_LLDP_RX_EVENT_UPDATED : SD_LLDP_RX_EVENT_ADDED, n);
168
169 return 1;
170
171finish:
172 if (old)
173 lldp_rx_callback(lldp_rx, SD_LLDP_RX_EVENT_REMOVED, old);
174
175 return r;
176}
177
178static int lldp_rx_handle_datagram(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *n) {
179 int r;
180
181 assert(lldp_rx);
182 assert(n);
183
184 r = lldp_neighbor_parse(n);
185 if (r < 0)
186 return r;
187
188 r = lldp_rx_add_neighbor(lldp_rx, n);
189 if (r < 0)
190 return log_lldp_rx_errno(lldp_rx, r, "Failed to add datagram. Ignoring.");
191
192 log_lldp_rx(lldp_rx, "Successfully processed LLDP datagram.");
193 return 0;
194}
195
196static int lldp_rx_receive_datagram(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
197 _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
198 ssize_t space, length;
199 sd_lldp_rx *lldp_rx = ASSERT_PTR(userdata);
200 struct timespec ts;
201
202 assert(fd >= 0);
203
204 space = next_datagram_size_fd(fd);
205 if (ERRNO_IS_NEG_TRANSIENT(space) || ERRNO_IS_NEG_DISCONNECT(space))
206 return 0;
207 if (space < 0) {
208 log_lldp_rx_errno(lldp_rx, space, "Failed to determine datagram size to read, ignoring: %m");
209 return 0;
210 }
211
212 n = lldp_neighbor_new(space);
213 if (!n) {
214 log_oom_debug();
215 return 0;
216 }
217
218 length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT);
219 if (length < 0) {
220 if (ERRNO_IS_TRANSIENT(errno) || ERRNO_IS_DISCONNECT(errno))
221 return 0;
222
223 log_lldp_rx_errno(lldp_rx, errno, "Failed to read LLDP datagram, ignoring: %m");
224 return 0;
225 }
226
227 if ((size_t) length != n->raw_size) {
228 log_lldp_rx(lldp_rx, "Packet size mismatch, ignoring");
229 return 0;
230 }
231
232 /* Try to get the timestamp of this packet if it is known */
233 if (ioctl(fd, SIOCGSTAMPNS, &ts) >= 0)
234 triple_timestamp_from_realtime(&n->timestamp, timespec_load(&ts));
235 else
236 triple_timestamp_now(&n->timestamp);
237
238 (void) lldp_rx_handle_datagram(lldp_rx, n);
239 return 0;
240}
241
242static void lldp_rx_reset(sd_lldp_rx *lldp_rx) {
243 assert(lldp_rx);
244
245 (void) event_source_disable(lldp_rx->timer_event_source);
246 lldp_rx->io_event_source = sd_event_source_disable_unref(lldp_rx->io_event_source);
247 lldp_rx->fd = safe_close(lldp_rx->fd);
248}
249
250int sd_lldp_rx_is_running(sd_lldp_rx *lldp_rx) {
251 if (!lldp_rx)
252 return false;
253
254 return lldp_rx->fd >= 0;
255}
256
257int sd_lldp_rx_start(sd_lldp_rx *lldp_rx) {
258 int r;
259
260 assert_return(lldp_rx, -EINVAL);
261 assert_return(lldp_rx->event, -EINVAL);
262 assert_return(lldp_rx->ifindex > 0, -EINVAL);
263
264 if (sd_lldp_rx_is_running(lldp_rx))
265 return 0;
266
267 assert(!lldp_rx->io_event_source);
268
269 lldp_rx->fd = lldp_network_bind_raw_socket(lldp_rx->ifindex);
270 if (lldp_rx->fd < 0)
271 return lldp_rx->fd;
272
273 r = sd_event_add_io(lldp_rx->event, &lldp_rx->io_event_source, lldp_rx->fd, EPOLLIN, lldp_rx_receive_datagram, lldp_rx);
274 if (r < 0)
275 goto fail;
276
277 r = sd_event_source_set_priority(lldp_rx->io_event_source, lldp_rx->event_priority);
278 if (r < 0)
279 goto fail;
280
281 (void) sd_event_source_set_description(lldp_rx->io_event_source, "lldp-rx-io");
282
283 log_lldp_rx(lldp_rx, "Started LLDP client");
284 return 1;
285
286fail:
287 lldp_rx_reset(lldp_rx);
288 return r;
289}
290
291int sd_lldp_rx_stop(sd_lldp_rx *lldp_rx) {
292 if (!sd_lldp_rx_is_running(lldp_rx))
293 return 0;
294
295 log_lldp_rx(lldp_rx, "Stopping LLDP client");
296
297 lldp_rx_reset(lldp_rx);
298 lldp_rx_flush_neighbors(lldp_rx);
299
300 return 1;
301}
302
303int sd_lldp_rx_attach_event(sd_lldp_rx *lldp_rx, sd_event *event, int64_t priority) {
304 int r;
305
306 assert_return(lldp_rx, -EINVAL);
307 assert_return(!sd_lldp_rx_is_running(lldp_rx), -EBUSY);
308 assert_return(!lldp_rx->event, -EBUSY);
309
310 if (event)
311 lldp_rx->event = sd_event_ref(event);
312 else {
313 r = sd_event_default(&lldp_rx->event);
314 if (r < 0)
315 return r;
316 }
317
318 lldp_rx->event_priority = priority;
319
320 return 0;
321}
322
323int sd_lldp_rx_detach_event(sd_lldp_rx *lldp_rx) {
324 assert_return(lldp_rx, -EINVAL);
325 assert_return(!sd_lldp_rx_is_running(lldp_rx), -EBUSY);
326
327 lldp_rx->io_event_source = sd_event_source_disable_unref(lldp_rx->io_event_source);
328 lldp_rx->timer_event_source = sd_event_source_disable_unref(lldp_rx->timer_event_source);
329 lldp_rx->event = sd_event_unref(lldp_rx->event);
330 return 0;
331}
332
333sd_event* sd_lldp_rx_get_event(sd_lldp_rx *lldp_rx) {
334 assert_return(lldp_rx, NULL);
335
336 return lldp_rx->event;
337}
338
339int sd_lldp_rx_set_callback(sd_lldp_rx *lldp_rx, sd_lldp_rx_callback_t cb, void *userdata) {
340 assert_return(lldp_rx, -EINVAL);
341
342 lldp_rx->callback = cb;
343 lldp_rx->userdata = userdata;
344
345 return 0;
346}
347
348int sd_lldp_rx_set_ifindex(sd_lldp_rx *lldp_rx, int ifindex) {
349 assert_return(lldp_rx, -EINVAL);
350 assert_return(ifindex > 0, -EINVAL);
351 assert_return(!sd_lldp_rx_is_running(lldp_rx), -EBUSY);
352
353 lldp_rx->ifindex = ifindex;
354 return 0;
355}
356
357int sd_lldp_rx_set_ifname(sd_lldp_rx *lldp_rx, const char *ifname) {
358 assert_return(lldp_rx, -EINVAL);
359 assert_return(ifname, -EINVAL);
360
361 if (!ifname_valid_full(ifname, IFNAME_VALID_ALTERNATIVE))
362 return -EINVAL;
363
364 return free_and_strdup(&lldp_rx->ifname, ifname);
365}
366
367int sd_lldp_rx_get_ifname(sd_lldp_rx *lldp_rx, const char **ret) {
368 int r;
369
370 assert_return(lldp_rx, -EINVAL);
371
372 r = get_ifname(lldp_rx->ifindex, &lldp_rx->ifname);
373 if (r < 0)
374 return r;
375
376 if (ret)
377 *ret = lldp_rx->ifname;
378
379 return 0;
380}
381
382static sd_lldp_rx *lldp_rx_free(sd_lldp_rx *lldp_rx) {
383 if (!lldp_rx)
384 return NULL;
385
386 lldp_rx_reset(lldp_rx);
387
388 sd_lldp_rx_detach_event(lldp_rx);
389
390 lldp_rx_flush_neighbors(lldp_rx);
391
392 hashmap_free(lldp_rx->neighbor_by_id);
393 prioq_free(lldp_rx->neighbor_by_expiry);
394 free(lldp_rx->ifname);
395 return mfree(lldp_rx);
396}
397
398DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_lldp_rx, sd_lldp_rx, lldp_rx_free);
399
400int sd_lldp_rx_new(sd_lldp_rx **ret) {
401 _cleanup_(sd_lldp_rx_unrefp) sd_lldp_rx *lldp_rx = NULL;
402
403 assert_return(ret, -EINVAL);
404
405 lldp_rx = new(sd_lldp_rx, 1);
406 if (!lldp_rx)
407 return -ENOMEM;
408
409 *lldp_rx = (sd_lldp_rx) {
410 .n_ref = 1,
411 .fd = -EBADF,
412 .neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX,
413 .capability_mask = UINT16_MAX,
414 };
415
416 *ret = TAKE_PTR(lldp_rx);
417 return 0;
418}
419
420static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) {
421 sd_lldp_rx *lldp_rx = userdata;
422 int r;
423
424 r = lldp_rx_make_space(lldp_rx, 0);
425 if (r < 0) {
426 log_lldp_rx_errno(lldp_rx, r, "Failed to make space, ignoring: %m");
427 return 0;
428 }
429
430 r = lldp_rx_start_timer(lldp_rx, NULL);
431 if (r < 0) {
432 log_lldp_rx_errno(lldp_rx, r, "Failed to restart timer, ignoring: %m");
433 return 0;
434 }
435
436 return 0;
437}
438
439static int lldp_rx_start_timer(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *neighbor) {
440 sd_lldp_neighbor *n;
441
442 assert(lldp_rx);
443 assert(lldp_rx->event);
444
445 if (neighbor)
446 lldp_neighbor_start_ttl(neighbor);
447
448 n = prioq_peek(lldp_rx->neighbor_by_expiry);
449 if (!n)
450 return event_source_disable(lldp_rx->timer_event_source);
451
452 return event_reset_time(lldp_rx->event, &lldp_rx->timer_event_source,
453 CLOCK_BOOTTIME,
454 n->until, 0,
455 on_timer_event, lldp_rx,
456 lldp_rx->event_priority, "lldp-rx-timer", true);
457}
458
459static int neighbor_compare_func(sd_lldp_neighbor * const *a, sd_lldp_neighbor * const *b) {
460 assert(a);
461 assert(b);
462 assert(*a);
463 assert(*b);
464
465 return lldp_neighbor_id_compare_func(&(*a)->id, &(*b)->id);
466}
467
468int sd_lldp_rx_get_neighbors(sd_lldp_rx *lldp_rx, sd_lldp_neighbor ***ret) {
469 _cleanup_free_ sd_lldp_neighbor **l = NULL;
470 sd_lldp_neighbor *n;
471 int k = 0;
472
473 assert_return(lldp_rx, -EINVAL);
474 assert_return(ret, -EINVAL);
475
476 if (hashmap_isempty(lldp_rx->neighbor_by_id)) { /* Special shortcut */
477 *ret = NULL;
478 return 0;
479 }
480
481 l = new0(sd_lldp_neighbor*, hashmap_size(lldp_rx->neighbor_by_id));
482 if (!l)
483 return -ENOMEM;
484
485 HASHMAP_FOREACH(n, lldp_rx->neighbor_by_id)
486 l[k++] = sd_lldp_neighbor_ref(n);
487
488 assert((size_t) k == hashmap_size(lldp_rx->neighbor_by_id));
489
490 /* Return things in a stable order */
491 typesafe_qsort(l, k, neighbor_compare_func);
492 *ret = TAKE_PTR(l);
493
494 return k;
495}
496
497int lldp_rx_build_neighbors_json(sd_lldp_rx *lldp_rx, sd_json_variant **ret) {
498 _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
499 int r;
500
501 assert(lldp_rx);
502 assert(ret);
503
504 sd_lldp_neighbor *n;
505 HASHMAP_FOREACH(n, lldp_rx->neighbor_by_id) {
506 _cleanup_(sd_json_variant_unrefp) sd_json_variant *w = NULL;
507
508 r = lldp_neighbor_build_json(n, &w);
509 if (r < 0)
510 return r;
511
512 r = sd_json_variant_append_array(&v, w);
513 if (r < 0)
514 return r;
515 }
516
517 *ret = TAKE_PTR(v);
518 return 0;
519}
520
521int sd_lldp_rx_set_neighbors_max(sd_lldp_rx *lldp_rx, uint64_t m) {
522 assert_return(lldp_rx, -EINVAL);
523 assert_return(m > 0, -EINVAL);
524
525 lldp_rx->neighbors_max = m;
526 lldp_rx_make_space(lldp_rx, 0);
527
528 return 0;
529}
530
531int sd_lldp_rx_match_capabilities(sd_lldp_rx *lldp_rx, uint16_t mask) {
532 assert_return(lldp_rx, -EINVAL);
533 assert_return(mask != 0, -EINVAL);
534
535 lldp_rx->capability_mask = mask;
536
537 return 0;
538}
539
540int sd_lldp_rx_set_filter_address(sd_lldp_rx *lldp_rx, const struct ether_addr *addr) {
541 assert_return(lldp_rx, -EINVAL);
542
543 /* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so
544 * that our own can be filtered out here. */
545
546 if (addr)
547 lldp_rx->filter_address = *addr;
548 else
549 zero(lldp_rx->filter_address);
550
551 return 0;
552}