]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
networkd: add support for externally managed vxlan devices
authorErik Larsson <who+github@cnackers.org>
Sun, 16 Mar 2025 11:25:35 +0000 (12:25 +0100)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 20 Mar 2025 11:29:47 +0000 (20:29 +0900)
With this a vxlan interface can be created which is managed by
for example a EVPN control plane.

man/systemd.netdev.xml
src/libsystemd/sd-netlink/netlink-types-rtnl.c
src/network/netdev/netdev-gperf.gperf
src/network/netdev/vxlan.c
src/network/netdev/vxlan.h
test/test-network/conf/25-vxlan-external.netdev [new file with mode: 0644]
test/test-network/conf/25-vxlan-external.network [new file with mode: 0644]
test/test-network/systemd-networkd-tests.py

index 1b4673ab804d77fd9805f9a620706118ac3610d9..fe7342bfd0412f56b4cd83794904d128679d1b0a 100644 (file)
       <varlistentry>
         <term><varname>VNI=</varname></term>
         <listitem>
-          <para>The VXLAN Network Identifier (or VXLAN Segment ID). Takes a number in the range 1…16777215.</para>
+          <para>The VXLAN Network Identifier (or VXLAN Segment ID). Takes a number in the range 1…16777215.
+          Ignored if <varname>External=</varname> is set to true.</para>
 
         <xi:include href="version-info.xml" xpointer="v243"/>
         </listitem>
           <xi:include href="version-info.xml" xpointer="v247"/>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term><varname>External=</varname></term>
+        <listitem>
+          <para>Takes a boolean. When true, the vxlan interface is externally controlled, for example by an
+          EVPN control plane. Defaults to false.</para>
+
+          <xi:include href="version-info.xml" xpointer="v258"/>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>VNIFilter=</varname></term>
+        <listitem>
+          <para>Takes a boolean. When true, the interface is capable of filtering VNI filtering.
+          Ignored if <varname>External=</varname> is set to false. Defaults to false.</para>
+
+          <xi:include href="version-info.xml" xpointer="v258"/>
+        </listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
index 187d9b6756aa3cc1ce1fa248f7e5f9f8c58447f1..d0d8bfe9291a69020e5cffd523a955208cc7bba0 100644 (file)
@@ -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[] = {
index 4535c2cf3e0d2bb1ea95869e728b8a4b4d4f19aa..35016f24d166355b1fbfefb83b5fb3bfa34e0a3f 100644 (file)
@@ -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)
index d8a066370d7d10c777716d81b451d0a134505e0b..ca0b4f47205226e8c48548712c917e2c777e02b9 100644 (file)
@@ -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 = {
index 29874942bc562d7829e51f947e5d538408181b8d..9d4e88526647deb9c30d422f9f5446a0be5875cf 100644 (file)
@@ -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 (file)
index 0000000..3e3c2bc
--- /dev/null
@@ -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 (file)
index 0000000..276a386
--- /dev/null
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=vxlan-external
+
+[Network]
+Address=::/64
index 872fc489a7ebc0a2264ec8f01c82630ba9971978..71ae689156195739f5972e69a9eac408b9b56b0c 100755 (executable)
@@ -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',