all lists on lists.proxmox.com
 help / color / mirror / Atom feed
From: Stefan Hanreich <s.hanreich@proxmox.com>
To: pve-devel@lists.proxmox.com
Cc: Lukas Sichert <l.sichert@proxmox.com>
Subject: Re: [PATCH network] sdn: zones: simple, evpn: do not stack duplicate snat rules
Date: Tue, 20 Oct 2026 18:02:15 +0200	[thread overview]
Message-ID: <b7588c65-4782-40d2-8b0d-a85f49252f9d@proxmox.com> (raw)
In-Reply-To: <179241063976.803488.17141942444436193145@gmail.com>

On 10/20/26 4:04 PM, Dzidula Gati wrote:
> The SNAT and conntrack zone rules are installed with plain
> `iptables -A`/`-I` in post-up. ifupdown2 re-runs post-up when an
> existing interface is reconfigured by `ifreload -a` (e.g. on every
> SDN apply), without running the old post-down first. Each apply
> therefore adds another copy of the same rules.
> 
> Since post-down only removes a single exact match, the extra copies
> are never cleaned up. If the SNAT source address changed in the
> meantime (e.g. the node's IP was changed and SDN re-applied), the
> stale rules also stay in front of the new one, keep matching all
> traffic first and break outbound connectivity of the guests until
> they are removed by hand or the node is rebooted.
> 
> Check whether the rule already exists before adding it, so that
> re-running post-up is idempotent. This does not remove rules that
> were left behind with an outdated source address, but it stops new
> duplicates from being created.

There has been work done on that issue, as well as the remove issue
already here:

https://lore.proxmox.com/pve-devel/20260623133721.29483-1-l.sichert@proxmox.com/T/#t

see also:

https://bugzilla.proxmox.com/show_bug.cgi?id=5066

> Signed-off-by: Dzidula Gati <dzidulajubilee@gmail.com>
> ---
> Tested on PVE 9.2 / ifupdown2 3.3.0: the full test suite passes
> (zones/ipams/dns/subnets/vnets_blackbox, 235/235). Verified in a network
> namespace that running the generated post-up commands repeatedly leaves
> exactly one rule, and a single post-down removes it again.
> 
>  src/PVE/Network/SDN/Zones/EvpnPlugin.pm           | 15 +++++++++------
>  src/PVE/Network/SDN/Zones/SimplePlugin.pm         | 15 +++++++++------
>  .../evpn/exitnode_snat/expected_sdn_interfaces    |  8 ++++----
>  .../zones/simple/ipv4snat/expected_sdn_interfaces |  4 ++--
>  .../zones/simple/ipv6snat/expected_sdn_interfaces |  4 ++--
>  5 files changed, 26 insertions(+), 20 deletions(-)
> 
> diff --git a/src/PVE/Network/SDN/Zones/EvpnPlugin.pm b/src/PVE/Network/SDN/Zones/EvpnPlugin.pm
> index 0e79707..5b4efbc 100644
> --- a/src/PVE/Network/SDN/Zones/EvpnPlugin.pm
> +++ b/src/PVE/Network/SDN/Zones/EvpnPlugin.pm
> @@ -282,15 +282,18 @@ sub generate_sdn_config {
>              }
>              if ($outip && $outiface && $is_evpn_gateway) {
>                  #use snat, faster than masquerade
> +                #check before adding, so that re-running post-up on ifreload does not
> +                #stack duplicate rules
> +                my $snat_rule =
> +                    "POSTROUTING -s '$cidr' -o $outiface -j SNAT --to-source $outip";
>                  push @iface_config,
> -                    "post-up $iptables -t nat -A POSTROUTING -s '$cidr' -o $outiface -j SNAT --to-source $outip";
> -                push @iface_config,
> -                    "post-down $iptables -t nat -D POSTROUTING -s '$cidr' -o $outiface -j SNAT --to-source $outip";
> +                    "post-up $iptables -t nat -C $snat_rule 2>/dev/null || $iptables -t nat -A $snat_rule";
> +                push @iface_config, "post-down $iptables -t nat -D $snat_rule";
>                  #add conntrack zone once on outgoing interface
> +                my $ct_rule = "PREROUTING -i fwbr+ -j CT --zone 1";
>                  push @iface_config,
> -                    "post-up $iptables -t raw -I PREROUTING -i fwbr+ -j CT --zone 1";
> -                push @iface_config,
> -                    "post-down $iptables -t raw -D PREROUTING -i fwbr+ -j CT --zone 1";
> +                    "post-up $iptables -t raw -C $ct_rule 2>/dev/null || $iptables -t raw -I $ct_rule";
> +                push @iface_config, "post-down $iptables -t raw -D $ct_rule";
>              }
>          }
>      }
> diff --git a/src/PVE/Network/SDN/Zones/SimplePlugin.pm b/src/PVE/Network/SDN/Zones/SimplePlugin.pm
> index 347eee9..dac1b55 100644
> --- a/src/PVE/Network/SDN/Zones/SimplePlugin.pm
> +++ b/src/PVE/Network/SDN/Zones/SimplePlugin.pm
> @@ -125,15 +125,18 @@ sub generate_sdn_config {
>              }
>              if ($outip && $outiface) {
>                  #use snat, faster than masquerade
> +                #check before adding, so that re-running post-up on ifreload does not
> +                #stack duplicate rules
> +                my $snat_rule =
> +                    "POSTROUTING -s '$cidr' -o $outiface -j SNAT --to-source $outip";
>                  push @iface_config,
> -                    "post-up $iptables -t nat -A POSTROUTING -s '$cidr' -o $outiface -j SNAT --to-source $outip";
> -                push @iface_config,
> -                    "post-down $iptables -t nat -D POSTROUTING -s '$cidr' -o $outiface -j SNAT --to-source $outip";
> +                    "post-up $iptables -t nat -C $snat_rule 2>/dev/null || $iptables -t nat -A $snat_rule";
> +                push @iface_config, "post-down $iptables -t nat -D $snat_rule";
>                  #add conntrack zone once on outgoing interface
> +                my $ct_rule = "PREROUTING -i fwbr+ -j CT --zone 1";
>                  push @iface_config,
> -                    "post-up $iptables -t raw -I PREROUTING -i fwbr+ -j CT --zone 1";
> -                push @iface_config,
> -                    "post-down $iptables -t raw -D PREROUTING -i fwbr+ -j CT --zone 1";
> +                    "post-up $iptables -t raw -C $ct_rule 2>/dev/null || $iptables -t raw -I $ct_rule";
> +                push @iface_config, "post-down $iptables -t raw -D $ct_rule";
>              }
>          }
>      }
> diff --git a/src/test/zones/evpn/exitnode_snat/expected_sdn_interfaces b/src/test/zones/evpn/exitnode_snat/expected_sdn_interfaces
> index 0d7d174..8c69da4 100644
> --- a/src/test/zones/evpn/exitnode_snat/expected_sdn_interfaces
> +++ b/src/test/zones/evpn/exitnode_snat/expected_sdn_interfaces
> @@ -3,9 +3,9 @@
>  auto myvnet
>  iface myvnet
>  	address 10.0.0.1/24
> -	post-up iptables -t nat -A POSTROUTING -s '10.0.0.0/24' -o vmbr0 -j SNAT --to-source 192.168.0.1
> +	post-up iptables -t nat -C POSTROUTING -s '10.0.0.0/24' -o vmbr0 -j SNAT --to-source 192.168.0.1 2>/dev/null || iptables -t nat -A POSTROUTING -s '10.0.0.0/24' -o vmbr0 -j SNAT --to-source 192.168.0.1
>  	post-down iptables -t nat -D POSTROUTING -s '10.0.0.0/24' -o vmbr0 -j SNAT --to-source 192.168.0.1
> -	post-up iptables -t raw -I PREROUTING -i fwbr+ -j CT --zone 1
> +	post-up iptables -t raw -C PREROUTING -i fwbr+ -j CT --zone 1 2>/dev/null || iptables -t raw -I PREROUTING -i fwbr+ -j CT --zone 1
>  	post-down iptables -t raw -D PREROUTING -i fwbr+ -j CT --zone 1
>  	bridge_ports vxlan_myvnet
>  	bridge_stp off
> @@ -18,9 +18,9 @@ iface myvnet
>  auto myvnet2
>  iface myvnet2
>  	address 2a08:2142:302:3::1/64
> -	post-up ip6tables -t nat -A POSTROUTING -s '2a08:2142:302:3::/64' -o vmbr0 -j SNAT --to-source 2001:db8::2
> +	post-up ip6tables -t nat -C POSTROUTING -s '2a08:2142:302:3::/64' -o vmbr0 -j SNAT --to-source 2001:db8::2 2>/dev/null || ip6tables -t nat -A POSTROUTING -s '2a08:2142:302:3::/64' -o vmbr0 -j SNAT --to-source 2001:db8::2
>  	post-down ip6tables -t nat -D POSTROUTING -s '2a08:2142:302:3::/64' -o vmbr0 -j SNAT --to-source 2001:db8::2
> -	post-up ip6tables -t raw -I PREROUTING -i fwbr+ -j CT --zone 1
> +	post-up ip6tables -t raw -C PREROUTING -i fwbr+ -j CT --zone 1 2>/dev/null || ip6tables -t raw -I PREROUTING -i fwbr+ -j CT --zone 1
>  	post-down ip6tables -t raw -D PREROUTING -i fwbr+ -j CT --zone 1
>  	bridge_ports vxlan_myvnet2
>  	bridge_stp off
> diff --git a/src/test/zones/simple/ipv4snat/expected_sdn_interfaces b/src/test/zones/simple/ipv4snat/expected_sdn_interfaces
> index 69d7986..c527d41 100644
> --- a/src/test/zones/simple/ipv4snat/expected_sdn_interfaces
> +++ b/src/test/zones/simple/ipv4snat/expected_sdn_interfaces
> @@ -3,9 +3,9 @@
>  auto myvnet
>  iface myvnet
>  	address 10.0.0.1/24
> -	post-up iptables -t nat -A POSTROUTING -s '10.0.0.0/24' -o vmbr0 -j SNAT --to-source 192.168.0.1
> +	post-up iptables -t nat -C POSTROUTING -s '10.0.0.0/24' -o vmbr0 -j SNAT --to-source 192.168.0.1 2>/dev/null || iptables -t nat -A POSTROUTING -s '10.0.0.0/24' -o vmbr0 -j SNAT --to-source 192.168.0.1
>  	post-down iptables -t nat -D POSTROUTING -s '10.0.0.0/24' -o vmbr0 -j SNAT --to-source 192.168.0.1
> -	post-up iptables -t raw -I PREROUTING -i fwbr+ -j CT --zone 1
> +	post-up iptables -t raw -C PREROUTING -i fwbr+ -j CT --zone 1 2>/dev/null || iptables -t raw -I PREROUTING -i fwbr+ -j CT --zone 1
>  	post-down iptables -t raw -D PREROUTING -i fwbr+ -j CT --zone 1
>  	bridge_ports none
>  	bridge_stp off
> diff --git a/src/test/zones/simple/ipv6snat/expected_sdn_interfaces b/src/test/zones/simple/ipv6snat/expected_sdn_interfaces
> index 5f6d40b..7075c51 100644
> --- a/src/test/zones/simple/ipv6snat/expected_sdn_interfaces
> +++ b/src/test/zones/simple/ipv6snat/expected_sdn_interfaces
> @@ -3,9 +3,9 @@
>  auto myvnet
>  iface myvnet
>  	address 2a08:2142:302:3::1/64
> -	post-up ip6tables -t nat -A POSTROUTING -s '2a08:2142:302:3::/64' -o vmbr0 -j SNAT --to-source 2001:db8::2
> +	post-up ip6tables -t nat -C POSTROUTING -s '2a08:2142:302:3::/64' -o vmbr0 -j SNAT --to-source 2001:db8::2 2>/dev/null || ip6tables -t nat -A POSTROUTING -s '2a08:2142:302:3::/64' -o vmbr0 -j SNAT --to-source 2001:db8::2
>  	post-down ip6tables -t nat -D POSTROUTING -s '2a08:2142:302:3::/64' -o vmbr0 -j SNAT --to-source 2001:db8::2
> -	post-up ip6tables -t raw -I PREROUTING -i fwbr+ -j CT --zone 1
> +	post-up ip6tables -t raw -C PREROUTING -i fwbr+ -j CT --zone 1 2>/dev/null || ip6tables -t raw -I PREROUTING -i fwbr+ -j CT --zone 1
>  	post-down ip6tables -t raw -D PREROUTING -i fwbr+ -j CT --zone 1
>  	bridge_ports none
>  	bridge_stp off





      reply	other threads:[~2026-10-20 16:02 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-19 11:50 [PATCH network] sdn: zones: simple, evpn: do not stack duplicate snat rules Dzidula Gati
2026-10-20 16:02 ` Stefan Hanreich [this message]

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=b7588c65-4782-40d2-8b0d-a85f49252f9d@proxmox.com \
    --to=s.hanreich@proxmox.com \
    --cc=l.sichert@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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal