Skip to content

Route Reflection

Thomas Mangin edited this page Aug 1, 2026 · 4 revisions

BGP 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.

Table of Contents

Overview

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.

Why Route Reflection?

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.

Route Reflection Architecture

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

Terminology

  • 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

Route Reflection Concepts

Route Reflection Rules

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 Reflector Attributes

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

Route Reflector Configuration

Configure ExaBGP as a route reflector.

Basic Route Reflector Configuration

# /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;
    }
}

Route Reflector with Multiple Address Families

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;
    }
}

Route Reflector Client Configuration

Configure ExaBGP as a route reflector client.

Basic Client Configuration

# /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 ];
    }
}

Client with Route Announcements

#!/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)

Cluster Configuration

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.

Single Cluster

# /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;
    }
}

Multiple Route Reflectors (Same Cluster)

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;
    }
}

Hierarchical Route Reflection

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;
    }
}

Loop Prevention

Route reflectors use two BGP attributes to prevent routing loops:

ORIGINATOR_ID

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.

CLUSTER_LIST

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.

Example: Loop Prevention in Action

   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)

Verifying Loop Prevention

Check ExaBGP logs for rejected routes:

tail -f /var/log/exabgp/exabgp.log | grep -i "cluster\|originator"

Best Practices

1. Use Redundant Route Reflectors

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;
    }
}

2. Use Consistent Cluster IDs

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.2

3. Plan Your Cluster Hierarchy

For 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

4. Monitor Route Reflector Health

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")

5. Document Your Topology

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.23

6. Test Failover Scenarios

Regularly 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@rr1

7. Use Route Refresh

Enable 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;
    }
}

Troubleshooting

Problem: Routes Not Being Reflected

Symptoms: Clients don't receive routes from other clients.

Debugging steps:

  1. Verify route reflector client configuration:
neighbor 192.0.2.11 {
}
  1. Check BGP sessions are established:
grep "Peer.*up" /var/log/exabgp/exabgp.log
  1. Verify address families match:
# RR and clients must have matching address families
family {
    ipv4 unicast;
}
  1. Check for loop prevention rejections:
tail -f /var/log/exabgp/exabgp.log | grep -i "loop\|cluster\|originator"

Problem: Routing Loops

Symptoms: Routes loop between route reflectors, consuming bandwidth.

Solutions:

  1. Verify cluster IDs are configured:
neighbor 192.0.2.11 {
}
  1. Check cluster IDs are identical within a cluster:
# All RRs in same cluster must have same cluster-id
  1. 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

Problem: Inconsistent Routes

Symptoms: Different clients receive different routes.

Debugging steps:

  1. Check route policies are consistent across RRs:
# All RRs in a cluster should have identical policies
  1. 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;
    }
}
  1. Check for split-brain scenario:
# Verify RRs can communicate with each other

Problem: Route Reflector Overload

Symptoms: Route reflector CPU/memory usage is high.

Solutions:

  1. Add more route reflectors:
# Distribute clients across more RRs
  1. Use hierarchical route reflection:
# Create regional clusters
  1. Optimize address families:
# Only reflect required address families
family {
    ipv4 unicast;  # Don't enable unnecessary families
}

See Also


Clone this wiki locally