diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml index d083fb9a6f..3cce776cc2 100644 --- a/man/systemd.netdev.xml +++ b/man/systemd.netdev.xml @@ -179,6 +179,9 @@ fou Foo-over-UDP tunneling. + xfrm + A virtual tunnel interface like vti/vti6 but with several advantages. + @@ -1848,6 +1851,36 @@ Linux Ethernet Bonding Driver HOWTO + + [Xfrm] Section Options + + The [Xfrm] section accepts the following + keys: + + + + InterfaceId= + + Sets the ID/key of the xfrm interface which needs to be associated with a SA/policy. + Can be decimal or hexadecimal, valid range is 0-0xffffffff, defaults to 0. + + + + Independent= + + Takes a boolean. If set to no, the xfrm interface should have an + underlying device which can be used for hardware offloading. Defaults to no. + See systemd.network5 + for how to configure the underlying device. + + + + + For more detail information see + + Virtual xfrm interfaces + + [VRF] Section Options The [VRF] section only applies for @@ -2048,6 +2081,16 @@ PublicKey=RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA= AllowedIPs=fd31:bf08:57cb::/48,192.168.26.0/24 Endpoint=wireguard.example.com:51820 + + + /etc/systemd/network/27-xfrm.netdev + [Xfrm] +Name=xfrm0 +Kind=xfrm + +[Xfrm] +Independent=yes + diff --git a/man/systemd.network.xml b/man/systemd.network.xml index d32b60a2c9..c48b294551 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -856,6 +856,14 @@ + + Xfrm= + + The name of the xfrm to create on the link. See + systemd.netdev5. + This option may be specified more than once. + + KeepConfiguration= @@ -2338,6 +2346,29 @@ Name=enp0s25 MACVTAP=macvtap-test + + + A Xfrm interface with physical underlying device. + + # /etc/systemd/network/27-xfrm.netdev +[NetDev] +Name=xfrm0 + +[Xfrm] +InterfaceId=7 + + # /etc/systemd/network/27-eth0.network +[Match] +Name=eth0 + +[Network] +Xfrm=xfrm0 + + This creates a xfrm0 interface and binds it to the eth0 device. + This allows hardware based ipsec offloading to the eth0 nic. + If offloading is not needed, xfrm interfaces can be assigned to the lo device. + + diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c index 6850773977..450e298f2c 100644 --- a/src/libsystemd/sd-netlink/netlink-types.c +++ b/src/libsystemd/sd-netlink/netlink-types.c @@ -327,6 +327,11 @@ static const NLType rtnl_link_info_data_macsec_types[] = { [IFLA_MACSEC_VALIDATION] = { .type = NETLINK_TYPE_U8 }, }; +static const NLType rtnl_link_info_data_xfrm_types[] = { + [IFLA_XFRM_LINK] = { .type = NETLINK_TYPE_U32 }, + [IFLA_XFRM_IF_ID] = { .type = NETLINK_TYPE_U32 } +}; + /* 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", @@ -358,6 +363,7 @@ static const char* const nl_union_link_info_data_table[] = { [NL_UNION_LINK_INFO_DATA_CAN] = "can", [NL_UNION_LINK_INFO_DATA_MACSEC] = "macsec", [NL_UNION_LINK_INFO_DATA_NLMON] = "nlmon", + [NL_UNION_LINK_INFO_DATA_XFRM] = "xfrm", }; DEFINE_STRING_TABLE_LOOKUP(nl_union_link_info_data, NLUnionLinkInfoData); @@ -411,6 +417,8 @@ static const NLTypeSystem rtnl_link_info_data_type_systems[] = { .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 }, + [NL_UNION_LINK_INFO_DATA_XFRM] = { .count = ELEMENTSOF(rtnl_link_info_data_xfrm_types), + .types = rtnl_link_info_data_xfrm_types }, }; static const NLTypeSystemUnion rtnl_link_info_data_type_system_union = { diff --git a/src/libsystemd/sd-netlink/netlink-types.h b/src/libsystemd/sd-netlink/netlink-types.h index fed43ae43c..efc59a0a4b 100644 --- a/src/libsystemd/sd-netlink/netlink-types.h +++ b/src/libsystemd/sd-netlink/netlink-types.h @@ -83,6 +83,7 @@ typedef enum NLUnionLinkInfoData { NL_UNION_LINK_INFO_DATA_CAN, NL_UNION_LINK_INFO_DATA_MACSEC, NL_UNION_LINK_INFO_DATA_NLMON, + NL_UNION_LINK_INFO_DATA_XFRM, _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 14951c544c..32317c7c9f 100644 --- a/src/network/meson.build +++ b/src/network/meson.build @@ -43,6 +43,8 @@ sources = files(''' netdev/l2tp-tunnel.h netdev/macsec.c netdev/macsec.h + netdev/xfrm.c + netdev/xfrm.h networkd-address-label.c networkd-address-label.h networkd-address-pool.c diff --git a/src/network/netdev/netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf index 0cf6949968..33f7b3058d 100644 --- a/src/network/netdev/netdev-gperf.gperf +++ b/src/network/netdev/netdev-gperf.gperf @@ -22,6 +22,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"") #include "netdev/wireguard.h" #include "netdev/fou-tunnel.h" #include "netdev/l2tp-tunnel.h" +#include "netdev/xfrm.h" #include "vlan-util.h" %} struct ConfigPerfItem; @@ -218,3 +219,5 @@ WireGuardPeer.PublicKey, config_parse_wireguard_public_key, WireGuardPeer.PresharedKey, config_parse_wireguard_preshared_key, 0, 0 WireGuardPeer.PresharedKeyFile, config_parse_wireguard_preshared_key_file, 0, 0 WireGuardPeer.PersistentKeepalive, config_parse_wireguard_keepalive, 0, 0 +Xfrm.InterfaceId, config_parse_uint32, 0, offsetof(Xfrm, if_id) +Xfrm.Independent, config_parse_bool, 0, offsetof(Xfrm, independent) diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c index 5429237603..7735b455b7 100644 --- a/src/network/netdev/netdev.c +++ b/src/network/netdev/netdev.c @@ -29,6 +29,7 @@ #include "netdev/vxcan.h" #include "netdev/vxlan.h" #include "netdev/wireguard.h" +#include "netdev/xfrm.h" #include "netlink-util.h" #include "network-internal.h" #include "networkd-link.h" @@ -72,6 +73,7 @@ const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = { [NETDEV_KIND_L2TP] = &l2tptnl_vtable, [NETDEV_KIND_MACSEC] = &macsec_vtable, [NETDEV_KIND_NLMON] = &nlmon_vtable, + [NETDEV_KIND_XFRM] = &xfrm_vtable, }; static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = { @@ -107,6 +109,7 @@ static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = { [NETDEV_KIND_L2TP] = "l2tp", [NETDEV_KIND_MACSEC] = "macsec", [NETDEV_KIND_NLMON] = "nlmon", + [NETDEV_KIND_XFRM] = "xfrm", }; DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind); @@ -807,6 +810,9 @@ int netdev_load_one(Manager *manager, const char *filename) { case NETDEV_KIND_ERSPAN: independent = ERSPAN(netdev)->independent; break; + case NETDEV_KIND_XFRM: + independent = XFRM(netdev)->independent; + break; default: break; } diff --git a/src/network/netdev/netdev.h b/src/network/netdev/netdev.h index 57fabbef40..d63b1912d8 100644 --- a/src/network/netdev/netdev.h +++ b/src/network/netdev/netdev.h @@ -50,6 +50,7 @@ typedef enum NetDevKind { NETDEV_KIND_L2TP, NETDEV_KIND_MACSEC, NETDEV_KIND_NLMON, + NETDEV_KIND_XFRM, _NETDEV_KIND_MAX, _NETDEV_KIND_TUNNEL, /* Used by config_parse_stacked_netdev() */ _NETDEV_KIND_INVALID = -1 diff --git a/src/network/netdev/xfrm.c b/src/network/netdev/xfrm.c new file mode 100644 index 0000000000..c60b0b1ab7 --- /dev/null +++ b/src/network/netdev/xfrm.c @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "missing_network.h" +#include "netdev/xfrm.h" + +static int xfrm_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *message) { + int if_idx, r; + Xfrm *x; + + assert(netdev); + assert(message); + + x = XFRM(netdev); + + if (x->independent) + if_idx = LOOPBACK_IFINDEX; + else { + assert(link); + if (link->ifindex == 0) + return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(ENODEV), "Could not get interface index: %m"); + if_idx = link->ifindex; + } + + r = sd_netlink_message_append_u32(message, IFLA_XFRM_LINK, if_idx); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_XFRM_LINK: %m"); + + r = sd_netlink_message_append_u32(message, IFLA_XFRM_IF_ID, x->if_id); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_XFRM_IF_ID: %m"); + + return 0; +} + +const NetDevVTable xfrm_vtable = { + .object_size = sizeof(Xfrm), + .sections = "Match\0NetDev\0Xfrm\0", + .fill_message_create = xfrm_fill_message_create, + .create_type = NETDEV_CREATE_STACKED +}; diff --git a/src/network/netdev/xfrm.h b/src/network/netdev/xfrm.h new file mode 100644 index 0000000000..47355a53d8 --- /dev/null +++ b/src/network/netdev/xfrm.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include "netdev/netdev.h" + +typedef struct Xfrm { + NetDev meta; + + uint32_t if_id; + bool independent; +} Xfrm; + +DEFINE_NETDEV_CAST(XFRM, Xfrm); +extern const NetDevVTable xfrm_vtable; diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 5f91d66dc0..e92ffda721 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -158,7 +158,9 @@ bool link_ipv4ll_enabled(Link *link, AddressFamilyBoolean mask) { if (!link->network) return false; - if (STRPTR_IN_SET(link->kind, "vrf", "wireguard", "ipip", "gre", "ip6gre", "ip6tnl", "sit", "vti", "vti6", "can", "vcan", "vxcan", "nlmon")) + if (STRPTR_IN_SET(link->kind, + "vrf", "wireguard", "ipip", "gre", "ip6gre","ip6tnl", "sit", "vti", + "vti6", "can", "vcan", "vxcan", "nlmon", "xfrm")) return false; /* L3 or L3S mode do not support ARP. */ diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index e6723f2e90..5a2f3f4ea9 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -55,6 +55,7 @@ Network.VXLAN, config_parse_stacked_netdev, 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.Xfrm, config_parse_stacked_netdev, NETDEV_KIND_XFRM, offsetof(Network, stacked_netdev_names) Network.VRF, config_parse_ifname, 0, offsetof(Network, vrf_name) Network.DHCP, config_parse_dhcp, 0, offsetof(Network, dhcp) Network.DHCPServer, config_parse_bool, 0, offsetof(Network, dhcp_server) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 0b5205e8e2..3f1753049c 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -693,7 +693,8 @@ 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_IPVTAP, NETDEV_KIND_VXLAN, - NETDEV_KIND_L2TP, NETDEV_KIND_MACSEC, _NETDEV_KIND_TUNNEL)); + NETDEV_KIND_L2TP, NETDEV_KIND_MACSEC, _NETDEV_KIND_TUNNEL, + NETDEV_KIND_XFRM)); if (!ifname_valid(rvalue)) { log_syntax(unit, LOG_ERR, filename, line, 0, diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 72f1b94666..a16ec74131 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -69,6 +69,7 @@ struct Network { NetDev *bridge; NetDev *bond; NetDev *vrf; + NetDev *xfrm; Hashmap *stacked_netdevs; char *bridge_name; char *bond_name; diff --git a/test/fuzz/fuzz-netdev-parser/27-xfrm.netdev b/test/fuzz/fuzz-netdev-parser/27-xfrm.netdev new file mode 100644 index 0000000000..cdcb2d22cd --- /dev/null +++ b/test/fuzz/fuzz-netdev-parser/27-xfrm.netdev @@ -0,0 +1,7 @@ +[NetDev] +Name=xfrm99 +Kind=xfrm + +[Xfrm] +InterfaceId=7 +Independent=false diff --git a/test/fuzz/fuzz-netdev-parser/directives.netdev b/test/fuzz/fuzz-netdev-parser/directives.netdev index be1b197024..07e54d9e44 100644 --- a/test/fuzz/fuzz-netdev-parser/directives.netdev +++ b/test/fuzz/fuzz-netdev-parser/directives.netdev @@ -207,3 +207,6 @@ KeyId= Key= KeyFile= Activate= +[Xfrm] +Independent= +InterfaceId= diff --git a/test/fuzz/fuzz-network-parser/27-xfrm.network b/test/fuzz/fuzz-network-parser/27-xfrm.network new file mode 100644 index 0000000000..fa26c84e53 --- /dev/null +++ b/test/fuzz/fuzz-network-parser/27-xfrm.network @@ -0,0 +1,5 @@ +[Match] +Name=eth0 + +[Network] +Xfrm=xfrm99 diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index 26dd83d8da..ddafaa7840 100644 --- a/test/fuzz/fuzz-network-parser/directives.network +++ b/test/fuzz/fuzz-network-parser/directives.network @@ -121,6 +121,7 @@ DNSSEC= IPv6HopLimit= IPForward= IPv6Token= +Xfrm= Description= VXLAN= L2TP=