1
1
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:
Lennart Poettering 2016-05-06 21:27:36 +02:00
parent d31645adef
commit 7272b25e16
7 changed files with 126 additions and 41 deletions

View File

@ -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>

View File

@ -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");
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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);

View File

@ -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

View File

@ -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);