From: Hannes Laimer <h.laimer@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [PATCH pve-network] sdn: zones: vxlan: enforce a single address family in the underlay
Date: Wed, 7 Oct 2026 08:45:53 +0200 [thread overview]
Message-ID: <20261007064553.164447-1-h.laimer@proxmox.com> (raw)
Mixed peer lists are refused. The kernel fixes a vxlan device's
address family at creation from vxlan-local-tunnelip, IPv4 without
one, and refuses flood entries of the other family. ifupdown2 drops
that error, so peers of the other family are silently missing.
Only fabrics where all nodes share an address family can be used. If
all nodes have both, IPv6 is preferred.
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
should only be applied after [1], without it we cant have a v6 vxlan
device to begin with
[1] https://lore.proxmox.com/pve-devel/20260925134050.754528-1-h.laimer@proxmox.com/
src/PVE/Network/SDN/Controllers/EvpnPlugin.pm | 11 +---
src/PVE/Network/SDN/Zones/Plugin.pm | 13 +++++
src/PVE/Network/SDN/Zones/VxlanPlugin.pm | 19 +++++--
.../vxlan/fabric_ipv4/expected_sdn_interfaces | 27 ++++++++++
src/test/zones/vxlan/fabric_ipv4/interfaces | 5 ++
src/test/zones/vxlan/fabric_ipv4/sdn_config | 50 +++++++++++++++++
.../vxlan/fabric_ipv6/expected_sdn_interfaces | 33 ++++++++++++
src/test/zones/vxlan/fabric_ipv6/interfaces | 5 ++
src/test/zones/vxlan/fabric_ipv6/sdn_config | 54 +++++++++++++++++++
9 files changed, 204 insertions(+), 13 deletions(-)
create mode 100644 src/test/zones/vxlan/fabric_ipv4/expected_sdn_interfaces
create mode 100644 src/test/zones/vxlan/fabric_ipv4/interfaces
create mode 100644 src/test/zones/vxlan/fabric_ipv4/sdn_config
create mode 100644 src/test/zones/vxlan/fabric_ipv6/expected_sdn_interfaces
create mode 100644 src/test/zones/vxlan/fabric_ipv6/interfaces
create mode 100644 src/test/zones/vxlan/fabric_ipv6/sdn_config
diff --git a/src/PVE/Network/SDN/Controllers/EvpnPlugin.pm b/src/PVE/Network/SDN/Controllers/EvpnPlugin.pm
index 4220cb67..e1c69603 100644
--- a/src/PVE/Network/SDN/Controllers/EvpnPlugin.pm
+++ b/src/PVE/Network/SDN/Controllers/EvpnPlugin.pm
@@ -690,15 +690,8 @@ sub on_update_hook {
|| !($controller->{peers} || $controller->{fabric});
if ($controller->{peers}) {
my @peers = PVE::Tools::split_list($controller->{peers});
- my $family;
-
- foreach my $peer (@peers) {
- my $peer_family = Net::IP::ip_is_ipv6($peer) ? 6 : 4;
- if (defined($family) && $family != $peer_family) {
- die "peers must contain only IPv4 or only IPv6 addresses\n";
- }
- $family = $peer_family;
- }
+ die "peers must contain only IPv4 or only IPv6 addresses\n"
+ if !PVE::Network::SDN::Zones::Plugin::peers_family(\@peers);
}
}
}
diff --git a/src/PVE/Network/SDN/Zones/Plugin.pm b/src/PVE/Network/SDN/Zones/Plugin.pm
index 74a3384c..85167b2c 100644
--- a/src/PVE/Network/SDN/Zones/Plugin.pm
+++ b/src/PVE/Network/SDN/Zones/Plugin.pm
@@ -286,6 +286,19 @@ sub ip_family {
return defined($ip) ? ip_get_version($ip) : undef;
}
+# the family shared by every peer, undef when the list mixes families
+sub peers_family {
+ my ($peers) = @_;
+
+ my $family;
+ for my $peer (@$peers) {
+ my $peer_family = ip_family($peer);
+ return undef if defined($family) && $family != $peer_family;
+ $family = $peer_family;
+ }
+ return $family;
+}
+
sub get_iface_addresses {
my ($iface_cfg) = @_;
diff --git a/src/PVE/Network/SDN/Zones/VxlanPlugin.pm b/src/PVE/Network/SDN/Zones/VxlanPlugin.pm
index 8eb7e1c1..88d0c3b3 100644
--- a/src/PVE/Network/SDN/Zones/VxlanPlugin.pm
+++ b/src/PVE/Network/SDN/Zones/VxlanPlugin.pm
@@ -104,15 +104,20 @@ sub generate_sdn_config {
my $current_node = eval { $config->get_node($plugin_config->{fabric}, $local_node) };
die "could not configure VXLAN zone $plugin_config->{id}: $@" if $@;
+ my $addr_key = PVE::Network::SDN::Controllers::EvpnPlugin::fabric_addr_key($nodes);
die
- "Node $local_node requires an IP in the fabric $fabric->{id} to configure the VXLAN zone $plugin_config->{id}"
- if !$current_node->{ip};
+ "Fabric $fabric->{id} has no consistent address family for all nodes (need all v6 or all v4)"
+ if !$addr_key;
+
+ die
+ "Node $local_node requires a $addr_key address in the fabric $fabric->{id} to configure the VXLAN zone $plugin_config->{id}"
+ if !$current_node->{$addr_key};
for my $node (values %$nodes) {
- push @peers, $node->{ip} if $node->{ip};
+ push @peers, $node->{$addr_key} if $node->{$addr_key};
}
- $ifaceip = $current_node->{ip};
+ $ifaceip = $current_node->{$addr_key};
} else {
die "neither peers nor fabric configured for VXLAN zone $plugin_config->{id}";
}
@@ -164,6 +169,12 @@ sub on_update_hook {
fabric => "must have exactly one of peers / fabric defined",
});
}
+
+ if ($zone->{peers}) {
+ my @peers = PVE::Tools::split_list($zone->{peers});
+ raise_param_exc({ peers => "must contain only IPv4 or only IPv6 addresses" })
+ if !PVE::Network::SDN::Zones::Plugin::peers_family(\@peers);
+ }
}
sub vnet_update_hook {
diff --git a/src/test/zones/vxlan/fabric_ipv4/expected_sdn_interfaces b/src/test/zones/vxlan/fabric_ipv4/expected_sdn_interfaces
new file mode 100644
index 00000000..61eeb710
--- /dev/null
+++ b/src/test/zones/vxlan/fabric_ipv4/expected_sdn_interfaces
@@ -0,0 +1,27 @@
+#version:1
+
+auto myvnet
+iface myvnet
+ bridge_ports vxlan_myvnet
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+
+auto vxlan_myvnet
+iface vxlan_myvnet
+ vxlan-id 100
+ vxlan-local-tunnelip 172.20.3.1
+ vxlan_remoteip 172.20.3.2
+ vxlan_remoteip 172.20.3.3
+ mtu 1450
+
+auto dummy_test
+iface dummy_test inet static
+ address 172.20.3.1/32
+ link-type dummy
+ ip-forward 1
+
+auto ens18
+iface ens18 inet static
+ address 172.20.3.1/32
+ ip-forward 1
diff --git a/src/test/zones/vxlan/fabric_ipv4/interfaces b/src/test/zones/vxlan/fabric_ipv4/interfaces
new file mode 100644
index 00000000..68b6a886
--- /dev/null
+++ b/src/test/zones/vxlan/fabric_ipv4/interfaces
@@ -0,0 +1,5 @@
+auto vmbr0
+iface vmbr0 inet manual
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
diff --git a/src/test/zones/vxlan/fabric_ipv4/sdn_config b/src/test/zones/vxlan/fabric_ipv4/sdn_config
new file mode 100644
index 00000000..eaaf3a9f
--- /dev/null
+++ b/src/test/zones/vxlan/fabric_ipv4/sdn_config
@@ -0,0 +1,50 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { tag => 100, type => "vnet", zone => "myzone" },
+ },
+ },
+ zones => {
+ ids => {
+ myzone => {
+ ipam => "pve",
+ type => "vxlan",
+ fabric => "test",
+ },
+ },
+ },
+ fabrics => {
+ ids => {
+ test => {
+ type => 'openfabric_fabric',
+ id => 'test',
+ ip_prefix => '172.20.3.0/24',
+ },
+ test_localhost => {
+ id => 'test_localhost',
+ type => 'openfabric_node',
+ interfaces => [
+ 'name=ens18',
+ ],
+ ip => '172.20.3.1',
+ },
+ test_node2 => {
+ id => 'test_node2',
+ type => 'openfabric_node',
+ interfaces => [
+ 'name=ens18',
+ ],
+ ip => '172.20.3.2',
+ },
+ test_node3 => {
+ id => 'test_node3',
+ type => 'openfabric_node',
+ interfaces => [
+ 'name=ens18',
+ ],
+ ip => '172.20.3.3',
+ },
+ },
+ },
+}
diff --git a/src/test/zones/vxlan/fabric_ipv6/expected_sdn_interfaces b/src/test/zones/vxlan/fabric_ipv6/expected_sdn_interfaces
new file mode 100644
index 00000000..44b368a3
--- /dev/null
+++ b/src/test/zones/vxlan/fabric_ipv6/expected_sdn_interfaces
@@ -0,0 +1,33 @@
+#version:1
+
+auto myvnet
+iface myvnet
+ bridge_ports vxlan_myvnet
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+
+auto vxlan_myvnet
+iface vxlan_myvnet
+ vxlan-id 100
+ vxlan-local-tunnelip fd00::1
+ vxlan_remoteip fd00::2
+ vxlan_remoteip fd00::3
+ mtu 1450
+
+auto dummy_test
+iface dummy_test inet static
+ address 172.20.3.1/32
+ link-type dummy
+ ip-forward 1
+
+auto dummy_test
+iface dummy_test inet6 static
+ address fd00::1/128
+ link-type dummy
+ ip-forward 1
+
+auto ens18
+iface ens18 inet static
+ address 172.20.3.1/32
+ ip-forward 1
diff --git a/src/test/zones/vxlan/fabric_ipv6/interfaces b/src/test/zones/vxlan/fabric_ipv6/interfaces
new file mode 100644
index 00000000..68b6a886
--- /dev/null
+++ b/src/test/zones/vxlan/fabric_ipv6/interfaces
@@ -0,0 +1,5 @@
+auto vmbr0
+iface vmbr0 inet manual
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
diff --git a/src/test/zones/vxlan/fabric_ipv6/sdn_config b/src/test/zones/vxlan/fabric_ipv6/sdn_config
new file mode 100644
index 00000000..f463a886
--- /dev/null
+++ b/src/test/zones/vxlan/fabric_ipv6/sdn_config
@@ -0,0 +1,54 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { tag => 100, type => "vnet", zone => "myzone" },
+ },
+ },
+ zones => {
+ ids => {
+ myzone => {
+ ipam => "pve",
+ type => "vxlan",
+ fabric => "test",
+ },
+ },
+ },
+ fabrics => {
+ ids => {
+ test => {
+ type => 'openfabric_fabric',
+ id => 'test',
+ ip_prefix => '172.20.3.0/24',
+ ip6_prefix => 'fd00::/64',
+ },
+ test_localhost => {
+ id => 'test_localhost',
+ type => 'openfabric_node',
+ interfaces => [
+ 'name=ens18',
+ ],
+ ip => '172.20.3.1',
+ ip6 => 'fd00::1',
+ },
+ test_node2 => {
+ id => 'test_node2',
+ type => 'openfabric_node',
+ interfaces => [
+ 'name=ens18',
+ ],
+ ip => '172.20.3.2',
+ ip6 => 'fd00::2',
+ },
+ test_node3 => {
+ id => 'test_node3',
+ type => 'openfabric_node',
+ interfaces => [
+ 'name=ens18',
+ ],
+ ip => '172.20.3.3',
+ ip6 => 'fd00::3',
+ },
+ },
+ },
+}
--
2.47.3
reply other threads:[~2026-10-07 6:46 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261007064553.164447-1-h.laimer@proxmox.com \
--to=h.laimer@proxmox.com \
--cc=pve-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox