diff --git a/man/systemd.network.xml b/man/systemd.network.xml index d2cdb59f46..8d11922c71 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -459,6 +459,7 @@ reception. + BindCarrier= @@ -2351,6 +2352,28 @@ + + [LLDP] Section Options + The [LLDP] section manages the Link Layer Discovery Protocol (LLDP) and accepts the + following keys. + + + MUDURL= + + 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 + RFC 8520 for details. The MUD URL received + from the LLDP packets will be saved at the state files and can be read via + sd_lldp_neighbor_get_mud_url() function. + + + + + [CAN] Section Options The [CAN] section manages the Controller Area Network (CAN bus) and accepts the diff --git a/src/network/networkd-lldp-tx.c b/src/network/networkd-lldp-tx.c index 51db60e1f2..1fbf9d0dd9 100644 --- a/src/network/networkd-lldp-tx.c +++ b/src/network/networkd-lldp-tx.c @@ -6,6 +6,7 @@ #include #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); +} diff --git a/src/network/networkd-lldp-tx.h b/src/network/networkd-lldp-tx.h index 561becda41..1409984ac0 100644 --- a/src/network/networkd-lldp-tx.h +++ b/src/network/networkd-lldp-tx.h @@ -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); diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index fe6adc4089..c63f57f962 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -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) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index f46b91e724..9edf9e1561 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -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); diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index e6182327e1..1c600ae7bd 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -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); diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index 672c2a9665..147ea1b639 100644 --- a/test/fuzz/fuzz-network-parser/directives.network +++ b/test/fuzz/fuzz-network-parser/directives.network @@ -199,6 +199,8 @@ LifetimeSec= EgressUntagged= VLAN= PVID= +[LLDP] +MUDURL= [CAN] SamplePoint= BitRate=