-
Notifications
You must be signed in to change notification settings - Fork 460
Use Cases IPTV
ExaBGP enables efficient IPTV and video distribution using multicast BGP, Multicast VPN, and optimized content delivery.
IPTV and video distribution systems need:
- Efficient multicast: One stream to many receivers
- Quality of Service: Prioritize video traffic
- Channel management: Dynamic channel lineup changes
- Multi-tenancy: Separate content per customer/subscriber
- Source redundancy: Failover between video sources
- Geographic distribution: Regional content delivery
ExaBGP enables IPTV distribution by:
- Multicast source advertisement: Announce video sources via BGP
- Multicast VPN: Isolate IPTV traffic per customer/tenant
- Source failover: Automatically switch between video sources
- Channel control: Dynamically add/remove channels
Important: ExaBGP announces multicast routes but does NOT handle IGMP/PIM or actual multicast forwarding. Your router or multicast daemon handles packet replication.
Advertise multicast source:
#!/usr/bin/env python3
import sys
# Video source configuration
VIDEO_SOURCES = {
'hd-sports': {
'group': '239.1.1.10',
'source': '10.1.1.100',
'rate': '8000' # kbps
},
'hd-news': {
'group': '239.1.1.11',
'source': '10.1.1.101',
'rate': '8000'
},
'sd-general': {
'group': '239.1.1.20',
'source': '10.1.1.102',
'rate': '3000'
}
}
NEXT_HOP = "10.1.1.1"
# Announce multicast sources
for channel, config in VIDEO_SOURCES.items():
# Announce (S,G) state via BGP
print(f"announce route {config['group']}/32 "
f"next-hop {NEXT_HOP} "
f"origin igp", flush=True)
while True:
line = sys.stdin.readline().strip()
if not line:
breakFailover between primary and backup video sources:
#!/usr/bin/env python3
import sys
import time
import socket
# Channel configuration
CHANNEL = {
'group': '239.1.1.10',
'primary_source': '10.1.1.100',
'backup_source': '10.1.1.101'
}
NEXT_HOP_PRIMARY = "10.1.1.1"
NEXT_HOP_BACKUP = "10.1.1.2"
def check_video_source(source_ip, port=5000):
"""Check if video source is streaming"""
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(2)
sock.bind(('', 0))
# Try to receive multicast traffic
import struct
group = socket.inet_aton(CHANNEL['group'])
source = socket.inet_aton(source_ip)
# Set socket options for multicast
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP,
group + source)
# Check for traffic
sock.recvfrom(1024)
sock.close()
return True
except:
return False
# Monitor sources and switch
current_source = 'primary'
announced = False
while True:
primary_ok = check_video_source(CHANNEL['primary_source'])
backup_ok = check_video_source(CHANNEL['backup_source'])
if primary_ok and current_source != 'primary':
# Switch to primary
if announced:
print(f"withdraw route {CHANNEL['group']}/32 "
f"next-hop {NEXT_HOP_BACKUP}", flush=True)
print(f"announce route {CHANNEL['group']}/32 "
f"next-hop {NEXT_HOP_PRIMARY}", flush=True)
current_source = 'primary'
announced = True
elif not primary_ok and backup_ok and current_source != 'backup':
# Switch to backup
if announced:
print(f"withdraw route {CHANNEL['group']}/32 "
f"next-hop {NEXT_HOP_PRIMARY}", flush=True)
print(f"announce route {CHANNEL['group']}/32 "
f"next-hop {NEXT_HOP_BACKUP}", flush=True)
current_source = 'backup'
announced = True
time.sleep(5)Isolate IPTV streams per customer using Multicast VPN:
#!/usr/bin/env python3
import sys
# Customer IPTV configurations
CUSTOMERS = {
'customer-a': {
'vrf': 'CUST-A-IPTV',
'rd': '10.1.1.1:100',
'rt': '65000:100',
'channels': {
'sports': '239.100.1.1',
'news': '239.100.1.2',
'movies': '239.100.1.3'
}
},
'customer-b': {
'vrf': 'CUST-B-IPTV',
'rd': '10.1.1.1:200',
'rt': '65000:200',
'channels': {
'sports': '239.200.1.1',
'news': '239.200.1.2',
'entertainment': '239.200.1.4'
}
}
}
NEXT_HOP = "10.1.1.1"
# Announce per-customer channels
for customer, config in CUSTOMERS.items():
rd = config['rd']
rt = config['rt']
for channel_name, group in config['channels'].items():
# Announce multicast group in customer VRF
print(f"announce route {group}/32 "
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:
breakDeliver different content per region:
#!/usr/bin/env python3
import sys
# Regional channel lineup
REGIONS = {
'us-east': {
'rd': '10.1.0.1:1',
'rt': '65000:1',
'channels': ['239.1.1.1', '239.1.1.2', '239.1.1.3']
},
'us-west': {
'rd': '10.2.0.1:1',
'rt': '65000:2',
'channels': ['239.2.1.1', '239.2.1.2', '239.2.1.3']
},
'eu': {
'rd': '10.3.0.1:1',
'rt': '65000:3',
'channels': ['239.3.1.1', '239.3.1.2', '239.3.1.3']
}
}
# Determine region
import socket
hostname = socket.gethostname()
region = None
for r in REGIONS:
if r in hostname:
region = r
break
if region:
config = REGIONS[region]
for group in config['channels']:
print(f"announce route {group}/32 "
f"next-hop self "
f"route-distinguisher {config['rd']} "
f"route-target {config['rt']}", flush=True)
while True:
line = sys.stdin.readline().strip()
if not line:
breakUse communities to mark IPTV traffic for QoS:
#!/usr/bin/env python3
import sys
# QoS classes
QOS_CLASSES = {
'4K-premium': {
'community': '65000:10', # Highest priority
'dscp': 46 # EF
},
'HD': {
'community': '65000:20',
'dscp': 34 # AF41
},
'SD': {
'community': '65000:30',
'dscp': 26 # AF31
}
}
# Channels with QoS
CHANNELS = {
'4k-sports': {
'group': '239.1.1.1',
'qos': '4K-premium'
},
'hd-news': {
'group': '239.1.1.2',
'qos': 'HD'
},
'sd-general': {
'group': '239.1.1.3',
'qos': 'SD'
}
}
NEXT_HOP = "10.1.1.1"
# Announce with QoS communities
for channel, config in CHANNELS.items():
qos_class = QOS_CLASSES.get(config['qos'])
if qos_class:
print(f"announce route {config['group']}/32 "
f"next-hop {NEXT_HOP} "
f"community [{qos_class['community']}]", flush=True)
while True:
line = sys.stdin.readline().strip()
if not line:
breakAnnounce available bandwidth for admission control:
#!/usr/bin/env python3
import sys
import psutil
# Video source capacity
MAX_BANDWIDTH_MBPS = 1000
CHANNEL_BANDWIDTH_MBPS = 8 # HD channel
def get_available_bandwidth():
"""Calculate available IPTV bandwidth"""
net_io = psutil.net_io_counters()
# Simplified: check interface utilization
# In production, query actual multicast stream count
return MAX_BANDWIDTH_MBPS
def announce_capacity():
"""Announce available capacity to admission control"""
available = get_available_bandwidth()
max_channels = int(available / CHANNEL_BANDWIDTH_MBPS)
# Use extended community for capacity
# This is a simplified example
print(f"announce route 10.1.1.1/32 "
f"next-hop self "
f"community [65000:100]", flush=True)
announce_capacity()
while True:
line = sys.stdin.readline().strip()
if not line:
breakConfiguration (/etc/exabgp/iptv-headend.conf):
process iptv-channels {
run python3 /etc/exabgp/iptv-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 multicast;
}
api {
processes [ iptv-channels ];
}
}
neighbor 10.0.0.2 {
router-id 10.1.1.1;
local-address 10.1.1.1;
local-as 65001;
peer-as 65001;
family {
ipv4 multicast;
}
api {
processes [ iptv-channels ];
}
}Configuration (/etc/exabgp/mvpn-pe.conf):
process mvpn-controller {
run python3 /etc/exabgp/mvpn-announce.py;
encoder text;
}
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 multicast-vpn;
}
api {
processes [ mvpn-controller ];
}
}API Program (/etc/exabgp/iptv-announce.py):
#!/usr/bin/env python3
import sys
import json
import time
# Channel database (simplified)
ACTIVE_CHANNELS = {}
def load_channel_lineup():
"""Load channel lineup from config/database"""
return {
'hd-1': {'group': '239.1.1.1', 'source': '10.1.1.100'},
'hd-2': {'group': '239.1.1.2', 'source': '10.1.1.101'},
'sd-1': {'group': '239.1.1.10', 'source': '10.1.1.102'}
}
def announce_channel(channel_id, group, source):
"""Announce channel availability"""
print(f"announce route {group}/32 next-hop {source}", flush=True)
ACTIVE_CHANNELS[channel_id] = {'group': group, 'source': source}
def withdraw_channel(channel_id):
"""Remove channel from lineup"""
if channel_id in ACTIVE_CHANNELS:
config = ACTIVE_CHANNELS[channel_id]
print(f"withdraw route {config['group']}/32 "
f"next-hop {config['source']}", flush=True)
del ACTIVE_CHANNELS[channel_id]
# Initial channel lineup
channels = load_channel_lineup()
for channel_id, config in channels.items():
announce_channel(channel_id, config['group'], config['source'])
# Listen for channel changes (from EPG system, etc.)
while True:
line = sys.stdin.readline().strip()
if not line:
continue
try:
command = json.loads(line)
if command.get('action') == 'add-channel':
announce_channel(
command['channel_id'],
command['group'],
command['source']
)
elif command.get('action') == 'remove-channel':
withdraw_channel(command['channel_id'])
except:
pass
# Periodic refresh
time.sleep(60)π» 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)