1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright (C) 2014 Tom Gundersen
6 Copyright (C) 2014 Susant Sahani
10 #include <linux/sockios.h>
14 #include "alloc-util.h"
16 #include "lldp-internal.h"
17 #include "lldp-neighbor.h"
18 #include "lldp-network.h"
19 #include "socket-util.h"
20 #include "ether-addr-util.h"
22 #define LLDP_DEFAULT_NEIGHBORS_MAX 128U
24 static void lldp_flush_neighbors(sd_lldp
*lldp
) {
29 while ((n
= hashmap_first(lldp
->neighbor_by_id
)))
30 lldp_neighbor_unlink(n
);
33 static void lldp_callback(sd_lldp
*lldp
, sd_lldp_event event
, sd_lldp_neighbor
*n
) {
36 log_lldp("Invoking callback for '%c'.", event
);
41 lldp
->callback(lldp
, event
, n
, lldp
->userdata
);
44 static int lldp_make_space(sd_lldp
*lldp
, size_t extra
) {
45 usec_t t
= USEC_INFINITY
;
50 /* Remove all entries that are past their TTL, and more until at least the specified number of extra entries
54 _cleanup_(sd_lldp_neighbor_unrefp
) sd_lldp_neighbor
*n
= NULL
;
56 n
= prioq_peek(lldp
->neighbor_by_expiry
);
60 sd_lldp_neighbor_ref(n
);
62 if (hashmap_size(lldp
->neighbor_by_id
) > LESS_BY(lldp
->neighbors_max
, extra
))
65 if (t
== USEC_INFINITY
)
66 t
= now(clock_boottime_or_monotonic());
72 lldp_neighbor_unlink(n
);
73 lldp_callback(lldp
, SD_LLDP_EVENT_REMOVED
, n
);
80 static bool lldp_keep_neighbor(sd_lldp
*lldp
, sd_lldp_neighbor
*n
) {
84 /* Don't keep data with a zero TTL */
88 /* Filter out data from the filter address */
89 if (!ether_addr_is_null(&lldp
->filter_address
) &&
90 ether_addr_equal(&lldp
->filter_address
, &n
->source_address
))
93 /* Only add if the neighbor has a capability we are interested in. Note that we also store all neighbors with
94 * no caps field set. */
95 if (n
->has_capabilities
&&
96 (n
->enabled_capabilities
& lldp
->capability_mask
) == 0)
99 /* Keep everything else */
103 static int lldp_start_timer(sd_lldp
*lldp
, sd_lldp_neighbor
*neighbor
);
105 static int lldp_add_neighbor(sd_lldp
*lldp
, sd_lldp_neighbor
*n
) {
106 _cleanup_(sd_lldp_neighbor_unrefp
) sd_lldp_neighbor
*old
= NULL
;
114 keep
= lldp_keep_neighbor(lldp
, n
);
116 /* First retrieve the old entry for this MSAP */
117 old
= hashmap_get(lldp
->neighbor_by_id
, &n
->id
);
119 sd_lldp_neighbor_ref(old
);
122 lldp_neighbor_unlink(old
);
123 lldp_callback(lldp
, SD_LLDP_EVENT_REMOVED
, old
);
127 if (lldp_neighbor_equal(n
, old
)) {
128 /* Is this equal, then restart the TTL counter, but don't do anyting else. */
129 old
->timestamp
= n
->timestamp
;
130 lldp_start_timer(lldp
, old
);
131 lldp_callback(lldp
, SD_LLDP_EVENT_REFRESHED
, old
);
135 /* Data changed, remove the old entry, and add a new one */
136 lldp_neighbor_unlink(old
);
141 /* Then, make room for at least one new neighbor */
142 lldp_make_space(lldp
, 1);
144 r
= hashmap_put(lldp
->neighbor_by_id
, &n
->id
, n
);
148 r
= prioq_put(lldp
->neighbor_by_expiry
, n
, &n
->prioq_idx
);
150 assert_se(hashmap_remove(lldp
->neighbor_by_id
, &n
->id
) == n
);
156 lldp_start_timer(lldp
, n
);
157 lldp_callback(lldp
, old
? SD_LLDP_EVENT_UPDATED
: SD_LLDP_EVENT_ADDED
, n
);
163 lldp_callback(lldp
, SD_LLDP_EVENT_REMOVED
, old
);
168 static int lldp_handle_datagram(sd_lldp
*lldp
, sd_lldp_neighbor
*n
) {
174 r
= lldp_neighbor_parse(n
);
175 if (r
== -EBADMSG
) /* Ignore bad messages */
180 r
= lldp_add_neighbor(lldp
, n
);
182 log_lldp_errno(r
, "Failed to add datagram. Ignoring.");
186 log_lldp("Successfully processed LLDP datagram.");
190 static int lldp_receive_datagram(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
191 _cleanup_(sd_lldp_neighbor_unrefp
) sd_lldp_neighbor
*n
= NULL
;
192 ssize_t space
, length
;
193 sd_lldp
*lldp
= userdata
;
199 space
= next_datagram_size_fd(fd
);
201 return log_lldp_errno(space
, "Failed to determine datagram size to read: %m");
203 n
= lldp_neighbor_new(space
);
207 length
= recv(fd
, LLDP_NEIGHBOR_RAW(n
), n
->raw_size
, MSG_DONTWAIT
);
209 if (IN_SET(errno
, EAGAIN
, EINTR
))
212 return log_lldp_errno(errno
, "Failed to read LLDP datagram: %m");
215 if ((size_t) length
!= n
->raw_size
) {
216 log_lldp("Packet size mismatch.");
220 /* Try to get the timestamp of this packet if it is known */
221 if (ioctl(fd
, SIOCGSTAMPNS
, &ts
) >= 0)
222 triple_timestamp_from_realtime(&n
->timestamp
, timespec_load(&ts
));
224 triple_timestamp_get(&n
->timestamp
);
226 return lldp_handle_datagram(lldp
, n
);
229 static void lldp_reset(sd_lldp
*lldp
) {
232 lldp
->timer_event_source
= sd_event_source_unref(lldp
->timer_event_source
);
233 lldp
->io_event_source
= sd_event_source_unref(lldp
->io_event_source
);
234 lldp
->fd
= safe_close(lldp
->fd
);
237 _public_
int sd_lldp_start(sd_lldp
*lldp
) {
240 assert_return(lldp
, -EINVAL
);
241 assert_return(lldp
->event
, -EINVAL
);
242 assert_return(lldp
->ifindex
> 0, -EINVAL
);
247 assert(!lldp
->io_event_source
);
249 lldp
->fd
= lldp_network_bind_raw_socket(lldp
->ifindex
);
253 r
= sd_event_add_io(lldp
->event
, &lldp
->io_event_source
, lldp
->fd
, EPOLLIN
, lldp_receive_datagram
, lldp
);
257 r
= sd_event_source_set_priority(lldp
->io_event_source
, lldp
->event_priority
);
261 (void) sd_event_source_set_description(lldp
->io_event_source
, "lldp-io");
263 log_lldp("Started LLDP client");
271 _public_
int sd_lldp_stop(sd_lldp
*lldp
) {
272 assert_return(lldp
, -EINVAL
);
277 log_lldp("Stopping LLDP client");
280 lldp_flush_neighbors(lldp
);
285 _public_
int sd_lldp_attach_event(sd_lldp
*lldp
, sd_event
*event
, int64_t priority
) {
288 assert_return(lldp
, -EINVAL
);
289 assert_return(lldp
->fd
< 0, -EBUSY
);
290 assert_return(!lldp
->event
, -EBUSY
);
293 lldp
->event
= sd_event_ref(event
);
295 r
= sd_event_default(&lldp
->event
);
300 lldp
->event_priority
= priority
;
305 _public_
int sd_lldp_detach_event(sd_lldp
*lldp
) {
307 assert_return(lldp
, -EINVAL
);
308 assert_return(lldp
->fd
< 0, -EBUSY
);
310 lldp
->event
= sd_event_unref(lldp
->event
);
314 _public_ sd_event
* sd_lldp_get_event(sd_lldp
*lldp
) {
315 assert_return(lldp
, NULL
);
320 _public_
int sd_lldp_set_callback(sd_lldp
*lldp
, sd_lldp_callback_t cb
, void *userdata
) {
321 assert_return(lldp
, -EINVAL
);
324 lldp
->userdata
= userdata
;
329 _public_
int sd_lldp_set_ifindex(sd_lldp
*lldp
, int ifindex
) {
330 assert_return(lldp
, -EINVAL
);
331 assert_return(ifindex
> 0, -EINVAL
);
332 assert_return(lldp
->fd
< 0, -EBUSY
);
334 lldp
->ifindex
= ifindex
;
338 _public_ sd_lldp
* sd_lldp_ref(sd_lldp
*lldp
) {
343 assert(lldp
->n_ref
> 0);
349 _public_ sd_lldp
* sd_lldp_unref(sd_lldp
*lldp
) {
354 assert(lldp
->n_ref
> 0);
361 sd_lldp_detach_event(lldp
);
362 lldp_flush_neighbors(lldp
);
364 hashmap_free(lldp
->neighbor_by_id
);
365 prioq_free(lldp
->neighbor_by_expiry
);
369 _public_
int sd_lldp_new(sd_lldp
**ret
) {
370 _cleanup_(sd_lldp_unrefp
) sd_lldp
*lldp
= NULL
;
373 assert_return(ret
, -EINVAL
);
375 lldp
= new0(sd_lldp
, 1);
381 lldp
->neighbors_max
= LLDP_DEFAULT_NEIGHBORS_MAX
;
382 lldp
->capability_mask
= (uint16_t) -1;
384 lldp
->neighbor_by_id
= hashmap_new(&lldp_neighbor_id_hash_ops
);
385 if (!lldp
->neighbor_by_id
)
388 r
= prioq_ensure_allocated(&lldp
->neighbor_by_expiry
, lldp_neighbor_prioq_compare_func
);
392 *ret
= TAKE_PTR(lldp
);
397 static int neighbor_compare_func(const void *a
, const void *b
) {
398 const sd_lldp_neighbor
* const*x
= a
, * const *y
= b
;
400 return lldp_neighbor_id_hash_ops
.compare(&(*x
)->id
, &(*y
)->id
);
403 static int on_timer_event(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
404 sd_lldp
*lldp
= userdata
;
407 r
= lldp_make_space(lldp
, 0);
409 return log_lldp_errno(r
, "Failed to make space: %m");
411 q
= lldp_start_timer(lldp
, NULL
);
413 return log_lldp_errno(q
, "Failed to restart timer: %m");
418 static int lldp_start_timer(sd_lldp
*lldp
, sd_lldp_neighbor
*neighbor
) {
425 lldp_neighbor_start_ttl(neighbor
);
427 n
= prioq_peek(lldp
->neighbor_by_expiry
);
430 if (lldp
->timer_event_source
)
431 return sd_event_source_set_enabled(lldp
->timer_event_source
, SD_EVENT_OFF
);
436 if (lldp
->timer_event_source
) {
437 r
= sd_event_source_set_time(lldp
->timer_event_source
, n
->until
);
441 return sd_event_source_set_enabled(lldp
->timer_event_source
, SD_EVENT_ONESHOT
);
447 r
= sd_event_add_time(lldp
->event
, &lldp
->timer_event_source
, clock_boottime_or_monotonic(), n
->until
, 0, on_timer_event
, lldp
);
451 r
= sd_event_source_set_priority(lldp
->timer_event_source
, lldp
->event_priority
);
455 (void) sd_event_source_set_description(lldp
->timer_event_source
, "lldp-timer");
459 _public_
int sd_lldp_get_neighbors(sd_lldp
*lldp
, sd_lldp_neighbor
***ret
) {
460 sd_lldp_neighbor
**l
= NULL
, *n
;
464 assert_return(lldp
, -EINVAL
);
465 assert_return(ret
, -EINVAL
);
467 if (hashmap_isempty(lldp
->neighbor_by_id
)) { /* Special shortcut */
472 l
= new0(sd_lldp_neighbor
*, hashmap_size(lldp
->neighbor_by_id
));
476 r
= lldp_start_timer(lldp
, NULL
);
482 HASHMAP_FOREACH(n
, lldp
->neighbor_by_id
, i
)
483 l
[k
++] = sd_lldp_neighbor_ref(n
);
485 assert((size_t) k
== hashmap_size(lldp
->neighbor_by_id
));
487 /* Return things in a stable order */
488 qsort(l
, k
, sizeof(sd_lldp_neighbor
*), neighbor_compare_func
);
494 _public_
int sd_lldp_set_neighbors_max(sd_lldp
*lldp
, uint64_t m
) {
495 assert_return(lldp
, -EINVAL
);
496 assert_return(m
<= 0, -EINVAL
);
498 lldp
->neighbors_max
= m
;
499 lldp_make_space(lldp
, 0);
504 _public_
int sd_lldp_match_capabilities(sd_lldp
*lldp
, uint16_t mask
) {
505 assert_return(lldp
, -EINVAL
);
506 assert_return(mask
!= 0, -EINVAL
);
508 lldp
->capability_mask
= mask
;
513 _public_
int sd_lldp_set_filter_address(sd_lldp
*lldp
, const struct ether_addr
*addr
) {
514 assert_return(lldp
, -EINVAL
);
516 /* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so
517 * that our own can be filtered out here. */
520 lldp
->filter_address
= *addr
;
522 zero(lldp
->filter_address
);