-
Notifications
You must be signed in to change notification settings - Fork 459
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; } "
"}")
# Not a fragment
print("announce flow route { "
"match { fragment [ not-a-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 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 ]; } "
"}")# 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 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")print("withdraw vpn 100.10.0.0/24 route-distinguisher 65001:100")Ethernet VPN (RFC 7432) for data center fabrics.
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 ]")print("announce evpn multicast "
"100.10.0.1 "
"route-distinguisher 65001:100 "
"next-hop self "
"extended-community [ target:65001:100 ]")print("withdraw evpn mac-ip aa:bb:cc:dd:ee:ff route-distinguisher 65001:100")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 vpn 100.10.0.0/24 route-distinguisher 65001:100")print("withdraw evpn mac-ip aa:bb:cc:dd:ee:ff route-distinguisher 65001:100")# 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 vpn <prefix> next-hop self route-distinguisher <rd> label <label> [extended-community [...]]
withdraw vpn <prefix> route-distinguisher <rd>announce evpn mac-ip <mac> [<ip>] route-distinguisher <rd> next-hop self label [<label>] extended-community [...]
withdraw evpn mac-ip <mac> route-distinguisher <rd>- 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 β
π» Ghost written by Claude (Anthropic AI)
π Home
π Getting Started
π§ API
π‘οΈ Use Cases
π Address Families
βοΈ Configuration
π Operations
π Reference
- Architecture
- BGP State Machine
- Communities (RFC)
- Extended Communities
- BGP Ecosystem
- Capabilities (AFI/SAFI)
- RFC Support
π Migration
π Community
π External
- GitHub Repo β
- Slack β
- Issues β
π» Ghost written by Claude (Anthropic AI)