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