diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml index 1836b5fe00..a58de37b3c 100644 --- a/man/systemd.netdev.xml +++ b/man/systemd.netdev.xml @@ -151,6 +151,9 @@ l2tp 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 + 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. + vrf A Virtual Routing and Forwarding (VRF) interface to create separate routing and forwarding domains. @@ -851,6 +854,161 @@ + + [MACsec] Section Options + The [MACsec] section only applies for network devices of kind + macsec, and accepts the following keys: + + + + Port= + + 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. + + + + + Encrypt= + + Takes a boolean. When true, enable encryption. Defaults to unset. + + + + + + [MACsecReceiveChannel] Section Options + The [MACsecReceiveChannel] section only applies for network devices of + kind macsec, and accepts the following keys: + + + + Port= + + 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. + + + + MACAddress= + + 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. + + + + + + [MACsecTransmitAssociation] Section Options + The [MACsecTransmitAssociation] section only applies for network devices + of kind macsec, and accepts the following keys: + + + + PacketNumber= + + 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. + + + + + KeyId= + + Specifies the identification for the key. Takes a number between 0-255. This option + is compulsory, and is not set by default. + + + + Key= + + 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 + dffafc8d7b9a43d5b9a3dfbbf6a30c16. + + + + KeyFile= + + 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, + Key= is ignored. Note that the file must be readable by the user + systemd-network, so it should be, e.g., owned by + root:systemd-network with a 0640 file mode. + + + + Activate= + + Takes a boolean. If enabled, then the security association is activated. Defaults to + unset. + + + + UseForEncoding= + + Takes a boolean. If enabled, then the security association is used for encoding. Only + one [MACsecTransmitAssociation] section can enable this option. When enabled, + Activate=yes is implied. Defaults to unset. + + + + + + [MACsecReceiveAssociation] Section Options + The [MACsecReceiveAssociation] section only applies for + network devices of kind macsec, and accepts the + following keys: + + + + Port= + + Accepts the same key in [MACsecReceiveChannel] section. + + + + MACAddress= + + Accepts the same key in [MACsecReceiveChannel] section. + + + + PacketNumber= + + Accepts the same key in [MACsecTransmitAssociation] section. + + + + KeyId= + + Accepts the same key in [MACsecTransmitAssociation] section. + + + + Key= + + Accepts the same key in [MACsecTransmitAssociation] section. + + + + KeyFile= + + Accepts the same key in [MACsecTransmitAssociation] section. + + + + Activate= + + Accepts the same key in [MACsecTransmitAssociation] section. + + + + [Tunnel] Section Options 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. + + MACsec= + + The name of a MACsec device to create on the link. See + systemd.netdev5. + This option may be specified more than once. + + ActiveSlave= diff --git a/src/basic/fileio.c b/src/basic/fileio.c index 85a49b1f9e..2cec054610 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -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; - r = unbase64mem_full(buf, l, flags & READ_FULL_FILE_SECURE, (void **) ret_contents, ret_size); + 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; } diff --git a/src/basic/fileio.h b/src/basic/fileio.h index ffe900b486..eb551c7ac1 100644 --- a/src/basic/fileio.h +++ b/src/basic/fileio.h @@ -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); diff --git a/src/basic/hexdecoct.c b/src/basic/hexdecoct.c index 132439fd1c..5e425b0231 100644 --- a/src/basic/hexdecoct.c +++ b/src/basic/hexdecoct.c @@ -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 diff --git a/src/basic/hexdecoct.h b/src/basic/hexdecoct.h index fa6013ee75..dfdff1e9bb 100644 --- a/src/basic/hexdecoct.h +++ b/src/basic/hexdecoct.h @@ -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_; diff --git a/src/basic/linux/if_macsec.h b/src/basic/linux/if_macsec.h new file mode 100644 index 0000000000..98e4d5d7c4 --- /dev/null +++ b/src/basic/linux/if_macsec.h @@ -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 + * + * 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 + +#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 */ diff --git a/src/basic/meson.build b/src/basic/meson.build index de1e42013d..524f3785dc 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -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 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 #include #include +#include #include #include #include @@ -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..15b5378f30 --- /dev/null +++ b/src/network/netdev/macsec.c @@ -0,0 +1,1249 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include +#include +#include +#include + +#include "conf-parser.h" +#include "fileio.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 "path-util.h" +#include "sd-netlink.h" +#include "socket-util.h" +#include "string-table.h" +#include "string-util.h" +#include "util.h" + +static void security_association_clear(SecurityAssociation *sa) { + if (!sa) + return; + + explicit_bzero_safe(sa->key, sa->key_len); + free(sa->key); + free(sa->key_file); +} + +static void security_association_init(SecurityAssociation *sa) { + assert(sa); + + sa->activate = -1; + sa->use_for_encoding = -1; +} + +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); + security_association_clear(&c->sa); + + 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), + }; + + security_association_init(&c->sa); + + 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) { + if (c->sci.as_uint64 > 0) + ordered_hashmap_remove(c->macsec->receive_channels, &c->sci.as_uint64); + + if (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(MACsec *s, uint64_t sci, ReceiveChannel **ret) { + ReceiveChannel *c; + + assert(s); + + c = new(ReceiveChannel, 1); + if (!c) + return -ENOMEM; + + *c = (ReceiveChannel) { + .macsec = s, + .sci.as_uint64 = sci, + }; + + *ret = c; + return 0; +} + +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; + } + + r = macsec_receive_channel_new(s, 0, &c); + if (r < 0) + return r; + + c->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); + security_association_clear(&a->sa); + + 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), + }; + + security_association_init(&a->sa); + + 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"); + } + + if (a->activate >= 0) { + r = sd_netlink_message_append_u8(m, MACSEC_SA_ATTR_ACTIVE, a->activate); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append MACSEC_SA_ATTR_ACTIVE 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, ReceiveChannel *c) { + NetDev *netdev; + unsigned i; + int r; + + assert(c); + assert(c->macsec); + + netdev = NETDEV(c->macsec); + + 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"); + + for (i = 0; i < c->n_rxsa; i++) { + r = netdev_macsec_configure_receive_association(netdev, c->rxsa[i]); + if (r < 0) { + log_netdev_warning_errno(netdev, r, + "Failed to configure receive security association: %m"); + netdev_drop(netdev); + return 1; + } + } + + return 1; +} + +static void receive_channel_destroy_callback(ReceiveChannel *c) { + assert(c); + assert(c->macsec); + + netdev_unref(NETDEV(c->macsec)); +} + +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, + receive_channel_destroy_callback, c); + 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) { + 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, i) { + r = netdev_macsec_configure_receive_channel(netdev, c); + 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"); + } + + r = sd_netlink_message_append_u8(m, IFLA_MACSEC_ENCODING_SA, v->encoding_an); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_MACSEC_ENCODING_SA 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); + + (void) warn_file_is_world_accessible(filename, NULL, unit, line); + + 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_full(rvalue, strlen(rvalue), true, &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 */ + explicit_bzero_safe(p, l); + 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_file( + 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_ char *path = NULL; + MACsec *s = userdata; + char **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.key_file : &b->sa.key_file; + + if (isempty(rvalue)) { + *dest = mfree(*dest); + return 0; + } + + path = strdup(rvalue); + if (!path) + return log_oom(); + + if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0) + return 0; + + free_and_replace(*dest, path); + 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; +} + +int config_parse_macsec_sa_activate( + 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; + int *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.activate : &b->sa.activate; + + if (isempty(rvalue)) + r = -1; + else { + r = parse_boolean(rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse activation mode of %s security association. " + "Ignoring assignment: %s", + streq(section, "MACsecTransmitAssociation") ? "transmit" : "receive", + rvalue); + return 0; + } + } + + *dest = r; + TAKE_PTR(a); + TAKE_PTR(b); + + return 0; +} + +int config_parse_macsec_use_for_encoding( + 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; + MACsec *s = userdata; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = macsec_transmit_association_new_static(s, filename, section_line, &a); + if (r < 0) + return r; + + if (isempty(rvalue)) + r = -1; + else { + r = parse_boolean(rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse %s= setting. Ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + } + + a->sa.use_for_encoding = r; + if (a->sa.use_for_encoding > 0) + a->sa.activate = true; + + TAKE_PTR(a); + + return 0; +} + +static int macsec_read_key_file(NetDev *netdev, SecurityAssociation *sa) { + _cleanup_free_ uint8_t *key = NULL; + size_t key_len; + int r; + + assert(netdev); + assert(sa); + + if (!sa->key_file) + return 0; + + r = read_full_file_full(sa->key_file, READ_FULL_FILE_SECURE | READ_FULL_FILE_UNHEX, (char **) &key, &key_len); + if (r < 0) + return log_netdev_error_errno(netdev, r, + "Failed to read key from '%s', ignoring: %m", + sa->key_file); + if (key_len != 16) { + explicit_bzero_safe(key, key_len); + return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "Invalid key length (%zu bytes), ignoring: %m", + key_len); + } + + explicit_bzero_safe(sa->key, sa->key_len); + free_and_replace(sa->key, key); + sa->key_len = key_len; + + return 0; +} + +static int macsec_receive_channel_verify(ReceiveChannel *c) { + NetDev *netdev; + int r; + + 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); + + r = ordered_hashmap_ensure_allocated(&c->macsec->receive_channels, &uint64_hash_ops); + if (r < 0) + return log_oom(); + + r = ordered_hashmap_put(c->macsec->receive_channels, &c->sci.as_uint64, c); + if (r == -EEXIST) + return log_netdev_error_errno(netdev, r, + "%s: Multiple [MACsecReceiveChannel] sections have same SCI, " + "Ignoring [MACsecReceiveChannel] section from line %u", + c->section->filename, c->section->line); + if (r < 0) + return log_netdev_error_errno(netdev, r, + "%s: Failed to store [MACsecReceiveChannel] section at hashmap, " + "Ignoring [MACsecReceiveChannel] section from line %u", + c->section->filename, c->section->line); + return 0; +} + +static int macsec_transmit_association_verify(TransmitAssociation *t) { + NetDev *netdev; + int r; + + 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); + + r = macsec_read_key_file(netdev, &t->sa); + if (r < 0) + return r; + + 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) { + ReceiveChannel *c; + NetDev *netdev; + int r; + + assert(a); + assert(a->macsec); + + netdev = NETDEV(a->macsec); + + if (section_is_invalid(a->section)) + return -EINVAL; + + r = macsec_read_key_file(netdev, &a->sa); + if (r < 0) + return r; + + 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); + + c = ordered_hashmap_get(a->macsec->receive_channels, &a->sci.as_uint64); + if (!c) { + _cleanup_(macsec_receive_channel_freep) ReceiveChannel *new_channel = NULL; + + r = macsec_receive_channel_new(a->macsec, a->sci.as_uint64, &new_channel); + if (r < 0) + return log_oom(); + + r = ordered_hashmap_ensure_allocated(&a->macsec->receive_channels, &uint64_hash_ops); + if (r < 0) + return log_oom(); + + r = ordered_hashmap_put(a->macsec->receive_channels, &new_channel->sci.as_uint64, new_channel); + if (r < 0) + return log_netdev_error_errno(netdev, r, + "%s: Failed to store receive channel at hashmap, " + "Ignoring [MACsecReceiveAssociation] section from line %u", + a->section->filename, a->section->line); + c = TAKE_PTR(new_channel); + } + if (c->n_rxsa >= MACSEC_MAX_ASSOCIATION_NUMBER) + return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(ERANGE), + "%s: Too many [MACsecReceiveAssociation] sections for the same receive channel, " + "Ignoring [MACsecReceiveAssociation] section from line %u", + a->section->filename, a->section->line); + + a->sa.association_number = c->n_rxsa; + c->rxsa[c->n_rxsa++] = a; + + return 0; +} + +static int netdev_macsec_verify(NetDev *netdev, const char *filename) { + MACsec *v = MACSEC(netdev); + TransmitAssociation *a; + ReceiveAssociation *n; + ReceiveChannel *c; + Iterator i; + uint8_t an, encoding_an; + bool use_for_encoding; + 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); + } + + an = 0; + use_for_encoding = false; + encoding_an = 0; + ORDERED_HASHMAP_FOREACH(a, v->transmit_associations_by_section, i) { + r = macsec_transmit_association_verify(a); + if (r < 0) { + macsec_transmit_association_free(a); + continue; + } + + if (an >= MACSEC_MAX_ASSOCIATION_NUMBER) { + log_netdev_error(netdev, + "%s: Too many [MACsecTransmitAssociation] sections configured. " + "Ignoring [MACsecTransmitAssociation] section from line %u", + a->section->filename, a->section->line); + macsec_transmit_association_free(a); + continue; + } + + a->sa.association_number = an++; + + if (a->sa.use_for_encoding > 0) { + if (use_for_encoding) { + log_netdev_warning(netdev, + "%s: Multiple security associations are set to be used for transmit channel." + "Disabling UseForEncoding= in [MACsecTransmitAssociation] section from line %u", + a->section->filename, a->section->line); + a->sa.use_for_encoding = false; + } else { + encoding_an = a->sa.association_number; + use_for_encoding = true; + } + } + } + + assert(encoding_an < MACSEC_MAX_ASSOCIATION_NUMBER); + v->encoding_an = encoding_an; + + 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, macsec_receive_channel_free); + 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..2bd08ac500 --- /dev/null +++ b/src/network/netdev/macsec.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include + +#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); diff --git a/src/network/netdev/netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf index 1a3d6caeb9..107827567e 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" @@ -34,158 +35,175 @@ struct ConfigPerfItem; %struct-type %includes %% -Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(NetDev, conditions) -Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(NetDev, conditions) -Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(NetDev, conditions) -Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(NetDev, conditions) -Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(NetDev, conditions) -NetDev.Description, config_parse_string, 0, offsetof(NetDev, description) -NetDev.Name, config_parse_ifname, 0, offsetof(NetDev, ifname) -NetDev.Kind, config_parse_netdev_kind, 0, offsetof(NetDev, kind) -NetDev.MTUBytes, config_parse_mtu, AF_UNSPEC, offsetof(NetDev, mtu) -NetDev.MACAddress, config_parse_hwaddr, 0, offsetof(NetDev, mac) -VLAN.Id, config_parse_vlanid, 0, offsetof(VLan, id) -VLAN.GVRP, config_parse_tristate, 0, offsetof(VLan, gvrp) -VLAN.MVRP, config_parse_tristate, 0, offsetof(VLan, mvrp) -VLAN.LooseBinding, config_parse_tristate, 0, offsetof(VLan, loose_binding) -VLAN.ReorderHeader, config_parse_tristate, 0, offsetof(VLan, reorder_hdr) -MACVLAN.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode) -MACVTAP.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode) -IPVLAN.Mode, config_parse_ipvlan_mode, 0, offsetof(IPVlan, mode) -IPVLAN.Flags, config_parse_ipvlan_flags, 0, offsetof(IPVlan, flags) -Tunnel.Local, config_parse_tunnel_address, 0, offsetof(Tunnel, local) -Tunnel.Remote, config_parse_tunnel_address, 0, offsetof(Tunnel, remote) -Tunnel.TOS, config_parse_unsigned, 0, offsetof(Tunnel, tos) -Tunnel.TTL, config_parse_unsigned, 0, offsetof(Tunnel, ttl) -Tunnel.Key, config_parse_tunnel_key, 0, offsetof(Tunnel, key) -Tunnel.InputKey, config_parse_tunnel_key, 0, offsetof(Tunnel, ikey) -Tunnel.OutputKey, config_parse_tunnel_key, 0, offsetof(Tunnel, okey) -Tunnel.DiscoverPathMTU, config_parse_bool, 0, offsetof(Tunnel, pmtudisc) -Tunnel.Mode, config_parse_ip6tnl_mode, 0, offsetof(Tunnel, ip6tnl_mode) -Tunnel.IPv6FlowLabel, config_parse_ipv6_flowlabel, 0, offsetof(Tunnel, ipv6_flowlabel) -Tunnel.CopyDSCP, config_parse_bool, 0, offsetof(Tunnel, copy_dscp) -Tunnel.EncapsulationLimit, config_parse_encap_limit, 0, offsetof(Tunnel, encap_limit) -Tunnel.Independent, config_parse_bool, 0, offsetof(Tunnel, independent) -Tunnel.AllowLocalRemote, config_parse_tristate, 0, offsetof(Tunnel, allow_localremote) -Tunnel.FooOverUDP, config_parse_bool, 0, offsetof(Tunnel, fou_tunnel) -Tunnel.FOUDestinationPort, config_parse_ip_port, 0, offsetof(Tunnel, fou_destination_port) -Tunnel.FOUSourcePort, config_parse_ip_port, 0, offsetof(Tunnel, encap_src_port) -Tunnel.Encapsulation, config_parse_fou_encap_type, 0, offsetof(Tunnel, fou_encap_type) -Tunnel.IPv6RapidDeploymentPrefix, config_parse_6rd_prefix, 0, 0 -Tunnel.ERSPANIndex, config_parse_uint32, 0, offsetof(Tunnel, erspan_index) -Tunnel.SerializeTunneledPackets, config_parse_tristate, 0, offsetof(Tunnel, gre_erspan_sequence) -Tunnel.ISATAP, config_parse_tristate, 0, offsetof(Tunnel, isatap) -FooOverUDP.Protocol, config_parse_ip_protocol, 0, offsetof(FouTunnel, fou_protocol) -FooOverUDP.Encapsulation, config_parse_fou_encap_type, 0, offsetof(FouTunnel, fou_encap_type) -FooOverUDP.Port, config_parse_ip_port, 0, offsetof(FouTunnel, port) -L2TP.TunnelId, config_parse_l2tp_tunnel_id, 0, offsetof(L2tpTunnel, tunnel_id) -L2TP.PeerTunnelId, config_parse_l2tp_tunnel_id, 0, offsetof(L2tpTunnel, peer_tunnel_id) -L2TP.UDPSourcePort, config_parse_ip_port, 0, offsetof(L2tpTunnel, l2tp_udp_sport) -L2TP.UDPDestinationPort, config_parse_ip_port, 0, offsetof(L2tpTunnel, l2tp_udp_dport) -L2TP.Local, config_parse_l2tp_tunnel_address, 0, offsetof(L2tpTunnel, local) -L2TP.Remote, config_parse_l2tp_tunnel_address, 0, offsetof(L2tpTunnel, remote) -L2TP.EncapsulationType, config_parse_l2tp_encap_type, 0, offsetof(L2tpTunnel, l2tp_encap_type) -L2TP.UDPCheckSum, config_parse_bool, 0, offsetof(L2tpTunnel, udp_csum) -L2TP.UDP6CheckSumRx, config_parse_bool, 0, offsetof(L2tpTunnel, udp6_csum_rx) -L2TP.UDP6CheckSumTx, config_parse_bool, 0, offsetof(L2tpTunnel, udp6_csum_tx) -L2TPSession.SessionId, config_parse_l2tp_session_id, 0, 0 -L2TPSession.PeerSessionId, config_parse_l2tp_session_id, 0, 0 -L2TPSession.Layer2SpecificHeader, config_parse_l2tp_session_l2spec, 0, 0 -L2TPSession.Name, config_parse_l2tp_session_name, 0, 0 -Peer.Name, config_parse_ifname, 0, offsetof(Veth, ifname_peer) -Peer.MACAddress, config_parse_hwaddr, 0, offsetof(Veth, mac_peer) -VXCAN.Peer, config_parse_ifname, 0, offsetof(VxCan, ifname_peer) -VXLAN.Id, config_parse_uint64, 0, offsetof(VxLan, id) -VXLAN.Group, config_parse_vxlan_address, 0, offsetof(VxLan, remote) -VXLAN.Local, config_parse_vxlan_address, 0, offsetof(VxLan, local) -VXLAN.Remote, config_parse_vxlan_address, 0, offsetof(VxLan, remote) -VXLAN.TOS, config_parse_unsigned, 0, offsetof(VxLan, tos) -VXLAN.TTL, config_parse_unsigned, 0, offsetof(VxLan, ttl) -VXLAN.MacLearning, config_parse_bool, 0, offsetof(VxLan, learning) -VXLAN.ARPProxy, config_parse_bool, 0, offsetof(VxLan, arp_proxy) -VXLAN.ReduceARPProxy, config_parse_bool, 0, offsetof(VxLan, arp_proxy) -VXLAN.L2MissNotification, config_parse_bool, 0, offsetof(VxLan, l2miss) -VXLAN.L3MissNotification, config_parse_bool, 0, offsetof(VxLan, l3miss) -VXLAN.RouteShortCircuit, config_parse_bool, 0, offsetof(VxLan, route_short_circuit) -VXLAN.UDPCheckSum, config_parse_bool, 0, offsetof(VxLan, udpcsum) -VXLAN.UDPChecksum, config_parse_bool, 0, offsetof(VxLan, udpcsum) -VXLAN.UDP6ZeroCheckSumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx) -VXLAN.UDP6ZeroChecksumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx) -VXLAN.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx) -VXLAN.UDP6ZeroChecksumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx) -VXLAN.RemoteChecksumTx, config_parse_bool, 0, offsetof(VxLan, remote_csum_tx) -VXLAN.RemoteChecksumRx, config_parse_bool, 0, offsetof(VxLan, remote_csum_rx) -VXLAN.FDBAgeingSec, config_parse_sec, 0, offsetof(VxLan, fdb_ageing) -VXLAN.GroupPolicyExtension, config_parse_bool, 0, offsetof(VxLan, group_policy) -VXLAN.MaximumFDBEntries, config_parse_unsigned, 0, offsetof(VxLan, max_fdb) -VXLAN.PortRange, config_parse_port_range, 0, 0 -VXLAN.DestinationPort, config_parse_ip_port, 0, offsetof(VxLan, dest_port) -VXLAN.FlowLabel, config_parse_flow_label, 0, 0 -GENEVE.Id, config_parse_geneve_vni, 0, offsetof(Geneve, id) -GENEVE.Remote, config_parse_geneve_address, 0, offsetof(Geneve, remote) -GENEVE.TOS, config_parse_uint8, 0, offsetof(Geneve, tos) -GENEVE.TTL, config_parse_uint8, 0, offsetof(Geneve, ttl) -GENEVE.UDPChecksum, config_parse_bool, 0, offsetof(Geneve, udpcsum) -GENEVE.UDP6ZeroCheckSumRx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumrx) -GENEVE.UDP6ZeroChecksumRx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumrx) -GENEVE.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumtx) -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 -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) -Tun.User, config_parse_string, 0, offsetof(TunTap, user_name) -Tun.Group, config_parse_string, 0, offsetof(TunTap, group_name) -Tap.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue) -Tap.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue) -Tap.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info) -Tap.VNetHeader, config_parse_bool, 0, offsetof(TunTap, vnet_hdr) -Tap.User, config_parse_string, 0, offsetof(TunTap, user_name) -Tap.Group, config_parse_string, 0, offsetof(TunTap, group_name) -Bond.Mode, config_parse_bond_mode, 0, offsetof(Bond, mode) -Bond.TransmitHashPolicy, config_parse_bond_xmit_hash_policy, 0, offsetof(Bond, xmit_hash_policy) -Bond.LACPTransmitRate, config_parse_bond_lacp_rate, 0, offsetof(Bond, lacp_rate) -Bond.AdSelect, config_parse_bond_ad_select, 0, offsetof(Bond, ad_select) -Bond.FailOverMACPolicy, config_parse_bond_fail_over_mac, 0, offsetof(Bond, fail_over_mac) -Bond.ARPIPTargets, config_parse_arp_ip_target_address, 0, 0 -Bond.ARPValidate, config_parse_bond_arp_validate, 0, offsetof(Bond, arp_validate) -Bond.ARPAllTargets, config_parse_bond_arp_all_targets, 0, offsetof(Bond, arp_all_targets) -Bond.PrimaryReselectPolicy, config_parse_bond_primary_reselect, 0, offsetof(Bond, primary_reselect) -Bond.ResendIGMP, config_parse_unsigned, 0, offsetof(Bond, resend_igmp) -Bond.PacketsPerSlave, config_parse_unsigned, 0, offsetof(Bond, packets_per_slave) -Bond.GratuitousARP, config_parse_unsigned, 0, offsetof(Bond, num_grat_arp) -Bond.AllSlavesActive, config_parse_bool, 0, offsetof(Bond, all_slaves_active) -Bond.DynamicTransmitLoadBalancing, config_parse_tristate, 0, offsetof(Bond, tlb_dynamic_lb) -Bond.MinLinks, config_parse_unsigned, 0, offsetof(Bond, min_links) -Bond.MIIMonitorSec, config_parse_sec, 0, offsetof(Bond, miimon) -Bond.UpDelaySec, config_parse_sec, 0, offsetof(Bond, updelay) -Bond.DownDelaySec, config_parse_sec, 0, offsetof(Bond, downdelay) -Bond.ARPIntervalSec, config_parse_sec, 0, offsetof(Bond, arp_interval) -Bond.LearnPacketIntervalSec, config_parse_sec, 0, offsetof(Bond, lp_interval) -Bond.AdActorSystemPriority, config_parse_ad_actor_sys_prio, 0, offsetof(Bond, ad_actor_sys_prio) -Bond.AdUserPortKey, config_parse_ad_user_port_key, 0, offsetof(Bond, ad_user_port_key) -Bond.AdActorSystem, config_parse_ad_actor_system, 0, offsetof(Bond, ad_actor_system) -Bridge.HelloTimeSec, config_parse_sec, 0, offsetof(Bridge, hello_time) -Bridge.MaxAgeSec, config_parse_sec, 0, offsetof(Bridge, max_age) -Bridge.AgeingTimeSec, config_parse_sec, 0, offsetof(Bridge, ageing_time) -Bridge.ForwardDelaySec, config_parse_sec, 0, offsetof(Bridge, forward_delay) -Bridge.Priority, config_parse_uint16, 0, offsetof(Bridge, priority) -Bridge.GroupForwardMask, config_parse_uint16, 0, offsetof(Bridge, group_fwd_mask) -Bridge.DefaultPVID, config_parse_default_port_vlanid, 0, offsetof(Bridge, default_pvid) -Bridge.MulticastQuerier, config_parse_tristate, 0, offsetof(Bridge, mcast_querier) -Bridge.MulticastSnooping, config_parse_tristate, 0, offsetof(Bridge, mcast_snooping) -Bridge.VLANFiltering, config_parse_tristate, 0, offsetof(Bridge, vlan_filtering) -Bridge.STP, config_parse_tristate, 0, offsetof(Bridge, stp) -VRF.TableId, config_parse_uint32, 0, offsetof(Vrf, table) /* deprecated */ -VRF.Table, config_parse_uint32, 0, offsetof(Vrf, table) -WireGuard.FwMark, config_parse_unsigned, 0, offsetof(Wireguard, fwmark) -WireGuard.ListenPort, config_parse_wireguard_listen_port, 0, offsetof(Wireguard, port) -WireGuard.PrivateKey, config_parse_wireguard_private_key, 0, 0 -WireGuard.PrivateKeyFile, config_parse_wireguard_private_key_file, 0, 0 -WireGuardPeer.AllowedIPs, config_parse_wireguard_allowed_ips, 0, 0 -WireGuardPeer.Endpoint, config_parse_wireguard_endpoint, 0, 0 -WireGuardPeer.PublicKey, config_parse_wireguard_public_key, 0, 0 -WireGuardPeer.PresharedKey, config_parse_wireguard_preshared_key, 0, 0 -WireGuardPeer.PresharedKeyFile, config_parse_wireguard_preshared_key_file, 0, 0 -WireGuardPeer.PersistentKeepalive, config_parse_wireguard_keepalive, 0, 0 +Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(NetDev, conditions) +Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(NetDev, conditions) +Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(NetDev, conditions) +Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(NetDev, conditions) +Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(NetDev, conditions) +NetDev.Description, config_parse_string, 0, offsetof(NetDev, description) +NetDev.Name, config_parse_ifname, 0, offsetof(NetDev, ifname) +NetDev.Kind, config_parse_netdev_kind, 0, offsetof(NetDev, kind) +NetDev.MTUBytes, config_parse_mtu, AF_UNSPEC, offsetof(NetDev, mtu) +NetDev.MACAddress, config_parse_hwaddr, 0, offsetof(NetDev, mac) +VLAN.Id, config_parse_vlanid, 0, offsetof(VLan, id) +VLAN.GVRP, config_parse_tristate, 0, offsetof(VLan, gvrp) +VLAN.MVRP, config_parse_tristate, 0, offsetof(VLan, mvrp) +VLAN.LooseBinding, config_parse_tristate, 0, offsetof(VLan, loose_binding) +VLAN.ReorderHeader, config_parse_tristate, 0, offsetof(VLan, reorder_hdr) +MACVLAN.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode) +MACVTAP.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode) +IPVLAN.Mode, config_parse_ipvlan_mode, 0, offsetof(IPVlan, mode) +IPVLAN.Flags, config_parse_ipvlan_flags, 0, offsetof(IPVlan, flags) +Tunnel.Local, config_parse_tunnel_address, 0, offsetof(Tunnel, local) +Tunnel.Remote, config_parse_tunnel_address, 0, offsetof(Tunnel, remote) +Tunnel.TOS, config_parse_unsigned, 0, offsetof(Tunnel, tos) +Tunnel.TTL, config_parse_unsigned, 0, offsetof(Tunnel, ttl) +Tunnel.Key, config_parse_tunnel_key, 0, offsetof(Tunnel, key) +Tunnel.InputKey, config_parse_tunnel_key, 0, offsetof(Tunnel, ikey) +Tunnel.OutputKey, config_parse_tunnel_key, 0, offsetof(Tunnel, okey) +Tunnel.DiscoverPathMTU, config_parse_bool, 0, offsetof(Tunnel, pmtudisc) +Tunnel.Mode, config_parse_ip6tnl_mode, 0, offsetof(Tunnel, ip6tnl_mode) +Tunnel.IPv6FlowLabel, config_parse_ipv6_flowlabel, 0, offsetof(Tunnel, ipv6_flowlabel) +Tunnel.CopyDSCP, config_parse_bool, 0, offsetof(Tunnel, copy_dscp) +Tunnel.EncapsulationLimit, config_parse_encap_limit, 0, offsetof(Tunnel, encap_limit) +Tunnel.Independent, config_parse_bool, 0, offsetof(Tunnel, independent) +Tunnel.AllowLocalRemote, config_parse_tristate, 0, offsetof(Tunnel, allow_localremote) +Tunnel.FooOverUDP, config_parse_bool, 0, offsetof(Tunnel, fou_tunnel) +Tunnel.FOUDestinationPort, config_parse_ip_port, 0, offsetof(Tunnel, fou_destination_port) +Tunnel.FOUSourcePort, config_parse_ip_port, 0, offsetof(Tunnel, encap_src_port) +Tunnel.Encapsulation, config_parse_fou_encap_type, 0, offsetof(Tunnel, fou_encap_type) +Tunnel.IPv6RapidDeploymentPrefix, config_parse_6rd_prefix, 0, 0 +Tunnel.ERSPANIndex, config_parse_uint32, 0, offsetof(Tunnel, erspan_index) +Tunnel.SerializeTunneledPackets, config_parse_tristate, 0, offsetof(Tunnel, gre_erspan_sequence) +Tunnel.ISATAP, config_parse_tristate, 0, offsetof(Tunnel, isatap) +FooOverUDP.Protocol, config_parse_ip_protocol, 0, offsetof(FouTunnel, fou_protocol) +FooOverUDP.Encapsulation, config_parse_fou_encap_type, 0, offsetof(FouTunnel, fou_encap_type) +FooOverUDP.Port, config_parse_ip_port, 0, offsetof(FouTunnel, port) +L2TP.TunnelId, config_parse_l2tp_tunnel_id, 0, offsetof(L2tpTunnel, tunnel_id) +L2TP.PeerTunnelId, config_parse_l2tp_tunnel_id, 0, offsetof(L2tpTunnel, peer_tunnel_id) +L2TP.UDPSourcePort, config_parse_ip_port, 0, offsetof(L2tpTunnel, l2tp_udp_sport) +L2TP.UDPDestinationPort, config_parse_ip_port, 0, offsetof(L2tpTunnel, l2tp_udp_dport) +L2TP.Local, config_parse_l2tp_tunnel_address, 0, offsetof(L2tpTunnel, local) +L2TP.Remote, config_parse_l2tp_tunnel_address, 0, offsetof(L2tpTunnel, remote) +L2TP.EncapsulationType, config_parse_l2tp_encap_type, 0, offsetof(L2tpTunnel, l2tp_encap_type) +L2TP.UDPCheckSum, config_parse_bool, 0, offsetof(L2tpTunnel, udp_csum) +L2TP.UDP6CheckSumRx, config_parse_bool, 0, offsetof(L2tpTunnel, udp6_csum_rx) +L2TP.UDP6CheckSumTx, config_parse_bool, 0, offsetof(L2tpTunnel, udp6_csum_tx) +L2TPSession.SessionId, config_parse_l2tp_session_id, 0, 0 +L2TPSession.PeerSessionId, config_parse_l2tp_session_id, 0, 0 +L2TPSession.Layer2SpecificHeader, config_parse_l2tp_session_l2spec, 0, 0 +L2TPSession.Name, config_parse_l2tp_session_name, 0, 0 +Peer.Name, config_parse_ifname, 0, offsetof(Veth, ifname_peer) +Peer.MACAddress, config_parse_hwaddr, 0, offsetof(Veth, mac_peer) +VXCAN.Peer, config_parse_ifname, 0, offsetof(VxCan, ifname_peer) +VXLAN.Id, config_parse_uint64, 0, offsetof(VxLan, id) +VXLAN.Group, config_parse_vxlan_address, 0, offsetof(VxLan, remote) +VXLAN.Local, config_parse_vxlan_address, 0, offsetof(VxLan, local) +VXLAN.Remote, config_parse_vxlan_address, 0, offsetof(VxLan, remote) +VXLAN.TOS, config_parse_unsigned, 0, offsetof(VxLan, tos) +VXLAN.TTL, config_parse_unsigned, 0, offsetof(VxLan, ttl) +VXLAN.MacLearning, config_parse_bool, 0, offsetof(VxLan, learning) +VXLAN.ARPProxy, config_parse_bool, 0, offsetof(VxLan, arp_proxy) +VXLAN.ReduceARPProxy, config_parse_bool, 0, offsetof(VxLan, arp_proxy) +VXLAN.L2MissNotification, config_parse_bool, 0, offsetof(VxLan, l2miss) +VXLAN.L3MissNotification, config_parse_bool, 0, offsetof(VxLan, l3miss) +VXLAN.RouteShortCircuit, config_parse_bool, 0, offsetof(VxLan, route_short_circuit) +VXLAN.UDPCheckSum, config_parse_bool, 0, offsetof(VxLan, udpcsum) +VXLAN.UDPChecksum, config_parse_bool, 0, offsetof(VxLan, udpcsum) +VXLAN.UDP6ZeroCheckSumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx) +VXLAN.UDP6ZeroChecksumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx) +VXLAN.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx) +VXLAN.UDP6ZeroChecksumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx) +VXLAN.RemoteChecksumTx, config_parse_bool, 0, offsetof(VxLan, remote_csum_tx) +VXLAN.RemoteChecksumRx, config_parse_bool, 0, offsetof(VxLan, remote_csum_rx) +VXLAN.FDBAgeingSec, config_parse_sec, 0, offsetof(VxLan, fdb_ageing) +VXLAN.GroupPolicyExtension, config_parse_bool, 0, offsetof(VxLan, group_policy) +VXLAN.MaximumFDBEntries, config_parse_unsigned, 0, offsetof(VxLan, max_fdb) +VXLAN.PortRange, config_parse_port_range, 0, 0 +VXLAN.DestinationPort, config_parse_ip_port, 0, offsetof(VxLan, dest_port) +VXLAN.FlowLabel, config_parse_flow_label, 0, 0 +GENEVE.Id, config_parse_geneve_vni, 0, offsetof(Geneve, id) +GENEVE.Remote, config_parse_geneve_address, 0, offsetof(Geneve, remote) +GENEVE.TOS, config_parse_uint8, 0, offsetof(Geneve, tos) +GENEVE.TTL, config_parse_uint8, 0, offsetof(Geneve, ttl) +GENEVE.UDPChecksum, config_parse_bool, 0, offsetof(Geneve, udpcsum) +GENEVE.UDP6ZeroCheckSumRx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumrx) +GENEVE.UDP6ZeroChecksumRx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumrx) +GENEVE.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumtx) +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) +Tun.User, config_parse_string, 0, offsetof(TunTap, user_name) +Tun.Group, config_parse_string, 0, offsetof(TunTap, group_name) +Tap.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue) +Tap.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue) +Tap.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info) +Tap.VNetHeader, config_parse_bool, 0, offsetof(TunTap, vnet_hdr) +Tap.User, config_parse_string, 0, offsetof(TunTap, user_name) +Tap.Group, config_parse_string, 0, offsetof(TunTap, group_name) +Bond.Mode, config_parse_bond_mode, 0, offsetof(Bond, mode) +Bond.TransmitHashPolicy, config_parse_bond_xmit_hash_policy, 0, offsetof(Bond, xmit_hash_policy) +Bond.LACPTransmitRate, config_parse_bond_lacp_rate, 0, offsetof(Bond, lacp_rate) +Bond.AdSelect, config_parse_bond_ad_select, 0, offsetof(Bond, ad_select) +Bond.FailOverMACPolicy, config_parse_bond_fail_over_mac, 0, offsetof(Bond, fail_over_mac) +Bond.ARPIPTargets, config_parse_arp_ip_target_address, 0, 0 +Bond.ARPValidate, config_parse_bond_arp_validate, 0, offsetof(Bond, arp_validate) +Bond.ARPAllTargets, config_parse_bond_arp_all_targets, 0, offsetof(Bond, arp_all_targets) +Bond.PrimaryReselectPolicy, config_parse_bond_primary_reselect, 0, offsetof(Bond, primary_reselect) +Bond.ResendIGMP, config_parse_unsigned, 0, offsetof(Bond, resend_igmp) +Bond.PacketsPerSlave, config_parse_unsigned, 0, offsetof(Bond, packets_per_slave) +Bond.GratuitousARP, config_parse_unsigned, 0, offsetof(Bond, num_grat_arp) +Bond.AllSlavesActive, config_parse_bool, 0, offsetof(Bond, all_slaves_active) +Bond.DynamicTransmitLoadBalancing, config_parse_tristate, 0, offsetof(Bond, tlb_dynamic_lb) +Bond.MinLinks, config_parse_unsigned, 0, offsetof(Bond, min_links) +Bond.MIIMonitorSec, config_parse_sec, 0, offsetof(Bond, miimon) +Bond.UpDelaySec, config_parse_sec, 0, offsetof(Bond, updelay) +Bond.DownDelaySec, config_parse_sec, 0, offsetof(Bond, downdelay) +Bond.ARPIntervalSec, config_parse_sec, 0, offsetof(Bond, arp_interval) +Bond.LearnPacketIntervalSec, config_parse_sec, 0, offsetof(Bond, lp_interval) +Bond.AdActorSystemPriority, config_parse_ad_actor_sys_prio, 0, offsetof(Bond, ad_actor_sys_prio) +Bond.AdUserPortKey, config_parse_ad_user_port_key, 0, offsetof(Bond, ad_user_port_key) +Bond.AdActorSystem, config_parse_ad_actor_system, 0, offsetof(Bond, ad_actor_system) +Bridge.HelloTimeSec, config_parse_sec, 0, offsetof(Bridge, hello_time) +Bridge.MaxAgeSec, config_parse_sec, 0, offsetof(Bridge, max_age) +Bridge.AgeingTimeSec, config_parse_sec, 0, offsetof(Bridge, ageing_time) +Bridge.ForwardDelaySec, config_parse_sec, 0, offsetof(Bridge, forward_delay) +Bridge.Priority, config_parse_uint16, 0, offsetof(Bridge, priority) +Bridge.GroupForwardMask, config_parse_uint16, 0, offsetof(Bridge, group_fwd_mask) +Bridge.DefaultPVID, config_parse_default_port_vlanid, 0, offsetof(Bridge, default_pvid) +Bridge.MulticastQuerier, config_parse_tristate, 0, offsetof(Bridge, mcast_querier) +Bridge.MulticastSnooping, config_parse_tristate, 0, offsetof(Bridge, mcast_snooping) +Bridge.VLANFiltering, config_parse_tristate, 0, offsetof(Bridge, vlan_filtering) +Bridge.STP, config_parse_tristate, 0, offsetof(Bridge, stp) +VRF.TableId, config_parse_uint32, 0, offsetof(Vrf, table) /* deprecated */ +VRF.Table, config_parse_uint32, 0, offsetof(Vrf, table) +WireGuard.FwMark, config_parse_unsigned, 0, offsetof(Wireguard, fwmark) +WireGuard.ListenPort, config_parse_wireguard_listen_port, 0, offsetof(Wireguard, port) +WireGuard.PrivateKey, config_parse_wireguard_private_key, 0, 0 +WireGuard.PrivateKeyFile, config_parse_wireguard_private_key_file, 0, 0 +WireGuardPeer.AllowedIPs, config_parse_wireguard_allowed_ips, 0, 0 +WireGuardPeer.Endpoint, config_parse_wireguard_endpoint, 0, 0 +WireGuardPeer.PublicKey, config_parse_wireguard_public_key, 0, 0 +WireGuardPeer.PresharedKey, config_parse_wireguard_preshared_key, 0, 0 +WireGuardPeer.PresharedKeyFile, config_parse_wireguard_preshared_key_file, 0, 0 +WireGuardPeer.PersistentKeepalive, config_parse_wireguard_keepalive, 0, 0 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..0db59473ff 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -20,191 +20,192 @@ struct ConfigPerfItem; %struct-type %includes %% -Match.MACAddress, config_parse_hwaddrs, 0, offsetof(Network, match_mac) -Match.Path, config_parse_strv, 0, offsetof(Network, match_path) -Match.Driver, config_parse_strv, 0, offsetof(Network, match_driver) -Match.Type, config_parse_strv, 0, offsetof(Network, match_type) -Match.Name, config_parse_ifnames, 0, offsetof(Network, match_name) -Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(Network, conditions) -Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(Network, conditions) -Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(Network, conditions) -Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(Network, conditions) -Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(Network, conditions) -Link.MACAddress, config_parse_hwaddr, 0, offsetof(Network, mac) -Link.MTUBytes, config_parse_mtu, AF_UNSPEC, offsetof(Network, mtu) -Link.ARP, config_parse_tristate, 0, offsetof(Network, arp) -Link.Multicast, config_parse_tristate, 0, offsetof(Network, multicast) -Link.AllMulticast, config_parse_tristate, 0, offsetof(Network, allmulticast) -Link.Unmanaged, config_parse_bool, 0, offsetof(Network, unmanaged) -Link.RequiredForOnline, config_parse_required_for_online, 0, 0 -Network.Description, config_parse_string, 0, offsetof(Network, description) -Network.Bridge, config_parse_ifname, 0, offsetof(Network, bridge_name) -Network.Bond, config_parse_ifname, 0, offsetof(Network, bond_name) -Network.VLAN, config_parse_stacked_netdev, NETDEV_KIND_VLAN, offsetof(Network, stacked_netdev_names) -Network.MACVLAN, config_parse_stacked_netdev, NETDEV_KIND_MACVLAN, offsetof(Network, stacked_netdev_names) -Network.MACVTAP, config_parse_stacked_netdev, NETDEV_KIND_MACVTAP, offsetof(Network, stacked_netdev_names) -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.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) -Network.DHCPServer, config_parse_bool, 0, offsetof(Network, dhcp_server) -Network.LinkLocalAddressing, config_parse_address_family_boolean, 0, offsetof(Network, link_local) -Network.IPv4LLRoute, config_parse_bool, 0, offsetof(Network, ipv4ll_route) -Network.IPv6Token, config_parse_ipv6token, 0, offsetof(Network, ipv6_token) -Network.LLDP, config_parse_lldp_mode, 0, offsetof(Network, lldp_mode) -Network.EmitLLDP, config_parse_lldp_emit, 0, offsetof(Network, lldp_emit) -Network.Address, config_parse_address, 0, 0 -Network.Gateway, config_parse_gateway, 0, 0 -Network.Domains, config_parse_domains, 0, 0 -Network.DNS, config_parse_dns, 0, 0 -Network.DNSDefaultRoute, config_parse_tristate, 0, offsetof(Network, dns_default_route) -Network.LLMNR, config_parse_resolve_support, 0, offsetof(Network, llmnr) -Network.MulticastDNS, config_parse_resolve_support, 0, offsetof(Network, mdns) -Network.DNSOverTLS, config_parse_dns_over_tls_mode, 0, offsetof(Network, 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.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) -Network.IPv6AcceptRouterAdvertisements, config_parse_tristate, 0, offsetof(Network, ipv6_accept_ra) -Network.IPv6DuplicateAddressDetection, config_parse_int, 0, offsetof(Network, ipv6_dad_transmits) -Network.IPv6HopLimit, config_parse_int, 0, offsetof(Network, ipv6_hop_limit) -Network.IPv6ProxyNDP, config_parse_tristate, 0, offsetof(Network, ipv6_proxy_ndp) -Network.IPv6MTUBytes, config_parse_mtu, AF_INET6, offsetof(Network, ipv6_mtu) -Network.ActiveSlave, config_parse_bool, 0, offsetof(Network, active_slave) -Network.PrimarySlave, config_parse_bool, 0, offsetof(Network, primary_slave) -Network.IPv4ProxyARP, config_parse_tristate, 0, offsetof(Network, proxy_arp) -Network.ProxyARP, config_parse_tristate, 0, offsetof(Network, proxy_arp) -Network.IPv6ProxyNDPAddress, config_parse_ipv6_proxy_ndp_address, 0, 0 -Network.BindCarrier, config_parse_strv, 0, offsetof(Network, bind_carrier) -Network.ConfigureWithoutCarrier, config_parse_bool, 0, offsetof(Network, configure_without_carrier) -Network.IgnoreCarrierLoss, config_parse_bool, 0, offsetof(Network, ignore_carrier_loss) -Address.Address, config_parse_address, 0, 0 -Address.Peer, config_parse_address, 0, 0 -Address.Broadcast, config_parse_broadcast, 0, 0 -Address.Label, config_parse_label, 0, 0 -Address.PreferredLifetime, config_parse_lifetime, 0, 0 -Address.HomeAddress, config_parse_address_flags, 0, 0 -Address.DuplicateAddressDetection, config_parse_address_flags, 0, 0 -Address.ManageTemporaryAddress, config_parse_address_flags, 0, 0 -Address.PrefixRoute, config_parse_address_flags, 0, 0 -Address.AutoJoin, config_parse_address_flags, 0, 0 -Address.Scope, config_parse_address_scope, 0, 0 -IPv6AddressLabel.Prefix, config_parse_address_label_prefix, 0, 0 -IPv6AddressLabel.Label, config_parse_address_label, 0, 0 -Neighbor.Address, config_parse_neighbor_address, 0, 0 -Neighbor.MACAddress, config_parse_neighbor_hwaddr, 0, 0 -RoutingPolicyRule.TypeOfService, config_parse_routing_policy_rule_tos, 0, 0 -RoutingPolicyRule.Priority, config_parse_routing_policy_rule_priority, 0, 0 -RoutingPolicyRule.Table, config_parse_routing_policy_rule_table, 0, 0 -RoutingPolicyRule.FirewallMark, config_parse_routing_policy_rule_fwmark_mask, 0, 0 -RoutingPolicyRule.From, config_parse_routing_policy_rule_prefix, 0, 0 -RoutingPolicyRule.To, config_parse_routing_policy_rule_prefix, 0, 0 -RoutingPolicyRule.IncomingInterface, config_parse_routing_policy_rule_device, 0, 0 -RoutingPolicyRule.OutgoingInterface, config_parse_routing_policy_rule_device, 0, 0 -RoutingPolicyRule.IPProtocol, config_parse_routing_policy_rule_ip_protocol, 0, 0 -RoutingPolicyRule.SourcePort, config_parse_routing_policy_rule_port_range, 0, 0 -RoutingPolicyRule.DestinationPort, config_parse_routing_policy_rule_port_range, 0, 0 -RoutingPolicyRule.InvertRule, config_parse_routing_policy_rule_invert, 0, 0 -Route.Gateway, config_parse_gateway, 0, 0 -Route.Destination, config_parse_destination, 0, 0 -Route.Source, config_parse_destination, 0, 0 -Route.Metric, config_parse_route_priority, 0, 0 -Route.Scope, config_parse_route_scope, 0, 0 -Route.PreferredSource, config_parse_preferred_src, 0, 0 -Route.Table, config_parse_route_table, 0, 0 -Route.MTUBytes, config_parse_route_mtu, AF_UNSPEC, 0 -Route.GatewayOnLink, config_parse_gateway_onlink, 0, 0 -Route.GatewayOnlink, config_parse_gateway_onlink, 0, 0 -Route.IPv6Preference, config_parse_ipv6_route_preference, 0, 0 -Route.Protocol, config_parse_route_protocol, 0, 0 -Route.Type, config_parse_route_type, 0, 0 -Route.InitialCongestionWindow, config_parse_tcp_window, 0, 0 -Route.InitialAdvertisedReceiveWindow, config_parse_tcp_window, 0, 0 -Route.QuickAck, config_parse_quickack, 0, 0 -DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier) -DHCP.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns) -DHCP.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp_use_ntp) -DHCP.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_use_mtu) -DHCP.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_use_hostname) -DHCP.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains) -DHCP.UseRoutes, config_parse_bool, 0, offsetof(Network, dhcp_use_routes) -DHCP.Anonymize, config_parse_bool, 0, offsetof(Network, dhcp_anonymize) -DHCP.SendHostname, config_parse_bool, 0, offsetof(Network, dhcp_send_hostname) -DHCP.Hostname, config_parse_hostname, 0, offsetof(Network, dhcp_hostname) -DHCP.RequestBroadcast, config_parse_bool, 0, offsetof(Network, dhcp_broadcast) -DHCP.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical) -DHCP.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier) -DHCP.UserClass, config_parse_dhcp_user_class, 0, offsetof(Network, dhcp_user_class) -DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Network, duid) -DHCP.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Network, duid) -DHCP.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric) -DHCP.RouteTable, config_parse_section_route_table, 0, 0 -DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone) -DHCP.IAID, config_parse_iaid, 0, 0 -DHCP.ListenPort, config_parse_uint16, 0, offsetof(Network, dhcp_client_port) -DHCP.RapidCommit, config_parse_bool, 0, offsetof(Network, rapid_commit) -DHCP.ForceDHCPv6PDOtherInformation, config_parse_bool, 0, offsetof(Network, dhcp6_force_pd_other_information) -IPv6AcceptRA.UseAutonomousPrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_autonomous_prefix) -IPv6AcceptRA.UseOnLinkPrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_onlink_prefix) -IPv6AcceptRA.UseDNS, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_dns) -IPv6AcceptRA.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, ipv6_accept_ra_use_domains) -IPv6AcceptRA.RouteTable, config_parse_section_route_table, 0, 0 -DHCPServer.MaxLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_max_lease_time_usec) -DHCPServer.DefaultLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_default_lease_time_usec) -DHCPServer.EmitDNS, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_dns) -DHCPServer.DNS, config_parse_dhcp_server_dns, 0, 0 -DHCPServer.EmitNTP, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_ntp) -DHCPServer.NTP, config_parse_dhcp_server_ntp, 0, 0 -DHCPServer.EmitRouter, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_router) -DHCPServer.EmitTimezone, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_timezone) -DHCPServer.Timezone, config_parse_timezone, 0, offsetof(Network, dhcp_server_timezone) -DHCPServer.PoolOffset, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_offset) -DHCPServer.PoolSize, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_size) -Bridge.Cost, config_parse_uint32, 0, offsetof(Network, cost) -Bridge.UseBPDU, config_parse_tristate, 0, offsetof(Network, use_bpdu) -Bridge.HairPin, config_parse_tristate, 0, offsetof(Network, hairpin) -Bridge.FastLeave, config_parse_tristate, 0, offsetof(Network, fast_leave) -Bridge.AllowPortToBeRoot, config_parse_tristate, 0, offsetof(Network, allow_port_to_be_root) -Bridge.UnicastFlood, config_parse_tristate, 0, offsetof(Network, unicast_flood) -Bridge.MulticastFlood, config_parse_tristate, 0, offsetof(Network, multicast_flood) -Bridge.MulticastToUnicast, config_parse_tristate, 0, offsetof(Network, multicast_to_unicast) -Bridge.NeighborSuppression, config_parse_tristate, 0, offsetof(Network, neighbor_suppression) -Bridge.Learning, config_parse_tristate, 0, offsetof(Network, learning) -Bridge.Priority, config_parse_bridge_port_priority, 0, offsetof(Network, priority) -BridgeFDB.MACAddress, config_parse_fdb_hwaddr, 0, 0 -BridgeFDB.VLANId, config_parse_fdb_vlan_id, 0, 0 -BridgeVLAN.PVID, config_parse_brvlan_pvid, 0, 0 -BridgeVLAN.VLAN, config_parse_brvlan_vlan, 0, 0 -BridgeVLAN.EgressUntagged, config_parse_brvlan_untagged, 0, 0 -Network.IPv6PrefixDelegation, config_parse_router_prefix_delegation, 0, 0 -IPv6PrefixDelegation.RouterLifetimeSec, config_parse_sec, 0, offsetof(Network, router_lifetime_usec) -IPv6PrefixDelegation.Managed, config_parse_bool, 0, offsetof(Network, router_managed) -IPv6PrefixDelegation.OtherInformation, config_parse_bool, 0, offsetof(Network, router_other_information) -IPv6PrefixDelegation.RouterPreference, config_parse_router_preference, 0, 0 -IPv6PrefixDelegation.EmitDNS, config_parse_bool, 0, offsetof(Network, router_emit_dns) -IPv6PrefixDelegation.DNS, config_parse_radv_dns, 0, 0 -IPv6PrefixDelegation.EmitDomains, config_parse_bool, 0, offsetof(Network, router_emit_domains) -IPv6PrefixDelegation.Domains, config_parse_radv_search_domains, 0, 0 -IPv6PrefixDelegation.DNSLifetimeSec, config_parse_sec, 0, offsetof(Network, router_dns_lifetime_usec) -IPv6Prefix.Prefix, config_parse_prefix, 0, 0 -IPv6Prefix.OnLink, config_parse_prefix_flags, 0, 0 -IPv6Prefix.AddressAutoconfiguration, config_parse_prefix_flags, 0, 0 -IPv6Prefix.ValidLifetimeSec, config_parse_prefix_lifetime, 0, 0 -IPv6Prefix.PreferredLifetimeSec, config_parse_prefix_lifetime, 0, 0 -CAN.BitRate, config_parse_si_size, 0, offsetof(Network, can_bitrate) -CAN.SamplePoint, config_parse_permille, 0, offsetof(Network, can_sample_point) -CAN.RestartSec, config_parse_sec, 0, offsetof(Network, can_restart_us) -CAN.TripleSampling, config_parse_tristate, 0, offsetof(Network, can_triple_sampling) +Match.MACAddress, config_parse_hwaddrs, 0, offsetof(Network, match_mac) +Match.Path, config_parse_strv, 0, offsetof(Network, match_path) +Match.Driver, config_parse_strv, 0, offsetof(Network, match_driver) +Match.Type, config_parse_strv, 0, offsetof(Network, match_type) +Match.Name, config_parse_ifnames, 0, offsetof(Network, match_name) +Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(Network, conditions) +Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(Network, conditions) +Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(Network, conditions) +Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(Network, conditions) +Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(Network, conditions) +Link.MACAddress, config_parse_hwaddr, 0, offsetof(Network, mac) +Link.MTUBytes, config_parse_mtu, AF_UNSPEC, offsetof(Network, mtu) +Link.ARP, config_parse_tristate, 0, offsetof(Network, arp) +Link.Multicast, config_parse_tristate, 0, offsetof(Network, multicast) +Link.AllMulticast, config_parse_tristate, 0, offsetof(Network, allmulticast) +Link.Unmanaged, config_parse_bool, 0, offsetof(Network, unmanaged) +Link.RequiredForOnline, config_parse_required_for_online, 0, 0 +Network.Description, config_parse_string, 0, offsetof(Network, description) +Network.Bridge, config_parse_ifname, 0, offsetof(Network, bridge_name) +Network.Bond, config_parse_ifname, 0, offsetof(Network, bond_name) +Network.VLAN, config_parse_stacked_netdev, NETDEV_KIND_VLAN, offsetof(Network, stacked_netdev_names) +Network.MACVLAN, config_parse_stacked_netdev, NETDEV_KIND_MACVLAN, offsetof(Network, stacked_netdev_names) +Network.MACVTAP, config_parse_stacked_netdev, NETDEV_KIND_MACVTAP, offsetof(Network, stacked_netdev_names) +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) +Network.DHCPServer, config_parse_bool, 0, offsetof(Network, dhcp_server) +Network.LinkLocalAddressing, config_parse_address_family_boolean, 0, offsetof(Network, link_local) +Network.IPv4LLRoute, config_parse_bool, 0, offsetof(Network, ipv4ll_route) +Network.IPv6Token, config_parse_ipv6token, 0, offsetof(Network, ipv6_token) +Network.LLDP, config_parse_lldp_mode, 0, offsetof(Network, lldp_mode) +Network.EmitLLDP, config_parse_lldp_emit, 0, offsetof(Network, lldp_emit) +Network.Address, config_parse_address, 0, 0 +Network.Gateway, config_parse_gateway, 0, 0 +Network.Domains, config_parse_domains, 0, 0 +Network.DNS, config_parse_dns, 0, 0 +Network.DNSDefaultRoute, config_parse_tristate, 0, offsetof(Network, dns_default_route) +Network.LLMNR, config_parse_resolve_support, 0, offsetof(Network, llmnr) +Network.MulticastDNS, config_parse_resolve_support, 0, offsetof(Network, mdns) +Network.DNSOverTLS, config_parse_dns_over_tls_mode, 0, offsetof(Network, 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.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) +Network.IPv6AcceptRouterAdvertisements, config_parse_tristate, 0, offsetof(Network, ipv6_accept_ra) +Network.IPv6DuplicateAddressDetection, config_parse_int, 0, offsetof(Network, ipv6_dad_transmits) +Network.IPv6HopLimit, config_parse_int, 0, offsetof(Network, ipv6_hop_limit) +Network.IPv6ProxyNDP, config_parse_tristate, 0, offsetof(Network, ipv6_proxy_ndp) +Network.IPv6MTUBytes, config_parse_mtu, AF_INET6, offsetof(Network, ipv6_mtu) +Network.ActiveSlave, config_parse_bool, 0, offsetof(Network, active_slave) +Network.PrimarySlave, config_parse_bool, 0, offsetof(Network, primary_slave) +Network.IPv4ProxyARP, config_parse_tristate, 0, offsetof(Network, proxy_arp) +Network.ProxyARP, config_parse_tristate, 0, offsetof(Network, proxy_arp) +Network.IPv6ProxyNDPAddress, config_parse_ipv6_proxy_ndp_address, 0, 0 +Network.BindCarrier, config_parse_strv, 0, offsetof(Network, bind_carrier) +Network.ConfigureWithoutCarrier, config_parse_bool, 0, offsetof(Network, configure_without_carrier) +Network.IgnoreCarrierLoss, config_parse_bool, 0, offsetof(Network, ignore_carrier_loss) +Address.Address, config_parse_address, 0, 0 +Address.Peer, config_parse_address, 0, 0 +Address.Broadcast, config_parse_broadcast, 0, 0 +Address.Label, config_parse_label, 0, 0 +Address.PreferredLifetime, config_parse_lifetime, 0, 0 +Address.HomeAddress, config_parse_address_flags, 0, 0 +Address.DuplicateAddressDetection, config_parse_address_flags, 0, 0 +Address.ManageTemporaryAddress, config_parse_address_flags, 0, 0 +Address.PrefixRoute, config_parse_address_flags, 0, 0 +Address.AutoJoin, config_parse_address_flags, 0, 0 +Address.Scope, config_parse_address_scope, 0, 0 +IPv6AddressLabel.Prefix, config_parse_address_label_prefix, 0, 0 +IPv6AddressLabel.Label, config_parse_address_label, 0, 0 +Neighbor.Address, config_parse_neighbor_address, 0, 0 +Neighbor.MACAddress, config_parse_neighbor_hwaddr, 0, 0 +RoutingPolicyRule.TypeOfService, config_parse_routing_policy_rule_tos, 0, 0 +RoutingPolicyRule.Priority, config_parse_routing_policy_rule_priority, 0, 0 +RoutingPolicyRule.Table, config_parse_routing_policy_rule_table, 0, 0 +RoutingPolicyRule.FirewallMark, config_parse_routing_policy_rule_fwmark_mask, 0, 0 +RoutingPolicyRule.From, config_parse_routing_policy_rule_prefix, 0, 0 +RoutingPolicyRule.To, config_parse_routing_policy_rule_prefix, 0, 0 +RoutingPolicyRule.IncomingInterface, config_parse_routing_policy_rule_device, 0, 0 +RoutingPolicyRule.OutgoingInterface, config_parse_routing_policy_rule_device, 0, 0 +RoutingPolicyRule.IPProtocol, config_parse_routing_policy_rule_ip_protocol, 0, 0 +RoutingPolicyRule.SourcePort, config_parse_routing_policy_rule_port_range, 0, 0 +RoutingPolicyRule.DestinationPort, config_parse_routing_policy_rule_port_range, 0, 0 +RoutingPolicyRule.InvertRule, config_parse_routing_policy_rule_invert, 0, 0 +Route.Gateway, config_parse_gateway, 0, 0 +Route.Destination, config_parse_destination, 0, 0 +Route.Source, config_parse_destination, 0, 0 +Route.Metric, config_parse_route_priority, 0, 0 +Route.Scope, config_parse_route_scope, 0, 0 +Route.PreferredSource, config_parse_preferred_src, 0, 0 +Route.Table, config_parse_route_table, 0, 0 +Route.MTUBytes, config_parse_route_mtu, AF_UNSPEC, 0 +Route.GatewayOnLink, config_parse_gateway_onlink, 0, 0 +Route.GatewayOnlink, config_parse_gateway_onlink, 0, 0 +Route.IPv6Preference, config_parse_ipv6_route_preference, 0, 0 +Route.Protocol, config_parse_route_protocol, 0, 0 +Route.Type, config_parse_route_type, 0, 0 +Route.InitialCongestionWindow, config_parse_tcp_window, 0, 0 +Route.InitialAdvertisedReceiveWindow, config_parse_tcp_window, 0, 0 +Route.QuickAck, config_parse_quickack, 0, 0 +DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier) +DHCP.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns) +DHCP.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp_use_ntp) +DHCP.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_use_mtu) +DHCP.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_use_hostname) +DHCP.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains) +DHCP.UseRoutes, config_parse_bool, 0, offsetof(Network, dhcp_use_routes) +DHCP.Anonymize, config_parse_bool, 0, offsetof(Network, dhcp_anonymize) +DHCP.SendHostname, config_parse_bool, 0, offsetof(Network, dhcp_send_hostname) +DHCP.Hostname, config_parse_hostname, 0, offsetof(Network, dhcp_hostname) +DHCP.RequestBroadcast, config_parse_bool, 0, offsetof(Network, dhcp_broadcast) +DHCP.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical) +DHCP.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier) +DHCP.UserClass, config_parse_dhcp_user_class, 0, offsetof(Network, dhcp_user_class) +DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Network, duid) +DHCP.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Network, duid) +DHCP.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric) +DHCP.RouteTable, config_parse_section_route_table, 0, 0 +DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone) +DHCP.IAID, config_parse_iaid, 0, 0 +DHCP.ListenPort, config_parse_uint16, 0, offsetof(Network, dhcp_client_port) +DHCP.RapidCommit, config_parse_bool, 0, offsetof(Network, rapid_commit) +DHCP.ForceDHCPv6PDOtherInformation, config_parse_bool, 0, offsetof(Network, dhcp6_force_pd_other_information) +IPv6AcceptRA.UseAutonomousPrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_autonomous_prefix) +IPv6AcceptRA.UseOnLinkPrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_onlink_prefix) +IPv6AcceptRA.UseDNS, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_dns) +IPv6AcceptRA.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, ipv6_accept_ra_use_domains) +IPv6AcceptRA.RouteTable, config_parse_section_route_table, 0, 0 +DHCPServer.MaxLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_max_lease_time_usec) +DHCPServer.DefaultLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_default_lease_time_usec) +DHCPServer.EmitDNS, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_dns) +DHCPServer.DNS, config_parse_dhcp_server_dns, 0, 0 +DHCPServer.EmitNTP, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_ntp) +DHCPServer.NTP, config_parse_dhcp_server_ntp, 0, 0 +DHCPServer.EmitRouter, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_router) +DHCPServer.EmitTimezone, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_timezone) +DHCPServer.Timezone, config_parse_timezone, 0, offsetof(Network, dhcp_server_timezone) +DHCPServer.PoolOffset, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_offset) +DHCPServer.PoolSize, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_size) +Bridge.Cost, config_parse_uint32, 0, offsetof(Network, cost) +Bridge.UseBPDU, config_parse_tristate, 0, offsetof(Network, use_bpdu) +Bridge.HairPin, config_parse_tristate, 0, offsetof(Network, hairpin) +Bridge.FastLeave, config_parse_tristate, 0, offsetof(Network, fast_leave) +Bridge.AllowPortToBeRoot, config_parse_tristate, 0, offsetof(Network, allow_port_to_be_root) +Bridge.UnicastFlood, config_parse_tristate, 0, offsetof(Network, unicast_flood) +Bridge.MulticastFlood, config_parse_tristate, 0, offsetof(Network, multicast_flood) +Bridge.MulticastToUnicast, config_parse_tristate, 0, offsetof(Network, multicast_to_unicast) +Bridge.NeighborSuppression, config_parse_tristate, 0, offsetof(Network, neighbor_suppression) +Bridge.Learning, config_parse_tristate, 0, offsetof(Network, learning) +Bridge.Priority, config_parse_bridge_port_priority, 0, offsetof(Network, priority) +BridgeFDB.MACAddress, config_parse_fdb_hwaddr, 0, 0 +BridgeFDB.VLANId, config_parse_fdb_vlan_id, 0, 0 +BridgeVLAN.PVID, config_parse_brvlan_pvid, 0, 0 +BridgeVLAN.VLAN, config_parse_brvlan_vlan, 0, 0 +BridgeVLAN.EgressUntagged, config_parse_brvlan_untagged, 0, 0 +Network.IPv6PrefixDelegation, config_parse_router_prefix_delegation, 0, 0 +IPv6PrefixDelegation.RouterLifetimeSec, config_parse_sec, 0, offsetof(Network, router_lifetime_usec) +IPv6PrefixDelegation.Managed, config_parse_bool, 0, offsetof(Network, router_managed) +IPv6PrefixDelegation.OtherInformation, config_parse_bool, 0, offsetof(Network, router_other_information) +IPv6PrefixDelegation.RouterPreference, config_parse_router_preference, 0, 0 +IPv6PrefixDelegation.EmitDNS, config_parse_bool, 0, offsetof(Network, router_emit_dns) +IPv6PrefixDelegation.DNS, config_parse_radv_dns, 0, 0 +IPv6PrefixDelegation.EmitDomains, config_parse_bool, 0, offsetof(Network, router_emit_domains) +IPv6PrefixDelegation.Domains, config_parse_radv_search_domains, 0, 0 +IPv6PrefixDelegation.DNSLifetimeSec, config_parse_sec, 0, offsetof(Network, router_dns_lifetime_usec) +IPv6Prefix.Prefix, config_parse_prefix, 0, 0 +IPv6Prefix.OnLink, config_parse_prefix_flags, 0, 0 +IPv6Prefix.AddressAutoconfiguration, config_parse_prefix_flags, 0, 0 +IPv6Prefix.ValidLifetimeSec, config_parse_prefix_lifetime, 0, 0 +IPv6Prefix.PreferredLifetimeSec, config_parse_prefix_lifetime, 0, 0 +CAN.BitRate, config_parse_si_size, 0, offsetof(Network, can_bitrate) +CAN.SamplePoint, config_parse_permille, 0, offsetof(Network, can_sample_point) +CAN.RestartSec, config_parse_sec, 0, offsetof(Network, can_restart_us) +CAN.TripleSampling, config_parse_tristate, 0, offsetof(Network, can_triple_sampling) /* backwards compatibility: do not add new entries to this section */ -Network.IPv4LL, config_parse_ipv4ll, 0, offsetof(Network, link_local) -DHCPv4.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns) -DHCPv4.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_use_mtu) -DHCPv4.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_use_hostname) -DHCP.UseDomainName, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains) -DHCPv4.UseDomainName, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains) -DHCPv4.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical) +Network.IPv4LL, config_parse_ipv4ll, 0, offsetof(Network, link_local) +DHCPv4.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns) +DHCPv4.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_use_mtu) +DHCPv4.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_use_hostname) +DHCP.UseDomainName, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains) +DHCPv4.UseDomainName, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains) +DHCPv4.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical) 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..128f8b6341 100644 --- a/test/fuzz/fuzz-netdev-parser/directives.netdev +++ b/test/fuzz/fuzz-netdev-parser/directives.netdev @@ -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= 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= diff --git a/test/test-network/conf/25-macsec.key b/test/test-network/conf/25-macsec.key new file mode 100644 index 0000000000..56c51fa9f7 --- /dev/null +++ b/test/test-network/conf/25-macsec.key @@ -0,0 +1 @@ +85858585858585858585858585858585 diff --git a/test/test-network/conf/25-macsec.netdev b/test/test-network/conf/25-macsec.netdev new file mode 100644 index 0000000000..9b603ca039 --- /dev/null +++ b/test/test-network/conf/25-macsec.netdev @@ -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 diff --git a/test/test-network/conf/25-macsec.network b/test/test-network/conf/25-macsec.network new file mode 100644 index 0000000000..7037048a45 --- /dev/null +++ b/test/test-network/conf/25-macsec.network @@ -0,0 +1,6 @@ +[Match] +Name=macsec99 + +[Network] +IPv6AcceptRA=no +Address=10.1.2.3/16 diff --git a/test/test-network/conf/macsec.network b/test/test-network/conf/macsec.network new file mode 100644 index 0000000000..d1360a59eb --- /dev/null +++ b/test/test-network/conf/macsec.network @@ -0,0 +1,9 @@ +[Match] +Name=dummy98 + +[Link] +MACAddress=00:50:56:c0:00:19 + +[Network] +IPv6AcceptRA=no +MACsec=macsec99 diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index b44cfe8d45..d9db9c1657 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -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 =[