]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
networkd: reworkd LLDP emission to allow control of propagation level
authorLennart Poettering <lennart@poettering.net>
Fri, 6 May 2016 19:27:36 +0000 (21:27 +0200)
committerLennart Poettering <lennart@poettering.net>
Mon, 9 May 2016 13:45:31 +0000 (15:45 +0200)
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.

man/systemd.network.xml
src/network/networkd-link.c
src/network/networkd-link.h
src/network/networkd-lldp-tx.c
src/network/networkd-lldp-tx.h
src/network/networkd-network-gperf.gperf
src/network/networkd-network.h

index 6d45d6c8076958641ad07dd97237675ebb626db3..70e3804746c6f98ebed8606ca892a1e60550b6e0 100644 (file)
         <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>
index c646af1f1a67f903045a9b49497c5c540276f0fc..4e3f62cf5135c1891405fc66f3a0db0dc770f75a 100644 (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");
         }
index 86139be55757a9026da13e12ec5990b7bf4ca928..14c4a02c7efb5c1f3e440eeb265d51966704ea50 100644 (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;
index 03b694c3f1c520bdf0430e6a29df1bd70c1eba8c..3aa768388b6e84ef53112f5d519e55e059722612 100644 (file)
 #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
 
 /* 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;
 }
index 8c7f403005e64f48b555fd7aaf33c507eb8f8307..4680c9d950f5e6dd7ff718e9c6e444a255cca673 100644 (file)
 
 #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);
index a9a541559e2a19920660e431abb6c021693dc7c7..4425ee4e2f3e9063dc08327c76d64622f6dd0d86 100644 (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
index ff2414efdde5163282d118760ea4385226d71dd7..4cd0fa4ab83c2452f161d7d21873a53bebb7918d 100644 (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);