Latest Cisco, PMP, AWS, CompTIA, Microsoft Materials on SALE Get Now Get Now

Route-Map and Prefix-List Advanced Filtering: Three Matches That Look Alike and Select Different Things

Most engineers use perhaps four of the route-map's match criteria and three of its set actions, which covers the common cases and leaves a considerable amount of expressive power unused. match ip address matches the prefix being advertised. match ip next-hop matches where that prefix says to send traffic. match ip route-source matches which router told you about it. Those three look similar in the configuration and select entirely different things, and knowing the difference is what lets you write a policy that says "anything learned from that router" rather than enumerating what that router happens to advertise today.

The prefix-list is the other half. It is not merely a better access list — it is a data structure the router searches efficiently, with sequence numbers, per-entry hit counters, a reference count showing everywhere it is used, and a length-range syntax that expresses in one line what an access list cannot express at all. Learning to read show ip prefix-list detail turns a filter from something you hope is right into something whose behaviour you can observe.

This article is a working reference for both. Section one covers what a prefix-list can express and how to read its operational state. Section two enumerates every match criterion with what it selects and when it is the right one. Section three does the same for set actions, grouped by what they affect. Section four covers composition — the AND and OR rules, continue, and sequence-number strategy. Section five is the failure catalogue for combinations that produce results nobody predicted. Throughout, the emphasis is on the criteria and actions that exist and go unused rather than on the handful everybody already knows.

Blog Claimmatch ip address, match ip next-hop, and match ip route-source read almost identically and select three different things — and choosing the wrong one produces a policy that works today because the three happen to coincide, and fails the day they stop.
 
The three address-related matches select the prefix, its next hop, and its advertiser respectively; the full match and set vocabularies are considerably larger than the handful in common use.

What Can a Prefix-List Actually Express?

What does the syntax cover?

A network, a length, and optionally a length range expressed with ge and le. Without qualifiers the entry matches that exact prefix and length and nothing else. With ge alone the range runs from the ge value to 32; with le alone it runs from the stated length to le; with both it runs between them. Both qualifiers must exceed the stated length and ge must not exceed le. Beyond the syntax, a prefix-list carries sequence numbers, an optional description, per-entry hit counters, and a reference count — operational state that an access list does not provide and that turns a filter into something observable.

A Deeper Dive into Prefix-Lists

The recipes worth memorising

Requirement Entry Matches
Exactly this prefix permit 10.0.0.0/8 10.0.0.0/8 only
Anything inside it, any length permit 10.0.0.0/8 le 32 10.0.0.0/8 through /32 inside 10/8
Only /24s inside it permit 10.0.0.0/8 ge 24 le 24 Every /24 in 10/8, not the /8 itself
Anything more specific than /24 permit 0.0.0.0/0 ge 25 /25 through /32, anywhere
The default route only permit 0.0.0.0/0 0.0.0.0/0 and nothing else
Everything permit 0.0.0.0/0 le 32 All IPv4 prefixes
Host routes only permit 0.0.0.0/0 ge 32 /32s only
Everything except one range deny 10.99.0.0/16 le 32 then permit 0.0.0.0/0 le 32 All but 10.99/16 and its specifics

Sequence numbers and editing

Entries are auto-numbered in increments of five, which leaves room to insert without renumbering. Unlike an AS-path access-list, a prefix-list entry can be removed individually by sequence number, so editing a live list is surgical rather than a full replace. Auto-numbering can be disabled globally, which is occasionally done to force explicit numbering in configuration management.

! Auto-numbered in fives
ip prefix-list CUSTOMER permit 203.0.113.0/24
ip prefix-list CUSTOMER permit 198.51.100.0/22 le 24
!
R1# show ip prefix-list CUSTOMER
ip prefix-list CUSTOMER: 2 entries
   seq 5 permit 203.0.113.0/24
   seq 10 permit 198.51.100.0/22 le 24
!
! Insert between them without touching either
ip prefix-list CUSTOMER seq 7 permit 192.0.2.0/24
!
! Remove one entry, surgically
no ip prefix-list CUSTOMER seq 7
!
! Disable auto-numbering where config management supplies them
no ip prefix-list sequence-number

When to use a prefix-list directly and when to wrap it in a route-map

A prefix-list attached directly — as a BGP prefix-list in, or as a distribute-list prefix — permits or denies and does nothing else. That is the right shape when the decision is purely binary and no attribute needs setting. The moment the policy involves setting a tag, a community, a metric, or a local preference, the prefix-list becomes a match condition inside a route-map and the route-map is what attaches.

There is a maintenance argument for using a route-map even when a bare prefix-list would do: the map gives you somewhere to add a set action later without changing what the configuration attaches, and it keeps every decision for a direction expressible in one ordered object. The cost is one extra layer of indirection to read, which is small against the cost of editing a live attachment.

! Direct: binary decision, nothing to set
router bgp 65000
 address-family ipv4 unicast
  neighbor 192.0.2.2 prefix-list CUSTOMER in
 exit-address-family
!
! Wrapped: the same selection, plus attributes
route-map CUST-IN permit 10
 match ip address prefix-list CUSTOMER
 set local-preference 300
 set community 65000:1000 additive
!
router bgp 65000
 address-family ipv4 unicast
  neighbor 192.0.2.2 route-map CUST-IN in
 exit-address-family
! ^ Adding a set action later touches only the map, not the neighbour.

Reading the operational state

The detail view is the part most people never look at. It shows how many times each entry matched, which identifies entries that are dead, and it shows a reference count naming how many places in the configuration use the list — which is what tells you whether editing it is a local change or affects several policies at once.

R1# show ip prefix-list detail CUSTOMER
ip prefix-list CUSTOMER:
   Description: prefixes AS 65100 is registered to announce
   count: 2, range entries: 1, sequences: 5 - 10, refcount: 3
   seq 5 permit 203.0.113.0/24 (hit count: 1, refcount: 1)
   seq 10 permit 198.51.100.0/22 le 24 (hit count: 41, refcount: 1)
! refcount 3 = three places reference this list.
! hit count 0 on an entry = it has never matched anything.
!
! Reset the counters to measure a specific window
R1# clear ip prefix-list CUSTOMER
!
! Add a description so the next engineer knows why it exists
ip prefix-list CUSTOMER description prefixes AS 65100 is registered to announce

Why it outperforms an access list

A prefix-list is stored as a tree keyed on the prefix, so a lookup is proportional to the address length rather than to the number of entries. An access list is evaluated sequentially. On a list with hundreds of entries applied to a full BGP table that difference is measurable, and it is the reason prefix-lists exist as a separate object rather than as an access-list variant. On a router applying a filter to a full internet table at every refresh, that structural difference is the one that shows up in convergence time. The readability advantage is what you notice; the performance advantage is why it was built.

! An ACL cannot express a length range at all
access-list 10 permit 10.0.0.0 0.255.255.255
! ^ Matches 10.0.0.0/8, /16, /24, /32 - every prefix starting 10.
!   The mask is not examined. This is almost never the intent.
!
! The prefix-list says exactly what is meant
ip prefix-list P1 permit 10.0.0.0/8 ge 16 le 24
! ^ Inside 10/8, lengths 16 through 24. Nothing else.
IPv6 prefix-lists are identical in structureipv6 prefix-list takes the same syntax with IPv6 prefixes and lengths up to 128, and the same ge/le semantics. The recipes translate directly: permit ::/0 ge 49 selects anything longer than a /48, and permit 2001:db8::/32 le 48 selects that allocation and its /33 through /48 subdivisions.
Name lists after what they contain, not where they are usedA list called CUSTOMER-A-BLOCKS can be referenced from a new place without becoming misleading; one called GI0-1-IN cannot, and the second reference is where the naming starts costing time. The same applies to route-maps: name them for the policy they express rather than the neighbour they were first attached to.
Check the hit counts before removing an entryAn entry with a hit count of zero has never matched anything since the counters were last cleared, which is evidence — not proof, since the counter resets on reload — that it may be dead. Clearing the counters, waiting a week, and re-reading is a cheap way to find filter entries that outlived whatever they were for.
Sub claimA prefix-list is a data structure with observable state rather than a list of rules, and the detail view — hit counts and reference count — is what turns editing one from a guess into an informed change.

What Are All the Match Criteria and When Is Each Right?

Which criterion answers which question?

Match on the prefix when the policy is about address ranges. Match on the next hop when it is about where traffic will go — useful for de-preferring everything that would exit via a particular device. Match on the route source when it is about who told you, which stays correct as that router's advertisements change. Match on interface when the policy is about which link a route arrived over. Match on route type to separate internal from external, or E1 from E2. Match on tag for anything that crossed a redistribution boundary. And in BGP, match on community, extended community, or AS path.

A Deeper Dive into the Match Criteria

The three address matches, distinguished

! The prefix being advertised
route-map BY-PREFIX permit 10
 match ip address prefix-list CUSTOMER-BLOCKS
!
! Where traffic for that prefix would be sent
ip prefix-list VIA-FIREWALL seq 5 permit 10.9.0.1/32
route-map BY-NEXTHOP permit 10
 match ip next-hop prefix-list VIA-FIREWALL
 set local-preference 50
! ^ De-prefer everything whose next hop is the firewall,
!   whatever prefixes those happen to be today.
!
! Which router advertised it to us
ip prefix-list FROM-R3 seq 5 permit 10.0.0.3/32
route-map BY-SOURCE permit 10
 match ip route-source prefix-list FROM-R3
 set tag 300
! ^ Everything R3 tells us, regardless of prefix or next hop.

The complete match vocabulary

Criterion Selects on Available in Typical use
match ip address The prefix All contexts Address-range policy
match ip next-hop The route's next hop Redistribution, BGP Policy about the exit device
match ip route-source The advertising router Redistribution, BGP Policy about a neighbour
match interface The route's outgoing interface Redistribution Filtering redistribute connected
match metric N The route's metric, optionally ± a deviation Redistribution Separating routes by cost band
match route-type internal, external 1, external 2, level-1, level-2, local, nssa-external Redistribution Keeping externals out of a redistribution
match tag A 32-bit route tag All IGPs Cross-protocol loop prevention
match community BGP standard communities BGP Acting on a classification
match extcommunity Route Target, Site of Origin BGP, VPNv4 VPN membership policy
match as-path AS_PATH regular expression BGP Origin and transit filtering
match local-preference The LOCAL_PREF value BGP Acting on a prior classification
match length Packet size range PBR only Steering by payload size

match route-type, which is more useful than it looks

Separating internal from external at a redistribution boundary is a legitimate alternative to tagging in simple topologies, and it is the only way to distinguish an OSPF E1 from an E2 in a policy. It is also how you exclude NSSA externals from a redistribution that should carry only intra-area routes.

! Redistribute only OSPF internal routes into EIGRP
route-map INTERNAL-ONLY permit 10
 match route-type internal
!
router eigrp CORP
 address-family ipv4 unicast autonomous-system 100
  topology base
   redistribute ospf 1 metric 1000000 100 255 1 1500 route-map INTERNAL-ONLY
  exit-af-topology
 exit-address-family
!
! Or treat E1 and E2 differently on the way out
route-map BY-EXT-TYPE permit 10
 match route-type external type-1
 set metric 50
route-map BY-EXT-TYPE permit 20
 match route-type external type-2
 set metric 200
route-map BY-EXT-TYPE permit 30

match interface, for filtering connected redistribution

! Redistribute only these interfaces' subnets
route-map CONN-SELECTED permit 10
 match interface GigabitEthernet0/1 GigabitEthernet0/2 Loopback0
! ^ Multiple interfaces on one statement is an OR.
!
router ospf 1
 redistribute connected subnets route-map CONN-SELECTED
!
! Useful where addressing does not partition cleanly by prefix -
! the management interface is excluded by not being named.
R5# show ip route 192.168.99.0
% Network not in table

match metric with a deviation

! Match a metric within a tolerance either side
route-map BY-COST permit 10
 match metric 100 +- 20
 ! matches 80 through 120
 set tag 500
!
! Exact match, no deviation
route-map EXACT-COST permit 10
 match metric 4294967295
 ! the unreachable metric - useful for catching poisoned routes
Pitfall: match ip address where match ip route-source was meant Symptom: a policy written to apply to everything a particular neighbour advertises works correctly at first and silently stops covering new prefixes as that neighbour's advertisements change. Cause: the policy matched the prefixes that neighbour happened to advertise rather than the neighbour itself, so it is a snapshot rather than a rule. Confirm: compare the prefix-list contents against what the neighbour currently advertises; prefixes present in one and not the other are the gap. Fix: use match ip route-source with a prefix-list containing the neighbour's address. The policy then describes the relationship rather than its current contents, and stays correct without maintenance.
Sub claimThe choice among the match criteria is a choice about what the policy is really about — a set of addresses, a relationship with a neighbour, or a property of the route — and matching on addresses when the policy is about a relationship is what makes it decay.

What Can a Route-Map Set?

How do the set actions group?

Four families. Metric and type actions change how the route competes inside the destination protocol — set metric, set metric-type, set level. BGP attribute actions change path selection or signalling — local preference, weight, community, AS-path prepending, origin. Tag actions record provenance for a later filter. And forwarding actions, which appear only in policy-based routing, override where a packet goes rather than what a route says. The four families rarely mix in one clause because they belong to different contexts, and a set action used in a context that does not support it is silently ignored.

A Deeper Dive into Set Actions

The complete set vocabulary

Action Effect Valid in Note
set metric N Absolute metric Redistribution, BGP (as MED) Also +N and -N for relative
set metric bw dly rel ld mtu EIGRP composite seed metric Redistribution into EIGRP Five values, two of which matter
set metric-type type-1|type-2 OSPF external type Redistribution into OSPF E1 adds internal cost
set tag N 32-bit route tag Redistribution, all IGPs Survives across protocols
set local-preference N BGP LOCAL_PREF BGP inbound Best-path step 2
set weight N Cisco weight BGP inbound Best-path step 1, local only
set community ... [additive] BGP communities BGP either direction Replaces without additive
set comm-list NAME delete Remove matching communities BGP either direction Stripping internal tags
set as-path prepend Lengthen AS_PATH BGP, usually outbound Best-path step 4
set origin igp|egp|incomplete ORIGIN attribute BGP Best-path step 5
set ip next-hop Override forwarding PBR, BGP inbound Does not change selection
set interface Force an egress interface PBR only Point-to-point links only, safely
set vrf NAME Move the packet to a VRF PBR only VRF selection
set ip dscp | precedence Remark the packet PBR only Marking at ingress

Relative metric adjustment

set metric +10 adds to whatever the route already had rather than replacing it, which preserves the relative ordering the source protocol computed. That is the difference between de-preferring a set of routes and flattening them, and it is the correct instrument where the requirement is "make these slightly worse" rather than "give these a fixed cost".

! Absolute: every matched route gets metric 500, ordering lost
route-map FLATTEN permit 10
 match ip address prefix-list SOME-BLOCK
 set metric 500
!
! Relative: each keeps its own cost, plus 500. Ordering preserved.
route-map DEPREFER permit 10
 match ip address prefix-list SOME-BLOCK
 set metric +500
!
! Verify the difference in the resulting table
R5# show ip route ospf | include SOME

Set actions that are silently ignored

A set action valid in one context and used in another produces no error and no effect. set local-preference in a redistribution route-map does nothing because the destination is not BGP. set interface in a BGP route-map does nothing because it is a PBR action. set metric in a PBR route-map does nothing because a packet has no metric. The CLI accepts all of them, and the only symptom is a policy that partially works.

! Accepted, and does nothing - the destination is EIGRP, not BGP
route-map WRONG-CONTEXT permit 10
 set local-preference 200
 set metric 1000000 100 255 1 1500
!
router eigrp CORP
 address-family ipv4 unicast autonomous-system 100
  topology base
   redistribute ospf 1 route-map WRONG-CONTEXT
  exit-af-topology
 exit-address-family
! ^ The metric applies. The local-preference is discarded silently.
!
! Read the map back and check every set against the context
R1# show route-map WRONG-CONTEXT
route-map WRONG-CONTEXT, permit, sequence 10
  Match clauses:
  Set clauses:
    local-preference 200
    metric 1000000 100 255 1 1500

The set actions that replace rather than add

Three set actions destroy information unless told otherwise. set community replaces the entire community list unless additive is present. set as-path prepend is additive by nature but set as-path tag and the AS-path manipulation commands are not interchangeable. And set metric with an absolute value discards whatever the source protocol computed, where set metric +N preserves it. In each case the destructive form is the shorter one and the default, which is why it appears in configurations where the additive form was intended.

Pitfall: set community without additive in a shared route-map Symptom: a route-map used in two places starts stripping communities that another policy depends on, and the failure appears in a part of the network unrelated to the change. Cause: the map was edited to add a community for one purpose, using the replacing form; every route passing through it in either context now carries only that community. Confirm: show ip prefix-list detail or show running-config | include route-map NAME reveals the map is referenced from more than one place; comparing show ip bgp <prefix> before and after shows the lost communities. Fix: add additive. Before editing any route-map, check how many places reference it — a map used in one place is a local change and a map used in three is not.
! Destructive by default
route-map TAG-IT permit 10
 set community 65000:100
! ^ Every other community on the route is discarded.
!
! Preserving
route-map TAG-IT permit 10
 set community 65000:100 additive
!
! Find out how many places use a map before editing it
R1# show running-config | include route-map TAG-IT
 neighbor 192.0.2.2 route-map TAG-IT in
 neighbor 192.0.2.6 route-map TAG-IT in
 redistribute ospf 1 route-map TAG-IT
! ^ Three references. This is not a local change.

Where a route-map can be attached

Attachment Command What the map sees
Redistribution redistribute X route-map NAME Each candidate route
BGP neighbour policy neighbor X route-map NAME in|out Each path
BGP network statement network A.B.C.D route-map NAME The originated prefix
BGP aggregate aggregate-address ... attribute-map | suppress-map | advertise-map The aggregate or its contributors
Distribute-list distribute-list route-map NAME in|out Each advertised or received route
Policy-based routing ip policy route-map NAME on an interface Each packet
Local policy routing ip local policy route-map NAME Router-generated packets
NAT ip nat inside source route-map NAME ... Each translated flow
Table map table-map NAME under BGP Routes as they enter the RIB
Group set actions by family within a clauseA clause that sets a metric, a tag, and three BGP attributes is doing several unrelated jobs and will eventually need one of them changed independently. Splitting by concern — one clause per family, chained with continue if they must all apply — costs a few extra lines and makes each change local. Where the actions genuinely belong together, keeping them in a consistent order across every map makes reading a set of maps considerably faster.
PBR route-maps see packets, everything else sees routesThat is why match length and set ip dscp exist only in PBR, and why set metric is meaningless there. It is also why a PBR route-map's implicit deny means "use normal routing" rather than "discard" — the packet still has to go somewhere. Treating the two kinds of route-map as one object is the source of most set actions that quietly do nothing.
Sub claimA set action in the wrong context is accepted and ignored, which makes reading a route-map back with show route-map and checking each action against where the map is attached the only way to catch a policy that half works.

How Do I Compose Multi-Criteria Logic?

What are the combination rules?

Within one clause, match statements of different types are ANDed — all must succeed. Multiple values on one match statement, or multiple statements of the same type, are ORed — any one satisfies it. A clause with no match statements matches everything. Across clauses, evaluation runs in ascending sequence order and stops at the first match, so ordering encodes precedence. continue overrides the stop, allowing a matched permit clause to apply its set actions and proceed to a later clause, which is how layered policies are built without a combinatorial explosion of clauses. Those four rules explain nearly every route-map whose behaviour surprises somebody, and none of them has an exception worth remembering.

A Deeper Dive into Composition

AND, OR, and the shapes they produce

! AND: in this prefix range AND carrying this community
route-map NARROW permit 10
 match ip address prefix-list CUSTOMER-BLOCKS
 match community GOLD-TIER
 set local-preference 300
!
! OR: multiple values on one statement
route-map BROAD permit 10
 match community GOLD-TIER SILVER-TIER BRONZE-TIER
 set local-preference 200
!
! OR: multiple statements of the same type
route-map ALSO-BROAD permit 10
 match ip address prefix-list BLOCK-A
 match ip address prefix-list BLOCK-B
 set local-preference 200
!
! NOT: expressed as a deny clause before the permit
route-map NOT-THESE deny 10
 match ip address prefix-list EXCLUDED
route-map NOT-THESE permit 20
 set local-preference 150

continue, for layered policy

Without continue, a route matching an early clause never sees a later one, so a policy that wants to apply a geography tag and then separately a customer-class preference needs a clause for every combination. continue lets each concern be expressed once. The cost is readability: a map with continue cannot be read top to bottom as a list of alternatives, and it should be reserved for cases where the alternative is genuinely unmanageable.

! Layer 1: geography. Layer 2: class. Layer 3: default.
route-map LAYERED permit 10
 match ip address prefix-list EMEA-BLOCKS
 set community 65000:2001 additive
 continue 30
!
route-map LAYERED permit 20
 match ip address prefix-list AMER-BLOCKS
 set community 65000:2002 additive
 continue 30
!
route-map LAYERED permit 30
 match community GOLD-TIER
 set local-preference 300
!
route-map LAYERED permit 40
 set local-preference 100
!
! Without continue, clause 10 would match and stop, and the
! GOLD-TIER logic in clause 30 would never run for EMEA prefixes.

Sequence numbering as precedence

Because the first match wins, the sequence order is the policy's precedence order: specific cases first, general cases last, catch-all at the highest number. Numbering in tens leaves room to insert. The most common structural error is a clause with no match statements placed early, which matches everything and makes every later clause unreachable.

! Correct ordering: specific, then general, then catch-all
route-map INGRESS deny 10
 match ip address prefix-list BOGONS
route-map INGRESS permit 20
 match ip address prefix-list CUSTOMER-A
 match community GOLD-TIER
 set local-preference 300
route-map INGRESS permit 30
 match ip address prefix-list CUSTOMER-A
 set local-preference 250
route-map INGRESS permit 40
 ! catch-all, highest sequence
 set local-preference 100
!
! Read it back and check for an early empty-match clause
R1# show route-map INGRESS
route-map INGRESS, permit, sequence 40
  Match clauses:
  ! ^ Empty match section. Correct here because it is last.

What continue does not do

continue can only move forward. It cannot jump backwards to a lower sequence number, so a map cannot loop, and it cannot skip into the middle of a clause. If the named sequence does not exist, processing falls through to the next clause that does. A continue with no sequence number proceeds to the immediately following clause, which is occasionally what is meant and more often an omission.

The subtler limitation is that set actions accumulate as the route travels through clauses, and a later clause setting the same attribute overwrites the earlier one. Two clauses both setting local preference means the last one visited wins, which is easy to get backwards when the clauses were written at different times by different people.

Pitfall: continue making a later clause overwrite an earlier set Symptom: a layered route-map applies the geography community correctly and the local preference is always the catch-all value, never the tier-specific one. Each clause reads correctly in isolation. Cause: the continue target was omitted or pointed past the tier clause, so processing fell through to the catch-all which set local preference again, overwriting the earlier value. Confirm: show route-map NAME and trace the continue targets against the sequence numbers; a continue with no number goes to the very next clause, not to the one you had in mind. Fix: state the target sequence explicitly on every continue, and order the map so attributes are set once each. Where two clauses must both set the same attribute, the later one is the one that takes effect.

Composition patterns worth knowing

Requirement Shape Note
A and B Two match types in one clause Different types only
A or B Multiple values, or multiple statements of one type Same type
Not A deny clause on A, then permit Deny must come first
A and not B deny on B, then permit matching A Order matters
Apply X to all, plus Y to some continue from the Y clause to the X clause Or duplicate the X action in each clause
Everything else Empty permit at the highest sequence Must be last
Reject everything else Nothing — the implicit deny does it State it explicitly for clarity
Write the catch-all first, then insert above itStarting a route-map with an empty permit 100 that expresses the default behaviour, then inserting specific clauses at lower numbers, means the map is never in a state where the implicit deny is silently rejecting things. It also makes the default explicit, which is the thing most maps leave to be inferred.
Sub claimSequence order is the policy's precedence and the first match ends evaluation, which makes an empty-match clause at a low sequence number the one structural error that silently disables everything written after it.

Which Combination Mistakes Produce Wrong Results?

What goes wrong?

Six things. A clause with no match statements placed early matches everything and makes the rest unreachable. Two match statements of the same type intended as AND are evaluated as OR. A set action in a context that does not support it is silently ignored. A deny clause carrying set actions applies none of them, because rejection ends processing. An implicit deny rejects everything a filtering map did not explicitly permit. And a prefix-list used where a route-source or next-hop match was meant produces a policy that decays as the network changes.

A Deeper Dive into the Failure Catalogue

The same-type AND that is really an OR

! Intended: in BOTH lists. Actual: in EITHER list.
route-map WRONG permit 10
 match ip address prefix-list BLOCK-A
 match ip address prefix-list BLOCK-B
 set local-preference 300
! ^ Two statements of the SAME type = OR.
!
! To express AND on prefixes, use one list that expresses it,
! or combine with a different match type
route-map RIGHT permit 10
 match ip address prefix-list BLOCK-A
 match community GOLD-TIER
 set local-preference 300
! ^ Different types = AND.
!
! Verify by counting what each shape selects
R1# show ip bgp route-map WRONG | include Total
R1# show ip bgp route-map RIGHT | include Total

Set actions on a deny clause

! The set is never applied - deny ends processing
route-map POINTLESS deny 10
 match ip address prefix-list SOME-BLOCK
 set tag 500
 ! ^ Never executed. The route was rejected.
!
! To tag AND exclude, two separate concerns are needed:
! tag it in the direction that permits, exclude it in the other
route-map TAG-IT permit 10
 match ip address prefix-list SOME-BLOCK
 set tag 500
route-map TAG-IT permit 20
!
route-map EXCLUDE-IT deny 10
 match tag 500
route-map EXCLUDE-IT permit 20

Debugging a map that does not behave

Route-maps used for routing policy have no per-clause hit counters, so the practical technique is bisection: temporarily reduce the map to a single permissive clause and confirm the behaviour changes, which separates a matching problem from an eligibility or context problem. For PBR maps, debug ip policy shows each packet's decision including which clause matched, and is the one context where the router will tell you directly.

! Bisect: replace the map with a permissive one, temporarily
route-map TEMP permit 10
!
router bgp 65000
 address-family ipv4 unicast
  neighbor 192.0.2.2 route-map TEMP in
R1# clear ip bgp 192.0.2.2 soft in
R1# show ip bgp neighbors 192.0.2.2 routes | include Total
! Count changed  -> the original map was the cause
! Count the same -> the problem is elsewhere entirely
!
! For PBR only, the router explains itself
R1# debug ip policy
IP: s=10.1.1.5 (Gi0/1), d=10.9.9.9, len 84, policy match
IP: route map PBR-MAP, item 10, permit
IP: s=10.1.1.5 (Gi0/1), d=10.9.9.9, len 84, policy routed
IP: local to Gi0/2 10.0.2.1
R1# undebug all

Reviewing somebody else's route-map

Reading an inherited map efficiently follows a fixed order. Read it back with show route-map rather than from the running configuration, because that shows how the router parsed it including the clause ordering. Check the last clause: if it is not an empty permit, the implicit deny is active and the map is a filter. Check every clause with set actions against where the map is attached, because a set in the wrong context is silently dead. Check for an empty match section anywhere but the last clause, which makes everything after it unreachable. And check the reference count of every object it names, because changing one may affect policies elsewhere.

! The review sequence, as commands
R1# show route-map INHERITED
!
! Where is it attached? That determines which sets are valid.
R1# show running-config | include route-map INHERITED
!
! What does each referenced list contain, and who else uses it?
R1# show ip prefix-list detail | include ^ip prefix-list|refcount
R1# show ip community-list
R1# show ip as-path-access-list
!
! Does the last clause permit, or is the implicit deny active?
R1# show route-map INHERITED | include sequence|permit|deny
route-map INHERITED, permit, sequence 10
route-map INHERITED, deny, sequence 20
! ^ Ends on a deny. Everything not matched by clause 10 is rejected.

Verification commands per context

! Read the map as the router parsed it
R1# show route-map INGRESS
!
! Find every place a map or list is referenced
R1# show running-config | include route-map INGRESS
R1# show ip prefix-list detail CUSTOMER | include refcount
!
! Test a prefix-list against the live table without applying it
R1# show ip bgp | include 203.0.113
!
! For BGP, compare pre-policy and post-policy counts
R1# show ip bgp neighbors 192.0.2.2 received-routes | include Total
R1# show ip bgp neighbors 192.0.2.2 routes | include Total
!
! For PBR, confirm the policy is attached where you think
R1# show ip policy
Interface      Route map
Gi0/1          PBR-MAP
Mistake Symptom Confirming command Fix
Empty-match clause placed early All routes get one clause's actions show route-map — empty Match section not last Renumber so it is highest
Same-type matches as AND Selects more than intended Compare counts against expectation Use different match types, or one list
Set in the wrong context Half the policy works show route-map, check against attachment Remove it; use the right context
Set on a deny clause The set never happens show route-map — sets under a deny Separate the two concerns
Missing trailing permit Everything rejected Route count drops to zero Add an empty permit at the end
Prefix match where source was meant Policy decays as prefixes change Compare list against current advertisements match ip route-source
Inverted ge/le Prefix-list matches nothing show ip prefix-list, hit counts zero Both must exceed the stated length
Exam contextRoute-map and prefix-list construction is a P0 area of the CCIE Enterprise Infrastructure blueprint and is examined by outcome: a task specifies exactly which routes must be affected and expects a map that selects those and nothing else. The reliably tested points are the AND-across-types and OR-within-a-type rule, the implicit deny, and the ge/le semantics. The less commonly known criteria — match ip route-source, match route-type, match interface — appear where the obvious address-based approach would produce side effects the task explicitly forbids.
Sub claimRoute-maps have no hit counters outside PBR, which makes bisection with a permissive map the practical debugging technique and makes reading the map back the only way to catch a set action in the wrong context.

Conclusion

The route-map is a small language and most of it goes unused. The three address-related matches — prefix, next hop, route source — are the clearest example: they read almost identically and select the prefix being advertised, the place traffic for it will go, and the router that told you about it. A policy about a neighbour relationship written with match ip address is a snapshot of what that neighbour advertised on the day it was written, and it decays. The same policy written with match ip route-source describes the relationship and does not.

The prefix-list deserves more attention than it usually gets for a different reason: it has observable state. Sequence numbers make it editable in place, hit counters show which entries are doing work, and the reference count shows how many policies depend on it before you change it. None of that exists for an access list, and none of it is visible without show ip prefix-list detail — which is why filters accumulate in networks where nobody has ever looked at whether they still match anything.

The composition rules are three sentences and worth having exactly right. Different match types in one clause are ANDed; multiple values of one type are ORed; a clause with no match statements matches everything and therefore belongs last. Sequence order is precedence and the first match ends evaluation unless continue says otherwise. And an implicit deny follows the final clause, which is why a map written to filter three things filters everything unless a trailing permit is added. Those rules explain nearly every route-map that behaves unexpectedly, and none of them is difficult once stated.

Reference Notes

  1. Cisco IOS-XE IP Routing Configuration Guide — route-map clauses are evaluated in ascending sequence order and the first matching clause terminates processing.
  2. Cisco IOS-XE IP Routing Configuration Guide — a route-map clause with no match statements matches all routes.
  3. Cisco IOS-XE IP Routing Configuration Guide — match statements of different types within a clause are ANDed; multiple values of a single type are ORed.
  4. Cisco IOS-XE IP Routing Configuration Guide — the continue clause permits processing to proceed to a specified later clause after a match.
  5. Cisco IOS-XE IP Routing Configuration Guide — match ip address, match ip next-hop, and match ip route-source match the prefix, the next hop, and the advertising router respectively.
  6. Cisco IOS-XE IP Routing Configuration Guide — match route-type values including internal, external type-1, external type-2, level-1, level-2, and nssa-external.
  7. Cisco IOS-XE IP Routing Configuration Guide — prefix-list ge and le must both exceed the specified prefix length, with ge not exceeding le.
  8. Cisco IOS-XE IP Routing Configuration Guide — prefix-list entries are auto-numbered in increments of five; no ip prefix-list sequence-number disables auto-numbering.
  9. Cisco IOS-XE IP Routing Configuration Guide — show ip prefix-list detail reports per-entry hit counts and the reference count for the list.
  10. Cisco IOS-XE IP Routing Configuration Guide — set metric accepts an absolute value or a relative + or - adjustment.
  11. Cisco IOS-XE IP Application Services Configuration Guide — match length and the packet-oriented set actions are valid only in policy-based routing.
  12. Cisco IOS-XE IP Routing Configuration Guide — set actions in a deny clause are not applied, because the clause rejects the route.