Skip to content

Route Refresh

Thomas Mangin edited this page Nov 10, 2025 · 3 revisions

Route Refresh

BGP Route Refresh allows BGP speakers to request re-advertisement of routes from a peer without tearing down the BGP session. ExaBGP provides full RFC 2918 and RFC 7313 support, enabling soft reconfiguration and enhanced route refresh for dynamic policy changes and operational flexibility.

Table of Contents


What is Route Refresh?

Route Refresh is a BGP capability defined in RFC 2918 that enables a BGP speaker to request a peer to resend all routes for a specific address family without closing and re-establishing the BGP session.

The Problem Without Route Refresh

Before Route Refresh, applying new routing policies required:

  1. Soft reconfiguration inbound: Store duplicate copy of all received routes (memory intensive)
  2. Hard reset: Tear down BGP session and re-establish (service disruption)

Result: Either high memory usage or service interruption for policy changes.

The Solution With Route Refresh

With Route Refresh capability:

  1. Change routing policy (import filters, route-maps, etc.)
  2. Send Route Refresh request to peer
  3. Peer re-sends all routes for requested address family
  4. Apply new policy to refreshed routes
  5. No session disruption, no duplicate storage

Result: Dynamic policy changes without downtime or memory overhead.


How Route Refresh Works

Route Refresh Message Flow

[Policy Change with Route Refresh]

ExaBGP                                  BGP Peer
   β”‚                                        β”‚
   β”‚  BGP session established               β”‚
   β”‚  Route Refresh capability negotiated   β”‚
   β”‚                                        β”‚
   β”‚  Operator changes import policy        β”‚
   β”‚  (e.g., filter routes from AS 64512)  β”‚
   β”‚                                        β”‚
   │──── Route Refresh Request ────────────▢│
   β”‚     AFI: IPv4 (1)                      β”‚
   β”‚     SAFI: Unicast (1)                  β”‚
   β”‚                                        β”‚
   β”‚                                  Peer re-sends
   β”‚                                  all IPv4 routes
   β”‚                                        β”‚
   │◀──── UPDATE (route 1) ─────────────────│
   │◀──── UPDATE (route 2) ─────────────────│
   │◀──── UPDATE (route 3) ─────────────────│
   │◀──── ... ───────────────────────────────│
   │◀──── END-OF-RIB ────────────────────────│
   β”‚                                        β”‚
   β”‚  Apply new policy to all routes        β”‚
   β”‚  Filtered routes not installed         β”‚
   β”‚                                        β”‚
   β”‚  Policy change complete - NO SESSION   β”‚
   β”‚  DISRUPTION!                           β”‚

Key Points:

  • Request specifies AFI/SAFI (address family)
  • Peer re-advertises ALL routes for that family
  • Session remains established
  • Minimal disruption (just route processing)

ExaBGP Route Refresh Capabilities

ExaBGP provides full Route Refresh implementation:

βœ… Route Refresh Capability (RFC 2918):

  • Request route refresh for any address family
  • Support as both sender and receiver
  • Per-AFI/SAFI refresh requests

βœ… Enhanced Route Refresh (RFC 7313):

  • BoRR (Beginning-of-Route-Refresh) marker
  • EoRR (End-of-Route-Refresh) marker
  • Demarcate refresh operation boundaries

βœ… All Address Families:

  • IPv4/IPv6 Unicast
  • IPv4/IPv6 Multicast
  • VPNv4/VPNv6
  • FlowSpec (IPv4/IPv6)
  • EVPN
  • BGP-LS
  • All supported AFI/SAFI combinations

βœ… API Integration:

  • Trigger route refresh via API commands
  • Receive route refresh requests from peers
  • JSON and text format support

Implementation: src/exabgp/bgp/message/refresh.py

RFC Compliance:

  • RFC 2918: Route Refresh Capability for BGP-4
  • RFC 7313: Enhanced Route Refresh Capability for BGP-4

Route Refresh vs Soft Reconfiguration

Comparison

Feature Route Refresh Soft Reconfiguration Inbound
Memory Usage Normal (single RIB) High (duplicate routes stored)
Session Impact None None
RFC RFC 2918 Pre-RFC mechanism
Peer Support Required Yes (capability) No
Performance Re-transmission overhead Instant (stored routes)
Recommended βœ… Yes (modern) ❌ No (legacy)

When to Use Route Refresh

βœ… Use Route Refresh when:

  • Peer supports Route Refresh capability (most modern BGP implementations)
  • Memory is constrained
  • Moderate route refresh frequency
  • Standard deployment

❌ Avoid Route Refresh when:

  • Extremely frequent policy changes (multiple times per minute)
  • Peer does not support Route Refresh capability
  • Memory is abundant and instant policy application required

Recommendation: Always prefer Route Refresh over soft reconfiguration inbound. It's the modern, standard approach.


Configuration

Enable Route Refresh Capability

Basic configuration:

neighbor 192.168.1.1 {
    router-id 192.168.1.2;
    local-address 192.168.1.2;
    local-as 65001;
    peer-as 65000;

    capability {
        route-refresh;  # Enable route refresh capability
    }

    family {
        ipv4 unicast;
        ipv6 unicast;
    }

    api {
        processes [ policy-manager ];
    }
}

process policy-manager {
    run python3 /etc/exabgp/api/refresh.py;
    encoder text;
}

Notes:

  • route-refresh is typically enabled by default in modern ExaBGP versions
  • Explicit configuration ensures capability is advertised
  • Capability must be negotiated during session establishment

Disable Route Refresh

Disable Route Refresh (rare):

neighbor 192.168.1.1 {
    # ... neighbor config ...

    capability {
        route-refresh disable;  # Explicitly disable
    }
}

When to disable:

  • Testing legacy BGP behavior
  • Peer incompatibility (very rare)
  • Debugging route refresh issues

API Usage

Request Route Refresh (Text Format)

Trigger route refresh via API:

#!/usr/bin/env python3
import sys

def request_refresh():
    """Request IPv4 unicast route refresh"""
    sys.stdout.write("announce route-refresh ipv4 unicast\n")
    sys.stdout.flush()

def request_ipv6_refresh():
    """Request IPv6 unicast route refresh"""
    sys.stdout.write("announce route-refresh ipv6 unicast\n")
    sys.stdout.flush()

def request_flowspec_refresh():
    """Request IPv4 FlowSpec route refresh"""
    sys.stdout.write("announce route-refresh ipv4 flow\n")
    sys.stdout.flush()

# Trigger refresh after policy change
request_refresh()

Syntax:

announce route-refresh <afi> <safi>

Examples:

announce route-refresh ipv4 unicast
announce route-refresh ipv6 unicast
announce route-refresh ipv4 multicast
announce route-refresh ipv4 vpn
announce route-refresh ipv4 flow
announce route-refresh evpn
announce route-refresh bgpls

Request Route Refresh (JSON Format)

JSON API format:

#!/usr/bin/env python3
import sys
import json

def request_refresh_json():
    """Request route refresh using JSON API"""
    command = {
        "exabgp": "5.0",
        "time": 1699564800.0,
        "neighbor": "192.168.1.1",
        "message": {
            "refresh": {
                "afi": "ipv4",
                "safi": "unicast"
            }
        }
    }

    sys.stdout.write(json.dumps(command) + "\n")
    sys.stdout.flush()

request_refresh_json()

Receive Route Refresh Requests

Handle incoming refresh requests:

#!/usr/bin/env python3
import sys
import json

# Configure process for JSON
# encoder = json

while True:
    line = sys.stdin.readline().strip()
    if not line:
        break

    try:
        msg = json.loads(line)

        # Detect route refresh request from peer
        if msg.get('type') == 'refresh':
            afi = msg['refresh']['afi']
            safi = msg['refresh']['safi']

            print(f"Peer requested refresh: {afi}/{safi}", file=sys.stderr)

            # Re-announce all routes for this AFI/SAFI
            if afi == 'ipv4' and safi == 'unicast':
                re_announce_ipv4_routes()

    except json.JSONDecodeError:
        pass

def re_announce_ipv4_routes():
    """Re-announce all IPv4 routes after refresh request"""
    routes = [
        "announce route 10.0.0.0/8 next-hop 192.168.1.2",
        "announce route 172.16.0.0/12 next-hop 192.168.1.2",
        "announce route 192.168.0.0/16 next-hop 192.168.1.2",
    ]

    for route in routes:
        sys.stdout.write(f"{route}\n")
        sys.stdout.flush()

Enhanced Route Refresh (RFC 7313)

RFC 7313 defines Enhanced Route Refresh with BoRR (Beginning-of-Route-Refresh) and EoRR (End-of-Route-Refresh) markers to clearly demarcate refresh operations.

Benefits of Enhanced Route Refresh

  1. Clear Boundaries: Know when refresh starts and ends
  2. Concurrent Refresh: Support multiple simultaneous refresh operations
  3. Debugging: Easier troubleshooting of refresh operations
  4. Stale Route Detection: Identify routes not refreshed

How Enhanced Route Refresh Works

ExaBGP                                  BGP Peer
   β”‚                                        β”‚
   │──── Enhanced Route Refresh Request ───▢│
   β”‚     AFI: IPv4, SAFI: Unicast           β”‚
   β”‚     Subtype: BoRR (1)                  β”‚
   β”‚                                        β”‚
   │◀──── BoRR Marker ──────────────────────│
   β”‚     (Beginning-of-Route-Refresh)       β”‚
   β”‚                                        β”‚
   │◀──── UPDATE (route 1) ─────────────────│
   │◀──── UPDATE (route 2) ─────────────────│
   │◀──── UPDATE (route 3) ─────────────────│
   │◀──── ... ───────────────────────────────│
   β”‚                                        β”‚
   │◀──── EoRR Marker ──────────────────────│
   β”‚     (End-of-Route-Refresh)             β”‚
   β”‚                                        β”‚
   β”‚  All routes between BoRR and EoRR      β”‚
   β”‚  are part of refresh operation         β”‚

BoRR/EoRR Subtypes:

  • 0: Normal Route Refresh (RFC 2918)
  • 1: BoRR (Beginning-of-Route-Refresh)
  • 2: EoRR (End-of-Route-Refresh)

ExaBGP Support: Fully implemented in src/exabgp/bgp/message/refresh.py


Use Cases

1. Dynamic Policy Changes

Scenario: Change BGP import policy without session disruption.

Example:

#!/usr/bin/env python3
"""Dynamic BGP policy manager with route refresh"""
import sys
import time

def apply_new_policy():
    """Apply new import policy and refresh routes"""

    # Step 1: Update policy (e.g., filter specific AS)
    print("Applying new import policy: block AS 64512", file=sys.stderr)

    # Step 2: Request route refresh to apply new policy
    sys.stdout.write("announce route-refresh ipv4 unicast\n")
    sys.stdout.flush()

    print("Route refresh requested - policy will apply to refreshed routes", file=sys.stderr)

# Trigger policy change
apply_new_policy()

Benefits:

  • No BGP session reset
  • No traffic disruption
  • Immediate policy enforcement

2. Route Re-evaluation After External Event

Scenario: Re-evaluate routes after an external system event (health check, monitoring alert).

Example:

#!/usr/bin/env python3
"""Health check integration with route refresh"""
import sys
import requests

def check_backend_health():
    """Check if backend service is healthy"""
    try:
        response = requests.get("http://localhost:8080/health", timeout=2)
        return response.status_code == 200
    except:
        return False

def refresh_on_health_change(was_healthy):
    """Refresh routes if health status changed"""
    is_healthy = check_backend_health()

    if is_healthy != was_healthy:
        print(f"Health changed: {was_healthy} -> {is_healthy}", file=sys.stderr)

        # Request route refresh to re-evaluate announcements
        sys.stdout.write("announce route-refresh ipv4 unicast\n")
        sys.stdout.flush()

    return is_healthy

# Monitor health and refresh on change
healthy = check_backend_health()
while True:
    time.sleep(10)
    healthy = refresh_on_health_change(healthy)

3. FlowSpec Rule Refresh

Scenario: Update FlowSpec filtering rules dynamically.

Example:

#!/usr/bin/env python3
"""FlowSpec DDoS mitigation with route refresh"""
import sys

def update_ddos_filters():
    """Update FlowSpec filters and refresh"""

    # Announce new FlowSpec rule
    flowspec = (
        "announce flow route "
        "{ match { source 1.2.3.4/32; destination-port =80; protocol tcp; } "
        "then { rate-limit 0; } }"
    )

    sys.stdout.write(f"{flowspec}\n")
    sys.stdout.flush()

    # Request route refresh to ensure rules are synchronized
    sys.stdout.write("announce route-refresh ipv4 flow\n")
    sys.stdout.flush()

    print("FlowSpec filters updated and refresh requested", file=sys.stderr)

update_ddos_filters()

4. Route Reflector Operations

Scenario: Route Reflector client requests refresh after policy change.

Configuration:

neighbor 192.168.1.254 {  # Route Reflector
    router-id 192.168.1.2;
    local-address 192.168.1.2;
    local-as 65001;
    peer-as 65001;  # iBGP

    capability {
        route-refresh;
    }

    family {
        ipv4 unicast;
        ipv6 unicast;
        vpnv4;
    }

    api {
        processes [ rr-client ];
    }
}

API usage:

#!/usr/bin/env python3
"""Route Reflector client route refresh"""
import sys

def refresh_from_rr():
    """Request full route refresh from Route Reflector"""
    families = [
        "ipv4 unicast",
        "ipv6 unicast",
        "ipv4 vpn",
    ]

    for family in families:
        sys.stdout.write(f"announce route-refresh {family}\n")
        sys.stdout.flush()

    print("Requested route refresh from RR for all families", file=sys.stderr)

refresh_from_rr()

Monitoring and Verification

Via API (JSON Format)

Monitor route refresh events:

#!/usr/bin/env python3
"""Monitor route refresh events via API"""
import sys
import json

while True:
    line = sys.stdin.readline().strip()
    if not line:
        break

    try:
        msg = json.loads(line)

        # Incoming route refresh request from peer
        if msg.get('type') == 'refresh':
            print(f"REFRESH REQUEST: {msg}", file=sys.stderr)

        # BoRR marker (Enhanced Route Refresh)
        if msg.get('type') == 'refresh' and msg.get('subtype') == 'bor':
            print(f"BoRR: Route refresh starting for {msg['afi']}/{msg['safi']}", file=sys.stderr)

        # EoRR marker (Enhanced Route Refresh)
        if msg.get('type') == 'refresh' and msg.get('subtype') == 'eor':
            print(f"EoRR: Route refresh complete for {msg['afi']}/{msg['safi']}", file=sys.stderr)

    except json.JSONDecodeError:
        pass

Via Logging

Enable route refresh logging:

[exabgp.log]
level = INFO
packets = true
message = true

Log output:

INFO     Peer 192.168.1.1: Received ROUTE-REFRESH for ipv4/unicast
INFO     Peer 192.168.1.1: Sending ROUTE-REFRESH for ipv4/unicast
INFO     Peer 192.168.1.1: Enhanced Route Refresh BoRR received
INFO     Peer 192.168.1.1: Enhanced Route Refresh EoRR received

Verification Checklist

Verify Route Refresh capability:

# Check capability negotiation in logs
grep -i "route-refresh" /var/log/exabgp.log

# Verify capability in OPEN message
# Enable packet logging
[exabgp.log]
packets = true
level = DEBUG

Test route refresh:

#!/usr/bin/env python3
"""Test route refresh functionality"""
import sys
import time

# Request refresh
sys.stdout.write("announce route-refresh ipv4 unicast\n")
sys.stdout.flush()

print("Route refresh requested - check logs for UPDATE messages", file=sys.stderr)

Common Errors and Solutions

Error 1: Route Refresh Not Supported

Symptom: Route refresh request fails or ignored.

Logs:

WARNING  Peer 192.168.1.1: Route Refresh capability not negotiated
ERROR    Cannot send route refresh - capability not available

Causes:

  1. Peer does not support Route Refresh (RFC 2918)
  2. Capability not negotiated during OPEN
  3. ExaBGP capability disabled

Solutions:

# Ensure capability enabled in ExaBGP
neighbor 192.168.1.1 {
    capability {
        route-refresh;  # Enable explicitly
    }
}

# Verify peer configuration
# Cisco (should be default):
router bgp 65000
  neighbor 192.168.1.2 activate

# Check OPEN message for capability advertisement
# Capability Code: 2 (Route Refresh)

Error 2: AFI/SAFI Not Supported

Symptom: Route refresh for specific address family fails.

Logs:

ERROR    Route refresh failed: ipv6/unicast not negotiated

Causes:

  1. Address family not configured in family block
  2. Peer does not support requested AFI/SAFI
  3. Typo in AFI/SAFI name

Solutions:

# Ensure address family configured
neighbor 192.168.1.1 {
    family {
        ipv4 unicast;   # Must be configured
        ipv6 unicast;   # Must be configured
    }

    capability {
        route-refresh;
    }
}

# Verify API command syntax
# Correct:
announce route-refresh ipv4 unicast
announce route-refresh ipv6 unicast

# Incorrect:
announce route-refresh ipv4          # Missing SAFI
announce route-refresh ipv6-unicast  # Wrong format

Error 3: No Routes Received After Refresh

Symptom: Route refresh request sent but no UPDATE messages received.

Causes:

  1. Peer has no routes to advertise
  2. All routes filtered by peer's export policy
  3. Refresh request not received by peer

Solutions:

# Verify peer has routes
# Cisco:
show bgp ipv4 unicast neighbors 192.168.1.2 advertised-routes

# Juniper:
show route advertising-protocol bgp 192.168.1.2

# Check ExaBGP logs for UPDATE messages
tail -f /var/log/exabgp.log | grep UPDATE

# Enable debug logging
[exabgp.log]
level = DEBUG
packets = true
message = true

Error 4: Excessive Route Refresh Requests

Symptom: Too many route refresh requests causing instability.

Logs:

WARNING  Peer 192.168.1.1: Route refresh requested 10 times in 60 seconds

Causes:

  1. API process bug (refresh loop)
  2. Misconfigured automation
  3. Policy flapping

Solutions:

#!/usr/bin/env python3
"""Rate-limited route refresh"""
import sys
import time

class RateLimitedRefresh:
    def __init__(self, min_interval=60):
        self.min_interval = min_interval
        self.last_refresh = 0

    def request_refresh(self, afi, safi):
        """Request refresh with rate limiting"""
        now = time.time()

        if now - self.last_refresh < self.min_interval:
            remaining = self.min_interval - (now - self.last_refresh)
            print(f"Rate limit: wait {remaining:.0f}s before next refresh", file=sys.stderr)
            return False

        sys.stdout.write(f"announce route-refresh {afi} {safi}\n")
        sys.stdout.flush()

        self.last_refresh = now
        return True

# Use rate limiter
refresher = RateLimitedRefresh(min_interval=60)
refresher.request_refresh("ipv4", "unicast")

See Also


References

RFCs

ExaBGP Documentation

Implementation

  • Source Code: src/exabgp/bgp/message/refresh.py
  • Capability Type: 2 (Route Refresh)
  • Enhanced Capability Type: 70 (Enhanced Route Refresh)
  • ExaBGP Version: 3.x, 4.x, 5.x/main

πŸ‘» Ghost written by Claude (Anthropic AI)

Clone this wiki locally