2 This file is part of systemd.
4 Copyright (C) 2014 Tom Gundersen
5 Copyright (C) 2014 Susant Sahani
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 #include <arpa/inet.h>
22 #include <linux/sockios.h>
26 #include "alloc-util.h"
28 #include "lldp-internal.h"
29 #include "lldp-neighbor.h"
30 #include "lldp-network.h"
31 #include "socket-util.h"
32 #include "ether-addr-util.h"
34 #define LLDP_DEFAULT_NEIGHBORS_MAX 128U
36 static void lldp_flush_neighbors(sd_lldp
*lldp
) {
41 while ((n
= hashmap_first(lldp
->neighbor_by_id
)))
42 lldp_neighbor_unlink(n
);
45 static void lldp_callback(sd_lldp
*lldp
, sd_lldp_event event
, sd_lldp_neighbor
*n
) {
48 log_lldp("Invoking callback for '%c'.", event
);
53 lldp
->callback(lldp
, event
, n
, lldp
->userdata
);
56 static int lldp_make_space(sd_lldp
*lldp
, size_t extra
) {
57 usec_t t
= USEC_INFINITY
;
62 /* Remove all entries that are past their TTL, and more until at least the specified number of extra entries
66 _cleanup_(sd_lldp_neighbor_unrefp
) sd_lldp_neighbor
*n
= NULL
;
68 n
= prioq_peek(lldp
->neighbor_by_expiry
);
72 sd_lldp_neighbor_ref(n
);
74 if (hashmap_size(lldp
->neighbor_by_id
) > LESS_BY(lldp
->neighbors_max
, extra
))
77 if (t
== USEC_INFINITY
)
78 t
= now(clock_boottime_or_monotonic());
84 lldp_neighbor_unlink(n
);
85 lldp_callback(lldp
, SD_LLDP_EVENT_REMOVED
, n
);
92 static bool lldp_keep_neighbor(sd_lldp
*lldp
, sd_lldp_neighbor
*n
) {
96 /* Don't keep data with a zero TTL */
100 /* Filter out data from the filter address */
101 if (!ether_addr_is_null(&lldp
->filter_address
) &&
102 ether_addr_equal(&lldp
->filter_address
, &n
->source_address
))
105 /* Only add if the neighbor has a capability we are interested in. Note that we also store all neighbors with
106 * no caps field set. */
107 if (n
->has_capabilities
&&
108 (n
->enabled_capabilities
& lldp
->capability_mask
) == 0)
111 /* Keep everything else */
115 static int lldp_start_timer(sd_lldp
*lldp
, sd_lldp_neighbor
*neighbor
);
117 static int lldp_add_neighbor(sd_lldp
*lldp
, sd_lldp_neighbor
*n
) {
118 _cleanup_(sd_lldp_neighbor_unrefp
) sd_lldp_neighbor
*old
= NULL
;
126 keep
= lldp_keep_neighbor(lldp
, n
);
128 /* First retrieve the old entry for this MSAP */
129 old
= hashmap_get(lldp
->neighbor_by_id
, &n
->id
);
131 sd_lldp_neighbor_ref(old
);
134 lldp_neighbor_unlink(old
);
135 lldp_callback(lldp
, SD_LLDP_EVENT_REMOVED
, old
);
139 if (lldp_neighbor_equal(n
, old
)) {
140 /* Is this equal, then restart the TTL counter, but don't do anyting else. */
141 old
->timestamp
= n
->timestamp
;
142 lldp_start_timer(lldp
, old
);
143 lldp_callback(lldp
, SD_LLDP_EVENT_REFRESHED
, old
);
147 /* Data changed, remove the old entry, and add a new one */
148 lldp_neighbor_unlink(old
);
153 /* Then, make room for at least one new neighbor */
154 lldp_make_space(lldp
, 1);
156 r
= hashmap_put(lldp
->neighbor_by_id
, &n
->id
, n
);
160 r
= prioq_put(lldp
->neighbor_by_expiry
, n
, &n
->prioq_idx
);
162 assert_se(hashmap_remove(lldp
->neighbor_by_id
, &n
->id
) == n
);
168 lldp_start_timer(lldp
, n
);
169 lldp_callback(lldp
, old
? SD_LLDP_EVENT_UPDATED
: SD_LLDP_EVENT_ADDED
, n
);
175 lldp_callback(lldp
, SD_LLDP_EVENT_REMOVED
, old
);
180 static int lldp_handle_datagram(sd_lldp
*lldp
, sd_lldp_neighbor
*n
) {
186 r
= lldp_neighbor_parse(n
);
187 if (r
== -EBADMSG
) /* Ignore bad messages */
192 r
= lldp_add_neighbor(lldp
, n
);
194 log_lldp_errno(r
, "Failed to add datagram. Ignoring.");
198 log_lldp("Successfully processed LLDP datagram.");
202 static int lldp_receive_datagram(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
203 _cleanup_(sd_lldp_neighbor_unrefp
) sd_lldp_neighbor
*n
= NULL
;
204 ssize_t space
, length
;
205 sd_lldp
*lldp
= userdata
;
211 space
= next_datagram_size_fd(fd
);
213 return log_lldp_errno(space
, "Failed to determine datagram size to read: %m");
215 n
= lldp_neighbor_new(space
);
219 length
= recv(fd
, LLDP_NEIGHBOR_RAW(n
), n
->raw_size
, MSG_DONTWAIT
);
221 if (IN_SET(errno
, EAGAIN
, EINTR
))
224 return log_lldp_errno(errno
, "Failed to read LLDP datagram: %m");
227 if ((size_t) length
!= n
->raw_size
) {
228 log_lldp("Packet size mismatch.");
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_get(&n
->timestamp
);
238 return lldp_handle_datagram(lldp
, n
);
241 static void lldp_reset(sd_lldp
*lldp
) {
244 lldp
->timer_event_source
= sd_event_source_unref(lldp
->timer_event_source
);
245 lldp
->io_event_source
= sd_event_source_unref(lldp
->io_event_source
);
246 lldp
->fd
= safe_close(lldp
->fd
);
249 _public_
int sd_lldp_start(sd_lldp
*lldp
) {
252 assert_return(lldp
, -EINVAL
);
253 assert_return(lldp
->event
, -EINVAL
);
254 assert_return(lldp
->ifindex
> 0, -EINVAL
);
259 assert(!lldp
->io_event_source
);
261 lldp
->fd
= lldp_network_bind_raw_socket(lldp
->ifindex
);
265 r
= sd_event_add_io(lldp
->event
, &lldp
->io_event_source
, lldp
->fd
, EPOLLIN
, lldp_receive_datagram
, lldp
);
269 r
= sd_event_source_set_priority(lldp
->io_event_source
, lldp
->event_priority
);
273 (void) sd_event_source_set_description(lldp
->io_event_source
, "lldp-io");
275 log_lldp("Started LLDP client");
283 _public_
int sd_lldp_stop(sd_lldp
*lldp
) {
284 assert_return(lldp
, -EINVAL
);
289 log_lldp("Stopping LLDP client");
292 lldp_flush_neighbors(lldp
);
297 _public_
int sd_lldp_attach_event(sd_lldp
*lldp
, sd_event
*event
, int64_t priority
) {
300 assert_return(lldp
, -EINVAL
);
301 assert_return(lldp
->fd
< 0, -EBUSY
);
302 assert_return(!lldp
->event
, -EBUSY
);
305 lldp
->event
= sd_event_ref(event
);
307 r
= sd_event_default(&lldp
->event
);
312 lldp
->event_priority
= priority
;
317 _public_
int sd_lldp_detach_event(sd_lldp
*lldp
) {
319 assert_return(lldp
, -EINVAL
);
320 assert_return(lldp
->fd
< 0, -EBUSY
);
322 lldp
->event
= sd_event_unref(lldp
->event
);
326 _public_ sd_event
* sd_lldp_get_event(sd_lldp
*lldp
) {
327 assert_return(lldp
, NULL
);
332 _public_
int sd_lldp_set_callback(sd_lldp
*lldp
, sd_lldp_callback_t cb
, void *userdata
) {
333 assert_return(lldp
, -EINVAL
);
336 lldp
->userdata
= userdata
;
341 _public_
int sd_lldp_set_ifindex(sd_lldp
*lldp
, int ifindex
) {
342 assert_return(lldp
, -EINVAL
);
343 assert_return(ifindex
> 0, -EINVAL
);
344 assert_return(lldp
->fd
< 0, -EBUSY
);
346 lldp
->ifindex
= ifindex
;
350 _public_ sd_lldp
* sd_lldp_ref(sd_lldp
*lldp
) {
355 assert(lldp
->n_ref
> 0);
361 _public_ sd_lldp
* sd_lldp_unref(sd_lldp
*lldp
) {
366 assert(lldp
->n_ref
> 0);
373 sd_lldp_detach_event(lldp
);
374 lldp_flush_neighbors(lldp
);
376 hashmap_free(lldp
->neighbor_by_id
);
377 prioq_free(lldp
->neighbor_by_expiry
);
381 _public_
int sd_lldp_new(sd_lldp
**ret
) {
382 _cleanup_(sd_lldp_unrefp
) sd_lldp
*lldp
= NULL
;
385 assert_return(ret
, -EINVAL
);
387 lldp
= new0(sd_lldp
, 1);
393 lldp
->neighbors_max
= LLDP_DEFAULT_NEIGHBORS_MAX
;
394 lldp
->capability_mask
= (uint16_t) -1;
396 lldp
->neighbor_by_id
= hashmap_new(&lldp_neighbor_id_hash_ops
);
397 if (!lldp
->neighbor_by_id
)
400 r
= prioq_ensure_allocated(&lldp
->neighbor_by_expiry
, lldp_neighbor_prioq_compare_func
);
410 static int neighbor_compare_func(const void *a
, const void *b
) {
411 const sd_lldp_neighbor
* const*x
= a
, * const *y
= b
;
413 return lldp_neighbor_id_hash_ops
.compare(&(*x
)->id
, &(*y
)->id
);
416 static int on_timer_event(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
417 sd_lldp
*lldp
= userdata
;
420 r
= lldp_make_space(lldp
, 0);
422 return log_lldp_errno(r
, "Failed to make space: %m");
424 q
= lldp_start_timer(lldp
, NULL
);
426 return log_lldp_errno(q
, "Failed to restart timer: %m");
431 static int lldp_start_timer(sd_lldp
*lldp
, sd_lldp_neighbor
*neighbor
) {
438 lldp_neighbor_start_ttl(neighbor
);
440 n
= prioq_peek(lldp
->neighbor_by_expiry
);
443 if (lldp
->timer_event_source
)
444 return sd_event_source_set_enabled(lldp
->timer_event_source
, SD_EVENT_OFF
);
449 if (lldp
->timer_event_source
) {
450 r
= sd_event_source_set_time(lldp
->timer_event_source
, n
->until
);
454 return sd_event_source_set_enabled(lldp
->timer_event_source
, SD_EVENT_ONESHOT
);
460 r
= sd_event_add_time(lldp
->event
, &lldp
->timer_event_source
, clock_boottime_or_monotonic(), n
->until
, 0, on_timer_event
, lldp
);
464 r
= sd_event_source_set_priority(lldp
->timer_event_source
, lldp
->event_priority
);
468 (void) sd_event_source_set_description(lldp
->timer_event_source
, "lldp-timer");
472 _public_
int sd_lldp_get_neighbors(sd_lldp
*lldp
, sd_lldp_neighbor
***ret
) {
473 sd_lldp_neighbor
**l
= NULL
, *n
;
477 assert_return(lldp
, -EINVAL
);
478 assert_return(ret
, -EINVAL
);
480 if (hashmap_isempty(lldp
->neighbor_by_id
)) { /* Special shortcut */
485 l
= new0(sd_lldp_neighbor
*, hashmap_size(lldp
->neighbor_by_id
));
489 r
= lldp_start_timer(lldp
, NULL
);
495 HASHMAP_FOREACH(n
, lldp
->neighbor_by_id
, i
)
496 l
[k
++] = sd_lldp_neighbor_ref(n
);
498 assert((size_t) k
== hashmap_size(lldp
->neighbor_by_id
));
500 /* Return things in a stable order */
501 qsort(l
, k
, sizeof(sd_lldp_neighbor
*), neighbor_compare_func
);
507 _public_
int sd_lldp_set_neighbors_max(sd_lldp
*lldp
, uint64_t m
) {
508 assert_return(lldp
, -EINVAL
);
509 assert_return(m
<= 0, -EINVAL
);
511 lldp
->neighbors_max
= m
;
512 lldp_make_space(lldp
, 0);
517 _public_
int sd_lldp_match_capabilities(sd_lldp
*lldp
, uint16_t mask
) {
518 assert_return(lldp
, -EINVAL
);
519 assert_return(mask
!= 0, -EINVAL
);
521 lldp
->capability_mask
= mask
;
526 _public_
int sd_lldp_set_filter_address(sd_lldp
*lldp
, const struct ether_addr
*addr
) {
527 assert_return(lldp
, -EINVAL
);
529 /* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so
530 * that our own can be filtered out here. */
533 lldp
->filter_address
= *addr
;
535 zero(lldp
->filter_address
);