012-路由协议详解
难度:🔴 | 预计时间:180分钟 | 前置:008-子网划分与VLSM、009-网络设备与拓扑
学习目标
- 理解路由协议的分类和工作原理
- 掌握距离向量和链路状态算法
- 深入学习RIP、OSPF、BGP协议的配置和优化
- 了解路由策略和路由过滤技术
- 掌握路由协议的故障排除方法
路由协议概述
路由协议分类
路由协议按照不同标准可以进行多种分类:
| 分类标准 | 类型 | 协议示例 | 特点 |
|---|---|---|---|
| 算法类型 | 距离向量 | RIP、EIGRP | 基于跳数或复合度量 |
| 链路状态 | OSPF、IS-IS | 基于网络拓扑图 | |
| 路径向量 | BGP | 基于AS路径 | |
| 应用范围 | 内部网关协议(IGP) | RIP、OSPF、EIGRP | 自治系统内部 |
| 外部网关协议(EGP) | BGP | 自治系统之间 | |
| 是否分类 | 有类别 | RIPv1 | 不支持VLSM |
| 无类别 | RIPv2、OSPF、BGP | 支持VLSM和CIDR |
路由协议工作原理
路由度量值比较
| 协议 | 度量值 | 最大跳数 | 收敛时间 | 带宽开销 |
|---|---|---|---|---|
| RIPv1/v2 | 跳数 | 15 | 慢(分钟级) | 低 |
| OSPF | 开销(带宽) | 无限制 | 快(秒级) | 中等 |
| EIGRP | 复合度量 | 255 | 很快(秒级) | 低 |
| BGP | AS路径长度 | 无限制 | 慢(分钟级) | 高 |
RIP协议详解
RIP协议特点
RIP(Routing Information Protocol)是最简单的距离向量路由协议:
- 度量值: 跳数(Hop Count)
- 最大跳数: 15跳(16跳表示不可达)
- 更新周期: 30秒
- 版本: RIPv1(有类别)、RIPv2(无类别)
RIP工作机制
Python实现RIP协议模拟
# 文件路径: routing/rip_simulator.py
from typing import Dict, List, Optional, Set
from dataclasses import dataclass, field
from enum import Enum
import time
import threading
import json
class RouteState(Enum):
ACTIVE = "active"
INVALID = "invalid"
FLUSH = "flush"
@dataclass
class RIPRoute:
"""RIP路由条目"""
network: str
netmask: str
next_hop: str
metric: int
interface: str
state: RouteState = RouteState.ACTIVE
timer: int = 0
tag: int = 0
def __post_init__(self):
if self.metric > 16:
self.metric = 16 # 无穷大
def is_reachable(self) -> bool:
"""判断路由是否可达"""
return self.metric < 16 and self.state == RouteState.ACTIVE
def to_dict(self) -> dict:
"""转换为字典格式"""
return {
'network': self.network,
'netmask': self.netmask,
'next_hop': self.next_hop,
'metric': self.metric,
'interface': self.interface,
'state': self.state.value,
'timer': self.timer,
'tag': self.tag
}
@dataclass
class RIPInterface:
"""RIP接口配置"""
name: str
ip_address: str
netmask: str
enabled: bool = True
split_horizon: bool = True
poison_reverse: bool = False
def get_network(self) -> str:
"""获取网络地址"""
# 简化实现,实际需要进行子网计算
ip_parts = self.ip_address.split('.')
mask_parts = self.netmask.split('.')
network_parts = []
for i in range(4):
network_parts.append(str(int(ip_parts[i]) & int(mask_parts[i])))
return '.'.join(network_parts)
class RIPRouter:
"""RIP路由器实现"""
def __init__(self, router_id: str):
self.router_id = router_id
self.interfaces: Dict[str, RIPInterface] = {}
self.routing_table: Dict[str, RIPRoute] = {}
self.neighbors: Set[str] = set()
# RIP定时器
self.update_timer = 30 # 更新定时器(秒)
self.invalid_timer = 180 # 无效定时器(秒)
self.flush_timer = 240 # 刷新定时器(秒)
# 运行状态
self.running = False
self.update_thread = None
# 统计信息
self.stats = {
'updates_sent': 0,
'updates_received': 0,
'routes_learned': 0,
'routes_expired': 0
}
def add_interface(self, name: str, ip_address: str, netmask: str):
"""添加接口"""
interface = RIPInterface(name, ip_address, netmask)
self.interfaces[name] = interface
# 添加直连路由
network = interface.get_network()
route = RIPRoute(
network=network,
netmask=netmask,
next_hop="0.0.0.0", # 直连
metric=0,
interface=name
)
self.routing_table[network] = route
print(f"路由器 {self.router_id} 添加接口 {name}: {ip_address}/{netmask}")
def add_neighbor(self, neighbor_ip: str):
"""添加RIP邻居"""
self.neighbors.add(neighbor_ip)
print(f"路由器 {self.router_id} 添加邻居 {neighbor_ip}")
def start_rip(self):
"""启动RIP进程"""
if not self.running:
self.running = True
self.update_thread = threading.Thread(target=self._update_loop, daemon=True)
self.update_thread.start()
print(f"路由器 {self.router_id} 启动RIP进程")
def stop_rip(self):
"""停止RIP进程"""
self.running = False
if self.update_thread:
self.update_thread.join()
print(f"路由器 {self.router_id} 停止RIP进程")
def _update_loop(self):
"""RIP更新循环"""
while self.running:
try:
# 发送路由更新
self._send_updates()
# 更新路由定时器
self._update_timers()
# 等待下次更新
time.sleep(1) # 每秒检查一次
except Exception as e:
print(f"RIP更新循环错误: {e}")
def _send_updates(self):
"""发送路由更新"""
current_time = int(time.time())
# 每30秒发送一次完整更新
if current_time % self.update_timer == 0:
for neighbor in self.neighbors:
update_packet = self._create_update_packet(neighbor)
if update_packet:
self._send_packet(neighbor, update_packet)
self.stats['updates_sent'] += 1
def _create_update_packet(self, neighbor_ip: str) -> Optional[Dict]:
"""创建RIP更新包"""
routes = []
for network, route in self.routing_table.items():
if route.state != RouteState.ACTIVE:
continue
# 水平分割检查
if self._should_advertise_route(route, neighbor_ip):
advertised_metric = route.metric
# 毒性逆转
if (route.next_hop == neighbor_ip and
self._get_interface_for_neighbor(neighbor_ip) and
self.interfaces[self._get_interface_for_neighbor(neighbor_ip)].poison_reverse):
advertised_metric = 16
routes.append({
'network': route.network,
'netmask': route.netmask,
'metric': advertised_metric,
'tag': route.tag
})
if routes:
return {
'version': 2,
'command': 'response',
'router_id': self.router_id,
'routes': routes
}
return None
def _should_advertise_route(self, route: RIPRoute, neighbor_ip: str) -> bool:
"""判断是否应该向邻居通告路由"""
interface_name = self._get_interface_for_neighbor(neighbor_ip)
if not interface_name:
return False
interface = self.interfaces[interface_name]
# 水平分割:不向学习到路由的接口通告该路由
if interface.split_horizon and route.interface == interface_name:
return False
return True
def _get_interface_for_neighbor(self, neighbor_ip: str) -> Optional[str]:
"""获取到达邻居的接口"""
# 简化实现:假设邻居在同一网段
for name, interface in self.interfaces.items():
if self._same_network(interface.ip_address, neighbor_ip, interface.netmask):
return name
return None
def _same_network(self, ip1: str, ip2: str, netmask: str) -> bool:
"""判断两个IP是否在同一网络"""
# 简化实现
ip1_parts = ip1.split('.')
ip2_parts = ip2.split('.')
mask_parts = netmask.split('.')
for i in range(4):
if (int(ip1_parts[i]) & int(mask_parts[i])) != (int(ip2_parts[i]) & int(mask_parts[i])):
return False
return True
def _send_packet(self, neighbor_ip: str, packet: Dict):
"""发送数据包(模拟)"""
print(f"[{self.router_id}] 向 {neighbor_ip} 发送RIP更新: {len(packet['routes'])}条路由")
def receive_update(self, sender_ip: str, packet: Dict):
"""接收RIP更新"""
if packet.get('command') != 'response':
return
self.stats['updates_received'] += 1
routes_updated = 0
for route_info in packet.get('routes', []):
if self._process_route_update(sender_ip, route_info):
routes_updated += 1
if routes_updated > 0:
print(f"[{self.router_id}] 从 {sender_ip} 接收更新: {routes_updated}条路由变更")
def _process_route_update(self, sender_ip: str, route_info: Dict) -> bool:
"""处理单条路由更新"""
network = route_info['network']
netmask = route_info['netmask']
metric = min(route_info['metric'] + 1, 16) # 跳数+1
tag = route_info.get('tag', 0)
# 获取接收接口
interface_name = self._get_interface_for_neighbor(sender_ip)
if not interface_name:
return False
# 检查是否为更好的路由
if network in self.routing_table:
existing_route = self.routing_table[network]
# 来自同一下一跳的更新
if existing_route.next_hop == sender_ip:
if metric != existing_route.metric:
existing_route.metric = metric
existing_route.timer = 0 # 重置定时器
existing_route.state = RouteState.ACTIVE if metric < 16 else RouteState.INVALID
return True
# 更好的路由
elif metric < existing_route.metric:
existing_route.next_hop = sender_ip
existing_route.metric = metric
existing_route.interface = interface_name
existing_route.timer = 0
existing_route.state = RouteState.ACTIVE
existing_route.tag = tag
return True
else:
# 新路由
if metric < 16:
new_route = RIPRoute(
network=network,
netmask=netmask,
next_hop=sender_ip,
metric=metric,
interface=interface_name,
tag=tag
)
self.routing_table[network] = new_route
self.stats['routes_learned'] += 1
return True
return False
def _update_timers(self):
"""更新路由定时器"""
routes_to_remove = []
for network, route in self.routing_table.items():
if route.metric == 0: # 直连路由
continue
route.timer += 1
# 检查定时器状态
if route.timer >= self.flush_timer:
routes_to_remove.append(network)
elif route.timer >= self.invalid_timer and route.state == RouteState.ACTIVE:
route.state = RouteState.INVALID
route.metric = 16
self.stats['routes_expired'] += 1
# 删除过期路由
for network in routes_to_remove:
del self.routing_table[network]
def show_routing_table(self):
"""显示路由表"""
print(f"\n=== 路由器 {self.router_id} 路由表 ===")
print(f"{'网络':<15} {'掩码':<15} {'下一跳':<15} {'跳数':<5} {'接口':<10} {'状态':<8} {'定时器':<8}")
print("-" * 85)
for network, route in sorted(self.routing_table.items()):
next_hop = route.next_hop if route.next_hop != "0.0.0.0" else "直连"
print(f"{route.network:<15} {route.netmask:<15} {next_hop:<15} {route.metric:<5} {route.interface:<10} {route.state.value:<8} {route.timer:<8}")
def show_statistics(self):
"""显示统计信息"""
print(f"\n=== 路由器 {self.router_id} RIP统计 ===")
print(f"发送更新: {self.stats['updates_sent']}")
print(f"接收更新: {self.stats['updates_received']}")
print(f"学习路由: {self.stats['routes_learned']}")
print(f"过期路由: {self.stats['routes_expired']}")
print(f"邻居数量: {len(self.neighbors)}")
print(f"路由条目: {len(self.routing_table)}")
class RIPNetwork:
"""RIP网络模拟器"""
def __init__(self):
self.routers: Dict[str, RIPRouter] = {}
self.links: List[tuple] = [] # (router1, ip1, router2, ip2)
def add_router(self, router_id: str) -> RIPRouter:
"""添加路由器"""
router = RIPRouter(router_id)
self.routers[router_id] = router
return router
def connect_routers(self, router1_id: str, ip1: str, router2_id: str, ip2: str, netmask: str = "255.255.255.0"):
"""连接两个路由器"""
if router1_id in self.routers and router2_id in self.routers:
router1 = self.routers[router1_id]
router2 = self.routers[router2_id]
# 添加接口
interface1_name = f"eth-to-{router2_id}"
interface2_name = f"eth-to-{router1_id}"
router1.add_interface(interface1_name, ip1, netmask)
router2.add_interface(interface2_name, ip2, netmask)
# 添加邻居关系
router1.add_neighbor(ip2)
router2.add_neighbor(ip1)
self.links.append((router1_id, ip1, router2_id, ip2))
print(f"连接: {router1_id}({ip1}) <-> {router2_id}({ip2})")
def start_all_rip(self):
"""启动所有路由器的RIP进程"""
for router in self.routers.values():
router.start_rip()
def stop_all_rip(self):
"""停止所有路由器的RIP进程"""
for router in self.routers.values():
router.stop_rip()
def simulate_convergence(self, duration: int = 120):
"""模拟网络收敛"""
print(f"\n=== 开始RIP收敛模拟 ({duration}秒) ===")
start_time = time.time()
while time.time() - start_time < duration:
# 模拟路由更新传播
for router1_id, ip1, router2_id, ip2 in self.links:
router1 = self.routers[router1_id]
router2 = self.routers[router2_id]
# 双向更新
if router1.running:
packet1 = router1._create_update_packet(ip2)
if packet1:
router2.receive_update(ip1, packet1)
if router2.running:
packet2 = router2._create_update_packet(ip1)
if packet2:
router1.receive_update(ip2, packet2)
time.sleep(1)
print("\n=== RIP收敛模拟完成 ===")
def show_network_topology(self):
"""显示网络拓扑"""
print("\n=== 网络拓扑 ===")
for router_id, router in self.routers.items():
router.show_routing_table()
router.show_statistics()
def simulate_link_failure(self, router1_id: str, router2_id: str):
"""模拟链路故障"""
print(f"\n=== 模拟链路故障: {router1_id} <-> {router2_id} ===")
# 移除邻居关系
if router1_id in self.routers and router2_id in self.routers:
router1 = self.routers[router1_id]
router2 = self.routers[router2_id]
# 找到对应的IP地址
for r1_id, ip1, r2_id, ip2 in self.links:
if (r1_id == router1_id and r2_id == router2_id) or (r1_id == router2_id and r2_id == router1_id):
if r1_id == router1_id:
router1.neighbors.discard(ip2)
router2.neighbors.discard(ip1)
else:
router1.neighbors.discard(ip1)
router2.neighbors.discard(ip2)
break
print(f"链路 {router1_id} <-> {router2_id} 已断开")
# 使用示例
if __name__ == "__main__":
# 创建RIP网络
network = RIPNetwork()
# 添加路由器
r1 = network.add_router("R1")
r2 = network.add_router("R2")
r3 = network.add_router("R3")
r4 = network.add_router("R4")
# 连接路由器
network.connect_routers("R1", "192.168.1.1", "R2", "192.168.1.2")
network.connect_routers("R2", "192.168.2.1", "R3", "192.168.2.2")
network.connect_routers("R3", "192.168.3.1", "R4", "192.168.3.2")
network.connect_routers("R1", "192.168.4.1", "R4", "192.168.4.2") # 冗余链路
# 添加一些网络
r1.add_interface("lan1", "10.1.1.1", "255.255.255.0")
r4.add_interface("lan4", "10.4.4.1", "255.255.255.0")
# 启动RIP
network.start_all_rip()
# 模拟收敛
network.simulate_convergence(60)
# 显示结果
network.show_network_topology()
# 模拟链路故障
network.simulate_link_failure("R1", "R2")
# 再次收敛
network.simulate_convergence(60)
# 显示故障后的路由表
network.show_network_topology()
# 停止RIP
network.stop_all_rip()
RIP配置示例
Cisco RIP配置
# 启用RIP版本2
router rip
version 2
no auto-summary
# 通告网络
network 192.168.1.0
network 192.168.2.0
network 10.0.0.0
# 被动接口(不发送RIP更新)
passive-interface GigabitEthernet0/0
# 默认路由分发
default-information originate
# 路由过滤
distribute-list 10 out GigabitEthernet0/1
# 访问控制列表
access-list 10 deny 192.168.100.0 0.0.0.255
access-list 10 permit any
华为RIP配置
# 启用RIP进程
rip 1
version 2
undo summary
# 接口配置
interface GigabitEthernet0/0/1
ip address 192.168.1.1 255.255.255.0
rip version 2 multicast
rip split-horizon
# 网络通告
rip 1
network 192.168.1.0
network 192.168.2.0
# 路由汇总
summary 192.168.0.0 255.255.0.0
# 认证配置
interface GigabitEthernet0/0/1
rip authentication-mode simple password123
OSPF协议详解
OSPF协议特点
OSPF(Open Shortest Path First)是链路状态路由协议:
- 算法: Dijkstra最短路径优先算法
- 度量值: 开销(Cost),基于带宽计算
- 收敛: 快速收敛,支持增量更新
- 扩展性: 支持区域划分,适合大型网络
- 安全性: 支持认证机制
OSPF区域概念
OSPF路由器类型
| 路由器类型 | 英文缩写 | 功能描述 | 特点 |
|---|---|---|---|
| 内部路由器 | IR | 所有接口在同一区域 | 维护单一LSDB |
| 区域边界路由器 | ABR | 连接多个区域 | 维护多个LSDB |
| 自治系统边界路由器 | ASBR | 连接外部AS | 引入外部路由 |
| 骨干路由器 | BR | 至少一个接口在Area 0 | 转发区域间流量 |
OSPF LSA类型
| LSA类型 | 名称 | 描述 | 泛洪范围 |
|---|---|---|---|
| Type 1 | Router LSA | 路由器链路状态 | 区域内 |
| Type 2 | Network LSA | 网络链路状态 | 区域内 |
| Type 3 | Summary LSA | 网络汇总 | 区域间 |
| Type 4 | ASBR Summary LSA | ASBR汇总 | 区域间 |
| Type 5 | External LSA | 外部路由 | 整个AS |
| Type 7 | NSSA External LSA | NSSA外部路由 | NSSA区域 |
Python实现OSPF协议模拟
# 文件路径: routing/ospf_simulator.py
from typing import Dict, List, Set, Optional, Tuple
from dataclasses import dataclass, field
from enum import Enum
import heapq
import time
import json
class OSPFState(Enum):
DOWN = "down"
INIT = "init"
TWO_WAY = "2way"
EXSTART = "exstart"
EXCHANGE = "exchange"
LOADING = "loading"
FULL = "full"
class LSAType(Enum):
ROUTER = 1
NETWORK = 2
SUMMARY = 3
ASBR_SUMMARY = 4
EXTERNAL = 5
NSSA_EXTERNAL = 7
@dataclass
class OSPFInterface:
"""OSPF接口"""
name: str
ip_address: str
netmask: str
area_id: str
cost: int = 10
hello_interval: int = 10
dead_interval: int = 40
priority: int = 1
network_type: str = "broadcast" # broadcast, point-to-point, NBMA
def get_network(self) -> str:
"""获取网络地址"""
ip_parts = self.ip_address.split('.')
mask_parts = self.netmask.split('.')
network_parts = []
for i in range(4):
network_parts.append(str(int(ip_parts[i]) & int(mask_parts[i])))
return '.'.join(network_parts)
@dataclass
class OSPFNeighbor:
"""OSPF邻居"""
router_id: str
ip_address: str
priority: int
state: OSPFState = OSPFState.DOWN
dead_timer: int = 0
is_dr: bool = False
is_bdr: bool = False
def reset_dead_timer(self, interval: int):
"""重置Dead定时器"""
self.dead_timer = interval
@dataclass
class LSAHeader:
"""LSA头部"""
age: int
lsa_type: LSAType
link_state_id: str
advertising_router: str
sequence_number: int
checksum: int = 0
length: int = 20
def __hash__(self):
return hash((self.lsa_type, self.link_state_id, self.advertising_router))
@dataclass
class RouterLSA:
"""路由器LSA"""
header: LSAHeader
flags: int = 0 # V=Virtual, E=External, B=Border
links: List[Dict] = field(default_factory=list)
def add_link(self, link_id: str, link_data: str, link_type: int, metric: int):
"""添加链路"""
self.links.append({
'link_id': link_id,
'link_data': link_data,
'type': link_type, # 1=P2P, 2=Transit, 3=Stub, 4=Virtual
'metric': metric
})
class OSPFRouter:
"""OSPF路由器实现"""
def __init__(self, router_id: str):
self.router_id = router_id
self.interfaces: Dict[str, OSPFInterface] = {}
self.neighbors: Dict[str, Dict[str, OSPFNeighbor]] = {} # area_id -> {neighbor_id: neighbor}
self.lsdb: Dict[str, Dict[Tuple, LSAHeader]] = {} # area_id -> {(type,id,adv): LSA}
self.routing_table: Dict[str, Dict] = {}
# OSPF配置
self.areas: Set[str] = set()
self.is_abr = False
self.is_asbr = False
# 统计信息
self.stats = {
'hello_sent': 0,
'hello_received': 0,
'lsa_originated': 0,
'lsa_received': 0,
'spf_runs': 0
}
def add_interface(self, name: str, ip_address: str, netmask: str, area_id: str, cost: int = 10):
"""添加OSPF接口"""
interface = OSPFInterface(name, ip_address, netmask, area_id, cost)
self.interfaces[name] = interface
self.areas.add(area_id)
# 初始化邻居字典
if area_id not in self.neighbors:
self.neighbors[area_id] = {}
# 初始化LSDB
if area_id not in self.lsdb:
self.lsdb[area_id] = {}
# 检查是否为ABR
if len(self.areas) > 1:
self.is_abr = True
print(f"路由器 {self.router_id} 添加接口 {name}: {ip_address}/{netmask} (Area {area_id})")
def add_neighbor(self, area_id: str, neighbor_id: str, neighbor_ip: str, priority: int = 1):
"""添加OSPF邻居"""
if area_id in self.neighbors:
neighbor = OSPFNeighbor(neighbor_id, neighbor_ip, priority)
self.neighbors[area_id][neighbor_id] = neighbor
print(f"路由器 {self.router_id} 在区域 {area_id} 添加邻居 {neighbor_id}")
def send_hello(self, area_id: str):
"""发送Hello消息"""
if area_id not in self.neighbors:
return
hello_packet = {
'type': 'hello',
'router_id': self.router_id,
'area_id': area_id,
'hello_interval': 10,
'dead_interval': 40,
'neighbors': list(self.neighbors[area_id].keys())
}
self.stats['hello_sent'] += 1
print(f"[{self.router_id}] 在区域 {area_id} 发送Hello消息")
return hello_packet
def receive_hello(self, sender_id: str, packet: Dict):
"""接收Hello消息"""
area_id = packet['area_id']
sender_ip = packet.get('sender_ip', '0.0.0.0')
self.stats['hello_received'] += 1
# 检查邻居是否存在
if area_id not in self.neighbors:
self.neighbors[area_id] = {}
if sender_id not in self.neighbors[area_id]:
# 新邻居
neighbor = OSPFNeighbor(sender_id, sender_ip, 1)
self.neighbors[area_id][sender_id] = neighbor
neighbor.state = OSPFState.INIT
else:
neighbor = self.neighbors[area_id][sender_id]
# 重置Dead定时器
neighbor.reset_dead_timer(packet['dead_interval'])
# 检查双向通信
if self.router_id in packet.get('neighbors', []):
if neighbor.state == OSPFState.INIT:
neighbor.state = OSPFState.TWO_WAY
print(f"[{self.router_id}] 与邻居 {sender_id} 建立双向通信")
# 开始数据库同步
self._start_database_sync(area_id, sender_id)
def _start_database_sync(self, area_id: str, neighbor_id: str):
"""开始数据库同步"""
neighbor = self.neighbors[area_id][neighbor_id]
neighbor.state = OSPFState.EXSTART
# 模拟数据库交换过程
neighbor.state = OSPFState.EXCHANGE
neighbor.state = OSPFState.LOADING
neighbor.state = OSPFState.FULL
print(f"[{self.router_id}] 与邻居 {neighbor_id} 完成数据库同步")
def originate_router_lsa(self, area_id: str):
"""生成路由器LSA"""
header = LSAHeader(
age=0,
lsa_type=LSAType.ROUTER,
link_state_id=self.router_id,
advertising_router=self.router_id,
sequence_number=int(time.time()) % 0x80000000
)
router_lsa = RouterLSA(header)
# 添加接口链路
for interface in self.interfaces.values():
if interface.area_id == area_id:
# 添加Stub链路(网络)
network = interface.get_network()
router_lsa.add_link(
link_id=network,
link_data=interface.netmask,
link_type=3, # Stub
metric=interface.cost
)
# 添加邻居链路
for neighbor_id, neighbor in self.neighbors.get(area_id, {}).items():
if neighbor.state == OSPFState.FULL:
router_lsa.add_link(
link_id=neighbor_id,
link_data=interface.ip_address,
link_type=1, # Point-to-Point
metric=interface.cost
)
# 设置标志
if self.is_abr:
router_lsa.flags |= 0x01 # Border bit
if self.is_asbr:
router_lsa.flags |= 0x02 # External bit
# 添加到LSDB
lsa_key = (header.lsa_type, header.link_state_id, header.advertising_router)
self.lsdb[area_id][lsa_key] = header
self.stats['lsa_originated'] += 1
print(f"[{self.router_id}] 在区域 {area_id} 生成路由器LSA")
return router_lsa
def flood_lsa(self, area_id: str, lsa: LSAHeader, sender_id: Optional[str] = None):
"""泛洪LSA"""
flooded_to = []
for neighbor_id, neighbor in self.neighbors.get(area_id, {}).items():
if neighbor.state == OSPFState.FULL and neighbor_id != sender_id:
flooded_to.append(neighbor_id)
if flooded_to:
print(f"[{self.router_id}] 在区域 {area_id} 泛洪LSA到: {', '.join(flooded_to)}")
return flooded_to
def receive_lsa(self, area_id: str, lsa: LSAHeader, sender_id: str):
"""接收LSA"""
lsa_key = (lsa.lsa_type, lsa.link_state_id, lsa.advertising_router)
# 检查是否为新的或更新的LSA
if (lsa_key not in self.lsdb[area_id] or
lsa.sequence_number > self.lsdb[area_id][lsa_key].sequence_number):
self.lsdb[area_id][lsa_key] = lsa
self.stats['lsa_received'] += 1
print(f"[{self.router_id}] 接收到新LSA: {lsa.lsa_type.name} from {lsa.advertising_router}")
# 继续泛洪
self.flood_lsa(area_id, lsa, sender_id)
# 触发SPF计算
self.run_spf(area_id)
return True
return False
def run_spf(self, area_id: str):
"""运行SPF算法"""
print(f"[{self.router_id}] 在区域 {area_id} 运行SPF算法")
self.stats['spf_runs'] += 1
# 构建拓扑图
graph = self._build_topology_graph(area_id)
# 运行Dijkstra算法
distances, paths = self._dijkstra(graph, self.router_id)
# 更新路由表
self._update_routing_table(area_id, distances, paths)
def _build_topology_graph(self, area_id: str) -> Dict[str, Dict[str, int]]:
"""构建拓扑图"""
graph = {}
# 从LSDB构建图
for lsa_key, lsa in self.lsdb[area_id].items():
lsa_type, link_state_id, advertising_router = lsa_key
if lsa_type == LSAType.ROUTER:
if advertising_router not in graph:
graph[advertising_router] = {}
# 模拟从LSA中提取链路信息
# 实际实现需要解析RouterLSA的links字段
for neighbor_id in self.neighbors.get(area_id, {}):
if neighbor_id != advertising_router:
# 使用接口开销作为权重
cost = 10 # 默认开销
for interface in self.interfaces.values():
if interface.area_id == area_id:
cost = interface.cost
break
graph[advertising_router][neighbor_id] = cost
return graph
def _dijkstra(self, graph: Dict[str, Dict[str, int]], start: str) -> Tuple[Dict[str, int], Dict[str, str]]:
"""Dijkstra最短路径算法"""
distances = {node: float('inf') for node in graph}
distances[start] = 0
paths = {}
visited = set()
pq = [(0, start)]
while pq:
current_distance, current = heapq.heappop(pq)
if current in visited:
continue
visited.add(current)
for neighbor, weight in graph.get(current, {}).items():
distance = current_distance + weight
if distance < distances[neighbor]:
distances[neighbor] = distance
paths[neighbor] = current
heapq.heappush(pq, (distance, neighbor))
return distances, paths
def _update_routing_table(self, area_id: str, distances: Dict[str, int], paths: Dict[str, str]):
"""更新路由表"""
if area_id not in self.routing_table:
self.routing_table[area_id] = {}
for destination, distance in distances.items():
if destination != self.router_id and distance != float('inf'):
# 找到下一跳
next_hop = destination
while paths.get(next_hop) != self.router_id:
next_hop = paths.get(next_hop)
if next_hop is None:
break
if next_hop:
self.routing_table[area_id][destination] = {
'destination': destination,
'next_hop': next_hop,
'cost': distance,
'area': area_id
}
def show_neighbors(self):
"""显示邻居状态"""
print(f"\n=== 路由器 {self.router_id} 邻居状态 ===")
for area_id, neighbors in self.neighbors.items():
print(f"\n区域 {area_id}:")
print(f"{'邻居ID':<15} {'IP地址':<15} {'状态':<10} {'优先级':<8} {'Dead定时器':<10}")
print("-" * 70)
for neighbor_id, neighbor in neighbors.items():
print(f"{neighbor_id:<15} {neighbor.ip_address:<15} {neighbor.state.value:<10} {neighbor.priority:<8} {neighbor.dead_timer:<10}")
def show_lsdb(self):
"""显示链路状态数据库"""
print(f"\n=== 路由器 {self.router_id} LSDB ===")
for area_id, lsdb in self.lsdb.items():
print(f"\n区域 {area_id}:")
print(f"{'LSA类型':<12} {'链路状态ID':<15} {'通告路由器':<15} {'序列号':<12} {'年龄':<8}")
print("-" * 75)
for lsa_key, lsa in lsdb.items():
lsa_type, link_state_id, advertising_router = lsa_key
print(f"{lsa_type.name:<12} {link_state_id:<15} {advertising_router:<15} {lsa.sequence_number:<12} {lsa.age:<8}")
def show_routing_table(self):
"""显示路由表"""
print(f"\n=== 路由器 {self.router_id} 路由表 ===")
print(f"{'目的地':<15} {'下一跳':<15} {'开销':<8} {'区域':<10}")
print("-" * 55)
for area_id, routes in self.routing_table.items():
for dest, route in routes.items():
print(f"{route['destination']:<15} {route['next_hop']:<15} {route['cost']:<8} {route['area']:<10}")
def show_statistics(self):
"""显示统计信息"""
print(f"\n=== 路由器 {self.router_id} OSPF统计 ===")
print(f"Hello发送: {self.stats['hello_sent']}")
print(f"Hello接收: {self.stats['hello_received']}")
print(f"LSA生成: {self.stats['lsa_originated']}")
print(f"LSA接收: {self.stats['lsa_received']}")
print(f"SPF运行: {self.stats['spf_runs']}")
print(f"区域数量: {len(self.areas)}")
print(f"是否ABR: {'是' if self.is_abr else '否'}")
print(f"是否ASBR: {'是' if self.is_asbr else '否'}")
# 使用示例
if __name__ == "__main__":
# 创建OSPF路由器
r1 = OSPFRouter("1.1.1.1")
r2 = OSPFRouter("2.2.2.2")
r3 = OSPFRouter("3.3.3.3")
# 配置接口
r1.add_interface("eth0", "192.168.1.1", "255.255.255.0", "0.0.0.0", 10)
r1.add_interface("eth1", "192.168.2.1", "255.255.255.0", "0.0.0.1", 10)
r2.add_interface("eth0", "192.168.1.2", "255.255.255.0", "0.0.0.0", 10)
r2.add_interface("eth1", "192.168.3.1", "255.255.255.0", "0.0.0.0", 10)
r3.add_interface("eth0", "192.168.3.2", "255.255.255.0", "0.0.0.0", 10)
r3.add_interface("eth1", "192.168.2.2", "255.255.255.0", "0.0.0.1", 10)
# 建立邻居关系
r1.add_neighbor("0.0.0.0", "2.2.2.2", "192.168.1.2")
r2.add_neighbor("0.0.0.0", "1.1.1.1", "192.168.1.1")
r2.add_neighbor("0.0.0.0", "3.3.3.3", "192.168.3.2")
r3.add_neighbor("0.0.0.0", "2.2.2.2", "192.168.3.1")
# 模拟Hello交换
hello1 = r1.send_hello("0.0.0.0")
if hello1:
hello1['sender_ip'] = "192.168.1.1"
r2.receive_hello("1.1.1.1", hello1)
hello2 = r2.send_hello("0.0.0.0")
if hello2:
hello2['sender_ip'] = "192.168.1.2"
r1.receive_hello("2.2.2.2", hello2)
# 生成LSA
lsa1 = r1.originate_router_lsa("0.0.0.0")
lsa2 = r2.originate_router_lsa("0.0.0.0")
# 显示状态
r1.show_neighbors()
r1.show_lsdb()
r1.show_routing_table()
r1.show_statistics()
OSPF配置示例
Cisco OSPF配置
# 启用OSPF进程
router ospf 1
router-id 1.1.1.1
# 配置网络和区域
network 192.168.1.0 0.0.0.255 area 0
network 192.168.2.0 0.0.0.255 area 1
network 10.0.0.0 0.255.255.255 area 0
# 配置区域
area 1 stub
area 2 nssa
# 接口配置
interface GigabitEthernet0/0
ip ospf cost 100
ip ospf priority 10
ip ospf hello-interval 5
ip ospf dead-interval 20
# 认证配置
area 0 authentication message-digest
interface GigabitEthernet0/0
ip ospf message-digest-key 1 md5 password123
# 默认路由
default-information originate always metric 100
# 路由汇总
area 1 range 192.168.0.0 255.255.0.0
华为OSPF配置
# 启用OSPF进程
ospf 1 router-id 1.1.1.1
# 配置区域
area 0.0.0.0
network 192.168.1.0 0.0.0.255
network 10.0.0.0 0.255.255.255
area 0.0.0.1
network 192.168.2.0 0.0.0.255
stub
# 接口配置
interface GigabitEthernet0/0/1
ospf cost 100
ospf priority 10
ospf timer hello 5
ospf timer dead 20
# 认证配置
area 0.0.0.0
authentication-mode md5 1 password123
# 路由汇总
area 0.0.0.1
abr-summary 192.168.0.0 255.255.0.0
# LSA过滤
area 0.0.0.1
filter-lsa-out summary 192.168.100.0 255.255.255.0
BGP协议详解
BGP协议特点
BGP(Border Gateway Protocol)是路径向量外部网关协议:
- 用途: 自治系统间路由
- 算法: 路径向量算法
- 度量: AS路径长度
- 策略: 支持丰富的路由策略
- 扩展性: 适合大规模互联网路由
BGP消息类型
| 消息类型 | 功能 | 描述 |
|---|---|---|
| OPEN | 建立连接 | 协商BGP参数 |
| UPDATE | 路由更新 | 通告或撤销路由 |
| NOTIFICATION | 错误通知 | 报告错误并关闭连接 |
| KEEPALIVE | 保持连接 | 维持BGP会话 |
BGP状态机
BGP路径属性
| 属性类型 | 属性名称 | 必选/可选 | 描述 |
|---|---|---|---|
| Well-known Mandatory | ORIGIN | 必选 | 路由来源(IGP/EGP/Incomplete) |
| Well-known Mandatory | AS_PATH | 必选 | AS路径列表 |
| Well-known Mandatory | NEXT_HOP | 必选 | 下一跳地址 |
| Well-known Discretionary | LOCAL_PREF | 可选 | 本地优先级 |
| Well-known Discretionary | ATOMIC_AGGREGATE | 可选 | 原子聚合标志 |
| Optional Transitive | AGGREGATOR | 可选 | 聚合者信息 |
| Optional Transitive | COMMUNITY | 可选 | 团体属性 |
| Optional Non-transitive | MED | 可选 | 多出口鉴别符 |
Python实现BGP协议模拟
# 文件路径: routing/bgp_simulator.py
from typing import Dict, List, Set, Optional, Tuple
from dataclasses import dataclass, field
from enum import Enum
import time
import json
class BGPState(Enum):
IDLE = "idle"
CONNECT = "connect"
ACTIVE = "active"
OPENSENT = "opensent"
OPENCONFIRM = "openconfirm"
ESTABLISHED = "established"
class BGPMessageType(Enum):
OPEN = 1
UPDATE = 2
NOTIFICATION = 3
KEEPALIVE = 4
class OriginType(Enum):
IGP = 0
EGP = 1
INCOMPLETE = 2
@dataclass
class BGPPathAttribute:
"""BGP路径属性"""
origin: OriginType
as_path: List[int]
next_hop: str
local_pref: int = 100
med: Optional[int] = None
community: List[str] = field(default_factory=list)
atomic_aggregate: bool = False
aggregator: Optional[Tuple[int, str]] = None
@dataclass
class BGPRoute:
"""BGP路由条目"""
prefix: str
prefix_len: int
attributes: BGPPathAttribute
peer_ip: str
received_time: float = field(default_factory=time.time)
best: bool = False
def get_prefix_str(self) -> str:
"""获取前缀字符串"""
return f"{self.prefix}/{self.prefix_len}"
@dataclass
class BGPPeer:
"""BGP对等体"""
peer_ip: str
peer_as: int
local_as: int
state: BGPState = BGPState.IDLE
hold_time: int = 180
keepalive_time: int = 60
last_keepalive: float = 0
is_ebgp: bool = True
def __post_init__(self):
self.is_ebgp = self.peer_as != self.local_as
self.last_keepalive = time.time()
class BGPRouter:
"""BGP路由器实现"""
def __init__(self, router_id: str, local_as: int):
self.router_id = router_id
self.local_as = local_as
self.peers: Dict[str, BGPPeer] = {}
self.rib_in: Dict[str, Dict[str, BGPRoute]] = {} # peer_ip -> {prefix: route}
self.rib_out: Dict[str, Dict[str, BGPRoute]] = {} # peer_ip -> {prefix: route}
self.loc_rib: Dict[str, BGPRoute] = {} # prefix -> best_route
# BGP策略
self.import_policies: Dict[str, List] = {} # peer_ip -> [policy_functions]
self.export_policies: Dict[str, List] = {} # peer_ip -> [policy_functions]
# 统计信息
self.stats = {
'updates_sent': 0,
'updates_received': 0,
'routes_advertised': 0,
'routes_withdrawn': 0,
'keepalives_sent': 0,
'keepalives_received': 0
}
def add_peer(self, peer_ip: str, peer_as: int, hold_time: int = 180):
"""添加BGP对等体"""
peer = BGPPeer(peer_ip, peer_as, self.local_as, hold_time=hold_time)
self.peers[peer_ip] = peer
self.rib_in[peer_ip] = {}
self.rib_out[peer_ip] = {}
print(f"路由器 {self.router_id} (AS{self.local_as}) 添加对等体 {peer_ip} (AS{peer_as})")
def establish_session(self, peer_ip: str):
"""建立BGP会话"""
if peer_ip not in self.peers:
return False
peer = self.peers[peer_ip]
# 模拟状态转换
peer.state = BGPState.CONNECT
peer.state = BGPState.OPENSENT
peer.state = BGPState.OPENCONFIRM
peer.state = BGPState.ESTABLISHED
print(f"[{self.router_id}] 与对等体 {peer_ip} 建立BGP会话")
return True
def send_keepalive(self, peer_ip: str):
"""发送Keepalive消息"""
if peer_ip in self.peers and self.peers[peer_ip].state == BGPState.ESTABLISHED:
self.peers[peer_ip].last_keepalive = time.time()
self.stats['keepalives_sent'] += 1
print(f"[{self.router_id}] 向 {peer_ip} 发送Keepalive")
def receive_keepalive(self, peer_ip: str):
"""接收Keepalive消息"""
if peer_ip in self.peers:
self.peers[peer_ip].last_keepalive = time.time()
self.stats['keepalives_received'] += 1
def advertise_route(self, prefix: str, prefix_len: int, next_hop: str,
origin: OriginType = OriginType.IGP, local_pref: int = 100):
"""通告路由"""
attributes = BGPPathAttribute(
origin=origin,
as_path=[self.local_as],
next_hop=next_hop,
local_pref=local_pref
)
route = BGPRoute(prefix, prefix_len, attributes, "local")
prefix_str = route.get_prefix_str()
# 添加到Loc-RIB
self.loc_rib[prefix_str] = route
route.best = True
# 向所有对等体通告
for peer_ip, peer in self.peers.items():
if peer.state == BGPState.ESTABLISHED:
self._send_update(peer_ip, route)
print(f"[{self.router_id}] 通告路由 {prefix_str}")
def _send_update(self, peer_ip: str, route: BGPRoute):
"""发送UPDATE消息"""
peer = self.peers[peer_ip]
# 复制路由属性
new_attributes = BGPPathAttribute(
origin=route.attributes.origin,
as_path=route.attributes.as_path.copy(),
next_hop=route.attributes.next_hop,
local_pref=route.attributes.local_pref,
med=route.attributes.med,
community=route.attributes.community.copy()
)
# EBGP: 添加本地AS到AS_PATH
if peer.is_ebgp:
new_attributes.as_path.insert(0, self.local_as)
# EBGP不传递LOCAL_PREF
new_attributes.local_pref = None
# 应用出口策略
if self._apply_export_policy(peer_ip, route, new_attributes):
new_route = BGPRoute(
route.prefix, route.prefix_len, new_attributes, peer_ip
)
prefix_str = new_route.get_prefix_str()
self.rib_out[peer_ip][prefix_str] = new_route
self.stats['updates_sent'] += 1
self.stats['routes_advertised'] += 1
print(f"[{self.router_id}] 向 {peer_ip} 发送UPDATE: {prefix_str}")
def receive_update(self, peer_ip: str, prefix: str, prefix_len: int, attributes: BGPPathAttribute):
"""接收UPDATE消息"""
if peer_ip not in self.peers or self.peers[peer_ip].state != BGPState.ESTABLISHED:
return
route = BGPRoute(prefix, prefix_len, attributes, peer_ip)
prefix_str = route.get_prefix_str()
# 应用入口策略
if self._apply_import_policy(peer_ip, route):
# 添加到Adj-RIB-In
self.rib_in[peer_ip][prefix_str] = route
self.stats['updates_received'] += 1
print(f"[{self.router_id}] 从 {peer_ip} 接收UPDATE: {prefix_str}")
# 运行最佳路径选择
self._run_best_path_selection(prefix_str)
def _apply_import_policy(self, peer_ip: str, route: BGPRoute) -> bool:
"""应用入口策略"""
policies = self.import_policies.get(peer_ip, [])
for policy in policies:
if not policy(route):
return False
return True
def _apply_export_policy(self, peer_ip: str, route: BGPRoute, attributes: BGPPathAttribute) -> bool:
"""应用出口策略"""
policies = self.export_policies.get(peer_ip, [])
for policy in policies:
if not policy(route, attributes):
return False
return True
def _run_best_path_selection(self, prefix_str: str):
"""运行最佳路径选择算法"""
candidates = []
# 收集所有候选路由
for peer_ip, rib in self.rib_in.items():
if prefix_str in rib:
candidates.append(rib[prefix_str])
# 添加本地路由
if prefix_str in self.loc_rib and self.loc_rib[prefix_str].peer_ip == "local":
candidates.append(self.loc_rib[prefix_str])
if not candidates:
# 没有候选路由,删除最佳路由
if prefix_str in self.loc_rib:
del self.loc_rib[prefix_str]
return
# BGP最佳路径选择算法
best_route = self._select_best_path(candidates)
# 更新Loc-RIB
old_best = self.loc_rib.get(prefix_str)
if old_best != best_route:
# 清除旧的最佳标记
if old_best:
old_best.best = False
# 设置新的最佳路由
best_route.best = True
self.loc_rib[prefix_str] = best_route
print(f"[{self.router_id}] 选择最佳路径 {prefix_str}: 来自 {best_route.peer_ip}")
# 向其他对等体通告新的最佳路由
self._advertise_best_route(best_route)
def _select_best_path(self, candidates: List[BGPRoute]) -> BGPRoute:
"""BGP最佳路径选择算法"""
if len(candidates) == 1:
return candidates[0]
# 1. 最高LOCAL_PREF
max_local_pref = max(r.attributes.local_pref or 0 for r in candidates)
candidates = [r for r in candidates if (r.attributes.local_pref or 0) == max_local_pref]
if len(candidates) == 1:
return candidates[0]
# 2. 最短AS_PATH
min_as_path_len = min(len(r.attributes.as_path) for r in candidates)
candidates = [r for r in candidates if len(r.attributes.as_path) == min_as_path_len]
if len(candidates) == 1:
return candidates[0]
# 3. 最小ORIGIN (IGP < EGP < INCOMPLETE)
min_origin = min(r.attributes.origin.value for r in candidates)
candidates = [r for r in candidates if r.attributes.origin.value == min_origin]
if len(candidates) == 1:
return candidates[0]
# 4. 最小MED (仅比较来自同一AS的路由)
if len(set(r.attributes.as_path[0] if r.attributes.as_path else 0 for r in candidates)) == 1:
min_med = min(r.attributes.med or 0 for r in candidates)
candidates = [r for r in candidates if (r.attributes.med or 0) == min_med]
if len(candidates) == 1:
return candidates[0]
# 5. EBGP优于IBGP
ebgp_routes = [r for r in candidates if self.peers[r.peer_ip].is_ebgp]
if ebgp_routes:
candidates = ebgp_routes
if len(candidates) == 1:
return candidates[0]
# 6. 最小IGP度量到NEXT_HOP (简化为选择第一个)
return candidates[0]
def _advertise_best_route(self, route: BGPRoute):
"""向其他对等体通告最佳路由"""
for peer_ip, peer in self.peers.items():
if peer.state == BGPState.ESTABLISHED and peer_ip != route.peer_ip:
# 防止路由环路:不向学习到路由的对等体通告
self._send_update(peer_ip, route)
def add_import_policy(self, peer_ip: str, policy_func):
"""添加入口策略"""
if peer_ip not in self.import_policies:
self.import_policies[peer_ip] = []
self.import_policies[peer_ip].append(policy_func)
def add_export_policy(self, peer_ip: str, policy_func):
"""添加出口策略"""
if peer_ip not in self.export_policies:
self.export_policies[peer_ip] = []
self.export_policies[peer_ip].append(policy_func)
def show_peers(self):
"""显示BGP对等体状态"""
print(f"\n=== 路由器 {self.router_id} (AS{self.local_as}) BGP对等体 ===")
print(f"{'对等体IP':<15} {'对等体AS':<10} {'状态':<12} {'类型':<6} {'保持时间':<10}")
print("-" * 65)
for peer_ip, peer in self.peers.items():
peer_type = "EBGP" if peer.is_ebgp else "IBGP"
print(f"{peer_ip:<15} {peer.peer_as:<10} {peer.state.value:<12} {peer_type:<6} {peer.hold_time:<10}")
def show_rib(self, rib_type: str = "loc"):
"""显示RIB表"""
if rib_type == "loc":
rib = self.loc_rib
title = "Loc-RIB (最佳路由表)"
elif rib_type == "in":
title = "Adj-RIB-In (接收路由表)"
print(f"\n=== 路由器 {self.router_id} {title} ===")
for peer_ip, rib in self.rib_in.items():
if rib:
print(f"\n来自对等体 {peer_ip}:")
self._print_rib_entries(rib)
return
elif rib_type == "out":
title = "Adj-RIB-Out (发送路由表)"
print(f"\n=== 路由器 {self.router_id} {title} ===")
for peer_ip, rib in self.rib_out.items():
if rib:
print(f"\n发送给对等体 {peer_ip}:")
self._print_rib_entries(rib)
return
else:
return
print(f"\n=== 路由器 {self.router_id} {title} ===")
self._print_rib_entries(rib)
def _print_rib_entries(self, rib: Dict[str, BGPRoute]):
"""打印RIB条目"""
if not rib:
print(" (空)")
return
print(f"{'前缀':<18} {'下一跳':<15} {'AS路径':<20} {'本地优先级':<10} {'MED':<8} {'来源':<6}")
print("-" * 85)
for prefix, route in sorted(rib.items()):
as_path_str = " ".join(map(str, route.attributes.as_path))
local_pref = route.attributes.local_pref or "-"
med = route.attributes.med or "-"
origin = route.attributes.origin.name
prefix_display = f"{'*' if route.best else ' '}{prefix}"
print(f"{prefix_display:<18} {route.attributes.next_hop:<15} {as_path_str:<20} {local_pref:<10} {med:<8} {origin:<6}")
def show_statistics(self):
"""显示统计信息"""
print(f"\n=== 路由器 {self.router_id} BGP统计 ===")
print(f"UPDATE发送: {self.stats['updates_sent']}")
print(f"UPDATE接收: {self.stats['updates_received']}")
print(f"路由通告: {self.stats['routes_advertised']}")
print(f"路由撤销: {self.stats['routes_withdrawn']}")
print(f"Keepalive发送: {self.stats['keepalives_sent']}")
print(f"Keepalive接收: {self.stats['keepalives_received']}")
print(f"对等体数量: {len(self.peers)}")
print(f"最佳路由数: {len(self.loc_rib)}")
# BGP策略函数示例
def create_as_path_filter(allowed_as_list: List[int]):
"""创建AS路径过滤器"""
def filter_func(route: BGPRoute, attributes: Optional[BGPPathAttribute] = None) -> bool:
as_path = (attributes or route.attributes).as_path
return any(as_num in allowed_as_list for as_num in as_path)
return filter_func
def create_prefix_filter(allowed_prefixes: List[str]):
"""创建前缀过滤器"""
def filter_func(route: BGPRoute, attributes: Optional[BGPPathAttribute] = None) -> bool:
return route.get_prefix_str() in allowed_prefixes
return filter_func
def create_local_pref_modifier(new_local_pref: int):
"""创建本地优先级修改器"""
def modifier_func(route: BGPRoute, attributes: BGPPathAttribute) -> bool:
attributes.local_pref = new_local_pref
return True
return modifier_func
# 使用示例
if __name__ == "__main__":
# 创建BGP路由器
r1 = BGPRouter("1.1.1.1", 65001) # ISP A
r2 = BGPRouter("2.2.2.2", 65002) # ISP B
r3 = BGPRouter("3.3.3.3", 65003) # 企业网络
# 建立对等体关系
r1.add_peer("192.168.1.2", 65002) # R1 - R2
r1.add_peer("192.168.2.3", 65003) # R1 - R3
r2.add_peer("192.168.1.1", 65001) # R2 - R1
r2.add_peer("192.168.3.3", 65003) # R2 - R3
r3.add_peer("192.168.2.1", 65001) # R3 - R1
r3.add_peer("192.168.3.2", 65002) # R3 - R2
# 建立BGP会话
r1.establish_session("192.168.1.2")
r1.establish_session("192.168.2.3")
r2.establish_session("192.168.1.1")
r2.establish_session("192.168.3.3")
r3.establish_session("192.168.2.1")
r3.establish_session("192.168.3.2")
# 配置路由策略
# R3只接受来自AS 65001的路由
r3.add_import_policy("192.168.2.1", create_as_path_filter([65001]))
# R3对来自AS 65002的路由设置较低的本地优先级
r3.add_import_policy("192.168.3.2", create_local_pref_modifier(50))
# 通告路由
r1.advertise_route("10.1.0.0", 16, "192.168.1.1", OriginType.IGP, 100)
r2.advertise_route("10.2.0.0", 16, "192.168.1.2", OriginType.IGP, 100)
r3.advertise_route("192.168.100.0", 24, "192.168.2.3", OriginType.IGP, 100)
# 模拟路由传播
time.sleep(1)
# R1向R2通告路由
r2.receive_update("192.168.1.1", "10.1.0.0", 16, BGPPathAttribute(
origin=OriginType.IGP,
as_path=[65001],
next_hop="192.168.1.1"
))
# R2向R3通告路由
r3.receive_update("192.168.3.2", "10.1.0.0", 16, BGPPathAttribute(
origin=OriginType.IGP,
as_path=[65002, 65001],
next_hop="192.168.3.2"
))
# R1直接向R3通告路由
r3.receive_update("192.168.2.1", "10.1.0.0", 16, BGPPathAttribute(
origin=OriginType.IGP,
as_path=[65001],
next_hop="192.168.2.1"
))
# 显示结果
print("\n" + "="*50)
print("BGP路由表和统计信息")
print("="*50)
for router in [r1, r2, r3]:
router.show_peers()
router.show_rib("loc")
router.show_statistics()
print("\n" + "-"*30)
BGP配置示例
Cisco BGP配置
# 启用BGP进程
router bgp 65001
bgp router-id 1.1.1.1
bgp log-neighbor-changes
# 配置邻居
neighbor 192.168.1.2 remote-as 65002
neighbor 192.168.1.2 description "ISP-B"
neighbor 192.168.1.2 password bgp123
# 通告网络
network 10.1.0.0 mask 255.255.0.0
network 192.168.100.0 mask 255.255.255.0
# 路由策略
neighbor 192.168.1.2 route-map SET-LOCAL-PREF in
neighbor 192.168.1.2 route-map FILTER-OUT out
# 路由映射
route-map SET-LOCAL-PREF permit 10
match as-path 1
set local-preference 200
route-map FILTER-OUT deny 10
match ip address prefix-list PRIVATE-NETWORKS
route-map FILTER-OUT permit 20
# 前缀列表
ip prefix-list PRIVATE-NETWORKS seq 10 deny 192.168.0.0/16 le 24
ip prefix-list PRIVATE-NETWORKS seq 20 deny 10.0.0.0/8 le 24
# AS路径访问列表
ip as-path access-list 1 permit ^65002_
# BGP聚合
aggregate-address 10.0.0.0 255.0.0.0 summary-only
# BGP团体
neighbor 192.168.1.2 send-community
route-map SET-COMMUNITY permit 10
set community 65001:100
华为BGP配置
# 启用BGP进程
bgp 65001
router-id 1.1.1.1
# 配置对等体组
group EBGP-PEERS external
peer EBGP-PEERS as-number 65002
peer EBGP-PEERS password cipher bgp123
# 配置邻居
peer 192.168.1.2 group EBGP-PEERS
peer 192.168.1.2 description "ISP-B"
# IPv4单播地址族
ipv4-family unicast
undo synchronization
network 10.1.0.0 255.255.0.0
network 192.168.100.0 255.255.255.0
# 对等体策略
peer 192.168.1.2 enable
peer 192.168.1.2 route-policy SET-LOCAL-PREF import
peer 192.168.1.2 route-policy FILTER-OUT export
# 路由策略
route-policy SET-LOCAL-PREF permit node 10
if-match as-path-filter 1
apply local-preference 200
route-policy FILTER-OUT deny node 10
if-match ip-prefix PRIVATE-NETWORKS
route-policy FILTER-OUT permit node 20
# IP前缀列表
ip ip-prefix PRIVATE-NETWORKS index 10 deny 192.168.0.0 16 greater-equal 24
ip ip-prefix PRIVATE-NETWORKS index 20 deny 10.0.0.0 8 greater-equal 24
# AS路径过滤器
ip as-path-filter 1 permit ^65002_
# BGP聚合
aggregate 10.0.0.0 8 detail-suppressed
# BGP团体
peer 192.168.1.2 advertise-community
route-policy SET-COMMUNITY permit node 10
apply community 65001:100
路由协议比较与选择
协议特性对比
| 特性 | RIP | OSPF | BGP |
|---|---|---|---|
| 协议类型 | 距离矢量 | 链路状态 | 路径矢量 |
| 最大跳数 | 15 | 无限制 | 无限制 |
| 收敛速度 | 慢 | 快 | 中等 |
| 网络规模 | 小型 | 中大型 | 大型/互联网 |
| CPU消耗 | 低 | 中等 | 高 |
| 内存消耗 | 低 | 中等 | 高 |
| 配置复杂度 | 简单 | 中等 | 复杂 |
| 支持VLSM | RIPv2支持 | 支持 | 支持 |
| 支持认证 | RIPv2支持 | 支持 | 支持 |
| 负载均衡 | 等价路径 | 等价路径 | 策略控制 |
| 路由策略 | 有限 | 中等 | 丰富 |
应用场景选择
| 场景 | 推荐协议 | 理由 |
|---|---|---|
| 小型办公网络 | RIP | 配置简单,资源消耗低 |
| 企业内部网络 | OSPF | 快速收敛,支持层次化设计 |
| 数据中心网络 | OSPF + BGP | OSPF用于内部,BGP用于多租户 |
| 互联网服务提供商 | BGP | 丰富的策略控制,支持大规模网络 |
| 分支机构连接 | OSPF | 支持区域设计,便于管理 |
| 多厂商环境 | OSPF | 标准化程度高,兼容性好 |
实践练习
练习1:RIP协议配置与测试
目标:配置RIP网络并观察路由传播过程
步骤:
- 使用Python RIP模拟器创建3个路由器网络
- 配置RIP协议并通告网络
- 观察路由表的建立和更新过程
- 模拟链路故障,观察收敛过程
验收标准:
- 所有路由器能学习到完整的路由表
- 链路故障后能正确收敛到备用路径
- 理解RIP的15跳限制和无穷计数问题
练习2:OSPF区域设计与配置
目标:设计并实现OSPF多区域网络
步骤:
- 设计包含骨干区域和2个普通区域的网络拓扑
- 配置ABR路由器连接不同区域
- 实现区域间路由汇总
- 测试LSA传播和SPF计算
验收标准:
- 正确建立邻居关系和LSA数据库
- 区域间路由正常通信
- 路由汇总有效减少路由表大小
- 理解不同LSA类型的作用
练习3:BGP策略配置与优化
目标:配置BGP路由策略实现流量工程
步骤:
- 搭建多AS的BGP网络环境
- 配置路由过滤和属性修改策略
- 实现基于LOCAL_PREF的入站流量控制
- 实现基于AS_PATH prepending的出站流量控制
验收标准:
- BGP会话正常建立
- 路由策略正确生效
- 流量按预期路径转发
- 理解BGP路径选择算法
练习4:路由协议互操作
目标:实现不同路由协议间的路由重分发
步骤:
- 配置OSPF和BGP的路由重分发
- 设置重分发的度量值和策略
- 防止路由环路的配置
- 测试路由的双向重分发
验收标准:
- 路由重分发正常工作
- 没有出现路由环路
- 路由度量值合理设置
- 理解重分发的风险和最佳实践
练习5:路由协议故障排除
目标:诊断和解决常见的路由协议问题
步骤:
- 模拟各种路由协议故障场景
- 使用调试命令分析问题原因
- 制定并实施解决方案
- 验证问题解决效果
验收标准:
- 能快速定位路由协议问题
- 掌握常用的调试命令和方法
- 理解故障的根本原因
- 建立故障排除的系统方法
常见问题
Q1: RIP协议为什么有15跳的限制?
A: RIP使用跳数作为度量值,为了防止路由环路导致的无穷计数问题,将16跳定义为无穷大。当网络中出现路由环路时,跳数会不断增加,达到16跳时路由被认为不可达,从而避免无穷计数。这个限制也决定了RIP只适用于小型网络。
Q2: OSPF的DR/BDR选举规则是什么?
A: DR/BDR选举遵循以下规则:
- 优先级最高的路由器成为DR(默认优先级为1)
- 优先级第二高的成为BDR
- 优先级相同时,Router ID最大的获胜
- 优先级为0的路由器不参与选举
- DR/BDR选举是非抢占式的,除非重启或手动清除邻居关系
Q3: BGP的路径选择算法具体步骤是什么?
A: BGP路径选择遵循以下顺序:
- 最高的Weight(Cisco私有属性)
- 最高的LOCAL_PREF
- 本地通告的路由优于从邻居学到的路由
- 最短的AS_PATH
- 最小的ORIGIN(IGP < EGP < Incomplete)
- 最小的MED(仅比较来自同一AS的路由)
- EBGP路径优于IBGP路径
- 到NEXT_HOP的IGP度量最小
- 最老的路径(稳定性考虑)
- 最小的BGP Router ID
- 最小的邻居IP地址
Q4: 如何解决OSPF邻居关系无法建立的问题?
A: 检查以下配置:
- 网络连通性:确保物理连接正常
- 区域配置:确保接口在同一OSPF区域
- 网络类型:确保网络类型匹配(点对点、广播等)
- Hello参数:检查Hello间隔、Dead间隔是否一致
- 认证配置:确保认证类型和密钥一致
- MTU大小:确保接口MTU大小一致
- 子网掩码:确保接口IP地址和掩码正确
Q5: BGP会话建立失败的常见原因有哪些?
A: 常见原因包括:
- TCP连接问题:检查IP连通性和防火墙设置
- AS号配置错误:确保本地AS和对等体AS配置正确
- Router ID冲突:确保BGP Router ID唯一
- 认证失败:检查密码配置是否一致
- 更新源配置:确保更新源地址可达
- BGP版本不匹配:确保BGP版本兼容
- Hold时间不匹配:检查Hold时间配置
Q6: 如何优化大型网络中的路由协议性能?
A: 优化策略包括:
-
OSPF优化:
- 合理划分区域,控制LSA传播范围
- 配置区域汇总,减少路由表大小
- 调整SPF计算间隔,平衡收敛速度和CPU消耗
- 使用Stub区域减少LSA数量
-
BGP优化:
- 使用路由反射器减少IBGP全连接
- 配置路由聚合,减少路由表大小
- 调整BGP定时器,平衡收敛速度和稳定性
- 使用团体属性简化策略配置
-
通用优化:
- 合理规划IP地址,便于汇总
- 使用路由过滤,控制路由传播
- 监控路由表大小和收敛时间
- 定期清理无用的路由配置
总结
本章深入讲解了三种主要的路由协议:RIP、OSPF和BGP。通过学习本章内容,你应该掌握:
核心概念
- 距离矢量算法:RIP使用跳数作为度量,通过邻居交换路由信息
- 链路状态算法:OSPF维护完整的网络拓扑,使用SPF算法计算最短路径
- 路径矢量算法:BGP使用AS路径防止环路,支持丰富的路由策略
技术特点
- RIP:配置简单,适用于小型网络,但收敛慢且跳数有限
- OSPF:快速收敛,支持层次化设计,适用于企业网络
- BGP:策略丰富,支持大规模网络,是互联网的核心协议
实际应用
- 根据网络规模和需求选择合适的路由协议
- 理解不同协议的配置方法和调试技巧
- 掌握路由协议的故障排除和性能优化方法
发展趋势
- IPv6支持:所有主流路由协议都支持IPv6
- SDN集成:路由协议与SDN控制器的集成
- 安全增强:更强的认证和加密机制
- 自动化运维:基于API的自动化配置和监控
下一步
学完路由协议后,建议继续学习:
- 013-网络安全基础 - 了解网络安全机制
- 014-网络性能优化 - 学习QoS和流量控制
- 016-企业网络架构设计 - 应用路由协议进行网络设计
参考与引用
- RFC 2453 - RIP Version 2
- RFC 2328 - OSPF Version 2
- RFC 4271 - BGP-4
- Cisco BGP Configuration Guide
- 华为路由协议配置指南
- 《TCP/IP路由技术》- Jeff Doyle著
- 《互联网路由架构》- Bassam Halabi著
更新记录
- 更新时间: 2024-01-15 | 更新内容: 创建路由协议详解章节,包含RIP、OSPF、BGP协议的详细介绍和Python实现 | 更新人: Assistant

被折叠的 条评论
为什么被折叠?



