Skip to main content
RunBook Academy

VyOSXXX · BGP Route ReflectorsRoute reflectors

Route reflector configuration — `route-reflector-client`, the two-RR cluster, and the canonical client/server mesh

Advanced⏱ ~24 minshow bgp ipv4 unicastshow bgp ipv4 unicast <prefix>show bgp ipv4 unicast <prefix> jsonshow bgp ipv4 unicast summaryshow bgp ipv4 unicast neighbors <ip>show ip route <prefix>vtysh -c 'show running-config'

What you'll learn

  • Configure `route-reflector-client` in the right place on the VyOS 1.5 tree — inside the neighbour's address family
  • Build the canonical two-RR cluster: one cluster-id, every client peering with both reflectors
  • State the three RFC 4456 reflection rules and predict what an RR will and will not re-advertise
  • Verify reflection from the Originator and Cluster list attributes on the route
  • Diagnose a client that has the session but not the route, including the next-hop reachability case
  • Recognise the production failure modes: flag outside the address family, unset cluster-id, unreachable next hop

Prerequisites

Verified against VyOS 1.5.x LTS (circinus) · VyOS 1.4.x (sagitta) — legacy · FRRouting 10.x (VyOS 1.5) · Linux kernel 6.6 LTS (VyOS 1.5 base) · strongSwan 5.9.x (IPsec) · WireGuard 1.0.x (kernel module + userspace tooling) · 2026-08-19

Not yet marked complete on this device.

A route reflector is one flag on one side of one session. The flag tells the router “this iBGP neighbour is my client”, which grants the router permission to break the iBGP split-horizon rule and re-advertise the client’s routes to other iBGP speakers. Everything else about route reflection — the cluster, the redundancy, the loop prevention — follows from that one flag plus a cluster identifier.

The canonical production topology is two reflectors sharing a cluster-id. Clients form iBGP sessions with both. Clients do not peer with each other. The cluster-id is the loop-breaker that stops the two reflectors bouncing a route between themselves forever.

The single flag, and where it lives

set protocols bgp system-as 65001
set protocols bgp neighbor 10.0.0.1 remote-as '65001'
set protocols bgp neighbor 10.0.0.1 address-family ipv4-unicast route-reflector-client

Three things about that block are worth reading slowly.

The AS number is a leaf, not a path component. From VyOS 1.4 onward the local AS is set protocols bgp system-as 65001 and every peer hangs off set protocols bgp neighbor <ip>. A runbook written as set protocols bgp 65001 neighbor 10.0.0.1 ... fails at the set, before any commit.

The flag lives inside the address family, not on the neighbour. Route reflection is a per-address-family property in BGP, so route-reflector-client sits under address-family ipv4-unicast (or ipv6-unicast, or l2vpn-evpn). Setting it for IPv4 does nothing for the IPv6 table on the same session. A dual-stack cluster needs the flag twice.

remote-as equals the local AS. Route reflection is an iBGP mechanism. The flag on an eBGP session is meaningless — eBGP has no split-horizon rule to relax.

The client’s own configuration is that of an ordinary iBGP peer:

set protocols bgp system-as 65001
set protocols bgp neighbor 10.255.0.5 remote-as '65001'
set protocols bgp neighbor 10.255.0.255 remote-as '65001'

The client peers with both reflectors and has no idea it is a client. Reflection is asymmetric: the reflector stamps ORIGINATOR_ID and CLUSTER_LIST onto every route it reflects, and the client simply receives routes it would not otherwise have been sent.

The three reflection rules

RFC 4456 does not say “the RR re-advertises everything”. It defines three cases, and mixing them up is the source of most route-reflector surprises:

Route learned fromRe-advertised to clientsRe-advertised to non-client iBGP peersRe-advertised to eBGP peers
An eBGP peerYesYesYes
A clientYes (all other clients)YesYes
A non-client iBGP peerYesNoYes

The third row is the one that bites. A route the reflector learned from an ordinary iBGP peer is reflected down to clients but is not passed sideways to another non-client — that would be the split-horizon violation route reflection exists to make safe, and it is only made safe for clients.

This is why the two reflectors in a cluster must peer with each other as ordinary iBGP peers (no client flag between them) and why each of them needs its own sessions to the clients. RR-1 does not learn a client route “through” RR-2.

The canonical two-RR cluster

flowchart TD
  subgraph CLUSTER["Cluster 1.2.3.4"]
    RR1["RR-1<br/>router-id 10.255.0.5<br/>cluster-id 1.2.3.4"]
    RR2["RR-2<br/>router-id 10.255.0.255<br/>cluster-id 1.2.3.4"]
    C1["Client A<br/>10.0.0.1"]
    C2["Client B<br/>10.0.0.2"]
    C3["Client C<br/>10.0.0.3"]
  end
  subgraph OUTSIDE["Outside the AS"]
    EBGP1["eBGP peer X"]
    EBGP2["eBGP peer Y"]
  end
  RR1 -->|"iBGP (client)"| C1
  RR1 -->|"iBGP (client)"| C2
  RR1 -->|"iBGP (client)"| C3
  RR2 -->|"iBGP (client)"| C1
  RR2 -->|"iBGP (client)"| C2
  RR2 -->|"iBGP (client)"| C3
  RR1 ---|"iBGP (non-client)"| RR2
  EBGP1 -->|"eBGP"| RR1
  EBGP2 -->|"eBGP"| RR2

RR-1:

set protocols bgp system-as 65001
set protocols bgp parameters router-id '10.255.0.5'
set protocols bgp parameters cluster-id '1.2.3.4'

set protocols bgp neighbor 10.0.0.1 remote-as '65001'
set protocols bgp neighbor 10.0.0.1 update-source 'lo'
set protocols bgp neighbor 10.0.0.1 address-family ipv4-unicast route-reflector-client

set protocols bgp neighbor 10.0.0.2 remote-as '65001'
set protocols bgp neighbor 10.0.0.2 update-source 'lo'
set protocols bgp neighbor 10.0.0.2 address-family ipv4-unicast route-reflector-client

set protocols bgp neighbor 10.255.0.255 remote-as '65001'
set protocols bgp neighbor 10.255.0.255 update-source 'lo'

set protocols bgp neighbor 192.0.2.2 remote-as '64512'

RR-2 is the same file with its own router-id and the other reflector as its non-client peer:

set protocols bgp system-as 65001
set protocols bgp parameters router-id '10.255.0.255'
set protocols bgp parameters cluster-id '1.2.3.4'

set protocols bgp neighbor 10.0.0.1 remote-as '65001'
set protocols bgp neighbor 10.0.0.1 update-source 'lo'
set protocols bgp neighbor 10.0.0.1 address-family ipv4-unicast route-reflector-client

set protocols bgp neighbor 10.0.0.2 remote-as '65001'
set protocols bgp neighbor 10.0.0.2 update-source 'lo'
set protocols bgp neighbor 10.0.0.2 address-family ipv4-unicast route-reflector-client

set protocols bgp neighbor 10.255.0.5 remote-as '65001'
set protocols bgp neighbor 10.255.0.5 update-source 'lo'

set protocols bgp neighbor 198.51.100.1 remote-as '64513'

router-id and cluster-id are both BGP-instance settings and both live under protocols bgp parameters — they are not neighbour properties and there is no ASN in their path either.

Why the mesh shrinks

A full iBGP mesh over R routers needs R(R-1)/2 sessions. A cluster of N clients plus two reflectors needs 2N sessions from clients to reflectors, plus one session between the reflectors: 2N + 1.

ClientsRouters totalFull-mesh iBGP sessionsTwo-RR cluster sessionsReduction
1012662168%
50521 32610192%
1001025 15120196%

The eBGP sessions are unchanged by the design and are excluded from both columns — the reduction is entirely in the iBGP mesh, which is where the quadratic term lives.

What the table does not show is the cost: the reflectors now see every route and every client depends on them. Session count is not the only thing that scales.

Verifying the reflection

1 — Is the neighbour actually a client? The flag is a property of an address family, so it shows up in the per-address-family portion of the neighbour detail:

Read-only / Safeop mode
vyos@RR-1:~$ show bgp ipv4 unicast neighbors 10.0.0.1

If that line is missing while the session is Established, the flag was applied to a different address family than the one carrying your routes — the single commonest configuration mistake in a dual-stack or EVPN cluster.

2 — Was the route reflected, and by whom? A reflected route carries two attributes an ordinary iBGP route does not: ORIGINATOR_ID, the router-id of the iBGP speaker that first injected it into the AS, and CLUSTER_LIST, the cluster identifiers of every reflector that has passed it on.

Read-only / Safeop mode
vyos@ClientB:~$ show bgp ipv4 unicast 198.51.100.0/24
BGP routing table entry for 198.51.100.0/24
Paths: (2 available, best #1, table default)
Not advertised to any peer
64512
  192.0.2.2 from 10.255.0.5 (10.255.0.5)
    Origin IGP, localpref 100, valid, internal, best (Router ID)
    Originator: 10.255.0.5, Cluster list: 1.2.3.4
64512
  198.51.100.1 from 10.255.0.255 (10.255.0.255)
    Origin IGP, localpref 100, valid, internal
    Originator: 10.255.0.255, Cluster list: 1.2.3.4

Illustrative output

Read three things off that view. The from address is the reflector the update arrived over. The address on the line above Origin is the next hop, and it is not the reflector — reflection does not rewrite the next hop. And the Cluster list shows one entry, 1.2.3.4: the route crossed exactly one cluster. A cluster list that grows every time you look is a loop; a cluster list containing your own cluster-id on an inbound route is the loop-breaker doing its job.

The same view is available as show bgp ipv4 unicast 198.51.100.0/24 json when you need to assert on it from automation.

3 — Can the client reach the next hop? This is where first deployments fail. The reflector passes the eBGP next hop (192.0.2.2 above) through unchanged, because RFC 4456 forbids a reflector from modifying the next hop on reflection. The client therefore needs a route to the provider-facing link subnet, and if that subnet is not in the IGP the path is invalid and never becomes best.

Read-only / Safeop mode
vyos@ClientB:~$ show ip route 192.0.2.2

Two fixes, and they are not equivalent. Redistributing the eBGP link subnets into the IGP keeps the next hop honest and preserves the ability to route around a failed reflector-to-provider link. Setting next-hop-self towards the clients on the reflector makes the reflector the next hop for all those prefixes, which is simpler and makes the reflector a forwarding bottleneck it was never meant to be. Choose the first unless you know why you want the second.

How it fails

  • The route-reflector-client flag was set outside the address family carrying the routes. IPv4 configured, IPv6 or EVPN not. The session is Established, the client sees nothing for that family, and show bgp <afi> <safi> neighbors <ip> does not show the Route-Reflector Client line for it.
  • The client cannot reach the next hop. Session up, route present in the client’s BGP table, never selected as best. show bgp ipv4 unicast <prefix> shows the path without a best marker; show ip route <next-hop> shows why.
  • The reflectors are not peered with each other. Each reflector serves its clients from its own eBGP feed, and the two halves of the AS disagree about routes that only one of them learned. Client routes are especially affected: a route from a client of RR-1 reaches RR-2 only over the RR-to-RR session.
  • The cluster-id is different on the two reflectors when the design says it should be shared. Not fatal — ORIGINATOR_ID still prevents loops — but the redundancy behaviour and the path diversity clients see are not what the design assumed.
  • The flag is on the client, pointing at the reflector. The client now believes the reflector is its client and reflects on its behalf. In a two-node test lab this appears to work, which is exactly why it survives to production.
  • A route-map on the reflected session rewrites attributes. RFC 4456 tells reflectors not to modify NEXT_HOP, AS_PATH, LOCAL_PREF or MED on reflection, because clients are relying on seeing the same information the reflector saw. A “harmless” set local-preference on the client-facing export map makes the reflector’s best-path choice and the client’s diverge, and divergent best-path choices between iBGP speakers are how forwarding loops are built.

Rollback

Route reflector changes are ordinary VyOS configuration with an extraordinary blast radius: removing route-reflector-client from a neighbour withdraws every reflected route from that client, and the client’s forwarding table follows within seconds.

  • compare before commit, and read the leading path of each line — address-family ipv4-unicast is easy to miss in a diff.
  • commit-confirm for any change made over a path the change could break.
  • To undo one flag: delete protocols bgp neighbor 10.0.0.1 address-family ipv4-unicast route-reflector-client, then commit.
  • rollback restores an entire archived revision rather than one clause, and is not a quiet in-place edit — read what your release prompts before confirming it.

After the configuration is back, the BGP table may not be. Removing and re-adding a client flag changes what the reflector advertises from that point on; prefixes already in a client’s table are corrected by the withdrawal, but a policy change on a session usually needs a soft clear (clear bgp ipv4 unicast 10.0.0.1 soft out on the reflector) before what the client sees matches what the configuration says.

Production discipline

Cross-course references

The Linux course’s XIX-Linux-NetFoundations covers the FIB. The OPNsense course’s XXX-OPNsense-DynamicRouting covers the equivalent FRR-managed route reflector on the firewall side. The BGP lessons vyos-xxiv-01-bgp-config and vyos-xxiv-02-bgp-neighbour cover the prerequisites; vyos-xxx-01-rr-concept covers the rationale, vyos-xxx-03-cluster-id covers the cluster-id and redundancy in depth, and vyos-xxx-06-rr-troubleshoot walks the debugging of reflection failures.

Quiz

Knowledge check · 5 questions

  1. Q1. On VyOS 1.5, which command makes the iBGP neighbour 10.0.0.1 a route-reflector client for the IPv4 unicast table?

  2. Q2. In the canonical two-RR cluster, both reflectors share the same cluster-id; a reflector that finds its own cluster-id in a route's CLUSTER_LIST discards the route, and that is what stops the two reflectors looping it.

  3. Q3. A route reflector must not change NEXT_HOP, AS_PATH, LOCAL_PREF or MED on a route it reflects, which is why a client can end up with a path whose next hop it cannot reach.

  4. Q4. An operator configures RR-1 with the route-reflector-client flag for 10.0.0.1. The session is Established, the reflector's table has the prefix, and the client's table does not. Where does the diagnosis go?

    RR-1 has `set protocols bgp neighbor 10.0.0.1 address-family ipv4-unicast route-reflector-client`. The client 10.0.0.1 has `set protocols bgp neighbor 10.255.0.5 remote-as '65001'`. `show bgp ipv4 unicast summary` on both sides shows the session Established with a non-zero uptime. The prefix 198.51.100.0/24 is in RR-1's table and absent from the client's.

  5. Q5. A client has a reflected eBGP path in its BGP table. The path is valid, but it is never chosen as best and the traffic does not use it. What is the most likely cause?

    RR-1 learns 198.51.100.0/24 from its eBGP peer at 192.0.2.2 and reflects it to the clients. On Client B, `show bgp ipv4 unicast 198.51.100.0/24` shows the path with `from 10.255.0.5` and next hop `192.0.2.2`, but without the `best` marker. `show ip route 198.51.100.0/24` on Client B returns nothing.

Passing score: 75%. Answers are checked in this browser.