summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniil Baturin <daniil@vyos.io>2026-03-12 16:13:10 +0000
committerGitHub <noreply@github.com>2026-03-12 16:13:10 +0000
commitdbe48341d7eb804bdbe4955e7e0728bfea560b88 (patch)
tree7d7483818fd36f47581ca28bea4a43bfd8d923e3
parentaf8e4b83017e0c1b28cd1e447f760fdc32e4f2ff (diff)
parent78e770e21a2b294566fe8b34ac6497657bbc0b4e (diff)
downloadvyos-1x-dbe48341d7eb804bdbe4955e7e0728bfea560b88.tar.gz
vyos-1x-dbe48341d7eb804bdbe4955e7e0728bfea560b88.zip
Merge pull request #5028 from natali-rs1985/T8342
vpp: T8342: Add verification of members for bond and bridge interfaces
-rw-r--r--python/vyos/vpp/config_deps.py30
-rwxr-xr-xsrc/conf_mode/vpp.py10
-rw-r--r--src/conf_mode/vpp_interfaces_bonding.py18
-rw-r--r--src/conf_mode/vpp_interfaces_bridge.py20
4 files changed, 77 insertions, 1 deletions
diff --git a/python/vyos/vpp/config_deps.py b/python/vyos/vpp/config_deps.py
index 43cf6036f..a822825c3 100644
--- a/python/vyos/vpp/config_deps.py
+++ b/python/vyos/vpp/config_deps.py
@@ -74,3 +74,33 @@ def deps_bridge_dict(conf) -> dict[str, list[str]]:
bridge_members_dict.update({member_name: bridge_ifaces_list})
return bridge_members_dict
+
+
+def deps_bond_dict(conf) -> dict[str, list[str]]:
+ """Get a dict of all bonding interface members:
+
+ keys: members
+
+ values: bridge interfaces
+
+ Args:
+ conf (config): VyOS config object
+
+ Returns:
+ dict[str, list[str]]: dict of members
+ """
+ bond_members_dict: dict[str, list[str]] = {}
+ config = conf.get_config_dict(
+ ['interfaces', 'vpp', 'bonding'],
+ key_mangling=('-', '_'),
+ get_first_key=True,
+ no_tag_node_value_mangle=True,
+ )
+
+ for bond_name, bond_config in config.items():
+ for member_name in bond_config.get('member', {}).get('interface', []):
+ bond_ifaces_list = bond_members_dict.get(member_name, [])
+ bond_ifaces_list.append(bond_name)
+ bond_members_dict.update({member_name: bond_ifaces_list})
+
+ return bond_members_dict
diff --git a/src/conf_mode/vpp.py b/src/conf_mode/vpp.py
index e6b739179..22cf04020 100755
--- a/src/conf_mode/vpp.py
+++ b/src/conf_mode/vpp.py
@@ -48,6 +48,7 @@ from vyos.utils.process import is_systemd_service_active
from vyos.vpp import VPPControl
from vyos.vpp import control_host
from vyos.vpp import VppNotRunningError
+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 (
@@ -257,6 +258,7 @@ def get_config(config=None):
xconn_members = deps_xconnect_dict(conf)
bridge_members = deps_bridge_dict(conf)
+ bond_members = deps_bond_dict(conf)
removed_ifaces = []
tmp = node_changed(conf, base_settings + ['interface'])
@@ -298,6 +300,7 @@ def get_config(config=None):
'removed_ifaces': removed_ifaces,
'xconn_members': xconn_members,
'bridge_members': bridge_members,
+ 'bond_members': bond_members,
'persist_config': eth_ifaces_persist,
'interfaces_vpp': interfaces_config,
'remove': {},
@@ -438,6 +441,7 @@ def get_config(config=None):
config['removed_ifaces'] = removed_ifaces
config['xconn_members'] = xconn_members
config['bridge_members'] = bridge_members
+ config['bond_members'] = bond_members
config['interfaces_vpp'] = interfaces_config
@@ -601,7 +605,7 @@ def verify(config):
f'RX mode {rx_mode} is not supported for interface {iface}'
)
- # Check if deleted interfaces are not xconnect members or not bridge members
+ # Check if deleted interfaces are not xconnect/bridge/bond members
for iface_config in config.get('removed_ifaces', []):
if iface_config['iface_name'] in config.get('xconn_members', {}):
raise ConfigError(
@@ -611,6 +615,10 @@ def verify(config):
raise ConfigError(
f'Interface {iface_config["iface_name"]} is a bridge member and cannot be removed'
)
+ if iface_config['iface_name'] in config.get('bond_members', {}):
+ raise ConfigError(
+ f'Interface {iface_config["iface_name"]} is a bond member and cannot be removed'
+ )
verify_routes_count(config['settings'])
diff --git a/src/conf_mode/vpp_interfaces_bonding.py b/src/conf_mode/vpp_interfaces_bonding.py
index 553200832..582b82d99 100644
--- a/src/conf_mode/vpp_interfaces_bonding.py
+++ b/src/conf_mode/vpp_interfaces_bonding.py
@@ -24,6 +24,7 @@ from vyos.utils.assertion import assert_mac
from vyos.utils.process import is_systemd_service_active
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_vpp_remove_bridge_interface
@@ -92,6 +93,8 @@ def get_config(config=None) -> dict:
no_tag_node_value_mangle=True,
)
+ config['bond_members'] = deps_bond_dict(conf)
+
# Dependency
config['xconn_members'] = deps_xconnect_dict(conf)
if ifname in config['xconn_members']:
@@ -140,6 +143,21 @@ def verify(config):
if iface not in config['vpp_ifaces']:
raise ConfigError(f'{iface} must be a VPP interface for bonding')
+ # Each interface can belong only to one bond
+ bond_members = config['bond_members'][iface]
+ if len(bond_members) > 1:
+ raise ConfigError(
+ 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)}.'
+ )
+
if 'mac' in config:
mac = config['mac']
try:
diff --git a/src/conf_mode/vpp_interfaces_bridge.py b/src/conf_mode/vpp_interfaces_bridge.py
index 37cec7b94..162345e6e 100644
--- a/src/conf_mode/vpp_interfaces_bridge.py
+++ b/src/conf_mode/vpp_interfaces_bridge.py
@@ -22,6 +22,8 @@ from vyos import ConfigError
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
def get_config(config=None) -> dict:
@@ -63,6 +65,9 @@ def get_config(config=None) -> dict:
with_recursive_defaults=True,
)
+ config['bond_members'] = deps_bond_dict(conf)
+ config['bridge_members'] = deps_bridge_dict(conf)
+
return config
@@ -92,6 +97,21 @@ def verify(config):
f"Interface '{member}' not found in 'vpp settings interface' or does not start with allowed prefixes {allowed_prefixes}"
)
+ # Each interface can belong only to one bridge
+ bridge_members = config['bridge_members'][member]
+ if len(bridge_members) > 1:
+ raise ConfigError(
+ 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)}.'
+ )
+
# Check if BVI is already defined, only one BVI per bridge domain is allowed
if 'bvi' in member_config:
if bvi_exists: