network: Introduce MACsec

Media Access Control Security (MACsec) is an 802.1AE IEEE
industry-standard security technology that provides secure
communication for all traffic on Ethernet links.
MACsec provides point-to-point security on Ethernet links between
directly connected nodes and is capable of identifying and preventing
most security threats, including denial of service, intrusion,
man-in-the-middle, masquerading, passive wiretapping, and playback attacks.

Closes #5754
This commit is contained in:
Susant Sahani 2019-04-03 16:57:36 +05:30 committed by Yu Watanabe
parent 45cae4abfb
commit 81962db798
17 changed files with 1248 additions and 1 deletions

View file

@ -151,6 +151,9 @@
<row><entry><varname>l2tp</varname></entry>
<entry>A Layer 2 Tunneling Protocol (L2TP) is a tunneling protocol used to support virtual private networks (VPNs) or as part of the delivery of services by ISPs. It does not provide any encryption or confidentiality by itself</entry></row>
<row><entry><varname>macsec</varname></entry>
<entry>Media Access Control Security (MACsec) is an 802.1AE IEEE industry-standard security technology that provides secure communication for all traffic on Ethernet links. MACsec provides point-to-point security on Ethernet links between directly connected nodes and is capable of identifying and preventing most security threats.</entry></row>
<row><entry><varname>vrf</varname></entry>
<entry>A Virtual Routing and Forwarding (<ulink url="https://www.kernel.org/doc/Documentation/networking/vrf.txt">VRF</ulink>) interface to create separate routing and forwarding domains.</entry></row>
@ -851,6 +854,124 @@
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>[MACsec] Section Options</title>
<para>The <literal>[MACsec]</literal> section only applies for network devices of kind
<literal>macsec</literal>, and accepts the following keys:</para>
<variablelist class='network-directives'>
<varlistentry>
<term><varname>Port=</varname></term>
<listitem>
<para>Specifies the port to be used for the MACsec transmit channel. The port is used to make
secure channel identifier (SCI). Takes a value between 1 and 65535. Defaults to unset.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Encrypt=</varname></term>
<listitem>
<para>Takes a boolean. When true, enable encryption. Defaults to unset.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>[MACsecReceiveChannel] Section Options</title>
<para>The <literal>[MACsecReceiveChannel]</literal> section only applies for network devices of
kind <literal>macsec</literal>, and accepts the following keys:</para>
<variablelist class='network-directives'>
<varlistentry>
<term><varname>Port=</varname></term>
<listitem>
<para>Specifies the port to be used for the MACsec receive channel. The port is used to make
secure channel identifier (SCI). Takes a value between 1 and 65535. This option is
compulsory, and is not set by default.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>MACAddress=</varname></term>
<listitem>
<para>Specifies the MAC address to be used for the MACsec receive channel. The MAC address
used to make secure channel identifier (SCI). This option is compulsory, and is not set by
default.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>[MACsecTransmitAssociation] Section Options</title>
<para>The <literal>[MACsecTransmitAssociation]</literal> section only applies for network devices
of kind <literal>macsec</literal>, and accepts the following keys:</para>
<variablelist class='network-directives'>
<varlistentry>
<term><varname>PacketNumber=</varname></term>
<listitem>
<para>Specifies the packet number to be used for replay protection and the construction of
the initialization vector (along with the secure channel identifier [SCI]). Takes a value
between 1-4,294,967,295. Defaults to unset.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>KeyId=</varname></term>
<listitem>
<para>Specifies the identification for the key. Takes a number between 0-255. This option
is compulsory, and is not set by default.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Key=</varname></term>
<listitem>
<para>Specifies the encryption key used in the transmission channel. The same key must be
configured on the peers matching receive channel. This option is compulsory, and is not set
by default. Takes a 128-bit key encoded in a hexadecimal string, for example
<literal>dffafc8d7b9a43d5b9a3dfbbf6a30c16</literal>.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>[MACsecReceiveAssociation] Section Options</title>
<para>The <literal>[MACsecReceiveAssociation]</literal> section only applies for
network devices of kind <literal>macsec</literal>, and accepts the
following keys:</para>
<variablelist class='network-directives'>
<varlistentry>
<term><varname>Port=</varname></term>
<listitem>
<para>Accepts the same key in <literal>[MACsecReceiveChannel]</literal> section.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>MACAddress=</varname></term>
<listitem>
<para>Accepts the same key in <literal>[MACsecReceiveChannel]</literal> section.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>PacketNumber=</varname></term>
<listitem>
<para>Accepts the same key in <literal>[MACsecTransmitAssociation]</literal> section.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>KeyId=</varname></term>
<listitem>
<para>Accepts the same key in <literal>[MACsecTransmitAssociation]</literal> section.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Key=</varname></term>
<listitem>
<para>Accepts the same key in <literal>[MACsecTransmitAssociation]</literal> section.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>[Tunnel] Section Options</title>

View file

@ -768,6 +768,14 @@
This option may be specified more than once.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>MACsec=</varname></term>
<listitem>
<para>The name of a MACsec device to create on the link. See
<citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
This option may be specified more than once.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>ActiveSlave=</varname></term>
<listitem>

View file

@ -14,6 +14,7 @@ static const genl_family genl_families[] = {
[SD_GENL_WIREGUARD] = { .name = "wireguard", .version = 1 },
[SD_GENL_FOU] = { .name = "fou", .version = 1 },
[SD_GENL_L2TP] = { .name = "l2tp", .version = 1},
[SD_GENL_MACSEC] = { .name = "macsec", .version = 1},
};
int sd_genl_socket_open(sd_netlink **ret) {

View file

@ -318,6 +318,23 @@ int sd_netlink_message_append_u32(sd_netlink_message *m, unsigned short type, ui
return 0;
}
int sd_netlink_message_append_u64(sd_netlink_message *m, unsigned short type, uint64_t data) {
int r;
assert_return(m, -EINVAL);
assert_return(!m->sealed, -EPERM);
r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U64);
if (r < 0)
return r;
r = add_rtattr(m, type, &data, sizeof(uint64_t));
if (r < 0)
return r;
return 0;
}
int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, const void *data, size_t len) {
int r;

View file

@ -16,6 +16,7 @@
#include <linux/if_addrlabel.h>
#include <linux/if_bridge.h>
#include <linux/if_link.h>
#include <linux/if_macsec.h>
#include <linux/if_tunnel.h>
#include <linux/l2tp.h>
#include <linux/veth.h>
@ -306,6 +307,22 @@ static const NLType rtnl_link_info_data_can_types[] = {
[IFLA_CAN_CTRLMODE] = { .size = sizeof(struct can_ctrlmode) },
};
static const NLType rtnl_link_info_data_macsec_types[] = {
[IFLA_MACSEC_SCI] = { .type = NETLINK_TYPE_U64 },
[IFLA_MACSEC_PORT] = { .type = NETLINK_TYPE_U16 },
[IFLA_MACSEC_ICV_LEN] = { .type = NETLINK_TYPE_U8 },
[IFLA_MACSEC_CIPHER_SUITE] = { .type = NETLINK_TYPE_U64 },
[IFLA_MACSEC_WINDOW] = { .type = NETLINK_TYPE_U32 },
[IFLA_MACSEC_ENCODING_SA] = { .type = NETLINK_TYPE_U8 },
[IFLA_MACSEC_ENCRYPT] = { .type = NETLINK_TYPE_U8 },
[IFLA_MACSEC_PROTECT] = { .type = NETLINK_TYPE_U8 },
[IFLA_MACSEC_INC_SCI] = { .type = NETLINK_TYPE_U8 },
[IFLA_MACSEC_ES] = { .type = NETLINK_TYPE_U8 },
[IFLA_MACSEC_SCB] = { .type = NETLINK_TYPE_U8 },
[IFLA_MACSEC_REPLAY_PROTECT] = { .type = NETLINK_TYPE_U8 },
[IFLA_MACSEC_VALIDATION] = { .type = NETLINK_TYPE_U8 },
};
/* these strings must match the .kind entries in the kernel */
static const char* const nl_union_link_info_data_table[] = {
[NL_UNION_LINK_INFO_DATA_BOND] = "bond",
@ -334,6 +351,7 @@ static const char* const nl_union_link_info_data_table[] = {
[NL_UNION_LINK_INFO_DATA_WIREGUARD] = "wireguard",
[NL_UNION_LINK_INFO_DATA_NETDEVSIM] = "netdevsim",
[NL_UNION_LINK_INFO_DATA_CAN] = "can",
[NL_UNION_LINK_INFO_DATA_MACSEC] = "macsec",
};
DEFINE_STRING_TABLE_LOOKUP(nl_union_link_info_data, NLUnionLinkInfoData);
@ -383,6 +401,8 @@ static const NLTypeSystem rtnl_link_info_data_type_systems[] = {
.types = rtnl_link_info_data_vxcan_types },
[NL_UNION_LINK_INFO_DATA_CAN] = { .count = ELEMENTSOF(rtnl_link_info_data_can_types),
.types = rtnl_link_info_data_can_types },
[NL_UNION_LINK_INFO_DATA_MACSEC] = { .count = ELEMENTSOF(rtnl_link_info_data_macsec_types),
.types = rtnl_link_info_data_macsec_types },
};
static const NLTypeSystemUnion rtnl_link_info_data_type_system_union = {
@ -843,11 +863,76 @@ static const NLTypeSystem genl_l2tp_tunnel_session_type_system = {
.types = genl_l2tp,
};
static const NLType genl_rxsc_types[] = {
[MACSEC_RXSC_ATTR_SCI] = { .type = NETLINK_TYPE_U64 },
};
static const NLTypeSystem genl_rxsc_config_type_system = {
.count = ELEMENTSOF(genl_rxsc_types),
.types = genl_rxsc_types,
};
static const NLType genl_macsec_rxsc_types[] = {
[MACSEC_ATTR_IFINDEX] = { .type = NETLINK_TYPE_U32 },
[MACSEC_ATTR_RXSC_CONFIG] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_rxsc_config_type_system },
};
static const NLTypeSystem genl_macsec_rxsc_type_system = {
.count = ELEMENTSOF(genl_macsec_rxsc_types),
.types = genl_macsec_rxsc_types,
};
static const NLType genl_macsec_sa_config_types[] = {
[MACSEC_SA_ATTR_AN] = { .type = NETLINK_TYPE_U8 },
[MACSEC_SA_ATTR_ACTIVE] = { .type = NETLINK_TYPE_U8 },
[MACSEC_SA_ATTR_PN] = { .type = NETLINK_TYPE_U32 },
[MACSEC_SA_ATTR_KEYID] = { .size = MACSEC_KEYID_LEN },
[MACSEC_SA_ATTR_KEY] = { .size = MACSEC_MAX_KEY_LEN },
};
static const NLTypeSystem genl_macsec_sa_config_type_system = {
.count = ELEMENTSOF(genl_macsec_sa_config_types),
.types = genl_macsec_sa_config_types,
};
static const NLType genl_macsec_rxsa_types[] = {
[MACSEC_ATTR_IFINDEX] = { .type = NETLINK_TYPE_U32 },
[MACSEC_ATTR_SA_CONFIG] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_sa_config_type_system },
};
static const NLTypeSystem genl_macsec_rxsa_type_system = {
.count = ELEMENTSOF(genl_macsec_rxsa_types),
.types = genl_macsec_rxsa_types,
};
static const NLType genl_macsec_sa_types[] = {
[MACSEC_ATTR_IFINDEX] = { .type = NETLINK_TYPE_U32 },
[MACSEC_ATTR_RXSC_CONFIG] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_rxsc_config_type_system },
[MACSEC_ATTR_SA_CONFIG] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_sa_config_type_system },
};
static const NLTypeSystem genl_macsec_sa_type_system = {
.count = ELEMENTSOF(genl_macsec_sa_types),
.types = genl_macsec_sa_types,
};
static const NLType genl_macsec[] = {
[MACSEC_CMD_ADD_RXSC] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_rxsc_type_system },
[MACSEC_CMD_ADD_TXSA] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_rxsa_type_system},
[MACSEC_CMD_ADD_RXSA] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_sa_type_system },
};
static const NLTypeSystem genl_macsec_device_type_system = {
.count = ELEMENTSOF(genl_macsec),
.types = genl_macsec,
};
static const NLType genl_families[] = {
[SD_GENL_ID_CTRL] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_ctrl_id_ctrl_type_system },
[SD_GENL_WIREGUARD] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_type_system },
[SD_GENL_FOU] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_fou_cmds_type_system},
[SD_GENL_L2TP] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_l2tp_tunnel_session_type_system },
[SD_GENL_MACSEC] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_device_type_system },
};
const NLTypeSystem genl_family_type_system_root = {

View file

@ -80,6 +80,7 @@ typedef enum NLUnionLinkInfoData {
NL_UNION_LINK_INFO_DATA_WIREGUARD,
NL_UNION_LINK_INFO_DATA_NETDEVSIM,
NL_UNION_LINK_INFO_DATA_CAN,
NL_UNION_LINK_INFO_DATA_MACSEC,
_NL_UNION_LINK_INFO_DATA_MAX,
_NL_UNION_LINK_INFO_DATA_INVALID = -1
} NLUnionLinkInfoData;

View file

@ -39,6 +39,8 @@ sources = files('''
netdev/fou-tunnel.h
netdev/l2tp-tunnel.c
netdev/l2tp-tunnel.h
netdev/macsec.c
netdev/macsec.h
networkd-address-label.c
networkd-address-label.h
networkd-address-pool.c

903
src/network/netdev/macsec.c Normal file
View file

@ -0,0 +1,903 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include <arpa/inet.h>
#include <linux/if_ether.h>
#include <linux/if_macsec.h>
#include <linux/genetlink.h>
#include "conf-parser.h"
#include "hashmap.h"
#include "hexdecoct.h"
#include "macsec.h"
#include "memory-util.h"
#include "missing.h"
#include "netlink-util.h"
#include "network-internal.h"
#include "networkd-address.h"
#include "networkd-manager.h"
#include "sd-netlink.h"
#include "socket-util.h"
#include "string-table.h"
#include "string-util.h"
#include "util.h"
static void macsec_receive_association_free(ReceiveAssociation *c) {
if (!c)
return;
if (c->macsec && c->section)
ordered_hashmap_remove(c->macsec->receive_associations_by_section, c->section);
network_config_section_free(c->section);
free(c->sa.key);
free(c);
}
DEFINE_NETWORK_SECTION_FUNCTIONS(ReceiveAssociation, macsec_receive_association_free);
static int macsec_receive_association_new_static(MACsec *s, const char *filename, unsigned section_line, ReceiveAssociation **ret) {
_cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
_cleanup_(macsec_receive_association_freep) ReceiveAssociation *c = NULL;
int r;
assert(s);
assert(ret);
assert(filename);
assert(section_line > 0);
r = network_config_section_new(filename, section_line, &n);
if (r < 0)
return r;
c = ordered_hashmap_get(s->receive_associations_by_section, n);
if (c) {
*ret = TAKE_PTR(c);
return 0;
}
c = new(ReceiveAssociation, 1);
if (!c)
return -ENOMEM;
*c = (ReceiveAssociation) {
.macsec = s,
.section = TAKE_PTR(n),
};
r = ordered_hashmap_ensure_allocated(&s->receive_associations_by_section, &network_config_hash_ops);
if (r < 0)
return r;
r = ordered_hashmap_put(s->receive_associations_by_section, c->section, c);
if (r < 0)
return r;
*ret = TAKE_PTR(c);
return 0;
}
static void macsec_receive_channel_free(ReceiveChannel *c) {
if (!c)
return;
if (c->macsec && c->section)
ordered_hashmap_remove(c->macsec->receive_channels_by_section, c->section);
network_config_section_free(c->section);
free(c);
}
DEFINE_NETWORK_SECTION_FUNCTIONS(ReceiveChannel, macsec_receive_channel_free);
static int macsec_receive_channel_new_static(MACsec *s, const char *filename, unsigned section_line, ReceiveChannel **ret) {
_cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
_cleanup_(macsec_receive_channel_freep) ReceiveChannel *c = NULL;
int r;
assert(s);
assert(ret);
assert(filename);
assert(section_line > 0);
r = network_config_section_new(filename, section_line, &n);
if (r < 0)
return r;
c = ordered_hashmap_get(s->receive_channels_by_section, n);
if (c) {
*ret = TAKE_PTR(c);
return 0;
}
c = new(ReceiveChannel, 1);
if (!c)
return -ENOMEM;
*c = (ReceiveChannel) {
.macsec = s,
.section = TAKE_PTR(n),
};
r = ordered_hashmap_ensure_allocated(&s->receive_channels_by_section, &network_config_hash_ops);
if (r < 0)
return r;
r = ordered_hashmap_put(s->receive_channels_by_section, c->section, c);
if (r < 0)
return r;
*ret = TAKE_PTR(c);
return 0;
}
static void macsec_transmit_association_free(TransmitAssociation *a) {
if (!a)
return;
if (a->macsec && a->section)
ordered_hashmap_remove(a->macsec->transmit_associations_by_section, a->section);
network_config_section_free(a->section);
free(a->sa.key);
free(a);
}
DEFINE_NETWORK_SECTION_FUNCTIONS(TransmitAssociation, macsec_transmit_association_free);
static int macsec_transmit_association_new_static(MACsec *s, const char *filename, unsigned section_line, TransmitAssociation **ret) {
_cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
_cleanup_(macsec_transmit_association_freep) TransmitAssociation *a = NULL;
int r;
assert(s);
assert(ret);
assert(filename);
assert(section_line > 0);
r = network_config_section_new(filename, section_line, &n);
if (r < 0)
return r;
a = ordered_hashmap_get(s->transmit_associations_by_section, n);
if (a) {
*ret = TAKE_PTR(a);
return 0;
}
a = new(TransmitAssociation, 1);
if (!a)
return -ENOMEM;
*a = (TransmitAssociation) {
.macsec = s,
.section = TAKE_PTR(n),
};
r = ordered_hashmap_ensure_allocated(&s->transmit_associations_by_section, &network_config_hash_ops);
if (r < 0)
return r;
r = ordered_hashmap_put(s->transmit_associations_by_section, a->section, a);
if (r < 0)
return r;
*ret = TAKE_PTR(a);
return 0;
}
static int netdev_macsec_fill_message(NetDev *netdev, int command, sd_netlink_message **ret) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
assert(netdev);
assert(netdev->ifindex > 0);
r = sd_genl_message_new(netdev->manager->genl, SD_GENL_MACSEC, command, &m);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Failed to create generic netlink message: %m");
r = sd_netlink_message_append_u32(m, MACSEC_ATTR_IFINDEX, netdev->ifindex);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append MACSEC_ATTR_IFINDEX attribute: %m");
*ret = TAKE_PTR(m);
return 0;
}
static int netdev_macsec_fill_message_sci(NetDev *netdev, MACsecSCI *sci, sd_netlink_message *m) {
int r;
assert(netdev);
assert(m);
assert(sci);
r = sd_netlink_message_open_container(m, MACSEC_ATTR_RXSC_CONFIG);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append MACSEC_ATTR_RXSC_CONFIG attribute: %m");
r = sd_netlink_message_append_u64(m, MACSEC_RXSC_ATTR_SCI, sci->as_uint64);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append MACSEC_RXSC_ATTR_SCI attribute: %m");
r = sd_netlink_message_close_container(m);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append MACSEC_ATTR_RXSC_CONFIG attribute: %m");
return 0;
}
static int netdev_macsec_fill_message_sa(NetDev *netdev, SecurityAssociation *a, sd_netlink_message *m) {
int r;
assert(netdev);
assert(a);
assert(m);
r = sd_netlink_message_open_container(m, MACSEC_ATTR_SA_CONFIG);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append MACSEC_ATTR_SA_CONFIG attribute: %m");
r = sd_netlink_message_append_u8(m, MACSEC_SA_ATTR_AN, a->association_number);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append MACSEC_SA_ATTR_AN attribute: %m");
if (a->packet_number > 0) {
r = sd_netlink_message_append_u32(m, MACSEC_SA_ATTR_PN, a->packet_number);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append MACSEC_SA_ATTR_PN attribute: %m");
}
if (a->key_len > 0) {
r = sd_netlink_message_append_data(m, MACSEC_SA_ATTR_KEYID, a->key_id, MACSEC_KEYID_LEN);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append MACSEC_SA_ATTR_KEYID attribute: %m");
r = sd_netlink_message_append_data(m, MACSEC_SA_ATTR_KEY, a->key, a->key_len);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append MACSEC_SA_ATTR_KEY attribute: %m");
}
r = sd_netlink_message_close_container(m);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append MACSEC_ATTR_SA_CONFIG attribute: %m");
return 0;
}
static int macsec_receive_association_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) {
int r;
assert(netdev);
assert(netdev->state != _NETDEV_STATE_INVALID);
r = sd_netlink_message_get_errno(m);
if (r == -EEXIST)
log_netdev_info(netdev,
"MACsec receive secure association exists, "
"using existing without changing its parameters");
else if (r < 0) {
log_netdev_warning_errno(netdev, r,
"Failed to add receive secure association: %m");
netdev_drop(netdev);
return 1;
}
log_netdev_debug(netdev, "Receive secure association is configured");
return 1;
}
static int netdev_macsec_configure_receive_association(NetDev *netdev, ReceiveAssociation *a) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
assert(netdev);
assert(a);
r = netdev_macsec_fill_message(netdev, MACSEC_CMD_ADD_RXSA, &m);
if (r < 0)
return r;
r = netdev_macsec_fill_message_sa(netdev, &a->sa, m);
if (r < 0)
return r;
r = netdev_macsec_fill_message_sci(netdev, &a->sci, m);
if (r < 0)
return r;
r = netlink_call_async(netdev->manager->genl, NULL, m, macsec_receive_association_handler,
netdev_destroy_callback, netdev);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Failed to configure receive secure association: %m");
netdev_ref(netdev);
return 0;
}
static int macsec_receive_channel_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) {
int r;
assert(netdev);
assert(netdev->state != _NETDEV_STATE_INVALID);
r = sd_netlink_message_get_errno(m);
if (r == -EEXIST)
log_netdev_debug(netdev,
"MACsec receive channel exists, "
"using existing without changing its parameters");
else if (r < 0) {
log_netdev_warning_errno(netdev, r,
"Failed to add receive secure channel: %m");
netdev_drop(netdev);
return 1;
}
log_netdev_debug(netdev, "Receive channel is configured");
return 1;
}
static int netdev_macsec_configure_receive_channel(NetDev *netdev, ReceiveChannel *c) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
assert(netdev);
assert(c);
r = netdev_macsec_fill_message(netdev, MACSEC_CMD_ADD_RXSC, &m);
if (r < 0)
return r;
r = netdev_macsec_fill_message_sci(netdev, &c->sci, m);
if (r < 0)
return r;
r = netlink_call_async(netdev->manager->genl, NULL, m, macsec_receive_channel_handler,
netdev_destroy_callback, netdev);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Failed to configure receive channel: %m");
netdev_ref(netdev);
return 0;
}
static int macsec_transmit_association_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) {
int r;
assert(netdev);
assert(netdev->state != _NETDEV_STATE_INVALID);
r = sd_netlink_message_get_errno(m);
if (r == -EEXIST)
log_netdev_info(netdev,
"MACsec transmit secure association exists, "
"using existing without changing its parameters");
else if (r < 0) {
log_netdev_warning_errno(netdev, r,
"Failed to add transmit secure association: %m");
netdev_drop(netdev);
return 1;
}
log_netdev_debug(netdev, "Transmit secure association is configured");
return 1;
}
static int netdev_macsec_configure_transmit_association(NetDev *netdev, TransmitAssociation *a) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
assert(netdev);
assert(a);
r = netdev_macsec_fill_message(netdev, MACSEC_CMD_ADD_TXSA, &m);
if (r < 0)
return r;
r = netdev_macsec_fill_message_sa(netdev, &a->sa, m);
if (r < 0)
return r;
r = netlink_call_async(netdev->manager->genl, NULL, m, macsec_transmit_association_handler,
netdev_destroy_callback, netdev);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Failed to configure transmit secure association: %m");
netdev_ref(netdev);
return 0;
}
static int netdev_macsec_configure(NetDev *netdev, Link *link, sd_netlink_message *m) {
ReceiveAssociation *n;
TransmitAssociation *a;
ReceiveChannel *c;
Iterator i;
MACsec *s;
int r;
assert(netdev);
s = MACSEC(netdev);
assert(s);
ORDERED_HASHMAP_FOREACH(a, s->transmit_associations_by_section, i) {
r = netdev_macsec_configure_transmit_association(netdev, a);
if (r < 0)
return r;
}
ORDERED_HASHMAP_FOREACH(c, s->receive_channels_by_section, i) {
r = netdev_macsec_configure_receive_channel(netdev, c);
if (r < 0)
return r;
}
ORDERED_HASHMAP_FOREACH(n, s->receive_associations_by_section, i) {
r = netdev_macsec_configure_receive_association(netdev, n);
if (r < 0)
return r;
}
return 0;
}
static int netdev_macsec_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
MACsec *v;
int r;
assert(netdev);
assert(m);
v = MACSEC(netdev);
if (v->port > 0) {
r = sd_netlink_message_append_u16(m, IFLA_MACSEC_PORT, v->port);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_MACSEC_PORT attribute: %m");
}
if (v->encrypt >= 0) {
r = sd_netlink_message_append_u8(m, IFLA_MACSEC_ENCRYPT, v->encrypt);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_MACSEC_ENCRYPT attribute: %m");
}
return r;
}
int config_parse_macsec_port(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL;
_cleanup_(macsec_receive_channel_free_or_set_invalidp) ReceiveChannel *c = NULL;
MACsec *s = userdata;
uint16_t port;
be16_t *dest;
int r;
assert(filename);
assert(section);
assert(lvalue);
assert(rvalue);
assert(data);
/* This parses port used to make Secure Channel Identifier (SCI) */
if (streq(section, "MACsec"))
dest = &s->port;
else if (streq(section, "MACsecReceiveChannel")) {
r = macsec_receive_channel_new_static(s, filename, section_line, &c);
if (r < 0)
return r;
dest = &c->sci.port;
} else {
assert(streq(section, "MACsecReceiveAssociation"));
r = macsec_receive_association_new_static(s, filename, section_line, &b);
if (r < 0)
return r;
dest = &b->sci.port;
}
r = parse_ip_port(rvalue, &port);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse port '%s' for secure channel identifier. Ignoring assignment: %m",
rvalue);
return 0;
}
*dest = htobe16(port);
TAKE_PTR(b);
TAKE_PTR(c);
return 0;
}
int config_parse_macsec_hw_address(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL;
_cleanup_(macsec_receive_channel_free_or_set_invalidp) ReceiveChannel *c = NULL;
MACsec *s = userdata;
int r;
assert(filename);
assert(section);
assert(lvalue);
assert(rvalue);
assert(data);
if (streq(section, "MACsecReceiveChannel"))
r = macsec_receive_channel_new_static(s, filename, section_line, &c);
else
r = macsec_receive_association_new_static(s, filename, section_line, &b);
if (r < 0)
return r;
r = ether_addr_from_string(rvalue, b ? &b->sci.mac : &c->sci.mac);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse MAC address for secure channel identifier. "
"Ignoring assignment: %s", rvalue);
return 0;
}
TAKE_PTR(b);
TAKE_PTR(c);
return 0;
}
int config_parse_macsec_packet_number(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_(macsec_transmit_association_free_or_set_invalidp) TransmitAssociation *a = NULL;
_cleanup_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL;
MACsec *s = userdata;
uint32_t val, *dest;
int r;
assert(filename);
assert(section);
assert(lvalue);
assert(rvalue);
assert(data);
if (streq(section, "MACsecTransmitAssociation"))
r = macsec_transmit_association_new_static(s, filename, section_line, &a);
else
r = macsec_receive_association_new_static(s, filename, section_line, &b);
if (r < 0)
return r;
dest = a ? &a->sa.packet_number : &b->sa.packet_number;
r = safe_atou32(rvalue, &val);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse packet number. Ignoring assignment: %s", rvalue);
return 0;
}
if (streq(section, "MACsecTransmitAssociation") && val == 0) {
log_syntax(unit, LOG_ERR, filename, line, 0,
"Invalid packet number. Ignoring assignment: %s", rvalue);
return 0;
}
*dest = val;
TAKE_PTR(a);
TAKE_PTR(b);
return 0;
}
int config_parse_macsec_key(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_(macsec_transmit_association_free_or_set_invalidp) TransmitAssociation *a = NULL;
_cleanup_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL;
_cleanup_free_ void *p;
MACsec *s = userdata;
SecurityAssociation *dest;
size_t l;
int r;
assert(filename);
assert(section);
assert(lvalue);
assert(rvalue);
assert(data);
if (streq(section, "MACsecTransmitAssociation"))
r = macsec_transmit_association_new_static(s, filename, section_line, &a);
else
r = macsec_receive_association_new_static(s, filename, section_line, &b);
if (r < 0)
return r;
dest = a ? &a->sa : &b->sa;
r = unhexmem(rvalue, strlen(rvalue), &p, &l);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse key. Ignoring assignment: %m");
return 0;
}
if (l != 16) {
/* See DEFAULT_SAK_LEN in drivers/net/macsec.c */
log_syntax(unit, LOG_ERR, filename, line, 0,
"Invalid key length (%zu). Ignoring assignment", l);
return 0;
}
free_and_replace(dest->key, p);
dest->key_len = l;
TAKE_PTR(a);
TAKE_PTR(b);
return 0;
}
int config_parse_macsec_key_id(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_(macsec_transmit_association_free_or_set_invalidp) TransmitAssociation *a = NULL;
_cleanup_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL;
_cleanup_free_ void *p;
MACsec *s = userdata;
uint8_t *dest;
size_t l;
int r;
assert(filename);
assert(section);
assert(lvalue);
assert(rvalue);
assert(data);
if (streq(section, "MACsecTransmitAssociation"))
r = macsec_transmit_association_new_static(s, filename, section_line, &a);
else
r = macsec_receive_association_new_static(s, filename, section_line, &b);
if (r < 0)
return r;
r = unhexmem(rvalue, strlen(rvalue), &p, &l);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse key id. Ignoring assignment: %s", rvalue);
return 0;
}
if (l > MACSEC_KEYID_LEN) {
log_syntax(unit, LOG_ERR, filename, line, 0,
"The size of key id is too large (%zu), maximum of %zu permitted. "
"Ignoring assignment: %s", l, (size_t) MACSEC_KEYID_LEN, rvalue);
return 0;
}
dest = a ? a->sa.key_id : b->sa.key_id;
memcpy_safe(dest, p, l);
memzero(dest + l, MACSEC_KEYID_LEN - l);
TAKE_PTR(a);
TAKE_PTR(b);
return 0;
}
static int macsec_receive_channel_verify(ReceiveChannel *c) {
NetDev *netdev;
assert(c);
assert(c->macsec);
netdev = NETDEV(c->macsec);
if (section_is_invalid(c->section))
return -EINVAL;
if (ether_addr_is_null(&c->sci.mac))
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
"%s: MACsec receive channel without MAC address configured. "
"Ignoring [MACsecReceiveChannel] section from line %u",
c->section->filename, c->section->line);
if (c->sci.port == 0)
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
"%s: MACsec receive channel without port configured. "
"Ignoring [MACsecReceiveChannel] section from line %u",
c->section->filename, c->section->line);
return 0;
}
static int macsec_transmit_association_verify(TransmitAssociation *t) {
NetDev *netdev;
assert(t);
assert(t->macsec);
netdev = NETDEV(t->macsec);
if (section_is_invalid(t->section))
return -EINVAL;
if (t->sa.packet_number == 0)
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
"%s: MACsec transmit secure association without PacketNumber= configured. "
"Ignoring [MACsecTransmitAssociation] section from line %u",
t->section->filename, t->section->line);
if (t->sa.key_len <= 0)
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
"%s: MACsec transmit secure association without key configured. "
"Ignoring [MACsecTransmitAssociation] section from line %u",
t->section->filename, t->section->line);
return 0;
}
static int macsec_receive_association_verify(ReceiveAssociation *a) {
NetDev *netdev;
assert(a);
assert(a->macsec);
netdev = NETDEV(a->macsec);
if (section_is_invalid(a->section))
return -EINVAL;
if (a->sa.key_len <= 0)
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
"%s: MACsec receive secure association without key configured. "
"Ignoring [MACsecReceiveAssociation] section from line %u",
a->section->filename, a->section->line);
if (ether_addr_is_null(&a->sci.mac))
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
"%s: MACsec receive secure association without MAC address configured. "
"Ignoring [MACsecReceiveAssociation] section from line %u",
a->section->filename, a->section->line);
if (a->sci.port == 0)
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
"%s: MACsec receive secure association without port configured. "
"Ignoring [MACsecReceiveAssociation] section from line %u",
a->section->filename, a->section->line);
return 0;
}
static int netdev_macsec_verify(NetDev *netdev, const char *filename) {
MACsec *v = MACSEC(netdev);
TransmitAssociation *a;
ReceiveAssociation *n;
ReceiveChannel *c;
Iterator i;
int r;
assert(netdev);
assert(v);
assert(filename);
ORDERED_HASHMAP_FOREACH(c, v->receive_channels_by_section, i) {
r = macsec_receive_channel_verify(c);
if (r < 0)
macsec_receive_channel_free(c);
}
ORDERED_HASHMAP_FOREACH(a, v->transmit_associations_by_section, i) {
r = macsec_transmit_association_verify(a);
if (r < 0)
macsec_transmit_association_free(a);
}
ORDERED_HASHMAP_FOREACH(n, v->receive_associations_by_section, i) {
r = macsec_receive_association_verify(n);
if (r < 0)
macsec_receive_association_free(n);
}
return 0;
}
static void macsec_init(NetDev *netdev) {
MACsec *v;
assert(netdev);
v = MACSEC(netdev);
assert(v);
v->encrypt = -1;
}
static void macsec_done(NetDev *netdev) {
MACsec *t;
assert(netdev);
t = MACSEC(netdev);
assert(t);
ordered_hashmap_free_with_destructor(t->receive_channels_by_section, macsec_receive_channel_free);
ordered_hashmap_free_with_destructor(t->transmit_associations_by_section, macsec_transmit_association_free);
ordered_hashmap_free_with_destructor(t->receive_associations_by_section, macsec_receive_association_free);
}
const NetDevVTable macsec_vtable = {
.object_size = sizeof(MACsec),
.init = macsec_init,
.sections = "Match\0NetDev\0MACsec\0MACsecReceiveChannel\0MACsecTransmitAssociation\0MACsecReceiveAssociation\0",
.fill_message_create = netdev_macsec_fill_message_create,
.post_create = netdev_macsec_configure,
.done = macsec_done,
.create_type = NETDEV_CREATE_STACKED,
.config_verify = netdev_macsec_verify,
};

View file

@ -0,0 +1,72 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <linux/if_macsec.h>
#include "in-addr-util.h"
#include "netdev.h"
#include "networkd-util.h"
#include "sparse-endian.h"
typedef struct MACsec MACsec;
typedef union MACsecSCI {
uint64_t as_uint64;
struct {
struct ether_addr mac;
be16_t port;
} _packed_;
} MACsecSCI;
assert_cc(sizeof(MACsecSCI) == sizeof(uint64_t));
typedef struct SecurityAssociation {
uint8_t association_number;
uint32_t packet_number;
uint8_t key_id[MACSEC_KEYID_LEN];
uint8_t *key;
uint32_t key_len;
} SecurityAssociation;
typedef struct TransmitAssociation {
MACsec *macsec;
NetworkConfigSection *section;
SecurityAssociation sa;
} TransmitAssociation;
typedef struct ReceiveAssociation {
MACsec *macsec;
NetworkConfigSection *section;
MACsecSCI sci;
SecurityAssociation sa;
} ReceiveAssociation;
typedef struct ReceiveChannel {
MACsec *macsec;
NetworkConfigSection *section;
MACsecSCI sci;
} ReceiveChannel;
struct MACsec {
NetDev meta;
uint16_t port;
int encrypt;
OrderedHashmap *receive_channels_by_section;
OrderedHashmap *transmit_associations_by_section;
OrderedHashmap *receive_associations_by_section;
};
DEFINE_NETDEV_CAST(MACSEC, MACsec);
extern const NetDevVTable macsec_vtable;
CONFIG_PARSER_PROTOTYPE(config_parse_macsec_port);
CONFIG_PARSER_PROTOTYPE(config_parse_macsec_hw_address);
CONFIG_PARSER_PROTOTYPE(config_parse_macsec_packet_number);
CONFIG_PARSER_PROTOTYPE(config_parse_macsec_key_id);
CONFIG_PARSER_PROTOTYPE(config_parse_macsec_key);

View file

@ -9,6 +9,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
#include "netdev/bridge.h"
#include "netdev/geneve.h"
#include "netdev/ipvlan.h"
#include "netdev/macsec.h"
#include "netdev/macvlan.h"
#include "netdev/tunnel.h"
#include "netdev/tuntap.h"
@ -132,6 +133,18 @@ GENEVE.UDP6ZeroCheckSumTx, config_parse_bool, 0,
GENEVE.UDP6ZeroChecksumTx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumtx)
GENEVE.DestinationPort, config_parse_ip_port, 0, offsetof(Geneve, dest_port)
GENEVE.FlowLabel, config_parse_geneve_flow_label, 0, 0
MACsec.Port, config_parse_macsec_port, 0, 0
MACsec.Encrypt, config_parse_tristate, 0, offsetof(MACsec, encrypt)
MACsecReceiveChannel.Port, config_parse_macsec_port, 0, 0
MACsecReceiveChannel.MACAddress, config_parse_macsec_hw_address, 0, 0
MACsecTransmitAssociation.PacketNumber, config_parse_macsec_packet_number, 0, 0
MACsecTransmitAssociation.KeyId, config_parse_macsec_key_id, 0, 0
MACsecTransmitAssociation.Key, config_parse_macsec_key, 0, 0
MACsecReceiveAssociation.Port, config_parse_macsec_port, 0, 0
MACsecReceiveAssociation.MACAddress, config_parse_macsec_hw_address, 0, 0
MACsecReceiveAssociation.PacketNumber, config_parse_macsec_packet_number, 0, 0
MACsecReceiveAssociation.KeyId, config_parse_macsec_key_id, 0, 0
MACsecReceiveAssociation.Key, config_parse_macsec_key, 0, 0
Tun.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue)
Tun.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue)
Tun.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info)

View file

@ -14,6 +14,7 @@
#include "netdev/geneve.h"
#include "netdev/ipvlan.h"
#include "netdev/l2tp-tunnel.h"
#include "netdev/macsec.h"
#include "netdev/macvlan.h"
#include "netdev/netdev.h"
#include "netdev/netdevsim.h"
@ -66,6 +67,7 @@ const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
[NETDEV_KIND_FOU] = &foutnl_vtable,
[NETDEV_KIND_ERSPAN] = &erspan_vtable,
[NETDEV_KIND_L2TP] = &l2tptnl_vtable,
[NETDEV_KIND_MACSEC] = &macsec_vtable,
};
static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
@ -98,6 +100,7 @@ static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
[NETDEV_KIND_FOU] = "fou",
[NETDEV_KIND_ERSPAN] = "erspan",
[NETDEV_KIND_L2TP] = "l2tp",
[NETDEV_KIND_MACSEC] = "macsec",
};
DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);

View file

@ -47,6 +47,7 @@ typedef enum NetDevKind {
NETDEV_KIND_FOU,
NETDEV_KIND_ERSPAN,
NETDEV_KIND_L2TP,
NETDEV_KIND_MACSEC,
_NETDEV_KIND_MAX,
_NETDEV_KIND_TUNNEL, /* Used by config_parse_stacked_netdev() */
_NETDEV_KIND_INVALID = -1

View file

@ -46,6 +46,7 @@ Network.MACVTAP, config_parse_stacked_netdev,
Network.IPVLAN, config_parse_stacked_netdev, NETDEV_KIND_IPVLAN, offsetof(Network, stacked_netdev_names)
Network.VXLAN, config_parse_stacked_netdev, NETDEV_KIND_VXLAN, offsetof(Network, stacked_netdev_names)
Network.L2TP, config_parse_stacked_netdev, NETDEV_KIND_L2TP, offsetof(Network, stacked_netdev_names)
Network.MACsec, config_parse_stacked_netdev, NETDEV_KIND_MACSEC, offsetof(Network, stacked_netdev_names)
Network.Tunnel, config_parse_stacked_netdev, _NETDEV_KIND_TUNNEL, offsetof(Network, stacked_netdev_names)
Network.VRF, config_parse_ifname, 0, offsetof(Network, vrf_name)
Network.DHCP, config_parse_dhcp, 0, offsetof(Network, dhcp)

View file

@ -687,7 +687,7 @@ int config_parse_stacked_netdev(const char *unit,
assert(IN_SET(kind,
NETDEV_KIND_VLAN, NETDEV_KIND_MACVLAN, NETDEV_KIND_MACVTAP,
NETDEV_KIND_IPVLAN, NETDEV_KIND_VXLAN, NETDEV_KIND_L2TP,
_NETDEV_KIND_TUNNEL));
NETDEV_KIND_MACSEC, _NETDEV_KIND_TUNNEL));
if (!ifname_valid(rvalue)) {
log_syntax(unit, LOG_ERR, filename, line, 0,

View file

@ -40,6 +40,7 @@ typedef enum sd_gen_family {
SD_GENL_WIREGUARD,
SD_GENL_FOU,
SD_GENL_L2TP,
SD_GENL_MACSEC,
} sd_genl_family;
/* callback */
@ -81,6 +82,7 @@ int sd_netlink_message_append_flag(sd_netlink_message *m, unsigned short type);
int sd_netlink_message_append_u8(sd_netlink_message *m, unsigned short type, uint8_t data);
int sd_netlink_message_append_u16(sd_netlink_message *m, unsigned short type, uint16_t data);
int sd_netlink_message_append_u32(sd_netlink_message *m, unsigned short type, uint32_t data);
int sd_netlink_message_append_u64(sd_netlink_message *m, unsigned short type, uint64_t data);
int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, const void *data, size_t len);
int sd_netlink_message_append_in_addr(sd_netlink_message *m, unsigned short type, const struct in_addr *data);
int sd_netlink_message_append_in6_addr(sd_netlink_message *m, unsigned short type, const struct in6_addr *data);

View file

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

View file

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