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
) {
47 log_lldp("Invoking callback for '%c'.", event
);
52 lldp
->callback(lldp
, event
, n
, lldp
->userdata
);
55 static int lldp_make_space(sd_lldp
*lldp
, size_t extra
) {
56 usec_t t
= USEC_INFINITY
;
61 /* Remove all entries that are past their TTL, and more until at least the specified number of extra entries
65 _cleanup_(sd_lldp_neighbor_unrefp
) sd_lldp_neighbor
*n
= NULL
;
67 n
= prioq_peek(lldp
->neighbor_by_expiry
);
71 sd_lldp_neighbor_ref(n
);
73 if (hashmap_size(lldp
->neighbor_by_id
) > LESS_BY(lldp
->neighbors_max
, extra
))
76 if (t
== USEC_INFINITY
)
77 t
= now(clock_boottime_or_monotonic());
83 lldp_neighbor_unlink(n
);
84 lldp_callback(lldp
, SD_LLDP_EVENT_REMOVED
, n
);
91 static bool lldp_keep_neighbor(sd_lldp
*lldp
, sd_lldp_neighbor
*n
) {
95 /* Don't keep data with a zero TTL */
99 /* Filter out data from the filter address */
100 if (!ether_addr_is_null(&lldp
->filter_address
) &&
101 ether_addr_equal(&lldp
->filter_address
, &n
->source_address
))
104 /* Only add if the neighbor has a capability we are interested in. Note that we also store all neighbors with
105 * no caps field set. */
106 if (n
->has_capabilities
&&
107 (n
->enabled_capabilities
& lldp
->capability_mask
) == 0)
110 /* Keep everything else */
114 static int lldp_start_timer(sd_lldp
*lldp
, sd_lldp_neighbor
*neighbor
);
116 static int lldp_add_neighbor(sd_lldp
*lldp
, sd_lldp_neighbor
*n
) {
117 _cleanup_(sd_lldp_neighbor_unrefp
) sd_lldp_neighbor
*old
= NULL
;
125 keep
= lldp_keep_neighbor(lldp
, n
);
127 /* First retrieve the old entry for this MSAP */
128 old
= hashmap_get(lldp
->neighbor_by_id
, &n
->id
);
130 sd_lldp_neighbor_ref(old
);
133 lldp_neighbor_unlink(old
);
134 lldp_callback(lldp
, SD_LLDP_EVENT_REMOVED
, old
);
138 if (lldp_neighbor_equal(n
, old
)) {
139 /* Is this equal, then restart the TTL counter, but don't do anyting else. */
140 old
->timestamp
= n
->timestamp
;
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
, old
);
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
;
210 space
= next_datagram_size_fd(fd
);
212 return log_lldp_errno(space
, "Failed to determine datagram size to read: %m");
214 n
= lldp_neighbor_new(space
);
218 length
= recv(fd
, LLDP_NEIGHBOR_RAW(n
), n
->raw_size
, MSG_DONTWAIT
);
220 if (errno
== EAGAIN
|| errno
== EINTR
)
223 return log_lldp_errno(errno
, "Failed to read LLDP datagram: %m");
226 if ((size_t) length
!= n
->raw_size
) {
227 log_lldp("Packet size mismatch.");
231 /* Try to get the timestamp of this packet if it is known */
232 if (ioctl(fd
, SIOCGSTAMPNS
, &ts
) >= 0)
233 triple_timestamp_from_realtime(&n
->timestamp
, timespec_load(&ts
));
235 triple_timestamp_get(&n
->timestamp
);
237 return lldp_handle_datagram(lldp
, n
);
240 static void lldp_reset(sd_lldp
*lldp
) {
243 lldp
->timer_event_source
= sd_event_source_unref(lldp
->timer_event_source
);
244 lldp
->io_event_source
= sd_event_source_unref(lldp
->io_event_source
);
245 lldp
->fd
= safe_close(lldp
->fd
);
248 _public_
int sd_lldp_start(sd_lldp
*lldp
) {
251 assert_return(lldp
, -EINVAL
);
252 assert_return(lldp
->event
, -EINVAL
);
253 assert_return(lldp
->ifindex
> 0, -EINVAL
);
258 assert(!lldp
->io_event_source
);
260 lldp
->fd
= lldp_network_bind_raw_socket(lldp
->ifindex
);
264 r
= sd_event_add_io(lldp
->event
, &lldp
->io_event_source
, lldp
->fd
, EPOLLIN
, lldp_receive_datagram
, lldp
);
268 r
= sd_event_source_set_priority(lldp
->io_event_source
, lldp
->event_priority
);
272 (void) sd_event_source_set_description(lldp
->io_event_source
, "lldp-io");
274 log_lldp("Started LLDP client");
282 _public_
int sd_lldp_stop(sd_lldp
*lldp
) {
283 assert_return(lldp
, -EINVAL
);
288 log_lldp("Stopping LLDP client");
291 lldp_flush_neighbors(lldp
);
296 _public_
int sd_lldp_attach_event(sd_lldp
*lldp
, sd_event
*event
, int64_t priority
) {
299 assert_return(lldp
, -EINVAL
);
300 assert_return(lldp
->fd
< 0, -EBUSY
);
301 assert_return(!lldp
->event
, -EBUSY
);
304 lldp
->event
= sd_event_ref(event
);
306 r
= sd_event_default(&lldp
->event
);
311 lldp
->event_priority
= priority
;
316 _public_
int sd_lldp_detach_event(sd_lldp
*lldp
) {
318 assert_return(lldp
, -EINVAL
);
319 assert_return(lldp
->fd
< 0, -EBUSY
);
321 lldp
->event
= sd_event_unref(lldp
->event
);
325 _public_ sd_event
* sd_lldp_get_event(sd_lldp
*lldp
) {
326 assert_return(lldp
, NULL
);
331 _public_
int sd_lldp_set_callback(sd_lldp
*lldp
, sd_lldp_callback_t cb
, void *userdata
) {
332 assert_return(lldp
, -EINVAL
);
335 lldp
->userdata
= userdata
;
340 _public_
int sd_lldp_set_ifindex(sd_lldp
*lldp
, int ifindex
) {
341 assert_return(lldp
, -EINVAL
);
342 assert_return(ifindex
> 0, -EINVAL
);
343 assert_return(lldp
->fd
< 0, -EBUSY
);
345 lldp
->ifindex
= ifindex
;
349 _public_ sd_lldp
* sd_lldp_ref(sd_lldp
*lldp
) {
354 assert(lldp
->n_ref
> 0);
360 _public_ sd_lldp
* sd_lldp_unref(sd_lldp
*lldp
) {
365 assert(lldp
->n_ref
> 0);
372 sd_lldp_detach_event(lldp
);
373 lldp_flush_neighbors(lldp
);
375 hashmap_free(lldp
->neighbor_by_id
);
376 prioq_free(lldp
->neighbor_by_expiry
);
382 _public_
int sd_lldp_new(sd_lldp
**ret
) {
383 _cleanup_(sd_lldp_unrefp
) sd_lldp
*lldp
= NULL
;
386 assert_return(ret
, -EINVAL
);
388 lldp
= new0(sd_lldp
, 1);
394 lldp
->neighbors_max
= LLDP_DEFAULT_NEIGHBORS_MAX
;
395 lldp
->capability_mask
= (uint16_t) -1;
397 lldp
->neighbor_by_id
= hashmap_new(&lldp_neighbor_id_hash_ops
);
398 if (!lldp
->neighbor_by_id
)
401 r
= prioq_ensure_allocated(&lldp
->neighbor_by_expiry
, lldp_neighbor_prioq_compare_func
);
411 static int neighbor_compare_func(const void *a
, const void *b
) {
412 const sd_lldp_neighbor
* const*x
= a
, * const *y
= b
;
414 return lldp_neighbor_id_hash_ops
.compare(&(*x
)->id
, &(*y
)->id
);
417 static int on_timer_event(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
418 sd_lldp
*lldp
= userdata
;
421 r
= lldp_make_space(lldp
, 0);
423 return log_lldp_errno(r
, "Failed to make space: %m");
425 q
= lldp_start_timer(lldp
, NULL
);
427 return log_lldp_errno(q
, "Failed to restart timer: %m");
432 static int lldp_start_timer(sd_lldp
*lldp
, sd_lldp_neighbor
*neighbor
) {
439 lldp_neighbor_start_ttl(neighbor
);
441 n
= prioq_peek(lldp
->neighbor_by_expiry
);
444 if (lldp
->timer_event_source
)
445 return sd_event_source_set_enabled(lldp
->timer_event_source
, SD_EVENT_OFF
);
450 if (lldp
->timer_event_source
) {
451 r
= sd_event_source_set_time(lldp
->timer_event_source
, n
->until
);
455 return sd_event_source_set_enabled(lldp
->timer_event_source
, SD_EVENT_ONESHOT
);
461 r
= sd_event_add_time(lldp
->event
, &lldp
->timer_event_source
, clock_boottime_or_monotonic(), n
->until
, 0, on_timer_event
, lldp
);
465 r
= sd_event_source_set_priority(lldp
->timer_event_source
, lldp
->event_priority
);
469 (void) sd_event_source_set_description(lldp
->timer_event_source
, "lldp-timer");
473 _public_
int sd_lldp_get_neighbors(sd_lldp
*lldp
, sd_lldp_neighbor
***ret
) {
474 sd_lldp_neighbor
**l
= NULL
, *n
;
478 assert_return(lldp
, -EINVAL
);
479 assert_return(ret
, -EINVAL
);
481 if (hashmap_isempty(lldp
->neighbor_by_id
)) { /* Special shortcut */
486 l
= new0(sd_lldp_neighbor
*, hashmap_size(lldp
->neighbor_by_id
));
490 r
= lldp_start_timer(lldp
, NULL
);
496 HASHMAP_FOREACH(n
, lldp
->neighbor_by_id
, i
)
497 l
[k
++] = sd_lldp_neighbor_ref(n
);
499 assert((size_t) k
== hashmap_size(lldp
->neighbor_by_id
));
501 /* Return things in a stable order */
502 qsort(l
, k
, sizeof(sd_lldp_neighbor
*), neighbor_compare_func
);
508 _public_
int sd_lldp_set_neighbors_max(sd_lldp
*lldp
, uint64_t m
) {
509 assert_return(lldp
, -EINVAL
);
510 assert_return(m
<= 0, -EINVAL
);
512 lldp
->neighbors_max
= m
;
513 lldp_make_space(lldp
, 0);
518 _public_
int sd_lldp_match_capabilities(sd_lldp
*lldp
, uint16_t mask
) {
519 assert_return(lldp
, -EINVAL
);
520 assert_return(mask
!= 0, -EINVAL
);
522 lldp
->capability_mask
= mask
;
527 _public_
int sd_lldp_set_filter_address(sd_lldp
*lldp
, const struct ether_addr
*addr
) {
528 assert_return(lldp
, -EINVAL
);
530 /* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so
531 * that our own can be filtered out here. */
534 lldp
->filter_address
= *addr
;
536 zero(lldp
->filter_address
);