Merge pull request #15234 from ssahani/mud-lldp

LLDP : Introduce Manufacturer Usage Description (MUD)
This commit is contained in:
Lennart Poettering 2020-04-09 09:03:47 +02:00 committed by GitHub
commit 876acda0ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 137 additions and 2 deletions

View File

@ -459,6 +459,7 @@
reception.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>BindCarrier=</varname></term>
<listitem>
@ -2351,6 +2352,28 @@
</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

View File

@ -50,6 +50,7 @@ static void lldp_neighbor_free(sd_lldp_neighbor *n) {
free(n->port_description);
free(n->system_name);
free(n->system_description);
free(n->mud_url);
free(n->chassis_id_as_string);
free(n->port_id_as_string);
free(n);
@ -292,9 +293,20 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) {
break;
case SD_LLDP_TYPE_PRIVATE:
case SD_LLDP_TYPE_PRIVATE: {
if (length < 4)
log_lldp("Found private TLV that is too short, ignoring.");
else {
/* RFC 8520: MUD URL */
if (memcmp(p, SD_LLDP_OUI_MUD, sizeof(SD_LLDP_OUI_MUD)) == 0 &&
p[sizeof(SD_LLDP_OUI_MUD)] == SD_LLDP_OUI_SUBTYPE_MUD_USAGE_DESCRIPTION) {
r = parse_string(&n->mud_url, p + sizeof(SD_LLDP_OUI_MUD) + 1,
length - 1 - sizeof(SD_LLDP_OUI_MUD));
if (r < 0)
return r;
}
}
}
break;
}
@ -593,6 +605,17 @@ _public_ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const ch
return 0;
}
_public_ int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret) {
assert_return(n, -EINVAL);
assert_return(ret, -EINVAL);
if (!n->mud_url)
return -ENODATA;
*ret = n->mud_url;
return 0;
}
_public_ int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
assert_return(n, -EINVAL);
assert_return(ret, -EINVAL);

View File

@ -54,6 +54,7 @@ struct sd_lldp_neighbor {
char *port_description;
char *system_name;
char *system_description;
char *mud_url;
uint16_t port_vlan_id;

View 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);
}

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

View 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)

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

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

View File

@ -96,6 +96,9 @@ enum {
#define SD_LLDP_OUI_802_1 (uint8_t[]) { 0x00, 0x80, 0xc2 }
#define SD_LLDP_OUI_802_3 (uint8_t[]) { 0x00, 0x12, 0x0f }
#define SD_LLDP_OUI_MUD (uint8_t[]) { 0x00, 0x00, 0x5E }
#define SD_LLDP_OUI_SUBTYPE_MUD_USAGE_DESCRIPTION 0x01
/* IEEE 802.1AB-2009 Annex E */
enum {
SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID = 1,
@ -169,6 +172,7 @@ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec);
int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret);
int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret);
int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret);
int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret);
int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret);
int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret);

View File

@ -199,6 +199,8 @@ LifetimeSec=
EgressUntagged=
VLAN=
PVID=
[LLDP]
MUDURL=
[CAN]
SamplePoint=
BitRate=