-
Notifications
You must be signed in to change notification settings - Fork 465
Route Reflection
Route reflection is a BGP scaling technique that reduces the number of required IBGP (Internal BGP) peering sessions in large networks. This page covers how ExaBGP fits into a route reflected network: peering with a route reflector, and setting the route reflection attributes on the routes it announces.
- Overview
- Route Reflection Concepts
- Route Reflector Configuration
- Route Reflector Client Configuration
- Cluster Configuration
- Loop Prevention
- Best Practices
- Troubleshooting
- See Also
Important: ExaBGP is not a route reflector. It never re-advertises what it receives from one peer to another peer: received routes go to your program through the API, and what ExaBGP announces is what your program (or the configuration) tells it to announce.
There is no route-reflector-client directive. What ExaBGP does give you is the route reflection
attributes on the routes it announces, originator-id and cluster-list, so a program can generate
routes which look like reflected ones. To act as a real route reflector, use a routing daemon such as
BIRD or FRRouting.
In traditional IBGP, all routers must peer with each other (full mesh). For n routers, this requires n(n-1)/2 peering sessions:
- 5 routers: 10 sessions
- 10 routers: 45 sessions
- 50 routers: 1,225 sessions
- 100 routers: 4,950 sessions
Route reflection allows routers to peer with a route reflector instead of each other, dramatically reducing the number of sessions.
Traditional IBGP (Full Mesh): Route Reflection:
R1 ─────── R2 R1 ────┐
│ ╲ ╱ │ R2 ───┤
│ ╲ ╱ │ R3 ───┼─── RR (Route Reflector)
│ ╱ ╲ │ R4 ───┤
│ ╱ ╲ │ R5 ────┘
R3 ─────── R4
│ │ 5 sessions (vs 10 in full mesh)
└────R5───┘
10 sessions
- Route Reflector (RR) - Router that reflects routes to clients
- Route Reflector Client - Router that peers with the RR
- Non-Client - IBGP peer that is not a client (full mesh with RR)
- Cluster - Route reflector and its clients
- Cluster ID - Identifier for a cluster (prevents loops)
- Originator ID - Original router that advertised the route
A route reflector follows these rules when receiving routes from IBGP peers:
1. Routes from Clients:
- Reflect to all other clients in the cluster
- Reflect to all non-client IBGP peers
- Advertise to all EBGP peers
2. Routes from Non-Clients:
- Reflect to all clients in the cluster
- Do NOT reflect to other non-clients
- Advertise to all EBGP peers
3. Routes from EBGP peers:
- Reflect to all clients
- Advertise to all non-client IBGP peers
- Advertise to all other EBGP peers
Route reflectors add special BGP attributes to prevent loops:
- ORIGINATOR_ID (Type Code 9) - Router ID of the route originator
- CLUSTER_LIST (Type Code 10) - List of cluster IDs the route has traversed
Configure ExaBGP as a route reflector.
# /etc/exabgp/exabgp.conf
# ExaBGP as Route Reflector
# Client 1
neighbor 192.0.2.11 {
router-id 192.0.2.1;
local-address 192.0.2.1;
local-as 65001;
peer-as 65001;
family {
ipv4 unicast;
ipv6 unicast;
}
}
# Client 2
neighbor 192.0.2.12 {
router-id 192.0.2.1;
local-address 192.0.2.1;
local-as 65001;
peer-as 65001;
family {
ipv4 unicast;
ipv6 unicast;
}
}
# Client 3
neighbor 192.0.2.13 {
router-id 192.0.2.1;
local-address 192.0.2.1;
local-as 65001;
peer-as 65001;
family {
ipv4 unicast;
ipv6 unicast;
}
}
# Non-client IBGP peer (traditional full mesh with this peer)
neighbor 192.0.2.20 {
router-id 192.0.2.1;
local-address 192.0.2.1;
local-as 65001;
peer-as 65001;
family {
ipv4 unicast;
ipv6 unicast;
}
}neighbor 192.0.2.11 {
router-id 192.0.2.1;
local-address 192.0.2.1;
local-as 65001;
peer-as 65001;
# Route reflection for multiple address families
family {
ipv4 unicast;
ipv6 unicast;
ipv4 mpls-vpn;
ipv6 mpls-vpn;
l2vpn evpn;
}
}Configure ExaBGP as a route reflector client.
# /etc/exabgp/exabgp.conf
# ExaBGP as Route Reflector Client
# Peer with route reflector
neighbor 192.0.2.1 {
router-id 192.0.2.11;
local-address 192.0.2.11;
local-as 65001;
peer-as 65001;
# No special directive needed on client side
# Route reflector marks this as a client
family {
ipv4 unicast;
ipv6 unicast;
}
}
# API process to announce routes
process announce {
run /etc/exabgp/announce.py;
encoder text;
}
neighbor 192.0.2.1 {
api {
processes [ announce ];
}
}#!/usr/bin/env python3
# /etc/exabgp/announce.py
# Route reflector client announcing routes
import sys
import time
# Announce routes to route reflector
# Route reflector will reflect to other clients
routes = [
"198.51.100.0/24",
"203.0.113.0/24",
]
for route in routes:
print(f"announce route {route} next-hop self", flush=True)
sys.stderr.write(f"Announced {route} to route reflector\n")
# Keep process running
while True:
time.sleep(60)A cluster consists of a route reflector and its clients. Cluster IDs prevent routing loops.
ExaBGP has no cluster-id directive: the cluster identity is carried per route, in the cluster-list
attribute of the routes you announce (route 10.0.0.0/24 next-hop self cluster-list [ 192.0.2.254 ]).
The neighbor blocks below only set up the sessions.
# /etc/exabgp/exabgp.conf
# Route Reflector with Cluster ID
neighbor 192.0.2.11 {
router-id 192.0.2.1;
local-address 192.0.2.1;
local-as 65001;
peer-as 65001;
family {
ipv4 unicast;
}
}For redundancy, deploy multiple route reflectors in the same cluster:
RR1 (192.0.2.1)
/ \
/ \
Client1 Client2
\ /
\ /
RR2 (192.0.2.2)
Both RRs must use the SAME cluster ID:
# RR1 Configuration
neighbor 192.0.2.11 {
router-id 192.0.2.1;
local-address 192.0.2.1;
local-as 65001;
peer-as 65001;
family {
ipv4 unicast;
}
}
# RR2 Configuration
neighbor 192.0.2.11 {
router-id 192.0.2.2;
local-address 192.0.2.2;
local-as 65001;
peer-as 65001;
family {
ipv4 unicast;
}
}In very large networks, use hierarchical route reflection:
Top-Level RR
/ \
Regional RR1 Regional RR2
/ | \ / | \
C1 C2 C3 C4 C5 C6
Different clusters use different cluster IDs:
# Top-Level RR Configuration
neighbor 192.0.2.10 { # Regional RR1
router-id 192.0.2.1;
local-address 192.0.2.1;
local-as 65001;
peer-as 65001;
family {
ipv4 unicast;
}
}
# Regional RR1 Configuration
neighbor 192.0.2.11 { # Client 1
router-id 192.0.2.10;
local-address 192.0.2.10;
local-as 65001;
peer-as 65001;
family {
ipv4 unicast;
}
}Route reflectors use two BGP attributes to prevent routing loops:
The ORIGINATOR_ID is the router ID of the router that originally advertised the route into IBGP.
Loop Prevention Rule: If a router receives a route with its own router ID in the ORIGINATOR_ID, it rejects the route.
The CLUSTER_LIST is a list of cluster IDs the route has traversed.
Loop Prevention Rule: If a route reflector receives a route with its own cluster ID in the CLUSTER_LIST, it rejects the route.
RR1 (Cluster 192.0.2.1)
│
│ reflects route with:
│ ORIGINATOR_ID = 192.0.2.11
│ CLUSTER_LIST = [192.0.2.1]
▼
RR2 (Cluster 192.0.2.2)
│
│ reflects route with:
│ ORIGINATOR_ID = 192.0.2.11
│ CLUSTER_LIST = [192.0.2.1, 192.0.2.2]
▼
RR1 (Cluster 192.0.2.1)
│
└─ REJECTS route (own cluster ID in CLUSTER_LIST)
Check ExaBGP logs for rejected routes:
tail -f /var/log/exabgp/exabgp.log | grep -i "cluster\|originator"Deploy at least 2 route reflectors per cluster for redundancy:
# Client peers with both RRs
neighbor 192.0.2.1 { # RR1
router-id 192.0.2.11;
local-address 192.0.2.11;
local-as 65001;
peer-as 65001;
family {
ipv4 unicast;
}
}
neighbor 192.0.2.2 { # RR2
router-id 192.0.2.11;
local-address 192.0.2.11;
local-as 65001;
peer-as 65001;
family {
ipv4 unicast;
}
}All route reflectors in the same cluster MUST use the same cluster ID:
# GOOD: Same cluster ID
# RR1: cluster-id 192.0.2.254
# RR2: cluster-id 192.0.2.254
# BAD: Different cluster IDs
# RR1: cluster-id 192.0.2.1
# RR2: cluster-id 192.0.2.2For large networks, design a hierarchical route reflection architecture:
- Small networks (<50 routers): Single cluster, 2+ RRs
- Medium networks (50-500 routers): 2-3 clusters, 2+ RRs per cluster
- Large networks (>500 routers): Hierarchical, 3+ levels
Route reflectors are critical infrastructure. Monitor them carefully:
#!/usr/bin/env python3
# /etc/exabgp/rr-monitor.py
import sys
import json
client_count = 0
routes_reflected = 0
while True:
line = sys.stdin.readline()
if not line:
break
try:
data = json.loads(line)
if data.get('type') == 'state':
state = data['neighbor']['state']
peer = data['neighbor']['address']['peer']
if state == 'up':
client_count += 1
sys.stderr.write(f"Client {peer} connected (total: {client_count})\n")
elif state == 'down':
client_count -= 1
sys.stderr.write(f"Client {peer} disconnected (total: {client_count})\n")
elif data.get('type') == 'update':
routes_reflected += 1
sys.stderr.write(f"Routes reflected: {routes_reflected}\n")
except Exception as e:
sys.stderr.write(f"Error: {e}\n")Maintain documentation of your route reflection topology:
# route-reflection-topology.yaml
clusters:
- cluster_id: 192.0.2.1
name: "Core Cluster"
route_reflectors:
- ip: 192.0.2.1
hostname: rr1.example.com
- ip: 192.0.2.2
hostname: rr2.example.com
clients:
- 192.0.2.11
- 192.0.2.12
- 192.0.2.13
- 192.0.2.14
- 192.0.2.15
- cluster_id: 192.0.2.10
name: "Regional Cluster 1"
route_reflectors:
- ip: 192.0.2.10
hostname: rr-regional1.example.com
clients:
- 192.0.2.21
- 192.0.2.22
- 192.0.2.23Regularly test route reflector failover:
# Shutdown RR1, verify clients fail over to RR2
sudo systemctl stop exabgp@rr1
# Check client connectivity
# Clients should maintain connectivity via RR2
# Restore RR1
sudo systemctl start exabgp@rr1Enable route refresh capability to request route updates:
neighbor 192.0.2.11 {
router-id 192.0.2.1;
local-address 192.0.2.1;
local-as 65001;
peer-as 65001;
capability {
route-refresh enable;
}
family {
ipv4 unicast;
}
}Symptoms: Clients don't receive routes from other clients.
Debugging steps:
- Verify route reflector client configuration:
neighbor 192.0.2.11 {
}- Check BGP sessions are established:
grep "Peer.*up" /var/log/exabgp/exabgp.log- Verify address families match:
# RR and clients must have matching address families
family {
ipv4 unicast;
}- Check for loop prevention rejections:
tail -f /var/log/exabgp/exabgp.log | grep -i "loop\|cluster\|originator"Symptoms: Routes loop between route reflectors, consuming bandwidth.
Solutions:
- Verify cluster IDs are configured:
neighbor 192.0.2.11 {
}- Check cluster IDs are identical within a cluster:
# All RRs in same cluster must have same cluster-id- Review hierarchical design:
# Different clusters must have different cluster IDs
# Top-level: cluster-id 192.0.2.1
# Regional: cluster-id 192.0.2.10
Symptoms: Different clients receive different routes.
Debugging steps:
- Check route policies are consistent across RRs:
# All RRs in a cluster should have identical policies- Verify all RRs are receiving the same routes:
# Ask ExaBGP to hand the received routes to your program
api {
processes [ watcher ];
neighbor-changes;
receive {
parsed;
update;
}
}- Check for split-brain scenario:
# Verify RRs can communicate with each otherSymptoms: Route reflector CPU/memory usage is high.
Solutions:
- Add more route reflectors:
# Distribute clients across more RRs
- Use hierarchical route reflection:
# Create regional clusters
- Optimize address families:
# Only reflect required address families
family {
ipv4 unicast; # Don't enable unnecessary families
}- Configuration Syntax - ExaBGP configuration reference
- BGP-LS Overview - BGP Link-State for topology collection
- IPv4 Unicast - IPv4 unicast routing
- IPv6 Unicast - IPv6 unicast routing
- RFC 4456 - BGP Route Reflection
- RFC 2796 - BGP Route Reflection (Historic)
Getting Started
Configuration
- Configuration Syntax
- Neighbor Configuration
- Directives A-Z
- Templates
- Environment Variables
- Process Configuration
API
- API Overview
- Text API Reference
- JSON API Reference
- API Commands
- Writing API Programs
- Error Handling
- Production Best Practices
Address Families
- Overview
- IPv4 Unicast
- IPv6 Unicast
- FlowSpec
- EVPN
- L3VPN
- BGP-LS
- VPLS
- SRv6 / MUP
- Multicast
- RT Constraint
Features
Use Cases
Tools
Operations
Reference
- Architecture
- Design
- Attribute Reference
- Command Reference
- BGP State Machine
- Capabilities
- Communities
- Examples Index
- Glossary
- RFC Support
Integration
Migration
Community
External