Skip to content

Use Cases Enterprise WAN

Thomas Mangin edited this page Nov 15, 2025 · 1 revision

Enterprise WAN

ExaBGP enables enterprise WAN connectivity using MPLS L3VPN, SD-WAN integration, and hybrid multi-cloud networking for branch offices and remote sites.

Table of Contents

Overview

Enterprise WAN Requirements

Modern enterprise WANs need:

  • Multi-site connectivity: Connect headquarters, branch offices, data centers
  • Application performance: Prioritize critical applications
  • Cost efficiency: Balance MPLS, internet, and cloud connectivity
  • Security: Encrypt traffic, segment networks
  • Resilience: Automatic failover between links
  • Cloud integration: Connect to AWS, Azure, GCP

ExaBGP Role

ExaBGP enables enterprise WAN by:

  1. VPN route distribution: Advertise site prefixes via L3VPN
  2. SD-WAN integration: Dynamic path selection based on health/performance
  3. Cloud connectivity: Integrate with cloud provider BGP services
  4. Traffic engineering: Use communities for policy-based routing

Important: ExaBGP exchanges routes but does NOT create VPN tunnels. Use IPsec, GRE, or MPLS for actual connectivity.

MPLS L3VPN for Branch Offices

Architecture

[HQ Site]  <--L3VPN-->  [Service Provider MPLS]  <--L3VPN-->  [Branch Sites]
VRF: CORP                                                      VRF: CORP

Configuration

Branch Office (/etc/exabgp/branch-vpn.conf):

process vpn-routes {
    run python3 /etc/exabgp/announce-branch.py;
    encoder text;
}

neighbor 192.0.2.1 {
    router-id 10.1.1.1;
    local-address 10.1.1.1;
    local-as 65001;
    peer-as 65000;

    family {
        ipv4 mpls-vpn;
    }

    api {
        processes [ vpn-routes ];
    }
}

API Program (/etc/exabgp/announce-branch.py):

#!/usr/bin/env python3
import sys

# Branch site configuration
BRANCH_ID = "branch-01"
BRANCH_PREFIX = "10.100.1.0/24"
RD = "10.1.1.1:100"
RT_EXPORT = "65000:100"
RT_IMPORT = "65000:100"
NEXT_HOP = "10.1.1.1"

# Announce branch prefix to MPLS VPN
print(f"announce route {BRANCH_PREFIX} "
      f"next-hop {NEXT_HOP} "
      f"route-distinguisher {RD} "
      f"route-target {RT_EXPORT}", flush=True)

while True:
    line = sys.stdin.readline().strip()
    if not line:
        break

Multi-VRF Support

Support multiple customer VRFs:

#!/usr/bin/env python3
import sys

# Multiple VRFs for segmentation
VRFS = {
    'corporate': {
        'prefix': '10.100.1.0/24',
        'rd': '10.1.1.1:100',
        'rt': '65000:100'
    },
    'guest': {
        'prefix': '10.200.1.0/24',
        'rd': '10.1.1.1:200',
        'rt': '65000:200'
    },
    'iot': {
        'prefix': '10.300.1.0/24',
        'rd': '10.1.1.1:300',
        'rt': '65000:300'
    }
}

NEXT_HOP = "10.1.1.1"

# Announce all VRFs
for vrf_name, config in VRFS.items():
    print(f"announce route {config['prefix']} "
          f"next-hop {NEXT_HOP} "
          f"route-distinguisher {config['rd']} "
          f"route-target {config['rt']}", flush=True)

while True:
    line = sys.stdin.readline().strip()
    if not line:
        break

SD-WAN Integration

Path Selection

Dynamically select best WAN path:

#!/usr/bin/env python3
import sys
import time
import subprocess

# WAN links
LINKS = {
    'mpls': {'next_hop': '10.1.1.1', 'priority': 1},
    'internet': {'next_hop': '10.2.2.1', 'priority': 2},
    'lte': {'next_hop': '10.3.3.1', 'priority': 3}
}

PREFIX = "10.100.1.0/24"

def check_link_health(link_name):
    """Check if WAN link is healthy"""
    next_hop = LINKS[link_name]['next_hop']
    try:
        result = subprocess.run(
            ['ping', '-c', '3', '-W', '2', next_hop],
            capture_output=True
        )
        return result.returncode == 0
    except:
        return False

def select_best_link():
    """Select best available WAN link"""
    for link_name in sorted(LINKS.keys(), key=lambda x: LINKS[x]['priority']):
        if check_link_health(link_name):
            return link_name, LINKS[link_name]['next_hop']
    return None, None

# Monitor and switch paths
current_link = None

while True:
    best_link, next_hop = select_best_link()

    if best_link != current_link:
        # Withdraw old path
        if current_link:
            old_hop = LINKS[current_link]['next_hop']
            print(f"withdraw route {PREFIX} next-hop {old_hop}", flush=True)

        # Announce new path
        if next_hop:
            print(f"announce route {PREFIX} next-hop {next_hop}", flush=True)
            current_link = best_link

    time.sleep(10)

Application-Based Routing

Route based on application requirements:

#!/usr/bin/env python3
import sys

# Application-to-path mapping
APPLICATIONS = {
    'voice': {
        'prefix': '10.100.10.0/24',
        'path': 'mpls',  # Low latency
        'community': '65000:100'
    },
    'bulk-transfer': {
        'prefix': '10.100.20.0/24',
        'path': 'internet',  # High bandwidth
        'community': '65000:200'
    },
    'guest': {
        'prefix': '10.100.30.0/24',
        'path': 'internet',  # Separate from corporate
        'community': '65000:300'
    }
}

LINKS = {
    'mpls': '10.1.1.1',
    'internet': '10.2.2.1',
    'lte': '10.3.3.1'
}

# Announce application routes with appropriate paths
for app_name, config in APPLICATIONS.items():
    next_hop = LINKS.get(config['path'])
    if next_hop:
        print(f"announce route {config['prefix']} "
              f"next-hop {next_hop} "
              f"community [{config['community']}]", flush=True)

while True:
    line = sys.stdin.readline().strip()
    if not line:
        break

Hybrid Cloud Connectivity

AWS Integration

Connect enterprise WAN to AWS VPC:

#!/usr/bin/env python3
import sys
import boto3

# AWS configuration
AWS_REGION = 'us-east-1'
VPC_ID = 'vpc-12345678'

# On-premises configuration
ONPREM_PREFIX = "10.100.0.0/16"
NEXT_HOP = "10.1.1.1"

def get_vpc_prefixes():
    """Get VPC CIDR blocks from AWS"""
    ec2 = boto3.client('ec2', region_name=AWS_REGION)
    try:
        vpcs = ec2.describe_vpcs(VpcIds=[VPC_ID])
        cidrs = []
        for vpc in vpcs['Vpcs']:
            cidr = vpc.get('CidrBlock')
            if cidr:
                cidrs.append(cidr)
        return cidrs
    except:
        return []

# Announce on-premises routes to AWS
print(f"announce route {ONPREM_PREFIX} next-hop {NEXT_HOP}", flush=True)

# Can also learn and re-advertise AWS VPC routes
vpc_prefixes = get_vpc_prefixes()
for prefix in vpc_prefixes:
    # Re-advertise VPC routes to other sites if needed
    pass

while True:
    line = sys.stdin.readline().strip()
    if not line:
        break

Multi-Cloud Routing

Route between on-prem, AWS, Azure, GCP:

#!/usr/bin/env python3
import sys

# Cloud connectivity
CLOUDS = {
    'aws': {
        'prefix': '10.10.0.0/16',
        'next_hop': '169.254.1.1',  # AWS VGW
        'community': '65000:10'
    },
    'azure': {
        'prefix': '10.20.0.0/16',
        'next_hop': '169.254.2.1',  # Azure VNG
        'community': '65000:20'
    },
    'gcp': {
        'prefix': '10.30.0.0/16',
        'next_hop': '169.254.3.1',  # GCP Cloud Router
        'community': '65000:30'
    },
    'onprem': {
        'prefix': '10.100.0.0/16',
        'next_hop': '10.1.1.1',
        'community': '65000:100'
    }
}

# Announce all cloud routes
for cloud_name, config in CLOUDS.items():
    print(f"announce route {config['prefix']} "
          f"next-hop {config['next_hop']} "
          f"community [{config['community']}]", flush=True)

while True:
    line = sys.stdin.readline().strip()
    if not line:
        break

Configuration Examples

Hub-and-Spoke Topology

Central hub site connecting to multiple branches:

Hub Site (/etc/exabgp/hub.conf):

process hub-routes {
    run python3 /etc/exabgp/hub-announce.py;
    encoder json;
}

# Connection to service provider
neighbor 192.0.2.1 {
    router-id 10.0.0.1;
    local-address 10.0.0.1;
    local-as 65001;
    peer-as 65000;

    family {
        ipv4 mpls-vpn;
        ipv4 unicast;
    }

    api {
        processes [ hub-routes ];
    }
}

Hub API Program:

#!/usr/bin/env python3
import sys

# Hub site routes
HUB_ROUTES = {
    'datacenter': '10.0.0.0/16',
    'services': '10.1.0.0/16',
    'internet-gateway': '0.0.0.0/0'
}

RD = "10.0.0.1:100"
RT = "65000:100"
NEXT_HOP = "10.0.0.1"

# Announce hub routes to all branches
for route_name, prefix in HUB_ROUTES.items():
    print(f"announce route {prefix} "
          f"next-hop {NEXT_HOP} "
          f"route-distinguisher {RD} "
          f"route-target {RT}", flush=True)

while True:
    line = sys.stdin.readline().strip()
    if not line:
        break

Dual-Homed Branch

Branch with redundant WAN links:

#!/usr/bin/env python3
import sys
import time

BRANCH_PREFIX = "10.100.1.0/24"

# Primary and backup links
LINKS = {
    'primary': {
        'next_hop': '10.1.1.1',
        'as_prepend': '',  # Preferred path
        'local_pref': 200
    },
    'backup': {
        'next_hop': '10.2.2.1',
        'as_prepend': '65001 65001 65001',  # Less preferred
        'local_pref': 100
    }
}

def check_link(next_hop):
    """Check link health"""
    import subprocess
    try:
        result = subprocess.run(
            ['ping', '-c', '2', '-W', '1', next_hop],
            capture_output=True
        )
        return result.returncode == 0
    except:
        return False

# Announce via both links with different preferences
primary_ok = check_link(LINKS['primary']['next_hop'])
backup_ok = check_link(LINKS['backup']['next_hop'])

if primary_ok:
    print(f"announce route {BRANCH_PREFIX} "
          f"next-hop {LINKS['primary']['next_hop']} "
          f"local-preference {LINKS['primary']['local_pref']}", flush=True)

if backup_ok:
    print(f"announce route {BRANCH_PREFIX} "
          f"next-hop {LINKS['backup']['next_hop']} "
          f"local-preference {LINKS['backup']['local_pref']} "
          f"as-path [{LINKS['backup']['as_prepend']}]", flush=True)

while True:
    time.sleep(30)

See Also

Address Families

Related Use Cases

Configuration

Features


πŸ‘» Ghost written by Claude (Anthropic AI)

Clone this wiki locally