]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
LLDP: Add support to transmit MUD URL 15234/head
authorSusant Sahani <ssahani@vmware.com>
Mon, 30 Mar 2020 19:57:58 +0000 (21:57 +0200)
committerSusant Sahani <ssahani@vmware.com>
Tue, 7 Apr 2020 22:20:54 +0000 (00:20 +0200)
man/systemd.network.xml
src/network/networkd-lldp-tx.c
src/network/networkd-lldp-tx.h
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h
test/fuzz/fuzz-network-parser/directives.network

index d2cdb59f461636df053fea19e3a536b17b5d5b76..8d11922c71e2761a26e739d220ba90e32f655cc5 100644 (file)
             reception.</para>
           </listitem>
         </varlistentry>
+
         <varlistentry>
           <term><varname>BindCarrier=</varname></term>
           <listitem>
       </variablelist>
   </refsect1>
 
+  <refsect1>
+    <title>[LLDP] Section Options</title>
+      <para>The <literal>[LLDP]</literal> section manages the Link Layer Discovery Protocol (LLDP) and accepts the
+      following keys.</para>
+      <variablelist class='network-directives'>
+        <varlistentry>
+          <term><varname>MUDURL=</varname></term>
+          <listitem>
+            <para>Controls support for Ethernet LLDP packet's Manufacturer Usage Description (MUD). MUD is an embedded software
+            standard defined by the IETF that allows IoT Device makers to advertise device specifications, including the intended
+            communication patterns for their device when it connects to the network. The network can then use this intent to author
+            a context-specific access policy, so the device functions only within those parameters. Takes an URL of length up to 255
+            characters. A superficial verification that the string is a valid URL
+            will be performed. See
+            <ulink url="https://tools.ietf.org/html/rfc8520">RFC 8520</ulink> for details. The MUD URL received
+            from the LLDP packets will be saved at the state files and can be read via
+            <function>sd_lldp_neighbor_get_mud_url()</function> function.</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+  </refsect1>
+
   <refsect1>
     <title>[CAN] Section Options</title>
       <para>The <literal>[CAN]</literal> section manages the Controller Area Network (CAN bus) and accepts the
index 51db60e1f2e816a987801c3510187a66bebf827a..1fbf9d0dd9161657cce39dfabcaca3c0e1452e4d 100644 (file)
@@ -6,6 +6,7 @@
 #include <net/if_arp.h>
 
 #include "alloc-util.h"
+#include "escape.h"
 #include "env-file.h"
 #include "fd-util.h"
 #include "hostname-util.h"
@@ -18,6 +19,7 @@
 #include "socket-util.h"
 #include "string-util.h"
 #include "unaligned.h"
+#include "web-util.h"
 
 /* The LLDP spec calls this "txFastInit", see 9.2.5.19 */
 #define LLDP_TX_FAST_INIT 4U
@@ -81,9 +83,11 @@ static int lldp_make_packet(
                 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;
+        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;
@@ -110,6 +114,9 @@ static int lldp_make_packet(
         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 +
@@ -134,6 +141,10 @@ static int lldp_make_packet(
         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;
@@ -184,6 +195,32 @@ static int lldp_make_packet(
                 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;
@@ -281,6 +318,7 @@ static int link_send_lldp(Link *link) {
                              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);
         if (r < 0)
                 return r;
@@ -414,3 +452,40 @@ int config_parse_lldp_emit(
 
         return 0;
 }
+
+int config_parse_lldp_mud(
+                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) {
+
+        _cleanup_free_ char *unescaped = NULL;
+        Network *n = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+
+        r = cunescape(rvalue, 0, &unescaped);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Failed to Failed to unescape LLDP MUD URL, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        if (!http_url_is_valid(unescaped) || strlen(unescaped) > 255) {
+                log_syntax(unit, LOG_ERR, filename, line, 0,
+                           "Failed to parse LLDP MUD URL '%s', ignoring: %m", rvalue);
+
+                return 0;
+        }
+
+        return free_and_replace(n->lldp_mud, unescaped);
+}
index 561becda41c84fec4d967165e0fe181009e9c637..1409984ac02fda3f0bcf80897694f3089a727ccf 100644 (file)
@@ -20,3 +20,4 @@ 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);
index fe6adc4089a298cd0d924719427801ff95b91953..c63f57f96249761677f4063acc4ad200e64c2e54 100644 (file)
@@ -259,6 +259,7 @@ IPv6Prefix.PreferredLifetimeSec,             config_parse_prefix_lifetime,
 IPv6Prefix.Assign,                           config_parse_prefix_assign,                               0,                             0
 IPv6RoutePrefix.Route,                       config_parse_route_prefix,                                0,                             0
 IPv6RoutePrefix.LifetimeSec,                 config_parse_route_prefix_lifetime,                       0,                             0
+LLDP.MUDURL,                                 config_parse_lldp_mud,                                    0,                             0
 CAN.BitRate,                                 config_parse_can_bitrate,                                 0,                             offsetof(Network, can_bitrate)
 CAN.SamplePoint,                             config_parse_permille,                                    0,                             offsetof(Network, can_sample_point)
 CAN.DataBitRate,                             config_parse_can_bitrate,                                 0,                             offsetof(Network, can_data_bitrate)
index f46b91e724392a1395e0167482cfe04c076aae42..9edf9e15616d7c93db39cbebeb6db160b2fc0070 100644 (file)
@@ -485,6 +485,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
                               "IPv6PrefixDelegation\0"
                               "IPv6Prefix\0"
                               "IPv6RoutePrefix\0"
+                              "LLDP\0"
                               "TrafficControlQueueingDiscipline\0"
                               "CAN\0"
                               "QDisc\0"
@@ -726,6 +727,8 @@ static Network *network_free(Network *network) {
 
         set_free_free(network->dnssec_negative_trust_anchors);
 
+        free(network->lldp_mud);
+
         ordered_hashmap_free(network->dhcp_client_send_options);
         ordered_hashmap_free(network->dhcp_client_send_vendor_options);
         ordered_hashmap_free(network->dhcp_server_send_options);
index e6182327e103398984958d8f3522f14f3f025715..1c600ae7bd81bd5e98bea95bca5f88e931bb8c16 100644 (file)
@@ -258,8 +258,10 @@ struct Network {
         bool required_for_online; /* Is this network required to be considered online? */
         LinkOperationalStateRange required_operstate_for_online;
 
+        /* LLDP support */
         LLDPMode lldp_mode; /* LLDP reception */
         LLDPEmit lldp_emit; /* LLDP transmission */
+        char *lldp_mud;    /* LLDP MUD URL */
 
         LIST_HEAD(Address, static_addresses);
         LIST_HEAD(Route, static_routes);
index 672c2a96652cff810b2418c4154af2d2b12c506c..147ea1b63945b76d1e7f728bbb1ffe7cff6799ef 100644 (file)
@@ -199,6 +199,8 @@ LifetimeSec=
 EgressUntagged=
 VLAN=
 PVID=
+[LLDP]
+MUDURL=
 [CAN]
 SamplePoint=
 BitRate=