From c90512f68dbabe6f005c259f6e787abe527fe975 Mon Sep 17 00:00:00 2001 From: Nataliia Solomko Date: Fri, 27 Feb 2026 21:22:15 +0200 Subject: vpp: T8327: Migrate bridge interface to 'interfaces vpp bridge' --- python/vyos/ifconfig/vpp/__init__.py | 2 + python/vyos/ifconfig/vpp/bridge.py | 139 ++++++++++++++++++++++++++++++++++ python/vyos/vpp/config_deps.py | 4 +- python/vyos/vpp/config_verify.py | 11 +++ python/vyos/vpp/interface/__init__.py | 2 - python/vyos/vpp/interface/bridge.py | 129 ------------------------------- 6 files changed, 154 insertions(+), 133 deletions(-) create mode 100644 python/vyos/ifconfig/vpp/bridge.py delete mode 100644 python/vyos/vpp/interface/bridge.py (limited to 'python') diff --git a/python/vyos/ifconfig/vpp/__init__.py b/python/vyos/ifconfig/vpp/__init__.py index 7abe402c6..38f72e42f 100644 --- a/python/vyos/ifconfig/vpp/__init__.py +++ b/python/vyos/ifconfig/vpp/__init__.py @@ -16,6 +16,7 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from .bond import VPPBondInterface +from .bridge import VPPBridgeInterface from .gre import VPPGREInterface from .interface import VPPInterface from .ipip import VPPIPIPInterface @@ -24,6 +25,7 @@ from .vxlan import VPPVXLANInterface __all__ = [ 'VPPBondInterface', + 'VPPBridgeInterface', 'VPPGREInterface', 'VPPInterface', 'VPPIPIPInterface', diff --git a/python/vyos/ifconfig/vpp/bridge.py b/python/vyos/ifconfig/vpp/bridge.py new file mode 100644 index 000000000..fce0dbfbb --- /dev/null +++ b/python/vyos/ifconfig/vpp/bridge.py @@ -0,0 +1,139 @@ +# +# Copyright (C) VyOS Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +from vyos.ifconfig.vpp.interface import VPPInterface +from vyos.vpp.utils import iftunnel_transform + + +class VPPBridgeInterface(VPPInterface): + def __init__(self, ifname): + self.instance = int(ifname.removeprefix('vppbr')) + self.vpp_ifname = f'br{self.instance}' + + super().__init__(self.vpp_ifname) + + def add(self): + """Create Bridge interface + https://github.com/FDio/vpp/blob/stable/2306/src/vnet/l2/l2.api + + Bridge-domain 0 is reserved for the default bridge-domain. + + Example: + from vyos.ifconfig.vpp import VPPBridgeInterface + a = VPPBridgeInterface(ifname='br23') + a.add() + """ + self.vpp.api.bridge_domain_add_del_v2( + is_add=True, + bd_id=self.instance, + flood=True, + forward=True, + learn=True, + uu_flood=True, + arp_term=False, + ) + + def delete(self): + """Delete Bridge interface + + Bridge-members must be detached before deleting the bridge interface. + + Example: + from vyos.ifconfig.vpp import VPPBridgeInterface + a = VPPBridgeInterface(ifname='br23') + a.delete() + """ + self.vpp.api.bridge_domain_add_del_v2(is_add=False, bd_id=self.instance) + + def get_members(self): + bridge = self.vpp.api.bridge_domain_dump(bd_id=self.instance)[0] + members = [] + for member in bridge.sw_if_details: + members.append(member.sw_if_index) + return members + + def add_member(self, member: str | int, port_type: int = 0): + """Add member to Bridge interface + + Attaches a VPP interface to the Bridge interface specified by `interface_suffix`. + The `member` parameter can be either the name (str) or the index (int) of the network + VPP interface to be added as a member to the bridge. + + Args: + member (str or int): The name or index of the VPP network interface + to be added as a member to the bridge. + port_type: 0 - Normal port, 1 - BVI port + + Example: + from vyos.ifconfig.vpp import VPPBridgeInterface + a = VPPBridgeInterface(ifname='br23') + a.add_member(member='eth0') + """ + member_if_index = self.vpp.get_sw_if_index(member) + return self.vpp.api.sw_interface_set_l2_bridge( + rx_sw_if_index=member_if_index, bd_id=self.instance, port_type=port_type + ) + + def detach_member(self, member: int): + """Detach member from Bridge interface. + Bridge-domain 0 is reserved for the default bridge-domain. + The `member` parameter can be either the name (str) or the index (int) + of the network VPP interface + + Args: + member (str or int): The name or index of the VPP network interface + to be detached from the bridge. + + Example: + from vyos.ifconfig.vpp import VPPBridgeInterface + a = VPPBridgeInterface(ifname='br23') + a.detach_member(member='eth0') + """ + # enable=0, 0 = Enable L3 mode + return self.vpp.api.sw_interface_set_l2_bridge( + rx_sw_if_index=member, bd_id=0, port_type=0, enable=0 + ) + + def remove(self): + if self.vpp.api.bridge_domain_dump(bd_id=self.instance): + # Detach all existing members + members = self.get_members() + for member in members: + self.detach_member(member=member) + + # Delete bridge interface + self.delete() + + def update(self, config): + # Add bridge interface + self.add() + + # Add members to bridge + for member, member_config in ( + config.get('member', {}).get('interface', []).items() + ): + member = member.removeprefix('vpp') + if member.startswith('vxlan'): + member = iftunnel_transform(member) + elif member.startswith('lo'): + # interface name in VPP is loopX + member = member.replace('lo', 'loop') + elif member.startswith('bond'): + # interface name in VPP is BondEthernetX + member = member.replace('bond', 'BondEthernet') + port = 1 if 'bvi' in member_config else 0 + self.add_member(member=member, port_type=port) diff --git a/python/vyos/vpp/config_deps.py b/python/vyos/vpp/config_deps.py index dffd865d1..4aab90a4a 100644 --- a/python/vyos/vpp/config_deps.py +++ b/python/vyos/vpp/config_deps.py @@ -61,7 +61,7 @@ def deps_bridge_dict(conf) -> dict[str, list[str]]: """ bridge_members_dict: dict[str, list[str]] = {} config = conf.get_config_dict( - ['vpp', 'interfaces', 'bridge'], + ['interfaces', 'vpp', 'bridge'], key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True, @@ -69,7 +69,7 @@ def deps_bridge_dict(conf) -> dict[str, list[str]]: for bridge_name, bridge_config in config.items(): for member_name in bridge_config.get('member', {}).get('interface', []): - bridge_ifaces_list = bridge_members_dict.get(bridge_name, []) + bridge_ifaces_list = bridge_members_dict.get(member_name, []) bridge_ifaces_list.append(bridge_name) bridge_members_dict.update({member_name: bridge_ifaces_list}) diff --git a/python/vyos/vpp/config_verify.py b/python/vyos/vpp/config_verify.py index 84c32f1e6..dab22cf8f 100644 --- a/python/vyos/vpp/config_verify.py +++ b/python/vyos/vpp/config_verify.py @@ -108,6 +108,17 @@ def verify_vpp_remove_xconnect_interface(config: dict): ) +def verify_vpp_remove_bridge_interface(config: dict): + if not 'deleted' in config: + return + for bridge_member, bridge_iface in config.get('bridge_members').items(): + if bridge_member == config.get('ifname'): + raise ConfigError( + f'interface "{bridge_member}" is still in use within "interfaces vpp bridge". ' + f'Please remove it from "interface vpp bridge {bridge_iface}" before proceeding.' + ) + + def verify_vpp_tunnel_source_address(config: dict): from vyos.utils.network import is_intf_addr_assigned diff --git a/python/vyos/vpp/interface/__init__.py b/python/vyos/vpp/interface/__init__.py index cdfb0c0b8..0a3176199 100644 --- a/python/vyos/vpp/interface/__init__.py +++ b/python/vyos/vpp/interface/__init__.py @@ -15,7 +15,6 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -from .bridge import BridgeInterface from .ethernet import EthernetInterface from .geneve import GeneveInterface from .interface import Interface @@ -23,7 +22,6 @@ from .wireguard import WireguardInterface from .xconnect import XconnectInterface __all__ = [ - 'BridgeInterface', 'EthernetInterface', 'GeneveInterface', 'Interface', diff --git a/python/vyos/vpp/interface/bridge.py b/python/vyos/vpp/interface/bridge.py deleted file mode 100644 index 1d1a6e1b4..000000000 --- a/python/vyos/vpp/interface/bridge.py +++ /dev/null @@ -1,129 +0,0 @@ -# -# Copyright (C) VyOS Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program 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 General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -from vyos.vpp import VPPControl - - -class BridgeInterface: - def __init__( - self, - ifname: str, - flood: bool = True, - forward: bool = True, - learn: bool = True, - uu_flood: bool = True, - arp_term: bool = False, - ): - self.ifname = ifname - self.interface_suffix = int(self.ifname.replace('br', '')) - self.flood = flood - self.forward = forward - self.learn = learn - self.uu_flood = uu_flood - self.arp_term = arp_term - self.vpp = VPPControl() - - def add(self): - """Create Bridge interface - https://github.com/FDio/vpp/blob/stable/2306/src/vnet/l2/l2.api - - Bridge-domain 0 is reserved for the default bridge-domain. - - Example: - from vyos.vpp.interface import BridgeInterface - a = BridgeInterface(ifname='br23') - a.add() - """ - self.vpp.api.bridge_domain_add_del_v2( - is_add=True, - bd_id=self.interface_suffix, - flood=self.flood, - forward=self.forward, - learn=self.learn, - uu_flood=self.uu_flood, - arp_term=self.arp_term, - ) - - def delete(self): - """Delete Bridge interface - - Bridge-members must be detached before deleting the bridge interface. - - Example: - from vyos.vpp.interface import BridgeInterface - a = BridgeInterface(ifname='br23') - a.delete() - """ - self.vpp.api.bridge_domain_add_del_v2(is_add=False, bd_id=self.interface_suffix) - - def add_member(self, member: str | int, port_type: int = 0): - """Add member to Bridge interface - - Attaches a VPP interface to the Bridge interface specified by `interface_suffix`. - The `member` parameter can be either the name (str) or the index (int) of the network - VPP interface to be added as a member to the bridge. - - Args: - member (str or int): The name or index of the VPP network interface - to be added as a member to the bridge. - port_type: 0 - Normal port, 1 - BVI port - - Example: - from vyos.vpp.interface import BridgeInterface - a = BridgeInterface(ifname='br23') - a.add_member(member='eth0') - """ - bridge_index = self.interface_suffix - # If the 'member' is an Integer or digit, assume it's an interface index - if isinstance(member, int): - member_if_index = member - elif member.isdigit(): - member_if_index = int(member) - else: - member_if_index = self.vpp.get_sw_if_index(member) - - return self.vpp.api.sw_interface_set_l2_bridge( - rx_sw_if_index=member_if_index, bd_id=bridge_index, port_type=port_type - ) - - def detach_member(self, member: str | int): - """Detach member from Bridge interface. - Bridge-domain 0 is reserved for the default bridge-domain. - The `member` parameter can be either the name (str) or the index (int) - of the network VPP interface - - Args: - member (str or int): The name or index of the VPP network interface - to be detached from the bridge. - - Example: - from vyos.vpp.interface import BridgeInterface - a = BridgeInterface(ifname='br23') - a.detach_member(member='eth0') - """ - # If the 'member' is an Integer or digit, assume it's an interface index - if isinstance(member, int): - member_if_index = member - elif member.isdigit(): - member_if_index = int(member) - else: - member_if_index = self.vpp.get_sw_if_index(member) - - # enable=0, 0 = Enable L3 mode - return self.vpp.api.sw_interface_set_l2_bridge( - rx_sw_if_index=member_if_index, bd_id=0, port_type=0, enable=0 - ) -- cgit v1.2.3