Skip to content

Text API Reference

Thomas Mangin edited this page Nov 23, 2025 · 12 revisions

Text API Reference

Complete reference for ExaBGP's text-based API commands


Table of Contents


Overview

The text encoder uses human-readable commands for announcing and withdrawing routes.

Configuration:

process my-program {
    run /etc/exabgp/api/announce.py;
    encoder text;
}

Usage in your program:

import sys

# Announce a route
sys.stdout.write("announce route 100.10.0.0/24 next-hop self\n")
sys.stdout.flush()  # CRITICAL - always flush!

# Withdraw a route
sys.stdout.write("withdraw route 100.10.0.0/24\n")
sys.stdout.flush()

Command Syntax

Basic Structure

<action> <family> <prefix> [attributes]

Actions:

  • announce - Add/update a route
  • withdraw - Remove a route

Families:

  • route - IPv4/IPv6 unicast
  • flow - FlowSpec
  • vpn - L3VPN
  • evpn - EVPN

Critical Rules

  1. Always end with newline (\n)
  2. Always flush after write (sys.stdout.flush())
  3. One command per line
  4. Case-sensitive (announce not ANNOUNCE)

Neighbor Selectors

When ExaBGP is configured with multiple BGP neighbors, you can use neighbor selectors to target specific peers with your API commands.

Syntax

neighbor <ip> [selector-options] <command>

By default, commands apply to all configured neighbors. Use the neighbor keyword to target specific peer(s) instead of broadcasting to all neighbors.

Available Selectors

Selector Description Example
neighbor <ip> Required: Target neighbor by IP neighbor 127.0.0.1
local-ip <ip> Filter by local IP address local-ip 192.168.1.2
local-as <asn> Filter by local ASN local-as 65001
peer-as <asn> Filter by peer ASN peer-as 65000
router-id <id> Filter by router ID router-id 192.168.1.1
family-allowed <families> Filter by address family capabilities family-allowed ipv4-unicast/ipv6-unicast

Examples

Single neighbor:

# Announce to one specific neighbor
print("neighbor 127.0.0.1 announce route 10.0.0.0/24 next-hop 192.168.1.1")

All neighbors (wildcard):

# Announce to ALL configured neighbors
print("neighbor * announce route 10.0.0.0/24 next-hop self")

Multiple neighbors (comma-separated):

# Announce to specific set of neighbors
print("neighbor 10.0.0.1,10.0.0.2,10.0.0.3 announce route 100.10.0.0/24 next-hop self")

With peer-as filter:

# Only announce to neighbors with peer-as 65000
print("neighbor 127.0.0.1 peer-as 65000 announce route 10.0.0.0/24 next-hop self")

With local-as filter:

# Differentiate when multiple neighbors share same IP but different local-as
print("neighbor 127.0.0.1 local-as 65001 announce route 10.0.0.0/24 next-hop self")

Multiple selectors:

# Combine multiple selectors (AND logic)
print("neighbor 127.0.0.1 local-as 65001 peer-as 65000 announce route 10.0.0.0/24 next-hop self")

Filter by address family capabilities:

# Target neighbors with FlowSpec support
print("neighbor * family-allowed ipv4-flowspec announce flow route { match { destination 10.0.0.0/8; } then { discard; } }")

# Target multi-session neighbors with dual-stack
print("neighbor * family-allowed ipv4-unicast/ipv6-unicast announce route 10.0.0.0/24 next-hop self")

# Target single-session neighbors
print("neighbor * family-allowed in-open announce route 10.0.0.0/24 next-hop self")

Note: The family-allowed value is:

  • in-open for single-session neighbors (default - all families in BGP OPEN)
  • Slash-separated families for multi-session neighbors (e.g., ipv4-unicast/ipv6-unicast/ipv4-flowspec)

Complete Example

#!/usr/bin/env python3
"""
selective_announcement.py - Announce routes to specific neighbors
"""
import sys
import time

time.sleep(2)  # Wait for BGP sessions

# Announce to all neighbors
sys.stdout.write("neighbor * announce route 100.10.0.0/24 next-hop self\n")
sys.stdout.flush()

# Announce specific route to peer in AS 65000
sys.stdout.write("neighbor * peer-as 65000 announce route 100.20.0.0/24 next-hop self\n")
sys.stdout.flush()

# Announce with AS path prepending to one neighbor
sys.stdout.write("neighbor 192.168.1.1 announce route 100.30.0.0/24 next-hop self as-path [65001 65001 65001]\n")
sys.stdout.flush()

# Announce different routes to different providers
sys.stdout.write("neighbor 10.0.1.1 announce route 100.40.0.0/24 next-hop self community [65001:100]\n")  # Provider A
sys.stdout.flush()

sys.stdout.write("neighbor 10.0.2.1 announce route 100.40.0.0/24 next-hop self community [65001:200]\n")  # Provider B
sys.stdout.flush()

while True:
    time.sleep(60)

FlowSpec with Neighbor Selectors

# Announce FlowSpec rule to specific neighbor
print("neighbor 192.168.1.1 announce flow route { match { destination 10.0.0.0/8; } then { discard; } }")

# Announce FlowSpec to all neighbors
print("neighbor * announce flow route { match { source 203.0.113.0/24; destination-port =80; protocol =tcp; } then { discard; } }")

# Target specific peer-as for DDoS mitigation
print("neighbor * peer-as 65000 announce flow route { match { destination 100.10.0.0/24; } then { rate-limit 1000000; } }")

Withdraw with Neighbor Selectors

# Withdraw from specific neighbor
print("neighbor 192.168.1.1 withdraw route 10.0.0.0/24")

# Withdraw from all neighbors
print("neighbor * withdraw route 10.0.0.0/24")

# Withdraw from multiple neighbors
print("neighbor 10.0.0.1,10.0.0.2 withdraw route 100.10.0.0/24")

Important Notes

  • πŸ’‘ Default behavior: Commands without neighbor selector apply to all configured neighbors
  • πŸ’‘ Selective targeting: Use neighbor keyword to target specific peer(s)
  • πŸ’‘ Selectors use AND logic - all specified selectors must match
  • πŸ’‘ neighbor * explicitly targets all configured neighbors (same as omitting selector)
  • πŸ’‘ If no neighbor matches your selectors, the command is silently ignored
  • πŸ’‘ Comma-separated IPs are treated as OR - any match succeeds

IPv4/IPv6 Unicast

IPv4 Unicast

Basic Announcement

IPv4:

# IPv4 prefix with explicit next-hop
print("announce route 100.10.0.0/24 next-hop 192.0.2.1")

# /32 host route
print("announce route 100.10.0.100/32 next-hop 192.0.2.1")

IPv6 Unicast

IPv6:

# IPv6 prefix
print("announce route 2001:db8::/32 next-hop 2001:db8::1")

# IPv6 /128 host route
print("announce route 2001:db8::100/128 next-hop 2001:db8::1")

Withdrawal

# IPv4
print("withdraw route 100.10.0.0/24")

# IPv6
print("withdraw route 2001:db8::/32")

# Note: No need to specify next-hop or attributes when withdrawing

Route Attributes

Next-Hop

⚠️ Warning: next-hop self is an EXPERIMENTAL feature. Always use explicit next-hop IP addresses in production.

Explicit next-hop (recommended):

print("announce route 100.10.0.0/24 next-hop 192.0.2.1")
# Use the appropriate next-hop IP for your network
print("announce route 2001:db8::/32 next-hop 2001:db8::1")

Third-party next-hop:

# Tell peer to use different next-hop
print("announce route 100.10.0.0/24 next-hop 10.0.0.1")

MED (Multi-Exit Discriminator)

Lower MED is preferred. Use for traffic engineering.

# Preferred path (lower MED)
print("announce route 100.10.0.0/24 next-hop self med 50")

# Backup path (higher MED)
print("announce route 100.10.0.0/24 next-hop self med 100")

Local Preference

Higher local-preference is preferred. iBGP only.

# Preferred path (higher local-pref)
print("announce route 100.10.0.0/24 next-hop self local-preference 200")

# Less preferred
print("announce route 100.10.0.0/24 next-hop self local-preference 100")

AS Path

Normal announcement:

print("announce route 100.10.0.0/24 next-hop self")
# AS path: [ 65001 ] (your local-as)

AS path prepending:

# Prepend your AS twice
print("announce route 100.10.0.0/24 next-hop self as-path [ 65001 65001 65001 ]")
# AS path becomes: 65001 65001 65001 (looks like longer path)

Custom AS path:

# Announce as if from different AS
print("announce route 100.10.0.0/24 next-hop self as-path [ 65002 65003 ]")

Communities

Standard communities (RFC 1997):

# Single community
print("announce route 100.10.0.0/24 next-hop self community [ 65001:100 ]")

# Multiple communities
print("announce route 100.10.0.0/24 next-hop self community [ 65001:100 65001:200 ]")

# Well-known communities
print("announce route 100.10.0.0/24 next-hop self community [ no-export ]")
print("announce route 100.10.0.0/24 next-hop self community [ no-advertise ]")

Well-known communities:

  • no-export (0xFFFFFF01) - Don't advertise to eBGP peers
  • no-advertise (0xFFFFFF02) - Don't advertise to any peer
  • no-export-subconfed (0xFFFFFF03) - Don't export outside confederation

Extended communities (RFC 4360):

# Route target
print("announce route 100.10.0.0/24 next-hop self extended-community [ target:65001:100 ]")

# Route origin
print("announce route 100.10.0.0/24 next-hop self extended-community [ origin:65001:100 ]")

# Multiple
print("announce route 100.10.0.0/24 next-hop self extended-community [ target:65001:100 target:65001:200 ]")

Large communities (RFC 8092):

# Format: global:local1:local2
print("announce route 100.10.0.0/24 next-hop self large-community [ 65001:100:200 ]")

Origin

# IGP origin (default)
print("announce route 100.10.0.0/24 next-hop self origin igp")

# EGP origin
print("announce route 100.10.0.0/24 next-hop self origin egp")

# Incomplete origin
print("announce route 100.10.0.0/24 next-hop self origin incomplete")

Combining Attributes

# Complex announcement with multiple attributes
print("announce route 100.10.0.0/24 next-hop self "
      "med 100 "
      "local-preference 200 "
      "community [ 65001:100 65001:200 ] "
      "as-path [ 65001 ]")

Bulk Announcements (announce attributes ... nlri)

Version Availability: ExaBGP 4.0+

Purpose: Announce multiple prefixes with the same attributes in a single command, reducing parsing overhead and improving performance.

Syntax:

announce attribute[s] <attributes> nlri <prefix1> <prefix2> <prefix3> ...
announce attributes <attributes> nlri <prefix1> <prefix2> ...

Both singular attribute and plural attributes work identically.

Basic Usage

Announce multiple prefixes with same next-hop and MED:

# Two prefixes with same attributes
print("announce attributes med 100 next-hop 101.1.101.1 nlri 1.0.0.1/32 1.0.0.2/32")

Multiple prefixes with AS-PATH and local-preference:

print("announce attributes local-preference 200 as-path [ 1 2 3 4 ] next-hop 202.2.202.2 nlri 2.0.0.1/32 2.0.0.2/32")

Advanced: VPN with Labels

# L3VPN with route-distinguisher and label
print("announce attribute route-distinguisher 63333:100 "
      "label [ 110 ] "
      "next-hop 10.0.99.12 "
      "origin igp "
      "as-path [100, 500] "
      "local-preference 100 "
      "extended-community 0:0 "
      "originator-id 10.0.99.12 "
      "nlri 128.0.64.0/18 128.0.0.0/18")

Bulk Announcements (Hundreds of Prefixes)

Efficient for announcing many prefixes:

#!/usr/bin/env python3
import sys

# Build list of prefixes
prefixes = []
for ip in range(0, 224):
    prefixes.append(f"{ip}.0.0.0/8")

# Announce all prefixes with same attributes in ONE command
nlri_list = ' '.join(prefixes)
command = f"announce attribute next-hop 1.2.3.4 med 100 as-path [ 100 101 102 ] nlri {nlri_list}"

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

This announces 224 prefixes in a single command instead of 224 separate announce route commands.

Performance Benefits

Without bulk announcement (slow):

# 1000 separate commands
for i in range(1000):
    print(f"announce route 10.{i//256}.{i%256}.0/24 next-hop 192.0.2.1 med 100")
    sys.stdout.flush()

With bulk announcement (fast):

# 1 command for 1000 prefixes
prefixes = [f"10.{i//256}.{i%256}.0/24" for i in range(1000)]
print(f"announce attributes next-hop 192.0.2.1 med 100 nlri {' '.join(prefixes)}")
sys.stdout.flush()

Performance gain: ~10-100x faster for bulk operations (reduces parsing overhead, fewer command round-trips).

Withdrawal (Bulk)

Syntax:

withdraw attribute[s] <attributes> nlri <prefix1> <prefix2> ...

Withdraw multiple prefixes:

# Withdraw multiple prefixes
print("withdraw attributes nlri 1.0.0.1/32 1.0.0.2/32")

With next-hop (if required):

# Some configurations require next-hop for withdrawal
print("withdraw attributes next-hop 101.1.101.1 nlri 2.0.0.1/32 2.0.0.2/32")

When to Use Bulk Announcements

βœ… Use bulk announcements when:

  • Announcing 10+ prefixes with identical attributes
  • Building initial full BGP table (thousands of routes)
  • Performance-critical applications
  • High route churn environments

❌ Use individual announce route when:

  • Prefixes have different attributes
  • Announcing 1-5 routes
  • Attributes differ per prefix
  • Debugging (easier to see individual commands)

Examples

Example 1: Anycast service announcement:

# Announce service IPs from multiple PoPs with same attributes
service_ips = [
    "100.64.1.1/32",
    "100.64.1.2/32",
    "100.64.1.3/32",
    "100.64.1.4/32",
]

print(f"announce attributes next-hop self community [ 65001:100 ] nlri {' '.join(service_ips)}")

Example 2: DDoS blackhole (bulk):

# Blackhole multiple attacking IPs
attackers = [
    "203.0.113.10/32",
    "203.0.113.20/32",
    "203.0.113.30/32",
]

# Blackhole community + no-export
print(f"announce attributes next-hop 192.0.2.1 community [ 65001:666 no-export ] nlri {' '.join(attackers)}")

Example 3: Load balancer pool (dynamic):

# Announce healthy backends only
healthy_backends = check_health()  # Returns list of IPs

if healthy_backends:
    prefixes = [f"{ip}/32" for ip in healthy_backends]
    print(f"announce attributes next-hop self local-preference 200 nlri {' '.join(prefixes)}")

Common Errors

❌ Wrong: Mixing attributes per prefix

# This does NOT work - all prefixes get same attributes
print("announce attributes next-hop 1.2.3.4 nlri 10.0.0.0/24 next-hop 5.6.7.8 20.0.0.0/24")
# ERROR: Syntax error

βœ… Correct: Use separate commands for different attributes

# Different attributes = different commands
print("announce route 10.0.0.0/24 next-hop 1.2.3.4")
print("announce route 20.0.0.0/24 next-hop 5.6.7.8")

❌ Wrong: Forgetting nlri keyword

# Missing 'nlri' keyword
print("announce attributes next-hop 1.2.3.4 10.0.0.0/24 20.0.0.0/24")
# ERROR: Will fail to parse

βœ… Correct: Include nlri keyword

print("announce attributes next-hop 1.2.3.4 nlri 10.0.0.0/24 20.0.0.0/24")

Compatibility

Feature ExaBGP 3.x ExaBGP 4.x ExaBGP 5.x
announce attribute[s] ... nlri ❌ Not available βœ… Available βœ… Available
withdraw attribute[s] ... nlri ❌ Not available βœ… Available βœ… Available

Migration note: If upgrading from 3.x, you can now use bulk announcements to improve performance.


FlowSpec (IPv4/IPv6)

FlowSpec allows BGP-based traffic filtering. ExaBGP pioneered open-source FlowSpec support (now also in GoBGP, FRRouting, BIRD).

Basic Structure

announce flow route {
    match {
        <match-conditions>
    }
    then {
        <actions>
    }
}

Match Conditions

Destination prefix:

print("announce flow route { "
      "match { destination 100.10.0.0/24; } "
      "then { discard; } "
      "}")

Source prefix:

print("announce flow route { "
      "match { source 10.0.0.0/8; } "
      "then { discard; } "
      "}")

Ports:

# Destination port
print("announce flow route { "
      "match { destination-port =80; } "
      "then { discard; } "
      "}")

# Source port
print("announce flow route { "
      "match { source-port =1234; } "
      "then { discard; } "
      "}")

# Port range
print("announce flow route { "
      "match { destination-port >=1024&<=65535; } "
      "then { discard; } "
      "}")

Port operators:

  • =80 - Equals 80
  • >1023 - Greater than 1023
  • <1024 - Less than 1024
  • >=1024 - Greater than or equal
  • <=65535 - Less than or equal
  • >=1024&<=65535 - Range (AND)

Protocol:

# TCP (6)
print("announce flow route { "
      "match { protocol =tcp; } "
      "then { discard; } "
      "}")

# UDP (17)
print("announce flow route { "
      "match { protocol =udp; } "
      "then { discard; } "
      "}")

# ICMP (1)
print("announce flow route { "
      "match { protocol =icmp; } "
      "then { discard; } "
      "}")

# Numeric
print("announce flow route { "
      "match { protocol =6; } "
      "then { discard; } "
      "}")

TCP flags:

# SYN flag set
print("announce flow route { "
      "match { tcp-flags [ syn ]; } "
      "then { discard; } "
      "}")

# SYN+ACK
print("announce flow route { "
      "match { tcp-flags [ syn ack ]; } "
      "then { discard; } "
      "}")

# All flags
print("announce flow route { "
      "match { tcp-flags [ fin syn rst psh ack urg ]; } "
      "then { discard; } "
      "}")

Packet length:

print("announce flow route { "
      "match { packet-length >=1000; } "
      "then { discard; } "
      "}")

ICMP type/code:

# ICMP echo request (type 8)
print("announce flow route { "
      "match { icmp-type =8; } "
      "then { discard; } "
      "}")

# ICMP echo reply (type 0)
print("announce flow route { "
      "match { icmp-type =0; icmp-code =0; } "
      "then { discard; } "
      "}")

DSCP (Differentiated Services Code Point):

print("announce flow route { "
      "match { dscp =46; } "
      "then { discard; } "
      "}")

Fragment:

# Fragmented packets
print("announce flow route { "
      "match { fragment [ is-fragment ]; } "
      "then { discard; } "
      "}")

# First fragment
print("announce flow route { "
      "match { fragment [ first-fragment ]; } "
      "then { discard; } "
      "}")

# Not a fragment
print("announce flow route { "
      "match { fragment [ not-a-fragment ]; } "
      "then { discard; } "
      "}")

Multiple Match Conditions (AND logic)

# Block SYN floods to port 80 from 10.0.0.0/8
print("announce flow route { "
      "match { "
        "source 10.0.0.0/8; "
        "destination-port =80; "
        "protocol =tcp; "
        "tcp-flags [ syn ]; "
      "} "
      "then { discard; } "
      "}")

Actions

Discard (drop):

print("announce flow route { "
      "match { destination 100.10.0.0/24; } "
      "then { discard; } "
      "}")

Rate-limit:

# Limit to 1 Mbps (1000000 bytes/sec)
print("announce flow route { "
      "match { destination 100.10.0.0/24; } "
      "then { rate-limit 1000000; } "
      "}")

# Limit to 10 Mbps
print("announce flow route { "
      "match { source 10.0.0.0/8; } "
      "then { rate-limit 10000000; } "
      "}")

Redirect to VRF:

print("announce flow route { "
      "match { destination 100.10.0.0/24; } "
      "then { redirect 65001:100; } "
      "}")

Mark (DSCP remarking):

print("announce flow route { "
      "match { destination 100.10.0.0/24; } "
      "then { mark 46; } "
      "}")

Community (tagging):

print("announce flow route { "
      "match { destination 100.10.0.0/24; } "
      "then { community [ 65001:666 ]; } "
      "}")

FlowSpec Withdrawal

# Withdraw by match conditions
print("withdraw flow route { "
      "match { destination 100.10.0.0/24; } "
      "}")

FlowSpec Examples

Block DDoS attack:

# Block UDP flood from 10.0.0.0/8 to DNS port
print("announce flow route { "
      "match { "
        "source 10.0.0.0/8; "
        "destination-port =53; "
        "protocol =udp; "
      "} "
      "then { discard; } "
      "}")

Rate-limit HTTP traffic:

# Rate-limit HTTP traffic (100 MB/sec = bytes/sec per RFC 5575)
print("announce flow route { "
      "match { "
        "destination-port =80; "
        "protocol =tcp; "
      "} "
      "then { rate-limit 100000000; } "
      "}")

Block large packets (possible attack):

# Drop packets > 1500 bytes
print("announce flow route { "
      "match { packet-length >1500; } "
      "then { discard; } "
      "}")

L3VPN (VPNv4/VPNv6)

Layer 3 VPN (RFC 4364) for MPLS VPNs.

Announcement

# Basic L3VPN route
print("announce vpn 100.10.0.0/24 "
      "next-hop self "
      "route-distinguisher 65001:100 "
      "label 10000")

# With route target
print("announce vpn 100.10.0.0/24 "
      "next-hop self "
      "route-distinguisher 65001:100 "
      "extended-community [ target:65001:100 ] "
      "label 10000")

# Multiple route targets (export to multiple VRFs)
print("announce vpn 100.10.0.0/24 "
      "next-hop self "
      "route-distinguisher 65001:100 "
      "extended-community [ target:65001:100 target:65001:200 ] "
      "label 10000")

Withdrawal

print("withdraw vpn 100.10.0.0/24 route-distinguisher 65001:100")

EVPN

Ethernet VPN (RFC 7432) for data center fabrics.

EVPN Route Type 2 (MAC/IP Advertisement)

MAC only:

print("announce evpn mac-ip "
      "aa:bb:cc:dd:ee:ff "
      "route-distinguisher 65001:100 "
      "next-hop self "
      "label [ 10000 ] "
      "extended-community [ target:65001:100 ]")

MAC + IP:

print("announce evpn mac-ip "
      "aa:bb:cc:dd:ee:ff 100.10.0.100 "
      "route-distinguisher 65001:100 "
      "next-hop self "
      "label [ 10000 ] "
      "extended-community [ target:65001:100 ]")

With VNI (VXLAN):

print("announce evpn mac-ip "
      "aa:bb:cc:dd:ee:ff 100.10.0.100 "
      "route-distinguisher 65001:100 "
      "next-hop self "
      "label [ 5000 ] "
      "extended-community [ target:65001:100 encapsulation:vxlan ]")

EVPN Route Type 3 (Inclusive Multicast)

print("announce evpn multicast "
      "100.10.0.1 "
      "route-distinguisher 65001:100 "
      "next-hop self "
      "extended-community [ target:65001:100 ]")

Withdrawal

print("withdraw evpn mac-ip aa:bb:cc:dd:ee:ff route-distinguisher 65001:100")

BGP-LS

BGP Link-State (RFC 7752) for topology collection.

Note: BGP-LS is typically receive-only in ExaBGP. Used for collecting topology information from IGP.

Configuration to receive BGP-LS:

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

    family {
        ipv4 link-state;
        ipv6 link-state;
    }

    api {
        processes [ receive-topology ];
    }
}

No text API for announcing BGP-LS - it's receive-only.


VPLS

Virtual Private LAN Service (L2VPN).

# VPLS route
print("announce vpls "
      "route-distinguisher 65001:100 "
      "endpoint 192.168.1.2 "
      "offset 10 "
      "size 8 "
      "base 10000 "
      "extended-community [ target:65001:100 ]")

Withdrawal

print("withdraw vpls route-distinguisher 65001:100")

Multicast

Multicast routing (IPv4 and IPv6 multicast).

IPv4 Multicast

# Announce IPv4 multicast route
print("announce route 239.1.1.0/24 next-hop self")

# With source (S,G multicast)
print("announce route 239.1.1.0/24 next-hop self")

IPv6 Multicast

# Announce IPv6 multicast route
print("announce route ff02::1/128 next-hop 2001:db8::1")

Note: Multicast configuration requires appropriate address family configuration in ExaBGP config file.


RT-Constraint

Route Target Constraint (RFC 4684) for efficient VPN route distribution.

Announcement

# Announce RT membership (I'm interested in routes with this RT)
print("announce rtc 65001:100 next-hop self")

# Withdraw RT membership
print("withdraw rtc 65001:100")

Use case: Reduces unnecessary VPN route advertisements by signaling which route targets you're interested in.


Withdraw Commands

IPv4/IPv6 Unicast Withdrawal

# IPv4
print("withdraw route 100.10.0.0/24")

# IPv6
print("withdraw route 2001:db8::/32")

FlowSpec Withdrawal

# Withdraw FlowSpec rule by match conditions
print("withdraw flow route { match { destination 100.10.0.0/24; } }")

L3VPN Withdrawal

print("withdraw vpn 100.10.0.0/24 route-distinguisher 65001:100")

EVPN Withdrawal

print("withdraw evpn mac-ip aa:bb:cc:dd:ee:ff route-distinguisher 65001:100")

Bulk Withdrawal

# Withdraw multiple prefixes at once
print("withdraw attributes nlri 100.10.0.0/24 100.20.0.0/24 100.30.0.0/24")

Operational Commands

Refresh Routes

Force route refresh (send all routes again):

print("announce route-refresh ipv4 unicast")
print("announce route-refresh ipv6 unicast")

Operational Messages

Send operational messages:

# Query operational state
print("operational afi ipv4 safi unicast advisory rib in")

ACK Control Commands (ExaBGP 5.x/main)

Dynamically control command acknowledgment behavior at runtime:

# Re-enable ACK responses (sends "done" for this command, then enables ACK for future commands)
print("enable-ack")

# Disable ACK responses gracefully (sends final "done" for this command, then disables ACK)
print("disable-ack")

# Disable ACK immediately (NO response for this command, immediate silence)
print("silence-ack")

Use cases:

  • enable-ack: Re-enable error checking after fire-and-forget mode
  • disable-ack: Graceful transition to high-performance mode (final ACK confirms the change)
  • silence-ack: Maximum performance (no overhead, immediate silence)

Example: Optimize bulk announcements

import sys
import time

# Step 1: Verify ExaBGP is ready (with ACK)
sys.stdout.write("announce route 10.0.0.1/32 next-hop self\n")
sys.stdout.flush()
# Check for "done" response...

# Step 2: Disable ACK for bulk operation (10,000 routes)
sys.stdout.write("silence-ack\n")  # Immediate silence for max performance
sys.stdout.flush()

# Step 3: Bulk announce without ACK overhead
for i in range(10000):
    prefix = f"10.{i//256}.{i%256}.0/24"
    sys.stdout.write(f"announce route {prefix} next-hop self\n")
    sys.stdout.flush()

# Step 4: Re-enable ACK to verify completion
sys.stdout.write("enable-ack\n")
sys.stdout.flush()
# Check for "done" to confirm ExaBGP is responsive

Version compatibility:

  • ExaBGP 4.x: Commands are ignored with warning (safe, no harm)
  • ExaBGP 5.x/main: ACK can be controlled dynamically at runtime with these commands

Best practice for non-ACK-aware programs:

Use disable-ack at the start of your program when running on ExaBGP 5.x/main:

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

# Best practice: Safe on all ExaBGP versions
# - ExaBGP 5.x/main: Disables ACK (works as intended)
# - ExaBGP 4.x: Prints warning but continues (no harm)
sys.stdout.write("disable-ack\n")
sys.stdout.flush()
time.sleep(0.1)  # Brief pause for processing

# Your legacy code works without modification
while True:
    sys.stdout.write("announce route 100.10.0.100/32 next-hop self\n")
    sys.stdout.flush()
    time.sleep(5)

For maximum compatibility across ExaBGP 4.x and 5.x:

# Combine environment variable + disable-ack command
export exabgp.api.ack=false  # Works on 4.x and 5.x
exabgp /etc/exabgp/exabgp.conf

See also:


Examples by Use Case

High Availability with Anycast

Announce service IP when healthy:

import sys
import time
import socket

SERVICE_IP = "100.10.0.100"
SERVICE_PORT = 80

def is_healthy():
    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(2)
        result = sock.connect_ex((SERVICE_IP, SERVICE_PORT))
        sock.close()
        return result == 0
    except:
        return False

time.sleep(2)
announced = False

while True:
    if is_healthy():
        if not announced:
            sys.stdout.write(f"announce route {SERVICE_IP}/32 next-hop self\n")
            sys.stdout.flush()
            announced = True
    else:
        if announced:
            sys.stdout.write(f"withdraw route {SERVICE_IP}/32\n")
            sys.stdout.flush()
            announced = False
    time.sleep(5)

DDoS Mitigation (FlowSpec)

Auto-block detected attacks:

import sys
import time

def detect_attack():
    # Your DDoS detection logic
    # Returns (source_ip, dest_port, protocol) if attack detected
    return ("10.0.0.0/8", 80, "tcp")

time.sleep(2)

while True:
    attack = detect_attack()
    if attack:
        source, port, proto = attack
        # Block the attack
        sys.stdout.write(
            f"announce flow route {{ "
            f"match {{ source {source}; destination-port ={port}; protocol ={proto}; }} "
            f"then {{ discard; }} "
            f"}}\n"
        )
        sys.stdout.flush()
        sys.stderr.write(f"[BLOCKED] Attack from {source} to port {port}\n")

    time.sleep(1)

Load Balancing with Metrics

Announce with different metrics for load distribution:

import sys
import time

time.sleep(2)

# This server gets most traffic (lower MED)
sys.stdout.write("announce route 100.10.0.100/32 next-hop self med 100\n")
sys.stdout.flush()

# Another server as backup (higher MED)
# On the other server, announce with med 200

Traffic Engineering

Prefer specific path with communities:

import sys
import time

time.sleep(2)

# Mark route with community for policy
sys.stdout.write(
    "announce route 100.10.0.0/24 next-hop self "
    "community [ 65001:100 ] "
    "local-preference 200\n"
)
sys.stdout.flush()

Common Errors

1. Forgot to Flush

Wrong:

print("announce route 100.10.0.0/24 next-hop self")
# Nothing happens - stuck in buffer

Correct:

print("announce route 100.10.0.0/24 next-hop self")
sys.stdout.flush()  # Now it works

2. Missing next-hop

Wrong:

print("announce route 100.10.0.0/24")
# Error: next-hop required

Correct:

print("announce route 100.10.0.0/24 next-hop self")

3. Invalid Syntax

Wrong:

print("announce 100.10.0.0/24 next-hop self")
# Missing 'route' keyword

Correct:

print("announce route 100.10.0.0/24 next-hop self")

4. FlowSpec Missing Braces

Wrong:

print("announce flow route match destination 100.10.0.0/24 then discard")
# Missing { } braces

Correct:

print("announce flow route { match { destination 100.10.0.0/24; } then { discard; } }")

5. Withdraw with Attributes

Wrong:

print("withdraw route 100.10.0.0/24 next-hop self")
# Withdraw doesn't need attributes

Correct:

print("withdraw route 100.10.0.0/24")

Quick Reference

IPv4 Unicast

announce route <prefix> next-hop self [attributes]
withdraw route <prefix>

Bulk Announcements (4.0+)

announce attributes <attributes> nlri <prefix1> <prefix2> ...
withdraw attributes nlri <prefix1> <prefix2> ...

FlowSpec

announce flow route { match { <conditions>; } then { <action>; } }
withdraw flow route { match { <conditions>; } }

L3VPN

announce vpn <prefix> next-hop self route-distinguisher <rd> label <label> [extended-community [...]]
withdraw vpn <prefix> route-distinguisher <rd>

EVPN

announce evpn mac-ip <mac> [<ip>] route-distinguisher <rd> next-hop self label [<label>] extended-community [...]
withdraw evpn mac-ip <mac> route-distinguisher <rd>

See Also


Need JSON format? See JSON API Reference β†’


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

Clone this wiki locally