summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNataliia Solomko <natalirs1985@gmail.com>2025-11-03 13:46:52 +0200
committerNataliia Solomko <natalirs1985@gmail.com>2025-11-13 17:15:52 +0200
commitdb494bc47c9bf2bba363b6b598e475bccdd4c8e6 (patch)
tree440bab582663ee8e3893eeec8e6a9ce7fd62578c
parent23e61833f4ac2a0dce040520c1324bab8c025209 (diff)
downloadvyos-1x-db494bc47c9bf2bba363b6b598e475bccdd4c8e6.tar.gz
vyos-1x-db494bc47c9bf2bba363b6b598e475bccdd4c8e6.zip
T7950: VPP: Unexpected None interface in CGNAT when ethernet subinterface is removed from vif
Do not allow to delete subinterface if it is in use in VPP features
-rwxr-xr-xsrc/conf_mode/interfaces_ethernet.py59
1 files changed, 59 insertions, 0 deletions
diff --git a/src/conf_mode/interfaces_ethernet.py b/src/conf_mode/interfaces_ethernet.py
index 14bda1b3d..edcc6563c 100755
--- a/src/conf_mode/interfaces_ethernet.py
+++ b/src/conf_mode/interfaces_ethernet.py
@@ -15,6 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
+import re
from sys import exit
@@ -173,6 +174,13 @@ def get_config(config=None):
ethernet['flowtable_interfaces'] = get_flowtable_interfaces(conf)
+ ethernet['vpp'] = conf.get_config_dict(
+ ['vpp'],
+ key_mangling=('-', '_'),
+ get_first_key=True,
+ no_tag_node_value_mangle=True,
+ )
+
# Protocols static arp dependency
if 'static_arp' in ethernet:
set_dependents('static_arp', conf)
@@ -307,8 +315,59 @@ def verify_flowtable(ethernet: dict):
if vifcname in ethernet['flowtable_interfaces']:
raise ConfigError(f'Cannot delete interface "{vifcname}", still referenced on a flowtable')
+def verify_vpp_remove_vif(ethernet: dict):
+ """Ensure that VIF interfaces being removed are not used by VPP features"""
+ vpp_paths_pattern = re.compile(
+ # Known paths that already use VLAN interfaces
+ r'(nat\.cgnat\.interface\.inside)|'
+ r'(nat\.cgnat\.interface\.outside)|'
+ r'(nat44\.interface\.inside)|'
+ r'(nat44\.interface\.outside)|'
+ # Potential paths for VLAN interfaces
+ r'(nat44\.address_pool\.translation\.interface)|'
+ r'(nat44\.address_pool\.twice_nat\.interface)|'
+ r'(nat44\.exclude\.rule\.(\d)+\.external_interface)|'
+ r'(interfaces\.bonding\.bond(\d)+\.member\.interface)|'
+ r'(interfaces\.bridge\.br(\d)+\.member\.interface)|'
+ r'(interfaces\.xconnect\.xcon(\d)+\.member\.interface)|'
+ r'(acl\.ip\.interface)|'
+ r'(acl\.macip\.interface)'
+ )
+ ifname = ethernet['ifname']
+
+ vlan_names = [
+ f'{ifname}.{vif_id}'
+ for vif_group in ['vif_remove', 'vif_s_remove']
+ for vif_id in ethernet.get(vif_group, [])
+ ]
+
+ if not vlan_names:
+ return
+
+ vpp_flat = dict_to_paths_values(ethernet.get('vpp', {}))
+
+ candidate_keys = []
+ for key, value in vpp_flat.items():
+ # Normalize values to list for consistent processing
+ values = value if isinstance(value, list) else [value]
+ if any(vlan in values for vlan in vlan_names):
+ candidate_keys.append((key, values))
+
+ if not candidate_keys:
+ return
+
+ for key, values in candidate_keys:
+ if vpp_paths_pattern.fullmatch(key):
+ used_vlans = [v for v in vlan_names if v in values]
+ if used_vlans:
+ raise ConfigError(
+ f'Cannot delete interface "{used_vlans[0]}", '
+ f'it is still in use by "vpp {key.replace(".", " ")}"'
+ )
+
def verify(ethernet):
verify_flowtable(ethernet)
+ verify_vpp_remove_vif(ethernet)
if 'deleted' in ethernet:
return None