]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-lldp.c
tree-wide: use mfree more
[thirdparty/systemd.git] / src / libsystemd-network / sd-lldp.c
CommitLineData
ad1ad5c8 1/***
34437b4f 2 This file is part of systemd.
ad1ad5c8 3
34437b4f
LP
4 Copyright (C) 2014 Tom Gundersen
5 Copyright (C) 2014 Susant Sahani
ad1ad5c8 6
34437b4f
LP
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.
ad1ad5c8 11
34437b4f
LP
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.
ad1ad5c8 16
34437b4f
LP
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/>.
ad1ad5c8
SS
19***/
20
21#include <arpa/inet.h>
22
ad1ad5c8 23#include "sd-lldp.h"
07630cea 24
b5efdb8a 25#include "alloc-util.h"
3ffd4af2 26#include "fd-util.h"
ad1ad5c8 27#include "lldp-internal.h"
34437b4f 28#include "lldp-neighbor.h"
032b27f5 29#include "lldp-network.h"
34437b4f 30#include "socket-util.h"
b553a6b1 31#include "ether-addr-util.h"
49699bac 32
34437b4f 33#define LLDP_DEFAULT_NEIGHBORS_MAX 128U
032b27f5 34
34437b4f
LP
35static void lldp_flush_neighbors(sd_lldp *lldp) {
36 sd_lldp_neighbor *n;
ad1ad5c8
SS
37
38 assert(lldp);
ad1ad5c8 39
34437b4f
LP
40 while ((n = hashmap_first(lldp->neighbor_by_id)))
41 lldp_neighbor_unlink(n);
ad1ad5c8
SS
42}
43
90dffb22
LP
44static void lldp_callback(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n) {
45 assert(lldp);
90dffb22
LP
46
47 log_lldp("Invoking callback for '%c'.", event);
48
49 if (!lldp->callback)
50 return;
51
52 lldp->callback(lldp, event, n, lldp->userdata);
53}
54
34437b4f 55static int lldp_make_space(sd_lldp *lldp, size_t extra) {
34437b4f
LP
56 usec_t t = USEC_INFINITY;
57 bool changed = false;
ad1ad5c8 58
34437b4f 59 assert(lldp);
ad1ad5c8 60
34437b4f
LP
61 /* Remove all entries that are past their TTL, and more until at least the specified number of extra entries
62 * are free. */
ad1ad5c8 63
34437b4f 64 for (;;) {
90dffb22
LP
65 _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
66
34437b4f
LP
67 n = prioq_peek(lldp->neighbor_by_expiry);
68 if (!n)
859c37b1 69 break;
859c37b1 70
90dffb22
LP
71 sd_lldp_neighbor_ref(n);
72
34437b4f
LP
73 if (hashmap_size(lldp->neighbor_by_id) > LESS_BY(lldp->neighbors_max, extra))
74 goto remove_one;
859c37b1 75
34437b4f
LP
76 if (t == USEC_INFINITY)
77 t = now(clock_boottime_or_monotonic());
859c37b1 78
34437b4f 79 if (n->until > t)
859c37b1 80 break;
859c37b1 81
34437b4f
LP
82 remove_one:
83 lldp_neighbor_unlink(n);
90dffb22 84 lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, n);
34437b4f
LP
85 changed = true;
86 }
859c37b1 87
34437b4f
LP
88 return changed;
89}
ad1ad5c8 90
90dffb22
LP
91static bool lldp_keep_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) {
92 assert(lldp);
93 assert(n);
94
95 /* Don't keep data with a zero TTL */
96 if (n->ttl <= 0)
97 return false;
98
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))
102 return false;
103
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)
108 return false;
109
110 /* Keep everything else */
111 return true;
112}
113
0513ea4e
TH
114static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor);
115
34437b4f 116static int lldp_add_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) {
90dffb22
LP
117 _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *old = NULL;
118 bool keep;
34437b4f 119 int r;
ad1ad5c8 120
34437b4f
LP
121 assert(lldp);
122 assert(n);
123 assert(!n->lldp);
124
90dffb22
LP
125 keep = lldp_keep_neighbor(lldp, n);
126
34437b4f
LP
127 /* First retrieve the old entry for this MSAP */
128 old = hashmap_get(lldp->neighbor_by_id, &n->id);
129 if (old) {
90dffb22
LP
130 sd_lldp_neighbor_ref(old);
131
132 if (!keep) {
133 lldp_neighbor_unlink(old);
134 lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old);
135 return 0;
136 }
137
34437b4f
LP
138 if (lldp_neighbor_equal(n, old)) {
139 /* Is this equal, then restart the TTL counter, but don't do anyting else. */
16fed825 140 old->timestamp = n->timestamp;
0513ea4e 141 lldp_start_timer(lldp, old);
90dffb22 142 lldp_callback(lldp, SD_LLDP_EVENT_REFRESHED, old);
34437b4f 143 return 0;
ad1ad5c8 144 }
34437b4f
LP
145
146 /* Data changed, remove the old entry, and add a new one */
147 lldp_neighbor_unlink(old);
ad1ad5c8 148
90dffb22
LP
149 } else if (!keep)
150 return 0;
ad1ad5c8 151
34437b4f
LP
152 /* Then, make room for at least one new neighbor */
153 lldp_make_space(lldp, 1);
ad1ad5c8 154
34437b4f
LP
155 r = hashmap_put(lldp->neighbor_by_id, &n->id, n);
156 if (r < 0)
90dffb22 157 goto finish;
ad1ad5c8 158
34437b4f
LP
159 r = prioq_put(lldp->neighbor_by_expiry, n, &n->prioq_idx);
160 if (r < 0) {
161 assert_se(hashmap_remove(lldp->neighbor_by_id, &n->id) == n);
90dffb22 162 goto finish;
ad1ad5c8
SS
163 }
164
34437b4f 165 n->lldp = lldp;
ad1ad5c8 166
0513ea4e 167 lldp_start_timer(lldp, n);
90dffb22
LP
168 lldp_callback(lldp, old ? SD_LLDP_EVENT_UPDATED : SD_LLDP_EVENT_ADDED, n);
169
170 return 1;
171
172finish:
173 if (old)
35ad2cd7 174 lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old);
90dffb22
LP
175
176 return r;
ad1ad5c8
SS
177}
178
34437b4f
LP
179static int lldp_handle_datagram(sd_lldp *lldp, sd_lldp_neighbor *n) {
180 int r;
ad1ad5c8 181
34437b4f
LP
182 assert(lldp);
183 assert(n);
ad1ad5c8 184
34437b4f
LP
185 r = lldp_neighbor_parse(n);
186 if (r == -EBADMSG) /* Ignore bad messages */
187 return 0;
188 if (r < 0)
189 return r;
ad1ad5c8 190
34437b4f
LP
191 r = lldp_add_neighbor(lldp, n);
192 if (r < 0) {
193 log_lldp_errno(r, "Failed to add datagram. Ignoring.");
194 return 0;
ad1ad5c8 195 }
ad1ad5c8 196
34437b4f 197 log_lldp("Successfully processed LLDP datagram.");
34437b4f 198 return 0;
ad1ad5c8
SS
199}
200
34437b4f
LP
201static 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;
16fed825 205 struct timespec ts;
49699bac 206
34437b4f 207 assert(fd >= 0);
49699bac 208 assert(lldp);
49699bac 209
34437b4f
LP
210 space = next_datagram_size_fd(fd);
211 if (space < 0)
212 return log_lldp_errno(space, "Failed to determine datagram size to read: %m");
49699bac 213
34437b4f
LP
214 n = lldp_neighbor_new(space);
215 if (!n)
216 return -ENOMEM;
49699bac 217
34437b4f 218 length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT);
f3315c58
LP
219 if (length < 0) {
220 if (errno == EAGAIN || errno == EINTR)
221 return 0;
222
34437b4f 223 return log_lldp_errno(errno, "Failed to read LLDP datagram: %m");
f3315c58 224 }
dacd6cee 225
34437b4f
LP
226 if ((size_t) length != n->raw_size) {
227 log_lldp("Packet size mismatch.");
228 return -EINVAL;
229 }
49699bac 230
16fed825
LP
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));
234 else
235 triple_timestamp_get(&n->timestamp);
236
34437b4f 237 return lldp_handle_datagram(lldp, n);
49699bac
SS
238}
239
fc6a313b
LP
240static void lldp_reset(sd_lldp *lldp) {
241 assert(lldp);
242
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);
246}
247
34437b4f 248_public_ int sd_lldp_start(sd_lldp *lldp) {
ad1ad5c8
SS
249 int r;
250
251 assert_return(lldp, -EINVAL);
fc6a313b
LP
252 assert_return(lldp->event, -EINVAL);
253 assert_return(lldp->ifindex > 0, -EINVAL);
ad1ad5c8 254
032b27f5
LP
255 if (lldp->fd >= 0)
256 return 0;
ad1ad5c8 257
34437b4f 258 assert(!lldp->io_event_source);
49699bac 259
032b27f5
LP
260 lldp->fd = lldp_network_bind_raw_socket(lldp->ifindex);
261 if (lldp->fd < 0)
262 return lldp->fd;
263
fc6a313b
LP
264 r = sd_event_add_io(lldp->event, &lldp->io_event_source, lldp->fd, EPOLLIN, lldp_receive_datagram, lldp);
265 if (r < 0)
266 goto fail;
032b27f5 267
fc6a313b
LP
268 r = sd_event_source_set_priority(lldp->io_event_source, lldp->event_priority);
269 if (r < 0)
270 goto fail;
032b27f5 271
fc6a313b 272 (void) sd_event_source_set_description(lldp->io_event_source, "lldp-io");
ad1ad5c8 273
fc6a313b 274 log_lldp("Started LLDP client");
032b27f5
LP
275 return 1;
276
277fail:
fc6a313b 278 lldp_reset(lldp);
032b27f5 279 return r;
ad1ad5c8
SS
280}
281
34437b4f 282_public_ int sd_lldp_stop(sd_lldp *lldp) {
ad1ad5c8 283 assert_return(lldp, -EINVAL);
ad1ad5c8 284
032b27f5
LP
285 if (lldp->fd < 0)
286 return 0;
ad1ad5c8 287
fc6a313b 288 log_lldp("Stopping LLDP client");
ad1ad5c8 289
fc6a313b 290 lldp_reset(lldp);
34437b4f 291 lldp_flush_neighbors(lldp);
ad1ad5c8 292
032b27f5 293 return 1;
ad1ad5c8
SS
294}
295
34437b4f 296_public_ int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int64_t priority) {
ad1ad5c8
SS
297 int r;
298
299 assert_return(lldp, -EINVAL);
032b27f5
LP
300 assert_return(lldp->fd < 0, -EBUSY);
301 assert_return(!lldp->event, -EBUSY);
ad1ad5c8
SS
302
303 if (event)
032b27f5 304 lldp->event = sd_event_ref(event);
ad1ad5c8 305 else {
032b27f5 306 r = sd_event_default(&lldp->event);
ad1ad5c8
SS
307 if (r < 0)
308 return r;
309 }
310
032b27f5 311 lldp->event_priority = priority;
ad1ad5c8
SS
312
313 return 0;
314}
315
34437b4f 316_public_ int sd_lldp_detach_event(sd_lldp *lldp) {
ad1ad5c8
SS
317
318 assert_return(lldp, -EINVAL);
032b27f5 319 assert_return(lldp->fd < 0, -EBUSY);
ad1ad5c8 320
032b27f5 321 lldp->event = sd_event_unref(lldp->event);
ad1ad5c8
SS
322 return 0;
323}
324
3db2ec56
LP
325_public_ sd_event* sd_lldp_get_event(sd_lldp *lldp) {
326 assert_return(lldp, NULL);
327
328 return lldp->event;
329}
330
34437b4f 331_public_ int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata) {
49699bac
SS
332 assert_return(lldp, -EINVAL);
333
ccf86354 334 lldp->callback = cb;
49699bac
SS
335 lldp->userdata = userdata;
336
337 return 0;
338}
339
fc6a313b
LP
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);
344
345 lldp->ifindex = ifindex;
346 return 0;
347}
348
349_public_ sd_lldp* sd_lldp_ref(sd_lldp *lldp) {
350
351 if (!lldp)
352 return NULL;
353
354 assert(lldp->n_ref > 0);
355 lldp->n_ref++;
356
357 return lldp;
358}
359
34437b4f 360_public_ sd_lldp* sd_lldp_unref(sd_lldp *lldp) {
ad1ad5c8
SS
361
362 if (!lldp)
4afd3348 363 return NULL;
ad1ad5c8 364
fc6a313b
LP
365 assert(lldp->n_ref > 0);
366 lldp->n_ref --;
367
368 if (lldp->n_ref > 0)
369 return NULL;
370
371 lldp_reset(lldp);
372 sd_lldp_detach_event(lldp);
34437b4f 373 lldp_flush_neighbors(lldp);
ad1ad5c8 374
34437b4f
LP
375 hashmap_free(lldp->neighbor_by_id);
376 prioq_free(lldp->neighbor_by_expiry);
6b430fdb 377 return mfree(lldp);
ad1ad5c8
SS
378}
379
fc6a313b 380_public_ int sd_lldp_new(sd_lldp **ret) {
4afd3348 381 _cleanup_(sd_lldp_unrefp) sd_lldp *lldp = NULL;
ad1ad5c8
SS
382 int r;
383
384 assert_return(ret, -EINVAL);
ad1ad5c8
SS
385
386 lldp = new0(sd_lldp, 1);
387 if (!lldp)
388 return -ENOMEM;
389
fc6a313b 390 lldp->n_ref = 1;
032b27f5 391 lldp->fd = -1;
34437b4f
LP
392 lldp->neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX;
393 lldp->capability_mask = (uint16_t) -1;
ad1ad5c8 394
34437b4f
LP
395 lldp->neighbor_by_id = hashmap_new(&lldp_neighbor_id_hash_ops);
396 if (!lldp->neighbor_by_id)
ad1ad5c8
SS
397 return -ENOMEM;
398
34437b4f 399 r = prioq_ensure_allocated(&lldp->neighbor_by_expiry, lldp_neighbor_prioq_compare_func);
ad1ad5c8
SS
400 if (r < 0)
401 return r;
402
403 *ret = lldp;
404 lldp = NULL;
405
406 return 0;
407}
7434883c 408
34437b4f
LP
409static int neighbor_compare_func(const void *a, const void *b) {
410 const sd_lldp_neighbor * const*x = a, * const *y = b;
7434883c 411
34437b4f
LP
412 return lldp_neighbor_id_hash_ops.compare(&(*x)->id, &(*y)->id);
413}
414
34437b4f
LP
415static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) {
416 sd_lldp *lldp = userdata;
417 int r, q;
418
419 r = lldp_make_space(lldp, 0);
420 if (r < 0)
421 return log_lldp_errno(r, "Failed to make space: %m");
422
0513ea4e 423 q = lldp_start_timer(lldp, NULL);
34437b4f
LP
424 if (q < 0)
425 return log_lldp_errno(q, "Failed to restart timer: %m");
426
34437b4f
LP
427 return 0;
428}
7434883c 429
0513ea4e 430static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor) {
34437b4f
LP
431 sd_lldp_neighbor *n;
432 int r;
433
434 assert(lldp);
435
0513ea4e
TH
436 if (neighbor)
437 lldp_neighbor_start_ttl(neighbor);
438
34437b4f
LP
439 n = prioq_peek(lldp->neighbor_by_expiry);
440 if (!n) {
441
442 if (lldp->timer_event_source)
443 return sd_event_source_set_enabled(lldp->timer_event_source, SD_EVENT_OFF);
444
445 return 0;
446 }
447
448 if (lldp->timer_event_source) {
449 r = sd_event_source_set_time(lldp->timer_event_source, n->until);
450 if (r < 0)
451 return r;
452
453 return sd_event_source_set_enabled(lldp->timer_event_source, SD_EVENT_ONESHOT);
7434883c
BG
454 }
455
34437b4f
LP
456 if (!lldp->event)
457 return 0;
458
459 r = sd_event_add_time(lldp->event, &lldp->timer_event_source, clock_boottime_or_monotonic(), n->until, 0, on_timer_event, lldp);
460 if (r < 0)
461 return r;
462
463 r = sd_event_source_set_priority(lldp->timer_event_source, lldp->event_priority);
464 if (r < 0)
465 return r;
466
467 (void) sd_event_source_set_description(lldp->timer_event_source, "lldp-timer");
468 return 0;
469}
470
471_public_ int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***ret) {
472 sd_lldp_neighbor **l = NULL, *n;
473 Iterator i;
474 int k = 0, r;
475
476 assert_return(lldp, -EINVAL);
477 assert_return(ret, -EINVAL);
478
34437b4f
LP
479 if (hashmap_isempty(lldp->neighbor_by_id)) { /* Special shortcut */
480 *ret = NULL;
7434883c
BG
481 return 0;
482 }
483
34437b4f
LP
484 l = new0(sd_lldp_neighbor*, hashmap_size(lldp->neighbor_by_id));
485 if (!l)
7434883c
BG
486 return -ENOMEM;
487
0513ea4e 488 r = lldp_start_timer(lldp, NULL);
34437b4f
LP
489 if (r < 0) {
490 free(l);
491 return r;
7434883c
BG
492 }
493
34437b4f
LP
494 HASHMAP_FOREACH(n, lldp->neighbor_by_id, i)
495 l[k++] = sd_lldp_neighbor_ref(n);
496
497 assert((size_t) k == hashmap_size(lldp->neighbor_by_id));
498
499 /* Return things in a stable order */
500 qsort(l, k, sizeof(sd_lldp_neighbor*), neighbor_compare_func);
501 *ret = l;
502
503 return k;
504}
505
506_public_ int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t m) {
507 assert_return(lldp, -EINVAL);
508 assert_return(m <= 0, -EINVAL);
509
510 lldp->neighbors_max = m;
511 lldp_make_space(lldp, 0);
512
513 return 0;
514}
515
516_public_ int sd_lldp_match_capabilities(sd_lldp *lldp, uint16_t mask) {
517 assert_return(lldp, -EINVAL);
518 assert_return(mask != 0, -EINVAL);
519
520 lldp->capability_mask = mask;
521
522 return 0;
7434883c 523}
b553a6b1
LP
524
525_public_ int sd_lldp_set_filter_address(sd_lldp *lldp, const struct ether_addr *addr) {
526 assert_return(lldp, -EINVAL);
527
528 /* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so
529 * that our own can be filtered out here. */
530
a1fb61b0
LP
531 if (addr)
532 lldp->filter_address = *addr;
533 else
b553a6b1 534 zero(lldp->filter_address);
b553a6b1 535
b553a6b1
LP
536 return 0;
537}