1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2024-12-23 17:34:00 +03:00

Merge pull request #12222 from yuwata/macsec

network: introduce MACsec
This commit is contained in:
Lennart Poettering 2019-04-12 13:59:30 +02:00 committed by GitHub
commit b51629ad84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 2322 additions and 353 deletions

View File

@ -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,161 @@
</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 peers 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>
<varlistentry>
<term><varname>KeyFile=</varname></term>
<listitem>
<para>Takes a absolute path to a file which contains a 128-bit key encoded in a hexadecimal
string, which will be used in the transmission channel. When this option is specified,
<varname>Key=</varname> is ignored. Note that the file must be readable by the user
<literal>systemd-network</literal>, so it should be, e.g., owned by
<literal>root:systemd-network</literal> with a <literal>0640</literal> file mode.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Activate=</varname></term>
<listitem>
<para>Takes a boolean. If enabled, then the security association is activated. Defaults to
unset.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>UseForEncoding=</varname></term>
<listitem>
<para>Takes a boolean. If enabled, then the security association is used for encoding. Only
one <literal>[MACsecTransmitAssociation]</literal> section can enable this option. When enabled,
<varname>Activate=yes</varname> is implied. Defaults to unset.</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>
<varlistentry>
<term><varname>KeyFile=</varname></term>
<listitem>
<para>Accepts the same key in <literal>[MACsecTransmitAssociation]</literal> section.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Activate=</varname></term>
<listitem>
<para>Accepts the same key in <literal>[MACsecTransmitAssociation]</literal> section.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>[Tunnel] Section Options</title>

View File

@ -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>

View File

@ -317,7 +317,8 @@ int read_full_stream_full(
assert(f);
assert(ret_contents);
assert(!(flags & READ_FULL_FILE_UNBASE64) || ret_size);
assert(!FLAGS_SET(flags, READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX));
assert(!(flags & (READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX)) || ret_size);
n_next = LINE_MAX; /* Start size */
@ -394,9 +395,12 @@ int read_full_stream_full(
n_next = MIN(n * 2, READ_FULL_BYTES_MAX);
}
if (flags & READ_FULL_FILE_UNBASE64) {
if (flags & (READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX)) {
buf[l++] = 0;
if (flags & READ_FULL_FILE_UNBASE64)
r = unbase64mem_full(buf, l, flags & READ_FULL_FILE_SECURE, (void **) ret_contents, ret_size);
else
r = unhexmem_full(buf, l, flags & READ_FULL_FILE_SECURE, (void **) ret_contents, ret_size);
goto finalize;
}

View File

@ -31,6 +31,7 @@ typedef enum {
typedef enum {
READ_FULL_FILE_SECURE = 1 << 0,
READ_FULL_FILE_UNBASE64 = 1 << 1,
READ_FULL_FILE_UNHEX = 1 << 2,
} ReadFullFileFlags;
int fopen_unlocked(const char *path, const char *options, FILE **ret);

View File

@ -108,10 +108,12 @@ static int unhex_next(const char **p, size_t *l) {
return r;
}
int unhexmem(const char *p, size_t l, void **ret, size_t *ret_len) {
int unhexmem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_len) {
_cleanup_free_ uint8_t *buf = NULL;
size_t buf_size;
const char *x;
uint8_t *z;
int r;
assert(ret);
assert(ret_len);
@ -121,7 +123,8 @@ int unhexmem(const char *p, size_t l, void **ret, size_t *ret_len) {
l = strlen(p);
/* Note that the calculation of memory size is an upper boundary, as we ignore whitespace while decoding */
buf = malloc((l + 1) / 2 + 1);
buf_size = (l + 1) / 2 + 1;
buf = malloc(buf_size);
if (!buf)
return -ENOMEM;
@ -131,12 +134,16 @@ int unhexmem(const char *p, size_t l, void **ret, size_t *ret_len) {
a = unhex_next(&x, &l);
if (a == -EPIPE) /* End of string */
break;
if (a < 0)
return a;
if (a < 0) {
r = a;
goto on_failure;
}
b = unhex_next(&x, &l);
if (b < 0)
return b;
if (b < 0) {
r = b;
goto on_failure;
}
*(z++) = (uint8_t) a << 4 | (uint8_t) b;
}
@ -147,6 +154,12 @@ int unhexmem(const char *p, size_t l, void **ret, size_t *ret_len) {
*ret = TAKE_PTR(buf);
return 0;
on_failure:
if (secure)
explicit_bzero_safe(buf, buf_size);
return r;
}
/* https://tools.ietf.org/html/rfc4648#section-6

View File

@ -18,7 +18,10 @@ char hexchar(int x) _const_;
int unhexchar(char c) _const_;
char *hexmem(const void *p, size_t l);
int unhexmem(const char *p, size_t l, void **mem, size_t *len);
int unhexmem_full(const char *p, size_t l, bool secure, void **mem, size_t *len);
static inline int unhexmem(const char *p, size_t l, void **mem, size_t *len) {
return unhexmem_full(p, l, false, mem, len);
}
char base32hexchar(int x) _const_;
int unbase32hexchar(char c) _const_;

177
src/basic/linux/if_macsec.h Normal file
View File

@ -0,0 +1,177 @@
/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
/*
* include/uapi/linux/if_macsec.h - MACsec device
*
* Copyright (c) 2015 Sabrina Dubroca <sd@queasysnail.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef _UAPI_MACSEC_H
#define _UAPI_MACSEC_H
#include <linux/types.h>
#define MACSEC_GENL_NAME "macsec"
#define MACSEC_GENL_VERSION 1
#define MACSEC_MAX_KEY_LEN 128
#define MACSEC_KEYID_LEN 16
/* cipher IDs as per IEEE802.1AEbn-2011 */
#define MACSEC_CIPHER_ID_GCM_AES_128 0x0080C20001000001ULL
#define MACSEC_CIPHER_ID_GCM_AES_256 0x0080C20001000002ULL
/* deprecated cipher ID for GCM-AES-128 */
#define MACSEC_DEFAULT_CIPHER_ID 0x0080020001000001ULL
#define MACSEC_DEFAULT_CIPHER_ALT MACSEC_CIPHER_ID_GCM_AES_128
#define MACSEC_MIN_ICV_LEN 8
#define MACSEC_MAX_ICV_LEN 32
/* upper limit for ICV length as recommended by IEEE802.1AE-2006 */
#define MACSEC_STD_ICV_LEN 16
enum macsec_attrs {
MACSEC_ATTR_UNSPEC,
MACSEC_ATTR_IFINDEX, /* u32, ifindex of the MACsec netdevice */
MACSEC_ATTR_RXSC_CONFIG, /* config, nested macsec_rxsc_attrs */
MACSEC_ATTR_SA_CONFIG, /* config, nested macsec_sa_attrs */
MACSEC_ATTR_SECY, /* dump, nested macsec_secy_attrs */
MACSEC_ATTR_TXSA_LIST, /* dump, nested, macsec_sa_attrs for each TXSA */
MACSEC_ATTR_RXSC_LIST, /* dump, nested, macsec_rxsc_attrs for each RXSC */
MACSEC_ATTR_TXSC_STATS, /* dump, nested, macsec_txsc_stats_attr */
MACSEC_ATTR_SECY_STATS, /* dump, nested, macsec_secy_stats_attr */
__MACSEC_ATTR_END,
NUM_MACSEC_ATTR = __MACSEC_ATTR_END,
MACSEC_ATTR_MAX = __MACSEC_ATTR_END - 1,
};
enum macsec_secy_attrs {
MACSEC_SECY_ATTR_UNSPEC,
MACSEC_SECY_ATTR_SCI,
MACSEC_SECY_ATTR_ENCODING_SA,
MACSEC_SECY_ATTR_WINDOW,
MACSEC_SECY_ATTR_CIPHER_SUITE,
MACSEC_SECY_ATTR_ICV_LEN,
MACSEC_SECY_ATTR_PROTECT,
MACSEC_SECY_ATTR_REPLAY,
MACSEC_SECY_ATTR_OPER,
MACSEC_SECY_ATTR_VALIDATE,
MACSEC_SECY_ATTR_ENCRYPT,
MACSEC_SECY_ATTR_INC_SCI,
MACSEC_SECY_ATTR_ES,
MACSEC_SECY_ATTR_SCB,
MACSEC_SECY_ATTR_PAD,
__MACSEC_SECY_ATTR_END,
NUM_MACSEC_SECY_ATTR = __MACSEC_SECY_ATTR_END,
MACSEC_SECY_ATTR_MAX = __MACSEC_SECY_ATTR_END - 1,
};
enum macsec_rxsc_attrs {
MACSEC_RXSC_ATTR_UNSPEC,
MACSEC_RXSC_ATTR_SCI, /* config/dump, u64 */
MACSEC_RXSC_ATTR_ACTIVE, /* config/dump, u8 0..1 */
MACSEC_RXSC_ATTR_SA_LIST, /* dump, nested */
MACSEC_RXSC_ATTR_STATS, /* dump, nested, macsec_rxsc_stats_attr */
MACSEC_RXSC_ATTR_PAD,
__MACSEC_RXSC_ATTR_END,
NUM_MACSEC_RXSC_ATTR = __MACSEC_RXSC_ATTR_END,
MACSEC_RXSC_ATTR_MAX = __MACSEC_RXSC_ATTR_END - 1,
};
enum macsec_sa_attrs {
MACSEC_SA_ATTR_UNSPEC,
MACSEC_SA_ATTR_AN, /* config/dump, u8 0..3 */
MACSEC_SA_ATTR_ACTIVE, /* config/dump, u8 0..1 */
MACSEC_SA_ATTR_PN, /* config/dump, u32 */
MACSEC_SA_ATTR_KEY, /* config, data */
MACSEC_SA_ATTR_KEYID, /* config/dump, 128-bit */
MACSEC_SA_ATTR_STATS, /* dump, nested, macsec_sa_stats_attr */
MACSEC_SA_ATTR_PAD,
__MACSEC_SA_ATTR_END,
NUM_MACSEC_SA_ATTR = __MACSEC_SA_ATTR_END,
MACSEC_SA_ATTR_MAX = __MACSEC_SA_ATTR_END - 1,
};
enum macsec_nl_commands {
MACSEC_CMD_GET_TXSC,
MACSEC_CMD_ADD_RXSC,
MACSEC_CMD_DEL_RXSC,
MACSEC_CMD_UPD_RXSC,
MACSEC_CMD_ADD_TXSA,
MACSEC_CMD_DEL_TXSA,
MACSEC_CMD_UPD_TXSA,
MACSEC_CMD_ADD_RXSA,
MACSEC_CMD_DEL_RXSA,
MACSEC_CMD_UPD_RXSA,
};
/* u64 per-RXSC stats */
enum macsec_rxsc_stats_attr {
MACSEC_RXSC_STATS_ATTR_UNSPEC,
MACSEC_RXSC_STATS_ATTR_IN_OCTETS_VALIDATED,
MACSEC_RXSC_STATS_ATTR_IN_OCTETS_DECRYPTED,
MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNCHECKED,
MACSEC_RXSC_STATS_ATTR_IN_PKTS_DELAYED,
MACSEC_RXSC_STATS_ATTR_IN_PKTS_OK,
MACSEC_RXSC_STATS_ATTR_IN_PKTS_INVALID,
MACSEC_RXSC_STATS_ATTR_IN_PKTS_LATE,
MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_VALID,
MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_USING_SA,
MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNUSED_SA,
MACSEC_RXSC_STATS_ATTR_PAD,
__MACSEC_RXSC_STATS_ATTR_END,
NUM_MACSEC_RXSC_STATS_ATTR = __MACSEC_RXSC_STATS_ATTR_END,
MACSEC_RXSC_STATS_ATTR_MAX = __MACSEC_RXSC_STATS_ATTR_END - 1,
};
/* u32 per-{RX,TX}SA stats */
enum macsec_sa_stats_attr {
MACSEC_SA_STATS_ATTR_UNSPEC,
MACSEC_SA_STATS_ATTR_IN_PKTS_OK,
MACSEC_SA_STATS_ATTR_IN_PKTS_INVALID,
MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_VALID,
MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_USING_SA,
MACSEC_SA_STATS_ATTR_IN_PKTS_UNUSED_SA,
MACSEC_SA_STATS_ATTR_OUT_PKTS_PROTECTED,
MACSEC_SA_STATS_ATTR_OUT_PKTS_ENCRYPTED,
__MACSEC_SA_STATS_ATTR_END,
NUM_MACSEC_SA_STATS_ATTR = __MACSEC_SA_STATS_ATTR_END,
MACSEC_SA_STATS_ATTR_MAX = __MACSEC_SA_STATS_ATTR_END - 1,
};
/* u64 per-TXSC stats */
enum macsec_txsc_stats_attr {
MACSEC_TXSC_STATS_ATTR_UNSPEC,
MACSEC_TXSC_STATS_ATTR_OUT_PKTS_PROTECTED,
MACSEC_TXSC_STATS_ATTR_OUT_PKTS_ENCRYPTED,
MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_PROTECTED,
MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_ENCRYPTED,
MACSEC_TXSC_STATS_ATTR_PAD,
__MACSEC_TXSC_STATS_ATTR_END,
NUM_MACSEC_TXSC_STATS_ATTR = __MACSEC_TXSC_STATS_ATTR_END,
MACSEC_TXSC_STATS_ATTR_MAX = __MACSEC_TXSC_STATS_ATTR_END - 1,
};
/* u64 per-SecY stats */
enum macsec_secy_stats_attr {
MACSEC_SECY_STATS_ATTR_UNSPEC,
MACSEC_SECY_STATS_ATTR_OUT_PKTS_UNTAGGED,
MACSEC_SECY_STATS_ATTR_IN_PKTS_UNTAGGED,
MACSEC_SECY_STATS_ATTR_OUT_PKTS_TOO_LONG,
MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_TAG,
MACSEC_SECY_STATS_ATTR_IN_PKTS_BAD_TAG,
MACSEC_SECY_STATS_ATTR_IN_PKTS_UNKNOWN_SCI,
MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_SCI,
MACSEC_SECY_STATS_ATTR_IN_PKTS_OVERRUN,
MACSEC_SECY_STATS_ATTR_PAD,
__MACSEC_SECY_STATS_ATTR_END,
NUM_MACSEC_SECY_STATS_ATTR = __MACSEC_SECY_STATS_ATTR_END,
MACSEC_SECY_STATS_ATTR_MAX = __MACSEC_SECY_STATS_ATTR_END - 1,
};
#endif /* _UAPI_MACSEC_H */

View File

@ -95,6 +95,7 @@ basic_sources = files('''
linux/if_bonding.h
linux/if_bridge.h
linux/if_link.h
linux/if_macsec.h
linux/if_tun.h
linux/if_tunnel.h
linux/libc-compat.h

View File

@ -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) {

View File

@ -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;

View File

@ -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 = {

View File

@ -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;

View File

@ -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

1249
src/network/netdev/macsec.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,85 @@
/* 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"
/* See the definition of MACSEC_NUM_AN in kernel's drivers/net/macsec.c */
#define MACSEC_MAX_ASSOCIATION_NUMBER 4
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;
char *key_file;
int activate;
int use_for_encoding;
} 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;
ReceiveAssociation *rxsa[MACSEC_MAX_ASSOCIATION_NUMBER];
unsigned n_rxsa;
} ReceiveChannel;
struct MACsec {
NetDev meta;
uint16_t port;
int encrypt;
uint8_t encoding_an;
OrderedHashmap *receive_channels;
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);
CONFIG_PARSER_PROTOTYPE(config_parse_macsec_key_file);
CONFIG_PARSER_PROTOTYPE(config_parse_macsec_sa_activate);
CONFIG_PARSER_PROTOTYPE(config_parse_macsec_use_for_encoding);

View File

@ -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,23 @@ 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
MACsecTransmitAssociation.KeyFile, config_parse_macsec_key_file, 0, 0
MACsecTransmitAssociation.Activate, config_parse_macsec_sa_activate, 0, 0
MACsecTransmitAssociation.UseForEncoding, config_parse_macsec_use_for_encoding, 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
MACsecReceiveAssociation.KeyFile, config_parse_macsec_key_file, 0, 0
MACsecReceiveAssociation.Activate, config_parse_macsec_sa_activate, 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)

View File

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

View File

@ -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

View File

@ -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)
@ -66,7 +67,7 @@ Network.DNSOverTLS, config_parse_dns_over_tls_mode,
Network.DNSSEC, config_parse_dnssec_mode, 0, offsetof(Network, dnssec_mode)
Network.DNSSECNegativeTrustAnchors, config_parse_dnssec_negative_trust_anchors, 0, 0
Network.NTP, config_parse_ntp, 0, offsetof(Network, ntp)
Network.IPForward, config_parse_address_family_boolean_with_kernel,0, offsetof(Network, ip_forward)
Network.IPForward, config_parse_address_family_boolean_with_kernel, 0, offsetof(Network, ip_forward)
Network.IPMasquerade, config_parse_bool, 0, offsetof(Network, ip_masquerade)
Network.IPv6PrivacyExtensions, config_parse_ipv6_privacy_extensions, 0, offsetof(Network, ipv6_privacy_extensions)
Network.IPv6AcceptRA, config_parse_tristate, 0, offsetof(Network, ipv6_accept_ra)

View File

@ -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,

View File

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

View File

@ -174,3 +174,24 @@ SessionId=
PeerSessionId=
Layer2SpecificHeader=
Name=
[MACSEC]
Port=
Encrypt=
[MACsecReceiveAssociation]
Port=
MACAddress=
PacketNumber=
KeyId=
Key=
KeyFile=
Activate=
UseForEncoding=
[MACsecReceiveChannel]
Port=
MACAddress=
[MACsecTransmitAssociation]
PacketNumber=
KeyId=
Key=
KeyFile=
Activate=

View File

@ -111,6 +111,7 @@ IPv6Token=
Description=
VXLAN=
L2TP=
MACsec=
LinkLocalAddressing=
ConfigureWithoutCarrier=
NTP=

View File

@ -0,0 +1 @@
85858585858585858585858585858585

View File

@ -0,0 +1,68 @@
[NetDev]
Name=macsec99
Kind=macsec
[MACsec]
Port=11
Encrypt=yes
[MACsecTransmitAssociation]
PacketNumber=1024
KeyId=01
Key=81818181818181818181818181818181
Activate=yes
[MACsecTransmitAssociation]
PacketNumber=512
KeyId=0203
Key=82828282828282828282828282828282
UseForEncoding=yes
[MACsecReceiveChannel]
Port=2
MACAddress=8c:16:45:6c:83:a9
[MACsecReceiveAssociation]
Port=2
MACAddress=8c:16:45:6c:83:a9
PacketNumber=16
KeyId=020304
Key=83838383838383838383838383838383
[MACsecReceiveAssociation]
Port=256
MACAddress=c6:19:52:8f:e6:a0
PacketNumber=32
KeyId=02030405
Key=84848484848484848484848484848484
Activate=yes
[MACsecReceiveAssociation]
Port=256
MACAddress=c6:19:52:8f:e6:a0
PacketNumber=128
KeyId=0203040506
KeyFile=/run/systemd/network/25-macsec.key
Activate=yes
[MACsecReceiveAssociation]
Port=256
MACAddress=c6:19:52:8f:e6:a0
KeyId=020304050607
Key=86868686868686868686868686868686
Activate=no
[MACsecReceiveAssociation]
Port=256
MACAddress=c6:19:52:8f:e6:a0
KeyId=02030405060708
Key=87878787878787878787878787878787
Activate=no
[MACsecReceiveAssociation]
# This section should be dropped.
Port=256
MACAddress=c6:19:52:8f:e6:a0
KeyId=0203040506070809
Key=88888888888888888888888888888888
Activate=no

View File

@ -0,0 +1,6 @@
[Match]
Name=macsec99
[Network]
IPv6AcceptRA=no
Address=10.1.2.3/16

View File

@ -0,0 +1,9 @@
[Match]
Name=dummy98
[Link]
MACAddress=00:50:56:c0:00:19
[Network]
IPv6AcceptRA=no
MACsec=macsec99

View File

@ -290,6 +290,9 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
'25-ipip-tunnel.netdev',
'25-ipvlan.netdev',
'25-isatap-tunnel.netdev',
'25-macsec.key',
'25-macsec.netdev',
'25-macsec.network',
'25-sit-tunnel-local-any.netdev',
'25-sit-tunnel-remote-any.netdev',
'25-sit-tunnel.netdev',
@ -322,6 +325,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
'ipip.network',
'ipvlan.network',
'isatap.network',
'macsec.network',
'macvlan.network',
'macvtap.network',
'sit.network',
@ -875,6 +879,35 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
self.assertRegex(output, 'remcsumrx')
self.assertRegex(output, 'gbp')
def test_macsec(self):
self.copy_unit_to_networkd_unit_path('25-macsec.netdev', '25-macsec.network', '25-macsec.key',
'macsec.network', '12-dummy.netdev')
self.start_networkd(0)
self.wait_online(['dummy98:degraded', 'macsec99:routable'])
output = subprocess.check_output(['ip', '-d', 'link', 'show', 'macsec99']).rstrip().decode('utf-8')
print(output)
self.assertRegex(output, 'macsec99@dummy98')
self.assertRegex(output, 'macsec sci [0-9a-f]*000b')
self.assertRegex(output, 'encrypt on')
output = subprocess.check_output(['ip', 'macsec', 'show', 'macsec99']).rstrip().decode('utf-8')
print(output)
self.assertRegex(output, 'encrypt on')
self.assertRegex(output, 'TXSC: [0-9a-f]*000b on SA 1')
self.assertRegex(output, '0: PN [0-9]*, state on, key 01000000000000000000000000000000')
self.assertRegex(output, '1: PN [0-9]*, state on, key 02030000000000000000000000000000')
self.assertRegex(output, 'RXSC: c619528fe6a00100, state on')
self.assertRegex(output, '0: PN [0-9]*, state on, key 02030405000000000000000000000000')
self.assertRegex(output, '1: PN [0-9]*, state on, key 02030405060000000000000000000000')
self.assertRegex(output, '2: PN [0-9]*, state off, key 02030405060700000000000000000000')
self.assertRegex(output, '3: PN [0-9]*, state off, key 02030405060708000000000000000000')
self.assertNotRegex(output, 'key 02030405067080900000000000000000')
self.assertRegex(output, 'RXSC: 8c16456c83a90002, state on')
self.assertRegex(output, '0: PN [0-9]*, state off, key 02030400000000000000000000000000')
class NetworkdL2TPTests(unittest.TestCase, Utilities):
links =[