1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright (C) 2014 Tom Gundersen
4 Copyright (C) 2014 Susant Sahani
8 #include <linux/sockios.h>
12 #include "alloc-util.h"
14 #include "lldp-internal.h"
15 #include "lldp-neighbor.h"
16 #include "lldp-network.h"
17 #include "socket-util.h"
18 #include "ether-addr-util.h"
20 #define LLDP_DEFAULT_NEIGHBORS_MAX 128U
22 static void lldp_flush_neighbors(sd_lldp
*lldp
) {
27 while ((n
= hashmap_first(lldp
->neighbor_by_id
)))
28 lldp_neighbor_unlink(n
);
31 static void lldp_callback(sd_lldp
*lldp
, sd_lldp_event event
, sd_lldp_neighbor
*n
) {
34 log_lldp("Invoking callback for '%c'.", event
);
39 lldp
->callback(lldp
, event
, n
, lldp
->userdata
);
42 static int lldp_make_space(sd_lldp
*lldp
, size_t extra
) {
43 usec_t t
= USEC_INFINITY
;
48 /* Remove all entries that are past their TTL, and more until at least the specified number of extra entries
52 _cleanup_(sd_lldp_neighbor_unrefp
) sd_lldp_neighbor
*n
= NULL
;
54 n
= prioq_peek(lldp
->neighbor_by_expiry
);
58 sd_lldp_neighbor_ref(n
);
60 if (hashmap_size(lldp
->neighbor_by_id
) > LESS_BY(lldp
->neighbors_max
, extra
))
63 if (t
== USEC_INFINITY
)
64 t
= now(clock_boottime_or_monotonic());
70 lldp_neighbor_unlink(n
);
71 lldp_callback(lldp
, SD_LLDP_EVENT_REMOVED
, n
);
78 static bool lldp_keep_neighbor(sd_lldp
*lldp
, sd_lldp_neighbor
*n
) {
82 /* Don't keep data with a zero TTL */
86 /* Filter out data from the filter address */
87 if (!ether_addr_is_null(&lldp
->filter_address
) &&
88 ether_addr_equal(&lldp
->filter_address
, &n
->source_address
))
91 /* Only add if the neighbor has a capability we are interested in. Note that we also store all neighbors with
92 * no caps field set. */
93 if (n
->has_capabilities
&&
94 (n
->enabled_capabilities
& lldp
->capability_mask
) == 0)
97 /* Keep everything else */
101 static int lldp_start_timer(sd_lldp
*lldp
, sd_lldp_neighbor
*neighbor
);
103 static int lldp_add_neighbor(sd_lldp
*lldp
, sd_lldp_neighbor
*n
) {
104 _cleanup_(sd_lldp_neighbor_unrefp
) sd_lldp_neighbor
*old
= NULL
;
112 keep
= lldp_keep_neighbor(lldp
, n
);
114 /* First retrieve the old entry for this MSAP */
115 old
= hashmap_get(lldp
->neighbor_by_id
, &n
->id
);
117 sd_lldp_neighbor_ref(old
);
120 lldp_neighbor_unlink(old
);
121 lldp_callback(lldp
, SD_LLDP_EVENT_REMOVED
, old
);
125 if (lldp_neighbor_equal(n
, old
)) {
126 /* Is this equal, then restart the TTL counter, but don't do anyting else. */
127 old
->timestamp
= n
->timestamp
;
128 lldp_start_timer(lldp
, old
);
129 lldp_callback(lldp
, SD_LLDP_EVENT_REFRESHED
, old
);
133 /* Data changed, remove the old entry, and add a new one */
134 lldp_neighbor_unlink(old
);
139 /* Then, make room for at least one new neighbor */
140 lldp_make_space(lldp
, 1);
142 r
= hashmap_put(lldp
->neighbor_by_id
, &n
->id
, n
);
146 r
= prioq_put(lldp
->neighbor_by_expiry
, n
, &n
->prioq_idx
);
148 assert_se(hashmap_remove(lldp
->neighbor_by_id
, &n
->id
) == n
);
154 lldp_start_timer(lldp
, n
);
155 lldp_callback(lldp
, old
? SD_LLDP_EVENT_UPDATED
: SD_LLDP_EVENT_ADDED
, n
);
161 lldp_callback(lldp
, SD_LLDP_EVENT_REMOVED
, old
);
166 static int lldp_handle_datagram(sd_lldp
*lldp
, sd_lldp_neighbor
*n
) {
172 r
= lldp_neighbor_parse(n
);
173 if (r
== -EBADMSG
) /* Ignore bad messages */
178 r
= lldp_add_neighbor(lldp
, n
);
180 log_lldp_errno(r
, "Failed to add datagram. Ignoring.");
184 log_lldp("Successfully processed LLDP datagram.");
188 static int lldp_receive_datagram(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
189 _cleanup_(sd_lldp_neighbor_unrefp
) sd_lldp_neighbor
*n
= NULL
;
190 ssize_t space
, length
;
191 sd_lldp
*lldp
= userdata
;
197 space
= next_datagram_size_fd(fd
);
199 return log_lldp_errno(space
, "Failed to determine datagram size to read: %m");
201 n
= lldp_neighbor_new(space
);
205 length
= recv(fd
, LLDP_NEIGHBOR_RAW(n
), n
->raw_size
, MSG_DONTWAIT
);
207 if (IN_SET(errno
, EAGAIN
, EINTR
))
210 return log_lldp_errno(errno
, "Failed to read LLDP datagram: %m");
213 if ((size_t) length
!= n
->raw_size
) {
214 log_lldp("Packet size mismatch.");
218 /* Try to get the timestamp of this packet if it is known */
219 if (ioctl(fd
, SIOCGSTAMPNS
, &ts
) >= 0)
220 triple_timestamp_from_realtime(&n
->timestamp
, timespec_load(&ts
));
222 triple_timestamp_get(&n
->timestamp
);
224 return lldp_handle_datagram(lldp
, n
);
227 static void lldp_reset(sd_lldp
*lldp
) {
230 lldp
->timer_event_source
= sd_event_source_unref(lldp
->timer_event_source
);
231 lldp
->io_event_source
= sd_event_source_unref(lldp
->io_event_source
);
232 lldp
->fd
= safe_close(lldp
->fd
);
235 _public_
int sd_lldp_start(sd_lldp
*lldp
) {
238 assert_return(lldp
, -EINVAL
);
239 assert_return(lldp
->event
, -EINVAL
);
240 assert_return(lldp
->ifindex
> 0, -EINVAL
);
245 assert(!lldp
->io_event_source
);
247 lldp
->fd
= lldp_network_bind_raw_socket(lldp
->ifindex
);
251 r
= sd_event_add_io(lldp
->event
, &lldp
->io_event_source
, lldp
->fd
, EPOLLIN
, lldp_receive_datagram
, lldp
);
255 r
= sd_event_source_set_priority(lldp
->io_event_source
, lldp
->event_priority
);
259 (void) sd_event_source_set_description(lldp
->io_event_source
, "lldp-io");
261 log_lldp("Started LLDP client");
269 _public_
int sd_lldp_stop(sd_lldp
*lldp
) {
270 assert_return(lldp
, -EINVAL
);
275 log_lldp("Stopping LLDP client");
278 lldp_flush_neighbors(lldp
);
283 _public_
int sd_lldp_attach_event(sd_lldp
*lldp
, sd_event
*event
, int64_t priority
) {
286 assert_return(lldp
, -EINVAL
);
287 assert_return(lldp
->fd
< 0, -EBUSY
);
288 assert_return(!lldp
->event
, -EBUSY
);
291 lldp
->event
= sd_event_ref(event
);
293 r
= sd_event_default(&lldp
->event
);
298 lldp
->event_priority
= priority
;
303 _public_
int sd_lldp_detach_event(sd_lldp
*lldp
) {
305 assert_return(lldp
, -EINVAL
);
306 assert_return(lldp
->fd
< 0, -EBUSY
);
308 lldp
->event
= sd_event_unref(lldp
->event
);
312 _public_ sd_event
* sd_lldp_get_event(sd_lldp
*lldp
) {
313 assert_return(lldp
, NULL
);
318 _public_
int sd_lldp_set_callback(sd_lldp
*lldp
, sd_lldp_callback_t cb
, void *userdata
) {
319 assert_return(lldp
, -EINVAL
);
322 lldp
->userdata
= userdata
;
327 _public_
int sd_lldp_set_ifindex(sd_lldp
*lldp
, int ifindex
) {
328 assert_return(lldp
, -EINVAL
);
329 assert_return(ifindex
> 0, -EINVAL
);
330 assert_return(lldp
->fd
< 0, -EBUSY
);
332 lldp
->ifindex
= ifindex
;
336 _public_ sd_lldp
* sd_lldp_ref(sd_lldp
*lldp
) {
341 assert(lldp
->n_ref
> 0);
347 _public_ sd_lldp
* sd_lldp_unref(sd_lldp
*lldp
) {
352 assert(lldp
->n_ref
> 0);
359 sd_lldp_detach_event(lldp
);
360 lldp_flush_neighbors(lldp
);
362 hashmap_free(lldp
->neighbor_by_id
);
363 prioq_free(lldp
->neighbor_by_expiry
);
367 _public_
int sd_lldp_new(sd_lldp
**ret
) {
368 _cleanup_(sd_lldp_unrefp
) sd_lldp
*lldp
= NULL
;
371 assert_return(ret
, -EINVAL
);
373 lldp
= new0(sd_lldp
, 1);
379 lldp
->neighbors_max
= LLDP_DEFAULT_NEIGHBORS_MAX
;
380 lldp
->capability_mask
= (uint16_t) -1;
382 lldp
->neighbor_by_id
= hashmap_new(&lldp_neighbor_id_hash_ops
);
383 if (!lldp
->neighbor_by_id
)
386 r
= prioq_ensure_allocated(&lldp
->neighbor_by_expiry
, lldp_neighbor_prioq_compare_func
);
390 *ret
= TAKE_PTR(lldp
);
395 static int neighbor_compare_func(const void *a
, const void *b
) {
396 const sd_lldp_neighbor
* const*x
= a
, * const *y
= b
;
398 return lldp_neighbor_id_hash_ops
.compare(&(*x
)->id
, &(*y
)->id
);
401 static int on_timer_event(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
402 sd_lldp
*lldp
= userdata
;
405 r
= lldp_make_space(lldp
, 0);
407 return log_lldp_errno(r
, "Failed to make space: %m");
409 r
= lldp_start_timer(lldp
, NULL
);
411 return log_lldp_errno(r
, "Failed to restart timer: %m");
416 static int lldp_start_timer(sd_lldp
*lldp
, sd_lldp_neighbor
*neighbor
) {
423 lldp_neighbor_start_ttl(neighbor
);
425 n
= prioq_peek(lldp
->neighbor_by_expiry
);
428 if (lldp
->timer_event_source
)
429 return sd_event_source_set_enabled(lldp
->timer_event_source
, SD_EVENT_OFF
);
434 if (lldp
->timer_event_source
) {
435 r
= sd_event_source_set_time(lldp
->timer_event_source
, n
->until
);
439 return sd_event_source_set_enabled(lldp
->timer_event_source
, SD_EVENT_ONESHOT
);
445 r
= sd_event_add_time(lldp
->event
, &lldp
->timer_event_source
, clock_boottime_or_monotonic(), n
->until
, 0, on_timer_event
, lldp
);
449 r
= sd_event_source_set_priority(lldp
->timer_event_source
, lldp
->event_priority
);
453 (void) sd_event_source_set_description(lldp
->timer_event_source
, "lldp-timer");
457 _public_
int sd_lldp_get_neighbors(sd_lldp
*lldp
, sd_lldp_neighbor
***ret
) {
458 sd_lldp_neighbor
**l
= NULL
, *n
;
462 assert_return(lldp
, -EINVAL
);
463 assert_return(ret
, -EINVAL
);
465 if (hashmap_isempty(lldp
->neighbor_by_id
)) { /* Special shortcut */
470 l
= new0(sd_lldp_neighbor
*, hashmap_size(lldp
->neighbor_by_id
));
474 r
= lldp_start_timer(lldp
, NULL
);
480 HASHMAP_FOREACH(n
, lldp
->neighbor_by_id
, i
)
481 l
[k
++] = sd_lldp_neighbor_ref(n
);
483 assert((size_t) k
== hashmap_size(lldp
->neighbor_by_id
));
485 /* Return things in a stable order */
486 qsort(l
, k
, sizeof(sd_lldp_neighbor
*), neighbor_compare_func
);
492 _public_
int sd_lldp_set_neighbors_max(sd_lldp
*lldp
, uint64_t m
) {
493 assert_return(lldp
, -EINVAL
);
494 assert_return(m
<= 0, -EINVAL
);
496 lldp
->neighbors_max
= m
;
497 lldp_make_space(lldp
, 0);
502 _public_
int sd_lldp_match_capabilities(sd_lldp
*lldp
, uint16_t mask
) {
503 assert_return(lldp
, -EINVAL
);
504 assert_return(mask
!= 0, -EINVAL
);
506 lldp
->capability_mask
= mask
;
511 _public_
int sd_lldp_set_filter_address(sd_lldp
*lldp
, const struct ether_addr
*addr
) {
512 assert_return(lldp
, -EINVAL
);
514 /* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so
515 * that our own can be filtered out here. */
518 lldp
->filter_address
= *addr
;
520 zero(lldp
->filter_address
);