Skip to content
LinkState
Go back

Refactoring regex ACL and prefix-list sprawl

Introduction to Route-Policy Stacks

Route-policy stacks are layered constructs that combine filtering mechanisms used by most network operating systems to decide which routes are admitted, modified, or advertised. The typical building blocks are:

ComponentPurposeTypical CLI representation
AS-Path ACL / AS-Path SetMatch on BGP AS_PATH attributes (regular-expression or exact-match lists)ip as-path access-list, as-path-set
Prefix ListMatch on NLRI prefixes (exact, range, or le/ge)ip prefix-list, prefix-set
Route-Map / Route-PolicyCombine matches, set actions (local-pref, MED, community, weight, etc.) and optionally call other policiesroute-map, route-policy (with apply)

In practice, engineers often start with simple ACLs and prefix lists, then copy-paste the same route-map stanza for each neighbor or peer-group. Over time, the stack becomes dense and hard to read, diff, or test.

Challenges with Overlapping AS-Path ACLs, Prefix Lists, and Route-Maps

These pains motivate a refactor toward named policy stages – reusable, independently versioned policy fragments that are composed rather than duplicated.

Identifying Refactoring Opportunities

Analyzing Existing Route-Policy Configurations

  1. Export the current policy: show running-config | section route-map (IOS) or show configuration policy-options policy-statement (Junos) or show route-map (FRR).
  2. Flatten the hierarchy: Convert every match clause into a tuple (type, value) and note the originating ACL/prefix-list name.
  3. Build a dependency graph: Nodes are ACLs/prefix-lists/route-maps; edges indicate “uses”. Overlap appears as multiple incoming edges to the same node from different parents.
  4. Quantify duplication: Count identical regex strings or prefix-list entries across ACLs; a count > 1 signals a refactor candidate.
RP/0/RP0/CPU0:router# show running-config route-policy
!
route-policy PEER-A-IN
  if destination in PFX-LIST-100 then
    set local-preference 120
  endif
  if as-path in AS-PATH-ACL-200 then
    set med 10
  endif
  apply COMMON-OUT
end-policy
!
route-policy PEER-B-IN
  if destination in PFX-LIST-100 then
    set local-preference 120
  endif
  if as-path in AS-PATH-ACL-200 then
    set med 10
  endif
  apply COMMON-OUT
end-policy

Both PEER-A-IN and PEER-B-IN repeat the same two if blocks – a clear duplication.

Detecting Overlapping and Redundant Configurations

Identifying Performance Bottlenecks and Scaling Limitations

If you observe rising CPU during BGP updates or TCAM exhaustion alerts, the policy stack is a prime scaling target.

Refactoring Route-Policy Stacks

Defining Named Policy Stages

A named policy stage is a self-contained policy fragment that performs a single logical function (e.g., “set local-pref for routes with AS-65000 origin”, “drop prefixes longer than /24”). Stages are invoked via an apply (IOS XR/Junos) or a call-like construct (FRR via continue or external scripting). The key properties:

Converting AS-Path ACLs to Named Policy Stages

Replace an ip as-path access-list with an as-path-set (IOS XR) or a community-set-like construct, then reference that set from a dedicated route-policy that only performs the match and returns a Boolean.

as-path-set AS65000-SET
  _65000_
end-set
!
route-policy MATCH_AS65000
  if as-path in AS65000-SET then
    pass
  else
    drop
  endif
end-policy
!
route-policy PEER-A-IN
  apply MATCH_AS65000
  set local-preference 130
end-policy

Now the AS-path match lives in MATCH_AS65000; any change to the regex is made in one place.

Converting Prefix Lists to Named Policy Stages

Similarly, extract prefix lists into prefix-sets and wrap them in a policy that only returns pass/drop.

prefix-set PFX-100-SET
  10.0.0.0/8 le 24
end-set
!
route-policy MATCH_PFX100
  if destination in PFX-100-SET then
    pass
  else
    drop
  endif
end-policy
!
route-policy PEER-B-IN
  apply MATCH_PFX100
  set community 65000:100 additive
end-policy

Converting Route-Maps to Named Policy Stages

Large route-maps that perform multiple independent actions are split into stage policies each handling one action set, then composed.

route-policy STAGE_SET_LOCALPREF_PFX100
  if destination in PFX-100-SET then
    set local-preference 150
  endif
end-policy
!
route-policy STAGE_SET_MED_COMM_AS65000
  if as-path in AS65000-SET then
    set med 5
    set community 65000:200 additive
  endif
end-policy
!
route-policy STAGE_SET_WEIGHT_COMMS300
  if community matches-any COMMS-300-SET then
    set weight 200
  endif
end-policy
!
route-policy PEER-C-IN
  apply STAGE_SET_LOCALPREF_PFX100
  apply STAGE_SET_MED_COMM_AS65000
  apply STAGE_SET_WEIGHT_COMMS300
end-policy

Each stage can be versioned, tested, and rolled back independently.

Implementing Named Policy Stages

Configuring Named Policy Stages using CLI

On IOS XR (similar on Junos with policy-statement and apply-path):

configure
  as-path-set AS65000-SET
    _65000_
  end-set
  !
  prefix-set PFX-100-SET
    10.0.0.0/8 le 24
  end-set
  !
  route-policy MATCH_AS65000
    if as-path in AS65000-SET then
      pass
    else
      drop
    endif
  end-policy
  !
  route-policy MATCH_PFX100
    if destination in PFX-100-SET then
      pass
    else
      drop
    endif
  end-policy
  !
  route-policy PEER-A-IN
    apply MATCH_AS65000
    set local-preference 130
  end-policy
  !
  route-policy PEER-B-IN
    apply MATCH_PFX100
    set community 65000:100 additive
  end-policy
commit

Verification:

show route-policy PEER-A-IN detail
show as-path-set AS65000-SET
show prefix-set PFX-100-SET

Configuring Named Policy Stages using Code (e.g., Python, Ansible)

Ansible (using cisco.iosxr.iosxr_config module) – keep the staged policy in a Jinja2 template.

File: templates/route-policy-stages.j2

as-path-set {{ as_path_set.name }}
{% for regex in as_path_set.regexes %}
  {{ regex }}
{% endfor %}
end-set
!
prefix-set {{ prefix_set.name }}
{% for prefix in prefix_set.prefixes %}
  {{ prefix }}
{% endfor %}
end-set
!
route-policy {{ match_as_policy.name }}
  if as-path in {{ as_path_set.name }} then
    pass
  else
    drop
  endif
end-policy
!
route-policy {{ match_pfx_policy.name }}
  if destination in {{ prefix_set.name }} then
    pass
  else
    drop
  endif
end-policy
!
route-policy {{ peer_policy.name }}
  apply {{ match_as_policy.name }}
  set local-preference {{ peer_policy.local_pref }}
end-policy

Playbook snippet:

- name: Deploy staged BGP policy
  hosts: ixr
  vars:
    as_path_set:
      name: AS65000-SET
      regexes:
        - "_65000_"
    prefix_set:
      name: PFX-100-SET
      prefixes:
        - "10.0.0.0/8 le 24"
    match_as_policy:
      name: MATCH_AS65000
    match_pfx_policy:
      name: MATCH_PFX100
    peer_policy:
      name: PEER-A-IN
      local_pref: 130
  tasks:
  - name: Configure route-policy stages
    iosxr_config:
      lines: "{{ lookup('template', 'route-policy-stages.j2') }}"
      before: []

Share this post on:

Previous Post
When neighbor tables disagree with EVPN truth
Next Post
Measuring tool-call latency inside an incident loop