* [PATCH frr 1/2] frr: backport VXLAN local VTEP fallback fix
@ 2026-07-14 11:18 Gabriel Goller
2026-07-14 11:18 ` [PATCH frr 2/2] frr: bump to 10.6.1-1+pve3 Gabriel Goller
0 siblings, 1 reply; 2+ messages in thread
From: Gabriel Goller @ 2026-07-14 11:18 UTC (permalink / raw)
To: pve-devel
Backport the upstream fix for EVPN VXLAN interfaces without an explicit
local IP address. Encode an unspecified VTEP as 0.0.0.0 so bgpd can fall
back to the BGP router ID.
Also include the upstream regression topotest.
For more information also check the pve patch (optional):
https://lore.proxmox.com/pve-devel/20260702143349.252142-1-g.goller@proxmox.com/
Fixes: #7766
Upstream: https://github.com/FRRouting/frr/pull/22555
Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
---
debian/patches/series | 2 +
...ix-VXLAN-interface-local-IP-fallback.patch | 84 ++++++++++
...EVPN-L2VNI-unspecified-VTEP-topotest.patch | 153 ++++++++++++++++++
3 files changed, 239 insertions(+)
create mode 100644 debian/patches/upstream/0028-bgpd-zebra-fix-VXLAN-interface-local-IP-fallback.patch
create mode 100644 debian/patches/upstream/0029-tests-add-EVPN-L2VNI-unspecified-VTEP-topotest.patch
diff --git a/debian/patches/series b/debian/patches/series
index 45125e05e6e9..47c123827e2e 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -25,6 +25,8 @@ upstream/0024-Cleanup-snmp-memory-leaks.patch
upstream/0025-pimd-cleanup-of-leaked-memory-on-shutdown.patch
upstream/0026-tests-make-the-topotest-fail-if-a-memory-leak-is-det.patch
upstream/0027-Use-hash_clean_and_free-remove-hash_free.patch
+upstream/0028-bgpd-zebra-fix-VXLAN-interface-local-IP-fallback.patch
+upstream/0029-tests-add-EVPN-L2VNI-unspecified-VTEP-topotest.patch
pve/0001-enable-bgp-bfd-daemons.patch
pve/0002-bgpd-add-an-option-for-RT-auto-derivation-to-force-A.patch
pve/0003-tests-add-bgp-evpn-autort-test.patch
diff --git a/debian/patches/upstream/0028-bgpd-zebra-fix-VXLAN-interface-local-IP-fallback.patch b/debian/patches/upstream/0028-bgpd-zebra-fix-VXLAN-interface-local-IP-fallback.patch
new file mode 100644
index 000000000000..1fcc7dad14ef
--- /dev/null
+++ b/debian/patches/upstream/0028-bgpd-zebra-fix-VXLAN-interface-local-IP-fallback.patch
@@ -0,0 +1,84 @@
+From e8da5eda308db2cca319403b2a29ca9a0c6d996d Mon Sep 17 00:00:00 2001
+From: Gabriel Goller <g.goller@proxmox.com>
+Date: Thu, 2 Jul 2026 16:29:41 +0200
+Subject: [PATCH] bgpd, zebra: fix VXLAN interface local IP fallback
+
+FRR 10.6 breaks VXLAN interfaces that do not have a local IP. It changes
+EVPN VTEP handling from the IPv4 struct in_addr to the generic struct
+ipaddr for IPv6 VTEP support. That makes zebra store a missing VXLAN
+vxlan local address as IPADDR_NONE instead of IPv4 0.0.0.0.
+
+ZEBRA_VNI_ADD still uses the normal ipaddr stream helpers. These helpers
+reject IPADDR_NONE, so zebra no longer sends an add, and bgpd's existing
+router-id fallback for an unspecified VTEP never runs.
+
+Encode an IPADDR_NONE local VTEP as IPv4 0.0.0.0 when zebra builds
+ZEBRA_VNI_ADD. In bgpd, keep the fallback and treat IPv4/IPv6 zero as
+unspecified, so bgpd uses the router-id as the EVPN VTEP.
+
+Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
+---
+ bgpd/bgp_zebra.c | 9 ++++++++-
+ zebra/zebra_evpn.c | 10 ++++++++--
+ 2 files changed, 16 insertions(+), 3 deletions(-)
+
+diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
+index d1034ff42a18..d6c09003a853 100644
+--- a/bgpd/bgp_zebra.c
++++ b/bgpd/bgp_zebra.c
+@@ -3440,9 +3440,16 @@ static int bgp_zebra_process_local_vni(ZAPI_CALLBACK_ARGS)
+ vrf_id_to_name(vrf_id), vni,
+ vrf_id_to_name(tenant_vrf_id), svi_ifindex);
+
+- if (ipaddr_is_zero(&vtep_ip)) {
++ /* Preserve legacy behavior for VXLAN devices with no explicit local
++ * address: treat IPv4/IPv6 zero as unspecified and use the router-id as
++ * the EVPN VTEP/originator IP.
++ */
++ if (cmd == ZEBRA_VNI_ADD && ipaddr_is_zero(&vtep_ip)) {
+ SET_IPADDR_V4(&vtep_ip);
+ vtep_ip.ipaddr_v4 = bgp->router_id;
++ if (BGP_DEBUG(zebra, ZEBRA))
++ zlog_debug("Rx VNI add with unspecified VTEP IP, using router-id %pIA",
++ &vtep_ip);
+ }
+
+ if (cmd == ZEBRA_VNI_ADD) {
+diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c
+index 258e818ec5d2..724d549a4f51 100644
+--- a/zebra/zebra_evpn.c
++++ b/zebra/zebra_evpn.c
+@@ -1102,6 +1102,7 @@ int zebra_evpn_send_add_to_client(struct zebra_evpn *zevpn)
+ struct zserv *client;
+ struct stream *s;
+ ifindex_t svi_index;
++ struct ipaddr local_vtep_ip;
+ int rc;
+
+ client = zserv_find_client(ZEBRA_ROUTE_BGP, 0);
+@@ -1115,7 +1116,12 @@ int zebra_evpn_send_add_to_client(struct zebra_evpn *zevpn)
+
+ zclient_create_header(s, ZEBRA_VNI_ADD, zebra_vrf_get_evpn_id());
+ stream_putl(s, zevpn->vni);
+- stream_put_ipaddr(s, &zevpn->local_vtep_ip);
++ local_vtep_ip = zevpn->local_vtep_ip;
++ if (IS_IPADDR_NONE(&local_vtep_ip)) {
++ SET_IPADDR_V4(&local_vtep_ip);
++ local_vtep_ip.ipaddr_v4.s_addr = INADDR_ANY;
++ }
++ stream_put_ipaddr(s, &local_vtep_ip);
+ stream_put(s, &zevpn->vrf_id, sizeof(vrf_id_t)); /* tenant vrf */
+ stream_put_in_addr(s, &zevpn->mcast_grp);
+ stream_put(s, &svi_index, sizeof(ifindex_t));
+@@ -1125,7 +1131,7 @@ int zebra_evpn_send_add_to_client(struct zebra_evpn *zevpn)
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Send EVPN_ADD %u %pIA tenant vrf %s(%u) SVI index %u to %s", zevpn->vni,
+- &zevpn->local_vtep_ip, vrf_id_to_name(zevpn->vrf_id), zevpn->vrf_id,
++ &local_vtep_ip, vrf_id_to_name(zevpn->vrf_id), zevpn->vrf_id,
+ (zevpn->svi_if ? zevpn->svi_if->ifindex : 0),
+ zebra_route_string(client->proto));
+
+--
+2.47.3
+
diff --git a/debian/patches/upstream/0029-tests-add-EVPN-L2VNI-unspecified-VTEP-topotest.patch b/debian/patches/upstream/0029-tests-add-EVPN-L2VNI-unspecified-VTEP-topotest.patch
new file mode 100644
index 000000000000..aff477e4221d
--- /dev/null
+++ b/debian/patches/upstream/0029-tests-add-EVPN-L2VNI-unspecified-VTEP-topotest.patch
@@ -0,0 +1,153 @@
+From 347899ac7ce61bdae48ccef8f91894c2ab210b04 Mon Sep 17 00:00:00 2001
+From: Gabriel Goller <g.goller@proxmox.com>
+Date: Fri, 3 Jul 2026 13:40:11 +0200
+Subject: [PATCH] tests: add EVPN L2VNI unspecified VTEP topotest
+
+Add a short topotest that creates an L2VNI without a VXLAN local address
+and checks that bgpd uses the BGP router-id as the VTEP.
+
+Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
+---
+ .../__init__.py | 0
+ .../r1/frr.conf | 17 +++++
+ .../r2/frr.conf | 16 ++++
+ .../test_bgp_evpn_vxlan_local_vtep_none.py | 76 +++++++++++++++++++
+ 4 files changed, 109 insertions(+)
+ create mode 100644 tests/topotests/bgp_evpn_vxlan_local_vtep_none/__init__.py
+ create mode 100644 tests/topotests/bgp_evpn_vxlan_local_vtep_none/r1/frr.conf
+ create mode 100644 tests/topotests/bgp_evpn_vxlan_local_vtep_none/r2/frr.conf
+ create mode 100644 tests/topotests/bgp_evpn_vxlan_local_vtep_none/test_bgp_evpn_vxlan_local_vtep_none.py
+
+diff --git a/tests/topotests/bgp_evpn_vxlan_local_vtep_none/__init__.py b/tests/topotests/bgp_evpn_vxlan_local_vtep_none/__init__.py
+new file mode 100644
+index 000000000000..e69de29bb2d1
+diff --git a/tests/topotests/bgp_evpn_vxlan_local_vtep_none/r1/frr.conf b/tests/topotests/bgp_evpn_vxlan_local_vtep_none/r1/frr.conf
+new file mode 100644
+index 000000000000..3b88e4b614f8
+--- /dev/null
++++ b/tests/topotests/bgp_evpn_vxlan_local_vtep_none/r1/frr.conf
+@@ -0,0 +1,17 @@
++!
++interface lo
++ ip address 10.0.0.1/32
++!
++interface r1-eth0
++ ip address 192.0.2.1/24
++!
++router bgp 65000
++ bgp router-id 10.0.0.1
++ no bgp default ipv4-unicast
++ neighbor 192.0.2.2 remote-as 65000
++ !
++ address-family l2vpn evpn
++ neighbor 192.0.2.2 activate
++ advertise-all-vni
++ exit-address-family
++!
+diff --git a/tests/topotests/bgp_evpn_vxlan_local_vtep_none/r2/frr.conf b/tests/topotests/bgp_evpn_vxlan_local_vtep_none/r2/frr.conf
+new file mode 100644
+index 000000000000..5d6d8c7a57bb
+--- /dev/null
++++ b/tests/topotests/bgp_evpn_vxlan_local_vtep_none/r2/frr.conf
+@@ -0,0 +1,16 @@
++!
++interface lo
++ ip address 10.0.0.2/32
++!
++interface r2-eth0
++ ip address 192.0.2.2/24
++!
++router bgp 65000
++ bgp router-id 10.0.0.2
++ no bgp default ipv4-unicast
++ neighbor 192.0.2.1 remote-as 65000
++ !
++ address-family l2vpn evpn
++ neighbor 192.0.2.1 activate
++ exit-address-family
++!
+diff --git a/tests/topotests/bgp_evpn_vxlan_local_vtep_none/test_bgp_evpn_vxlan_local_vtep_none.py b/tests/topotests/bgp_evpn_vxlan_local_vtep_none/test_bgp_evpn_vxlan_local_vtep_none.py
+new file mode 100644
+index 000000000000..876fc40fa73c
+--- /dev/null
++++ b/tests/topotests/bgp_evpn_vxlan_local_vtep_none/test_bgp_evpn_vxlan_local_vtep_none.py
+@@ -0,0 +1,76 @@
++#!/usr/bin/env python
++# SPDX-License-Identifier: ISC
++
++"""
++Verify that an L2VNI with no VXLAN local address reaches bgpd and uses the
++router-id as its originator IP.
++"""
++
++import os
++import sys
++import pytest
++
++CWD = os.path.dirname(os.path.realpath(__file__))
++sys.path.append(os.path.join(CWD, "../"))
++
++# pylint: disable=C0413
++from lib import topotest
++from lib.topogen import Topogen, get_topogen
++
++pytestmark = [pytest.mark.bgpd, pytest.mark.evpn]
++
++VNI = 101
++ROUTER_ID = "10.0.0.1"
++
++
++def setup_module(mod):
++ topodef = {"s1": ("r1", "r2")}
++ tgen = Topogen(topodef, mod.__name__)
++ tgen.start_topology()
++
++ router = tgen.gears["r1"]
++ router.cmd_raises(f"ip link add name br{VNI} type bridge")
++ router.cmd_raises("ip link set dev r1-eth0 up")
++ router.cmd_raises(
++ f"ip link add vxlan{VNI} type vxlan id {VNI} dstport 4789 "
++ "group 239.1.1.1 dev r1-eth0 nolearning"
++ )
++ router.cmd_raises(f"ip link set dev vxlan{VNI} master br{VNI}")
++ router.cmd_raises(f"ip link set dev br{VNI} up")
++ router.cmd_raises(f"ip link set dev vxlan{VNI} up")
++
++ for router in tgen.routers().values():
++ router.load_frr_config()
++
++ tgen.start_router()
++
++
++def teardown_module(mod):
++ tgen = get_topogen()
++ tgen.stop_topology()
++
++
++def test_bgp_vni_without_local_vtep_uses_router_id():
++ tgen = get_topogen()
++
++ if tgen.routers_have_failure():
++ pytest.skip(tgen.errors)
++
++ router = tgen.gears["r1"]
++
++ def _check_bgp_vni():
++ output = router.vtysh_cmd(
++ f"show bgp l2vpn evpn vni {VNI} json", isjson=True
++ )
++ expected = {"originatorIp": ROUTER_ID}
++ return topotest.json_cmp(output, expected)
++
++ _, result = topotest.run_and_expect(_check_bgp_vni, None, count=30, wait=1)
++ assert result is None, (
++ f"VNI {VNI} originator IP did not fall back to router-id {ROUTER_ID}"
++ )
++
++
++if __name__ == "__main__":
++ args = ["-s"] + sys.argv[1:]
++ sys.exit(pytest.main(args))
+--
+2.47.3
+
--
2.47.3
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-07-14 11:19 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-14 11:18 [PATCH frr 1/2] frr: backport VXLAN local VTEP fallback fix Gabriel Goller
2026-07-14 11:18 ` [PATCH frr 2/2] frr: bump to 10.6.1-1+pve3 Gabriel Goller
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.