public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [PATCH many v5 00/10] sdn: enable force_forwarding for ipv6 forwarding
@ 2026-11-04 14:55 Lukas Sichert
  2026-11-04 14:55 ` [PATCH ifupdown2 v5 01/10] d/patches: add ipv6 force_forwarding for bridges Lukas Sichert
                   ` (9 more replies)
  0 siblings, 10 replies; 11+ messages in thread
From: Lukas Sichert @ 2026-11-04 14:55 UTC (permalink / raw)
  To: pve-devel; +Cc: Lukas Sichert

Gabriel's upstream kernel patch [1] added
net.ipv6.conf.<iface>.force_forwarding. This allows enabling IPv6
forwarding on selected interfaces without requiring
net.ipv6.conf.all.forwarding.

This is useful for SDN setups because all.forwarding has host-wide side
effects. In particular, it disables Router Advertisement processing by
default, which can break SLAAC on unrelated interfaces.

This series introduces the force_forwarding kernel flag to ifupdown2,
where it mimics the activation behaviour of the classic forwarding flag.
This means force_forwarding is only turned off for a bridge if all of
the following hold:
  - it has no upper interfaces, such as an SVI or VLAN slave interface
  - it has no address configured
  - its address method is set and it is not auto or dhcp
This is consistent with current behaviour and important for the bridges
created manually as their flags are not set by the ifupdown2 configs
generated in pve-network. This also means for a lot of bridges in the
SDN stack force_forwarding is enabled by default, still the
ip6-force-forward stanzas will be written to interfaces.d/sdn to
activate it explicitly.

The series also adds missing stanzas for ipv4 and ipv6 forwarding, where
the enabling came from ifupdown2 default behaviour only.

The tests are adjusted for the generated /etc/network/interfaces.d/sdn
output. Also the series rewrites the documentation to reflect the
updated behaviour and removes the UI warning to enable 'all.forwarding'.

Note: ifupdown2 needs to be applied first, then perl-rs, then network
(its tests render fabrics via perl-rs). Needs kernel >= 6.17, older
kernels ignore 'ip6-force-forward' with a warning.

[1] lkml.org/lkml/2025/7/7/577

Changes from v4 to v5 (thanks @Hannes):

-add flags and automated behaviour for activation of force_forwarding
  in ifupdown2
-remove the post-up/post down stanzas and use the ifupdown2 flags to 
  explicitly enable forwarding instead

changes from v3 to v4 (thanks @Stefan):
-wrap resolving outgoing interface in eval block
-remove unnecessary 'ip6-forward' for vrfbr interface
-Drop the fabric edit GUI hint that told users to enable
net.ipv6.conf.all.forwarding
-remove whitespace formatting changes

changes from v2 to v3 (thanks @Gabriel):
-Move the IPv6 force_forwarding post-up/post-down commands out of the
subnet loop, so they are generated only once for the VNet instead of
once per subnet.
-Enable ip6-forward and force_forwarding on EVPN L3VNI VRF bridge
interfaces, fixing IPv6 forwarding when traffic exits through another
node.

changes from v1 to v2 (thanks @Gabriel, @Hannes):
-add force_forwarding also to bgp fabrics
-explicitly mention the force_forwarding flag in the documentation
-add a reference link to the sysctl documentation
-mention bgp as a fabric with ipv6 support


ifupdown2:

Lukas Sichert (1):
  d/patches: add ipv6 force_forwarding for bridges

 ...set-ipv6-force_forwarding-on-bridges.patch | 197 ++++++++++++++++++
 debian/patches/series                         |   1 +
 2 files changed, 198 insertions(+)
 create mode 100644 debian/patches/pve/0017-addons-address-set-ipv6-force_forwarding-on-bridges.patch


network:

Lukas Sichert (4):
  sdn: evpn: enable ip forwarding on the vrf bridge interface
  sdn: evpn: enable force_forwarding for ipv6 forwarding to subnets
  sdn: simple: enable force_forwarding for ipv6 forwarding to subnets
  tests: sdn: update fabric expectations for explicit ipv6 forwarding

 src/PVE/Network/SDN/Zones/EvpnPlugin.pm        | 18 +++++++++++++++++-
 src/PVE/Network/SDN/Zones/SimplePlugin.pm      |  6 +++++-
 .../advertise_subnets/expected_sdn_interfaces  |  1 +
 .../evpn/auto_mode/expected_sdn_interfaces     |  1 +
 .../evpn/bgp_ebgp/expected_sdn_interfaces      |  1 +
 .../bgp_ebgp_multihop/expected_sdn_interfaces  |  1 +
 .../expected_sdn_interfaces                    |  1 +
 .../expected_sdn_interfaces                    |  1 +
 .../evpn/bgp_fabric/expected_sdn_interfaces    |  3 +++
 .../evpn/bgp_loopback/expected_sdn_interfaces  |  1 +
 .../expected_sdn_interfaces                    |  1 +
 .../combined_bgp_isis/expected_sdn_interfaces  |  1 +
 .../expected_sdn_interfaces                    |  1 +
 .../zones/evpn/ebgp/expected_sdn_interfaces    |  1 +
 .../expected_sdn_interfaces                    |  1 +
 .../expected_sdn_interfaces                    |  1 +
 .../expected_sdn_interfaces                    |  2 ++
 .../evpn/ebgp_loopback/expected_sdn_interfaces |  1 +
 .../expected_sdn_interfaces                    |  1 +
 .../expected_sdn_interfaces                    |  2 ++
 .../evpn/exitnode/expected_sdn_interfaces      |  1 +
 .../expected_sdn_interfaces                    |  1 +
 .../expected_sdn_interfaces                    |  3 +++
 .../exitnode_primary/expected_sdn_interfaces   |  1 +
 .../evpn/exitnode_snat/expected_sdn_interfaces |  4 ++++
 .../exitnodenullroute/expected_sdn_interfaces  |  5 +++++
 .../frr_local_merge/expected_sdn_interfaces    |  1 +
 .../expected_sdn_interfaces                    |  1 +
 .../zones/evpn/ipv4/expected_sdn_interfaces    |  1 +
 .../evpn/ipv4ipv6/expected_sdn_interfaces      |  4 ++++
 .../zones/evpn/ipv6/expected_sdn_interfaces    |  3 +++
 .../evpn/ipv6underlay/expected_sdn_interfaces  |  3 +++
 .../zones/evpn/isis/expected_sdn_interfaces    |  1 +
 .../evpn/isis_loopback/expected_sdn_interfaces |  1 +
 .../multipath_relax/expected_sdn_interfaces    |  1 +
 .../evpn/multiplezones/expected_sdn_interfaces |  2 ++
 .../openfabric_fabric/expected_sdn_interfaces  |  1 +
 .../expected_sdn_interfaces                    |  3 +++
 .../expected_sdn_interfaces                    |  7 +++++++
 .../evpn/ospf_fabric/expected_sdn_interfaces   |  1 +
 .../evpn/routemap/expected_sdn_interfaces      |  1 +
 .../routemap_exit_node/expected_sdn_interfaces |  1 +
 .../expected_sdn_interfaces                    |  1 +
 .../evpn/rt_import/expected_sdn_interfaces     |  1 +
 .../evpn/vxlanport/expected_sdn_interfaces     |  1 +
 .../simple/ipv4v6/expected_sdn_interfaces      |  1 +
 .../simple/ipv6snat/expected_sdn_interfaces    |  1 +
 47 files changed, 96 insertions(+), 2 deletions(-)


perl-rs:

Lukas Sichert (3):
  openfabric: set ip6-forward explicitly
  fabrics: openfabric: enable force_forwarding for ipv6 transit traffic
  fabrics: bgp: enable force_forwarding for ipv6 transit traffic

 pve-rs/src/bindings/sdn/fabrics.rs | 5 +++++
 1 file changed, 5 insertions(+)


manager:

Lukas Sichert (1):
  ui: sdn: remove IPv6 forwarding hint from fabric edit window

 www/manager6/sdn/fabrics/FabricEdit.js | 59 +++++++++-----------------
 1 file changed, 20 insertions(+), 39 deletions(-)


docs:

Lukas Sichert (1):
  sdn: drop global ipv6 forwarding workaround from docs

 pvesdn.adoc | 27 +++++++--------------------
 1 file changed, 7 insertions(+), 20 deletions(-)


Summary over all repositories:
  52 files changed, 326 insertions(+), 61 deletions(-)

-- 
Generated by murpp 0.12.0




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

* [PATCH ifupdown2 v5 01/10] d/patches: add ipv6 force_forwarding for bridges
  2026-11-04 14:55 [PATCH many v5 00/10] sdn: enable force_forwarding for ipv6 forwarding Lukas Sichert
@ 2026-11-04 14:55 ` Lukas Sichert
  2026-11-04 14:55 ` [PATCH network v5 02/10] sdn: evpn: enable ip forwarding on the vrf bridge interface Lukas Sichert
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Lukas Sichert @ 2026-11-04 14:55 UTC (permalink / raw)
  To: pve-devel; +Cc: Lukas Sichert

The per-interface ipv6 forwarding sysctl does not make the kernel
forward packets on that interface, but force_forwarding does (since
Linux 6.17). Set it on bridges whenever forwarding is set, and add an
ip6-force-forward option to configure it manually.

Signed-off-by: Lukas Sichert <l.sichert@proxmox.com>
---
 ...set-ipv6-force_forwarding-on-bridges.patch | 197 ++++++++++++++++++
 debian/patches/series                         |   1 +
 2 files changed, 198 insertions(+)
 create mode 100644 debian/patches/pve/0017-addons-address-set-ipv6-force_forwarding-on-bridges.patch

diff --git a/debian/patches/pve/0017-addons-address-set-ipv6-force_forwarding-on-bridges.patch b/debian/patches/pve/0017-addons-address-set-ipv6-force_forwarding-on-bridges.patch
new file mode 100644
index 0000000..b483f2d
--- /dev/null
+++ b/debian/patches/pve/0017-addons-address-set-ipv6-force_forwarding-on-bridges.patch
@@ -0,0 +1,197 @@
+From abac8c62aa62eabc0622fc7102d3bc808571f3d6 Mon Sep 17 00:00:00 2001
+From: Lukas Sichert <l.sichert@proxmox.com>
+Date: Fri, 23 Oct 2026 11:22:05 +0200
+Subject: [PATCH] addons: address: set ipv6 force_forwarding on bridges
+
+The per-interface ipv6 "forwarding" sysctl does not make the kernel
+forward packets on that interface. Since Linux 6.17 the per-interface
+force_forwarding sysctl does.
+
+Enable it for bridges with an address, an upper interface or dhcp and
+clear it otherwise. The kernel does not report force_forwarding in its
+netconf messages, so the value is read from /proc. Kernels without the
+sysctl are skipped. Additionally introduce a flag to configure it
+manually.
+
+Signed-off-by: Lukas Sichert <l.sichert@proxmox.com>
+---
+ ifupdown2/addons/address.py | 90 +++++++++++++++++++++++++++++++++++++
+ 1 file changed, 90 insertions(+)
+
+diff --git a/ifupdown2/addons/address.py b/ifupdown2/addons/address.py
+index 46226a9..b26d610 100644
+--- a/ifupdown2/addons/address.py
++++ b/ifupdown2/addons/address.py
+@@ -167,6 +167,12 @@ class address(AddonWithIpBlackList, moduleBase):
+                 'default': 'off',
+                 'example': ['ip6-forward off']
+             },
++            'ip6-force-forward': {
++                'help': 'ipv6 force_forwarding flag',
++                'validvals': ['on', 'off', 'yes', 'no', '0', '1'],
++                'default': 'off',
++                'example': ['ip6-force-forward off']
++            },
+             'mpls-enable': {
+                 'help': 'mpls enable flag',
+                 'validvals': ['yes', 'no'],
+@@ -217,6 +223,8 @@ class address(AddonWithIpBlackList, moduleBase):
+         self._bridge_fdb_query_cache = {}
+         self.ipforward = policymanager.policymanager_api.get_attr_default(module_name=self.__class__.__name__, attr='ip-forward')
+         self.ip6forward = policymanager.policymanager_api.get_attr_default(module_name=self.__class__.__name__, attr='ip6-forward')
++        self.ip6forceforward = policymanager.policymanager_api.get_attr_default(module_name=self.__class__.__name__, attr='ip6-force-forward')
++        self.ip6forceforward_supported = (self.read_file_oneline('/proc/sys/net/ipv6/conf/default/force_forwarding') is not None)
+         self.ifaces_defaults = policymanager.policymanager_api.get_iface_defaults(module_name=self.__class__.__name__)
+         self.enable_l3_iface_forwarding_checks = utils.get_boolean_from_string(
+             policymanager.policymanager_api.get_module_globals(
+@@ -371,6 +379,12 @@ class address(AddonWithIpBlackList, moduleBase):
+                 self.logger.error("%s: misconfiguration: disabling ip6 forwarding on an l3-svi is not allowed" % ifaceobj.name)
+                 return False
+ 
++            ip6_force_forward = ifaceobj.get_attr_value_first("ip6-force-forward")
++
++            if ip6_force_forward and not utils.get_boolean_from_string(ip6_force_forward) and self.ip6forceforward_supported:
++                self.logger.error("%s: misconfiguration: disabling ip6 force_forwarding on an l3-svi is not allowed" % ifaceobj.name)
++                return False
++
+         return True
+ 
+     def syntax_check_enable_l3_iface_forwardings(self, ifaceobj, ifaceobj_getfunc, syntax_check=False):
+@@ -420,6 +434,11 @@ class address(AddonWithIpBlackList, moduleBase):
+             result = False
+             self.log_error('%s: \'ip6-forward\' is not supported for '
+                            'bridge port' %ifaceobj.name)
++        ip6forceforward = ifaceobj.get_attr_value_first('ip6-force-forward')
++        if bridge_port and ip6forceforward:
++            result = False
++            self.log_error('%s: \'ip6-force-forward\' is not supported for '
++                           'bridge port' %ifaceobj.name)
+         return result
+ 
+     def syntax_check_mtu(self, ifaceobj, ifaceobj_getfunc):
+@@ -955,15 +974,29 @@ class address(AddonWithIpBlackList, moduleBase):
+                 self.sysctl_write_forwarding_value_to_proc(ifname, "ipv4", 0)
+             if netconf_ipv6_forwarding:
+                 self.sysctl_write_forwarding_value_to_proc(ifname, "ipv6", 0)
++            self.sysctl_write_force_forwarding_value_to_proc(ifname, 0)
+         else:
+             if not netconf_ipv4_forwarding:
+                 self.sysctl_write_forwarding_value_to_proc(ifname, "ipv4", 1)
+             if not netconf_ipv6_forwarding:
+                 self.sysctl_write_forwarding_value_to_proc(ifname, "ipv6", 1)
++            self.sysctl_write_force_forwarding_value_to_proc(ifname, 1)
++
++    def get_ip6_force_forwarding(self, ifname):
++        # kernels <6.17 do not support force_forwarding
++        if not self.ip6forceforward_supported:
++            return None
++        value = self.read_file_oneline("/proc/sys/net/ipv6/conf/%s/force_forwarding" % ifname)
++        return None if value is None else int(value)
+ 
+     def sysctl_write_forwarding_value_to_proc(self, ifname, family, value):
+         self.write_file("/proc/sys/net/%s/conf/%s/forwarding" % (family, ifname), "%s\n" % value)
+ 
++    def sysctl_write_force_forwarding_value_to_proc(self, ifname, value):
++        current = self.get_ip6_force_forwarding(ifname)
++        if current is not None and current != value:
++            self.write_file("/proc/sys/net/ipv6/conf/%s/force_forwarding" % ifname, "%s\n" % value)
++
+     def _sysctl_slaac(self, ifaceobj):
+         ifacename = self.cache.link_translate_altname(ifaceobj.name)
+         addr_method = ifaceobj.addr_method
+@@ -1037,6 +1070,7 @@ class address(AddonWithIpBlackList, moduleBase):
+ 
+         ipforward = ifaceobj.get_attr_value_first('ip-forward')
+         ip6forward = ifaceobj.get_attr_value_first('ip6-forward')
++        ip6forceforward = ifaceobj.get_attr_value_first('ip6-force-forward')
+         if ifupdownflags.flags.PERFMODE:
+             if ipforward:
+                 self.sysctl_set('net.ipv4.conf.%s.forwarding'
+@@ -1046,7 +1080,17 @@ class address(AddonWithIpBlackList, moduleBase):
+                 self.sysctl_set('net.ipv6.conf.%s.forwarding'
+                                 %('/'.join(ifacename.split("."))),
+                                 utils.boolean_support_binary(ip6forward))
++            if ip6forceforward and self.ip6forceforward_supported:
++                self.sysctl_set('net.ipv6.conf.%s.force_forwarding'
++                                %('/'.join(ifacename.split("."))),
++                                utils.boolean_support_binary(ip6forceforward))
+             return
++
++        if ip6forceforward and not self.ip6forceforward_supported:
++            self.log_warn('%s: \'ip6-force-forward\' is not supported by '
++                          'this kernel (needs >= 6.17)' % ifacename)
++            ip6forceforward = None
++
+         bridge_port = ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT
+         if bridge_port:
+             if ipforward:
+@@ -1055,6 +1099,9 @@ class address(AddonWithIpBlackList, moduleBase):
+             if ip6forward:
+                 self.log_error('%s: \'ip6-forward\' is not supported for '
+                                'bridge port' %ifacename)
++            if ip6forceforward:
++                self.log_error('%s: \'ip6-force-forward\' is not supported for '
++                               'bridge port' %ifacename)
+             return
+         setting_default_value = False
+         if not ipforward:
+@@ -1096,6 +1143,27 @@ class address(AddonWithIpBlackList, moduleBase):
+                        ifaceobj.status = ifaceStatus.ERROR
+                        self.logger.error('%s: %s' %(ifacename, str(e)))
+ 
++        setting_default_value = False
++
++        if not ip6forceforward and self.ip6forceforward_supported:
++            setting_default_value = True
++            ip6forceforward = self.ip6forceforward
++
++        if ip6forceforward:
++            ip6forceforward = int(utils.get_boolean_from_string(ip6forceforward))
++            running_ip6forceforward = self.get_ip6_force_forwarding(ifacename)
++            if ip6forceforward != running_ip6forceforward:
++                try:
++                    self.sysctl_set('net.ipv6.conf.%s.force_forwarding'
++                                    %('/'.join(ifacename.split("."))),
++                                    ip6forceforward)
++                except Exception as e:
++                    # Analogous to ipv6 forwarding ipv6 force_forwarding
++                    # may only be configured if ipv6 is enabled
++                    if not setting_default_value:
++                       ifaceobj.status = ifaceStatus.ERROR
++                       self.logger.error('%s: %s' %(ifacename, str(e)))
++
+         self._sysctl_slaac(ifaceobj)
+ 
+     def process_mtu(self, ifaceobj, ifaceobj_getfunc):
+@@ -1471,6 +1539,28 @@ class address(AddonWithIpBlackList, moduleBase):
+                     'on' if running_ip6forward else 'off',
+                     running_ip6forward != config_ip6forward
+                 )
++
++        ip6forceforward = ifaceobj.get_attr_value_first('ip6-force-forward')
++        if ip6forceforward:
++            if bridge_port:
++                ifaceobjcurr.status = ifaceStatus.ERROR
++                ifaceobjcurr.status_str = ('\'ip6-force-forward\' not supported ' +
++                                           'for bridge port')
++                ifaceobjcurr.update_config_with_status('ip6-force-forward', 1, None)
++            elif not self.ip6forceforward_supported:
++                ifaceobjcurr.status = ifaceStatus.ERROR
++                ifaceobjcurr.status_str = ('\'ip6-force-forward\' is not supported ' +
++                                           'by this kernel (needs >= 6.17)')
++                ifaceobjcurr.update_config_with_status('ip6-force-forward', 1, None)
++            else:
++                running_ip6forceforward = self.get_ip6_force_forwarding(ifaceobj.name)
++                config_ip6forceforward = utils.get_boolean_from_string(ip6forceforward)
++                ifaceobjcurr.update_config_with_status(
++                    'ip6-force-forward',
++                    'on' if running_ip6forceforward else 'off',
++                    running_ip6forceforward != config_ip6forceforward
++                )
++
+         mpls_enable = ifaceobj.get_attr_value_first('mpls-enable');
+         if mpls_enable:
+             running_mpls_enable = utils.get_yesno_from_onezero(str(self.cache.get_netconf_mpls_input(ifaceobj.name)))
+-- 
+2.47.3
+
diff --git a/debian/patches/series b/debian/patches/series
index ee2cb46..1eea3d0 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -17,3 +17,4 @@ upstream/0002-vxlan-add-support-for-IPv6-vxlan-local-tunnelip.patch
 pve/0014-nlmanager-read-ipv6-devconf-disable_ipv6-attribute-t.patch
 pve/0015-revert-addons-bond-warn-if-sub-interface-is-detected-on-bond-slave.patch
 pve/0016-addons-vxlan-add-vxlan-gbp-attribute.patch
+pve/0017-addons-address-set-ipv6-force_forwarding-on-bridges.patch
-- 
2.47.3





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

* [PATCH network v5 02/10] sdn: evpn: enable ip forwarding on the vrf bridge interface
  2026-11-04 14:55 [PATCH many v5 00/10] sdn: enable force_forwarding for ipv6 forwarding Lukas Sichert
  2026-11-04 14:55 ` [PATCH ifupdown2 v5 01/10] d/patches: add ipv6 force_forwarding for bridges Lukas Sichert
@ 2026-11-04 14:55 ` Lukas Sichert
  2026-11-04 14:55 ` [PATCH network v5 03/10] sdn: evpn: enable force_forwarding for ipv6 forwarding to subnets Lukas Sichert
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Lukas Sichert @ 2026-11-04 14:55 UTC (permalink / raw)
  To: pve-devel; +Cc: Lukas Sichert

The vnet bridges already set ip-forward/ip6-forward explicitly. Do
the same for the vrf bridge shared by the vnets of a zone, so the
config is consistent and does not rely on the ifupdown2 default.

Signed-off-by: Lukas Sichert <l.sichert@proxmox.com>
---
 src/PVE/Network/SDN/Zones/EvpnPlugin.pm                | 10 ++++++++++
 .../evpn/advertise_subnets/expected_sdn_interfaces     |  1 +
 src/test/zones/evpn/auto_mode/expected_sdn_interfaces  |  1 +
 src/test/zones/evpn/bgp_ebgp/expected_sdn_interfaces   |  1 +
 .../evpn/bgp_ebgp_multihop/expected_sdn_interfaces     |  1 +
 .../bgp_ebgp_reverse_order/expected_sdn_interfaces     |  1 +
 .../expected_sdn_interfaces                            |  1 +
 src/test/zones/evpn/bgp_fabric/expected_sdn_interfaces |  1 +
 .../zones/evpn/bgp_loopback/expected_sdn_interfaces    |  1 +
 .../evpn/bgp_multipath_relax/expected_sdn_interfaces   |  1 +
 .../evpn/combined_bgp_isis/expected_sdn_interfaces     |  1 +
 .../disable_arp_nd_suppression/expected_sdn_interfaces |  1 +
 src/test/zones/evpn/ebgp/expected_sdn_interfaces       |  1 +
 .../expected_sdn_interfaces                            |  1 +
 .../expected_sdn_interfaces                            |  1 +
 .../expected_sdn_interfaces                            |  2 ++
 .../zones/evpn/ebgp_loopback/expected_sdn_interfaces   |  1 +
 .../expected_sdn_interfaces                            |  1 +
 .../expected_sdn_interfaces                            |  2 ++
 src/test/zones/evpn/exitnode/expected_sdn_interfaces   |  1 +
 .../exitnode_local_routing/expected_sdn_interfaces     |  1 +
 .../expected_sdn_interfaces                            |  1 +
 .../evpn/exitnode_primary/expected_sdn_interfaces      |  1 +
 .../zones/evpn/exitnode_snat/expected_sdn_interfaces   |  2 ++
 .../evpn/exitnodenullroute/expected_sdn_interfaces     |  3 +++
 .../zones/evpn/frr_local_merge/expected_sdn_interfaces |  1 +
 .../expected_sdn_interfaces                            |  1 +
 src/test/zones/evpn/ipv4/expected_sdn_interfaces       |  1 +
 src/test/zones/evpn/ipv4ipv6/expected_sdn_interfaces   |  2 ++
 src/test/zones/evpn/ipv6/expected_sdn_interfaces       |  1 +
 .../zones/evpn/ipv6underlay/expected_sdn_interfaces    |  1 +
 src/test/zones/evpn/isis/expected_sdn_interfaces       |  1 +
 .../zones/evpn/isis_loopback/expected_sdn_interfaces   |  1 +
 .../zones/evpn/multipath_relax/expected_sdn_interfaces |  1 +
 .../zones/evpn/multiplezones/expected_sdn_interfaces   |  2 ++
 .../evpn/openfabric_fabric/expected_sdn_interfaces     |  1 +
 .../openfabric_fabric_ipv6/expected_sdn_interfaces     |  1 +
 .../expected_sdn_interfaces                            |  1 +
 .../zones/evpn/ospf_fabric/expected_sdn_interfaces     |  1 +
 src/test/zones/evpn/routemap/expected_sdn_interfaces   |  1 +
 .../evpn/routemap_exit_node/expected_sdn_interfaces    |  1 +
 .../evpn/routemap_prefix_list/expected_sdn_interfaces  |  1 +
 src/test/zones/evpn/rt_import/expected_sdn_interfaces  |  1 +
 src/test/zones/evpn/vxlanport/expected_sdn_interfaces  |  1 +
 44 files changed, 60 insertions(+)

diff --git a/src/PVE/Network/SDN/Zones/EvpnPlugin.pm b/src/PVE/Network/SDN/Zones/EvpnPlugin.pm
index c31b6c6..8a45c51 100644
--- a/src/PVE/Network/SDN/Zones/EvpnPlugin.pm
+++ b/src/PVE/Network/SDN/Zones/EvpnPlugin.pm
@@ -349,6 +349,16 @@ sub generate_sdn_config {
             push @iface_config, "mtu $mtu" if $mtu;
             push @iface_config, "vrf $vrf_iface";
             push(@{ $config->{$brvrf} }, @iface_config) if !$config->{$brvrf};
+
+            my @forward_opts;
+            push @forward_opts, 'ip-forward on' if $enable_forward_v4;
+            if ($enable_forward_v6) {
+                push @forward_opts, 'ip6-forward on';
+            }
+            for my $opt (@forward_opts) {
+                my $opt_absent = !grep { $_ eq $opt } @{ $config->{$brvrf} };
+                push @{ $config->{$brvrf} }, $opt if $opt_absent;
+            }
         }
 
         if ($is_evpn_gateway && $exitnodes_local_routing) {
diff --git a/src/test/zones/evpn/advertise_subnets/expected_sdn_interfaces b/src/test/zones/evpn/advertise_subnets/expected_sdn_interfaces
index 9d1c64c..a7a232b 100644
--- a/src/test/zones/evpn/advertise_subnets/expected_sdn_interfaces
+++ b/src/test/zones/evpn/advertise_subnets/expected_sdn_interfaces
@@ -24,6 +24,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/auto_mode/expected_sdn_interfaces b/src/test/zones/evpn/auto_mode/expected_sdn_interfaces
index 4cf13e0..ee0ad41 100644
--- a/src/test/zones/evpn/auto_mode/expected_sdn_interfaces
+++ b/src/test/zones/evpn/auto_mode/expected_sdn_interfaces
@@ -23,6 +23,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/bgp_ebgp/expected_sdn_interfaces b/src/test/zones/evpn/bgp_ebgp/expected_sdn_interfaces
index 4cf13e0..ee0ad41 100644
--- a/src/test/zones/evpn/bgp_ebgp/expected_sdn_interfaces
+++ b/src/test/zones/evpn/bgp_ebgp/expected_sdn_interfaces
@@ -23,6 +23,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/bgp_ebgp_multihop/expected_sdn_interfaces b/src/test/zones/evpn/bgp_ebgp_multihop/expected_sdn_interfaces
index 4cf13e0..ee0ad41 100644
--- a/src/test/zones/evpn/bgp_ebgp_multihop/expected_sdn_interfaces
+++ b/src/test/zones/evpn/bgp_ebgp_multihop/expected_sdn_interfaces
@@ -23,6 +23,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/bgp_ebgp_reverse_order/expected_sdn_interfaces b/src/test/zones/evpn/bgp_ebgp_reverse_order/expected_sdn_interfaces
index 4cf13e0..ee0ad41 100644
--- a/src/test/zones/evpn/bgp_ebgp_reverse_order/expected_sdn_interfaces
+++ b/src/test/zones/evpn/bgp_ebgp_reverse_order/expected_sdn_interfaces
@@ -23,6 +23,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/bgp_evpn_routemap_prefix_list/expected_sdn_interfaces b/src/test/zones/evpn/bgp_evpn_routemap_prefix_list/expected_sdn_interfaces
index 4cf13e0..ee0ad41 100644
--- a/src/test/zones/evpn/bgp_evpn_routemap_prefix_list/expected_sdn_interfaces
+++ b/src/test/zones/evpn/bgp_evpn_routemap_prefix_list/expected_sdn_interfaces
@@ -23,6 +23,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/bgp_fabric/expected_sdn_interfaces b/src/test/zones/evpn/bgp_fabric/expected_sdn_interfaces
index fd16853..d321e7b 100644
--- a/src/test/zones/evpn/bgp_fabric/expected_sdn_interfaces
+++ b/src/test/zones/evpn/bgp_fabric/expected_sdn_interfaces
@@ -24,6 +24,7 @@ iface vrfbr_evpn
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_evpn
+	ip-forward on
 
 auto vrfvx_evpn
 iface vrfvx_evpn
diff --git a/src/test/zones/evpn/bgp_loopback/expected_sdn_interfaces b/src/test/zones/evpn/bgp_loopback/expected_sdn_interfaces
index 4cf13e0..ee0ad41 100644
--- a/src/test/zones/evpn/bgp_loopback/expected_sdn_interfaces
+++ b/src/test/zones/evpn/bgp_loopback/expected_sdn_interfaces
@@ -23,6 +23,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/bgp_multipath_relax/expected_sdn_interfaces b/src/test/zones/evpn/bgp_multipath_relax/expected_sdn_interfaces
index 4cf13e0..ee0ad41 100644
--- a/src/test/zones/evpn/bgp_multipath_relax/expected_sdn_interfaces
+++ b/src/test/zones/evpn/bgp_multipath_relax/expected_sdn_interfaces
@@ -23,6 +23,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/combined_bgp_isis/expected_sdn_interfaces b/src/test/zones/evpn/combined_bgp_isis/expected_sdn_interfaces
index 4cf13e0..ee0ad41 100644
--- a/src/test/zones/evpn/combined_bgp_isis/expected_sdn_interfaces
+++ b/src/test/zones/evpn/combined_bgp_isis/expected_sdn_interfaces
@@ -23,6 +23,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/disable_arp_nd_suppression/expected_sdn_interfaces b/src/test/zones/evpn/disable_arp_nd_suppression/expected_sdn_interfaces
index bbde906..7b605d9 100644
--- a/src/test/zones/evpn/disable_arp_nd_suppression/expected_sdn_interfaces
+++ b/src/test/zones/evpn/disable_arp_nd_suppression/expected_sdn_interfaces
@@ -24,6 +24,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/ebgp/expected_sdn_interfaces b/src/test/zones/evpn/ebgp/expected_sdn_interfaces
index 4cf13e0..ee0ad41 100644
--- a/src/test/zones/evpn/ebgp/expected_sdn_interfaces
+++ b/src/test/zones/evpn/ebgp/expected_sdn_interfaces
@@ -23,6 +23,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/ebgp_evpn_ibgp_wan_evpn_ebgp/expected_sdn_interfaces b/src/test/zones/evpn/ebgp_evpn_ibgp_wan_evpn_ebgp/expected_sdn_interfaces
index 7481d36..ed5392e 100644
--- a/src/test/zones/evpn/ebgp_evpn_ibgp_wan_evpn_ebgp/expected_sdn_interfaces
+++ b/src/test/zones/evpn/ebgp_evpn_ibgp_wan_evpn_ebgp/expected_sdn_interfaces
@@ -23,6 +23,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/ebgp_evpn_ibgp_wan_evpn_ebgp_rt_import/expected_sdn_interfaces b/src/test/zones/evpn/ebgp_evpn_ibgp_wan_evpn_ebgp_rt_import/expected_sdn_interfaces
index 7481d36..ed5392e 100644
--- a/src/test/zones/evpn/ebgp_evpn_ibgp_wan_evpn_ebgp_rt_import/expected_sdn_interfaces
+++ b/src/test/zones/evpn/ebgp_evpn_ibgp_wan_evpn_ebgp_rt_import/expected_sdn_interfaces
@@ -23,6 +23,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/ebgp_evpn_ibgp_wan_evpn_ebgp_rt_import_mixed/expected_sdn_interfaces b/src/test/zones/evpn/ebgp_evpn_ibgp_wan_evpn_ebgp_rt_import_mixed/expected_sdn_interfaces
index 39f3beb..417fe54 100644
--- a/src/test/zones/evpn/ebgp_evpn_ibgp_wan_evpn_ebgp_rt_import_mixed/expected_sdn_interfaces
+++ b/src/test/zones/evpn/ebgp_evpn_ibgp_wan_evpn_ebgp_rt_import_mixed/expected_sdn_interfaces
@@ -39,6 +39,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
 
 auto vrfbr_myzone2
 iface vrfbr_myzone2
@@ -47,6 +48,7 @@ iface vrfbr_myzone2
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone2
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/ebgp_loopback/expected_sdn_interfaces b/src/test/zones/evpn/ebgp_loopback/expected_sdn_interfaces
index 4cf13e0..ee0ad41 100644
--- a/src/test/zones/evpn/ebgp_loopback/expected_sdn_interfaces
+++ b/src/test/zones/evpn/ebgp_loopback/expected_sdn_interfaces
@@ -23,6 +23,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/evpn_cluster_ibgp_uplink_ebgp/expected_sdn_interfaces b/src/test/zones/evpn/evpn_cluster_ibgp_uplink_ebgp/expected_sdn_interfaces
index 4cf13e0..ee0ad41 100644
--- a/src/test/zones/evpn/evpn_cluster_ibgp_uplink_ebgp/expected_sdn_interfaces
+++ b/src/test/zones/evpn/evpn_cluster_ibgp_uplink_ebgp/expected_sdn_interfaces
@@ -23,6 +23,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/evpn_multiple_zones_cluster_ibgp_uplink_ebgp/expected_sdn_interfaces b/src/test/zones/evpn/evpn_multiple_zones_cluster_ibgp_uplink_ebgp/expected_sdn_interfaces
index 88706b7..eea0961 100644
--- a/src/test/zones/evpn/evpn_multiple_zones_cluster_ibgp_uplink_ebgp/expected_sdn_interfaces
+++ b/src/test/zones/evpn/evpn_multiple_zones_cluster_ibgp_uplink_ebgp/expected_sdn_interfaces
@@ -39,6 +39,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
 
 auto vrfbr_myzone2
 iface vrfbr_myzone2
@@ -47,6 +48,7 @@ iface vrfbr_myzone2
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone2
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/exitnode/expected_sdn_interfaces b/src/test/zones/evpn/exitnode/expected_sdn_interfaces
index 5ab3084..4f23b82 100644
--- a/src/test/zones/evpn/exitnode/expected_sdn_interfaces
+++ b/src/test/zones/evpn/exitnode/expected_sdn_interfaces
@@ -23,6 +23,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/exitnode_local_routing/expected_sdn_interfaces b/src/test/zones/evpn/exitnode_local_routing/expected_sdn_interfaces
index 301f5b3..c349d26 100644
--- a/src/test/zones/evpn/exitnode_local_routing/expected_sdn_interfaces
+++ b/src/test/zones/evpn/exitnode_local_routing/expected_sdn_interfaces
@@ -23,6 +23,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/exitnode_local_routing_ipv6/expected_sdn_interfaces b/src/test/zones/evpn/exitnode_local_routing_ipv6/expected_sdn_interfaces
index b46d4e7..02b52c1 100644
--- a/src/test/zones/evpn/exitnode_local_routing_ipv6/expected_sdn_interfaces
+++ b/src/test/zones/evpn/exitnode_local_routing_ipv6/expected_sdn_interfaces
@@ -23,6 +23,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip6-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/exitnode_primary/expected_sdn_interfaces b/src/test/zones/evpn/exitnode_primary/expected_sdn_interfaces
index 5ab3084..4f23b82 100644
--- a/src/test/zones/evpn/exitnode_primary/expected_sdn_interfaces
+++ b/src/test/zones/evpn/exitnode_primary/expected_sdn_interfaces
@@ -23,6 +23,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/exitnode_snat/expected_sdn_interfaces b/src/test/zones/evpn/exitnode_snat/expected_sdn_interfaces
index 0d7d174..522a54e 100644
--- a/src/test/zones/evpn/exitnode_snat/expected_sdn_interfaces
+++ b/src/test/zones/evpn/exitnode_snat/expected_sdn_interfaces
@@ -42,6 +42,8 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
+	ip6-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/exitnodenullroute/expected_sdn_interfaces b/src/test/zones/evpn/exitnodenullroute/expected_sdn_interfaces
index 4bf5ccf..6878397 100644
--- a/src/test/zones/evpn/exitnodenullroute/expected_sdn_interfaces
+++ b/src/test/zones/evpn/exitnodenullroute/expected_sdn_interfaces
@@ -47,6 +47,8 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
+	ip6-forward on
 
 auto vrfbr_myzone2
 iface vrfbr_myzone2
@@ -55,6 +57,7 @@ iface vrfbr_myzone2
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone2
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/frr_local_merge/expected_sdn_interfaces b/src/test/zones/evpn/frr_local_merge/expected_sdn_interfaces
index c7ddf44..7c707d2 100644
--- a/src/test/zones/evpn/frr_local_merge/expected_sdn_interfaces
+++ b/src/test/zones/evpn/frr_local_merge/expected_sdn_interfaces
@@ -24,6 +24,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/frr_local_merge_router_without_exit/expected_sdn_interfaces b/src/test/zones/evpn/frr_local_merge_router_without_exit/expected_sdn_interfaces
index 169f32d..f298297 100644
--- a/src/test/zones/evpn/frr_local_merge_router_without_exit/expected_sdn_interfaces
+++ b/src/test/zones/evpn/frr_local_merge_router_without_exit/expected_sdn_interfaces
@@ -24,6 +24,7 @@ iface vrfbr_evpnz
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_evpnz
+	ip-forward on
 
 auto vrfvx_evpnz
 iface vrfvx_evpnz
diff --git a/src/test/zones/evpn/ipv4/expected_sdn_interfaces b/src/test/zones/evpn/ipv4/expected_sdn_interfaces
index 9d1c64c..a7a232b 100644
--- a/src/test/zones/evpn/ipv4/expected_sdn_interfaces
+++ b/src/test/zones/evpn/ipv4/expected_sdn_interfaces
@@ -24,6 +24,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/ipv4ipv6/expected_sdn_interfaces b/src/test/zones/evpn/ipv4ipv6/expected_sdn_interfaces
index 7a5d741..0bde7a1 100644
--- a/src/test/zones/evpn/ipv4ipv6/expected_sdn_interfaces
+++ b/src/test/zones/evpn/ipv4ipv6/expected_sdn_interfaces
@@ -26,6 +26,8 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
+	ip6-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/ipv6/expected_sdn_interfaces b/src/test/zones/evpn/ipv6/expected_sdn_interfaces
index b2bdbfe..98793a1 100644
--- a/src/test/zones/evpn/ipv6/expected_sdn_interfaces
+++ b/src/test/zones/evpn/ipv6/expected_sdn_interfaces
@@ -24,6 +24,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip6-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/ipv6underlay/expected_sdn_interfaces b/src/test/zones/evpn/ipv6underlay/expected_sdn_interfaces
index 3b91f75..62f05df 100644
--- a/src/test/zones/evpn/ipv6underlay/expected_sdn_interfaces
+++ b/src/test/zones/evpn/ipv6underlay/expected_sdn_interfaces
@@ -24,6 +24,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip6-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/isis/expected_sdn_interfaces b/src/test/zones/evpn/isis/expected_sdn_interfaces
index 4cf13e0..ee0ad41 100644
--- a/src/test/zones/evpn/isis/expected_sdn_interfaces
+++ b/src/test/zones/evpn/isis/expected_sdn_interfaces
@@ -23,6 +23,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/isis_loopback/expected_sdn_interfaces b/src/test/zones/evpn/isis_loopback/expected_sdn_interfaces
index e595dc9..db83e94 100644
--- a/src/test/zones/evpn/isis_loopback/expected_sdn_interfaces
+++ b/src/test/zones/evpn/isis_loopback/expected_sdn_interfaces
@@ -23,6 +23,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/multipath_relax/expected_sdn_interfaces b/src/test/zones/evpn/multipath_relax/expected_sdn_interfaces
index 4cf13e0..ee0ad41 100644
--- a/src/test/zones/evpn/multipath_relax/expected_sdn_interfaces
+++ b/src/test/zones/evpn/multipath_relax/expected_sdn_interfaces
@@ -23,6 +23,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/multiplezones/expected_sdn_interfaces b/src/test/zones/evpn/multiplezones/expected_sdn_interfaces
index e279b0a..71cc9cd 100644
--- a/src/test/zones/evpn/multiplezones/expected_sdn_interfaces
+++ b/src/test/zones/evpn/multiplezones/expected_sdn_interfaces
@@ -39,6 +39,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
 
 auto vrfbr_myzone2
 iface vrfbr_myzone2
@@ -47,6 +48,7 @@ iface vrfbr_myzone2
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone2
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/openfabric_fabric/expected_sdn_interfaces b/src/test/zones/evpn/openfabric_fabric/expected_sdn_interfaces
index 7460b12..451c782 100644
--- a/src/test/zones/evpn/openfabric_fabric/expected_sdn_interfaces
+++ b/src/test/zones/evpn/openfabric_fabric/expected_sdn_interfaces
@@ -24,6 +24,7 @@ iface vrfbr_evpn
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_evpn
+	ip-forward on
 
 auto vrfvx_evpn
 iface vrfvx_evpn
diff --git a/src/test/zones/evpn/openfabric_fabric_ipv6/expected_sdn_interfaces b/src/test/zones/evpn/openfabric_fabric_ipv6/expected_sdn_interfaces
index d017713..a4ad244 100644
--- a/src/test/zones/evpn/openfabric_fabric_ipv6/expected_sdn_interfaces
+++ b/src/test/zones/evpn/openfabric_fabric_ipv6/expected_sdn_interfaces
@@ -24,6 +24,7 @@ iface vrfbr_evpn
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_evpn
+	ip-forward on
 
 auto vrfvx_evpn
 iface vrfvx_evpn
diff --git a/src/test/zones/evpn/openfabric_fabric_ipv6_only/expected_sdn_interfaces b/src/test/zones/evpn/openfabric_fabric_ipv6_only/expected_sdn_interfaces
index 51507d7..bb54526 100644
--- a/src/test/zones/evpn/openfabric_fabric_ipv6_only/expected_sdn_interfaces
+++ b/src/test/zones/evpn/openfabric_fabric_ipv6_only/expected_sdn_interfaces
@@ -24,6 +24,7 @@ iface vrfbr_evpn
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_evpn
+	ip-forward on
 
 auto vrfvx_evpn
 iface vrfvx_evpn
diff --git a/src/test/zones/evpn/ospf_fabric/expected_sdn_interfaces b/src/test/zones/evpn/ospf_fabric/expected_sdn_interfaces
index 2543304..173488e 100644
--- a/src/test/zones/evpn/ospf_fabric/expected_sdn_interfaces
+++ b/src/test/zones/evpn/ospf_fabric/expected_sdn_interfaces
@@ -24,6 +24,7 @@ iface vrfbr_evpn
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_evpn
+	ip-forward on
 
 auto vrfvx_evpn
 iface vrfvx_evpn
diff --git a/src/test/zones/evpn/routemap/expected_sdn_interfaces b/src/test/zones/evpn/routemap/expected_sdn_interfaces
index 4cf13e0..ee0ad41 100644
--- a/src/test/zones/evpn/routemap/expected_sdn_interfaces
+++ b/src/test/zones/evpn/routemap/expected_sdn_interfaces
@@ -23,6 +23,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/routemap_exit_node/expected_sdn_interfaces b/src/test/zones/evpn/routemap_exit_node/expected_sdn_interfaces
index 5ab3084..4f23b82 100644
--- a/src/test/zones/evpn/routemap_exit_node/expected_sdn_interfaces
+++ b/src/test/zones/evpn/routemap_exit_node/expected_sdn_interfaces
@@ -23,6 +23,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/routemap_prefix_list/expected_sdn_interfaces b/src/test/zones/evpn/routemap_prefix_list/expected_sdn_interfaces
index 4cf13e0..ee0ad41 100644
--- a/src/test/zones/evpn/routemap_prefix_list/expected_sdn_interfaces
+++ b/src/test/zones/evpn/routemap_prefix_list/expected_sdn_interfaces
@@ -23,6 +23,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/rt_import/expected_sdn_interfaces b/src/test/zones/evpn/rt_import/expected_sdn_interfaces
index 9d1c64c..a7a232b 100644
--- a/src/test/zones/evpn/rt_import/expected_sdn_interfaces
+++ b/src/test/zones/evpn/rt_import/expected_sdn_interfaces
@@ -24,6 +24,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/vxlanport/expected_sdn_interfaces b/src/test/zones/evpn/vxlanport/expected_sdn_interfaces
index d511bf9..4f844f1 100644
--- a/src/test/zones/evpn/vxlanport/expected_sdn_interfaces
+++ b/src/test/zones/evpn/vxlanport/expected_sdn_interfaces
@@ -24,6 +24,7 @@ iface vrfbr_myzone
 	bridge_fd 0
 	mtu 1450
 	vrf vrf_myzone
+	ip-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
-- 
2.47.3





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

* [PATCH network v5 03/10] sdn: evpn: enable force_forwarding for ipv6 forwarding to subnets
  2026-11-04 14:55 [PATCH many v5 00/10] sdn: enable force_forwarding for ipv6 forwarding Lukas Sichert
  2026-11-04 14:55 ` [PATCH ifupdown2 v5 01/10] d/patches: add ipv6 force_forwarding for bridges Lukas Sichert
  2026-11-04 14:55 ` [PATCH network v5 02/10] sdn: evpn: enable ip forwarding on the vrf bridge interface Lukas Sichert
@ 2026-11-04 14:55 ` Lukas Sichert
  2026-11-04 14:55 ` [PATCH network v5 04/10] sdn: simple: " Lukas Sichert
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Lukas Sichert @ 2026-11-04 14:55 UTC (permalink / raw)
  To: pve-devel; +Cc: Lukas Sichert

EVPN zones can route IPv6 subnet traffic through a VNet, an outgoing
interface and a VRF bridge. Until now, this depended on global IPv6
forwarding state, which also changes Router Advertisement handling for
the whole host.

Enable 'force_forwarding' explicitly on the Subnet and VRF bridge using
the 'ip6-force-forward' ifupdown2 flag.

Signed-off-by: Lukas Sichert <l.sichert@proxmox.com>
---
 src/PVE/Network/SDN/Zones/EvpnPlugin.pm                   | 8 +++++++-
 .../exitnode_local_routing_ipv6/expected_sdn_interfaces   | 2 ++
 src/test/zones/evpn/exitnode_snat/expected_sdn_interfaces | 2 ++
 .../zones/evpn/exitnodenullroute/expected_sdn_interfaces  | 2 ++
 src/test/zones/evpn/ipv4ipv6/expected_sdn_interfaces      | 2 ++
 src/test/zones/evpn/ipv6/expected_sdn_interfaces          | 2 ++
 src/test/zones/evpn/ipv6underlay/expected_sdn_interfaces  | 2 ++
 7 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/src/PVE/Network/SDN/Zones/EvpnPlugin.pm b/src/PVE/Network/SDN/Zones/EvpnPlugin.pm
index 8a45c51..1bec8c2 100644
--- a/src/PVE/Network/SDN/Zones/EvpnPlugin.pm
+++ b/src/PVE/Network/SDN/Zones/EvpnPlugin.pm
@@ -308,7 +308,12 @@ sub generate_sdn_config {
     push @iface_config, "mtu $mtu" if $mtu;
     push @iface_config, "alias $alias" if $alias;
     push @iface_config, "ip-forward on" if $enable_forward_v4;
-    push @iface_config, "ip6-forward on" if $enable_forward_v6;
+
+    if ($enable_forward_v6) {
+        push @iface_config, "ip6-forward on";
+        push @iface_config, "ip6-force-forward on";
+    }
+
     push @iface_config, "arp-accept on" if $ipv4 || $ipv6;
     push @iface_config, "vrf $vrf_iface" if $vrf_iface;
     push(@{ $config->{$vnetid} }, @iface_config) if !$config->{$vnetid};
@@ -354,6 +359,7 @@ sub generate_sdn_config {
             push @forward_opts, 'ip-forward on' if $enable_forward_v4;
             if ($enable_forward_v6) {
                 push @forward_opts, 'ip6-forward on';
+                push @forward_opts, 'ip6-force-forward on';
             }
             for my $opt (@forward_opts) {
                 my $opt_absent = !grep { $_ eq $opt } @{ $config->{$brvrf} };
diff --git a/src/test/zones/evpn/exitnode_local_routing_ipv6/expected_sdn_interfaces b/src/test/zones/evpn/exitnode_local_routing_ipv6/expected_sdn_interfaces
index 02b52c1..97e0e06 100644
--- a/src/test/zones/evpn/exitnode_local_routing_ipv6/expected_sdn_interfaces
+++ b/src/test/zones/evpn/exitnode_local_routing_ipv6/expected_sdn_interfaces
@@ -8,6 +8,7 @@ iface myvnet
 	bridge_fd 0
 	mtu 1450
 	ip6-forward on
+	ip6-force-forward on
 	arp-accept on
 	vrf vrf_myzone
 
@@ -24,6 +25,7 @@ iface vrfbr_myzone
 	mtu 1450
 	vrf vrf_myzone
 	ip6-forward on
+	ip6-force-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/exitnode_snat/expected_sdn_interfaces b/src/test/zones/evpn/exitnode_snat/expected_sdn_interfaces
index 522a54e..ce7f86f 100644
--- a/src/test/zones/evpn/exitnode_snat/expected_sdn_interfaces
+++ b/src/test/zones/evpn/exitnode_snat/expected_sdn_interfaces
@@ -27,6 +27,7 @@ iface myvnet2
 	bridge_fd 0
 	mtu 1450
 	ip6-forward on
+	ip6-force-forward on
 	arp-accept on
 	vrf vrf_myzone
 
@@ -44,6 +45,7 @@ iface vrfbr_myzone
 	vrf vrf_myzone
 	ip-forward on
 	ip6-forward on
+	ip6-force-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/exitnodenullroute/expected_sdn_interfaces b/src/test/zones/evpn/exitnodenullroute/expected_sdn_interfaces
index 6878397..88bb214 100644
--- a/src/test/zones/evpn/exitnodenullroute/expected_sdn_interfaces
+++ b/src/test/zones/evpn/exitnodenullroute/expected_sdn_interfaces
@@ -14,6 +14,7 @@ iface myvnet
 	mtu 1450
 	ip-forward on
 	ip6-forward on
+	ip6-force-forward on
 	arp-accept on
 	vrf vrf_myzone
 
@@ -49,6 +50,7 @@ iface vrfbr_myzone
 	vrf vrf_myzone
 	ip-forward on
 	ip6-forward on
+	ip6-force-forward on
 
 auto vrfbr_myzone2
 iface vrfbr_myzone2
diff --git a/src/test/zones/evpn/ipv4ipv6/expected_sdn_interfaces b/src/test/zones/evpn/ipv4ipv6/expected_sdn_interfaces
index 0bde7a1..45d8e63 100644
--- a/src/test/zones/evpn/ipv4ipv6/expected_sdn_interfaces
+++ b/src/test/zones/evpn/ipv4ipv6/expected_sdn_interfaces
@@ -11,6 +11,7 @@ iface myvnet
 	mtu 1450
 	ip-forward on
 	ip6-forward on
+	ip6-force-forward on
 	arp-accept on
 	vrf vrf_myzone
 
@@ -28,6 +29,7 @@ iface vrfbr_myzone
 	vrf vrf_myzone
 	ip-forward on
 	ip6-forward on
+	ip6-force-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/ipv6/expected_sdn_interfaces b/src/test/zones/evpn/ipv6/expected_sdn_interfaces
index 98793a1..882dd72 100644
--- a/src/test/zones/evpn/ipv6/expected_sdn_interfaces
+++ b/src/test/zones/evpn/ipv6/expected_sdn_interfaces
@@ -9,6 +9,7 @@ iface myvnet
 	bridge_fd 0
 	mtu 1450
 	ip6-forward on
+	ip6-force-forward on
 	arp-accept on
 	vrf vrf_myzone
 
@@ -25,6 +26,7 @@ iface vrfbr_myzone
 	mtu 1450
 	vrf vrf_myzone
 	ip6-forward on
+	ip6-force-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
diff --git a/src/test/zones/evpn/ipv6underlay/expected_sdn_interfaces b/src/test/zones/evpn/ipv6underlay/expected_sdn_interfaces
index 62f05df..98cd43f 100644
--- a/src/test/zones/evpn/ipv6underlay/expected_sdn_interfaces
+++ b/src/test/zones/evpn/ipv6underlay/expected_sdn_interfaces
@@ -9,6 +9,7 @@ iface myvnet
 	bridge_fd 0
 	mtu 1450
 	ip6-forward on
+	ip6-force-forward on
 	arp-accept on
 	vrf vrf_myzone
 
@@ -25,6 +26,7 @@ iface vrfbr_myzone
 	mtu 1450
 	vrf vrf_myzone
 	ip6-forward on
+	ip6-force-forward on
 
 auto vrfvx_myzone
 iface vrfvx_myzone
-- 
2.47.3





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

* [PATCH network v5 04/10] sdn: simple: enable force_forwarding for ipv6 forwarding to subnets
  2026-11-04 14:55 [PATCH many v5 00/10] sdn: enable force_forwarding for ipv6 forwarding Lukas Sichert
                   ` (2 preceding siblings ...)
  2026-11-04 14:55 ` [PATCH network v5 03/10] sdn: evpn: enable force_forwarding for ipv6 forwarding to subnets Lukas Sichert
@ 2026-11-04 14:55 ` Lukas Sichert
  2026-11-04 14:55 ` [PATCH network v5 05/10] tests: sdn: update fabric expectations for explicit ipv6 forwarding Lukas Sichert
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Lukas Sichert @ 2026-11-04 14:55 UTC (permalink / raw)
  To: pve-devel; +Cc: Lukas Sichert

Simple zones can route ipv6 subnet traffic through a VNet bridge. Until
now, this depended on global ipv6 forwarding state, which also changes
Router Advertisement handling for the whole host.

Enable 'force_forwarding' explicitly on the Subnet bridge using the
'ip6-force-forward' ifupdown2 flag.

Signed-off-by: Lukas Sichert <l.sichert@proxmox.com>
---
 src/PVE/Network/SDN/Zones/SimplePlugin.pm              | 6 +++++-
 src/test/zones/simple/ipv4v6/expected_sdn_interfaces   | 1 +
 src/test/zones/simple/ipv6snat/expected_sdn_interfaces | 1 +
 3 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/src/PVE/Network/SDN/Zones/SimplePlugin.pm b/src/PVE/Network/SDN/Zones/SimplePlugin.pm
index 347eee9..db605cc 100644
--- a/src/PVE/Network/SDN/Zones/SimplePlugin.pm
+++ b/src/PVE/Network/SDN/Zones/SimplePlugin.pm
@@ -149,7 +149,11 @@ sub generate_sdn_config {
     push @iface_config, "mtu $mtu" if $mtu;
     push @iface_config, "alias $alias" if $alias;
     push @iface_config, "ip-forward on" if $enable_forward_v4;
-    push @iface_config, "ip6-forward on" if $enable_forward_v6;
+
+    if ($enable_forward_v6) {
+        push @iface_config, "ip6-forward on";
+        push @iface_config, 'ip6-force-forward on';
+    }
 
     push @{ $config->{$vnetid} }, @iface_config;
 
diff --git a/src/test/zones/simple/ipv4v6/expected_sdn_interfaces b/src/test/zones/simple/ipv4v6/expected_sdn_interfaces
index 34ed5db..4b02ce3 100644
--- a/src/test/zones/simple/ipv4v6/expected_sdn_interfaces
+++ b/src/test/zones/simple/ipv4v6/expected_sdn_interfaces
@@ -9,3 +9,4 @@ iface myvnet
 	bridge_fd 0
 	ip-forward on
 	ip6-forward on
+	ip6-force-forward on
diff --git a/src/test/zones/simple/ipv6snat/expected_sdn_interfaces b/src/test/zones/simple/ipv6snat/expected_sdn_interfaces
index 5f6d40b..38393fd 100644
--- a/src/test/zones/simple/ipv6snat/expected_sdn_interfaces
+++ b/src/test/zones/simple/ipv6snat/expected_sdn_interfaces
@@ -11,3 +11,4 @@ iface myvnet
 	bridge_stp off
 	bridge_fd 0
 	ip6-forward on
+	ip6-force-forward on
-- 
2.47.3





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

* [PATCH network v5 05/10] tests: sdn: update fabric expectations for explicit ipv6 forwarding
  2026-11-04 14:55 [PATCH many v5 00/10] sdn: enable force_forwarding for ipv6 forwarding Lukas Sichert
                   ` (3 preceding siblings ...)
  2026-11-04 14:55 ` [PATCH network v5 04/10] sdn: simple: " Lukas Sichert
@ 2026-11-04 14:55 ` Lukas Sichert
  2026-11-04 14:55 ` [PATCH perl-rs v5 06/10] openfabric: set ip6-forward explicitly Lukas Sichert
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Lukas Sichert @ 2026-11-04 14:55 UTC (permalink / raw)
  To: pve-devel; +Cc: Lukas Sichert

The fabric interfaces are rendered by pve-rs, which now sets ip6-forward
and ip6-force-forward explicitly for IPv6 OpenFabric and for BGP fabric
interfaces. Adjust the expected interfaces of the EVPN zone tests that
use these fabrics.

Signed-off-by: Lukas Sichert <l.sichert@proxmox.com>
---
 src/test/zones/evpn/bgp_fabric/expected_sdn_interfaces      | 2 ++
 .../evpn/openfabric_fabric_ipv6/expected_sdn_interfaces     | 2 ++
 .../openfabric_fabric_ipv6_only/expected_sdn_interfaces     | 6 ++++++
 3 files changed, 10 insertions(+)

diff --git a/src/test/zones/evpn/bgp_fabric/expected_sdn_interfaces b/src/test/zones/evpn/bgp_fabric/expected_sdn_interfaces
index d321e7b..bf614ef 100644
--- a/src/test/zones/evpn/bgp_fabric/expected_sdn_interfaces
+++ b/src/test/zones/evpn/bgp_fabric/expected_sdn_interfaces
@@ -52,10 +52,12 @@ auto ens18
 iface ens18 inet manual
 	ip-forward 1
 	ip6-forward 1
+	ip6-force-forward 1
 	accept-ra 0
 
 auto ens19
 iface ens19 inet manual
 	ip-forward 1
 	ip6-forward 1
+	ip6-force-forward 1
 	accept-ra 0
diff --git a/src/test/zones/evpn/openfabric_fabric_ipv6/expected_sdn_interfaces b/src/test/zones/evpn/openfabric_fabric_ipv6/expected_sdn_interfaces
index a4ad244..fe373dc 100644
--- a/src/test/zones/evpn/openfabric_fabric_ipv6/expected_sdn_interfaces
+++ b/src/test/zones/evpn/openfabric_fabric_ipv6/expected_sdn_interfaces
@@ -53,6 +53,8 @@ iface dummy_test inet6 static
 	address fd00::1/128
 	link-type dummy
 	ip-forward 1
+	ip6-forward 1
+	ip6-force-forward 1
 
 auto ens20
 iface ens20 inet static
diff --git a/src/test/zones/evpn/openfabric_fabric_ipv6_only/expected_sdn_interfaces b/src/test/zones/evpn/openfabric_fabric_ipv6_only/expected_sdn_interfaces
index bb54526..04531d7 100644
--- a/src/test/zones/evpn/openfabric_fabric_ipv6_only/expected_sdn_interfaces
+++ b/src/test/zones/evpn/openfabric_fabric_ipv6_only/expected_sdn_interfaces
@@ -47,13 +47,19 @@ iface dummy_test inet6 static
 	address fd00::1/128
 	link-type dummy
 	ip-forward 1
+	ip6-forward 1
+	ip6-force-forward 1
 
 auto ens20
 iface ens20 inet6 static
 	address fd00::1/128
 	ip-forward 1
+	ip6-forward 1
+	ip6-force-forward 1
 
 auto ens21
 iface ens21 inet6 static
 	address fd00::1/128
 	ip-forward 1
+	ip6-forward 1
+	ip6-force-forward 1
-- 
2.47.3





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

* [PATCH perl-rs v5 06/10] openfabric: set ip6-forward explicitly
  2026-11-04 14:55 [PATCH many v5 00/10] sdn: enable force_forwarding for ipv6 forwarding Lukas Sichert
                   ` (4 preceding siblings ...)
  2026-11-04 14:55 ` [PATCH network v5 05/10] tests: sdn: update fabric expectations for explicit ipv6 forwarding Lukas Sichert
@ 2026-11-04 14:55 ` Lukas Sichert
  2026-11-04 14:55 ` [PATCH perl-rs v5 07/10] fabrics: openfabric: enable force_forwarding for ipv6 transit traffic Lukas Sichert
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Lukas Sichert @ 2026-11-04 14:55 UTC (permalink / raw)
  To: pve-devel; +Cc: Lukas Sichert

The rendered interface already sets ip-forward. Do the same for
ip6-forward on IPv6 addresses, so the config is consistent and does
not rely on the ifupdown2 default.

Signed-off-by: Lukas Sichert <l.sichert@proxmox.com>
---
 pve-rs/src/bindings/sdn/fabrics.rs | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/pve-rs/src/bindings/sdn/fabrics.rs b/pve-rs/src/bindings/sdn/fabrics.rs
index 1d3e8b5..74e1140 100644
--- a/pve-rs/src/bindings/sdn/fabrics.rs
+++ b/pve-rs/src/bindings/sdn/fabrics.rs
@@ -599,6 +599,9 @@ pub mod pve_rs_sdn_fabrics {
             writeln!(interface, "\tlink-type {link_type}")?;
         }
         writeln!(interface, "\tip-forward 1")?;
+        if cidr.is_ipv6() {
+            writeln!(interface, "\tip6-forward 1")?;
+        }
 
         Ok(interface)
     }
-- 
2.47.3





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

* [PATCH perl-rs v5 07/10] fabrics: openfabric: enable force_forwarding for ipv6 transit traffic
  2026-11-04 14:55 [PATCH many v5 00/10] sdn: enable force_forwarding for ipv6 forwarding Lukas Sichert
                   ` (5 preceding siblings ...)
  2026-11-04 14:55 ` [PATCH perl-rs v5 06/10] openfabric: set ip6-forward explicitly Lukas Sichert
@ 2026-11-04 14:55 ` Lukas Sichert
  2026-11-04 14:55 ` [PATCH perl-rs v5 08/10] fabrics: bgp: " Lukas Sichert
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Lukas Sichert @ 2026-11-04 14:55 UTC (permalink / raw)
  To: pve-devel; +Cc: Lukas Sichert

In a non-full-mesh OpenFabric setup, nodes may need to relay traffic
between peers that are not directly connected. This requires forwarding
ipv6 packets between fabric interfaces. With the addition of
'force_forwarding' to the Linux kernel, this can be done without
enabling 'all.forwarding', which disables Router Advertisements for the
whole host.

Configure fabric interfaces with the ifupdown2 ip6-force-forward flag.

Signed-off-by: Lukas Sichert <l.sichert@proxmox.com>
---

Notes:
    In [1] I said that I would prevent setting the flag on dummy interfaces.
    As the ip-forward flag is set on dummy interfaces as well I think it
    would be more confusing to not also set ip6-forward and
    ip6-force-forward. The flags will therefore also be set on dummy
    interfaces.
    
    [1] https://lore.proxmox.com/all/DM6DIG3FPYS6.I5M2777SP04X@proxmox.com/

 pve-rs/src/bindings/sdn/fabrics.rs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/pve-rs/src/bindings/sdn/fabrics.rs b/pve-rs/src/bindings/sdn/fabrics.rs
index 74e1140..8413168 100644
--- a/pve-rs/src/bindings/sdn/fabrics.rs
+++ b/pve-rs/src/bindings/sdn/fabrics.rs
@@ -601,6 +601,7 @@ pub mod pve_rs_sdn_fabrics {
         writeln!(interface, "\tip-forward 1")?;
         if cidr.is_ipv6() {
             writeln!(interface, "\tip6-forward 1")?;
+            writeln!(interface, "\tip6-force-forward 1")?;
         }
 
         Ok(interface)
-- 
2.47.3





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

* [PATCH perl-rs v5 08/10] fabrics: bgp: enable force_forwarding for ipv6 transit traffic
  2026-11-04 14:55 [PATCH many v5 00/10] sdn: enable force_forwarding for ipv6 forwarding Lukas Sichert
                   ` (6 preceding siblings ...)
  2026-11-04 14:55 ` [PATCH perl-rs v5 07/10] fabrics: openfabric: enable force_forwarding for ipv6 transit traffic Lukas Sichert
@ 2026-11-04 14:55 ` Lukas Sichert
  2026-11-04 14:55 ` [PATCH manager v5 09/10] ui: sdn: remove IPv6 forwarding hint from fabric edit window Lukas Sichert
  2026-11-04 14:55 ` [PATCH docs v5 10/10] sdn: drop global ipv6 forwarding workaround from docs Lukas Sichert
  9 siblings, 0 replies; 11+ messages in thread
From: Lukas Sichert @ 2026-11-04 14:55 UTC (permalink / raw)
  To: pve-devel; +Cc: Lukas Sichert

In a non-full-mesh BGP-fabric setup, nodes may need to relay traffic
between peers that are not directly connected. This requires forwarding
ipv6 packets between fabric interfaces. With the addition of
'force_forwarding' to the Linux kernel, this can be done without
enabling 'all.forwarding', which disables Router Advertisements.

Configure fabric interfaces with the ifupdown2 ip6-force-forward flag.

Signed-off-by: Lukas Sichert <l.sichert@proxmox.com>
---
 pve-rs/src/bindings/sdn/fabrics.rs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/pve-rs/src/bindings/sdn/fabrics.rs b/pve-rs/src/bindings/sdn/fabrics.rs
index 8413168..864e060 100644
--- a/pve-rs/src/bindings/sdn/fabrics.rs
+++ b/pve-rs/src/bindings/sdn/fabrics.rs
@@ -771,6 +771,7 @@ pub mod pve_rs_sdn_fabrics {
                             writeln!(interfaces, "iface {name} inet manual")?;
                             writeln!(interfaces, "\tip-forward 1")?;
                             writeln!(interfaces, "\tip6-forward 1")?;
+                            writeln!(interfaces, "\tip6-force-forward 1")?;
                             // BGP unnumbered uses RAs to discover peer link-local
                             // addresses. frr listens for them itself, but the kernel
                             // would otherwise install RA-derived routes we don't want.
-- 
2.47.3





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

* [PATCH manager v5 09/10] ui: sdn: remove IPv6 forwarding hint from fabric edit window
  2026-11-04 14:55 [PATCH many v5 00/10] sdn: enable force_forwarding for ipv6 forwarding Lukas Sichert
                   ` (7 preceding siblings ...)
  2026-11-04 14:55 ` [PATCH perl-rs v5 08/10] fabrics: bgp: " Lukas Sichert
@ 2026-11-04 14:55 ` Lukas Sichert
  2026-11-04 14:55 ` [PATCH docs v5 10/10] sdn: drop global ipv6 forwarding workaround from docs Lukas Sichert
  9 siblings, 0 replies; 11+ messages in thread
From: Lukas Sichert @ 2026-11-04 14:55 UTC (permalink / raw)
  To: pve-devel; +Cc: Lukas Sichert

The fabric edit window warned users to enable global IPv6 forwarding
when configuring an IPv6 fabric prefix. This is no longer required, as
the SDN backend now enables per-interface IPv6 forwarding where needed
via force_forwarding.

Remove the hint to avoid suggesting the broader host-wide sysctl
setting, which can disable Router Advertisements on the host.

Signed-off-by: Lukas Sichert <l.sichert@proxmox.com>
---
 www/manager6/sdn/fabrics/FabricEdit.js | 59 +++++++++-----------------
 1 file changed, 20 insertions(+), 39 deletions(-)

diff --git a/www/manager6/sdn/fabrics/FabricEdit.js b/www/manager6/sdn/fabrics/FabricEdit.js
index e9e0d1fa..a8c71785 100644
--- a/www/manager6/sdn/fabrics/FabricEdit.js
+++ b/www/manager6/sdn/fabrics/FabricEdit.js
@@ -13,12 +13,6 @@ Ext.define('PVE.sdn.Fabric.Fabric.Edit', {
 
     baseUrl: '/cluster/sdn/fabrics/fabric',
 
-    viewModel: {
-        data: {
-            showIpv6ForwardingHint: false,
-        },
-    },
-
     items: [
         {
             xtype: 'textfield',
@@ -83,42 +77,29 @@ Ext.define('PVE.sdn.Fabric.Fabric.Edit', {
         }
 
         if (me.hasIpv6Support) {
-            me.items.push(
-                {
-                    xtype: 'displayfield',
-                    value: 'To make IPv6 fabrics work, enable global IPv6 forwarding on all nodes. Click on the Help button for more details.',
-                    bind: {
-                        hidden: '{!showIpv6ForwardingHint}',
-                    },
-                    userCls: 'pmx-hint',
+            me.items.push({
+                xtype: 'proxmoxtextfield',
+                fieldLabel: gettext('IPv6 Prefix'),
+                labelWidth: 120,
+                name: 'ip6_prefix',
+                disabled: me.disableIpPrefixEdit,
+                allowBlank: true,
+                vtype: 'IP6CIDRAddress',
+                skipEmptyText: true,
+                deleteEmpty: !me.isCreate,
+                validator: function (value) {
+                    let ipPrefix = this.up('window').down('[name=ip_prefix]')?.getValue();
+                    if (value || ipPrefix) {
+                        return true;
+                    }
+                    return gettext('Either IPv4 Prefix or IPv6 Prefix is required');
                 },
-                {
-                    xtype: 'proxmoxtextfield',
-                    fieldLabel: gettext('IPv6 Prefix'),
-                    labelWidth: 120,
-                    name: 'ip6_prefix',
-                    disabled: me.disableIpPrefixEdit,
-                    allowBlank: true,
-                    vtype: 'IP6CIDRAddress',
-                    skipEmptyText: true,
-                    deleteEmpty: !me.isCreate,
-                    validator: function (value) {
-                        let ipPrefix = this.up('window').down('[name=ip_prefix]')?.getValue();
-                        if (value || ipPrefix) {
-                            return true;
-                        }
-                        return gettext('Either IPv4 Prefix or IPv6 Prefix is required');
-                    },
-                    listeners: {
-                        change: function (textbox, value) {
-                            let win = textbox.up('window');
-                            let vm = win.getViewModel();
-                            vm.set('showIpv6ForwardingHint', !!value);
-                            win.down('[name=ip_prefix]')?.validate();
-                        },
+                listeners: {
+                    change: function (textbox) {
+                        textbox.up('window').down('[name=ip_prefix]')?.validate();
                     },
                 },
-            );
+            });
         }
 
         if (me.additionalTabs.length > 0) {
-- 
2.47.3





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

* [PATCH docs v5 10/10] sdn: drop global ipv6 forwarding workaround from docs
  2026-11-04 14:55 [PATCH many v5 00/10] sdn: enable force_forwarding for ipv6 forwarding Lukas Sichert
                   ` (8 preceding siblings ...)
  2026-11-04 14:55 ` [PATCH manager v5 09/10] ui: sdn: remove IPv6 forwarding hint from fabric edit window Lukas Sichert
@ 2026-11-04 14:55 ` Lukas Sichert
  9 siblings, 0 replies; 11+ messages in thread
From: Lukas Sichert @ 2026-11-04 14:55 UTC (permalink / raw)
  To: pve-devel; +Cc: Lukas Sichert

OpenFabric and BGP fabrics with ipv6 need forwarding on transit nodes so
packets can be relayed between peers that are not directly connected.

Drop the old recommendation to enable host-wide
net.ipv6.conf.all.forwarding from the documentation. The generated
fabric interface configuration now handles the required forwarding
setup, so the manual global forwarding workaround is no longer needed.

Signed-off-by: Lukas Sichert <l.sichert@proxmox.com>
---
 pvesdn.adoc | 27 +++++++--------------------
 1 file changed, 7 insertions(+), 20 deletions(-)

diff --git a/pvesdn.adoc b/pvesdn.adoc
index c85d86a..1894270 100644
--- a/pvesdn.adoc
+++ b/pvesdn.adoc
@@ -673,23 +673,13 @@ behavior and proper source address selection throughout the fabric.
 Notes on IPv6
 ^^^^^^^^^^^^^
 
-IPv6 is currently only usable on OpenFabric fabrics. These IPv6 Fabrics need
-global IPv6 forwarding enabled on all nodes contained in the fabric. Without
-IPv6 forwarding, non-full-mesh fabrics won't work because the transit nodes
-don't forward packets to the outer nodes. Currently there isn't an easy way to
-enable IPv6 forwarding per-interface like with IPv4, so it has to be enabled
-globally. This can be accomplished by appending this line:
-
-----
-post-up sysctl -w net.ipv6.conf.all.forwarding=1
-----
-
-to a fabric interface in the `/etc/network/interfaces` file. This will enable
-IPv6 forwarding globally once that interface comes up. Note that this affects
-how your interfaces handle automatic IPv6 setup (SLAAC), Neighbour
-Advertisements, Router Solicitations, and Router Advertisements. More details
-here: https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt under
-`net.ipv6.conf.all.forwarding`.
+IPv6 is currently only usable on OpenFabric and BGP fabrics. These IPv6 Fabrics
+need IPv6 forwarding enabled on all transit nodes contained in the fabric.
+Without IPv6 forwarding, non-full-mesh fabrics won't work because the transit
+nodes don't forward packets to the outer nodes. IPv6 forwarding is enabled by
+default on the necessary interfaces using the force_forwarding flag. This flag
+is only present on kernel 6.17 or newer. More details here:
+https://docs.kernel.org/networking/ip-sysctl.html
 
 [[pvesdn_openfabric]]
 OpenFabric
@@ -715,9 +705,6 @@ all router-IDs in the fabric are contained within this prefix.
 IPv6 Prefix:: IPv6 CIDR network range (e.g., 2001:db8::/64) used to verify that
 all router-IDs in the fabric are contained within this prefix.
 
-WARNING: For IPv6 fabrics to work, global forwarding needs to be enabled on all
-nodes. Check xref:pvesdn_config_fabrics_ipv6[Notes on IPv6] for how to do it and additional info.
-
 Hello Interval:: Controls how frequently (in seconds) hello packets are sent to
 discover and maintain connections with neighboring nodes. Lower values detect
 failures faster but increase network traffic. This option is global on the
-- 
2.47.3





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

end of thread, other threads:[~2026-11-04 14:56 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-04 14:55 [PATCH many v5 00/10] sdn: enable force_forwarding for ipv6 forwarding Lukas Sichert
2026-11-04 14:55 ` [PATCH ifupdown2 v5 01/10] d/patches: add ipv6 force_forwarding for bridges Lukas Sichert
2026-11-04 14:55 ` [PATCH network v5 02/10] sdn: evpn: enable ip forwarding on the vrf bridge interface Lukas Sichert
2026-11-04 14:55 ` [PATCH network v5 03/10] sdn: evpn: enable force_forwarding for ipv6 forwarding to subnets Lukas Sichert
2026-11-04 14:55 ` [PATCH network v5 04/10] sdn: simple: " Lukas Sichert
2026-11-04 14:55 ` [PATCH network v5 05/10] tests: sdn: update fabric expectations for explicit ipv6 forwarding Lukas Sichert
2026-11-04 14:55 ` [PATCH perl-rs v5 06/10] openfabric: set ip6-forward explicitly Lukas Sichert
2026-11-04 14:55 ` [PATCH perl-rs v5 07/10] fabrics: openfabric: enable force_forwarding for ipv6 transit traffic Lukas Sichert
2026-11-04 14:55 ` [PATCH perl-rs v5 08/10] fabrics: bgp: " Lukas Sichert
2026-11-04 14:55 ` [PATCH manager v5 09/10] ui: sdn: remove IPv6 forwarding hint from fabric edit window Lukas Sichert
2026-11-04 14:55 ` [PATCH docs v5 10/10] sdn: drop global ipv6 forwarding workaround from docs 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