From: Hannes Laimer <h.laimer@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [PATCH pve-network 2/3] sdn: evpn: accept untracked IPv6 NA on EVPN vnet bridges
Date: Wed, 18 Feb 2026 11:23:46 +0100 [thread overview]
Message-ID: <20260218102350.211294-3-h.laimer@proxmox.com> (raw)
In-Reply-To: <20260218102350.211294-1-h.laimer@proxmox.com>
In EVPN setups with per-node anycast first-hop gateways, guests may use
the shared gateway link-local address as next-hop, while return traffic
targets the guest GUA. If an exit node has no neighbor entry for that
GUA, it sends an NS.
Only the exit-node kernel tracks that NS state. Because ND traffic is
seen across EVPN nodes, a non-exit node can receive the guest's NA
without a matching local INCOMPLETE entry and can treat it as untracked.
Ignoring that NA prevents neighbor learning and can break IPv6 return
traffic.
Set `accept_untracked_na=2`[1] on EVPN vnet bridges that have IPv6
subnets so valid NA replies are accepted in this distributed gateway
topology.
Router Advertisements can trigger this, but RA presence is neither a
necessary nor a sufficient selector. Keying this to EVPN vnets with IPv6
subnets is therefore more robust, even though it is broader than just
looking at whether RAs are enabled.
Without this, deployments depend on pre-populated neighbor state (for
example guest-initiated traffic/pings first), which is fragile and
causes intermittent first-packet IPv6 failures.
[1] https://docs.kernel.org/networking/ip-sysctl.html#proc-sys-net-ipv6-variables
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
src/PVE/Network/SDN/Zones/EvpnPlugin.pm | 14 +++++++++++++-
.../evpn/exitnode_snat/expected_sdn_interfaces | 1 +
.../evpn/exitnodenullroute/expected_sdn_interfaces | 1 +
.../zones/evpn/ipv4ipv6/expected_sdn_interfaces | 1 +
.../evpn/ipv4ipv6nogateway/expected_sdn_interfaces | 1 +
src/test/zones/evpn/ipv6/expected_sdn_interfaces | 1 +
.../evpn/ipv6underlay/expected_sdn_interfaces | 1 +
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 8e7ddfd..c2895bc 100644
--- a/src/PVE/Network/SDN/Zones/EvpnPlugin.pm
+++ b/src/PVE/Network/SDN/Zones/EvpnPlugin.pm
@@ -219,6 +219,7 @@ sub generate_sdn_config {
my $address = {};
my $ipv4 = undef;
my $ipv6 = undef;
+ my $has_ipv6_subnet = undef;
my $enable_forward_v4 = undef;
my $enable_forward_v6 = undef;
my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
@@ -226,6 +227,7 @@ sub generate_sdn_config {
my $subnet = $subnets->{$subnetid};
my $cidr = $subnet->{cidr};
my $mask = $subnet->{mask};
+ my ($subnet_ip) = split(/\//, $cidr);
my $gateway = $subnet->{gateway};
if ($gateway) {
@@ -233,9 +235,16 @@ sub generate_sdn_config {
$address->{$gateway} = 1;
}
+ $has_ipv6_subnet = 1 if $subnet_ip && Net::IP::ip_is_ipv6($subnet_ip);
+
my $iptables = undef;
my $checkrouteip = undef;
- my $ipversion = Net::IP::ip_is_ipv6($gateway) ? 6 : 4;
+ my $ipversion = 4;
+ if ($gateway) {
+ $ipversion = Net::IP::ip_is_ipv6($gateway) ? 6 : 4;
+ } elsif ($subnet_ip) {
+ $ipversion = Net::IP::ip_is_ipv6($subnet_ip) ? 6 : 4;
+ }
if ($ipversion == 6) {
$ipv6 = 1;
@@ -278,6 +287,9 @@ sub generate_sdn_config {
push @iface_config, "ip-forward on" if $enable_forward_v4;
push @iface_config, "ip6-forward on" if $enable_forward_v6;
push @iface_config, "arp-accept on" if $ipv4 || $ipv6;
+ push @iface_config,
+ "post-up echo 2 > /proc/sys/net/ipv6/conf/$vnetid/accept_untracked_na || true"
+ if $has_ipv6_subnet;
push @iface_config, "vrf $vrf_iface" if $vrf_iface;
push(@{ $config->{$vnetid} }, @iface_config) if !$config->{$vnetid};
diff --git a/src/test/zones/evpn/exitnode_snat/expected_sdn_interfaces b/src/test/zones/evpn/exitnode_snat/expected_sdn_interfaces
index 47df77a..e63c409 100644
--- a/src/test/zones/evpn/exitnode_snat/expected_sdn_interfaces
+++ b/src/test/zones/evpn/exitnode_snat/expected_sdn_interfaces
@@ -28,6 +28,7 @@ iface myvnet2
mtu 1450
ip6-forward on
arp-accept on
+ post-up echo 2 > /proc/sys/net/ipv6/conf/myvnet2/accept_untracked_na || true
vrf vrf_myzone
auto vrf_myzone
diff --git a/src/test/zones/evpn/exitnodenullroute/expected_sdn_interfaces b/src/test/zones/evpn/exitnodenullroute/expected_sdn_interfaces
index 4bf5ccf..81a3b39 100644
--- a/src/test/zones/evpn/exitnodenullroute/expected_sdn_interfaces
+++ b/src/test/zones/evpn/exitnodenullroute/expected_sdn_interfaces
@@ -15,6 +15,7 @@ iface myvnet
ip-forward on
ip6-forward on
arp-accept on
+ post-up echo 2 > /proc/sys/net/ipv6/conf/myvnet/accept_untracked_na || true
vrf vrf_myzone
auto myvnet2
diff --git a/src/test/zones/evpn/ipv4ipv6/expected_sdn_interfaces b/src/test/zones/evpn/ipv4ipv6/expected_sdn_interfaces
index 7a5d741..7b1727b 100644
--- a/src/test/zones/evpn/ipv4ipv6/expected_sdn_interfaces
+++ b/src/test/zones/evpn/ipv4ipv6/expected_sdn_interfaces
@@ -12,6 +12,7 @@ iface myvnet
ip-forward on
ip6-forward on
arp-accept on
+ post-up echo 2 > /proc/sys/net/ipv6/conf/myvnet/accept_untracked_na || true
vrf vrf_myzone
auto vrf_myzone
diff --git a/src/test/zones/evpn/ipv4ipv6nogateway/expected_sdn_interfaces b/src/test/zones/evpn/ipv4ipv6nogateway/expected_sdn_interfaces
index 378fa77..4d904be 100644
--- a/src/test/zones/evpn/ipv4ipv6nogateway/expected_sdn_interfaces
+++ b/src/test/zones/evpn/ipv4ipv6nogateway/expected_sdn_interfaces
@@ -8,6 +8,7 @@ iface myvnet
bridge_fd 0
mtu 1450
arp-accept on
+ post-up echo 2 > /proc/sys/net/ipv6/conf/myvnet/accept_untracked_na || true
vrf vrf_myzone
auto vrf_myzone
diff --git a/src/test/zones/evpn/ipv6/expected_sdn_interfaces b/src/test/zones/evpn/ipv6/expected_sdn_interfaces
index b2bdbfe..f776122 100644
--- a/src/test/zones/evpn/ipv6/expected_sdn_interfaces
+++ b/src/test/zones/evpn/ipv6/expected_sdn_interfaces
@@ -10,6 +10,7 @@ iface myvnet
mtu 1450
ip6-forward on
arp-accept on
+ post-up echo 2 > /proc/sys/net/ipv6/conf/myvnet/accept_untracked_na || true
vrf vrf_myzone
auto vrf_myzone
diff --git a/src/test/zones/evpn/ipv6underlay/expected_sdn_interfaces b/src/test/zones/evpn/ipv6underlay/expected_sdn_interfaces
index 3b91f75..ab5988f 100644
--- a/src/test/zones/evpn/ipv6underlay/expected_sdn_interfaces
+++ b/src/test/zones/evpn/ipv6underlay/expected_sdn_interfaces
@@ -10,6 +10,7 @@ iface myvnet
mtu 1450
ip6-forward on
arp-accept on
+ post-up echo 2 > /proc/sys/net/ipv6/conf/myvnet/accept_untracked_na || true
vrf vrf_myzone
auto vrf_myzone
--
2.47.3
next prev parent reply other threads:[~2026-02-18 10:23 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-02-18 10:23 [PATCH docs/manager/network 0/6] add SLAAC support for subnets in EVPN zones Hannes Laimer
2026-02-18 10:23 ` [PATCH pve-network 1/3] sdn: evpn: add ipv6-nd support for subnets Hannes Laimer
2026-02-18 10:23 ` Hannes Laimer [this message]
2026-02-18 10:23 ` [PATCH pve-network 3/3] api: vnet: include zone-type in vnet list Hannes Laimer
2026-02-18 10:23 ` [PATCH pve-manager 1/1] ui: sdn: add ipv6 options for subnets in evpns zones Hannes Laimer
2026-02-18 10:23 ` [PATCH pve-docs 1/2] sdn: add subnet ipv6 options section Hannes Laimer
2026-02-18 10:23 ` [PATCH pve-docs 2/2] sdn: add exmaple for ipv6 in an evpn zone Hannes Laimer
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=20260218102350.211294-3-h.laimer@proxmox.com \
--to=h.laimer@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.