public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [RFC manager/network v2 0/3] fix #5066: make generated snat rules flushable
@ 2026-06-23 13:37 Lukas Sichert
  2026-06-23 13:37 ` [PATCH network v2 1/3] fix #5066: snat: push evpn snat rules into separate iptables chain Lukas Sichert
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Lukas Sichert @ 2026-06-23 13:37 UTC (permalink / raw)
  To: pve-devel; +Cc: Lukas Sichert

When creating a subnet with SNAT enabled and applying the changes, then
afterwards disabling SNAT and applying the changes again, the iptables
POSTROUTING rule still persists. This is because ifreload -a only
executes (post/pre-)down hooks when an interface is removed from
/etc/network/interfaces, while the (post/pre-)up hooks are always
executed [1]. As a result, the SNAT rule is not removed by 'ifreload -a' and
only a restart or 'ifdown' will remove it.

This series moves generated SDN SNAT rules into a dedicated
'PROXMOX-SDN' chain in the iptables nat table and adds a jump from
POSTROUTING to that chain. This keeps the generated rules separate from
custom rules added by users or other components.

The dedicated chain can then be flushed during network reload, removing
stale SDN SNAT rules without touching unrelated POSTROUTING rules.

As this changes the generated /etc/network/interfaces.d/sdn output, the
expected test output is adjusted accordingly.

[1] manpages.debian.org/testing/ifupdown2/ifreload.8.en.html
Link: https://bugzilla.proxmox.com/show_bug.cgi?id=5066

changes from v1 to v2 (thanks @Stefan):
- rebase on top of master
- create chain only when '$is_evpn_gateway' is true
- add Links to commits


network:

Lukas Sichert (2):
  fix #5066: snat: push evpn snat rules into separate iptables chain
  fix #5066: snat: push simplezone snat rules into separate iptables
    chain

 src/PVE/Network/SDN/Zones/EvpnPlugin.pm          | 11 +++++++++--
 src/PVE/Network/SDN/Zones/SimplePlugin.pm        | 15 ++++++++++++---
 .../evpn/exitnode_snat/expected_sdn_interfaces   | 16 ++++++++++++----
 .../simple/ipv4snat/expected_sdn_interfaces      |  8 ++++++--
 .../simple/ipv6snat/expected_sdn_interfaces      |  8 ++++++--
 5 files changed, 45 insertions(+), 13 deletions(-)


manager:

Lukas Sichert (1):
  fix #5066: reload networking: flush PROXMOX-SDN iptables chain at
    reload

 PVE/API2/Network.pm | 3 +++
 1 file changed, 3 insertions(+)


Summary over all repositories:
  6 files changed, 48 insertions(+), 13 deletions(-)

-- 
Generated by murpp 0.12.0




^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH network v2 1/3] fix #5066: snat: push evpn snat rules into separate iptables chain
  2026-06-23 13:37 [RFC manager/network v2 0/3] fix #5066: make generated snat rules flushable Lukas Sichert
@ 2026-06-23 13:37 ` Lukas Sichert
  2026-06-23 13:37 ` [PATCH network v2 2/3] fix #5066: snat: push simplezone " Lukas Sichert
  2026-06-23 13:37 ` [PATCH manager v2 3/3] fix #5066: reload networking: flush PROXMOX-SDN iptables chain at reload Lukas Sichert
  2 siblings, 0 replies; 4+ messages in thread
From: Lukas Sichert @ 2026-06-23 13:37 UTC (permalink / raw)
  To: pve-devel; +Cc: Lukas Sichert

When creating a Subnet with SNAT enabled and applying the changes, then
afterwards disabling SNAT and applying the changes, the rule still
persists in iptables. This is because ifreload -a only executes
(post/pre-)down hooks when an interface is removed from
/etc/network/interfaces, but the (post/pre-)up hooks are always
executed. As a result, the SNAT rule is not removed by 'ifreload -a' and
only a restart or 'ifdown' will remove it.

To be able to flush only the rules created by the Proxmox stack, add a
separate 'PROXMOX-SDN' chain to the iptables nat table, if a plugin
needs SNAT rules. Then add a jump from POSTROUTING to the new chain and
append all SNAT rules to the new chain. The new chain can then be
flushed separately.

Signed-off-by: Lukas Sichert <l.sichert@proxmox.com>
Link: https://bugzilla.proxmox.com/show_bug.cgi?id=5066
---
 src/PVE/Network/SDN/Zones/EvpnPlugin.pm          | 11 +++++++++--
 .../evpn/exitnode_snat/expected_sdn_interfaces   | 16 ++++++++++++----
 2 files changed, 21 insertions(+), 6 deletions(-)

diff --git a/src/PVE/Network/SDN/Zones/EvpnPlugin.pm b/src/PVE/Network/SDN/Zones/EvpnPlugin.pm
index dfbd7e9..b1bfd56 100644
--- a/src/PVE/Network/SDN/Zones/EvpnPlugin.pm
+++ b/src/PVE/Network/SDN/Zones/EvpnPlugin.pm
@@ -273,10 +273,17 @@ sub generate_sdn_config {
                 PVE::Network::SDN::Zones::Plugin::get_local_route_ip($checkrouteip);
             if ($outip && $outiface && $is_evpn_gateway) {
                 #use snat, faster than masquerade
+                push @iface_config, "post-up $iptables -t nat -N PROXMOX-SDN 2>/dev/null || true";
                 push @iface_config,
-                    "post-up $iptables -t nat -A POSTROUTING -s '$cidr' -o $outiface -j SNAT --to-source $outip";
+                    "post-up $iptables -t nat -C POSTROUTING -j PROXMOX-SDN 2>/dev/null || $iptables -t nat -A POSTROUTING -j PROXMOX-SDN";
+                push @iface_config, "post-down $iptables -t nat -N PROXMOX-SDN 2>/dev/null || true";
                 push @iface_config,
-                    "post-down $iptables -t nat -D POSTROUTING -s '$cidr' -o $outiface -j SNAT --to-source $outip";
+                    "post-down $iptables -t nat -D POSTROUTING -j PROXMOX-SDN 2>/dev/null || true";
+
+                push @iface_config,
+                    "post-up $iptables -t nat -A PROXMOX-SDN -s '$cidr' -o $outiface -j SNAT --to-source $outip";
+                push @iface_config,
+                    "post-down $iptables -t nat -D PROXMOX-SDN -s '$cidr' -o $outiface -j SNAT --to-source $outip";
                 #add conntrack zone once on outgoing interface
                 push @iface_config,
                     "post-up $iptables -t raw -I PREROUTING -i fwbr+ -j CT --zone 1";
diff --git a/src/test/zones/evpn/exitnode_snat/expected_sdn_interfaces b/src/test/zones/evpn/exitnode_snat/expected_sdn_interfaces
index 0d7d174..2905f20 100644
--- a/src/test/zones/evpn/exitnode_snat/expected_sdn_interfaces
+++ b/src/test/zones/evpn/exitnode_snat/expected_sdn_interfaces
@@ -3,8 +3,12 @@
 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-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 nat -N PROXMOX-SDN 2>/dev/null || true
+	post-up iptables -t nat -C POSTROUTING -j PROXMOX-SDN 2>/dev/null || iptables -t nat -A POSTROUTING -j PROXMOX-SDN
+	post-down iptables -t nat -N PROXMOX-SDN 2>/dev/null || true
+	post-down iptables -t nat -D POSTROUTING -j PROXMOX-SDN 2>/dev/null || true
+	post-up iptables -t nat -A PROXMOX-SDN -s '10.0.0.0/24' -o vmbr0 -j SNAT --to-source 192.168.0.1
+	post-down iptables -t nat -D PROXMOX-SDN -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-down iptables -t raw -D PREROUTING -i fwbr+ -j CT --zone 1
 	bridge_ports vxlan_myvnet
@@ -18,8 +22,12 @@ 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-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 nat -N PROXMOX-SDN 2>/dev/null || true
+	post-up ip6tables -t nat -C POSTROUTING -j PROXMOX-SDN 2>/dev/null || ip6tables -t nat -A POSTROUTING -j PROXMOX-SDN
+	post-down ip6tables -t nat -N PROXMOX-SDN 2>/dev/null || true
+	post-down ip6tables -t nat -D POSTROUTING -j PROXMOX-SDN 2>/dev/null || true
+	post-up ip6tables -t nat -A PROXMOX-SDN -s '2a08:2142:302:3::/64' -o vmbr0 -j SNAT --to-source 2001:db8::2
+	post-down ip6tables -t nat -D PROXMOX-SDN -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-down ip6tables -t raw -D PREROUTING -i fwbr+ -j CT --zone 1
 	bridge_ports vxlan_myvnet2
-- 
2.47.3





^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH network v2 2/3] fix #5066: snat: push simplezone snat rules into separate iptables chain
  2026-06-23 13:37 [RFC manager/network v2 0/3] fix #5066: make generated snat rules flushable Lukas Sichert
  2026-06-23 13:37 ` [PATCH network v2 1/3] fix #5066: snat: push evpn snat rules into separate iptables chain Lukas Sichert
@ 2026-06-23 13:37 ` Lukas Sichert
  2026-06-23 13:37 ` [PATCH manager v2 3/3] fix #5066: reload networking: flush PROXMOX-SDN iptables chain at reload Lukas Sichert
  2 siblings, 0 replies; 4+ messages in thread
From: Lukas Sichert @ 2026-06-23 13:37 UTC (permalink / raw)
  To: pve-devel; +Cc: Lukas Sichert

When creating a Subnet with SNAT enabled and applying the changes, then
afterwards disabling SNAT and applying the changes, the rule still
persists in iptables. This is because ifreload -a only executes
(post/pre-)down hooks when an interface is removed from
/etc/network/interfaces, but the (post/pre-)up hooks are always
executed. As a result, the SNAT rule is not removed by 'ifreload -a' and
only a restart or 'ifdown' will remove it.

To be able to flush only the rules created by the Proxmox stack, add a
separate 'PROXMOX-SDN' chain to the iptables nat table, if a plugin
needs SNAT rules. Then add a jump from POSTROUTING to the new chain and
append all SNAT rules to the new chain. The new chain can then be
flushed separately.

Signed-off-by: Lukas Sichert <l.sichert@proxmox.com>
Link: https://bugzilla.proxmox.com/show_bug.cgi?id=5066
---
 src/PVE/Network/SDN/Zones/SimplePlugin.pm         | 15 ++++++++++++---
 .../zones/simple/ipv4snat/expected_sdn_interfaces |  8 ++++++--
 .../zones/simple/ipv6snat/expected_sdn_interfaces |  8 ++++++--
 3 files changed, 24 insertions(+), 7 deletions(-)

diff --git a/src/PVE/Network/SDN/Zones/SimplePlugin.pm b/src/PVE/Network/SDN/Zones/SimplePlugin.pm
index f5cd18e..29943e8 100644
--- a/src/PVE/Network/SDN/Zones/SimplePlugin.pm
+++ b/src/PVE/Network/SDN/Zones/SimplePlugin.pm
@@ -114,12 +114,22 @@ sub generate_sdn_config {
             #find outgoing interface
             my ($outip, $outiface) =
                 PVE::Network::SDN::Zones::Plugin::get_local_route_ip($checkrouteip);
+
+            push @iface_config, "post-up $iptables -t nat -N PROXMOX-SDN 2>/dev/null || true";
+            push @iface_config,
+                "post-up $iptables -t nat -C POSTROUTING -j PROXMOX-SDN 2>/dev/null || $iptables -t nat -A POSTROUTING -j PROXMOX-SDN";
+
+            push @iface_config, "post-down $iptables -t nat -N PROXMOX-SDN 2>/dev/null || true";
+            push @iface_config,
+                "post-down $iptables -t nat -D POSTROUTING -j PROXMOX-SDN 2>/dev/null || true";
             if ($outip && $outiface) {
                 #use snat, faster than masquerade
                 push @iface_config,
-                    "post-up $iptables -t nat -A POSTROUTING -s '$cidr' -o $outiface -j SNAT --to-source $outip";
+                    "post-up $iptables -t nat -A PROXMOX-SDN -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-down $iptables -t nat -D PROXMOX-SDN -s '$cidr' -o $outiface -j SNAT --to-source $outip";
+
                 #add conntrack zone once on outgoing interface
                 push @iface_config,
                     "post-up $iptables -t raw -I PREROUTING -i fwbr+ -j CT --zone 1";
@@ -168,4 +178,3 @@ sub get_mtu {
 }
 
 1;
-
diff --git a/src/test/zones/simple/ipv4snat/expected_sdn_interfaces b/src/test/zones/simple/ipv4snat/expected_sdn_interfaces
index 69d7986..34488eb 100644
--- a/src/test/zones/simple/ipv4snat/expected_sdn_interfaces
+++ b/src/test/zones/simple/ipv4snat/expected_sdn_interfaces
@@ -3,8 +3,12 @@
 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-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 nat -N PROXMOX-SDN 2>/dev/null || true
+	post-up iptables -t nat -C POSTROUTING -j PROXMOX-SDN 2>/dev/null || iptables -t nat -A POSTROUTING -j PROXMOX-SDN
+	post-down iptables -t nat -N PROXMOX-SDN 2>/dev/null || true
+	post-down iptables -t nat -D POSTROUTING -j PROXMOX-SDN 2>/dev/null || true
+	post-up iptables -t nat -A PROXMOX-SDN -s '10.0.0.0/24' -o vmbr0 -j SNAT --to-source 192.168.0.1
+	post-down iptables -t nat -D PROXMOX-SDN -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-down iptables -t raw -D PREROUTING -i fwbr+ -j CT --zone 1
 	bridge_ports none
diff --git a/src/test/zones/simple/ipv6snat/expected_sdn_interfaces b/src/test/zones/simple/ipv6snat/expected_sdn_interfaces
index 5f6d40b..c5cdfd1 100644
--- a/src/test/zones/simple/ipv6snat/expected_sdn_interfaces
+++ b/src/test/zones/simple/ipv6snat/expected_sdn_interfaces
@@ -3,8 +3,12 @@
 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-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 nat -N PROXMOX-SDN 2>/dev/null || true
+	post-up ip6tables -t nat -C POSTROUTING -j PROXMOX-SDN 2>/dev/null || ip6tables -t nat -A POSTROUTING -j PROXMOX-SDN
+	post-down ip6tables -t nat -N PROXMOX-SDN 2>/dev/null || true
+	post-down ip6tables -t nat -D POSTROUTING -j PROXMOX-SDN 2>/dev/null || true
+	post-up ip6tables -t nat -A PROXMOX-SDN -s '2a08:2142:302:3::/64' -o vmbr0 -j SNAT --to-source 2001:db8::2
+	post-down ip6tables -t nat -D PROXMOX-SDN -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-down ip6tables -t raw -D PREROUTING -i fwbr+ -j CT --zone 1
 	bridge_ports none
-- 
2.47.3





^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH manager v2 3/3] fix #5066: reload networking: flush PROXMOX-SDN iptables chain at reload
  2026-06-23 13:37 [RFC manager/network v2 0/3] fix #5066: make generated snat rules flushable Lukas Sichert
  2026-06-23 13:37 ` [PATCH network v2 1/3] fix #5066: snat: push evpn snat rules into separate iptables chain Lukas Sichert
  2026-06-23 13:37 ` [PATCH network v2 2/3] fix #5066: snat: push simplezone " Lukas Sichert
@ 2026-06-23 13:37 ` Lukas Sichert
  2 siblings, 0 replies; 4+ messages in thread
From: Lukas Sichert @ 2026-06-23 13:37 UTC (permalink / raw)
  To: pve-devel; +Cc: Lukas Sichert

With the addition of a separate PROXMOX-SDN iptables chain it is now
possible to flush this distinct chain, without affecting unrelated
POSTROUTING rules set by the user.

Flush old rules by running 'iptables -t nat -F PROXMOX-SDN' at the end
of the reload_network_config API call, before executing 'ifreload -a'.
The reload then adds the currently valid rules again.

Signed-off-by: Lukas Sichert <l.sichert@proxmox.com>
Link: https://bugzilla.proxmox.com/show_bug.cgi?id=5066
---
 PVE/API2/Network.pm | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/PVE/API2/Network.pm b/PVE/API2/Network.pm
index c5863ca7..a8ec88a4 100644
--- a/PVE/API2/Network.pm
+++ b/PVE/API2/Network.pm
@@ -932,6 +932,9 @@ __PACKAGE__->register_method({
                     print "$2 : $line \n";
                 }
             };
+            PVE::Tools::run_command(['iptables', '-t', 'nat', '-F', 'PROXMOX-SDN'], noerr => 1);
+            PVE::Tools::run_command(['ip6tables', '-t', 'nat', '-F', 'PROXMOX-SDN'],
+                noerr => 1);
             PVE::Tools::run_command(['ifreload', '-a'], errfunc => $err);
 
             if (defined($regenerate_frr)) {
-- 
2.47.3





^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-06-23 13:38 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-23 13:37 [RFC manager/network v2 0/3] fix #5066: make generated snat rules flushable Lukas Sichert
2026-06-23 13:37 ` [PATCH network v2 1/3] fix #5066: snat: push evpn snat rules into separate iptables chain Lukas Sichert
2026-06-23 13:37 ` [PATCH network v2 2/3] fix #5066: snat: push simplezone " Lukas Sichert
2026-06-23 13:37 ` [PATCH manager v2 3/3] fix #5066: reload networking: flush PROXMOX-SDN iptables chain at reload Lukas Sichert

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal