# Text API Reference **Complete reference for ExaBGP's text-based API commands** --- ## Table of Contents - [Overview](#overview) - [Command Syntax](#command-syntax) - [Neighbor Selectors](#neighbor-selectors) - [IPv4/IPv6 Unicast](#ipv4ipv6-unicast) - [Route Attributes](#route-attributes) - [Bulk Announcements (announce attributes ... nlri)](#bulk-announcements-announce-attributes--nlri) - [FlowSpec](#flowspec) - [L3VPN](#l3vpn) - [EVPN](#evpn) - [BGP-LS](#bgp-ls) - [VPLS](#vpls) - [Operational Commands](#operational-commands) - [Refresh Routes](#refresh-routes) - [Operational Messages](#operational-messages) - [ACK Control Commands (ExaBGP 5.x/main)](#ack-control-commands-exabgp-5xmain) - [Examples by Use Case](#examples-by-use-case) - [Common Errors](#common-errors) --- ## Overview The **text encoder** uses human-readable commands for announcing and withdrawing routes. **Configuration:** ```ini process my-program { run /etc/exabgp/api/announce.py; encoder text; } ``` **Usage in your program:** ```python import sys # Announce a route sys.stdout.write("announce route 100.10.0.0/24 next-hop self\n") sys.stdout.flush() # CRITICAL - always flush! # Withdraw a route sys.stdout.write("withdraw route 100.10.0.0/24\n") sys.stdout.flush() ``` --- ## Command Syntax ### Basic Structure ``` [attributes] ``` **Actions:** - `announce` - Add/update a route - `withdraw` - Remove a route **Families:** - `route` - IPv4/IPv6 unicast - `flow` - FlowSpec - `vpn` - L3VPN - `evpn` - EVPN ### Critical Rules 1. **Always end with newline** (`\n`) 2. **Always flush after write** (`sys.stdout.flush()`) 3. **One command per line** 4. **Case-sensitive** (`announce` not `ANNOUNCE`) --- ## Neighbor Selectors When ExaBGP is configured with **multiple BGP neighbors**, you can use **neighbor selectors** to target specific peers with your API commands. ### Syntax ``` neighbor [selector-options] ``` **By default, commands apply to all configured neighbors.** Use the `neighbor` keyword to target specific peer(s) instead of broadcasting to all neighbors. ### Available Selectors | Selector | Description | Example | |----------|-------------|---------| | `neighbor ` | **Required**: Target 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` | ### Examples **Single neighbor:** ```python # 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):** ```python # Announce to ALL configured neighbors print("neighbor * announce route 10.0.0.0/24 next-hop self") ``` **Multiple neighbors (comma-separated):** ```python # 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:** ```python # 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:** ```python # 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:** ```python # 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:** ```python # Target neighbors with FlowSpec support print("neighbor * family-allowed ipv4-flowspec announce flow route { match { destination 10.0.0.0/8; } then { discard; } }") # Target multi-session neighbors with dual-stack print("neighbor * family-allowed ipv4-unicast/ipv6-unicast announce route 10.0.0.0/24 next-hop self") # Target single-session neighbors print("neighbor * family-allowed in-open announce route 10.0.0.0/24 next-hop self") ``` **Note:** The `family-allowed` value is: - `in-open` for single-session neighbors (default - all families in BGP OPEN) - Slash-separated families for multi-session neighbors (e.g., `ipv4-unicast/ipv6-unicast/ipv4-flowspec`) ### Complete Example ```python #!/usr/bin/env python3 """ selective_announcement.py - Announce routes to specific neighbors """ import sys import time time.sleep(2) # Wait for BGP sessions # Announce to all neighbors sys.stdout.write("neighbor * announce route 100.10.0.0/24 next-hop self\n") sys.stdout.flush() # Announce specific route to peer in AS 65000 sys.stdout.write("neighbor * peer-as 65000 announce route 100.20.0.0/24 next-hop self\n") sys.stdout.flush() # Announce with AS path prepending to one neighbor sys.stdout.write("neighbor 192.168.1.1 announce route 100.30.0.0/24 next-hop self as-path [65001 65001 65001]\n") sys.stdout.flush() # Announce different routes to different providers sys.stdout.write("neighbor 10.0.1.1 announce route 100.40.0.0/24 next-hop self community [65001:100]\n") # Provider A sys.stdout.flush() sys.stdout.write("neighbor 10.0.2.1 announce route 100.40.0.0/24 next-hop self community [65001:200]\n") # Provider B sys.stdout.flush() while True: time.sleep(60) ``` ### FlowSpec with Neighbor Selectors ```python # Announce FlowSpec rule to specific neighbor print("neighbor 192.168.1.1 announce flow route { match { destination 10.0.0.0/8; } then { discard; } }") # Announce FlowSpec to all neighbors print("neighbor * announce flow route { match { source 203.0.113.0/24; destination-port =80; protocol =tcp; } then { discard; } }") # Target specific peer-as for DDoS mitigation print("neighbor * peer-as 65000 announce flow route { match { destination 100.10.0.0/24; } then { rate-limit 1000000; } }") ``` ### Withdraw with Neighbor Selectors ```python # Withdraw from specific neighbor print("neighbor 192.168.1.1 withdraw route 10.0.0.0/24") # Withdraw from all neighbors print("neighbor * withdraw route 10.0.0.0/24") # Withdraw from multiple neighbors print("neighbor 10.0.0.1,10.0.0.2 withdraw route 100.10.0.0/24") ``` ### Important Notes - 💡 **Default behavior**: Commands without `neighbor` selector apply to **all configured neighbors** - 💡 **Selective targeting**: Use `neighbor` keyword to target specific peer(s) - 💡 Selectors use **AND logic** - all specified selectors must match - 💡 `neighbor *` explicitly targets **all configured neighbors** (same as omitting selector) - 💡 If no neighbor matches your selectors, the command is silently ignored - 💡 Comma-separated IPs are treated as **OR** - any match succeeds --- ## IPv4/IPv6 Unicast ### IPv4 Unicast ### Basic Announcement **IPv4:** ```python # IPv4 prefix with explicit next-hop print("announce route 100.10.0.0/24 next-hop 192.0.2.1") # /32 host route print("announce route 100.10.0.100/32 next-hop 192.0.2.1") ``` ### IPv6 Unicast **IPv6:** ```python # IPv6 prefix print("announce route 2001:db8::/32 next-hop 2001:db8::1") # IPv6 /128 host route print("announce route 2001:db8::100/128 next-hop 2001:db8::1") ``` ### Withdrawal ```python # IPv4 print("withdraw route 100.10.0.0/24") # IPv6 print("withdraw route 2001:db8::/32") # Note: No need to specify next-hop or attributes when withdrawing ``` --- ## Route Attributes ### Next-Hop > **⚠️ Warning**: `next-hop self` is an EXPERIMENTAL feature. Always use explicit next-hop IP addresses in production. **Explicit next-hop (recommended):** ```python 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:** ```python # Tell peer to use different next-hop print("announce route 100.10.0.0/24 next-hop 10.0.0.1") ``` --- ### MED (Multi-Exit Discriminator) Lower MED is preferred. Use for traffic engineering. ```python # Preferred path (lower MED) print("announce route 100.10.0.0/24 next-hop self med 50") # Backup path (higher MED) print("announce route 100.10.0.0/24 next-hop self med 100") ``` --- ### Local Preference Higher local-preference is preferred. iBGP only. ```python # Preferred path (higher local-pref) print("announce route 100.10.0.0/24 next-hop self local-preference 200") # Less preferred print("announce route 100.10.0.0/24 next-hop self local-preference 100") ``` --- ### AS Path **Normal announcement:** ```python print("announce route 100.10.0.0/24 next-hop self") # AS path: [ 65001 ] (your local-as) ``` **AS path prepending:** ```python # 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:** ```python # Announce as if from different AS print("announce route 100.10.0.0/24 next-hop self as-path [ 65002 65003 ]") ``` --- ### Communities **Standard communities (RFC 1997):** ```python # 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):** ```python # 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):** ```python # Format: global:local1:local2 print("announce route 100.10.0.0/24 next-hop self large-community [ 65001:100:200 ]") ``` --- ### Origin ```python # IGP origin (default) print("announce route 100.10.0.0/24 next-hop self origin igp") # EGP origin print("announce route 100.10.0.0/24 next-hop self origin egp") # Incomplete origin print("announce route 100.10.0.0/24 next-hop self origin incomplete") ``` --- ### Combining Attributes ```python # Complex announcement with multiple attributes print("announce route 100.10.0.0/24 next-hop self " "med 100 " "local-preference 200 " "community [ 65001:100 65001:200 ] " "as-path [ 65001 ]") ``` --- ## Bulk Announcements (announce attributes ... nlri) > **Version Availability**: ExaBGP 4.0+ **Purpose**: Announce multiple prefixes with the same attributes in a single command, reducing parsing overhead and improving performance. **Syntax**: ``` announce attribute[s] nlri ... announce attributes nlri ... ``` **Both singular `attribute` and plural `attributes` work identically.** ### Basic Usage **Announce multiple prefixes with same next-hop and MED**: ```python # 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**: ```python print("announce attributes local-preference 200 as-path [ 1 2 3 4 ] next-hop 202.2.202.2 nlri 2.0.0.1/32 2.0.0.2/32") ``` ### Advanced: VPN with Labels ```python # L3VPN with route-distinguisher and label print("announce attribute route-distinguisher 63333:100 " "label [ 110 ] " "next-hop 10.0.99.12 " "origin igp " "as-path [100, 500] " "local-preference 100 " "extended-community 0:0 " "originator-id 10.0.99.12 " "nlri 128.0.64.0/18 128.0.0.0/18") ``` ### Bulk Announcements (Hundreds of Prefixes) **Efficient for announcing many prefixes**: ```python #!/usr/bin/env python3 import sys # Build list of prefixes prefixes = [] for ip in range(0, 224): prefixes.append(f"{ip}.0.0.0/8") # Announce all prefixes with same attributes in ONE command nlri_list = ' '.join(prefixes) command = f"announce attribute next-hop 1.2.3.4 med 100 as-path [ 100 101 102 ] nlri {nlri_list}" sys.stdout.write(command + "\n") sys.stdout.flush() ``` **This announces 224 prefixes in a single command** instead of 224 separate `announce route` commands. ### Performance Benefits **Without bulk announcement (slow)**: ```python # 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)**: ```python # 1 command for 1000 prefixes prefixes = [f"10.{i//256}.{i%256}.0/24" for i in range(1000)] print(f"announce attributes next-hop 192.0.2.1 med 100 nlri {' '.join(prefixes)}") sys.stdout.flush() ``` **Performance gain**: ~10-100x faster for bulk operations (reduces parsing overhead, fewer command round-trips). ### Withdrawal (Bulk) **Syntax**: ``` withdraw attribute[s] nlri ... ``` **Withdraw multiple prefixes**: ```python # Withdraw multiple prefixes print("withdraw attributes nlri 1.0.0.1/32 1.0.0.2/32") ``` **With next-hop (if required)**: ```python # Some configurations require next-hop for withdrawal print("withdraw attributes next-hop 101.1.101.1 nlri 2.0.0.1/32 2.0.0.2/32") ``` ### When to Use Bulk Announcements ✅ **Use bulk announcements when**: - Announcing 10+ prefixes with identical attributes - Building initial full BGP table (thousands of routes) - Performance-critical applications - High route churn environments ❌ **Use individual `announce route` when**: - Prefixes have different attributes - Announcing 1-5 routes - Attributes differ per prefix - Debugging (easier to see individual commands) ### Examples **Example 1: Anycast service announcement**: ```python # 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)**: ```python # 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)**: ```python # Announce healthy backends only healthy_backends = check_health() # Returns list of IPs if healthy_backends: prefixes = [f"{ip}/32" for ip in healthy_backends] print(f"announce attributes next-hop self local-preference 200 nlri {' '.join(prefixes)}") ``` ### Common Errors **❌ Wrong: Mixing attributes per prefix** ```python # 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** ```python # 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** ```python # 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** ```python print("announce attributes next-hop 1.2.3.4 nlri 10.0.0.0/24 20.0.0.0/24") ``` ### Compatibility | Feature | ExaBGP 3.x | ExaBGP 4.x | ExaBGP 5.x | |---------|-----------|-----------|-----------| | `announce attribute[s] ... nlri` | ❌ Not available | ✅ Available | ✅ Available | | `withdraw attribute[s] ... nlri` | ❌ Not available | ✅ Available | ✅ Available | **Migration note**: If upgrading from 3.x, you can now use bulk announcements to improve performance. --- ## FlowSpec (IPv4/IPv6) FlowSpec allows BGP-based traffic filtering. ExaBGP **pioneered open-source FlowSpec support** (now also in GoBGP, FRRouting, BIRD). ### Basic Structure ```python announce flow route { match { } then { } } ``` ### Match Conditions **Destination prefix:** ```python print("announce flow route { " "match { destination 100.10.0.0/24; } " "then { discard; } " "}") ``` **Source prefix:** ```python print("announce flow route { " "match { source 10.0.0.0/8; } " "then { discard; } " "}") ``` **Ports:** ```python # 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:** ```python # 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:** ```python # 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:** ```python print("announce flow route { " "match { packet-length >=1000; } " "then { discard; } " "}") ``` **ICMP type/code:** ```python # 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):** ```python print("announce flow route { " "match { dscp =46; } " "then { discard; } " "}") ``` **Fragment:** ```python # 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; } " "}") ``` ### Multiple Match Conditions (AND logic) ```python # Block SYN floods to port 80 from 10.0.0.0/8 print("announce flow route { " "match { " "source 10.0.0.0/8; " "destination-port =80; " "protocol =tcp; " "tcp-flags [ syn ]; " "} " "then { discard; } " "}") ``` ### Actions **Discard (drop):** ```python print("announce flow route { " "match { destination 100.10.0.0/24; } " "then { discard; } " "}") ``` **Rate-limit:** ```python # 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:** ```python print("announce flow route { " "match { destination 100.10.0.0/24; } " "then { redirect 65001:100; } " "}") ``` **Mark (DSCP remarking):** ```python print("announce flow route { " "match { destination 100.10.0.0/24; } " "then { mark 46; } " "}") ``` **Community (tagging):** ```python print("announce flow route { " "match { destination 100.10.0.0/24; } " "then { community [ 65001:666 ]; } " "}") ``` ### FlowSpec Withdrawal ```python # Withdraw by match conditions print("withdraw flow route { " "match { destination 100.10.0.0/24; } " "}") ``` ### FlowSpec Examples **Block DDoS attack:** ```python # 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:** ```python # 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):** ```python # Drop packets > 1500 bytes print("announce flow route { " "match { packet-length >1500; } " "then { discard; } " "}") ``` --- ## L3VPN (VPNv4/VPNv6) Layer 3 VPN (RFC 4364) for MPLS VPNs. ### Announcement ```python # 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") ``` ### Withdrawal ```python print("withdraw ipv4 mpls-vpn 100.10.0.0/24 route-distinguisher 65001:100") ``` --- ## EVPN Ethernet VPN (RFC 7432) for data center fabrics. > **Note:** The text API does not have a dedicated `announce evpn` command. EVPN routes are typically configured via the ExaBGP configuration file, not the text API. The examples below show the configuration file syntax for reference. ### EVPN Route Type 2 (MAC/IP Advertisement) - Configuration File **MAC only:** ```ini 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:** ```ini 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):** ```ini 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 ]; } ``` ### EVPN Route Type 3 (Inclusive Multicast) - Configuration File ```ini route-type multicast 100.10.0.1 { route-distinguisher 65001:100; next-hop self; extended-community [ target:65001:100 ]; } ``` ### Note on EVPN via the Text API 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](Configuration-Syntax) for details. --- ## BGP-LS BGP Link-State (RFC 7752) for topology collection. **Note:** BGP-LS is typically receive-only in ExaBGP. Used for collecting topology information from IGP. **Configuration to receive BGP-LS:** ```ini neighbor 192.168.1.1 { router-id 192.168.1.2; local-address 192.168.1.2; local-as 65001; peer-as 65000; family { ipv4 link-state; ipv6 link-state; } api { processes [ receive-topology ]; } } ``` **No text API for announcing BGP-LS** - it's receive-only. --- ## VPLS Virtual Private LAN Service (L2VPN). ```python # VPLS route print("announce vpls " "route-distinguisher 65001:100 " "endpoint 192.168.1.2 " "offset 10 " "size 8 " "base 10000 " "extended-community [ target:65001:100 ]") ``` ### Withdrawal ```python print("withdraw vpls route-distinguisher 65001:100") ``` --- ## Multicast Multicast routing (IPv4 and IPv6 multicast). ### IPv4 Multicast ```python # Announce IPv4 multicast route print("announce route 239.1.1.0/24 next-hop self") # With source (S,G multicast) print("announce route 239.1.1.0/24 next-hop self") ``` ### IPv6 Multicast ```python # Announce IPv6 multicast route print("announce route ff02::1/128 next-hop 2001:db8::1") ``` **Note**: Multicast configuration requires appropriate address family configuration in ExaBGP config file. --- ## RT-Constraint Route Target Constraint (RFC 4684) for efficient VPN route distribution. ### Announcement ```python # Announce RT membership (I'm interested in routes with this RT) print("announce rtc 65001:100 next-hop self") # Withdraw RT membership print("withdraw rtc 65001:100") ``` **Use case**: Reduces unnecessary VPN route advertisements by signaling which route targets you're interested in. --- ## Withdraw Commands ### IPv4/IPv6 Unicast Withdrawal ```python # IPv4 print("withdraw route 100.10.0.0/24") # IPv6 print("withdraw route 2001:db8::/32") ``` ### FlowSpec Withdrawal ```python # Withdraw FlowSpec rule by match conditions print("withdraw flow route { match { destination 100.10.0.0/24; } }") ``` ### L3VPN Withdrawal ```python print("withdraw ipv4 mpls-vpn 100.10.0.0/24 route-distinguisher 65001:100") ``` ### EVPN Withdrawal > **Note:** EVPN routes cannot be withdrawn via the text API. EVPN is configured via the ExaBGP configuration file. See the [EVPN section](#evpn) above. ### Bulk Withdrawal ```python # Withdraw multiple prefixes at once print("withdraw attributes nlri 100.10.0.0/24 100.20.0.0/24 100.30.0.0/24") ``` --- ## Operational Commands ### Refresh Routes Force route refresh (send all routes again): ```python print("announce route-refresh ipv4 unicast") print("announce route-refresh ipv6 unicast") ``` ### Operational Messages Send operational messages: ```python # Query operational state print("operational afi ipv4 safi unicast advisory rib in") ``` ### ACK Control Commands (ExaBGP 5.x/main) Dynamically control command acknowledgment behavior at runtime: ```python # 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** ```python import sys import time # Step 1: Verify ExaBGP is ready (with ACK) sys.stdout.write("announce route 10.0.0.1/32 next-hop self\n") sys.stdout.flush() # Check for "done" response... # Step 2: Disable ACK for bulk operation (10,000 routes) sys.stdout.write("silence-ack\n") # Immediate silence for max performance sys.stdout.flush() # Step 3: Bulk announce without ACK overhead for i in range(10000): prefix = f"10.{i//256}.{i%256}.0/24" sys.stdout.write(f"announce route {prefix} next-hop self\n") sys.stdout.flush() # Step 4: Re-enable ACK to verify completion sys.stdout.write("enable-ack\n") sys.stdout.flush() # Check for "done" to confirm ExaBGP is responsive ``` **Version compatibility:** - ExaBGP 4.x: Commands are ignored with warning (safe, no harm) - ExaBGP 5.x/main: ACK can be controlled dynamically at runtime with these commands **Best practice for non-ACK-aware programs:** Use `disable-ack` at the start of your program when running on ExaBGP 5.x/main: ```python #!/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: ```bash # Combine environment variable + disable-ack command export exabgp.api.ack=false # Works on 4.x and 5.x exabgp /etc/exabgp/exabgp.conf ``` See also: - [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) --- ## Examples by Use Case ### High Availability with Anycast **Announce service IP when healthy:** ```python import sys import time import socket SERVICE_IP = "100.10.0.100" SERVICE_PORT = 80 def is_healthy(): try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(2) result = sock.connect_ex((SERVICE_IP, SERVICE_PORT)) sock.close() return result == 0 except: return False time.sleep(2) announced = False while True: if is_healthy(): if not announced: sys.stdout.write(f"announce route {SERVICE_IP}/32 next-hop self\n") sys.stdout.flush() announced = True else: if announced: sys.stdout.write(f"withdraw route {SERVICE_IP}/32\n") sys.stdout.flush() announced = False time.sleep(5) ``` --- ### DDoS Mitigation (FlowSpec) **Auto-block detected attacks:** ```python import sys import time def detect_attack(): # Your DDoS detection logic # Returns (source_ip, dest_port, protocol) if attack detected return ("10.0.0.0/8", 80, "tcp") time.sleep(2) while True: attack = detect_attack() if attack: source, port, proto = attack # Block the attack sys.stdout.write( f"announce flow route {{ " f"match {{ source {source}; destination-port ={port}; protocol ={proto}; }} " f"then {{ discard; }} " f"}}\n" ) sys.stdout.flush() sys.stderr.write(f"[BLOCKED] Attack from {source} to port {port}\n") time.sleep(1) ``` --- ### Load Balancing with Metrics **Announce with different metrics for load distribution:** ```python import sys import time time.sleep(2) # This server gets most traffic (lower MED) sys.stdout.write("announce route 100.10.0.100/32 next-hop self med 100\n") sys.stdout.flush() # Another server as backup (higher MED) # On the other server, announce with med 200 ``` --- ### Traffic Engineering **Prefer specific path with communities:** ```python import sys import time time.sleep(2) # Mark route with community for policy sys.stdout.write( "announce route 100.10.0.0/24 next-hop self " "community [ 65001:100 ] " "local-preference 200\n" ) sys.stdout.flush() ``` --- ## Common Errors ### 1. Forgot to Flush **Wrong:** ```python print("announce route 100.10.0.0/24 next-hop self") # Nothing happens - stuck in buffer ``` **Correct:** ```python print("announce route 100.10.0.0/24 next-hop self") sys.stdout.flush() # Now it works ``` --- ### 2. Missing next-hop **Wrong:** ```python print("announce route 100.10.0.0/24") # Error: next-hop required ``` **Correct:** ```python print("announce route 100.10.0.0/24 next-hop self") ``` --- ### 3. Invalid Syntax **Wrong:** ```python print("announce 100.10.0.0/24 next-hop self") # Missing 'route' keyword ``` **Correct:** ```python print("announce route 100.10.0.0/24 next-hop self") ``` --- ### 4. FlowSpec Missing Braces **Wrong:** ```python print("announce flow route match destination 100.10.0.0/24 then discard") # Missing { } braces ``` **Correct:** ```python print("announce flow route { match { destination 100.10.0.0/24; } then { discard; } }") ``` --- ### 5. Withdraw with Attributes **Wrong:** ```python print("withdraw route 100.10.0.0/24 next-hop self") # Withdraw doesn't need attributes ``` **Correct:** ```python print("withdraw route 100.10.0.0/24") ``` --- ## Quick Reference ### IPv4 Unicast ```python announce route next-hop self [attributes] withdraw route ``` ### Bulk Announcements (4.0+) ```python announce attributes nlri ... withdraw attributes nlri ... ``` ### FlowSpec ```python announce flow route { match { ; } then { ; } } withdraw flow route { match { ; } } ``` ### L3VPN ```python announce ipv4 mpls-vpn next-hop self route-distinguisher label