diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml index a44018cad6d6c..ebd3bfcdeb8ff 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,120 @@ + + [MACSEC] Section Options + The [MACSEC] section only applies for + netdevs of kind macsec, and accepts the + following keys: + + + + Port= + + Specifies the to be used for the MACsec. Takes either value between 1 and 65535. This option is compulsory. + + + + Encrypt= + + Takes a boolean. When true, enable encryption. Defaults to unset. + + + + + + [MACsecReceiveChannel] Section Options + The [MACsecReceiveChannel] section only applies for + netdevs of kind macsec, and accepts the + following keys: + + + + Port= + + Specifies the port to be used for the MACsec Receive Channel. The port used to make Secure Channel Identifier (SCI). + Takes either value between 1 and 65535. This option is compulsory. + + + + MACAddress= + + Specifies the MAC address to be used for the MACsec Receive Channel. The port used to make Secure Channel Identifier (SCI). + This option is compulsory. + + + + + + [MACsecTransmitAssociation] Section Options + The [MACsecTransmitAssociation] section only applies for + netdevs 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]). Ranges a number between 1 and 4,294,967,295. + + + + + KeyId= + + Specifies the identification for they key. Ranges a number between 0 to 255 This option is compulsory. + + + + Key= + + Specifies the identification for the key used in the transmit channel. That same key must be configured on the peer’s matching receive channel. + This option is compulsory. Takes a 128-bits key for example "dffafc8d7b9a43d5b9a3dfbbf6a30c16". + + + + + + [MACsecReceiveAssociation] Section Options + The [MACsecReceiveAssociation] section only applies for + netdevs of kind macsec, and accepts the + following keys: + + + + Port= + + See [MACsecTransmitAssociation] Section. + + + + MACAddress= + + See [MACsecTransmitAssociation] Section. + + + + PacketNumber= + + See [MACsecTransmitAssociation] Section. + + + + + KeyId= + + See [MACsecTransmitAssociation] Section. + + + + Key= + + See [MACsecTransmitAssociation] Section. + + + + [Tunnel] Section Options diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c index 87724af693068..5c939298c195b 100644 --- a/src/basic/parse-util.c +++ b/src/basic/parse-util.c @@ -566,6 +566,38 @@ int safe_atod(const char *s, double *ret_d) { return 0; } +int safe_hexstring_aton(const char *s, uint8_t *b, unsigned int buf_len, unsigned int *len) { + unsigned int c = 0; + char *p; + + assert(s); + assert(b); + assert(len); + + if (strlen(s) % 2) + return -EINVAL; + + for(c = 0; c < buf_len && strlen(s) > 1; s += 2) { + unsigned int a; + char t[3]; + + strncpy(t, s, 2); + t[2] = '\0'; + + errno = 0; + + a = strtoul(t, &p, 16); + if (errno != 0 || a > 0xFF || *p != '\0') + return -EINVAL; + + b[c++] = a; + } + + *len = c; + + return 0; +} + int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) { size_t i; unsigned val = 0; diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h index e47641b429544..41d07998d71a7 100644 --- a/src/basic/parse-util.h +++ b/src/basic/parse-util.h @@ -104,6 +104,8 @@ static inline int safe_atozu(const char *s, size_t *ret_u) { int safe_atod(const char *s, double *ret_d); +int safe_hexstring_aton(const char *s, uint8_t *b, unsigned int buf_len, unsigned int *len); + int parse_fractional_part_u(const char **s, size_t digits, unsigned *res); int parse_percent_unbounded(const char *p); diff --git a/src/libsystemd/sd-netlink/generic-netlink.c b/src/libsystemd/sd-netlink/generic-netlink.c index 384072e881451..473d8670a93b7 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 0dcc53be55a15..68b232b7d4224 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 8248ac0f5a502..8ffe0239cf1ec 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 #if HAVE_LINUX_FOU_H @@ -312,6 +313,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", @@ -340,6 +357,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); @@ -389,6 +407,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 = { @@ -849,11 +869,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 b84fa4762b074..a2b3087d15961 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 c95e7503069a9..2acbe858bb5a5 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 0000000000000..3214c912bcc3f --- /dev/null +++ b/src/network/netdev/macsec.c @@ -0,0 +1,915 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include +#include +#include +#include + +#include "conf-parser.h" +#include "hashmap.h" +#include "hexdecoct.h" +#include "macsec.h" +#include "missing.h" +#include "netlink-util.h" +#include "network-internal.h" +#include "networkd-address.h" +#include "networkd-manager.h" +#include "parse-util.h" +#include "sd-netlink.h" +#include "socket-util.h" +#include "string-table.h" +#include "string-util.h" +#include "util.h" + +static void macsec_receive_association_free(ReceiveAssociation *c) { + if (!c) + return; + + if (c->mac_sec && c->section) + ordered_hashmap_remove(c->mac_sec->receive_association_by_section, c); + + network_config_section_free(c->section); + + 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_association_by_section, n); + if (c) { + *ret = TAKE_PTR(c); + return 0; + } + + c = new(ReceiveAssociation, 1); + if (!c) + return -ENOMEM; + + *c = (ReceiveAssociation) { + .mac_sec = s, + .section = TAKE_PTR(n), + }; + + r = ordered_hashmap_ensure_allocated(&s->receive_association_by_section, &network_config_hash_ops); + if (r < 0) + return r; + + r = ordered_hashmap_put(s->receive_association_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->mac_sec && c->section) + ordered_hashmap_remove(c->mac_sec->rx_channel_by_section, c); + + network_config_section_free(c->section); + + free(c); +} + +DEFINE_NETWORK_SECTION_FUNCTIONS(ReceiveChannel, macsec_receive_channel_free); + +static int macsec_receive_channel_new_static(MACsec *s, const char *filename, unsigned section_line, ReceiveChannel **ret) { + _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; + _cleanup_(macsec_receive_channel_freep) ReceiveChannel *c = NULL; + int r; + + assert(s); + assert(ret); + assert(filename); + assert(section_line > 0); + + r = network_config_section_new(filename, section_line, &n); + if (r < 0) + return r; + + c = ordered_hashmap_get(s->rx_channel_by_section, n); + if (c) { + *ret = TAKE_PTR(c); + return 0; + } + + c = new(ReceiveChannel, 1); + if (!c) + return -ENOMEM; + + *c = (ReceiveChannel) { + .mac_sec = s, + .section = TAKE_PTR(n), + }; + + r = ordered_hashmap_ensure_allocated(&s->rx_channel_by_section, &network_config_hash_ops); + if (r < 0) + return r; + + r = ordered_hashmap_put(s->rx_channel_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->mac_sec && a->section) + ordered_hashmap_remove(a->mac_sec->transmit_association_by_section, a); + + network_config_section_free(a->section); + + free(a); +} + +DEFINE_NETWORK_SECTION_FUNCTIONS(TransmitAssociation, macsec_transmit_association_free); + +static int macsec_transmit_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_association_by_section, n); + if (a) { + *ret = TAKE_PTR(a); + return 0; + } + + a = new(TransmitAssociation, 1); + if (!a) + return -ENOMEM; + + *a = (TransmitAssociation) { + .mac_sec = s, + .section = TAKE_PTR(n), + }; + + r = ordered_hashmap_ensure_allocated(&s->transmit_association_by_section, &network_config_hash_ops); + if (r < 0) + return r; + + r = ordered_hashmap_put(s->transmit_association_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; + MACsec *t; + int r; + + assert(netdev); + assert(netdev->ifindex > 0); + + t = MACSEC(netdev); + + assert(t); + + 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, ReceiveChannel *c, sd_netlink_message *m) { + uint64_t sci; + MACsec *t; + int r; + + assert(netdev); + assert(m); + + t = MACSEC(netdev); + + assert(t); + assert(m); + + assert(c); + assert(c->mac); + assert(c->port > 0); + + 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"); + + memcpy(&sci, c->mac, ETH_ALEN); + memcpy(((char *) &sci) + ETH_ALEN, &c->port, sizeof(c->port)); + + r = sd_netlink_message_append_u64(m, MACSEC_RXSC_ATTR_SCI, sci); + 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, TransmitAssociation *a, sd_netlink_message *m) { + MACsec *t; + int r; + + assert(netdev); + assert(a); + assert(m); + + t = MACSEC(netdev); + + assert(t); + + 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->an); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append MACSEC_SA_ATTR_AN attribute: %m"); + + if (a->pn) { + r = sd_netlink_message_append_u32(m, MACSEC_SA_ATTR_PN, a->pn); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append MACSEC_SA_ATTR_PN attribute: %m"); + } + + if (a->key_len) { + r = sd_netlink_message_append_data(m, MACSEC_SA_ATTR_KEYID, a->key_id, MACSEC_KEYID_LEN); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append MACSEC_SA_ATTR_KEYID attribute: %m"); + + r = sd_netlink_message_append_data(m, MACSEC_SA_ATTR_KEY, &a->key, a->key_len); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append MACSEC_SA_ATTR_KEY attribute: %m"); + } + + r = sd_netlink_message_close_container(m); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append MACSEC_ATTR_SA_CONFIG attribute: %m"); + + return 0; +} + +static int macsec_receive_association_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) { + MACsec *t; + int r; + + assert(netdev); + assert(netdev->state != _NETDEV_STATE_INVALID); + + t = MACSEC(netdev); + + assert(t); + + r = sd_netlink_message_get_errno(m); + if (r == -EEXIST) + log_netdev_info(netdev, "netdev exists, using existing without changing its parameters"); + else if (r < 0) { + log_netdev_warning_errno(netdev, r, "netdev could not be add receive association configurations: %m"); + netdev_drop(netdev); + + return 1; + } + + log_netdev_debug(netdev, "MACsec add receive association configuration success"); + + return 1; +} + +static int netdev_macsec_receive_association_fill_message(NetDev *netdev, ReceiveAssociation *a, sd_netlink_message **ret) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + MACsec *t; + int r; + + assert(netdev); + assert(a); + + t = MACSEC(netdev); + + assert(t); + + 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->rx, m); + if (r < 0) + return r; + + *ret = TAKE_PTR(m); + + return 0; +} + +static int macsec_add_tx_sa_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) { + MACsec *t; + int r; + + assert(netdev); + assert(netdev->state != _NETDEV_STATE_INVALID); + + t = MACSEC(netdev); + + assert(t); + + r = sd_netlink_message_get_errno(m); + if (r == -EEXIST) + log_netdev_info(netdev, "netdev exists, using existing without changing its parameters"); + else if (r < 0) { + log_netdev_warning_errno(netdev, r, "netdev could not be add rxsc configurations: %m"); + netdev_drop(netdev); + + return 1; + } + + log_netdev_debug(netdev, "MACsec add rxsc configuration success"); + + return 1; +} + +static int netdev_macsec_add_tx_sa_fill_message(NetDev *netdev, TransmitAssociation *a, sd_netlink_message **ret) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + MACsec *t; + int r; + + assert(netdev); + assert(a); + + t = MACSEC(netdev); + + assert(t); + + r = netdev_macsec_fill_message(netdev, MACSEC_CMD_ADD_TXSA, &m); + if (r < 0) + return r; + + r = netdev_macsec_fill_message_sa(netdev, a, m); + if (r < 0) + return r; + + *ret = TAKE_PTR(m); + + return 0; +} + +static int macsec_add_rxsc_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) { + MACsec *t; + int r; + + assert(netdev); + assert(netdev->state != _NETDEV_STATE_INVALID); + + t = MACSEC(netdev); + + assert(t); + + r = sd_netlink_message_get_errno(m); + if (r == -EEXIST) + log_netdev_info(netdev, "netdev exists, using existing without changing its parameters"); + else if (r < 0) { + log_netdev_warning_errno(netdev, r, "netdev could not be add rxsc configurations: %m"); + netdev_drop(netdev); + + return 1; + } + + log_netdev_debug(netdev, "MACsec add rxsc configuration success"); + + return 1; +} + +static int netdev_macsec_add_rxsc_fill_message(NetDev *netdev, ReceiveChannel *c, sd_netlink_message **ret) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + MACsec *t; + int r; + + assert(netdev); + assert(c); + + t = MACSEC(netdev); + + assert(t); + + r = netdev_macsec_fill_message(netdev, MACSEC_CMD_ADD_RXSC, &m); + if (r < 0) + return r; + + r = netdev_macsec_fill_message_sci(netdev, c, m); + if (r < 0) + return r; + + *ret = TAKE_PTR(m); + + return 0; +} + +static int netdev_macsec_configure(NetDev *netdev, Link *link, sd_netlink_message *m) { + ReceiveAssociation *n; + TransmitAssociation *a; + ReceiveChannel *c; + Iterator i; + MACsec *s; + int r; + + assert(netdev); + + s = MACSEC(netdev); + + assert(s); + + ORDERED_HASHMAP_FOREACH(c, s->rx_channel_by_section, i) { + r = netdev_macsec_add_rxsc_fill_message(netdev, c, &m); + if (r < 0) + return r; + + r = netlink_call_async(netdev->manager->genl, NULL, m, macsec_add_rxsc_handler, + netdev_destroy_callback, netdev); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Failed to configure receive channel: %m"); + + netdev_ref(netdev); + } + + ORDERED_HASHMAP_FOREACH(a, s->transmit_association_by_section, i) { + r = netdev_macsec_add_tx_sa_fill_message(netdev, a, &m); + if (r < 0) + return r; + + r = netlink_call_async(netdev->manager->genl, NULL, m, macsec_add_tx_sa_handler, + netdev_destroy_callback, netdev); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Failed to configure secure association: %m"); + + netdev_ref(netdev); + } + + ORDERED_HASHMAP_FOREACH(n, s->receive_association_by_section, i) { + r = netdev_macsec_receive_association_fill_message(netdev, n, &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 association: %m"); + + netdev_ref(netdev); + } + + 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); + + 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 != -1) { + r = sd_netlink_message_append_u8(m, IFLA_MACSEC_ENCRYPT, v->encrypt); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_MACSEC_ENCRYPT attribute: %m"); + } + + return r; +} + +int config_parse_macsec_port( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + _cleanup_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL; + _cleanup_(macsec_receive_channel_free_or_set_invalidp) ReceiveChannel *c = NULL; + MACsec *s = userdata; + 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; + + if (b) + r = parse_ip_port(rvalue, &b->rx.port); + else + r = parse_ip_port(rvalue, &c->port); + + if (r < 0) + return 0; + + b = NULL; + c = NULL; + + 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; + + if (b) + r = config_parse_hwaddr(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &b->rx.mac, userdata); + else + r = config_parse_hwaddr(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &c->mac, userdata); + + if (r < 0) + return 0; + + c = NULL; + b = NULL; + + return 0; +} + +int config_parse_macsec_pn( + 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 r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + if (streq(section, "MACsecTransmitAssociation")) + r = macsec_transmit_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; + + if (a) + r = safe_atou32(rvalue, &a->pn); + else + r = safe_atou32(rvalue, &b->sa.pn); + + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "Failed to parse packet number. Ignoring assignment: %s", rvalue); + return 0; + } + + a = NULL; + b = NULL; + + 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_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL; + _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); + + if (streq(section, "MACsecTransmitAssociation")) + r = macsec_transmit_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; + + if (a) + r = safe_hexstring_aton(rvalue, a->key, MACSEC_MAX_KEY_LEN, &a->key_len); + else + r = safe_hexstring_aton(rvalue, b->sa.key, MACSEC_MAX_KEY_LEN, &b->sa.key_len); + + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse key. Ignoring assignment: %s", rvalue); + return 0; + } + + a = NULL; + b = NULL; + + 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; + MACsec *s = userdata; + unsigned int len; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + if (streq(section, "MACsecTransmitAssociation")) + r = macsec_transmit_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; + + if (a) + r = safe_hexstring_aton(rvalue, a->key_id, MACSEC_KEYID_LEN, &len); + else + r = safe_hexstring_aton(rvalue, b->sa.key_id, MACSEC_KEYID_LEN, &len); + + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse key id. Ignoring assignment: %s", rvalue); + return 0; + } + + a = NULL; + b = NULL; + + return 0; +} + +static int macsec_receive_channel_verify(ReceiveChannel *c) { + NetDev *netdev; + + assert(c); + assert(c->mac_sec); + + netdev = NETDEV(c->mac_sec); + + if (section_is_invalid(c->section)) + return -EINVAL; + + if (!c->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->port == 0) + return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "%s: MACsec receive channel without port configured. " + "Ignoring [MACsecReceiveChannel] section from line %u", + c->section->filename, c->section->line); + + return 0; +} + +static int macsec_transmit_association_verify(TransmitAssociation *c) { + NetDev *netdev; + + assert(c); + assert(c->mac_sec); + + netdev = NETDEV(c->mac_sec); + + if (section_is_invalid(c->section)) + return -EINVAL; + + if (c->key_len <= 0) + return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "%s: MACsec secure association without key configured. " + "Ignoring [MACsecTransmitAssociation] section from line %u", + c->section->filename, c->section->line); + + return 0; +} + +static int macsec_receive_association_verify(ReceiveAssociation *c) { + NetDev *netdev; + + assert(c); + assert(c->mac_sec); + + netdev = NETDEV(c->mac_sec); + + if (section_is_invalid(c->section)) + return -EINVAL; + + if (c->sa.key_len <= 0) + return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "%s: MACsec receive association without key configured. " + "Ignoring [MACsecReceiveAssociation] section from line %u", + c->section->filename, c->section->line); + + if (!c->rx.mac) + return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "%s: MACsec receive association without MAC address configured. " + "Ignoring [MACsecReceiveAssociation] section from line %u", + c->section->filename, c->section->line); + + if (c->rx.port == 0) + return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "%s: MACsec receive association without port configured. " + "Ignoring [MACsecReceiveAssociation] section from line %u", + c->section->filename, c->section->line); + + return 0; +} + +static int netdev_macsec_verify(NetDev *netdev, const char *filename) { + MACsec *v = MACSEC(netdev); + TransmitAssociation *a; + ReceiveAssociation *n; + ReceiveChannel *c; + Iterator i; + int r; + + assert(netdev); + assert(v); + assert(filename); + + if (v->port == 0) + return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "Invalid MACsec port value '0' configured in '%s'. " + "Ignoring [MACSEC] section from line %u", + c->section->filename, c->section->line); + + ORDERED_HASHMAP_FOREACH(c, v->rx_channel_by_section, i) { + r = macsec_receive_channel_verify(c); + if (r < 0) + macsec_receive_channel_free(c); + } + + ORDERED_HASHMAP_FOREACH(a, v->transmit_association_by_section, i) { + r = macsec_transmit_association_verify(a); + if (r < 0) + macsec_transmit_association_free(a); + } + + ORDERED_HASHMAP_FOREACH(n, v->receive_association_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->rx_channel_by_section, macsec_receive_channel_free); + ordered_hashmap_free_with_destructor(t->transmit_association_by_section, macsec_transmit_association_free); + ordered_hashmap_free_with_destructor(t->receive_association_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 0000000000000..8e98708861cf8 --- /dev/null +++ b/src/network/netdev/macsec.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include + +#include "in-addr-util.h" +#include "netdev.h" +#include "networkd-util.h" + +typedef struct MACsec MACsec; + +typedef struct ReceiveChannel { + MACsec *mac_sec; + NetworkConfigSection *section; + + uint16_t port; + struct ether_addr *mac; +} ReceiveChannel; + +typedef struct TransmitAssociation { + MACsec *mac_sec; + NetworkConfigSection *section; + + uint8_t an; + uint8_t active; + + uint32_t pn; + uint32_t key_len; + + uint8_t key_id[MACSEC_KEYID_LEN]; + uint8_t key[MACSEC_MAX_KEY_LEN]; + +} TransmitAssociation; + +typedef struct ReceiveAssociation { + MACsec *mac_sec; + NetworkConfigSection *section; + + TransmitAssociation sa; + ReceiveChannel rx; + +} ReceiveAssociation; + +struct MACsec { + NetDev meta; + + uint16_t port; + int encrypt; + + OrderedHashmap *rx_channel_by_section; + OrderedHashmap *transmit_association_by_section; + OrderedHashmap *receive_association_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_pn); +CONFIG_PARSER_PROTOTYPE(config_parse_macsec_key_id); +CONFIG_PARSER_PROTOTYPE(config_parse_macsec_key); diff --git a/src/network/netdev/netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf index fcd2ec2097bab..936f51bd5fb6c 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,157 +35,169 @@ 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.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_ip_port, 0, offsetof(MACsec, port) +MACSEC.Encrypt, config_parse_bool, 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_pn, 0, 0 +MACsecTransmitAssociation.KeyId, config_parse_macsec_key_id, 0, 0 +MACsecTransmitAssociation.Key, config_parse_macsec_key, 0, 0 +MACsecReceiveAssociation.Port, config_parse_macsec_port, 0, 0 +MACsecReceiveAssociation.MACAddress, config_parse_macsec_hw_address, 0, 0 +MACsecReceiveAssociation.PacketNumber, config_parse_macsec_pn, 0, 0 +MACsecReceiveAssociation.KeyId, config_parse_macsec_key_id, 0, 0 +MACsecReceiveAssociation.Key, config_parse_macsec_key, 0, 0 +Tun.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue) +Tun.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue) +Tun.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info) +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.PersistentKeepalive, config_parse_wireguard_keepalive, 0, 0 diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c index c1bcfc66e6618..494ac788f89e5 100644 --- a/src/network/netdev/netdev.c +++ b/src/network/netdev/netdev.c @@ -15,6 +15,7 @@ #include "netdev/ipvlan.h" #include "netdev/l2tp-tunnel.h" #include "netdev/macvlan.h" +#include "netdev/macsec.h" #include "netdev/netdev.h" #include "netdev/netdevsim.h" #include "netdev/tunnel.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 ad4dd2e2b0d50..29ecead029b8b 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 47a9f7d808a6c..d559ccf9e06bd 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -46,6 +46,7 @@ Network.MACVTAP, config_parse_stacked_netdev, Network.IPVLAN, config_parse_stacked_netdev, NETDEV_KIND_IPVLAN, offsetof(Network, stacked_netdev_names) Network.VXLAN, config_parse_stacked_netdev, NETDEV_KIND_VXLAN, offsetof(Network, stacked_netdev_names) Network.L2TP, config_parse_stacked_netdev, NETDEV_KIND_L2TP, offsetof(Network, stacked_netdev_names) +Network.MACSEC, config_parse_stacked_netdev, NETDEV_KIND_MACSEC, offsetof(Network, stacked_netdev_names) Network.Tunnel, config_parse_stacked_netdev, _NETDEV_KIND_TUNNEL, offsetof(Network, stacked_netdev_names) Network.VRF, config_parse_ifname, 0, offsetof(Network, vrf_name) Network.DHCP, config_parse_dhcp, 0, offsetof(Network, dhcp) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 836776ae8496c..045b12e2d92cd 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -689,7 +689,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 804fe9f03ecd8..d327b27308b68 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 e0756dc755485..aed0610b5da52 100644 --- a/test/fuzz/fuzz-netdev-parser/directives.netdev +++ b/test/fuzz/fuzz-netdev-parser/directives.netdev @@ -173,3 +173,18 @@ SessionId= PeerSessionId= Layer2SpecificHeader= Name= +[MACSEC] +Port=11 +[MACsecReceiveAssociation] +Port= +MACAddress= +PacketNumber= +KeyId= +Key= +[MACsecReceiveChannel] +Port= +MACAddress= +[MACsecTransmitAssociation] +PacketNumber= +KeyId= +Key=