blob: ca323583c0112b35019bd378bdd2d8d0f9862dc8 [file] [log] [blame]
Tomáš Pecka785ceb32020-10-15 18:28:47 +02001From 9b8b7ba40accbd303a3378d75022d6d92a2b7771 Mon Sep 17 00:00:00 2001
2From: Tomas Pecka <peckato1@fit.cvut.cz>
3Date: Mon, 7 Sep 2020 18:26:47 +0200
4Subject: [PATCH] Move sd_lldp to libsystemd
5
6Make sd_lldp public in libsystemd. Clients can therefore operate
7with LLDP structures and files (e.g. parse LLDP neighbours).
8---
9 meson.build | 1 +
10 src/fuzz/meson.build | 9 +-
11 src/libsystemd-network/lldp-internal.h | 39 --
12 src/libsystemd-network/lldp-neighbor.c | 769 -------------------------
13 src/libsystemd-network/lldp-neighbor.h | 91 ---
14 src/libsystemd-network/lldp-network.c | 78 ---
15 src/libsystemd-network/lldp-network.h | 6 -
16 src/libsystemd-network/meson.build | 6 -
17 src/libsystemd-network/sd-lldp.c | 498 ----------------
18 src/libsystemd-network/test-lldp.c | 379 ------------
19 src/libsystemd/libsystemd.sym | 41 ++
20 src/libsystemd/meson.build | 6 +
21 src/libsystemd/sd-lldp/lldp-internal.h | 39 ++
22 src/libsystemd/sd-lldp/lldp-neighbor.c | 769 +++++++++++++++++++++++++
23 src/libsystemd/sd-lldp/lldp-neighbor.h | 91 +++
24 src/libsystemd/sd-lldp/lldp-network.c | 78 +++
25 src/libsystemd/sd-lldp/lldp-network.h | 6 +
26 src/libsystemd/sd-lldp/sd-lldp.c | 498 ++++++++++++++++
27 src/libsystemd/sd-lldp/test-lldp.c | 379 ++++++++++++
28 src/systemd/meson.build | 2 +-
29 src/test/meson.build | 10 +-
30 21 files changed, 1919 insertions(+), 1876 deletions(-)
31 delete mode 100644 src/libsystemd-network/lldp-internal.h
32 delete mode 100644 src/libsystemd-network/lldp-neighbor.c
33 delete mode 100644 src/libsystemd-network/lldp-neighbor.h
34 delete mode 100644 src/libsystemd-network/lldp-network.c
35 delete mode 100644 src/libsystemd-network/lldp-network.h
36 delete mode 100644 src/libsystemd-network/sd-lldp.c
37 delete mode 100644 src/libsystemd-network/test-lldp.c
38 create mode 100644 src/libsystemd/sd-lldp/lldp-internal.h
39 create mode 100644 src/libsystemd/sd-lldp/lldp-neighbor.c
40 create mode 100644 src/libsystemd/sd-lldp/lldp-neighbor.h
41 create mode 100644 src/libsystemd/sd-lldp/lldp-network.c
42 create mode 100644 src/libsystemd/sd-lldp/lldp-network.h
43 create mode 100644 src/libsystemd/sd-lldp/sd-lldp.c
44 create mode 100644 src/libsystemd/sd-lldp/test-lldp.c
45
46diff --git a/meson.build b/meson.build
47index 8ccc947e37..1c2099d093 100644
48--- a/meson.build
49+++ b/meson.build
50@@ -1398,6 +1398,7 @@ includes = include_directories('src/basic',
51 'src/libsystemd/sd-event',
52 'src/libsystemd/sd-hwdb',
53 'src/libsystemd/sd-id128',
54+ 'src/libsystemd/sd-lldp',
55 'src/libsystemd/sd-netlink',
56 'src/libsystemd/sd-network',
57 'src/libsystemd/sd-resolve',
58diff --git a/src/fuzz/meson.build b/src/fuzz/meson.build
59index c88812d1de..8171bf58c1 100644
60--- a/src/fuzz/meson.build
61+++ b/src/fuzz/meson.build
62@@ -33,8 +33,8 @@ fuzzers += [
63 []],
64
65 [['src/fuzz/fuzz-lldp.c'],
66- [libshared,
67- libsystemd_network],
68+ [libbasic,libshared_static,libsystemd_static
69+ ],
70 []],
71
72 [['src/fuzz/fuzz-ndisc-rs.c',
73@@ -43,8 +43,9 @@ fuzzers += [
74 'src/libsystemd-network/icmp6-util.h',
75 'src/systemd/sd-dhcp6-client.h',
76 'src/systemd/sd-ndisc.h'],
77- [libshared,
78- libsystemd_network],
79+ [libsystemd_network,
80+ libshared
81+ ],
82 []],
83
84 [['src/fuzz/fuzz-json.c'],
85diff --git a/src/libsystemd-network/lldp-internal.h b/src/libsystemd-network/lldp-internal.h
86deleted file mode 100644
87index 9598438dba..0000000000
88--- a/src/libsystemd-network/lldp-internal.h
89+++ /dev/null
90@@ -1,39 +0,0 @@
91-/* SPDX-License-Identifier: LGPL-2.1+ */
92-#pragma once
93-
94-#include "sd-event.h"
95-#include "sd-lldp.h"
96-
97-#include "hashmap.h"
98-#include "log.h"
99-#include "prioq.h"
100-
101-struct sd_lldp {
102- unsigned n_ref;
103-
104- int ifindex;
105- int fd;
106-
107- sd_event *event;
108- int64_t event_priority;
109- sd_event_source *io_event_source;
110- sd_event_source *timer_event_source;
111-
112- Prioq *neighbor_by_expiry;
113- Hashmap *neighbor_by_id;
114-
115- uint64_t neighbors_max;
116-
117- sd_lldp_callback_t callback;
118- void *userdata;
119-
120- uint16_t capability_mask;
121-
122- struct ether_addr filter_address;
123-};
124-
125-#define log_lldp_errno(error, fmt, ...) log_internal(LOG_DEBUG, error, PROJECT_FILE, __LINE__, __func__, "LLDP: " fmt, ##__VA_ARGS__)
126-#define log_lldp(fmt, ...) log_lldp_errno(0, fmt, ##__VA_ARGS__)
127-
128-const char* lldp_event_to_string(sd_lldp_event e) _const_;
129-sd_lldp_event lldp_event_from_string(const char *s) _pure_;
130diff --git a/src/libsystemd-network/lldp-neighbor.c b/src/libsystemd-network/lldp-neighbor.c
131deleted file mode 100644
132index 9bae4a3c6e..0000000000
133--- a/src/libsystemd-network/lldp-neighbor.c
134+++ /dev/null
135@@ -1,769 +0,0 @@
136-/* SPDX-License-Identifier: LGPL-2.1+ */
137-
138-#include "alloc-util.h"
139-#include "escape.h"
140-#include "ether-addr-util.h"
141-#include "hexdecoct.h"
142-#include "in-addr-util.h"
143-#include "lldp-internal.h"
144-#include "lldp-neighbor.h"
145-#include "memory-util.h"
146-#include "missing.h"
147-#include "unaligned.h"
148-
149-static void lldp_neighbor_id_hash_func(const LLDPNeighborID *id, struct siphash *state) {
150- siphash24_compress(id->chassis_id, id->chassis_id_size, state);
151- siphash24_compress(&id->chassis_id_size, sizeof(id->chassis_id_size), state);
152- siphash24_compress(id->port_id, id->port_id_size, state);
153- siphash24_compress(&id->port_id_size, sizeof(id->port_id_size), state);
154-}
155-
156-int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y) {
157- return memcmp_nn(x->chassis_id, x->chassis_id_size, y->chassis_id, y->chassis_id_size)
158- ?: memcmp_nn(x->port_id, x->port_id_size, y->port_id, y->port_id_size);
159-}
160-
161-DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(lldp_neighbor_hash_ops, LLDPNeighborID, lldp_neighbor_id_hash_func, lldp_neighbor_id_compare_func,
162- sd_lldp_neighbor, lldp_neighbor_unlink);
163-
164-int lldp_neighbor_prioq_compare_func(const void *a, const void *b) {
165- const sd_lldp_neighbor *x = a, *y = b;
166-
167- return CMP(x->until, y->until);
168-}
169-
170-_public_ sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n) {
171- if (!n)
172- return NULL;
173-
174- assert(n->n_ref > 0 || n->lldp);
175- n->n_ref++;
176-
177- return n;
178-}
179-
180-static void lldp_neighbor_free(sd_lldp_neighbor *n) {
181- assert(n);
182-
183- free(n->id.port_id);
184- free(n->id.chassis_id);
185- free(n->port_description);
186- free(n->system_name);
187- free(n->system_description);
188- free(n->chassis_id_as_string);
189- free(n->port_id_as_string);
190- free(n);
191-}
192-
193-_public_ sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n) {
194-
195- /* Drops one reference from the neighbor. Note that the object is not freed unless it is already unlinked from
196- * the sd_lldp object. */
197-
198- if (!n)
199- return NULL;
200-
201- assert(n->n_ref > 0);
202- n->n_ref--;
203-
204- if (n->n_ref <= 0 && !n->lldp)
205- lldp_neighbor_free(n);
206-
207- return NULL;
208-}
209-
210-sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n) {
211-
212- /* Removes the neighbor object from the LLDP object, and frees it if it also has no other reference. */
213-
214- if (!n)
215- return NULL;
216-
217- if (!n->lldp)
218- return NULL;
219-
220- /* Only remove the neighbor object from the hash table if it's in there, don't complain if it isn't. This is
221- * because we are used as destructor call for hashmap_clear() and thus sometimes are called to de-register
222- * ourselves from the hashtable and sometimes are called after we already are de-registered. */
223-
224- (void) hashmap_remove_value(n->lldp->neighbor_by_id, &n->id, n);
225-
226- assert_se(prioq_remove(n->lldp->neighbor_by_expiry, n, &n->prioq_idx) >= 0);
227-
228- n->lldp = NULL;
229-
230- if (n->n_ref <= 0)
231- lldp_neighbor_free(n);
232-
233- return NULL;
234-}
235-
236-sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size) {
237- sd_lldp_neighbor *n;
238-
239- n = malloc0(ALIGN(sizeof(sd_lldp_neighbor)) + raw_size);
240- if (!n)
241- return NULL;
242-
243- n->raw_size = raw_size;
244- n->n_ref = 1;
245-
246- return n;
247-}
248-
249-static int parse_string(char **s, const void *q, size_t n) {
250- const char *p = q;
251- char *k;
252-
253- assert(s);
254- assert(p || n == 0);
255-
256- if (*s) {
257- log_lldp("Found duplicate string, ignoring field.");
258- return 0;
259- }
260-
261- /* Strip trailing NULs, just to be nice */
262- while (n > 0 && p[n-1] == 0)
263- n--;
264-
265- if (n <= 0) /* Ignore empty strings */
266- return 0;
267-
268- /* Look for inner NULs */
269- if (memchr(p, 0, n)) {
270- log_lldp("Found inner NUL in string, ignoring field.");
271- return 0;
272- }
273-
274- /* Let's escape weird chars, for security reasons */
275- k = cescape_length(p, n);
276- if (!k)
277- return -ENOMEM;
278-
279- free(*s);
280- *s = k;
281-
282- return 1;
283-}
284-
285-int lldp_neighbor_parse(sd_lldp_neighbor *n) {
286- struct ether_header h;
287- const uint8_t *p;
288- size_t left;
289- int r;
290-
291- assert(n);
292-
293- if (n->raw_size < sizeof(struct ether_header)) {
294- log_lldp("Received truncated packet, ignoring.");
295- return -EBADMSG;
296- }
297-
298- memcpy(&h, LLDP_NEIGHBOR_RAW(n), sizeof(h));
299-
300- if (h.ether_type != htobe16(ETHERTYPE_LLDP)) {
301- log_lldp("Received packet with wrong type, ignoring.");
302- return -EBADMSG;
303- }
304-
305- if (h.ether_dhost[0] != 0x01 ||
306- h.ether_dhost[1] != 0x80 ||
307- h.ether_dhost[2] != 0xc2 ||
308- h.ether_dhost[3] != 0x00 ||
309- h.ether_dhost[4] != 0x00 ||
310- !IN_SET(h.ether_dhost[5], 0x00, 0x03, 0x0e)) {
311- log_lldp("Received packet with wrong destination address, ignoring.");
312- return -EBADMSG;
313- }
314-
315- memcpy(&n->source_address, h.ether_shost, sizeof(struct ether_addr));
316- memcpy(&n->destination_address, h.ether_dhost, sizeof(struct ether_addr));
317-
318- p = (const uint8_t*) LLDP_NEIGHBOR_RAW(n) + sizeof(struct ether_header);
319- left = n->raw_size - sizeof(struct ether_header);
320-
321- for (;;) {
322- uint8_t type;
323- uint16_t length;
324-
325- if (left < 2) {
326- log_lldp("TLV lacks header, ignoring.");
327- return -EBADMSG;
328- }
329-
330- type = p[0] >> 1;
331- length = p[1] + (((uint16_t) (p[0] & 1)) << 8);
332- p += 2, left -= 2;
333-
334- if (left < length) {
335- log_lldp("TLV truncated, ignoring datagram.");
336- return -EBADMSG;
337- }
338-
339- switch (type) {
340-
341- case SD_LLDP_TYPE_END:
342- if (length != 0) {
343- log_lldp("End marker TLV not zero-sized, ignoring datagram.");
344- return -EBADMSG;
345- }
346-
347- /* Note that after processing the SD_LLDP_TYPE_END left could still be > 0
348- * as the message may contain padding (see IEEE 802.1AB-2016, sec. 8.5.12) */
349-
350- goto end_marker;
351-
352- case SD_LLDP_TYPE_CHASSIS_ID:
353- if (length < 2 || length > 256) { /* includes the chassis subtype, hence one extra byte */
354- log_lldp("Chassis ID field size out of range, ignoring datagram.");
355- return -EBADMSG;
356- }
357- if (n->id.chassis_id) {
358- log_lldp("Duplicate chassis ID field, ignoring datagram.");
359- return -EBADMSG;
360- }
361-
362- n->id.chassis_id = memdup(p, length);
363- if (!n->id.chassis_id)
364- return -ENOMEM;
365-
366- n->id.chassis_id_size = length;
367- break;
368-
369- case SD_LLDP_TYPE_PORT_ID:
370- if (length < 2 || length > 256) { /* includes the port subtype, hence one extra byte */
371- log_lldp("Port ID field size out of range, ignoring datagram.");
372- return -EBADMSG;
373- }
374- if (n->id.port_id) {
375- log_lldp("Duplicate port ID field, ignoring datagram.");
376- return -EBADMSG;
377- }
378-
379- n->id.port_id = memdup(p, length);
380- if (!n->id.port_id)
381- return -ENOMEM;
382-
383- n->id.port_id_size = length;
384- break;
385-
386- case SD_LLDP_TYPE_TTL:
387- if (length != 2) {
388- log_lldp("TTL field has wrong size, ignoring datagram.");
389- return -EBADMSG;
390- }
391-
392- if (n->has_ttl) {
393- log_lldp("Duplicate TTL field, ignoring datagram.");
394- return -EBADMSG;
395- }
396-
397- n->ttl = unaligned_read_be16(p);
398- n->has_ttl = true;
399- break;
400-
401- case SD_LLDP_TYPE_PORT_DESCRIPTION:
402- r = parse_string(&n->port_description, p, length);
403- if (r < 0)
404- return r;
405- break;
406-
407- case SD_LLDP_TYPE_SYSTEM_NAME:
408- r = parse_string(&n->system_name, p, length);
409- if (r < 0)
410- return r;
411- break;
412-
413- case SD_LLDP_TYPE_SYSTEM_DESCRIPTION:
414- r = parse_string(&n->system_description, p, length);
415- if (r < 0)
416- return r;
417- break;
418-
419- case SD_LLDP_TYPE_SYSTEM_CAPABILITIES:
420- if (length != 4)
421- log_lldp("System capabilities field has wrong size, ignoring.");
422- else {
423- n->system_capabilities = unaligned_read_be16(p);
424- n->enabled_capabilities = unaligned_read_be16(p + 2);
425- n->has_capabilities = true;
426- }
427-
428- break;
429-
430- case SD_LLDP_TYPE_PRIVATE:
431- if (length < 4)
432- log_lldp("Found private TLV that is too short, ignoring.");
433-
434- break;
435- }
436-
437- p += length, left -= length;
438- }
439-
440-end_marker:
441- if (!n->id.chassis_id || !n->id.port_id || !n->has_ttl) {
442- log_lldp("One or more mandatory TLV missing in datagram. Ignoring.");
443- return -EBADMSG;
444-
445- }
446-
447- n->rindex = sizeof(struct ether_header);
448-
449- return 0;
450-}
451-
452-void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) {
453- assert(n);
454-
455- if (n->ttl > 0) {
456- usec_t base;
457-
458- /* Use the packet's timestamp if there is one known */
459- base = triple_timestamp_by_clock(&n->timestamp, clock_boottime_or_monotonic());
460- if (base <= 0 || base == USEC_INFINITY)
461- base = now(clock_boottime_or_monotonic()); /* Otherwise, take the current time */
462-
463- n->until = usec_add(base, n->ttl * USEC_PER_SEC);
464- } else
465- n->until = 0;
466-
467- if (n->lldp)
468- prioq_reshuffle(n->lldp->neighbor_by_expiry, n, &n->prioq_idx);
469-}
470-
471-bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b) {
472- if (a == b)
473- return true;
474-
475- if (!a || !b)
476- return false;
477-
478- if (a->raw_size != b->raw_size)
479- return false;
480-
481- return memcmp(LLDP_NEIGHBOR_RAW(a), LLDP_NEIGHBOR_RAW(b), a->raw_size) == 0;
482-}
483-
484-_public_ int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address) {
485- assert_return(n, -EINVAL);
486- assert_return(address, -EINVAL);
487-
488- *address = n->source_address;
489- return 0;
490-}
491-
492-_public_ int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address) {
493- assert_return(n, -EINVAL);
494- assert_return(address, -EINVAL);
495-
496- *address = n->destination_address;
497- return 0;
498-}
499-
500-_public_ int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
501- assert_return(n, -EINVAL);
502- assert_return(ret, -EINVAL);
503- assert_return(size, -EINVAL);
504-
505- *ret = LLDP_NEIGHBOR_RAW(n);
506- *size = n->raw_size;
507-
508- return 0;
509-}
510-
511-_public_ int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
512- assert_return(n, -EINVAL);
513- assert_return(type, -EINVAL);
514- assert_return(ret, -EINVAL);
515- assert_return(size, -EINVAL);
516-
517- assert(n->id.chassis_id_size > 0);
518-
519- *type = *(uint8_t*) n->id.chassis_id;
520- *ret = (uint8_t*) n->id.chassis_id + 1;
521- *size = n->id.chassis_id_size - 1;
522-
523- return 0;
524-}
525-
526-static int format_mac_address(const void *data, size_t sz, char **ret) {
527- struct ether_addr a;
528- char *k;
529-
530- assert(data || sz <= 0);
531-
532- if (sz != 7)
533- return 0;
534-
535- memcpy(&a, (uint8_t*) data + 1, sizeof(a));
536-
537- k = new(char, ETHER_ADDR_TO_STRING_MAX);
538- if (!k)
539- return -ENOMEM;
540-
541- *ret = ether_addr_to_string(&a, k);
542- return 1;
543-}
544-
545-static int format_network_address(const void *data, size_t sz, char **ret) {
546- union in_addr_union a;
547- int family, r;
548-
549- if (sz == 6 && ((uint8_t*) data)[1] == 1) {
550- memcpy(&a.in, (uint8_t*) data + 2, sizeof(a.in));
551- family = AF_INET;
552- } else if (sz == 18 && ((uint8_t*) data)[1] == 2) {
553- memcpy(&a.in6, (uint8_t*) data + 2, sizeof(a.in6));
554- family = AF_INET6;
555- } else
556- return 0;
557-
558- r = in_addr_to_string(family, &a, ret);
559- if (r < 0)
560- return r;
561- return 1;
562-}
563-
564-_public_ int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret) {
565- char *k;
566- int r;
567-
568- assert_return(n, -EINVAL);
569- assert_return(ret, -EINVAL);
570-
571- if (n->chassis_id_as_string) {
572- *ret = n->chassis_id_as_string;
573- return 0;
574- }
575-
576- assert(n->id.chassis_id_size > 0);
577-
578- switch (*(uint8_t*) n->id.chassis_id) {
579-
580- case SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT:
581- case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS:
582- case SD_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT:
583- case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME:
584- case SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED:
585- k = cescape_length((char*) n->id.chassis_id + 1, n->id.chassis_id_size - 1);
586- if (!k)
587- return -ENOMEM;
588-
589- goto done;
590-
591- case SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
592- r = format_mac_address(n->id.chassis_id, n->id.chassis_id_size, &k);
593- if (r < 0)
594- return r;
595- if (r > 0)
596- goto done;
597-
598- break;
599-
600- case SD_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS:
601- r = format_network_address(n->id.chassis_id, n->id.chassis_id_size, &k);
602- if (r < 0)
603- return r;
604- if (r > 0)
605- goto done;
606-
607- break;
608- }
609-
610- /* Generic fallback */
611- k = hexmem(n->id.chassis_id, n->id.chassis_id_size);
612- if (!k)
613- return -ENOMEM;
614-
615-done:
616- *ret = n->chassis_id_as_string = k;
617- return 0;
618-}
619-
620-_public_ int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
621- assert_return(n, -EINVAL);
622- assert_return(type, -EINVAL);
623- assert_return(ret, -EINVAL);
624- assert_return(size, -EINVAL);
625-
626- assert(n->id.port_id_size > 0);
627-
628- *type = *(uint8_t*) n->id.port_id;
629- *ret = (uint8_t*) n->id.port_id + 1;
630- *size = n->id.port_id_size - 1;
631-
632- return 0;
633-}
634-
635-_public_ int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret) {
636- char *k;
637- int r;
638-
639- assert_return(n, -EINVAL);
640- assert_return(ret, -EINVAL);
641-
642- if (n->port_id_as_string) {
643- *ret = n->port_id_as_string;
644- return 0;
645- }
646-
647- assert(n->id.port_id_size > 0);
648-
649- switch (*(uint8_t*) n->id.port_id) {
650-
651- case SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
652- case SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT:
653- case SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME:
654- case SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
655- k = cescape_length((char*) n->id.port_id + 1, n->id.port_id_size - 1);
656- if (!k)
657- return -ENOMEM;
658-
659- goto done;
660-
661- case SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS:
662- r = format_mac_address(n->id.port_id, n->id.port_id_size, &k);
663- if (r < 0)
664- return r;
665- if (r > 0)
666- goto done;
667-
668- break;
669-
670- case SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS:
671- r = format_network_address(n->id.port_id, n->id.port_id_size, &k);
672- if (r < 0)
673- return r;
674- if (r > 0)
675- goto done;
676-
677- break;
678- }
679-
680- /* Generic fallback */
681- k = hexmem(n->id.port_id, n->id.port_id_size);
682- if (!k)
683- return -ENOMEM;
684-
685-done:
686- *ret = n->port_id_as_string = k;
687- return 0;
688-}
689-
690-_public_ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec) {
691- assert_return(n, -EINVAL);
692- assert_return(ret_sec, -EINVAL);
693-
694- *ret_sec = n->ttl;
695- return 0;
696-}
697-
698-_public_ int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret) {
699- assert_return(n, -EINVAL);
700- assert_return(ret, -EINVAL);
701-
702- if (!n->system_name)
703- return -ENODATA;
704-
705- *ret = n->system_name;
706- return 0;
707-}
708-
709-_public_ int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret) {
710- assert_return(n, -EINVAL);
711- assert_return(ret, -EINVAL);
712-
713- if (!n->system_description)
714- return -ENODATA;
715-
716- *ret = n->system_description;
717- return 0;
718-}
719-
720-_public_ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret) {
721- assert_return(n, -EINVAL);
722- assert_return(ret, -EINVAL);
723-
724- if (!n->port_description)
725- return -ENODATA;
726-
727- *ret = n->port_description;
728- return 0;
729-}
730-
731-_public_ int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
732- assert_return(n, -EINVAL);
733- assert_return(ret, -EINVAL);
734-
735- if (!n->has_capabilities)
736- return -ENODATA;
737-
738- *ret = n->system_capabilities;
739- return 0;
740-}
741-
742-_public_ int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
743- assert_return(n, -EINVAL);
744- assert_return(ret, -EINVAL);
745-
746- if (!n->has_capabilities)
747- return -ENODATA;
748-
749- *ret = n->enabled_capabilities;
750- return 0;
751-}
752-
753-_public_ int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) {
754- _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
755- int r;
756-
757- assert_return(ret, -EINVAL);
758- assert_return(raw || raw_size <= 0, -EINVAL);
759-
760- n = lldp_neighbor_new(raw_size);
761- if (!n)
762- return -ENOMEM;
763-
764- memcpy(LLDP_NEIGHBOR_RAW(n), raw, raw_size);
765- r = lldp_neighbor_parse(n);
766- if (r < 0)
767- return r;
768-
769- *ret = TAKE_PTR(n);
770-
771- return r;
772-}
773-
774-_public_ int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) {
775- assert_return(n, -EINVAL);
776-
777- assert(n->raw_size >= sizeof(struct ether_header));
778- n->rindex = sizeof(struct ether_header);
779-
780- return n->rindex < n->raw_size;
781-}
782-
783-_public_ int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) {
784- size_t length;
785-
786- assert_return(n, -EINVAL);
787-
788- if (n->rindex == n->raw_size) /* EOF */
789- return -ESPIPE;
790-
791- if (n->rindex + 2 > n->raw_size) /* Truncated message */
792- return -EBADMSG;
793-
794- length = LLDP_NEIGHBOR_TLV_LENGTH(n);
795- if (n->rindex + 2 + length > n->raw_size)
796- return -EBADMSG;
797-
798- n->rindex += 2 + length;
799- return n->rindex < n->raw_size;
800-}
801-
802-_public_ int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) {
803- assert_return(n, -EINVAL);
804- assert_return(type, -EINVAL);
805-
806- if (n->rindex == n->raw_size) /* EOF */
807- return -ESPIPE;
808-
809- if (n->rindex + 2 > n->raw_size)
810- return -EBADMSG;
811-
812- *type = LLDP_NEIGHBOR_TLV_TYPE(n);
813- return 0;
814-}
815-
816-_public_ int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) {
817- uint8_t k;
818- int r;
819-
820- assert_return(n, -EINVAL);
821-
822- r = sd_lldp_neighbor_tlv_get_type(n, &k);
823- if (r < 0)
824- return r;
825-
826- return type == k;
827-}
828-
829-_public_ int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t *subtype) {
830- const uint8_t *d;
831- size_t length;
832- int r;
833-
834- assert_return(n, -EINVAL);
835- assert_return(oui, -EINVAL);
836- assert_return(subtype, -EINVAL);
837-
838- r = sd_lldp_neighbor_tlv_is_type(n, SD_LLDP_TYPE_PRIVATE);
839- if (r < 0)
840- return r;
841- if (r == 0)
842- return -ENXIO;
843-
844- length = LLDP_NEIGHBOR_TLV_LENGTH(n);
845- if (length < 4)
846- return -EBADMSG;
847-
848- if (n->rindex + 2 + length > n->raw_size)
849- return -EBADMSG;
850-
851- d = LLDP_NEIGHBOR_TLV_DATA(n);
852- memcpy(oui, d, 3);
853- *subtype = d[3];
854-
855- return 0;
856-}
857-
858-_public_ int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t subtype) {
859- uint8_t k[3], st;
860- int r;
861-
862- r = sd_lldp_neighbor_tlv_get_oui(n, k, &st);
863- if (r == -ENXIO)
864- return 0;
865- if (r < 0)
866- return r;
867-
868- return memcmp(k, oui, 3) == 0 && st == subtype;
869-}
870-
871-_public_ int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
872- size_t length;
873-
874- assert_return(n, -EINVAL);
875- assert_return(ret, -EINVAL);
876- assert_return(size, -EINVAL);
877-
878- /* Note that this returns the full TLV, including the TLV header */
879-
880- if (n->rindex + 2 > n->raw_size)
881- return -EBADMSG;
882-
883- length = LLDP_NEIGHBOR_TLV_LENGTH(n);
884- if (n->rindex + 2 + length > n->raw_size)
885- return -EBADMSG;
886-
887- *ret = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex;
888- *size = length + 2;
889-
890- return 0;
891-}
892-
893-_public_ int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret) {
894- assert_return(n, -EINVAL);
895- assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
896- assert_return(clock_supported(clock), -EOPNOTSUPP);
897- assert_return(ret, -EINVAL);
898-
899- if (!triple_timestamp_is_set(&n->timestamp))
900- return -ENODATA;
901-
902- *ret = triple_timestamp_by_clock(&n->timestamp, clock);
903- return 0;
904-}
905diff --git a/src/libsystemd-network/lldp-neighbor.h b/src/libsystemd-network/lldp-neighbor.h
906deleted file mode 100644
907index 62dbff42ca..0000000000
908--- a/src/libsystemd-network/lldp-neighbor.h
909+++ /dev/null
910@@ -1,91 +0,0 @@
911-/* SPDX-License-Identifier: LGPL-2.1+ */
912-#pragma once
913-
914-#include <inttypes.h>
915-#include <stdbool.h>
916-#include <sys/types.h>
917-
918-#include "sd-lldp.h"
919-
920-#include "hash-funcs.h"
921-#include "lldp-internal.h"
922-#include "time-util.h"
923-
924-typedef struct LLDPNeighborID {
925- /* The spec calls this an "MSAP identifier" */
926- void *chassis_id;
927- size_t chassis_id_size;
928-
929- void *port_id;
930- size_t port_id_size;
931-} LLDPNeighborID;
932-
933-struct sd_lldp_neighbor {
934- /* Neighbor objects stay around as long as they are linked into an "sd_lldp" object or n_ref > 0. */
935- sd_lldp *lldp;
936- unsigned n_ref;
937-
938- triple_timestamp timestamp;
939-
940- usec_t until;
941- unsigned prioq_idx;
942-
943- struct ether_addr source_address;
944- struct ether_addr destination_address;
945-
946- LLDPNeighborID id;
947-
948- /* The raw packet size. The data is appended to the object, accessible via LLDP_NEIGHBOR_RAW() */
949- size_t raw_size;
950-
951- /* The current read index for the iterative TLV interface */
952- size_t rindex;
953-
954- /* And a couple of fields parsed out. */
955- bool has_ttl:1;
956- bool has_capabilities:1;
957- bool has_port_vlan_id:1;
958-
959- uint16_t ttl;
960-
961- uint16_t system_capabilities;
962- uint16_t enabled_capabilities;
963-
964- char *port_description;
965- char *system_name;
966- char *system_description;
967-
968- uint16_t port_vlan_id;
969-
970- char *chassis_id_as_string;
971- char *port_id_as_string;
972-};
973-
974-static inline void *LLDP_NEIGHBOR_RAW(const sd_lldp_neighbor *n) {
975- return (uint8_t*) n + ALIGN(sizeof(sd_lldp_neighbor));
976-}
977-
978-static inline uint8_t LLDP_NEIGHBOR_TLV_TYPE(const sd_lldp_neighbor *n) {
979- return ((uint8_t*) LLDP_NEIGHBOR_RAW(n))[n->rindex] >> 1;
980-}
981-
982-static inline size_t LLDP_NEIGHBOR_TLV_LENGTH(const sd_lldp_neighbor *n) {
983- uint8_t *p;
984-
985- p = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex;
986- return p[1] + (((size_t) (p[0] & 1)) << 8);
987-}
988-
989-static inline void* LLDP_NEIGHBOR_TLV_DATA(const sd_lldp_neighbor *n) {
990- return ((uint8_t*) LLDP_NEIGHBOR_RAW(n)) + n->rindex + 2;
991-}
992-
993-extern const struct hash_ops lldp_neighbor_hash_ops;
994-int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y);
995-int lldp_neighbor_prioq_compare_func(const void *a, const void *b);
996-
997-sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n);
998-sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size);
999-int lldp_neighbor_parse(sd_lldp_neighbor *n);
1000-void lldp_neighbor_start_ttl(sd_lldp_neighbor *n);
1001-bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b);
1002diff --git a/src/libsystemd-network/lldp-network.c b/src/libsystemd-network/lldp-network.c
1003deleted file mode 100644
1004index 870584c0db..0000000000
1005--- a/src/libsystemd-network/lldp-network.c
1006+++ /dev/null
1007@@ -1,78 +0,0 @@
1008-/* SPDX-License-Identifier: LGPL-2.1+ */
1009-
1010-#include <linux/filter.h>
1011-#include <netinet/if_ether.h>
1012-
1013-#include "fd-util.h"
1014-#include "lldp-network.h"
1015-#include "missing.h"
1016-#include "socket-util.h"
1017-
1018-int lldp_network_bind_raw_socket(int ifindex) {
1019-
1020- static const struct sock_filter filter[] = {
1021- BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ethhdr, h_dest)), /* A <- 4 bytes of destination MAC */
1022- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0180c200, 1, 0), /* A != 01:80:c2:00 */
1023- BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */
1024- BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_dest) + 4), /* A <- remaining 2 bytes of destination MAC */
1025- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0000, 3, 0), /* A != 00:00 */
1026- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0003, 2, 0), /* A != 00:03 */
1027- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x000e, 1, 0), /* A != 00:0e */
1028- BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */
1029- BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_proto)), /* A <- protocol */
1030- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_LLDP, 1, 0), /* A != ETHERTYPE_LLDP */
1031- BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */
1032- BPF_STMT(BPF_RET + BPF_K, (uint32_t) -1), /* accept packet */
1033- };
1034-
1035- static const struct sock_fprog fprog = {
1036- .len = ELEMENTSOF(filter),
1037- .filter = (struct sock_filter*) filter,
1038- };
1039-
1040- struct packet_mreq mreq = {
1041- .mr_ifindex = ifindex,
1042- .mr_type = PACKET_MR_MULTICAST,
1043- .mr_alen = ETH_ALEN,
1044- .mr_address = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x00 }
1045- };
1046-
1047- union sockaddr_union saddrll = {
1048- .ll.sll_family = AF_PACKET,
1049- .ll.sll_ifindex = ifindex,
1050- };
1051-
1052- _cleanup_close_ int fd = -1;
1053- int r;
1054-
1055- assert(ifindex > 0);
1056-
1057- fd = socket(PF_PACKET, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK,
1058- htobe16(ETHERTYPE_LLDP));
1059- if (fd < 0)
1060- return -errno;
1061-
1062- r = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
1063- if (r < 0)
1064- return -errno;
1065-
1066- r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
1067- if (r < 0)
1068- return -errno;
1069-
1070- mreq.mr_address[ETH_ALEN - 1] = 0x03;
1071- r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
1072- if (r < 0)
1073- return -errno;
1074-
1075- mreq.mr_address[ETH_ALEN - 1] = 0x0E;
1076- r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
1077- if (r < 0)
1078- return -errno;
1079-
1080- r = bind(fd, &saddrll.sa, sizeof(saddrll.ll));
1081- if (r < 0)
1082- return -errno;
1083-
1084- return TAKE_FD(fd);
1085-}
1086diff --git a/src/libsystemd-network/lldp-network.h b/src/libsystemd-network/lldp-network.h
1087deleted file mode 100644
1088index e4ed2898a5..0000000000
1089--- a/src/libsystemd-network/lldp-network.h
1090+++ /dev/null
1091@@ -1,6 +0,0 @@
1092-/* SPDX-License-Identifier: LGPL-2.1+ */
1093-#pragma once
1094-
1095-#include "sd-event.h"
1096-
1097-int lldp_network_bind_raw_socket(int ifindex);
1098diff --git a/src/libsystemd-network/meson.build b/src/libsystemd-network/meson.build
1099index 56d470ff68..88a72aa900 100644
1100--- a/src/libsystemd-network/meson.build
1101+++ b/src/libsystemd-network/meson.build
1102@@ -33,12 +33,6 @@ sources = files('''
1103 sd-dhcp6-lease.c
1104 dhcp-identifier.h
1105 dhcp-identifier.c
1106- lldp-internal.h
1107- lldp-network.h
1108- lldp-network.c
1109- lldp-neighbor.h
1110- lldp-neighbor.c
1111- sd-lldp.c
1112 '''.split())
1113
1114 network_internal_h = files('network-internal.h')
1115diff --git a/src/libsystemd-network/sd-lldp.c b/src/libsystemd-network/sd-lldp.c
1116deleted file mode 100644
1117index 1f28c5731f..0000000000
1118--- a/src/libsystemd-network/sd-lldp.c
1119+++ /dev/null
1120@@ -1,498 +0,0 @@
1121-/* SPDX-License-Identifier: LGPL-2.1+ */
1122-
1123-#include <arpa/inet.h>
1124-#include <linux/sockios.h>
1125-#include <sys/ioctl.h>
1126-
1127-#include "sd-lldp.h"
1128-
1129-#include "alloc-util.h"
1130-#include "ether-addr-util.h"
1131-#include "event-util.h"
1132-#include "fd-util.h"
1133-#include "lldp-internal.h"
1134-#include "lldp-neighbor.h"
1135-#include "lldp-network.h"
1136-#include "memory-util.h"
1137-#include "socket-util.h"
1138-#include "sort-util.h"
1139-#include "string-table.h"
1140-
1141-#define LLDP_DEFAULT_NEIGHBORS_MAX 128U
1142-
1143-static const char * const lldp_event_table[_SD_LLDP_EVENT_MAX] = {
1144- [SD_LLDP_EVENT_ADDED] = "added",
1145- [SD_LLDP_EVENT_REMOVED] = "removed",
1146- [SD_LLDP_EVENT_UPDATED] = "updated",
1147- [SD_LLDP_EVENT_REFRESHED] = "refreshed",
1148-};
1149-
1150-DEFINE_STRING_TABLE_LOOKUP(lldp_event, sd_lldp_event);
1151-
1152-static void lldp_flush_neighbors(sd_lldp *lldp) {
1153- assert(lldp);
1154-
1155- hashmap_clear(lldp->neighbor_by_id);
1156-}
1157-
1158-static void lldp_callback(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n) {
1159- assert(lldp);
1160- assert(event >= 0 && event < _SD_LLDP_EVENT_MAX);
1161-
1162- if (!lldp->callback) {
1163- log_lldp("Received '%s' event.", lldp_event_to_string(event));
1164- return;
1165- }
1166-
1167- log_lldp("Invoking callback for '%s' event.", lldp_event_to_string(event));
1168- lldp->callback(lldp, event, n, lldp->userdata);
1169-}
1170-
1171-static int lldp_make_space(sd_lldp *lldp, size_t extra) {
1172- usec_t t = USEC_INFINITY;
1173- bool changed = false;
1174-
1175- assert(lldp);
1176-
1177- /* Remove all entries that are past their TTL, and more until at least the specified number of extra entries
1178- * are free. */
1179-
1180- for (;;) {
1181- _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
1182-
1183- n = prioq_peek(lldp->neighbor_by_expiry);
1184- if (!n)
1185- break;
1186-
1187- sd_lldp_neighbor_ref(n);
1188-
1189- if (hashmap_size(lldp->neighbor_by_id) > LESS_BY(lldp->neighbors_max, extra))
1190- goto remove_one;
1191-
1192- if (t == USEC_INFINITY)
1193- t = now(clock_boottime_or_monotonic());
1194-
1195- if (n->until > t)
1196- break;
1197-
1198- remove_one:
1199- lldp_neighbor_unlink(n);
1200- lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, n);
1201- changed = true;
1202- }
1203-
1204- return changed;
1205-}
1206-
1207-static bool lldp_keep_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) {
1208- assert(lldp);
1209- assert(n);
1210-
1211- /* Don't keep data with a zero TTL */
1212- if (n->ttl <= 0)
1213- return false;
1214-
1215- /* Filter out data from the filter address */
1216- if (!ether_addr_is_null(&lldp->filter_address) &&
1217- ether_addr_equal(&lldp->filter_address, &n->source_address))
1218- return false;
1219-
1220- /* Only add if the neighbor has a capability we are interested in. Note that we also store all neighbors with
1221- * no caps field set. */
1222- if (n->has_capabilities &&
1223- (n->enabled_capabilities & lldp->capability_mask) == 0)
1224- return false;
1225-
1226- /* Keep everything else */
1227- return true;
1228-}
1229-
1230-static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor);
1231-
1232-static int lldp_add_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) {
1233- _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *old = NULL;
1234- bool keep;
1235- int r;
1236-
1237- assert(lldp);
1238- assert(n);
1239- assert(!n->lldp);
1240-
1241- keep = lldp_keep_neighbor(lldp, n);
1242-
1243- /* First retrieve the old entry for this MSAP */
1244- old = hashmap_get(lldp->neighbor_by_id, &n->id);
1245- if (old) {
1246- sd_lldp_neighbor_ref(old);
1247-
1248- if (!keep) {
1249- lldp_neighbor_unlink(old);
1250- lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old);
1251- return 0;
1252- }
1253-
1254- if (lldp_neighbor_equal(n, old)) {
1255- /* Is this equal, then restart the TTL counter, but don't do anything else. */
1256- old->timestamp = n->timestamp;
1257- lldp_start_timer(lldp, old);
1258- lldp_callback(lldp, SD_LLDP_EVENT_REFRESHED, old);
1259- return 0;
1260- }
1261-
1262- /* Data changed, remove the old entry, and add a new one */
1263- lldp_neighbor_unlink(old);
1264-
1265- } else if (!keep)
1266- return 0;
1267-
1268- /* Then, make room for at least one new neighbor */
1269- lldp_make_space(lldp, 1);
1270-
1271- r = hashmap_put(lldp->neighbor_by_id, &n->id, n);
1272- if (r < 0)
1273- goto finish;
1274-
1275- r = prioq_put(lldp->neighbor_by_expiry, n, &n->prioq_idx);
1276- if (r < 0) {
1277- assert_se(hashmap_remove(lldp->neighbor_by_id, &n->id) == n);
1278- goto finish;
1279- }
1280-
1281- n->lldp = lldp;
1282-
1283- lldp_start_timer(lldp, n);
1284- lldp_callback(lldp, old ? SD_LLDP_EVENT_UPDATED : SD_LLDP_EVENT_ADDED, n);
1285-
1286- return 1;
1287-
1288-finish:
1289- if (old)
1290- lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old);
1291-
1292- return r;
1293-}
1294-
1295-static int lldp_handle_datagram(sd_lldp *lldp, sd_lldp_neighbor *n) {
1296- int r;
1297-
1298- assert(lldp);
1299- assert(n);
1300-
1301- r = lldp_neighbor_parse(n);
1302- if (r == -EBADMSG) /* Ignore bad messages */
1303- return 0;
1304- if (r < 0)
1305- return r;
1306-
1307- r = lldp_add_neighbor(lldp, n);
1308- if (r < 0) {
1309- log_lldp_errno(r, "Failed to add datagram. Ignoring.");
1310- return 0;
1311- }
1312-
1313- log_lldp("Successfully processed LLDP datagram.");
1314- return 0;
1315-}
1316-
1317-static int lldp_receive_datagram(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
1318- _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
1319- ssize_t space, length;
1320- sd_lldp *lldp = userdata;
1321- struct timespec ts;
1322-
1323- assert(fd >= 0);
1324- assert(lldp);
1325-
1326- space = next_datagram_size_fd(fd);
1327- if (space < 0)
1328- return log_lldp_errno(space, "Failed to determine datagram size to read: %m");
1329-
1330- n = lldp_neighbor_new(space);
1331- if (!n)
1332- return -ENOMEM;
1333-
1334- length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT);
1335- if (length < 0) {
1336- if (IN_SET(errno, EAGAIN, EINTR))
1337- return 0;
1338-
1339- return log_lldp_errno(errno, "Failed to read LLDP datagram: %m");
1340- }
1341-
1342- if ((size_t) length != n->raw_size) {
1343- log_lldp("Packet size mismatch.");
1344- return -EINVAL;
1345- }
1346-
1347- /* Try to get the timestamp of this packet if it is known */
1348- if (ioctl(fd, SIOCGSTAMPNS, &ts) >= 0)
1349- triple_timestamp_from_realtime(&n->timestamp, timespec_load(&ts));
1350- else
1351- triple_timestamp_get(&n->timestamp);
1352-
1353- return lldp_handle_datagram(lldp, n);
1354-}
1355-
1356-static void lldp_reset(sd_lldp *lldp) {
1357- assert(lldp);
1358-
1359- (void) event_source_disable(lldp->timer_event_source);
1360- lldp->io_event_source = sd_event_source_unref(lldp->io_event_source);
1361- lldp->fd = safe_close(lldp->fd);
1362-}
1363-
1364-_public_ int sd_lldp_start(sd_lldp *lldp) {
1365- int r;
1366-
1367- assert_return(lldp, -EINVAL);
1368- assert_return(lldp->event, -EINVAL);
1369- assert_return(lldp->ifindex > 0, -EINVAL);
1370-
1371- if (lldp->fd >= 0)
1372- return 0;
1373-
1374- assert(!lldp->io_event_source);
1375-
1376- lldp->fd = lldp_network_bind_raw_socket(lldp->ifindex);
1377- if (lldp->fd < 0)
1378- return lldp->fd;
1379-
1380- r = sd_event_add_io(lldp->event, &lldp->io_event_source, lldp->fd, EPOLLIN, lldp_receive_datagram, lldp);
1381- if (r < 0)
1382- goto fail;
1383-
1384- r = sd_event_source_set_priority(lldp->io_event_source, lldp->event_priority);
1385- if (r < 0)
1386- goto fail;
1387-
1388- (void) sd_event_source_set_description(lldp->io_event_source, "lldp-io");
1389-
1390- log_lldp("Started LLDP client");
1391- return 1;
1392-
1393-fail:
1394- lldp_reset(lldp);
1395- return r;
1396-}
1397-
1398-_public_ int sd_lldp_stop(sd_lldp *lldp) {
1399- assert_return(lldp, -EINVAL);
1400-
1401- if (lldp->fd < 0)
1402- return 0;
1403-
1404- log_lldp("Stopping LLDP client");
1405-
1406- lldp_reset(lldp);
1407- lldp_flush_neighbors(lldp);
1408-
1409- return 1;
1410-}
1411-
1412-_public_ int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int64_t priority) {
1413- int r;
1414-
1415- assert_return(lldp, -EINVAL);
1416- assert_return(lldp->fd < 0, -EBUSY);
1417- assert_return(!lldp->event, -EBUSY);
1418-
1419- if (event)
1420- lldp->event = sd_event_ref(event);
1421- else {
1422- r = sd_event_default(&lldp->event);
1423- if (r < 0)
1424- return r;
1425- }
1426-
1427- lldp->event_priority = priority;
1428-
1429- return 0;
1430-}
1431-
1432-_public_ int sd_lldp_detach_event(sd_lldp *lldp) {
1433-
1434- assert_return(lldp, -EINVAL);
1435- assert_return(lldp->fd < 0, -EBUSY);
1436-
1437- lldp->event = sd_event_unref(lldp->event);
1438- return 0;
1439-}
1440-
1441-_public_ sd_event* sd_lldp_get_event(sd_lldp *lldp) {
1442- assert_return(lldp, NULL);
1443-
1444- return lldp->event;
1445-}
1446-
1447-_public_ int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata) {
1448- assert_return(lldp, -EINVAL);
1449-
1450- lldp->callback = cb;
1451- lldp->userdata = userdata;
1452-
1453- return 0;
1454-}
1455-
1456-_public_ int sd_lldp_set_ifindex(sd_lldp *lldp, int ifindex) {
1457- assert_return(lldp, -EINVAL);
1458- assert_return(ifindex > 0, -EINVAL);
1459- assert_return(lldp->fd < 0, -EBUSY);
1460-
1461- lldp->ifindex = ifindex;
1462- return 0;
1463-}
1464-
1465-static sd_lldp* lldp_free(sd_lldp *lldp) {
1466- assert(lldp);
1467-
1468- lldp->timer_event_source = sd_event_source_unref(lldp->timer_event_source);
1469-
1470- lldp_reset(lldp);
1471- sd_lldp_detach_event(lldp);
1472- lldp_flush_neighbors(lldp);
1473-
1474- hashmap_free(lldp->neighbor_by_id);
1475- prioq_free(lldp->neighbor_by_expiry);
1476- return mfree(lldp);
1477-}
1478-
1479-DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_lldp, sd_lldp, lldp_free);
1480-
1481-_public_ int sd_lldp_new(sd_lldp **ret) {
1482- _cleanup_(sd_lldp_unrefp) sd_lldp *lldp = NULL;
1483- int r;
1484-
1485- assert_return(ret, -EINVAL);
1486-
1487- lldp = new(sd_lldp, 1);
1488- if (!lldp)
1489- return -ENOMEM;
1490-
1491- *lldp = (sd_lldp) {
1492- .n_ref = 1,
1493- .fd = -1,
1494- .neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX,
1495- .capability_mask = (uint16_t) -1,
1496- };
1497-
1498- lldp->neighbor_by_id = hashmap_new(&lldp_neighbor_hash_ops);
1499- if (!lldp->neighbor_by_id)
1500- return -ENOMEM;
1501-
1502- r = prioq_ensure_allocated(&lldp->neighbor_by_expiry, lldp_neighbor_prioq_compare_func);
1503- if (r < 0)
1504- return r;
1505-
1506- *ret = TAKE_PTR(lldp);
1507-
1508- return 0;
1509-}
1510-
1511-static int neighbor_compare_func(sd_lldp_neighbor * const *a, sd_lldp_neighbor * const *b) {
1512- return lldp_neighbor_id_compare_func(&(*a)->id, &(*b)->id);
1513-}
1514-
1515-static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) {
1516- sd_lldp *lldp = userdata;
1517- int r;
1518-
1519- r = lldp_make_space(lldp, 0);
1520- if (r < 0)
1521- return log_lldp_errno(r, "Failed to make space: %m");
1522-
1523- r = lldp_start_timer(lldp, NULL);
1524- if (r < 0)
1525- return log_lldp_errno(r, "Failed to restart timer: %m");
1526-
1527- return 0;
1528-}
1529-
1530-static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor) {
1531- sd_lldp_neighbor *n;
1532-
1533- assert(lldp);
1534-
1535- if (neighbor)
1536- lldp_neighbor_start_ttl(neighbor);
1537-
1538- n = prioq_peek(lldp->neighbor_by_expiry);
1539- if (!n)
1540- return event_source_disable(lldp->timer_event_source);
1541-
1542- if (!lldp->event)
1543- return 0;
1544-
1545- return event_reset_time(lldp->event, &lldp->timer_event_source,
1546- clock_boottime_or_monotonic(),
1547- n->until, 0,
1548- on_timer_event, lldp,
1549- lldp->event_priority, "lldp-timer", true);
1550-}
1551-
1552-_public_ int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***ret) {
1553- sd_lldp_neighbor **l = NULL, *n;
1554- Iterator i;
1555- int k = 0, r;
1556-
1557- assert_return(lldp, -EINVAL);
1558- assert_return(ret, -EINVAL);
1559-
1560- if (hashmap_isempty(lldp->neighbor_by_id)) { /* Special shortcut */
1561- *ret = NULL;
1562- return 0;
1563- }
1564-
1565- l = new0(sd_lldp_neighbor*, hashmap_size(lldp->neighbor_by_id));
1566- if (!l)
1567- return -ENOMEM;
1568-
1569- r = lldp_start_timer(lldp, NULL);
1570- if (r < 0) {
1571- free(l);
1572- return r;
1573- }
1574-
1575- HASHMAP_FOREACH(n, lldp->neighbor_by_id, i)
1576- l[k++] = sd_lldp_neighbor_ref(n);
1577-
1578- assert((size_t) k == hashmap_size(lldp->neighbor_by_id));
1579-
1580- /* Return things in a stable order */
1581- typesafe_qsort(l, k, neighbor_compare_func);
1582- *ret = l;
1583-
1584- return k;
1585-}
1586-
1587-_public_ int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t m) {
1588- assert_return(lldp, -EINVAL);
1589- assert_return(m <= 0, -EINVAL);
1590-
1591- lldp->neighbors_max = m;
1592- lldp_make_space(lldp, 0);
1593-
1594- return 0;
1595-}
1596-
1597-_public_ int sd_lldp_match_capabilities(sd_lldp *lldp, uint16_t mask) {
1598- assert_return(lldp, -EINVAL);
1599- assert_return(mask != 0, -EINVAL);
1600-
1601- lldp->capability_mask = mask;
1602-
1603- return 0;
1604-}
1605-
1606-_public_ int sd_lldp_set_filter_address(sd_lldp *lldp, const struct ether_addr *addr) {
1607- assert_return(lldp, -EINVAL);
1608-
1609- /* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so
1610- * that our own can be filtered out here. */
1611-
1612- if (addr)
1613- lldp->filter_address = *addr;
1614- else
1615- zero(lldp->filter_address);
1616-
1617- return 0;
1618-}
1619diff --git a/src/libsystemd-network/test-lldp.c b/src/libsystemd-network/test-lldp.c
1620deleted file mode 100644
1621index 7406f94ce0..0000000000
1622--- a/src/libsystemd-network/test-lldp.c
1623+++ /dev/null
1624@@ -1,379 +0,0 @@
1625-/* SPDX-License-Identifier: LGPL-2.1+ */
1626-
1627-#include <arpa/inet.h>
1628-#include <errno.h>
1629-#include <net/ethernet.h>
1630-#include <stdio.h>
1631-#include <string.h>
1632-#include <unistd.h>
1633-
1634-#include "sd-event.h"
1635-#include "sd-lldp.h"
1636-
1637-#include "alloc-util.h"
1638-#include "fd-util.h"
1639-#include "lldp-network.h"
1640-#include "macro.h"
1641-#include "string-util.h"
1642-#include "tests.h"
1643-
1644-#define TEST_LLDP_PORT "em1"
1645-#define TEST_LLDP_TYPE_SYSTEM_NAME "systemd-lldp"
1646-#define TEST_LLDP_TYPE_SYSTEM_DESC "systemd-lldp-desc"
1647-
1648-static int test_fd[2] = { -1, -1 };
1649-static int lldp_handler_calls;
1650-
1651-int lldp_network_bind_raw_socket(int ifindex) {
1652- if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0)
1653- return -errno;
1654-
1655- return test_fd[0];
1656-}
1657-
1658-static void lldp_handler(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata) {
1659- lldp_handler_calls++;
1660-}
1661-
1662-static int start_lldp(sd_lldp **lldp, sd_event *e, sd_lldp_callback_t cb, void *cb_data) {
1663- int r;
1664-
1665- r = sd_lldp_new(lldp);
1666- if (r < 0)
1667- return r;
1668-
1669- r = sd_lldp_set_ifindex(*lldp, 42);
1670- if (r < 0)
1671- return r;
1672-
1673- r = sd_lldp_set_callback(*lldp, cb, cb_data);
1674- if (r < 0)
1675- return r;
1676-
1677- r = sd_lldp_attach_event(*lldp, e, 0);
1678- if (r < 0)
1679- return r;
1680-
1681- r = sd_lldp_start(*lldp);
1682- if (r < 0)
1683- return r;
1684-
1685- return 0;
1686-}
1687-
1688-static int stop_lldp(sd_lldp *lldp) {
1689- int r;
1690-
1691- r = sd_lldp_stop(lldp);
1692- if (r < 0)
1693- return r;
1694-
1695- r = sd_lldp_detach_event(lldp);
1696- if (r < 0)
1697- return r;
1698-
1699- sd_lldp_unref(lldp);
1700- safe_close(test_fd[1]);
1701-
1702- return 0;
1703-}
1704-
1705-static void test_receive_basic_packet(sd_event *e) {
1706-
1707- static const uint8_t frame[] = {
1708- /* Ethernet header */
1709- 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */
1710- 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
1711- 0x88, 0xcc, /* Ethertype */
1712- /* LLDP mandatory TLVs */
1713- 0x02, 0x07, 0x04, 0x00, 0x01, 0x02, /* Chassis: MAC, 00:01:02:03:04:05 */
1714- 0x03, 0x04, 0x05,
1715- 0x04, 0x04, 0x05, 0x31, 0x2f, 0x33, /* Port: interface name, "1/3" */
1716- 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */
1717- /* LLDP optional TLVs */
1718- 0x08, 0x04, 0x50, 0x6f, 0x72, 0x74, /* Port Description: "Port" */
1719- 0x0a, 0x03, 0x53, 0x59, 0x53, /* System Name: "SYS" */
1720- 0x0c, 0x04, 0x66, 0x6f, 0x6f, 0x00, /* System Description: "foo" (NULL-terminated) */
1721- 0x00, 0x00 /* End Of LLDPDU */
1722- };
1723-
1724- sd_lldp *lldp;
1725- sd_lldp_neighbor **neighbors;
1726- uint8_t type;
1727- const void *data;
1728- uint16_t ttl;
1729- size_t length;
1730- const char *str;
1731-
1732- lldp_handler_calls = 0;
1733- assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0);
1734-
1735- assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame));
1736- sd_event_run(e, 0);
1737- assert_se(lldp_handler_calls == 1);
1738- assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 1);
1739-
1740- assert_se(sd_lldp_neighbor_get_chassis_id(neighbors[0], &type, &data, &length) == 0);
1741- assert_se(type == SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS);
1742- assert_se(length == ETH_ALEN);
1743- assert_se(!memcmp(data, "\x00\x01\x02\x03\x04\x05", ETH_ALEN));
1744-
1745- assert_se(sd_lldp_neighbor_get_port_id(neighbors[0], &type, &data, &length) == 0);
1746- assert_se(type == SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME);
1747- assert_se(length == 3);
1748- assert_se(!memcmp(data, "1/3", 3));
1749-
1750- assert_se(sd_lldp_neighbor_get_port_description(neighbors[0], &str) == 0);
1751- assert_se(streq(str, "Port"));
1752-
1753- assert_se(sd_lldp_neighbor_get_system_name(neighbors[0], &str) == 0);
1754- assert_se(streq(str, "SYS"));
1755-
1756- assert_se(sd_lldp_neighbor_get_system_description(neighbors[0], &str) == 0);
1757- assert_se(streq(str, "foo"));
1758-
1759- assert_se(sd_lldp_neighbor_get_ttl(neighbors[0], &ttl) == 0);
1760- assert_se(ttl == 120);
1761-
1762- sd_lldp_neighbor_unref(neighbors[0]);
1763- free(neighbors);
1764-
1765- assert_se(stop_lldp(lldp) == 0);
1766-}
1767-
1768-static void test_receive_incomplete_packet(sd_event *e) {
1769- sd_lldp *lldp;
1770- sd_lldp_neighbor **neighbors;
1771- uint8_t frame[] = {
1772- /* Ethernet header */
1773- 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */
1774- 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
1775- 0x88, 0xcc, /* Ethertype */
1776- /* LLDP mandatory TLVs */
1777- 0x02, 0x07, 0x04, 0x00, 0x01, 0x02, /* Chassis: MAC, 00:01:02:03:04:05 */
1778- 0x03, 0x04, 0x05,
1779- 0x04, 0x04, 0x05, 0x31, 0x2f, 0x33, /* Port: interface name, "1/3" */
1780- /* Missing TTL */
1781- 0x00, 0x00 /* End Of LLDPDU */
1782- };
1783-
1784- lldp_handler_calls = 0;
1785- assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0);
1786-
1787- assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame));
1788- sd_event_run(e, 0);
1789- assert_se(lldp_handler_calls == 0);
1790- assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 0);
1791-
1792- assert_se(stop_lldp(lldp) == 0);
1793-}
1794-
1795-static void test_receive_oui_packet(sd_event *e) {
1796- sd_lldp *lldp;
1797- sd_lldp_neighbor **neighbors;
1798- uint8_t frame[] = {
1799- /* Ethernet header */
1800- 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */
1801- 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
1802- 0x88, 0xcc, /* Ethertype */
1803- /* LLDP mandatory TLVs */
1804- 0x02, 0x07, 0x04, 0x00, 0x01, 0x02, /* Chassis: MAC, 00:01:02:03:04:05 */
1805- 0x03, 0x04, 0x05,
1806- 0x04, 0x04, 0x05, 0x31, 0x2f, 0x33, /* Port TLV: interface name, "1/3" */
1807- 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */
1808- /* LLDP optional TLVs */
1809- 0xfe, 0x06, 0x00, 0x80, 0xc2, 0x01, /* Port VLAN ID: 0x1234 */
1810- 0x12, 0x34,
1811- 0xfe, 0x07, 0x00, 0x80, 0xc2, 0x02, /* Port and protocol: flag 1, PPVID 0x7788 */
1812- 0x01, 0x77, 0x88,
1813- 0xfe, 0x0d, 0x00, 0x80, 0xc2, 0x03, /* VLAN Name: ID 0x1234, name "Vlan51" */
1814- 0x12, 0x34, 0x06, 0x56, 0x6c, 0x61,
1815- 0x6e, 0x35, 0x31,
1816- 0xfe, 0x06, 0x00, 0x80, 0xc2, 0x06, /* Management VID: 0x0102 */
1817- 0x01, 0x02,
1818- 0xfe, 0x09, 0x00, 0x80, 0xc2, 0x07, /* Link aggregation: status 1, ID 0x00140012 */
1819- 0x01, 0x00, 0x14, 0x00, 0x12,
1820- 0xfe, 0x07, 0x00, 0x12, 0x0f, 0x02, /* 802.3 Power via MDI: PSE, MDI enabled */
1821- 0x07, 0x01, 0x00,
1822- 0x00, 0x00 /* End of LLDPDU */
1823- };
1824-
1825- lldp_handler_calls = 0;
1826- assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0);
1827-
1828- assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame));
1829- sd_event_run(e, 0);
1830- assert_se(lldp_handler_calls == 1);
1831- assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 1);
1832-
1833- assert_se(sd_lldp_neighbor_tlv_rewind(neighbors[0]) >= 0);
1834- assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_CHASSIS_ID) > 0);
1835- assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
1836- assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_PORT_ID) > 0);
1837- assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
1838- assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_TTL) > 0);
1839- assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
1840- assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID) > 0);
1841- assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
1842- assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID) > 0);
1843- assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
1844- assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME) > 0);
1845- assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
1846- assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_MANAGEMENT_VID) > 0);
1847- assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
1848- assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION) > 0);
1849- assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
1850- assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_3, SD_LLDP_OUI_802_3_SUBTYPE_POWER_VIA_MDI) > 0);
1851- assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
1852- assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_END) > 0);
1853- assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) == 0);
1854-
1855- sd_lldp_neighbor_unref(neighbors[0]);
1856- free(neighbors);
1857-
1858- assert_se(stop_lldp(lldp) == 0);
1859-}
1860-
1861-static void test_multiple_neighbors_sorted(sd_event *e) {
1862-
1863- static const uint8_t frame1[] = {
1864- /* Ethernet header */
1865- 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */
1866- 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
1867- 0x88, 0xcc, /* Ethertype */
1868- /* LLDP mandatory TLVs */
1869- 0x02, 0x04, 0x01, '1', '/', '2', /* Chassis component: "1/2" */
1870- 0x04, 0x04, 0x02, '2', '/', '3', /* Port component: "2/3" */
1871- 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */
1872- 0x00, 0x00 /* End Of LLDPDU */
1873- };
1874- static const uint8_t frame2[] = {
1875- /* Ethernet header */
1876- 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */
1877- 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
1878- 0x88, 0xcc, /* Ethertype */
1879- /* LLDP mandatory TLVs */
1880- 0x02, 0x04, 0x01, '2', '/', '1', /* Chassis component: "2/1" */
1881- 0x04, 0x04, 0x02, '1', '/', '3', /* Port component: "1/3" */
1882- 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */
1883- 0x00, 0x00 /* End Of LLDPDU */
1884- };
1885- static const uint8_t frame3[] = {
1886- /* Ethernet header */
1887- 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */
1888- 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
1889- 0x88, 0xcc, /* Ethertype */
1890- /* LLDP mandatory TLVs */
1891- 0x02, 0x05, 0x01, '2', '/', '1', '0', /* Chassis component: "2/10" */
1892- 0x04, 0x04, 0x02, '1', '/', '0', /* Port component: "1/0" */
1893- 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */
1894- 0x00, 0x00 /* End Of LLDPDU */
1895- };
1896- static const uint8_t frame4[] = {
1897- /* Ethernet header */
1898- 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */
1899- 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
1900- 0x88, 0xcc, /* Ethertype */
1901- /* LLDP mandatory TLVs */
1902- 0x02, 0x05, 0x01, '2', '/', '1', '9', /* Chassis component: "2/19" */
1903- 0x04, 0x04, 0x02, '1', '/', '0', /* Port component: "1/0" */
1904- 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */
1905- 0x00, 0x00 /* End Of LLDPDU */
1906- };
1907- static const uint8_t frame5[] = {
1908- /* Ethernet header */
1909- 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */
1910- 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
1911- 0x88, 0xcc, /* Ethertype */
1912- /* LLDP mandatory TLVs */
1913- 0x02, 0x04, 0x01, '1', '/', '2', /* Chassis component: "1/2" */
1914- 0x04, 0x05, 0x02, '2', '/', '1', '0', /* Port component: "2/10" */
1915- 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */
1916- 0x00, 0x00 /* End Of LLDPDU */
1917- };
1918- static const uint8_t frame6[] = {
1919- /* Ethernet header */
1920- 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */
1921- 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
1922- 0x88, 0xcc, /* Ethertype */
1923- /* LLDP mandatory TLVs */
1924- 0x02, 0x04, 0x01, '1', '/', '2', /* Chassis component: "1/2" */
1925- 0x04, 0x05, 0x02, '2', '/', '3', '9', /* Port component: "2/10" */
1926- 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */
1927- 0x00, 0x00 /* End Of LLDPDU */
1928- };
1929- static const char* expected[] = {
1930- /* ordered pairs of Chassis+Port */
1931- "1/2", "2/10",
1932- "1/2", "2/3",
1933- "1/2", "2/39",
1934- "2/1", "1/3",
1935- "2/10", "1/0",
1936- "2/19", "1/0",
1937- };
1938-
1939- sd_lldp *lldp;
1940- sd_lldp_neighbor **neighbors;
1941- int i;
1942- uint8_t type;
1943- const void *data;
1944- size_t length, expected_length;
1945- uint16_t ttl;
1946-
1947- lldp_handler_calls = 0;
1948- assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0);
1949-
1950- assert_se(write(test_fd[1], frame1, sizeof(frame1)) == sizeof(frame1));
1951- sd_event_run(e, 0);
1952- assert_se(write(test_fd[1], frame2, sizeof(frame2)) == sizeof(frame2));
1953- sd_event_run(e, 0);
1954- assert_se(write(test_fd[1], frame3, sizeof(frame3)) == sizeof(frame3));
1955- sd_event_run(e, 0);
1956- assert_se(write(test_fd[1], frame4, sizeof(frame4)) == sizeof(frame4));
1957- sd_event_run(e, 0);
1958- assert_se(write(test_fd[1], frame5, sizeof(frame5)) == sizeof(frame5));
1959- sd_event_run(e, 0);
1960- assert_se(write(test_fd[1], frame6, sizeof(frame6)) == sizeof(frame6));
1961- sd_event_run(e, 0);
1962- assert_se(lldp_handler_calls == 6);
1963-
1964- assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 6);
1965-
1966- for (i = 0; i < 6; i++) {
1967- assert_se(sd_lldp_neighbor_get_chassis_id(neighbors[i], &type, &data, &length) == 0);
1968- assert_se(type == SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT);
1969- expected_length = strlen(expected[2 * i]);
1970- assert_se(length == expected_length);
1971- assert_se(memcmp(data, expected[2 * i], expected_length) == 0);
1972-
1973- assert_se(sd_lldp_neighbor_get_port_id(neighbors[i], &type, &data, &length) == 0);
1974- assert_se(type == SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT);
1975- expected_length = strlen(expected[2 * i + 1]);
1976- assert_se(length == expected_length);
1977- assert_se(memcmp(data, expected[2 * i + 1], expected_length) == 0);
1978-
1979- assert_se(sd_lldp_neighbor_get_ttl(neighbors[i], &ttl) == 0);
1980- assert_se(ttl == 120);
1981- }
1982-
1983- for (i = 0; i < 6; i++)
1984- sd_lldp_neighbor_unref(neighbors[i]);
1985- free(neighbors);
1986-
1987- assert_se(stop_lldp(lldp) == 0);
1988-}
1989-
1990-int main(int argc, char *argv[]) {
1991- _cleanup_(sd_event_unrefp) sd_event *e = NULL;
1992-
1993- test_setup_logging(LOG_DEBUG);
1994-
1995- /* LLDP reception tests */
1996- assert_se(sd_event_new(&e) == 0);
1997- test_receive_basic_packet(e);
1998- test_receive_incomplete_packet(e);
1999- test_receive_oui_packet(e);
2000- test_multiple_neighbors_sorted(e);
2001-
2002- return 0;
2003-}
2004diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym
2005index 5ec42e0f1f..48021b86d3 100644
2006--- a/src/libsystemd/libsystemd.sym
2007+++ b/src/libsystemd/libsystemd.sym
2008@@ -681,4 +681,45 @@ LIBSYSTEMD_243 {
2009 global:
2010 sd_bus_object_vtable_format;
2011 sd_event_source_disable_unref;
2012+
2013+ sd_lldp_new;
2014+ sd_lldp_ref;
2015+ sd_lldp_unref;
2016+ sd_lldp_start;
2017+ sd_lldp_stop;
2018+ sd_lldp_attach_event;
2019+ sd_lldp_detach_event;
2020+ sd_lldp_get_event;
2021+ sd_lldp_set_callback;
2022+ sd_lldp_set_ifindex;
2023+ sd_lldp_set_neighbors_max;
2024+ sd_lldp_match_capabilities;
2025+ sd_lldp_set_filter_address;
2026+ sd_lldp_get_neighbors;
2027+ sd_lldp_neighbor_from_raw;
2028+ sd_lldp_neighbor_ref;
2029+ sd_lldp_neighbor_unref;
2030+ sd_lldp_neighbor_get_source_address;
2031+ sd_lldp_neighbor_get_destination_address;
2032+ sd_lldp_neighbor_get_timestamp;
2033+ sd_lldp_neighbor_get_raw;
2034+ sd_lldp_neighbor_get_chassis_id;
2035+ sd_lldp_neighbor_get_chassis_id_as_string;
2036+ sd_lldp_neighbor_get_port_id;
2037+ sd_lldp_neighbor_get_port_id_as_string;
2038+ sd_lldp_neighbor_get_ttl;
2039+ sd_lldp_neighbor_get_system_name;
2040+ sd_lldp_neighbor_get_system_description;
2041+ sd_lldp_neighbor_get_port_description;
2042+ sd_lldp_neighbor_get_mud_url;
2043+ sd_lldp_neighbor_get_system_capabilities;
2044+ sd_lldp_neighbor_get_enabled_capabilities;
2045+ sd_lldp_neighbor_tlv_rewind;
2046+ sd_lldp_neighbor_tlv_next;
2047+ sd_lldp_neighbor_tlv_get_type;
2048+ sd_lldp_neighbor_tlv_is_type;
2049+ sd_lldp_neighbor_tlv_get_oui;
2050+ sd_lldp_neighbor_tlv_is_oui;
2051+ sd_lldp_neighbor_tlv_get_raw;
2052+
2053 } LIBSYSTEMD_241;
2054diff --git a/src/libsystemd/meson.build b/src/libsystemd/meson.build
2055index 77fe6e780f..e60853959f 100644
2056--- a/src/libsystemd/meson.build
2057+++ b/src/libsystemd/meson.build
2058@@ -70,6 +70,12 @@ libsystemd_sources = files('''
2059 sd-hwdb/hwdb-util.c
2060 sd-hwdb/hwdb-util.h
2061 sd-hwdb/sd-hwdb.c
2062+ sd-lldp/sd-lldp.c
2063+ sd-lldp/lldp-internal.h
2064+ sd-lldp/lldp-neighbor.c
2065+ sd-lldp/lldp-neighbor.h
2066+ sd-lldp/lldp-network.c
2067+ sd-lldp/lldp-network.h
2068 sd-netlink/generic-netlink.c
2069 sd-netlink/netlink-internal.h
2070 sd-netlink/netlink-message.c
2071diff --git a/src/libsystemd/sd-lldp/lldp-internal.h b/src/libsystemd/sd-lldp/lldp-internal.h
2072new file mode 100644
2073index 0000000000..9598438dba
2074--- /dev/null
2075+++ b/src/libsystemd/sd-lldp/lldp-internal.h
2076@@ -0,0 +1,39 @@
2077+/* SPDX-License-Identifier: LGPL-2.1+ */
2078+#pragma once
2079+
2080+#include "sd-event.h"
2081+#include "sd-lldp.h"
2082+
2083+#include "hashmap.h"
2084+#include "log.h"
2085+#include "prioq.h"
2086+
2087+struct sd_lldp {
2088+ unsigned n_ref;
2089+
2090+ int ifindex;
2091+ int fd;
2092+
2093+ sd_event *event;
2094+ int64_t event_priority;
2095+ sd_event_source *io_event_source;
2096+ sd_event_source *timer_event_source;
2097+
2098+ Prioq *neighbor_by_expiry;
2099+ Hashmap *neighbor_by_id;
2100+
2101+ uint64_t neighbors_max;
2102+
2103+ sd_lldp_callback_t callback;
2104+ void *userdata;
2105+
2106+ uint16_t capability_mask;
2107+
2108+ struct ether_addr filter_address;
2109+};
2110+
2111+#define log_lldp_errno(error, fmt, ...) log_internal(LOG_DEBUG, error, PROJECT_FILE, __LINE__, __func__, "LLDP: " fmt, ##__VA_ARGS__)
2112+#define log_lldp(fmt, ...) log_lldp_errno(0, fmt, ##__VA_ARGS__)
2113+
2114+const char* lldp_event_to_string(sd_lldp_event e) _const_;
2115+sd_lldp_event lldp_event_from_string(const char *s) _pure_;
2116diff --git a/src/libsystemd/sd-lldp/lldp-neighbor.c b/src/libsystemd/sd-lldp/lldp-neighbor.c
2117new file mode 100644
2118index 0000000000..9bae4a3c6e
2119--- /dev/null
2120+++ b/src/libsystemd/sd-lldp/lldp-neighbor.c
2121@@ -0,0 +1,769 @@
2122+/* SPDX-License-Identifier: LGPL-2.1+ */
2123+
2124+#include "alloc-util.h"
2125+#include "escape.h"
2126+#include "ether-addr-util.h"
2127+#include "hexdecoct.h"
2128+#include "in-addr-util.h"
2129+#include "lldp-internal.h"
2130+#include "lldp-neighbor.h"
2131+#include "memory-util.h"
2132+#include "missing.h"
2133+#include "unaligned.h"
2134+
2135+static void lldp_neighbor_id_hash_func(const LLDPNeighborID *id, struct siphash *state) {
2136+ siphash24_compress(id->chassis_id, id->chassis_id_size, state);
2137+ siphash24_compress(&id->chassis_id_size, sizeof(id->chassis_id_size), state);
2138+ siphash24_compress(id->port_id, id->port_id_size, state);
2139+ siphash24_compress(&id->port_id_size, sizeof(id->port_id_size), state);
2140+}
2141+
2142+int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y) {
2143+ return memcmp_nn(x->chassis_id, x->chassis_id_size, y->chassis_id, y->chassis_id_size)
2144+ ?: memcmp_nn(x->port_id, x->port_id_size, y->port_id, y->port_id_size);
2145+}
2146+
2147+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(lldp_neighbor_hash_ops, LLDPNeighborID, lldp_neighbor_id_hash_func, lldp_neighbor_id_compare_func,
2148+ sd_lldp_neighbor, lldp_neighbor_unlink);
2149+
2150+int lldp_neighbor_prioq_compare_func(const void *a, const void *b) {
2151+ const sd_lldp_neighbor *x = a, *y = b;
2152+
2153+ return CMP(x->until, y->until);
2154+}
2155+
2156+_public_ sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n) {
2157+ if (!n)
2158+ return NULL;
2159+
2160+ assert(n->n_ref > 0 || n->lldp);
2161+ n->n_ref++;
2162+
2163+ return n;
2164+}
2165+
2166+static void lldp_neighbor_free(sd_lldp_neighbor *n) {
2167+ assert(n);
2168+
2169+ free(n->id.port_id);
2170+ free(n->id.chassis_id);
2171+ free(n->port_description);
2172+ free(n->system_name);
2173+ free(n->system_description);
2174+ free(n->chassis_id_as_string);
2175+ free(n->port_id_as_string);
2176+ free(n);
2177+}
2178+
2179+_public_ sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n) {
2180+
2181+ /* Drops one reference from the neighbor. Note that the object is not freed unless it is already unlinked from
2182+ * the sd_lldp object. */
2183+
2184+ if (!n)
2185+ return NULL;
2186+
2187+ assert(n->n_ref > 0);
2188+ n->n_ref--;
2189+
2190+ if (n->n_ref <= 0 && !n->lldp)
2191+ lldp_neighbor_free(n);
2192+
2193+ return NULL;
2194+}
2195+
2196+sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n) {
2197+
2198+ /* Removes the neighbor object from the LLDP object, and frees it if it also has no other reference. */
2199+
2200+ if (!n)
2201+ return NULL;
2202+
2203+ if (!n->lldp)
2204+ return NULL;
2205+
2206+ /* Only remove the neighbor object from the hash table if it's in there, don't complain if it isn't. This is
2207+ * because we are used as destructor call for hashmap_clear() and thus sometimes are called to de-register
2208+ * ourselves from the hashtable and sometimes are called after we already are de-registered. */
2209+
2210+ (void) hashmap_remove_value(n->lldp->neighbor_by_id, &n->id, n);
2211+
2212+ assert_se(prioq_remove(n->lldp->neighbor_by_expiry, n, &n->prioq_idx) >= 0);
2213+
2214+ n->lldp = NULL;
2215+
2216+ if (n->n_ref <= 0)
2217+ lldp_neighbor_free(n);
2218+
2219+ return NULL;
2220+}
2221+
2222+sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size) {
2223+ sd_lldp_neighbor *n;
2224+
2225+ n = malloc0(ALIGN(sizeof(sd_lldp_neighbor)) + raw_size);
2226+ if (!n)
2227+ return NULL;
2228+
2229+ n->raw_size = raw_size;
2230+ n->n_ref = 1;
2231+
2232+ return n;
2233+}
2234+
2235+static int parse_string(char **s, const void *q, size_t n) {
2236+ const char *p = q;
2237+ char *k;
2238+
2239+ assert(s);
2240+ assert(p || n == 0);
2241+
2242+ if (*s) {
2243+ log_lldp("Found duplicate string, ignoring field.");
2244+ return 0;
2245+ }
2246+
2247+ /* Strip trailing NULs, just to be nice */
2248+ while (n > 0 && p[n-1] == 0)
2249+ n--;
2250+
2251+ if (n <= 0) /* Ignore empty strings */
2252+ return 0;
2253+
2254+ /* Look for inner NULs */
2255+ if (memchr(p, 0, n)) {
2256+ log_lldp("Found inner NUL in string, ignoring field.");
2257+ return 0;
2258+ }
2259+
2260+ /* Let's escape weird chars, for security reasons */
2261+ k = cescape_length(p, n);
2262+ if (!k)
2263+ return -ENOMEM;
2264+
2265+ free(*s);
2266+ *s = k;
2267+
2268+ return 1;
2269+}
2270+
2271+int lldp_neighbor_parse(sd_lldp_neighbor *n) {
2272+ struct ether_header h;
2273+ const uint8_t *p;
2274+ size_t left;
2275+ int r;
2276+
2277+ assert(n);
2278+
2279+ if (n->raw_size < sizeof(struct ether_header)) {
2280+ log_lldp("Received truncated packet, ignoring.");
2281+ return -EBADMSG;
2282+ }
2283+
2284+ memcpy(&h, LLDP_NEIGHBOR_RAW(n), sizeof(h));
2285+
2286+ if (h.ether_type != htobe16(ETHERTYPE_LLDP)) {
2287+ log_lldp("Received packet with wrong type, ignoring.");
2288+ return -EBADMSG;
2289+ }
2290+
2291+ if (h.ether_dhost[0] != 0x01 ||
2292+ h.ether_dhost[1] != 0x80 ||
2293+ h.ether_dhost[2] != 0xc2 ||
2294+ h.ether_dhost[3] != 0x00 ||
2295+ h.ether_dhost[4] != 0x00 ||
2296+ !IN_SET(h.ether_dhost[5], 0x00, 0x03, 0x0e)) {
2297+ log_lldp("Received packet with wrong destination address, ignoring.");
2298+ return -EBADMSG;
2299+ }
2300+
2301+ memcpy(&n->source_address, h.ether_shost, sizeof(struct ether_addr));
2302+ memcpy(&n->destination_address, h.ether_dhost, sizeof(struct ether_addr));
2303+
2304+ p = (const uint8_t*) LLDP_NEIGHBOR_RAW(n) + sizeof(struct ether_header);
2305+ left = n->raw_size - sizeof(struct ether_header);
2306+
2307+ for (;;) {
2308+ uint8_t type;
2309+ uint16_t length;
2310+
2311+ if (left < 2) {
2312+ log_lldp("TLV lacks header, ignoring.");
2313+ return -EBADMSG;
2314+ }
2315+
2316+ type = p[0] >> 1;
2317+ length = p[1] + (((uint16_t) (p[0] & 1)) << 8);
2318+ p += 2, left -= 2;
2319+
2320+ if (left < length) {
2321+ log_lldp("TLV truncated, ignoring datagram.");
2322+ return -EBADMSG;
2323+ }
2324+
2325+ switch (type) {
2326+
2327+ case SD_LLDP_TYPE_END:
2328+ if (length != 0) {
2329+ log_lldp("End marker TLV not zero-sized, ignoring datagram.");
2330+ return -EBADMSG;
2331+ }
2332+
2333+ /* Note that after processing the SD_LLDP_TYPE_END left could still be > 0
2334+ * as the message may contain padding (see IEEE 802.1AB-2016, sec. 8.5.12) */
2335+
2336+ goto end_marker;
2337+
2338+ case SD_LLDP_TYPE_CHASSIS_ID:
2339+ if (length < 2 || length > 256) { /* includes the chassis subtype, hence one extra byte */
2340+ log_lldp("Chassis ID field size out of range, ignoring datagram.");
2341+ return -EBADMSG;
2342+ }
2343+ if (n->id.chassis_id) {
2344+ log_lldp("Duplicate chassis ID field, ignoring datagram.");
2345+ return -EBADMSG;
2346+ }
2347+
2348+ n->id.chassis_id = memdup(p, length);
2349+ if (!n->id.chassis_id)
2350+ return -ENOMEM;
2351+
2352+ n->id.chassis_id_size = length;
2353+ break;
2354+
2355+ case SD_LLDP_TYPE_PORT_ID:
2356+ if (length < 2 || length > 256) { /* includes the port subtype, hence one extra byte */
2357+ log_lldp("Port ID field size out of range, ignoring datagram.");
2358+ return -EBADMSG;
2359+ }
2360+ if (n->id.port_id) {
2361+ log_lldp("Duplicate port ID field, ignoring datagram.");
2362+ return -EBADMSG;
2363+ }
2364+
2365+ n->id.port_id = memdup(p, length);
2366+ if (!n->id.port_id)
2367+ return -ENOMEM;
2368+
2369+ n->id.port_id_size = length;
2370+ break;
2371+
2372+ case SD_LLDP_TYPE_TTL:
2373+ if (length != 2) {
2374+ log_lldp("TTL field has wrong size, ignoring datagram.");
2375+ return -EBADMSG;
2376+ }
2377+
2378+ if (n->has_ttl) {
2379+ log_lldp("Duplicate TTL field, ignoring datagram.");
2380+ return -EBADMSG;
2381+ }
2382+
2383+ n->ttl = unaligned_read_be16(p);
2384+ n->has_ttl = true;
2385+ break;
2386+
2387+ case SD_LLDP_TYPE_PORT_DESCRIPTION:
2388+ r = parse_string(&n->port_description, p, length);
2389+ if (r < 0)
2390+ return r;
2391+ break;
2392+
2393+ case SD_LLDP_TYPE_SYSTEM_NAME:
2394+ r = parse_string(&n->system_name, p, length);
2395+ if (r < 0)
2396+ return r;
2397+ break;
2398+
2399+ case SD_LLDP_TYPE_SYSTEM_DESCRIPTION:
2400+ r = parse_string(&n->system_description, p, length);
2401+ if (r < 0)
2402+ return r;
2403+ break;
2404+
2405+ case SD_LLDP_TYPE_SYSTEM_CAPABILITIES:
2406+ if (length != 4)
2407+ log_lldp("System capabilities field has wrong size, ignoring.");
2408+ else {
2409+ n->system_capabilities = unaligned_read_be16(p);
2410+ n->enabled_capabilities = unaligned_read_be16(p + 2);
2411+ n->has_capabilities = true;
2412+ }
2413+
2414+ break;
2415+
2416+ case SD_LLDP_TYPE_PRIVATE:
2417+ if (length < 4)
2418+ log_lldp("Found private TLV that is too short, ignoring.");
2419+
2420+ break;
2421+ }
2422+
2423+ p += length, left -= length;
2424+ }
2425+
2426+end_marker:
2427+ if (!n->id.chassis_id || !n->id.port_id || !n->has_ttl) {
2428+ log_lldp("One or more mandatory TLV missing in datagram. Ignoring.");
2429+ return -EBADMSG;
2430+
2431+ }
2432+
2433+ n->rindex = sizeof(struct ether_header);
2434+
2435+ return 0;
2436+}
2437+
2438+void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) {
2439+ assert(n);
2440+
2441+ if (n->ttl > 0) {
2442+ usec_t base;
2443+
2444+ /* Use the packet's timestamp if there is one known */
2445+ base = triple_timestamp_by_clock(&n->timestamp, clock_boottime_or_monotonic());
2446+ if (base <= 0 || base == USEC_INFINITY)
2447+ base = now(clock_boottime_or_monotonic()); /* Otherwise, take the current time */
2448+
2449+ n->until = usec_add(base, n->ttl * USEC_PER_SEC);
2450+ } else
2451+ n->until = 0;
2452+
2453+ if (n->lldp)
2454+ prioq_reshuffle(n->lldp->neighbor_by_expiry, n, &n->prioq_idx);
2455+}
2456+
2457+bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b) {
2458+ if (a == b)
2459+ return true;
2460+
2461+ if (!a || !b)
2462+ return false;
2463+
2464+ if (a->raw_size != b->raw_size)
2465+ return false;
2466+
2467+ return memcmp(LLDP_NEIGHBOR_RAW(a), LLDP_NEIGHBOR_RAW(b), a->raw_size) == 0;
2468+}
2469+
2470+_public_ int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address) {
2471+ assert_return(n, -EINVAL);
2472+ assert_return(address, -EINVAL);
2473+
2474+ *address = n->source_address;
2475+ return 0;
2476+}
2477+
2478+_public_ int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address) {
2479+ assert_return(n, -EINVAL);
2480+ assert_return(address, -EINVAL);
2481+
2482+ *address = n->destination_address;
2483+ return 0;
2484+}
2485+
2486+_public_ int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
2487+ assert_return(n, -EINVAL);
2488+ assert_return(ret, -EINVAL);
2489+ assert_return(size, -EINVAL);
2490+
2491+ *ret = LLDP_NEIGHBOR_RAW(n);
2492+ *size = n->raw_size;
2493+
2494+ return 0;
2495+}
2496+
2497+_public_ int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
2498+ assert_return(n, -EINVAL);
2499+ assert_return(type, -EINVAL);
2500+ assert_return(ret, -EINVAL);
2501+ assert_return(size, -EINVAL);
2502+
2503+ assert(n->id.chassis_id_size > 0);
2504+
2505+ *type = *(uint8_t*) n->id.chassis_id;
2506+ *ret = (uint8_t*) n->id.chassis_id + 1;
2507+ *size = n->id.chassis_id_size - 1;
2508+
2509+ return 0;
2510+}
2511+
2512+static int format_mac_address(const void *data, size_t sz, char **ret) {
2513+ struct ether_addr a;
2514+ char *k;
2515+
2516+ assert(data || sz <= 0);
2517+
2518+ if (sz != 7)
2519+ return 0;
2520+
2521+ memcpy(&a, (uint8_t*) data + 1, sizeof(a));
2522+
2523+ k = new(char, ETHER_ADDR_TO_STRING_MAX);
2524+ if (!k)
2525+ return -ENOMEM;
2526+
2527+ *ret = ether_addr_to_string(&a, k);
2528+ return 1;
2529+}
2530+
2531+static int format_network_address(const void *data, size_t sz, char **ret) {
2532+ union in_addr_union a;
2533+ int family, r;
2534+
2535+ if (sz == 6 && ((uint8_t*) data)[1] == 1) {
2536+ memcpy(&a.in, (uint8_t*) data + 2, sizeof(a.in));
2537+ family = AF_INET;
2538+ } else if (sz == 18 && ((uint8_t*) data)[1] == 2) {
2539+ memcpy(&a.in6, (uint8_t*) data + 2, sizeof(a.in6));
2540+ family = AF_INET6;
2541+ } else
2542+ return 0;
2543+
2544+ r = in_addr_to_string(family, &a, ret);
2545+ if (r < 0)
2546+ return r;
2547+ return 1;
2548+}
2549+
2550+_public_ int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret) {
2551+ char *k;
2552+ int r;
2553+
2554+ assert_return(n, -EINVAL);
2555+ assert_return(ret, -EINVAL);
2556+
2557+ if (n->chassis_id_as_string) {
2558+ *ret = n->chassis_id_as_string;
2559+ return 0;
2560+ }
2561+
2562+ assert(n->id.chassis_id_size > 0);
2563+
2564+ switch (*(uint8_t*) n->id.chassis_id) {
2565+
2566+ case SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT:
2567+ case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS:
2568+ case SD_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT:
2569+ case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME:
2570+ case SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED:
2571+ k = cescape_length((char*) n->id.chassis_id + 1, n->id.chassis_id_size - 1);
2572+ if (!k)
2573+ return -ENOMEM;
2574+
2575+ goto done;
2576+
2577+ case SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
2578+ r = format_mac_address(n->id.chassis_id, n->id.chassis_id_size, &k);
2579+ if (r < 0)
2580+ return r;
2581+ if (r > 0)
2582+ goto done;
2583+
2584+ break;
2585+
2586+ case SD_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS:
2587+ r = format_network_address(n->id.chassis_id, n->id.chassis_id_size, &k);
2588+ if (r < 0)
2589+ return r;
2590+ if (r > 0)
2591+ goto done;
2592+
2593+ break;
2594+ }
2595+
2596+ /* Generic fallback */
2597+ k = hexmem(n->id.chassis_id, n->id.chassis_id_size);
2598+ if (!k)
2599+ return -ENOMEM;
2600+
2601+done:
2602+ *ret = n->chassis_id_as_string = k;
2603+ return 0;
2604+}
2605+
2606+_public_ int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
2607+ assert_return(n, -EINVAL);
2608+ assert_return(type, -EINVAL);
2609+ assert_return(ret, -EINVAL);
2610+ assert_return(size, -EINVAL);
2611+
2612+ assert(n->id.port_id_size > 0);
2613+
2614+ *type = *(uint8_t*) n->id.port_id;
2615+ *ret = (uint8_t*) n->id.port_id + 1;
2616+ *size = n->id.port_id_size - 1;
2617+
2618+ return 0;
2619+}
2620+
2621+_public_ int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret) {
2622+ char *k;
2623+ int r;
2624+
2625+ assert_return(n, -EINVAL);
2626+ assert_return(ret, -EINVAL);
2627+
2628+ if (n->port_id_as_string) {
2629+ *ret = n->port_id_as_string;
2630+ return 0;
2631+ }
2632+
2633+ assert(n->id.port_id_size > 0);
2634+
2635+ switch (*(uint8_t*) n->id.port_id) {
2636+
2637+ case SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
2638+ case SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT:
2639+ case SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME:
2640+ case SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
2641+ k = cescape_length((char*) n->id.port_id + 1, n->id.port_id_size - 1);
2642+ if (!k)
2643+ return -ENOMEM;
2644+
2645+ goto done;
2646+
2647+ case SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS:
2648+ r = format_mac_address(n->id.port_id, n->id.port_id_size, &k);
2649+ if (r < 0)
2650+ return r;
2651+ if (r > 0)
2652+ goto done;
2653+
2654+ break;
2655+
2656+ case SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS:
2657+ r = format_network_address(n->id.port_id, n->id.port_id_size, &k);
2658+ if (r < 0)
2659+ return r;
2660+ if (r > 0)
2661+ goto done;
2662+
2663+ break;
2664+ }
2665+
2666+ /* Generic fallback */
2667+ k = hexmem(n->id.port_id, n->id.port_id_size);
2668+ if (!k)
2669+ return -ENOMEM;
2670+
2671+done:
2672+ *ret = n->port_id_as_string = k;
2673+ return 0;
2674+}
2675+
2676+_public_ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec) {
2677+ assert_return(n, -EINVAL);
2678+ assert_return(ret_sec, -EINVAL);
2679+
2680+ *ret_sec = n->ttl;
2681+ return 0;
2682+}
2683+
2684+_public_ int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret) {
2685+ assert_return(n, -EINVAL);
2686+ assert_return(ret, -EINVAL);
2687+
2688+ if (!n->system_name)
2689+ return -ENODATA;
2690+
2691+ *ret = n->system_name;
2692+ return 0;
2693+}
2694+
2695+_public_ int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret) {
2696+ assert_return(n, -EINVAL);
2697+ assert_return(ret, -EINVAL);
2698+
2699+ if (!n->system_description)
2700+ return -ENODATA;
2701+
2702+ *ret = n->system_description;
2703+ return 0;
2704+}
2705+
2706+_public_ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret) {
2707+ assert_return(n, -EINVAL);
2708+ assert_return(ret, -EINVAL);
2709+
2710+ if (!n->port_description)
2711+ return -ENODATA;
2712+
2713+ *ret = n->port_description;
2714+ return 0;
2715+}
2716+
2717+_public_ int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
2718+ assert_return(n, -EINVAL);
2719+ assert_return(ret, -EINVAL);
2720+
2721+ if (!n->has_capabilities)
2722+ return -ENODATA;
2723+
2724+ *ret = n->system_capabilities;
2725+ return 0;
2726+}
2727+
2728+_public_ int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
2729+ assert_return(n, -EINVAL);
2730+ assert_return(ret, -EINVAL);
2731+
2732+ if (!n->has_capabilities)
2733+ return -ENODATA;
2734+
2735+ *ret = n->enabled_capabilities;
2736+ return 0;
2737+}
2738+
2739+_public_ int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) {
2740+ _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
2741+ int r;
2742+
2743+ assert_return(ret, -EINVAL);
2744+ assert_return(raw || raw_size <= 0, -EINVAL);
2745+
2746+ n = lldp_neighbor_new(raw_size);
2747+ if (!n)
2748+ return -ENOMEM;
2749+
2750+ memcpy(LLDP_NEIGHBOR_RAW(n), raw, raw_size);
2751+ r = lldp_neighbor_parse(n);
2752+ if (r < 0)
2753+ return r;
2754+
2755+ *ret = TAKE_PTR(n);
2756+
2757+ return r;
2758+}
2759+
2760+_public_ int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) {
2761+ assert_return(n, -EINVAL);
2762+
2763+ assert(n->raw_size >= sizeof(struct ether_header));
2764+ n->rindex = sizeof(struct ether_header);
2765+
2766+ return n->rindex < n->raw_size;
2767+}
2768+
2769+_public_ int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) {
2770+ size_t length;
2771+
2772+ assert_return(n, -EINVAL);
2773+
2774+ if (n->rindex == n->raw_size) /* EOF */
2775+ return -ESPIPE;
2776+
2777+ if (n->rindex + 2 > n->raw_size) /* Truncated message */
2778+ return -EBADMSG;
2779+
2780+ length = LLDP_NEIGHBOR_TLV_LENGTH(n);
2781+ if (n->rindex + 2 + length > n->raw_size)
2782+ return -EBADMSG;
2783+
2784+ n->rindex += 2 + length;
2785+ return n->rindex < n->raw_size;
2786+}
2787+
2788+_public_ int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) {
2789+ assert_return(n, -EINVAL);
2790+ assert_return(type, -EINVAL);
2791+
2792+ if (n->rindex == n->raw_size) /* EOF */
2793+ return -ESPIPE;
2794+
2795+ if (n->rindex + 2 > n->raw_size)
2796+ return -EBADMSG;
2797+
2798+ *type = LLDP_NEIGHBOR_TLV_TYPE(n);
2799+ return 0;
2800+}
2801+
2802+_public_ int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) {
2803+ uint8_t k;
2804+ int r;
2805+
2806+ assert_return(n, -EINVAL);
2807+
2808+ r = sd_lldp_neighbor_tlv_get_type(n, &k);
2809+ if (r < 0)
2810+ return r;
2811+
2812+ return type == k;
2813+}
2814+
2815+_public_ int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t *subtype) {
2816+ const uint8_t *d;
2817+ size_t length;
2818+ int r;
2819+
2820+ assert_return(n, -EINVAL);
2821+ assert_return(oui, -EINVAL);
2822+ assert_return(subtype, -EINVAL);
2823+
2824+ r = sd_lldp_neighbor_tlv_is_type(n, SD_LLDP_TYPE_PRIVATE);
2825+ if (r < 0)
2826+ return r;
2827+ if (r == 0)
2828+ return -ENXIO;
2829+
2830+ length = LLDP_NEIGHBOR_TLV_LENGTH(n);
2831+ if (length < 4)
2832+ return -EBADMSG;
2833+
2834+ if (n->rindex + 2 + length > n->raw_size)
2835+ return -EBADMSG;
2836+
2837+ d = LLDP_NEIGHBOR_TLV_DATA(n);
2838+ memcpy(oui, d, 3);
2839+ *subtype = d[3];
2840+
2841+ return 0;
2842+}
2843+
2844+_public_ int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t subtype) {
2845+ uint8_t k[3], st;
2846+ int r;
2847+
2848+ r = sd_lldp_neighbor_tlv_get_oui(n, k, &st);
2849+ if (r == -ENXIO)
2850+ return 0;
2851+ if (r < 0)
2852+ return r;
2853+
2854+ return memcmp(k, oui, 3) == 0 && st == subtype;
2855+}
2856+
2857+_public_ int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
2858+ size_t length;
2859+
2860+ assert_return(n, -EINVAL);
2861+ assert_return(ret, -EINVAL);
2862+ assert_return(size, -EINVAL);
2863+
2864+ /* Note that this returns the full TLV, including the TLV header */
2865+
2866+ if (n->rindex + 2 > n->raw_size)
2867+ return -EBADMSG;
2868+
2869+ length = LLDP_NEIGHBOR_TLV_LENGTH(n);
2870+ if (n->rindex + 2 + length > n->raw_size)
2871+ return -EBADMSG;
2872+
2873+ *ret = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex;
2874+ *size = length + 2;
2875+
2876+ return 0;
2877+}
2878+
2879+_public_ int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret) {
2880+ assert_return(n, -EINVAL);
2881+ assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
2882+ assert_return(clock_supported(clock), -EOPNOTSUPP);
2883+ assert_return(ret, -EINVAL);
2884+
2885+ if (!triple_timestamp_is_set(&n->timestamp))
2886+ return -ENODATA;
2887+
2888+ *ret = triple_timestamp_by_clock(&n->timestamp, clock);
2889+ return 0;
2890+}
2891diff --git a/src/libsystemd/sd-lldp/lldp-neighbor.h b/src/libsystemd/sd-lldp/lldp-neighbor.h
2892new file mode 100644
2893index 0000000000..62dbff42ca
2894--- /dev/null
2895+++ b/src/libsystemd/sd-lldp/lldp-neighbor.h
2896@@ -0,0 +1,91 @@
2897+/* SPDX-License-Identifier: LGPL-2.1+ */
2898+#pragma once
2899+
2900+#include <inttypes.h>
2901+#include <stdbool.h>
2902+#include <sys/types.h>
2903+
2904+#include "sd-lldp.h"
2905+
2906+#include "hash-funcs.h"
2907+#include "lldp-internal.h"
2908+#include "time-util.h"
2909+
2910+typedef struct LLDPNeighborID {
2911+ /* The spec calls this an "MSAP identifier" */
2912+ void *chassis_id;
2913+ size_t chassis_id_size;
2914+
2915+ void *port_id;
2916+ size_t port_id_size;
2917+} LLDPNeighborID;
2918+
2919+struct sd_lldp_neighbor {
2920+ /* Neighbor objects stay around as long as they are linked into an "sd_lldp" object or n_ref > 0. */
2921+ sd_lldp *lldp;
2922+ unsigned n_ref;
2923+
2924+ triple_timestamp timestamp;
2925+
2926+ usec_t until;
2927+ unsigned prioq_idx;
2928+
2929+ struct ether_addr source_address;
2930+ struct ether_addr destination_address;
2931+
2932+ LLDPNeighborID id;
2933+
2934+ /* The raw packet size. The data is appended to the object, accessible via LLDP_NEIGHBOR_RAW() */
2935+ size_t raw_size;
2936+
2937+ /* The current read index for the iterative TLV interface */
2938+ size_t rindex;
2939+
2940+ /* And a couple of fields parsed out. */
2941+ bool has_ttl:1;
2942+ bool has_capabilities:1;
2943+ bool has_port_vlan_id:1;
2944+
2945+ uint16_t ttl;
2946+
2947+ uint16_t system_capabilities;
2948+ uint16_t enabled_capabilities;
2949+
2950+ char *port_description;
2951+ char *system_name;
2952+ char *system_description;
2953+
2954+ uint16_t port_vlan_id;
2955+
2956+ char *chassis_id_as_string;
2957+ char *port_id_as_string;
2958+};
2959+
2960+static inline void *LLDP_NEIGHBOR_RAW(const sd_lldp_neighbor *n) {
2961+ return (uint8_t*) n + ALIGN(sizeof(sd_lldp_neighbor));
2962+}
2963+
2964+static inline uint8_t LLDP_NEIGHBOR_TLV_TYPE(const sd_lldp_neighbor *n) {
2965+ return ((uint8_t*) LLDP_NEIGHBOR_RAW(n))[n->rindex] >> 1;
2966+}
2967+
2968+static inline size_t LLDP_NEIGHBOR_TLV_LENGTH(const sd_lldp_neighbor *n) {
2969+ uint8_t *p;
2970+
2971+ p = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex;
2972+ return p[1] + (((size_t) (p[0] & 1)) << 8);
2973+}
2974+
2975+static inline void* LLDP_NEIGHBOR_TLV_DATA(const sd_lldp_neighbor *n) {
2976+ return ((uint8_t*) LLDP_NEIGHBOR_RAW(n)) + n->rindex + 2;
2977+}
2978+
2979+extern const struct hash_ops lldp_neighbor_hash_ops;
2980+int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y);
2981+int lldp_neighbor_prioq_compare_func(const void *a, const void *b);
2982+
2983+sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n);
2984+sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size);
2985+int lldp_neighbor_parse(sd_lldp_neighbor *n);
2986+void lldp_neighbor_start_ttl(sd_lldp_neighbor *n);
2987+bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b);
2988diff --git a/src/libsystemd/sd-lldp/lldp-network.c b/src/libsystemd/sd-lldp/lldp-network.c
2989new file mode 100644
2990index 0000000000..870584c0db
2991--- /dev/null
2992+++ b/src/libsystemd/sd-lldp/lldp-network.c
2993@@ -0,0 +1,78 @@
2994+/* SPDX-License-Identifier: LGPL-2.1+ */
2995+
2996+#include <linux/filter.h>
2997+#include <netinet/if_ether.h>
2998+
2999+#include "fd-util.h"
3000+#include "lldp-network.h"
3001+#include "missing.h"
3002+#include "socket-util.h"
3003+
3004+int lldp_network_bind_raw_socket(int ifindex) {
3005+
3006+ static const struct sock_filter filter[] = {
3007+ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ethhdr, h_dest)), /* A <- 4 bytes of destination MAC */
3008+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0180c200, 1, 0), /* A != 01:80:c2:00 */
3009+ BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */
3010+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_dest) + 4), /* A <- remaining 2 bytes of destination MAC */
3011+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0000, 3, 0), /* A != 00:00 */
3012+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0003, 2, 0), /* A != 00:03 */
3013+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x000e, 1, 0), /* A != 00:0e */
3014+ BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */
3015+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_proto)), /* A <- protocol */
3016+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_LLDP, 1, 0), /* A != ETHERTYPE_LLDP */
3017+ BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */
3018+ BPF_STMT(BPF_RET + BPF_K, (uint32_t) -1), /* accept packet */
3019+ };
3020+
3021+ static const struct sock_fprog fprog = {
3022+ .len = ELEMENTSOF(filter),
3023+ .filter = (struct sock_filter*) filter,
3024+ };
3025+
3026+ struct packet_mreq mreq = {
3027+ .mr_ifindex = ifindex,
3028+ .mr_type = PACKET_MR_MULTICAST,
3029+ .mr_alen = ETH_ALEN,
3030+ .mr_address = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x00 }
3031+ };
3032+
3033+ union sockaddr_union saddrll = {
3034+ .ll.sll_family = AF_PACKET,
3035+ .ll.sll_ifindex = ifindex,
3036+ };
3037+
3038+ _cleanup_close_ int fd = -1;
3039+ int r;
3040+
3041+ assert(ifindex > 0);
3042+
3043+ fd = socket(PF_PACKET, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK,
3044+ htobe16(ETHERTYPE_LLDP));
3045+ if (fd < 0)
3046+ return -errno;
3047+
3048+ r = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
3049+ if (r < 0)
3050+ return -errno;
3051+
3052+ r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
3053+ if (r < 0)
3054+ return -errno;
3055+
3056+ mreq.mr_address[ETH_ALEN - 1] = 0x03;
3057+ r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
3058+ if (r < 0)
3059+ return -errno;
3060+
3061+ mreq.mr_address[ETH_ALEN - 1] = 0x0E;
3062+ r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
3063+ if (r < 0)
3064+ return -errno;
3065+
3066+ r = bind(fd, &saddrll.sa, sizeof(saddrll.ll));
3067+ if (r < 0)
3068+ return -errno;
3069+
3070+ return TAKE_FD(fd);
3071+}
3072diff --git a/src/libsystemd/sd-lldp/lldp-network.h b/src/libsystemd/sd-lldp/lldp-network.h
3073new file mode 100644
3074index 0000000000..e4ed2898a5
3075--- /dev/null
3076+++ b/src/libsystemd/sd-lldp/lldp-network.h
3077@@ -0,0 +1,6 @@
3078+/* SPDX-License-Identifier: LGPL-2.1+ */
3079+#pragma once
3080+
3081+#include "sd-event.h"
3082+
3083+int lldp_network_bind_raw_socket(int ifindex);
3084diff --git a/src/libsystemd/sd-lldp/sd-lldp.c b/src/libsystemd/sd-lldp/sd-lldp.c
3085new file mode 100644
3086index 0000000000..1f28c5731f
3087--- /dev/null
3088+++ b/src/libsystemd/sd-lldp/sd-lldp.c
3089@@ -0,0 +1,498 @@
3090+/* SPDX-License-Identifier: LGPL-2.1+ */
3091+
3092+#include <arpa/inet.h>
3093+#include <linux/sockios.h>
3094+#include <sys/ioctl.h>
3095+
3096+#include "sd-lldp.h"
3097+
3098+#include "alloc-util.h"
3099+#include "ether-addr-util.h"
3100+#include "event-util.h"
3101+#include "fd-util.h"
3102+#include "lldp-internal.h"
3103+#include "lldp-neighbor.h"
3104+#include "lldp-network.h"
3105+#include "memory-util.h"
3106+#include "socket-util.h"
3107+#include "sort-util.h"
3108+#include "string-table.h"
3109+
3110+#define LLDP_DEFAULT_NEIGHBORS_MAX 128U
3111+
3112+static const char * const lldp_event_table[_SD_LLDP_EVENT_MAX] = {
3113+ [SD_LLDP_EVENT_ADDED] = "added",
3114+ [SD_LLDP_EVENT_REMOVED] = "removed",
3115+ [SD_LLDP_EVENT_UPDATED] = "updated",
3116+ [SD_LLDP_EVENT_REFRESHED] = "refreshed",
3117+};
3118+
3119+DEFINE_STRING_TABLE_LOOKUP(lldp_event, sd_lldp_event);
3120+
3121+static void lldp_flush_neighbors(sd_lldp *lldp) {
3122+ assert(lldp);
3123+
3124+ hashmap_clear(lldp->neighbor_by_id);
3125+}
3126+
3127+static void lldp_callback(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n) {
3128+ assert(lldp);
3129+ assert(event >= 0 && event < _SD_LLDP_EVENT_MAX);
3130+
3131+ if (!lldp->callback) {
3132+ log_lldp("Received '%s' event.", lldp_event_to_string(event));
3133+ return;
3134+ }
3135+
3136+ log_lldp("Invoking callback for '%s' event.", lldp_event_to_string(event));
3137+ lldp->callback(lldp, event, n, lldp->userdata);
3138+}
3139+
3140+static int lldp_make_space(sd_lldp *lldp, size_t extra) {
3141+ usec_t t = USEC_INFINITY;
3142+ bool changed = false;
3143+
3144+ assert(lldp);
3145+
3146+ /* Remove all entries that are past their TTL, and more until at least the specified number of extra entries
3147+ * are free. */
3148+
3149+ for (;;) {
3150+ _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
3151+
3152+ n = prioq_peek(lldp->neighbor_by_expiry);
3153+ if (!n)
3154+ break;
3155+
3156+ sd_lldp_neighbor_ref(n);
3157+
3158+ if (hashmap_size(lldp->neighbor_by_id) > LESS_BY(lldp->neighbors_max, extra))
3159+ goto remove_one;
3160+
3161+ if (t == USEC_INFINITY)
3162+ t = now(clock_boottime_or_monotonic());
3163+
3164+ if (n->until > t)
3165+ break;
3166+
3167+ remove_one:
3168+ lldp_neighbor_unlink(n);
3169+ lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, n);
3170+ changed = true;
3171+ }
3172+
3173+ return changed;
3174+}
3175+
3176+static bool lldp_keep_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) {
3177+ assert(lldp);
3178+ assert(n);
3179+
3180+ /* Don't keep data with a zero TTL */
3181+ if (n->ttl <= 0)
3182+ return false;
3183+
3184+ /* Filter out data from the filter address */
3185+ if (!ether_addr_is_null(&lldp->filter_address) &&
3186+ ether_addr_equal(&lldp->filter_address, &n->source_address))
3187+ return false;
3188+
3189+ /* Only add if the neighbor has a capability we are interested in. Note that we also store all neighbors with
3190+ * no caps field set. */
3191+ if (n->has_capabilities &&
3192+ (n->enabled_capabilities & lldp->capability_mask) == 0)
3193+ return false;
3194+
3195+ /* Keep everything else */
3196+ return true;
3197+}
3198+
3199+static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor);
3200+
3201+static int lldp_add_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) {
3202+ _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *old = NULL;
3203+ bool keep;
3204+ int r;
3205+
3206+ assert(lldp);
3207+ assert(n);
3208+ assert(!n->lldp);
3209+
3210+ keep = lldp_keep_neighbor(lldp, n);
3211+
3212+ /* First retrieve the old entry for this MSAP */
3213+ old = hashmap_get(lldp->neighbor_by_id, &n->id);
3214+ if (old) {
3215+ sd_lldp_neighbor_ref(old);
3216+
3217+ if (!keep) {
3218+ lldp_neighbor_unlink(old);
3219+ lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old);
3220+ return 0;
3221+ }
3222+
3223+ if (lldp_neighbor_equal(n, old)) {
3224+ /* Is this equal, then restart the TTL counter, but don't do anything else. */
3225+ old->timestamp = n->timestamp;
3226+ lldp_start_timer(lldp, old);
3227+ lldp_callback(lldp, SD_LLDP_EVENT_REFRESHED, old);
3228+ return 0;
3229+ }
3230+
3231+ /* Data changed, remove the old entry, and add a new one */
3232+ lldp_neighbor_unlink(old);
3233+
3234+ } else if (!keep)
3235+ return 0;
3236+
3237+ /* Then, make room for at least one new neighbor */
3238+ lldp_make_space(lldp, 1);
3239+
3240+ r = hashmap_put(lldp->neighbor_by_id, &n->id, n);
3241+ if (r < 0)
3242+ goto finish;
3243+
3244+ r = prioq_put(lldp->neighbor_by_expiry, n, &n->prioq_idx);
3245+ if (r < 0) {
3246+ assert_se(hashmap_remove(lldp->neighbor_by_id, &n->id) == n);
3247+ goto finish;
3248+ }
3249+
3250+ n->lldp = lldp;
3251+
3252+ lldp_start_timer(lldp, n);
3253+ lldp_callback(lldp, old ? SD_LLDP_EVENT_UPDATED : SD_LLDP_EVENT_ADDED, n);
3254+
3255+ return 1;
3256+
3257+finish:
3258+ if (old)
3259+ lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old);
3260+
3261+ return r;
3262+}
3263+
3264+static int lldp_handle_datagram(sd_lldp *lldp, sd_lldp_neighbor *n) {
3265+ int r;
3266+
3267+ assert(lldp);
3268+ assert(n);
3269+
3270+ r = lldp_neighbor_parse(n);
3271+ if (r == -EBADMSG) /* Ignore bad messages */
3272+ return 0;
3273+ if (r < 0)
3274+ return r;
3275+
3276+ r = lldp_add_neighbor(lldp, n);
3277+ if (r < 0) {
3278+ log_lldp_errno(r, "Failed to add datagram. Ignoring.");
3279+ return 0;
3280+ }
3281+
3282+ log_lldp("Successfully processed LLDP datagram.");
3283+ return 0;
3284+}
3285+
3286+static int lldp_receive_datagram(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
3287+ _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
3288+ ssize_t space, length;
3289+ sd_lldp *lldp = userdata;
3290+ struct timespec ts;
3291+
3292+ assert(fd >= 0);
3293+ assert(lldp);
3294+
3295+ space = next_datagram_size_fd(fd);
3296+ if (space < 0)
3297+ return log_lldp_errno(space, "Failed to determine datagram size to read: %m");
3298+
3299+ n = lldp_neighbor_new(space);
3300+ if (!n)
3301+ return -ENOMEM;
3302+
3303+ length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT);
3304+ if (length < 0) {
3305+ if (IN_SET(errno, EAGAIN, EINTR))
3306+ return 0;
3307+
3308+ return log_lldp_errno(errno, "Failed to read LLDP datagram: %m");
3309+ }
3310+
3311+ if ((size_t) length != n->raw_size) {
3312+ log_lldp("Packet size mismatch.");
3313+ return -EINVAL;
3314+ }
3315+
3316+ /* Try to get the timestamp of this packet if it is known */
3317+ if (ioctl(fd, SIOCGSTAMPNS, &ts) >= 0)
3318+ triple_timestamp_from_realtime(&n->timestamp, timespec_load(&ts));
3319+ else
3320+ triple_timestamp_get(&n->timestamp);
3321+
3322+ return lldp_handle_datagram(lldp, n);
3323+}
3324+
3325+static void lldp_reset(sd_lldp *lldp) {
3326+ assert(lldp);
3327+
3328+ (void) event_source_disable(lldp->timer_event_source);
3329+ lldp->io_event_source = sd_event_source_unref(lldp->io_event_source);
3330+ lldp->fd = safe_close(lldp->fd);
3331+}
3332+
3333+_public_ int sd_lldp_start(sd_lldp *lldp) {
3334+ int r;
3335+
3336+ assert_return(lldp, -EINVAL);
3337+ assert_return(lldp->event, -EINVAL);
3338+ assert_return(lldp->ifindex > 0, -EINVAL);
3339+
3340+ if (lldp->fd >= 0)
3341+ return 0;
3342+
3343+ assert(!lldp->io_event_source);
3344+
3345+ lldp->fd = lldp_network_bind_raw_socket(lldp->ifindex);
3346+ if (lldp->fd < 0)
3347+ return lldp->fd;
3348+
3349+ r = sd_event_add_io(lldp->event, &lldp->io_event_source, lldp->fd, EPOLLIN, lldp_receive_datagram, lldp);
3350+ if (r < 0)
3351+ goto fail;
3352+
3353+ r = sd_event_source_set_priority(lldp->io_event_source, lldp->event_priority);
3354+ if (r < 0)
3355+ goto fail;
3356+
3357+ (void) sd_event_source_set_description(lldp->io_event_source, "lldp-io");
3358+
3359+ log_lldp("Started LLDP client");
3360+ return 1;
3361+
3362+fail:
3363+ lldp_reset(lldp);
3364+ return r;
3365+}
3366+
3367+_public_ int sd_lldp_stop(sd_lldp *lldp) {
3368+ assert_return(lldp, -EINVAL);
3369+
3370+ if (lldp->fd < 0)
3371+ return 0;
3372+
3373+ log_lldp("Stopping LLDP client");
3374+
3375+ lldp_reset(lldp);
3376+ lldp_flush_neighbors(lldp);
3377+
3378+ return 1;
3379+}
3380+
3381+_public_ int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int64_t priority) {
3382+ int r;
3383+
3384+ assert_return(lldp, -EINVAL);
3385+ assert_return(lldp->fd < 0, -EBUSY);
3386+ assert_return(!lldp->event, -EBUSY);
3387+
3388+ if (event)
3389+ lldp->event = sd_event_ref(event);
3390+ else {
3391+ r = sd_event_default(&lldp->event);
3392+ if (r < 0)
3393+ return r;
3394+ }
3395+
3396+ lldp->event_priority = priority;
3397+
3398+ return 0;
3399+}
3400+
3401+_public_ int sd_lldp_detach_event(sd_lldp *lldp) {
3402+
3403+ assert_return(lldp, -EINVAL);
3404+ assert_return(lldp->fd < 0, -EBUSY);
3405+
3406+ lldp->event = sd_event_unref(lldp->event);
3407+ return 0;
3408+}
3409+
3410+_public_ sd_event* sd_lldp_get_event(sd_lldp *lldp) {
3411+ assert_return(lldp, NULL);
3412+
3413+ return lldp->event;
3414+}
3415+
3416+_public_ int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata) {
3417+ assert_return(lldp, -EINVAL);
3418+
3419+ lldp->callback = cb;
3420+ lldp->userdata = userdata;
3421+
3422+ return 0;
3423+}
3424+
3425+_public_ int sd_lldp_set_ifindex(sd_lldp *lldp, int ifindex) {
3426+ assert_return(lldp, -EINVAL);
3427+ assert_return(ifindex > 0, -EINVAL);
3428+ assert_return(lldp->fd < 0, -EBUSY);
3429+
3430+ lldp->ifindex = ifindex;
3431+ return 0;
3432+}
3433+
3434+static sd_lldp* lldp_free(sd_lldp *lldp) {
3435+ assert(lldp);
3436+
3437+ lldp->timer_event_source = sd_event_source_unref(lldp->timer_event_source);
3438+
3439+ lldp_reset(lldp);
3440+ sd_lldp_detach_event(lldp);
3441+ lldp_flush_neighbors(lldp);
3442+
3443+ hashmap_free(lldp->neighbor_by_id);
3444+ prioq_free(lldp->neighbor_by_expiry);
3445+ return mfree(lldp);
3446+}
3447+
3448+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_lldp, sd_lldp, lldp_free);
3449+
3450+_public_ int sd_lldp_new(sd_lldp **ret) {
3451+ _cleanup_(sd_lldp_unrefp) sd_lldp *lldp = NULL;
3452+ int r;
3453+
3454+ assert_return(ret, -EINVAL);
3455+
3456+ lldp = new(sd_lldp, 1);
3457+ if (!lldp)
3458+ return -ENOMEM;
3459+
3460+ *lldp = (sd_lldp) {
3461+ .n_ref = 1,
3462+ .fd = -1,
3463+ .neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX,
3464+ .capability_mask = (uint16_t) -1,
3465+ };
3466+
3467+ lldp->neighbor_by_id = hashmap_new(&lldp_neighbor_hash_ops);
3468+ if (!lldp->neighbor_by_id)
3469+ return -ENOMEM;
3470+
3471+ r = prioq_ensure_allocated(&lldp->neighbor_by_expiry, lldp_neighbor_prioq_compare_func);
3472+ if (r < 0)
3473+ return r;
3474+
3475+ *ret = TAKE_PTR(lldp);
3476+
3477+ return 0;
3478+}
3479+
3480+static int neighbor_compare_func(sd_lldp_neighbor * const *a, sd_lldp_neighbor * const *b) {
3481+ return lldp_neighbor_id_compare_func(&(*a)->id, &(*b)->id);
3482+}
3483+
3484+static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) {
3485+ sd_lldp *lldp = userdata;
3486+ int r;
3487+
3488+ r = lldp_make_space(lldp, 0);
3489+ if (r < 0)
3490+ return log_lldp_errno(r, "Failed to make space: %m");
3491+
3492+ r = lldp_start_timer(lldp, NULL);
3493+ if (r < 0)
3494+ return log_lldp_errno(r, "Failed to restart timer: %m");
3495+
3496+ return 0;
3497+}
3498+
3499+static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor) {
3500+ sd_lldp_neighbor *n;
3501+
3502+ assert(lldp);
3503+
3504+ if (neighbor)
3505+ lldp_neighbor_start_ttl(neighbor);
3506+
3507+ n = prioq_peek(lldp->neighbor_by_expiry);
3508+ if (!n)
3509+ return event_source_disable(lldp->timer_event_source);
3510+
3511+ if (!lldp->event)
3512+ return 0;
3513+
3514+ return event_reset_time(lldp->event, &lldp->timer_event_source,
3515+ clock_boottime_or_monotonic(),
3516+ n->until, 0,
3517+ on_timer_event, lldp,
3518+ lldp->event_priority, "lldp-timer", true);
3519+}
3520+
3521+_public_ int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***ret) {
3522+ sd_lldp_neighbor **l = NULL, *n;
3523+ Iterator i;
3524+ int k = 0, r;
3525+
3526+ assert_return(lldp, -EINVAL);
3527+ assert_return(ret, -EINVAL);
3528+
3529+ if (hashmap_isempty(lldp->neighbor_by_id)) { /* Special shortcut */
3530+ *ret = NULL;
3531+ return 0;
3532+ }
3533+
3534+ l = new0(sd_lldp_neighbor*, hashmap_size(lldp->neighbor_by_id));
3535+ if (!l)
3536+ return -ENOMEM;
3537+
3538+ r = lldp_start_timer(lldp, NULL);
3539+ if (r < 0) {
3540+ free(l);
3541+ return r;
3542+ }
3543+
3544+ HASHMAP_FOREACH(n, lldp->neighbor_by_id, i)
3545+ l[k++] = sd_lldp_neighbor_ref(n);
3546+
3547+ assert((size_t) k == hashmap_size(lldp->neighbor_by_id));
3548+
3549+ /* Return things in a stable order */
3550+ typesafe_qsort(l, k, neighbor_compare_func);
3551+ *ret = l;
3552+
3553+ return k;
3554+}
3555+
3556+_public_ int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t m) {
3557+ assert_return(lldp, -EINVAL);
3558+ assert_return(m <= 0, -EINVAL);
3559+
3560+ lldp->neighbors_max = m;
3561+ lldp_make_space(lldp, 0);
3562+
3563+ return 0;
3564+}
3565+
3566+_public_ int sd_lldp_match_capabilities(sd_lldp *lldp, uint16_t mask) {
3567+ assert_return(lldp, -EINVAL);
3568+ assert_return(mask != 0, -EINVAL);
3569+
3570+ lldp->capability_mask = mask;
3571+
3572+ return 0;
3573+}
3574+
3575+_public_ int sd_lldp_set_filter_address(sd_lldp *lldp, const struct ether_addr *addr) {
3576+ assert_return(lldp, -EINVAL);
3577+
3578+ /* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so
3579+ * that our own can be filtered out here. */
3580+
3581+ if (addr)
3582+ lldp->filter_address = *addr;
3583+ else
3584+ zero(lldp->filter_address);
3585+
3586+ return 0;
3587+}
3588diff --git a/src/libsystemd/sd-lldp/test-lldp.c b/src/libsystemd/sd-lldp/test-lldp.c
3589new file mode 100644
3590index 0000000000..7406f94ce0
3591--- /dev/null
3592+++ b/src/libsystemd/sd-lldp/test-lldp.c
3593@@ -0,0 +1,379 @@
3594+/* SPDX-License-Identifier: LGPL-2.1+ */
3595+
3596+#include <arpa/inet.h>
3597+#include <errno.h>
3598+#include <net/ethernet.h>
3599+#include <stdio.h>
3600+#include <string.h>
3601+#include <unistd.h>
3602+
3603+#include "sd-event.h"
3604+#include "sd-lldp.h"
3605+
3606+#include "alloc-util.h"
3607+#include "fd-util.h"
3608+#include "lldp-network.h"
3609+#include "macro.h"
3610+#include "string-util.h"
3611+#include "tests.h"
3612+
3613+#define TEST_LLDP_PORT "em1"
3614+#define TEST_LLDP_TYPE_SYSTEM_NAME "systemd-lldp"
3615+#define TEST_LLDP_TYPE_SYSTEM_DESC "systemd-lldp-desc"
3616+
3617+static int test_fd[2] = { -1, -1 };
3618+static int lldp_handler_calls;
3619+
3620+int lldp_network_bind_raw_socket(int ifindex) {
3621+ if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0)
3622+ return -errno;
3623+
3624+ return test_fd[0];
3625+}
3626+
3627+static void lldp_handler(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata) {
3628+ lldp_handler_calls++;
3629+}
3630+
3631+static int start_lldp(sd_lldp **lldp, sd_event *e, sd_lldp_callback_t cb, void *cb_data) {
3632+ int r;
3633+
3634+ r = sd_lldp_new(lldp);
3635+ if (r < 0)
3636+ return r;
3637+
3638+ r = sd_lldp_set_ifindex(*lldp, 42);
3639+ if (r < 0)
3640+ return r;
3641+
3642+ r = sd_lldp_set_callback(*lldp, cb, cb_data);
3643+ if (r < 0)
3644+ return r;
3645+
3646+ r = sd_lldp_attach_event(*lldp, e, 0);
3647+ if (r < 0)
3648+ return r;
3649+
3650+ r = sd_lldp_start(*lldp);
3651+ if (r < 0)
3652+ return r;
3653+
3654+ return 0;
3655+}
3656+
3657+static int stop_lldp(sd_lldp *lldp) {
3658+ int r;
3659+
3660+ r = sd_lldp_stop(lldp);
3661+ if (r < 0)
3662+ return r;
3663+
3664+ r = sd_lldp_detach_event(lldp);
3665+ if (r < 0)
3666+ return r;
3667+
3668+ sd_lldp_unref(lldp);
3669+ safe_close(test_fd[1]);
3670+
3671+ return 0;
3672+}
3673+
3674+static void test_receive_basic_packet(sd_event *e) {
3675+
3676+ static const uint8_t frame[] = {
3677+ /* Ethernet header */
3678+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */
3679+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
3680+ 0x88, 0xcc, /* Ethertype */
3681+ /* LLDP mandatory TLVs */
3682+ 0x02, 0x07, 0x04, 0x00, 0x01, 0x02, /* Chassis: MAC, 00:01:02:03:04:05 */
3683+ 0x03, 0x04, 0x05,
3684+ 0x04, 0x04, 0x05, 0x31, 0x2f, 0x33, /* Port: interface name, "1/3" */
3685+ 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */
3686+ /* LLDP optional TLVs */
3687+ 0x08, 0x04, 0x50, 0x6f, 0x72, 0x74, /* Port Description: "Port" */
3688+ 0x0a, 0x03, 0x53, 0x59, 0x53, /* System Name: "SYS" */
3689+ 0x0c, 0x04, 0x66, 0x6f, 0x6f, 0x00, /* System Description: "foo" (NULL-terminated) */
3690+ 0x00, 0x00 /* End Of LLDPDU */
3691+ };
3692+
3693+ sd_lldp *lldp;
3694+ sd_lldp_neighbor **neighbors;
3695+ uint8_t type;
3696+ const void *data;
3697+ uint16_t ttl;
3698+ size_t length;
3699+ const char *str;
3700+
3701+ lldp_handler_calls = 0;
3702+ assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0);
3703+
3704+ assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame));
3705+ sd_event_run(e, 0);
3706+ assert_se(lldp_handler_calls == 1);
3707+ assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 1);
3708+
3709+ assert_se(sd_lldp_neighbor_get_chassis_id(neighbors[0], &type, &data, &length) == 0);
3710+ assert_se(type == SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS);
3711+ assert_se(length == ETH_ALEN);
3712+ assert_se(!memcmp(data, "\x00\x01\x02\x03\x04\x05", ETH_ALEN));
3713+
3714+ assert_se(sd_lldp_neighbor_get_port_id(neighbors[0], &type, &data, &length) == 0);
3715+ assert_se(type == SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME);
3716+ assert_se(length == 3);
3717+ assert_se(!memcmp(data, "1/3", 3));
3718+
3719+ assert_se(sd_lldp_neighbor_get_port_description(neighbors[0], &str) == 0);
3720+ assert_se(streq(str, "Port"));
3721+
3722+ assert_se(sd_lldp_neighbor_get_system_name(neighbors[0], &str) == 0);
3723+ assert_se(streq(str, "SYS"));
3724+
3725+ assert_se(sd_lldp_neighbor_get_system_description(neighbors[0], &str) == 0);
3726+ assert_se(streq(str, "foo"));
3727+
3728+ assert_se(sd_lldp_neighbor_get_ttl(neighbors[0], &ttl) == 0);
3729+ assert_se(ttl == 120);
3730+
3731+ sd_lldp_neighbor_unref(neighbors[0]);
3732+ free(neighbors);
3733+
3734+ assert_se(stop_lldp(lldp) == 0);
3735+}
3736+
3737+static void test_receive_incomplete_packet(sd_event *e) {
3738+ sd_lldp *lldp;
3739+ sd_lldp_neighbor **neighbors;
3740+ uint8_t frame[] = {
3741+ /* Ethernet header */
3742+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */
3743+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
3744+ 0x88, 0xcc, /* Ethertype */
3745+ /* LLDP mandatory TLVs */
3746+ 0x02, 0x07, 0x04, 0x00, 0x01, 0x02, /* Chassis: MAC, 00:01:02:03:04:05 */
3747+ 0x03, 0x04, 0x05,
3748+ 0x04, 0x04, 0x05, 0x31, 0x2f, 0x33, /* Port: interface name, "1/3" */
3749+ /* Missing TTL */
3750+ 0x00, 0x00 /* End Of LLDPDU */
3751+ };
3752+
3753+ lldp_handler_calls = 0;
3754+ assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0);
3755+
3756+ assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame));
3757+ sd_event_run(e, 0);
3758+ assert_se(lldp_handler_calls == 0);
3759+ assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 0);
3760+
3761+ assert_se(stop_lldp(lldp) == 0);
3762+}
3763+
3764+static void test_receive_oui_packet(sd_event *e) {
3765+ sd_lldp *lldp;
3766+ sd_lldp_neighbor **neighbors;
3767+ uint8_t frame[] = {
3768+ /* Ethernet header */
3769+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */
3770+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
3771+ 0x88, 0xcc, /* Ethertype */
3772+ /* LLDP mandatory TLVs */
3773+ 0x02, 0x07, 0x04, 0x00, 0x01, 0x02, /* Chassis: MAC, 00:01:02:03:04:05 */
3774+ 0x03, 0x04, 0x05,
3775+ 0x04, 0x04, 0x05, 0x31, 0x2f, 0x33, /* Port TLV: interface name, "1/3" */
3776+ 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */
3777+ /* LLDP optional TLVs */
3778+ 0xfe, 0x06, 0x00, 0x80, 0xc2, 0x01, /* Port VLAN ID: 0x1234 */
3779+ 0x12, 0x34,
3780+ 0xfe, 0x07, 0x00, 0x80, 0xc2, 0x02, /* Port and protocol: flag 1, PPVID 0x7788 */
3781+ 0x01, 0x77, 0x88,
3782+ 0xfe, 0x0d, 0x00, 0x80, 0xc2, 0x03, /* VLAN Name: ID 0x1234, name "Vlan51" */
3783+ 0x12, 0x34, 0x06, 0x56, 0x6c, 0x61,
3784+ 0x6e, 0x35, 0x31,
3785+ 0xfe, 0x06, 0x00, 0x80, 0xc2, 0x06, /* Management VID: 0x0102 */
3786+ 0x01, 0x02,
3787+ 0xfe, 0x09, 0x00, 0x80, 0xc2, 0x07, /* Link aggregation: status 1, ID 0x00140012 */
3788+ 0x01, 0x00, 0x14, 0x00, 0x12,
3789+ 0xfe, 0x07, 0x00, 0x12, 0x0f, 0x02, /* 802.3 Power via MDI: PSE, MDI enabled */
3790+ 0x07, 0x01, 0x00,
3791+ 0x00, 0x00 /* End of LLDPDU */
3792+ };
3793+
3794+ lldp_handler_calls = 0;
3795+ assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0);
3796+
3797+ assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame));
3798+ sd_event_run(e, 0);
3799+ assert_se(lldp_handler_calls == 1);
3800+ assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 1);
3801+
3802+ assert_se(sd_lldp_neighbor_tlv_rewind(neighbors[0]) >= 0);
3803+ assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_CHASSIS_ID) > 0);
3804+ assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
3805+ assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_PORT_ID) > 0);
3806+ assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
3807+ assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_TTL) > 0);
3808+ assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
3809+ assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID) > 0);
3810+ assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
3811+ assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID) > 0);
3812+ assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
3813+ assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME) > 0);
3814+ assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
3815+ assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_MANAGEMENT_VID) > 0);
3816+ assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
3817+ assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION) > 0);
3818+ assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
3819+ assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_3, SD_LLDP_OUI_802_3_SUBTYPE_POWER_VIA_MDI) > 0);
3820+ assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
3821+ assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_END) > 0);
3822+ assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) == 0);
3823+
3824+ sd_lldp_neighbor_unref(neighbors[0]);
3825+ free(neighbors);
3826+
3827+ assert_se(stop_lldp(lldp) == 0);
3828+}
3829+
3830+static void test_multiple_neighbors_sorted(sd_event *e) {
3831+
3832+ static const uint8_t frame1[] = {
3833+ /* Ethernet header */
3834+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */
3835+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
3836+ 0x88, 0xcc, /* Ethertype */
3837+ /* LLDP mandatory TLVs */
3838+ 0x02, 0x04, 0x01, '1', '/', '2', /* Chassis component: "1/2" */
3839+ 0x04, 0x04, 0x02, '2', '/', '3', /* Port component: "2/3" */
3840+ 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */
3841+ 0x00, 0x00 /* End Of LLDPDU */
3842+ };
3843+ static const uint8_t frame2[] = {
3844+ /* Ethernet header */
3845+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */
3846+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
3847+ 0x88, 0xcc, /* Ethertype */
3848+ /* LLDP mandatory TLVs */
3849+ 0x02, 0x04, 0x01, '2', '/', '1', /* Chassis component: "2/1" */
3850+ 0x04, 0x04, 0x02, '1', '/', '3', /* Port component: "1/3" */
3851+ 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */
3852+ 0x00, 0x00 /* End Of LLDPDU */
3853+ };
3854+ static const uint8_t frame3[] = {
3855+ /* Ethernet header */
3856+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */
3857+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
3858+ 0x88, 0xcc, /* Ethertype */
3859+ /* LLDP mandatory TLVs */
3860+ 0x02, 0x05, 0x01, '2', '/', '1', '0', /* Chassis component: "2/10" */
3861+ 0x04, 0x04, 0x02, '1', '/', '0', /* Port component: "1/0" */
3862+ 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */
3863+ 0x00, 0x00 /* End Of LLDPDU */
3864+ };
3865+ static const uint8_t frame4[] = {
3866+ /* Ethernet header */
3867+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */
3868+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
3869+ 0x88, 0xcc, /* Ethertype */
3870+ /* LLDP mandatory TLVs */
3871+ 0x02, 0x05, 0x01, '2', '/', '1', '9', /* Chassis component: "2/19" */
3872+ 0x04, 0x04, 0x02, '1', '/', '0', /* Port component: "1/0" */
3873+ 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */
3874+ 0x00, 0x00 /* End Of LLDPDU */
3875+ };
3876+ static const uint8_t frame5[] = {
3877+ /* Ethernet header */
3878+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */
3879+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
3880+ 0x88, 0xcc, /* Ethertype */
3881+ /* LLDP mandatory TLVs */
3882+ 0x02, 0x04, 0x01, '1', '/', '2', /* Chassis component: "1/2" */
3883+ 0x04, 0x05, 0x02, '2', '/', '1', '0', /* Port component: "2/10" */
3884+ 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */
3885+ 0x00, 0x00 /* End Of LLDPDU */
3886+ };
3887+ static const uint8_t frame6[] = {
3888+ /* Ethernet header */
3889+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */
3890+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
3891+ 0x88, 0xcc, /* Ethertype */
3892+ /* LLDP mandatory TLVs */
3893+ 0x02, 0x04, 0x01, '1', '/', '2', /* Chassis component: "1/2" */
3894+ 0x04, 0x05, 0x02, '2', '/', '3', '9', /* Port component: "2/10" */
3895+ 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */
3896+ 0x00, 0x00 /* End Of LLDPDU */
3897+ };
3898+ static const char* expected[] = {
3899+ /* ordered pairs of Chassis+Port */
3900+ "1/2", "2/10",
3901+ "1/2", "2/3",
3902+ "1/2", "2/39",
3903+ "2/1", "1/3",
3904+ "2/10", "1/0",
3905+ "2/19", "1/0",
3906+ };
3907+
3908+ sd_lldp *lldp;
3909+ sd_lldp_neighbor **neighbors;
3910+ int i;
3911+ uint8_t type;
3912+ const void *data;
3913+ size_t length, expected_length;
3914+ uint16_t ttl;
3915+
3916+ lldp_handler_calls = 0;
3917+ assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0);
3918+
3919+ assert_se(write(test_fd[1], frame1, sizeof(frame1)) == sizeof(frame1));
3920+ sd_event_run(e, 0);
3921+ assert_se(write(test_fd[1], frame2, sizeof(frame2)) == sizeof(frame2));
3922+ sd_event_run(e, 0);
3923+ assert_se(write(test_fd[1], frame3, sizeof(frame3)) == sizeof(frame3));
3924+ sd_event_run(e, 0);
3925+ assert_se(write(test_fd[1], frame4, sizeof(frame4)) == sizeof(frame4));
3926+ sd_event_run(e, 0);
3927+ assert_se(write(test_fd[1], frame5, sizeof(frame5)) == sizeof(frame5));
3928+ sd_event_run(e, 0);
3929+ assert_se(write(test_fd[1], frame6, sizeof(frame6)) == sizeof(frame6));
3930+ sd_event_run(e, 0);
3931+ assert_se(lldp_handler_calls == 6);
3932+
3933+ assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 6);
3934+
3935+ for (i = 0; i < 6; i++) {
3936+ assert_se(sd_lldp_neighbor_get_chassis_id(neighbors[i], &type, &data, &length) == 0);
3937+ assert_se(type == SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT);
3938+ expected_length = strlen(expected[2 * i]);
3939+ assert_se(length == expected_length);
3940+ assert_se(memcmp(data, expected[2 * i], expected_length) == 0);
3941+
3942+ assert_se(sd_lldp_neighbor_get_port_id(neighbors[i], &type, &data, &length) == 0);
3943+ assert_se(type == SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT);
3944+ expected_length = strlen(expected[2 * i + 1]);
3945+ assert_se(length == expected_length);
3946+ assert_se(memcmp(data, expected[2 * i + 1], expected_length) == 0);
3947+
3948+ assert_se(sd_lldp_neighbor_get_ttl(neighbors[i], &ttl) == 0);
3949+ assert_se(ttl == 120);
3950+ }
3951+
3952+ for (i = 0; i < 6; i++)
3953+ sd_lldp_neighbor_unref(neighbors[i]);
3954+ free(neighbors);
3955+
3956+ assert_se(stop_lldp(lldp) == 0);
3957+}
3958+
3959+int main(int argc, char *argv[]) {
3960+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
3961+
3962+ test_setup_logging(LOG_DEBUG);
3963+
3964+ /* LLDP reception tests */
3965+ assert_se(sd_event_new(&e) == 0);
3966+ test_receive_basic_packet(e);
3967+ test_receive_incomplete_packet(e);
3968+ test_receive_oui_packet(e);
3969+ test_multiple_neighbors_sorted(e);
3970+
3971+ return 0;
3972+}
3973diff --git a/src/systemd/meson.build b/src/systemd/meson.build
3974index 75c48b07a5..887421bee0 100644
3975--- a/src/systemd/meson.build
3976+++ b/src/systemd/meson.build
3977@@ -12,6 +12,7 @@ _systemd_headers = '''
3978 sd-journal.h
3979 sd-login.h
3980 sd-messages.h
3981+ sd-lldp.h
3982 '''.split()
3983
3984 # https://github.com/mesonbuild/meson/issues/1633
3985@@ -25,7 +26,6 @@ _not_installed_headers = '''
3986 sd-dhcp-server.h
3987 sd-ipv4acd.h
3988 sd-ipv4ll.h
3989- sd-lldp.h
3990 sd-ndisc.h
3991 sd-netlink.h
3992 sd-network.h
3993diff --git a/src/test/meson.build b/src/test/meson.build
3994index de31e977bc..680d1dec6b 100644
3995--- a/src/test/meson.build
3996+++ b/src/test/meson.build
3997@@ -1010,6 +1010,11 @@ tests += [
3998 [],
3999 []],
4000
4001+ [['src/libsystemd/sd-lldp/test-lldp.c'],
4002+ [libbasic,libshared_static,libsystemd_static
4003+ ],
4004+ []],
4005+
4006 ]
4007
4008 # test-bus-vtable-cc.cc is a symlink and symlinks get lost in containers on FuzzBuzz.
4009@@ -1098,11 +1103,6 @@ tests += [
4010 [libshared,
4011 libsystemd_network],
4012 []],
4013-
4014- [['src/libsystemd-network/test-lldp.c'],
4015- [libshared,
4016- libsystemd_network],
4017- []],
4018 ]
4019
4020 ############################################################
4021--
40222.28.0
4023