BGP Routing Policies: The Tooling, the Sharp Edges, and the Filter Everyone Forgets
A BGP router with no policy will accept everything a neighbour sends and re-advertise everything it knows. That is the default behaviour that produced every large route leak of the last two decades, and it is why RFC 8212 now specifies that an eBGP session should exchange nothing until an operator has explicitly said what to exchange. Policy in BGP is not an optimisation applied after the protocol works. It is the mechanism that decides what the protocol does at all, and a session without it is a session whose behaviour was chosen by whoever is on the other end.
The tooling is small and the semantics are precise. Four filter mechanisms attach to a neighbour in each direction — prefix-list, filter-list for AS paths, distribute-list, and route-map — and a prefix must pass every one that is configured. Route-maps are the general instrument: an ordered sequence of numbered clauses, each either permit or deny, each with zero or more match conditions and zero or more set actions, terminated by an implicit deny that catches everything no clause matched. Prefix-lists match address ranges with ge and le qualifiers whose meanings are easy to invert. AS-path access-lists use regular expressions where the underscore is a delimiter class rather than a literal character. Community-lists come in standard and expanded forms with different matching semantics.
This article covers the whole toolkit and the reasoning behind each choice. Section one maps the tools onto the places they attach and explains what happens when several apply at once. Section two dissects route-map processing — clause order, permit versus deny, the implicit deny, and continue. Section three covers precise matching: prefix-list ge/le, AS-path regular expressions, and the two kinds of community list. Section four covers what policy can change and which best-path step each change affects. Section five is the failure catalogue, starting with the route leak that an outbound filter would have prevented.

What Are the Policy Tools and Where Does Each Apply?
Which mechanism should I use for which job?
Use a prefix-list when the decision is purely about address ranges, because it is fast, readable, and expresses length constraints directly. Use a filter-list when the decision is about AS path, since it takes an AS-path access-list and nothing else does. Use a route-map when the decision involves more than one criterion, or when you need to change an attribute rather than only permit or deny. Distribute-lists are the legacy mechanism and offer nothing a prefix-list does not, aside from extended access-list matching on prefix and mask together. All of them attach to a neighbour under a specific address family, in a specific direction, and every one configured in that direction must permit a prefix for it to pass.
A Deeper Dive into the Attachment Model
The four mechanisms side by side
| Mechanism | Matches on | Can modify attributes | Command | Best used for |
|---|---|---|---|---|
| Prefix-list | Prefix and length | No | neighbor X prefix-list NAME in|out |
Clean address-range filtering |
| Filter-list | AS_PATH regular expression | No | neighbor X filter-list NAME in|out |
Accepting or rejecting by origin or transit AS |
| Distribute-list | Standard or extended ACL | No | neighbor X distribute-list NAME in|out |
Legacy; extended ACL can match prefix plus mask |
| Route-map | Anything, including several criteria together | Yes | neighbor X route-map NAME in|out |
Combined matching, and any attribute change |
| Table-map | Applied on RIB installation | Limited — traffic-index, local policy | table-map NAME |
Marking routes as they enter the RIB |
| ORF | Prefix-list pushed to the peer | No | neighbor X capability orf prefix-list send |
Making the peer filter on your behalf |
Policy attaches per address family
A route-map applied under address-family ipv4 unicast has no effect on VPNv4 or IPv6 prefixes from the same neighbour. This is easy to miss when a neighbour is activated in several families, and the symptom is policy that works for one family and silently does nothing for another. Every filter statement must be repeated in every family where it should apply.
! One neighbour, three families, three separate policy attachments
router bgp 65000
neighbor 192.0.2.2 remote-as 65001
!
address-family ipv4 unicast
neighbor 192.0.2.2 activate
neighbor 192.0.2.2 route-map CUST-IN in
neighbor 192.0.2.2 prefix-list CUST-PFX in
neighbor 192.0.2.2 route-map CUST-OUT out
exit-address-family
!
address-family ipv6 unicast
neighbor 192.0.2.2 activate
! Separate policy required - the IPv4 maps do not apply here
neighbor 192.0.2.2 route-map CUST-IN-V6 in
neighbor 192.0.2.2 route-map CUST-OUT-V6 out
exit-address-family
Multiple filters in one direction are ANDed
Configuring a prefix-list and a route-map inbound on the same neighbour means a prefix must be permitted by both. That is occasionally useful — a coarse prefix-list plus a route-map that sets attributes — and frequently confusing, because a prefix denied by the prefix-list never reaches the route-map and no amount of reading the route-map explains why. Using one mechanism per direction removes the ambiguity entirely, and a route-map can express anything the others can.
! Both must permit. A prefix rejected by the list never sees the map.
neighbor 192.0.2.2 prefix-list ALLOWED in
neighbor 192.0.2.2 route-map SET-ATTRS in
!
! Cleaner: one route-map that does both jobs
ip prefix-list ALLOWED seq 5 permit 203.0.113.0/24
!
route-map CUST-IN permit 10
match ip address prefix-list ALLOWED
set local-preference 200
set community 65000:1000 additive
! implicit deny 65535 rejects everything else
!
router bgp 65000
address-family ipv4 unicast
neighbor 192.0.2.2 route-map CUST-IN in
Reusing policy across many neighbours
A network with fifty customer sessions does not want fifty copies of the same policy. Peer templates solve this in two layers: a session template carries the transport-level settings — remote AS, source interface, timers, password — while a policy template carries the address-family settings including route-maps and prefix-lists. A neighbour inherits one of each, and templates can inherit from other templates, so a base customer policy can be specialised for a particular class of customer without duplication.
Peer-groups are the older mechanism and do both jobs at once. They remain widely deployed and have one operational advantage: members share an update group, so an advertisement is formatted once and sent to all of them, which matters on a router with hundreds of sessions. Templates achieve the same update-group efficiency when the resulting policy is identical, so the choice is mostly about configuration clarity rather than performance.
! Session template: transport settings only
template peer-session CUSTOMER-SESSION
timers 10 30
password 7 08351F1B1D431F5B
update-source Loopback0
exit-peer-session
!
! Policy template: address-family settings
template peer-policy CUSTOMER-POLICY
route-map CUST-IN in
route-map CUST-OUT out
maximum-prefix 500 80 warning-only
send-community both
exit-peer-policy
!
router bgp 65000
neighbor 192.0.2.2 remote-as 65100
neighbor 192.0.2.2 inherit peer-session CUSTOMER-SESSION
address-family ipv4 unicast
neighbor 192.0.2.2 activate
neighbor 192.0.2.2 inherit peer-policy CUSTOMER-POLICY
exit-address-family
!
! Confirm what a neighbour actually inherited
R1# show ip bgp template peer-policy CUSTOMER-POLICY
R1# show ip bgp neighbors 192.0.2.2 policy
Applying and reverting policy needs a refresh
Changing an inbound policy does not re-evaluate prefixes the peer already sent. Either the peer must resend them, which route refresh accomplishes without dropping the session, or the local router must have kept a pre-policy copy via soft reconfiguration. Route refresh is negotiated by essentially every modern implementation and costs nothing; soft reconfiguration costs memory proportional to the table size and should be enabled only for a troubleshooting window.
! Confirm route refresh is available before relying on it
R1# show ip bgp neighbors 192.0.2.2 | include Route refresh
Route refresh: advertised and received(new)
!
! Re-apply inbound policy without dropping the session
R1# clear ip bgp 192.0.2.2 soft in
! Re-apply outbound policy - always local, never disruptive
R1# clear ip bgp 192.0.2.2 soft out
!
! Pre-policy view, for troubleshooting only
router bgp 65000
address-family ipv4 unicast
neighbor 192.0.2.2 soft-reconfiguration inbound
How Does a Route-Map Actually Process a Prefix?
What are the processing rules?
Clauses are evaluated in ascending sequence number order. Within a clause, every match statement of a different type must succeed, while multiple values of the same type are an OR. A clause with no match statements matches every prefix. When a clause matches, processing stops: a permit clause permits the prefix and applies its set statements, a deny clause rejects it and any set statements are ignored. If no clause matches, an implicit deny at the end rejects the prefix. That last rule is the one that catches people, because a route-map written to deny a few prefixes rejects everything else as well unless a trailing permit clause is added.
A Deeper Dive into Route-Map Semantics
The implicit deny, demonstrated
! WRONG - this rejects everything, not just the bogons
route-map CUST-IN deny 10
match ip address prefix-list BOGONS
! implicit: deny everything else. Nothing is accepted at all.
!
! RIGHT - deny the bogons, then explicitly permit the rest
route-map CUST-IN deny 10
match ip address prefix-list BOGONS
!
route-map CUST-IN permit 20
set local-preference 200
! ^ No match clauses, so this matches everything that survived
! clause 10, permits it, and sets local preference.
AND across match types, OR within one
Two match statements of different types in the same clause must both succeed. Two values on the same match statement, or two statements of the same type, are alternatives. This distinction determines whether a clause is narrow or broad and is worth verifying whenever a route-map matches more or fewer prefixes than expected.
! AND: must be in the prefix-list AND carry the community
route-map NARROW permit 10
match ip address prefix-list CUSTOMER-BLOCKS
match community CUST-TAG
set local-preference 250
!
! OR: either community list satisfies the clause
route-map BROAD permit 10
match community CUST-TAG PEER-TAG
set local-preference 150
!
! Also OR: two 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 statements on a deny clause do nothing
A deny clause rejects the prefix, and rejection is the end of processing. Any set statements in that clause are never applied. This appears in configurations where someone intended to deny a prefix from being advertised while still tagging it locally, which is not what the syntax does — tagging requires a permit clause somewhere.
The continue statement
continue overrides the stop-on-match behaviour, allowing a matched permit clause to apply its set statements and then proceed to a later clause. It enables layered policies where several clauses each contribute part of the final attribute set. It also makes a route-map considerably harder to read, so use it where the alternative is a combinatorial explosion of clauses and not merely to save typing.
! Layered tagging: geography, then customer class, then action
route-map INGRESS permit 10
match ip address prefix-list EMEA-BLOCKS
set community 65000:1001 additive
continue 30
!
route-map INGRESS permit 20
match ip address prefix-list AMER-BLOCKS
set community 65000:1002 additive
continue 30
!
route-map INGRESS permit 30
match community GOLD-CUSTOMER
set local-preference 300
!
route-map INGRESS permit 40
set local-preference 100
! ^ Without 'continue', a prefix matching clause 10 would never
! reach the local-preference logic in clause 30.
Verifying what a route-map does before applying it
! Read the map back with its hit counters
R1# show route-map CUST-IN
route-map CUST-IN, deny, sequence 10
Match clauses:
ip address prefix-lists: BOGONS
Set clauses:
Policy routing matches: 0 packets, 0 bytes
route-map CUST-IN, permit, sequence 20
Match clauses:
Set clauses:
local-preference 200
!
! Find every neighbour a given map is attached to
R1# show running-config | include route-map CUST-IN
!
! Compare pre-policy and post-policy for one neighbour
R1# show ip bgp neighbors 192.0.2.2 received-routes | count network
R1# show ip bgp neighbors 192.0.2.2 routes | count network
! ^ A large difference is your inbound policy doing its job, or
! doing considerably more than you intended.
match statements sits at a lower sequence number than the specific clauses, so it matches every prefix first and processing stops there. Confirm: show route-map NAME and look for a clause with an empty Match clauses section that is not the last one. Fix: renumber so the catch-all clause has the highest sequence number. Leave gaps of ten between clauses when writing a map so a later insertion does not require renumbering.How Do I Match Prefixes, AS Paths, and Communities Precisely?
What are the three match languages?
Prefix-lists match an address and a length, with optional ge and le qualifiers that constrain the length range; without them the length must match exactly. AS-path access-lists use regular expressions over the AS_PATH string, where the underscore matches any delimiter — space, comma, brace, parenthesis, or the start and end of the string — which makes _65001_ match the AS number 65001 anywhere without also matching 650011. Community-lists come in two forms: standard, which takes literal community values and treats multiple values on one line as an AND, and expanded, which takes a regular expression over the community string.
A Deeper Dive into the Match Languages
Prefix-list ge and le, precisely
A prefix-list entry names a network and a length. With no qualifiers, only that exact prefix and length matches. ge sets a minimum length and le a maximum, and both must be greater than the stated length. When only ge is given, the range runs from ge to 32; when only le is given, it runs from the stated length to le. Getting these backwards is the most common prefix-list error, and the symptom is a list that matches nothing or everything.
| Entry | Matches | Does not match |
|---|---|---|
permit 10.0.0.0/8 |
Exactly 10.0.0.0/8 | 10.0.0.0/16, 10.1.0.0/16 |
permit 10.0.0.0/8 le 24 |
Any prefix inside 10/8 with length 8–24 | 10.1.2.0/25 |
permit 10.0.0.0/8 ge 24 |
Any prefix inside 10/8 with length 24–32 | 10.1.0.0/16 |
permit 10.0.0.0/8 ge 24 le 24 |
Only /24s inside 10/8 | 10.0.0.0/8 itself, 10.1.2.128/25 |
permit 0.0.0.0/0 |
Only the default route | Everything else |
permit 0.0.0.0/0 le 32 |
Everything | Nothing |
! Accept only customer /24s from within their allocation
ip prefix-list CUST-A seq 5 permit 203.0.113.0/24
ip prefix-list CUST-A seq 10 permit 198.51.100.0/22 ge 24 le 24
!
! Reject anything longer than /24 from the internet
ip prefix-list NO-LONGER-THAN-24 seq 5 deny 0.0.0.0/0 ge 25
ip prefix-list NO-LONGER-THAN-24 seq 10 permit 0.0.0.0/0 le 24
!
! Test a prefix against a list without applying it
R1# show ip prefix-list CUST-A
ip prefix-list CUST-A: 2 entries
seq 5 permit 203.0.113.0/24
seq 10 permit 198.51.100.0/22 ge 24 le 24
AS-path regular expressions
The AS_PATH is matched as a string of AS numbers separated by spaces. The metacharacters follow standard regular expression conventions with one addition: the underscore matches any delimiter, which includes a space, a comma, a brace or parenthesis from confederation and AS_SET notation, and the start or end of the string. That makes the underscore the correct way to delimit an AS number, because a bare 65001 would also match inside 650011.
| Expression | Matches | Typical use |
|---|---|---|
^$ |
Empty AS_PATH — prefixes originated locally | Advertise only your own prefixes |
^65001_ |
Received directly from AS 65001 | Filter by directly connected neighbour |
_65001$ |
Originated by AS 65001 | Accept only a specific origin AS |
_65001_ |
AS 65001 anywhere in the path | Reject anything transiting a given AS |
^65001(_65001)*$ |
Only AS 65001, with any amount of prepending | Accept a customer's own prefixes only |
^[0-9]+$ |
Exactly one AS in the path | Directly connected origins only |
.* |
Everything | Catch-all permit |
! Accept only the customer's own prefixes, allowing prepending
ip as-path access-list 10 permit ^65100(_65100)*$
!
! Advertise only locally originated prefixes to a peer
ip as-path access-list 20 permit ^$
!
! Never accept anything that transited a known bad AS
ip as-path access-list 30 deny _64999_
ip as-path access-list 30 permit .*
!
router bgp 65000
address-family ipv4 unicast
neighbor 192.0.2.2 filter-list 10 in
neighbor 192.0.2.6 filter-list 20 out
!
! Test an expression against the live table before applying it
R1# show ip bgp regexp ^65001_
R1# show ip bgp filter-list 10
Standard versus expanded community lists
A standard community list takes literal values. Several values on a single permit line form an AND — a route must carry all of them. Several lines form an OR. An expanded community list takes a regular expression matched against the community string, which is what you need for patterns like "any community belonging to AS 65000".
! Standard: AND on one line
ip community-list standard GOLD-EMEA permit 65000:1001 65000:2000
! ^ Route must carry BOTH communities.
!
! Standard: OR across lines
ip community-list standard ANY-REGION permit 65000:1001
ip community-list standard ANY-REGION permit 65000:1002
ip community-list standard ANY-REGION permit 65000:1003
!
! Expanded: regex over the community string
ip community-list expanded OURS permit _65000:[0-9]+_
!
! Match exactly this set and nothing else
route-map EXACT permit 10
match community GOLD-EMEA exact-match
permit line, which requires the route to carry all three simultaneously. Confirm: show ip community-list and check whether the values share a line; then show ip bgp community 65000:1001 to see what actually carries each. Fix: put each value on its own permit line to get OR semantics, and reserve the multi-value line for the rare case where AND is genuinely intended.show ip bgp regexp <expr>, show ip bgp filter-list <n>, and show ip bgp community <value> each evaluate a match against the table already in memory, without touching any policy. Running the expression before attaching it turns a guess into a count, and a regular expression that returns zero prefixes on a full table is almost always wrong rather than genuinely selective.exact-match changes the questionWithout exact-match, a community match succeeds if the route carries the listed communities among others. With it, the route's community set must be exactly the listed set — no extras. That is occasionally what you want and is almost never what someone means when they add the keyword to make a match stricter, because in practice routes accumulate communities from several sources.ge/le bounds a length range rather than a prefix, the underscore is a delimiter class rather than a character, and a multi-value community line is an AND — and those three account for most matching that silently does the wrong thing.What Can Policy Change, and What Does Each Change Affect?
Which set actions matter and where do they act?
Six do real work. set weight decides at best-path step one and never leaves the router. set local-preference decides at step two and propagates through the whole autonomous system. set as-path prepend lengthens the path, which is evaluated at step four by other autonomous systems. set origin acts at step five. set metric sets MED, evaluated at step six and only against paths from the same neighbouring AS. set community changes nothing directly but signals policy that some other router may act on. Two more manipulate rather than decide: set ip next-hop overrides forwarding, and set comm-list delete removes specific communities.
A Deeper Dive into Set Actions
Matching the instrument to the intended scope
| Set action | Best-path step | Scope of effect | Direction it influences | Applied where |
|---|---|---|---|---|
set weight |
1 | This router only | Outbound from this router | Inbound |
set local-preference |
2 | Entire AS | Outbound from the AS | Inbound |
set as-path prepend |
4 — in other ASes | Global | Inbound to the AS | Outbound |
set origin |
5 | Global | Rarely used deliberately | Either |
set metric (MED) |
6 | One neighbouring AS | Inbound to the AS | Outbound |
set community |
— | Wherever it propagates | Signals only | Either |
set ip next-hop |
— | This router's forwarding | Forwarding, not selection | Inbound |
set comm-list delete |
— | Removes matched communities | Cleans up signalling | Either |
Inbound versus outbound placement
Attributes that influence your own decision belong inbound, because you are changing how you evaluate what a peer sent. Attributes that influence somebody else's decision belong outbound, because you are changing what you advertise. Applying set local-preference outbound to an eBGP peer is a no-op, since local preference is stripped before leaving the AS; applying set as-path prepend inbound affects only your own step-four comparison and nobody else's.
! INBOUND: change how I evaluate what this peer sends
route-map TRANSIT-A-IN permit 10
match ip address prefix-list CUSTOMER-ROUTES
set local-preference 200
set community 65000:1000 additive
route-map TRANSIT-A-IN permit 20
set local-preference 150
!
! OUTBOUND: change what other autonomous systems see
route-map TRANSIT-B-OUT permit 10
match as-path 20
set as-path prepend 65000 65000 65000
set metric 200
route-map TRANSIT-B-OUT deny 20
! ^ Advertise only locally originated prefixes, prepended.
Cleaning up communities on the way out
set comm-list delete removes every community that matches a named list, which is how internal signalling is stripped before advertisement to an external peer. Leaving internal communities on prefixes you advertise to the world exposes your topology and, worse, may be interpreted by somebody else's policy in a way you did not intend.
! Strip our internal 65000:1xxx tags before advertising externally
ip community-list expanded INTERNAL-TAGS permit _65000:1[0-9][0-9][0-9]_
!
route-map TRANSIT-OUT permit 10
match as-path 20
set comm-list INTERNAL-TAGS delete
set community 65001:100 additive
!
! Verify what the peer actually receives
R1# show ip bgp neighbors 192.0.2.6 advertised-routes
R-PEER# show ip bgp 203.0.113.0/24 | include Community
Changing forwarding without changing selection
set ip next-hop in an inbound route-map rewrites where this router forwards traffic for the matched prefixes, without altering which path won selection or what gets advertised onward. That separation is occasionally exactly what a design needs — steering a subset of traffic through a scrubbing appliance or an inspection device while leaving the control plane untouched — and it is a frequent source of confusion when inherited, because the routing table and the forwarding behaviour disagree with each other in a way that no BGP command explains.
Two variants matter. set ip next-hop peer-address substitutes the address of the peer the route came from or is being sent to, depending on direction. set ip next-hop self in an outbound map is the route-map equivalent of the next-hop-self neighbour command but applied selectively rather than to every prefix.
! Steer matched prefixes through an inspection device
route-map SCRUB-IN permit 10
match ip address prefix-list PROTECTED-BLOCKS
set ip next-hop 10.99.0.10
route-map SCRUB-IN permit 20
!
! Selective next-hop-self, rather than for every prefix
route-map SELECTIVE-NHS permit 10
match ip address prefix-list EXTERNAL-ORIGIN
set ip next-hop self
route-map SELECTIVE-NHS permit 20
!
! The routing table shows the rewritten next hop, not the BGP one
R1# show ip bgp 203.0.113.0/24 | include next hop|10.99
R1# show ip route 203.0.113.0
Known via "bgp 65000", distance 20, metric 0
* 10.99.0.10, from 192.0.2.2, 00:04:11 ago
! ^ 'from' is the BGP peer; the next hop is the rewritten address.
Conditional advertisement and maximum-prefix
Two protective mechanisms sit alongside ordinary policy. Conditional advertisement advertises a prefix only while some other prefix is absent, which implements backup transit without relying on the other side to prefer correctly. Maximum-prefix caps how many prefixes a peer may send, with a warning threshold and an optional automatic restart — it is the single most effective protection against a peer accidentally sending a full table on a session sized for a handful of routes.
! Cap what a customer session will accept
router bgp 65000
address-family ipv4 unicast
neighbor 192.0.2.2 maximum-prefix 100 80 restart 15
! 100 prefix limit, warn at 80%, tear down and retry after 15 min
!
! Advertise the backup block only while the primary is absent
route-map ADVERTISE-BACKUP permit 10
match ip address prefix-list BACKUP-BLOCK
route-map WATCH-PRIMARY permit 10
match ip address prefix-list PRIMARY-BLOCK
!
router bgp 65000
address-family ipv4 unicast
neighbor 192.0.2.6 advertise-map ADVERTISE-BACKUP non-exist-map WATCH-PRIMARY
!
R1# show ip bgp neighbors 192.0.2.6 | include Condition
Condition-map WATCH-PRIMARY, Advertise-map ADVERTISE-BACKUP,
status: Withdraw
! ^ 'Withdraw' means the primary IS present, so the backup is not
! being advertised. 'Advertise' means the primary has gone.
Which Policy Mistakes Cause Outages or Leaks?
What goes wrong most often?
Five things. An outbound policy that permits more than intended re-advertises one provider's routes to another, which is the anatomy of every large route leak. A route-map's implicit deny rejects everything because a trailing permit clause is missing. A policy change is applied and never refreshed, so the peer's existing prefixes are still evaluated by the old rules. A prefix-list ge/le qualifier is inverted and matches nothing. And a filter applied to the wrong address family silently does nothing at all.
A Deeper Dive into the Failure Catalogue
The route leak, and the filter that prevents it
A network with two transit providers that advertises everything it knows to both becomes transit between them. Traffic that has no business traversing your network arrives, your circuits saturate, and the two providers see each other through you. The prevention is a single outbound filter expressing the only correct policy for a non-transit network: advertise your own prefixes and your customers' prefixes, and nothing else.
! The only outbound policy a non-transit network needs
ip as-path access-list 20 permit ^$
ip as-path access-list 20 permit ^65100(_65100)*$
ip as-path access-list 20 permit ^65200(_65200)*$
! ^ Own prefixes (empty path) plus each customer AS. Nothing else.
!
route-map TRANSIT-OUT permit 10
match as-path 20
route-map TRANSIT-OUT deny 20
! ^ Explicit deny for clarity; the implicit one would suffice.
!
router bgp 65000
address-family ipv4 unicast
neighbor 192.0.2.2 route-map TRANSIT-OUT out
neighbor 192.0.2.6 route-map TRANSIT-OUT out
! Belt and braces: cap what we will ever advertise
neighbor 192.0.2.2 maximum-prefix 500 80 warning-only
show ip bgp neighbors <peer> advertised-routes | count returns a number close to your full table size rather than close to your prefix count. Fix: an outbound AS-path filter permitting only ^$ and your customer ASes. Apply it to every eBGP peer, including ones you consider low-risk.Policy applied but never refreshed
An inbound route-map change affects prefixes the peer sends from that moment. Prefixes already in the table were evaluated under the previous policy and stay as they were until the peer resends them. The symptom is a policy that appears to work for new prefixes and not for existing ones, which looks like a partial failure and is a missing soft reset.
! After every inbound policy change
R1# clear ip bgp 192.0.2.2 soft in
!
! After every outbound policy change
R1# clear ip bgp 192.0.2.6 soft out
!
! Or refresh everything, which is safe with route refresh negotiated
R1# clear ip bgp * soft
!
! Confirm the change took effect by counting before and after
R1# show ip bgp neighbors 192.0.2.2 routes | count network
The address-family trap
! WRONG - applied outside any address family, or in the wrong one
router bgp 65000
neighbor 2001:db8::2 remote-as 65001
address-family ipv4 unicast
neighbor 2001:db8::2 route-map V6-POLICY in
exit-address-family
! ^ The v6 neighbour is not even activated in ipv4 unicast.
!
! RIGHT
router bgp 65000
neighbor 2001:db8::2 remote-as 65001
address-family ipv6 unicast
neighbor 2001:db8::2 activate
neighbor 2001:db8::2 route-map V6-POLICY in
exit-address-family
!
! Audit which families a neighbour is active in
R1# show ip bgp neighbors 2001:db8::2 | include address family|Policy
For address family: IPv6 Unicast
Route map for incoming advertisements is V6-POLICY
Validating a policy change before it reaches production
Most policy mistakes are visible before the change is committed, provided you look at counts rather than at configuration. Record how many prefixes a neighbour currently sends and how many you currently advertise to it, apply the change, refresh, and compare. A number that moved by an unexpected amount in either direction is the entire signal — you do not need to know which clause caused it to know that something did. On a session carrying a full table this takes seconds and catches the class of error where a route-map was correct in isolation and wrong in combination with a prefix-list applied in the same direction.
! Baseline before the change
R1# show ip bgp neighbors 192.0.2.2 routes | include Total
Total number of prefixes 847
R1# show ip bgp neighbors 192.0.2.6 advertised-routes | include Total
Total number of prefixes 12
!
! ... apply the change, then refresh both directions ...
R1# clear ip bgp 192.0.2.2 soft in
R1# clear ip bgp 192.0.2.6 soft out
!
! Compare. Twelve advertised prefixes becoming 900000 is a leak
! in progress and is visible within one refresh cycle.
R1# show ip bgp neighbors 192.0.2.6 advertised-routes | include Total
Total number of prefixes 12
Defensive defaults worth adopting
- Every eBGP session has an inbound and an outbound policy, even when the policy is permissive — an explicit permit documents intent that a missing filter does not.
- Every eBGP session has a
maximum-prefixsized to the expected table plus headroom. - Bogon and reserved-space filters inbound, and a maximum prefix length, typically rejecting anything longer than a /24 in IPv4.
- Never accept a default route from a peer unless the design explicitly requires one.
- Outbound policy is AS-path based, so it stays correct when a customer adds a prefix without telling you.
! A defensible inbound baseline for an internet peer
ip prefix-list SANE-IN seq 5 deny 0.0.0.0/0
ip prefix-list SANE-IN seq 10 deny 0.0.0.0/8 le 32
ip prefix-list SANE-IN seq 15 deny 10.0.0.0/8 le 32
ip prefix-list SANE-IN seq 20 deny 127.0.0.0/8 le 32
ip prefix-list SANE-IN seq 25 deny 169.254.0.0/16 le 32
ip prefix-list SANE-IN seq 30 deny 172.16.0.0/12 le 32
ip prefix-list SANE-IN seq 35 deny 192.168.0.0/16 le 32
ip prefix-list SANE-IN seq 40 deny 224.0.0.0/4 le 32
ip prefix-list SANE-IN seq 45 deny 0.0.0.0/0 ge 25
ip prefix-list SANE-IN seq 50 permit 0.0.0.0/0 le 24
!
route-map PEER-IN permit 10
match ip address prefix-list SANE-IN
set local-preference 150
set community 65000:2000 additive
| Mistake | Symptom | Confirming command | Fix |
|---|---|---|---|
| No outbound filter | Becoming transit between two providers | show ip bgp neighbors X advertised-routes | count |
AS-path filter permitting ^$ plus customers |
| Missing trailing permit | Route-map rejects everything | show route-map NAME |
Add a permit clause with the highest sequence |
| Empty clause too early | All prefixes get one clause's attributes | show route-map NAME — empty Match section |
Renumber so the catch-all is last |
| Policy not refreshed | Works for new prefixes only | Compare received-routes and routes |
clear ip bgp X soft in|out |
Inverted ge/le |
Prefix-list matches nothing | show ip prefix-list NAME |
Both qualifiers must exceed the stated length |
| Wrong address family | Policy has no effect at all | show ip bgp neighbors X per family |
Repeat the attachment in each family |
| Community list AND semantics | Match succeeds far less often than expected | show ip community-list |
One value per permit line |
ge/le semantics, and the requirement to soft-reset after applying inbound policy. ENARSI 300-410 covers the same tooling with more emphasis on prefix-lists and route-maps and less on AS-path regular expressions.Conclusion
BGP policy is a small language with a few sharp edges, and every one of those edges produces a failure that looks like something else. The implicit deny turns a filter for three prefixes into a filter for everything. A clause with no match statements matches everything, so its position in the sequence decides whether it is a sensible default or a policy that swallows the entire map. The ge and le qualifiers bound a length range rather than a prefix. A multi-value community line is an AND. Learning those four rules eliminates most of the surprises, and none of them is difficult once stated explicitly.
The structural principle underneath the syntax is scope. Every policy decision has a blast radius, and the instruments differ mainly in how far their effects travel. Weight stops at the router. Local preference stops at the autonomous system boundary. Prepending and communities travel to other people's networks, where they take effect only if that network's policy chooses to act on them. Matching the instrument to the intended reach is what separates a policy that works from one that works on the router you tested it on.
Above all, treat the outbound filter as the important one. Inbound policy protects your own routing table, and a mistake there is visible to you immediately. Outbound policy determines what the rest of the internet believes about your network, and a mistake there is visible to everyone except you until the circuits fill. An AS-path filter permitting an empty path and your customer autonomous systems, applied to every external neighbour, is four lines and prevents the entire category. Build a three-AS lab, write the outbound filter first, and then verify with advertised-routes rather than assuming — the count is the whole answer.
External Links Recommendations
- RFC 4271 — A Border Gateway Protocol 4 (BGP-4): the decision process and the attributes that policy manipulates.
- RFC 8212 — Default External BGP Route Propagation Behavior Without Policies: why a policy-free eBGP session should exchange nothing.
- RFC 7454 — BGP Operations and Security: prefix filtering, maximum-prefix, and the operational practices that prevent leaks.
- Cisco — BGP Case Studies: worked route-map, prefix-list, and AS-path filtering examples.
- Cisco — Using Regular Expressions in BGP: the metacharacters, including the underscore delimiter class.
- Cisco IOS-XE BGP Configuration Guide: current syntax for route-maps, filter-lists, conditional advertisement, and maximum-prefix.
- Cisco Learning Network — CCIE Enterprise Infrastructure: current blueprint and lab equipment list.
Reference Notes
- RFC 4271, Section 9.1 — the decision process, whose steps determine where each policy-set attribute takes effect.
- RFC 4271, Section 5.1.5 — LOCAL_PREF is included only in UPDATE messages sent to internal peers, which is why setting it outbound to an eBGP peer has no effect.
- RFC 4271, Section 5.1.4 — MULTI_EXIT_DISC is compared only between routes received from the same neighbouring autonomous system.
- RFC 8212 — a BGP speaker must not advertise or accept routes on an eBGP session until import and export policies are explicitly configured.
- RFC 7454, Section 6 — prefix filtering recommendations including bogon filtering and maximum prefix length limits.
- RFC 7454, Section 9 — maximum-prefix limits as protection against a peer announcing an unexpected number of routes.
- RFC 2918 — the Route Refresh capability, which allows inbound policy to be re-applied without resetting the session.
- Cisco, "Using Regular Expressions in BGP" — the underscore matches any delimiter including spaces, commas, braces, parentheses, and the start or end of the AS_PATH string.
- Cisco IOS-XE BGP Configuration Guide — route-map clauses are evaluated in ascending sequence order, the first matching clause terminates processing, and an implicit deny follows the final clause.
- Cisco IOS-XE BGP Configuration Guide — a route-map clause with no
matchstatements matches all routes. - Cisco IOS-XE IP Routing Configuration Guide — prefix-list
geandlevalues must be greater than the specified prefix length, andgemust not exceedle. - Cisco IOS-XE BGP Configuration Guide — a standard community list line containing multiple values requires a route to carry all of them; separate lines provide alternative matches.