-
Notifications
You must be signed in to change notification settings - Fork 460
Use Cases Multi Tenant
ExaBGP enables multi-tenant network isolation using VRFs, Route Target filtering, and tenant separation for cloud providers and service providers.
Multi-tenant networks require:
- Isolation: Complete separation between tenants
- Scalability: Support thousands of tenants
- Flexibility: Different topologies per tenant
- Security: Prevent cross-tenant traffic
- Automation: Dynamic tenant provisioning
- Resource efficiency: Share infrastructure efficiently
ExaBGP enables multi-tenancy by:
- VRF route distribution: Advertise tenant routes with Route Distinguishers
- Route Target filtering: Control route import/export per tenant
- Dynamic provisioning: Create/delete tenant networks via API
- Cross-tenant services: Controlled inter-tenant connectivity
Important: ExaBGP announces tenant routes but does NOT create VRFs or manipulate forwarding tables. Your network OS must configure VRFs and route installation.
Isolate tenants using VRFs:
#!/usr/bin/env python3
import sys
# Tenant configuration
TENANTS = {
'tenant-001': {
'vrf': 'TENANT-001',
'rd': '10.0.0.1:1001',
'rt_export': '65000:1001',
'rt_import': '65000:1001',
'prefixes': ['192.168.1.0/24', '192.168.2.0/24']
},
'tenant-002': {
'vrf': 'TENANT-002',
'rd': '10.0.0.1:1002',
'rt_export': '65000:1002',
'rt_import': '65000:1002',
'prefixes': ['192.168.1.0/24', '192.168.3.0/24'] # Can overlap!
},
'tenant-003': {
'vrf': 'TENANT-003',
'rd': '10.0.0.1:1003',
'rt_export': '65000:1003',
'rt_import': '65000:1003',
'prefixes': ['10.0.0.0/16']
}
}
NEXT_HOP = "10.0.0.1"
# Announce tenant routes with isolation
for tenant_id, config in TENANTS.items():
for prefix in config['prefixes']:
print(f"announce route {prefix} "
f"next-hop {NEXT_HOP} "
f"route-distinguisher {config['rd']} "
f"route-target {config['rt_export']}", flush=True)
while True:
line = sys.stdin.readline().strip()
if not line:
breakSupport sub-tenants with hierarchical VRFs:
#!/usr/bin/env python3
import sys
# Hierarchical tenant structure
HIERARCHY = {
'enterprise-a': {
'rd': '10.0.0.1:1000',
'rt': '65000:1000',
'prefixes': ['10.100.0.0/16'], # Enterprise-wide
'sub-tenants': {
'dept-sales': {
'rd': '10.0.0.1:1001',
'rt': '65000:1001',
'rt_import_parent': '65000:1000', # Import from parent
'prefixes': ['10.100.1.0/24']
},
'dept-eng': {
'rd': '10.0.0.1:1002',
'rt': '65000:1002',
'rt_import_parent': '65000:1000',
'prefixes': ['10.100.2.0/24']
}
}
}
}
NEXT_HOP = "10.0.0.1"
# Announce parent tenant
for enterprise, config in HIERARCHY.items():
for prefix in config['prefixes']:
print(f"announce route {prefix} "
f"next-hop {NEXT_HOP} "
f"route-distinguisher {config['rd']} "
f"route-target {config['rt']}", flush=True)
# Announce sub-tenants
for dept, sub_config in config.get('sub-tenants', {}).items():
for prefix in sub_config['prefixes']:
# Export both sub-tenant RT and parent RT for selective import
print(f"announce route {prefix} "
f"next-hop {NEXT_HOP} "
f"route-distinguisher {sub_config['rd']} "
f"route-target {sub_config['rt']} "
f"route-target {sub_config['rt_import_parent']}", flush=True)
while True:
line = sys.stdin.readline().strip()
if not line:
breakUse Route Target Constraint (RFC 4684) to reduce route propagation:
#!/usr/bin/env python3
import sys
# Tenant-to-PE mapping
PE_TENANTS = {
'pe1': ['65000:1001', '65000:1002', '65000:1003'],
'pe2': ['65000:2001', '65000:2002'],
'pe3': ['65000:3001', '65000:3002', '65000:3003', '65000:3004']
}
PE_ID = 'pe1' # Current PE
# Announce RT-Constrain for subscribed tenants
for rt in PE_TENANTS.get(PE_ID, []):
# RFC 4684 Route Target Constraint
print(f"announce route-target {rt}", flush=True)
while True:
line = sys.stdin.readline().strip()
if not line:
breakControl which tenant routes are imported:
#!/usr/bin/env python3
import sys
import json
# Tenant route import policy
IMPORT_POLICY = {
'tenant-001': ['65000:1001', '65000:9999'], # Import own + shared services
'tenant-002': ['65000:1002', '65000:9999'],
'tenant-003': ['65000:1003'] # Isolated, no shared services
}
def should_import_route(tenant_id, route_targets):
"""Determine if route should be imported for tenant"""
allowed_rts = IMPORT_POLICY.get(tenant_id, [])
return any(rt in allowed_rts for rt in route_targets)
# Listen for route updates
while True:
line = sys.stdin.readline().strip()
if not line:
continue
try:
data = json.loads(line)
# Parse and filter routes based on policy
# This is illustrative - actual filtering happens in router
except:
passCreate tenants on-demand via API:
#!/usr/bin/env python3
import sys
import json
# Tenant database (in-memory for example)
TENANTS = {}
# Route Distinguisher allocation
RD_BASE = "10.0.0.1"
RT_BASE = 65000
next_tenant_id = 1000
def create_tenant(tenant_name, prefixes):
"""Dynamically create new tenant"""
global next_tenant_id
tenant_id = next_tenant_id
next_tenant_id += 1
tenant_config = {
'name': tenant_name,
'id': tenant_id,
'rd': f"{RD_BASE}:{tenant_id}",
'rt_export': f"{RT_BASE}:{tenant_id}",
'rt_import': f"{RT_BASE}:{tenant_id}",
'prefixes': prefixes
}
TENANTS[tenant_name] = tenant_config
# Announce tenant routes
for prefix in prefixes:
print(f"announce route {prefix} "
f"next-hop self "
f"route-distinguisher {tenant_config['rd']} "
f"route-target {tenant_config['rt_export']}", flush=True)
return tenant_config
def delete_tenant(tenant_name):
"""Remove tenant and withdraw routes"""
if tenant_name not in TENANTS:
return
tenant_config = TENANTS[tenant_name]
# Withdraw all tenant routes
for prefix in tenant_config['prefixes']:
print(f"withdraw route {prefix} "
f"route-distinguisher {tenant_config['rd']}", flush=True)
del TENANTS[tenant_name]
def add_tenant_prefix(tenant_name, prefix):
"""Add prefix to existing tenant"""
if tenant_name not in TENANTS:
return
tenant_config = TENANTS[tenant_name]
tenant_config['prefixes'].append(prefix)
print(f"announce route {prefix} "
f"next-hop self "
f"route-distinguisher {tenant_config['rd']} "
f"route-target {tenant_config['rt_export']}", flush=True)
# Listen for provisioning commands
while True:
line = sys.stdin.readline().strip()
if not line:
continue
try:
command = json.loads(line)
if command.get('action') == 'create_tenant':
create_tenant(
command['tenant_name'],
command.get('prefixes', [])
)
elif command.get('action') == 'delete_tenant':
delete_tenant(command['tenant_name'])
elif command.get('action') == 'add_prefix':
add_tenant_prefix(
command['tenant_name'],
command['prefix']
)
except Exception as e:
# Log error
passIntegrate with OpenStack/Kubernetes for automatic tenant provisioning:
#!/usr/bin/env python3
import sys
import json
from neutronclient.v2_0 import client as neutron_client
# OpenStack Neutron integration
OS_AUTH_URL = "http://controller:5000/v3"
OS_USERNAME = "admin"
OS_PASSWORD = "secret"
OS_PROJECT = "admin"
RD_BASE = "10.0.0.1"
RT_BASE = 65000
def get_neutron_client():
return neutron_client.Client(
username=OS_USERNAME,
password=OS_PASSWORD,
project_name=OS_PROJECT,
auth_url=OS_AUTH_URL
)
def sync_neutron_networks():
"""Sync Neutron networks to BGP VPNs"""
neutron = get_neutron_client()
networks = neutron.list_networks()['networks']
for network in networks:
network_id = network['id']
network_name = network['name']
# Derive VPN parameters from network ID
tenant_id = int(network_id[:8], 16) % 65535
rd = f"{RD_BASE}:{tenant_id}"
rt = f"{RT_BASE}:{tenant_id}"
# Get subnets for this network
subnets = neutron.list_subnets(network_id=network_id)['subnets']
for subnet in subnets:
cidr = subnet['cidr']
print(f"announce route {cidr} "
f"next-hop self "
f"route-distinguisher {rd} "
f"route-target {rt}", flush=True)
# Initial sync
sync_neutron_networks()
# Periodic sync
import time
while True:
time.sleep(300) # Re-sync every 5 minutes
sync_neutron_networks()Configuration (/etc/exabgp/multi-tenant-pe.conf):
process tenant-controller {
run python3 /etc/exabgp/tenant-announce.py;
encoder json;
}
neighbor 10.0.0.1 {
router-id 10.1.1.1;
local-address 10.1.1.1;
local-as 65001;
peer-as 65001;
family {
ipv4 mpls-vpn;
ipv4 rtc; # Route Target Constraint
}
api {
processes [ tenant-controller ];
}
}API Program (/etc/exabgp/shared-services.py):
#!/usr/bin/env python3
import sys
# Shared services VRF (DNS, NTP, monitoring)
SHARED_SERVICES = {
'rd': '10.0.0.1:9999',
'rt_export': '65000:9999', # All tenants import this
'prefixes': [
'10.255.1.0/24', # DNS servers
'10.255.2.0/24', # NTP servers
'10.255.3.0/24' # Monitoring
]
}
NEXT_HOP = "10.0.0.1"
# Announce shared services
for prefix in SHARED_SERVICES['prefixes']:
print(f"announce route {prefix} "
f"next-hop {NEXT_HOP} "
f"route-distinguisher {SHARED_SERVICES['rd']} "
f"route-target {SHARED_SERVICES['rt_export']}", flush=True)
while True:
line = sys.stdin.readline().strip()
if not line:
breakDifferent internet gateways per tenant:
#!/usr/bin/env python3
import sys
# Tenant internet gateway mappings
TENANT_GATEWAYS = {
'tenant-001': {
'gateway': '10.1.1.1',
'rd': '10.0.0.1:1001',
'rt': '65000:1001'
},
'tenant-002': {
'gateway': '10.1.2.1',
'rd': '10.0.0.1:1002',
'rt': '65000:1002'
}
}
# Announce default route per tenant
for tenant, config in TENANT_GATEWAYS.items():
print(f"announce route 0.0.0.0/0 "
f"next-hop {config['gateway']} "
f"route-distinguisher {config['rd']} "
f"route-target {config['rt']}", flush=True)
while True:
line = sys.stdin.readline().strip()
if not line:
breakπ» 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)