From: Erik Larsson Date: Sun, 16 Mar 2025 11:25:35 +0000 (+0100) Subject: networkd: add support for externally managed vxlan devices X-Git-Tag: v258-rc1~1038 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=6e529860ba57e6b7f9b7b19950da0b25960505a8;p=thirdparty%2Fsystemd.git networkd: add support for externally managed vxlan devices With this a vxlan interface can be created which is managed by for example a EVPN control plane. --- diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml index 1b4673ab804..fe7342bfd04 100644 --- a/man/systemd.netdev.xml +++ b/man/systemd.netdev.xml @@ -670,7 +670,8 @@ VNI= - The VXLAN Network Identifier (or VXLAN Segment ID). Takes a number in the range 1…16777215. + The VXLAN Network Identifier (or VXLAN Segment ID). Takes a number in the range 1…16777215. + Ignored if External= is set to true. @@ -900,6 +901,24 @@ + + External= + + Takes a boolean. When true, the vxlan interface is externally controlled, for example by an + EVPN control plane. Defaults to false. + + + + + + VNIFilter= + + Takes a boolean. When true, the interface is capable of filtering VNI filtering. + Ignored if External= is set to false. Defaults to false. + + + + diff --git a/src/libsystemd/sd-netlink/netlink-types-rtnl.c b/src/libsystemd/sd-netlink/netlink-types-rtnl.c index 187d9b6756a..d0d8bfe9291 100644 --- a/src/libsystemd/sd-netlink/netlink-types-rtnl.c +++ b/src/libsystemd/sd-netlink/netlink-types-rtnl.c @@ -390,6 +390,7 @@ static const NLAPolicy rtnl_link_info_data_vxlan_policies[] = { [IFLA_VXLAN_GPE] = BUILD_POLICY(FLAG), [IFLA_VXLAN_TTL_INHERIT] = BUILD_POLICY(FLAG), [IFLA_VXLAN_DF] = BUILD_POLICY(U8), + [IFLA_VXLAN_VNIFILTER] = BUILD_POLICY(U8), }; static const NLAPolicy rtnl_link_info_data_xfrm_policies[] = { diff --git a/src/network/netdev/netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf index 4535c2cf3e0..35016f24d16 100644 --- a/src/network/netdev/netdev-gperf.gperf +++ b/src/network/netdev/netdev-gperf.gperf @@ -157,6 +157,8 @@ VXLAN.DestinationPort, config_parse_ip_port, VXLAN.FlowLabel, config_parse_flow_label, 0, 0 VXLAN.IPDoNotFragment, config_parse_df, 0, offsetof(VxLan, df) VXLAN.Independent, config_parse_bool, 0, offsetof(VxLan, independent) +VXLAN.External, config_parse_bool, 0, offsetof(VxLan, external) +VXLAN.VNIFilter, config_parse_bool, 0, offsetof(VxLan, vnifilter) 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) diff --git a/src/network/netdev/vxlan.c b/src/network/netdev/vxlan.c index d8a066370d7..ca0b4f47205 100644 --- a/src/network/netdev/vxlan.c +++ b/src/network/netdev/vxlan.c @@ -109,7 +109,7 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli /* The properties below cannot be updated, and the kernel refuses the whole request if one of the * following attributes is set for an existing interface. */ - if (v->vni <= VXLAN_VID_MAX) { + if (!v->external && v->vni <= VXLAN_VID_MAX) { r = sd_netlink_message_append_u32(m, IFLA_VXLAN_ID, v->vni); if (r < 0) return r; @@ -194,6 +194,16 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli return r; } + r = sd_netlink_message_append_u8(m, IFLA_VXLAN_COLLECT_METADATA, v->external); + if (r < 0) + return r; + + if (v->external) { + r = sd_netlink_message_append_u8(m, IFLA_VXLAN_VNIFILTER, v->vnifilter); + if (r < 0) + return r; + } + return 0; } @@ -393,11 +403,6 @@ static int netdev_vxlan_verify(NetDev *netdev, const char *filename) { VxLan *v = VXLAN(netdev); - if (v->vni > VXLAN_VID_MAX) - return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL), - "%s: VXLAN without valid VNI (or VXLAN Segment ID) configured. Ignoring.", - filename); - if (v->ttl > 255) return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL), "%s: VXLAN TTL must be <= 255. Ignoring.", @@ -416,6 +421,18 @@ static int netdev_vxlan_verify(NetDev *netdev, const char *filename) { "The local address cannot be '%s' when Independent= is enabled, ignoring.", strna(netdev_local_address_type_to_string(v->local_type))); + if (v->external) { + if (v->vni <= VXLAN_VID_MAX) + log_netdev_warning(netdev, "VNI= is set while External= is enabled. VNI= setting will be ignored."); + } else { + if (v->vnifilter) + log_netdev_warning(netdev, "VNIFilter= is enabled while External= is disabled. VNIFilter= setting will be ignored."); + if (v->vni > VXLAN_VID_MAX) + return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "%s: VXLAN without valid VNI (or VXLAN Segment ID) configured. Ignoring.", + filename); + } + return 0; } @@ -446,6 +463,8 @@ static void vxlan_init(NetDev *netdev) { v->udpcsum = false; v->udp6zerocsumtx = false; v->udp6zerocsumrx = false; + v->external = false; + v->vnifilter = false; } const NetDevVTable vxlan_vtable = { diff --git a/src/network/netdev/vxlan.h b/src/network/netdev/vxlan.h index 29874942bc5..9d4e8852664 100644 --- a/src/network/netdev/vxlan.h +++ b/src/network/netdev/vxlan.h @@ -59,6 +59,8 @@ struct VxLan { bool generic_protocol_extension; bool inherit; bool independent; + bool external; /* a.k.a collect metadata mode */ + bool vnifilter; struct ifla_vxlan_port_range port_range; }; diff --git a/test/test-network/conf/25-vxlan-external.netdev b/test/test-network/conf/25-vxlan-external.netdev new file mode 100644 index 00000000000..3e3c2bcb13b --- /dev/null +++ b/test/test-network/conf/25-vxlan-external.netdev @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[NetDev] +Name=vxlan-external +Kind=vxlan + +[VXLAN] +External=true +VNIFilter=true +L2MissNotification=true +L3MissNotification=true +RouteShortCircuit=true +UDPChecksum=true +UDP6ZeroChecksumTx=true +UDP6ZeroChecksumRx=true +RemoteChecksumTx=true +RemoteChecksumRx=true +GroupPolicyExtension=true +Independent=true +DestinationPort=4444 diff --git a/test/test-network/conf/25-vxlan-external.network b/test/test-network/conf/25-vxlan-external.network new file mode 100644 index 00000000000..276a386b5e3 --- /dev/null +++ b/test/test-network/conf/25-vxlan-external.network @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +Name=vxlan-external + +[Network] +Address=::/64 diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 872fc489a7e..71ae6891561 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -3028,11 +3028,13 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-vxlan-ipv6.netdev', '25-vxlan-ipv6.network', '25-vxlan-independent.netdev', '26-netdev-link-local-addressing-yes.network', '25-veth.netdev', '25-vxlan-veth99.network', '25-ipv6-prefix.network', - '25-vxlan-local-slaac.netdev', '25-vxlan-local-slaac.network') + '25-vxlan-local-slaac.netdev', '25-vxlan-local-slaac.network', + '25-vxlan-external.netdev', '25-vxlan-external.network') start_networkd() self.wait_online('test1:degraded', 'veth99:routable', 'veth-peer:degraded', - 'vxlan99:degraded', 'vxlan98:degraded', 'vxlan97:degraded', 'vxlan-slaac:degraded') + 'vxlan99:degraded', 'vxlan98:degraded', 'vxlan97:degraded', 'vxlan-slaac:degraded', + 'vxlan-external:degraded') self.networkctl_check_unit('test1', '11-dummy', '25-vxlan-test1') self.networkctl_check_unit('veth99', '25-veth', '25-vxlan-veth99') self.networkctl_check_unit('veth-peer', '25-veth', '25-ipv6-prefix') @@ -3040,6 +3042,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.networkctl_check_unit('vxlan98', '25-vxlan-independent', '26-netdev-link-local-addressing-yes') self.networkctl_check_unit('vxlan97', '25-vxlan-ipv6', '25-vxlan-ipv6') self.networkctl_check_unit('vxlan-slaac', '25-vxlan-local-slaac', '25-vxlan-local-slaac') + self.networkctl_check_unit('vxlan-external', '25-vxlan-external', '25-vxlan-external') output = check_output('ip -d -d link show vxlan99') print(output) @@ -3084,6 +3087,12 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): print(output) self.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output) + output = check_output('ip -d link show vxlan-external') + print(output) + self.assertIn('id 0 ', output) + self.assertIn('external', output) + self.assertIn('vnifilter', output) + @unittest.skipUnless(compare_kernel_version("6"), reason="Causes kernel panic on unpatched kernels: https://bugzilla.kernel.org/show_bug.cgi?id=208315") def test_macsec(self): copy_network_unit('25-macsec.netdev', '25-macsec.network', '25-macsec.key',