BGP Communities Design and Lab: Classify Once at Ingress, Act at Egress, Strip on the Way Out
Communities are the only BGP attribute whose value is entirely conventional. A prefix carrying 65000:1001 means whatever your network has decided it means, and it means nothing at all to a router that has no route-map matching it. That property is what makes communities powerful — you can encode any classification you like — and it is also why community deployments fail. A scheme invented one prefix at a time, documented in one engineer's memory, applied inconsistently across border routers, is worse than no scheme, because policy now depends on tags whose meaning nobody can reconstruct.
The deployments that work all share a shape. Classification happens once, at ingress, where a prefix enters the autonomous system and the router knows what kind of neighbour sent it — customer, peer, or transit. That classification is written as a community and never recomputed. Every subsequent decision anywhere in the network matches on the community rather than on the prefix or the AS path, so adding a customer means tagging their session and nothing else. And at egress, internal communities are stripped before advertisement so your topology and your policy scheme do not leak into other people's networks.
This article builds that scheme as a lab. Section one lays out the topology and the numbering plan, and explains why the number space is designed rather than allocated ad hoc. Section two builds the ingress tagging on customer, peer, and transit sessions. Section three builds the egress policy that acts on those tags, including the outbound filter that prevents transit leaks. Section four covers the MPLS L3VPN portion of the lab, where extended communities do structurally similar work with a very different encoding. Section five is verification and troubleshooting for the whole scheme.

What Does the Lab Topology and Community Scheme Look Like?
How should the number space be designed?
Divide the sixteen-bit local half into ranges by purpose rather than allocating values as requirements arrive. One range for neighbour class, so every prefix in the network carries exactly one of customer, peer, or transit. One for geography, so reporting and regional preference can match on where a prefix entered. One for requested actions, which is the range a customer is permitted to set and your egress policy acts on. One for operational markings such as blackhole requests. Ranges make an expanded community list able to match a whole category with a single regular expression, and they make an unfamiliar value self-describing.
A Deeper Dive into the Plan and Topology
The lab
AS 65000 has two border routers and one PE. BR-1 faces two customers, AS 65100 originating 203.0.113.0/24 and AS 65200 originating 198.51.100.0/22. BR-2 faces a settlement-free peer, AS 65300, and a transit provider, AS 65400, that sends a full table. PE-1 carries an MPLS L3VPN for a customer with two sites. That covers the three external neighbour classes plus the extended-community case in one topology.
The numbering plan in full
| Community | Meaning | Set where | Acted on where |
|---|---|---|---|
65000:1000 |
Learned from a customer | Ingress on customer sessions | Egress to peers and transit |
65000:1100 |
Learned from a settlement-free peer | Ingress on peer sessions | Egress — never advertised onward |
65000:1200 |
Learned from transit | Ingress on transit sessions | Egress — never advertised onward |
65000:1900 |
Originated by AS 65000 itself | On the network statement |
Egress to everyone |
65000:2001 / 2002 |
Entered in EMEA / AMER | Ingress, per border router | Regional local-preference and reporting |
65000:3001 |
Customer request: prepend once to transit | Customer sets it; we honour it | Egress route-map to transit |
65000:3003 |
Customer request: prepend three times | Customer sets it | Egress route-map to transit |
65000:3100 |
Customer request: do not advertise to peers | Customer sets it | Egress route-map to peers |
65000:9999 |
Blackhole this prefix | Customer or operations | Sets next hop to a discard interface |
Base configuration on the border routers
! ===== BR-1: sessions and the community lists the policy will use
router bgp 65000
bgp router-id 10.0.0.1
bgp log-neighbor-changes
! Customers
neighbor 192.0.2.2 remote-as 65100
neighbor 192.0.2.6 remote-as 65200
!
address-family ipv4 unicast
network 10.100.0.0 mask 255.255.0.0 route-map TAG-OWN
neighbor 192.0.2.2 activate
neighbor 192.0.2.6 activate
! Communities are not sent unless this is present
neighbor 192.0.2.2 send-community both
neighbor 192.0.2.6 send-community both
exit-address-family
! ===== The community lists, defined once and reused everywhere
ip community-list standard CUSTOMER permit 65000:1000
ip community-list standard PEER permit 65000:1100
ip community-list standard TRANSIT permit 65000:1200
ip community-list standard OWN permit 65000:1900
!
! One list matching every prefix we may advertise externally
ip community-list standard ADVERTISABLE permit 65000:1000
ip community-list standard ADVERTISABLE permit 65000:1900
! ^ Separate lines = OR. A prefix needs only one of them.
!
! Customer-settable action communities
ip community-list standard PREPEND-1 permit 65000:3001
ip community-list standard PREPEND-3 permit 65000:3003
ip community-list standard NO-PEERS permit 65000:3100
ip community-list standard BLACKHOLE permit 65000:9999
!
! Everything in our own namespace, for stripping on egress
ip community-list expanded ALL-OURS permit _65000:[0-9]+_
What a community scheme replaces
Without communities, the same decisions are made by re-deriving the classification everywhere it is needed. An egress filter matches an AS-path list enumerating every customer AS, which means onboarding a customer is an edit on every border router. A regional preference matches a prefix-list enumerating every regional allocation, which drifts the moment an allocation changes. A blackhole request becomes a manual static route somebody has to remember to remove. Each of these works in isolation and none of them survives growth, because the number of places holding a copy of the classification grows with the number of borders and the classification itself changes weekly.
Replacing all of it with a tag applied once has a cost worth naming: the scheme becomes load-bearing. A prefix that arrives with no class tag is invisible to every egress filter and will simply never be advertised, with no error anywhere. That failure mode is the price of the design, and it is why section five treats "does every prefix carry exactly one class tag" as a first-class verification step rather than an afterthought.
aggregate-address statement that has no attribute-map, or a redistribution added without a route-map. Confirm: show ip bgp <prefix> shows no Community line; show ip bgp neighbors <peer> advertised-routes | include <prefix> returns nothing. Fix: apply the origination tag, and add a periodic check that the per-class prefix counts sum to the table size so an untagged prefix is caught by monitoring rather than by a customer._65000:1[0-9][0-9][0-9]_ and catch every neighbour-class tag including ones added next year. A scheme with values scattered across the number space needs every value enumerated in every list that should include it, and the list that somebody forgets to update is the one that causes the incident.How Do I Tag Routes at Ingress?
What does an ingress route-map need to do?
Four things, in one map per neighbour class. Reject anything that should never be accepted — bogons, overly specific prefixes, and for a customer session anything outside their registered allocation. Set the neighbour-class community, additively so nothing already present is lost. Set the geography community for the region this border router serves. Set local preference according to the class, so customer routes beat peer routes and peer routes beat transit. Anything else the design needs is a matter of adding clauses, but those four are the irreducible core.
A Deeper Dive into Ingress Policy
Customer ingress: the strictest of the three
A customer session accepts only prefixes from that customer's registered allocation, so the prefix-list is per customer and is the one piece of configuration that changes when the customer adds address space. Everything else in the map is identical across all customers, which is why it belongs in a template.
! ===== Per-customer prefix list - the only per-customer element
ip prefix-list CUST-A-PFX seq 5 permit 203.0.113.0/24
ip prefix-list CUST-B-PFX seq 5 permit 198.51.100.0/22 le 24
!
! ===== Customer ingress, CUST-A =====
route-map CUST-A-IN deny 10
match ip address prefix-list BOGONS
!
route-map CUST-A-IN permit 20
match ip address prefix-list CUST-A-PFX
! Class, then geography, then preference
set community 65000:1000 65000:2001 additive
set local-preference 300
!
! implicit deny 65535 - anything outside their allocation is rejected
!
router bgp 65000
address-family ipv4 unicast
neighbor 192.0.2.2 route-map CUST-A-IN in
neighbor 192.0.2.2 maximum-prefix 100 80 warning-only
Peer and transit ingress
A settlement-free peer should send only its own and its customers' prefixes; a transit provider sends a full table. Neither should ever send a default route unless the design asks for one, and neither should send anything longer than a /24 in IPv4. The class community and the local preference differ; the structure does not.
! ===== Peer ingress =====
route-map PEER-IN deny 10
match ip address prefix-list BOGONS
!
route-map PEER-IN permit 20
set community 65000:1100 65000:2002 additive
set local-preference 200
!
! ===== Transit ingress =====
route-map TRANSIT-IN deny 10
match ip address prefix-list BOGONS
!
route-map TRANSIT-IN permit 20
set community 65000:1200 65000:2002 additive
set local-preference 100
!
router bgp 65000
address-family ipv4 unicast
neighbor 198.51.0.2 route-map PEER-IN in
neighbor 198.51.0.6 route-map TRANSIT-IN in
! Transit sends a full table - size the limit accordingly
neighbor 198.51.0.6 maximum-prefix 1200000 90 restart 30
Ordering within the ingress map
The clauses in an ingress map have to run in a specific order and the order encodes security assumptions. Rejection comes first, because a bogon or an out-of-allocation prefix should never be evaluated further. Stripping the customer's attempt to set your internal tags comes second, before anything of yours is applied, so that a customer cannot pre-set a value your later clause would then match on. Your own tagging comes third. Local preference last, because it may depend on the tags just applied. Reversing any two of those produces a map that works in testing and has a hole in it.
The specific hole worth understanding: if you match on a class community in the same map that sets it, and the customer can set that community, then a customer prefix carrying 65000:1000 arrives already looking like a customer route to any clause that runs before your strip. On a customer session that is harmless because the class is what you were going to apply anyway. On a peer or transit session, where the class should be 1100 or 1200, a peer that sets 65000:1000 would have their prefixes treated as customer routes and re-advertised to your other providers. That is a leak somebody else can trigger.
! ===== Correct ordering, peer session =====
route-map PEER-IN deny 10
! 1. Reject before anything else is evaluated
match ip address prefix-list BOGONS
!
route-map PEER-IN permit 20
! 2. Strip everything in our namespace - a peer sets NONE of ours
set comm-list ALL-OURS delete
! 3. Apply our classification
set community 65000:1100 65000:2002 additive
! 4. Preference, which may depend on the tags above
set local-preference 200
! ^ On a peer or transit session, strip ALL of our namespace.
! Only customers are permitted to set 65000:3xxx.
Tagging your own prefixes
Prefixes originated by AS 65000 need a class community too, because the egress filter matches on class and a prefix with no class tag would be rejected. Apply it on the network statement or on the redistribution, so origination and classification happen in the same place.
route-map TAG-OWN permit 10
set community 65000:1900 additive
!
router bgp 65000
address-family ipv4 unicast
network 10.100.0.0 mask 255.255.0.0 route-map TAG-OWN
! Aggregates need it as well - they are separately originated
aggregate-address 10.100.0.0 255.255.0.0 summary-only attribute-map TAG-OWN
exit-address-family
Accepting customer action requests safely
Letting a customer set communities that drive your policy is genuinely useful and needs a guard: a customer must not be able to set your internal class or geography tags. Strip everything in your namespace that is not in the permitted action range before the class tag is applied, then add your own. That ordering matters — strip first, tag second.
! Everything of ours a customer must NOT be able to set
ip community-list expanded CUST-CANNOT-SET permit _65000:[12][0-9][0-9][0-9]_
! ^ Matches 65000:1xxx and 65000:2xxx. Leaves 65000:3xxx alone.
!
route-map CUST-A-IN permit 20
match ip address prefix-list CUST-A-PFX
! 1. Remove any class or geography tags the customer set
set comm-list CUST-CANNOT-SET delete
! 2. Now apply ours. Their 65000:3xxx requests survive.
set community 65000:1000 65000:2001 additive
set local-preference 300
set community without additive in an ingress map Symptom: customer-requested action communities stop working after an unrelated change to the ingress route-map, and prefixes arrive carrying only your class tag. Cause: a set community statement without additive replaces the entire community list, discarding whatever the customer sent — including the 65000:3xxx requests your egress policy was supposed to honour. Confirm: show ip bgp <prefix> and compare against show ip bgp neighbors <customer> received-routes, which shows the pre-policy community set. Fix: add additive. Where you genuinely need to clear first, use set comm-list ... delete for the specific ranges rather than wiping everything.How Do I Act on Communities at Egress?
What does egress policy have to get right?
Two things, and the second is the one that protects other people. First, advertise only what class permits: to a peer or a transit provider, that means prefixes tagged customer or own-origin and nothing else, which is the outbound filter that prevents your network becoming transit between two providers. Second, honour the action communities a customer set — prepending, peer suppression, blackholing — because that is the interface you published. Then strip every community in your own namespace before the update leaves the autonomous system.
A Deeper Dive into Egress Policy
The outbound filter, expressed as a class match
Because ingress already classified everything, the outbound filter is one match against one community list. It stays correct when a customer adds a prefix, when a new customer is onboarded, and when a new transit provider is added — none of which require touching this map.
! ===== Egress to transit and peers: class-based, not prefix-based
route-map TRANSIT-OUT permit 10
match community ADVERTISABLE
! Honour a triple prepend if the customer asked for one
match community PREPEND-3
set as-path prepend 65000 65000 65000
set comm-list ALL-OURS delete
!
route-map TRANSIT-OUT permit 20
match community ADVERTISABLE
match community PREPEND-1
set as-path prepend 65000
set comm-list ALL-OURS delete
!
route-map TRANSIT-OUT permit 30
match community ADVERTISABLE
set comm-list ALL-OURS delete
!
! implicit deny 65535 - anything not customer or own-origin is dropped
! ===== Egress to a settlement-free peer: same, plus 65000:3100
route-map PEER-OUT deny 10
! Customer asked us not to advertise this to peers
match community NO-PEERS
!
route-map PEER-OUT permit 20
match community ADVERTISABLE
set comm-list ALL-OURS delete
!
router bgp 65000
address-family ipv4 unicast
neighbor 198.51.0.2 route-map PEER-OUT out
neighbor 198.51.0.6 route-map TRANSIT-OUT out
neighbor 198.51.0.2 send-community both
neighbor 198.51.0.6 send-community both
Using the geography tag for regional exit preference
The second range in the plan carries where a prefix entered the network, and its most useful application is keeping traffic regional. A router in EMEA should prefer an exit that a prefix entered through in EMEA, all else being equal, rather than hauling traffic across the core to an AMER border. Because the tag was applied at ingress, expressing that preference is one clause in a route-map applied inbound on the iBGP sessions of each region — no prefix lists, no per-allocation maintenance.
The clause has to be careful about ordering with the class-based preference. Class must dominate: a customer route entering in AMER should still beat a transit route entering in EMEA, because reaching a customer directly matters more than staying regional. Setting the class preference in hundreds and the geography adjustment in tens keeps the two independent.
! Class sets the hundreds; geography adjusts within the class
! customer 300 base peer 200 base transit 100 base
! local region +10, remote region +0
!
! Applied inbound on iBGP sessions at an EMEA router
route-map REGIONAL-EMEA permit 10
match community CUSTOMER
match community GEO-EMEA
set local-preference 310
!
route-map REGIONAL-EMEA permit 20
match community CUSTOMER
set local-preference 300
!
route-map REGIONAL-EMEA permit 30
match community TRANSIT
match community GEO-EMEA
set local-preference 110
!
route-map REGIONAL-EMEA permit 40
set local-preference 100
!
ip community-list standard GEO-EMEA permit 65000:2001
ip community-list standard GEO-AMER permit 65000:2002
Why matching on class beats matching on AS path
| Approach | Onboarding a customer | Customer adds a prefix | Risk of drift |
|---|---|---|---|
| Community class match | Tag the new session; egress unchanged | Nothing to change | Low — one map, one list |
| AS-path filter list | Add the AS to the filter list on every border | Nothing to change | Medium — per-border edits |
| Prefix-list on egress | Add prefixes on every border | Edit every border | High — drifts constantly |
| No egress filter | Nothing | Nothing | Leak |
Blackhole communities
A blackhole community lets a customer or your own operations team ask that traffic to a prefix be discarded at the edge rather than carried across the network to a saturated link. The egress policy sets the next hop to an address routed to Null0, which the forwarding plane discards at line rate. RFC 7999 defines a well-known value, 65535:666, for interoperable use between networks.
! A discard route the blackhole next hop resolves to
ip route 192.0.2.254 255.255.255.255 Null0
!
! Honour a blackhole request from a customer, within their allocation
route-map CUST-A-IN permit 15
match ip address prefix-list CUST-A-PFX
match community BLACKHOLE
set ip next-hop 192.0.2.254
set community no-export additive
! ^ no-export keeps the blackhole inside our AS.
!
! The well-known interoperable value from RFC 7999
ip community-list standard RFC7999-BH permit 65535:666
!
! Verify a blackholed prefix resolves to the discard route
BR-1# show ip route 203.0.113.66
Known via "bgp 65000", distance 20, metric 0
* 192.0.2.254, from 192.0.2.2, 00:00:41 ago
BR-1# show ip cef 203.0.113.66 | include Null0
65000:1000, 65000:2001 and similar tags, exposing your internal classification and geography to anyone who queries. Occasionally another network's policy matches one of them with unintended results. Cause: the egress route-map has no set comm-list ... delete clause, so internal tags propagate. Confirm: show ip bgp neighbors <peer> advertised-routes detail, or look up one of your prefixes on any public looking glass. Fix: add set comm-list ALL-OURS delete to every clause of every external egress map. It must be in every clause, because a prefix takes only one.65000:3001 to request a prepend, that has to be documented somewhere they can read. An undocumented action community is a feature nobody uses; a documented one that a route-map no longer honours is worse, because customers will believe it is working.How Do Extended Communities Work in the MPLS L3VPN Part of the Lab?
What is structurally different about extended communities?
Three things. They are eight bytes rather than four, with a type and subtype field that gives them structure — a Route Target and a Site of Origin are both extended communities distinguished by their subtype, so a router knows what kind of value it is looking at without any local convention. They are carried in a separate attribute, type 16, which means send-community extended is a separate requirement from send-community. And in an L3VPN they are not policy hints but the actual mechanism of VPN membership: a VRF imports a prefix because its Route Target matches, not because a route-map said so.
A Deeper Dive into Extended Communities in the Lab
Route Target as membership, not policy
A VRF exports a Route Target on every prefix it originates and imports any prefix carrying a Route Target in its import list. That is the whole membership model. A hub-and-spoke VPN is built by giving the hub and spokes asymmetric import and export lists rather than by writing filters; an extranet between two customers is built by importing each other's Route Targets. No route-map is involved in the common cases.
! ===== PE-1: a simple any-to-any VPN =====
vrf definition CUST-A-VPN
rd 65000:100
address-family ipv4
route-target export 65000:100
route-target import 65000:100
exit-address-family
!
! ===== Hub-and-spoke: asymmetric RTs, no route-map needed
vrf definition HUB
rd 65000:200
address-family ipv4
route-target export 65000:200 ! hub routes
route-target import 65000:201 ! spoke routes
exit-address-family
!
vrf definition SPOKE
rd 65000:201
address-family ipv4
route-target export 65000:201 ! spoke routes
route-target import 65000:200 ! hub routes only
exit-address-family
! ^ Spokes cannot see each other because neither imports 65000:201.
Why the Route Distinguisher is not an extended community
An RD and an RT look alike and are unrelated. The Route Distinguisher is eight bytes prepended to the IPv4 prefix to make it unique across VRFs — it is part of the NLRI, not an attribute, and it exists so two customers using 10.0.0.0/8 produce two distinct VPNv4 prefixes. The Route Target is an extended community attached to that prefix and controls which VRFs import it. An RD affects uniqueness and nothing else; changing it does not change who receives a route. Confusing the two produces designs where somebody tries to control VPN membership by manipulating RDs, which does not work.
One place the distinction matters operationally: using a unique RD per PE for the same VPN gives every PE's copy of a prefix a different VPNv4 NLRI, so a route reflector treats them as distinct prefixes and reflects all of them rather than picking one best path. That is a common technique for restoring path diversity in an L3VPN without deploying Add-Path.
! Same VPN, unique RD per PE - restores multipath through an RR
! On PE-1:
vrf definition CUST-A-VPN
rd 65000:100
address-family ipv4
route-target export 65000:100
route-target import 65000:100
!
! On PE-2: different RD, SAME route targets
vrf definition CUST-A-VPN
rd 65000:101
address-family ipv4
route-target export 65000:100
route-target import 65000:100
!
! The RR now sees two distinct VPNv4 prefixes and reflects both
RR-1# show bgp vpnv4 unicast all | include 172.16.5.0
Route Distinguisher: 65000:100
*>i 172.16.5.0/24 10.0.0.1
Route Distinguisher: 65000:101
*>i 172.16.5.0/24 10.0.0.2
The send-community extended requirement
A VPNv4 session that does not send extended communities carries no Route Targets, so nothing imports anywhere and the VPN appears completely broken while every session is established and every prefix is present in the VPNv4 table. This is the single most common L3VPN misconfiguration and it is one missing keyword.
! ===== VPNv4 session - the keyword that must not be omitted
router bgp 65000
neighbor 10.0.0.9 remote-as 65000
neighbor 10.0.0.9 update-source Loopback0
!
address-family vpnv4
neighbor 10.0.0.9 activate
neighbor 10.0.0.9 send-community extended
! 'both' is safer as a habit - covers standard communities too
exit-address-family
!
! Symptom of the omission: prefixes present, nothing imported
PE-2# show bgp vpnv4 unicast all | count 65000:100
Number of lines which match regexp = 14
PE-2# show ip route vrf CUST-A-VPN | count ^B
Number of lines which match regexp = 0
! ^ VPNv4 table has them; the VRF imported none. Check the keyword.
Site of Origin, and the loop it prevents
A customer site with two PE attachments can receive its own prefix back from the second PE, install it, and create a loop. Site of Origin tags every prefix with the site it came from, and a PE will not advertise a prefix back toward a site whose SoO it carries. It is the extended-community equivalent of AS_PATH loop detection, scoped to a VPN site.
! Tag prefixes with the site they came from
route-map SET-SOO-SITE1 permit 10
set extcommunity soo 65000:1
!
router bgp 65000
address-family ipv4 vrf CUST-A-VPN
neighbor 172.16.1.2 remote-as 65100
neighbor 172.16.1.2 activate
neighbor 172.16.1.2 route-map SET-SOO-SITE1 in
exit-address-family
!
! Verify the SoO is attached
PE-1# show bgp vpnv4 unicast vrf CUST-A-VPN 172.16.5.0/24 | include Extended
Extended Community: RT:65000:100 SoO:65000:1
Standard and extended communities compared
| Property | Standard (RFC 1997) | Extended (RFC 4360) | Large (RFC 8092) |
|---|---|---|---|
| Attribute type code | 8 | 16 | 32 |
| Size | 4 bytes | 8 bytes | 12 bytes |
| Structure | AS:value by convention only | Type and subtype fields | Three 4-byte fields |
| Works with 4-byte ASNs | No — AS field is 16 bits | Yes, with the 4-octet type | Yes, natively |
| Meaning | Local convention | Standardised per subtype | Local convention |
| Typical use | Policy signalling | Route Target, SoO, OSPF Domain ID | Policy signalling with 4-byte ASNs |
| Cisco enabling keyword | send-community |
send-community extended |
send-community both plus large support |
! Matching on extended and large communities uses their own list types
ip extcommunity-list standard RT-CUSTA permit rt 65000:100
ip extcommunity-list standard SOO-SITE1 permit soo 65000:1
ip extcommunity-list expanded ANY-OUR-RT permit _RT:65000:[0-9]+_
!
ip large-community-list standard LC-CUST permit 4200000000:1000:1
!
route-map MATCH-RT permit 10
match extcommunity RT-CUSTA
route-map MATCH-LC permit 10
match large-community LC-CUST
65000:100 as a standard community is a policy tag your network invented; RT:65000:100 is a VPN membership rule. Using the same numeric value for both, which is common and convenient, makes the distinction easy to lose in a hurry — prefix the extended ones in documentation as RT: and SoO: so the type is never ambiguous.How Do I Verify and Troubleshoot the Whole Scheme?
What proves the scheme is working end to end?
Four checks in order. On ingress, confirm the class and geography communities were applied by comparing pre-policy and post-policy views of the same neighbour. Internally, confirm every prefix in the table carries exactly one class tag — a prefix with none will be dropped by egress, and a prefix with two means something classified it twice. On egress, confirm the advertised prefix count matches the number of customer and own-origin prefixes rather than the full table. And on the far side, confirm your internal communities are absent from what the peer received.
A Deeper Dive into Verification
Communities as an operational data source
Once every prefix carries a class and a geography tag, the BGP table becomes queryable in ways that would otherwise require an external database. How many prefixes does each customer contribute? How much of the table entered through a given region? Did a peer suddenly start sending an order of magnitude more than yesterday? Each is a single command against a community value, and each is cheap enough to poll on a schedule. That reporting capability is a genuine second benefit of the scheme rather than a side effect, and it is often what justifies the effort of building it.
The queries also make change validation concrete. Before a maintenance window, record the per-class counts. Afterwards, compare. A count that moved when nothing about that class was supposed to change is a finding, and it surfaces within seconds rather than during the next incident.
! Per-class inventory - one command each, cheap to poll
BR-1# show ip bgp community 65000:1000 | include Total
BR-1# show ip bgp community 65000:1100 | include Total
BR-1# show ip bgp community 65000:1200 | include Total
!
! Per-region inventory
BR-1# show ip bgp community 65000:2001 | include Total
BR-1# show ip bgp community 65000:2002 | include Total
!
! Which customers are asking for a prepend right now?
BR-1# show ip bgp community 65000:3003
!
! Everything currently blackholed - useful during an incident
BR-1# show ip bgp community 65000:9999
!
! Match a whole range with a community list rather than one value
ip community-list expanded ANY-ACTION permit _65000:3[0-9][0-9][0-9]_
BR-1# show ip bgp community-list ANY-ACTION | include Total
Check one: did ingress tagging work
! Pre-policy: what the customer actually sent
BR-1# show ip bgp neighbors 192.0.2.2 received-routes
! ^ Needs soft-reconfiguration inbound; enable it for the check
!
! Post-policy: what we kept and how we tagged it
BR-1# show ip bgp 203.0.113.0/24
65100
192.0.2.2 from 192.0.2.2 (192.0.2.2)
Origin IGP, metric 0, localpref 300, valid, external, best
Community: 65000:1000 65000:2001
! ^ Class 1000, geography 2001, local preference 300. Correct.
Check two: does every prefix carry exactly one class
! Count prefixes per class - the three should sum to the table size
BR-1# show ip bgp community 65000:1000 | include Total
Total number of prefixes 6
BR-1# show ip bgp community 65000:1100 | include Total
Total number of prefixes 4218
BR-1# show ip bgp community 65000:1200 | include Total
Total number of prefixes 892104
BR-1# show ip bgp | include Total number
Total number of prefixes 896330
! 6 + 4218 + 892104 + own-origin = 896330. Nothing untagged.
!
! Find anything with no class tag at all - these will be dropped
BR-1# show ip bgp community-list ADVERTISABLE | include Total
Check three: what is actually advertised outbound
! The number that matters. Should be customers plus own origin.
BR-2# show ip bgp neighbors 198.51.0.6 advertised-routes | include Total
Total number of prefixes 9
! ^ Six customer prefixes plus three of our own. Not 896330.
! A number near the full table size is a leak in progress.
!
! Confirm internal tags were stripped
BR-2# show ip bgp neighbors 198.51.0.6 advertised-routes 203.0.113.0
! Better still, check from the other side or a looking glass:
R-TRANSIT# show ip bgp 203.0.113.0/24 | include Community
Community: 65400:100
! ^ Only the transit provider's own tag. Ours are gone. Correct.
Troubleshooting a tag that is not where you expect it
When a community is missing, the question is always which of four places lost it, and the four are distinguishable in order. Did the sender transmit it — check received-routes, which shows the pre-policy view. Did our ingress map strip it — compare received-routes against routes for the same neighbour. Did an internal policy remove it in transit — check the same prefix on an intermediate router. Did our egress map strip it deliberately — check advertised-routes and compare against the map's comm-list delete clauses. Working the sequence takes four commands and identifies the exact hop, which is considerably faster than reading route-maps hoping to spot the clause.
! 1. Did the peer send it? (needs soft-reconfiguration inbound)
BR-1# show ip bgp neighbors 192.0.2.2 received-routes 203.0.113.0
!
! 2. Did our ingress keep it?
BR-1# show ip bgp neighbors 192.0.2.2 routes 203.0.113.0
!
! 3. Does it survive internally?
BR-2# show ip bgp 203.0.113.0/24 | include Community
!
! 4. What leaves the network?
BR-2# show ip bgp neighbors 198.51.0.6 advertised-routes 203.0.113.0
! ^ The first step where the community is absent is the culprit.
Check four: are the action communities honoured
! Customer sets 65000:3003 asking for a triple prepend
BR-2# show ip bgp 198.51.100.0/22 | include Community
Community: 65000:1000 65000:2001 65000:3003
!
! ...and the advertisement carries the prepend
R-TRANSIT# show ip bgp 198.51.100.0/22
65000 65000 65000 65000 65200
198.51.0.5 from 198.51.0.5 (10.0.0.2)
! ^ Our AS appears four times: one natural plus three prepended.
| Symptom | Likely cause | Confirming command | Fix |
|---|---|---|---|
| Peer sees no communities at all | send-community missing |
show ip bgp neighbors X | include Community |
Add send-community both |
| Customer action tags disappear | set community without additive |
Compare received-routes and routes |
Add additive |
| Prefixes not advertised outbound | No class tag, so the egress match fails | show ip bgp <prefix> Community line |
Tag on origination or ingress |
| Full table advertised to transit | Egress map missing or permits too much | advertised-routes | include Total |
Match ADVERTISABLE only |
| Internal tags visible externally | No comm-list delete on egress |
Public looking glass, or the peer's table | Add the delete to every egress clause |
| L3VPN imports nothing | send-community extended missing |
show bgp vpnv4 unicast all vs VRF table |
Add the keyword on the VPNv4 session |
| Community list matches nothing | Multiple values on one line means AND | show ip community-list |
One value per permit line |
no-export; "traffic from this customer must prefer the second provider" is a community that some route-map converts into local preference. The reliably tested items are send-community, the additive keyword, and the AND semantics of a multi-value standard community list. The extended-community half is examined more heavily in the MPLS L3VPN material.Conclusion
A community is a four-byte number with no intrinsic meaning, and everything useful about community-based policy comes from the discipline wrapped around that. The number space is designed in ranges so a regular expression can match a category. Classification happens once, at the only place in the network that knows what kind of neighbour sent a prefix. Every later decision reads the tag rather than re-deriving it, which is why onboarding a customer touches one session rather than every border router. And internal tags are stripped on the way out, because a community that escapes your autonomous system is both an information disclosure and an input to somebody else's policy.
Two configuration details cause a disproportionate share of the failures and both are single keywords. Cisco does not transmit communities without send-community, so a scheme can be entirely correct locally and invisible to every peer. And set community replaces rather than appends unless additive is present, so an ingress map that tags a prefix silently discards whatever the customer sent — including the action requests the egress policy was written to honour. Neither produces an error, and neither is visible without comparing pre-policy and post-policy views.
The extended-community half of the lab is worth building even if you do not run MPLS, because it demonstrates the difference between a convention and a mechanism. A Route Target is not a hint that some route-map might act on; it is the membership rule itself, and a VRF imports a prefix because the value matches. That structural difference — self-describing types rather than local agreement — is why Route Target scaled to millions of VPNs while standard communities remain a per-network convention. Build both halves, then verify with counts rather than configurations: prefixes per class, prefixes advertised per neighbour, and what the far side actually received.
External Links Recommendations
- RFC 1997 — BGP Communities Attribute: the standard community format and the well-known values.
- RFC 4360 — BGP Extended Communities Attribute: the eight-byte format, type and subtype fields, and Route Target.
- RFC 8092 — BGP Large Communities Attribute: the twelve-byte format for four-byte autonomous system numbers.
- RFC 7999 — BLACKHOLE Community: the well-known 65535:666 value for interoperable blackhole signalling.
- RFC 4364 — BGP/MPLS IP Virtual Private Networks: Route Targets as the VPN membership mechanism.
- Cisco IOS-XE BGP Configuration Guide: current syntax for
send-community,set comm-list delete, and extended community lists. - Cisco Learning Network — CCIE Enterprise Infrastructure: current blueprint and lab equipment list.
Reference Notes
- RFC 1997 — the COMMUNITY attribute is optional transitive, type code 8, carrying one or more four-octet values.
- RFC 1997 — the well-known communities NO_EXPORT (0xFFFFFF01), NO_ADVERTISE (0xFFFFFF02), and NO_EXPORT_SUBCONFED (0xFFFFFF03).
- RFC 4360, Section 2 — the extended community format: a two-octet type field followed by six octets of value, giving structured and self-describing semantics.
- RFC 4360, Section 4 — the Route Target extended community and its role in controlling route distribution.
- RFC 4360, Section 5 — the Route Origin (Site of Origin) extended community.
- RFC 4364, Section 4.3 — Route Targets determine which VRFs import a VPN-IPv4 route, independent of any local policy configuration.
- RFC 4364, Section 4.3.5 — Site of Origin prevents a route from being advertised back to the site from which it was learned.
- RFC 8092, Section 2 — the Large Community format of three four-octet fields, conventionally Global Administrator plus two Local Data parts.
- RFC 7999, Section 2 — the BLACKHOLE well-known community 65535:666 and the recommendation to pair it with NO_EXPORT or NO_ADVERTISE.
- Cisco IOS-XE BGP Configuration Guide — communities are not advertised to a neighbour unless
send-communityis configured, with separatestandard,extended, andbothoptions. - Cisco IOS-XE BGP Configuration Guide —
set communityreplaces the existing community list; theadditivekeyword appends instead. - Cisco IOS-XE BGP Configuration Guide —
set comm-list <name> deleteremoves every community matching the named list from the route.