Skip to content

ADD PATH

Thomas Mangin edited this page Nov 13, 2025 · 4 revisions

ADD-PATH

ADD-PATH is a BGP capability (RFC 7911) that allows a BGP speaker to advertise multiple paths for the same NLRI (Network Layer Reachability Information) without the need for additional BGP sessions. ExaBGP provides full ADD-PATH support for most address families, enabling path diversity, multipath load balancing, and improved convergence in BGP networks.

Table of Contents


What is ADD-PATH?

ADD-PATH (defined in RFC 7911) enables BGP to advertise multiple paths for the same network prefix to a single peer. Without ADD-PATH, BGP can only send one "best path" per prefix to each neighbor, even if multiple viable paths exist.

The Problem Without ADD-PATH

Traditional BGP behavior:

Route Reflector (RR)
       β”‚
       β”œβ”€ Receives 3 paths for 10.0.0.0/8:
       β”‚  1. via AS 100 (best path)
       β”‚  2. via AS 200
       β”‚  3. via AS 300
       β”‚
       └─ Advertises ONLY best path to clients:
          └─▢ Client 1: 10.0.0.0/8 via AS 100
          └─▢ Client 2: 10.0.0.0/8 via AS 100
          └─▢ Client 3: 10.0.0.0/8 via AS 100

Result: Clients lose path diversity!

Problems:

  • Path diversity loss: Clients only see RR's best path, not alternatives
  • Suboptimal routing: Client's best path may differ from RR's best path
  • BGP wedgies: Persistent routing loops in certain topologies
  • Slow convergence: Only one path available; failure requires full re-convergence

The Solution With ADD-PATH

With ADD-PATH enabled:

Route Reflector (RR) with ADD-PATH
       β”‚
       β”œβ”€ Receives 3 paths for 10.0.0.0/8:
       β”‚  1. via AS 100 (Path ID: 1)
       β”‚  2. via AS 200 (Path ID: 2)
       β”‚  3. via AS 300 (Path ID: 3)
       β”‚
       └─ Advertises ALL paths to clients:
          └─▢ Client 1: 10.0.0.0/8 via AS 100 (ID:1)
                        10.0.0.0/8 via AS 200 (ID:2)
                        10.0.0.0/8 via AS 300 (ID:3)

Result: Clients have full path visibility and diversity!

Benefits:

  • βœ… Path diversity preserved: Clients see multiple paths
  • βœ… Optimal routing: Each client selects its own best path
  • βœ… Faster convergence: Backup paths pre-installed
  • βœ… Load balancing: Multipath support for traffic engineering

How ADD-PATH Works

Path Identifier Encoding

ADD-PATH extends BGP UPDATE messages to include a Path Identifier for each NLRI:

Traditional BGP UPDATE:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Prefix: 10.0.0.0/8              β”‚
β”‚ Next-hop: 192.168.1.1           β”‚
β”‚ AS Path: 100 200                β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

ADD-PATH BGP UPDATE:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Path ID: 1                      β”‚  ← NEW: Unique identifier
β”‚ Prefix: 10.0.0.0/8              β”‚
β”‚ Next-hop: 192.168.1.1           β”‚
β”‚ AS Path: 100 200                β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Multiple paths for same prefix:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Path ID: 1                      β”‚
β”‚ Prefix: 10.0.0.0/8              β”‚
β”‚ Next-hop: 192.168.1.1           β”‚
β”‚ AS Path: 100 200                β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Path ID: 2                      β”‚
β”‚ Prefix: 10.0.0.0/8              β”‚
β”‚ Next-hop: 192.168.1.2           β”‚
β”‚ AS Path: 100 300                β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Key Points:

  • Path ID: 32-bit unsigned integer uniquely identifying the path
  • Per-prefix uniqueness: Path IDs must be unique per prefix, per peer
  • Opaque value: No semantic meaning; just an identifier
  • Sender-assigned: Advertising router assigns Path IDs

ADD-PATH Capability Negotiation

[Session Establishment with ADD-PATH]

ExaBGP                                  BGP Peer
   β”‚                                        β”‚
   │──── OPEN ─────────────────────────────▢│
   β”‚     Capabilities:                      β”‚
   β”‚     - ADD-PATH:                        β”‚
   β”‚       AFI: IPv4 (1)                    β”‚
   β”‚       SAFI: Unicast (1)                β”‚
   β”‚       Send/Receive: Both (3)           β”‚
   β”‚                                        β”‚
   │◀──── OPEN ─────────────────────────────│
   β”‚     Capabilities:                      β”‚
   β”‚     - ADD-PATH:                        β”‚
   β”‚       AFI: IPv4 (1)                    β”‚
   β”‚       SAFI: Unicast (1)                β”‚
   β”‚       Send/Receive: Both (3)           β”‚
   β”‚                                        β”‚
   │──── KEEPALIVE ────────────────────────▢│
   │◀──── KEEPALIVE ────────────────────────│
   β”‚                                        β”‚
   β”‚  ADD-PATH negotiated for IPv4 unicast  β”‚
   β”‚  Both peers can send/receive multiple  β”‚
   β”‚  paths per prefix                      β”‚

Send/Receive Values:

  • 1: Receive only
  • 2: Send only
  • 3: Send and Receive (both)

Per-AFI/SAFI: ADD-PATH is negotiated separately for each address family.


ExaBGP ADD-PATH Capabilities

ExaBGP provides full RFC 7911 ADD-PATH implementation:

βœ… Send/Receive Support:

  • Send multiple paths per prefix
  • Receive multiple paths per prefix
  • Send+Receive mode (bidirectional)

βœ… Supported Address Families:

  • βœ… IPv4 Unicast
  • βœ… IPv4 Multicast
  • βœ… IPv6 Unicast
  • βœ… IPv6 Multicast
  • βœ… VPNv4 (L3VPN)
  • βœ… VPNv6 (L3VPN)
  • βœ… IPv4 FlowSpec
  • βœ… IPv6 FlowSpec
  • βœ… VPLS

❌ Not Yet Supported:

  • ❌ EVPN (planned)
  • ❌ MVPN
  • ❌ Route Target Constraint (RTC)
  • ❌ BGP-LS

βœ… Path Identifier Management:

  • Automatic Path ID assignment
  • Manual Path ID specification via API
  • Per-prefix Path ID tracking

βœ… API Integration:

  • Advertise routes with Path IDs
  • Receive routes with Path IDs
  • Withdraw specific paths by ID

Implementation:

  • src/exabgp/bgp/message/open/capability/addpath.py
  • src/exabgp/bgp/neighbor.py (configuration)
  • NLRI encoding/decoding with Path IDs

RFC Compliance: RFC 7911 fully implemented


Why Use ADD-PATH?

Advantages

Benefit Without ADD-PATH With ADD-PATH
Path Diversity Only best path visible All paths visible
Routing Optimality RR's best path only Each router picks best
Convergence Single path; slow failover Multiple paths; fast failover
Load Balancing Limited (ECMP same AS-path) Flexible (multiple AS-paths)
BGP Wedgies Possible Prevented
Path Exploration Limited Full topology visibility

Use Cases Summary

  1. Route Reflector deployments: Preserve path diversity to clients
  2. Multipath load balancing: Advertise multiple equal-cost paths
  3. Fast convergence: Pre-install backup paths for sub-second failover
  4. Anycast services: Advertise from multiple locations simultaneously
  5. Path diversity for resilience: Multiple independent paths for redundancy

Configuration

Basic ADD-PATH Configuration

Enable ADD-PATH for IPv4 unicast (send and receive):

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

    capability {
        add-path send/receive;  # Enable ADD-PATH bidirectionally
    }

    family {
        ipv4 unicast;
    }

    api {
        processes [ announce-paths ];
    }
}

process announce-paths {
    run python3 /etc/exabgp/api/addpath.py;
    encoder text;
}

Notes:

  • send/receive: Bidirectional ADD-PATH (most common)
  • ADD-PATH applies to all configured families

Send-Only ADD-PATH

Only send multiple paths (don't receive):

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

    capability {
        add-path send;  # Send multiple paths only
    }

    family {
        ipv4 unicast;
    }
}

When to use:

  • ExaBGP advertises multiple paths (e.g., Route Reflector)
  • Peer only needs to receive paths, not send multiple

Receive-Only ADD-PATH

Only receive multiple paths (don't send):

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

    capability {
        add-path receive;  # Receive multiple paths only
    }

    family {
        ipv4 unicast;
    }
}

When to use:

  • ExaBGP receives multiple paths from peer
  • ExaBGP only sends single best path

Multiple Address Families

ADD-PATH for IPv4 and IPv6:

neighbor 2001:db8::1 {
    router-id 192.168.1.2;
    local-address 2001:db8::2;
    local-as 65001;
    peer-as 65000;

    capability {
        add-path send/receive;
    }

    family {
        ipv4 unicast;
        ipv6 unicast;
        ipv4 flow;  # FlowSpec also supports ADD-PATH
    }
}

Notes:

  • ADD-PATH negotiated per AFI/SAFI
  • Not all families support ADD-PATH (see limitations)

Disable ADD-PATH

Explicitly disable ADD-PATH:

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

    # No add-path directive = disabled (default)

    family {
        ipv4 unicast;
    }
}

API Usage

Advertise Routes with Path IDs (Text Format)

Announce multiple paths for the same prefix:

#!/usr/bin/env python3
"""Advertise multiple paths using ADD-PATH"""
import sys

def announce_multiple_paths():
    """Announce 10.0.0.0/8 via two different next-hops"""

    # Path 1: via 192.168.1.10 (Path ID: 1)
    route1 = "announce route 10.0.0.0/8 next-hop 192.168.1.10 path-information 1"
    sys.stdout.write(f"{route1}\n")
    sys.stdout.flush()

    # Path 2: via 192.168.1.20 (Path ID: 2)
    route2 = "announce route 10.0.0.0/8 next-hop 192.168.1.20 path-information 2"
    sys.stdout.write(f"{route2}\n")
    sys.stdout.flush()

    print("Announced 2 paths for 10.0.0.0/8", file=sys.stderr)

announce_multiple_paths()

Syntax:

announce route <prefix> next-hop <ip> path-information <path-id>

Key Points:

  • path-information <id>: Specifies the Path ID (32-bit unsigned integer)
  • Must be unique per prefix
  • If omitted, ExaBGP auto-assigns Path ID

Withdraw Specific Paths

Withdraw a specific path by Path ID:

#!/usr/bin/env python3
"""Withdraw specific path using ADD-PATH"""
import sys

def withdraw_specific_path():
    """Withdraw Path ID 1 for 10.0.0.0/8, keep Path ID 2"""

    # Withdraw only path with ID 1
    withdraw = "withdraw route 10.0.0.0/8 path-information 1"
    sys.stdout.write(f"{withdraw}\n")
    sys.stdout.flush()

    print("Withdrew path ID 1 (path ID 2 remains active)", file=sys.stderr)

withdraw_specific_path()

Without Path ID: Withdraws all paths for the prefix.

Anycast with Multiple Paths

Advertise same prefix from multiple locations:

#!/usr/bin/env python3
"""Anycast service with ADD-PATH"""
import sys

def announce_anycast_paths():
    """Announce 1.1.1.1/32 from 3 data centers"""

    paths = [
        ("1.1.1.1/32", "10.0.1.1", 1, "DC1"),
        ("1.1.1.1/32", "10.0.2.1", 2, "DC2"),
        ("1.1.1.1/32", "10.0.3.1", 3, "DC3"),
    ]

    for prefix, nexthop, path_id, dc in paths:
        route = f"announce route {prefix} next-hop {nexthop} path-information {path_id} community 65001:{path_id}"
        sys.stdout.write(f"{route}\n")
        sys.stdout.flush()
        print(f"Announced {prefix} from {dc} (Path ID {path_id})", file=sys.stderr)

announce_anycast_paths()

Receive Multiple Paths (JSON Format)

Process multiple received paths:

#!/usr/bin/env python3
"""Receive and process multiple paths via ADD-PATH"""
import sys
import json

def process_received_paths():
    """Process incoming routes with Path IDs"""

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

        try:
            msg = json.loads(line)

            if msg.get('type') == 'update':
                if 'announce' in msg:
                    for afi_safi, announcements in msg['announce'].items():
                        for nexthop, prefixes in announcements.items():
                            for prefix_data in prefixes:
                                prefix = prefix_data.get('nlri')
                                path_id = prefix_data.get('path-id')

                                print(f"Received: {prefix} via {nexthop} (Path ID: {path_id})", file=sys.stderr)

        except json.JSONDecodeError:
            pass

process_received_paths()

Load Balancing Across Multiple Paths

Use multiple paths for load balancing:

#!/usr/bin/env python3
"""ECMP load balancing with ADD-PATH"""
import sys

def announce_ecmp_paths():
    """Announce 4 equal-cost paths for load balancing"""

    prefix = "192.168.0.0/16"
    next_hops = [
        "10.0.1.1",
        "10.0.1.2",
        "10.0.1.3",
        "10.0.1.4",
    ]

    for idx, nexthop in enumerate(next_hops, start=1):
        route = (
            f"announce route {prefix} "
            f"next-hop {nexthop} "
            f"path-information {idx} "
            f"med 100 "  # Same MED for ECMP
            f"as-path [ 65001 ]"
        )
        sys.stdout.write(f"{route}\n")
        sys.stdout.flush()

    print(f"Announced {len(next_hops)} ECMP paths for {prefix}", file=sys.stderr)

announce_ecmp_paths()

Use Cases

1. Route Reflector with ADD-PATH

Scenario: Preserve path diversity in Route Reflector deployments.

Problem: Traditional RR only advertises best path to clients, hiding alternative paths.

Solution: Enable ADD-PATH on RR to advertise all paths to clients.

Architecture:

         Internet
            β”‚
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”
    β”‚       β”‚       β”‚
  PE1     PE2     PE3
    β”‚       β”‚       β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”˜
            β”‚
      Route Reflector
      (ExaBGP + ADD-PATH)
            β”‚
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”
    β”‚       β”‚       β”‚
  Client1 Client2 Client3

Without ADD-PATH: Clients see only RR's best path
With ADD-PATH: Clients see paths from PE1, PE2, PE3

Configuration (Route Reflector):

# RR advertises multiple paths to clients
neighbor 192.168.1.10 {  # Client 1
    router-id 192.168.1.254;
    local-address 192.168.1.254;
    local-as 65001;
    peer-as 65001;  # iBGP

    capability {
        add-path send/receive;
    }

    family {
        ipv4 unicast;
    }
}

Benefits:

  • Clients have full path visibility
  • Each client selects optimal path independently
  • No BGP wedgies
  • Faster convergence (backup paths pre-installed)

2. Multipath Load Balancing

Scenario: Advertise multiple paths for ECMP load balancing.

Example:

#!/usr/bin/env python3
"""Load balancing with ADD-PATH"""
import sys

def setup_load_balancing():
    """Advertise prefix via 4 next-hops for ECMP"""

    prefix = "10.0.0.0/8"
    uplinks = [
        ("10.1.1.1", 1, "ISP-1"),
        ("10.1.1.2", 2, "ISP-2"),
        ("10.1.1.3", 3, "ISP-3"),
        ("10.1.1.4", 4, "ISP-4"),
    ]

    for nexthop, path_id, isp in uplinks:
        route = (
            f"announce route {prefix} "
            f"next-hop {nexthop} "
            f"path-information {path_id} "
            f"as-path [ 65001 ]"
        )
        sys.stdout.write(f"{route}\n")
        sys.stdout.flush()

        print(f"Path {path_id}: {prefix} via {isp} ({nexthop})", file=sys.stderr)

setup_load_balancing()

Result: Traffic distributed across 4 uplinks using ECMP.

3. Fast Convergence with Backup Paths

Scenario: Pre-install backup paths for sub-second convergence.

Architecture:

Primary Path:   ExaBGP ──▢ Router A ──▢ Destination
Backup Path:    ExaBGP ──▢ Router B ──▢ Destination

ADD-PATH: Both paths advertised simultaneously
Router pre-installs both, uses primary, switches to backup instantly on failure

Example:

#!/usr/bin/env python3
"""Primary + backup paths with ADD-PATH"""
import sys
import time
import subprocess

def check_primary_health():
    """Check if primary path is healthy"""
    try:
        result = subprocess.run(
            ["ping", "-c", "1", "-W", "1", "10.0.1.1"],
            capture_output=True
        )
        return result.returncode == 0
    except:
        return False

def announce_paths_with_failover():
    """Announce primary + backup, adjust weights based on health"""

    primary_healthy = check_primary_health()

    if primary_healthy:
        # Primary: lower MED (preferred)
        primary = "announce route 192.168.0.0/16 next-hop 10.0.1.1 path-information 1 med 50"
        backup = "announce route 192.168.0.0/16 next-hop 10.0.2.1 path-information 2 med 100"
    else:
        # Primary failed: set higher MED
        primary = "announce route 192.168.0.0/16 next-hop 10.0.1.1 path-information 1 med 200"
        backup = "announce route 192.168.0.0/16 next-hop 10.0.2.1 path-information 2 med 50"

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

# Monitor and adjust
while True:
    announce_paths_with_failover()
    time.sleep(10)

4. Anycast Service Advertisement

Scenario: Advertise anycast IP from multiple PoPs.

Example:

#!/usr/bin/env python3
"""Anycast DNS with ADD-PATH"""
import sys

def announce_anycast_dns():
    """Announce 8.8.8.8 from 3 geographic locations"""

    anycast_ip = "8.8.8.8/32"
    pops = [
        ("10.1.1.1", 1, "US-East", 65001),
        ("10.2.1.1", 2, "EU-West", 65002),
        ("10.3.1.1", 3, "APAC", 65003),
    ]

    for nexthop, path_id, location, asn in pops:
        route = (
            f"announce route {anycast_ip} "
            f"next-hop {nexthop} "
            f"path-information {path_id} "
            f"as-path [ {asn} ] "
            f"community 65000:{path_id}"
        )
        sys.stdout.write(f"{route}\n")
        sys.stdout.flush()

        print(f"Anycast {anycast_ip} from {location} (Path {path_id})", file=sys.stderr)

announce_anycast_dns()

Result: All 3 paths advertised; routers select nearest PoP.


Path Identifiers

Path ID Assignment

Automatic assignment (ExaBGP assigns IDs):

# No path-information specified - ExaBGP auto-assigns
sys.stdout.write("announce route 10.0.0.0/8 next-hop 192.168.1.1\n")

Manual assignment (explicit Path ID):

# Manually specify Path ID
sys.stdout.write("announce route 10.0.0.0/8 next-hop 192.168.1.1 path-information 42\n")

Path ID Requirements

Per RFC 7911:

  • 32-bit unsigned integer (0 to 4,294,967,295)
  • Unique per prefix, per peer (not globally unique)
  • Opaque value (no semantic meaning)
  • Sender-assigned (advertising router controls)

Best Practices:

  • Use sequential IDs (1, 2, 3, ...) for simplicity
  • Use meaningful IDs for debugging (e.g., PoP number)
  • Avoid Path ID 0 (some implementations treat as invalid)
  • Document Path ID scheme

Path ID Persistence

Path IDs must remain stable for a given path:

  • Don't change Path ID unless path changes
  • Consistent IDs across restarts (if possible)
  • Withdraw old ID before announcing new ID for same prefix

Monitoring and Verification

Via API (JSON Format)

Monitor received paths with Path IDs:

#!/usr/bin/env python3
"""Monitor ADD-PATH routes"""
import sys
import json

paths_per_prefix = {}

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

    try:
        msg = json.loads(line)

        if msg.get('type') == 'update':
            # Process announcements
            if 'announce' in msg:
                for afi_safi, announcements in msg['announce'].items():
                    for nexthop, prefixes in announcements.items():
                        for prefix_data in prefixes:
                            prefix = prefix_data.get('nlri')
                            path_id = prefix_data.get('path-id', 'N/A')

                            if prefix not in paths_per_prefix:
                                paths_per_prefix[prefix] = []
                            paths_per_prefix[prefix].append(path_id)

                            print(f"ADD-PATH: {prefix} via {nexthop} (ID: {path_id})", file=sys.stderr)

            # Process withdrawals
            if 'withdraw' in msg:
                for afi_safi, withdrawals in msg['withdraw'].items():
                    for prefix_data in withdrawals:
                        prefix = prefix_data.get('nlri')
                        path_id = prefix_data.get('path-id', 'N/A')

                        print(f"WITHDRAW: {prefix} Path ID {path_id}", file=sys.stderr)

        # Periodically report paths per prefix
        for prefix, path_ids in paths_per_prefix.items():
            if len(path_ids) > 1:
                print(f"Multi-path: {prefix} has {len(path_ids)} paths: {path_ids}", file=sys.stderr)

    except json.JSONDecodeError:
        pass

Via Logging

Enable ADD-PATH logging:

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

Log output:

INFO     Peer 192.168.1.1: ADD-PATH capability negotiated (send/receive)
INFO     Peer 192.168.1.1: Received UPDATE with Path ID 1 for 10.0.0.0/8
INFO     Peer 192.168.1.1: Received UPDATE with Path ID 2 for 10.0.0.0/8
INFO     Peer 192.168.1.1: 2 paths for 10.0.0.0/8

Verification Checklist

Verify ADD-PATH negotiation:

# Check capability in logs
grep -i "add-path" /var/log/exabgp.log

# Verify Path IDs in UPDATE messages
grep "Path ID" /var/log/exabgp.log

Test multiple path advertisement:

#!/usr/bin/env python3
import sys

# Announce 2 paths
sys.stdout.write("announce route 10.0.0.0/8 next-hop 192.168.1.1 path-information 1\n")
sys.stdout.write("announce route 10.0.0.0/8 next-hop 192.168.1.2 path-information 2\n")
sys.stdout.flush()

# Verify peer receives both paths

Limitations and Considerations

ExaBGP Limitations

  1. Address Family Support

    • ❌ EVPN: Not yet supported (planned)
    • ❌ MVPN: Not yet supported
    • ❌ BGP-LS: Not yet supported
    • βœ… All other families supported
  2. Path ID Management

    • User must track Path IDs in API processes
    • No automatic Path ID deduplication
    • Path ID scope limited to per-prefix, per-peer

General ADD-PATH Considerations

  1. Memory Overhead

    • Multiple paths stored per prefix
    • More routes in RIB (N paths Γ— M prefixes)
    • Consider memory capacity
  2. CPU Overhead

    • More UPDATE messages to process
    • Path selection runs for each path
    • Consider CPU capacity for high path counts
  3. Peer Support

    • Both peers must negotiate ADD-PATH capability
    • Verify vendor support and limitations
    • Test before production deployment
  4. Path Explosion

    • Large networks: many paths per prefix possible
    • Limit paths advertised (e.g., best N paths)
    • Monitor path counts

Best Practices

βœ… Do:

  • Start with send/receive mode for flexibility
  • Use meaningful Path IDs for debugging
  • Monitor path counts per prefix
  • Test with small scale before full deployment
  • Document Path ID assignment scheme

❌ Don't:

  • Advertise unlimited paths (memory/CPU risk)
  • Change Path IDs unnecessarily
  • Assume all vendors support all features
  • Deploy without testing peer behavior

Common Errors and Solutions

Error 1: ADD-PATH Not Negotiated

Symptom: ADD-PATH configured but not negotiated.

Logs:

WARNING  Peer 192.168.1.1: ADD-PATH capability not negotiated

Causes:

  1. Peer does not support ADD-PATH (RFC 7911)
  2. Peer has ADD-PATH disabled
  3. AFI/SAFI mismatch

Solutions:

# Verify ExaBGP configuration
neighbor 192.168.1.1 {
    capability {
        add-path send/receive;  # Ensure enabled
    }

    family {
        ipv4 unicast;  # Must match peer
    }
}

# Check peer configuration
# Cisco:
router bgp 65000
  neighbor 192.168.1.2 activate
  address-family ipv4 unicast
    neighbor 192.168.1.2 advertise additional-paths all

# Juniper:
protocols {
    bgp {
        group peers {
            family inet {
                unicast {
                    add-path {
                        receive;
                        send {
                            path-count 6;
                        }
                    }
                }
            }
        }
    }
}

Error 2: Duplicate Path IDs

Symptom: Same Path ID used for different paths to same prefix.

Causes:

  1. API process bug
  2. Path ID not updated on path change
  3. Race condition in Path ID assignment

Solutions:

#!/usr/bin/env python3
"""Properly manage Path IDs"""
import sys

class PathManager:
    def __init__(self):
        self.next_path_id = 1
        self.prefix_paths = {}  # prefix -> {nexthop: path_id}

    def announce_path(self, prefix, nexthop):
        """Announce path with unique Path ID"""

        if prefix not in self.prefix_paths:
            self.prefix_paths[prefix] = {}

        if nexthop in self.prefix_paths[prefix]:
            # Re-use existing Path ID
            path_id = self.prefix_paths[prefix][nexthop]
        else:
            # Assign new Path ID
            path_id = self.next_path_id
            self.next_path_id += 1
            self.prefix_paths[prefix][nexthop] = path_id

        route = f"announce route {prefix} next-hop {nexthop} path-information {path_id}"
        sys.stdout.write(f"{route}\n")
        sys.stdout.flush()

        return path_id

    def withdraw_path(self, prefix, nexthop):
        """Withdraw specific path"""

        if prefix in self.prefix_paths and nexthop in self.prefix_paths[prefix]:
            path_id = self.prefix_paths[prefix][nexthop]
            withdraw = f"withdraw route {prefix} path-information {path_id}"
            sys.stdout.write(f"{withdraw}\n")
            sys.stdout.flush()

            del self.prefix_paths[prefix][nexthop]

# Use PathManager to avoid duplicate Path IDs
pm = PathManager()
pm.announce_path("10.0.0.0/8", "192.168.1.1")
pm.announce_path("10.0.0.0/8", "192.168.1.2")

Error 3: Path Not Withdrawn

Symptom: Withdrawn path still appears at peer.

Causes:

  1. Path ID not specified in withdraw
  2. Incorrect Path ID
  3. Peer issue

Solutions:

#!/usr/bin/env python3
"""Correct path withdrawal with Path ID"""
import sys

# INCORRECT: Withdraws all paths for prefix
sys.stdout.write("withdraw route 10.0.0.0/8\n")
sys.stdout.flush()

# CORRECT: Withdraws specific path by ID
sys.stdout.write("withdraw route 10.0.0.0/8 path-information 1\n")
sys.stdout.flush()

# Verify Path ID matches announced path
# Announce:  path-information 42
# Withdraw:  path-information 42  (must match!)

Error 4: Too Many Paths

Symptom: Memory or CPU exhaustion from too many paths.

Causes:

  1. No limit on paths per prefix
  2. Path explosion in large network
  3. API process announcing excessive paths

Solutions:

#!/usr/bin/env python3
"""Limit paths per prefix"""
import sys

class LimitedPathManager:
    MAX_PATHS_PER_PREFIX = 4  # Limit to 4 paths

    def __init__(self):
        self.prefix_paths = {}  # prefix -> [(nexthop, path_id), ...]

    def announce_path(self, prefix, nexthop, path_id):
        """Announce path with limit enforcement"""

        if prefix not in self.prefix_paths:
            self.prefix_paths[prefix] = []

        # Check limit
        if len(self.prefix_paths[prefix]) >= self.MAX_PATHS_PER_PREFIX:
            # Remove oldest path
            old_nexthop, old_path_id = self.prefix_paths[prefix].pop(0)
            withdraw = f"withdraw route {prefix} path-information {old_path_id}"
            sys.stdout.write(f"{withdraw}\n")
            sys.stdout.flush()
            print(f"Removed oldest path (ID {old_path_id}) due to limit", file=sys.stderr)

        # Announce new path
        self.prefix_paths[prefix].append((nexthop, path_id))
        route = f"announce route {prefix} next-hop {nexthop} path-information {path_id}"
        sys.stdout.write(f"{route}\n")
        sys.stdout.flush()

# Enforce path limit
lpm = LimitedPathManager()

See Also


References

RFCs

ExaBGP Documentation

Implementation

  • Source Code: src/exabgp/bgp/message/open/capability/addpath.py
  • Capability Type: 69 (ADD-PATH)
  • ExaBGP Version: 4.x, 5.x/main

Vendor Documentation


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

Clone this wiki locally