Compare commits

...

3 Commits

Author SHA1 Message Date
Lennart Poettering 876acda0ed
Merge pull request #15234 from ssahani/mud-lldp
LLDP : Introduce Manufacturer Usage Description (MUD)
2020-04-09 09:03:47 +02:00
Susant Sahani e9a8c550c1 LLDP: Add support to transmit MUD URL 2020-04-08 00:20:54 +02:00
Susant Sahani f69b4ae885 sd-lldp: Add support to receive MUD 2020-04-07 17:17:29 +02:00
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=