diff options
-rw-r--r-- | man/systemd.netdev.xml | 121 | ||||
-rw-r--r-- | man/systemd.network.xml | 8 | ||||
-rw-r--r-- | src/libsystemd/sd-netlink/generic-netlink.c | 1 | ||||
-rw-r--r-- | src/libsystemd/sd-netlink/netlink-message.c | 17 | ||||
-rw-r--r-- | src/libsystemd/sd-netlink/netlink-types.c | 85 | ||||
-rw-r--r-- | src/libsystemd/sd-netlink/netlink-types.h | 1 | ||||
-rw-r--r-- | src/network/meson.build | 2 | ||||
-rw-r--r-- | src/network/netdev/macsec.c | 903 | ||||
-rw-r--r-- | src/network/netdev/macsec.h | 72 | ||||
-rw-r--r-- | src/network/netdev/netdev-gperf.gperf | 13 | ||||
-rw-r--r-- | src/network/netdev/netdev.c | 3 | ||||
-rw-r--r-- | src/network/netdev/netdev.h | 1 | ||||
-rw-r--r-- | src/network/networkd-network-gperf.gperf | 1 | ||||
-rw-r--r-- | src/network/networkd-network.c | 2 | ||||
-rw-r--r-- | src/systemd/sd-netlink.h | 2 | ||||
-rw-r--r-- | test/fuzz/fuzz-netdev-parser/directives.netdev | 16 | ||||
-rw-r--r-- | test/fuzz/fuzz-network-parser/directives.network | 1 |
17 files changed, 1248 insertions, 1 deletions
diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml index 1836b5fe00..9b131a16b6 100644 --- a/man/systemd.netdev.xml +++ b/man/systemd.netdev.xml @@ -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> @@ -852,6 +855,124 @@ </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> <para>The <literal>[Tunnel]</literal> section only applies for diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 4127084703..2d8eeee88f 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -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> diff --git a/src/libsystemd/sd-netlink/generic-netlink.c b/src/libsystemd/sd-netlink/generic-netlink.c index 384072e881..473d8670a9 100644 --- a/src/libsystemd/sd-netlink/generic-netlink.c +++ b/src/libsystemd/sd-netlink/generic-netlink.c @@ -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) { diff --git a/src/libsystemd/sd-netlink/netlink-message.c b/src/libsystemd/sd-netlink/netlink-message.c index 0dcc53be55..68b232b7d4 100644 --- a/src/libsystemd/sd-netlink/netlink-message.c +++ b/src/libsystemd/sd-netlink/netlink-message.c @@ -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; diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c index 0c67d1c68f..118f319a20 100644 --- a/src/libsystemd/sd-netlink/netlink-types.c +++ b/src/libsystemd/sd-netlink/netlink-types.c @@ -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 = { diff --git a/src/libsystemd/sd-netlink/netlink-types.h b/src/libsystemd/sd-netlink/netlink-types.h index b84fa4762b..a2b3087d15 100644 --- a/src/libsystemd/sd-netlink/netlink-types.h +++ b/src/libsystemd/sd-netlink/netlink-types.h @@ -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; diff --git a/src/network/meson.build b/src/network/meson.build index c95e750306..2acbe858bb 100644 --- a/src/network/meson.build +++ b/src/network/meson.build @@ -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 diff --git a/src/network/netdev/macsec.c b/src/network/netdev/macsec.c new file mode 100644 index 0000000000..1581008be6 --- /dev/null +++ b/src/network/netdev/macsec.c @@ -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, +}; diff --git a/src/network/netdev/macsec.h b/src/network/netdev/macsec.h new file mode 100644 index 0000000000..780f4fc266 --- /dev/null +++ b/src/network/netdev/macsec.h @@ -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); diff --git a/src/network/netdev/netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf index 1a3d6caeb9..6dd8e10d59 100644 --- a/src/network/netdev/netdev-gperf.gperf +++ b/src/network/netdev/netdev-gperf.gperf @@ -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) diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c index c1bcfc66e6..e138393514 100644 --- a/src/network/netdev/netdev.c +++ b/src/network/netdev/netdev.c @@ -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); diff --git a/src/network/netdev/netdev.h b/src/network/netdev/netdev.h index ad4dd2e2b0..29ecead029 100644 --- a/src/network/netdev/netdev.h +++ b/src/network/netdev/netdev.h @@ -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 diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 47a9f7d808..6777d1aba8 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -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) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 2c8896530a..52cfc4bec1 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -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, diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h index 804fe9f03e..d327b27308 100644 --- a/src/systemd/sd-netlink.h +++ b/src/systemd/sd-netlink.h @@ -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); diff --git a/test/fuzz/fuzz-netdev-parser/directives.netdev b/test/fuzz/fuzz-netdev-parser/directives.netdev index 7da3955af6..9ea3475303 100644 --- a/test/fuzz/fuzz-netdev-parser/directives.netdev +++ b/test/fuzz/fuzz-netdev-parser/directives.netdev @@ -174,3 +174,19 @@ SessionId= PeerSessionId= Layer2SpecificHeader= Name= +[MACSEC] +Port= +Encrypt= +[MACsecReceiveAssociation] +Port= +MACAddress= +PacketNumber= +KeyId= +Key= +[MACsecReceiveChannel] +Port= +MACAddress= +[MACsecTransmitAssociation] +PacketNumber= +KeyId= +Key= diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index ddc60a9cbb..cd2031150f 100644 --- a/test/fuzz/fuzz-network-parser/directives.network +++ b/test/fuzz/fuzz-network-parser/directives.network @@ -111,6 +111,7 @@ IPv6Token= Description= VXLAN= L2TP= +MACsec= LinkLocalAddressing= ConfigureWithoutCarrier= NTP= |