mirror of
https://github.com/systemd/systemd
synced 2026-03-28 17:54:51 +01:00
Compare commits
15 Commits
3542da2442
...
07db7f6bb8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07db7f6bb8 | ||
|
|
bed159888e | ||
|
|
a50dadf2fd | ||
|
|
c3138b46bc | ||
|
|
aa0a23ec86 | ||
|
|
c01b9b879c | ||
|
|
7df9656782 | ||
|
|
3daf701014 | ||
|
|
3be1e84fe9 | ||
|
|
19b68e9e96 | ||
|
|
910fd79e8e | ||
|
|
35778343ab | ||
|
|
94832e6e55 | ||
|
|
3a2ee8554e | ||
|
|
82e0689cd8 |
@ -201,7 +201,7 @@ option('default-hierarchy', type : 'combo',
|
||||
choices : ['legacy', 'hybrid', 'unified'], value : 'unified',
|
||||
description : 'default cgroup hierarchy')
|
||||
option('default-net-naming-scheme', type : 'combo',
|
||||
choices : ['latest', 'v238', 'v239', 'v240'],
|
||||
choices : ['latest', 'v238', 'v239', 'v240', 'v241', 'v243', 'v245', 'v247', 'v249'],
|
||||
description : 'default net.naming-scheme= value')
|
||||
option('status-unit-format-default', type : 'combo',
|
||||
choices : ['description', 'name', 'combined'],
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
@ -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,13 +156,13 @@ 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),
|
||||
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),
|
||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Received packet with wrong type, ignoring.");
|
||||
|
||||
if (h.ether_dhost[0] != 0x01 ||
|
||||
@ -172,7 +171,7 @@ 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),
|
||||
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));
|
||||
@ -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,14 +193,14 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) {
|
||||
p += 2, left -= 2;
|
||||
|
||||
if (left < length)
|
||||
return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
|
||||
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),
|
||||
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
|
||||
@ -212,11 +211,11 @@ 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),
|
||||
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),
|
||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Duplicate chassis ID field, ignoring datagram.");
|
||||
|
||||
n->id.chassis_id = memdup(p, length);
|
||||
@ -229,11 +228,11 @@ 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),
|
||||
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),
|
||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Duplicate port ID field, ignoring datagram.");
|
||||
|
||||
n->id.port_id = memdup(p, length);
|
||||
@ -245,11 +244,11 @@ 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),
|
||||
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),
|
||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Duplicate TTL field, ignoring datagram.");
|
||||
|
||||
n->ttl = unaligned_read_be16(p);
|
||||
@ -257,26 +256,26 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) {
|
||||
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),
|
||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"System capabilities field has wrong size.");
|
||||
|
||||
n->system_capabilities = unaligned_read_be16(p);
|
||||
@ -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),
|
||||
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,7 +303,7 @@ 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),
|
||||
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) {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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__)
|
||||
@ -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]],
|
||||
|
||||
|
||||
523
src/libsystemd-network/sd-lldp-rx.c
Normal file
523
src/libsystemd-network/sd-lldp-rx.c
Normal file
@ -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;
|
||||
}
|
||||
615
src/libsystemd-network/sd-lldp-tx.c
Normal file
615
src/libsystemd-network/sd-lldp-tx.c
Normal file
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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[]) {
|
||||
@ -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"
|
||||
|
||||
|
||||
@ -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 (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: %m");
|
||||
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 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: %m");
|
||||
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 Tx: %m");
|
||||
}
|
||||
|
||||
if (link->ipv4ll) {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
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) {
|
||||
|
||||
@ -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 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;
|
||||
|
||||
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);
|
||||
|
||||
p = (uint8_t*) packet + sizeof(struct ether_header);
|
||||
|
||||
r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_CHASSIS_ID, 1 + machine_id_length);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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 link_lldp_tx_configure(Link *link) {
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
|
||||
if (!link->network || link->network->lldp_emit == LLDP_EMIT_NO)
|
||||
if (!link_lldp_tx_enabled(link))
|
||||
return 0;
|
||||
|
||||
assert(link->network->lldp_emit < _LLDP_EMIT_MAX);
|
||||
if (link->lldp_tx)
|
||||
return -EBUSY;
|
||||
|
||||
r = sd_id128_get_machine(&machine_id);
|
||||
r = sd_lldp_tx_new(&link->lldp_tx);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) gethostname_strict(&hostname);
|
||||
(void) parse_env_file(NULL, "/etc/machine-info", "PRETTY_HOSTNAME", &pretty_hostname);
|
||||
r = sd_lldp_tx_attach_event(link->lldp_tx, link->manager->event, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
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);
|
||||
r = sd_lldp_tx_set_ifindex(link->lldp_tx, link->ifindex);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
caps = (link->network && link->network->ip_forward != ADDRESS_FAMILY_NO) ?
|
||||
r = sd_lldp_tx_set_hwaddr(link->lldp_tx, &link->hw_addr.ether);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_lldp_tx_set_multicast_mode(link->lldp_tx, link->network->lldp_multicast_mode);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_STATION);
|
||||
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);
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Failed to enable LLDP timer: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int link_lldp_emit_start(Link *link) {
|
||||
usec_t next;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
|
||||
if (!link_lldp_emit_enabled(link)) {
|
||||
link_lldp_emit_stop(link);
|
||||
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);
|
||||
r = sd_lldp_tx_set_port_description(link->lldp_tx, link->network->description);
|
||||
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);
|
||||
r = sd_lldp_tx_set_mud_url(link->lldp_tx, link->network->lldp_mudurl);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) sd_event_source_set_description(link->lldp_emit_event_source, "lldp-tx");
|
||||
}
|
||||
|
||||
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",
|
||||
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_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");
|
||||
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(filename);
|
||||
assert(section);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(mode);
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
*mode = _SD_LLDP_MULTICAST_MODE_INVALID;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = parse_boolean(rvalue);
|
||||
if (r >= 0) {
|
||||
*mode = r == 0 ? _SD_LLDP_MULTICAST_MODE_INVALID : SD_LLDP_MULTICAST_MODE_NEAREST_BRIDGE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
int link_lldp_tx_configure(Link *link);
|
||||
|
||||
bool link_lldp_emit_enabled(Link *link);
|
||||
int link_lldp_emit_start(Link *link);
|
||||
void link_lldp_emit_stop(Link *link);
|
||||
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_lldp_emit);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_lldp_mud);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_lldp_multicast_mode);
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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));
|
||||
|
||||
|
||||
@ -216,6 +216,13 @@ static void write_resolv_conf_server(DnsServer *s, FILE *f, unsigned *count) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* resolv.conf simply doesn't support any other ports than 53, hence there's nothing much we can
|
||||
* do — we have to suppress these entries */
|
||||
if (dns_server_port(s) != 53) {
|
||||
log_debug("DNS server %s with non-standard UDP port number, suppressing from generated resolv.conf.", dns_server_string(s));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if the scope this DNS server belongs to is suitable as 'default' route for lookups; resolv.conf does
|
||||
* not have a syntax to express that, so it must not appear as a global name server to avoid routing unrelated
|
||||
* domains to it (which is a privacy violation, will most probably fail anyway, and adds unnecessary load) */
|
||||
|
||||
@ -15,6 +15,7 @@ static const NamingScheme naming_schemes[] = {
|
||||
{ "v247", NAMING_V247 },
|
||||
{ "v249", NAMING_V249 },
|
||||
/* … add more schemes here, as the logic to name devices is updated … */
|
||||
/* also remember to update the list of options in meson_options.txt */
|
||||
};
|
||||
|
||||
static const NamingScheme* naming_scheme_from_name(const char *name) {
|
||||
|
||||
@ -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
|
||||
|
||||
108
src/systemd/sd-lldp-rx.h
Normal file
108
src/systemd/sd-lldp-rx.h
Normal file
@ -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
|
||||
71
src/systemd/sd-lldp-tx.h
Normal file
71
src/systemd/sd-lldp-tx.h
Normal file
@ -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
|
||||
@ -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"
|
||||
|
||||
@ -94,11 +89,15 @@ enum {
|
||||
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_MUD (uint8_t[]) { 0x00, 0x00, 0x5E }
|
||||
#define SD_LLDP_OUI_SUBTYPE_MUD_USAGE_DESCRIPTION 0x01
|
||||
#define _SD_LLDP_OUI_IANA 0x00, 0x00, 0x5E
|
||||
#define SD_LLDP_OUI_IANA (const uint8_t[]) { _SD_LLDP_OUI_IANA }
|
||||
|
||||
#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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user