| From 9b8b7ba40accbd303a3378d75022d6d92a2b7771 Mon Sep 17 00:00:00 2001 |
| From: Tomas Pecka <peckato1@fit.cvut.cz> |
| Date: Mon, 7 Sep 2020 18:26:47 +0200 |
| Subject: [PATCH] Move sd_lldp to libsystemd |
| |
| Make sd_lldp public in libsystemd. Clients can therefore operate |
| with LLDP structures and files (e.g. parse LLDP neighbours). |
| --- |
| meson.build | 1 + |
| src/fuzz/meson.build | 9 +- |
| src/libsystemd-network/lldp-internal.h | 39 -- |
| src/libsystemd-network/lldp-neighbor.c | 769 ------------------------- |
| src/libsystemd-network/lldp-neighbor.h | 91 --- |
| src/libsystemd-network/lldp-network.c | 78 --- |
| src/libsystemd-network/lldp-network.h | 6 - |
| src/libsystemd-network/meson.build | 6 - |
| src/libsystemd-network/sd-lldp.c | 498 ---------------- |
| src/libsystemd-network/test-lldp.c | 379 ------------ |
| src/libsystemd/libsystemd.sym | 41 ++ |
| src/libsystemd/meson.build | 6 + |
| src/libsystemd/sd-lldp/lldp-internal.h | 39 ++ |
| src/libsystemd/sd-lldp/lldp-neighbor.c | 769 +++++++++++++++++++++++++ |
| src/libsystemd/sd-lldp/lldp-neighbor.h | 91 +++ |
| src/libsystemd/sd-lldp/lldp-network.c | 78 +++ |
| src/libsystemd/sd-lldp/lldp-network.h | 6 + |
| src/libsystemd/sd-lldp/sd-lldp.c | 498 ++++++++++++++++ |
| src/libsystemd/sd-lldp/test-lldp.c | 379 ++++++++++++ |
| src/systemd/meson.build | 2 +- |
| src/test/meson.build | 10 +- |
| 21 files changed, 1919 insertions(+), 1876 deletions(-) |
| delete mode 100644 src/libsystemd-network/lldp-internal.h |
| delete mode 100644 src/libsystemd-network/lldp-neighbor.c |
| delete mode 100644 src/libsystemd-network/lldp-neighbor.h |
| delete mode 100644 src/libsystemd-network/lldp-network.c |
| delete mode 100644 src/libsystemd-network/lldp-network.h |
| delete mode 100644 src/libsystemd-network/sd-lldp.c |
| delete mode 100644 src/libsystemd-network/test-lldp.c |
| create mode 100644 src/libsystemd/sd-lldp/lldp-internal.h |
| create mode 100644 src/libsystemd/sd-lldp/lldp-neighbor.c |
| create mode 100644 src/libsystemd/sd-lldp/lldp-neighbor.h |
| create mode 100644 src/libsystemd/sd-lldp/lldp-network.c |
| create mode 100644 src/libsystemd/sd-lldp/lldp-network.h |
| create mode 100644 src/libsystemd/sd-lldp/sd-lldp.c |
| create mode 100644 src/libsystemd/sd-lldp/test-lldp.c |
| |
| diff --git a/meson.build b/meson.build |
| index 8ccc947e37..1c2099d093 100644 |
| --- a/meson.build |
| +++ b/meson.build |
| @@ -1398,6 +1398,7 @@ includes = include_directories('src/basic', |
| 'src/libsystemd/sd-event', |
| 'src/libsystemd/sd-hwdb', |
| 'src/libsystemd/sd-id128', |
| + 'src/libsystemd/sd-lldp', |
| 'src/libsystemd/sd-netlink', |
| 'src/libsystemd/sd-network', |
| 'src/libsystemd/sd-resolve', |
| diff --git a/src/fuzz/meson.build b/src/fuzz/meson.build |
| index c88812d1de..8171bf58c1 100644 |
| --- a/src/fuzz/meson.build |
| +++ b/src/fuzz/meson.build |
| @@ -33,8 +33,8 @@ fuzzers += [ |
| []], |
| |
| [['src/fuzz/fuzz-lldp.c'], |
| - [libshared, |
| - libsystemd_network], |
| + [libbasic,libshared_static,libsystemd_static |
| + ], |
| []], |
| |
| [['src/fuzz/fuzz-ndisc-rs.c', |
| @@ -43,8 +43,9 @@ fuzzers += [ |
| 'src/libsystemd-network/icmp6-util.h', |
| 'src/systemd/sd-dhcp6-client.h', |
| 'src/systemd/sd-ndisc.h'], |
| - [libshared, |
| - libsystemd_network], |
| + [libsystemd_network, |
| + libshared |
| + ], |
| []], |
| |
| [['src/fuzz/fuzz-json.c'], |
| diff --git a/src/libsystemd-network/lldp-internal.h b/src/libsystemd-network/lldp-internal.h |
| deleted file mode 100644 |
| index 9598438dba..0000000000 |
| --- a/src/libsystemd-network/lldp-internal.h |
| +++ /dev/null |
| @@ -1,39 +0,0 @@ |
| -/* SPDX-License-Identifier: LGPL-2.1+ */ |
| -#pragma once |
| - |
| -#include "sd-event.h" |
| -#include "sd-lldp.h" |
| - |
| -#include "hashmap.h" |
| -#include "log.h" |
| -#include "prioq.h" |
| - |
| -struct sd_lldp { |
| - unsigned n_ref; |
| - |
| - int ifindex; |
| - int fd; |
| - |
| - sd_event *event; |
| - int64_t event_priority; |
| - sd_event_source *io_event_source; |
| - sd_event_source *timer_event_source; |
| - |
| - Prioq *neighbor_by_expiry; |
| - Hashmap *neighbor_by_id; |
| - |
| - uint64_t neighbors_max; |
| - |
| - sd_lldp_callback_t callback; |
| - void *userdata; |
| - |
| - uint16_t capability_mask; |
| - |
| - struct ether_addr filter_address; |
| -}; |
| - |
| -#define log_lldp_errno(error, fmt, ...) log_internal(LOG_DEBUG, error, PROJECT_FILE, __LINE__, __func__, "LLDP: " fmt, ##__VA_ARGS__) |
| -#define log_lldp(fmt, ...) log_lldp_errno(0, fmt, ##__VA_ARGS__) |
| - |
| -const char* lldp_event_to_string(sd_lldp_event e) _const_; |
| -sd_lldp_event lldp_event_from_string(const char *s) _pure_; |
| diff --git a/src/libsystemd-network/lldp-neighbor.c b/src/libsystemd-network/lldp-neighbor.c |
| deleted file mode 100644 |
| index 9bae4a3c6e..0000000000 |
| --- a/src/libsystemd-network/lldp-neighbor.c |
| +++ /dev/null |
| @@ -1,769 +0,0 @@ |
| -/* SPDX-License-Identifier: LGPL-2.1+ */ |
| - |
| -#include "alloc-util.h" |
| -#include "escape.h" |
| -#include "ether-addr-util.h" |
| -#include "hexdecoct.h" |
| -#include "in-addr-util.h" |
| -#include "lldp-internal.h" |
| -#include "lldp-neighbor.h" |
| -#include "memory-util.h" |
| -#include "missing.h" |
| -#include "unaligned.h" |
| - |
| -static void lldp_neighbor_id_hash_func(const LLDPNeighborID *id, struct siphash *state) { |
| - siphash24_compress(id->chassis_id, id->chassis_id_size, state); |
| - siphash24_compress(&id->chassis_id_size, sizeof(id->chassis_id_size), state); |
| - siphash24_compress(id->port_id, id->port_id_size, state); |
| - siphash24_compress(&id->port_id_size, sizeof(id->port_id_size), state); |
| -} |
| - |
| -int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y) { |
| - return memcmp_nn(x->chassis_id, x->chassis_id_size, y->chassis_id, y->chassis_id_size) |
| - ?: memcmp_nn(x->port_id, x->port_id_size, y->port_id, y->port_id_size); |
| -} |
| - |
| -DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(lldp_neighbor_hash_ops, LLDPNeighborID, lldp_neighbor_id_hash_func, lldp_neighbor_id_compare_func, |
| - sd_lldp_neighbor, lldp_neighbor_unlink); |
| - |
| -int lldp_neighbor_prioq_compare_func(const void *a, const void *b) { |
| - const sd_lldp_neighbor *x = a, *y = b; |
| - |
| - return CMP(x->until, y->until); |
| -} |
| - |
| -_public_ sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n) { |
| - if (!n) |
| - return NULL; |
| - |
| - assert(n->n_ref > 0 || n->lldp); |
| - n->n_ref++; |
| - |
| - return n; |
| -} |
| - |
| -static void lldp_neighbor_free(sd_lldp_neighbor *n) { |
| - assert(n); |
| - |
| - free(n->id.port_id); |
| - free(n->id.chassis_id); |
| - free(n->port_description); |
| - free(n->system_name); |
| - free(n->system_description); |
| - free(n->chassis_id_as_string); |
| - free(n->port_id_as_string); |
| - free(n); |
| -} |
| - |
| -_public_ sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n) { |
| - |
| - /* Drops one reference from the neighbor. Note that the object is not freed unless it is already unlinked from |
| - * the sd_lldp object. */ |
| - |
| - if (!n) |
| - return NULL; |
| - |
| - assert(n->n_ref > 0); |
| - n->n_ref--; |
| - |
| - if (n->n_ref <= 0 && !n->lldp) |
| - lldp_neighbor_free(n); |
| - |
| - return NULL; |
| -} |
| - |
| -sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n) { |
| - |
| - /* Removes the neighbor object from the LLDP object, and frees it if it also has no other reference. */ |
| - |
| - if (!n) |
| - return NULL; |
| - |
| - if (!n->lldp) |
| - return NULL; |
| - |
| - /* Only remove the neighbor object from the hash table if it's in there, don't complain if it isn't. This is |
| - * because we are used as destructor call for hashmap_clear() and thus sometimes are called to de-register |
| - * ourselves from the hashtable and sometimes are called after we already are de-registered. */ |
| - |
| - (void) hashmap_remove_value(n->lldp->neighbor_by_id, &n->id, n); |
| - |
| - assert_se(prioq_remove(n->lldp->neighbor_by_expiry, n, &n->prioq_idx) >= 0); |
| - |
| - n->lldp = NULL; |
| - |
| - if (n->n_ref <= 0) |
| - lldp_neighbor_free(n); |
| - |
| - return NULL; |
| -} |
| - |
| -sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size) { |
| - sd_lldp_neighbor *n; |
| - |
| - n = malloc0(ALIGN(sizeof(sd_lldp_neighbor)) + raw_size); |
| - if (!n) |
| - return NULL; |
| - |
| - n->raw_size = raw_size; |
| - n->n_ref = 1; |
| - |
| - return n; |
| -} |
| - |
| -static int parse_string(char **s, const void *q, size_t n) { |
| - const char *p = q; |
| - char *k; |
| - |
| - assert(s); |
| - assert(p || n == 0); |
| - |
| - if (*s) { |
| - log_lldp("Found duplicate string, ignoring field."); |
| - return 0; |
| - } |
| - |
| - /* Strip trailing NULs, just to be nice */ |
| - while (n > 0 && p[n-1] == 0) |
| - n--; |
| - |
| - if (n <= 0) /* Ignore empty strings */ |
| - return 0; |
| - |
| - /* Look for inner NULs */ |
| - if (memchr(p, 0, n)) { |
| - log_lldp("Found inner NUL in string, ignoring field."); |
| - return 0; |
| - } |
| - |
| - /* Let's escape weird chars, for security reasons */ |
| - k = cescape_length(p, n); |
| - if (!k) |
| - return -ENOMEM; |
| - |
| - free(*s); |
| - *s = k; |
| - |
| - return 1; |
| -} |
| - |
| -int lldp_neighbor_parse(sd_lldp_neighbor *n) { |
| - struct ether_header h; |
| - const uint8_t *p; |
| - size_t left; |
| - int r; |
| - |
| - assert(n); |
| - |
| - if (n->raw_size < sizeof(struct ether_header)) { |
| - log_lldp("Received truncated packet, ignoring."); |
| - return -EBADMSG; |
| - } |
| - |
| - memcpy(&h, LLDP_NEIGHBOR_RAW(n), sizeof(h)); |
| - |
| - if (h.ether_type != htobe16(ETHERTYPE_LLDP)) { |
| - log_lldp("Received packet with wrong type, ignoring."); |
| - return -EBADMSG; |
| - } |
| - |
| - if (h.ether_dhost[0] != 0x01 || |
| - h.ether_dhost[1] != 0x80 || |
| - h.ether_dhost[2] != 0xc2 || |
| - h.ether_dhost[3] != 0x00 || |
| - h.ether_dhost[4] != 0x00 || |
| - !IN_SET(h.ether_dhost[5], 0x00, 0x03, 0x0e)) { |
| - log_lldp("Received packet with wrong destination address, ignoring."); |
| - return -EBADMSG; |
| - } |
| - |
| - memcpy(&n->source_address, h.ether_shost, sizeof(struct ether_addr)); |
| - memcpy(&n->destination_address, h.ether_dhost, sizeof(struct ether_addr)); |
| - |
| - p = (const uint8_t*) LLDP_NEIGHBOR_RAW(n) + sizeof(struct ether_header); |
| - left = n->raw_size - sizeof(struct ether_header); |
| - |
| - for (;;) { |
| - uint8_t type; |
| - uint16_t length; |
| - |
| - if (left < 2) { |
| - log_lldp("TLV lacks header, ignoring."); |
| - return -EBADMSG; |
| - } |
| - |
| - type = p[0] >> 1; |
| - length = p[1] + (((uint16_t) (p[0] & 1)) << 8); |
| - p += 2, left -= 2; |
| - |
| - if (left < length) { |
| - log_lldp("TLV truncated, ignoring datagram."); |
| - return -EBADMSG; |
| - } |
| - |
| - switch (type) { |
| - |
| - case SD_LLDP_TYPE_END: |
| - if (length != 0) { |
| - log_lldp("End marker TLV not zero-sized, ignoring datagram."); |
| - return -EBADMSG; |
| - } |
| - |
| - /* Note that after processing the SD_LLDP_TYPE_END left could still be > 0 |
| - * as the message may contain padding (see IEEE 802.1AB-2016, sec. 8.5.12) */ |
| - |
| - goto end_marker; |
| - |
| - case SD_LLDP_TYPE_CHASSIS_ID: |
| - if (length < 2 || length > 256) { /* includes the chassis subtype, hence one extra byte */ |
| - log_lldp("Chassis ID field size out of range, ignoring datagram."); |
| - return -EBADMSG; |
| - } |
| - if (n->id.chassis_id) { |
| - log_lldp("Duplicate chassis ID field, ignoring datagram."); |
| - return -EBADMSG; |
| - } |
| - |
| - n->id.chassis_id = memdup(p, length); |
| - if (!n->id.chassis_id) |
| - return -ENOMEM; |
| - |
| - n->id.chassis_id_size = length; |
| - break; |
| - |
| - case SD_LLDP_TYPE_PORT_ID: |
| - if (length < 2 || length > 256) { /* includes the port subtype, hence one extra byte */ |
| - log_lldp("Port ID field size out of range, ignoring datagram."); |
| - return -EBADMSG; |
| - } |
| - if (n->id.port_id) { |
| - log_lldp("Duplicate port ID field, ignoring datagram."); |
| - return -EBADMSG; |
| - } |
| - |
| - n->id.port_id = memdup(p, length); |
| - if (!n->id.port_id) |
| - return -ENOMEM; |
| - |
| - n->id.port_id_size = length; |
| - break; |
| - |
| - case SD_LLDP_TYPE_TTL: |
| - if (length != 2) { |
| - log_lldp("TTL field has wrong size, ignoring datagram."); |
| - return -EBADMSG; |
| - } |
| - |
| - if (n->has_ttl) { |
| - log_lldp("Duplicate TTL field, ignoring datagram."); |
| - return -EBADMSG; |
| - } |
| - |
| - n->ttl = unaligned_read_be16(p); |
| - n->has_ttl = true; |
| - break; |
| - |
| - case SD_LLDP_TYPE_PORT_DESCRIPTION: |
| - r = parse_string(&n->port_description, p, length); |
| - if (r < 0) |
| - return r; |
| - break; |
| - |
| - case SD_LLDP_TYPE_SYSTEM_NAME: |
| - r = parse_string(&n->system_name, p, length); |
| - if (r < 0) |
| - return r; |
| - break; |
| - |
| - case SD_LLDP_TYPE_SYSTEM_DESCRIPTION: |
| - r = parse_string(&n->system_description, p, length); |
| - if (r < 0) |
| - return r; |
| - break; |
| - |
| - case SD_LLDP_TYPE_SYSTEM_CAPABILITIES: |
| - if (length != 4) |
| - log_lldp("System capabilities field has wrong size, ignoring."); |
| - else { |
| - n->system_capabilities = unaligned_read_be16(p); |
| - n->enabled_capabilities = unaligned_read_be16(p + 2); |
| - n->has_capabilities = true; |
| - } |
| - |
| - break; |
| - |
| - case SD_LLDP_TYPE_PRIVATE: |
| - if (length < 4) |
| - log_lldp("Found private TLV that is too short, ignoring."); |
| - |
| - break; |
| - } |
| - |
| - p += length, left -= length; |
| - } |
| - |
| -end_marker: |
| - if (!n->id.chassis_id || !n->id.port_id || !n->has_ttl) { |
| - log_lldp("One or more mandatory TLV missing in datagram. Ignoring."); |
| - return -EBADMSG; |
| - |
| - } |
| - |
| - n->rindex = sizeof(struct ether_header); |
| - |
| - return 0; |
| -} |
| - |
| -void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) { |
| - assert(n); |
| - |
| - if (n->ttl > 0) { |
| - usec_t base; |
| - |
| - /* Use the packet's timestamp if there is one known */ |
| - base = triple_timestamp_by_clock(&n->timestamp, clock_boottime_or_monotonic()); |
| - if (base <= 0 || base == USEC_INFINITY) |
| - base = now(clock_boottime_or_monotonic()); /* Otherwise, take the current time */ |
| - |
| - n->until = usec_add(base, n->ttl * USEC_PER_SEC); |
| - } else |
| - n->until = 0; |
| - |
| - if (n->lldp) |
| - prioq_reshuffle(n->lldp->neighbor_by_expiry, n, &n->prioq_idx); |
| -} |
| - |
| -bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b) { |
| - if (a == b) |
| - return true; |
| - |
| - if (!a || !b) |
| - return false; |
| - |
| - if (a->raw_size != b->raw_size) |
| - return false; |
| - |
| - return memcmp(LLDP_NEIGHBOR_RAW(a), LLDP_NEIGHBOR_RAW(b), a->raw_size) == 0; |
| -} |
| - |
| -_public_ int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address) { |
| - assert_return(n, -EINVAL); |
| - assert_return(address, -EINVAL); |
| - |
| - *address = n->source_address; |
| - return 0; |
| -} |
| - |
| -_public_ int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address) { |
| - assert_return(n, -EINVAL); |
| - assert_return(address, -EINVAL); |
| - |
| - *address = n->destination_address; |
| - return 0; |
| -} |
| - |
| -_public_ int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) { |
| - assert_return(n, -EINVAL); |
| - assert_return(ret, -EINVAL); |
| - assert_return(size, -EINVAL); |
| - |
| - *ret = LLDP_NEIGHBOR_RAW(n); |
| - *size = n->raw_size; |
| - |
| - return 0; |
| -} |
| - |
| -_public_ int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) { |
| - assert_return(n, -EINVAL); |
| - assert_return(type, -EINVAL); |
| - assert_return(ret, -EINVAL); |
| - assert_return(size, -EINVAL); |
| - |
| - assert(n->id.chassis_id_size > 0); |
| - |
| - *type = *(uint8_t*) n->id.chassis_id; |
| - *ret = (uint8_t*) n->id.chassis_id + 1; |
| - *size = n->id.chassis_id_size - 1; |
| - |
| - return 0; |
| -} |
| - |
| -static int format_mac_address(const void *data, size_t sz, char **ret) { |
| - struct ether_addr a; |
| - char *k; |
| - |
| - assert(data || sz <= 0); |
| - |
| - if (sz != 7) |
| - return 0; |
| - |
| - memcpy(&a, (uint8_t*) data + 1, sizeof(a)); |
| - |
| - k = new(char, ETHER_ADDR_TO_STRING_MAX); |
| - if (!k) |
| - return -ENOMEM; |
| - |
| - *ret = ether_addr_to_string(&a, k); |
| - return 1; |
| -} |
| - |
| -static int format_network_address(const void *data, size_t sz, char **ret) { |
| - union in_addr_union a; |
| - int family, r; |
| - |
| - if (sz == 6 && ((uint8_t*) data)[1] == 1) { |
| - memcpy(&a.in, (uint8_t*) data + 2, sizeof(a.in)); |
| - family = AF_INET; |
| - } else if (sz == 18 && ((uint8_t*) data)[1] == 2) { |
| - memcpy(&a.in6, (uint8_t*) data + 2, sizeof(a.in6)); |
| - family = AF_INET6; |
| - } else |
| - return 0; |
| - |
| - r = in_addr_to_string(family, &a, ret); |
| - if (r < 0) |
| - return r; |
| - return 1; |
| -} |
| - |
| -_public_ int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret) { |
| - char *k; |
| - int r; |
| - |
| - assert_return(n, -EINVAL); |
| - assert_return(ret, -EINVAL); |
| - |
| - if (n->chassis_id_as_string) { |
| - *ret = n->chassis_id_as_string; |
| - return 0; |
| - } |
| - |
| - assert(n->id.chassis_id_size > 0); |
| - |
| - switch (*(uint8_t*) n->id.chassis_id) { |
| - |
| - case SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT: |
| - case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS: |
| - case SD_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT: |
| - case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME: |
| - case SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED: |
| - k = cescape_length((char*) n->id.chassis_id + 1, n->id.chassis_id_size - 1); |
| - if (!k) |
| - return -ENOMEM; |
| - |
| - goto done; |
| - |
| - case SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS: |
| - r = format_mac_address(n->id.chassis_id, n->id.chassis_id_size, &k); |
| - if (r < 0) |
| - return r; |
| - if (r > 0) |
| - goto done; |
| - |
| - break; |
| - |
| - case SD_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS: |
| - r = format_network_address(n->id.chassis_id, n->id.chassis_id_size, &k); |
| - if (r < 0) |
| - return r; |
| - if (r > 0) |
| - goto done; |
| - |
| - break; |
| - } |
| - |
| - /* Generic fallback */ |
| - k = hexmem(n->id.chassis_id, n->id.chassis_id_size); |
| - if (!k) |
| - return -ENOMEM; |
| - |
| -done: |
| - *ret = n->chassis_id_as_string = k; |
| - return 0; |
| -} |
| - |
| -_public_ int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) { |
| - assert_return(n, -EINVAL); |
| - assert_return(type, -EINVAL); |
| - assert_return(ret, -EINVAL); |
| - assert_return(size, -EINVAL); |
| - |
| - assert(n->id.port_id_size > 0); |
| - |
| - *type = *(uint8_t*) n->id.port_id; |
| - *ret = (uint8_t*) n->id.port_id + 1; |
| - *size = n->id.port_id_size - 1; |
| - |
| - return 0; |
| -} |
| - |
| -_public_ int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret) { |
| - char *k; |
| - int r; |
| - |
| - assert_return(n, -EINVAL); |
| - assert_return(ret, -EINVAL); |
| - |
| - if (n->port_id_as_string) { |
| - *ret = n->port_id_as_string; |
| - return 0; |
| - } |
| - |
| - assert(n->id.port_id_size > 0); |
| - |
| - switch (*(uint8_t*) n->id.port_id) { |
| - |
| - case SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS: |
| - case SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT: |
| - case SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME: |
| - case SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED: |
| - k = cescape_length((char*) n->id.port_id + 1, n->id.port_id_size - 1); |
| - if (!k) |
| - return -ENOMEM; |
| - |
| - goto done; |
| - |
| - case SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS: |
| - r = format_mac_address(n->id.port_id, n->id.port_id_size, &k); |
| - if (r < 0) |
| - return r; |
| - if (r > 0) |
| - goto done; |
| - |
| - break; |
| - |
| - case SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS: |
| - r = format_network_address(n->id.port_id, n->id.port_id_size, &k); |
| - if (r < 0) |
| - return r; |
| - if (r > 0) |
| - goto done; |
| - |
| - break; |
| - } |
| - |
| - /* Generic fallback */ |
| - k = hexmem(n->id.port_id, n->id.port_id_size); |
| - if (!k) |
| - return -ENOMEM; |
| - |
| -done: |
| - *ret = n->port_id_as_string = k; |
| - return 0; |
| -} |
| - |
| -_public_ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec) { |
| - assert_return(n, -EINVAL); |
| - assert_return(ret_sec, -EINVAL); |
| - |
| - *ret_sec = n->ttl; |
| - return 0; |
| -} |
| - |
| -_public_ int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret) { |
| - assert_return(n, -EINVAL); |
| - assert_return(ret, -EINVAL); |
| - |
| - if (!n->system_name) |
| - return -ENODATA; |
| - |
| - *ret = n->system_name; |
| - return 0; |
| -} |
| - |
| -_public_ int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret) { |
| - assert_return(n, -EINVAL); |
| - assert_return(ret, -EINVAL); |
| - |
| - if (!n->system_description) |
| - return -ENODATA; |
| - |
| - *ret = n->system_description; |
| - return 0; |
| -} |
| - |
| -_public_ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret) { |
| - assert_return(n, -EINVAL); |
| - assert_return(ret, -EINVAL); |
| - |
| - if (!n->port_description) |
| - return -ENODATA; |
| - |
| - *ret = n->port_description; |
| - return 0; |
| -} |
| - |
| -_public_ int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) { |
| - assert_return(n, -EINVAL); |
| - assert_return(ret, -EINVAL); |
| - |
| - if (!n->has_capabilities) |
| - return -ENODATA; |
| - |
| - *ret = n->system_capabilities; |
| - return 0; |
| -} |
| - |
| -_public_ int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret) { |
| - assert_return(n, -EINVAL); |
| - assert_return(ret, -EINVAL); |
| - |
| - if (!n->has_capabilities) |
| - return -ENODATA; |
| - |
| - *ret = n->enabled_capabilities; |
| - return 0; |
| -} |
| - |
| -_public_ int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) { |
| - _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL; |
| - int r; |
| - |
| - assert_return(ret, -EINVAL); |
| - assert_return(raw || raw_size <= 0, -EINVAL); |
| - |
| - n = lldp_neighbor_new(raw_size); |
| - if (!n) |
| - return -ENOMEM; |
| - |
| - memcpy(LLDP_NEIGHBOR_RAW(n), raw, raw_size); |
| - r = lldp_neighbor_parse(n); |
| - if (r < 0) |
| - return r; |
| - |
| - *ret = TAKE_PTR(n); |
| - |
| - return r; |
| -} |
| - |
| -_public_ int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) { |
| - assert_return(n, -EINVAL); |
| - |
| - assert(n->raw_size >= sizeof(struct ether_header)); |
| - n->rindex = sizeof(struct ether_header); |
| - |
| - return n->rindex < n->raw_size; |
| -} |
| - |
| -_public_ int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) { |
| - size_t length; |
| - |
| - assert_return(n, -EINVAL); |
| - |
| - if (n->rindex == n->raw_size) /* EOF */ |
| - return -ESPIPE; |
| - |
| - if (n->rindex + 2 > n->raw_size) /* Truncated message */ |
| - return -EBADMSG; |
| - |
| - length = LLDP_NEIGHBOR_TLV_LENGTH(n); |
| - if (n->rindex + 2 + length > n->raw_size) |
| - return -EBADMSG; |
| - |
| - n->rindex += 2 + length; |
| - return n->rindex < n->raw_size; |
| -} |
| - |
| -_public_ int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) { |
| - assert_return(n, -EINVAL); |
| - assert_return(type, -EINVAL); |
| - |
| - if (n->rindex == n->raw_size) /* EOF */ |
| - return -ESPIPE; |
| - |
| - if (n->rindex + 2 > n->raw_size) |
| - return -EBADMSG; |
| - |
| - *type = LLDP_NEIGHBOR_TLV_TYPE(n); |
| - return 0; |
| -} |
| - |
| -_public_ int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) { |
| - uint8_t k; |
| - int r; |
| - |
| - assert_return(n, -EINVAL); |
| - |
| - r = sd_lldp_neighbor_tlv_get_type(n, &k); |
| - if (r < 0) |
| - return r; |
| - |
| - return type == k; |
| -} |
| - |
| -_public_ int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t *subtype) { |
| - const uint8_t *d; |
| - size_t length; |
| - int r; |
| - |
| - assert_return(n, -EINVAL); |
| - assert_return(oui, -EINVAL); |
| - assert_return(subtype, -EINVAL); |
| - |
| - r = sd_lldp_neighbor_tlv_is_type(n, SD_LLDP_TYPE_PRIVATE); |
| - if (r < 0) |
| - return r; |
| - if (r == 0) |
| - return -ENXIO; |
| - |
| - length = LLDP_NEIGHBOR_TLV_LENGTH(n); |
| - if (length < 4) |
| - return -EBADMSG; |
| - |
| - if (n->rindex + 2 + length > n->raw_size) |
| - return -EBADMSG; |
| - |
| - d = LLDP_NEIGHBOR_TLV_DATA(n); |
| - memcpy(oui, d, 3); |
| - *subtype = d[3]; |
| - |
| - return 0; |
| -} |
| - |
| -_public_ int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t subtype) { |
| - uint8_t k[3], st; |
| - int r; |
| - |
| - r = sd_lldp_neighbor_tlv_get_oui(n, k, &st); |
| - if (r == -ENXIO) |
| - return 0; |
| - if (r < 0) |
| - return r; |
| - |
| - return memcmp(k, oui, 3) == 0 && st == subtype; |
| -} |
| - |
| -_public_ int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) { |
| - size_t length; |
| - |
| - assert_return(n, -EINVAL); |
| - assert_return(ret, -EINVAL); |
| - assert_return(size, -EINVAL); |
| - |
| - /* Note that this returns the full TLV, including the TLV header */ |
| - |
| - if (n->rindex + 2 > n->raw_size) |
| - return -EBADMSG; |
| - |
| - length = LLDP_NEIGHBOR_TLV_LENGTH(n); |
| - if (n->rindex + 2 + length > n->raw_size) |
| - return -EBADMSG; |
| - |
| - *ret = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex; |
| - *size = length + 2; |
| - |
| - return 0; |
| -} |
| - |
| -_public_ int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret) { |
| - assert_return(n, -EINVAL); |
| - assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP); |
| - assert_return(clock_supported(clock), -EOPNOTSUPP); |
| - assert_return(ret, -EINVAL); |
| - |
| - if (!triple_timestamp_is_set(&n->timestamp)) |
| - return -ENODATA; |
| - |
| - *ret = triple_timestamp_by_clock(&n->timestamp, clock); |
| - return 0; |
| -} |
| diff --git a/src/libsystemd-network/lldp-neighbor.h b/src/libsystemd-network/lldp-neighbor.h |
| deleted file mode 100644 |
| index 62dbff42ca..0000000000 |
| --- a/src/libsystemd-network/lldp-neighbor.h |
| +++ /dev/null |
| @@ -1,91 +0,0 @@ |
| -/* SPDX-License-Identifier: LGPL-2.1+ */ |
| -#pragma once |
| - |
| -#include <inttypes.h> |
| -#include <stdbool.h> |
| -#include <sys/types.h> |
| - |
| -#include "sd-lldp.h" |
| - |
| -#include "hash-funcs.h" |
| -#include "lldp-internal.h" |
| -#include "time-util.h" |
| - |
| -typedef struct LLDPNeighborID { |
| - /* The spec calls this an "MSAP identifier" */ |
| - void *chassis_id; |
| - size_t chassis_id_size; |
| - |
| - void *port_id; |
| - size_t port_id_size; |
| -} LLDPNeighborID; |
| - |
| -struct sd_lldp_neighbor { |
| - /* Neighbor objects stay around as long as they are linked into an "sd_lldp" object or n_ref > 0. */ |
| - sd_lldp *lldp; |
| - unsigned n_ref; |
| - |
| - triple_timestamp timestamp; |
| - |
| - usec_t until; |
| - unsigned prioq_idx; |
| - |
| - struct ether_addr source_address; |
| - struct ether_addr destination_address; |
| - |
| - LLDPNeighborID id; |
| - |
| - /* The raw packet size. The data is appended to the object, accessible via LLDP_NEIGHBOR_RAW() */ |
| - size_t raw_size; |
| - |
| - /* The current read index for the iterative TLV interface */ |
| - size_t rindex; |
| - |
| - /* And a couple of fields parsed out. */ |
| - bool has_ttl:1; |
| - bool has_capabilities:1; |
| - bool has_port_vlan_id:1; |
| - |
| - uint16_t ttl; |
| - |
| - uint16_t system_capabilities; |
| - uint16_t enabled_capabilities; |
| - |
| - char *port_description; |
| - char *system_name; |
| - char *system_description; |
| - |
| - uint16_t port_vlan_id; |
| - |
| - char *chassis_id_as_string; |
| - char *port_id_as_string; |
| -}; |
| - |
| -static inline void *LLDP_NEIGHBOR_RAW(const sd_lldp_neighbor *n) { |
| - return (uint8_t*) n + ALIGN(sizeof(sd_lldp_neighbor)); |
| -} |
| - |
| -static inline uint8_t LLDP_NEIGHBOR_TLV_TYPE(const sd_lldp_neighbor *n) { |
| - return ((uint8_t*) LLDP_NEIGHBOR_RAW(n))[n->rindex] >> 1; |
| -} |
| - |
| -static inline size_t LLDP_NEIGHBOR_TLV_LENGTH(const sd_lldp_neighbor *n) { |
| - uint8_t *p; |
| - |
| - p = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex; |
| - return p[1] + (((size_t) (p[0] & 1)) << 8); |
| -} |
| - |
| -static inline void* LLDP_NEIGHBOR_TLV_DATA(const sd_lldp_neighbor *n) { |
| - return ((uint8_t*) LLDP_NEIGHBOR_RAW(n)) + n->rindex + 2; |
| -} |
| - |
| -extern const struct hash_ops lldp_neighbor_hash_ops; |
| -int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y); |
| -int lldp_neighbor_prioq_compare_func(const void *a, const void *b); |
| - |
| -sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n); |
| -sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size); |
| -int lldp_neighbor_parse(sd_lldp_neighbor *n); |
| -void lldp_neighbor_start_ttl(sd_lldp_neighbor *n); |
| -bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b); |
| diff --git a/src/libsystemd-network/lldp-network.c b/src/libsystemd-network/lldp-network.c |
| deleted file mode 100644 |
| index 870584c0db..0000000000 |
| --- a/src/libsystemd-network/lldp-network.c |
| +++ /dev/null |
| @@ -1,78 +0,0 @@ |
| -/* SPDX-License-Identifier: LGPL-2.1+ */ |
| - |
| -#include <linux/filter.h> |
| -#include <netinet/if_ether.h> |
| - |
| -#include "fd-util.h" |
| -#include "lldp-network.h" |
| -#include "missing.h" |
| -#include "socket-util.h" |
| - |
| -int lldp_network_bind_raw_socket(int ifindex) { |
| - |
| - static const struct sock_filter filter[] = { |
| - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ethhdr, h_dest)), /* A <- 4 bytes of destination MAC */ |
| - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0180c200, 1, 0), /* A != 01:80:c2:00 */ |
| - BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */ |
| - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_dest) + 4), /* A <- remaining 2 bytes of destination MAC */ |
| - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0000, 3, 0), /* A != 00:00 */ |
| - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0003, 2, 0), /* A != 00:03 */ |
| - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x000e, 1, 0), /* A != 00:0e */ |
| - BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */ |
| - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_proto)), /* A <- protocol */ |
| - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_LLDP, 1, 0), /* A != ETHERTYPE_LLDP */ |
| - BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */ |
| - BPF_STMT(BPF_RET + BPF_K, (uint32_t) -1), /* accept packet */ |
| - }; |
| - |
| - static const struct sock_fprog fprog = { |
| - .len = ELEMENTSOF(filter), |
| - .filter = (struct sock_filter*) filter, |
| - }; |
| - |
| - struct packet_mreq mreq = { |
| - .mr_ifindex = ifindex, |
| - .mr_type = PACKET_MR_MULTICAST, |
| - .mr_alen = ETH_ALEN, |
| - .mr_address = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x00 } |
| - }; |
| - |
| - union sockaddr_union saddrll = { |
| - .ll.sll_family = AF_PACKET, |
| - .ll.sll_ifindex = ifindex, |
| - }; |
| - |
| - _cleanup_close_ int fd = -1; |
| - int r; |
| - |
| - assert(ifindex > 0); |
| - |
| - fd = socket(PF_PACKET, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, |
| - htobe16(ETHERTYPE_LLDP)); |
| - if (fd < 0) |
| - return -errno; |
| - |
| - r = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); |
| - if (r < 0) |
| - return -errno; |
| - |
| - r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); |
| - if (r < 0) |
| - return -errno; |
| - |
| - mreq.mr_address[ETH_ALEN - 1] = 0x03; |
| - r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); |
| - if (r < 0) |
| - return -errno; |
| - |
| - mreq.mr_address[ETH_ALEN - 1] = 0x0E; |
| - r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); |
| - if (r < 0) |
| - return -errno; |
| - |
| - r = bind(fd, &saddrll.sa, sizeof(saddrll.ll)); |
| - if (r < 0) |
| - return -errno; |
| - |
| - return TAKE_FD(fd); |
| -} |
| diff --git a/src/libsystemd-network/lldp-network.h b/src/libsystemd-network/lldp-network.h |
| deleted file mode 100644 |
| index e4ed2898a5..0000000000 |
| --- a/src/libsystemd-network/lldp-network.h |
| +++ /dev/null |
| @@ -1,6 +0,0 @@ |
| -/* SPDX-License-Identifier: LGPL-2.1+ */ |
| -#pragma once |
| - |
| -#include "sd-event.h" |
| - |
| -int lldp_network_bind_raw_socket(int ifindex); |
| diff --git a/src/libsystemd-network/meson.build b/src/libsystemd-network/meson.build |
| index 56d470ff68..88a72aa900 100644 |
| --- a/src/libsystemd-network/meson.build |
| +++ b/src/libsystemd-network/meson.build |
| @@ -33,12 +33,6 @@ sources = files(''' |
| sd-dhcp6-lease.c |
| dhcp-identifier.h |
| dhcp-identifier.c |
| - lldp-internal.h |
| - lldp-network.h |
| - lldp-network.c |
| - lldp-neighbor.h |
| - lldp-neighbor.c |
| - sd-lldp.c |
| '''.split()) |
| |
| network_internal_h = files('network-internal.h') |
| diff --git a/src/libsystemd-network/sd-lldp.c b/src/libsystemd-network/sd-lldp.c |
| deleted file mode 100644 |
| index 1f28c5731f..0000000000 |
| --- a/src/libsystemd-network/sd-lldp.c |
| +++ /dev/null |
| @@ -1,498 +0,0 @@ |
| -/* SPDX-License-Identifier: LGPL-2.1+ */ |
| - |
| -#include <arpa/inet.h> |
| -#include <linux/sockios.h> |
| -#include <sys/ioctl.h> |
| - |
| -#include "sd-lldp.h" |
| - |
| -#include "alloc-util.h" |
| -#include "ether-addr-util.h" |
| -#include "event-util.h" |
| -#include "fd-util.h" |
| -#include "lldp-internal.h" |
| -#include "lldp-neighbor.h" |
| -#include "lldp-network.h" |
| -#include "memory-util.h" |
| -#include "socket-util.h" |
| -#include "sort-util.h" |
| -#include "string-table.h" |
| - |
| -#define LLDP_DEFAULT_NEIGHBORS_MAX 128U |
| - |
| -static const char * const lldp_event_table[_SD_LLDP_EVENT_MAX] = { |
| - [SD_LLDP_EVENT_ADDED] = "added", |
| - [SD_LLDP_EVENT_REMOVED] = "removed", |
| - [SD_LLDP_EVENT_UPDATED] = "updated", |
| - [SD_LLDP_EVENT_REFRESHED] = "refreshed", |
| -}; |
| - |
| -DEFINE_STRING_TABLE_LOOKUP(lldp_event, sd_lldp_event); |
| - |
| -static void lldp_flush_neighbors(sd_lldp *lldp) { |
| - assert(lldp); |
| - |
| - hashmap_clear(lldp->neighbor_by_id); |
| -} |
| - |
| -static void lldp_callback(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n) { |
| - assert(lldp); |
| - assert(event >= 0 && event < _SD_LLDP_EVENT_MAX); |
| - |
| - if (!lldp->callback) { |
| - log_lldp("Received '%s' event.", lldp_event_to_string(event)); |
| - return; |
| - } |
| - |
| - log_lldp("Invoking callback for '%s' event.", lldp_event_to_string(event)); |
| - lldp->callback(lldp, event, n, lldp->userdata); |
| -} |
| - |
| -static int lldp_make_space(sd_lldp *lldp, size_t extra) { |
| - usec_t t = USEC_INFINITY; |
| - bool changed = false; |
| - |
| - assert(lldp); |
| - |
| - /* Remove all entries that are past their TTL, and more until at least the specified number of extra entries |
| - * are free. */ |
| - |
| - for (;;) { |
| - _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL; |
| - |
| - n = prioq_peek(lldp->neighbor_by_expiry); |
| - if (!n) |
| - break; |
| - |
| - sd_lldp_neighbor_ref(n); |
| - |
| - if (hashmap_size(lldp->neighbor_by_id) > LESS_BY(lldp->neighbors_max, extra)) |
| - goto remove_one; |
| - |
| - if (t == USEC_INFINITY) |
| - t = now(clock_boottime_or_monotonic()); |
| - |
| - if (n->until > t) |
| - break; |
| - |
| - remove_one: |
| - lldp_neighbor_unlink(n); |
| - lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, n); |
| - changed = true; |
| - } |
| - |
| - return changed; |
| -} |
| - |
| -static bool lldp_keep_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) { |
| - assert(lldp); |
| - assert(n); |
| - |
| - /* Don't keep data with a zero TTL */ |
| - if (n->ttl <= 0) |
| - return false; |
| - |
| - /* Filter out data from the filter address */ |
| - if (!ether_addr_is_null(&lldp->filter_address) && |
| - ether_addr_equal(&lldp->filter_address, &n->source_address)) |
| - return false; |
| - |
| - /* Only add if the neighbor has a capability we are interested in. Note that we also store all neighbors with |
| - * no caps field set. */ |
| - if (n->has_capabilities && |
| - (n->enabled_capabilities & lldp->capability_mask) == 0) |
| - return false; |
| - |
| - /* Keep everything else */ |
| - return true; |
| -} |
| - |
| -static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor); |
| - |
| -static int lldp_add_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) { |
| - _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *old = NULL; |
| - bool keep; |
| - int r; |
| - |
| - assert(lldp); |
| - assert(n); |
| - assert(!n->lldp); |
| - |
| - keep = lldp_keep_neighbor(lldp, n); |
| - |
| - /* First retrieve the old entry for this MSAP */ |
| - old = hashmap_get(lldp->neighbor_by_id, &n->id); |
| - if (old) { |
| - sd_lldp_neighbor_ref(old); |
| - |
| - if (!keep) { |
| - lldp_neighbor_unlink(old); |
| - lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old); |
| - return 0; |
| - } |
| - |
| - if (lldp_neighbor_equal(n, old)) { |
| - /* Is this equal, then restart the TTL counter, but don't do anything else. */ |
| - old->timestamp = n->timestamp; |
| - lldp_start_timer(lldp, old); |
| - lldp_callback(lldp, SD_LLDP_EVENT_REFRESHED, old); |
| - return 0; |
| - } |
| - |
| - /* Data changed, remove the old entry, and add a new one */ |
| - lldp_neighbor_unlink(old); |
| - |
| - } else if (!keep) |
| - return 0; |
| - |
| - /* Then, make room for at least one new neighbor */ |
| - lldp_make_space(lldp, 1); |
| - |
| - r = hashmap_put(lldp->neighbor_by_id, &n->id, n); |
| - if (r < 0) |
| - goto finish; |
| - |
| - r = prioq_put(lldp->neighbor_by_expiry, n, &n->prioq_idx); |
| - if (r < 0) { |
| - assert_se(hashmap_remove(lldp->neighbor_by_id, &n->id) == n); |
| - goto finish; |
| - } |
| - |
| - n->lldp = lldp; |
| - |
| - lldp_start_timer(lldp, n); |
| - lldp_callback(lldp, old ? SD_LLDP_EVENT_UPDATED : SD_LLDP_EVENT_ADDED, n); |
| - |
| - return 1; |
| - |
| -finish: |
| - if (old) |
| - lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old); |
| - |
| - return r; |
| -} |
| - |
| -static int lldp_handle_datagram(sd_lldp *lldp, sd_lldp_neighbor *n) { |
| - int r; |
| - |
| - assert(lldp); |
| - assert(n); |
| - |
| - r = lldp_neighbor_parse(n); |
| - if (r == -EBADMSG) /* Ignore bad messages */ |
| - return 0; |
| - if (r < 0) |
| - return r; |
| - |
| - r = lldp_add_neighbor(lldp, n); |
| - if (r < 0) { |
| - log_lldp_errno(r, "Failed to add datagram. Ignoring."); |
| - return 0; |
| - } |
| - |
| - log_lldp("Successfully processed LLDP datagram."); |
| - return 0; |
| -} |
| - |
| -static int lldp_receive_datagram(sd_event_source *s, int fd, uint32_t revents, void *userdata) { |
| - _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL; |
| - ssize_t space, length; |
| - sd_lldp *lldp = userdata; |
| - struct timespec ts; |
| - |
| - assert(fd >= 0); |
| - assert(lldp); |
| - |
| - space = next_datagram_size_fd(fd); |
| - if (space < 0) |
| - return log_lldp_errno(space, "Failed to determine datagram size to read: %m"); |
| - |
| - n = lldp_neighbor_new(space); |
| - if (!n) |
| - return -ENOMEM; |
| - |
| - length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT); |
| - if (length < 0) { |
| - if (IN_SET(errno, EAGAIN, EINTR)) |
| - return 0; |
| - |
| - return log_lldp_errno(errno, "Failed to read LLDP datagram: %m"); |
| - } |
| - |
| - if ((size_t) length != n->raw_size) { |
| - log_lldp("Packet size mismatch."); |
| - return -EINVAL; |
| - } |
| - |
| - /* Try to get the timestamp of this packet if it is known */ |
| - if (ioctl(fd, SIOCGSTAMPNS, &ts) >= 0) |
| - triple_timestamp_from_realtime(&n->timestamp, timespec_load(&ts)); |
| - else |
| - triple_timestamp_get(&n->timestamp); |
| - |
| - return lldp_handle_datagram(lldp, n); |
| -} |
| - |
| -static void lldp_reset(sd_lldp *lldp) { |
| - assert(lldp); |
| - |
| - (void) event_source_disable(lldp->timer_event_source); |
| - lldp->io_event_source = sd_event_source_unref(lldp->io_event_source); |
| - lldp->fd = safe_close(lldp->fd); |
| -} |
| - |
| -_public_ int sd_lldp_start(sd_lldp *lldp) { |
| - int r; |
| - |
| - assert_return(lldp, -EINVAL); |
| - assert_return(lldp->event, -EINVAL); |
| - assert_return(lldp->ifindex > 0, -EINVAL); |
| - |
| - if (lldp->fd >= 0) |
| - return 0; |
| - |
| - assert(!lldp->io_event_source); |
| - |
| - lldp->fd = lldp_network_bind_raw_socket(lldp->ifindex); |
| - if (lldp->fd < 0) |
| - return lldp->fd; |
| - |
| - r = sd_event_add_io(lldp->event, &lldp->io_event_source, lldp->fd, EPOLLIN, lldp_receive_datagram, lldp); |
| - if (r < 0) |
| - goto fail; |
| - |
| - r = sd_event_source_set_priority(lldp->io_event_source, lldp->event_priority); |
| - if (r < 0) |
| - goto fail; |
| - |
| - (void) sd_event_source_set_description(lldp->io_event_source, "lldp-io"); |
| - |
| - log_lldp("Started LLDP client"); |
| - return 1; |
| - |
| -fail: |
| - lldp_reset(lldp); |
| - return r; |
| -} |
| - |
| -_public_ int sd_lldp_stop(sd_lldp *lldp) { |
| - assert_return(lldp, -EINVAL); |
| - |
| - if (lldp->fd < 0) |
| - return 0; |
| - |
| - log_lldp("Stopping LLDP client"); |
| - |
| - lldp_reset(lldp); |
| - lldp_flush_neighbors(lldp); |
| - |
| - return 1; |
| -} |
| - |
| -_public_ int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int64_t priority) { |
| - int r; |
| - |
| - assert_return(lldp, -EINVAL); |
| - assert_return(lldp->fd < 0, -EBUSY); |
| - assert_return(!lldp->event, -EBUSY); |
| - |
| - if (event) |
| - lldp->event = sd_event_ref(event); |
| - else { |
| - r = sd_event_default(&lldp->event); |
| - if (r < 0) |
| - return r; |
| - } |
| - |
| - lldp->event_priority = priority; |
| - |
| - return 0; |
| -} |
| - |
| -_public_ int sd_lldp_detach_event(sd_lldp *lldp) { |
| - |
| - assert_return(lldp, -EINVAL); |
| - assert_return(lldp->fd < 0, -EBUSY); |
| - |
| - lldp->event = sd_event_unref(lldp->event); |
| - return 0; |
| -} |
| - |
| -_public_ sd_event* sd_lldp_get_event(sd_lldp *lldp) { |
| - assert_return(lldp, NULL); |
| - |
| - return lldp->event; |
| -} |
| - |
| -_public_ int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata) { |
| - assert_return(lldp, -EINVAL); |
| - |
| - lldp->callback = cb; |
| - lldp->userdata = userdata; |
| - |
| - return 0; |
| -} |
| - |
| -_public_ int sd_lldp_set_ifindex(sd_lldp *lldp, int ifindex) { |
| - assert_return(lldp, -EINVAL); |
| - assert_return(ifindex > 0, -EINVAL); |
| - assert_return(lldp->fd < 0, -EBUSY); |
| - |
| - lldp->ifindex = ifindex; |
| - return 0; |
| -} |
| - |
| -static sd_lldp* lldp_free(sd_lldp *lldp) { |
| - assert(lldp); |
| - |
| - lldp->timer_event_source = sd_event_source_unref(lldp->timer_event_source); |
| - |
| - lldp_reset(lldp); |
| - sd_lldp_detach_event(lldp); |
| - lldp_flush_neighbors(lldp); |
| - |
| - hashmap_free(lldp->neighbor_by_id); |
| - prioq_free(lldp->neighbor_by_expiry); |
| - return mfree(lldp); |
| -} |
| - |
| -DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_lldp, sd_lldp, lldp_free); |
| - |
| -_public_ int sd_lldp_new(sd_lldp **ret) { |
| - _cleanup_(sd_lldp_unrefp) sd_lldp *lldp = NULL; |
| - int r; |
| - |
| - assert_return(ret, -EINVAL); |
| - |
| - lldp = new(sd_lldp, 1); |
| - if (!lldp) |
| - return -ENOMEM; |
| - |
| - *lldp = (sd_lldp) { |
| - .n_ref = 1, |
| - .fd = -1, |
| - .neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX, |
| - .capability_mask = (uint16_t) -1, |
| - }; |
| - |
| - lldp->neighbor_by_id = hashmap_new(&lldp_neighbor_hash_ops); |
| - if (!lldp->neighbor_by_id) |
| - return -ENOMEM; |
| - |
| - r = prioq_ensure_allocated(&lldp->neighbor_by_expiry, lldp_neighbor_prioq_compare_func); |
| - if (r < 0) |
| - return r; |
| - |
| - *ret = TAKE_PTR(lldp); |
| - |
| - return 0; |
| -} |
| - |
| -static int neighbor_compare_func(sd_lldp_neighbor * const *a, sd_lldp_neighbor * const *b) { |
| - return lldp_neighbor_id_compare_func(&(*a)->id, &(*b)->id); |
| -} |
| - |
| -static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) { |
| - sd_lldp *lldp = userdata; |
| - int r; |
| - |
| - r = lldp_make_space(lldp, 0); |
| - if (r < 0) |
| - return log_lldp_errno(r, "Failed to make space: %m"); |
| - |
| - r = lldp_start_timer(lldp, NULL); |
| - if (r < 0) |
| - return log_lldp_errno(r, "Failed to restart timer: %m"); |
| - |
| - return 0; |
| -} |
| - |
| -static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor) { |
| - sd_lldp_neighbor *n; |
| - |
| - assert(lldp); |
| - |
| - if (neighbor) |
| - lldp_neighbor_start_ttl(neighbor); |
| - |
| - n = prioq_peek(lldp->neighbor_by_expiry); |
| - if (!n) |
| - return event_source_disable(lldp->timer_event_source); |
| - |
| - if (!lldp->event) |
| - return 0; |
| - |
| - return event_reset_time(lldp->event, &lldp->timer_event_source, |
| - clock_boottime_or_monotonic(), |
| - n->until, 0, |
| - on_timer_event, lldp, |
| - lldp->event_priority, "lldp-timer", true); |
| -} |
| - |
| -_public_ int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***ret) { |
| - sd_lldp_neighbor **l = NULL, *n; |
| - Iterator i; |
| - int k = 0, r; |
| - |
| - assert_return(lldp, -EINVAL); |
| - assert_return(ret, -EINVAL); |
| - |
| - if (hashmap_isempty(lldp->neighbor_by_id)) { /* Special shortcut */ |
| - *ret = NULL; |
| - return 0; |
| - } |
| - |
| - l = new0(sd_lldp_neighbor*, hashmap_size(lldp->neighbor_by_id)); |
| - if (!l) |
| - return -ENOMEM; |
| - |
| - r = lldp_start_timer(lldp, NULL); |
| - if (r < 0) { |
| - free(l); |
| - return r; |
| - } |
| - |
| - HASHMAP_FOREACH(n, lldp->neighbor_by_id, i) |
| - l[k++] = sd_lldp_neighbor_ref(n); |
| - |
| - assert((size_t) k == hashmap_size(lldp->neighbor_by_id)); |
| - |
| - /* Return things in a stable order */ |
| - typesafe_qsort(l, k, neighbor_compare_func); |
| - *ret = l; |
| - |
| - return k; |
| -} |
| - |
| -_public_ int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t m) { |
| - assert_return(lldp, -EINVAL); |
| - assert_return(m <= 0, -EINVAL); |
| - |
| - lldp->neighbors_max = m; |
| - lldp_make_space(lldp, 0); |
| - |
| - return 0; |
| -} |
| - |
| -_public_ int sd_lldp_match_capabilities(sd_lldp *lldp, uint16_t mask) { |
| - assert_return(lldp, -EINVAL); |
| - assert_return(mask != 0, -EINVAL); |
| - |
| - lldp->capability_mask = mask; |
| - |
| - return 0; |
| -} |
| - |
| -_public_ int sd_lldp_set_filter_address(sd_lldp *lldp, const struct ether_addr *addr) { |
| - assert_return(lldp, -EINVAL); |
| - |
| - /* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so |
| - * that our own can be filtered out here. */ |
| - |
| - if (addr) |
| - lldp->filter_address = *addr; |
| - else |
| - zero(lldp->filter_address); |
| - |
| - return 0; |
| -} |
| diff --git a/src/libsystemd-network/test-lldp.c b/src/libsystemd-network/test-lldp.c |
| deleted file mode 100644 |
| index 7406f94ce0..0000000000 |
| --- a/src/libsystemd-network/test-lldp.c |
| +++ /dev/null |
| @@ -1,379 +0,0 @@ |
| -/* SPDX-License-Identifier: LGPL-2.1+ */ |
| - |
| -#include <arpa/inet.h> |
| -#include <errno.h> |
| -#include <net/ethernet.h> |
| -#include <stdio.h> |
| -#include <string.h> |
| -#include <unistd.h> |
| - |
| -#include "sd-event.h" |
| -#include "sd-lldp.h" |
| - |
| -#include "alloc-util.h" |
| -#include "fd-util.h" |
| -#include "lldp-network.h" |
| -#include "macro.h" |
| -#include "string-util.h" |
| -#include "tests.h" |
| - |
| -#define TEST_LLDP_PORT "em1" |
| -#define TEST_LLDP_TYPE_SYSTEM_NAME "systemd-lldp" |
| -#define TEST_LLDP_TYPE_SYSTEM_DESC "systemd-lldp-desc" |
| - |
| -static int test_fd[2] = { -1, -1 }; |
| -static int lldp_handler_calls; |
| - |
| -int lldp_network_bind_raw_socket(int ifindex) { |
| - if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0) |
| - return -errno; |
| - |
| - return test_fd[0]; |
| -} |
| - |
| -static void lldp_handler(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata) { |
| - lldp_handler_calls++; |
| -} |
| - |
| -static int start_lldp(sd_lldp **lldp, sd_event *e, sd_lldp_callback_t cb, void *cb_data) { |
| - int r; |
| - |
| - r = sd_lldp_new(lldp); |
| - if (r < 0) |
| - return r; |
| - |
| - r = sd_lldp_set_ifindex(*lldp, 42); |
| - if (r < 0) |
| - return r; |
| - |
| - r = sd_lldp_set_callback(*lldp, cb, cb_data); |
| - if (r < 0) |
| - return r; |
| - |
| - r = sd_lldp_attach_event(*lldp, e, 0); |
| - if (r < 0) |
| - return r; |
| - |
| - r = sd_lldp_start(*lldp); |
| - if (r < 0) |
| - return r; |
| - |
| - return 0; |
| -} |
| - |
| -static int stop_lldp(sd_lldp *lldp) { |
| - int r; |
| - |
| - r = sd_lldp_stop(lldp); |
| - if (r < 0) |
| - return r; |
| - |
| - r = sd_lldp_detach_event(lldp); |
| - if (r < 0) |
| - return r; |
| - |
| - sd_lldp_unref(lldp); |
| - safe_close(test_fd[1]); |
| - |
| - return 0; |
| -} |
| - |
| -static void test_receive_basic_packet(sd_event *e) { |
| - |
| - static const uint8_t frame[] = { |
| - /* Ethernet header */ |
| - 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */ |
| - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */ |
| - 0x88, 0xcc, /* Ethertype */ |
| - /* LLDP mandatory TLVs */ |
| - 0x02, 0x07, 0x04, 0x00, 0x01, 0x02, /* Chassis: MAC, 00:01:02:03:04:05 */ |
| - 0x03, 0x04, 0x05, |
| - 0x04, 0x04, 0x05, 0x31, 0x2f, 0x33, /* Port: interface name, "1/3" */ |
| - 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */ |
| - /* LLDP optional TLVs */ |
| - 0x08, 0x04, 0x50, 0x6f, 0x72, 0x74, /* Port Description: "Port" */ |
| - 0x0a, 0x03, 0x53, 0x59, 0x53, /* System Name: "SYS" */ |
| - 0x0c, 0x04, 0x66, 0x6f, 0x6f, 0x00, /* System Description: "foo" (NULL-terminated) */ |
| - 0x00, 0x00 /* End Of LLDPDU */ |
| - }; |
| - |
| - sd_lldp *lldp; |
| - sd_lldp_neighbor **neighbors; |
| - uint8_t type; |
| - const void *data; |
| - uint16_t ttl; |
| - size_t length; |
| - const char *str; |
| - |
| - lldp_handler_calls = 0; |
| - assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0); |
| - |
| - assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame)); |
| - sd_event_run(e, 0); |
| - assert_se(lldp_handler_calls == 1); |
| - assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 1); |
| - |
| - assert_se(sd_lldp_neighbor_get_chassis_id(neighbors[0], &type, &data, &length) == 0); |
| - assert_se(type == SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS); |
| - assert_se(length == ETH_ALEN); |
| - assert_se(!memcmp(data, "\x00\x01\x02\x03\x04\x05", ETH_ALEN)); |
| - |
| - assert_se(sd_lldp_neighbor_get_port_id(neighbors[0], &type, &data, &length) == 0); |
| - assert_se(type == SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME); |
| - assert_se(length == 3); |
| - assert_se(!memcmp(data, "1/3", 3)); |
| - |
| - assert_se(sd_lldp_neighbor_get_port_description(neighbors[0], &str) == 0); |
| - assert_se(streq(str, "Port")); |
| - |
| - assert_se(sd_lldp_neighbor_get_system_name(neighbors[0], &str) == 0); |
| - assert_se(streq(str, "SYS")); |
| - |
| - assert_se(sd_lldp_neighbor_get_system_description(neighbors[0], &str) == 0); |
| - assert_se(streq(str, "foo")); |
| - |
| - assert_se(sd_lldp_neighbor_get_ttl(neighbors[0], &ttl) == 0); |
| - assert_se(ttl == 120); |
| - |
| - sd_lldp_neighbor_unref(neighbors[0]); |
| - free(neighbors); |
| - |
| - assert_se(stop_lldp(lldp) == 0); |
| -} |
| - |
| -static void test_receive_incomplete_packet(sd_event *e) { |
| - sd_lldp *lldp; |
| - sd_lldp_neighbor **neighbors; |
| - uint8_t frame[] = { |
| - /* Ethernet header */ |
| - 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */ |
| - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */ |
| - 0x88, 0xcc, /* Ethertype */ |
| - /* LLDP mandatory TLVs */ |
| - 0x02, 0x07, 0x04, 0x00, 0x01, 0x02, /* Chassis: MAC, 00:01:02:03:04:05 */ |
| - 0x03, 0x04, 0x05, |
| - 0x04, 0x04, 0x05, 0x31, 0x2f, 0x33, /* Port: interface name, "1/3" */ |
| - /* Missing TTL */ |
| - 0x00, 0x00 /* End Of LLDPDU */ |
| - }; |
| - |
| - lldp_handler_calls = 0; |
| - assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0); |
| - |
| - assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame)); |
| - sd_event_run(e, 0); |
| - assert_se(lldp_handler_calls == 0); |
| - assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 0); |
| - |
| - assert_se(stop_lldp(lldp) == 0); |
| -} |
| - |
| -static void test_receive_oui_packet(sd_event *e) { |
| - sd_lldp *lldp; |
| - sd_lldp_neighbor **neighbors; |
| - uint8_t frame[] = { |
| - /* Ethernet header */ |
| - 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */ |
| - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */ |
| - 0x88, 0xcc, /* Ethertype */ |
| - /* LLDP mandatory TLVs */ |
| - 0x02, 0x07, 0x04, 0x00, 0x01, 0x02, /* Chassis: MAC, 00:01:02:03:04:05 */ |
| - 0x03, 0x04, 0x05, |
| - 0x04, 0x04, 0x05, 0x31, 0x2f, 0x33, /* Port TLV: interface name, "1/3" */ |
| - 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */ |
| - /* LLDP optional TLVs */ |
| - 0xfe, 0x06, 0x00, 0x80, 0xc2, 0x01, /* Port VLAN ID: 0x1234 */ |
| - 0x12, 0x34, |
| - 0xfe, 0x07, 0x00, 0x80, 0xc2, 0x02, /* Port and protocol: flag 1, PPVID 0x7788 */ |
| - 0x01, 0x77, 0x88, |
| - 0xfe, 0x0d, 0x00, 0x80, 0xc2, 0x03, /* VLAN Name: ID 0x1234, name "Vlan51" */ |
| - 0x12, 0x34, 0x06, 0x56, 0x6c, 0x61, |
| - 0x6e, 0x35, 0x31, |
| - 0xfe, 0x06, 0x00, 0x80, 0xc2, 0x06, /* Management VID: 0x0102 */ |
| - 0x01, 0x02, |
| - 0xfe, 0x09, 0x00, 0x80, 0xc2, 0x07, /* Link aggregation: status 1, ID 0x00140012 */ |
| - 0x01, 0x00, 0x14, 0x00, 0x12, |
| - 0xfe, 0x07, 0x00, 0x12, 0x0f, 0x02, /* 802.3 Power via MDI: PSE, MDI enabled */ |
| - 0x07, 0x01, 0x00, |
| - 0x00, 0x00 /* End of LLDPDU */ |
| - }; |
| - |
| - lldp_handler_calls = 0; |
| - assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0); |
| - |
| - assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame)); |
| - sd_event_run(e, 0); |
| - assert_se(lldp_handler_calls == 1); |
| - assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 1); |
| - |
| - assert_se(sd_lldp_neighbor_tlv_rewind(neighbors[0]) >= 0); |
| - assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_CHASSIS_ID) > 0); |
| - assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); |
| - assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_PORT_ID) > 0); |
| - assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); |
| - assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_TTL) > 0); |
| - assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); |
| - 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); |
| - assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); |
| - 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); |
| - assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); |
| - assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME) > 0); |
| - assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); |
| - assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_MANAGEMENT_VID) > 0); |
| - assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); |
| - assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION) > 0); |
| - assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); |
| - 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); |
| - assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); |
| - assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_END) > 0); |
| - assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) == 0); |
| - |
| - sd_lldp_neighbor_unref(neighbors[0]); |
| - free(neighbors); |
| - |
| - assert_se(stop_lldp(lldp) == 0); |
| -} |
| - |
| -static void test_multiple_neighbors_sorted(sd_event *e) { |
| - |
| - static const uint8_t frame1[] = { |
| - /* Ethernet header */ |
| - 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */ |
| - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */ |
| - 0x88, 0xcc, /* Ethertype */ |
| - /* LLDP mandatory TLVs */ |
| - 0x02, 0x04, 0x01, '1', '/', '2', /* Chassis component: "1/2" */ |
| - 0x04, 0x04, 0x02, '2', '/', '3', /* Port component: "2/3" */ |
| - 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */ |
| - 0x00, 0x00 /* End Of LLDPDU */ |
| - }; |
| - static const uint8_t frame2[] = { |
| - /* Ethernet header */ |
| - 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */ |
| - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */ |
| - 0x88, 0xcc, /* Ethertype */ |
| - /* LLDP mandatory TLVs */ |
| - 0x02, 0x04, 0x01, '2', '/', '1', /* Chassis component: "2/1" */ |
| - 0x04, 0x04, 0x02, '1', '/', '3', /* Port component: "1/3" */ |
| - 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */ |
| - 0x00, 0x00 /* End Of LLDPDU */ |
| - }; |
| - static const uint8_t frame3[] = { |
| - /* Ethernet header */ |
| - 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */ |
| - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */ |
| - 0x88, 0xcc, /* Ethertype */ |
| - /* LLDP mandatory TLVs */ |
| - 0x02, 0x05, 0x01, '2', '/', '1', '0', /* Chassis component: "2/10" */ |
| - 0x04, 0x04, 0x02, '1', '/', '0', /* Port component: "1/0" */ |
| - 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */ |
| - 0x00, 0x00 /* End Of LLDPDU */ |
| - }; |
| - static const uint8_t frame4[] = { |
| - /* Ethernet header */ |
| - 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */ |
| - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */ |
| - 0x88, 0xcc, /* Ethertype */ |
| - /* LLDP mandatory TLVs */ |
| - 0x02, 0x05, 0x01, '2', '/', '1', '9', /* Chassis component: "2/19" */ |
| - 0x04, 0x04, 0x02, '1', '/', '0', /* Port component: "1/0" */ |
| - 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */ |
| - 0x00, 0x00 /* End Of LLDPDU */ |
| - }; |
| - static const uint8_t frame5[] = { |
| - /* Ethernet header */ |
| - 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */ |
| - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */ |
| - 0x88, 0xcc, /* Ethertype */ |
| - /* LLDP mandatory TLVs */ |
| - 0x02, 0x04, 0x01, '1', '/', '2', /* Chassis component: "1/2" */ |
| - 0x04, 0x05, 0x02, '2', '/', '1', '0', /* Port component: "2/10" */ |
| - 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */ |
| - 0x00, 0x00 /* End Of LLDPDU */ |
| - }; |
| - static const uint8_t frame6[] = { |
| - /* Ethernet header */ |
| - 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */ |
| - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */ |
| - 0x88, 0xcc, /* Ethertype */ |
| - /* LLDP mandatory TLVs */ |
| - 0x02, 0x04, 0x01, '1', '/', '2', /* Chassis component: "1/2" */ |
| - 0x04, 0x05, 0x02, '2', '/', '3', '9', /* Port component: "2/10" */ |
| - 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */ |
| - 0x00, 0x00 /* End Of LLDPDU */ |
| - }; |
| - static const char* expected[] = { |
| - /* ordered pairs of Chassis+Port */ |
| - "1/2", "2/10", |
| - "1/2", "2/3", |
| - "1/2", "2/39", |
| - "2/1", "1/3", |
| - "2/10", "1/0", |
| - "2/19", "1/0", |
| - }; |
| - |
| - sd_lldp *lldp; |
| - sd_lldp_neighbor **neighbors; |
| - int i; |
| - uint8_t type; |
| - const void *data; |
| - size_t length, expected_length; |
| - uint16_t ttl; |
| - |
| - lldp_handler_calls = 0; |
| - assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0); |
| - |
| - assert_se(write(test_fd[1], frame1, sizeof(frame1)) == sizeof(frame1)); |
| - sd_event_run(e, 0); |
| - assert_se(write(test_fd[1], frame2, sizeof(frame2)) == sizeof(frame2)); |
| - sd_event_run(e, 0); |
| - assert_se(write(test_fd[1], frame3, sizeof(frame3)) == sizeof(frame3)); |
| - sd_event_run(e, 0); |
| - assert_se(write(test_fd[1], frame4, sizeof(frame4)) == sizeof(frame4)); |
| - sd_event_run(e, 0); |
| - assert_se(write(test_fd[1], frame5, sizeof(frame5)) == sizeof(frame5)); |
| - sd_event_run(e, 0); |
| - assert_se(write(test_fd[1], frame6, sizeof(frame6)) == sizeof(frame6)); |
| - sd_event_run(e, 0); |
| - assert_se(lldp_handler_calls == 6); |
| - |
| - assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 6); |
| - |
| - for (i = 0; i < 6; i++) { |
| - assert_se(sd_lldp_neighbor_get_chassis_id(neighbors[i], &type, &data, &length) == 0); |
| - assert_se(type == SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT); |
| - expected_length = strlen(expected[2 * i]); |
| - assert_se(length == expected_length); |
| - assert_se(memcmp(data, expected[2 * i], expected_length) == 0); |
| - |
| - assert_se(sd_lldp_neighbor_get_port_id(neighbors[i], &type, &data, &length) == 0); |
| - assert_se(type == SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT); |
| - expected_length = strlen(expected[2 * i + 1]); |
| - assert_se(length == expected_length); |
| - assert_se(memcmp(data, expected[2 * i + 1], expected_length) == 0); |
| - |
| - assert_se(sd_lldp_neighbor_get_ttl(neighbors[i], &ttl) == 0); |
| - assert_se(ttl == 120); |
| - } |
| - |
| - for (i = 0; i < 6; i++) |
| - sd_lldp_neighbor_unref(neighbors[i]); |
| - free(neighbors); |
| - |
| - assert_se(stop_lldp(lldp) == 0); |
| -} |
| - |
| -int main(int argc, char *argv[]) { |
| - _cleanup_(sd_event_unrefp) sd_event *e = NULL; |
| - |
| - test_setup_logging(LOG_DEBUG); |
| - |
| - /* LLDP reception tests */ |
| - assert_se(sd_event_new(&e) == 0); |
| - test_receive_basic_packet(e); |
| - test_receive_incomplete_packet(e); |
| - test_receive_oui_packet(e); |
| - test_multiple_neighbors_sorted(e); |
| - |
| - return 0; |
| -} |
| diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym |
| index 5ec42e0f1f..48021b86d3 100644 |
| --- a/src/libsystemd/libsystemd.sym |
| +++ b/src/libsystemd/libsystemd.sym |
| @@ -681,4 +681,45 @@ LIBSYSTEMD_243 { |
| global: |
| sd_bus_object_vtable_format; |
| sd_event_source_disable_unref; |
| + |
| + sd_lldp_new; |
| + sd_lldp_ref; |
| + sd_lldp_unref; |
| + sd_lldp_start; |
| + sd_lldp_stop; |
| + sd_lldp_attach_event; |
| + sd_lldp_detach_event; |
| + sd_lldp_get_event; |
| + sd_lldp_set_callback; |
| + sd_lldp_set_ifindex; |
| + sd_lldp_set_neighbors_max; |
| + sd_lldp_match_capabilities; |
| + sd_lldp_set_filter_address; |
| + sd_lldp_get_neighbors; |
| + sd_lldp_neighbor_from_raw; |
| + sd_lldp_neighbor_ref; |
| + sd_lldp_neighbor_unref; |
| + sd_lldp_neighbor_get_source_address; |
| + sd_lldp_neighbor_get_destination_address; |
| + sd_lldp_neighbor_get_timestamp; |
| + sd_lldp_neighbor_get_raw; |
| + sd_lldp_neighbor_get_chassis_id; |
| + sd_lldp_neighbor_get_chassis_id_as_string; |
| + sd_lldp_neighbor_get_port_id; |
| + sd_lldp_neighbor_get_port_id_as_string; |
| + sd_lldp_neighbor_get_ttl; |
| + sd_lldp_neighbor_get_system_name; |
| + sd_lldp_neighbor_get_system_description; |
| + sd_lldp_neighbor_get_port_description; |
| + sd_lldp_neighbor_get_mud_url; |
| + sd_lldp_neighbor_get_system_capabilities; |
| + sd_lldp_neighbor_get_enabled_capabilities; |
| + sd_lldp_neighbor_tlv_rewind; |
| + sd_lldp_neighbor_tlv_next; |
| + sd_lldp_neighbor_tlv_get_type; |
| + sd_lldp_neighbor_tlv_is_type; |
| + sd_lldp_neighbor_tlv_get_oui; |
| + sd_lldp_neighbor_tlv_is_oui; |
| + sd_lldp_neighbor_tlv_get_raw; |
| + |
| } LIBSYSTEMD_241; |
| diff --git a/src/libsystemd/meson.build b/src/libsystemd/meson.build |
| index 77fe6e780f..e60853959f 100644 |
| --- a/src/libsystemd/meson.build |
| +++ b/src/libsystemd/meson.build |
| @@ -70,6 +70,12 @@ libsystemd_sources = files(''' |
| sd-hwdb/hwdb-util.c |
| sd-hwdb/hwdb-util.h |
| sd-hwdb/sd-hwdb.c |
| + sd-lldp/sd-lldp.c |
| + sd-lldp/lldp-internal.h |
| + sd-lldp/lldp-neighbor.c |
| + sd-lldp/lldp-neighbor.h |
| + sd-lldp/lldp-network.c |
| + sd-lldp/lldp-network.h |
| sd-netlink/generic-netlink.c |
| sd-netlink/netlink-internal.h |
| sd-netlink/netlink-message.c |
| diff --git a/src/libsystemd/sd-lldp/lldp-internal.h b/src/libsystemd/sd-lldp/lldp-internal.h |
| new file mode 100644 |
| index 0000000000..9598438dba |
| --- /dev/null |
| +++ b/src/libsystemd/sd-lldp/lldp-internal.h |
| @@ -0,0 +1,39 @@ |
| +/* SPDX-License-Identifier: LGPL-2.1+ */ |
| +#pragma once |
| + |
| +#include "sd-event.h" |
| +#include "sd-lldp.h" |
| + |
| +#include "hashmap.h" |
| +#include "log.h" |
| +#include "prioq.h" |
| + |
| +struct sd_lldp { |
| + unsigned n_ref; |
| + |
| + int ifindex; |
| + int fd; |
| + |
| + sd_event *event; |
| + int64_t event_priority; |
| + sd_event_source *io_event_source; |
| + sd_event_source *timer_event_source; |
| + |
| + Prioq *neighbor_by_expiry; |
| + Hashmap *neighbor_by_id; |
| + |
| + uint64_t neighbors_max; |
| + |
| + sd_lldp_callback_t callback; |
| + void *userdata; |
| + |
| + uint16_t capability_mask; |
| + |
| + struct ether_addr filter_address; |
| +}; |
| + |
| +#define log_lldp_errno(error, fmt, ...) log_internal(LOG_DEBUG, error, PROJECT_FILE, __LINE__, __func__, "LLDP: " fmt, ##__VA_ARGS__) |
| +#define log_lldp(fmt, ...) log_lldp_errno(0, fmt, ##__VA_ARGS__) |
| + |
| +const char* lldp_event_to_string(sd_lldp_event e) _const_; |
| +sd_lldp_event lldp_event_from_string(const char *s) _pure_; |
| diff --git a/src/libsystemd/sd-lldp/lldp-neighbor.c b/src/libsystemd/sd-lldp/lldp-neighbor.c |
| new file mode 100644 |
| index 0000000000..9bae4a3c6e |
| --- /dev/null |
| +++ b/src/libsystemd/sd-lldp/lldp-neighbor.c |
| @@ -0,0 +1,769 @@ |
| +/* SPDX-License-Identifier: LGPL-2.1+ */ |
| + |
| +#include "alloc-util.h" |
| +#include "escape.h" |
| +#include "ether-addr-util.h" |
| +#include "hexdecoct.h" |
| +#include "in-addr-util.h" |
| +#include "lldp-internal.h" |
| +#include "lldp-neighbor.h" |
| +#include "memory-util.h" |
| +#include "missing.h" |
| +#include "unaligned.h" |
| + |
| +static void lldp_neighbor_id_hash_func(const LLDPNeighborID *id, struct siphash *state) { |
| + siphash24_compress(id->chassis_id, id->chassis_id_size, state); |
| + siphash24_compress(&id->chassis_id_size, sizeof(id->chassis_id_size), state); |
| + siphash24_compress(id->port_id, id->port_id_size, state); |
| + siphash24_compress(&id->port_id_size, sizeof(id->port_id_size), state); |
| +} |
| + |
| +int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y) { |
| + return memcmp_nn(x->chassis_id, x->chassis_id_size, y->chassis_id, y->chassis_id_size) |
| + ?: memcmp_nn(x->port_id, x->port_id_size, y->port_id, y->port_id_size); |
| +} |
| + |
| +DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(lldp_neighbor_hash_ops, LLDPNeighborID, lldp_neighbor_id_hash_func, lldp_neighbor_id_compare_func, |
| + sd_lldp_neighbor, lldp_neighbor_unlink); |
| + |
| +int lldp_neighbor_prioq_compare_func(const void *a, const void *b) { |
| + const sd_lldp_neighbor *x = a, *y = b; |
| + |
| + return CMP(x->until, y->until); |
| +} |
| + |
| +_public_ sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n) { |
| + if (!n) |
| + return NULL; |
| + |
| + assert(n->n_ref > 0 || n->lldp); |
| + n->n_ref++; |
| + |
| + return n; |
| +} |
| + |
| +static void lldp_neighbor_free(sd_lldp_neighbor *n) { |
| + assert(n); |
| + |
| + free(n->id.port_id); |
| + free(n->id.chassis_id); |
| + free(n->port_description); |
| + free(n->system_name); |
| + free(n->system_description); |
| + free(n->chassis_id_as_string); |
| + free(n->port_id_as_string); |
| + free(n); |
| +} |
| + |
| +_public_ sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n) { |
| + |
| + /* Drops one reference from the neighbor. Note that the object is not freed unless it is already unlinked from |
| + * the sd_lldp object. */ |
| + |
| + if (!n) |
| + return NULL; |
| + |
| + assert(n->n_ref > 0); |
| + n->n_ref--; |
| + |
| + if (n->n_ref <= 0 && !n->lldp) |
| + lldp_neighbor_free(n); |
| + |
| + return NULL; |
| +} |
| + |
| +sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n) { |
| + |
| + /* Removes the neighbor object from the LLDP object, and frees it if it also has no other reference. */ |
| + |
| + if (!n) |
| + return NULL; |
| + |
| + if (!n->lldp) |
| + return NULL; |
| + |
| + /* Only remove the neighbor object from the hash table if it's in there, don't complain if it isn't. This is |
| + * because we are used as destructor call for hashmap_clear() and thus sometimes are called to de-register |
| + * ourselves from the hashtable and sometimes are called after we already are de-registered. */ |
| + |
| + (void) hashmap_remove_value(n->lldp->neighbor_by_id, &n->id, n); |
| + |
| + assert_se(prioq_remove(n->lldp->neighbor_by_expiry, n, &n->prioq_idx) >= 0); |
| + |
| + n->lldp = NULL; |
| + |
| + if (n->n_ref <= 0) |
| + lldp_neighbor_free(n); |
| + |
| + return NULL; |
| +} |
| + |
| +sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size) { |
| + sd_lldp_neighbor *n; |
| + |
| + n = malloc0(ALIGN(sizeof(sd_lldp_neighbor)) + raw_size); |
| + if (!n) |
| + return NULL; |
| + |
| + n->raw_size = raw_size; |
| + n->n_ref = 1; |
| + |
| + return n; |
| +} |
| + |
| +static int parse_string(char **s, const void *q, size_t n) { |
| + const char *p = q; |
| + char *k; |
| + |
| + assert(s); |
| + assert(p || n == 0); |
| + |
| + if (*s) { |
| + log_lldp("Found duplicate string, ignoring field."); |
| + return 0; |
| + } |
| + |
| + /* Strip trailing NULs, just to be nice */ |
| + while (n > 0 && p[n-1] == 0) |
| + n--; |
| + |
| + if (n <= 0) /* Ignore empty strings */ |
| + return 0; |
| + |
| + /* Look for inner NULs */ |
| + if (memchr(p, 0, n)) { |
| + log_lldp("Found inner NUL in string, ignoring field."); |
| + return 0; |
| + } |
| + |
| + /* Let's escape weird chars, for security reasons */ |
| + k = cescape_length(p, n); |
| + if (!k) |
| + return -ENOMEM; |
| + |
| + free(*s); |
| + *s = k; |
| + |
| + return 1; |
| +} |
| + |
| +int lldp_neighbor_parse(sd_lldp_neighbor *n) { |
| + struct ether_header h; |
| + const uint8_t *p; |
| + size_t left; |
| + int r; |
| + |
| + assert(n); |
| + |
| + if (n->raw_size < sizeof(struct ether_header)) { |
| + log_lldp("Received truncated packet, ignoring."); |
| + return -EBADMSG; |
| + } |
| + |
| + memcpy(&h, LLDP_NEIGHBOR_RAW(n), sizeof(h)); |
| + |
| + if (h.ether_type != htobe16(ETHERTYPE_LLDP)) { |
| + log_lldp("Received packet with wrong type, ignoring."); |
| + return -EBADMSG; |
| + } |
| + |
| + if (h.ether_dhost[0] != 0x01 || |
| + h.ether_dhost[1] != 0x80 || |
| + h.ether_dhost[2] != 0xc2 || |
| + h.ether_dhost[3] != 0x00 || |
| + h.ether_dhost[4] != 0x00 || |
| + !IN_SET(h.ether_dhost[5], 0x00, 0x03, 0x0e)) { |
| + log_lldp("Received packet with wrong destination address, ignoring."); |
| + return -EBADMSG; |
| + } |
| + |
| + memcpy(&n->source_address, h.ether_shost, sizeof(struct ether_addr)); |
| + memcpy(&n->destination_address, h.ether_dhost, sizeof(struct ether_addr)); |
| + |
| + p = (const uint8_t*) LLDP_NEIGHBOR_RAW(n) + sizeof(struct ether_header); |
| + left = n->raw_size - sizeof(struct ether_header); |
| + |
| + for (;;) { |
| + uint8_t type; |
| + uint16_t length; |
| + |
| + if (left < 2) { |
| + log_lldp("TLV lacks header, ignoring."); |
| + return -EBADMSG; |
| + } |
| + |
| + type = p[0] >> 1; |
| + length = p[1] + (((uint16_t) (p[0] & 1)) << 8); |
| + p += 2, left -= 2; |
| + |
| + if (left < length) { |
| + log_lldp("TLV truncated, ignoring datagram."); |
| + return -EBADMSG; |
| + } |
| + |
| + switch (type) { |
| + |
| + case SD_LLDP_TYPE_END: |
| + if (length != 0) { |
| + log_lldp("End marker TLV not zero-sized, ignoring datagram."); |
| + return -EBADMSG; |
| + } |
| + |
| + /* Note that after processing the SD_LLDP_TYPE_END left could still be > 0 |
| + * as the message may contain padding (see IEEE 802.1AB-2016, sec. 8.5.12) */ |
| + |
| + goto end_marker; |
| + |
| + case SD_LLDP_TYPE_CHASSIS_ID: |
| + if (length < 2 || length > 256) { /* includes the chassis subtype, hence one extra byte */ |
| + log_lldp("Chassis ID field size out of range, ignoring datagram."); |
| + return -EBADMSG; |
| + } |
| + if (n->id.chassis_id) { |
| + log_lldp("Duplicate chassis ID field, ignoring datagram."); |
| + return -EBADMSG; |
| + } |
| + |
| + n->id.chassis_id = memdup(p, length); |
| + if (!n->id.chassis_id) |
| + return -ENOMEM; |
| + |
| + n->id.chassis_id_size = length; |
| + break; |
| + |
| + case SD_LLDP_TYPE_PORT_ID: |
| + if (length < 2 || length > 256) { /* includes the port subtype, hence one extra byte */ |
| + log_lldp("Port ID field size out of range, ignoring datagram."); |
| + return -EBADMSG; |
| + } |
| + if (n->id.port_id) { |
| + log_lldp("Duplicate port ID field, ignoring datagram."); |
| + return -EBADMSG; |
| + } |
| + |
| + n->id.port_id = memdup(p, length); |
| + if (!n->id.port_id) |
| + return -ENOMEM; |
| + |
| + n->id.port_id_size = length; |
| + break; |
| + |
| + case SD_LLDP_TYPE_TTL: |
| + if (length != 2) { |
| + log_lldp("TTL field has wrong size, ignoring datagram."); |
| + return -EBADMSG; |
| + } |
| + |
| + if (n->has_ttl) { |
| + log_lldp("Duplicate TTL field, ignoring datagram."); |
| + return -EBADMSG; |
| + } |
| + |
| + n->ttl = unaligned_read_be16(p); |
| + n->has_ttl = true; |
| + break; |
| + |
| + case SD_LLDP_TYPE_PORT_DESCRIPTION: |
| + r = parse_string(&n->port_description, p, length); |
| + if (r < 0) |
| + return r; |
| + break; |
| + |
| + case SD_LLDP_TYPE_SYSTEM_NAME: |
| + r = parse_string(&n->system_name, p, length); |
| + if (r < 0) |
| + return r; |
| + break; |
| + |
| + case SD_LLDP_TYPE_SYSTEM_DESCRIPTION: |
| + r = parse_string(&n->system_description, p, length); |
| + if (r < 0) |
| + return r; |
| + break; |
| + |
| + case SD_LLDP_TYPE_SYSTEM_CAPABILITIES: |
| + if (length != 4) |
| + log_lldp("System capabilities field has wrong size, ignoring."); |
| + else { |
| + n->system_capabilities = unaligned_read_be16(p); |
| + n->enabled_capabilities = unaligned_read_be16(p + 2); |
| + n->has_capabilities = true; |
| + } |
| + |
| + break; |
| + |
| + case SD_LLDP_TYPE_PRIVATE: |
| + if (length < 4) |
| + log_lldp("Found private TLV that is too short, ignoring."); |
| + |
| + break; |
| + } |
| + |
| + p += length, left -= length; |
| + } |
| + |
| +end_marker: |
| + if (!n->id.chassis_id || !n->id.port_id || !n->has_ttl) { |
| + log_lldp("One or more mandatory TLV missing in datagram. Ignoring."); |
| + return -EBADMSG; |
| + |
| + } |
| + |
| + n->rindex = sizeof(struct ether_header); |
| + |
| + return 0; |
| +} |
| + |
| +void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) { |
| + assert(n); |
| + |
| + if (n->ttl > 0) { |
| + usec_t base; |
| + |
| + /* Use the packet's timestamp if there is one known */ |
| + base = triple_timestamp_by_clock(&n->timestamp, clock_boottime_or_monotonic()); |
| + if (base <= 0 || base == USEC_INFINITY) |
| + base = now(clock_boottime_or_monotonic()); /* Otherwise, take the current time */ |
| + |
| + n->until = usec_add(base, n->ttl * USEC_PER_SEC); |
| + } else |
| + n->until = 0; |
| + |
| + if (n->lldp) |
| + prioq_reshuffle(n->lldp->neighbor_by_expiry, n, &n->prioq_idx); |
| +} |
| + |
| +bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b) { |
| + if (a == b) |
| + return true; |
| + |
| + if (!a || !b) |
| + return false; |
| + |
| + if (a->raw_size != b->raw_size) |
| + return false; |
| + |
| + return memcmp(LLDP_NEIGHBOR_RAW(a), LLDP_NEIGHBOR_RAW(b), a->raw_size) == 0; |
| +} |
| + |
| +_public_ int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address) { |
| + assert_return(n, -EINVAL); |
| + assert_return(address, -EINVAL); |
| + |
| + *address = n->source_address; |
| + return 0; |
| +} |
| + |
| +_public_ int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address) { |
| + assert_return(n, -EINVAL); |
| + assert_return(address, -EINVAL); |
| + |
| + *address = n->destination_address; |
| + return 0; |
| +} |
| + |
| +_public_ int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) { |
| + assert_return(n, -EINVAL); |
| + assert_return(ret, -EINVAL); |
| + assert_return(size, -EINVAL); |
| + |
| + *ret = LLDP_NEIGHBOR_RAW(n); |
| + *size = n->raw_size; |
| + |
| + return 0; |
| +} |
| + |
| +_public_ int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) { |
| + assert_return(n, -EINVAL); |
| + assert_return(type, -EINVAL); |
| + assert_return(ret, -EINVAL); |
| + assert_return(size, -EINVAL); |
| + |
| + assert(n->id.chassis_id_size > 0); |
| + |
| + *type = *(uint8_t*) n->id.chassis_id; |
| + *ret = (uint8_t*) n->id.chassis_id + 1; |
| + *size = n->id.chassis_id_size - 1; |
| + |
| + return 0; |
| +} |
| + |
| +static int format_mac_address(const void *data, size_t sz, char **ret) { |
| + struct ether_addr a; |
| + char *k; |
| + |
| + assert(data || sz <= 0); |
| + |
| + if (sz != 7) |
| + return 0; |
| + |
| + memcpy(&a, (uint8_t*) data + 1, sizeof(a)); |
| + |
| + k = new(char, ETHER_ADDR_TO_STRING_MAX); |
| + if (!k) |
| + return -ENOMEM; |
| + |
| + *ret = ether_addr_to_string(&a, k); |
| + return 1; |
| +} |
| + |
| +static int format_network_address(const void *data, size_t sz, char **ret) { |
| + union in_addr_union a; |
| + int family, r; |
| + |
| + if (sz == 6 && ((uint8_t*) data)[1] == 1) { |
| + memcpy(&a.in, (uint8_t*) data + 2, sizeof(a.in)); |
| + family = AF_INET; |
| + } else if (sz == 18 && ((uint8_t*) data)[1] == 2) { |
| + memcpy(&a.in6, (uint8_t*) data + 2, sizeof(a.in6)); |
| + family = AF_INET6; |
| + } else |
| + return 0; |
| + |
| + r = in_addr_to_string(family, &a, ret); |
| + if (r < 0) |
| + return r; |
| + return 1; |
| +} |
| + |
| +_public_ int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret) { |
| + char *k; |
| + int r; |
| + |
| + assert_return(n, -EINVAL); |
| + assert_return(ret, -EINVAL); |
| + |
| + if (n->chassis_id_as_string) { |
| + *ret = n->chassis_id_as_string; |
| + return 0; |
| + } |
| + |
| + assert(n->id.chassis_id_size > 0); |
| + |
| + switch (*(uint8_t*) n->id.chassis_id) { |
| + |
| + case SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT: |
| + case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS: |
| + case SD_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT: |
| + case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME: |
| + case SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED: |
| + k = cescape_length((char*) n->id.chassis_id + 1, n->id.chassis_id_size - 1); |
| + if (!k) |
| + return -ENOMEM; |
| + |
| + goto done; |
| + |
| + case SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS: |
| + r = format_mac_address(n->id.chassis_id, n->id.chassis_id_size, &k); |
| + if (r < 0) |
| + return r; |
| + if (r > 0) |
| + goto done; |
| + |
| + break; |
| + |
| + case SD_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS: |
| + r = format_network_address(n->id.chassis_id, n->id.chassis_id_size, &k); |
| + if (r < 0) |
| + return r; |
| + if (r > 0) |
| + goto done; |
| + |
| + break; |
| + } |
| + |
| + /* Generic fallback */ |
| + k = hexmem(n->id.chassis_id, n->id.chassis_id_size); |
| + if (!k) |
| + return -ENOMEM; |
| + |
| +done: |
| + *ret = n->chassis_id_as_string = k; |
| + return 0; |
| +} |
| + |
| +_public_ int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) { |
| + assert_return(n, -EINVAL); |
| + assert_return(type, -EINVAL); |
| + assert_return(ret, -EINVAL); |
| + assert_return(size, -EINVAL); |
| + |
| + assert(n->id.port_id_size > 0); |
| + |
| + *type = *(uint8_t*) n->id.port_id; |
| + *ret = (uint8_t*) n->id.port_id + 1; |
| + *size = n->id.port_id_size - 1; |
| + |
| + return 0; |
| +} |
| + |
| +_public_ int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret) { |
| + char *k; |
| + int r; |
| + |
| + assert_return(n, -EINVAL); |
| + assert_return(ret, -EINVAL); |
| + |
| + if (n->port_id_as_string) { |
| + *ret = n->port_id_as_string; |
| + return 0; |
| + } |
| + |
| + assert(n->id.port_id_size > 0); |
| + |
| + switch (*(uint8_t*) n->id.port_id) { |
| + |
| + case SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS: |
| + case SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT: |
| + case SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME: |
| + case SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED: |
| + k = cescape_length((char*) n->id.port_id + 1, n->id.port_id_size - 1); |
| + if (!k) |
| + return -ENOMEM; |
| + |
| + goto done; |
| + |
| + case SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS: |
| + r = format_mac_address(n->id.port_id, n->id.port_id_size, &k); |
| + if (r < 0) |
| + return r; |
| + if (r > 0) |
| + goto done; |
| + |
| + break; |
| + |
| + case SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS: |
| + r = format_network_address(n->id.port_id, n->id.port_id_size, &k); |
| + if (r < 0) |
| + return r; |
| + if (r > 0) |
| + goto done; |
| + |
| + break; |
| + } |
| + |
| + /* Generic fallback */ |
| + k = hexmem(n->id.port_id, n->id.port_id_size); |
| + if (!k) |
| + return -ENOMEM; |
| + |
| +done: |
| + *ret = n->port_id_as_string = k; |
| + return 0; |
| +} |
| + |
| +_public_ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec) { |
| + assert_return(n, -EINVAL); |
| + assert_return(ret_sec, -EINVAL); |
| + |
| + *ret_sec = n->ttl; |
| + return 0; |
| +} |
| + |
| +_public_ int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret) { |
| + assert_return(n, -EINVAL); |
| + assert_return(ret, -EINVAL); |
| + |
| + if (!n->system_name) |
| + return -ENODATA; |
| + |
| + *ret = n->system_name; |
| + return 0; |
| +} |
| + |
| +_public_ int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret) { |
| + assert_return(n, -EINVAL); |
| + assert_return(ret, -EINVAL); |
| + |
| + if (!n->system_description) |
| + return -ENODATA; |
| + |
| + *ret = n->system_description; |
| + return 0; |
| +} |
| + |
| +_public_ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret) { |
| + assert_return(n, -EINVAL); |
| + assert_return(ret, -EINVAL); |
| + |
| + if (!n->port_description) |
| + return -ENODATA; |
| + |
| + *ret = n->port_description; |
| + return 0; |
| +} |
| + |
| +_public_ int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) { |
| + assert_return(n, -EINVAL); |
| + assert_return(ret, -EINVAL); |
| + |
| + if (!n->has_capabilities) |
| + return -ENODATA; |
| + |
| + *ret = n->system_capabilities; |
| + return 0; |
| +} |
| + |
| +_public_ int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret) { |
| + assert_return(n, -EINVAL); |
| + assert_return(ret, -EINVAL); |
| + |
| + if (!n->has_capabilities) |
| + return -ENODATA; |
| + |
| + *ret = n->enabled_capabilities; |
| + return 0; |
| +} |
| + |
| +_public_ int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) { |
| + _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL; |
| + int r; |
| + |
| + assert_return(ret, -EINVAL); |
| + assert_return(raw || raw_size <= 0, -EINVAL); |
| + |
| + n = lldp_neighbor_new(raw_size); |
| + if (!n) |
| + return -ENOMEM; |
| + |
| + memcpy(LLDP_NEIGHBOR_RAW(n), raw, raw_size); |
| + r = lldp_neighbor_parse(n); |
| + if (r < 0) |
| + return r; |
| + |
| + *ret = TAKE_PTR(n); |
| + |
| + return r; |
| +} |
| + |
| +_public_ int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) { |
| + assert_return(n, -EINVAL); |
| + |
| + assert(n->raw_size >= sizeof(struct ether_header)); |
| + n->rindex = sizeof(struct ether_header); |
| + |
| + return n->rindex < n->raw_size; |
| +} |
| + |
| +_public_ int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) { |
| + size_t length; |
| + |
| + assert_return(n, -EINVAL); |
| + |
| + if (n->rindex == n->raw_size) /* EOF */ |
| + return -ESPIPE; |
| + |
| + if (n->rindex + 2 > n->raw_size) /* Truncated message */ |
| + return -EBADMSG; |
| + |
| + length = LLDP_NEIGHBOR_TLV_LENGTH(n); |
| + if (n->rindex + 2 + length > n->raw_size) |
| + return -EBADMSG; |
| + |
| + n->rindex += 2 + length; |
| + return n->rindex < n->raw_size; |
| +} |
| + |
| +_public_ int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) { |
| + assert_return(n, -EINVAL); |
| + assert_return(type, -EINVAL); |
| + |
| + if (n->rindex == n->raw_size) /* EOF */ |
| + return -ESPIPE; |
| + |
| + if (n->rindex + 2 > n->raw_size) |
| + return -EBADMSG; |
| + |
| + *type = LLDP_NEIGHBOR_TLV_TYPE(n); |
| + return 0; |
| +} |
| + |
| +_public_ int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) { |
| + uint8_t k; |
| + int r; |
| + |
| + assert_return(n, -EINVAL); |
| + |
| + r = sd_lldp_neighbor_tlv_get_type(n, &k); |
| + if (r < 0) |
| + return r; |
| + |
| + return type == k; |
| +} |
| + |
| +_public_ int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t *subtype) { |
| + const uint8_t *d; |
| + size_t length; |
| + int r; |
| + |
| + assert_return(n, -EINVAL); |
| + assert_return(oui, -EINVAL); |
| + assert_return(subtype, -EINVAL); |
| + |
| + r = sd_lldp_neighbor_tlv_is_type(n, SD_LLDP_TYPE_PRIVATE); |
| + if (r < 0) |
| + return r; |
| + if (r == 0) |
| + return -ENXIO; |
| + |
| + length = LLDP_NEIGHBOR_TLV_LENGTH(n); |
| + if (length < 4) |
| + return -EBADMSG; |
| + |
| + if (n->rindex + 2 + length > n->raw_size) |
| + return -EBADMSG; |
| + |
| + d = LLDP_NEIGHBOR_TLV_DATA(n); |
| + memcpy(oui, d, 3); |
| + *subtype = d[3]; |
| + |
| + return 0; |
| +} |
| + |
| +_public_ int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t subtype) { |
| + uint8_t k[3], st; |
| + int r; |
| + |
| + r = sd_lldp_neighbor_tlv_get_oui(n, k, &st); |
| + if (r == -ENXIO) |
| + return 0; |
| + if (r < 0) |
| + return r; |
| + |
| + return memcmp(k, oui, 3) == 0 && st == subtype; |
| +} |
| + |
| +_public_ int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) { |
| + size_t length; |
| + |
| + assert_return(n, -EINVAL); |
| + assert_return(ret, -EINVAL); |
| + assert_return(size, -EINVAL); |
| + |
| + /* Note that this returns the full TLV, including the TLV header */ |
| + |
| + if (n->rindex + 2 > n->raw_size) |
| + return -EBADMSG; |
| + |
| + length = LLDP_NEIGHBOR_TLV_LENGTH(n); |
| + if (n->rindex + 2 + length > n->raw_size) |
| + return -EBADMSG; |
| + |
| + *ret = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex; |
| + *size = length + 2; |
| + |
| + return 0; |
| +} |
| + |
| +_public_ int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret) { |
| + assert_return(n, -EINVAL); |
| + assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP); |
| + assert_return(clock_supported(clock), -EOPNOTSUPP); |
| + assert_return(ret, -EINVAL); |
| + |
| + if (!triple_timestamp_is_set(&n->timestamp)) |
| + return -ENODATA; |
| + |
| + *ret = triple_timestamp_by_clock(&n->timestamp, clock); |
| + return 0; |
| +} |
| diff --git a/src/libsystemd/sd-lldp/lldp-neighbor.h b/src/libsystemd/sd-lldp/lldp-neighbor.h |
| new file mode 100644 |
| index 0000000000..62dbff42ca |
| --- /dev/null |
| +++ b/src/libsystemd/sd-lldp/lldp-neighbor.h |
| @@ -0,0 +1,91 @@ |
| +/* SPDX-License-Identifier: LGPL-2.1+ */ |
| +#pragma once |
| + |
| +#include <inttypes.h> |
| +#include <stdbool.h> |
| +#include <sys/types.h> |
| + |
| +#include "sd-lldp.h" |
| + |
| +#include "hash-funcs.h" |
| +#include "lldp-internal.h" |
| +#include "time-util.h" |
| + |
| +typedef struct LLDPNeighborID { |
| + /* The spec calls this an "MSAP identifier" */ |
| + void *chassis_id; |
| + size_t chassis_id_size; |
| + |
| + void *port_id; |
| + size_t port_id_size; |
| +} LLDPNeighborID; |
| + |
| +struct sd_lldp_neighbor { |
| + /* Neighbor objects stay around as long as they are linked into an "sd_lldp" object or n_ref > 0. */ |
| + sd_lldp *lldp; |
| + unsigned n_ref; |
| + |
| + triple_timestamp timestamp; |
| + |
| + usec_t until; |
| + unsigned prioq_idx; |
| + |
| + struct ether_addr source_address; |
| + struct ether_addr destination_address; |
| + |
| + LLDPNeighborID id; |
| + |
| + /* The raw packet size. The data is appended to the object, accessible via LLDP_NEIGHBOR_RAW() */ |
| + size_t raw_size; |
| + |
| + /* The current read index for the iterative TLV interface */ |
| + size_t rindex; |
| + |
| + /* And a couple of fields parsed out. */ |
| + bool has_ttl:1; |
| + bool has_capabilities:1; |
| + bool has_port_vlan_id:1; |
| + |
| + uint16_t ttl; |
| + |
| + uint16_t system_capabilities; |
| + uint16_t enabled_capabilities; |
| + |
| + char *port_description; |
| + char *system_name; |
| + char *system_description; |
| + |
| + uint16_t port_vlan_id; |
| + |
| + char *chassis_id_as_string; |
| + char *port_id_as_string; |
| +}; |
| + |
| +static inline void *LLDP_NEIGHBOR_RAW(const sd_lldp_neighbor *n) { |
| + return (uint8_t*) n + ALIGN(sizeof(sd_lldp_neighbor)); |
| +} |
| + |
| +static inline uint8_t LLDP_NEIGHBOR_TLV_TYPE(const sd_lldp_neighbor *n) { |
| + return ((uint8_t*) LLDP_NEIGHBOR_RAW(n))[n->rindex] >> 1; |
| +} |
| + |
| +static inline size_t LLDP_NEIGHBOR_TLV_LENGTH(const sd_lldp_neighbor *n) { |
| + uint8_t *p; |
| + |
| + p = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex; |
| + return p[1] + (((size_t) (p[0] & 1)) << 8); |
| +} |
| + |
| +static inline void* LLDP_NEIGHBOR_TLV_DATA(const sd_lldp_neighbor *n) { |
| + return ((uint8_t*) LLDP_NEIGHBOR_RAW(n)) + n->rindex + 2; |
| +} |
| + |
| +extern const struct hash_ops lldp_neighbor_hash_ops; |
| +int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y); |
| +int lldp_neighbor_prioq_compare_func(const void *a, const void *b); |
| + |
| +sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n); |
| +sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size); |
| +int lldp_neighbor_parse(sd_lldp_neighbor *n); |
| +void lldp_neighbor_start_ttl(sd_lldp_neighbor *n); |
| +bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b); |
| diff --git a/src/libsystemd/sd-lldp/lldp-network.c b/src/libsystemd/sd-lldp/lldp-network.c |
| new file mode 100644 |
| index 0000000000..870584c0db |
| --- /dev/null |
| +++ b/src/libsystemd/sd-lldp/lldp-network.c |
| @@ -0,0 +1,78 @@ |
| +/* SPDX-License-Identifier: LGPL-2.1+ */ |
| + |
| +#include <linux/filter.h> |
| +#include <netinet/if_ether.h> |
| + |
| +#include "fd-util.h" |
| +#include "lldp-network.h" |
| +#include "missing.h" |
| +#include "socket-util.h" |
| + |
| +int lldp_network_bind_raw_socket(int ifindex) { |
| + |
| + static const struct sock_filter filter[] = { |
| + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ethhdr, h_dest)), /* A <- 4 bytes of destination MAC */ |
| + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0180c200, 1, 0), /* A != 01:80:c2:00 */ |
| + BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */ |
| + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_dest) + 4), /* A <- remaining 2 bytes of destination MAC */ |
| + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0000, 3, 0), /* A != 00:00 */ |
| + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0003, 2, 0), /* A != 00:03 */ |
| + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x000e, 1, 0), /* A != 00:0e */ |
| + BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */ |
| + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_proto)), /* A <- protocol */ |
| + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_LLDP, 1, 0), /* A != ETHERTYPE_LLDP */ |
| + BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */ |
| + BPF_STMT(BPF_RET + BPF_K, (uint32_t) -1), /* accept packet */ |
| + }; |
| + |
| + static const struct sock_fprog fprog = { |
| + .len = ELEMENTSOF(filter), |
| + .filter = (struct sock_filter*) filter, |
| + }; |
| + |
| + struct packet_mreq mreq = { |
| + .mr_ifindex = ifindex, |
| + .mr_type = PACKET_MR_MULTICAST, |
| + .mr_alen = ETH_ALEN, |
| + .mr_address = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x00 } |
| + }; |
| + |
| + union sockaddr_union saddrll = { |
| + .ll.sll_family = AF_PACKET, |
| + .ll.sll_ifindex = ifindex, |
| + }; |
| + |
| + _cleanup_close_ int fd = -1; |
| + int r; |
| + |
| + assert(ifindex > 0); |
| + |
| + fd = socket(PF_PACKET, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, |
| + htobe16(ETHERTYPE_LLDP)); |
| + if (fd < 0) |
| + return -errno; |
| + |
| + r = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); |
| + if (r < 0) |
| + return -errno; |
| + |
| + r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); |
| + if (r < 0) |
| + return -errno; |
| + |
| + mreq.mr_address[ETH_ALEN - 1] = 0x03; |
| + r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); |
| + if (r < 0) |
| + return -errno; |
| + |
| + mreq.mr_address[ETH_ALEN - 1] = 0x0E; |
| + r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); |
| + if (r < 0) |
| + return -errno; |
| + |
| + r = bind(fd, &saddrll.sa, sizeof(saddrll.ll)); |
| + if (r < 0) |
| + return -errno; |
| + |
| + return TAKE_FD(fd); |
| +} |
| diff --git a/src/libsystemd/sd-lldp/lldp-network.h b/src/libsystemd/sd-lldp/lldp-network.h |
| new file mode 100644 |
| index 0000000000..e4ed2898a5 |
| --- /dev/null |
| +++ b/src/libsystemd/sd-lldp/lldp-network.h |
| @@ -0,0 +1,6 @@ |
| +/* SPDX-License-Identifier: LGPL-2.1+ */ |
| +#pragma once |
| + |
| +#include "sd-event.h" |
| + |
| +int lldp_network_bind_raw_socket(int ifindex); |
| diff --git a/src/libsystemd/sd-lldp/sd-lldp.c b/src/libsystemd/sd-lldp/sd-lldp.c |
| new file mode 100644 |
| index 0000000000..1f28c5731f |
| --- /dev/null |
| +++ b/src/libsystemd/sd-lldp/sd-lldp.c |
| @@ -0,0 +1,498 @@ |
| +/* SPDX-License-Identifier: LGPL-2.1+ */ |
| + |
| +#include <arpa/inet.h> |
| +#include <linux/sockios.h> |
| +#include <sys/ioctl.h> |
| + |
| +#include "sd-lldp.h" |
| + |
| +#include "alloc-util.h" |
| +#include "ether-addr-util.h" |
| +#include "event-util.h" |
| +#include "fd-util.h" |
| +#include "lldp-internal.h" |
| +#include "lldp-neighbor.h" |
| +#include "lldp-network.h" |
| +#include "memory-util.h" |
| +#include "socket-util.h" |
| +#include "sort-util.h" |
| +#include "string-table.h" |
| + |
| +#define LLDP_DEFAULT_NEIGHBORS_MAX 128U |
| + |
| +static const char * const lldp_event_table[_SD_LLDP_EVENT_MAX] = { |
| + [SD_LLDP_EVENT_ADDED] = "added", |
| + [SD_LLDP_EVENT_REMOVED] = "removed", |
| + [SD_LLDP_EVENT_UPDATED] = "updated", |
| + [SD_LLDP_EVENT_REFRESHED] = "refreshed", |
| +}; |
| + |
| +DEFINE_STRING_TABLE_LOOKUP(lldp_event, sd_lldp_event); |
| + |
| +static void lldp_flush_neighbors(sd_lldp *lldp) { |
| + assert(lldp); |
| + |
| + hashmap_clear(lldp->neighbor_by_id); |
| +} |
| + |
| +static void lldp_callback(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n) { |
| + assert(lldp); |
| + assert(event >= 0 && event < _SD_LLDP_EVENT_MAX); |
| + |
| + if (!lldp->callback) { |
| + log_lldp("Received '%s' event.", lldp_event_to_string(event)); |
| + return; |
| + } |
| + |
| + log_lldp("Invoking callback for '%s' event.", lldp_event_to_string(event)); |
| + lldp->callback(lldp, event, n, lldp->userdata); |
| +} |
| + |
| +static int lldp_make_space(sd_lldp *lldp, size_t extra) { |
| + usec_t t = USEC_INFINITY; |
| + bool changed = false; |
| + |
| + assert(lldp); |
| + |
| + /* Remove all entries that are past their TTL, and more until at least the specified number of extra entries |
| + * are free. */ |
| + |
| + for (;;) { |
| + _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL; |
| + |
| + n = prioq_peek(lldp->neighbor_by_expiry); |
| + if (!n) |
| + break; |
| + |
| + sd_lldp_neighbor_ref(n); |
| + |
| + if (hashmap_size(lldp->neighbor_by_id) > LESS_BY(lldp->neighbors_max, extra)) |
| + goto remove_one; |
| + |
| + if (t == USEC_INFINITY) |
| + t = now(clock_boottime_or_monotonic()); |
| + |
| + if (n->until > t) |
| + break; |
| + |
| + remove_one: |
| + lldp_neighbor_unlink(n); |
| + lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, n); |
| + changed = true; |
| + } |
| + |
| + return changed; |
| +} |
| + |
| +static bool lldp_keep_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) { |
| + assert(lldp); |
| + assert(n); |
| + |
| + /* Don't keep data with a zero TTL */ |
| + if (n->ttl <= 0) |
| + return false; |
| + |
| + /* Filter out data from the filter address */ |
| + if (!ether_addr_is_null(&lldp->filter_address) && |
| + ether_addr_equal(&lldp->filter_address, &n->source_address)) |
| + return false; |
| + |
| + /* Only add if the neighbor has a capability we are interested in. Note that we also store all neighbors with |
| + * no caps field set. */ |
| + if (n->has_capabilities && |
| + (n->enabled_capabilities & lldp->capability_mask) == 0) |
| + return false; |
| + |
| + /* Keep everything else */ |
| + return true; |
| +} |
| + |
| +static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor); |
| + |
| +static int lldp_add_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) { |
| + _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *old = NULL; |
| + bool keep; |
| + int r; |
| + |
| + assert(lldp); |
| + assert(n); |
| + assert(!n->lldp); |
| + |
| + keep = lldp_keep_neighbor(lldp, n); |
| + |
| + /* First retrieve the old entry for this MSAP */ |
| + old = hashmap_get(lldp->neighbor_by_id, &n->id); |
| + if (old) { |
| + sd_lldp_neighbor_ref(old); |
| + |
| + if (!keep) { |
| + lldp_neighbor_unlink(old); |
| + lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old); |
| + return 0; |
| + } |
| + |
| + if (lldp_neighbor_equal(n, old)) { |
| + /* Is this equal, then restart the TTL counter, but don't do anything else. */ |
| + old->timestamp = n->timestamp; |
| + lldp_start_timer(lldp, old); |
| + lldp_callback(lldp, SD_LLDP_EVENT_REFRESHED, old); |
| + return 0; |
| + } |
| + |
| + /* Data changed, remove the old entry, and add a new one */ |
| + lldp_neighbor_unlink(old); |
| + |
| + } else if (!keep) |
| + return 0; |
| + |
| + /* Then, make room for at least one new neighbor */ |
| + lldp_make_space(lldp, 1); |
| + |
| + r = hashmap_put(lldp->neighbor_by_id, &n->id, n); |
| + if (r < 0) |
| + goto finish; |
| + |
| + r = prioq_put(lldp->neighbor_by_expiry, n, &n->prioq_idx); |
| + if (r < 0) { |
| + assert_se(hashmap_remove(lldp->neighbor_by_id, &n->id) == n); |
| + goto finish; |
| + } |
| + |
| + n->lldp = lldp; |
| + |
| + lldp_start_timer(lldp, n); |
| + lldp_callback(lldp, old ? SD_LLDP_EVENT_UPDATED : SD_LLDP_EVENT_ADDED, n); |
| + |
| + return 1; |
| + |
| +finish: |
| + if (old) |
| + lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old); |
| + |
| + return r; |
| +} |
| + |
| +static int lldp_handle_datagram(sd_lldp *lldp, sd_lldp_neighbor *n) { |
| + int r; |
| + |
| + assert(lldp); |
| + assert(n); |
| + |
| + r = lldp_neighbor_parse(n); |
| + if (r == -EBADMSG) /* Ignore bad messages */ |
| + return 0; |
| + if (r < 0) |
| + return r; |
| + |
| + r = lldp_add_neighbor(lldp, n); |
| + if (r < 0) { |
| + log_lldp_errno(r, "Failed to add datagram. Ignoring."); |
| + return 0; |
| + } |
| + |
| + log_lldp("Successfully processed LLDP datagram."); |
| + return 0; |
| +} |
| + |
| +static int lldp_receive_datagram(sd_event_source *s, int fd, uint32_t revents, void *userdata) { |
| + _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL; |
| + ssize_t space, length; |
| + sd_lldp *lldp = userdata; |
| + struct timespec ts; |
| + |
| + assert(fd >= 0); |
| + assert(lldp); |
| + |
| + space = next_datagram_size_fd(fd); |
| + if (space < 0) |
| + return log_lldp_errno(space, "Failed to determine datagram size to read: %m"); |
| + |
| + n = lldp_neighbor_new(space); |
| + if (!n) |
| + return -ENOMEM; |
| + |
| + length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT); |
| + if (length < 0) { |
| + if (IN_SET(errno, EAGAIN, EINTR)) |
| + return 0; |
| + |
| + return log_lldp_errno(errno, "Failed to read LLDP datagram: %m"); |
| + } |
| + |
| + if ((size_t) length != n->raw_size) { |
| + log_lldp("Packet size mismatch."); |
| + return -EINVAL; |
| + } |
| + |
| + /* Try to get the timestamp of this packet if it is known */ |
| + if (ioctl(fd, SIOCGSTAMPNS, &ts) >= 0) |
| + triple_timestamp_from_realtime(&n->timestamp, timespec_load(&ts)); |
| + else |
| + triple_timestamp_get(&n->timestamp); |
| + |
| + return lldp_handle_datagram(lldp, n); |
| +} |
| + |
| +static void lldp_reset(sd_lldp *lldp) { |
| + assert(lldp); |
| + |
| + (void) event_source_disable(lldp->timer_event_source); |
| + lldp->io_event_source = sd_event_source_unref(lldp->io_event_source); |
| + lldp->fd = safe_close(lldp->fd); |
| +} |
| + |
| +_public_ int sd_lldp_start(sd_lldp *lldp) { |
| + int r; |
| + |
| + assert_return(lldp, -EINVAL); |
| + assert_return(lldp->event, -EINVAL); |
| + assert_return(lldp->ifindex > 0, -EINVAL); |
| + |
| + if (lldp->fd >= 0) |
| + return 0; |
| + |
| + assert(!lldp->io_event_source); |
| + |
| + lldp->fd = lldp_network_bind_raw_socket(lldp->ifindex); |
| + if (lldp->fd < 0) |
| + return lldp->fd; |
| + |
| + r = sd_event_add_io(lldp->event, &lldp->io_event_source, lldp->fd, EPOLLIN, lldp_receive_datagram, lldp); |
| + if (r < 0) |
| + goto fail; |
| + |
| + r = sd_event_source_set_priority(lldp->io_event_source, lldp->event_priority); |
| + if (r < 0) |
| + goto fail; |
| + |
| + (void) sd_event_source_set_description(lldp->io_event_source, "lldp-io"); |
| + |
| + log_lldp("Started LLDP client"); |
| + return 1; |
| + |
| +fail: |
| + lldp_reset(lldp); |
| + return r; |
| +} |
| + |
| +_public_ int sd_lldp_stop(sd_lldp *lldp) { |
| + assert_return(lldp, -EINVAL); |
| + |
| + if (lldp->fd < 0) |
| + return 0; |
| + |
| + log_lldp("Stopping LLDP client"); |
| + |
| + lldp_reset(lldp); |
| + lldp_flush_neighbors(lldp); |
| + |
| + return 1; |
| +} |
| + |
| +_public_ int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int64_t priority) { |
| + int r; |
| + |
| + assert_return(lldp, -EINVAL); |
| + assert_return(lldp->fd < 0, -EBUSY); |
| + assert_return(!lldp->event, -EBUSY); |
| + |
| + if (event) |
| + lldp->event = sd_event_ref(event); |
| + else { |
| + r = sd_event_default(&lldp->event); |
| + if (r < 0) |
| + return r; |
| + } |
| + |
| + lldp->event_priority = priority; |
| + |
| + return 0; |
| +} |
| + |
| +_public_ int sd_lldp_detach_event(sd_lldp *lldp) { |
| + |
| + assert_return(lldp, -EINVAL); |
| + assert_return(lldp->fd < 0, -EBUSY); |
| + |
| + lldp->event = sd_event_unref(lldp->event); |
| + return 0; |
| +} |
| + |
| +_public_ sd_event* sd_lldp_get_event(sd_lldp *lldp) { |
| + assert_return(lldp, NULL); |
| + |
| + return lldp->event; |
| +} |
| + |
| +_public_ int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata) { |
| + assert_return(lldp, -EINVAL); |
| + |
| + lldp->callback = cb; |
| + lldp->userdata = userdata; |
| + |
| + return 0; |
| +} |
| + |
| +_public_ int sd_lldp_set_ifindex(sd_lldp *lldp, int ifindex) { |
| + assert_return(lldp, -EINVAL); |
| + assert_return(ifindex > 0, -EINVAL); |
| + assert_return(lldp->fd < 0, -EBUSY); |
| + |
| + lldp->ifindex = ifindex; |
| + return 0; |
| +} |
| + |
| +static sd_lldp* lldp_free(sd_lldp *lldp) { |
| + assert(lldp); |
| + |
| + lldp->timer_event_source = sd_event_source_unref(lldp->timer_event_source); |
| + |
| + lldp_reset(lldp); |
| + sd_lldp_detach_event(lldp); |
| + lldp_flush_neighbors(lldp); |
| + |
| + hashmap_free(lldp->neighbor_by_id); |
| + prioq_free(lldp->neighbor_by_expiry); |
| + return mfree(lldp); |
| +} |
| + |
| +DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_lldp, sd_lldp, lldp_free); |
| + |
| +_public_ int sd_lldp_new(sd_lldp **ret) { |
| + _cleanup_(sd_lldp_unrefp) sd_lldp *lldp = NULL; |
| + int r; |
| + |
| + assert_return(ret, -EINVAL); |
| + |
| + lldp = new(sd_lldp, 1); |
| + if (!lldp) |
| + return -ENOMEM; |
| + |
| + *lldp = (sd_lldp) { |
| + .n_ref = 1, |
| + .fd = -1, |
| + .neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX, |
| + .capability_mask = (uint16_t) -1, |
| + }; |
| + |
| + lldp->neighbor_by_id = hashmap_new(&lldp_neighbor_hash_ops); |
| + if (!lldp->neighbor_by_id) |
| + return -ENOMEM; |
| + |
| + r = prioq_ensure_allocated(&lldp->neighbor_by_expiry, lldp_neighbor_prioq_compare_func); |
| + if (r < 0) |
| + return r; |
| + |
| + *ret = TAKE_PTR(lldp); |
| + |
| + return 0; |
| +} |
| + |
| +static int neighbor_compare_func(sd_lldp_neighbor * const *a, sd_lldp_neighbor * const *b) { |
| + return lldp_neighbor_id_compare_func(&(*a)->id, &(*b)->id); |
| +} |
| + |
| +static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) { |
| + sd_lldp *lldp = userdata; |
| + int r; |
| + |
| + r = lldp_make_space(lldp, 0); |
| + if (r < 0) |
| + return log_lldp_errno(r, "Failed to make space: %m"); |
| + |
| + r = lldp_start_timer(lldp, NULL); |
| + if (r < 0) |
| + return log_lldp_errno(r, "Failed to restart timer: %m"); |
| + |
| + return 0; |
| +} |
| + |
| +static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor) { |
| + sd_lldp_neighbor *n; |
| + |
| + assert(lldp); |
| + |
| + if (neighbor) |
| + lldp_neighbor_start_ttl(neighbor); |
| + |
| + n = prioq_peek(lldp->neighbor_by_expiry); |
| + if (!n) |
| + return event_source_disable(lldp->timer_event_source); |
| + |
| + if (!lldp->event) |
| + return 0; |
| + |
| + return event_reset_time(lldp->event, &lldp->timer_event_source, |
| + clock_boottime_or_monotonic(), |
| + n->until, 0, |
| + on_timer_event, lldp, |
| + lldp->event_priority, "lldp-timer", true); |
| +} |
| + |
| +_public_ int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***ret) { |
| + sd_lldp_neighbor **l = NULL, *n; |
| + Iterator i; |
| + int k = 0, r; |
| + |
| + assert_return(lldp, -EINVAL); |
| + assert_return(ret, -EINVAL); |
| + |
| + if (hashmap_isempty(lldp->neighbor_by_id)) { /* Special shortcut */ |
| + *ret = NULL; |
| + return 0; |
| + } |
| + |
| + l = new0(sd_lldp_neighbor*, hashmap_size(lldp->neighbor_by_id)); |
| + if (!l) |
| + return -ENOMEM; |
| + |
| + r = lldp_start_timer(lldp, NULL); |
| + if (r < 0) { |
| + free(l); |
| + return r; |
| + } |
| + |
| + HASHMAP_FOREACH(n, lldp->neighbor_by_id, i) |
| + l[k++] = sd_lldp_neighbor_ref(n); |
| + |
| + assert((size_t) k == hashmap_size(lldp->neighbor_by_id)); |
| + |
| + /* Return things in a stable order */ |
| + typesafe_qsort(l, k, neighbor_compare_func); |
| + *ret = l; |
| + |
| + return k; |
| +} |
| + |
| +_public_ int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t m) { |
| + assert_return(lldp, -EINVAL); |
| + assert_return(m <= 0, -EINVAL); |
| + |
| + lldp->neighbors_max = m; |
| + lldp_make_space(lldp, 0); |
| + |
| + return 0; |
| +} |
| + |
| +_public_ int sd_lldp_match_capabilities(sd_lldp *lldp, uint16_t mask) { |
| + assert_return(lldp, -EINVAL); |
| + assert_return(mask != 0, -EINVAL); |
| + |
| + lldp->capability_mask = mask; |
| + |
| + return 0; |
| +} |
| + |
| +_public_ int sd_lldp_set_filter_address(sd_lldp *lldp, const struct ether_addr *addr) { |
| + assert_return(lldp, -EINVAL); |
| + |
| + /* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so |
| + * that our own can be filtered out here. */ |
| + |
| + if (addr) |
| + lldp->filter_address = *addr; |
| + else |
| + zero(lldp->filter_address); |
| + |
| + return 0; |
| +} |
| diff --git a/src/libsystemd/sd-lldp/test-lldp.c b/src/libsystemd/sd-lldp/test-lldp.c |
| new file mode 100644 |
| index 0000000000..7406f94ce0 |
| --- /dev/null |
| +++ b/src/libsystemd/sd-lldp/test-lldp.c |
| @@ -0,0 +1,379 @@ |
| +/* SPDX-License-Identifier: LGPL-2.1+ */ |
| + |
| +#include <arpa/inet.h> |
| +#include <errno.h> |
| +#include <net/ethernet.h> |
| +#include <stdio.h> |
| +#include <string.h> |
| +#include <unistd.h> |
| + |
| +#include "sd-event.h" |
| +#include "sd-lldp.h" |
| + |
| +#include "alloc-util.h" |
| +#include "fd-util.h" |
| +#include "lldp-network.h" |
| +#include "macro.h" |
| +#include "string-util.h" |
| +#include "tests.h" |
| + |
| +#define TEST_LLDP_PORT "em1" |
| +#define TEST_LLDP_TYPE_SYSTEM_NAME "systemd-lldp" |
| +#define TEST_LLDP_TYPE_SYSTEM_DESC "systemd-lldp-desc" |
| + |
| +static int test_fd[2] = { -1, -1 }; |
| +static int lldp_handler_calls; |
| + |
| +int lldp_network_bind_raw_socket(int ifindex) { |
| + if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0) |
| + return -errno; |
| + |
| + return test_fd[0]; |
| +} |
| + |
| +static void lldp_handler(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata) { |
| + lldp_handler_calls++; |
| +} |
| + |
| +static int start_lldp(sd_lldp **lldp, sd_event *e, sd_lldp_callback_t cb, void *cb_data) { |
| + int r; |
| + |
| + r = sd_lldp_new(lldp); |
| + if (r < 0) |
| + return r; |
| + |
| + r = sd_lldp_set_ifindex(*lldp, 42); |
| + if (r < 0) |
| + return r; |
| + |
| + r = sd_lldp_set_callback(*lldp, cb, cb_data); |
| + if (r < 0) |
| + return r; |
| + |
| + r = sd_lldp_attach_event(*lldp, e, 0); |
| + if (r < 0) |
| + return r; |
| + |
| + r = sd_lldp_start(*lldp); |
| + if (r < 0) |
| + return r; |
| + |
| + return 0; |
| +} |
| + |
| +static int stop_lldp(sd_lldp *lldp) { |
| + int r; |
| + |
| + r = sd_lldp_stop(lldp); |
| + if (r < 0) |
| + return r; |
| + |
| + r = sd_lldp_detach_event(lldp); |
| + if (r < 0) |
| + return r; |
| + |
| + sd_lldp_unref(lldp); |
| + safe_close(test_fd[1]); |
| + |
| + return 0; |
| +} |
| + |
| +static void test_receive_basic_packet(sd_event *e) { |
| + |
| + static const uint8_t frame[] = { |
| + /* Ethernet header */ |
| + 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */ |
| + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */ |
| + 0x88, 0xcc, /* Ethertype */ |
| + /* LLDP mandatory TLVs */ |
| + 0x02, 0x07, 0x04, 0x00, 0x01, 0x02, /* Chassis: MAC, 00:01:02:03:04:05 */ |
| + 0x03, 0x04, 0x05, |
| + 0x04, 0x04, 0x05, 0x31, 0x2f, 0x33, /* Port: interface name, "1/3" */ |
| + 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */ |
| + /* LLDP optional TLVs */ |
| + 0x08, 0x04, 0x50, 0x6f, 0x72, 0x74, /* Port Description: "Port" */ |
| + 0x0a, 0x03, 0x53, 0x59, 0x53, /* System Name: "SYS" */ |
| + 0x0c, 0x04, 0x66, 0x6f, 0x6f, 0x00, /* System Description: "foo" (NULL-terminated) */ |
| + 0x00, 0x00 /* End Of LLDPDU */ |
| + }; |
| + |
| + sd_lldp *lldp; |
| + sd_lldp_neighbor **neighbors; |
| + uint8_t type; |
| + const void *data; |
| + uint16_t ttl; |
| + size_t length; |
| + const char *str; |
| + |
| + lldp_handler_calls = 0; |
| + assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0); |
| + |
| + assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame)); |
| + sd_event_run(e, 0); |
| + assert_se(lldp_handler_calls == 1); |
| + assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 1); |
| + |
| + assert_se(sd_lldp_neighbor_get_chassis_id(neighbors[0], &type, &data, &length) == 0); |
| + assert_se(type == SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS); |
| + assert_se(length == ETH_ALEN); |
| + assert_se(!memcmp(data, "\x00\x01\x02\x03\x04\x05", ETH_ALEN)); |
| + |
| + assert_se(sd_lldp_neighbor_get_port_id(neighbors[0], &type, &data, &length) == 0); |
| + assert_se(type == SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME); |
| + assert_se(length == 3); |
| + assert_se(!memcmp(data, "1/3", 3)); |
| + |
| + assert_se(sd_lldp_neighbor_get_port_description(neighbors[0], &str) == 0); |
| + assert_se(streq(str, "Port")); |
| + |
| + assert_se(sd_lldp_neighbor_get_system_name(neighbors[0], &str) == 0); |
| + assert_se(streq(str, "SYS")); |
| + |
| + assert_se(sd_lldp_neighbor_get_system_description(neighbors[0], &str) == 0); |
| + assert_se(streq(str, "foo")); |
| + |
| + assert_se(sd_lldp_neighbor_get_ttl(neighbors[0], &ttl) == 0); |
| + assert_se(ttl == 120); |
| + |
| + sd_lldp_neighbor_unref(neighbors[0]); |
| + free(neighbors); |
| + |
| + assert_se(stop_lldp(lldp) == 0); |
| +} |
| + |
| +static void test_receive_incomplete_packet(sd_event *e) { |
| + sd_lldp *lldp; |
| + sd_lldp_neighbor **neighbors; |
| + uint8_t frame[] = { |
| + /* Ethernet header */ |
| + 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */ |
| + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */ |
| + 0x88, 0xcc, /* Ethertype */ |
| + /* LLDP mandatory TLVs */ |
| + 0x02, 0x07, 0x04, 0x00, 0x01, 0x02, /* Chassis: MAC, 00:01:02:03:04:05 */ |
| + 0x03, 0x04, 0x05, |
| + 0x04, 0x04, 0x05, 0x31, 0x2f, 0x33, /* Port: interface name, "1/3" */ |
| + /* Missing TTL */ |
| + 0x00, 0x00 /* End Of LLDPDU */ |
| + }; |
| + |
| + lldp_handler_calls = 0; |
| + assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0); |
| + |
| + assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame)); |
| + sd_event_run(e, 0); |
| + assert_se(lldp_handler_calls == 0); |
| + assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 0); |
| + |
| + assert_se(stop_lldp(lldp) == 0); |
| +} |
| + |
| +static void test_receive_oui_packet(sd_event *e) { |
| + sd_lldp *lldp; |
| + sd_lldp_neighbor **neighbors; |
| + uint8_t frame[] = { |
| + /* Ethernet header */ |
| + 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */ |
| + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */ |
| + 0x88, 0xcc, /* Ethertype */ |
| + /* LLDP mandatory TLVs */ |
| + 0x02, 0x07, 0x04, 0x00, 0x01, 0x02, /* Chassis: MAC, 00:01:02:03:04:05 */ |
| + 0x03, 0x04, 0x05, |
| + 0x04, 0x04, 0x05, 0x31, 0x2f, 0x33, /* Port TLV: interface name, "1/3" */ |
| + 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */ |
| + /* LLDP optional TLVs */ |
| + 0xfe, 0x06, 0x00, 0x80, 0xc2, 0x01, /* Port VLAN ID: 0x1234 */ |
| + 0x12, 0x34, |
| + 0xfe, 0x07, 0x00, 0x80, 0xc2, 0x02, /* Port and protocol: flag 1, PPVID 0x7788 */ |
| + 0x01, 0x77, 0x88, |
| + 0xfe, 0x0d, 0x00, 0x80, 0xc2, 0x03, /* VLAN Name: ID 0x1234, name "Vlan51" */ |
| + 0x12, 0x34, 0x06, 0x56, 0x6c, 0x61, |
| + 0x6e, 0x35, 0x31, |
| + 0xfe, 0x06, 0x00, 0x80, 0xc2, 0x06, /* Management VID: 0x0102 */ |
| + 0x01, 0x02, |
| + 0xfe, 0x09, 0x00, 0x80, 0xc2, 0x07, /* Link aggregation: status 1, ID 0x00140012 */ |
| + 0x01, 0x00, 0x14, 0x00, 0x12, |
| + 0xfe, 0x07, 0x00, 0x12, 0x0f, 0x02, /* 802.3 Power via MDI: PSE, MDI enabled */ |
| + 0x07, 0x01, 0x00, |
| + 0x00, 0x00 /* End of LLDPDU */ |
| + }; |
| + |
| + lldp_handler_calls = 0; |
| + assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0); |
| + |
| + assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame)); |
| + sd_event_run(e, 0); |
| + assert_se(lldp_handler_calls == 1); |
| + assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 1); |
| + |
| + assert_se(sd_lldp_neighbor_tlv_rewind(neighbors[0]) >= 0); |
| + assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_CHASSIS_ID) > 0); |
| + assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); |
| + assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_PORT_ID) > 0); |
| + assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); |
| + assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_TTL) > 0); |
| + assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); |
| + 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); |
| + assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); |
| + 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); |
| + assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); |
| + assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME) > 0); |
| + assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); |
| + assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_MANAGEMENT_VID) > 0); |
| + assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); |
| + assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION) > 0); |
| + assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); |
| + 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); |
| + assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); |
| + assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_END) > 0); |
| + assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) == 0); |
| + |
| + sd_lldp_neighbor_unref(neighbors[0]); |
| + free(neighbors); |
| + |
| + assert_se(stop_lldp(lldp) == 0); |
| +} |
| + |
| +static void test_multiple_neighbors_sorted(sd_event *e) { |
| + |
| + static const uint8_t frame1[] = { |
| + /* Ethernet header */ |
| + 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */ |
| + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */ |
| + 0x88, 0xcc, /* Ethertype */ |
| + /* LLDP mandatory TLVs */ |
| + 0x02, 0x04, 0x01, '1', '/', '2', /* Chassis component: "1/2" */ |
| + 0x04, 0x04, 0x02, '2', '/', '3', /* Port component: "2/3" */ |
| + 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */ |
| + 0x00, 0x00 /* End Of LLDPDU */ |
| + }; |
| + static const uint8_t frame2[] = { |
| + /* Ethernet header */ |
| + 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */ |
| + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */ |
| + 0x88, 0xcc, /* Ethertype */ |
| + /* LLDP mandatory TLVs */ |
| + 0x02, 0x04, 0x01, '2', '/', '1', /* Chassis component: "2/1" */ |
| + 0x04, 0x04, 0x02, '1', '/', '3', /* Port component: "1/3" */ |
| + 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */ |
| + 0x00, 0x00 /* End Of LLDPDU */ |
| + }; |
| + static const uint8_t frame3[] = { |
| + /* Ethernet header */ |
| + 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */ |
| + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */ |
| + 0x88, 0xcc, /* Ethertype */ |
| + /* LLDP mandatory TLVs */ |
| + 0x02, 0x05, 0x01, '2', '/', '1', '0', /* Chassis component: "2/10" */ |
| + 0x04, 0x04, 0x02, '1', '/', '0', /* Port component: "1/0" */ |
| + 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */ |
| + 0x00, 0x00 /* End Of LLDPDU */ |
| + }; |
| + static const uint8_t frame4[] = { |
| + /* Ethernet header */ |
| + 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */ |
| + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */ |
| + 0x88, 0xcc, /* Ethertype */ |
| + /* LLDP mandatory TLVs */ |
| + 0x02, 0x05, 0x01, '2', '/', '1', '9', /* Chassis component: "2/19" */ |
| + 0x04, 0x04, 0x02, '1', '/', '0', /* Port component: "1/0" */ |
| + 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */ |
| + 0x00, 0x00 /* End Of LLDPDU */ |
| + }; |
| + static const uint8_t frame5[] = { |
| + /* Ethernet header */ |
| + 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */ |
| + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */ |
| + 0x88, 0xcc, /* Ethertype */ |
| + /* LLDP mandatory TLVs */ |
| + 0x02, 0x04, 0x01, '1', '/', '2', /* Chassis component: "1/2" */ |
| + 0x04, 0x05, 0x02, '2', '/', '1', '0', /* Port component: "2/10" */ |
| + 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */ |
| + 0x00, 0x00 /* End Of LLDPDU */ |
| + }; |
| + static const uint8_t frame6[] = { |
| + /* Ethernet header */ |
| + 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */ |
| + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */ |
| + 0x88, 0xcc, /* Ethertype */ |
| + /* LLDP mandatory TLVs */ |
| + 0x02, 0x04, 0x01, '1', '/', '2', /* Chassis component: "1/2" */ |
| + 0x04, 0x05, 0x02, '2', '/', '3', '9', /* Port component: "2/10" */ |
| + 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */ |
| + 0x00, 0x00 /* End Of LLDPDU */ |
| + }; |
| + static const char* expected[] = { |
| + /* ordered pairs of Chassis+Port */ |
| + "1/2", "2/10", |
| + "1/2", "2/3", |
| + "1/2", "2/39", |
| + "2/1", "1/3", |
| + "2/10", "1/0", |
| + "2/19", "1/0", |
| + }; |
| + |
| + sd_lldp *lldp; |
| + sd_lldp_neighbor **neighbors; |
| + int i; |
| + uint8_t type; |
| + const void *data; |
| + size_t length, expected_length; |
| + uint16_t ttl; |
| + |
| + lldp_handler_calls = 0; |
| + assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0); |
| + |
| + assert_se(write(test_fd[1], frame1, sizeof(frame1)) == sizeof(frame1)); |
| + sd_event_run(e, 0); |
| + assert_se(write(test_fd[1], frame2, sizeof(frame2)) == sizeof(frame2)); |
| + sd_event_run(e, 0); |
| + assert_se(write(test_fd[1], frame3, sizeof(frame3)) == sizeof(frame3)); |
| + sd_event_run(e, 0); |
| + assert_se(write(test_fd[1], frame4, sizeof(frame4)) == sizeof(frame4)); |
| + sd_event_run(e, 0); |
| + assert_se(write(test_fd[1], frame5, sizeof(frame5)) == sizeof(frame5)); |
| + sd_event_run(e, 0); |
| + assert_se(write(test_fd[1], frame6, sizeof(frame6)) == sizeof(frame6)); |
| + sd_event_run(e, 0); |
| + assert_se(lldp_handler_calls == 6); |
| + |
| + assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 6); |
| + |
| + for (i = 0; i < 6; i++) { |
| + assert_se(sd_lldp_neighbor_get_chassis_id(neighbors[i], &type, &data, &length) == 0); |
| + assert_se(type == SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT); |
| + expected_length = strlen(expected[2 * i]); |
| + assert_se(length == expected_length); |
| + assert_se(memcmp(data, expected[2 * i], expected_length) == 0); |
| + |
| + assert_se(sd_lldp_neighbor_get_port_id(neighbors[i], &type, &data, &length) == 0); |
| + assert_se(type == SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT); |
| + expected_length = strlen(expected[2 * i + 1]); |
| + assert_se(length == expected_length); |
| + assert_se(memcmp(data, expected[2 * i + 1], expected_length) == 0); |
| + |
| + assert_se(sd_lldp_neighbor_get_ttl(neighbors[i], &ttl) == 0); |
| + assert_se(ttl == 120); |
| + } |
| + |
| + for (i = 0; i < 6; i++) |
| + sd_lldp_neighbor_unref(neighbors[i]); |
| + free(neighbors); |
| + |
| + assert_se(stop_lldp(lldp) == 0); |
| +} |
| + |
| +int main(int argc, char *argv[]) { |
| + _cleanup_(sd_event_unrefp) sd_event *e = NULL; |
| + |
| + test_setup_logging(LOG_DEBUG); |
| + |
| + /* LLDP reception tests */ |
| + assert_se(sd_event_new(&e) == 0); |
| + test_receive_basic_packet(e); |
| + test_receive_incomplete_packet(e); |
| + test_receive_oui_packet(e); |
| + test_multiple_neighbors_sorted(e); |
| + |
| + return 0; |
| +} |
| diff --git a/src/systemd/meson.build b/src/systemd/meson.build |
| index 75c48b07a5..887421bee0 100644 |
| --- a/src/systemd/meson.build |
| +++ b/src/systemd/meson.build |
| @@ -12,6 +12,7 @@ _systemd_headers = ''' |
| sd-journal.h |
| sd-login.h |
| sd-messages.h |
| + sd-lldp.h |
| '''.split() |
| |
| # https://github.com/mesonbuild/meson/issues/1633 |
| @@ -25,7 +26,6 @@ _not_installed_headers = ''' |
| sd-dhcp-server.h |
| sd-ipv4acd.h |
| sd-ipv4ll.h |
| - sd-lldp.h |
| sd-ndisc.h |
| sd-netlink.h |
| sd-network.h |
| diff --git a/src/test/meson.build b/src/test/meson.build |
| index de31e977bc..680d1dec6b 100644 |
| --- a/src/test/meson.build |
| +++ b/src/test/meson.build |
| @@ -1010,6 +1010,11 @@ tests += [ |
| [], |
| []], |
| |
| + [['src/libsystemd/sd-lldp/test-lldp.c'], |
| + [libbasic,libshared_static,libsystemd_static |
| + ], |
| + []], |
| + |
| ] |
| |
| # test-bus-vtable-cc.cc is a symlink and symlinks get lost in containers on FuzzBuzz. |
| @@ -1098,11 +1103,6 @@ tests += [ |
| [libshared, |
| libsystemd_network], |
| []], |
| - |
| - [['src/libsystemd-network/test-lldp.c'], |
| - [libshared, |
| - libsystemd_network], |
| - []], |
| ] |
| |
| ############################################################ |
| -- |
| 2.28.0 |
| |