From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [45.144.208.40]) by lore.proxmox.com (Postfix) with ESMTPS id D82221FF0AA for ; Fri, 21 Aug 2026 16:05:35 +0200 (CEST) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id 58DFE21786; Fri, 21 Aug 2026 16:04:27 +0200 (CEST) From: Gabriel Goller To: pve-devel@lists.proxmox.com Subject: [PATCH pve-network 11/15] sdn: always generate EVPN zone VRFs Date: Fri, 21 Aug 2026 16:03:55 +0200 Message-ID: <20260821140404.322081-12-g.goller@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260821140404.322081-1-g.goller@proxmox.com> References: <20260821140404.322081-1-g.goller@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1787321023479 X-SPAM-LEVEL: Spam detection results: 0 AWL -0.364 Adjusted score from AWL reputation of From: address DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment (newer systems) KAM_MAILER 2 Automated Mailer Tag Left in Email RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: UPPFC2BLHL3PATL74Z5JJXJWO2T7H6VD X-Message-ID-Hash: UPPFC2BLHL3PATL74Z5JJXJWO2T7H6VD X-MailFrom: g.goller@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Move per-zone interface generation into the zone plugin API and use it for EVPN zones. This creates the EVPN VRF even when the zone has no VNets -- previously the VRF was only created when at least one VNet existed on the zone. Keep the unreachable default route handling on the VRF device. Exit nodes remove that route, while other nodes install it. Add a test for an empty EVPN zone. Signed-off-by: Gabriel Goller --- src/PVE/Network/SDN/Zones.pm | 6 ++-- src/PVE/Network/SDN/Zones/EvpnPlugin.pm | 35 +++++++++++-------- src/PVE/Network/SDN/Zones/Plugin.pm | 6 ++++ .../evpn/vrf-empty/expected_sdn_interfaces | 6 ++++ src/test/zones/evpn/vrf-empty/interfaces | 2 ++ src/test/zones/evpn/vrf-empty/sdn_config | 13 +++++++ 6 files changed, 50 insertions(+), 18 deletions(-) create mode 100644 src/test/zones/evpn/vrf-empty/expected_sdn_interfaces create mode 100644 src/test/zones/evpn/vrf-empty/interfaces create mode 100644 src/test/zones/evpn/vrf-empty/sdn_config diff --git a/src/PVE/Network/SDN/Zones.pm b/src/PVE/Network/SDN/Zones.pm index 893ec436d536..ad23cee4c292 100644 --- a/src/PVE/Network/SDN/Zones.pm +++ b/src/PVE/Network/SDN/Zones.pm @@ -127,12 +127,10 @@ sub generate_etc_network_config { for my $zone_id (sort keys %$zone_ids) { my $plugin_config = $zone_ids->{$zone_id}; - next if $plugin_config->{type} ne 'simple'; next if defined($plugin_config->{nodes}) && !$plugin_config->{nodes}->{$nodename}; - PVE::Network::SDN::Zones::SimplePlugin->generate_zone_config( - $plugin_config, $zone_id, $config, - ); + my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type}); + $plugin->generate_zone_config($plugin_config, $zone_id, $config); } for my $id (sort keys %$vnet_ids) { diff --git a/src/PVE/Network/SDN/Zones/EvpnPlugin.pm b/src/PVE/Network/SDN/Zones/EvpnPlugin.pm index 0e79707c3746..4c019071ecce 100644 --- a/src/PVE/Network/SDN/Zones/EvpnPlugin.pm +++ b/src/PVE/Network/SDN/Zones/EvpnPlugin.pm @@ -129,6 +129,25 @@ sub options { } # Plugin implementation +sub generate_zone_config { + my ($class, $plugin_config, $zone_id, $config) = @_; + + my $vrf_iface = "vrf_$zone_id"; + my $local_node = PVE::INotify::nodename(); + my $is_evpn_gateway = + $plugin_config->{exitnodes} && $plugin_config->{exitnodes}->{$local_node}; + my $route_action = $is_evpn_gateway ? 'del' : 'add'; + + if (!defined($config->{$vrf_iface})) { + $config->{$vrf_iface} = [ + 'vrf-table auto', + "post-up ip route $route_action vrf $vrf_iface unreachable default metric 4278198272", + ]; + } + + return $config; +} + sub generate_sdn_config { my ( $class, @@ -209,7 +228,8 @@ sub generate_sdn_config { die "neither fabric nor peers configured for EVPN controller $controller->{id}"; } - my $is_evpn_gateway = $plugin_config->{'exitnodes'}->{$local_node}; + my $is_evpn_gateway = + $plugin_config->{exitnodes} && $plugin_config->{exitnodes}->{$local_node}; my $exitnodes_local_routing = $plugin_config->{'exitnodes-local-routing'}; my $mtu = 1450; @@ -308,19 +328,6 @@ sub generate_sdn_config { push(@{ $config->{$vnetid} }, @iface_config) if !$config->{$vnetid}; if ($vrf_iface) { - #vrf interface - @iface_config = (); - push @iface_config, "vrf-table auto"; - if (!$is_evpn_gateway) { - push @iface_config, - "post-up ip route add vrf $vrf_iface unreachable default metric 4278198272"; - } else { - push @iface_config, - "post-up ip route del vrf $vrf_iface unreachable default metric 4278198272"; - } - - push(@{ $config->{$vrf_iface} }, @iface_config) if !$config->{$vrf_iface}; - if ($vrfvxlan) { #l3vni vxlan interface my $iface_vrf_vxlan = "vrfvx_$zoneid"; diff --git a/src/PVE/Network/SDN/Zones/Plugin.pm b/src/PVE/Network/SDN/Zones/Plugin.pm index 74a3384cd7ae..5a752a0545f6 100644 --- a/src/PVE/Network/SDN/Zones/Plugin.pm +++ b/src/PVE/Network/SDN/Zones/Plugin.pm @@ -100,6 +100,12 @@ sub encode_value { return $value; } +sub generate_zone_config { + my ($class, $plugin_config, $zone_id, $config) = @_; + + return $config; +} + sub generate_sdn_config { my ( $class, diff --git a/src/test/zones/evpn/vrf-empty/expected_sdn_interfaces b/src/test/zones/evpn/vrf-empty/expected_sdn_interfaces new file mode 100644 index 000000000000..f78a7d21782c --- /dev/null +++ b/src/test/zones/evpn/vrf-empty/expected_sdn_interfaces @@ -0,0 +1,6 @@ +#version:1 + +auto vrf_myzone +iface vrf_myzone + vrf-table auto + post-up ip route add vrf vrf_myzone unreachable default metric 4278198272 diff --git a/src/test/zones/evpn/vrf-empty/interfaces b/src/test/zones/evpn/vrf-empty/interfaces new file mode 100644 index 000000000000..f1bd92ed2b43 --- /dev/null +++ b/src/test/zones/evpn/vrf-empty/interfaces @@ -0,0 +1,2 @@ +auto lo +iface lo inet loopback diff --git a/src/test/zones/evpn/vrf-empty/sdn_config b/src/test/zones/evpn/vrf-empty/sdn_config new file mode 100644 index 000000000000..00c1c724b95e --- /dev/null +++ b/src/test/zones/evpn/vrf-empty/sdn_config @@ -0,0 +1,13 @@ +{ + version => 1, + vnets => { ids => {} }, + zones => { + ids => { + myzone => { + type => "evpn", + controller => "evpnctl", + "vrf-vxlan" => 1000, + }, + }, + }, +} -- 2.47.3