-
Notifications
You must be signed in to change notification settings - Fork 458
API Overview
Understanding ExaBGP's STDIN/STDOUT API
- What is the ExaBGP API?
- Why This API Design?
- How It Works
- Text vs JSON Encoders
- API Architecture
- Basic Examples
- Process Lifecycle
- Communication Flow
- Command Acknowledgment (ACK Feature)
- Error Handling
- Best Practices
- Next Steps
The ExaBGP API is a simple STDIN/STDOUT interface that allows your programs to control BGP route announcements and receive BGP updates.
Key characteristics:
- Language-agnostic: Works with Python, Bash, Ruby, Go, Rust, Node.js, or any language
- Simple: No libraries needed - just read/write text or JSON
- Bidirectional: Your program sends commands, receives BGP messages
- Process-based: ExaBGP spawns your program as a subprocess
Think of it as:
Your Program β STDIN/STDOUT β ExaBGP β BGP Protocol β Router
"The best API is no API"
ExaBGP uses UNIX pipes (STDIN/STDOUT) instead of complex libraries.
Advantages:
- β No dependencies: Works with any language that can read/write streams
- β
Simple:
print()to send commands,readline()to receive - β Debugging-friendly: Can test manually with echo/cat
- β Language-agnostic: Not tied to Python or any specific ecosystem
- β Process isolation: Your program crashes don't affect ExaBGP
- β Easy integration: Wrap existing tools without modification
Compared to alternatives:
- Library-based (e.g., GoBGP's gRPC): Requires language-specific client
- REST API: Adds HTTP overhead, state management complexity
- File-based: Requires polling, slower updates
STDIN/STDOUT is:
- Fast (no network/HTTP overhead)
- Simple (just streams)
- Universal (every language supports it)
ExaBGP configuration defines which programs to run:
neighbor 192.168.1.1 {
router-id 192.168.1.2;
local-address 192.168.1.2;
local-as 65001;
peer-as 65000;
# API configuration
api {
processes [ my-program ];
}
}
process my-program {
run /etc/exabgp/api/my-program.py;
encoder text; # or 'json'
}When ExaBGP starts:
- ExaBGP spawns
/etc/exabgp/api/my-program.pyas subprocess - ExaBGP connects to program's STDIN/STDOUT
- Program can now:
- Write to STDOUT β Commands to ExaBGP
- Read from STDIN β BGP messages from ExaBGP
βββββββββββββββββββββββ
β Your Program β
β β
β print("announce β STDOUT βββββββ
β route ...") β β
β β βΌ
β for line in stdin: β STDIN ββββββββ¬ββββββββββ
β process(line) β β ExaBGP β
βββββββββββββββββββββββ β β
β BGP β
β Speaker β
ββββββ¬βββββ
β
β BGP Protocol
βΌ
βββββββββββ
β Router β
βββββββββββ
ExaBGP supports two encoding formats: Text and JSON.
β οΈ Important: Commands are ALWAYS text format
- Commands you send to ExaBGP (STDOUT): Always text format (
announce route ...,withdraw route ...)- Messages from ExaBGP (STDIN): Can be text or JSON based on
encodersetting- The
encodersetting controls ONLY what format ExaBGP uses when sending messages to your program- You CANNOT send JSON commands to ExaBGP - all commands must be text
Use case: Sending commands + receiving simple text messages
Configuration:
process my-program {
run /etc/exabgp/api/announce.py;
encoder text;
}Format: Human-readable text commands (bidirectional)
Example commands (your program β ExaBGP via STDOUT):
# Commands are ALWAYS text format, regardless of encoder setting
print("announce route 100.10.0.0/24 next-hop 192.0.2.1")
print("withdraw route 100.10.0.0/24")
print("announce flow route { match { destination 100.10.0.0/24; } then { discard; } }")Pros:
- β Human-readable (both directions)
- β Easy to test manually
- β Simple for both sending and receiving
Cons:
- β Received messages harder to parse programmatically
- β Limited structured data from ExaBGP
Use case: Receiving BGP updates in structured format (routes, session state)
Configuration:
process my-program {
run /etc/exabgp/api/receive.py;
encoder json;
receive {
parsed; # Receive parsed BGP messages
updates; # Receive route updates
}
}Format: JSON lines (one JSON object per line)
Example message:
{
"exabgp": "4.2.25",
"time": 1699564800.0,
"type": "update",
"neighbor": {
"address": {
"local": "192.168.1.2",
"peer": "192.168.1.1"
},
"message": {
"update": {
"announce": {
"ipv4 unicast": {
"100.10.0.0/24": [
{
"next-hop": "192.168.1.2"
}
]
}
}
}
}
}
}Pros:
- β Structured data
- β Easy to parse (every language has JSON support)
- β Complete information
Cons:
- β Verbose
- β Harder to read manually
Remember: The
encodersetting ONLY affects messages FROM ExaBGP TO your program. Commands from your program to ExaBGP are always text.
Use encoder text when:
- Only sending commands (announce/withdraw)
- Want simple, human-readable messages from ExaBGP
- Testing manually
- Don't need structured parsing of received messages
Use encoder json when:
- Receiving BGP updates from router
- Need structured data parsing
- Building complex integrations
- Want easy programmatic parsing of ExaBGP messages
Can use both (common pattern):
# Process 1: Send commands (text)
process announce {
run /etc/exabgp/api/announce.py;
encoder text;
}
# Process 2: Receive updates (JSON)
process receive {
run /etc/exabgp/api/receive.py;
encoder json;
receive {
parsed;
updates;
}
}
neighbor 192.168.1.1 {
router-id 192.168.1.2;
local-address 192.168.1.2;
local-as 65001;
peer-as 65000;
api {
processes [ announce, receive ];
}
}ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Your Program(s) β
β β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββ β
β β announce.py β β receive.py β β health.py β β
β β (text) β β (json) β β (text) β β
β ββββββββ¬ββββββββ ββββββββ¬ββββββββ ββββββββ¬ββββββ β
β β STDOUT β STDIN β STDOUTβ
βββββββββββΌββββββββββββββββββΌββββββββββββββββββΌββββββββ
β β β
βΌ βΌ βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ExaBGP β
β β
β βββββββββββββββ ββββββββββββββββ ββββββββββββ β
β β Process β β BGP State β β Route β β
β β Manager β β Machine β β Manager β β
β βββββββββββββββ ββββββββββββββββ ββββββββββββ β
ββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββ
β BGP Protocol
βΌ
ββββββββββββ
β Router β
ββββββββββββ
- Spawns your programs as subprocesses
- Monitors process health (restarts if crashed)
- Routes commands from STDOUT to BGP engine
- Sends BGP messages to process STDIN
1. Your program: print("announce route 100.10.0.0/24 next-hop self")
2. Process writes to STDOUT
3. ExaBGP reads from process STDOUT
4. ExaBGP parses command
5. ExaBGP generates BGP UPDATE message
6. ExaBGP sends UPDATE to router via TCP 179
7. Router installs route in RIB/FIB
1. Router sends BGP UPDATE to ExaBGP
2. ExaBGP receives UPDATE
3. ExaBGP parses BGP message
4. ExaBGP converts to JSON (if encoder json)
5. ExaBGP writes JSON to process STDIN
6. Your program reads from STDIN
7. Your program processes the update
#!/usr/bin/env python3
"""
announce.py - Announce static routes
"""
import sys
import time
# Wait for ExaBGP to be ready
time.sleep(2)
# Announce routes
routes = [
"100.10.0.0/24",
"100.20.0.0/24",
]
for route in routes:
sys.stdout.write(f"announce route {route} next-hop self\n")
sys.stdout.flush() # CRITICAL: Must flush!
# Keep process alive
while True:
time.sleep(60)Configuration:
process announce {
run /etc/exabgp/api/announce.py;
encoder text;
}#!/usr/bin/env python3
"""
healthcheck.py - Announce route only when service is healthy
"""
import sys
import time
import socket
SERVICE_IP = "100.10.0.100"
SERVICE_PORT = 80
CHECK_INTERVAL = 5
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(CHECK_INTERVAL)#!/usr/bin/env python3
"""
receive.py - Process incoming BGP updates
"""
import sys
import json
while True:
line = sys.stdin.readline().strip()
if not line:
break
try:
msg = json.loads(line)
# Route announcement
if msg['type'] == 'update':
if 'update' in msg['neighbor']['message']:
update = msg['neighbor']['message']['update']
# New routes announced
if 'announce' in update:
if 'ipv4 unicast' in update['announce']:
routes = update['announce']['ipv4 unicast']
for prefix, attrs in routes.items():
nexthop = attrs[0]['next-hop']
print(f"[RECEIVED] {prefix} via {nexthop}", file=sys.stderr)
# Routes withdrawn
if 'withdraw' in update:
if 'ipv4 unicast' in update['withdraw']:
for prefix in update['withdraw']['ipv4 unicast'].keys():
print(f"[WITHDRAWN] {prefix}", file=sys.stderr)
# Session state change
elif msg['type'] == 'state':
state = msg['neighbor']['state']
print(f"[STATE] BGP session: {state}", file=sys.stderr)
except Exception as e:
print(f"[ERROR] {e}", file=sys.stderr)Configuration:
process receive {
run /etc/exabgp/api/receive.py;
encoder json;
receive {
parsed;
updates;
}
}#!/bin/bash
# announce.sh - Announce routes from bash
# Wait for ExaBGP
sleep 2
# Announce routes
echo "announce route 100.10.0.0/24 next-hop self"
echo "announce route 100.20.0.0/24 next-hop self"
# Keep running
while true; do
sleep 60
donepackage main
import (
"fmt"
"os"
"time"
)
func main() {
// Wait for ExaBGP
time.Sleep(2 * time.Second)
// Announce routes
routes := []string{
"100.10.0.0/24",
"100.20.0.0/24",
}
for _, route := range routes {
fmt.Printf("announce route %s next-hop self\n", route)
}
// Keep alive
for {
time.Sleep(60 * time.Second)
}
}- ExaBGP starts
- Reads configuration file
- Spawns process(es) defined in
processblocks - Connects to process STDIN/STDOUT
- Waits for BGP session to establish
Your program should:
- Sleep 2-5 seconds to wait for BGP session
- Send initial announcements
- Enter main loop
Your program:
- Continuously runs (infinite loop or event-driven)
- Writes commands to STDOUT when needed
- Reads messages from STDIN if receiving updates
- Flushes STDOUT after each write
ExaBGP:
- Monitors process (checks if still running)
- Routes commands to BGP engine
- Sends BGP updates to process
Graceful shutdown (SIGTERM):
- ExaBGP receives SIGTERM
- ExaBGP sends SIGTERM to all processes
- Processes should exit cleanly
- ExaBGP closes BGP sessions (sends NOTIFICATION)
- ExaBGP exits
Process crash:
- Process exits/crashes
- ExaBGP detects (broken pipe or process exit)
- ExaBGP can restart process (if
respawnenabled)
Configuration for auto-restart:
process my-program {
run /etc/exabgp/api/my-program.py;
encoder text;
}ExaBGP automatically respawns crashed processes.
# WRONG - Not flushed, nothing happens
print("announce route 100.10.0.0/24 next-hop self")
# CORRECT - Flushed, ExaBGP receives it
sys.stdout.write("announce route 100.10.0.0/24 next-hop self\n")
sys.stdout.flush()
# ALSO CORRECT - Print and flush
print("announce route 100.10.0.0/24 next-hop self")
sys.stdout.flush()
# ALSO CORRECT - Unbuffered mode
print("announce route 100.10.0.0/24 next-hop self", flush=True)Critical: Always flush STDOUT, or use unbuffered mode:
python3 -u /etc/exabgp/api/announce.py# Read lines forever
while True:
line = sys.stdin.readline()
if not line:
break # EOF
process(line)Message types (JSON):
-
type: "state"- BGP session state changes -
type: "update"- Route announcements/withdrawals -
type: "notification"- BGP errors -
type: "keepalive"- Keepalive messages
β Available in ExaBGP 4.x and 5.x/main - Default: enabled in both versions
The ACK (acknowledgment) feature allows your program to receive feedback on whether commands succeeded or failed.
Behavior (when enabled, which is the default):
- Your program writes command to STDOUT
- ExaBGP reads and validates command
- ExaBGP sends response on your program's STDIN
- Your program can verify success/failure
Responses:
-
done\n- Command succeeded -
error\n- Command failed (syntax error, invalid route, etc.) -
shutdown\n- ExaBGP is shutting down
Default Configuration (both 4.x and 5.x/main):
# ACK is enabled by default in both versions
export exabgp.api.ack=true # Default
# To disable (for simpler programs that don't check responses):
export exabgp.api.ack=falseBenefits:
- β Reliability: Know if commands succeeded
- β Error Detection: Catch syntax errors immediately
- β Debugging: Easier to troubleshoot failures
- β Retry Logic: Can retry failed commands
- β Production-Ready: Critical for automated systems
When ACK is disabled:
- Commands are fire-and-forget
- No feedback on success/failure
- Simpler code (no need to read responses)
- Check ExaBGP logs for errors
Robust ACK handling with polling loop:
#!/usr/bin/env python3
"""
announce_with_ack.py - Send commands with ACK verification
"""
import sys
import select
import time
def wait_for_ack(expected_count=1, timeout=30):
"""
Wait for ACK responses from ExaBGP.
Polls STDIN until all expected ACK messages are received.
ExaBGP may not respond immediately, so we loop with sleep.
Handles both text and JSON encoder formats:
- Text: "done", "error", "shutdown"
- JSON: {"answer": "done|error|shutdown", "message": "..."}
Args:
expected_count: Number of ACK messages expected (default: 1)
timeout: Total timeout in seconds (default: 30)
Returns:
True if all ACKs received successfully
False if any command failed or timeout occurred
Raises:
SystemExit: If ExaBGP sends shutdown message
"""
import json
received = 0
start_time = time.time()
while received < expected_count:
# Check if we've exceeded timeout
elapsed = time.time() - start_time
if elapsed >= timeout:
return False
# Poll for data (non-blocking with short timeout)
ready, _, _ = select.select([sys.stdin], [], [], 0.1)
if ready:
line = sys.stdin.readline().strip()
# Parse response (could be text or JSON)
answer = None
if line.startswith('{'):
# JSON format: {"answer": "done|error|shutdown", ...}
try:
data = json.loads(line)
answer = data.get('answer')
except:
pass
else:
# Text format: done|error|shutdown
answer = line
if answer == "done":
received += 1
elif answer == "error":
return False
elif answer == "shutdown":
raise SystemExit(0)
# Ignore other messages (could be BGP updates if receiving)
else:
# No data yet, sleep briefly before next poll
time.sleep(0.1)
return True
def send_command(command):
"""Send command and wait for ACK"""
sys.stdout.write(command + "\n")
sys.stdout.flush()
return wait_for_ack(expected_count=1)
# Send commands with verification
if send_command("announce route 100.10.0.0/24 next-hop self"):
sys.stderr.write("[OK] Route 100.10.0.0/24 announced\n")
if send_command("announce route 100.20.0.0/24 next-hop self"):
sys.stderr.write("[OK] Route 100.20.0.0/24 announced\n")
# Keep running
while True:
time.sleep(60)When ACK is disabled (exabgp.api.ack=false):
#!/usr/bin/env python3
"""
announce_no_ack.py - Simple announcer when ACK is disabled
"""
import sys
import time
def send_command_no_ack(command):
"""Send command without waiting for ACK"""
sys.stdout.write(command + "\n")
sys.stdout.flush()
# No feedback - command is fire-and-forget
sys.stderr.write(f"[SENT] {command}\n")
# Simpler code when ACK is disabled
send_command_no_ack("announce route 100.10.0.0/24 next-hop self")
# Keep running
while True:
time.sleep(60)#!/usr/bin/env python3
"""
detect_ack.py - Auto-detect if ACK is enabled
"""
import sys
import select
import os
def has_ack_support():
"""Test if ExaBGP has ACK enabled"""
# Check environment variable
ack = os.getenv('exabgp.api.ack', 'true')
return ack.lower() == 'true'
def send_command(command):
"""Send command with or without ACK based on support"""
sys.stdout.write(command + "\n")
sys.stdout.flush()
if has_ack_support():
# Wait for ACK
ready, _, _ = select.select([sys.stdin], [], [], 5.0)
if ready:
response = sys.stdin.readline().strip()
return response == "done"
return False
else:
# No ACK - assume success
return True
# Auto-adapts to ExaBGP version
send_command("announce route 100.10.0.0/24 next-hop self")ACK is enabled by default in both ExaBGP 4.x and 5.x/main:
# Default (ACK enabled)
exabgp /etc/exabgp/exabgp.conf
# Explicitly enable ACK (already the default)
export exabgp.api.ack=true
exabgp /etc/exabgp/exabgp.confDisable ACK (for simpler programs that don't need feedback):
export exabgp.api.ack=false
exabgp /etc/exabgp/exabgp.confIn healthcheck application:
# With ACK (default)
python -m exabgp healthcheck --cmd "curl -sf http://localhost/health"
# Without ACK (simpler mode)
python -m exabgp healthcheck --no-ack --cmd "curl -sf http://localhost/health"Use ACK (recommended for production):
- Production deployments
- Automated DDoS mitigation
- Critical route announcements
- Any scenario where silent failures are unacceptable
- When you need reliable error handling
Disable ACK when:
- Simple static announcements that don't change
- Testing/development with manual verification
- Programs that don't need error feedback
- Simpler code without response handling
β Available in ExaBGP 5.x/main - Control ACK behavior dynamically at runtime
In addition to the startup configuration (exabgp.api.ack=true/false), ExaBGP 5.x/main provides three API commands to dynamically control ACK behavior during runtime:
1. enable-ack - Re-enable ACK Responses
enable-ack
- Re-enables ACK responses for this API connection
-
Sends "done" ACK for the
enable-ackcommand itself - All future commands will receive ACK responses (
doneorerror) - Use case: Re-enable error checking after disabling it
2. disable-ack - Disable ACK with Final Response
disable-ack
- Disables ACK responses for this API connection
-
Sends "done" ACK for the
disable-ackcommand itself (final ACK) - Future commands will NOT receive ACK responses (fire-and-forget mode)
- Use case: Disable ACK gracefully after receiving confirmation
3. silence-ack - Immediate Silence (No Response)
silence-ack
- Disables ACK responses immediately
-
Does NOT send ACK for the
silence-ackcommand itself - Future commands will NOT receive ACK responses (fire-and-forget mode)
- Use case: Disable ACK immediately without waiting for final response
| Command | Sends ACK for itself? | Future commands ACKed? | Use Case |
|---|---|---|---|
enable-ack |
β Yes ("done") | β Yes | Re-enable error checking |
disable-ack |
β Yes ("done", final ACK) | β No | Graceful transition to fire-and-forget |
silence-ack |
β No (immediate silence) | β No | Immediate transition to fire-and-forget |
Key difference:
-
disable-ack: Sends one final "done" response, THEN stops ACKing -
silence-ack: Stops ACKing immediately (no response to this command)
#!/usr/bin/env python3
"""
dynamic_ack_control.py - Demonstrate runtime ACK control
"""
import sys
import select
import time
def wait_for_ack(timeout=2):
"""Wait for ACK response (returns True if 'done', False otherwise)"""
ready, _, _ = select.select([sys.stdin], [], [], timeout)
if ready:
response = sys.stdin.readline().strip()
return response == "done"
return False
def send_command(command, expect_ack=True):
"""Send command with optional ACK verification"""
sys.stdout.write(command + "\n")
sys.stdout.flush()
if expect_ack:
if wait_for_ack():
sys.stderr.write(f"[OK] {command}\n")
return True
else:
sys.stderr.write(f"[FAIL] {command}\n")
return False
else:
sys.stderr.write(f"[SENT] {command} (no ACK expected)\n")
return True
# Initial state: ACK is enabled by default
time.sleep(0.2) # Wait for BGP session establishment
# Step 1: Send command with ACK (baseline)
send_command("announce route 100.10.0.0/24 next-hop self", expect_ack=True)
# Step 2: Disable ACK gracefully (receive final "done")
send_command("disable-ack", expect_ack=True) # Gets final ACK
# Step 3: Send commands without ACK (fire-and-forget mode)
send_command("announce route 100.20.0.0/24 next-hop self", expect_ack=False)
send_command("announce route 100.30.0.0/24 next-hop self", expect_ack=False)
# Step 4: Re-enable ACK
send_command("enable-ack", expect_ack=True) # ACK is back
# Step 5: Verify ACK is working again
send_command("announce route 100.40.0.0/24 next-hop self", expect_ack=True)
# Keep running
while True:
time.sleep(60)Output:
[OK] announce route 100.10.0.0/24 next-hop self
[OK] disable-ack
[SENT] announce route 100.20.0.0/24 next-hop self (no ACK expected)
[SENT] announce route 100.30.0.0/24 next-hop self (no ACK expected)
[OK] enable-ack
[OK] announce route 100.40.0.0/24 next-hop self
#!/usr/bin/env python3
"""
silence_ack_example.py - Demonstrate immediate ACK silencing
"""
import sys
import select
import time
def wait_for_ack(timeout=1):
"""Wait for ACK response"""
ready, _, _ = select.select([sys.stdin], [], [], timeout)
if ready:
response = sys.stdin.readline().strip()
return response == "done"
return False
time.sleep(0.2)
# Step 1: Send command with ACK
sys.stdout.write("announce route 200.10.0.0/24 next-hop self\n")
sys.stdout.flush()
if wait_for_ack():
sys.stderr.write("[OK] Route announced with ACK\n")
# Step 2: Use silence-ack (NO ACK for this command itself)
sys.stdout.write("silence-ack\n")
sys.stdout.flush()
# Note: No ACK expected for silence-ack itself
if wait_for_ack():
sys.stderr.write("[ERROR] Unexpected ACK for silence-ack\n")
else:
sys.stderr.write("[OK] silence-ack executed (no ACK as expected)\n")
# Step 3: Send commands without expecting ACK
sys.stdout.write("announce route 200.20.0.0/24 next-hop self\n")
sys.stdout.flush()
sys.stderr.write("[SENT] Route announced (no ACK expected)\n")
# Keep running
while True:
time.sleep(60)When to use enable-ack:
- Re-enable error checking after a batch of fire-and-forget commands
- Switch from performance mode to reliability mode
- Debugging: Enable ACK temporarily to verify commands
When to use disable-ack:
- High-performance bulk announcements where ACK overhead is too high
- Graceful transition to fire-and-forget mode
- You still want confirmation that ACK was disabled successfully
- Best practice: Using ExaBGP 5.x/main with non-ACK-aware API programs
When to use silence-ack:
- Maximum performance: Skip even the final ACK response
- Immediate transition to fire-and-forget mode
- Bulk operations where even one ACK adds latency
If you're running ExaBGP 5.x/main with legacy API programs that don't handle ACK responses, use disable-ack at the start of your program:
#!/usr/bin/env python3
"""
legacy_program.py - Old API program that doesn't handle ACK
"""
import sys
import time
# Best practice: Disable ACK at startup (safe on all ExaBGP versions)
# - ExaBGP 5.x/main: Disables ACK after sending final "done"
# - ExaBGP 4.x: Prints warning but continues (no harm)
sys.stdout.write("disable-ack\n")
sys.stdout.flush()
time.sleep(0.1) # Give ExaBGP time to process
# Now your legacy code works without modification
while True:
# Legacy code that doesn't expect ACK responses
sys.stdout.write("announce route 100.10.0.100/32 next-hop self\n")
sys.stdout.flush()
# No ACK handling needed - fire-and-forget mode
time.sleep(5)Why this works:
- β ExaBGP 5.x/main: ACK is disabled, your program works normally
β οΈ ExaBGP 4.x: Command is ignored with warning, butexabgp.api.ack=truedefault still applies (your program may hang if it doesn't handle ACK)- π‘ Solution: Also set
exabgp.api.ack=falsein environment for ExaBGP 4.x compatibility
For maximum compatibility across all ExaBGP versions:
# Run your program with both methods:
# 1. Environment variable (works on 4.x and 5.x)
# 2. disable-ack command (works on 5.x, ignored on 4.x)
export exabgp.api.ack=false
exabgp /etc/exabgp/exabgp.confThen in your program, still send disable-ack as defense-in-depth:
# Send disable-ack even if environment variable is set
# (no harm, ensures ACK is off regardless of config)
sys.stdout.write("disable-ack\n")
sys.stdout.flush()
time.sleep(0.1)Example: Bulk Announcement Optimization
#!/usr/bin/env python3
"""
bulk_with_ack_control.py - Optimize bulk announcements
"""
import sys
import time
time.sleep(0.2)
# 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 ACK...
# 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 ACK to confirm ExaBGP processed everything
# Step 5: Send verification command
sys.stdout.write("announce route 10.255.255.0/24 next-hop self\n")
sys.stdout.flush()
# Check ACK to confirm ExaBGP is still responsive
while True:
time.sleep(60)| Feature | ExaBGP 4.x | ExaBGP 5.x/main |
|---|---|---|
exabgp.api.ack=true/false (startup config) |
β Yes | β Yes |
enable-ack (runtime command) |
β Yes | |
disable-ack (runtime command) |
β Yes | |
silence-ack (runtime command) |
β Yes |
Backward Compatibility:
- β Safe to send to older versions: These commands are safe to send to ExaBGP 4.x
β οΈ Warning only: Older versions will print a warning but will NOT crash or fail- π― Best practice: Safe to include in API programs that may run on different ExaBGP versions
Migration notes:
- ExaBGP 4.x: ACK behavior can only be set at startup via environment variable
- ExaBGP 5.x/main: ACK behavior can be changed dynamically at runtime via API commands
-
Recommended: Use
disable-ackat the start of your API program if you don't want to handle ACK responses
ExaBGP logs errors but continues:
# Invalid command (when ACK is disabled)
print("announce route 100.10.0.0/24") # Missing next-hopExaBGP log:
ERROR: Could not parse command: announce route 100.10.0.0/24
Your program won't know unless you monitor logs.
ExaBGP sends error response:
# Invalid command (with ACK enabled - the default)
sys.stdout.write("announce route 100.10.0.0/24\n") # Missing next-hop
sys.stdout.flush()
# Wait for response
ready, _, _ = select.select([sys.stdin], [], [], 5.0)
if ready:
response = sys.stdin.readline().strip()
if response == "error":
sys.stderr.write("[ERROR] Command rejected by ExaBGP\n")
# Check ExaBGP logs for detailsYour program knows immediately that the command failed.
If your program crashes:
- ExaBGP detects (broken pipe)
- ExaBGP logs error
- ExaBGP restarts process automatically
Best practice:
try:
main_loop()
except Exception as e:
sys.stderr.write(f"[ERROR] {e}\n")
sys.exit(1)If BGP session drops:
- ExaBGP continues running
- Your process continues running
- Commands are queued until session re-establishes
Receive updates about session state:
if msg['type'] == 'state':
if msg['neighbor']['state'] == 'down':
# Session dropped - stop announcing?
passsys.stdout.flush() # After every writeOr use unbuffered mode:
#!/usr/bin/env python3 -utime.sleep(2) # Give ExaBGP time to establish sessionwhile True:
time.sleep(60) # Don't exit immediatelyimport sys
def log(message):
sys.stderr.write(f"[{time.time()}] {message}\n")
log("Service UP - announced route")STDERR goes to ExaBGP log.
try:
result = check_service()
except Exception as e:
log(f"Health check error: {e}")
# Assume down on error
result = Falseannounced = False
if should_announce and not announced:
announce()
announced = True
elif not should_announce and announced:
withdraw()
announced = FalseAvoids redundant announcements.
def announce_route(prefix, nexthop="self"):
if not is_valid_prefix(prefix):
log(f"Invalid prefix: {prefix}")
return
sys.stdout.write(f"announce route {prefix} next-hop {nexthop}\n")
sys.stdout.flush()- Text API Reference - All text commands
- JSON API Reference - JSON message format
- API Commands - Complete command reference
- Writing API Programs - Best practices guide
- Production Best Practices - Production deployment
- Quick Start - Basic examples
- High Availability - Health check patterns
- DDoS Mitigation - FlowSpec automation
- Configuration Syntax - Process configuration
- Environment Variables - API environment
Ready to dive deeper? Continue to Text 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)