# Process Configuration The `process` directive in ExaBGP configures external programs that communicate with ExaBGP via its API. Processes are used for dynamic route control, health checking, monitoring, and integration with external systems. ## Table of Contents - [Overview](#overview) - [Basic Process Configuration](#basic-process-configuration) - [Process Directives](#process-directives) - [Environment Variables](#environment-variables) - [API Communication](#api-communication) - [Multiple Processes](#multiple-processes) - [Process Types](#process-types) - [Best Practices](#best-practices) - [Troubleshooting](#troubleshooting) - [See Also](#see-also) ## Overview **Important**: ExaBGP does NOT manipulate the routing table (RIB/FIB). External processes use the ExaBGP API to dynamically announce and withdraw routes based on application logic (health checks, monitoring, traffic engineering, etc.). ### Process Architecture ``` ┌─────────────────────┐ │ ExaBGP Main │ │ Process │ └──────┬──────────────┘ │ stdin/stdout │ pipe communication ▼ ┌─────────────────────┐ │ API Process │ │ (Python/Bash/Go) │ └──────┬──────────────┘ │ │ checks service/logic ▼ ┌─────────────────────┐ │ External System │ │ (Service/Monitor) │ └─────────────────────┘ ``` ### Key Concepts - **Process** - External program that communicates with ExaBGP via API - **API** - Text or JSON-based protocol for sending commands to ExaBGP - **Encoder** - Format for API messages (text or JSON) - **Run** - Command to execute the process - **Environment Variables** - Configuration passed to the process ## Basic Process Configuration ### Minimal Process Configuration ```ini # /etc/exabgp/exabgp.conf process announce-routes { run /etc/exabgp/announce.py; encoder text; } neighbor 192.0.2.1 { router-id 192.0.2.10; local-address 192.0.2.10; local-as 65001; peer-as 65001; family { ipv4 unicast; } api { processes [ announce-routes ]; } } ``` ### Simple Announcement Script ```python #!/usr/bin/env python3 # /etc/exabgp/announce.py import sys import time # Announce a route print("announce route 198.51.100.0/24 next-hop 192.0.2.10", flush=True) # Keep process running while True: time.sleep(60) ``` ## Process Directives ### Complete Process Syntax ```ini process process-name { # Command to execute (required) run /path/to/program [arguments]; # API message format: text or json (default: text) encoder text; # Receive neighbor state updates (default: false) neighbor-changes; # Receive route updates from peers (default: false) receive-routes; # Receive parsed routes (default: false) receive-parsed; # Receive raw BGP packets (default: false) receive-packets; } ``` ### Directive Reference | Directive | Description | Values | Default | |-----------|-------------|--------|---------| | `run` | Command to execute | Path + arguments | Required | | `encoder` | API message format | `text`, `json` | `text` | | `neighbor-changes` | Receive neighbor state updates | Flag | Disabled | | `receive-routes` | Receive route updates from peers | Flag | Disabled | | `receive-parsed` | Receive parsed route information | Flag | Disabled | | `receive-packets` | Receive raw BGP packets | Flag | Disabled | ### Process with Arguments ```ini process healthcheck { run /usr/bin/python3 /etc/exabgp/healthcheck.py --interval 10 --route 198.51.100.0/24; encoder text; } ``` ### Process with Neighbor Updates ```ini process monitor { run /etc/exabgp/monitor.py; encoder json; neighbor-changes; receive-routes; } ``` ## Environment Variables ExaBGP passes configuration information to processes via environment variables prefixed with `exabgp.`. ### Available Environment Variables ```bash # Process Information exabgp.version # ExaBGP version (e.g., "5.0.0") exabgp.api.encoder # Encoder type (text or json) # Neighbor Information (per neighbor) exabgp.neighbor.address # Neighbor IP address exabgp.neighbor.as # Neighbor AS number exabgp.neighbor.local_address # Local IP address exabgp.neighbor.local_as # Local AS number exabgp.neighbor.peer_as # Peer AS number # File Locations exabgp.etc # ExaBGP configuration directory exabgp.log # Log file location ``` ### Using Environment Variables in Python ```python #!/usr/bin/env python3 # /etc/exabgp/announce-with-env.py import os import sys # Read ExaBGP environment variables version = os.environ.get('exabgp.version', 'unknown') encoder = os.environ.get('exabgp.api.encoder', 'text') neighbor = os.environ.get('exabgp.neighbor.address', 'unknown') sys.stderr.write(f"ExaBGP version: {version}\n") sys.stderr.write(f"API encoder: {encoder}\n") sys.stderr.write(f"Neighbor: {neighbor}\n") # Use local address for next-hop local_address = os.environ.get('exabgp.neighbor.local_address', 'self') print(f"announce route 198.51.100.0/24 next-hop {local_address}", flush=True) while True: import time time.sleep(60) ``` ### Using Environment Variables in Bash ```bash #!/bin/bash # /etc/exabgp/announce-with-env.sh # Read ExaBGP environment variables NEIGHBOR="${exabgp.neighbor.address}" LOCAL_AS="${exabgp.neighbor.local_as}" PEER_AS="${exabgp.neighbor.peer_as}" echo "Connected to neighbor: $NEIGHBOR (AS $PEER_AS)" >&2 echo "Our AS: $LOCAL_AS" >&2 # Announce route echo "announce route 198.51.100.0/24 next-hop self" # Keep running while true; do sleep 60 done ``` ## API Communication ### Text API Format Text API uses simple command syntax: ```python #!/usr/bin/env python3 # /etc/exabgp/text-api.py import sys # Announce route print("announce route 198.51.100.0/24 next-hop 192.0.2.10", flush=True) # Announce with attributes print("announce route 203.0.113.0/24 next-hop 192.0.2.10 as-path [ 65001 65002 ] community [ 65001:100 ]", flush=True) # Withdraw route print("withdraw route 198.51.100.0/24 next-hop 192.0.2.10", flush=True) # FlowSpec rule print("announce flow route { match { source 192.0.2.0/24; destination-port =80; } then { rate-limit 9600; } }", flush=True) while True: import time time.sleep(60) ``` ### JSON API Format JSON API uses structured JSON messages: ```python #!/usr/bin/env python3 # /etc/exabgp/json-api.py import sys import json # Announce route (JSON format) route = { "exabgp": "5.0.0", "type": "update", "neighbor": { "address": {"local": "192.0.2.10", "peer": "192.0.2.1"}, "asn": {"local": 65001, "peer": 65001} }, "announce": { "ipv4 unicast": { "192.0.2.10": { "198.51.100.0/24": { "next-hop": "192.0.2.10", "as-path": [65001], "community": [[65001, 100]] } } } } } print(json.dumps(route), flush=True) while True: import time time.sleep(60) ``` ### Configuration for JSON Encoder ```ini process json-api { run /etc/exabgp/json-api.py; encoder json; # Use JSON format } ``` ## Multiple Processes You can configure multiple processes for different purposes. ### Multiple Process Configuration ```ini # /etc/exabgp/exabgp.conf # Health check process process healthcheck { run /etc/exabgp/healthcheck.py; encoder text; } # Monitoring process process monitor { run /etc/exabgp/monitor.py; encoder json; neighbor-changes; receive-routes; } # Metrics exporter process metrics { run /usr/bin/python3 -m exabgp.application.metrics; encoder json; } neighbor 192.0.2.1 { router-id 192.0.2.10; local-address 192.0.2.10; local-as 65001; peer-as 65001; family { ipv4 unicast; ipv4 flow; } api { # All processes can send commands processes [ healthcheck, monitor, metrics ]; } } ``` ### Process Coordination Multiple processes can coordinate by reading shared state: ```python #!/usr/bin/env python3 # /etc/exabgp/coordinated-announce.py import sys import time import json import os STATE_FILE = "/var/run/exabgp/state.json" def read_state(): """Read shared state file""" try: with open(STATE_FILE, 'r') as f: return json.load(f) except: return {} def should_announce(): """Check if we should announce based on shared state""" state = read_state() return state.get('all_healthy', False) route_announced = False while True: if should_announce() and not route_announced: print("announce route 198.51.100.0/24 next-hop self", flush=True) route_announced = True sys.stderr.write("Route announced based on shared state\n") elif not should_announce() and route_announced: print("withdraw route 198.51.100.0/24 next-hop self", flush=True) route_announced = False sys.stderr.write("Route withdrawn based on shared state\n") time.sleep(10) ``` ## Process Types ### Health Check Process ```ini process healthcheck { run /etc/exabgp/healthcheck.py; encoder text; } ``` ```python #!/usr/bin/env python3 import sys, time, requests while True: try: r = requests.get("http://127.0.0.1/health", timeout=5) if r.status_code == 200: print("announce route 198.51.100.0/24 next-hop self", flush=True) else: print("withdraw route 198.51.100.0/24 next-hop self", flush=True) except: print("withdraw route 198.51.100.0/24 next-hop self", flush=True) time.sleep(10) ``` ### Monitoring Process ```ini process monitor { run /etc/exabgp/monitor.py; encoder json; neighbor-changes; receive-routes; } ``` ```python #!/usr/bin/env python3 import sys import json # Read updates from ExaBGP while True: line = sys.stdin.readline().strip() if not line: break try: data = json.loads(line) # Log neighbor state changes if data.get('type') == 'state': neighbor = data['neighbor']['address']['peer'] state = data['neighbor']['state'] sys.stderr.write(f"Neighbor {neighbor} is {state}\n") # Log received routes elif data.get('type') == 'update': sys.stderr.write(f"Received update: {json.dumps(data)}\n") except Exception as e: sys.stderr.write(f"Error processing message: {e}\n") ``` ### Traffic Engineering Process ```ini process traffic-engineering { run /etc/exabgp/traffic-engineering.py; encoder text; } ``` ```python #!/usr/bin/env python3 import sys import time import random ROUTES = ["198.51.100.0/24", "203.0.113.0/24"] AS_PATHS = [ "[65001 65002]", "[65001 65003 65003]", # Longer path ] while True: # Randomly adjust AS path to influence routing for route in ROUTES: as_path = random.choice(AS_PATHS) print(f"announce route {route} next-hop self as-path {as_path}", flush=True) sys.stderr.write(f"Updated {route} with AS path {as_path}\n") time.sleep(300) # Adjust every 5 minutes ``` ### FlowSpec DDoS Mitigation Process ```ini process ddos-mitigation { run /etc/exabgp/ddos-mitigation.py; encoder text; } ``` ```python #!/usr/bin/env python3 import sys import time def block_attack(source_ip, dport): """Send FlowSpec rule to block attack""" rule = f"announce flow route {{ match {{ source {source_ip}/32; destination-port ={dport}; }} then {{ discard; }} }}" print(rule, flush=True) sys.stderr.write(f"Blocked {source_ip} attacking port {dport}\n") # Example: Block detected attacks while True: # In real implementation, read from IDS/monitoring system # For demo, just block example attack block_attack("192.0.2.100", 80) time.sleep(3600) # Check every hour ``` ## Best Practices ### 1. Always Flush stdout ExaBGP reads from the process's stdout. Always flush after printing: ```python print("announce route 198.51.100.0/24 next-hop self", flush=True) ``` Or set unbuffered output: ```python #!/usr/bin/env python3 -u # -u flag makes Python unbuffered ``` ### 2. Handle Signals Gracefully Withdraw routes before exiting: ```python import signal import sys def signal_handler(signum, frame): print("withdraw route 198.51.100.0/24 next-hop self", flush=True) sys.stderr.write("Graceful shutdown - routes withdrawn\n") sys.exit(0) signal.signal(signal.SIGTERM, signal_handler) signal.signal(signal.SIGINT, signal_handler) ``` ### 3. Use stderr for Logging stdout is for API commands only. Use stderr for logging: ```python sys.stderr.write("Health check passed\n") # Logging print("announce route 198.51.100.0/24 next-hop self", flush=True) # API command ``` ### 4. Keep Processes Running Process must stay running to maintain routes: ```python # GOOD: Process keeps running while True: check_and_announce() time.sleep(10) # BAD: Process exits after announcing print("announce route 198.51.100.0/24 next-hop self", flush=True) # Process exits here - routes will be withdrawn! ``` ### 5. Make Scripts Executable ```bash chmod +x /etc/exabgp/announce.py ``` ### 6. Use Absolute Paths ```ini # GOOD run /usr/bin/python3 /etc/exabgp/announce.py; # BAD (may not work) run python3 announce.py; ``` ### 7. Test Scripts Independently Test your script before using it with ExaBGP: ```bash # Test script runs and outputs correctly /etc/exabgp/announce.py # Should output: # announce route 198.51.100.0/24 next-hop self ``` ### 8. Monitor Process Health Use systemd or supervisor to restart failed processes: ```ini # /etc/systemd/system/exabgp.service [Service] Restart=always RestartSec=10 ``` ## Troubleshooting ### Problem: Process Not Starting **Symptoms**: ExaBGP logs show process start failures. **Solutions**: 1. Check script is executable: ```bash chmod +x /etc/exabgp/announce.py ``` 2. Check shebang line: ```python #!/usr/bin/env python3 # Correct #!/usr/bin/python3 # Also correct ``` 3. Test script manually: ```bash /etc/exabgp/announce.py # Should run without errors ``` 4. Check ExaBGP logs: ```bash tail -f /var/log/exabgp/exabgp.log ``` ### Problem: Routes Not Being Announced **Symptoms**: Process runs but routes don't appear. **Solutions**: 1. Ensure stdout is flushed: ```python print("announce route ...", flush=True) ``` 2. Check API is enabled for the process: ```ini api { processes [ announce-routes ]; # Process must be listed } ``` 3. Verify BGP session is established: ```bash grep "Peer.*up" /var/log/exabgp/exabgp.log ``` 4. Check address family is configured: ```ini family { ipv4 unicast; # Must match route type } ``` ### Problem: Process Crashes **Symptoms**: Process exits unexpectedly. **Solutions**: 1. Add exception handling: ```python try: while True: check_and_announce() time.sleep(10) except Exception as e: sys.stderr.write(f"Fatal error: {e}\n") sys.exit(1) ``` 2. Check for syntax errors: ```bash python3 -m py_compile /etc/exabgp/announce.py ``` 3. Review stderr output: ```bash # ExaBGP logs process stderr tail -f /var/log/exabgp/exabgp.log | grep -i error ``` ### Problem: Process Receives No Updates **Symptoms**: Monitoring process doesn't receive neighbor/route updates. **Solutions**: 1. Enable update reception: ```ini process monitor { neighbor-changes; # For neighbor state updates receive-routes; # For route updates } ``` 2. Use correct encoder: ```ini process monitor { encoder json; # Updates are in JSON format } ``` 3. Read from stdin properly: ```python while True: line = sys.stdin.readline() if not line: break # Process update ``` ### Problem: Multiple Processes Conflict **Symptoms**: Processes announce conflicting routes. **Solutions**: 1. Use different routes per process: ```python # Process 1: Announces 198.51.100.0/24 # Process 2: Announces 203.0.113.0/24 ``` 2. Coordinate via shared state: ```python # Use lock file or shared state file ``` 3. Use single master process: ```python # One process coordinates and announces all routes ``` ## See Also - [API Overview](../API/API-Overview) - ExaBGP API architecture - [Text API Reference](../API/Text-API-Reference) - Text API command syntax - [JSON API Reference](../API/JSON-API-Reference) - JSON API format - [Configuration Syntax](Configuration-Syntax) - Overall configuration reference - [Health Checks](../Operations/Health-Checks) - Health checking patterns - [Writing API Programs](../API/Writing-API-Programs) - API program development guide ---