mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-02-27 13:57:26 +03:00
networkd: reworkd LLDP emission to allow control of propagation level
This allows selecting the propagation level of emitted LLDP packets (specifically: the destination MAC address of the packets). This is useful because it allows generating LLDP packets that optionally cross certain types of bridges. See 802.11ab-2009, Table 7-1 for details.
This commit is contained in:
parent
d31645adef
commit
7272b25e16
@ -363,18 +363,26 @@
|
||||
<varlistentry>
|
||||
<term><varname>EmitLLDP=</varname></term>
|
||||
<listitem>
|
||||
<para>Controls support for Ethernet LLDP packet emission. Accepts a boolean parameter and defaults to
|
||||
false. If enabled a short LLDP packet with information about the local system is sent out in regular
|
||||
intervals on the link. The LLDP packet will contain information about the local host name, the local
|
||||
machine ID (as stored in
|
||||
<citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>) and the
|
||||
<para>Controls support for Ethernet LLDP packet emission. Accepts a boolean parameter or the special values
|
||||
<literal>nearest-bridge</literal>, <literal>non-tpmr-bridge</literal> and
|
||||
<literal>customer-bridge</literal>. Defaults to false, which turns off LLDP packet emission. If not false,
|
||||
a short LLDP packet with information about the local system is sent out in regular intervals on the
|
||||
link. The LLDP packet will contain information about the local host name, the local machine ID (as stored
|
||||
in <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>) and the
|
||||
local interface name, as well as the pretty hostname of the system (as set in
|
||||
<citerefentry><refentrytitle>machine-info</refentrytitle><manvolnum>5</manvolnum></citerefentry>). LLDP
|
||||
emission is only available on Ethernet links. Note that this setting passed data suitable for
|
||||
identification of host to the network and should thus not be used on untrusted networks, where such
|
||||
identification data should not be made available. Use this option to enable other systems to identify on
|
||||
which interface they are connected to this system. See <varname>LLDP=</varname> above for an option to
|
||||
enable LLDP reception.</para>
|
||||
emission is only available on Ethernet links. Note that this setting passes data suitable for
|
||||
identification of host to the network and should thus not be enabled on untrusted networks, where such
|
||||
identification data should not be made available. Use this option to permit other systems to identify on
|
||||
which interfaces they are connected to this system. The three special values control propagation of the
|
||||
LLDP packets. The <literal>nearest-bridge</literal> setting permits propagation only to the nearest
|
||||
connected bridge, <literal>non-tpmr-bridge</literal> permits propagation across Two-Port MAC Relays, but
|
||||
not any other bridges, and <literal>customer-bridge</literal> permits propagation until a customer bridge
|
||||
is reached. For details about these concepts, see <ulink
|
||||
url="http://standards.ieee.org/getieee802/download/802.1AB-2009.pdf">IEEE 802.1AB-2009</ulink>. Note that
|
||||
configuring this setting to true is equivalent to <literal>nearest-bridge</literal>, the recommended and
|
||||
most restricted level of propagation. See <varname>LLDP=</varname> above for an option to enable LLDP
|
||||
reception.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
|
@ -131,7 +131,7 @@ static bool link_lldp_rx_enabled(Link *link) {
|
||||
return link->network->lldp_mode != LLDP_MODE_NO;
|
||||
}
|
||||
|
||||
static bool link_lldp_tx_enabled(Link *link) {
|
||||
static bool link_lldp_emit_enabled(Link *link) {
|
||||
assert(link);
|
||||
|
||||
if (link->flags & IFF_LOOPBACK)
|
||||
@ -143,7 +143,7 @@ static bool link_lldp_tx_enabled(Link *link) {
|
||||
if (!link->network)
|
||||
return false;
|
||||
|
||||
return link->network->lldp_emit;
|
||||
return link->network->lldp_emit != LLDP_EMIT_NO;
|
||||
}
|
||||
|
||||
static bool link_ipv4_forward_enabled(Link *link) {
|
||||
@ -491,7 +491,7 @@ static void link_free(Link *link) {
|
||||
sd_dhcp_client_unref(link->dhcp_client);
|
||||
sd_dhcp_lease_unref(link->dhcp_lease);
|
||||
|
||||
link_lldp_tx_stop(link);
|
||||
link_lldp_emit_stop(link);
|
||||
|
||||
free(link->lease_file);
|
||||
|
||||
@ -618,7 +618,7 @@ static int link_stop_clients(Link *link) {
|
||||
r = log_link_warning_errno(link, k, "Could not stop IPv6 Router Discovery: %m");
|
||||
}
|
||||
|
||||
link_lldp_tx_stop(link);
|
||||
link_lldp_emit_stop(link);
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -1411,12 +1411,12 @@ static void lldp_handler(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n
|
||||
|
||||
(void) link_lldp_save(link);
|
||||
|
||||
if (link_lldp_tx_enabled(link) && event == SD_LLDP_EVENT_ADDED) {
|
||||
if (link_lldp_emit_enabled(link) && event == SD_LLDP_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_tx_start(link);
|
||||
r = link_lldp_emit_start(link);
|
||||
if (r < 0)
|
||||
log_link_warning_errno(link, r, "Failed to restart LLDP transmission: %m");
|
||||
}
|
||||
@ -1501,8 +1501,8 @@ static int link_acquire_conf(Link *link) {
|
||||
return r;
|
||||
}
|
||||
|
||||
if (link_lldp_tx_enabled(link)) {
|
||||
r = link_lldp_tx_start(link);
|
||||
if (link_lldp_emit_enabled(link)) {
|
||||
r = link_lldp_emit_start(link);
|
||||
if (r < 0)
|
||||
return log_link_warning_errno(link, r, "Failed to start LLDP transmission: %m");
|
||||
}
|
||||
|
@ -121,7 +121,7 @@ typedef struct Link {
|
||||
|
||||
/* 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_tx_event_source;
|
||||
sd_event_source *lldp_emit_event_source;
|
||||
|
||||
Hashmap *bound_by_links;
|
||||
Hashmap *bound_to_links;
|
||||
|
@ -25,16 +25,14 @@
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "hostname-util.h"
|
||||
#include "networkd-lldp-tx.h"
|
||||
#include "networkd.h"
|
||||
#include "parse-util.h"
|
||||
#include "random-util.h"
|
||||
#include "socket-util.h"
|
||||
#include "string-util.h"
|
||||
#include "unaligned.h"
|
||||
|
||||
#include "networkd.h"
|
||||
#include "networkd-lldp-tx.h"
|
||||
|
||||
#define LLDP_MULTICAST_ADDR { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }
|
||||
|
||||
/* The LLDP spec calls this "txFastInit", see 9.2.5.19 */
|
||||
#define LLDP_TX_FAST_INIT 4U
|
||||
|
||||
@ -50,6 +48,12 @@
|
||||
/* 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 }},
|
||||
};
|
||||
|
||||
static int lldp_write_tlv_header(uint8_t **p, uint8_t id, size_t sz) {
|
||||
assert(p);
|
||||
|
||||
@ -66,6 +70,7 @@ static int lldp_write_tlv_header(uint8_t **p, uint8_t id, size_t sz) {
|
||||
}
|
||||
|
||||
static int lldp_make_packet(
|
||||
LLDPEmit mode,
|
||||
const struct ether_addr *hwaddr,
|
||||
const char *machine_id,
|
||||
const char *ifname,
|
||||
@ -84,6 +89,8 @@ static int lldp_make_packet(
|
||||
size_t l;
|
||||
int r;
|
||||
|
||||
assert(mode > LLDP_EMIT_NO);
|
||||
assert(mode < _LLDP_EMIT_MAX);
|
||||
assert(hwaddr);
|
||||
assert(machine_id);
|
||||
assert(ifname);
|
||||
@ -132,7 +139,7 @@ static int lldp_make_packet(
|
||||
|
||||
h = (struct ether_header*) packet;
|
||||
h->ether_type = htobe16(ETHERTYPE_LLDP);
|
||||
memcpy(h->ether_dhost, &(struct ether_addr) { LLDP_MULTICAST_ADDR }, ETH_ALEN);
|
||||
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);
|
||||
@ -197,22 +204,28 @@ static int lldp_make_packet(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lldp_send_packet(int ifindex, const void *packet, size_t packet_size) {
|
||||
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,
|
||||
.ll.sll_addr = LLDP_MULTICAST_ADDR,
|
||||
};
|
||||
|
||||
_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(PF_PACKET, SOCK_RAW|SOCK_CLOEXEC, IPPROTO_RAW);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
@ -237,6 +250,13 @@ static int link_send_lldp(Link *link) {
|
||||
usec_t ttl;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
|
||||
if (!link->network || link->network->lldp_emit == LLDP_EMIT_NO)
|
||||
return 0;
|
||||
|
||||
assert(link->network->lldp_emit < _LLDP_EMIT_MAX);
|
||||
|
||||
r = sd_id128_get_machine(&machine_id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -251,7 +271,8 @@ static int link_send_lldp(Link *link) {
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_ROUTER :
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_STATION;
|
||||
|
||||
r = lldp_make_packet(&link->mac,
|
||||
r = lldp_make_packet(link->network->lldp_emit,
|
||||
&link->mac,
|
||||
sd_id128_to_string(machine_id, machine_id_string),
|
||||
link->ifname,
|
||||
(uint16_t) ttl,
|
||||
@ -264,7 +285,7 @@ static int link_send_lldp(Link *link) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return lldp_send_packet(link->ifindex, packet, packet_size);
|
||||
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) {
|
||||
@ -300,12 +321,17 @@ static int on_lldp_timer(sd_event_source *s, usec_t t, void *userdata) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int link_lldp_tx_start(Link *link) {
|
||||
int link_lldp_emit_start(Link *link) {
|
||||
usec_t next;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
|
||||
if (!link->network || link->network->lldp_emit == LLDP_EMIT_NO) {
|
||||
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;
|
||||
@ -313,22 +339,22 @@ int link_lldp_tx_start(Link *link) {
|
||||
next = usec_add(usec_add(now(clock_boottime_or_monotonic()), LLDP_FAST_TX_USEC),
|
||||
(usec_t) random_u64() % LLDP_JITTER_USEC);
|
||||
|
||||
if (link->lldp_tx_event_source) {
|
||||
if (link->lldp_emit_event_source) {
|
||||
usec_t old;
|
||||
|
||||
/* Lower the timeout, maybe */
|
||||
r = sd_event_source_get_time(link->lldp_tx_event_source, &old);
|
||||
r = sd_event_source_get_time(link->lldp_emit_event_source, &old);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (old <= next)
|
||||
return 0;
|
||||
|
||||
return sd_event_source_set_time(link->lldp_tx_event_source, next);
|
||||
return sd_event_source_set_time(link->lldp_emit_event_source, next);
|
||||
} else {
|
||||
r = sd_event_add_time(
|
||||
link->manager->event,
|
||||
&link->lldp_tx_event_source,
|
||||
&link->lldp_emit_event_source,
|
||||
clock_boottime_or_monotonic(),
|
||||
next,
|
||||
0,
|
||||
@ -337,14 +363,54 @@ int link_lldp_tx_start(Link *link) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) sd_event_source_set_description(link->lldp_tx_event_source, "lldp-tx");
|
||||
(void) sd_event_source_set_description(link->lldp_emit_event_source, "lldp-tx");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void link_lldp_tx_stop(Link *link) {
|
||||
void link_lldp_emit_stop(Link *link) {
|
||||
assert(link);
|
||||
|
||||
link->lldp_tx_event_source = sd_event_source_unref(link->lldp_tx_event_source);
|
||||
link->lldp_emit_event_source = sd_event_source_unref(link->lldp_emit_event_source);
|
||||
}
|
||||
|
||||
int config_parse_lldp_emit(
|
||||
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) {
|
||||
|
||||
LLDPEmit *emit = data;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
|
||||
if (isempty(rvalue))
|
||||
*emit = LLDP_EMIT_NO;
|
||||
else if (streq(rvalue, "nearest-bridge"))
|
||||
*emit = LLDP_EMIT_NEAREST_BRIDGE;
|
||||
else if (streq(rvalue, "non-tpmr-bridge"))
|
||||
*emit = LLDP_EMIT_NON_TPMR_BRIDGE;
|
||||
else if (streq(rvalue, "customer-bridge"))
|
||||
*emit = LLDP_EMIT_CUSTOMER_BRIDGE;
|
||||
else {
|
||||
r = parse_boolean(rvalue);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse LLDP emission setting, ignoring: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*emit = r ? LLDP_EMIT_NEAREST_BRIDGE : LLDP_EMIT_NO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -21,5 +21,15 @@
|
||||
|
||||
#include "networkd-link.h"
|
||||
|
||||
int link_lldp_tx_start(Link *link);
|
||||
void link_lldp_tx_stop(Link *link);
|
||||
typedef enum LLDPEmit {
|
||||
LLDP_EMIT_NO,
|
||||
LLDP_EMIT_NEAREST_BRIDGE,
|
||||
LLDP_EMIT_NON_TPMR_BRIDGE,
|
||||
LLDP_EMIT_CUSTOMER_BRIDGE,
|
||||
_LLDP_EMIT_MAX,
|
||||
} LLDPEmit;
|
||||
|
||||
int link_lldp_emit_start(Link *link);
|
||||
void link_lldp_emit_stop(Link *link);
|
||||
|
||||
int config_parse_lldp_emit(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);
|
||||
|
@ -42,7 +42,7 @@ Network.LinkLocalAddressing, config_parse_address_family_boolean,
|
||||
Network.IPv4LLRoute, config_parse_bool, 0, offsetof(Network, ipv4ll_route)
|
||||
Network.IPv6Token, config_parse_ipv6token, 0, offsetof(Network, ipv6_token)
|
||||
Network.LLDP, config_parse_lldp_mode, 0, offsetof(Network, lldp_mode)
|
||||
Network.EmitLLDP, config_parse_bool, 0, offsetof(Network, lldp_emit)
|
||||
Network.EmitLLDP, config_parse_lldp_emit, 0, offsetof(Network, lldp_emit)
|
||||
Network.Address, config_parse_address, 0, 0
|
||||
Network.Gateway, config_parse_gateway, 0, 0
|
||||
Network.Domains, config_parse_domains, 0, 0
|
||||
|
@ -29,6 +29,7 @@
|
||||
|
||||
#include "networkd-address.h"
|
||||
#include "networkd-fdb.h"
|
||||
#include "networkd-lldp-tx.h"
|
||||
#include "networkd-netdev.h"
|
||||
#include "networkd-route.h"
|
||||
#include "networkd-util.h"
|
||||
@ -161,7 +162,7 @@ struct Network {
|
||||
DUID duid;
|
||||
|
||||
LLDPMode lldp_mode; /* LLDP reception */
|
||||
bool lldp_emit; /* LLDP transmission */
|
||||
LLDPEmit lldp_emit; /* LLDP transmission */
|
||||
|
||||
LIST_HEAD(Address, static_addresses);
|
||||
LIST_HEAD(Route, static_routes);
|
||||
|
Loading…
x
Reference in New Issue
Block a user