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>
25 #include "alloc-util.h"
27 #include "lldp-internal.h"
28 #include "lldp-neighbor.h"
29 #include "lldp-network.h"
30 #include "socket-util.h"
31 #include "ether-addr-util.h"
33 #define LLDP_DEFAULT_NEIGHBORS_MAX 128U
35 static void lldp_flush_neighbors(sd_lldp
*lldp
) {
40 while ((n
= hashmap_first(lldp
->neighbor_by_id
)))
41 lldp_neighbor_unlink(n
);
44 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_add_neighbor(sd_lldp
*lldp
, sd_lldp_neighbor
*n
) {
116 _cleanup_(sd_lldp_neighbor_unrefp
) sd_lldp_neighbor
*old
= NULL
;
124 keep
= lldp_keep_neighbor(lldp
, n
);
126 /* First retrieve the old entry for this MSAP */
127 old
= hashmap_get(lldp
->neighbor_by_id
, &n
->id
);
129 sd_lldp_neighbor_ref(old
);
132 lldp_neighbor_unlink(old
);
133 lldp_callback(lldp
, SD_LLDP_EVENT_REMOVED
, old
);
137 if (lldp_neighbor_equal(n
, old
)) {
138 /* Is this equal, then restart the TTL counter, but don't do anyting else. */
139 lldp_neighbor_start_ttl(old
);
140 lldp_callback(lldp
, SD_LLDP_EVENT_REFRESHED
, old
);
144 /* Data changed, remove the old entry, and add a new one */
145 lldp_neighbor_unlink(old
);
150 /* Then, make room for at least one new neighbor */
151 lldp_make_space(lldp
, 1);
153 r
= hashmap_put(lldp
->neighbor_by_id
, &n
->id
, n
);
157 r
= prioq_put(lldp
->neighbor_by_expiry
, n
, &n
->prioq_idx
);
159 assert_se(hashmap_remove(lldp
->neighbor_by_id
, &n
->id
) == n
);
165 lldp_neighbor_start_ttl(n
);
166 lldp_callback(lldp
, old
? SD_LLDP_EVENT_UPDATED
: SD_LLDP_EVENT_ADDED
, n
);
172 lldp_callback(lldp
, SD_LLDP_EVENT_REMOVED
, n
);
177 static int lldp_handle_datagram(sd_lldp
*lldp
, sd_lldp_neighbor
*n
) {
183 r
= lldp_neighbor_parse(n
);
184 if (r
== -EBADMSG
) /* Ignore bad messages */
189 r
= lldp_add_neighbor(lldp
, n
);
191 log_lldp_errno(r
, "Failed to add datagram. Ignoring.");
195 log_lldp("Successfully processed LLDP datagram.");
199 static int lldp_receive_datagram(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
200 _cleanup_(sd_lldp_neighbor_unrefp
) sd_lldp_neighbor
*n
= NULL
;
201 ssize_t space
, length
;
202 sd_lldp
*lldp
= userdata
;
207 space
= next_datagram_size_fd(fd
);
209 return log_lldp_errno(space
, "Failed to determine datagram size to read: %m");
211 n
= lldp_neighbor_new(space
);
215 length
= recv(fd
, LLDP_NEIGHBOR_RAW(n
), n
->raw_size
, MSG_DONTWAIT
);
217 return log_lldp_errno(errno
, "Failed to read LLDP datagram: %m");
219 if ((size_t) length
!= n
->raw_size
) {
220 log_lldp("Packet size mismatch.");
224 return lldp_handle_datagram(lldp
, n
);
227 _public_
int sd_lldp_start(sd_lldp
*lldp
) {
230 assert_return(lldp
, -EINVAL
);
235 assert(!lldp
->io_event_source
);
237 lldp
->fd
= lldp_network_bind_raw_socket(lldp
->ifindex
);
242 r
= sd_event_add_io(lldp
->event
, &lldp
->io_event_source
, lldp
->fd
, EPOLLIN
, lldp_receive_datagram
, lldp
);
246 r
= sd_event_source_set_priority(lldp
->io_event_source
, lldp
->event_priority
);
250 (void) sd_event_source_set_description(lldp
->io_event_source
, "lldp-io");
256 lldp
->io_event_source
= sd_event_source_unref(lldp
->io_event_source
);
257 lldp
->fd
= safe_close(lldp
->fd
);
262 _public_
int sd_lldp_stop(sd_lldp
*lldp
) {
263 assert_return(lldp
, -EINVAL
);
268 lldp
->timer_event_source
= sd_event_source_unref(lldp
->timer_event_source
);
269 lldp
->io_event_source
= sd_event_source_unref(lldp
->io_event_source
);
270 lldp
->fd
= safe_close(lldp
->fd
);
272 lldp_flush_neighbors(lldp
);
277 _public_
int sd_lldp_attach_event(sd_lldp
*lldp
, sd_event
*event
, int64_t priority
) {
280 assert_return(lldp
, -EINVAL
);
281 assert_return(lldp
->fd
< 0, -EBUSY
);
282 assert_return(!lldp
->event
, -EBUSY
);
285 lldp
->event
= sd_event_ref(event
);
287 r
= sd_event_default(&lldp
->event
);
292 lldp
->event_priority
= priority
;
297 _public_
int sd_lldp_detach_event(sd_lldp
*lldp
) {
299 assert_return(lldp
, -EINVAL
);
300 assert_return(lldp
->fd
< 0, -EBUSY
);
302 lldp
->event
= sd_event_unref(lldp
->event
);
306 _public_
int sd_lldp_set_callback(sd_lldp
*lldp
, sd_lldp_callback_t cb
, void *userdata
) {
307 assert_return(lldp
, -EINVAL
);
310 lldp
->userdata
= userdata
;
315 _public_ sd_lldp
* sd_lldp_unref(sd_lldp
*lldp
) {
320 lldp_flush_neighbors(lldp
);
322 hashmap_free(lldp
->neighbor_by_id
);
323 prioq_free(lldp
->neighbor_by_expiry
);
325 sd_event_source_unref(lldp
->io_event_source
);
326 sd_event_source_unref(lldp
->timer_event_source
);
327 sd_event_unref(lldp
->event
);
328 safe_close(lldp
->fd
);
335 _public_
int sd_lldp_new(sd_lldp
**ret
, int ifindex
) {
336 _cleanup_(sd_lldp_unrefp
) sd_lldp
*lldp
= NULL
;
339 assert_return(ret
, -EINVAL
);
340 assert_return(ifindex
> 0, -EINVAL
);
342 lldp
= new0(sd_lldp
, 1);
347 lldp
->ifindex
= ifindex
;
348 lldp
->neighbors_max
= LLDP_DEFAULT_NEIGHBORS_MAX
;
349 lldp
->capability_mask
= (uint16_t) -1;
351 lldp
->neighbor_by_id
= hashmap_new(&lldp_neighbor_id_hash_ops
);
352 if (!lldp
->neighbor_by_id
)
355 r
= prioq_ensure_allocated(&lldp
->neighbor_by_expiry
, lldp_neighbor_prioq_compare_func
);
365 static int neighbor_compare_func(const void *a
, const void *b
) {
366 const sd_lldp_neighbor
* const*x
= a
, * const *y
= b
;
368 return lldp_neighbor_id_hash_ops
.compare(&(*x
)->id
, &(*y
)->id
);
371 static int lldp_start_timer(sd_lldp
*lldp
);
373 static int on_timer_event(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
374 sd_lldp
*lldp
= userdata
;
377 r
= lldp_make_space(lldp
, 0);
379 return log_lldp_errno(r
, "Failed to make space: %m");
381 q
= lldp_start_timer(lldp
);
383 return log_lldp_errno(q
, "Failed to restart timer: %m");
388 static int lldp_start_timer(sd_lldp
*lldp
) {
394 n
= prioq_peek(lldp
->neighbor_by_expiry
);
397 if (lldp
->timer_event_source
)
398 return sd_event_source_set_enabled(lldp
->timer_event_source
, SD_EVENT_OFF
);
403 if (lldp
->timer_event_source
) {
404 r
= sd_event_source_set_time(lldp
->timer_event_source
, n
->until
);
408 return sd_event_source_set_enabled(lldp
->timer_event_source
, SD_EVENT_ONESHOT
);
414 r
= sd_event_add_time(lldp
->event
, &lldp
->timer_event_source
, clock_boottime_or_monotonic(), n
->until
, 0, on_timer_event
, lldp
);
418 r
= sd_event_source_set_priority(lldp
->timer_event_source
, lldp
->event_priority
);
422 (void) sd_event_source_set_description(lldp
->timer_event_source
, "lldp-timer");
426 _public_
int sd_lldp_get_neighbors(sd_lldp
*lldp
, sd_lldp_neighbor
***ret
) {
427 sd_lldp_neighbor
**l
= NULL
, *n
;
431 assert_return(lldp
, -EINVAL
);
432 assert_return(ret
, -EINVAL
);
434 if (hashmap_isempty(lldp
->neighbor_by_id
)) { /* Special shortcut */
439 l
= new0(sd_lldp_neighbor
*, hashmap_size(lldp
->neighbor_by_id
));
443 r
= lldp_start_timer(lldp
);
449 HASHMAP_FOREACH(n
, lldp
->neighbor_by_id
, i
)
450 l
[k
++] = sd_lldp_neighbor_ref(n
);
452 assert((size_t) k
== hashmap_size(lldp
->neighbor_by_id
));
454 /* Return things in a stable order */
455 qsort(l
, k
, sizeof(sd_lldp_neighbor
*), neighbor_compare_func
);
461 _public_
int sd_lldp_set_neighbors_max(sd_lldp
*lldp
, uint64_t m
) {
462 assert_return(lldp
, -EINVAL
);
463 assert_return(m
<= 0, -EINVAL
);
465 lldp
->neighbors_max
= m
;
466 lldp_make_space(lldp
, 0);
471 _public_
int sd_lldp_match_capabilities(sd_lldp
*lldp
, uint16_t mask
) {
472 assert_return(lldp
, -EINVAL
);
473 assert_return(mask
!= 0, -EINVAL
);
475 lldp
->capability_mask
= mask
;
480 _public_
int sd_lldp_set_filter_address(sd_lldp
*lldp
, const struct ether_addr
*addr
) {
481 assert_return(lldp
, -EINVAL
);
483 /* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so
484 * that our own can be filtered out here. */
487 zero(lldp
->filter_address
);
491 lldp
->filter_address
= *addr
;