012-路由协议详解

012-路由协议详解

难度:🔴 | 预计时间:180分钟 | 前置:008-子网划分与VLSM009-网络设备与拓扑

学习目标

  • 理解路由协议的分类和工作原理
  • 掌握距离向量和链路状态算法
  • 深入学习RIP、OSPF、BGP协议的配置和优化
  • 了解路由策略和路由过滤技术
  • 掌握路由协议的故障排除方法

路由协议概述

路由协议分类

路由协议按照不同标准可以进行多种分类:

分类标准类型协议示例特点
算法类型距离向量RIP、EIGRP基于跳数或复合度量
链路状态OSPF、IS-IS基于网络拓扑图
路径向量BGP基于AS路径
应用范围内部网关协议(IGP)RIP、OSPF、EIGRP自治系统内部
外部网关协议(EGP)BGP自治系统之间
是否分类有类别RIPv1不支持VLSM
无类别RIPv2、OSPF、BGP支持VLSM和CIDR

路由协议工作原理

路由器启动
初始化路由表
发送Hello消息
建立邻居关系
交换路由信息
计算最佳路径
更新路由表
转发数据包
网络变化?
重新计算路由
定期更新

路由度量值比较

协议度量值最大跳数收敛时间带宽开销
RIPv1/v2跳数15慢(分钟级)
OSPF开销(带宽)无限制快(秒级)中等
EIGRP复合度量255很快(秒级)
BGPAS路径长度无限制慢(分钟级)

RIP协议详解

RIP协议特点

RIP(Routing Information Protocol)是最简单的距离向量路由协议:

  • 度量值: 跳数(Hop Count)
  • 最大跳数: 15跳(16跳表示不可达)
  • 更新周期: 30秒
  • 版本: RIPv1(有类别)、RIPv2(无类别)

RIP工作机制

路由器A 路由器B 路由器C RIP路由更新过程 路由更新(网络1, 跳数1) 路由更新(网络1, 跳数2) 路由更新(网络3, 跳数1) 路由更新(网络3, 跳数2) 30秒后定期更新 完整路由表 完整路由表 完整路由表 完整路由表 路由器A 路由器B 路由器C

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自治系统
骨干区域 Area 0
普通区域 Area 1
末梢区域 Area 2
外部AS
外部路由器
路由器3
路由器4
路由器1
路由器2
ABR1
ABR2
ABR3
ASBR

OSPF路由器类型

路由器类型英文缩写功能描述特点
内部路由器IR所有接口在同一区域维护单一LSDB
区域边界路由器ABR连接多个区域维护多个LSDB
自治系统边界路由器ASBR连接外部AS引入外部路由
骨干路由器BR至少一个接口在Area 0转发区域间流量

OSPF LSA类型

LSA类型名称描述泛洪范围
Type 1Router LSA路由器链路状态区域内
Type 2Network LSA网络链路状态区域内
Type 3Summary LSA网络汇总区域间
Type 4ASBR Summary LSAASBR汇总区域间
Type 5External LSA外部路由整个AS
Type 7NSSA External LSANSSA外部路由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状态机

Start
TCP连接建立
TCP连接失败
重试连接
收到OPEN消息
收到NOTIFICATION
收到KEEPALIVE
收到NOTIFICATION
连接错误
正常运行
Idle
Connect
OpenSent
Active
OpenConfirm
Established

BGP路径属性

属性类型属性名称必选/可选描述
Well-known MandatoryORIGIN必选路由来源(IGP/EGP/Incomplete)
Well-known MandatoryAS_PATH必选AS路径列表
Well-known MandatoryNEXT_HOP必选下一跳地址
Well-known DiscretionaryLOCAL_PREF可选本地优先级
Well-known DiscretionaryATOMIC_AGGREGATE可选原子聚合标志
Optional TransitiveAGGREGATOR可选聚合者信息
Optional TransitiveCOMMUNITY可选团体属性
Optional Non-transitiveMED可选多出口鉴别符

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

路由协议比较与选择

协议特性对比

特性RIPOSPFBGP
协议类型距离矢量链路状态路径矢量
最大跳数15无限制无限制
收敛速度中等
网络规模小型中大型大型/互联网
CPU消耗中等
内存消耗中等
配置复杂度简单中等复杂
支持VLSMRIPv2支持支持支持
支持认证RIPv2支持支持支持
负载均衡等价路径等价路径策略控制
路由策略有限中等丰富

应用场景选择

场景推荐协议理由
小型办公网络RIP配置简单,资源消耗低
企业内部网络OSPF快速收敛,支持层次化设计
数据中心网络OSPF + BGPOSPF用于内部,BGP用于多租户
互联网服务提供商BGP丰富的策略控制,支持大规模网络
分支机构连接OSPF支持区域设计,便于管理
多厂商环境OSPF标准化程度高,兼容性好

实践练习

练习1:RIP协议配置与测试

目标:配置RIP网络并观察路由传播过程

步骤

  1. 使用Python RIP模拟器创建3个路由器网络
  2. 配置RIP协议并通告网络
  3. 观察路由表的建立和更新过程
  4. 模拟链路故障,观察收敛过程

验收标准

  • 所有路由器能学习到完整的路由表
  • 链路故障后能正确收敛到备用路径
  • 理解RIP的15跳限制和无穷计数问题

练习2:OSPF区域设计与配置

目标:设计并实现OSPF多区域网络

步骤

  1. 设计包含骨干区域和2个普通区域的网络拓扑
  2. 配置ABR路由器连接不同区域
  3. 实现区域间路由汇总
  4. 测试LSA传播和SPF计算

验收标准

  • 正确建立邻居关系和LSA数据库
  • 区域间路由正常通信
  • 路由汇总有效减少路由表大小
  • 理解不同LSA类型的作用

练习3:BGP策略配置与优化

目标:配置BGP路由策略实现流量工程

步骤

  1. 搭建多AS的BGP网络环境
  2. 配置路由过滤和属性修改策略
  3. 实现基于LOCAL_PREF的入站流量控制
  4. 实现基于AS_PATH prepending的出站流量控制

验收标准

  • BGP会话正常建立
  • 路由策略正确生效
  • 流量按预期路径转发
  • 理解BGP路径选择算法

练习4:路由协议互操作

目标:实现不同路由协议间的路由重分发

步骤

  1. 配置OSPF和BGP的路由重分发
  2. 设置重分发的度量值和策略
  3. 防止路由环路的配置
  4. 测试路由的双向重分发

验收标准

  • 路由重分发正常工作
  • 没有出现路由环路
  • 路由度量值合理设置
  • 理解重分发的风险和最佳实践

练习5:路由协议故障排除

目标:诊断和解决常见的路由协议问题

步骤

  1. 模拟各种路由协议故障场景
  2. 使用调试命令分析问题原因
  3. 制定并实施解决方案
  4. 验证问题解决效果

验收标准

  • 能快速定位路由协议问题
  • 掌握常用的调试命令和方法
  • 理解故障的根本原因
  • 建立故障排除的系统方法

常见问题

Q1: RIP协议为什么有15跳的限制?
A: RIP使用跳数作为度量值,为了防止路由环路导致的无穷计数问题,将16跳定义为无穷大。当网络中出现路由环路时,跳数会不断增加,达到16跳时路由被认为不可达,从而避免无穷计数。这个限制也决定了RIP只适用于小型网络。

Q2: OSPF的DR/BDR选举规则是什么?
A: DR/BDR选举遵循以下规则:

  1. 优先级最高的路由器成为DR(默认优先级为1)
  2. 优先级第二高的成为BDR
  3. 优先级相同时,Router ID最大的获胜
  4. 优先级为0的路由器不参与选举
  5. DR/BDR选举是非抢占式的,除非重启或手动清除邻居关系

Q3: BGP的路径选择算法具体步骤是什么?
A: BGP路径选择遵循以下顺序:

  1. 最高的Weight(Cisco私有属性)
  2. 最高的LOCAL_PREF
  3. 本地通告的路由优于从邻居学到的路由
  4. 最短的AS_PATH
  5. 最小的ORIGIN(IGP < EGP < Incomplete)
  6. 最小的MED(仅比较来自同一AS的路由)
  7. EBGP路径优于IBGP路径
  8. 到NEXT_HOP的IGP度量最小
  9. 最老的路径(稳定性考虑)
  10. 最小的BGP Router ID
  11. 最小的邻居IP地址

Q4: 如何解决OSPF邻居关系无法建立的问题?
A: 检查以下配置:

  1. 网络连通性:确保物理连接正常
  2. 区域配置:确保接口在同一OSPF区域
  3. 网络类型:确保网络类型匹配(点对点、广播等)
  4. Hello参数:检查Hello间隔、Dead间隔是否一致
  5. 认证配置:确保认证类型和密钥一致
  6. MTU大小:确保接口MTU大小一致
  7. 子网掩码:确保接口IP地址和掩码正确

Q5: BGP会话建立失败的常见原因有哪些?
A: 常见原因包括:

  1. TCP连接问题:检查IP连通性和防火墙设置
  2. AS号配置错误:确保本地AS和对等体AS配置正确
  3. Router ID冲突:确保BGP Router ID唯一
  4. 认证失败:检查密码配置是否一致
  5. 更新源配置:确保更新源地址可达
  6. BGP版本不匹配:确保BGP版本兼容
  7. Hold时间不匹配:检查Hold时间配置

Q6: 如何优化大型网络中的路由协议性能?
A: 优化策略包括:

  1. OSPF优化

    • 合理划分区域,控制LSA传播范围
    • 配置区域汇总,减少路由表大小
    • 调整SPF计算间隔,平衡收敛速度和CPU消耗
    • 使用Stub区域减少LSA数量
  2. BGP优化

    • 使用路由反射器减少IBGP全连接
    • 配置路由聚合,减少路由表大小
    • 调整BGP定时器,平衡收敛速度和稳定性
    • 使用团体属性简化策略配置
  3. 通用优化

    • 合理规划IP地址,便于汇总
    • 使用路由过滤,控制路由传播
    • 监控路由表大小和收敛时间
    • 定期清理无用的路由配置

总结

本章深入讲解了三种主要的路由协议:RIP、OSPF和BGP。通过学习本章内容,你应该掌握:

核心概念

  • 距离矢量算法:RIP使用跳数作为度量,通过邻居交换路由信息
  • 链路状态算法:OSPF维护完整的网络拓扑,使用SPF算法计算最短路径
  • 路径矢量算法:BGP使用AS路径防止环路,支持丰富的路由策略

技术特点

  • RIP:配置简单,适用于小型网络,但收敛慢且跳数有限
  • OSPF:快速收敛,支持层次化设计,适用于企业网络
  • BGP:策略丰富,支持大规模网络,是互联网的核心协议

实际应用

  • 根据网络规模和需求选择合适的路由协议
  • 理解不同协议的配置方法和调试技巧
  • 掌握路由协议的故障排除和性能优化方法

发展趋势

  • IPv6支持:所有主流路由协议都支持IPv6
  • SDN集成:路由协议与SDN控制器的集成
  • 安全增强:更强的认证和加密机制
  • 自动化运维:基于API的自动化配置和监控

下一步

学完路由协议后,建议继续学习:

参考与引用

更新记录

  • 更新时间: 2024-01-15 | 更新内容: 创建路由协议详解章节,包含RIP、OSPF、BGP协议的详细介绍和Python实现 | 更新人: Assistant
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值