1. 为什么大规模路由实验值得反复做
刚入行那会儿,我对"大规模路由"这四个字的理解非常肤浅,觉得无非就是把几台路由器连起来,配几个网段,能ping通就算完事。直到有一次参与一个园区网的割接项目,现场有十几台设备、四个路由域、双出口冗余,结果OSPF邻居死活起不来,排查了三个小时才发现是两台设备之间的MTU不匹配导致DD报文被丢弃。那次之后我才真正意识到,小实验练的是命令,大实验练的是全局观和排错直觉。
H3C的大规模路由综合实验,核心价值不在于让你背下多少条配置命令,而在于逼着你在一个接近真实网络的环境里,同时处理多协议共存、路由重分发、策略控制、冗余备份这几件事。它解决的是一个很具体的问题:当你面对一张有几十个网段、多种路由协议、多条出口链路的拓扑时,你能不能理清楚数据包到底会走哪条路,以及当某条路断了之后,它又会走哪条路。
这个内容适合谁?如果你已经能独立配通单区域的OSPF或者简单的静态路由,但一遇到"OSPF和RIP互相引入""路由环路""次优路径"这些词就头大,那这个实验就是为你准备的。它不需要你有多高深的数学功底,但需要你有耐心去一条条看路由表、一个个抓包分析。下面我会按照我自己做这个实验的完整思路,从拓扑设计一路讲到排错收尾,把中间踩过的坑和想明白的道理都摊开来说。
2. 实验拓扑与地址规划的前置思考
2.1 拓扑不是画着好看,是画着好排错
很多人做实验第一步就是打开模拟器随手拖几台设备连起来,我觉得这是最坏的习惯。拓扑结构直接决定了你后面排错的难度。我的建议是:先想清楚你要验证什么,再决定拓扑长什么样。
大规模路由实验通常要覆盖这几个场景:多区域OSPF(至少包含骨干区域和非骨干区域)、OSPF与RIP的双向重分发、静态路由与动态路由的配合、以及至少一条冗余链路。基于这些目标,我设计的拓扑大致是这样的:
- 核心层两台设备(记为Core-A、Core-B)运行OSPF骨干区域,同时作为边界路由器连接外部RIP域
- 汇聚层三台设备(Agg-1、Agg-2、Agg-3)分别属于不同的OSPF非骨干区域
- 接入层用两台设备模拟RIP域,通过Core-A和Core-B与OSPF域互通
- Core-A和Core-B之间跑一条链路作为冗余,同时各自有一条静态默认路由指向模拟的"互联网"出口
这个拓扑的好处是:它天然包含了水平分割和次优路径的隐患点。比如RIP域的路由从Core-A引入OSPF后,Core-B也会学到,如果重分发配置不当,Core-B又会把这条路由引回RIP域,形成环路。这种问题在小拓扑里根本遇不到,但在这里是必然会碰上的。
2.2 地址规划要留出"呼吸空间"
地址规划这件事,我的原则是:每个区域用连续的地址块,区域之间留出至少一个/24的间隔。不是为了好看,是为了后面做路由汇总的时候不用回头改地址。
具体来说,我习惯这样分配:
| 区域/用途 | 地址段 | 说明 |
|---|---|---|
| OSPF骨干区域 | 10.0.0.0/24 | Core-A与Core-B互联及环回口 |
| OSPF区域1 | 10.0.1.0/24 | Agg-1及其下挂网段 |
| OSPF区域2 | 10.0.2.0/24 | Agg-2及其下挂网段 |
| OSPF区域3 | 10.0.3.0/24 | Agg-3及其下挂网段 |
| RIP域 | 10.0.10.0/24 | 接入层设备及模拟终端网段 |
| 互联链路 | 10.0.100.0/24 | 各设备之间的点对点链路 |
| 模拟出口 | 192.168.100.0/24 | 静态默认路由指向 |
环回口地址我统一用x.x.x.x/32的格式,比如Core-A的环回口是1.1.1.1/32,Core-B是2.2.2.2/32。这样做的好处是,后面做OSPF Router ID和BGP对等体的时候,直接拿环回口地址用,不用再额外规划。
提示:地址规划阶段一定要把每个网段的用途写在文档里,不要只存在脑子里。实验做到一半忘了某个网段是干嘛的,回头翻配置会浪费大量时间。
2.3 模拟器选型与资源分配
H3C的实验环境,我一般用HCL(H3C Cloud Lab)来做。它对H3C设备的支持最完整,命令和真机几乎一致。如果你用的是其他模拟器,需要注意有些命令可能不支持,比如某些高级的OSPF LSA过滤功能。
资源分配上,大规模路由实验对内存的消耗比想象中大。我建议给每台设备至少分配512MB内存,如果模拟器支持的话,Core层设备给到1GB。否则在OSPF收敛或者路由重分发的时候,设备可能会因为内存不足而卡死,让你误以为是配置问题。
还有一个细节:接口的MTU和带宽参数要手动配。模拟器默认的接口带宽可能都是100M或者1G,但实际网络中不同链路带宽不同。如果你要做基于带宽的路径选择或者QoS策略,这些参数不配准,实验结果就是错的。我一般会在互联链路上显式配置bandwidth和mtu,确保和设计一致。
3. OSPF多区域配置里的那些"隐形规则"
3.1 区域划分不是随便切的
OSPF多区域的核心目的是减少LSA泛洪范围,但区域怎么切是有讲究的。我见过有人把区域切得特别碎,结果ABR(区域边界路由器)上路由表爆炸,反而增加了复杂度。
我的经验是:区域数量控制在3到5个之间,每个区域内的网段数量不要超过20个。超过这个数,你就该考虑用路由汇总来压缩路由表了。在这个实验里,我用了三个非骨干区域,每个区域下挂两到三个网段,规模刚好。
区域划分的另一个原则是:所有非骨干区域必须直接或间接连接到骨干区域。如果你有一个区域没有物理链路连到区域0,就必须配置虚链路(Virtual Link)。虚链路这个东西,能不用就不用,它会让你的拓扑变得难以理解,而且一旦中间链路出问题,排查起来非常麻烦。我在实验里刻意避免使用虚链路,所有区域都直接挂在Core-A或Core-B上。
3.2 ABR和ASBR的角色不能混
这是我在实验里踩过的一个大坑。ABR是连接骨干区域和非骨干区域的路由器,ASBR是引入外部路由的路由器。一台设备可以同时是ABR和ASBR,但配置的时候要分清楚哪些命令是针对区域的,哪些是针对进程的。
举个例子:abr-summary命令是在OSPF进程下配置的,但它作用于特定的区域。如果你在Core-A上配置了abr-summary 10.0.1.0 255.255.255.0,但没有指定区域,设备可能不知道你要汇总哪个区域的路由。正确的做法是进入ospf进程后,用area 1进入区域视图,再配置abr-summary。
另一个容易混淆的是asbr-summary,它用来汇总引入的外部路由。这个命令是在OSPF进程下直接配置的,不需要进入区域视图。我当初就是因为把这两个命令搞混了,导致汇总不生效,路由表里还是一堆明细路由。
3.3 网络类型与DR/BDR选举
OSPF的网络类型决定了邻居关系和DR/BDR的选举方式。在实验环境里,点对点链路我一般显式配置ospf network-type p2p,这样可以跳过DR/BDR选举,加快收敛速度。广播网络(比如以太网)则保持默认的broadcast类型。
这里有一个细节:如果两台路由器的网络类型不匹配,邻居关系会卡在ExStart状态。我遇到过好几次,一边配了p2p,另一边还是broadcast,结果display ospf peer显示邻居一直在ExStart和Down之间跳。排查的时候,先看两边的display ospf interface,确认网络类型和MTU是否一致。
DR/BDR的选举规则是:先比优先级,优先级相同比Router ID,越大越优先。优先级为0的设备不参与选举。在实验里,我通常把Core-A的优先级设为100,Core-B设为50,确保Core-A稳定成为DR。这样做是为了避免DR频繁切换导致LSA重新泛洪。
注意:DR/BDR的选举不是抢占式的。如果你先启动了一台优先级低的设备,它成了DR,后面即使启动了优先级高的设备,也不会抢走DR角色,除非重启OSPF进程或者重启设备。这一点在实验里要特别注意,否则你会以为自己的优先级配置没生效。
3.4 LSA类型与路由表的对应关系
OSPF的LSA类型是理解路由表的关键。我刚开始学的时候,总是记不住哪种LSA对应哪种路由。后来我总结了一个简单的对应关系:
| LSA类型 | 产生者 | 传播范围 | 对应路由 |
|---|---|---|---|
| Type 1 (Router LSA) | 每台路由器 | 本区域 | 区域内路由 |
| Type 2 (Network LSA) | DR | 本区域 | 区域内路由 |
| Type 3 (Summary LSA) | ABR | 跨区域 | 区域间路由 |
| Type 4 (ASBR Summary) | ABR | 跨区域 | 指向ASBR的路由 |
| Type 5 (External LSA) | ASBR | 整个OSPF域 | 外部路由 |
| Type 7 (NSSA External) | ASBR | NSSA区域 | NSSA外部路由 |
在实验里,你可以通过display ospf lsdb命令查看每种LSA的详细信息。我建议在做重分发之前,先确认一下各区域的LSDB是否正常,特别是Type 3 LSA有没有正确生成。如果ABR没有生成Type 3 LSA,区域间的路由就不会出现在路由表里。
4. 重分发:路由环路和次优路径的温床
4.1 双向重分发的"死循环"是怎么形成的
OSPF和RIP双向重分发,听起来很简单:在Core-A上把RIP引入OSPF,在Core-B上把OSPF引入RIP。但如果你真的这么配了,大概率会出问题。
问题的根源在于:路由是双向传播的。Core-A把RIP路由引入OSPF后,Core-B通过OSPF学到了这些路由。然后Core-B又把OSPF路由引入RIP,其中就包括了刚从Core-A学来的那些RIP路由。这些路由被引回RIP域后,又会被Core-A通过RIP学到,再引入OSPF……虽然路由协议本身有防环机制(比如RIP的跳数限制、OSPF的LSA序列号),但这种配置会导致路由表里出现大量冗余条目,收敛速度变慢,严重时甚至引起路由震荡。
解决这个问题的标准做法是:在重分发的时候使用路由策略(Route-Policy)或者标签(Tag)来标记路由来源,然后在引入时过滤掉带特定标记的路由。
具体操作上,我习惯这样做:
- 在Core-A上,把RIP路由引入OSPF时,给这些路由打上Tag 100
- 在Core-B上,把OSPF路由引入RIP时,用Route-Policy拒绝Tag为100的路由
- 反过来,Core-B引入OSPF的路由打Tag 200,Core-A引入RIP时拒绝Tag 200
这样就能确保路由不会在两个协议之间来回"弹跳"。
4.2 种子度量值不能忽略
重分发的时候,如果不指定种子度量值(Seed Metric),路由引入后度量值可能是无穷大或者默认值,导致路由不可用。不同的协议默认行为不一样:
- OSPF引入外部路由时,默认度量值是20(Type 2外部路由)
- RIP引入外部路由时,默认度量值是0,但RIP认为0是无效的,所以必须手动指定
我在实验里一般这样配:
# 在OSPF进程下引入RIP路由,指定种子度量值为10,类型为Type 2 import-route rip 1 type 2 cost 10 tag 100 # 在RIP进程下引入OSPF路由,指定种子度量值为5 import-route ospf 1 cost 5种子度量值的选择不是随意的。它会影响路由的优先级。比如,如果你希望OSPF域内的流量优先走Core-A出口,那Core-A引入的RIP路由度量值就应该比Core-B引入的小。这个值需要根据你的流量规划来定。
4.3 次优路径的识别与修正
次优路径是重分发实验里最隐蔽的问题。它不会导致网络不通,但会让流量绕远路。识别次优路径的方法是:在源设备上traceroute目标地址,看数据包实际走了哪条路,然后和你的预期路径对比。
我遇到过一种典型情况:RIP域有一台设备要访问OSPF区域1的某个网段。理论上应该走Core-A(因为Core-A离区域1更近),但实际上流量走了Core-B。原因是Core-B引入OSPF路由到RIP时,没有区分区域,所有OSPF路由的度量值都一样,RIP就选了跳数少的路径。
修正的方法有两种:一是调整种子度量值,让Core-A引入的路由度量值更小;二是用Route-Policy在Core-B上过滤掉特定区域的路由,不让它们被引入RIP。我一般优先用第一种方法,因为它更简单,不需要维护复杂的策略。
提示:做重分发实验的时候,一定要在每台设备上
display ip routing-table看一遍,确认路由的来源(Protocol列)和度量值(Pre和Cost列)。不要只看能不能ping通,能ping通不代表路径是对的。
5. 策略路由与流量控制的实操细节
5.1 Filter-Policy和Route-Policy的区别
这两个东西名字很像,但用途完全不同。Filter-Policy是用来过滤路由的,它决定哪些路由能进入路由表或者能被发布出去。Route-Policy是用来修改路由属性的,它可以匹配路由并设置度量值、Tag、下一跳等。
我刚开始学的时候经常搞混,后来记了一个简单的区分方法:Filter-Policy只做"允许/拒绝",Route-Policy可以做"允许/拒绝+修改"。如果你只需要过滤路由,用Filter-Policy就够了;如果你需要修改路由属性,就必须用Route-Policy。
在重分发场景里,我通常用Route-Policy来打Tag和设置度量值,用Filter-Policy来做最后的兜底过滤。比如:
# 定义Route-Policy,匹配RIP路由并打Tag route-policy RP_RIP2OSPF permit node 10 if-match protocol rip apply tag 100 apply cost 10 # 在OSPF进程下应用 import-route rip 1 route-policy RP_RIP2OSPF5.2 策略路由(PBR)与路由策略的区别
策略路由(Policy-Based Routing,PBR)和路由策略(Routing Policy)是两码事。路由策略影响的是路由表,而策略路由影响的是数据包的转发路径。PBR的优先级高于路由表,它可以在接口上直接指定数据包的下一跳。
在大规模路由实验里,PBR通常用来做流量工程。比如,你希望某个网段的流量走Core-A出口,即使路由表显示应该走Core-B,就可以用PBR强制指定下一跳。
配置PBR的步骤是:
- 定义ACL匹配感兴趣的流量
- 定义Route-Policy或者PBR策略,指定下一跳
- 在接口上应用PBR策略
# 定义ACL匹配源地址为10.0.1.0/24的流量 acl number 3000 rule 10 permit ip source 10.0.1.0 0.0.0.255 # 定义PBR策略 policy-based-route PBR_TEST permit node 10 if-match acl 3000 apply next-hop 10.0.100.2 # 在接口上应用 interface GigabitEthernet0/1 ip policy-based-route PBR_TESTPBR的坑在于:它只对进入接口的流量生效,对本地产生的流量不生效。如果你想让路由器自己产生的流量也走PBR,需要用ip local policy-based-route命令。这一点在实验里很容易忽略。
5.3 路由汇总的时机和粒度
路由汇总是减少路由表规模的有效手段,但汇总的时机和粒度需要仔细考虑。汇总得太早,可能会把不可达的路由也包含进去,导致黑洞;汇总得太晚,路由表已经很大了,效果不明显。
我的经验是:在ABR上做区域间汇总,在ASBR上做外部路由汇总。区域间汇总用abr-summary,外部路由汇总用asbr-summary。汇总的粒度一般是按区域或者按地址块来,比如把10.0.1.0/24到10.0.3.0/24汇总成10.0.0.0/22。
汇总的一个常见问题是:汇总路由的度量值取的是明细路由中最小的那个。如果明细路由的度量值差异很大,汇总后可能会导致流量都走度量值最小的那条路,造成负载不均衡。解决方法是配合Route-Policy调整度量值,或者在汇总的时候使用not-advertise参数来抑制某些明细路由。
6. 冗余与故障切换的验证方法
6.1 冗余链路不是配了就行
冗余链路的核心要求是:主链路故障时,备用链路能在可接受的时间内接管流量。这个"可接受的时间"取决于你的业务需求,对于大多数企业网来说,1到3秒是可以接受的。
在OSPF里,故障检测主要靠Hello报文。默认的Hello间隔是10秒,Dead间隔是40秒。也就是说,链路故障后,最快也要40秒才能检测到。这个时间对于很多业务来说太长了。我一般会把Hello间隔改成3秒,Dead间隔改成10秒,这样故障检测时间缩短到10秒以内。
interface GigabitEthernet0/1 ospf timer hello 3 ospf timer dead 10但要注意:Hello和Dead间隔必须在链路两端一致,否则邻居关系建立不起来。我在实验里就因为一边改了另一边没改,导致邻居一直卡在Init状态。
6.2 验证故障切换的完整流程
验证冗余切换不能只是"拔掉一根线看看通不通",需要有一套完整的流程:
- 基线测试:在正常情况下,traceroute目标地址,记录路径和延迟
- 故障注入:关闭主链路接口,同时启动一个持续的ping或者traceroute
- 观察收敛:记录从故障发生到流量恢复的时间,以及恢复后的路径
- 恢复测试:重新开启主链路接口,观察流量是否切回主路径
- 重复测试:至少重复三次,确认结果一致
我通常会用一个简单的脚本来自动化这个过程:
# 持续ping目标地址,记录时间戳 ping -c 1000 -i 0.2 10.0.10.1 | while read line; do echo "$(date +%s) $line"; done > ping_log.txt然后在另一个终端上关闭接口,观察ping_log.txt里的丢包情况。丢包的个数乘以ping间隔,就是大致的收敛时间。
6.3 BFD与快速收敛
如果对收敛时间要求更高(比如毫秒级),可以启用BFD(Bidirectional Forwarding Detection)。BFD是一种轻量级的故障检测协议,它可以在毫秒级检测到链路故障,然后通知OSPF快速收敛。
配置BFD的步骤是:
# 全局启用BFD bfd multi-hop enable # 在OSPF进程下启用BFD ospf 1 bfd all-interfaces enable # 在接口上配置BFD参数 interface GigabitEthernet0/1 ospf bfd min-transmit-interval 100 ospf bfd min-receive-interval 100 ospf bfd detect-multiplier 3BFD的检测时间 = min-transmit-interval × detect-multiplier。上面的配置是100ms × 3 = 300ms。也就是说,链路故障后300ms内就能检测到。
但BFD不是万能的。它需要两端都支持并且配置一致。如果一端配了BFD另一端没配,BFD会话建立不起来,OSPF会回退到普通的Hello检测。我在实验里遇到过这种情况,排查的时候用display bfd session命令查看会话状态,发现一直是Down,最后发现是对端没配。
7. 排错思路:从现象到根因的完整链路
7.1 邻居关系起不来,先看这五个地方
OSPF邻居关系起不来是最常见的问题。我的排查顺序是:
- 接口状态:
display ip interface brief,确认接口是Up的,IP地址配置正确 - 区域ID:
display ospf brief,确认两端的区域ID一致 - 网络类型:
display ospf interface,确认两端的网络类型匹配 - Hello/Dead间隔:确认两端的定时器一致
- 认证:如果配置了认证,确认两端的认证方式和密钥一致
这五个地方检查完,90%的邻居问题都能解决。如果还是不行,就要看底层了:VLAN配置、链路聚合、MTU等。
我遇到过一个比较隐蔽的问题:两台设备之间的链路是Trunk口,但VLAN允许列表没有包含OSPF所在的VLAN,导致OSPF报文被丢弃。这种情况下,接口是Up的,但OSPF邻居就是起不来。排查的时候用display interface看接口的收发统计,发现没有OSPF报文,才定位到VLAN问题。
7.2 路由表里有路由但ping不通
这种情况通常是路由的下一跳不可达或者被更优的路由覆盖了。排查步骤是:
display ip routing-table x.x.x.x,看目标地址匹配的是哪条路由- 检查这条路由的下一跳地址是否可达(
ping下一跳) - 检查是否有更优的路由(比如直连路由或者静态路由)覆盖了动态路由
- 检查接口的ACL或者防火墙策略是否阻止了流量
我遇到过一种情况:路由表里有两条去往同一目标的路由,一条是OSPF学到的,一条是静态配置的。静态路由的优先级(Pre)是60,OSPF是10,所以OSPF路由被优选了。但OSPF路由的下一跳指向的是一个不可达的地址,导致ping不通。解决方法是删除静态路由,或者调整优先级。
7.3 重分发后路由丢失
重分发后路由丢失,通常是因为种子度量值没有配置或者Route-Policy过滤掉了路由。排查方法是:
display ospf lsdb,看Type 5 LSA是否存在display ip routing-table protocol ospf,看外部路由是否在路由表里- 检查
import-route命令是否配置了正确的协议和进程号 - 检查Route-Policy的匹配条件是否正确
我踩过一个坑:在OSPF进程下配置了import-route rip 1,但RIP进程号实际上是2,不是1。结果命令看起来配了,但没有任何路由被引入。排查的时候用display current-configuration看配置,才发现进程号写错了。
7.4 路由震荡的排查
路由震荡表现为路由表里的条目频繁出现和消失,网络时通时断。常见原因是物理链路不稳定或者路由协议定时器配置不当。
排查方法是:
display logbuffer,看是否有接口Up/Down的日志display interface,看接口的CRC错误计数是否在增长- 检查OSPF的Hello/Dead间隔是否过短,导致误判邻居Down
- 检查是否有路由环路导致LSA频繁更新
我在实验里遇到过一次路由震荡,最后发现是因为两台设备之间的链路质量不好,模拟器上表现为间歇性丢包。把链路换成点对点直连后问题就消失了。这也提醒我,模拟器虽然方便,但它的链路质量和真机还是有差距的。
8. 实验收尾与配置固化
8.1 配置保存不是终点
实验做完之后,很多人直接save一下就完事了。但我觉得,配置保存只是最低要求,真正的收尾是整理一份可复现的文档。这份文档应该包含:
- 拓扑图(标注设备角色、接口、地址)
- 每台设备的完整配置(可以用
display current-configuration导出) - 关键验证命令的输出(路由表、邻居表、LSDB等)
- 遇到的问题和解决方法
这样做的好处是,下次再做类似的实验,你可以直接参考这份文档,不用从头再来。而且,如果你要把实验内容分享给别人,这份文档就是最好的素材。
8.2 配置备份的自动化
如果你经常做实验,手动备份配置很麻烦。我一般会写一个简单的脚本,通过SSH登录到每台设备,执行display current-configuration并把输出保存到本地文件。
import paramiko import time devices = [ {"ip": "10.0.0.1", "user": "admin", "pass": "password"}, {"ip": "10.0.0.2", "user": "admin", "pass": "password"}, ] for dev in devices: ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect(dev["ip"], username=dev["user"], password=dev["pass"]) shell = ssh.invoke_shell() shell.send("screen-length disable\n") time.sleep(1) shell.send("display current-configuration\n") time.sleep(3) output = shell.recv(65535).decode() with open(f"config_{dev['ip']}.txt", "w") as f: f.write(output) ssh.close()这个脚本很简单,但很实用。注意screen-length disable命令,它是用来关闭分屏显示的,否则输出会被分页截断。
8.3 从实验到生产的距离
最后说一点我自己的体会:实验环境和生产环境最大的区别不是规模,而是"不确定性"。实验里你知道每台设备的状态,知道每条链路的质量,知道每个配置的含义。但生产环境里,你可能不知道某台设备上有没有人配过ACL,不知道某条链路中间经过了什么设备,不知道某个网段是不是已经被占用了。
所以,做实验的时候,我会有意识地培养一种习惯:每配一条命令,都想一下"如果这条命令在生产环境里生效了,会有什么影响"。比如,配import-route的时候,想一下会不会引入不该引入的路由;配abr-summary的时候,想一下汇总后的地址块会不会和现有网段冲突。
这种思维习惯,比记住多少条命令都重要。实验可以重来,生产环境的一次误操作可能就是几个小时的中断。把实验当成生产来做,把生产当成实验来谨慎,这是我做了这么多年网络之后最深的体会。