Page MenuHomeVyOS Platform

policy: Route-map deletion check misses nested BGP VPN/VRF references
Open, LowPublicBUG

Description

Description

VyOS incorrectly allows a route-map definition to be deleted even when it is still referenced by BGP VRF/VPN import/export.

Reproduction

Configure a route-map and import a connected route from VRF red into the default BGP instance:

configure

set policy route-map KEEP rule 10 action permit

set vrf name red table 1001
set interfaces dummy dum0 address 203.0.113.1/32
set interfaces dummy dum0 vrf red
set vrf name red protocols bgp system-as 65100
set vrf name red protocols bgp parameters router-id 203.0.113.1
set vrf name red protocols bgp address-family ipv4-unicast redistribute connected

set protocols bgp system-as 65100
set protocols bgp parameters router-id 203.0.113.2
set protocols bgp address-family ipv4-unicast import vrf red
set protocols bgp address-family ipv4-unicast route-map vrf import KEEP

commit

Delete only the route-map definition, leaving its BGP reference unchanged:

delete policy route-map KEEP
commit

Expected result: The commit should be rejected by the existing policy-in-use validation:

Cannot delete route-map "KEEP", still in use!

Actual result: The commit succeeds. FRR no longer contains the route-map KEEP permit 10 definition, but retains:

router bgp 65100
 address-family ipv4 unicast
  import vrf route-map KEEP
  import vrf red

Cause

The issue originates in routing_policy_find() in src/conf_mode/policy.py:

if k == key:
    if isinstance(v, dict):
        for a, b in v.items():
            if a in ['import', 'export']:
                yield b
    else:
        yield v
elif isinstance(v, dict):
    for result in routing_policy_find(key, v):
        yield result

Once route_map is matched, only its immediate import and export children are checked. The helper does not descend into intermediate vrf or vpn dictionaries, so references such as the following are missed:

{'route_map': {'vrf': {'import': 'KEEP'}}}

Proposed fix

Recursively inspect nested dictionaries under the matched policy key. Wrap b as {key: b} so the recursive call retains the policy-key context and reuses the existing import/export handling.

         if k == key:
             if isinstance(v, dict):
                 for a, b in v.items():
                     if a in ['import', 'export']:
                         yield b
+                    elif isinstance(b, dict):
+                        # BGP AF policies also nest directions under vpn/vrf.
+                        yield from routing_policy_find(key, {key: b})
             else:
                 yield v

Details

Version
rolling
Is it a breaking change?
Perfectly compatible
Issue type
Bug (incorrect behavior)