-
Notifications
You must be signed in to change notification settings - Fork 460
SDN Integration
Bridging SDN controllers with BGP networks
π BGP as SDN southbound protocol - ExaBGP enables SDN control over BGP networks
- Overview
- SDN and BGP Convergence
- Architecture Patterns
- BGP-LS for Topology Discovery
- Programmatic Route Control
- Integration with SDN Controllers
- Path Computation
- Use Case Scenarios
- Implementation Examples
- Best Practices
- Monitoring and Validation
- Troubleshooting
SDN (Software-Defined Networking) with ExaBGP enables programmatic control of BGP routing from centralized controllers.
Software-Defined Networking separates the control plane from the data plane:
Traditional Networking:
βββββββββββββββββββ
β Router β
β βββββββββββββββ β
β β Control β β β Integrated
β β Plane β β
β βββββββββββββββ β
β βββββββββββββββ β
β β Data β β
β β Plane β β
β βββββββββββββββ β
βββββββββββββββββββ
SDN Networking:
βββββββββββββββββββββββ
β SDN Controller β β Centralized control
β (Control Plane) β
ββββββββββββ¬βββββββββββ
β Southbound API
βΌ
βββββββββββββββββββ
β Switch/Router β
β βββββββββββββββ β
β β Data β β β Simplified
β β Plane β β
β βββββββββββββββ β
βββββββββββββββββββ
Benefits:
- Centralized control and visibility
- Programmable network behavior
- Rapid deployment of new services
- Network automation
- Application-aware routing
ExaBGP is the perfect BGP southbound interface:
βββββββββββββββββββββββββββββββ
β SDN Controller β
β (OpenDaylight, ONOS, etc.)β
ββββββββββββββββ¬βββββββββββββββ
β REST API / gRPC
βΌ
ββββββββββββββββββββββββββββββββ
β ExaBGP β β BGP API gateway
β (BGP Speaker) β
ββββββββββββββββ¬ββββββββββββββββ
β BGP Protocol
βΌ
ββββββββββββββββββββββββββββββββ
β Network Devices β
β (Routers, Switches) β
ββββββββββββββββββββββββββββββββ
ExaBGP advantages:
- Simple API (STDIN/STDOUT, JSON)
- Language-agnostic (Python, Go, Java, etc.)
- Full BGP protocol support
- Lightweight and portable
- No RIB/FIB manipulation (pure control plane)
Phase 1: OpenFlow (2008)
- New protocol for switch control
- Limited deployment (requires OpenFlow switches)
Phase 2: Protocol-Agnostic SDN (2012+)
- NETCONF, RESTCONF for device management
- Still requires device support
Phase 3: BGP as SDN Protocol (2014+)
- ExaBGP enables BGP-based SDN
- Works with existing BGP infrastructure
- No hardware changes required
- Leverages BGP's proven scalability
1. Dynamic Traffic Engineering
SDN Controller monitors traffic
β Detects congestion on link A
β Commands ExaBGP to adjust BGP attributes
β Traffic shifts to link B
2. Service Chaining
Application requests path through firewall + IDS
β SDN controller computes path
β ExaBGP announces routes with specific next-hops
β Traffic flows through service chain
3. Multi-Tenant Isolation
Tenant A and Tenant B require isolated networks
β SDN controller maintains tenant policies
β ExaBGP announces routes with VPN labels/communities
β Network enforces isolation
Simple integration:
βββββββββββββββββββββββββββββββββββββββ
β SDN Controller β
β (OpenDaylight, ONOS, Custom) β
ββββββββββββββββββ¬βββββββββββββββββββββ
β REST API
βΌ
βββββββββββββββββββββββββββββββββββββββ
β ExaBGP Middleware β
β (API gateway: HTTP β ExaBGP API) β
ββββββββββββββββββ¬βββββββββββββββββββββ
β STDIN/STDOUT
βΌ
βββββββββββββββββββββββββββββββββββββββ
β ExaBGP β
ββββββββββββββββββ¬βββββββββββββββββββββ
β BGP
βΌ
ββββββββββββββββββ
β Network β
β (Routers) β
ββββββββββββββββββ
Components:
- SDN Controller: High-level policy and orchestration
- ExaBGP Middleware: Translates controller commands to BGP
- ExaBGP: BGP protocol implementation
- Network: Standard BGP routers
SDN controller learns network topology via BGP-LS:
βββββββββββββββββββββββββββββββββββββββ
β SDN Controller β
β (Builds topology graph) β
ββββββββββββββββββ²βββββββββββββββββββββ
β BGP-LS
β (link-state info)
ββββββββββββββββββ΄βββββββββββββββββββββ
β ExaBGP β
β (Receives BGP-LS from routers) β
ββββββββββββββββββ²βββββββββββββββββββββ
β BGP-LS
β
ββββββββββ΄βββββββββ
β Routers with β
β BGP-LS enabled β
βββββββββββββββββββ
Benefits:
- Real-time topology awareness
- Link utilization monitoring
- Dynamic path computation
- Automatic failure detection
Combine OpenFlow for edge, BGP for core:
ββββββββββββββββββββββββ
β SDN Controller β
βββββββ¬βββββββββββ¬ββββββ
β β
OpenFlow BGP
β β
βΌ βΌ
βββββββββββ ββββββββββ
β Edge β βExaBGP β
β OpenFlowβ β β
β Switchesβ ββββββ¬ββββ
βββββββββββ β
β β
βββββββ¬ββββββ
βΌ
ββββββββββββββ
β Core BGP β
β Network β
ββββββββββββββ
Use case: Fine-grained control at edge, scalable BGP in core
BGP Link-State (RFC 7752) distributes network topology information via BGP.
Information carried:
- Nodes (routers)
- Links (connections)
- Prefixes (routes)
- Link metrics (bandwidth, delay, utilization)
- TE attributes
βββββββββββββββββββββββββββββββββββββββ
β SDN Controller β
β ββββββββββββββββββββββββββββββββ β
β β Topology Database β β
β β - Nodes, Links, Metrics β β
β β - Path computation β β
β ββββββββββββββββββββββββββββββββ β
ββββββββββββββββ²βββββββββββββββββββββββ
β BGP-LS
β (receives topology updates)
ββββββββββββββββ΄βββββββββββββββββββββββ
β ExaBGP β
β (BGP-LS collector) β
ββββββββββββββββ²βββββββββββββββββββββββ
β BGP-LS
β
βββββββββββββΌβββββββββββ
β β β
βΌ βΌ βΌ
ββββββββ ββββββββ ββββββββ
βRouterβ βRouterβ βRouterβ
β A β β B β β C β
ββββββββ ββββββββ ββββββββ
Receive BGP-LS from routers:
# /etc/exabgp/bgp-ls.conf
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 unicast;
bgp-ls bgp-ls; # Enable BGP-LS
}
api {
processes [ bgp-ls-processor ];
}
}
process bgp-ls-processor {
run /etc/exabgp/bgp-ls-processor.py;
encoder json; # Use JSON for structured data
}Parse BGP-LS and build topology:
#!/usr/bin/env python3
"""
BGP-LS processor for SDN topology discovery
Receives BGP-LS updates and builds network topology graph
"""
import sys
import json
import time
import requests
SDN_CONTROLLER_URL = "http://localhost:8080/topology"
# Topology database
topology = {
'nodes': {},
'links': {},
}
def process_bgp_ls_update(message):
"""Process BGP-LS update message"""
try:
msg = json.loads(message)
if 'neighbor' not in msg:
return
neighbor = msg['neighbor']['address']['peer']
if 'update' in msg:
update = msg['update']
# Process BGP-LS announcements
if 'announce' in update and 'bgp-ls bgp-ls' in update['announce']:
bgp_ls_data = update['announce']['bgp-ls bgp-ls']
for nlri_data in bgp_ls_data.values():
for nlri in nlri_data:
process_nlri(nlri)
# Process BGP-LS withdrawals
if 'withdraw' in update and 'bgp-ls bgp-ls' in update['withdraw']:
bgp_ls_data = update['withdraw']['bgp-ls bgp-ls']
for nlri_data in bgp_ls_data.values():
for nlri in nlri_data:
remove_nlri(nlri)
# Update SDN controller
update_sdn_controller()
except Exception as e:
sys.stderr.write(f"[BGP-LS ERROR] {e}\n")
def process_nlri(nlri):
"""Process BGP-LS NLRI (node, link, or prefix)"""
nlri_type = nlri.get('ls-nlri-type')
if nlri_type == 'node':
# Node advertisement
node_id = nlri.get('node-descriptors', {}).get('router-id')
if node_id:
topology['nodes'][node_id] = {
'router_id': node_id,
'as_number': nlri.get('node-descriptors', {}).get('as-number'),
'isis_area': nlri.get('node-descriptors', {}).get('isis-area-id'),
}
sys.stderr.write(f"[BGP-LS] Added node: {node_id}\n")
elif nlri_type == 'link':
# Link advertisement
local_node = nlri.get('local-node-descriptors', {}).get('router-id')
remote_node = nlri.get('remote-node-descriptors', {}).get('router-id')
if local_node and remote_node:
link_id = f"{local_node}_{remote_node}"
topology['links'][link_id] = {
'local_node': local_node,
'remote_node': remote_node,
'local_ip': nlri.get('link-descriptors', {}).get('ipv4-interface-address'),
'remote_ip': nlri.get('link-descriptors', {}).get('ipv4-neighbor-address'),
'metric': nlri.get('link-attributes', {}).get('igp-metric', 0),
'bandwidth': nlri.get('link-attributes', {}).get('max-link-bandwidth', 0),
}
sys.stderr.write(f"[BGP-LS] Added link: {link_id}\n")
elif nlri_type == 'ipv4-prefix' or nlri_type == 'ipv6-prefix':
# Prefix advertisement
prefix = nlri.get('prefix')
node_id = nlri.get('node-descriptors', {}).get('router-id')
if prefix and node_id:
sys.stderr.write(f"[BGP-LS] Node {node_id} has prefix {prefix}\n")
def remove_nlri(nlri):
"""Remove NLRI from topology"""
nlri_type = nlri.get('ls-nlri-type')
if nlri_type == 'node':
node_id = nlri.get('node-descriptors', {}).get('router-id')
if node_id and node_id in topology['nodes']:
del topology['nodes'][node_id]
sys.stderr.write(f"[BGP-LS] Removed node: {node_id}\n")
elif nlri_type == 'link':
local_node = nlri.get('local-node-descriptors', {}).get('router-id')
remote_node = nlri.get('remote-node-descriptors', {}).get('router-id')
link_id = f"{local_node}_{remote_node}"
if link_id in topology['links']:
del topology['links'][link_id]
sys.stderr.write(f"[BGP-LS] Removed link: {link_id}\n")
def update_sdn_controller():
"""Send topology to SDN controller"""
try:
response = requests.post(
SDN_CONTROLLER_URL,
json=topology,
timeout=5
)
sys.stderr.write(f"[BGP-LS] Updated SDN controller: {response.status_code}\n")
except Exception as e:
sys.stderr.write(f"[BGP-LS] Failed to update controller: {e}\n")
# Main loop
time.sleep(2)
sys.stderr.write("[BGP-LS] Topology collector started\n")
while True:
line = sys.stdin.readline()
if line:
process_bgp_ls_update(line)HTTP gateway for SDN controllers:
#!/usr/bin/env python3
"""
REST API gateway for ExaBGP
Allows SDN controllers to control BGP via HTTP
"""
from flask import Flask, request, jsonify
import subprocess
import threading
import queue
app = Flask(__name__)
# Command queue for ExaBGP
command_queue = queue.Queue()
# Active routes tracking
active_routes = {}
@app.route('/bgp/announce', methods=['POST'])
def announce_route():
"""
Announce BGP route
POST /bgp/announce
{
"prefix": "203.0.113.0/24",
"next_hop": "192.168.1.10",
"med": 100,
"as_path": [65001],
"communities": ["65000:100"]
}
"""
data = request.json
prefix = data.get('prefix')
next_hop = data.get('next_hop', 'self')
med = data.get('med', 100)
as_path = data.get('as_path', [])
communities = data.get('communities', [])
# Build ExaBGP command
command = f"announce route {prefix} next-hop {next_hop}"
if med:
command += f" med {med}"
if as_path:
as_path_str = ' '.join(map(str, as_path))
command += f" as-path [ {as_path_str} ]"
if communities:
community_str = ' '.join(communities)
command += f" community [ {community_str} ]"
# Send to ExaBGP
command_queue.put(command)
# Track active route
active_routes[prefix] = data
return jsonify({
'status': 'success',
'message': f'Announced {prefix}',
'command': command
}), 200
@app.route('/bgp/withdraw', methods=['POST'])
def withdraw_route():
"""
Withdraw BGP route
POST /bgp/withdraw
{
"prefix": "203.0.113.0/24"
}
"""
data = request.json
prefix = data.get('prefix')
command = f"withdraw route {prefix}"
command_queue.put(command)
# Remove from active routes
if prefix in active_routes:
del active_routes[prefix]
return jsonify({
'status': 'success',
'message': f'Withdrew {prefix}'
}), 200
@app.route('/bgp/routes', methods=['GET'])
def get_routes():
"""Get all active routes"""
return jsonify({
'routes': active_routes
}), 200
def exabgp_worker():
"""Send commands to ExaBGP via STDOUT"""
import sys
import time
time.sleep(2)
while True:
try:
command = command_queue.get(timeout=1)
sys.stdout.write(command + "\n")
sys.stdout.flush()
sys.stderr.write(f"[API] Executed: {command}\n")
except queue.Empty:
pass
if __name__ == '__main__':
# Start ExaBGP worker thread
worker = threading.Thread(target=exabgp_worker, daemon=True)
worker.start()
# Start Flask API
app.run(host='0.0.0.0', port=5000)ExaBGP Configuration:
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 unicast;
}
api {
processes [ rest-api ];
}
}
process rest-api {
run /etc/exabgp/rest-api.py;
encoder text;
}Usage from SDN controller:
# Announce route
curl -X POST http://localhost:5000/bgp/announce \
-H "Content-Type: application/json" \
-d '{
"prefix": "203.0.113.0/24",
"next_hop": "192.168.1.10",
"med": 50,
"communities": ["65000:100"]
}'
# Withdraw route
curl -X POST http://localhost:5000/bgp/withdraw \
-H "Content-Type: application/json" \
-d '{"prefix": "203.0.113.0/24"}'
# List active routes
curl http://localhost:5000/bgp/routesOpenDaylight is a popular open-source SDN controller.
Architecture:
βββββββββββββββββββββββββββββββββββββββ
β OpenDaylight Controller β
β ββββββββββββββββββββββββββββββββ β
β β BGP Northbound API β β
β ββββββββββββββββ¬ββββββββββββββββ β
β β β
β ββββββββββββββββΌββββββββββββββββ β
β β BGP Application Module β β
β ββββββββββββββββ¬ββββββββββββββββ β
β β β
β ββββββββββββββββΌββββββββββββββββ β
β β ExaBGP Connector Plugin β β
β ββββββββββββββββ¬ββββββββββββββββ β
βββββββββββββββββββΌββββββββββββββββββββ
β REST/gRPC
βΌ
βββββββββββββββββββ
β ExaBGP β
βββββββββββββββββββ
Implementation:
// OpenDaylight BGP service
public class ExaBGPService {
private static final String EXABGP_API_URL = "http://localhost:5000";
public void announceRoute(String prefix, String nextHop, int med) {
HttpClient client = HttpClient.newHttpClient();
String json = String.format(
"{\"prefix\":\"%s\",\"next_hop\":\"%s\",\"med\":%d}",
prefix, nextHop, med
);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(EXABGP_API_URL + "/bgp/announce"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(json))
.build();
client.sendAsync(request, HttpRequest.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println);
}
public void withdrawRoute(String prefix) {
// Similar implementation for withdrawal
}
}ONOS (Open Network Operating System) is another SDN controller platform.
ExaBGP Application for ONOS:
#!/usr/bin/env python3
"""
ONOS integration with ExaBGP
Receives routing decisions from ONOS and programs BGP
"""
import sys
import time
import requests
import json
ONOS_API_URL = "http://localhost:8181/onos/v1"
ONOS_USERNAME = "onos"
ONOS_PASSWORD = "rocks"
def get_routing_decisions():
"""Query ONOS for routing decisions"""
try:
response = requests.get(
f"{ONOS_API_URL}/routes",
auth=(ONOS_USERNAME, ONOS_PASSWORD),
timeout=5
)
if response.status_code == 200:
return response.json().get('routes', [])
except Exception as e:
sys.stderr.write(f"[ONOS] Error querying routes: {e}\n")
return []
def announce_route(prefix, next_hop, attributes):
"""Announce route via ExaBGP"""
command = f"announce route {prefix} next-hop {next_hop}"
if 'med' in attributes:
command += f" med {attributes['med']}"
if 'communities' in attributes:
community_str = ' '.join(attributes['communities'])
command += f" community [{community_str}]"
sys.stdout.write(command + "\n")
sys.stdout.flush()
sys.stderr.write(f"[ONOS] Announced: {command}\n")
time.sleep(2)
sys.stderr.write("[ONOS] ExaBGP-ONOS integration started\n")
while True:
routes = get_routing_decisions()
for route in routes:
prefix = route.get('prefix')
next_hop = route.get('nextHop')
attributes = route.get('attributes', {})
if prefix and next_hop:
announce_route(prefix, next_hop, attributes)
time.sleep(30)SDN controller computes paths with constraints:
#!/usr/bin/env python3
"""
Path computation engine for SDN
Uses topology from BGP-LS to compute optimal paths
"""
import networkx as nx
class PathComputationEngine:
def __init__(self):
self.topology = nx.Graph()
def update_topology(self, bgp_ls_data):
"""Update topology from BGP-LS"""
# Add nodes
for node_id, node_data in bgp_ls_data['nodes'].items():
self.topology.add_node(node_id, **node_data)
# Add links
for link_id, link_data in bgp_ls_data['links'].items():
local = link_data['local_node']
remote = link_data['remote_node']
weight = link_data.get('metric', 1)
self.topology.add_edge(
local, remote,
weight=weight,
bandwidth=link_data.get('bandwidth', 0)
)
def compute_path(self, source, destination, constraints=None):
"""
Compute path from source to destination
Constraints:
- min_bandwidth: Minimum link bandwidth required
- max_latency: Maximum acceptable latency
- avoid_nodes: List of nodes to avoid
"""
if constraints is None:
constraints = {}
# Filter topology based on constraints
filtered_graph = self.topology.copy()
# Bandwidth constraint
min_bandwidth = constraints.get('min_bandwidth', 0)
if min_bandwidth > 0:
edges_to_remove = [
(u, v) for u, v, data in filtered_graph.edges(data=True)
if data.get('bandwidth', 0) < min_bandwidth
]
filtered_graph.remove_edges_from(edges_to_remove)
# Node avoidance constraint
avoid_nodes = constraints.get('avoid_nodes', [])
filtered_graph.remove_nodes_from(avoid_nodes)
# Compute shortest path
try:
path = nx.shortest_path(
filtered_graph,
source=source,
target=destination,
weight='weight'
)
return path
except nx.NetworkXNoPath:
return None
def compute_multiple_paths(self, source, destination, k=3):
"""Compute K shortest paths"""
try:
paths = list(nx.shortest_simple_paths(
self.topology,
source=source,
target=destination,
weight='weight'
))
return paths[:k]
except:
return []
# Example usage
pce = PathComputationEngine()
# Update with BGP-LS data
pce.update_topology(bgp_ls_topology)
# Compute path with constraints
path = pce.compute_path(
source='10.0.0.1',
destination='10.0.0.10',
constraints={
'min_bandwidth': 1000, # 1 Gbps
'avoid_nodes': ['10.0.0.5'], # Avoid this router
}
)
if path:
print(f"Computed path: {' β '.join(path)}")
# Program path via ExaBGP
else:
print("No path found with constraints")Scenario: Automatically shift traffic away from congested links
#!/usr/bin/env python3
"""
Dynamic traffic engineering based on link utilization
Monitors BGP-LS metrics and adjusts BGP announcements
"""
import sys
import time
CONGESTION_THRESHOLD = 80 # Percentage
def get_link_utilization(link_id):
"""Get link utilization from BGP-LS or SNMP"""
# TODO: Query from BGP-LS or monitoring system
return 0
def adjust_routing_for_congestion(link_id, utilization):
"""Adjust BGP to avoid congested link"""
if utilization > CONGESTION_THRESHOLD:
# Prepend AS-PATH for routes using this link
sys.stdout.write(
f"announce route 203.0.113.0/24 "
f"next-hop self "
f"as-path [ 65001 65001 65001 ]\n" # Prepend to make less attractive
)
sys.stdout.flush()
sys.stderr.write(
f"[SDN-TE] Link {link_id} congested ({utilization}%), "
f"prepending AS-PATH\n"
)
else:
# Normal announcement
sys.stdout.write(
f"announce route 203.0.113.0/24 next-hop self\n"
)
sys.stdout.flush()
time.sleep(2)
while True:
# Check all critical links
for link_id in ['link1', 'link2', 'link3']:
utilization = get_link_utilization(link_id)
adjust_routing_for_congestion(link_id, utilization)
time.sleep(30)Scenario: Route traffic through security appliances
#!/usr/bin/env python3
"""
Service Function Chaining with BGP
Route traffic through specific next-hops (firewall, IDS, etc.)
"""
import sys
import time
# Service chain topology
SERVICE_CHAINS = {
'critical': [
'192.168.10.1', # Firewall
'192.168.10.2', # IDS
'192.168.10.3', # DLP
],
'standard': [
'192.168.10.1', # Firewall only
],
}
def announce_with_service_chain(prefix, chain_type):
"""Announce prefix with specific next-hop for service chaining"""
chain = SERVICE_CHAINS.get(chain_type, [])
if not chain:
return
# Use first service in chain as next-hop
next_hop = chain[0]
# Tag with community indicating chain type
community = f"65000:{100 if chain_type == 'critical' else 200}"
sys.stdout.write(
f"announce route {prefix} "
f"next-hop {next_hop} "
f"community [{community}]\n"
)
sys.stdout.flush()
sys.stderr.write(
f"[SFC] {prefix} β {chain_type} chain via {next_hop}\n"
)
# Announce critical applications through full service chain
announce_with_service_chain("10.1.0.0/24", "critical")
# Announce standard applications through firewall only
announce_with_service_chain("10.2.0.0/24", "standard")Scenario: Isolated networks for different tenants
#!/usr/bin/env python3
"""
Multi-tenant routing with VPN labels
Each tenant gets isolated routing domain
"""
import sys
import time
# Tenant configuration
TENANTS = {
'tenant_a': {
'prefixes': ['172.16.0.0/16'],
'rd': '65001:100',
'rt_export': '65001:100',
'rt_import': '65001:100',
},
'tenant_b': {
'prefixes': ['172.17.0.0/16'],
'rd': '65001:200',
'rt_export': '65001:200',
'rt_import': '65001:200',
},
}
def announce_tenant_routes():
"""Announce routes with VPN labels for tenant isolation"""
for tenant_id, config in TENANTS.items():
rd = config['rd']
rt_export = config['rt_export']
for prefix in config['prefixes']:
# Announce with route-distinguisher and route-target
sys.stdout.write(
f"announce route {prefix} "
f"next-hop self "
f"route-distinguisher {rd} "
f"extended-community [ target:{rt_export} ]\n"
)
sys.stderr.write(f"[SDN] Announced routes for {tenant_id}\n")
sys.stdout.flush()
time.sleep(2)
# Announce tenant routes
announce_tenant_routes()
# Keep process alive
while True:
time.sleep(300)Good:
SDN Controller β ExaBGP β BGP Network
(Control) (API) (Data)
Bad:
SDN Controller directly manipulating router configs
# Always maintain accurate topology
def update_topology_regularly():
while True:
topology = get_bgp_ls_topology()
validate_topology(topology)
update_path_computation(topology)
time.sleep(60)def validate_path(path):
"""Ensure computed path is valid"""
# Check path exists
if not path:
return False
# Check no loops
if len(path) != len(set(path)):
return False
# Check all links operational
for i in range(len(path) - 1):
if not is_link_up(path[i], path[i+1]):
return False
return True# If optimal path not available, use backup
primary_path = compute_path(src, dst, constraints)
if not primary_path:
# Relax constraints
backup_path = compute_path(src, dst, relaxed_constraints)
if backup_path:
use_path(backup_path)from prometheus_client import Gauge, Counter
topology_nodes = Gauge('sdn_topology_nodes', 'Number of nodes')
topology_links = Gauge('sdn_topology_links', 'Number of links')
paths_computed = Counter('sdn_paths_computed', 'Paths computed')
bgp_routes_announced = Counter('sdn_bgp_announced', 'BGP routes announced')def verify_topology():
"""Compare BGP-LS topology with actual network"""
bgp_ls_topology = get_bgp_ls_topology()
actual_topology = poll_network_devices()
# Check for discrepancies
missing_nodes = set(actual_topology['nodes']) - set(bgp_ls_topology['nodes'])
missing_links = set(actual_topology['links']) - set(bgp_ls_topology['links'])
if missing_nodes or missing_links:
alert(f"Topology mismatch: {len(missing_nodes)} nodes, {len(missing_links)} links")# Check routes announced by ExaBGP
exabgpcli show adj-rib out
# Verify routes received by routers
show ip bgp neighbors 192.168.1.2 routesSymptoms: BGP-LS not receiving updates
Check:
# Verify BGP-LS enabled on routers
show bgp link-state link-state summary
# Check ExaBGP receiving BGP-LS
tail -f /var/log/exabgp.log | grep bgp-lsSymptoms: ExaBGP announces routes but traffic doesn't follow
Diagnosis:
# Check if routes in router RIB
show ip bgp 203.0.113.0
# Check if routes installed in FIB
show ip route 203.0.113.0
# Verify next-hops are reachable
ping 192.168.1.10Symptoms: SDN controller commands timeout
Solutions:
- Increase API timeout values
- Check network connectivity
- Monitor ExaBGP process health
- Implement request queuing
- BGP-LS Overview - BGP-LS details
- Traffic Engineering - Advanced BGP control
- Load Balancing - Load distribution
- Monitoring - Monitoring setup
- Debugging - Troubleshooting guide
- API Overview - API patterns
- Configuration Syntax - Config reference
Ready to integrate SDN? See Quick Start β
π» Ghost written by Claude (Anthropic AI)
π Home
π Getting Started
π§ API
π‘οΈ Use Cases
π Address Families
βοΈ Configuration
π Operations
π Reference
- Architecture
- BGP State Machine
- Communities (RFC)
- Extended Communities
- BGP Ecosystem
- Capabilities (AFI/SAFI)
- RFC Support
π Migration
π Community
π External
- GitHub Repo β
- Slack β
- Issues β
π» Ghost written by Claude (Anthropic AI)