What Is ExaBGP?
ExaBGP is a Python-based BGP implementation designed specifically for programmatic route injection. Unlike full-featured routing daemons such as BIRD or FRRouting, ExaBGP is not a router. It does not maintain a RIB, does not make forwarding decisions, and does not participate in your data plane. Instead, it speaks BGP to your upstream routers and announces or withdraws routes on demand, driven by external scripts or processes.
This makes ExaBGP the perfect tool for RTBH (Remotely Triggered Black Hole) routing. When a DDoS attack hits, you need to announce a blackhole route for the targeted IP prefix to your upstream providers as quickly as possible. ExaBGP lets you do that with a single line written to stdout from a Python script, a shell command, or an API webhook. No manual router CLI sessions. No configuration commits. Just instant, programmatic route announcements.
ExaBGP is widely deployed across ISPs, hosting providers, and enterprise networks for exactly this purpose. It is actively maintained, supports BGP4 and MP-BGP (IPv4/IPv6 unicast, FlowSpec, and more), and runs on any Linux system with Python 3.
ExaBGP is not a replacement for your routing daemon. Think of it as a sidecar that speaks BGP to inject or withdraw specific routes on demand. Your primary routing (OSPF, IS-IS, eBGP peering) stays on your existing router or routing daemon.
Prerequisites
Before you begin, make sure you have the following in place:
- Linux server: Any Debian, Ubuntu, CentOS, or RHEL-based system. ExaBGP runs in userspace and does not require kernel modifications.
- Python 3.6 or later: ExaBGP 4.x requires Python 3. Verify with
python3 --version. - Upstream router with BGP session support: Your edge router (Cisco, Juniper, Arista, MikroTik, or any BGP-capable device) must accept a BGP peering session from the ExaBGP host.
- RTBH community support: Your upstream provider must honor blackhole communities. Most Tier 1 and Tier 2 providers do. Check your provider's peering documentation for the specific community string.
- Network reachability: The ExaBGP host must be able to reach the upstream router's BGP port (TCP 179). Ensure firewall rules allow this.
- An AS number: You need a private or public ASN for the ExaBGP peering session. If you are using iBGP, your existing ASN works. For eBGP with a provider, you need a registered ASN.
Step 1: Install ExaBGP
The simplest installation method is via pip. This installs ExaBGP system-wide and makes the exabgp binary available in your PATH:
# Install via pip (recommended) pip3 install exabgp # Verify installation exabgp --version # Alternative: install from GitHub for the latest development version git clone https://github.com/Exa-Networks/exabgp.git cd exabgp pip3 install .
On Debian/Ubuntu, you may also find ExaBGP in the package repositories, but the pip version is typically more current:
# Debian/Ubuntu package (may be older) apt install exabgp # Create the ExaBGP environment directory mkdir -p /etc/exabgp exabgp --fi > /etc/exabgp/exabgp.env
The exabgp.env file controls runtime settings such as logging, daemon mode, and PID file location. The default values work for most setups, but you can customize logging paths and verbosity later for production use.
Step 2: ExaBGP Configuration File
The core of your ExaBGP setup is the configuration file. Create /etc/exabgp/exabgp.conf with the following structure. Every line is annotated so you understand what each directive does:
# /etc/exabgp/exabgp.conf
# Define the BGP neighbor (your upstream router)
neighbor 198.51.100.1 {
# Description for logging and identification
description "Upstream-Router-RTBH";
# The router-id for this ExaBGP instance
# Typically the IP address of the ExaBGP host
router-id 198.51.100.10;
# Local AS number (your ASN)
local-as 65001;
# Peer AS number (upstream router's ASN)
# Use your own ASN here for iBGP, or the provider ASN for eBGP
peer-as 65000;
# Local address ExaBGP binds to for this session
local-address 198.51.100.10;
# Hold time in seconds (default 180, lower = faster dead peer detection)
hold-time 90;
# Address families to negotiate
family {
ipv4 unicast;
# Uncomment for IPv6 RTBH support:
# ipv6 unicast;
}
# Static routes announced immediately when the session comes up
static {
# Example: permanently blackhole a known-bad prefix
# route 192.0.2.66/32 next-hop self community [65535:666];
}
# Process for dynamic route injection
# ExaBGP reads announce/withdraw commands from this process's stdout
process rtbh-controller {
run /etc/exabgp/rtbh-controller.py;
encoder text;
}
}
Key points about this configuration:
- neighbor: The IP address of your upstream router that will receive RTBH routes.
- local-as / peer-as: For iBGP peering (same AS on both sides), set both to your ASN. For eBGP, set
local-asto your ASN andpeer-asto the upstream's ASN. - family: Specifies which address families to negotiate. For IPv4-only RTBH,
ipv4 unicastis sufficient. - static: Routes defined here are announced as soon as the BGP session establishes. Use this for permanent blackholes (rare) or leave it empty.
- process: This is where ExaBGP becomes powerful. It runs an external script and reads BGP commands from that script's stdout. The script can announce or withdraw routes dynamically based on any logic you implement.
If you peer with multiple upstream routers, define multiple neighbor blocks. ExaBGP can maintain BGP sessions with many peers simultaneously:
# Multiple upstream peers for redundant RTBH
neighbor 198.51.100.1 {
description "Upstream-A";
router-id 198.51.100.10;
local-as 65001;
peer-as 65000;
local-address 198.51.100.10;
family { ipv4 unicast; }
process rtbh-controller { run /etc/exabgp/rtbh-controller.py; encoder text; }
}
neighbor 203.0.113.1 {
description "Upstream-B";
router-id 198.51.100.10;
local-as 65001;
peer-as 65002;
local-address 203.0.113.10;
family { ipv4 unicast; }
process rtbh-controller { run /etc/exabgp/rtbh-controller.py; encoder text; }
}
Step 3: Understanding RTBH Communities
BGP communities are the signaling mechanism that tells your upstream provider to blackhole a route rather than forward traffic to it. When you announce a /32 with the appropriate blackhole community attached, the upstream router installs a route pointing that prefix to a null interface (discard route), effectively dropping all traffic destined for that IP before it enters your network.
RFC 7999: The BLACKHOLE Well-Known Community
RFC 7999 defines the well-known BGP community 65535:666 (also written as BLACKHOLE) specifically for this purpose. Any provider that supports RFC 7999 will treat routes tagged with this community as blackhole requests. This is the standard you should use unless your provider requires a different community.
Provider-Specific Communities
Many providers also support their own blackhole community strings, sometimes in addition to RFC 7999. Here are common examples:
Provider ASN Blackhole Community ───────────────────────────────────────────────────── RFC 7999 (standard) - 65535:666 Cogent 174 174:666 Lumen / CenturyLink 3356 3356:9999 NTT / Verio 2914 2914:666 GTT 3257 3257:666 Telia Carrier 1299 1299:666 Zayo 6461 6461:666 Hurricane Electric 6939 6939:666 Arelion 1299 1299:666 (same as Telia) RETN 9002 9002:666 Core-Backbone 33891 33891:666
Always check your provider's peering portal or NOC documentation for the exact community string. Some providers use non-standard values, and using the wrong community means your blackhole request will be silently ignored.
Large Communities for IPv6
For IPv6 RTBH, some providers use BGP Large Communities (RFC 8092) instead of standard communities. Large communities use a three-part format: ASN:function:parameter. For example:
# Large community format for IPv6 blackhole # Syntax: large-community [ASN:function:parameter] announce route 2001:db8::1/128 next-hop self large-community [65001:666:0]
Step 4: Static RTBH Announcement
The simplest way to blackhole an IP is a static announcement in your ExaBGP configuration. This is useful for testing your setup or for permanent blackholes of known-bad addresses:
# In the neighbor block's static section:
static {
# Blackhole 192.0.2.66/32 using RFC 7999 community
route 192.0.2.66/32 next-hop self community [65535:666];
# Blackhole with provider-specific community (Cogent example)
route 192.0.2.67/32 next-hop self community [65535:666 174:666];
# Multiple communities for multi-homed networks
# Announce to all upstreams simultaneously
route 192.0.2.68/32 next-hop self community [65535:666 174:666 3356:9999 2914:666];
}
The next-hop self directive tells ExaBGP to set the BGP next-hop to its own address. Your upstream router should have a static route pointing to a null/discard interface for the next-hop used in blackhole routes. This is the standard RTBH trigger mechanism: the upstream sees the route, matches the blackhole community, and installs a discard route regardless of the next-hop.
After adding static routes, start ExaBGP and verify the session establishes:
# Start ExaBGP in foreground with debug output exabgp /etc/exabgp/exabgp.conf # You should see output like: # | network | Peer 198.51.100.1 ASN 65000 connected # | reactor | Peer 198.51.100.1 new state: established
Step 5: Dynamic Route Injection with a Process Script
Static routes are limited. For real RTBH automation, you need dynamic route injection. ExaBGP's process mechanism runs an external script and reads BGP commands from that script's standard output. The script can announce and withdraw routes at any time based on whatever logic you implement.
The ExaBGP Process Protocol
The protocol is simple. Your script writes lines to stdout, and ExaBGP parses them as BGP commands:
- Announce a route:
announce route 192.0.2.1/32 next-hop self community [65535:666] - Withdraw a route:
withdraw route 192.0.2.1/32 next-hop self
ExaBGP also writes messages to your script's stdin (received routes, state changes, etc.), but for RTBH injection you typically only need stdout. Here is a complete, production-ready Python controller script:
#!/usr/bin/env python3
"""
/etc/exabgp/rtbh-controller.py
ExaBGP process script for dynamic RTBH route injection.
Reads commands from a named pipe (FIFO) and translates them
into ExaBGP announce/withdraw statements.
"""
import os
import sys
import time
import signal
import json
# Configuration
FIFO_PATH = "/var/run/exabgp/rtbh.pipe"
COMMUNITY = "65535:666"
LOG_FILE = "/var/log/exabgp/rtbh-controller.log"
# Track announced routes for state management
announced_routes = set()
def log(message):
"""Write timestamped log entry."""
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
with open(LOG_FILE, "a") as f:
f.write(f"{timestamp} {message}\n")
def announce(prefix):
"""Announce a blackhole route."""
cmd = f"announce route {prefix} next-hop self community [{COMMUNITY}]"
sys.stdout.write(cmd + "\n")
sys.stdout.flush()
announced_routes.add(prefix)
log(f"ANNOUNCE {prefix}")
def withdraw(prefix):
"""Withdraw a blackhole route."""
cmd = f"withdraw route {prefix} next-hop self"
sys.stdout.write(cmd + "\n")
sys.stdout.flush()
announced_routes.discard(prefix)
log(f"WITHDRAW {prefix}")
def withdraw_all():
"""Withdraw all announced routes (cleanup on exit)."""
for prefix in list(announced_routes):
withdraw(prefix)
def setup_fifo():
"""Create the named pipe if it does not exist."""
fifo_dir = os.path.dirname(FIFO_PATH)
os.makedirs(fifo_dir, exist_ok=True)
if not os.path.exists(FIFO_PATH):
os.mkfifo(FIFO_PATH)
log("FIFO ready at " + FIFO_PATH)
def handle_signal(signum, frame):
"""Graceful shutdown: withdraw all routes before exiting."""
log(f"Received signal {signum}, withdrawing all routes")
withdraw_all()
sys.exit(0)
def main():
signal.signal(signal.SIGTERM, handle_signal)
signal.signal(signal.SIGINT, handle_signal)
setup_fifo()
log("RTBH controller started")
while True:
try:
# Open FIFO for reading (blocks until a writer connects)
with open(FIFO_PATH, "r") as fifo:
for line in fifo:
line = line.strip()
if not line or line.startswith("#"):
continue
parts = line.split()
if len(parts) < 2:
log(f"INVALID command: {line}")
continue
action = parts[0].lower()
prefix = parts[1]
if action == "announce":
announce(prefix)
elif action == "withdraw":
withdraw(prefix)
elif action == "withdraw-all":
withdraw_all()
else:
log(f"UNKNOWN action: {action}")
except Exception as e:
log(f"ERROR: {e}")
time.sleep(1)
if __name__ == "__main__":
main()
Make the script executable and create the required directories:
chmod +x /etc/exabgp/rtbh-controller.py mkdir -p /var/run/exabgp mkdir -p /var/log/exabgp
Now you can inject routes dynamically by writing to the named pipe from any other process:
# Announce a blackhole from the command line echo "announce 192.0.2.100/32" > /var/run/exabgp/rtbh.pipe # Withdraw it later echo "withdraw 192.0.2.100/32" > /var/run/exabgp/rtbh.pipe # Emergency: withdraw everything echo "withdraw-all" > /var/run/exabgp/rtbh.pipe
Step 6: Triggering RTBH from a DDoS Detection System
The real power of this setup is connecting it to an automated DDoS detection system. When an attack is detected, the detection system sends a command to ExaBGP's named pipe, and the blackhole route is announced within seconds. Here is a webhook receiver that bridges HTTP-based detection alerts to ExaBGP:
#!/usr/bin/env python3
"""
/etc/exabgp/webhook-receiver.py
HTTP webhook receiver that accepts DDoS detection alerts
and triggers RTBH announcements via the ExaBGP named pipe.
Usage: python3 webhook-receiver.py
Listens on port 8099 by default.
"""
import json
import time
import ipaddress
import threading
from http.server import HTTPServer, BaseHTTPRequestHandler
FIFO_PATH = "/var/run/exabgp/rtbh.pipe"
LISTEN_PORT = 8099
AUTH_TOKEN = "your-secret-webhook-token"
LOG_FILE = "/var/log/exabgp/webhook.log"
# Safety: IPs that must never be blackholed
WHITELIST = {
ipaddress.ip_network("198.51.100.0/24"), # Management network
ipaddress.ip_network("203.0.113.10/32"), # DNS servers
}
# Rate limiting: max announcements per minute
MAX_ANNOUNCEMENTS_PER_MIN = 10
announcement_times = []
# Auto-withdraw timers
active_timers = {}
DEFAULT_WITHDRAW_SECONDS = 3600 # 1 hour
def log(msg):
ts = time.strftime("%Y-%m-%d %H:%M:%S")
with open(LOG_FILE, "a") as f:
f.write(f"{ts} {msg}\n")
def is_whitelisted(ip_str):
"""Check if an IP falls within any whitelisted network."""
try:
addr = ipaddress.ip_address(ip_str.split("/")[0])
return any(addr in net for net in WHITELIST)
except ValueError:
return True # Invalid IPs are treated as whitelisted (blocked)
def is_rate_limited():
"""Check if we have exceeded the announcement rate limit."""
now = time.time()
cutoff = now - 60
announcement_times[:] = [t for t in announcement_times if t > cutoff]
return len(announcement_times) >= MAX_ANNOUNCEMENTS_PER_MIN
def write_to_fifo(command):
"""Write a command to the ExaBGP named pipe."""
with open(FIFO_PATH, "w") as fifo:
fifo.write(command + "\n")
def schedule_withdraw(prefix, seconds):
"""Schedule automatic route withdrawal after a timeout."""
if prefix in active_timers:
active_timers[prefix].cancel()
def do_withdraw():
write_to_fifo(f"withdraw {prefix}")
log(f"AUTO-WITHDRAW {prefix} after {seconds}s timeout")
active_timers.pop(prefix, None)
timer = threading.Timer(seconds, do_withdraw)
timer.start()
active_timers[prefix] = timer
class WebhookHandler(BaseHTTPRequestHandler):
def do_POST(self):
# Authenticate
token = self.headers.get("X-Auth-Token", "")
if token != AUTH_TOKEN:
self.send_response(403)
self.end_headers()
log(f"AUTH FAILED from {self.client_address[0]}")
return
# Parse body
length = int(self.headers.get("Content-Length", 0))
body = json.loads(self.rfile.read(length))
action = body.get("action", "announce")
target_ip = body.get("target_ip", "")
prefix = f"{target_ip}/32" if "/" not in target_ip else target_ip
withdraw_after = body.get("withdraw_after", DEFAULT_WITHDRAW_SECONDS)
# Safety checks
if is_whitelisted(target_ip):
self.send_response(403)
self.end_headers()
self.wfile.write(b'{"error":"IP is whitelisted"}')
log(f"BLOCKED whitelisted IP {target_ip}")
return
if action == "announce" and is_rate_limited():
self.send_response(429)
self.end_headers()
self.wfile.write(b'{"error":"Rate limit exceeded"}')
log(f"RATE LIMITED announcement for {prefix}")
return
# Execute
if action == "announce":
write_to_fifo(f"announce {prefix}")
announcement_times.append(time.time())
schedule_withdraw(prefix, withdraw_after)
log(f"WEBHOOK ANNOUNCE {prefix} (auto-withdraw in {withdraw_after}s)")
elif action == "withdraw":
write_to_fifo(f"withdraw {prefix}")
if prefix in active_timers:
active_timers[prefix].cancel()
del active_timers[prefix]
log(f"WEBHOOK WITHDRAW {prefix}")
self.send_response(200)
self.end_headers()
self.wfile.write(json.dumps({"status": "ok", "prefix": prefix}).encode())
def log_message(self, format, *args):
pass # Suppress default HTTP logging
if __name__ == "__main__":
server = HTTPServer(("127.0.0.1", LISTEN_PORT), WebhookHandler)
log(f"Webhook receiver listening on port {LISTEN_PORT}")
print(f"Listening on 127.0.0.1:{LISTEN_PORT}")
server.serve_forever()
With this webhook receiver running, your DDoS detection system can trigger RTBH with a simple HTTP POST:
# Trigger a blackhole announcement
curl -X POST http://127.0.0.1:8099/ \
-H "X-Auth-Token: your-secret-webhook-token" \
-H "Content-Type: application/json" \
-d '{"action":"announce","target_ip":"192.0.2.100","withdraw_after":3600}'
# Manually withdraw a blackhole
curl -X POST http://127.0.0.1:8099/ \
-H "X-Auth-Token: your-secret-webhook-token" \
-H "Content-Type: application/json" \
-d '{"action":"withdraw","target_ip":"192.0.2.100"}'
The safety checks built into this receiver are critical for production use:
- Whitelist: Prevents accidental blackholing of management IPs, DNS servers, or other critical infrastructure. Always maintain a whitelist of prefixes that must never be blackholed.
- Rate limiting: Caps the number of announcements per minute. This prevents a runaway detection system or an attacker who compromises your webhook from blackholing your entire address space.
- Auto-withdraw timer: Every announcement automatically schedules a withdrawal after a configurable timeout (default 1 hour). This prevents forgotten blackhole routes from silently dropping legitimate traffic indefinitely.
Step 7: Testing the Setup
Before relying on this in production, thoroughly test every component of the chain. ExaBGP has excellent debugging output that makes this straightforward.
Start ExaBGP in Debug Mode
# Run with full debug output exabgp /etc/exabgp/exabgp.conf --debug # You will see detailed BGP message exchange: # OPEN messages (capabilities negotiation) # UPDATE messages (route announcements) # KEEPALIVE messages (session maintenance) # Alternative: increase log verbosity in exabgp.env # [exabgp.log] # all = true # level = DEBUG
Verify the BGP Session
On your upstream router, confirm that the BGP session with ExaBGP has established. The commands vary by platform:
# Cisco IOS / IOS-XE show ip bgp summary show ip bgp neighbors 198.51.100.10 # Juniper JunOS show bgp summary show bgp neighbor 198.51.100.10 # Arista EOS show ip bgp summary show ip bgp neighbors 198.51.100.10 # MikroTik RouterOS /routing bgp peer print detail
Verify Blackhole Routes
After announcing a test blackhole route, verify it appears on the upstream router with the correct community:
# Cisco: show routes with blackhole community show ip bgp community 65535:666 show ip bgp 192.0.2.100/32 # Expected output: # *> 192.0.2.100/32 198.51.100.10 0 0 65001 ? # Community: 65535:666 # Juniper: verify blackhole route show route 192.0.2.100/32 detail show route community 65535:666 # Verify the route points to discard/null show route 192.0.2.100/32 table inet.0 | match "to" # Should show: Discard or Null0
End-to-End Test
Run through the full chain: write a command to the FIFO, confirm ExaBGP announces the route, verify it appears on the router, and confirm traffic to the target IP is being dropped:
# 1. Announce a test blackhole echo "announce 192.0.2.200/32" > /var/run/exabgp/rtbh.pipe # 2. Check ExaBGP debug output for the UPDATE message # Look for: ">> UPDATE ... 192.0.2.200/32 ... community 65535:666" # 3. On the router, verify the route # show ip bgp 192.0.2.200/32 # 4. Test traffic drop (from an external host) ping 192.0.2.200 # Should timeout (100% packet loss) # 5. Withdraw the test route echo "withdraw 192.0.2.200/32" > /var/run/exabgp/rtbh.pipe # 6. Verify route is removed from the router # show ip bgp 192.0.2.200/32 # Should show: "not in table" or empty
Step 8: Production Hardening
A working ExaBGP setup is just the beginning. For production use, you need process management, monitoring, and safety guardrails.
systemd Service File
Run ExaBGP as a managed systemd service so it starts on boot, restarts on failure, and integrates with your system's logging infrastructure:
# /etc/systemd/system/exabgp.service [Unit] Description=ExaBGP BGP Route Injector After=network-online.target Wants=network-online.target [Service] Type=simple User=exabgp Group=exabgp Environment=exabgp_daemon_daemonize=false ExecStart=/usr/local/bin/exabgp /etc/exabgp/exabgp.conf ExecReload=/bin/kill -USR1 $MAINPID Restart=always RestartSec=5 StandardOutput=journal StandardError=journal SyslogIdentifier=exabgp # Security hardening NoNewPrivileges=yes ProtectSystem=strict ReadWritePaths=/var/run/exabgp /var/log/exabgp PrivateTmp=yes [Install] WantedBy=multi-user.target
# Create the exabgp user and enable the service useradd -r -s /usr/sbin/nologin exabgp chown -R exabgp:exabgp /etc/exabgp /var/run/exabgp /var/log/exabgp systemctl daemon-reload systemctl enable exabgp systemctl start exabgp # Check status systemctl status exabgp journalctl -u exabgp -f
Also Run the Webhook Receiver as a Service
# /etc/systemd/system/exabgp-webhook.service [Unit] Description=ExaBGP RTBH Webhook Receiver After=exabgp.service Requires=exabgp.service [Service] Type=simple User=exabgp Group=exabgp ExecStart=/usr/bin/python3 /etc/exabgp/webhook-receiver.py Restart=always RestartSec=5 StandardOutput=journal StandardError=journal SyslogIdentifier=exabgp-webhook [Install] WantedBy=multi-user.target
Logging and Monitoring
Monitor your RTBH system with the same rigor you apply to any critical infrastructure component:
- BGP session state: Alert if the ExaBGP-to-router BGP session drops. A down session means RTBH announcements will not reach the upstream.
- Announcement count: Track how many routes are currently announced. A sudden spike could indicate a runaway detection system or compromise.
- Log aggregation: Ship ExaBGP and webhook receiver logs to your SIEM or log aggregation platform. Every announce and withdraw should be auditable.
- Latency monitoring: Measure the time from detection alert to route announcement. This is your RTBH response time and should be under 5 seconds.
Maximum Prefix Safety
Configure a maximum prefix limit on your upstream router's BGP session with ExaBGP. This prevents a compromised ExaBGP instance from announcing thousands of blackhole routes and taking down your entire network:
# Cisco IOS: limit ExaBGP to 50 prefixes maximum
router bgp 65000
neighbor 198.51.100.10 maximum-prefix 50 warning-only
# Juniper JunOS
protocols bgp group RTBH neighbor 198.51.100.10 {
family inet {
unicast {
prefix-limit {
maximum 50;
teardown 80; # Tear down session at 80% of limit
}
}
}
}
On the ExaBGP side, you can also add a prefix count check in your controller script to refuse announcements beyond a threshold. Defense in depth applies here: limit on both sides.
Prefix Validation
Your upstream router should only accept blackhole routes for prefixes you legitimately own. Configure a prefix filter to ensure ExaBGP cannot announce blackhole routes for arbitrary third-party prefixes:
# Cisco: only accept blackholes for your own prefix space ip prefix-list RTBH-ALLOWED seq 10 permit 198.51.100.0/24 ge 32 le 32 ip prefix-list RTBH-ALLOWED seq 20 permit 203.0.113.0/24 ge 32 le 32 router bgp 65000 neighbor 198.51.100.10 prefix-list RTBH-ALLOWED in
How Flowtriq Integrates with ExaBGP
Flowtriq's DDoS detection engine can trigger ExaBGP-based RTBH announcements automatically as part of its multi-level auto-escalation chain. When Flowtriq detects a volumetric attack that exceeds your local mitigation capacity, it escalates to RTBH by sending a webhook to your ExaBGP webhook receiver (or by writing directly to the named pipe via the Flowtriq agent running on the same host).
The integration works in both directions:
- Announce on attack detection: Flowtriq sends the target IP and attack metadata to your ExaBGP webhook endpoint. The blackhole route is announced within seconds of detection.
- Withdraw on attack end: When Flowtriq's real-time analysis confirms the attack has subsided and the cool-down period has elapsed, it sends a withdraw command. The blackhole route is removed, and traffic flows normally again.
- Smart escalation: Flowtriq only escalates to RTBH when lower-level mitigations (firewall rules, FlowSpec) are insufficient. It does not blackhole an IP for a 200 Mbps flood that your firewall can handle.
- Audit trail: Every RTBH announcement and withdrawal triggered by Flowtriq is logged in your dashboard's audit log with full context: the triggering incident, attack metrics, duration of the blackhole, and the escalation path that led to it.
To connect Flowtriq to your ExaBGP setup, navigate to Dashboard → Integrations and configure the RTBH webhook endpoint. Provide the webhook URL (http://127.0.0.1:8099/ if running on the same host) and the authentication token. Flowtriq will validate the connection and run a dry-run test to confirm the pipeline is working end-to-end.
Ready to automate RTBH for your network? Start a free 7-day trial of Flowtriq and connect your ExaBGP setup in minutes. Flowtriq handles the detection and decision-making; ExaBGP handles the route injection. Together, they give you fully automated blackhole routing with zero manual intervention.