diff options
author | Yu Watanabe <watanabe.yu+github@gmail.com> | 2021-09-28 11:29:34 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-28 11:29:34 +0900 |
commit | 07db7f6bb8a94f8ec3aaaf043b76f5432d477501 (patch) | |
tree | 053da942d20ae36f695fc44ac67e684dfd664aac /src | |
parent | bed159888e288c0b6e18d9bbb4fd6af41cf8c968 (diff) | |
parent | c01b9b879c729a2099e0c9bd00d6ca10a2dd8688 (diff) | |
download | systemd-07db7f6bb8a94f8ec3aaaf043b76f5432d477501.tar.gz |
Merge pull request #20846 from yuwata/sd-lldp-tx
network: move LLDP transmission feature to libsystemd-network
Diffstat (limited to 'src')
25 files changed, 1615 insertions, 1168 deletions
diff --git a/src/basic/hostname-util.h b/src/basic/hostname-util.h index 28975c8792..0d1574db9e 100644 --- a/src/basic/hostname-util.h +++ b/src/basic/hostname-util.h @@ -4,6 +4,7 @@ #include <stdbool.h> #include <stdio.h> +#include "env-file.h" #include "macro.h" #include "strv.h" @@ -60,3 +61,7 @@ static inline bool is_outbound_hostname(const char *hostname) { /* This tries to identify the valid syntaxes for the our synthetic "outbound" host. */ return STRCASE_IN_SET(hostname, "_outbound", "_outbound."); } + +static inline int get_pretty_hostname(char **ret) { + return parse_env_file(NULL, "/etc/machine-info", "PRETTY_HOSTNAME", ret); +} diff --git a/src/libsystemd-network/fuzz-lldp.c b/src/libsystemd-network/fuzz-lldp-rx.c index 5747135b32..6ee623fa11 100644 --- a/src/libsystemd-network/fuzz-lldp.c +++ b/src/libsystemd-network/fuzz-lldp-rx.c @@ -4,7 +4,7 @@ #include <unistd.h> #include "sd-event.h" -#include "sd-lldp.h" +#include "sd-lldp-rx.h" #include "fd-util.h" #include "fuzz.h" @@ -21,22 +21,22 @@ int lldp_network_bind_raw_socket(int ifindex) { int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { _cleanup_(sd_event_unrefp) sd_event *e = NULL; - _cleanup_(sd_lldp_unrefp) sd_lldp *lldp = NULL; + _cleanup_(sd_lldp_rx_unrefp) sd_lldp_rx *lldp_rx = NULL; if (size > 2048) return 0; assert_se(sd_event_new(&e) == 0); - assert_se(sd_lldp_new(&lldp) >= 0); - assert_se(sd_lldp_set_ifindex(lldp, 42) >= 0); - assert_se(sd_lldp_attach_event(lldp, e, 0) >= 0); - assert_se(sd_lldp_start(lldp) >= 0); + assert_se(sd_lldp_rx_new(&lldp_rx) >= 0); + assert_se(sd_lldp_rx_set_ifindex(lldp_rx, 42) >= 0); + assert_se(sd_lldp_rx_attach_event(lldp_rx, e, 0) >= 0); + assert_se(sd_lldp_rx_start(lldp_rx) >= 0); assert_se(write(test_fd[1], data, size) == (ssize_t) size); assert_se(sd_event_run(e, 0) >= 0); - assert_se(sd_lldp_stop(lldp) >= 0); - assert_se(sd_lldp_detach_event(lldp) >= 0); + assert_se(sd_lldp_rx_stop(lldp_rx) >= 0); + assert_se(sd_lldp_rx_detach_event(lldp_rx) >= 0); test_fd[1] = safe_close(test_fd[1]); return 0; diff --git a/src/libsystemd-network/fuzz-lldp.options b/src/libsystemd-network/fuzz-lldp-rx.options index 60bd9b0b2f..60bd9b0b2f 100644 --- a/src/libsystemd-network/fuzz-lldp.options +++ b/src/libsystemd-network/fuzz-lldp-rx.options diff --git a/src/libsystemd-network/lldp-neighbor.c b/src/libsystemd-network/lldp-neighbor.c index 3bd775158e..342e490603 100644 --- a/src/libsystemd-network/lldp-neighbor.c +++ b/src/libsystemd-network/lldp-neighbor.c @@ -5,7 +5,6 @@ #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_network.h" @@ -36,7 +35,7 @@ _public_ sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n) { if (!n) return NULL; - assert(n->n_ref > 0 || n->lldp); + assert(n->n_ref > 0 || n->lldp_rx); n->n_ref++; return n; @@ -67,7 +66,7 @@ _public_ sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n) { assert(n->n_ref > 0); n->n_ref--; - if (n->n_ref <= 0 && !n->lldp) + if (n->n_ref <= 0 && !n->lldp_rx) lldp_neighbor_free(n); return NULL; @@ -80,18 +79,18 @@ sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n) { if (!n) return NULL; - if (!n->lldp) + if (!n->lldp_rx) 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); + (void) hashmap_remove_value(n->lldp_rx->neighbor_by_id, &n->id, n); - assert_se(prioq_remove(n->lldp->neighbor_by_expiry, n, &n->prioq_idx) >= 0); + assert_se(prioq_remove(n->lldp_rx->neighbor_by_expiry, n, &n->prioq_idx) >= 0); - n->lldp = NULL; + n->lldp_rx = NULL; if (n->n_ref <= 0) lldp_neighbor_free(n); @@ -112,7 +111,7 @@ sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size) { return n; } -static int parse_string(sd_lldp *lldp, char **s, const void *q, size_t n) { +static int parse_string(sd_lldp_rx *lldp_rx, char **s, const void *q, size_t n) { const char *p = q; char *k; @@ -120,7 +119,7 @@ static int parse_string(sd_lldp *lldp, char **s, const void *q, size_t n) { assert(p || n == 0); if (*s) { - log_lldp(lldp, "Found duplicate string, ignoring field."); + log_lldp_rx(lldp_rx, "Found duplicate string, ignoring field."); return 0; } @@ -133,7 +132,7 @@ static int parse_string(sd_lldp *lldp, char **s, const void *q, size_t n) { /* Look for inner NULs */ if (memchr(p, 0, n)) { - log_lldp(lldp, "Found inner NUL in string, ignoring field."); + log_lldp_rx(lldp_rx, "Found inner NUL in string, ignoring field."); return 0; } @@ -157,14 +156,14 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) { assert(n); if (n->raw_size < sizeof(struct ether_header)) - return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG), - "Received truncated packet, ignoring."); + return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), + "Received truncated packet, ignoring."); memcpy(&h, LLDP_NEIGHBOR_RAW(n), sizeof(h)); if (h.ether_type != htobe16(ETHERTYPE_LLDP)) - return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG), - "Received packet with wrong type, ignoring."); + return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), + "Received packet with wrong type, ignoring."); if (h.ether_dhost[0] != 0x01 || h.ether_dhost[1] != 0x80 || @@ -172,8 +171,8 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) { h.ether_dhost[3] != 0x00 || h.ether_dhost[4] != 0x00 || !IN_SET(h.ether_dhost[5], 0x00, 0x03, 0x0e)) - return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG), - "Received packet with wrong destination address, ignoring."); + return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), + "Received packet with wrong destination address, ignoring."); memcpy(&n->source_address, h.ether_shost, sizeof(struct ether_addr)); memcpy(&n->destination_address, h.ether_dhost, sizeof(struct ether_addr)); @@ -186,7 +185,7 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) { uint16_t length; if (left < 2) - return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG), + return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), "TLV lacks header, ignoring."); type = p[0] >> 1; @@ -194,15 +193,15 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) { p += 2, left -= 2; if (left < length) - return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG), - "TLV truncated, ignoring datagram."); + return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), + "TLV truncated, ignoring datagram."); switch (type) { case SD_LLDP_TYPE_END: if (length != 0) - return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG), - "End marker TLV not zero-sized, ignoring datagram."); + return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), + "End marker TLV not zero-sized, ignoring datagram."); /* 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) */ @@ -212,12 +211,12 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) { case SD_LLDP_TYPE_CHASSIS_ID: if (length < 2 || length > 256) /* includes the chassis subtype, hence one extra byte */ - return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG), - "Chassis ID field size out of range, ignoring datagram."); + return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), + "Chassis ID field size out of range, ignoring datagram."); if (n->id.chassis_id) - return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG), - "Duplicate chassis ID field, ignoring datagram."); + return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), + "Duplicate chassis ID field, ignoring datagram."); n->id.chassis_id = memdup(p, length); if (!n->id.chassis_id) @@ -229,12 +228,12 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) { case SD_LLDP_TYPE_PORT_ID: if (length < 2 || length > 256) /* includes the port subtype, hence one extra byte */ - return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG), - "Port ID field size out of range, ignoring datagram."); + return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), + "Port ID field size out of range, ignoring datagram."); if (n->id.port_id) - return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG), - "Duplicate port ID field, ignoring datagram."); + return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), + "Duplicate port ID field, ignoring datagram."); n->id.port_id = memdup(p, length); if (!n->id.port_id) @@ -245,39 +244,39 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) { case SD_LLDP_TYPE_TTL: if (length != 2) - return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG), - "TTL field has wrong size, ignoring datagram."); + return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), + "TTL field has wrong size, ignoring datagram."); if (n->has_ttl) - return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG), - "Duplicate TTL field, ignoring datagram."); + return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), + "Duplicate TTL field, ignoring datagram."); n->ttl = unaligned_read_be16(p); n->has_ttl = true; break; case SD_LLDP_TYPE_PORT_DESCRIPTION: - r = parse_string(n->lldp, &n->port_description, p, length); + r = parse_string(n->lldp_rx, &n->port_description, p, length); if (r < 0) return r; break; case SD_LLDP_TYPE_SYSTEM_NAME: - r = parse_string(n->lldp, &n->system_name, p, length); + r = parse_string(n->lldp_rx, &n->system_name, p, length); if (r < 0) return r; break; case SD_LLDP_TYPE_SYSTEM_DESCRIPTION: - r = parse_string(n->lldp, &n->system_description, p, length); + r = parse_string(n->lldp_rx, &n->system_description, p, length); if (r < 0) return r; break; case SD_LLDP_TYPE_SYSTEM_CAPABILITIES: if (length != 4) - return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG), - "System capabilities field has wrong size."); + return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), + "System capabilities field has wrong size."); n->system_capabilities = unaligned_read_be16(p); n->enabled_capabilities = unaligned_read_be16(p + 2); @@ -286,14 +285,13 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) { case SD_LLDP_TYPE_PRIVATE: if (length < 4) - return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG), - "Found private TLV that is too short, ignoring."); + return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), + "Found private TLV that is too short, ignoring."); /* RFC 8520: MUD URL */ - if (memcmp(p, SD_LLDP_OUI_MUD, sizeof(SD_LLDP_OUI_MUD)) == 0 && - p[sizeof(SD_LLDP_OUI_MUD)] == SD_LLDP_OUI_SUBTYPE_MUD_USAGE_DESCRIPTION) { - r = parse_string(n->lldp, &n->mud_url, p + sizeof(SD_LLDP_OUI_MUD) + 1, - length - 1 - sizeof(SD_LLDP_OUI_MUD)); + if (memcmp(p, SD_LLDP_OUI_IANA_MUD, sizeof(SD_LLDP_OUI_IANA_MUD)) == 0) { + r = parse_string(n->lldp_rx, &n->mud_url, p + sizeof(SD_LLDP_OUI_IANA_MUD), + length - sizeof(SD_LLDP_OUI_IANA_MUD)); if (r < 0) return r; } @@ -305,8 +303,8 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) { end_marker: if (!n->id.chassis_id || !n->id.port_id || !n->has_ttl) - return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG), - "One or more mandatory TLV missing in datagram. Ignoring."); + return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), + "One or more mandatory TLV missing in datagram. Ignoring."); n->rindex = sizeof(struct ether_header); @@ -328,8 +326,8 @@ void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) { } else n->until = 0; - if (n->lldp) - prioq_reshuffle(n->lldp->neighbor_by_expiry, n, &n->prioq_idx); + if (n->lldp_rx) + prioq_reshuffle(n->lldp_rx->neighbor_by_expiry, n, &n->prioq_idx); } bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b) { diff --git a/src/libsystemd-network/lldp-neighbor.h b/src/libsystemd-network/lldp-neighbor.h index a5718c8c31..016286b17d 100644 --- a/src/libsystemd-network/lldp-neighbor.h +++ b/src/libsystemd-network/lldp-neighbor.h @@ -5,10 +5,10 @@ #include <stdbool.h> #include <sys/types.h> -#include "sd-lldp.h" +#include "sd-lldp-rx.h" #include "hash-funcs.h" -#include "lldp-internal.h" +#include "lldp-rx-internal.h" #include "time-util.h" typedef struct LLDPNeighborID { @@ -21,8 +21,8 @@ typedef struct LLDPNeighborID { } 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; + /* Neighbor objects stay around as long as they are linked into an "sd_lldp_rx" object or n_ref > 0. */ + sd_lldp_rx *lldp_rx; unsigned n_ref; triple_timestamp timestamp; diff --git a/src/libsystemd-network/lldp-internal.h b/src/libsystemd-network/lldp-rx-internal.h index cf0578c5c2..6d3ce0e1bc 100644 --- a/src/libsystemd-network/lldp-internal.h +++ b/src/libsystemd-network/lldp-rx-internal.h @@ -2,13 +2,13 @@ #pragma once #include "sd-event.h" -#include "sd-lldp.h" +#include "sd-lldp-rx.h" #include "hashmap.h" #include "log-link.h" #include "prioq.h" -struct sd_lldp { +struct sd_lldp_rx { unsigned n_ref; int ifindex; @@ -25,7 +25,7 @@ struct sd_lldp { uint64_t neighbors_max; - sd_lldp_callback_t callback; + sd_lldp_rx_callback_t callback; void *userdata; uint16_t capability_mask; @@ -33,16 +33,16 @@ struct sd_lldp { struct ether_addr filter_address; }; -const char* lldp_event_to_string(sd_lldp_event_t e) _const_; -sd_lldp_event_t lldp_event_from_string(const char *s) _pure_; +const char* lldp_rx_event_to_string(sd_lldp_rx_event_t e) _const_; +sd_lldp_rx_event_t lldp_rx_event_from_string(const char *s) _pure_; -#define log_lldp_errno(lldp, error, fmt, ...) \ +#define log_lldp_rx_errno(lldp_rx, error, fmt, ...) \ log_interface_prefix_full_errno( \ - "LLDP: ", \ - sd_lldp_get_ifname(lldp), \ + "LLDP Rx: ", \ + sd_lldp_rx_get_ifname(lldp_rx), \ error, fmt, ##__VA_ARGS__) -#define log_lldp(lldp, fmt, ...) \ +#define log_lldp_rx(lldp_rx, fmt, ...) \ log_interface_prefix_full_errno_zerook( \ - "LLDP: ", \ - sd_lldp_get_ifname(lldp), \ + "LLDP Rx: ", \ + sd_lldp_rx_get_ifname(lldp_rx), \ 0, fmt, ##__VA_ARGS__) diff --git a/src/libsystemd-network/meson.build b/src/libsystemd-network/meson.build index ca3393eff3..54248a1e6e 100644 --- a/src/libsystemd-network/meson.build +++ b/src/libsystemd-network/meson.build @@ -1,48 +1,49 @@ # SPDX-License-Identifier: LGPL-2.1-or-later sources = files(''' - sd-dhcp-client.c - sd-dhcp-server.c + arp-util.c + arp-util.h dhcp-client-internal.h + dhcp-identifier.c + dhcp-identifier.h + dhcp-internal.h + dhcp-lease-internal.h dhcp-network.c dhcp-option.c dhcp-packet.c - dhcp-internal.h - dhcp-server-internal.h dhcp-protocol.h - dhcp-lease-internal.h - sd-dhcp-lease.c - sd-ipv4ll.c - sd-ipv4acd.c - arp-util.h - arp-util.c + dhcp-server-internal.h + dhcp6-internal.h + dhcp6-lease-internal.h + dhcp6-network.c + dhcp6-option.c + dhcp6-protocol.h + icmp6-util.c + icmp6-util.h + lldp-neighbor.c + lldp-neighbor.h + lldp-network.c + lldp-network.h + lldp-rx-internal.h + ndisc-internal.h + ndisc-router.c + ndisc-router.h network-common.c network-common.h network-internal.c network-internal.h - sd-ndisc.c - ndisc-internal.h - ndisc-router.h - ndisc-router.c - sd-radv.c radv-internal.h - icmp6-util.h - icmp6-util.c + sd-dhcp-client.c + sd-dhcp-lease.c + sd-dhcp-server.c sd-dhcp6-client.c - dhcp6-internal.h - dhcp6-protocol.h - dhcp6-network.c - dhcp6-option.c - dhcp6-lease-internal.h 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 + sd-ipv4acd.c + sd-ipv4ll.c + sd-lldp-rx.c + sd-lldp-tx.c + sd-ndisc.c + sd-radv.c '''.split()) libsystemd_network = static_library( @@ -97,7 +98,7 @@ tests += [ [libshared, libsystemd_network]], - [['src/libsystemd-network/test-lldp.c'], + [['src/libsystemd-network/test-lldp-rx.c'], [libshared, libsystemd_network]], ] @@ -111,7 +112,7 @@ fuzzers += [ [libsystemd_network, libshared]], - [['src/libsystemd-network/fuzz-lldp.c'], + [['src/libsystemd-network/fuzz-lldp-rx.c'], [libshared, libsystemd_network]], diff --git a/src/libsystemd-network/sd-lldp-rx.c b/src/libsystemd-network/sd-lldp-rx.c new file mode 100644 index 0000000000..8d9b132722 --- /dev/null +++ b/src/libsystemd-network/sd-lldp-rx.c @@ -0,0 +1,523 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include <arpa/inet.h> +#include <linux/sockios.h> +#include <sys/ioctl.h> + +#include "sd-lldp-rx.h" + +#include "alloc-util.h" +#include "ether-addr-util.h" +#include "event-util.h" +#include "fd-util.h" +#include "lldp-neighbor.h" +#include "lldp-network.h" +#include "lldp-rx-internal.h" +#include "memory-util.h" +#include "network-common.h" +#include "socket-util.h" +#include "sort-util.h" +#include "string-table.h" + +#define LLDP_DEFAULT_NEIGHBORS_MAX 128U + +static const char * const lldp_rx_event_table[_SD_LLDP_RX_EVENT_MAX] = { + [SD_LLDP_RX_EVENT_ADDED] = "added", + [SD_LLDP_RX_EVENT_REMOVED] = "removed", + [SD_LLDP_RX_EVENT_UPDATED] = "updated", + [SD_LLDP_RX_EVENT_REFRESHED] = "refreshed", +}; + +DEFINE_STRING_TABLE_LOOKUP(lldp_rx_event, sd_lldp_rx_event_t); + +static void lldp_rx_flush_neighbors(sd_lldp_rx *lldp_rx) { + assert(lldp_rx); + + hashmap_clear(lldp_rx->neighbor_by_id); +} + +static void lldp_rx_callback(sd_lldp_rx *lldp_rx, sd_lldp_rx_event_t event, sd_lldp_neighbor *n) { + assert(lldp_rx); + assert(event >= 0 && event < _SD_LLDP_RX_EVENT_MAX); + + if (!lldp_rx->callback) + return (void) log_lldp_rx(lldp_rx, "Received '%s' event.", lldp_rx_event_to_string(event)); + + log_lldp_rx(lldp_rx, "Invoking callback for '%s' event.", lldp_rx_event_to_string(event)); + lldp_rx->callback(lldp_rx, event, n, lldp_rx->userdata); +} + +static int lldp_rx_make_space(sd_lldp_rx *lldp_rx, size_t extra) { + usec_t t = USEC_INFINITY; + bool changed = false; + + assert(lldp_rx); + + /* 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_rx->neighbor_by_expiry); + if (!n) + break; + + sd_lldp_neighbor_ref(n); + + if (hashmap_size(lldp_rx->neighbor_by_id) > LESS_BY(lldp_rx->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_rx_callback(lldp_rx, SD_LLDP_RX_EVENT_REMOVED, n); + changed = true; + } + + return changed; +} + +static bool lldp_rx_keep_neighbor(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *n) { + assert(lldp_rx); + 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_rx->filter_address) && + ether_addr_equal(&lldp_rx->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_rx->capability_mask) == 0) + return false; + + /* Keep everything else */ + return true; +} + +static int lldp_rx_start_timer(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *neighbor); + +static int lldp_rx_add_neighbor(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *n) { + _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *old = NULL; + bool keep; + int r; + + assert(lldp_rx); + assert(n); + assert(!n->lldp_rx); + + keep = lldp_rx_keep_neighbor(lldp_rx, n); + + /* First retrieve the old entry for this MSAP */ + old = hashmap_get(lldp_rx->neighbor_by_id, &n->id); + if (old) { + sd_lldp_neighbor_ref(old); + + if (!keep) { + lldp_neighbor_unlink(old); + lldp_rx_callback(lldp_rx, SD_LLDP_RX_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_rx_start_timer(lldp_rx, old); + lldp_rx_callback(lldp_rx, SD_LLDP_RX_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_rx_make_space(lldp_rx, 1); + + r = hashmap_put(lldp_rx->neighbor_by_id, &n->id, n); + if (r < 0) + goto finish; + + r = prioq_put(lldp_rx->neighbor_by_expiry, n, &n->prioq_idx); + if (r < 0) { + assert_se(hashmap_remove(lldp_rx->neighbor_by_id, &n->id) == n); + goto finish; + } + + n->lldp_rx = lldp_rx; + + lldp_rx_start_timer(lldp_rx, n); + lldp_rx_callback(lldp_rx, old ? SD_LLDP_RX_EVENT_UPDATED : SD_LLDP_RX_EVENT_ADDED, n); + + return 1; + +finish: + if (old) + lldp_rx_callback(lldp_rx, SD_LLDP_RX_EVENT_REMOVED, old); + + return r; +} + +static int lldp_rx_handle_datagram(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *n) { + int r; + + assert(lldp_rx); + assert(n); + + r = lldp_neighbor_parse(n); + if (r == -EBADMSG) /* Ignore bad messages */ + return 0; + if (r < 0) + return r; + + r = lldp_rx_add_neighbor(lldp_rx, n); + if (r < 0) { + log_lldp_rx_errno(lldp_rx, r, "Failed to add datagram. Ignoring."); + return 0; + } + + log_lldp_rx(lldp_rx, "Successfully processed LLDP datagram."); + return 0; +} + +static int lldp_rx_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_rx *lldp_rx = userdata; + struct timespec ts; + + assert(fd >= 0); + assert(lldp_rx); + + space = next_datagram_size_fd(fd); + if (space < 0) { + log_lldp_rx_errno(lldp_rx, space, "Failed to determine datagram size to read, ignoring: %m"); + return 0; + } + + 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; + + log_lldp_rx_errno(lldp_rx, errno, "Failed to read LLDP datagram, ignoring: %m"); + return 0; + } + + if ((size_t) length != n->raw_size) { + log_lldp_rx(lldp_rx, "Packet size mismatch, ignoring"); + return 0; + } + + /* 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_rx_handle_datagram(lldp_rx, n); +} + +static void lldp_rx_reset(sd_lldp_rx *lldp_rx) { + assert(lldp_rx); + + (void) event_source_disable(lldp_rx->timer_event_source); + lldp_rx->io_event_source = sd_event_source_disable_unref(lldp_rx->io_event_source); + lldp_rx->fd = safe_close(lldp_rx->fd); +} + +_public_ int sd_lldp_rx_start(sd_lldp_rx *lldp_rx) { + int r; + + assert_return(lldp_rx, -EINVAL); + assert_return(lldp_rx->event, -EINVAL); + assert_return(lldp_rx->ifindex > 0, -EINVAL); + + if (lldp_rx->fd >= 0) + return 0; + + assert(!lldp_rx->io_event_source); + + lldp_rx->fd = lldp_network_bind_raw_socket(lldp_rx->ifindex); + if (lldp_rx->fd < 0) + return lldp_rx->fd; + + r = sd_event_add_io(lldp_rx->event, &lldp_rx->io_event_source, lldp_rx->fd, EPOLLIN, lldp_rx_receive_datagram, lldp_rx); + if (r < 0) + goto fail; + + r = sd_event_source_set_priority(lldp_rx->io_event_source, lldp_rx->event_priority); + if (r < 0) + goto fail; + + (void) sd_event_source_set_description(lldp_rx->io_event_source, "lldp-rx-io"); + + log_lldp_rx(lldp_rx, "Started LLDP client"); + return 1; + +fail: + lldp_rx_reset(lldp_rx); + return r; +} + +_public_ int sd_lldp_rx_stop(sd_lldp_rx *lldp_rx) { + if (!lldp_rx) + return 0; + + if (lldp_rx->fd < 0) + return 0; + + log_lldp_rx(lldp_rx, "Stopping LLDP client"); + + lldp_rx_reset(lldp_rx); + lldp_rx_flush_neighbors(lldp_rx); + + return 1; +} + +_public_ int sd_lldp_rx_attach_event(sd_lldp_rx *lldp_rx, sd_event *event, int64_t priority) { + int r; + + assert_return(lldp_rx, -EINVAL); + assert_return(lldp_rx->fd < 0, -EBUSY); + assert_return(!lldp_rx->event, -EBUSY); + + if (event) + lldp_rx->event = sd_event_ref(event); + else { + r = sd_event_default(&lldp_rx->event); + if (r < 0) + return r; + } + + lldp_rx->event_priority = priority; + + return 0; +} + +_public_ int sd_lldp_rx_detach_event(sd_lldp_rx *lldp_rx) { + + assert_return(lldp_rx, -EINVAL); + assert_return(lldp_rx->fd < 0, -EBUSY); + + lldp_rx->event = sd_event_unref(lldp_rx->event); + return 0; +} + +_public_ sd_event* sd_lldp_rx_get_event(sd_lldp_rx *lldp_rx) { + assert_return(lldp_rx, NULL); + + return lldp_rx->event; +} + +_public_ int sd_lldp_rx_set_callback(sd_lldp_rx *lldp_rx, sd_lldp_rx_callback_t cb, void *userdata) { + assert_return(lldp_rx, -EINVAL); + + lldp_rx->callback = cb; + lldp_rx->userdata = userdata; + + return 0; +} + +_public_ int sd_lldp_rx_set_ifindex(sd_lldp_rx *lldp_rx, int ifindex) { + assert_return(lldp_rx, -EINVAL); + assert_return(ifindex > 0, -EINVAL); + assert_return(lldp_rx->fd < 0, -EBUSY); + + lldp_rx->ifindex = ifindex; + return 0; +} + +int sd_lldp_rx_set_ifname(sd_lldp_rx *lldp_rx, const char *ifname) { + assert_return(lldp_rx, -EINVAL); + assert_return(ifname, -EINVAL); + + if (!ifname_valid_full(ifname, IFNAME_VALID_ALTERNATIVE)) + return -EINVAL; + + return free_and_strdup(&lldp_rx->ifname, ifname); +} + +const char *sd_lldp_rx_get_ifname(sd_lldp_rx *lldp_rx) { + if (!lldp_rx) + return NULL; + + return get_ifname(lldp_rx->ifindex, &lldp_rx->ifname); +} + +static sd_lldp_rx *lldp_rx_free(sd_lldp_rx *lldp_rx) { + assert(lldp_rx); + + lldp_rx_reset(lldp_rx); + + sd_event_source_unref(lldp_rx->timer_event_source); + sd_lldp_rx_detach_event(lldp_rx); + + lldp_rx_flush_neighbors(lldp_rx); + + hashmap_free(lldp_rx->neighbor_by_id); + prioq_free(lldp_rx->neighbor_by_expiry); + free(lldp_rx->ifname); + return mfree(lldp_rx); +} + +DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_lldp_rx, sd_lldp_rx, lldp_rx_free); + +_public_ int sd_lldp_rx_new(sd_lldp_rx **ret) { + _cleanup_(sd_lldp_rx_unrefp) sd_lldp_rx *lldp_rx = NULL; + int r; + + assert_return(ret, -EINVAL); + + lldp_rx = new(sd_lldp_rx, 1); + if (!lldp_rx) + return -ENOMEM; + + *lldp_rx = (sd_lldp_rx) { + .n_ref = 1, + .fd = -1, + .neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX, + .capability_mask = UINT16_MAX, + }; + + lldp_rx->neighbor_by_id = hashmap_new(&lldp_neighbor_hash_ops); + if (!lldp_rx->neighbor_by_id) + return -ENOMEM; + + r = prioq_ensure_allocated(&lldp_rx->neighbor_by_expiry, lldp_neighbor_prioq_compare_func); + if (r < 0) + return r; + + *ret = TAKE_PTR(lldp_rx); + + 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_rx *lldp_rx = userdata; + int r; + + r = lldp_rx_make_space(lldp_rx, 0); + if (r < 0) { + log_lldp_rx_errno(lldp_rx, r, "Failed to make space, ignoring: %m"); + return 0; + } + + r = lldp_rx_start_timer(lldp_rx, NULL); + if (r < 0) { + log_lldp_rx_errno(lldp_rx, r, "Failed to restart timer, ignoring: %m"); + return 0; + } + + return 0; +} + +static int lldp_rx_start_timer(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *neighbor) { + sd_lldp_neighbor *n; + + assert(lldp_rx); + + if (neighbor) + lldp_neighbor_start_ttl(neighbor); + + n = prioq_peek(lldp_rx->neighbor_by_expiry); + if (!n) + return event_source_disable(lldp_rx->timer_event_source); + + if (!lldp_rx->event) + return 0; + + return event_reset_time(lldp_rx->event, &lldp_rx->timer_event_source, + clock_boottime_or_monotonic(), + n->until, 0, + on_timer_event, lldp_rx, + lldp_rx->event_priority, "lldp-rx-timer", true); +} + +_public_ int sd_lldp_rx_get_neighbors(sd_lldp_rx *lldp_rx, sd_lldp_neighbor ***ret) { + sd_lldp_neighbor **l = NULL, *n; + int k = 0, r; + + assert_return(lldp_rx, -EINVAL); + assert_return(ret, -EINVAL); + + if (hashmap_isempty(lldp_rx->neighbor_by_id)) { /* Special shortcut */ + *ret = NULL; + return 0; + } + + l = new0(sd_lldp_neighbor*, hashmap_size(lldp_rx->neighbor_by_id)); + if (!l) + return -ENOMEM; + + r = lldp_rx_start_timer(lldp_rx, NULL); + if (r < 0) { + free(l); + return r; + } + + HASHMAP_FOREACH(n, lldp_rx->neighbor_by_id) + l[k++] = sd_lldp_neighbor_ref(n); + + assert((size_t) k == hashmap_size(lldp_rx->neighbor_by_id)); + + /* Return things in a stable order */ + typesafe_qsort(l, k, neighbor_compare_func); + *ret = l; + + return k; +} + +_public_ int sd_lldp_rx_set_neighbors_max(sd_lldp_rx *lldp_rx, uint64_t m) { + assert_return(lldp_rx, -EINVAL); + assert_return(m > 0, -EINVAL); + + lldp_rx->neighbors_max = m; + lldp_rx_make_space(lldp_rx, 0); + + return 0; +} + +_public_ int sd_lldp_rx_match_capabilities(sd_lldp_rx *lldp_rx, uint16_t mask) { + assert_return(lldp_rx, -EINVAL); + assert_return(mask != 0, -EINVAL); + + lldp_rx->capability_mask = mask; + + return 0; +} + +_public_ int sd_lldp_rx_set_filter_address(sd_lldp_rx *lldp_rx, const struct ether_addr *addr) { + assert_return(lldp_rx, -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_rx->filter_address = *addr; + else + zero(lldp_rx->filter_address); + + return 0; +} diff --git a/src/libsystemd-network/sd-lldp-tx.c b/src/libsystemd-network/sd-lldp-tx.c new file mode 100644 index 0000000000..24003af7e1 --- /dev/null +++ b/src/libsystemd-network/sd-lldp-tx.c @@ -0,0 +1,615 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include <arpa/inet.h> +#include <linux/sockios.h> +#include <sys/ioctl.h> + +#include "sd-event.h" +#include "sd-id128.h" +#include "sd-lldp-tx.h" + +#include "alloc-util.h" +#include "ether-addr-util.h" +#include "fd-util.h" +#include "hostname-util.h" +#include "log-link.h" +#include "network-common.h" +#include "random-util.h" +#include "socket-util.h" +#include "string-util.h" +#include "time-util.h" +#include "unaligned.h" +#include "web-util.h" + +/* The LLDP spec calls this "txFastInit", see 9.2.5.19 */ +#define LLDP_FAST_TX_INIT 4U + +/* The LLDP spec calls this "msgTxHold", see 9.2.5.6 */ +#define LLDP_TX_HOLD 4U + +/* The jitter range to add, see 9.2.2. */ +#define LLDP_TX_JITTER_USEC (400U * USEC_PER_MSEC) + +/* The LLDP spec calls this msgTxInterval, but we subtract half the jitter off it. */ +#define LLDP_TX_INTERVAL_USEC (30U * USEC_PER_SEC - LLDP_TX_JITTER_USEC / 2) + +/* The LLDP spec calls this msgFastTx, but we subtract half the jitter off it. */ +#define LLDP_FAST_TX_INTERVAL_USEC (1U * USEC_PER_SEC - LLDP_TX_JITTER_USEC / 2) + +#define LLDP_TX_TTL ((uint16_t) DIV_ROUND_UP(LLDP_TX_INTERVAL_USEC * LLDP_TX_HOLD + 1, USEC_PER_SEC)) + +static const struct ether_addr lldp_multicast_addr[_SD_LLDP_MULTICAST_MODE_MAX] = { + [SD_LLDP_MULTICAST_MODE_NEAREST_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }}, + [SD_LLDP_MULTICAST_MODE_NON_TPMR_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }}, + [SD_LLDP_MULTICAST_MODE_CUSTOMER_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }}, +}; + +struct sd_lldp_tx { + unsigned n_ref; + + int ifindex; + char *ifname; + + sd_event *event; + int64_t event_priority; + sd_event_source *timer_event_source; + + unsigned fast_tx; + + sd_lldp_multicast_mode_t mode; + struct ether_addr hwaddr; + + char *port_description; + char *hostname; + char *pretty_hostname; + char *mud_url; + uint16_t supported_capabilities; + uint16_t enabled_capabilities; +}; + +#define log_lldp_tx_errno(lldp_tx, error, fmt, ...) \ + log_interface_prefix_full_errno( \ + "LLDP Tx: ", \ + sd_lldp_tx_get_ifname(lldp_tx), \ + error, fmt, ##__VA_ARGS__) +#define log_lldp_tx(lldp_tx, fmt, ...) \ + log_interface_prefix_full_errno_zerook( \ + "LLDP Tx: ", \ + sd_lldp_tx_get_ifname(lldp_tx), \ + 0, fmt, ##__VA_ARGS__) + +static sd_lldp_tx *lldp_tx_free(sd_lldp_tx *lldp_tx) { + if (!lldp_tx) + return NULL; + + sd_lldp_tx_detach_event(lldp_tx); + + free(lldp_tx->port_description); + free(lldp_tx->hostname); + free(lldp_tx->pretty_hostname); + free(lldp_tx->mud_url); + + free(lldp_tx->ifname); + return mfree(lldp_tx); +} + +DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_lldp_tx, sd_lldp_tx, lldp_tx_free); + +int sd_lldp_tx_new(sd_lldp_tx **ret) { + _cleanup_(sd_lldp_tx_unrefp) sd_lldp_tx *lldp_tx = NULL; + + assert_return(ret, -EINVAL); + + lldp_tx = new(sd_lldp_tx, 1); + if (!lldp_tx) + return -ENOMEM; + + *lldp_tx = (sd_lldp_tx) { + .n_ref = 1, + .mode = _SD_LLDP_MULTICAST_MODE_INVALID, + }; + + *ret = TAKE_PTR(lldp_tx); + return 0; +} + +int sd_lldp_tx_set_ifindex(sd_lldp_tx *lldp_tx, int ifindex) { + assert_return(lldp_tx, -EINVAL); + assert_return(ifindex > 0, -EINVAL); + + lldp_tx->ifindex = ifindex; + return 0; +} + +int sd_lldp_tx_set_ifname(sd_lldp_tx *lldp_tx, const char *ifname) { + assert_return(lldp_tx, -EINVAL); + assert_return(ifname, -EINVAL); + + if (!ifname_valid_full(ifname, IFNAME_VALID_ALTERNATIVE)) + return -EINVAL; + + return free_and_strdup(&lldp_tx->ifname, ifname); +} + +const char *sd_lldp_tx_get_ifname(sd_lldp_tx *lldp_tx) { + if (!lldp_tx) + return NULL; + + return get_ifname(lldp_tx->ifindex, &lldp_tx->ifname); +} + +int sd_lldp_tx_set_multicast_mode(sd_lldp_tx *lldp_tx, sd_lldp_multicast_mode_t mode) { + assert_return(lldp_tx, -EINVAL); + assert_return(mode >= 0 && mode < _SD_LLDP_MULTICAST_MODE_MAX, -EINVAL); + + lldp_tx->mode = mode; + return 0; +} + +int sd_lldp_tx_set_hwaddr(sd_lldp_tx *lldp_tx, const struct ether_addr *hwaddr) { + assert_return(lldp_tx, -EINVAL); + assert_return(!ether_addr_is_null(hwaddr), -EINVAL); + + lldp_tx->hwaddr = *hwaddr; + return 0; +} + +int sd_lldp_tx_set_capabilities(sd_lldp_tx *lldp_tx, uint16_t supported, uint16_t enabled) { + assert_return(lldp_tx, -EINVAL); + assert_return((enabled & ~supported) == 0, -EINVAL); + + lldp_tx->supported_capabilities = supported; + lldp_tx->enabled_capabilities = enabled; + return 0; +} + +int sd_lldp_tx_set_port_description(sd_lldp_tx *lldp_tx, const char *port_description) { + assert_return(lldp_tx, -EINVAL); + + /* An empty string unset the previously set hostname. */ + if (strlen_ptr(port_description) >= 512) + return -EINVAL; + + return free_and_strdup(&lldp_tx->port_description, empty_to_null(port_description)); +} + +int sd_lldp_tx_set_hostname(sd_lldp_tx *lldp_tx, const char *hostname) { + assert_return(lldp_tx, -EINVAL); + + /* An empty string unset the previously set hostname. */ + if (!isempty(hostname)) { + assert_cc(HOST_NAME_MAX < 512); + + if (!hostname_is_valid(hostname, 0)) + return -EINVAL; + } + + return free_and_strdup(&lldp_tx->hostname, empty_to_null(hostname)); +} + +int sd_lldp_tx_set_pretty_hostname(sd_lldp_tx *lldp_tx, const char *pretty_hostname) { + assert_return(lldp_tx, -EINVAL); + + /* An empty string unset the previously set hostname. */ + if (strlen_ptr(pretty_hostname) >= 512) + return -EINVAL; + + return free_and_strdup(&lldp_tx->pretty_hostname, empty_to_null(pretty_hostname)); +} + +int sd_lldp_tx_set_mud_url(sd_lldp_tx *lldp_tx, const char *mud_url) { + assert_return(lldp_tx, -EINVAL); + + /* An empty string unset the previously set hostname. */ + if (!isempty(mud_url)) { + /* Unless the maximum length of each value is 511, the MUD url must be smaller than 256. + * See RFC 8520. */ + if (strlen(mud_url) >= 256) + return -EINVAL; + + if (!http_url_is_valid(mud_url)) + return -EINVAL; + } + + return free_and_strdup(&lldp_tx->mud_url, empty_to_null(mud_url)); +} + +static size_t lldp_tx_calculate_maximum_packet_size(sd_lldp_tx *lldp_tx, const char *hostname, const char *pretty_hostname) { + assert(lldp_tx); + assert(lldp_tx->ifindex > 0); + + return sizeof(struct ether_header) + + /* Chassis ID */ + 2 + 1 + (SD_ID128_STRING_MAX - 1) + + /* Port ID */ + 2 + 1 + strlen_ptr(sd_lldp_tx_get_ifname(lldp_tx)) + + /* Port description */ + 2 + strlen_ptr(lldp_tx->port_description) + + /* System name */ + 2 + strlen_ptr(hostname) + + /* System description */ + 2 + strlen_ptr(pretty_hostname) + + /* MUD URL */ + 2 + sizeof(SD_LLDP_OUI_IANA_MUD) + strlen_ptr(lldp_tx->mud_url) + + /* TTL */ + 2 + 2 + + /* System Capabilities */ + 2 + 4 + + /* End */ + 2; +} + +static int packet_append_tlv_header(uint8_t *packet, size_t packet_size, size_t *offset, uint8_t type, size_t data_len) { + assert(packet); + assert(offset); + + /* + * +--------+--------+-------------- + * |TLV Type| len | value + * |(7 bits)|(9 bits)|(0-511 octets) + * +--------+--------+-------------- + * where: + * + * len = indicates the length of value + */ + + /* The type field is 7-bits. */ + if (type >= 128) + return -EINVAL; + + /* The data length field is 9-bits. */ + if (data_len >= 512) + return -EINVAL; + + if (packet_size < 2 + data_len) + return -ENOBUFS; + + if (*offset > packet_size - 2 - data_len) + return -ENOBUFS; + + packet[(*offset)++] = (type << 1) | !!(data_len >> 8); + packet[(*offset)++] = data_len & (size_t) UINT8_MAX; + + return 0; +} + +static int packet_append_prefixed_string( + uint8_t *packet, + size_t packet_size, + size_t *offset, + uint8_t type, + size_t prefix_len, + const void *prefix, + const char *str) { + + size_t len; + int r; + + assert(packet); + assert(offset); + assert(prefix_len == 0 || prefix); + + if (isempty(str)) + return 0; + + len = strlen(str); + + /* Check for overflow */ + if (len > SIZE_MAX - prefix_len) + return -ENOBUFS; + + r = packet_append_tlv_header(packet, packet_size, offset, type, prefix_len + len); + if (r < 0) + return r; + + memcpy_safe(packet + *offset, prefix, prefix_len); + *offset += prefix_len; + + memcpy(packet + *offset, str, len); + *offset += len; + + return 0; +} + +static int packet_append_string( + uint8_t *packet, + size_t packet_size, + size_t *offset, + uint8_t type, + const char *str) { + + return packet_append_prefixed_string(packet, packet_size, offset, type, 0, NULL, str); +} + +static int lldp_tx_create_packet(sd_lldp_tx *lldp_tx, size_t *ret_packet_size, uint8_t **ret_packet) { + _cleanup_free_ char *hostname = NULL, *pretty_hostname = NULL; + _cleanup_free_ uint8_t *packet = NULL; + struct ether_header *header; + size_t packet_size, offset; + sd_id128_t machine_id; + int r; + + assert(lldp_tx); + assert(lldp_tx->ifindex > 0); + assert(ret_packet_size); + assert(ret_packet); + + r = sd_id128_get_machine(&machine_id); + if (r < 0) + return r; + + if (!lldp_tx->hostname) + (void) gethostname_strict(&hostname); + if (!lldp_tx->pretty_hostname) + (void) get_pretty_hostname(&pretty_hostname); + + packet_size = lldp_tx_calculate_maximum_packet_size(lldp_tx, + lldp_tx->hostname ?: hostname, + lldp_tx->pretty_hostname ?: pretty_hostname); + + packet = new(uint8_t, packet_size); + if (!packet) + return -ENOMEM; + + header = (struct ether_header*) packet; + header->ether_type = htobe16(ETHERTYPE_LLDP); + memcpy(header->ether_dhost, lldp_multicast_addr + lldp_tx->mode, ETH_ALEN); + memcpy(header->ether_shost, &lldp_tx->hwaddr, ETH_ALEN); + + offset = sizeof(struct ether_header); + r = packet_append_prefixed_string(packet, packet_size, &offset, SD_LLDP_TYPE_CHASSIS_ID, + 1, (const uint8_t[]) { SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED }, + SD_ID128_TO_STRING(machine_id)); + if (r < 0) + return r; + + r = packet_append_prefixed_string(packet, packet_size, &offset, SD_LLDP_TYPE_PORT_ID, + 1, (const uint8_t[]) { SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME }, + sd_lldp_tx_get_ifname(lldp_tx)); + if (r < 0) + return r; + + r = packet_append_string(packet, packet_size, &offset, SD_LLDP_TYPE_PORT_DESCRIPTION, + lldp_tx->port_description); + if (r < 0) + return r; + + r = packet_append_string(packet, packet_size, &offset, SD_LLDP_TYPE_SYSTEM_NAME, + lldp_tx->hostname ?: hostname); + if (r < 0) + return r; + + r = packet_append_string(packet, packet_size, &offset, SD_LLDP_TYPE_SYSTEM_DESCRIPTION, + lldp_tx->pretty_hostname ?: pretty_hostname); + if (r < 0) + return r; + + /* See section 12 of RFC 8520. + * +--------+--------+----------+---------+-------------- + * |TLV Type| len | OUI |subtype | MUDString + * | =127 | |= 00 00 5E| = 1 | + * |(7 bits)|(9 bits)|(3 octets)|(1 octet)|(1-255 octets) + * +--------+--------+----------+---------+-------------- + * where: + * + * o TLV Type = 127 indicates a vendor-specific TLV + * o len = indicates the TLV string length + * o OUI = 00 00 5E is the organizationally unique identifier of IANA + * o subtype = 1 (as assigned by IANA for the MUDstring) + * o MUDstring = the length MUST NOT exceed 255 octets + */ + r = packet_append_prefixed_string(packet, packet_size, &offset, SD_LLDP_TYPE_PRIVATE, + sizeof(SD_LLDP_OUI_IANA_MUD), SD_LLDP_OUI_IANA_MUD, + lldp_tx->mud_url); + if (r < 0) + return r; + + r = packet_append_tlv_header(packet, packet_size, &offset, SD_LLDP_TYPE_TTL, 2); + if (r < 0) + return r; + + unaligned_write_be16(packet + offset, LLDP_TX_TTL); + offset += 2; + + r = packet_append_tlv_header(packet, packet_size, &offset, SD_LLDP_TYPE_SYSTEM_CAPABILITIES, 4); + if (r < 0) + return r; + + unaligned_write_be16(packet + offset, lldp_tx->supported_capabilities); + offset += 2; + unaligned_write_be16(packet + offset, lldp_tx->enabled_capabilities); + offset += 2; + + r = packet_append_tlv_header(packet, packet_size, &offset, SD_LLDP_TYPE_END, 0); + if (r < 0) + return r; + + *ret_packet_size = offset; + *ret_packet = TAKE_PTR(packet); + return 0; +} + +static int lldp_tx_send_packet(sd_lldp_tx *lldp_tx, size_t packet_size, const uint8_t *packet) { + _cleanup_close_ int fd = -1; + union sockaddr_union sa; + ssize_t l; + + assert(lldp_tx); + assert(lldp_tx->ifindex > 0); + assert(packet_size > sizeof(struct ether_header)); + assert(packet); + + sa = (union sockaddr_union) { + .ll.sll_family = AF_PACKET, + .ll.sll_protocol = htobe16(ETHERTYPE_LLDP), + .ll.sll_ifindex = lldp_tx->ifindex, + .ll.sll_halen = ETH_ALEN, + }; + memcpy(sa.ll.sll_addr, lldp_multicast_addr + lldp_tx->mode, ETH_ALEN); + + fd = socket(AF_PACKET, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW); + if (fd < 0) + return -errno; + + l = sendto(fd, packet, packet_size, MSG_NOSIGNAL, &sa.sa, sizeof(sa.ll)); + if (l < 0) + return -errno; + + if ((size_t) l != packet_size) + return -EIO; + + return 0; +} + +static int lldp_tx_send(sd_lldp_tx *lldp_tx) { + _cleanup_free_ uint8_t *packet = NULL; + size_t packet_size = 0; /* avoid false maybe-uninitialized warning */ + int r; + + assert(lldp_tx); + + r = lldp_tx_create_packet(lldp_tx, &packet_size, &packet); + if (r < 0) + return r; + + return lldp_tx_send_packet(lldp_tx, packet_size, packet); +} + +int sd_lldp_tx_attach_event(sd_lldp_tx *lldp_tx, sd_event *event, int64_t priority) { + int r; + + assert_return(lldp_tx, -EINVAL); + assert_return(!lldp_tx->event, -EBUSY); + + if (event) + lldp_tx->event = sd_event_ref(event); + else { + r = sd_event_default(&lldp_tx->event); + if (r < 0) + return r; + } + + lldp_tx->event_priority = priority; + + return 0; +} + +int sd_lldp_tx_detach_event(sd_lldp_tx *lldp_tx) { + assert_return(lldp_tx, -EINVAL); + + lldp_tx->timer_event_source = sd_event_source_disable_unref(lldp_tx->timer_event_source); + lldp_tx->event = sd_event_unref(lldp_tx->event); + return 0; +} + +sd_event* sd_lldp_tx_get_event(sd_lldp_tx *lldp_tx) { + assert_return(lldp_tx, NULL); + + return lldp_tx->event; +} + +static usec_t lldp_tx_get_delay(sd_lldp_tx *lldp_tx) { + assert(lldp_tx); + + return usec_add(lldp_tx->fast_tx > 0 ? LLDP_FAST_TX_INTERVAL_USEC : LLDP_TX_INTERVAL_USEC, + (usec_t) random_u64() % LLDP_TX_JITTER_USEC); +} + +static int lldp_tx_reset_timer(sd_lldp_tx *lldp_tx) { + usec_t delay; + int r; + + assert(lldp_tx); + assert(lldp_tx->timer_event_source); + + delay = lldp_tx_get_delay(lldp_tx); + + r = sd_event_source_set_time_relative(lldp_tx->timer_event_source, delay); + if (r < 0) + return r; + + return sd_event_source_set_enabled(lldp_tx->timer_event_source, SD_EVENT_ONESHOT); +} + +static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) { + sd_lldp_tx *lldp_tx = userdata; + int r; + + assert(lldp_tx); + + r = lldp_tx_send(lldp_tx); + if (r < 0) + log_lldp_tx_errno(lldp_tx, r, "Failed to send packet, ignoring: %m"); + + if (lldp_tx->fast_tx > 0) + lldp_tx->fast_tx--; + + r = lldp_tx_reset_timer(lldp_tx); + if (r < 0) + log_lldp_tx_errno(lldp_tx, r, "Failed to reset timer: %m"); + + return 0; +} + +int sd_lldp_tx_is_running(sd_lldp_tx *lldp_tx) { + int enabled; + + if (!lldp_tx) + return 0; + + if (!lldp_tx->timer_event_source) + return 0; + + if (sd_event_source_get_enabled(lldp_tx->timer_event_source, &enabled) < 0) + return 0; + + return enabled == SD_EVENT_ONESHOT; +} + +int sd_lldp_tx_stop(sd_lldp_tx *lldp_tx) { + if (!lldp_tx) + return 0; + + if (!lldp_tx->timer_event_source) + return 0; + + (void) sd_event_source_set_enabled(lldp_tx->timer_event_source, SD_EVENT_OFF); + + return 1; +} +int sd_lldp_tx_start(sd_lldp_tx *lldp_tx) { + usec_t delay; + int r; + + assert_return(lldp_tx, -EINVAL); + assert_return(lldp_tx->event, -EINVAL); + assert_return(lldp_tx->ifindex > 0, -EINVAL); + assert_return(lldp_tx->mode >= 0 && lldp_tx->mode < _SD_LLDP_MULTICAST_MODE_MAX, -EINVAL); + assert_return(!ether_addr_is_null(&lldp_tx->hwaddr), -EINVAL); + + if (sd_lldp_tx_is_running(lldp_tx)) + return 0; + + lldp_tx->fast_tx = LLDP_FAST_TX_INIT; + + if (lldp_tx->timer_event_source) { + r = lldp_tx_reset_timer(lldp_tx); + if (r < 0) + return log_lldp_tx_errno(lldp_tx, r, "Failed to re-enable timer: %m"); + + return 0; + } + + delay = lldp_tx_get_delay(lldp_tx); + + r = sd_event_add_time_relative(lldp_tx->event, &lldp_tx->timer_event_source, + clock_boottime_or_monotonic(), delay, 0, + on_timer_event, lldp_tx); + if (r < 0) + return r; + + (void) sd_event_source_set_description(lldp_tx->timer_event_source, "lldp-tx-timer"); + (void) sd_event_source_set_priority(lldp_tx->timer_event_source, lldp_tx->event_priority); + + return 0; +} diff --git a/src/libsystemd-network/sd-lldp.c b/src/libsystemd-network/sd-lldp.c deleted file mode 100644 index b38d6dbd1e..0000000000 --- a/src/libsystemd-network/sd-lldp.c +++ /dev/null @@ -1,523 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#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 "network-common.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_t); - -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_t event, sd_lldp_neighbor *n) { - assert(lldp); - assert(event >= 0 && event < _SD_LLDP_EVENT_MAX); - - if (!lldp->callback) - return (void) log_lldp(lldp, "Received '%s' event.", lldp_event_to_string(event)); - - log_lldp(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(lldp, r, "Failed to add datagram. Ignoring."); - return 0; - } - - log_lldp(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) { - log_lldp_errno(lldp, space, "Failed to determine datagram size to read, ignoring: %m"); - return 0; - } - - 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; - - log_lldp_errno(lldp, errno, "Failed to read LLDP datagram, ignoring: %m"); - return 0; - } - - if ((size_t) length != n->raw_size) { - log_lldp(lldp, "Packet size mismatch, ignoring"); - return 0; - } - - /* 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_disable_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(lldp, "Started LLDP client"); - return 1; - -fail: - lldp_reset(lldp); - return r; -} - -_public_ int sd_lldp_stop(sd_lldp *lldp) { - if (!lldp) - return 0; - - if (lldp->fd < 0) - return 0; - - log_lldp(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; -} - -int sd_lldp_set_ifname(sd_lldp *lldp, const char *ifname) { - assert_return(lldp, -EINVAL); - assert_return(ifname, -EINVAL); - - if (!ifname_valid_full(ifname, IFNAME_VALID_ALTERNATIVE)) - return -EINVAL; - - return free_and_strdup(&lldp->ifname, ifname); -} - -const char *sd_lldp_get_ifname(sd_lldp *lldp) { - if (!lldp) - return NULL; - - return get_ifname(lldp->ifindex, &lldp->ifname); -} - -static sd_lldp* lldp_free(sd_lldp *lldp) { - assert(lldp); - - lldp_reset(lldp); - - sd_event_source_unref(lldp->timer_event_source); - sd_lldp_detach_event(lldp); - - lldp_flush_neighbors(lldp); - - hashmap_free(lldp->neighbor_by_id); - prioq_free(lldp->neighbor_by_expiry); - free(lldp->ifname); - 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_MAX, - }; - - 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) { - log_lldp_errno(lldp, r, "Failed to make space, ignoring: %m"); - return 0; - } - - r = lldp_start_timer(lldp, NULL); - if (r < 0) { - log_lldp_errno(lldp, r, "Failed to restart timer, ignoring: %m"); - return 0; - } - - 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; - 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) - 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-rx.c index 0331e387f0..11049a85b9 100644 --- a/src/libsystemd-network/test-lldp.c +++ b/src/libsystemd-network/test-lldp-rx.c @@ -7,7 +7,7 @@ #include <unistd.h> #include "sd-event.h" -#include "sd-lldp.h" +#include "sd-lldp-rx.h" #include "alloc-util.h" #include "fd-util.h" @@ -21,7 +21,7 @@ #define TEST_LLDP_TYPE_SYSTEM_DESC "systemd-lldp-desc" static int test_fd[2] = { -1, -1 }; -static int lldp_handler_calls; +static int lldp_rx_handler_calls; int lldp_network_bind_raw_socket(int ifindex) { if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0) @@ -30,48 +30,48 @@ int lldp_network_bind_raw_socket(int ifindex) { return test_fd[0]; } -static void lldp_handler(sd_lldp *lldp, sd_lldp_event_t event, sd_lldp_neighbor *n, void *userdata) { - lldp_handler_calls++; +static void lldp_rx_handler(sd_lldp_rx *lldp_rx, sd_lldp_rx_event_t event, sd_lldp_neighbor *n, void *userdata) { + lldp_rx_handler_calls++; } -static int start_lldp(sd_lldp **lldp, sd_event *e, sd_lldp_callback_t cb, void *cb_data) { +static int start_lldp_rx(sd_lldp_rx **lldp_rx, sd_event *e, sd_lldp_rx_callback_t cb, void *cb_data) { int r; - r = sd_lldp_new(lldp); + r = sd_lldp_rx_new(lldp_rx); if (r < 0) return r; - r = sd_lldp_set_ifindex(*lldp, 42); + r = sd_lldp_rx_set_ifindex(*lldp_rx, 42); if (r < 0) return r; - r = sd_lldp_set_callback(*lldp, cb, cb_data); + r = sd_lldp_rx_set_callback(*lldp_rx, cb, cb_data); if (r < 0) return r; - r = sd_lldp_attach_event(*lldp, e, 0); + r = sd_lldp_rx_attach_event(*lldp_rx, e, 0); if (r < 0) return r; - r = sd_lldp_start(*lldp); + r = sd_lldp_rx_start(*lldp_rx); if (r < 0) return r; return 0; } -static int stop_lldp(sd_lldp *lldp) { +static int stop_lldp_rx(sd_lldp_rx *lldp_rx) { int r; - r = sd_lldp_stop(lldp); + r = sd_lldp_rx_stop(lldp_rx); if (r < 0) return r; - r = sd_lldp_detach_event(lldp); + r = sd_lldp_rx_detach_event(lldp_rx); if (r < 0) return r; - sd_lldp_unref(lldp); + sd_lldp_rx_unref(lldp_rx); safe_close(test_fd[1]); return 0; @@ -96,7 +96,7 @@ static void test_receive_basic_packet(sd_event *e) { 0x00, 0x00 /* End Of LLDPDU */ }; - sd_lldp *lldp; + sd_lldp_rx *lldp_rx; sd_lldp_neighbor **neighbors; uint8_t type; const void *data; @@ -104,13 +104,13 @@ static void test_receive_basic_packet(sd_event *e) { size_t length; const char *str; - lldp_handler_calls = 0; - assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0); + lldp_rx_handler_calls = 0; + assert_se(start_lldp_rx(&lldp_rx, e, lldp_rx_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(lldp_rx_handler_calls == 1); + assert_se(sd_lldp_rx_get_neighbors(lldp_rx, &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); @@ -137,11 +137,11 @@ static void test_receive_basic_packet(sd_event *e) { sd_lldp_neighbor_unref(neighbors[0]); free(neighbors); - assert_se(stop_lldp(lldp) == 0); + assert_se(stop_lldp_rx(lldp_rx) == 0); } static void test_receive_incomplete_packet(sd_event *e) { - sd_lldp *lldp; + sd_lldp_rx *lldp_rx; sd_lldp_neighbor **neighbors; uint8_t frame[] = { /* Ethernet header */ @@ -156,19 +156,19 @@ static void test_receive_incomplete_packet(sd_event *e) { 0x00, 0x00 /* End Of LLDPDU */ }; - lldp_handler_calls = 0; - assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0); + lldp_rx_handler_calls = 0; + assert_se(start_lldp_rx(&lldp_rx, e, lldp_rx_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(lldp_rx_handler_calls == 0); + assert_se(sd_lldp_rx_get_neighbors(lldp_rx, &neighbors) == 0); - assert_se(stop_lldp(lldp) == 0); + assert_se(stop_lldp_rx(lldp_rx) == 0); } static void test_receive_oui_packet(sd_event *e) { - sd_lldp *lldp; + sd_lldp_rx *lldp_rx; sd_lldp_neighbor **neighbors; uint8_t frame[] = { /* Ethernet header */ @@ -197,13 +197,13 @@ static void test_receive_oui_packet(sd_event *e) { 0x00, 0x00 /* End of LLDPDU */ }; - lldp_handler_calls = 0; - assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0); + lldp_rx_handler_calls = 0; + assert_se(start_lldp_rx(&lldp_rx, e, lldp_rx_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(lldp_rx_handler_calls == 1); + assert_se(sd_lldp_rx_get_neighbors(lldp_rx, &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); @@ -230,7 +230,7 @@ static void test_receive_oui_packet(sd_event *e) { sd_lldp_neighbor_unref(neighbors[0]); free(neighbors); - assert_se(stop_lldp(lldp) == 0); + assert_se(stop_lldp_rx(lldp_rx) == 0); } static void test_multiple_neighbors_sorted(sd_event *e) { @@ -311,7 +311,7 @@ static void test_multiple_neighbors_sorted(sd_event *e) { "2/19", "1/0", }; - sd_lldp *lldp; + sd_lldp_rx *lldp_rx; sd_lldp_neighbor **neighbors; int i; uint8_t type; @@ -319,8 +319,8 @@ static void test_multiple_neighbors_sorted(sd_event *e) { size_t length, expected_length; uint16_t ttl; - lldp_handler_calls = 0; - assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0); + lldp_rx_handler_calls = 0; + assert_se(start_lldp_rx(&lldp_rx, e, lldp_rx_handler, NULL) == 0); assert_se(write(test_fd[1], frame1, sizeof(frame1)) == sizeof(frame1)); sd_event_run(e, 0); @@ -334,9 +334,9 @@ static void test_multiple_neighbors_sorted(sd_event *e) { 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(lldp_rx_handler_calls == 6); - assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 6); + assert_se(sd_lldp_rx_get_neighbors(lldp_rx, &neighbors) == 6); for (i = 0; i < 6; i++) { assert_se(sd_lldp_neighbor_get_chassis_id(neighbors[i], &type, &data, &length) == 0); @@ -359,7 +359,7 @@ static void test_multiple_neighbors_sorted(sd_event *e) { sd_lldp_neighbor_unref(neighbors[i]); free(neighbors); - assert_se(stop_lldp(lldp) == 0); + assert_se(stop_lldp_rx(lldp_rx) == 0); } int main(int argc, char *argv[]) { diff --git a/src/network/networkctl.c b/src/network/networkctl.c index 09a19192bc..d4a7da9f71 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -15,7 +15,7 @@ #include "sd-device.h" #include "sd-dhcp-client.h" #include "sd-hwdb.h" -#include "sd-lldp.h" +#include "sd-lldp-rx.h" #include "sd-netlink.h" #include "sd-network.h" diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index ad39f690f6..467f58873c 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -209,8 +209,8 @@ static void link_free_engines(Link *link) { link->dhcp_client = sd_dhcp_client_unref(link->dhcp_client); link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease); - link->lldp = sd_lldp_unref(link->lldp); - link_lldp_emit_stop(link); + link->lldp_rx = sd_lldp_rx_unref(link->lldp_rx); + link->lldp_tx = sd_lldp_tx_unref(link->lldp_tx); ndisc_flush(link); @@ -375,9 +375,13 @@ int link_stop_engines(Link *link, bool may_keep_dhcp) { if (k < 0) r = log_link_warning_errno(link, k, "Could not stop DHCPv4 server: %m"); - k = sd_lldp_stop(link->lldp); + k = sd_lldp_rx_stop(link->lldp_rx); if (k < 0) - r = log_link_warning_errno(link, k, "Could not stop LLDP: %m"); + r = log_link_warning_errno(link, k, "Could not stop LLDP Rx: %m"); + + k = sd_lldp_tx_stop(link->lldp_tx); + if (k < 0) + r = log_link_warning_errno(link, k, "Could not stop LLDP Tx: %m"); k = sd_ipv4ll_stop(link->ipv4ll); if (k < 0) @@ -403,7 +407,6 @@ int link_stop_engines(Link *link, bool may_keep_dhcp) { if (k < 0) r = log_link_warning_errno(link, k, "Could not stop IPv6 Router Advertisement: %m"); - link_lldp_emit_stop(link); return r; } @@ -686,12 +689,14 @@ static int link_acquire_dynamic_conf(Link *link) { return r; } - r = link_lldp_emit_start(link); - if (r < 0) - return log_link_warning_errno(link, r, "Failed to start LLDP transmission: %m"); + if (link->lldp_tx) { + r = sd_lldp_tx_start(link->lldp_tx); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to start LLDP transmission: %m"); + } - if (link->lldp) { - r = sd_lldp_start(link->lldp); + if (link->lldp_rx) { + r = sd_lldp_rx_start(link->lldp_rx); if (r < 0) return log_link_warning_errno(link, r, "Failed to start LLDP client: %m"); } @@ -1162,6 +1167,10 @@ static int link_configure(Link *link) { if (r < 0) return r; + r = link_lldp_tx_configure(link); + if (r < 0) + return r; + r = link_drop_foreign_config(link); if (r < 0) return r; @@ -2102,10 +2111,16 @@ static int link_update_hardware_address(Link *link, sd_netlink_message *message) return log_link_debug_errno(link, r, "Could not update MAC for NDisc: %m"); } - if (link->lldp) { - r = sd_lldp_set_filter_address(link->lldp, &link->hw_addr.ether); + if (link->lldp_rx) { + r = sd_lldp_rx_set_filter_address(link->lldp_rx, &link->hw_addr.ether); + if (r < 0) + return log_link_debug_errno(link, r, "Could not update MAC address for LLDP Rx: %m"); + } + + if (link->lldp_tx) { + r = sd_lldp_tx_set_hwaddr(link->lldp_tx, &link->hw_addr.ether); if (r < 0) - return log_link_debug_errno(link, r, "Could not update MAC address for LLDP: %m"); + return log_link_debug_errno(link, r, "Could not update MAC address for LLDP Tx: %m"); } return 0; @@ -2266,10 +2281,16 @@ static int link_update_name(Link *link, sd_netlink_message *message) { return log_link_debug_errno(link, r, "Failed to update interface name in Router Advertisement: %m"); } - if (link->lldp) { - r = sd_lldp_set_ifname(link->lldp, link->ifname); + if (link->lldp_rx) { + r = sd_lldp_rx_set_ifname(link->lldp_rx, link->ifname); + if (r < 0) + return log_link_debug_errno(link, r, "Failed to update interface name in LLDP Rx: %m"); + } + + if (link->lldp_tx) { + r = sd_lldp_tx_set_ifname(link->lldp_tx, link->ifname); if (r < 0) - return log_link_debug_errno(link, r, "Failed to update interface name in LLDP: %m"); + return log_link_debug_errno(link, r, "Failed to update interface name in LLDP Tx: %m"); } if (link->ipv4ll) { diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 7190310a13..9424a10830 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -11,7 +11,8 @@ #include "sd-dhcp6-client.h" #include "sd-ipv4acd.h" #include "sd-ipv4ll.h" -#include "sd-lldp.h" +#include "sd-lldp-rx.h" +#include "sd-lldp-tx.h" #include "sd-ndisc.h" #include "sd-radv.h" #include "sd-netlink.h" @@ -171,12 +172,11 @@ typedef struct Link { bool dhcp6_pd_prefixes_assigned:1; /* This is about LLDP reception */ - sd_lldp *lldp; + sd_lldp_rx *lldp_rx; char *lldp_file; /* This is about LLDP transmission */ - unsigned lldp_tx_fast; /* The LLDP txFast counter (See 802.1ab-2009, section 9.2.5.18) */ - sd_event_source *lldp_emit_event_source; + sd_lldp_tx *lldp_tx; Hashmap *bound_by_links; Hashmap *bound_to_links; diff --git a/src/network/networkd-lldp-rx.c b/src/network/networkd-lldp-rx.c index 57c8446ade..c441047a37 100644 --- a/src/network/networkd-lldp-rx.c +++ b/src/network/networkd-lldp-rx.c @@ -48,7 +48,7 @@ static bool link_lldp_rx_enabled(Link *link) { return link->network->lldp_mode != LLDP_MODE_NO; } -static void lldp_handler(sd_lldp *lldp, sd_lldp_event_t event, sd_lldp_neighbor *n, void *userdata) { +static void lldp_rx_handler(sd_lldp_rx *lldp_rx, sd_lldp_rx_event_t event, sd_lldp_neighbor *n, void *userdata) { Link *link = userdata; int r; @@ -56,12 +56,13 @@ static void lldp_handler(sd_lldp *lldp, sd_lldp_event_t event, sd_lldp_neighbor (void) link_lldp_save(link); - if (link_lldp_emit_enabled(link) && event == SD_LLDP_EVENT_ADDED) { + if (link->lldp_tx && event == SD_LLDP_RX_EVENT_ADDED) { /* If we received information about a new neighbor, restart the LLDP "fast" logic */ log_link_debug(link, "Received LLDP datagram from previously unknown neighbor, restarting 'fast' LLDP transmission."); - r = link_lldp_emit_start(link); + (void) sd_lldp_tx_stop(link->lldp_tx); + r = sd_lldp_tx_start(link->lldp_tx); if (r < 0) log_link_warning_errno(link, r, "Failed to restart LLDP transmission: %m"); } @@ -73,33 +74,33 @@ int link_lldp_rx_configure(Link *link) { if (!link_lldp_rx_enabled(link)) return 0; - if (link->lldp) + if (link->lldp_rx) return -EBUSY; - r = sd_lldp_new(&link->lldp); + r = sd_lldp_rx_new(&link->lldp_rx); if (r < 0) return r; - r = sd_lldp_attach_event(link->lldp, link->manager->event, 0); + r = sd_lldp_rx_attach_event(link->lldp_rx, link->manager->event, 0); if (r < 0) return r; - r = sd_lldp_set_ifindex(link->lldp, link->ifindex); + r = sd_lldp_rx_set_ifindex(link->lldp_rx, link->ifindex); if (r < 0) return r; - r = sd_lldp_match_capabilities(link->lldp, - link->network->lldp_mode == LLDP_MODE_ROUTERS_ONLY ? - SD_LLDP_SYSTEM_CAPABILITIES_ALL_ROUTERS : - SD_LLDP_SYSTEM_CAPABILITIES_ALL); + r = sd_lldp_rx_match_capabilities(link->lldp_rx, + link->network->lldp_mode == LLDP_MODE_ROUTERS_ONLY ? + SD_LLDP_SYSTEM_CAPABILITIES_ALL_ROUTERS : + SD_LLDP_SYSTEM_CAPABILITIES_ALL); if (r < 0) return r; - r = sd_lldp_set_filter_address(link->lldp, &link->hw_addr.ether); + r = sd_lldp_rx_set_filter_address(link->lldp_rx, &link->hw_addr.ether); if (r < 0) return r; - r = sd_lldp_set_callback(link->lldp, lldp_handler, link); + r = sd_lldp_rx_set_callback(link->lldp_rx, lldp_rx_handler, link); if (r < 0) return r; @@ -115,12 +116,12 @@ int link_lldp_save(Link *link) { assert(link); assert(link->lldp_file); - if (!link->lldp) { + if (!link->lldp_rx) { (void) unlink(link->lldp_file); return 0; } - r = sd_lldp_get_neighbors(link->lldp, &l); + r = sd_lldp_rx_get_neighbors(link->lldp_rx, &l); if (r < 0) return r; if (r == 0) { diff --git a/src/network/networkd-lldp-tx.c b/src/network/networkd-lldp-tx.c index c1cbe60d73..a32f6b8e27 100644 --- a/src/network/networkd-lldp-tx.c +++ b/src/network/networkd-lldp-tx.c @@ -1,48 +1,19 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include <endian.h> -#include <inttypes.h> #include <net/if.h> #include <net/if_arp.h> -#include "alloc-util.h" -#include "env-file.h" -#include "fd-util.h" -#include "hostname-util.h" -#include "missing_network.h" +#include "sd-lldp-tx.h" + #include "networkd-link.h" #include "networkd-lldp-tx.h" #include "networkd-manager.h" #include "parse-util.h" -#include "random-util.h" -#include "socket-util.h" #include "string-table.h" #include "string-util.h" #include "strv.h" -#include "unaligned.h" - -/* The LLDP spec calls this "txFastInit", see 9.2.5.19 */ -#define LLDP_TX_FAST_INIT 4U - -/* The LLDP spec calls this "msgTxHold", see 9.2.5.6 */ -#define LLDP_TX_HOLD 4U - -/* The jitter range to add, see 9.2.2. */ -#define LLDP_JITTER_USEC (400U * USEC_PER_MSEC) -/* The LLDP spec calls this msgTxInterval, but we subtract half the jitter off it. */ -#define LLDP_TX_INTERVAL_USEC (30U * USEC_PER_SEC - LLDP_JITTER_USEC / 2) - -/* The LLDP spec calls this msgFastTx, but we subtract half the jitter off it. */ -#define LLDP_FAST_TX_USEC (1U * USEC_PER_SEC - LLDP_JITTER_USEC / 2) - -static const struct ether_addr lldp_multicast_addr[_LLDP_EMIT_MAX] = { - [LLDP_EMIT_NEAREST_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }}, - [LLDP_EMIT_NON_TPMR_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }}, - [LLDP_EMIT_CUSTOMER_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }}, -}; - -bool link_lldp_emit_enabled(Link *link) { +static bool link_lldp_tx_enabled(Link *link) { assert(link); if (link->flags & IFF_LOOPBACK) @@ -57,368 +28,109 @@ bool link_lldp_emit_enabled(Link *link) { if (link->kind && STR_IN_SET(link->kind, "bridge", "bond")) return false; - return link->network->lldp_emit != LLDP_EMIT_NO; + return link->network->lldp_multicast_mode >= 0 && + link->network->lldp_multicast_mode < _SD_LLDP_MULTICAST_MODE_MAX; } -static int lldp_write_tlv_header(uint8_t **p, uint8_t id, size_t sz) { - assert(p); - - if (id > 127) - return -EBADMSG; - if (sz > 511) - return -ENOBUFS; - - (*p)[0] = (id << 1) | !!(sz & 256); - (*p)[1] = sz & 255; - - *p = *p + 2; - return 0; -} - -static int lldp_make_packet( - LLDPEmit mode, - const struct ether_addr *hwaddr, - const char *machine_id, - const char *ifname, - uint16_t ttl, - const char *port_description, - const char *hostname, - const char *pretty_hostname, - uint16_t system_capabilities, - uint16_t enabled_capabilities, - char *mud, - void **ret, size_t *sz) { - - size_t machine_id_length, ifname_length, port_description_length = 0, hostname_length = 0, - pretty_hostname_length = 0, mud_length = 0; - _cleanup_free_ void *packet = NULL; - struct ether_header *h; - uint8_t *p; - size_t l; +int link_lldp_tx_configure(Link *link) { int r; - assert(mode > LLDP_EMIT_NO); - assert(mode < _LLDP_EMIT_MAX); - assert(hwaddr); - assert(machine_id); - assert(ifname); - assert(ret); - assert(sz); - - machine_id_length = strlen(machine_id); - ifname_length = strlen(ifname); - - if (port_description) - port_description_length = strlen(port_description); - - if (hostname) - hostname_length = strlen(hostname); - - if (pretty_hostname) - pretty_hostname_length = strlen(pretty_hostname); - - if (mud) - mud_length = strlen(mud); - - l = sizeof(struct ether_header) + - /* Chassis ID */ - 2 + 1 + machine_id_length + - /* Port ID */ - 2 + 1 + ifname_length + - /* TTL */ - 2 + 2 + - /* System Capabilities */ - 2 + 4 + - /* End */ - 2; - - /* Port Description */ - if (port_description) - l += 2 + port_description_length; - - /* System Name */ - if (hostname) - l += 2 + hostname_length; - - /* System Description */ - if (pretty_hostname) - l += 2 + pretty_hostname_length; - - /* MUD URL */ - if (mud) - l += 2 + sizeof(SD_LLDP_OUI_MUD) + 1 + mud_length; - - packet = malloc(l); - if (!packet) - return -ENOMEM; + assert(link); - h = (struct ether_header*) packet; - h->ether_type = htobe16(ETHERTYPE_LLDP); - memcpy(h->ether_dhost, lldp_multicast_addr + mode, ETH_ALEN); - memcpy(h->ether_shost, hwaddr, ETH_ALEN); + if (!link_lldp_tx_enabled(link)) + return 0; - p = (uint8_t*) packet + sizeof(struct ether_header); + if (link->lldp_tx) + return -EBUSY; - r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_CHASSIS_ID, 1 + machine_id_length); + r = sd_lldp_tx_new(&link->lldp_tx); if (r < 0) return r; - *(p++) = SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED; - p = mempcpy(p, machine_id, machine_id_length); - r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_PORT_ID, 1 + ifname_length); + r = sd_lldp_tx_attach_event(link->lldp_tx, link->manager->event, 0); if (r < 0) return r; - *(p++) = SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME; - p = mempcpy(p, ifname, ifname_length); - r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_TTL, 2); + r = sd_lldp_tx_set_ifindex(link->lldp_tx, link->ifindex); if (r < 0) return r; - unaligned_write_be16(p, ttl); - p += 2; - - if (port_description) { - r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_PORT_DESCRIPTION, port_description_length); - if (r < 0) - return r; - p = mempcpy(p, port_description, port_description_length); - } - - if (hostname) { - r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_SYSTEM_NAME, hostname_length); - if (r < 0) - return r; - p = mempcpy(p, hostname, hostname_length); - } - - if (pretty_hostname) { - r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_SYSTEM_DESCRIPTION, pretty_hostname_length); - if (r < 0) - return r; - p = mempcpy(p, pretty_hostname, pretty_hostname_length); - } - - if (mud) { - uint8_t oui_mud[sizeof(SD_LLDP_OUI_MUD)] = {0x00, 0x00, 0x5E}; - /* - * +--------+--------+----------+---------+-------------- - * |TLV Type| len | OUI |subtype | MUDString - * | =127 | |= 00 00 5E| = 1 | - * |(7 bits)|(9 bits)|(3 octets)|(1 octet)|(1-255 octets) - * +--------+--------+----------+---------+-------------- - * where: - - * o TLV Type = 127 indicates a vendor-specific TLV - * o len = indicates the TLV string length - * o OUI = 00 00 5E is the organizationally unique identifier of IANA - * o subtype = 1 (as assigned by IANA for the MUDstring) - * o MUDstring = the length MUST NOT exceed 255 octets - */ - - r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_PRIVATE, sizeof(SD_LLDP_OUI_MUD) + 1 + mud_length); - if (r < 0) - return r; - p = mempcpy(p, &oui_mud, sizeof(SD_LLDP_OUI_MUD)); - *(p++) = SD_LLDP_OUI_SUBTYPE_MUD_USAGE_DESCRIPTION; - p = mempcpy(p, mud, mud_length); - } - - r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_SYSTEM_CAPABILITIES, 4); + r = sd_lldp_tx_set_hwaddr(link->lldp_tx, &link->hw_addr.ether); if (r < 0) return r; - unaligned_write_be16(p, system_capabilities); - p += 2; - unaligned_write_be16(p, enabled_capabilities); - p += 2; - r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_END, 0); + r = sd_lldp_tx_set_multicast_mode(link->lldp_tx, link->network->lldp_multicast_mode); if (r < 0) return r; - assert(p == (uint8_t*) packet + l); - - *ret = TAKE_PTR(packet); - *sz = l; - - return 0; -} - -static int lldp_send_packet( - int ifindex, - const struct ether_addr *address, - const void *packet, - size_t packet_size) { - - union sockaddr_union sa = { - .ll.sll_family = AF_PACKET, - .ll.sll_protocol = htobe16(ETHERTYPE_LLDP), - .ll.sll_ifindex = ifindex, - .ll.sll_halen = ETH_ALEN, - }; - - _cleanup_close_ int fd = -1; - ssize_t l; - - assert(ifindex > 0); - assert(address); - assert(packet || packet_size <= 0); - - memcpy(sa.ll.sll_addr, address, ETH_ALEN); - - fd = socket(AF_PACKET, SOCK_RAW|SOCK_CLOEXEC, IPPROTO_RAW); - if (fd < 0) - return -errno; - - l = sendto(fd, packet, packet_size, MSG_NOSIGNAL, &sa.sa, sizeof(sa.ll)); - if (l < 0) - return -errno; - - if ((size_t) l != packet_size) - return -EIO; - - return 0; -} - -static int link_send_lldp(Link *link) { - _cleanup_free_ char *hostname = NULL, *pretty_hostname = NULL; - _cleanup_free_ void *packet = NULL; - size_t packet_size = 0; - sd_id128_t machine_id; - uint16_t caps; - usec_t ttl; - int r; - - assert(link); - - if (!link->network || link->network->lldp_emit == LLDP_EMIT_NO) - return 0; - - assert(link->network->lldp_emit < _LLDP_EMIT_MAX); - - r = sd_id128_get_machine(&machine_id); + r = sd_lldp_tx_set_capabilities(link->lldp_tx, + SD_LLDP_SYSTEM_CAPABILITIES_STATION | + SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE | + SD_LLDP_SYSTEM_CAPABILITIES_ROUTER, + (link->network && link->network->ip_forward != ADDRESS_FAMILY_NO) ? + SD_LLDP_SYSTEM_CAPABILITIES_ROUTER : + SD_LLDP_SYSTEM_CAPABILITIES_STATION); if (r < 0) return r; - (void) gethostname_strict(&hostname); - (void) parse_env_file(NULL, "/etc/machine-info", "PRETTY_HOSTNAME", &pretty_hostname); - - assert_cc(LLDP_TX_INTERVAL_USEC * LLDP_TX_HOLD + 1 <= (UINT16_MAX - 1) * USEC_PER_SEC); - ttl = DIV_ROUND_UP(LLDP_TX_INTERVAL_USEC * LLDP_TX_HOLD + 1, USEC_PER_SEC); - - caps = (link->network && link->network->ip_forward != ADDRESS_FAMILY_NO) ? - SD_LLDP_SYSTEM_CAPABILITIES_ROUTER : - SD_LLDP_SYSTEM_CAPABILITIES_STATION; - - r = lldp_make_packet(link->network->lldp_emit, - &link->hw_addr.ether, - SD_ID128_TO_STRING(machine_id), - link->ifname, - (uint16_t) ttl, - link->network ? link->network->description : NULL, - hostname, - pretty_hostname, - SD_LLDP_SYSTEM_CAPABILITIES_STATION|SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE|SD_LLDP_SYSTEM_CAPABILITIES_ROUTER, - caps, - link->network ? link->network->lldp_mud : NULL, - &packet, &packet_size); + r = sd_lldp_tx_set_port_description(link->lldp_tx, link->network->description); if (r < 0) return r; - return lldp_send_packet(link->ifindex, lldp_multicast_addr + link->network->lldp_emit, packet, packet_size); -} - -static int on_lldp_timer(sd_event_source *s, usec_t t, void *userdata) { - Link *link = userdata; - usec_t delay; - int r; - - assert(s); - assert(userdata); - - log_link_debug(link, "Sending LLDP packet..."); - - r = link_send_lldp(link); - if (r < 0) - log_link_debug_errno(link, r, "Failed to send LLDP packet, ignoring: %m"); - - if (link->lldp_tx_fast > 0) - link->lldp_tx_fast--; - - delay = link->lldp_tx_fast > 0 ? LLDP_FAST_TX_USEC : LLDP_TX_INTERVAL_USEC; - delay = usec_add(delay, (usec_t) random_u64() % LLDP_JITTER_USEC); - - r = sd_event_source_set_time_relative(s, delay); - if (r < 0) - return log_link_error_errno(link, r, "Failed to restart LLDP timer: %m"); - - r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT); + r = sd_lldp_tx_set_mud_url(link->lldp_tx, link->network->lldp_mudurl); if (r < 0) - return log_link_error_errno(link, r, "Failed to enable LLDP timer: %m"); + return r; return 0; } -int link_lldp_emit_start(Link *link) { - usec_t next; +static const char * const lldp_multicast_mode_table[_SD_LLDP_MULTICAST_MODE_MAX] = { + [SD_LLDP_MULTICAST_MODE_NEAREST_BRIDGE] = "nearest-bridge", + [SD_LLDP_MULTICAST_MODE_NON_TPMR_BRIDGE] = "non-tpmr-bridge", + [SD_LLDP_MULTICAST_MODE_CUSTOMER_BRIDGE] = "customer-bridge", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(lldp_multicast_mode, sd_lldp_multicast_mode_t); + +int config_parse_lldp_multicast_mode( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + sd_lldp_multicast_mode_t m, *mode = data; int r; - assert(link); + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(mode); - if (!link_lldp_emit_enabled(link)) { - link_lldp_emit_stop(link); + if (isempty(rvalue)) { + *mode = _SD_LLDP_MULTICAST_MODE_INVALID; return 0; } - /* Starts the LLDP transmission in "fast" mode. If it is already started, turns "fast" mode back on again. */ - - link->lldp_tx_fast = LLDP_TX_FAST_INIT; - - next = usec_add(usec_add(now(clock_boottime_or_monotonic()), LLDP_FAST_TX_USEC), - (usec_t) random_u64() % LLDP_JITTER_USEC); - - if (link->lldp_emit_event_source) { - usec_t old; - - /* Lower the timeout, maybe */ - r = sd_event_source_get_time(link->lldp_emit_event_source, &old); - if (r < 0) - return r; - - if (old <= next) - return 0; - - return sd_event_source_set_time(link->lldp_emit_event_source, next); - } else { - r = sd_event_add_time( - link->manager->event, - &link->lldp_emit_event_source, - clock_boottime_or_monotonic(), - next, - 0, - on_lldp_timer, - link); - if (r < 0) - return r; + r = parse_boolean(rvalue); + if (r >= 0) { + *mode = r == 0 ? _SD_LLDP_MULTICAST_MODE_INVALID : SD_LLDP_MULTICAST_MODE_NEAREST_BRIDGE; + return 0; + } - (void) sd_event_source_set_description(link->lldp_emit_event_source, "lldp-tx"); + m = lldp_multicast_mode_from_string(rvalue); + if (m < 0) { + log_syntax(unit, LOG_WARNING, filename, line, m, + "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue); + return 0; } + *mode = m; return 0; } - -void link_lldp_emit_stop(Link *link) { - assert(link); - - link->lldp_emit_event_source = sd_event_source_disable_unref(link->lldp_emit_event_source); -} - -static const char * const lldp_emit_table[_LLDP_EMIT_MAX] = { - [LLDP_EMIT_NO] = "no", - [LLDP_EMIT_NEAREST_BRIDGE] = "nearest-bridge", - [LLDP_EMIT_NON_TPMR_BRIDGE] = "non-tpmr-bridge", - [LLDP_EMIT_CUSTOMER_BRIDGE] = "customer-bridge", -}; - -DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(lldp_emit, LLDPEmit, LLDP_EMIT_NEAREST_BRIDGE); -DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_lldp_emit, lldp_emit, LLDPEmit, LLDP_EMIT_NO, "Failed to parse LLDP emission setting"); diff --git a/src/network/networkd-lldp-tx.h b/src/network/networkd-lldp-tx.h index b8917f9de8..73757f1725 100644 --- a/src/network/networkd-lldp-tx.h +++ b/src/network/networkd-lldp-tx.h @@ -1,24 +1,10 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -#include <stdbool.h> - #include "conf-parser.h" typedef struct Link Link; -typedef enum LLDPEmit { - LLDP_EMIT_NO, - LLDP_EMIT_NEAREST_BRIDGE, - LLDP_EMIT_NON_TPMR_BRIDGE, - LLDP_EMIT_CUSTOMER_BRIDGE, - _LLDP_EMIT_MAX, - _LLDP_EMIT_INVALID = -EINVAL, -} LLDPEmit; - -bool link_lldp_emit_enabled(Link *link); -int link_lldp_emit_start(Link *link); -void link_lldp_emit_stop(Link *link); +int link_lldp_tx_configure(Link *link); -CONFIG_PARSER_PROTOTYPE(config_parse_lldp_emit); -CONFIG_PARSER_PROTOTYPE(config_parse_lldp_mud); +CONFIG_PARSER_PROTOTYPE(config_parse_lldp_multicast_mode); diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 53e23d9c0a..6168446be4 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -20,6 +20,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"") #include "networkd-dhcp6.h" #include "networkd-ipv4ll.h" #include "networkd-ipv6-proxy-ndp.h" +#include "networkd-lldp-tx.h" #include "networkd-ndisc.h" #include "networkd-network.h" #include "networkd-neighbor.h" @@ -104,7 +105,7 @@ Network.IPv4LLRoute, config_parse_bool, Network.DefaultRouteOnDevice, config_parse_bool, 0, offsetof(Network, default_route_on_device) Network.IPv6Token, config_parse_address_generation_type, 0, 0 Network.LLDP, config_parse_lldp_mode, 0, offsetof(Network, lldp_mode) -Network.EmitLLDP, config_parse_lldp_emit, 0, offsetof(Network, lldp_emit) +Network.EmitLLDP, config_parse_lldp_multicast_mode, 0, offsetof(Network, lldp_multicast_mode) Network.Address, config_parse_address, 0, 0 Network.Gateway, config_parse_gateway, 0, 0 Network.Domains, config_parse_domains, 0, 0 @@ -348,7 +349,7 @@ IPv6Prefix.Assign, config_parse_prefix_assign, IPv6Prefix.RouteMetric, config_parse_prefix_metric, 0, 0 IPv6RoutePrefix.Route, config_parse_route_prefix, 0, 0 IPv6RoutePrefix.LifetimeSec, config_parse_route_prefix_lifetime, 0, 0 -LLDP.MUDURL, config_parse_mud_url, 0, offsetof(Network, lldp_mud) +LLDP.MUDURL, config_parse_mud_url, 0, offsetof(Network, lldp_mudurl) CAN.BitRate, config_parse_can_bitrate, 0, offsetof(Network, can_bitrate) CAN.SamplePoint, config_parse_permille, 0, offsetof(Network, can_sample_point) CAN.TimeQuantaNSec, config_parse_can_time_quanta, 0, offsetof(Network, can_time_quanta_ns) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 4aee6fa43c..a772ae7c8f 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -432,6 +432,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi .multicast_router = _MULTICAST_ROUTER_INVALID, .lldp_mode = LLDP_MODE_ROUTERS_ONLY, + .lldp_multicast_mode = _SD_LLDP_MULTICAST_MODE_INVALID, .dns_default_route = -1, .llmnr = RESOLVE_SUPPORT_YES, @@ -690,7 +691,7 @@ static Network *network_free(Network *network) { set_free_free(network->dnssec_negative_trust_anchors); - free(network->lldp_mud); + free(network->lldp_mudurl); ordered_hashmap_free(network->dhcp_client_send_options); ordered_hashmap_free(network->dhcp_client_send_vendor_options); diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 40a52797a6..fb896598a8 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -5,6 +5,7 @@ #include "sd-bus.h" #include "sd-device.h" +#include "sd-lldp-tx.h" #include "bridge.h" #include "condition.h" @@ -17,7 +18,6 @@ #include "networkd-dhcp4.h" #include "networkd-dhcp6.h" #include "networkd-lldp-rx.h" -#include "networkd-lldp-tx.h" #include "networkd-ndisc.h" #include "networkd-radv.h" #include "networkd-sysctl.h" @@ -325,8 +325,8 @@ struct Network { /* LLDP support */ LLDPMode lldp_mode; /* LLDP reception */ - LLDPEmit lldp_emit; /* LLDP transmission */ - char *lldp_mud; /* LLDP MUD URL */ + sd_lldp_multicast_mode_t lldp_multicast_mode; /* LLDP transmission */ + char *lldp_mudurl; /* LLDP MUD URL */ OrderedHashmap *addresses_by_section; Hashmap *routes_by_section; diff --git a/src/network/test-network-tables.c b/src/network/test-network-tables.c index b7b536f3b5..c1c26d21c5 100644 --- a/src/network/test-network-tables.c +++ b/src/network/test-network-tables.c @@ -5,7 +5,7 @@ #include "dhcp6-protocol.h" #include "ethtool-util.h" #include "ipvlan.h" -#include "lldp-internal.h" +#include "lldp-rx-internal.h" #include "macvlan.h" #include "ndisc-internal.h" #include "networkd-link.h" @@ -35,7 +35,7 @@ int main(int argc, char **argv) { test_table(lldp_mode, LLDP_MODE); test_table(netdev_kind, NETDEV_KIND); test_table(radv_prefix_delegation, RADV_PREFIX_DELEGATION); - test_table(lldp_event, SD_LLDP_EVENT); + test_table(lldp_rx_event, SD_LLDP_RX_EVENT); test_table(ndisc_event, SD_NDISC_EVENT); test_table(dhcp_lease_server_type, SD_DHCP_LEASE_SERVER_TYPE); @@ -43,7 +43,7 @@ int main(int argc, char **argv) { test_table_sparse(macvlan_mode, NETDEV_MACVLAN_MODE); test_table_sparse(address_family, ADDRESS_FAMILY); - assert_cc(sizeof(sd_lldp_event_t) == sizeof(int64_t)); + assert_cc(sizeof(sd_lldp_rx_event_t) == sizeof(int64_t)); assert_cc(sizeof(sd_ndisc_event_t) == sizeof(int64_t)); assert_cc(sizeof(sd_dhcp_lease_server_type_t) == sizeof(int64_t)); diff --git a/src/systemd/meson.build b/src/systemd/meson.build index 6cae6a5767..4f40b9f57f 100644 --- a/src/systemd/meson.build +++ b/src/systemd/meson.build @@ -28,6 +28,8 @@ _not_installed_headers = ''' sd-dhcp-server.h sd-ipv4acd.h sd-ipv4ll.h + sd-lldp-rx.h + sd-lldp-tx.h sd-lldp.h sd-ndisc.h sd-netlink.h diff --git a/src/systemd/sd-lldp-rx.h b/src/systemd/sd-lldp-rx.h new file mode 100644 index 0000000000..060c677f3d --- /dev/null +++ b/src/systemd/sd-lldp-rx.h @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#ifndef foosdlldprxhfoo +#define foosdlldprxhfoo + +/*** + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <errno.h> +#include <inttypes.h> +#include <net/ethernet.h> +#include <sys/types.h> + +#include "sd-event.h" +#include "sd-lldp.h" + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +typedef struct sd_lldp_rx sd_lldp_rx; +typedef struct sd_lldp_neighbor sd_lldp_neighbor; + +typedef enum sd_lldp_rx_event_t { + SD_LLDP_RX_EVENT_ADDED, + SD_LLDP_RX_EVENT_REMOVED, + SD_LLDP_RX_EVENT_UPDATED, + SD_LLDP_RX_EVENT_REFRESHED, + _SD_LLDP_RX_EVENT_MAX, + _SD_LLDP_RX_EVENT_INVALID = -EINVAL, + _SD_ENUM_FORCE_S64(LLDP_RX_EVENT), +} sd_lldp_rx_event_t; + +typedef void (*sd_lldp_rx_callback_t)(sd_lldp_rx *lldp_rx, sd_lldp_rx_event_t event, sd_lldp_neighbor *n, void *userdata); + +int sd_lldp_rx_new(sd_lldp_rx **ret); +sd_lldp_rx *sd_lldp_rx_ref(sd_lldp_rx *lldp_rx); +sd_lldp_rx *sd_lldp_rx_unref(sd_lldp_rx *lldp_rx); + +int sd_lldp_rx_start(sd_lldp_rx *lldp_rx); +int sd_lldp_rx_stop(sd_lldp_rx *lldp_rx); + +int sd_lldp_rx_attach_event(sd_lldp_rx *lldp_rx, sd_event *event, int64_t priority); +int sd_lldp_rx_detach_event(sd_lldp_rx *lldp_rx); +sd_event *sd_lldp_rx_get_event(sd_lldp_rx *lldp_rx); + +int sd_lldp_rx_set_callback(sd_lldp_rx *lldp_rx, sd_lldp_rx_callback_t cb, void *userdata); +int sd_lldp_rx_set_ifindex(sd_lldp_rx *lldp_rx, int ifindex); +int sd_lldp_rx_set_ifname(sd_lldp_rx *lldp_rx, const char *ifname); +const char *sd_lldp_rx_get_ifname(sd_lldp_rx *lldp_rx); + +/* Controls how much and what to store in the neighbors database */ +int sd_lldp_rx_set_neighbors_max(sd_lldp_rx *lldp_rx, uint64_t n); +int sd_lldp_rx_match_capabilities(sd_lldp_rx *lldp_rx, uint16_t mask); +int sd_lldp_rx_set_filter_address(sd_lldp_rx *lldp_rx, const struct ether_addr *address); + +int sd_lldp_rx_get_neighbors(sd_lldp_rx *lldp_rx, sd_lldp_neighbor ***neighbors); + +int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size); +sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n); +sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n); + +/* Access to LLDP frame metadata */ +int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address); +int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address); +int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret); +int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size); + +/* High-level, direct, parsed out field access. These fields exist at most once, hence may be queried directly. */ +int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size); +int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret); +int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size); +int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret); +int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec); +int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret); +int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret); +int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret); +int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret); +int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret); +int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret); + +/* Low-level, iterative TLV access. This is for everything else, it iteratively goes through all available TLVs + * (including the ones covered with the calls above), and allows multiple TLVs for the same fields. */ +int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n); +int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n); +int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type); +int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type); +int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t *subtype); +int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t subtype); +int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size); + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_rx, sd_lldp_rx_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_neighbor, sd_lldp_neighbor_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/systemd/sd-lldp-tx.h b/src/systemd/sd-lldp-tx.h new file mode 100644 index 0000000000..aef06ed7eb --- /dev/null +++ b/src/systemd/sd-lldp-tx.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#ifndef foosdlldptxhfoo +#define foosdlldptxhfoo + +/*** + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <errno.h> +#include <inttypes.h> +#include <net/ethernet.h> +#include <sys/types.h> + +#include "sd-event.h" +#include "sd-lldp.h" + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +typedef struct sd_lldp_tx sd_lldp_tx; + +typedef enum sd_lldp_multicast_mode_t { + SD_LLDP_MULTICAST_MODE_NEAREST_BRIDGE, + SD_LLDP_MULTICAST_MODE_NON_TPMR_BRIDGE, + SD_LLDP_MULTICAST_MODE_CUSTOMER_BRIDGE, + _SD_LLDP_MULTICAST_MODE_MAX, + _SD_LLDP_MULTICAST_MODE_INVALID = -EINVAL, + _SD_ENUM_FORCE_S64(LLDP_TX_MODE), +} sd_lldp_multicast_mode_t; + +int sd_lldp_tx_new(sd_lldp_tx **ret); +sd_lldp_tx *sd_lldp_tx_ref(sd_lldp_tx *lldp_tx); +sd_lldp_tx *sd_lldp_tx_unref(sd_lldp_tx *lldp_tx); + +int sd_lldp_tx_start(sd_lldp_tx *lldp_tx); +int sd_lldp_tx_stop(sd_lldp_tx *lldp_tx); +int sd_lldp_tx_is_running(sd_lldp_tx *lldp_tx); + +int sd_lldp_tx_attach_event(sd_lldp_tx *lldp_tx, sd_event *event, int64_t priority); +int sd_lldp_tx_detach_event(sd_lldp_tx *lldp_tx); +sd_event *sd_lldp_tx_get_event(sd_lldp_tx *lldp_tx); + +int sd_lldp_tx_set_ifindex(sd_lldp_tx *lldp_tx, int ifindex); +int sd_lldp_tx_set_ifname(sd_lldp_tx *lldp_tx, const char *ifname); +const char *sd_lldp_tx_get_ifname(sd_lldp_tx *lldp_tx); + +int sd_lldp_tx_set_multicast_mode(sd_lldp_tx *lldp_tx, sd_lldp_multicast_mode_t mode); +int sd_lldp_tx_set_hwaddr(sd_lldp_tx *lldp_tx, const struct ether_addr *hwaddr); +int sd_lldp_tx_set_port_description(sd_lldp_tx *lldp_tx, const char *port_description); +int sd_lldp_tx_set_hostname(sd_lldp_tx *lldp_tx, const char *hostname); +int sd_lldp_tx_set_pretty_hostname(sd_lldp_tx *lldp_tx, const char *pretty_hostname); +int sd_lldp_tx_set_mud_url(sd_lldp_tx *lldp_tx, const char *mud_url); +int sd_lldp_tx_set_capabilities(sd_lldp_tx *lldp_tx, uint16_t supported, uint16_t enabled); + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_tx, sd_lldp_tx_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/systemd/sd-lldp.h b/src/systemd/sd-lldp.h index 64047ee817..c32d789476 100644 --- a/src/systemd/sd-lldp.h +++ b/src/systemd/sd-lldp.h @@ -17,12 +17,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <errno.h> #include <inttypes.h> -#include <net/ethernet.h> -#include <sys/types.h> - -#include "sd-event.h" #include "_sd-common.h" @@ -85,20 +80,24 @@ enum { #define SD_LLDP_SYSTEM_CAPABILITIES_ALL_ROUTERS \ ((uint16_t) \ - (SD_LLDP_SYSTEM_CAPABILITIES_REPEATER| \ - SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE| \ - SD_LLDP_SYSTEM_CAPABILITIES_WLAN_AP| \ - SD_LLDP_SYSTEM_CAPABILITIES_ROUTER| \ - SD_LLDP_SYSTEM_CAPABILITIES_DOCSIS| \ - SD_LLDP_SYSTEM_CAPABILITIES_CVLAN| \ - SD_LLDP_SYSTEM_CAPABILITIES_SVLAN| \ + (SD_LLDP_SYSTEM_CAPABILITIES_REPEATER | \ + SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE | \ + SD_LLDP_SYSTEM_CAPABILITIES_WLAN_AP | \ + SD_LLDP_SYSTEM_CAPABILITIES_ROUTER | \ + SD_LLDP_SYSTEM_CAPABILITIES_DOCSIS | \ + SD_LLDP_SYSTEM_CAPABILITIES_CVLAN | \ + SD_LLDP_SYSTEM_CAPABILITIES_SVLAN | \ SD_LLDP_SYSTEM_CAPABILITIES_TPMR)) -#define SD_LLDP_OUI_802_1 (uint8_t[]) { 0x00, 0x80, 0xc2 } -#define SD_LLDP_OUI_802_3 (uint8_t[]) { 0x00, 0x12, 0x0f } +#define SD_LLDP_OUI_802_1 (const uint8_t[]) { 0x00, 0x80, 0xc2 } +#define SD_LLDP_OUI_802_3 (const uint8_t[]) { 0x00, 0x12, 0x0f } + +#define _SD_LLDP_OUI_IANA 0x00, 0x00, 0x5E +#define SD_LLDP_OUI_IANA (const uint8_t[]) { _SD_LLDP_OUI_IANA } -#define SD_LLDP_OUI_MUD (uint8_t[]) { 0x00, 0x00, 0x5E } -#define SD_LLDP_OUI_SUBTYPE_MUD_USAGE_DESCRIPTION 0x01 +#define SD_LLDP_OUI_IANA_SUBTYPE_MUD 0x01 +#define SD_LLDP_OUI_IANA_MUD \ + (const uint8_t[]) { _SD_LLDP_OUI_IANA, SD_LLDP_OUI_IANA_SUBTYPE_MUD } /* IEEE 802.1AB-2009 Annex E */ enum { @@ -119,80 +118,6 @@ enum { SD_LLDP_OUI_802_3_SUBTYPE_MAXIMUM_FRAME_SIZE = 4, }; -typedef struct sd_lldp sd_lldp; -typedef struct sd_lldp_neighbor sd_lldp_neighbor; - -typedef enum sd_lldp_event_t { - SD_LLDP_EVENT_ADDED, - SD_LLDP_EVENT_REMOVED, - SD_LLDP_EVENT_UPDATED, - SD_LLDP_EVENT_REFRESHED, - _SD_LLDP_EVENT_MAX, - _SD_LLDP_EVENT_INVALID = -EINVAL, - _SD_ENUM_FORCE_S64(LLDP_EVENT), -} sd_lldp_event_t; - -typedef void (*sd_lldp_callback_t)(sd_lldp *lldp, sd_lldp_event_t event, sd_lldp_neighbor *n, void *userdata); - -int sd_lldp_new(sd_lldp **ret); -sd_lldp* sd_lldp_ref(sd_lldp *lldp); -sd_lldp* sd_lldp_unref(sd_lldp *lldp); - -int sd_lldp_start(sd_lldp *lldp); -int sd_lldp_stop(sd_lldp *lldp); - -int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int64_t priority); -int sd_lldp_detach_event(sd_lldp *lldp); -sd_event *sd_lldp_get_event(sd_lldp *lldp); - -int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata); -int sd_lldp_set_ifindex(sd_lldp *lldp, int ifindex); -int sd_lldp_set_ifname(sd_lldp *lldp, const char *ifname); -const char *sd_lldp_get_ifname(sd_lldp *lldp); - -/* Controls how much and what to store in the neighbors database */ -int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t n); -int sd_lldp_match_capabilities(sd_lldp *lldp, uint16_t mask); -int sd_lldp_set_filter_address(sd_lldp *lldp, const struct ether_addr *address); - -int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***neighbors); - -int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size); -sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n); -sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n); - -/* Access to LLDP frame metadata */ -int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address); -int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address); -int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret); -int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size); - -/* High-level, direct, parsed out field access. These fields exist at most once, hence may be queried directly. */ -int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size); -int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret); -int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size); -int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret); -int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec); -int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret); -int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret); -int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret); -int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret); -int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret); -int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret); - -/* Low-level, iterative TLV access. This is for everything else, it iteratively goes through all available TLVs - * (including the ones covered with the calls above), and allows multiple TLVs for the same fields. */ -int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n); -int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n); -int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type); -int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type); -int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t *subtype); -int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t subtype); -int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size); - -_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp, sd_lldp_unref); -_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_neighbor, sd_lldp_neighbor_unref); - _SD_END_DECLARATIONS; #endif |