-
Notifications
You must be signed in to change notification settings - Fork 461
Text API Reference
Complete reference for ExaBGP's text-based API commands
- Overview
- Command Syntax
- Neighbor Selectors
- IPv4/IPv6 Unicast
- Route Attributes
- Bulk Announcements (announce attributes ... nlri)
- FlowSpec
- L3VPN
- EVPN
- BGP-LS
- VPLS
- Operational Commands
- Examples by Use Case
- Common Errors
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()<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
-
Always end with newline (
\n) -
Always flush after write (
sys.stdout.flush()) - One command per line
-
Case-sensitive (
announcenotANNOUNCE)
When ExaBGP is configured with multiple BGP neighbors, you can use neighbor selectors to target specific peers with your API commands.
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.
| 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 |
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-openfor single-session neighbors (default - all families in BGP OPEN) - Slash-separated families for multi-session neighbors (e.g.,
ipv4-unicast/ipv6-unicast/ipv4-flowspec)
#!/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)# 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 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")- 💡 Default behavior: Commands without
neighborselector apply to all configured neighbors - 💡 Selective targeting: Use
neighborkeyword 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:
# 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:
# 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")# 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
⚠️ Warning:next-hop selfis 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")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")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")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 ]")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 ]")# 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")# 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 ]")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.
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")# 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")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.
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).
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")✅ 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)
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)}")❌ 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")| 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 allows BGP-based traffic filtering. ExaBGP pioneered open-source FlowSpec support (now also in GoBGP, FRRouting, BIRD).
announce flow route {
match {
<match-conditions>
}
then {
<actions>
}
}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; } "
"}")
# Don't fragment (DF bit set)
print("announce flow route { "
"match { fragment [ dont-fragment ]; } "
"then { discard; } "
"}")# 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; } "
"}")Discard (drop):
print("announce flow route { "
"match { destination 100.10.0.0/24; } "
"then { discard; } "
"}")Rate-limit:
# Limit to 1 MB/s (~8 Mbps) (1,000,000 bytes/sec per RFC 5575)
print("announce flow route { "
"match { destination 100.10.0.0/24; } "
"then { rate-limit 1000000; } "
"}")
# Limit to 10 MB/s (~80 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 ]; } "
"}")# Withdraw by match conditions
print("withdraw flow route { "
"match { destination 100.10.0.0/24; } "
"}")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; } "
"}")Layer 3 VPN (RFC 4364) for MPLS VPNs.
# Basic L3VPN route
print("announce ipv4 mpls-vpn 100.10.0.0/24 "
"next-hop self "
"route-distinguisher 65001:100 "
"label 10000")
# With route target
print("announce ipv4 mpls-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 ipv4 mpls-vpn 100.10.0.0/24 "
"next-hop self "
"route-distinguisher 65001:100 "
"extended-community [ target:65001:100 target:65001:200 ] "
"label 10000")print("withdraw ipv4 mpls-vpn 100.10.0.0/24 route-distinguisher 65001:100")Ethernet VPN (RFC 7432) for data center fabrics.
Note: The text API does not have a dedicated
announce evpncommand. EVPN routes are typically configured via the ExaBGP configuration file, not the text API. The examples below show the configuration file syntax for reference.
MAC only:
route-type mac-ip aa:bb:cc:dd:ee:ff {
route-distinguisher 65001:100;
next-hop self;
label [ 10000 ];
extended-community [ target:65001:100 ];
}MAC + IP:
route-type 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):
route-type 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 ];
}route-type multicast 100.10.0.1 {
route-distinguisher 65001:100;
next-hop self;
extended-community [ target:65001:100 ];
}EVPN routes cannot be announced or withdrawn via the text API. Use the ExaBGP configuration file to define EVPN routes within an l2vpn family block. See the Configuration Syntax for details.
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.
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 ]")print("withdraw vpls route-distinguisher 65001:100")Multicast routing (IPv4 and IPv6 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")# 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.
Route Target Constraint (RFC 4684) for efficient VPN route distribution.
# 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.
# IPv4
print("withdraw route 100.10.0.0/24")
# IPv6
print("withdraw route 2001:db8::/32")# Withdraw FlowSpec rule by match conditions
print("withdraw flow route { match { destination 100.10.0.0/24; } }")print("withdraw ipv4 mpls-vpn 100.10.0.0/24 route-distinguisher 65001:100")Note: EVPN routes cannot be withdrawn via the text API. EVPN is configured via the ExaBGP configuration file. See the EVPN section above.
# Withdraw multiple prefixes at once
print("withdraw attributes nlri 100.10.0.0/24 100.20.0.0/24 100.30.0.0/24")Force route refresh (send all routes again):
print("announce route-refresh ipv4 unicast")
print("announce route-refresh ipv6 unicast")Send operational messages:
# Query operational state
print("operational afi ipv4 safi unicast advisory rib in")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 responsiveVersion 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.confSee also:
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)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)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 200Prefer 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()Wrong:
print("announce route 100.10.0.0/24 next-hop self")
# Nothing happens - stuck in bufferCorrect:
print("announce route 100.10.0.0/24 next-hop self")
sys.stdout.flush() # Now it worksWrong:
print("announce route 100.10.0.0/24")
# Error: next-hop requiredCorrect:
print("announce route 100.10.0.0/24 next-hop self")Wrong:
print("announce 100.10.0.0/24 next-hop self")
# Missing 'route' keywordCorrect:
print("announce route 100.10.0.0/24 next-hop self")Wrong:
print("announce flow route match destination 100.10.0.0/24 then discard")
# Missing { } bracesCorrect:
print("announce flow route { match { destination 100.10.0.0/24; } then { discard; } }")Wrong:
print("withdraw route 100.10.0.0/24 next-hop self")
# Withdraw doesn't need attributesCorrect:
print("withdraw route 100.10.0.0/24")announce route <prefix> next-hop self [attributes]
withdraw route <prefix>announce attributes <attributes> nlri <prefix1> <prefix2> ...
withdraw attributes nlri <prefix1> <prefix2> ...announce flow route { match { <conditions>; } then { <action>; } }
withdraw flow route { match { <conditions>; } }announce ipv4 mpls-vpn <prefix> next-hop self route-distinguisher <rd> label <label> [extended-community [...]]
withdraw ipv4 mpls-vpn <prefix> route-distinguisher <rd>EVPN routes are configured via the ExaBGP configuration file, not the text API. See the EVPN section above.
- API Overview - API architecture and concepts
- JSON API Reference - JSON message format
- API Commands - Complete command reference
- FlowSpec Overview - FlowSpec detailed guide
- Configuration Syntax - Process configuration
Need JSON format? See JSON API Reference →
Getting Started
Configuration
- Configuration Syntax
- Neighbor Configuration
- Directives A-Z
- Templates
- Environment Variables
- Process Configuration
API
- API Overview
- Text API Reference
- JSON API Reference
- API Commands
- Writing API Programs
- Error Handling
- Production Best Practices
Address Families
- Overview
- IPv4 Unicast
- IPv6 Unicast
- FlowSpec
- EVPN
- L3VPN
- BGP-LS
- VPLS
- SRv6 / MUP
- Multicast
- RT Constraint
Features
Use Cases
Tools
Operations
Reference
- Architecture
- Design
- Attribute Reference
- Command Reference
- BGP State Machine
- Capabilities
- Communities
- Examples Index
- Glossary
- RFC Support
Integration
Migration
Community
External