summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Breunig <christian@breunig.cc>2026-03-26 16:20:41 +0100
committerGitHub <noreply@github.com>2026-03-26 16:20:41 +0100
commit1e694a1997ccc2a7267e25eb9c2cdfe29b0e9ce8 (patch)
treedcfe044722abb5c6eae9490c8bc3ef034e961928
parent9ed6c0c2ab164cbd3ed05a5b559135435841dd03 (diff)
parent29625d6081ac27c87dd8ce8e18371c54dc0cabe8 (diff)
downloadvyos-1x-1e694a1997ccc2a7267e25eb9c2cdfe29b0e9ce8.tar.gz
vyos-1x-1e694a1997ccc2a7267e25eb9c2cdfe29b0e9ce8.zip
Merge pull request #5080 from natali-rs1985/T8416
vpp: T8416: Prevent interfaces from being assigned to xconnect and bridge/bonding at the same time
-rw-r--r--python/vyos/vpp/config_verify.py29
-rwxr-xr-xsmoketest/scripts/cli/test_vpp.py23
-rw-r--r--src/conf_mode/vpp_interfaces_bonding.py9
-rw-r--r--src/conf_mode/vpp_interfaces_bridge.py11
-rw-r--r--src/conf_mode/vpp_interfaces_xconnect.py9
5 files changed, 66 insertions, 15 deletions
diff --git a/python/vyos/vpp/config_verify.py b/python/vyos/vpp/config_verify.py
index c93a647f9..b32544a40 100644
--- a/python/vyos/vpp/config_verify.py
+++ b/python/vyos/vpp/config_verify.py
@@ -62,6 +62,35 @@ def verify_vpp_tunnel_source_address(config: dict):
)
+def verify_member_conflicts(iface, config, current_type):
+ """
+ Check that a member interface is not already used by another interface type.
+
+ Args:
+ iface (str): interface name to check
+ config (dict): configuration dictionary
+ current_type (str): the membership type being assigned
+ to the interface ('bridge', 'bond', or 'xconn')
+
+ Raises:
+ ConfigError: If the interface is already a member of a conflicting interface type.
+ """
+ iface_type_map = {
+ 'bridge': 'bridge',
+ 'bond': 'bonding',
+ 'xconn': 'xconnect',
+ }
+ for iface_type, label in iface_type_map.items():
+ if iface_type == current_type:
+ continue
+ members = config.get(f'{iface_type}_members', {}).get(iface)
+ if members:
+ raise ConfigError(
+ f'Interface {iface} cannot be a member of {iface_type_map[current_type]} '
+ f'because it already belongs to {label} interface(s): {", ".join(members)}.'
+ )
+
+
def create_cpu_error_message(cpus_required: int, cpus_available: int = None) -> str:
cpu_info = get_cpus()
logical_cores = sum(
diff --git a/smoketest/scripts/cli/test_vpp.py b/smoketest/scripts/cli/test_vpp.py
index a98b5ff0b..108741790 100755
--- a/smoketest/scripts/cli/test_vpp.py
+++ b/smoketest/scripts/cli/test_vpp.py
@@ -543,6 +543,19 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
self.assertRegex(out, r'\s*eth1\s+\d+\s+\d+')
self.assertRegex(out, r'\s*vxlan_tunnel23\s+\d+\s+\d+')
+ # Cannot add members of bridge interface to cross-connect
+ # expect raise ConfigError
+ self.cli_set(
+ interfaces_path + ['xconnect', 'vppxcon1', 'member', 'interface', interface]
+ )
+ self.cli_set(
+ interfaces_path
+ + ['xconnect', 'vppxcon1', 'member', 'interface', interface_vxlan]
+ )
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_delete(interfaces_path + ['xconnect'])
+
# Add Loopback BVI to the bridge
self.cli_set(interfaces_path + ['loopback', f'vpplo{vni}'])
self.cli_set(
@@ -653,6 +666,16 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
for required_string in required_str_list:
self.assertIn(required_string, out)
+ # Cannot add members of cross-connect interface to bond/bridge
+ # expect raise ConfigError
+ self.cli_set(
+ interfaces_path
+ + ['bonding', 'vppbond1', 'member', 'interface', interface_vxlan]
+ )
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_delete(interfaces_path + ['bonding'])
+
# delete xconnect interface
self.cli_delete(xconn_path + [interface_xconnect])
self.cli_commit()
diff --git a/src/conf_mode/vpp_interfaces_bonding.py b/src/conf_mode/vpp_interfaces_bonding.py
index 46252c635..51272c087 100644
--- a/src/conf_mode/vpp_interfaces_bonding.py
+++ b/src/conf_mode/vpp_interfaces_bonding.py
@@ -29,6 +29,7 @@ from vyos.ifconfig.vpp import VPPBondInterface
from vyos.vpp.config_deps import deps_bond_dict
from vyos.vpp.config_deps import deps_bridge_dict
from vyos.vpp.config_deps import deps_xconnect_dict
+from vyos.vpp.config_verify import verify_member_conflicts
from vyos.vpp.config_verify import verify_vpp_remove_bridge_interface
from vyos.vpp.config_verify import verify_vpp_remove_xconnect_interface
from vyos.vpp.utils import cli_ifaces_list
@@ -168,13 +169,7 @@ def verify(config):
f'Interface {iface} cannot be a member of multiple bonding interfaces: {", ".join(bond_members)}'
)
- # Interface cannot be a member of a bridge and a bond at the same time
- bridge_members = config['bridge_members'].get(iface)
- if bridge_members:
- raise ConfigError(
- f'Interface {iface} cannot be a member of a bond because '
- f'it already belongs to bridge interface: {", ".join(bridge_members)}.'
- )
+ verify_member_conflicts(iface, config, 'bond')
if mtu := config.get('mtu'):
mtu = int(mtu)
diff --git a/src/conf_mode/vpp_interfaces_bridge.py b/src/conf_mode/vpp_interfaces_bridge.py
index 162345e6e..25fad5e5b 100644
--- a/src/conf_mode/vpp_interfaces_bridge.py
+++ b/src/conf_mode/vpp_interfaces_bridge.py
@@ -24,6 +24,8 @@ from vyos.utils.process import is_systemd_service_active
from vyos.ifconfig.vpp import VPPBridgeInterface
from vyos.vpp.config_deps import deps_bond_dict
from vyos.vpp.config_deps import deps_bridge_dict
+from vyos.vpp.config_deps import deps_xconnect_dict
+from vyos.vpp.config_verify import verify_member_conflicts
def get_config(config=None) -> dict:
@@ -67,6 +69,7 @@ def get_config(config=None) -> dict:
config['bond_members'] = deps_bond_dict(conf)
config['bridge_members'] = deps_bridge_dict(conf)
+ config['xconn_members'] = deps_xconnect_dict(conf)
return config
@@ -104,13 +107,7 @@ def verify(config):
f'Interface {member} is added to more than one bridge: {", ".join(bridge_members)}'
)
- # Interface cannot be a member of a bridge and a bond at the same time
- bond_members = config['bond_members'].get(member)
- if bond_members:
- raise ConfigError(
- f'Interface {member} cannot be a member of a bridge '
- f'because it already belongs to bonding interface: {", ".join(bond_members)}.'
- )
+ verify_member_conflicts(member, config, 'bridge')
# Check if BVI is already defined, only one BVI per bridge domain is allowed
if 'bvi' in member_config:
diff --git a/src/conf_mode/vpp_interfaces_xconnect.py b/src/conf_mode/vpp_interfaces_xconnect.py
index 090d913b0..91fa27c1b 100644
--- a/src/conf_mode/vpp_interfaces_xconnect.py
+++ b/src/conf_mode/vpp_interfaces_xconnect.py
@@ -22,7 +22,10 @@ from vyos.configdict import get_interface_dict
from vyos.utils.process import is_systemd_service_active
from vyos.ifconfig.vpp import VPPXconnectInterface
+from vyos.vpp.config_deps import deps_bond_dict
+from vyos.vpp.config_deps import deps_bridge_dict
from vyos.vpp.config_deps import deps_xconnect_dict
+from vyos.vpp.config_verify import verify_member_conflicts
from vyos.vpp.utils import cli_ifaces_list
@@ -59,6 +62,8 @@ def get_config(config=None) -> dict:
if effective_config:
config.update({'effective': effective_config})
+ config['bond_members'] = deps_bond_dict(conf)
+ config['bridge_members'] = deps_bridge_dict(conf)
config['xconn_members'] = deps_xconnect_dict(conf)
config['vpp_ifaces'] = cli_ifaces_list(conf, 'candidate')
@@ -78,7 +83,7 @@ def verify(config):
if len(config.get('member', {}).get('interface')) != 2:
raise ConfigError('Cross connect requires 2 members')
- not_allowed_prefixes = ('vppbond', 'vppbridge', 'vpplo')
+ not_allowed_prefixes = ('vppbond', 'vppbr', 'vpplo')
for iface in config.get('member', {}).get('interface', []):
# Ensure the interface is allowed as xconnect member
if iface.startswith(not_allowed_prefixes):
@@ -94,6 +99,8 @@ def verify(config):
f'Interface {iface} added to more than one xconnect: {", ".join(xconn_members)}'
)
+ verify_member_conflicts(iface, config, 'xconn')
+
def generate(config):
pass