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_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 lldp_start_timer(lldp
, old
);
142 lldp_callback(lldp
, SD_LLDP_EVENT_REFRESHED
, old
);
146 /* Data changed, remove the old entry, and add a new one */
147 lldp_neighbor_unlink(old
);
152 /* Then, make room for at least one new neighbor */
153 lldp_make_space(lldp
, 1);
155 r
= hashmap_put(lldp
->neighbor_by_id
, &n
->id
, n
);
159 r
= prioq_put(lldp
->neighbor_by_expiry
, n
, &n
->prioq_idx
);
161 assert_se(hashmap_remove(lldp
->neighbor_by_id
, &n
->id
) == n
);
167 lldp_start_timer(lldp
, n
);
168 lldp_callback(lldp
, old
? SD_LLDP_EVENT_UPDATED
: SD_LLDP_EVENT_ADDED
, n
);
174 lldp_callback(lldp
, SD_LLDP_EVENT_REMOVED
, n
);
179 static int lldp_handle_datagram(sd_lldp
*lldp
, sd_lldp_neighbor
*n
) {
185 r
= lldp_neighbor_parse(n
);
186 if (r
== -EBADMSG
) /* Ignore bad messages */
191 r
= lldp_add_neighbor(lldp
, n
);
193 log_lldp_errno(r
, "Failed to add datagram. Ignoring.");
197 log_lldp("Successfully processed LLDP datagram.");
201 static int lldp_receive_datagram(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
202 _cleanup_(sd_lldp_neighbor_unrefp
) sd_lldp_neighbor
*n
= NULL
;
203 ssize_t space
, length
;
204 sd_lldp
*lldp
= userdata
;
209 space
= next_datagram_size_fd(fd
);
211 return log_lldp_errno(space
, "Failed to determine datagram size to read: %m");
213 n
= lldp_neighbor_new(space
);
217 length
= recv(fd
, LLDP_NEIGHBOR_RAW(n
), n
->raw_size
, MSG_DONTWAIT
);
219 return log_lldp_errno(errno
, "Failed to read LLDP datagram: %m");
221 if ((size_t) length
!= n
->raw_size
) {
222 log_lldp("Packet size mismatch.");
226 return lldp_handle_datagram(lldp
, n
);
229 _public_
int sd_lldp_start(sd_lldp
*lldp
) {
232 assert_return(lldp
, -EINVAL
);
237 assert(!lldp
->io_event_source
);
239 lldp
->fd
= lldp_network_bind_raw_socket(lldp
->ifindex
);
244 r
= sd_event_add_io(lldp
->event
, &lldp
->io_event_source
, lldp
->fd
, EPOLLIN
, lldp_receive_datagram
, lldp
);
248 r
= sd_event_source_set_priority(lldp
->io_event_source
, lldp
->event_priority
);
252 (void) sd_event_source_set_description(lldp
->io_event_source
, "lldp-io");
258 lldp
->io_event_source
= sd_event_source_unref(lldp
->io_event_source
);
259 lldp
->fd
= safe_close(lldp
->fd
);
264 _public_
int sd_lldp_stop(sd_lldp
*lldp
) {
265 assert_return(lldp
, -EINVAL
);
270 lldp
->timer_event_source
= sd_event_source_unref(lldp
->timer_event_source
);
271 lldp
->io_event_source
= sd_event_source_unref(lldp
->io_event_source
);
272 lldp
->fd
= safe_close(lldp
->fd
);
274 lldp_flush_neighbors(lldp
);
279 _public_
int sd_lldp_attach_event(sd_lldp
*lldp
, sd_event
*event
, int64_t priority
) {
282 assert_return(lldp
, -EINVAL
);
283 assert_return(lldp
->fd
< 0, -EBUSY
);
284 assert_return(!lldp
->event
, -EBUSY
);
287 lldp
->event
= sd_event_ref(event
);
289 r
= sd_event_default(&lldp
->event
);
294 lldp
->event_priority
= priority
;
299 _public_
int sd_lldp_detach_event(sd_lldp
*lldp
) {
301 assert_return(lldp
, -EINVAL
);
302 assert_return(lldp
->fd
< 0, -EBUSY
);
304 lldp
->event
= sd_event_unref(lldp
->event
);
308 _public_
int sd_lldp_set_callback(sd_lldp
*lldp
, sd_lldp_callback_t cb
, void *userdata
) {
309 assert_return(lldp
, -EINVAL
);
312 lldp
->userdata
= userdata
;
317 _public_ sd_lldp
* sd_lldp_unref(sd_lldp
*lldp
) {
322 lldp_flush_neighbors(lldp
);
324 hashmap_free(lldp
->neighbor_by_id
);
325 prioq_free(lldp
->neighbor_by_expiry
);
327 sd_event_source_unref(lldp
->io_event_source
);
328 sd_event_source_unref(lldp
->timer_event_source
);
329 sd_event_unref(lldp
->event
);
330 safe_close(lldp
->fd
);
337 _public_
int sd_lldp_new(sd_lldp
**ret
, int ifindex
) {
338 _cleanup_(sd_lldp_unrefp
) sd_lldp
*lldp
= NULL
;
341 assert_return(ret
, -EINVAL
);
342 assert_return(ifindex
> 0, -EINVAL
);
344 lldp
= new0(sd_lldp
, 1);
349 lldp
->ifindex
= ifindex
;
350 lldp
->neighbors_max
= LLDP_DEFAULT_NEIGHBORS_MAX
;
351 lldp
->capability_mask
= (uint16_t) -1;
353 lldp
->neighbor_by_id
= hashmap_new(&lldp_neighbor_id_hash_ops
);
354 if (!lldp
->neighbor_by_id
)
357 r
= prioq_ensure_allocated(&lldp
->neighbor_by_expiry
, lldp_neighbor_prioq_compare_func
);
367 static int neighbor_compare_func(const void *a
, const void *b
) {
368 const sd_lldp_neighbor
* const*x
= a
, * const *y
= b
;
370 return lldp_neighbor_id_hash_ops
.compare(&(*x
)->id
, &(*y
)->id
);
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
, NULL
);
383 return log_lldp_errno(q
, "Failed to restart timer: %m");
388 static int lldp_start_timer(sd_lldp
*lldp
, sd_lldp_neighbor
*neighbor
) {
395 lldp_neighbor_start_ttl(neighbor
);
397 n
= prioq_peek(lldp
->neighbor_by_expiry
);
400 if (lldp
->timer_event_source
)
401 return sd_event_source_set_enabled(lldp
->timer_event_source
, SD_EVENT_OFF
);
406 if (lldp
->timer_event_source
) {
407 r
= sd_event_source_set_time(lldp
->timer_event_source
, n
->until
);
411 return sd_event_source_set_enabled(lldp
->timer_event_source
, SD_EVENT_ONESHOT
);
417 r
= sd_event_add_time(lldp
->event
, &lldp
->timer_event_source
, clock_boottime_or_monotonic(), n
->until
, 0, on_timer_event
, lldp
);
421 r
= sd_event_source_set_priority(lldp
->timer_event_source
, lldp
->event_priority
);
425 (void) sd_event_source_set_description(lldp
->timer_event_source
, "lldp-timer");
429 _public_
int sd_lldp_get_neighbors(sd_lldp
*lldp
, sd_lldp_neighbor
***ret
) {
430 sd_lldp_neighbor
**l
= NULL
, *n
;
434 assert_return(lldp
, -EINVAL
);
435 assert_return(ret
, -EINVAL
);
437 if (hashmap_isempty(lldp
->neighbor_by_id
)) { /* Special shortcut */
442 l
= new0(sd_lldp_neighbor
*, hashmap_size(lldp
->neighbor_by_id
));
446 r
= lldp_start_timer(lldp
, NULL
);
452 HASHMAP_FOREACH(n
, lldp
->neighbor_by_id
, i
)
453 l
[k
++] = sd_lldp_neighbor_ref(n
);
455 assert((size_t) k
== hashmap_size(lldp
->neighbor_by_id
));
457 /* Return things in a stable order */
458 qsort(l
, k
, sizeof(sd_lldp_neighbor
*), neighbor_compare_func
);
464 _public_
int sd_lldp_set_neighbors_max(sd_lldp
*lldp
, uint64_t m
) {
465 assert_return(lldp
, -EINVAL
);
466 assert_return(m
<= 0, -EINVAL
);
468 lldp
->neighbors_max
= m
;
469 lldp_make_space(lldp
, 0);
474 _public_
int sd_lldp_match_capabilities(sd_lldp
*lldp
, uint16_t mask
) {
475 assert_return(lldp
, -EINVAL
);
476 assert_return(mask
!= 0, -EINVAL
);
478 lldp
->capability_mask
= mask
;
483 _public_
int sd_lldp_set_filter_address(sd_lldp
*lldp
, const struct ether_addr
*addr
) {
484 assert_return(lldp
, -EINVAL
);
486 /* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so
487 * that our own can be filtered out here. */
490 zero(lldp
->filter_address
);
494 lldp
->filter_address
= *addr
;