]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/network/networkd-lldp-tx.c
Merge pull request #16532 from yuwata/network-sync-state-file
[thirdparty/systemd.git] / src / network / networkd-lldp-tx.c
index 253308e36cd3129f4aeb6a38fb1806689d649472..9e0b4475240be697a2390cdefe733933d8af6456 100644 (file)
@@ -2,19 +2,25 @@
 
 #include <endian.h>
 #include <inttypes.h>
-#include <string.h>
+#include <net/if.h>
+#include <net/if_arp.h>
 
 #include "alloc-util.h"
 #include "env-file.h"
+#include "escape.h"
 #include "fd-util.h"
 #include "hostname-util.h"
+#include "missing_network.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-util.h"
+#include "strv.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
@@ -37,6 +43,24 @@ static const struct ether_addr lldp_multicast_addr[_LLDP_EMIT_MAX] = {
         [LLDP_EMIT_CUSTOMER_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }},
 };
 
+bool link_lldp_emit_enabled(Link *link) {
+        assert(link);
+
+        if (link->flags & IFF_LOOPBACK)
+                return false;
+
+        if (link->iftype != ARPHRD_ETHER)
+                return false;
+
+        if (!link->network)
+                return false;
+
+        if (link->kind && STR_IN_SET(link->kind, "bridge", "bond"))
+                return false;
+
+        return link->network->lldp_emit != LLDP_EMIT_NO;
+}
+
 static int lldp_write_tlv_header(uint8_t **p, uint8_t id, size_t sz) {
         assert(p);
 
@@ -63,9 +87,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;
@@ -92,6 +118,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 +
@@ -116,6 +145,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;
@@ -166,6 +199,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;
@@ -263,6 +322,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;
@@ -387,7 +447,7 @@ int config_parse_lldp_emit(
         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);
+                        log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse LLDP emission setting, ignoring: %s", rvalue);
                         return 0;
                 }
 
@@ -396,3 +456,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_WARNING, 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_WARNING, filename, line, 0,
+                           "Failed to parse LLDP MUD URL '%s', ignoring: %m", rvalue);
+
+                return 0;
+        }
+
+        return free_and_replace(n->lldp_mud, unescaped);
+}