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"
32 #define LLDP_DEFAULT_NEIGHBORS_MAX 128U
34 static void lldp_flush_neighbors(sd_lldp
*lldp
) {
39 while ((n
= hashmap_first(lldp
->neighbor_by_id
)))
40 lldp_neighbor_unlink(n
);
43 static int lldp_make_space(sd_lldp
*lldp
, size_t extra
) {
45 usec_t t
= USEC_INFINITY
;
50 /* Remove all entries that are past their TTL, and more until at least the specified number of extra entries
54 n
= prioq_peek(lldp
->neighbor_by_expiry
);
58 if (hashmap_size(lldp
->neighbor_by_id
) > LESS_BY(lldp
->neighbors_max
, extra
))
61 if (t
== USEC_INFINITY
)
62 t
= now(clock_boottime_or_monotonic());
68 lldp_neighbor_unlink(n
);
75 static int lldp_add_neighbor(sd_lldp
*lldp
, sd_lldp_neighbor
*n
) {
76 sd_lldp_neighbor
*old
;
84 /* First retrieve the old entry for this MSAP */
85 old
= hashmap_get(lldp
->neighbor_by_id
, &n
->id
);
87 if (lldp_neighbor_equal(n
, old
)) {
88 /* Is this equal, then restart the TTL counter, but don't do anyting else. */
89 lldp_neighbor_start_ttl(old
);
93 /* Data changed, remove the old entry, and add a new one */
94 lldp_neighbor_unlink(old
);
98 /* Then, add the new entry in its place, but only if it has a non-zero TTL. */
102 /* Only add if the neighbor has a capability we are interested in. Note that we also store all neighbors with
103 * no caps field set. */
104 if (n
->has_capabilities
&&
105 (n
->enabled_capabilities
& lldp
->capability_mask
) == 0)
108 /* Then, make room for at least one new neighbor */
109 lldp_make_space(lldp
, 1);
111 r
= hashmap_put(lldp
->neighbor_by_id
, &n
->id
, n
);
115 r
= prioq_put(lldp
->neighbor_by_expiry
, n
, &n
->prioq_idx
);
117 assert_se(hashmap_remove(lldp
->neighbor_by_id
, &n
->id
) == n
);
126 static int lldp_handle_datagram(sd_lldp
*lldp
, sd_lldp_neighbor
*n
) {
132 r
= lldp_neighbor_parse(n
);
133 if (r
== -EBADMSG
) /* Ignore bad messages */
138 lldp_neighbor_start_ttl(n
);
140 r
= lldp_add_neighbor(lldp
, n
);
142 log_lldp_errno(r
, "Failed to add datagram. Ignoring.");
146 log_lldp("Successfully processed LLDP datagram.");
148 if (r
> 0 && lldp
->callback
) /* Only invoke the callback if something actually changed. */
149 lldp
->callback(lldp
, lldp
->userdata
);
154 static int lldp_receive_datagram(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
155 _cleanup_(sd_lldp_neighbor_unrefp
) sd_lldp_neighbor
*n
= NULL
;
156 ssize_t space
, length
;
157 sd_lldp
*lldp
= userdata
;
162 space
= next_datagram_size_fd(fd
);
164 return log_lldp_errno(space
, "Failed to determine datagram size to read: %m");
166 n
= lldp_neighbor_new(space
);
170 length
= recv(fd
, LLDP_NEIGHBOR_RAW(n
), n
->raw_size
, MSG_DONTWAIT
);
172 return log_lldp_errno(errno
, "Failed to read LLDP datagram: %m");
174 if ((size_t) length
!= n
->raw_size
) {
175 log_lldp("Packet size mismatch.");
179 return lldp_handle_datagram(lldp
, n
);
182 _public_
int sd_lldp_start(sd_lldp
*lldp
) {
185 assert_return(lldp
, -EINVAL
);
190 assert(!lldp
->io_event_source
);
192 lldp
->fd
= lldp_network_bind_raw_socket(lldp
->ifindex
);
197 r
= sd_event_add_io(lldp
->event
, &lldp
->io_event_source
, lldp
->fd
, EPOLLIN
, lldp_receive_datagram
, lldp
);
201 r
= sd_event_source_set_priority(lldp
->io_event_source
, lldp
->event_priority
);
205 (void) sd_event_source_set_description(lldp
->io_event_source
, "lldp-io");
211 lldp
->io_event_source
= sd_event_source_unref(lldp
->io_event_source
);
212 lldp
->fd
= safe_close(lldp
->fd
);
217 _public_
int sd_lldp_stop(sd_lldp
*lldp
) {
218 assert_return(lldp
, -EINVAL
);
223 lldp
->timer_event_source
= sd_event_source_unref(lldp
->timer_event_source
);
224 lldp
->io_event_source
= sd_event_source_unref(lldp
->io_event_source
);
225 lldp
->fd
= safe_close(lldp
->fd
);
227 lldp_flush_neighbors(lldp
);
232 _public_
int sd_lldp_attach_event(sd_lldp
*lldp
, sd_event
*event
, int64_t priority
) {
235 assert_return(lldp
, -EINVAL
);
236 assert_return(lldp
->fd
< 0, -EBUSY
);
237 assert_return(!lldp
->event
, -EBUSY
);
240 lldp
->event
= sd_event_ref(event
);
242 r
= sd_event_default(&lldp
->event
);
247 lldp
->event_priority
= priority
;
252 _public_
int sd_lldp_detach_event(sd_lldp
*lldp
) {
254 assert_return(lldp
, -EINVAL
);
255 assert_return(lldp
->fd
< 0, -EBUSY
);
257 lldp
->event
= sd_event_unref(lldp
->event
);
261 _public_
int sd_lldp_set_callback(sd_lldp
*lldp
, sd_lldp_callback_t cb
, void *userdata
) {
262 assert_return(lldp
, -EINVAL
);
265 lldp
->userdata
= userdata
;
270 _public_ sd_lldp
* sd_lldp_unref(sd_lldp
*lldp
) {
275 lldp_flush_neighbors(lldp
);
277 hashmap_free(lldp
->neighbor_by_id
);
278 prioq_free(lldp
->neighbor_by_expiry
);
280 sd_event_source_unref(lldp
->io_event_source
);
281 sd_event_source_unref(lldp
->timer_event_source
);
282 sd_event_unref(lldp
->event
);
283 safe_close(lldp
->fd
);
290 _public_
int sd_lldp_new(sd_lldp
**ret
, int ifindex
) {
291 _cleanup_(sd_lldp_unrefp
) sd_lldp
*lldp
= NULL
;
294 assert_return(ret
, -EINVAL
);
295 assert_return(ifindex
> 0, -EINVAL
);
297 lldp
= new0(sd_lldp
, 1);
302 lldp
->ifindex
= ifindex
;
303 lldp
->neighbors_max
= LLDP_DEFAULT_NEIGHBORS_MAX
;
304 lldp
->capability_mask
= (uint16_t) -1;
306 lldp
->neighbor_by_id
= hashmap_new(&lldp_neighbor_id_hash_ops
);
307 if (!lldp
->neighbor_by_id
)
310 r
= prioq_ensure_allocated(&lldp
->neighbor_by_expiry
, lldp_neighbor_prioq_compare_func
);
320 static int neighbor_compare_func(const void *a
, const void *b
) {
321 const sd_lldp_neighbor
* const*x
= a
, * const *y
= b
;
323 return lldp_neighbor_id_hash_ops
.compare(&(*x
)->id
, &(*y
)->id
);
326 static int lldp_start_timer(sd_lldp
*lldp
);
328 static int on_timer_event(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
329 sd_lldp
*lldp
= userdata
;
332 r
= lldp_make_space(lldp
, 0);
334 return log_lldp_errno(r
, "Failed to make space: %m");
336 q
= lldp_start_timer(lldp
);
338 return log_lldp_errno(q
, "Failed to restart timer: %m");
340 log_lldp("LLDP timer event hit.");
341 if (r
> 0 && lldp
->callback
) /* Invoke callback if we dropped an entry */
342 lldp
->callback(lldp
, lldp
->userdata
);
347 static int lldp_start_timer(sd_lldp
*lldp
) {
353 n
= prioq_peek(lldp
->neighbor_by_expiry
);
356 if (lldp
->timer_event_source
)
357 return sd_event_source_set_enabled(lldp
->timer_event_source
, SD_EVENT_OFF
);
362 if (lldp
->timer_event_source
) {
363 r
= sd_event_source_set_time(lldp
->timer_event_source
, n
->until
);
367 return sd_event_source_set_enabled(lldp
->timer_event_source
, SD_EVENT_ONESHOT
);
373 r
= sd_event_add_time(lldp
->event
, &lldp
->timer_event_source
, clock_boottime_or_monotonic(), n
->until
, 0, on_timer_event
, lldp
);
377 r
= sd_event_source_set_priority(lldp
->timer_event_source
, lldp
->event_priority
);
381 (void) sd_event_source_set_description(lldp
->timer_event_source
, "lldp-timer");
385 _public_
int sd_lldp_get_neighbors(sd_lldp
*lldp
, sd_lldp_neighbor
***ret
) {
386 sd_lldp_neighbor
**l
= NULL
, *n
;
390 assert_return(lldp
, -EINVAL
);
391 assert_return(ret
, -EINVAL
);
393 /* Flush out old entries, before we return data */
394 (void) lldp_make_space(lldp
, 0);
396 if (hashmap_isempty(lldp
->neighbor_by_id
)) { /* Special shortcut */
401 l
= new0(sd_lldp_neighbor
*, hashmap_size(lldp
->neighbor_by_id
));
405 r
= lldp_start_timer(lldp
);
411 HASHMAP_FOREACH(n
, lldp
->neighbor_by_id
, i
)
412 l
[k
++] = sd_lldp_neighbor_ref(n
);
414 assert((size_t) k
== hashmap_size(lldp
->neighbor_by_id
));
416 /* Return things in a stable order */
417 qsort(l
, k
, sizeof(sd_lldp_neighbor
*), neighbor_compare_func
);
423 _public_
int sd_lldp_set_neighbors_max(sd_lldp
*lldp
, uint64_t m
) {
424 assert_return(lldp
, -EINVAL
);
425 assert_return(m
<= 0, -EINVAL
);
427 lldp
->neighbors_max
= m
;
428 lldp_make_space(lldp
, 0);
433 _public_
int sd_lldp_match_capabilities(sd_lldp
*lldp
, uint16_t mask
) {
434 assert_return(lldp
, -EINVAL
);
435 assert_return(mask
!= 0, -EINVAL
);
437 lldp
->capability_mask
= mask
;