1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <linux/sockios.h>
7 #include "sd-lldp-rx.h"
9 #include "alloc-util.h"
10 #include "errno-util.h"
11 #include "ether-addr-util.h"
12 #include "event-util.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"
21 #include "socket-util.h"
22 #include "sort-util.h"
23 #include "string-table.h"
24 #include "string-util.h"
26 #define LLDP_DEFAULT_NEIGHBORS_MAX 128U
28 static 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",
35 DEFINE_STRING_TABLE_LOOKUP(lldp_rx_event
, sd_lldp_rx_event_t
);
37 static void lldp_rx_flush_neighbors(sd_lldp_rx
*lldp_rx
) {
40 hashmap_clear(lldp_rx
->neighbor_by_id
);
43 static void lldp_rx_callback(sd_lldp_rx
*lldp_rx
, sd_lldp_rx_event_t event
, sd_lldp_neighbor
*n
) {
45 assert(event
>= 0 && event
< _SD_LLDP_RX_EVENT_MAX
);
47 if (!lldp_rx
->callback
)
48 return (void) log_lldp_rx(lldp_rx
, "Received '%s' event.", lldp_rx_event_to_string(event
));
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
);
54 static int lldp_rx_make_space(sd_lldp_rx
*lldp_rx
, size_t extra
) {
55 usec_t t
= USEC_INFINITY
;
60 /* Remove all entries that are past their TTL, and more until at least the specified number of extra entries
64 _cleanup_(sd_lldp_neighbor_unrefp
) sd_lldp_neighbor
*n
= NULL
;
66 n
= prioq_peek(lldp_rx
->neighbor_by_expiry
);
70 sd_lldp_neighbor_ref(n
);
72 if (hashmap_size(lldp_rx
->neighbor_by_id
) > LESS_BY(lldp_rx
->neighbors_max
, extra
))
75 if (t
== USEC_INFINITY
)
76 t
= now(CLOCK_BOOTTIME
);
82 lldp_neighbor_unlink(n
);
83 lldp_rx_callback(lldp_rx
, SD_LLDP_RX_EVENT_REMOVED
, n
);
90 static bool lldp_rx_keep_neighbor(sd_lldp_rx
*lldp_rx
, sd_lldp_neighbor
*n
) {
94 /* Don't keep data with a zero TTL */
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
))
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)
109 /* Keep everything else */
113 static int lldp_rx_start_timer(sd_lldp_rx
*lldp_rx
, sd_lldp_neighbor
*neighbor
);
115 static 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
;
124 keep
= lldp_rx_keep_neighbor(lldp_rx
, n
);
126 /* First retrieve the old entry for this MSAP */
127 old
= hashmap_get(lldp_rx
->neighbor_by_id
, &n
->id
);
129 sd_lldp_neighbor_ref(old
);
132 lldp_neighbor_unlink(old
);
133 lldp_rx_callback(lldp_rx
, SD_LLDP_RX_EVENT_REMOVED
, old
);
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
);
145 /* Data changed, remove the old entry, and add a new one */
146 lldp_neighbor_unlink(old
);
151 /* Then, make room for at least one new neighbor */
152 lldp_rx_make_space(lldp_rx
, 1);
154 r
= hashmap_ensure_put(&lldp_rx
->neighbor_by_id
, &lldp_neighbor_hash_ops
, &n
->id
, n
);
158 r
= prioq_ensure_put(&lldp_rx
->neighbor_by_expiry
, lldp_neighbor_prioq_compare_func
, n
, &n
->prioq_idx
);
160 assert_se(hashmap_remove(lldp_rx
->neighbor_by_id
, &n
->id
) == n
);
164 n
->lldp_rx
= lldp_rx
;
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
);
173 lldp_rx_callback(lldp_rx
, SD_LLDP_RX_EVENT_REMOVED
, old
);
178 static int lldp_rx_handle_datagram(sd_lldp_rx
*lldp_rx
, sd_lldp_neighbor
*n
) {
184 r
= lldp_neighbor_parse(n
);
188 r
= lldp_rx_add_neighbor(lldp_rx
, n
);
190 return log_lldp_rx_errno(lldp_rx
, r
, "Failed to add datagram. Ignoring.");
192 log_lldp_rx(lldp_rx
, "Successfully processed LLDP datagram.");
196 static 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
);
204 space
= next_datagram_size_fd(fd
);
205 if (ERRNO_IS_NEG_TRANSIENT(space
) || ERRNO_IS_NEG_DISCONNECT(space
))
208 log_lldp_rx_errno(lldp_rx
, space
, "Failed to determine datagram size to read, ignoring: %m");
212 n
= lldp_neighbor_new(space
);
218 length
= recv(fd
, LLDP_NEIGHBOR_RAW(n
), n
->raw_size
, MSG_DONTWAIT
);
220 if (ERRNO_IS_TRANSIENT(errno
) || ERRNO_IS_DISCONNECT(errno
))
223 log_lldp_rx_errno(lldp_rx
, errno
, "Failed to read LLDP datagram, ignoring: %m");
227 if ((size_t) length
!= n
->raw_size
) {
228 log_lldp_rx(lldp_rx
, "Packet size mismatch, ignoring");
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
));
236 triple_timestamp_now(&n
->timestamp
);
238 (void) lldp_rx_handle_datagram(lldp_rx
, n
);
242 static void lldp_rx_reset(sd_lldp_rx
*lldp_rx
) {
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
);
250 int sd_lldp_rx_is_running(sd_lldp_rx
*lldp_rx
) {
254 return lldp_rx
->fd
>= 0;
257 int sd_lldp_rx_start(sd_lldp_rx
*lldp_rx
) {
260 assert_return(lldp_rx
, -EINVAL
);
261 assert_return(lldp_rx
->event
, -EINVAL
);
262 assert_return(lldp_rx
->ifindex
> 0, -EINVAL
);
264 if (sd_lldp_rx_is_running(lldp_rx
))
267 assert(!lldp_rx
->io_event_source
);
269 lldp_rx
->fd
= lldp_network_bind_raw_socket(lldp_rx
->ifindex
);
273 r
= sd_event_add_io(lldp_rx
->event
, &lldp_rx
->io_event_source
, lldp_rx
->fd
, EPOLLIN
, lldp_rx_receive_datagram
, lldp_rx
);
277 r
= sd_event_source_set_priority(lldp_rx
->io_event_source
, lldp_rx
->event_priority
);
281 (void) sd_event_source_set_description(lldp_rx
->io_event_source
, "lldp-rx-io");
283 log_lldp_rx(lldp_rx
, "Started LLDP client");
287 lldp_rx_reset(lldp_rx
);
291 int sd_lldp_rx_stop(sd_lldp_rx
*lldp_rx
) {
292 if (!sd_lldp_rx_is_running(lldp_rx
))
295 log_lldp_rx(lldp_rx
, "Stopping LLDP client");
297 lldp_rx_reset(lldp_rx
);
298 lldp_rx_flush_neighbors(lldp_rx
);
303 int sd_lldp_rx_attach_event(sd_lldp_rx
*lldp_rx
, sd_event
*event
, int64_t priority
) {
306 assert_return(lldp_rx
, -EINVAL
);
307 assert_return(!sd_lldp_rx_is_running(lldp_rx
), -EBUSY
);
308 assert_return(!lldp_rx
->event
, -EBUSY
);
311 lldp_rx
->event
= sd_event_ref(event
);
313 r
= sd_event_default(&lldp_rx
->event
);
318 lldp_rx
->event_priority
= priority
;
323 int 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
);
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
);
333 sd_event
* sd_lldp_rx_get_event(sd_lldp_rx
*lldp_rx
) {
334 assert_return(lldp_rx
, NULL
);
336 return lldp_rx
->event
;
339 int sd_lldp_rx_set_callback(sd_lldp_rx
*lldp_rx
, sd_lldp_rx_callback_t cb
, void *userdata
) {
340 assert_return(lldp_rx
, -EINVAL
);
342 lldp_rx
->callback
= cb
;
343 lldp_rx
->userdata
= userdata
;
348 int 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
);
353 lldp_rx
->ifindex
= ifindex
;
357 int sd_lldp_rx_set_ifname(sd_lldp_rx
*lldp_rx
, const char *ifname
) {
358 assert_return(lldp_rx
, -EINVAL
);
359 assert_return(ifname
, -EINVAL
);
361 if (!ifname_valid_full(ifname
, IFNAME_VALID_ALTERNATIVE
))
364 return free_and_strdup(&lldp_rx
->ifname
, ifname
);
367 int sd_lldp_rx_get_ifname(sd_lldp_rx
*lldp_rx
, const char **ret
) {
370 assert_return(lldp_rx
, -EINVAL
);
372 r
= get_ifname(lldp_rx
->ifindex
, &lldp_rx
->ifname
);
377 *ret
= lldp_rx
->ifname
;
382 static sd_lldp_rx
*lldp_rx_free(sd_lldp_rx
*lldp_rx
) {
386 lldp_rx_reset(lldp_rx
);
388 sd_lldp_rx_detach_event(lldp_rx
);
390 lldp_rx_flush_neighbors(lldp_rx
);
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
);
398 DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_lldp_rx
, sd_lldp_rx
, lldp_rx_free
);
400 int sd_lldp_rx_new(sd_lldp_rx
**ret
) {
401 _cleanup_(sd_lldp_rx_unrefp
) sd_lldp_rx
*lldp_rx
= NULL
;
403 assert_return(ret
, -EINVAL
);
405 lldp_rx
= new(sd_lldp_rx
, 1);
409 *lldp_rx
= (sd_lldp_rx
) {
412 .neighbors_max
= LLDP_DEFAULT_NEIGHBORS_MAX
,
413 .capability_mask
= UINT16_MAX
,
416 *ret
= TAKE_PTR(lldp_rx
);
420 static int on_timer_event(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
421 sd_lldp_rx
*lldp_rx
= userdata
;
424 r
= lldp_rx_make_space(lldp_rx
, 0);
426 log_lldp_rx_errno(lldp_rx
, r
, "Failed to make space, ignoring: %m");
430 r
= lldp_rx_start_timer(lldp_rx
, NULL
);
432 log_lldp_rx_errno(lldp_rx
, r
, "Failed to restart timer, ignoring: %m");
439 static int lldp_rx_start_timer(sd_lldp_rx
*lldp_rx
, sd_lldp_neighbor
*neighbor
) {
443 assert(lldp_rx
->event
);
446 lldp_neighbor_start_ttl(neighbor
);
448 n
= prioq_peek(lldp_rx
->neighbor_by_expiry
);
450 return event_source_disable(lldp_rx
->timer_event_source
);
452 return event_reset_time(lldp_rx
->event
, &lldp_rx
->timer_event_source
,
455 on_timer_event
, lldp_rx
,
456 lldp_rx
->event_priority
, "lldp-rx-timer", true);
459 static int neighbor_compare_func(sd_lldp_neighbor
* const *a
, sd_lldp_neighbor
* const *b
) {
465 return lldp_neighbor_id_compare_func(&(*a
)->id
, &(*b
)->id
);
468 int sd_lldp_rx_get_neighbors(sd_lldp_rx
*lldp_rx
, sd_lldp_neighbor
***ret
) {
469 _cleanup_free_ sd_lldp_neighbor
**l
= NULL
;
473 assert_return(lldp_rx
, -EINVAL
);
474 assert_return(ret
, -EINVAL
);
476 if (hashmap_isempty(lldp_rx
->neighbor_by_id
)) { /* Special shortcut */
481 l
= new0(sd_lldp_neighbor
*, hashmap_size(lldp_rx
->neighbor_by_id
));
485 HASHMAP_FOREACH(n
, lldp_rx
->neighbor_by_id
)
486 l
[k
++] = sd_lldp_neighbor_ref(n
);
488 assert((size_t) k
== hashmap_size(lldp_rx
->neighbor_by_id
));
490 /* Return things in a stable order */
491 typesafe_qsort(l
, k
, neighbor_compare_func
);
497 int 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
;
505 HASHMAP_FOREACH(n
, lldp_rx
->neighbor_by_id
) {
506 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*w
= NULL
;
508 r
= lldp_neighbor_build_json(n
, &w
);
512 r
= sd_json_variant_append_array(&v
, w
);
521 int 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
);
525 lldp_rx
->neighbors_max
= m
;
526 lldp_rx_make_space(lldp_rx
, 0);
531 int 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
);
535 lldp_rx
->capability_mask
= mask
;
540 int sd_lldp_rx_set_filter_address(sd_lldp_rx
*lldp_rx
, const struct ether_addr
*addr
) {
541 assert_return(lldp_rx
, -EINVAL
);
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. */
547 lldp_rx
->filter_address
= *addr
;
549 zero(lldp_rx
->filter_address
);