mirror of
https://github.com/systemd/systemd
synced 2024-10-07 00:30:59 +00:00
network: Introduce MACsec
Media Access Control Security (MACsec) is an 802.1AE IEEE industry-standard security technology that provides secure communication for all traffic on Ethernet links. MACsec provides point-to-point security on Ethernet links between directly connected nodes and is capable of identifying and preventing most security threats, including denial of service, intrusion, man-in-the-middle, masquerading, passive wiretapping, and playback attacks. Closes #5754
This commit is contained in:
parent
45cae4abfb
commit
81962db798
|
@ -151,6 +151,9 @@
|
|||
<row><entry><varname>l2tp</varname></entry>
|
||||
<entry>A Layer 2 Tunneling Protocol (L2TP) is a tunneling protocol used to support virtual private networks (VPNs) or as part of the delivery of services by ISPs. It does not provide any encryption or confidentiality by itself</entry></row>
|
||||
|
||||
<row><entry><varname>macsec</varname></entry>
|
||||
<entry>Media Access Control Security (MACsec) is an 802.1AE IEEE industry-standard security technology that provides secure communication for all traffic on Ethernet links. MACsec provides point-to-point security on Ethernet links between directly connected nodes and is capable of identifying and preventing most security threats.</entry></row>
|
||||
|
||||
<row><entry><varname>vrf</varname></entry>
|
||||
<entry>A Virtual Routing and Forwarding (<ulink url="https://www.kernel.org/doc/Documentation/networking/vrf.txt">VRF</ulink>) interface to create separate routing and forwarding domains.</entry></row>
|
||||
|
||||
|
@ -851,6 +854,124 @@
|
|||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
<refsect1>
|
||||
<title>[MACsec] Section Options</title>
|
||||
<para>The <literal>[MACsec]</literal> section only applies for network devices of kind
|
||||
<literal>macsec</literal>, and accepts the following keys:</para>
|
||||
|
||||
<variablelist class='network-directives'>
|
||||
<varlistentry>
|
||||
<term><varname>Port=</varname></term>
|
||||
<listitem>
|
||||
<para>Specifies the port to be used for the MACsec transmit channel. The port is used to make
|
||||
secure channel identifier (SCI). Takes a value between 1 and 65535. Defaults to unset.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>Encrypt=</varname></term>
|
||||
<listitem>
|
||||
<para>Takes a boolean. When true, enable encryption. Defaults to unset.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
<refsect1>
|
||||
<title>[MACsecReceiveChannel] Section Options</title>
|
||||
<para>The <literal>[MACsecReceiveChannel]</literal> section only applies for network devices of
|
||||
kind <literal>macsec</literal>, and accepts the following keys:</para>
|
||||
|
||||
<variablelist class='network-directives'>
|
||||
<varlistentry>
|
||||
<term><varname>Port=</varname></term>
|
||||
<listitem>
|
||||
<para>Specifies the port to be used for the MACsec receive channel. The port is used to make
|
||||
secure channel identifier (SCI). Takes a value between 1 and 65535. This option is
|
||||
compulsory, and is not set by default.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>MACAddress=</varname></term>
|
||||
<listitem>
|
||||
<para>Specifies the MAC address to be used for the MACsec receive channel. The MAC address
|
||||
used to make secure channel identifier (SCI). This option is compulsory, and is not set by
|
||||
default.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
<refsect1>
|
||||
<title>[MACsecTransmitAssociation] Section Options</title>
|
||||
<para>The <literal>[MACsecTransmitAssociation]</literal> section only applies for network devices
|
||||
of kind <literal>macsec</literal>, and accepts the following keys:</para>
|
||||
|
||||
<variablelist class='network-directives'>
|
||||
<varlistentry>
|
||||
<term><varname>PacketNumber=</varname></term>
|
||||
<listitem>
|
||||
<para>Specifies the packet number to be used for replay protection and the construction of
|
||||
the initialization vector (along with the secure channel identifier [SCI]). Takes a value
|
||||
between 1-4,294,967,295. Defaults to unset.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>KeyId=</varname></term>
|
||||
<listitem>
|
||||
<para>Specifies the identification for the key. Takes a number between 0-255. This option
|
||||
is compulsory, and is not set by default.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>Key=</varname></term>
|
||||
<listitem>
|
||||
<para>Specifies the encryption key used in the transmission channel. The same key must be
|
||||
configured on the peer’s matching receive channel. This option is compulsory, and is not set
|
||||
by default. Takes a 128-bit key encoded in a hexadecimal string, for example
|
||||
<literal>dffafc8d7b9a43d5b9a3dfbbf6a30c16</literal>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
<refsect1>
|
||||
<title>[MACsecReceiveAssociation] Section Options</title>
|
||||
<para>The <literal>[MACsecReceiveAssociation]</literal> section only applies for
|
||||
network devices of kind <literal>macsec</literal>, and accepts the
|
||||
following keys:</para>
|
||||
|
||||
<variablelist class='network-directives'>
|
||||
<varlistentry>
|
||||
<term><varname>Port=</varname></term>
|
||||
<listitem>
|
||||
<para>Accepts the same key in <literal>[MACsecReceiveChannel]</literal> section.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>MACAddress=</varname></term>
|
||||
<listitem>
|
||||
<para>Accepts the same key in <literal>[MACsecReceiveChannel]</literal> section.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>PacketNumber=</varname></term>
|
||||
<listitem>
|
||||
<para>Accepts the same key in <literal>[MACsecTransmitAssociation]</literal> section.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>KeyId=</varname></term>
|
||||
<listitem>
|
||||
<para>Accepts the same key in <literal>[MACsecTransmitAssociation]</literal> section.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>Key=</varname></term>
|
||||
<listitem>
|
||||
<para>Accepts the same key in <literal>[MACsecTransmitAssociation]</literal> section.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
<refsect1>
|
||||
<title>[Tunnel] Section Options</title>
|
||||
|
||||
|
|
|
@ -768,6 +768,14 @@
|
|||
This option may be specified more than once.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>MACsec=</varname></term>
|
||||
<listitem>
|
||||
<para>The name of a MACsec device to create on the link. See
|
||||
<citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
|
||||
This option may be specified more than once.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>ActiveSlave=</varname></term>
|
||||
<listitem>
|
||||
|
|
|
@ -14,6 +14,7 @@ static const genl_family genl_families[] = {
|
|||
[SD_GENL_WIREGUARD] = { .name = "wireguard", .version = 1 },
|
||||
[SD_GENL_FOU] = { .name = "fou", .version = 1 },
|
||||
[SD_GENL_L2TP] = { .name = "l2tp", .version = 1},
|
||||
[SD_GENL_MACSEC] = { .name = "macsec", .version = 1},
|
||||
};
|
||||
|
||||
int sd_genl_socket_open(sd_netlink **ret) {
|
||||
|
|
|
@ -318,6 +318,23 @@ int sd_netlink_message_append_u32(sd_netlink_message *m, unsigned short type, ui
|
|||
return 0;
|
||||
}
|
||||
|
||||
int sd_netlink_message_append_u64(sd_netlink_message *m, unsigned short type, uint64_t data) {
|
||||
int r;
|
||||
|
||||
assert_return(m, -EINVAL);
|
||||
assert_return(!m->sealed, -EPERM);
|
||||
|
||||
r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U64);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = add_rtattr(m, type, &data, sizeof(uint64_t));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, const void *data, size_t len) {
|
||||
int r;
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/if_addrlabel.h>
|
||||
#include <linux/if_bridge.h>
|
||||
#include <linux/if_link.h>
|
||||
#include <linux/if_macsec.h>
|
||||
#include <linux/if_tunnel.h>
|
||||
#include <linux/l2tp.h>
|
||||
#include <linux/veth.h>
|
||||
|
@ -306,6 +307,22 @@ static const NLType rtnl_link_info_data_can_types[] = {
|
|||
[IFLA_CAN_CTRLMODE] = { .size = sizeof(struct can_ctrlmode) },
|
||||
};
|
||||
|
||||
static const NLType rtnl_link_info_data_macsec_types[] = {
|
||||
[IFLA_MACSEC_SCI] = { .type = NETLINK_TYPE_U64 },
|
||||
[IFLA_MACSEC_PORT] = { .type = NETLINK_TYPE_U16 },
|
||||
[IFLA_MACSEC_ICV_LEN] = { .type = NETLINK_TYPE_U8 },
|
||||
[IFLA_MACSEC_CIPHER_SUITE] = { .type = NETLINK_TYPE_U64 },
|
||||
[IFLA_MACSEC_WINDOW] = { .type = NETLINK_TYPE_U32 },
|
||||
[IFLA_MACSEC_ENCODING_SA] = { .type = NETLINK_TYPE_U8 },
|
||||
[IFLA_MACSEC_ENCRYPT] = { .type = NETLINK_TYPE_U8 },
|
||||
[IFLA_MACSEC_PROTECT] = { .type = NETLINK_TYPE_U8 },
|
||||
[IFLA_MACSEC_INC_SCI] = { .type = NETLINK_TYPE_U8 },
|
||||
[IFLA_MACSEC_ES] = { .type = NETLINK_TYPE_U8 },
|
||||
[IFLA_MACSEC_SCB] = { .type = NETLINK_TYPE_U8 },
|
||||
[IFLA_MACSEC_REPLAY_PROTECT] = { .type = NETLINK_TYPE_U8 },
|
||||
[IFLA_MACSEC_VALIDATION] = { .type = NETLINK_TYPE_U8 },
|
||||
};
|
||||
|
||||
/* these strings must match the .kind entries in the kernel */
|
||||
static const char* const nl_union_link_info_data_table[] = {
|
||||
[NL_UNION_LINK_INFO_DATA_BOND] = "bond",
|
||||
|
@ -334,6 +351,7 @@ static const char* const nl_union_link_info_data_table[] = {
|
|||
[NL_UNION_LINK_INFO_DATA_WIREGUARD] = "wireguard",
|
||||
[NL_UNION_LINK_INFO_DATA_NETDEVSIM] = "netdevsim",
|
||||
[NL_UNION_LINK_INFO_DATA_CAN] = "can",
|
||||
[NL_UNION_LINK_INFO_DATA_MACSEC] = "macsec",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(nl_union_link_info_data, NLUnionLinkInfoData);
|
||||
|
@ -383,6 +401,8 @@ static const NLTypeSystem rtnl_link_info_data_type_systems[] = {
|
|||
.types = rtnl_link_info_data_vxcan_types },
|
||||
[NL_UNION_LINK_INFO_DATA_CAN] = { .count = ELEMENTSOF(rtnl_link_info_data_can_types),
|
||||
.types = rtnl_link_info_data_can_types },
|
||||
[NL_UNION_LINK_INFO_DATA_MACSEC] = { .count = ELEMENTSOF(rtnl_link_info_data_macsec_types),
|
||||
.types = rtnl_link_info_data_macsec_types },
|
||||
};
|
||||
|
||||
static const NLTypeSystemUnion rtnl_link_info_data_type_system_union = {
|
||||
|
@ -843,11 +863,76 @@ static const NLTypeSystem genl_l2tp_tunnel_session_type_system = {
|
|||
.types = genl_l2tp,
|
||||
};
|
||||
|
||||
static const NLType genl_rxsc_types[] = {
|
||||
[MACSEC_RXSC_ATTR_SCI] = { .type = NETLINK_TYPE_U64 },
|
||||
};
|
||||
|
||||
static const NLTypeSystem genl_rxsc_config_type_system = {
|
||||
.count = ELEMENTSOF(genl_rxsc_types),
|
||||
.types = genl_rxsc_types,
|
||||
};
|
||||
|
||||
static const NLType genl_macsec_rxsc_types[] = {
|
||||
[MACSEC_ATTR_IFINDEX] = { .type = NETLINK_TYPE_U32 },
|
||||
[MACSEC_ATTR_RXSC_CONFIG] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_rxsc_config_type_system },
|
||||
};
|
||||
|
||||
static const NLTypeSystem genl_macsec_rxsc_type_system = {
|
||||
.count = ELEMENTSOF(genl_macsec_rxsc_types),
|
||||
.types = genl_macsec_rxsc_types,
|
||||
};
|
||||
|
||||
static const NLType genl_macsec_sa_config_types[] = {
|
||||
[MACSEC_SA_ATTR_AN] = { .type = NETLINK_TYPE_U8 },
|
||||
[MACSEC_SA_ATTR_ACTIVE] = { .type = NETLINK_TYPE_U8 },
|
||||
[MACSEC_SA_ATTR_PN] = { .type = NETLINK_TYPE_U32 },
|
||||
[MACSEC_SA_ATTR_KEYID] = { .size = MACSEC_KEYID_LEN },
|
||||
[MACSEC_SA_ATTR_KEY] = { .size = MACSEC_MAX_KEY_LEN },
|
||||
};
|
||||
|
||||
static const NLTypeSystem genl_macsec_sa_config_type_system = {
|
||||
.count = ELEMENTSOF(genl_macsec_sa_config_types),
|
||||
.types = genl_macsec_sa_config_types,
|
||||
};
|
||||
|
||||
static const NLType genl_macsec_rxsa_types[] = {
|
||||
[MACSEC_ATTR_IFINDEX] = { .type = NETLINK_TYPE_U32 },
|
||||
[MACSEC_ATTR_SA_CONFIG] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_sa_config_type_system },
|
||||
};
|
||||
|
||||
static const NLTypeSystem genl_macsec_rxsa_type_system = {
|
||||
.count = ELEMENTSOF(genl_macsec_rxsa_types),
|
||||
.types = genl_macsec_rxsa_types,
|
||||
};
|
||||
|
||||
static const NLType genl_macsec_sa_types[] = {
|
||||
[MACSEC_ATTR_IFINDEX] = { .type = NETLINK_TYPE_U32 },
|
||||
[MACSEC_ATTR_RXSC_CONFIG] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_rxsc_config_type_system },
|
||||
[MACSEC_ATTR_SA_CONFIG] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_sa_config_type_system },
|
||||
};
|
||||
|
||||
static const NLTypeSystem genl_macsec_sa_type_system = {
|
||||
.count = ELEMENTSOF(genl_macsec_sa_types),
|
||||
.types = genl_macsec_sa_types,
|
||||
};
|
||||
|
||||
static const NLType genl_macsec[] = {
|
||||
[MACSEC_CMD_ADD_RXSC] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_rxsc_type_system },
|
||||
[MACSEC_CMD_ADD_TXSA] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_rxsa_type_system},
|
||||
[MACSEC_CMD_ADD_RXSA] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_sa_type_system },
|
||||
};
|
||||
|
||||
static const NLTypeSystem genl_macsec_device_type_system = {
|
||||
.count = ELEMENTSOF(genl_macsec),
|
||||
.types = genl_macsec,
|
||||
};
|
||||
|
||||
static const NLType genl_families[] = {
|
||||
[SD_GENL_ID_CTRL] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_ctrl_id_ctrl_type_system },
|
||||
[SD_GENL_WIREGUARD] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_type_system },
|
||||
[SD_GENL_FOU] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_fou_cmds_type_system},
|
||||
[SD_GENL_L2TP] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_l2tp_tunnel_session_type_system },
|
||||
[SD_GENL_MACSEC] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_device_type_system },
|
||||
};
|
||||
|
||||
const NLTypeSystem genl_family_type_system_root = {
|
||||
|
|
|
@ -80,6 +80,7 @@ typedef enum NLUnionLinkInfoData {
|
|||
NL_UNION_LINK_INFO_DATA_WIREGUARD,
|
||||
NL_UNION_LINK_INFO_DATA_NETDEVSIM,
|
||||
NL_UNION_LINK_INFO_DATA_CAN,
|
||||
NL_UNION_LINK_INFO_DATA_MACSEC,
|
||||
_NL_UNION_LINK_INFO_DATA_MAX,
|
||||
_NL_UNION_LINK_INFO_DATA_INVALID = -1
|
||||
} NLUnionLinkInfoData;
|
||||
|
|
|
@ -39,6 +39,8 @@ sources = files('''
|
|||
netdev/fou-tunnel.h
|
||||
netdev/l2tp-tunnel.c
|
||||
netdev/l2tp-tunnel.h
|
||||
netdev/macsec.c
|
||||
netdev/macsec.h
|
||||
networkd-address-label.c
|
||||
networkd-address-label.h
|
||||
networkd-address-pool.c
|
||||
|
|
903
src/network/netdev/macsec.c
Normal file
903
src/network/netdev/macsec.c
Normal file
|
@ -0,0 +1,903 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_macsec.h>
|
||||
#include <linux/genetlink.h>
|
||||
|
||||
#include "conf-parser.h"
|
||||
#include "hashmap.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "macsec.h"
|
||||
#include "memory-util.h"
|
||||
#include "missing.h"
|
||||
#include "netlink-util.h"
|
||||
#include "network-internal.h"
|
||||
#include "networkd-address.h"
|
||||
#include "networkd-manager.h"
|
||||
#include "sd-netlink.h"
|
||||
#include "socket-util.h"
|
||||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
#include "util.h"
|
||||
|
||||
static void macsec_receive_association_free(ReceiveAssociation *c) {
|
||||
if (!c)
|
||||
return;
|
||||
|
||||
if (c->macsec && c->section)
|
||||
ordered_hashmap_remove(c->macsec->receive_associations_by_section, c->section);
|
||||
|
||||
network_config_section_free(c->section);
|
||||
free(c->sa.key);
|
||||
|
||||
free(c);
|
||||
}
|
||||
|
||||
DEFINE_NETWORK_SECTION_FUNCTIONS(ReceiveAssociation, macsec_receive_association_free);
|
||||
|
||||
static int macsec_receive_association_new_static(MACsec *s, const char *filename, unsigned section_line, ReceiveAssociation **ret) {
|
||||
_cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
|
||||
_cleanup_(macsec_receive_association_freep) ReceiveAssociation *c = NULL;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(ret);
|
||||
assert(filename);
|
||||
assert(section_line > 0);
|
||||
|
||||
r = network_config_section_new(filename, section_line, &n);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
c = ordered_hashmap_get(s->receive_associations_by_section, n);
|
||||
if (c) {
|
||||
*ret = TAKE_PTR(c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
c = new(ReceiveAssociation, 1);
|
||||
if (!c)
|
||||
return -ENOMEM;
|
||||
|
||||
*c = (ReceiveAssociation) {
|
||||
.macsec = s,
|
||||
.section = TAKE_PTR(n),
|
||||
};
|
||||
|
||||
r = ordered_hashmap_ensure_allocated(&s->receive_associations_by_section, &network_config_hash_ops);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = ordered_hashmap_put(s->receive_associations_by_section, c->section, c);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void macsec_receive_channel_free(ReceiveChannel *c) {
|
||||
if (!c)
|
||||
return;
|
||||
|
||||
if (c->macsec && c->section)
|
||||
ordered_hashmap_remove(c->macsec->receive_channels_by_section, c->section);
|
||||
|
||||
network_config_section_free(c->section);
|
||||
|
||||
free(c);
|
||||
}
|
||||
|
||||
DEFINE_NETWORK_SECTION_FUNCTIONS(ReceiveChannel, macsec_receive_channel_free);
|
||||
|
||||
static int macsec_receive_channel_new_static(MACsec *s, const char *filename, unsigned section_line, ReceiveChannel **ret) {
|
||||
_cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
|
||||
_cleanup_(macsec_receive_channel_freep) ReceiveChannel *c = NULL;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(ret);
|
||||
assert(filename);
|
||||
assert(section_line > 0);
|
||||
|
||||
r = network_config_section_new(filename, section_line, &n);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
c = ordered_hashmap_get(s->receive_channels_by_section, n);
|
||||
if (c) {
|
||||
*ret = TAKE_PTR(c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
c = new(ReceiveChannel, 1);
|
||||
if (!c)
|
||||
return -ENOMEM;
|
||||
|
||||
*c = (ReceiveChannel) {
|
||||
.macsec = s,
|
||||
.section = TAKE_PTR(n),
|
||||
};
|
||||
|
||||
r = ordered_hashmap_ensure_allocated(&s->receive_channels_by_section, &network_config_hash_ops);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = ordered_hashmap_put(s->receive_channels_by_section, c->section, c);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void macsec_transmit_association_free(TransmitAssociation *a) {
|
||||
if (!a)
|
||||
return;
|
||||
|
||||
if (a->macsec && a->section)
|
||||
ordered_hashmap_remove(a->macsec->transmit_associations_by_section, a->section);
|
||||
|
||||
network_config_section_free(a->section);
|
||||
free(a->sa.key);
|
||||
|
||||
free(a);
|
||||
}
|
||||
|
||||
DEFINE_NETWORK_SECTION_FUNCTIONS(TransmitAssociation, macsec_transmit_association_free);
|
||||
|
||||
static int macsec_transmit_association_new_static(MACsec *s, const char *filename, unsigned section_line, TransmitAssociation **ret) {
|
||||
_cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
|
||||
_cleanup_(macsec_transmit_association_freep) TransmitAssociation *a = NULL;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(ret);
|
||||
assert(filename);
|
||||
assert(section_line > 0);
|
||||
|
||||
r = network_config_section_new(filename, section_line, &n);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
a = ordered_hashmap_get(s->transmit_associations_by_section, n);
|
||||
if (a) {
|
||||
*ret = TAKE_PTR(a);
|
||||
return 0;
|
||||
}
|
||||
|
||||
a = new(TransmitAssociation, 1);
|
||||
if (!a)
|
||||
return -ENOMEM;
|
||||
|
||||
*a = (TransmitAssociation) {
|
||||
.macsec = s,
|
||||
.section = TAKE_PTR(n),
|
||||
};
|
||||
|
||||
r = ordered_hashmap_ensure_allocated(&s->transmit_associations_by_section, &network_config_hash_ops);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = ordered_hashmap_put(s->transmit_associations_by_section, a->section, a);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(a);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int netdev_macsec_fill_message(NetDev *netdev, int command, sd_netlink_message **ret) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
int r;
|
||||
|
||||
assert(netdev);
|
||||
assert(netdev->ifindex > 0);
|
||||
|
||||
r = sd_genl_message_new(netdev->manager->genl, SD_GENL_MACSEC, command, &m);
|
||||
if (r < 0)
|
||||
return log_netdev_error_errno(netdev, r, "Failed to create generic netlink message: %m");
|
||||
|
||||
r = sd_netlink_message_append_u32(m, MACSEC_ATTR_IFINDEX, netdev->ifindex);
|
||||
if (r < 0)
|
||||
return log_netdev_error_errno(netdev, r, "Could not append MACSEC_ATTR_IFINDEX attribute: %m");
|
||||
|
||||
*ret = TAKE_PTR(m);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int netdev_macsec_fill_message_sci(NetDev *netdev, MACsecSCI *sci, sd_netlink_message *m) {
|
||||
int r;
|
||||
|
||||
assert(netdev);
|
||||
assert(m);
|
||||
assert(sci);
|
||||
|
||||
r = sd_netlink_message_open_container(m, MACSEC_ATTR_RXSC_CONFIG);
|
||||
if (r < 0)
|
||||
return log_netdev_error_errno(netdev, r, "Could not append MACSEC_ATTR_RXSC_CONFIG attribute: %m");
|
||||
|
||||
r = sd_netlink_message_append_u64(m, MACSEC_RXSC_ATTR_SCI, sci->as_uint64);
|
||||
if (r < 0)
|
||||
return log_netdev_error_errno(netdev, r, "Could not append MACSEC_RXSC_ATTR_SCI attribute: %m");
|
||||
|
||||
r = sd_netlink_message_close_container(m);
|
||||
if (r < 0)
|
||||
return log_netdev_error_errno(netdev, r, "Could not append MACSEC_ATTR_RXSC_CONFIG attribute: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int netdev_macsec_fill_message_sa(NetDev *netdev, SecurityAssociation *a, sd_netlink_message *m) {
|
||||
int r;
|
||||
|
||||
assert(netdev);
|
||||
assert(a);
|
||||
assert(m);
|
||||
|
||||
r = sd_netlink_message_open_container(m, MACSEC_ATTR_SA_CONFIG);
|
||||
if (r < 0)
|
||||
return log_netdev_error_errno(netdev, r, "Could not append MACSEC_ATTR_SA_CONFIG attribute: %m");
|
||||
|
||||
r = sd_netlink_message_append_u8(m, MACSEC_SA_ATTR_AN, a->association_number);
|
||||
if (r < 0)
|
||||
return log_netdev_error_errno(netdev, r, "Could not append MACSEC_SA_ATTR_AN attribute: %m");
|
||||
|
||||
if (a->packet_number > 0) {
|
||||
r = sd_netlink_message_append_u32(m, MACSEC_SA_ATTR_PN, a->packet_number);
|
||||
if (r < 0)
|
||||
return log_netdev_error_errno(netdev, r, "Could not append MACSEC_SA_ATTR_PN attribute: %m");
|
||||
}
|
||||
|
||||
if (a->key_len > 0) {
|
||||
r = sd_netlink_message_append_data(m, MACSEC_SA_ATTR_KEYID, a->key_id, MACSEC_KEYID_LEN);
|
||||
if (r < 0)
|
||||
return log_netdev_error_errno(netdev, r, "Could not append MACSEC_SA_ATTR_KEYID attribute: %m");
|
||||
|
||||
r = sd_netlink_message_append_data(m, MACSEC_SA_ATTR_KEY, a->key, a->key_len);
|
||||
if (r < 0)
|
||||
return log_netdev_error_errno(netdev, r, "Could not append MACSEC_SA_ATTR_KEY attribute: %m");
|
||||
}
|
||||
|
||||
r = sd_netlink_message_close_container(m);
|
||||
if (r < 0)
|
||||
return log_netdev_error_errno(netdev, r, "Could not append MACSEC_ATTR_SA_CONFIG attribute: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int macsec_receive_association_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) {
|
||||
int r;
|
||||
|
||||
assert(netdev);
|
||||
assert(netdev->state != _NETDEV_STATE_INVALID);
|
||||
|
||||
r = sd_netlink_message_get_errno(m);
|
||||
if (r == -EEXIST)
|
||||
log_netdev_info(netdev,
|
||||
"MACsec receive secure association exists, "
|
||||
"using existing without changing its parameters");
|
||||
else if (r < 0) {
|
||||
log_netdev_warning_errno(netdev, r,
|
||||
"Failed to add receive secure association: %m");
|
||||
netdev_drop(netdev);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
log_netdev_debug(netdev, "Receive secure association is configured");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int netdev_macsec_configure_receive_association(NetDev *netdev, ReceiveAssociation *a) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
int r;
|
||||
|
||||
assert(netdev);
|
||||
assert(a);
|
||||
|
||||
r = netdev_macsec_fill_message(netdev, MACSEC_CMD_ADD_RXSA, &m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = netdev_macsec_fill_message_sa(netdev, &a->sa, m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = netdev_macsec_fill_message_sci(netdev, &a->sci, m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = netlink_call_async(netdev->manager->genl, NULL, m, macsec_receive_association_handler,
|
||||
netdev_destroy_callback, netdev);
|
||||
if (r < 0)
|
||||
return log_netdev_error_errno(netdev, r, "Failed to configure receive secure association: %m");
|
||||
|
||||
netdev_ref(netdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int macsec_receive_channel_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) {
|
||||
int r;
|
||||
|
||||
assert(netdev);
|
||||
assert(netdev->state != _NETDEV_STATE_INVALID);
|
||||
|
||||
r = sd_netlink_message_get_errno(m);
|
||||
if (r == -EEXIST)
|
||||
log_netdev_debug(netdev,
|
||||
"MACsec receive channel exists, "
|
||||
"using existing without changing its parameters");
|
||||
else if (r < 0) {
|
||||
log_netdev_warning_errno(netdev, r,
|
||||
"Failed to add receive secure channel: %m");
|
||||
netdev_drop(netdev);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
log_netdev_debug(netdev, "Receive channel is configured");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int netdev_macsec_configure_receive_channel(NetDev *netdev, ReceiveChannel *c) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
int r;
|
||||
|
||||
assert(netdev);
|
||||
assert(c);
|
||||
|
||||
r = netdev_macsec_fill_message(netdev, MACSEC_CMD_ADD_RXSC, &m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = netdev_macsec_fill_message_sci(netdev, &c->sci, m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = netlink_call_async(netdev->manager->genl, NULL, m, macsec_receive_channel_handler,
|
||||
netdev_destroy_callback, netdev);
|
||||
if (r < 0)
|
||||
return log_netdev_error_errno(netdev, r, "Failed to configure receive channel: %m");
|
||||
|
||||
netdev_ref(netdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int macsec_transmit_association_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) {
|
||||
int r;
|
||||
|
||||
assert(netdev);
|
||||
assert(netdev->state != _NETDEV_STATE_INVALID);
|
||||
|
||||
r = sd_netlink_message_get_errno(m);
|
||||
if (r == -EEXIST)
|
||||
log_netdev_info(netdev,
|
||||
"MACsec transmit secure association exists, "
|
||||
"using existing without changing its parameters");
|
||||
else if (r < 0) {
|
||||
log_netdev_warning_errno(netdev, r,
|
||||
"Failed to add transmit secure association: %m");
|
||||
netdev_drop(netdev);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
log_netdev_debug(netdev, "Transmit secure association is configured");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int netdev_macsec_configure_transmit_association(NetDev *netdev, TransmitAssociation *a) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
int r;
|
||||
|
||||
assert(netdev);
|
||||
assert(a);
|
||||
|
||||
r = netdev_macsec_fill_message(netdev, MACSEC_CMD_ADD_TXSA, &m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = netdev_macsec_fill_message_sa(netdev, &a->sa, m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = netlink_call_async(netdev->manager->genl, NULL, m, macsec_transmit_association_handler,
|
||||
netdev_destroy_callback, netdev);
|
||||
if (r < 0)
|
||||
return log_netdev_error_errno(netdev, r, "Failed to configure transmit secure association: %m");
|
||||
|
||||
netdev_ref(netdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int netdev_macsec_configure(NetDev *netdev, Link *link, sd_netlink_message *m) {
|
||||
ReceiveAssociation *n;
|
||||
TransmitAssociation *a;
|
||||
ReceiveChannel *c;
|
||||
Iterator i;
|
||||
MACsec *s;
|
||||
int r;
|
||||
|
||||
assert(netdev);
|
||||
s = MACSEC(netdev);
|
||||
assert(s);
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(a, s->transmit_associations_by_section, i) {
|
||||
r = netdev_macsec_configure_transmit_association(netdev, a);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(c, s->receive_channels_by_section, i) {
|
||||
r = netdev_macsec_configure_receive_channel(netdev, c);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(n, s->receive_associations_by_section, i) {
|
||||
r = netdev_macsec_configure_receive_association(netdev, n);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int netdev_macsec_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
|
||||
MACsec *v;
|
||||
int r;
|
||||
|
||||
assert(netdev);
|
||||
assert(m);
|
||||
|
||||
v = MACSEC(netdev);
|
||||
|
||||
if (v->port > 0) {
|
||||
r = sd_netlink_message_append_u16(m, IFLA_MACSEC_PORT, v->port);
|
||||
if (r < 0)
|
||||
return log_netdev_error_errno(netdev, r, "Could not append IFLA_MACSEC_PORT attribute: %m");
|
||||
}
|
||||
|
||||
if (v->encrypt >= 0) {
|
||||
r = sd_netlink_message_append_u8(m, IFLA_MACSEC_ENCRYPT, v->encrypt);
|
||||
if (r < 0)
|
||||
return log_netdev_error_errno(netdev, r, "Could not append IFLA_MACSEC_ENCRYPT attribute: %m");
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int config_parse_macsec_port(
|
||||
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_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL;
|
||||
_cleanup_(macsec_receive_channel_free_or_set_invalidp) ReceiveChannel *c = NULL;
|
||||
MACsec *s = userdata;
|
||||
uint16_t port;
|
||||
be16_t *dest;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(section);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
/* This parses port used to make Secure Channel Identifier (SCI) */
|
||||
|
||||
if (streq(section, "MACsec"))
|
||||
dest = &s->port;
|
||||
else if (streq(section, "MACsecReceiveChannel")) {
|
||||
r = macsec_receive_channel_new_static(s, filename, section_line, &c);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
dest = &c->sci.port;
|
||||
} else {
|
||||
assert(streq(section, "MACsecReceiveAssociation"));
|
||||
|
||||
r = macsec_receive_association_new_static(s, filename, section_line, &b);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
dest = &b->sci.port;
|
||||
}
|
||||
|
||||
r = parse_ip_port(rvalue, &port);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r,
|
||||
"Failed to parse port '%s' for secure channel identifier. Ignoring assignment: %m",
|
||||
rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*dest = htobe16(port);
|
||||
|
||||
TAKE_PTR(b);
|
||||
TAKE_PTR(c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_macsec_hw_address(
|
||||
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_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL;
|
||||
_cleanup_(macsec_receive_channel_free_or_set_invalidp) ReceiveChannel *c = NULL;
|
||||
MACsec *s = userdata;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(section);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
if (streq(section, "MACsecReceiveChannel"))
|
||||
r = macsec_receive_channel_new_static(s, filename, section_line, &c);
|
||||
else
|
||||
r = macsec_receive_association_new_static(s, filename, section_line, &b);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = ether_addr_from_string(rvalue, b ? &b->sci.mac : &c->sci.mac);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r,
|
||||
"Failed to parse MAC address for secure channel identifier. "
|
||||
"Ignoring assignment: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
TAKE_PTR(b);
|
||||
TAKE_PTR(c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_macsec_packet_number(
|
||||
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_(macsec_transmit_association_free_or_set_invalidp) TransmitAssociation *a = NULL;
|
||||
_cleanup_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL;
|
||||
MACsec *s = userdata;
|
||||
uint32_t val, *dest;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(section);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
if (streq(section, "MACsecTransmitAssociation"))
|
||||
r = macsec_transmit_association_new_static(s, filename, section_line, &a);
|
||||
else
|
||||
r = macsec_receive_association_new_static(s, filename, section_line, &b);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
dest = a ? &a->sa.packet_number : &b->sa.packet_number;
|
||||
|
||||
r = safe_atou32(rvalue, &val);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r,
|
||||
"Failed to parse packet number. Ignoring assignment: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
if (streq(section, "MACsecTransmitAssociation") && val == 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0,
|
||||
"Invalid packet number. Ignoring assignment: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*dest = val;
|
||||
TAKE_PTR(a);
|
||||
TAKE_PTR(b);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_macsec_key(
|
||||
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_(macsec_transmit_association_free_or_set_invalidp) TransmitAssociation *a = NULL;
|
||||
_cleanup_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL;
|
||||
_cleanup_free_ void *p;
|
||||
MACsec *s = userdata;
|
||||
SecurityAssociation *dest;
|
||||
size_t l;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(section);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
if (streq(section, "MACsecTransmitAssociation"))
|
||||
r = macsec_transmit_association_new_static(s, filename, section_line, &a);
|
||||
else
|
||||
r = macsec_receive_association_new_static(s, filename, section_line, &b);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
dest = a ? &a->sa : &b->sa;
|
||||
|
||||
r = unhexmem(rvalue, strlen(rvalue), &p, &l);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r,
|
||||
"Failed to parse key. Ignoring assignment: %m");
|
||||
return 0;
|
||||
}
|
||||
if (l != 16) {
|
||||
/* See DEFAULT_SAK_LEN in drivers/net/macsec.c */
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0,
|
||||
"Invalid key length (%zu). Ignoring assignment", l);
|
||||
return 0;
|
||||
}
|
||||
|
||||
free_and_replace(dest->key, p);
|
||||
dest->key_len = l;
|
||||
|
||||
TAKE_PTR(a);
|
||||
TAKE_PTR(b);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_macsec_key_id(
|
||||
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_(macsec_transmit_association_free_or_set_invalidp) TransmitAssociation *a = NULL;
|
||||
_cleanup_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL;
|
||||
_cleanup_free_ void *p;
|
||||
MACsec *s = userdata;
|
||||
uint8_t *dest;
|
||||
size_t l;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(section);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
if (streq(section, "MACsecTransmitAssociation"))
|
||||
r = macsec_transmit_association_new_static(s, filename, section_line, &a);
|
||||
else
|
||||
r = macsec_receive_association_new_static(s, filename, section_line, &b);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = unhexmem(rvalue, strlen(rvalue), &p, &l);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse key id. Ignoring assignment: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
if (l > MACSEC_KEYID_LEN) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0,
|
||||
"The size of key id is too large (%zu), maximum of %zu permitted. "
|
||||
"Ignoring assignment: %s", l, (size_t) MACSEC_KEYID_LEN, rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dest = a ? a->sa.key_id : b->sa.key_id;
|
||||
memcpy_safe(dest, p, l);
|
||||
memzero(dest + l, MACSEC_KEYID_LEN - l);
|
||||
|
||||
TAKE_PTR(a);
|
||||
TAKE_PTR(b);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int macsec_receive_channel_verify(ReceiveChannel *c) {
|
||||
NetDev *netdev;
|
||||
|
||||
assert(c);
|
||||
assert(c->macsec);
|
||||
|
||||
netdev = NETDEV(c->macsec);
|
||||
|
||||
if (section_is_invalid(c->section))
|
||||
return -EINVAL;
|
||||
|
||||
if (ether_addr_is_null(&c->sci.mac))
|
||||
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: MACsec receive channel without MAC address configured. "
|
||||
"Ignoring [MACsecReceiveChannel] section from line %u",
|
||||
c->section->filename, c->section->line);
|
||||
|
||||
if (c->sci.port == 0)
|
||||
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: MACsec receive channel without port configured. "
|
||||
"Ignoring [MACsecReceiveChannel] section from line %u",
|
||||
c->section->filename, c->section->line);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int macsec_transmit_association_verify(TransmitAssociation *t) {
|
||||
NetDev *netdev;
|
||||
|
||||
assert(t);
|
||||
assert(t->macsec);
|
||||
|
||||
netdev = NETDEV(t->macsec);
|
||||
|
||||
if (section_is_invalid(t->section))
|
||||
return -EINVAL;
|
||||
|
||||
if (t->sa.packet_number == 0)
|
||||
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: MACsec transmit secure association without PacketNumber= configured. "
|
||||
"Ignoring [MACsecTransmitAssociation] section from line %u",
|
||||
t->section->filename, t->section->line);
|
||||
|
||||
if (t->sa.key_len <= 0)
|
||||
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: MACsec transmit secure association without key configured. "
|
||||
"Ignoring [MACsecTransmitAssociation] section from line %u",
|
||||
t->section->filename, t->section->line);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int macsec_receive_association_verify(ReceiveAssociation *a) {
|
||||
NetDev *netdev;
|
||||
|
||||
assert(a);
|
||||
assert(a->macsec);
|
||||
|
||||
netdev = NETDEV(a->macsec);
|
||||
|
||||
if (section_is_invalid(a->section))
|
||||
return -EINVAL;
|
||||
|
||||
if (a->sa.key_len <= 0)
|
||||
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: MACsec receive secure association without key configured. "
|
||||
"Ignoring [MACsecReceiveAssociation] section from line %u",
|
||||
a->section->filename, a->section->line);
|
||||
|
||||
if (ether_addr_is_null(&a->sci.mac))
|
||||
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: MACsec receive secure association without MAC address configured. "
|
||||
"Ignoring [MACsecReceiveAssociation] section from line %u",
|
||||
a->section->filename, a->section->line);
|
||||
|
||||
if (a->sci.port == 0)
|
||||
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: MACsec receive secure association without port configured. "
|
||||
"Ignoring [MACsecReceiveAssociation] section from line %u",
|
||||
a->section->filename, a->section->line);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int netdev_macsec_verify(NetDev *netdev, const char *filename) {
|
||||
MACsec *v = MACSEC(netdev);
|
||||
TransmitAssociation *a;
|
||||
ReceiveAssociation *n;
|
||||
ReceiveChannel *c;
|
||||
Iterator i;
|
||||
int r;
|
||||
|
||||
assert(netdev);
|
||||
assert(v);
|
||||
assert(filename);
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(c, v->receive_channels_by_section, i) {
|
||||
r = macsec_receive_channel_verify(c);
|
||||
if (r < 0)
|
||||
macsec_receive_channel_free(c);
|
||||
}
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(a, v->transmit_associations_by_section, i) {
|
||||
r = macsec_transmit_association_verify(a);
|
||||
if (r < 0)
|
||||
macsec_transmit_association_free(a);
|
||||
}
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(n, v->receive_associations_by_section, i) {
|
||||
r = macsec_receive_association_verify(n);
|
||||
if (r < 0)
|
||||
macsec_receive_association_free(n);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void macsec_init(NetDev *netdev) {
|
||||
MACsec *v;
|
||||
|
||||
assert(netdev);
|
||||
|
||||
v = MACSEC(netdev);
|
||||
|
||||
assert(v);
|
||||
|
||||
v->encrypt = -1;
|
||||
}
|
||||
|
||||
static void macsec_done(NetDev *netdev) {
|
||||
MACsec *t;
|
||||
|
||||
assert(netdev);
|
||||
|
||||
t = MACSEC(netdev);
|
||||
|
||||
assert(t);
|
||||
|
||||
ordered_hashmap_free_with_destructor(t->receive_channels_by_section, macsec_receive_channel_free);
|
||||
ordered_hashmap_free_with_destructor(t->transmit_associations_by_section, macsec_transmit_association_free);
|
||||
ordered_hashmap_free_with_destructor(t->receive_associations_by_section, macsec_receive_association_free);
|
||||
}
|
||||
|
||||
const NetDevVTable macsec_vtable = {
|
||||
.object_size = sizeof(MACsec),
|
||||
.init = macsec_init,
|
||||
.sections = "Match\0NetDev\0MACsec\0MACsecReceiveChannel\0MACsecTransmitAssociation\0MACsecReceiveAssociation\0",
|
||||
.fill_message_create = netdev_macsec_fill_message_create,
|
||||
.post_create = netdev_macsec_configure,
|
||||
.done = macsec_done,
|
||||
.create_type = NETDEV_CREATE_STACKED,
|
||||
.config_verify = netdev_macsec_verify,
|
||||
};
|
72
src/network/netdev/macsec.h
Normal file
72
src/network/netdev/macsec.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#include <linux/if_macsec.h>
|
||||
|
||||
#include "in-addr-util.h"
|
||||
#include "netdev.h"
|
||||
#include "networkd-util.h"
|
||||
#include "sparse-endian.h"
|
||||
|
||||
typedef struct MACsec MACsec;
|
||||
|
||||
typedef union MACsecSCI {
|
||||
uint64_t as_uint64;
|
||||
|
||||
struct {
|
||||
struct ether_addr mac;
|
||||
be16_t port;
|
||||
} _packed_;
|
||||
} MACsecSCI;
|
||||
|
||||
assert_cc(sizeof(MACsecSCI) == sizeof(uint64_t));
|
||||
|
||||
typedef struct SecurityAssociation {
|
||||
uint8_t association_number;
|
||||
uint32_t packet_number;
|
||||
uint8_t key_id[MACSEC_KEYID_LEN];
|
||||
uint8_t *key;
|
||||
uint32_t key_len;
|
||||
} SecurityAssociation;
|
||||
|
||||
typedef struct TransmitAssociation {
|
||||
MACsec *macsec;
|
||||
NetworkConfigSection *section;
|
||||
|
||||
SecurityAssociation sa;
|
||||
} TransmitAssociation;
|
||||
|
||||
typedef struct ReceiveAssociation {
|
||||
MACsec *macsec;
|
||||
NetworkConfigSection *section;
|
||||
|
||||
MACsecSCI sci;
|
||||
SecurityAssociation sa;
|
||||
} ReceiveAssociation;
|
||||
|
||||
typedef struct ReceiveChannel {
|
||||
MACsec *macsec;
|
||||
NetworkConfigSection *section;
|
||||
|
||||
MACsecSCI sci;
|
||||
} ReceiveChannel;
|
||||
|
||||
struct MACsec {
|
||||
NetDev meta;
|
||||
|
||||
uint16_t port;
|
||||
int encrypt;
|
||||
|
||||
OrderedHashmap *receive_channels_by_section;
|
||||
OrderedHashmap *transmit_associations_by_section;
|
||||
OrderedHashmap *receive_associations_by_section;
|
||||
};
|
||||
|
||||
DEFINE_NETDEV_CAST(MACSEC, MACsec);
|
||||
extern const NetDevVTable macsec_vtable;
|
||||
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_macsec_port);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_macsec_hw_address);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_macsec_packet_number);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_macsec_key_id);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_macsec_key);
|
|
@ -9,6 +9,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
|
|||
#include "netdev/bridge.h"
|
||||
#include "netdev/geneve.h"
|
||||
#include "netdev/ipvlan.h"
|
||||
#include "netdev/macsec.h"
|
||||
#include "netdev/macvlan.h"
|
||||
#include "netdev/tunnel.h"
|
||||
#include "netdev/tuntap.h"
|
||||
|
@ -132,6 +133,18 @@ GENEVE.UDP6ZeroCheckSumTx, config_parse_bool, 0,
|
|||
GENEVE.UDP6ZeroChecksumTx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumtx)
|
||||
GENEVE.DestinationPort, config_parse_ip_port, 0, offsetof(Geneve, dest_port)
|
||||
GENEVE.FlowLabel, config_parse_geneve_flow_label, 0, 0
|
||||
MACsec.Port, config_parse_macsec_port, 0, 0
|
||||
MACsec.Encrypt, config_parse_tristate, 0, offsetof(MACsec, encrypt)
|
||||
MACsecReceiveChannel.Port, config_parse_macsec_port, 0, 0
|
||||
MACsecReceiveChannel.MACAddress, config_parse_macsec_hw_address, 0, 0
|
||||
MACsecTransmitAssociation.PacketNumber, config_parse_macsec_packet_number, 0, 0
|
||||
MACsecTransmitAssociation.KeyId, config_parse_macsec_key_id, 0, 0
|
||||
MACsecTransmitAssociation.Key, config_parse_macsec_key, 0, 0
|
||||
MACsecReceiveAssociation.Port, config_parse_macsec_port, 0, 0
|
||||
MACsecReceiveAssociation.MACAddress, config_parse_macsec_hw_address, 0, 0
|
||||
MACsecReceiveAssociation.PacketNumber, config_parse_macsec_packet_number, 0, 0
|
||||
MACsecReceiveAssociation.KeyId, config_parse_macsec_key_id, 0, 0
|
||||
MACsecReceiveAssociation.Key, config_parse_macsec_key, 0, 0
|
||||
Tun.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue)
|
||||
Tun.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue)
|
||||
Tun.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info)
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "netdev/geneve.h"
|
||||
#include "netdev/ipvlan.h"
|
||||
#include "netdev/l2tp-tunnel.h"
|
||||
#include "netdev/macsec.h"
|
||||
#include "netdev/macvlan.h"
|
||||
#include "netdev/netdev.h"
|
||||
#include "netdev/netdevsim.h"
|
||||
|
@ -66,6 +67,7 @@ const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
|
|||
[NETDEV_KIND_FOU] = &foutnl_vtable,
|
||||
[NETDEV_KIND_ERSPAN] = &erspan_vtable,
|
||||
[NETDEV_KIND_L2TP] = &l2tptnl_vtable,
|
||||
[NETDEV_KIND_MACSEC] = &macsec_vtable,
|
||||
};
|
||||
|
||||
static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
|
||||
|
@ -98,6 +100,7 @@ static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
|
|||
[NETDEV_KIND_FOU] = "fou",
|
||||
[NETDEV_KIND_ERSPAN] = "erspan",
|
||||
[NETDEV_KIND_L2TP] = "l2tp",
|
||||
[NETDEV_KIND_MACSEC] = "macsec",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
|
||||
|
|
|
@ -47,6 +47,7 @@ typedef enum NetDevKind {
|
|||
NETDEV_KIND_FOU,
|
||||
NETDEV_KIND_ERSPAN,
|
||||
NETDEV_KIND_L2TP,
|
||||
NETDEV_KIND_MACSEC,
|
||||
_NETDEV_KIND_MAX,
|
||||
_NETDEV_KIND_TUNNEL, /* Used by config_parse_stacked_netdev() */
|
||||
_NETDEV_KIND_INVALID = -1
|
||||
|
|
|
@ -46,6 +46,7 @@ Network.MACVTAP, config_parse_stacked_netdev,
|
|||
Network.IPVLAN, config_parse_stacked_netdev, NETDEV_KIND_IPVLAN, offsetof(Network, stacked_netdev_names)
|
||||
Network.VXLAN, config_parse_stacked_netdev, NETDEV_KIND_VXLAN, offsetof(Network, stacked_netdev_names)
|
||||
Network.L2TP, config_parse_stacked_netdev, NETDEV_KIND_L2TP, offsetof(Network, stacked_netdev_names)
|
||||
Network.MACsec, config_parse_stacked_netdev, NETDEV_KIND_MACSEC, offsetof(Network, stacked_netdev_names)
|
||||
Network.Tunnel, config_parse_stacked_netdev, _NETDEV_KIND_TUNNEL, offsetof(Network, stacked_netdev_names)
|
||||
Network.VRF, config_parse_ifname, 0, offsetof(Network, vrf_name)
|
||||
Network.DHCP, config_parse_dhcp, 0, offsetof(Network, dhcp)
|
||||
|
|
|
@ -687,7 +687,7 @@ int config_parse_stacked_netdev(const char *unit,
|
|||
assert(IN_SET(kind,
|
||||
NETDEV_KIND_VLAN, NETDEV_KIND_MACVLAN, NETDEV_KIND_MACVTAP,
|
||||
NETDEV_KIND_IPVLAN, NETDEV_KIND_VXLAN, NETDEV_KIND_L2TP,
|
||||
_NETDEV_KIND_TUNNEL));
|
||||
NETDEV_KIND_MACSEC, _NETDEV_KIND_TUNNEL));
|
||||
|
||||
if (!ifname_valid(rvalue)) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0,
|
||||
|
|
|
@ -40,6 +40,7 @@ typedef enum sd_gen_family {
|
|||
SD_GENL_WIREGUARD,
|
||||
SD_GENL_FOU,
|
||||
SD_GENL_L2TP,
|
||||
SD_GENL_MACSEC,
|
||||
} sd_genl_family;
|
||||
|
||||
/* callback */
|
||||
|
@ -81,6 +82,7 @@ int sd_netlink_message_append_flag(sd_netlink_message *m, unsigned short type);
|
|||
int sd_netlink_message_append_u8(sd_netlink_message *m, unsigned short type, uint8_t data);
|
||||
int sd_netlink_message_append_u16(sd_netlink_message *m, unsigned short type, uint16_t data);
|
||||
int sd_netlink_message_append_u32(sd_netlink_message *m, unsigned short type, uint32_t data);
|
||||
int sd_netlink_message_append_u64(sd_netlink_message *m, unsigned short type, uint64_t data);
|
||||
int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, const void *data, size_t len);
|
||||
int sd_netlink_message_append_in_addr(sd_netlink_message *m, unsigned short type, const struct in_addr *data);
|
||||
int sd_netlink_message_append_in6_addr(sd_netlink_message *m, unsigned short type, const struct in6_addr *data);
|
||||
|
|
|
@ -174,3 +174,19 @@ SessionId=
|
|||
PeerSessionId=
|
||||
Layer2SpecificHeader=
|
||||
Name=
|
||||
[MACSEC]
|
||||
Port=
|
||||
Encrypt=
|
||||
[MACsecReceiveAssociation]
|
||||
Port=
|
||||
MACAddress=
|
||||
PacketNumber=
|
||||
KeyId=
|
||||
Key=
|
||||
[MACsecReceiveChannel]
|
||||
Port=
|
||||
MACAddress=
|
||||
[MACsecTransmitAssociation]
|
||||
PacketNumber=
|
||||
KeyId=
|
||||
Key=
|
||||
|
|
|
@ -111,6 +111,7 @@ IPv6Token=
|
|||
Description=
|
||||
VXLAN=
|
||||
L2TP=
|
||||
MACsec=
|
||||
LinkLocalAddressing=
|
||||
ConfigureWithoutCarrier=
|
||||
NTP=
|
||||
|
|
Loading…
Reference in a new issue