From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id A20D41FF13C for ; Thu, 25 Jun 2026 18:10:57 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 3F0B2155F2; Thu, 25 Jun 2026 18:10:55 +0200 (CEST) Message-ID: <3ef74118-51a9-4ea3-bb6f-b8c93be35aa6@proxmox.com> Date: Thu, 25 Jun 2026 18:10:19 +0200 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH network v3] fix #6255: snat: update changed bridge IPs in SDN config To: pve-devel@lists.proxmox.com References: <20260625122420.51674-1-l.sichert@proxmox.com> Content-Language: en-US From: Stefan Hanreich In-Reply-To: <20260625122420.51674-1-l.sichert@proxmox.com> Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.444 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment POISEN_SPAM_PILL 0.1 Meta: its spam POISEN_SPAM_PILL_1 0.1 random spam to be learned in bayes POISEN_SPAM_PILL_3 0.1 random spam to be learned in bayes SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [evpnplugin.pm,simpleplugin.pm,plugin.pm,proxmox.com] Message-ID-Hash: VRYC6ULHTJLMZPRNE3SYX3EE3NO6QEOC X-Message-ID-Hash: VRYC6ULHTJLMZPRNE3SYX3EE3NO6QEOC X-MailFrom: s.hanreich@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: gave this another quick spin with an IPv{4,6} simple vnet, lgtm: Tested-by: Stefan Hanreich Reviewed-by: Stefan Hanreich On 6/25/26 2:25 PM, Lukas Sichert wrote: > When changing the IP address of an SNAT bridge in a Simple or EVPN zone, > the generated SDN configuration retains the old address. The address is > currently obtained through 'ip route get', which still reports the > previous state before 'ifreload' runs. > > Parse the incoming '/etc/network/interfaces' and look up the interface > used by 'ip route'. If it still has a gateway and a changed IP address, > update the SNAT address in the generated SDN configuration and log the > change to the journal. > > Signed-off-by: Lukas Sichert > Link: https://bugzilla.proxmox.com/show_bug.cgi?id=6255 > --- > > Notes: > changes from v2 to v3: (thanks @Stefan): > - simplified the address lookup by selecting the address and gateway key > based on the IP version > - add address and gateway check before canonicalzing as canonical_ip > creates default ip from undef input > - simplified the fallback logic for changed SNAT bridge addresses > > changes from v1 to v2: (thanks @Gabriel, @Stefan) > - add handling for IPV6 addresses > - remove warnings for missing ip/gateway, as they might trigger > in some legitimate scenarios > - add canonical formatting for IPv4 and IPV6 > - execute make tidy > - small restructuring of if clauses for earlier exit > - fix typos > > src/PVE/Network/SDN/Zones/EvpnPlugin.pm | 5 ++-- > src/PVE/Network/SDN/Zones/Plugin.pm | 32 +++++++++++++++++++++-- > src/PVE/Network/SDN/Zones/SimplePlugin.pm | 5 ++-- > 3 files changed, 36 insertions(+), 6 deletions(-) > > diff --git a/src/PVE/Network/SDN/Zones/EvpnPlugin.pm b/src/PVE/Network/SDN/Zones/EvpnPlugin.pm > index dfbd7e9..37bc9f5 100644 > --- a/src/PVE/Network/SDN/Zones/EvpnPlugin.pm > +++ b/src/PVE/Network/SDN/Zones/EvpnPlugin.pm > @@ -269,8 +269,9 @@ sub generate_sdn_config { > if ($subnet->{snat}) { > > #find outgoing interface > - my ($outip, $outiface) = > - PVE::Network::SDN::Zones::Plugin::get_local_route_ip($checkrouteip); > + my ($outip, $outiface) = PVE::Network::SDN::Zones::Plugin::get_local_route_ip( > + $checkrouteip, $interfaces_config, > + ); > if ($outip && $outiface && $is_evpn_gateway) { > #use snat, faster than masquerade > push @iface_config, > diff --git a/src/PVE/Network/SDN/Zones/Plugin.pm b/src/PVE/Network/SDN/Zones/Plugin.pm > index 74a3384..5493ec1 100644 > --- a/src/PVE/Network/SDN/Zones/Plugin.pm > +++ b/src/PVE/Network/SDN/Zones/Plugin.pm > @@ -9,6 +9,7 @@ use PVE::IPRoute2; > use PVE::JSONSchema; > use PVE::Cluster; > use PVE::Network; > +use PVE::SafeSyslog; > > use PVE::JSONSchema qw(get_standard_option); > use base qw(PVE::SectionConfig); > @@ -306,7 +307,7 @@ sub get_iface_addresses { > } > > sub get_local_route_ip { > - my ($targetip) = @_; > + my ($targetip, $interfaces_config) = @_; > > my $ip = undef; > my $interface = undef; > @@ -323,6 +324,33 @@ sub get_local_route_ip { > > }, > ); > + my $ipversion = Net::IP::ip_is_ipv6($targetip) ? 6 : 4; > + my $interface_in_config = $interfaces_config->{ifaces}->{$interface}; > + > + my $address_key = ($ipversion == 6) ? 'address6' : 'address'; > + my $gateway_key = ($ipversion == 6) ? 'gateway6' : 'gateway'; > + > + # check if ip address and gateway exist in config before normalizing, because > + # canonical_ip will return 0.0.0.0, if called with undef > + my $ip_address_in_config; > + if ($ip_address_in_config = $interface_in_config->{$address_key}) { > + $ip_address_in_config = PVE::Network::canonical_ip($ip_address_in_config); > + } > + > + my $gateway_in_config; > + if ($gateway_in_config = $interface_in_config->{$gateway_key}) { > + $gateway_in_config = PVE::Network::canonical_ip($gateway_in_config); > + } > + > + # if the device currently used for routing still has a valid description in /network/interfaces/, use it > + if ($interface_in_config && $gateway_in_config && $ip_address_in_config ne $ip) { > + syslog( > + "warning", > + "ip address $ip_address_in_config of interface $interface in /etc/network/interfaces does not match with its ip address reported by ip route: $ip, switching to $ip_address_in_config for SNAT", > + ); > + return ($ip_address_in_config, $interface); > + } > + > return ($ip, $interface); > } > > @@ -368,7 +396,7 @@ sub find_local_ip_interface_peers { > > #if peer is remote, find source with ip route > foreach my $address (@{$peers}) { > - my ($ip, $interface) = get_local_route_ip($address); > + my ($ip, $interface) = get_local_route_ip($address, $network_config); > return ($ip, $interface); > } > } > diff --git a/src/PVE/Network/SDN/Zones/SimplePlugin.pm b/src/PVE/Network/SDN/Zones/SimplePlugin.pm > index f5cd18e..ac772e7 100644 > --- a/src/PVE/Network/SDN/Zones/SimplePlugin.pm > +++ b/src/PVE/Network/SDN/Zones/SimplePlugin.pm > @@ -112,8 +112,9 @@ sub generate_sdn_config { > push @iface_config, "up ip route add $cidr dev $vnetid" if $mask == 32 && $ipversion == 4; > if ($subnet->{snat}) { > #find outgoing interface > - my ($outip, $outiface) = > - PVE::Network::SDN::Zones::Plugin::get_local_route_ip($checkrouteip); > + my ($outip, $outiface) = PVE::Network::SDN::Zones::Plugin::get_local_route_ip( > + $checkrouteip, $interfaces_config, > + ); > if ($outip && $outiface) { > #use snat, faster than masquerade > push @iface_config,