summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--interface-definitions/include/version/bgp-version.xml.i2
-rw-r--r--smoketest/configs/bgp-evpn-l3vpn-pe-router4
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_bgp.py67
-rwxr-xr-xsrc/conf_mode/protocols_bgp.py23
-rw-r--r--src/migration-scripts/bgp/7-to-845
5 files changed, 140 insertions, 1 deletions
diff --git a/interface-definitions/include/version/bgp-version.xml.i b/interface-definitions/include/version/bgp-version.xml.i
index 21fddf9ae..a283b9dd2 100644
--- a/interface-definitions/include/version/bgp-version.xml.i
+++ b/interface-definitions/include/version/bgp-version.xml.i
@@ -1,3 +1,3 @@
<!-- include start from include/version/bgp-version.xml.i -->
-<syntaxVersion component='bgp' version='7'></syntaxVersion>
+<syntaxVersion component='bgp' version='8'></syntaxVersion>
<!-- include end -->
diff --git a/smoketest/configs/bgp-evpn-l3vpn-pe-router b/smoketest/configs/bgp-evpn-l3vpn-pe-router
index 17d03c27d..fb2fb89dc 100644
--- a/smoketest/configs/bgp-evpn-l3vpn-pe-router
+++ b/smoketest/configs/bgp-evpn-l3vpn-pe-router
@@ -227,6 +227,8 @@ vrf {
}
}
advertise-all-vni
+ vni 2000 {
+ }
}
}
local-as 100
@@ -253,6 +255,8 @@ vrf {
}
}
advertise-all-vni
+ vni 4000 {
+ }
}
}
local-as 100
diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py
index 27fd9f1fc..ab0fa216f 100755
--- a/smoketest/scripts/cli/test_protocols_bgp.py
+++ b/smoketest/scripts/cli/test_protocols_bgp.py
@@ -1092,6 +1092,73 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_delete(vrf_path + ['protocols', 'bgp', 'address-family'])
self.cli_commit()
+ def test_bgp_08_l2vpn_evpn_advertise_all_vni_conflicts_with_vrf_vni(self):
+ # T8865: Configuring a "vni" sub-block under VRF BGP l2vpn-evpn while
+ # "advertise-all-vni" is active in the default BGP instance causes FRR
+ # to return "% Failed to create VNI" and perform an early exit from config
+ # processing.
+
+ vrf1, vrf2, vrf3 = 'alfa', 'beta', 'gamma'
+ vni1, vni2, vni3 = '5050', '5051', '5052'
+ vrf1_path = ['vrf', 'name', vrf1]
+ vrf2_path = ['vrf', 'name', vrf2]
+ vrf3_path = ['vrf', 'name', vrf3]
+
+ # Set up VRFs with L3VNIs
+ self.cli_set(vrf1_path + ['vni', vni1])
+ self.cli_set(vrf1_path + ['table', '1001'])
+ self.cli_set(vrf2_path + ['vni', vni2])
+ self.cli_set(vrf2_path + ['table', '1002'])
+ self.cli_set(vrf3_path + ['vni', vni3])
+ self.cli_set(vrf3_path + ['table', '1003'])
+
+ # Configure default BGP with advertise-all-vni
+ self.cli_set(base_path + ['system-as', ASN])
+ self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'advertise-all-vni'])
+
+ # Configure VRF BGP instances
+ for vrf_path in (vrf1_path, vrf2_path, vrf3_path):
+ self.cli_set(vrf_path + ['protocols', 'bgp', 'system-as', ASN])
+ unicast_path = ['l2vpn-evpn', 'advertise', 'ipv4', 'unicast']
+ self.cli_set(
+ vrf_path + ['protocols', 'bgp', 'address-family'] + unicast_path
+ )
+
+ # Adding "vni" sub-block under any VRF l2vpn-evpn while advertise-all-vni
+ # is active globally must be rejected
+ vrf1_af_path = vrf1_path + ['protocols', 'bgp', 'address-family']
+ vrf1_vni_path = vrf1_af_path + ['l2vpn-evpn', 'vni']
+ self.cli_set(vrf1_vni_path + [vni1])
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ # Remove the conflicting vni sub-block - commit must now succeed
+ self.cli_delete(vrf1_vni_path)
+ self.cli_commit()
+
+ # Verify default BGP has advertise-all-vni
+ frrconfig = self.getFRRconfig(f'router bgp {ASN}', stop_section='^exit')
+ self.assertIn(f'router bgp {ASN}', frrconfig)
+ self.assertIn(' advertise-all-vni', frrconfig)
+
+ # Verify all three VRF BGP instances are present in FRR config - this is
+ # the key regression check: FRR must not early-exit and silently drop
+ # l2vpn-evpn config for vrf2 and vrf3
+ for vrf_name, vni_name in ((vrf1, vni1), (vrf2, vni2), (vrf3, vni3)):
+ with self.subTest(vrf_name=vrf_name):
+ frr_vrf_config = self.getFRRconfig(
+ f'router bgp {ASN} vrf {vrf_name}', stop_section='^exit'
+ )
+ self.assertIn(f'router bgp {ASN} vrf {vrf_name}', frr_vrf_config)
+ self.assertIn(' address-family l2vpn evpn', frr_vrf_config)
+ # "vni" sub-block must NOT appear under any VRF BGP instance
+ self.assertNotIn(f' vni {vni_name}', frr_vrf_config)
+
+ # Cleanup
+ for path in (base_path, vrf1_path, vrf2_path, vrf3_path):
+ self.cli_delete(path)
+ self.cli_commit()
+
def test_bgp_09_distance_and_flowspec(self):
distance_external = '25'
distance_internal = '30'
diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py
index 2ec631f4c..bde6fcdd0 100755
--- a/src/conf_mode/protocols_bgp.py
+++ b/src/conf_mode/protocols_bgp.py
@@ -647,6 +647,29 @@ def verify(config_dict):
if 'route_target' in vni_config and 'advertise_all_vni' not in afi_config:
raise ConfigError('BGP EVPN "route-target" requires "advertise-all-vni" to be set!')
+ # T8865: Detect conflict between VRF-level "vni" sub-block and global
+ # "advertise-all-vni". When advertise-all-vni is active in the default
+ # BGP instance, FRR already owns all VNIs discovered from the kernel.
+ # Attempting to create the same VNI again via a VRF BGP "vni" sub-block
+ # causes FRR to return "% Failed to create VNI" and perform an early exit
+ # from config processing.
+ if vrf and 'vni' in afi_config:
+ default_bgp_evpn = dict_search(
+ 'dependent_vrfs.default.protocols.bgp.address_family.l2vpn_evpn',
+ bgp,
+ )
+ if (
+ default_bgp_evpn is not None
+ and 'deleted' not in default_bgp_evpn
+ ):
+ if 'advertise_all_vni' in default_bgp_evpn:
+ raise ConfigError(
+ 'BGP EVPN "vni" sub-configuration conflicts with '
+ '"advertise-all-vni" in the default BGP instance. '
+ 'Remove "vni" from the VRF l2vpn-evpn configuration '
+ 'or disable "advertise-all-vni" for the default BGP.'
+ )
+
return None
def generate(config_dict):
diff --git a/src/migration-scripts/bgp/7-to-8 b/src/migration-scripts/bgp/7-to-8
new file mode 100644
index 000000000..4db4b3211
--- /dev/null
+++ b/src/migration-scripts/bgp/7-to-8
@@ -0,0 +1,45 @@
+# Copyright (C) VyOS Inc.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+# T8865: When "address-family l2vpn-evpn advertise-all-vni" is
+# configured remove the conflicting "vni" sub-node from each affected VRF
+# because FRR returns "Failed to create VNI".
+
+from vyos.configtree import ConfigTree
+
+vrf_base = ['vrf', 'name']
+bgp_base = ['protocols', 'bgp']
+bgp_l2vpn_evpn = bgp_base + ['address-family', 'l2vpn-evpn']
+
+
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(vrf_base):
+ return # Nothing to do
+
+ # Only act when advertise-all-vni is set in the default BGP instance
+ if not config.exists(bgp_l2vpn_evpn + ['advertise-all-vni']):
+ return
+
+ for vrf in config.list_nodes(vrf_base):
+ vni_path = vrf_base + [
+ vrf,
+ 'protocols',
+ 'bgp',
+ 'address-family',
+ 'l2vpn-evpn',
+ 'vni',
+ ]
+ if config.exists(vni_path):
+ config.delete(vni_path)