# API Commands Reference **A-Z alphabetical reference for all ExaBGP API commands** > 📚 **For detailed examples and patterns**, see [Text API Reference](Text-API-Reference) and [JSON API Reference](JSON-API-Reference) --- ## Table of Contents - [Command Format](#command-format) - [Neighbor Selectors (Targeting Specific Peers)](#neighbor-selectors-targeting-specific-peers) - [Announce Commands](#announce-commands) - [Withdraw Commands](#withdraw-commands) - [Operational Commands](#operational-commands) - [Control Commands](#control-commands) - [flush](#flush) - [enable-ack](#enable-ack) - [disable-ack](#disable-ack) - [silence-ack](#silence-ack) - [Group Commands (6.0.0+)](#group-commands-600) - [Quick Reference Table](#quick-reference-table) - [Command Acknowledgment (ACK)](#command-acknowledgment-ack) - [Common Errors](#common-errors) --- ## Command Format All API commands are sent to ExaBGP via **STDOUT** from your process. **Basic pattern:** ```python import sys # Send command sys.stdout.write("announce route 100.10.0.100/32 next-hop self\n") sys.stdout.flush() # CRITICAL: Always flush! ``` **Important:** - ✅ Always end commands with `\n` - ✅ Always call `sys.stdout.flush()` after writing - ✅ Commands are **case-sensitive** - ✅ ExaBGP 4.x and 5.0.0+ send ACK responses by default (`done`/`error`/`shutdown`) - â„šī¸ ACK can be disabled: - Environment variable: `exabgp.api.ack=false` (4.x and 5.0.0+) - Runtime commands: `disable-ack` or `silence-ack` (5.0.0+ only) --- ## Neighbor Selectors (Targeting Specific Peers) When you have **multiple BGP peers** configured with the same API process, you can use **neighbor selectors** to target specific peers with your announce/withdraw commands. ### Syntax ``` neighbor [] ``` **By default, commands apply to all configured neighbors** (equivalent to `neighbor *`). Use the `neighbor` keyword when you want to target specific peer(s) instead of broadcasting to all neighbors. ### Selector Options Use these optional filters to differentiate between multiple peers with the same IP or to target groups of peers: | Selector | Purpose | Example | |----------|---------|---------| | `neighbor ` | **Required**: Target specific neighbor by IP | `neighbor 127.0.0.1` | | `local-ip ` | Filter by local IP address | `local-ip 192.168.1.2` | | `local-as ` | Filter by local ASN | `local-as 65001` | | `peer-as ` | Filter by peer ASN | `peer-as 65000` | | `router-id ` | Filter by router ID | `router-id 192.168.1.1` | | `family-allowed ` | Filter by address family capabilities | `family-allowed ipv4-unicast/ipv6-unicast` | ### Special Syntax **Multiple neighbors (comma-separated):** ```python # Target multiple specific neighbors neighbor 127.0.0.1,192.168.1.1 announce route 10.0.0.0/24 next-hop self ``` **Wildcard (all neighbors):** ```python # Target ALL configured neighbors neighbor * announce route 10.0.0.0/24 next-hop self ``` ### Basic Examples **Simple single neighbor:** ```python # Announce to specific neighbor sys.stdout.write("neighbor 127.0.0.1 announce route 10.0.0.0/24 next-hop 192.168.1.1\n") sys.stdout.flush() ``` **Target all neighbors:** ```python # Announce to all neighbors sys.stdout.write("neighbor * announce route 10.0.0.0/24 next-hop self\n") sys.stdout.flush() ``` **Multiple neighbors:** ```python # Announce to two specific neighbors sys.stdout.write("neighbor 10.0.0.1,10.0.0.2 announce route 100.10.0.0/24 next-hop self\n") sys.stdout.flush() ``` ### Advanced Examples with Filters **Differentiate by local ASN:** ```python # If you have multiple neighbors at same IP but different local-as sys.stdout.write("neighbor 127.0.0.1 local-as 65000 announce route 10.0.0.0/24 next-hop self\n") sys.stdout.flush() ``` **Differentiate by peer ASN:** ```python # Target specific peer-as sys.stdout.write("neighbor 127.0.0.1 peer-as 65001 announce route 10.0.0.0/24 next-hop self\n") sys.stdout.flush() ``` **Multiple selectors:** ```python # Combine multiple selectors for precise targeting sys.stdout.write("neighbor 127.0.0.1 local-as 65000 peer-as 65001 announce route 10.0.0.0/24 next-hop self\n") sys.stdout.flush() ``` **Filter by address family:** ```python # Target neighbors that support IPv4 unicast sys.stdout.write("neighbor * family-allowed ipv4-unicast announce route 10.0.0.0/24 next-hop self\n") sys.stdout.flush() # Target neighbors supporting both IPv4 and IPv6 unicast (multi-session) sys.stdout.write("neighbor * family-allowed ipv4-unicast/ipv6-unicast announce route 10.0.0.0/24 next-hop self\n") sys.stdout.flush() # Target neighbors with FlowSpec capability sys.stdout.write("neighbor * family-allowed ipv4-flowspec announce flow route { match { destination 10.0.0.0/8; } then { discard; } }\n") sys.stdout.flush() ``` ### Complete Example: Multi-Peer Announcement ```python #!/usr/bin/env python3 """ multi_peer_announce.py - Announce different routes to different peers """ 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 specific neighbor sys.stdout.write("neighbor 192.168.1.1 announce route 100.20.0.0/24 next-hop self\n") sys.stdout.flush() # Announce to multiple neighbors sys.stdout.write("neighbor 192.168.1.1,192.168.1.2 announce route 100.30.0.0/24 next-hop self\n") sys.stdout.flush() # Target by peer AS sys.stdout.write("neighbor 192.168.1.1 peer-as 65000 announce route 100.40.0.0/24 next-hop self\n") sys.stdout.flush() # Keep running while True: time.sleep(60) ``` ### Understanding family-allowed The `family-allowed` selector filters neighbors based on their negotiated address family capabilities. The value format depends on the neighbor configuration: **Single-session neighbors** (default): - Value: `in-open` - All address families are negotiated in the BGP OPEN message - Example: `family-allowed in-open` **Multi-session neighbors** (when `multi-session` capability is enabled): - Value: Slash-separated list of address families - Format: `-/-/...` - Example: `family-allowed ipv4-unicast/ipv6-unicast/ipv4-flowspec` **Common address family values:** - `ipv4-unicast` - IPv4 unicast routes - `ipv6-unicast` - IPv6 unicast routes - `ipv4-flowspec` - IPv4 FlowSpec rules - `ipv6-flowspec` - IPv6 FlowSpec rules - `ipv4-mpls-vpn` - IPv4 L3VPN (VPNv4) - `ipv6-mpls-vpn` - IPv6 L3VPN (VPNv6) - `l2vpn-evpn` - EVPN routes - `ipv4-multicast` - IPv4 multicast - `ipv6-multicast` - IPv6 multicast **Examples:** ```python # Target only neighbors with FlowSpec capability (multi-session) sys.stdout.write("neighbor * family-allowed ipv4-flowspec announce flow route { match { destination 10.0.0.0/8; } then { discard; } }\n") sys.stdout.flush() # Target neighbors with dual-stack unicast capability sys.stdout.write("neighbor * family-allowed ipv4-unicast/ipv6-unicast announce route 10.0.0.0/24 next-hop self\n") sys.stdout.flush() # Target single-session neighbors (all families negotiated in OPEN) sys.stdout.write("neighbor * family-allowed in-open announce route 10.0.0.0/24 next-hop self\n") sys.stdout.flush() ``` **Use case:** Send FlowSpec rules only to neighbors that support FlowSpec, avoiding errors on neighbors that don't support it. ### Use Cases **1. Per-Peer Route Customization:** ```python # Announce different routes to different transit providers sys.stdout.write("neighbor 10.0.1.1 announce route 100.10.0.0/24 next-hop self as-path [65001 65001]\n") # Provider A sys.stdout.flush() sys.stdout.write("neighbor 10.0.2.1 announce route 100.10.0.0/24 next-hop self\n") # Provider B (preferred) sys.stdout.flush() ``` **2. Geographic Load Balancing:** ```python # US datacenter announces to US peers sys.stdout.write("neighbor 192.168.1.1 announce route 100.10.0.100/32 next-hop self local-preference 200\n") sys.stdout.flush() # EU datacenter announces to EU peers sys.stdout.write("neighbor 192.168.2.1 announce route 100.10.0.100/32 next-hop self local-preference 100\n") sys.stdout.flush() ``` **3. ASN-Based Filtering:** ```python # Only announce to peers in specific AS sys.stdout.write("neighbor * peer-as 65000 announce route 10.0.0.0/8 next-hop self\n") sys.stdout.flush() ``` ### Important Notes - 💡 **Default behavior**: Commands without `neighbor` selector apply to **all configured neighbors** (equivalent to `neighbor *`) - 💡 **Selective targeting**: Use `neighbor` keyword to target specific peer(s) instead of all neighbors - 💡 The `neighbor` selector always comes **before** the command (announce/withdraw) - 💡 Selectors are evaluated as **AND logic** - all specified selectors must match - 💡 If no neighbor matches your selectors, the command is silently ignored ### Configuration Example ```ini 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 [ my-program ]; } } neighbor 192.168.2.1 { router-id 192.168.1.2; local-address 192.168.1.2; local-as 65001; peer-as 65002; api { processes [ my-program ]; } } process my-program { run /etc/exabgp/api/announce.py; encoder text; } ``` With this configuration, your API program can selectively target either neighbor using selectors. --- ## Announce Commands Commands to advertise BGP routes. ### announce route **Announce IPv4/IPv6 unicast route** ```python announce route next-hop [attributes] ``` **Examples:** ```python # Basic IPv4 announce route 100.10.0.100/32 next-hop self # IPv6 announce route 2001:db8::1/128 next-hop 2001:db8::2 # With attributes announce route 100.10.0.100/32 next-hop self community [65001:100] announce route 100.10.0.100/32 next-hop self as-path [65001 65002] announce route 100.10.0.100/32 next-hop self local-preference 200 announce route 100.10.0.100/32 next-hop self med 50 ``` **Attributes:** - `next-hop self` - Use local router IP - `next-hop ` - Explicit next-hop - `community [:]` - BGP communities - `as-path [ ...]` - AS path - `local-preference ` - Local preference (higher = preferred) - `med ` - Multi-Exit Discriminator (lower = preferred) - `origin igp|egp|incomplete` - Origin attribute **See also:** - [Text API Reference - IPv4 Unicast](Text-API-Reference#ipv4-unicast) - [Text API Reference - IPv6 Unicast](Text-API-Reference#ipv6-unicast) --- ### announce flow **Announce FlowSpec rule** ```python announce flow route { match { } then { } } ``` **Examples:** ```python # Block TCP port 80 from 10.0.0.0/8 announce flow route { match { source 10.0.0.0/8; destination-port =80; protocol =tcp; } then { discard; } } # Rate-limit DNS announce flow route { match { destination-port =53; protocol =udp; } then { rate-limit 1000000; } } # Block SYN flood announce flow route { match { destination-port =443; tcp-flags [ syn ]; protocol =tcp; } then { discard; } } # Redirect to VRF announce flow route { match { destination 100.10.0.0/24; } then { redirect 65001:100; } } ``` **Match conditions:** - `source ` - Source IP - `destination ` - Destination IP - `protocol =` - IP protocol (tcp=6, udp=17, icmp=1) - `source-port ` - Source port - `destination-port ` - Destination port - `packet-length ` - Packet length - `tcp-flags [ ]` - TCP flags - `icmp-type =` - ICMP type - `icmp-code =` - ICMP code - `fragment [ ]` - Fragment flags - `dscp =` - DSCP value **Actions:** - `discard` - Drop packets - `rate-limit ` - Rate limit - `redirect ` - Redirect to VRF - `mark ` - Mark DSCP - `community [ ]` - Tag with community **See also:** - [FlowSpec Overview](FlowSpec-Overview) - [Text API Reference - FlowSpec](Text-API-Reference#flowspec-ipv4ipv6) - [Match Conditions Reference](Match-Conditions) - [Actions Reference](Actions-Reference) --- ### announce ipv4 mpls-vpn **Announce L3VPN IPv4 route** ```python announce ipv4 mpls-vpn next-hop route-distinguisher [attributes] ``` **Examples:** ```python # Basic VPNv4 announce ipv4 mpls-vpn 100.10.0.0/24 next-hop self route-distinguisher 65001:100 label 10000 # With label and route-target announce ipv4 mpls-vpn 100.10.0.0/24 next-hop self route-distinguisher 65001:100 label 10000 extended-community [ target:65001:100 ] ``` **See also:** - [Text API Reference - L3VPN](Text-API-Reference#l3vpn-vpnv4vpnv6) --- ### announce ipv6 mpls-vpn **Announce L3VPN IPv6 route** ```python announce ipv6 mpls-vpn next-hop route-distinguisher [attributes] ``` **Examples:** ```python announce ipv6 mpls-vpn 2001:db8::/32 next-hop 2001:db8::1 route-distinguisher 65001:100 label 10000 announce ipv6 mpls-vpn 2001:db8::/32 next-hop 2001:db8::1 route-distinguisher 65001:100 label 10000 extended-community [ target:65001:100 ] ``` **See also:** - [Text API Reference - L3VPN](Text-API-Reference#l3vpn-vpnv4vpnv6) --- ### announce ipv4 evpn **Announce EVPN route (via `announce ipv4` with l2vpn evpn family)** **EVPN MAC Advertisement:** ```python announce ipv4 evpn macadvertisement aa:bb:cc:dd:ee:ff 100.10.0.1 1000 rd 65001:100 route-target [ target:65001:100 ] ``` **EVPN Multicast:** ```python announce ipv4 evpn multicast 192.168.1.1 rd 65001:100 route-target [ target:65001:100 ] ``` **See also:** - [Text API Reference - EVPN](Text-API-Reference#evpn) --- ### announce vpls **Announce VPLS route** ```python announce vpls rd route-target [ ] ``` **Example:** ```python announce vpls 192.168.1.1 10 8 rd 65001:100 route-target [ target:65001:100 ] ``` **See also:** - [Text API Reference - VPLS](Text-API-Reference#vpls) --- ### announce refresh **Request route refresh from peer** ```python announce refresh ``` **Examples:** ```python # Request IPv4 unicast refresh announce refresh ipv4 unicast # Request FlowSpec refresh announce refresh ipv4 flow ``` **See also:** - [Operational Commands](#operational-commands) --- ## Withdraw Commands Commands to remove previously announced routes. ### withdraw route **Withdraw IPv4/IPv6 unicast route** ```python withdraw route next-hop ``` **Examples:** ```python # Withdraw IPv4 withdraw route 100.10.0.100/32 next-hop self # Withdraw IPv6 withdraw route 2001:db8::1/128 next-hop 2001:db8::2 # Alternative: withdraw by prefix only (if unique) withdraw route 100.10.0.100/32 ``` **Important:** Withdrawal must match announcement (prefix + next-hop + attributes). **See also:** - [Text API Reference - IPv4 Unicast](Text-API-Reference#ipv4-unicast) --- ### withdraw flow **Withdraw FlowSpec rule** ```python withdraw flow route { match { } } ``` **Examples:** ```python # Withdraw by exact match withdraw flow route { match { source 10.0.0.0/8; destination-port =80; protocol =tcp; } } # Auto-withdraw pattern import threading def auto_withdraw(rule, timeout=300): time.sleep(timeout) withdraw = rule.replace('announce', 'withdraw').replace('then { discard; }', '') sys.stdout.write(withdraw + "\n") sys.stdout.flush() rule = "announce flow route { match { source 10.0.0.0/8; } then { discard; } }" sys.stdout.write(rule + "\n") sys.stdout.flush() threading.Thread(target=auto_withdraw, args=(rule,)).start() ``` **See also:** - [FlowSpec Overview - Best Practices](FlowSpec-Overview#best-practices) --- ### withdraw ipv4 mpls-vpn **Withdraw L3VPN IPv4 route** ```python withdraw ipv4 mpls-vpn route-distinguisher ``` **Example:** ```python withdraw ipv4 mpls-vpn 100.10.0.0/24 route-distinguisher 65001:100 ``` --- ### withdraw ipv6 mpls-vpn **Withdraw L3VPN IPv6 route** ```python withdraw ipv6 mpls-vpn route-distinguisher ``` **Example:** ```python withdraw ipv6 mpls-vpn 2001:db8::/32 route-distinguisher 65001:100 ``` --- ### withdraw ipv4 evpn **Withdraw EVPN route (via `withdraw ipv4` with l2vpn evpn family)** ```python withdraw ipv4 evpn macadvertisement aa:bb:cc:dd:ee:ff 100.10.0.1 1000 rd 65001:100 ``` --- ### withdraw vpls **Withdraw VPLS route** ```python withdraw vpls 192.168.1.1 10 8 rd 65001:100 ``` --- ## Operational Commands Commands for BGP session control and monitoring. ### shutdown **Gracefully shutdown ExaBGP** ```python shutdown ``` **Example:** ```python import signal import sys def signal_handler(sig, frame): sys.stdout.write("shutdown\n") sys.stdout.flush() sys.exit(0) signal.signal(signal.SIGTERM, signal_handler) ``` **Effect:** - Sends BGP NOTIFICATION to all peers - Closes connections gracefully - Exits ExaBGP process --- ### restart **Restart BGP connection to peer** ```python restart ``` **Example:** ```python # Restart after configuration change sys.stdout.write("restart\n") sys.stdout.flush() ``` **Effect:** - Closes existing connections - Re-reads configuration - Establishes new connections --- ### reload **Reload configuration without restarting** ```python reload ``` **Effect:** - Re-reads configuration file - Applies changes without dropping sessions (if possible) --- ### version **Query ExaBGP version** ```python version ``` **Response (on STDIN):** ``` exabgp 4.2.25 ``` --- ## Control Commands Commands for flow control and debugging. ### flush **Flush pending route updates** ```python flush ``` **Use case:** ```python # Announce multiple routes, then flush for prefix in prefixes: sys.stdout.write(f"announce route {prefix} next-hop self\n") sys.stdout.write("flush\n") sys.stdout.flush() ``` **Effect:** Forces ExaBGP to send pending BGP UPDATEs immediately. --- ### enable-ack **Re-enable ACK responses (ExaBGP 5.0.0+, safe on 4.x)** ```python enable-ack ``` **Behavior:** - ✅ **ExaBGP 5.0.0+**: Sends "done" ACK, re-enables ACK for future commands - âš ī¸ **ExaBGP 4.x**: Prints warning but continues (safe, no harm) - ✅ All future commands will receive ACK responses (`done` or `error`) **Use case:** ```python # Re-enable error checking after fire-and-forget mode sys.stdout.write("enable-ack\n") sys.stdout.flush() # Check for "done" response to confirm ACK is re-enabled # Now all future commands will receive ACK ``` --- ### disable-ack **Disable ACK responses gracefully (ExaBGP 5.0.0+, safe on 4.x)** ```python disable-ack ``` **Behavior:** - ✅ **ExaBGP 5.0.0+**: Sends final "done" ACK, then disables future ACKs - âš ī¸ **ExaBGP 4.x**: Prints warning but continues (safe, no harm) - ❌ Future commands will NOT receive ACK responses (fire-and-forget mode) **Use cases:** 1. **Transition to high-performance mode** 2. **Best practice for non-ACK-aware programs on ExaBGP 5.0.0+** **Example:** ```python # Transition to high-performance mode sys.stdout.write("disable-ack\n") sys.stdout.flush() # Receive final "done" ACK confirming ACK is now disabled # Future commands are fire-and-forget (no 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() # No ACK expected - maximum performance ``` --- ### silence-ack **Disable ACK responses immediately (ExaBGP 5.0.0+, safe on 4.x)** ```python silence-ack ``` **Behavior:** - ❌ **ExaBGP 5.0.0+**: Does NOT send ACK for this command (immediate silence) - âš ī¸ **ExaBGP 4.x**: Prints warning but continues (safe, no harm) - ❌ Future commands will NOT receive ACK responses (fire-and-forget mode) **Use case:** ```python # Maximum performance - skip even the final ACK sys.stdout.write("silence-ack\n") sys.stdout.flush() # No ACK expected for silence-ack itself # All future commands are fire-and-forget (no 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() ``` **Comparison:** | Command | Sends ACK for itself? | Future commands ACKed? | |---------|----------------------|------------------------| | `enable-ack` | ✅ Yes ("done") | ✅ Yes | | `disable-ack` | ✅ Yes (final "done") | ❌ No | | `silence-ack` | ❌ No (immediate silence) | ❌ No | **Version compatibility:** - ExaBGP 4.x: Commands are ignored with warning (safe, no harm) - ExaBGP 5.x/main: ACK can be controlled dynamically with these runtime commands **Best practice for non-ACK-aware programs:** If you're using ExaBGP 5.x/main with API programs that don't handle ACK responses, send `disable-ack` at startup: ```python #!/usr/bin/env python3 import sys import time # Safe on all ExaBGP versions (5.x disables, 4.x warns but continues) sys.stdout.write("disable-ack\n") sys.stdout.flush() time.sleep(0.1) # 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 both methods: ```bash # Set environment variable (works on 4.x and 5.x) export exabgp.api.ack=false # AND send disable-ack in your program (defense-in-depth) ``` **See also:** - [API Overview - Dynamic ACK Control](API-Overview#dynamic-ack-control-runtime-commands) - [API Overview - Best Practice: Non-ACK-Aware Programs](API-Overview#best-practice-non-ack-aware-programs-on-exabgp-5xmain) - [Text API Reference - ACK Control Commands](Text-API-Reference#ack-control-commands-exabgp-5xmain) --- ## Group Commands (6.0.0+) **ExaBGP 6.0.0** introduces **group commands** for batching multiple announcements into single UPDATE messages. This enables: - **Exact wire-format reproduction** for multi-NLRI UPDATEs - **Atomic updates** (all-or-nothing) - **Reduced UPDATE count** for bulk operations - **RIB-level batching** - routes with same attributes are automatically combined ### Single-Line Group Batch multiple commands on a single line, separated by semicolons: ```python # Announce multiple routes in one UPDATE print("group announce route 10.0.0.0/24 next-hop self ; announce route 10.0.1.0/24 next-hop self") # Mix announce and withdraw print("group announce route 10.0.0.0/24 next-hop self ; withdraw route 192.168.0.0/24") ``` ### Multi-Line Group Use `group start` and `group end` to buffer commands across multiple lines: ```python import sys # Start grouping print("group start", flush=True) # Buffer commands (they are NOT sent yet) print("announce route 10.0.0.0/24 next-hop 192.0.2.1", flush=True) print("announce route 10.0.1.0/24 next-hop 192.0.2.1", flush=True) print("announce route 10.0.2.0/24 next-hop 192.0.2.1", flush=True) print("withdraw route 172.16.0.0/16", flush=True) # End grouping - all commands processed together print("group end", flush=True) ``` ### Group with Shared Attributes Use `attributes` as the first command to set shared attributes for subsequent commands: ```python # All routes get the same attributes print("group attributes med 100 community [ 65001:100 ] ; announce route 10.0.0.0/24 next-hop self ; announce route 10.0.1.0/24 next-hop self") ``` ### Response Format **Text response:** ``` group processed: 3 announced, 1 withdrawn ``` **JSON response:** ```json {"status": "group processed", "announced": 3, "withdrawn": 1} ``` **With errors:** ```json {"status": "group processed", "announced": 2, "withdrawn": 1, "errors": ["could not parse: invalid route"]} ``` ### Group Command Reference | Command | Description | |---------|-------------| | `group start` | Begin buffering commands | | `group end` | Process all buffered commands | | `group ; ; ...` | Single-line batch (semicolon-separated) | ### Important Notes - **No nested groups** - `group start` inside another group is an error - **Sync mode** - Determined from the first command in the group - **Errors don't stop processing** - Other commands continue even if one fails - **RIB batching** - Routes with identical attributes are automatically combined into single UPDATEs ### When to Use Groups ✅ **Use groups when:** - Announcing many routes with the same attributes - You need atomic updates (all succeed or visible together) - Reducing the number of UPDATE messages matters - Reproducing exact wire formats for testing ❌ **Don't use groups when:** - Announcing single routes - Each route has different attributes (no batching benefit) - You need individual error handling per route --- ## Quick Reference Table | Command | Purpose | Example | |---------|---------|---------| | `announce route` | Announce IPv4/IPv6 route | `announce route 1.1.1.1/32 next-hop self` | | `announce flow` | Announce FlowSpec rule | `announce flow route { match { ... } then { ... } }` | | `announce ipv4 mpls-vpn` | Announce L3VPN IPv4 | `announce ipv4 mpls-vpn 100.10.0.0/24 next-hop self route-distinguisher 65001:100 label 10000` | | `announce ipv6 mpls-vpn` | Announce L3VPN IPv6 | `announce ipv6 mpls-vpn 2001:db8::/32 next-hop 2001:db8::1 route-distinguisher 65001:100 label 10000` | | `announce ipv4 evpn` | Announce EVPN route | `announce ipv4 evpn macadvertisement ...` | | `announce vpls` | Announce VPLS route | `announce vpls 192.168.1.1 ...` | | `announce refresh` | Request route refresh | `announce refresh ipv4 unicast` | | `withdraw route` | Withdraw IPv4/IPv6 route | `withdraw route 1.1.1.1/32` | | `withdraw flow` | Withdraw FlowSpec rule | `withdraw flow route { match { ... } }` | | `withdraw ipv4 mpls-vpn` | Withdraw L3VPN IPv4 | `withdraw ipv4 mpls-vpn 100.10.0.0/24 route-distinguisher 65001:100` | | `withdraw ipv6 mpls-vpn` | Withdraw L3VPN IPv6 | `withdraw ipv6 mpls-vpn 2001:db8::/32 route-distinguisher 65001:100` | | `withdraw ipv4 evpn` | Withdraw EVPN route | `withdraw ipv4 evpn macadvertisement ...` | | `withdraw vpls` | Withdraw VPLS route | `withdraw vpls 192.168.1.1 ...` | | `shutdown` | Gracefully shutdown | `shutdown` | | `restart` | Restart BGP session | `restart` | | `reload` | Reload configuration | `reload` | | `version` | Query version | `version` | | `flush` | Flush pending updates | `flush` | | `enable-ack` | Re-enable ACK responses (5.x/main, safe on 4.x) | `enable-ack` | | `disable-ack` | Disable ACK gracefully (5.x/main, safe on 4.x) | `disable-ack` | | `silence-ack` | Disable ACK immediately (5.x/main, safe on 4.x) | `silence-ack` | | `group start` | Start buffering commands (6.0.0+) | `group start` | | `group end` | Process buffered commands (6.0.0+) | `group end` | | `group ` | Single-line batch (6.0.0+) | `group announce ... ; announce ...` | --- ## Command Acknowledgment (ACK) ### ACK Feature (Enabled by Default) **ExaBGP 4.x and 5.x** send command acknowledgments via **STDIN** (enabled by default). **Response types:** ```python done # Command succeeded error # Command failed shutdown # ExaBGP is shutting down ``` **Robust pattern with polling loop:** ```python #!/usr/bin/env python3 import sys import select import time def wait_for_ack(expected_count=1, timeout=30): """ Wait for ACK responses with polling loop. ExaBGP may not respond immediately, so we poll with sleep. Handles both text and JSON encoder formats: - Text: "done", "error", "shutdown" - JSON: {"answer": "done|error|shutdown", "message": "..."} """ import json received = 0 start_time = time.time() while received < expected_count: if time.time() - start_time >= timeout: return False 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('{'): try: data = json.loads(line) answer = data.get('answer') except: pass else: answer = line if answer == "done": received += 1 elif answer == "error": return False elif answer == "shutdown": raise SystemExit(0) else: 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) # Use it send_command("announce route 100.10.0.100/32 next-hop self") ``` **Configuration:** ```ini [exabgp.api] ack = true # Default in 5.x/main ``` **See also:** - [API Overview - Command Acknowledgment](API-Overview#command-acknowledgment-ack-feature) --- ### ExaBGP 4.x (No ACK) **ExaBGP 4.x does NOT send acknowledgments.** **Pattern (fire-and-forget):** ```python #!/usr/bin/env python3 import sys import time # Send commands (no ACK) sys.stdout.write("announce route 100.10.0.100/32 next-hop self\n") sys.stdout.flush() # Add delay to avoid overwhelming ExaBGP time.sleep(0.1) ``` **Issues:** - ❌ No confirmation of success/failure - ❌ Hard to debug command errors - ❌ Can't detect ExaBGP shutdown **Upgrade to ExaBGP 5.x/main for ACK support.** --- ## Common Errors ### Error: Command not sent **Symptom:** Routes not announced, no error message. **Cause:** Forgot to flush STDOUT. **Fix:** ```python # ❌ WRONG sys.stdout.write("announce route 100.10.0.100/32 next-hop self\n") # Buffer not flushed! # ✅ CORRECT sys.stdout.write("announce route 100.10.0.100/32 next-hop self\n") sys.stdout.flush() # Always flush! ``` --- ### Error: Syntax error in command **Symptom (ExaBGP 5.x):** `error` response. **Cause:** Invalid command syntax. **Examples:** ```python # ❌ Missing next-hop announce route 100.10.0.100/32 # ✅ Correct announce route 100.10.0.100/32 next-hop self # ❌ Invalid FlowSpec syntax announce flow route { match { source 10.0.0.0/8 } } # Missing 'then' # ✅ Correct announce flow route { match { source 10.0.0.0/8; } then { discard; } } ``` **Fix:** Check command syntax against this reference. --- ### Error: Withdrawal doesn't match **Symptom:** Route not withdrawn. **Cause:** Withdrawal doesn't match announcement exactly. **Example:** ```python # Announce with community announce route 100.10.0.100/32 next-hop self community [65001:100] # ❌ Withdraw without community (doesn't match) withdraw route 100.10.0.100/32 next-hop self # ✅ Withdraw with same attributes withdraw route 100.10.0.100/32 next-hop self community [65001:100] ``` **Fix:** Withdrawal must match all attributes from announcement. --- ### Error: Process hangs waiting for input **Symptom (ExaBGP 5.x):** Process blocks after sending command. **Cause:** Not reading ACK response from STDIN. **Fix:** ```python # ❌ WRONG (blocks forever) sys.stdout.write("announce route 100.10.0.100/32 next-hop self\n") sys.stdout.flush() # ExaBGP waiting to send 'done', but we're not reading! # ✅ CORRECT sys.stdout.write("announce route 100.10.0.100/32 next-hop self\n") sys.stdout.flush() # Read ACK response = sys.stdin.readline().strip() # or use select() ``` --- ### Error: Too many commands too fast **Symptom:** Routes not announced, ExaBGP unresponsive. **Cause:** Overwhelming ExaBGP with rapid commands. **Fix:** ```python # ❌ WRONG (too fast) for prefix in range(1000): sys.stdout.write(f"announce route 10.0.{prefix}.0/24 next-hop self\n") sys.stdout.flush() # ✅ CORRECT (batched) for prefix in range(1000): sys.stdout.write(f"announce route 10.0.{prefix}.0/24 next-hop self\n") if prefix % 100 == 0: sys.stdout.flush() time.sleep(0.1) # Give ExaBGP time to process sys.stdout.flush() ``` **Or use flush command:** ```python for prefix in range(1000): sys.stdout.write(f"announce route 10.0.{prefix}.0/24 next-hop self\n") sys.stdout.write("flush\n") sys.stdout.flush() ``` --- ## Next Steps ### Learn More - **[Text API Reference](Text-API-Reference)** - Detailed examples for all address families - **[JSON API Reference](JSON-API-Reference)** - Receiving BGP messages from ExaBGP - **[API Overview](API-Overview)** - API architecture and patterns - **[Configuration Syntax](Configuration-Syntax)** - Process configuration ### Examples - **[Quick Start](Quick-Start)** - First API script - **[FlowSpec Overview](FlowSpec-Overview)** - FlowSpec examples ### Production - **[Production Best Practices](Production-Best-Practices)** - Error handling, logging, monitoring - **[Health Checks Guide](Health-Checks)** - Health check patterns --- **Need help?** Join the [ExaBGP Slack](https://exabgp.slack.com/) or check [GitHub Discussions](https://github.com/Exa-Networks/exabgp/discussions) → ---