Skip to content

Text API Reference

Thomas Mangin edited this page Nov 10, 2025 · 15 revisions

Text API Reference

Complete reference for ExaBGP's text-based API commands


Table of Contents


Overview

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()

Command Syntax

Basic Structure

<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

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)

IPv4/IPv6 Unicast

Basic Announcement

IPv4:

# Using 'self' (recommended)
print("announce route 100.10.0.0/24 next-hop self")

# Explicit next-hop
print("announce route 100.10.0.0/24 next-hop 192.168.1.2")

# /32 host route
print("announce route 100.10.0.100/32 next-hop self")

IPv6:

# IPv6 prefix
print("announce route 2001:db8::/32 next-hop self")

# IPv6 /128 host route
print("announce route 2001:db8::100/128 next-hop self")

Withdrawal

# 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

Using 'self' (recommended):

print("announce route 100.10.0.0/24 next-hop self")
# ExaBGP auto-fills with local-address

Explicit next-hop:

print("announce route 100.10.0.0/24 next-hop 192.168.1.2")
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")

MED (Multi-Exit Discriminator)

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")

Local Preference

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")

AS Path

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 ]")

Communities

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 ]")

Origin

# 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

# 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 ]")

FlowSpec

FlowSpec allows BGP-based traffic filtering. ExaBGP pioneered open-source FlowSpec support (now also in GoBGP, FRRouting, BIRD).

Basic Structure

announce flow route {
    match {
        <match-conditions>
    }
    then {
        <actions>
    }
}

Match Conditions

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; } "
      "}")

Multiple Match Conditions (AND logic)

# 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):

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 ]; } "
      "}")

FlowSpec Withdrawal

# Withdraw by match conditions
print("withdraw flow route { "
      "match { destination 100.10.0.0/24; } "
      "}")

FlowSpec Examples

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:

# Limit HTTP to 100 Mbps per source
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; } "
      "}")

L3VPN

Layer 3 VPN (RFC 4364) for MPLS VPNs.

Announcement

# 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")

Withdrawal

print("withdraw vpn 100.10.0.0/24 route-distinguisher 65001:100")

EVPN

Ethernet VPN (RFC 7432) for data center fabrics.

EVPN Route Type 2 (MAC/IP Advertisement)

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 ]")

EVPN Route Type 3 (Inclusive Multicast)

print("announce evpn multicast "
      "100.10.0.1 "
      "route-distinguisher 65001:100 "
      "next-hop self "
      "extended-community [ target:65001:100 ]")

Withdrawal

print("withdraw evpn mac-ip aa:bb:cc:dd:ee:ff route-distinguisher 65001:100")

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:

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).

# 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

print("withdraw vpls route-distinguisher 65001:100")

Operational Commands

Refresh Routes

Force route refresh (send all routes again):

print("announce route-refresh ipv4 unicast")
print("announce route-refresh ipv6 unicast")

Operational Messages

Send operational messages:

# Query operational state
print("operational afi ipv4 safi unicast advisory rib in")

Examples by Use Case

High Availability with Anycast

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)

DDoS Mitigation (FlowSpec)

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)

Load Balancing with Metrics

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 200

Traffic Engineering

Prefer 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()

Common Errors

1. Forgot to Flush

Wrong:

print("announce route 100.10.0.0/24 next-hop self")
# Nothing happens - stuck in buffer

Correct:

print("announce route 100.10.0.0/24 next-hop self")
sys.stdout.flush()  # Now it works

2. Missing next-hop

Wrong:

print("announce route 100.10.0.0/24")
# Error: next-hop required

Correct:

print("announce route 100.10.0.0/24 next-hop self")

3. Invalid Syntax

Wrong:

print("announce 100.10.0.0/24 next-hop self")
# Missing 'route' keyword

Correct:

print("announce route 100.10.0.0/24 next-hop self")

4. FlowSpec Missing Braces

Wrong:

print("announce flow route match destination 100.10.0.0/24 then discard")
# Missing { } braces

Correct:

print("announce flow route { match { destination 100.10.0.0/24; } then { discard; } }")

5. Withdraw with Attributes

Wrong:

print("withdraw route 100.10.0.0/24 next-hop self")
# Withdraw doesn't need attributes

Correct:

print("withdraw route 100.10.0.0/24")

Quick Reference

IPv4 Unicast

announce route <prefix> next-hop self [attributes]
withdraw route <prefix>

FlowSpec

announce flow route { match { <conditions>; } then { <action>; } }
withdraw flow route { match { <conditions>; } }

L3VPN

announce vpn <prefix> next-hop self route-distinguisher <rd> label <label> [extended-community [...]]
withdraw vpn <prefix> route-distinguisher <rd>

EVPN

announce evpn mac-ip <mac> [<ip>] route-distinguisher <rd> next-hop self label [<label>] extended-community [...]
withdraw evpn mac-ip <mac> route-distinguisher <rd>

See Also


Need JSON format? See JSON API Reference


👻 Ghost written by Claude (Anthropic AI)

Clone this wiki locally