路由环路排查与预防:静态互指、默认路由、双点双向重分发

路由环路排查与预防:静态互指、默认路由、双点双向重分发 路由环路这四个字听着像教科书里的概念但只要在运维一线待过就知道它有多折腾人。我印象最深的一次是凌晨被电话叫醒机房两台设备的面板灯闪得跟过节一样业务侧只说时通时断登上去一看 CPU 直接顶到 90% 以上链路流量打满可路由表翻来翻去又看不出什么大毛病。折腾了快四十分钟才定位到根源两条静态路由互相指向了对方中间还夹着一条默认路由兜底数据包就这么在两台设备之间来回转直到 TTL 耗尽被悄悄丢掉。从那之后我把环路的成因、排查、修复整理成了一套固定动作。下面要讲清楚三件事路由环路到底是怎么被喂出来的、在设备上怎么用几条命令把它圈出来、以及怎么在配置下发之前就把它掐死。内容覆盖静态路由、动态协议重分发、路由汇总、多实例与 NAT 场景最后会给一套能在模拟器里复现的实验步骤。刚入行的运维照着走能少走弯路做了几年的网工也可以拿它当一份排查清单。1. 环路现场的三个信号它和网络慢根本不是一回事1.1 CPU 高、链路打满、业务时通时断三个一起出现才有味道很多新人第一次遇到环路第一反应是网络慢然后去查带宽、查运营商线路、查服务器负载方向全跑偏了。环路和普通的拥塞有一个本质区别环路上的包永远不会到达目的地它是靠 TTL 耗尽被丢弃的。也就是说流量不是走得慢而是根本没走通只是这个过程消耗了大量转发资源。三个信号叠加出现时基本可以往环路方向想。第一是设备 CPU 利用率异常升高因为每一个环回的包都要重新做一次路由查表、TTL 递减、二层封装转发面和控制面一起被拖住CPU 自然下不来。第二是链路利用率被单条流打满而且是两个方向的接口同时飙高你去看接口计数器某个接口的 output 速率和对面设备的 output 速率几乎一样。第三是业务表现成时通时断而不是全断因为 TTL 起始值不同64、128、255有些小包可能因为某条明细路由短暂存在而侥幸到达大包和长连接就直接超时。这里还要提一下容易被混淆的二层环路。二层环路的表现是广播包数量爆炸、MAC 地址表频繁震荡、接口指示灯高频闪烁症状和三层的路由环路很像但根因完全不同——通常是 STP 没收敛、链路聚合配错或者有人把网线两头插在同一台交换机的两个口上。判断方法很简单看广播包占比和 MAC 表是否在几个口之间反复跳。三层环路不会引起 MAC 表震荡这是最直观的分界线。提示如果 CPU 高但流量不高、路由表也很稳定优先怀疑是不是路由协议在频繁重收敛flapping而不是环路。环路一定伴随异常流量。1.2 用 TTL 和路径记录把环画出来TTL 是排查环路时最便宜也最好用的工具。IP 头里的 TTL 初始值一般是 64、128 或 255每经过一跳减 1减到 0 时路由器丢弃报文并回一个 ICMP 超时报文。所以你在两端做一次路径跟踪如果中间出现同一组地址反复循环那就是环路实锤了。# 华为设备 tracert -a 192.168.1.100 10.10.10.10 # 思科设备 traceroute 10.10.10.10 source 192.168.1.100 # 带路径记录的 ping可以看到去程完整路径 ping -R 10.10.10.10但这里有三个坑必须提前知道否则很容易误判。第一tracert 只能看到去程如果环路发生在回程你在源端看结果会一切正常必须两个方向都做一次或者直接在两端设备上做带源地址的 traceroute。第二有些设备默认禁 ICMP 回应tracert 结果全是星号这不代表没有环路别把看不到当成没问题。第三某些设备会改写 TTLNAT 设备、部分安全设备、隧道封装改写之后 TTL 递减规律就被破坏了这时候要靠抓包看实际的源目 MAC 交替才能确认。我个人习惯是一旦怀疑环路先在源端和目标端各做一次 traceroute把两条路径的地址序列复制下来并排放在一起看。凡是出现 A→B→A→B 这种交替的直接锁定这两台设备去查它们的路由表。这个方法不用抓包五分钟内就能从怀疑变成确认。2. 静态路由互指与默认路由兜底最常见也最容易忽略的成因2.1 下一跳依赖目标可达这个死结静态路由互指是环路里最经典的一种。构造它只需要两条配置# R1 上 ip route-static 10.1.2.0 255.255.255.0 10.0.0.2 # R2 上 ip route-static 10.1.1.0 255.255.255.0 10.0.0.1单看每一台设备配置都是有道理的R1 认为去 10.1.2.0 网段应该走 R2R2 认为去 10.1.1.0 网段应该走 R1。问题出在当 10.1.2.0 这个网段实际上并不挂在 R2 后面或者 R2 那一侧已经摘掉了R2 收下包之后按自己的默认路由或者别的明细路由又把包送回 R1。两台设备的转发意愿叠加起来就形成了一个封闭的圆。更隐蔽的是递归查表失败引发的环路。如果写静态路由时只写了下一跳地址、没写出接口设备需要再查一次路由表来确定怎么到达这个下一跳。要是这条用于解析下一跳的路由本身指向对端或者被默认路由兜住就会出现A 为了到达 B 需要先到达 B的死结。解决办法不复杂静态路由明确写出接口加下一跳例如ip route-static 10.1.2.0 24 GigabitEthernet0/0/1 10.0.0.2这样设备不需要再做一次递归查表转发路径是确定的。我踩过的一个坑是早期为了配置简洁习惯只写下一跳不写出接口结果在一台有多条等价路径的设备上静态路由被解析到了错误的接口上包出去之后又被邻居送回来绕了一大圈才排到。从那之后我的规矩是——静态路由一律写出接口 下一跳除非确认对端是点到点链路。2.2 默认路由互指的隐蔽性内网正常只有外网不通比静态互指更难查的是两端都写了默认路由指向对方# R1 ip route-static 0.0.0.0 0.0.0.0 10.0.0.2 # R2 ip route-static 0.0.0.0 0.0.0.0 10.0.0.1这种配置最常见的来源是想做一条备用线路配的时候图省事两端各指对面想着反正有一条通就行。结果就是内网互访全正常因为有明细路由只有访问外网或者某些不在明细表里的网段才出问题。这时候很多人会去查运营商、查光猫、查 DNS方向完全跑偏。这种环路的排查切入点很特别看哪些流量受影响反推它落在了哪张表里。如果是访问外网不通但内网全通优先怀疑默认路由。在两台设备上各执行一次display ip routing-table 0.0.0.0看到的下一跳要是指向对方设备而对方设备又指回来环就成立。正确的做法是默认路由只能有一个明确出口。如果确实需要主备两条默认路由要用**不同的优先级preference**来区分而不是两端互指# R1 上两条默认路由主用优先级 60备用优先级 100 ip route-static 0.0.0.0 0.0.0.0 10.0.0.2 preference 60 ip route-static 0.0.0.0 0.0.0.0 10.0.0.6 preference 100注意优先级数值越小越优两条默认路由的优先级必须不同否则会形成等价负载分担一旦其中一条的下一跳不可达流量分配就会出问题。3. 动态路由重分发与路由汇总协议交互里长出来的环路3.1 双点双向重分发为什么几乎必然出问题静态路由的环是人写出来的动态协议的环则是协议之间传出来的。最典型的就是双点双向重分发两个不同的路由域通过两台边界设备互联每台边界设备都做了双向重分发。这种设计在现网里非常普遍比如一个 OSPF 域要和一个 BGP 域对接或者是两个不同 OSPF 进程之间互引。为什么这种结构容易出环举个具体过程。R1 和 R2 是两台边界设备左侧是协议 A右侧是协议 B。R1 把协议 A 的路由引入协议 BR2 也把协议 B 的路由引入协议 A。当 R1 把一条 A 域路由注入 B 之后这条路由会沿着 B 域传到 R2R2 因为在做双向重分发会把这条本来来自 A 域的路由再注回 A 域。于是 R1 又从 A 域里学到了它自己发出去的路由而且这条回注的路由度量值可能更优R1 就把去往该网段的流量指向了 R2而 R2 那边又指回 R1环形成了。这里的关键在于路由协议自带的防环机制管不了跨协议这件事。OSPF 内部靠链路状态数据库天然无环BGP 靠 AS-PATH 防环但一条路由从 OSPF 出去、经过 BGP、再回到 OSPF中间没有任何机制记录它已经出过一次门。所以跨域重分发必须靠人为打标记来防环。我在排查这类问题时习惯先看两件事一是两台边界设备上同一条外部路由的下一跳是否互指二是路由是否在短时间内反复出现又消失flapping。同时满足这两条基本就是双向重分发没打标记。3.2 汇总路由缺了 Null0 兜底黑洞和环路就在一线之间路由汇总本身不产生环路但汇总之后缺少兜底路由会带来两类问题一类是黑洞一类是环路。假设一台 ABR 上配置了abr-summary 10.1.0.0 255.255.0.0把这个大网段通告出去。如果这台 ABR 自己有完整的明细路由没问题但如果明细路由是从别处学来的、只存在一部分那么当对端设备把目的地址落在汇总范围内、却没有任何明细匹配的流量送过来时ABR 按汇总路由匹配上却发现没有下一跳可以转发只能丢弃。那环路是怎么来的如果这台 ABR 上还配了一条默认路由指向另一台设备那么这批汇总范围里找不到明细的流量就会顺着默认路由被送到另一台设备而那台设备如果又有一条指回来的路由比如它也做了重分发两个设备就会兜圈。黑洞和环路的分界线就在于汇总设备有没有一条明确的兜底丢弃路由# 汇总网段指向 NULL0明确丢弃 ip route-static 10.1.0.0 255.255.0.0 NULL0 # BGP 聚合时抑制明细 aggregate 10.1.0.0 255.255.0.0 detail-suppressed这条 NULL0 路由看起来没用因为它把流量丢了。但它丢得明确、干净、不消耗额外资源远好过让流量在两个设备之间来回绕。这一点很多人在配汇总时会漏掉以为汇总配完就万事大吉。3.3 协议自带的防环机制到底能挡住多少把各个协议的防环能力列清楚排查时心里就有底了协议主要防环机制能防住的场景防不住的场景RIP最大 15 跳、水平分割、毒性逆转、触发更新协议内部计数到无穷跨协议重分发、静态互指OSPF链路状态算法、区域间骨干规则、外部路由 tag区域内、区域间重分发未打 tag、汇总无兜底ISIS分层结构、ATT 位、SPF协议内部跨协议重分发BGPAS-PATH、ORIGIN、Next-Hop域间防环本地重分发、iBGP 全互联缺失看这张表就明白了一个结论协议内部的环基本不用担心真正的风险全部集中在协议与协议之间和人与设备之间。所以排查环路时优先级最高的动作不是去看协议状态而是去看重分发配置、静态路由和策略路由这三处。4. 多实例、NAT 与策略路由虚拟化和转换场景下的隐藏环路4.1 虚拟系统之间的路由泄露别用丢给对方实例当兜底现在很多防火墙和路由器都支持虚拟系统或者 VRF一台物理设备上跑多个独立的路由实例每个实例有自己的路由表和接口。这种设计本身很干净但实例之间的路由关系如果没理清楚环路比单实例更难查——因为你在一个实例里查路由表看到的永远只是半张图。典型的风险场景是这样的实例 A 处理某个业务网段实例 B 处理另一个。为了让 A 里访问不到的流量有个出口有人在 A 里写了一条默认路由指向 B同时 B 里为了兜底也写了一条默认路由指回 A。单看每个实例的配置都很合理合起来就是一个死循环。而且因为跨实例的转发会经过内部通道抓包都不一定抓得到排查难度直线上升。我的处理原则很明确每个实例的兜底路由必须是 NULL0绝对不能用丢给另一个实例来当兜底。跨实例的引流必须是有明确目的地的明细路由而不是一条包罗万象的默认路由。同时跨实例引流一定要保证回程路径存在否则就会退化成一去一回两个默认路由的循环。4.2 NAT 引流与回程路由不一致会话表是唯一真相NAT 场景下的环路很多人会卡很久因为路由表看起来完全正常。原因是 NAT 会改变报文的源地址或目的地址转发的两个方向查的是不同的路由条目。举个典型情况源 NAT 之后内部主机的地址被转换成出口地址。回程流量到达时目的地址已经是转换后的地址设备需要把会话还原再转发给内网主机。如果回程流量到达的是错误的实例或者错误的接口而那一侧的默认路由又指向另一台设备报文就会被送出去、再被送回来形成环路。整个过程在路由表上看不出任何异常但会话表里会有大量半开连接。排查这类问题时我建议直接看会话# 华为防火墙 display firewall session table verbose destination 10.10.10.10 display firewall server-map # 思科 ASA / 类似设备 show conn detail show nat detail会话表里重点看三件事转换前后的地址、入接口和出接口、会话的存活时间和包计数。如果某个会话的包计数在两个方向上持续增长但业务不通基本上就是回程路径错了。修正的方向是让回程流量在正确的接口和实例上被匹配到而不是简单地加一条默认路由——加默认路由正是很多环路事故的起点。4.3 策略路由绕开路由表让看表排查彻底失效策略路由PBR是排查环路时最容易被忽略的一环。它的优先级高于普通路由表也就是说即使路由表配得完全正确只要有一条策略路由命中了流量转发行为就由策略决定。这带来的直接后果是你盯着路由表看半天怎么都对不上号。常见的环路构造是这样的PBR 把某个源地址的流量强制引到下一跳 R2而 R2 上没有针对该源地址的回程路由于是 R2 按默认路由把回包送到 R3R3 又有一条路由指回 R1。整条路径绕了一圈但因为去程和回程查的是不同机制你在任何一台设备上单独看都看不出问题。两个实用建议。第一PBR 的匹配条件要尽可能精细用 ACL 精确匹配源、目的、端口避免命中范围过大。第二PBR 配置里最好有一条明确的兜底放行或者兜底丢弃让不匹配的流量老老实实走路由表而不是被默认动作送进某个不确定的方向。排查时务必在所有沿途设备上执行一次 PBR 相关的查看命令# 华为 display policy-based-route display acl all # 思科 show route-map show ip policy5. 排查链路从接口计数器逐跳定位到环路圈5.1 第一轮三个动作确认环路是否真实存在排查环路第一步不是查配置而是确认现象。我通常按固定顺序做三个动作三分钟出结果。第一看双向路径。从源端和目标端各做一次 traceroute把结果并排比对找重复出现的地址段。第二看接口计数器。在两台嫌疑设备的互联接口上执行接口查看命令重点看两个方向的速率是否同时异常高以及错误包、丢弃包的数量# 华为设备常用 display interface GigabitEthernet0/0/1 display interface brief | include up display cpu-usage display ip routing-table statistics # 思科设备常用 show interfaces GigabitEthernet0/1 | include rate|errors show processes cpu sorted show ip route summary第三看路由表规模有没有异常抖动。执行两次路由表统计间隔十秒对比条目数变化。如果条目数在短时间内反复加减说明有协议在重收敛这和环路常常是一对伴生现象环路导致协议报文丢失协议重收敛又加剧了路由变化。提示做接口计数的时候一定要用带速率显示的完整命令不要只看brief版本。brief只给 up/down 状态看不出流量异常。5.2 第二轮列出参与环路的设备清单确认环路存在之后下一步是把环路圈里的设备一台台找出来。做法是沿 traceroute 的结果逐跳走对路径上每一个地址对应的设备查它去往目标网段的下一跳看是否指向了路径上的上一台设备。具体操作上我会准备一张纸或者一个文本文件左边写下路径上出现的地址序列右边写下每台设备去往目标网段的路由来源静态 / OSPF / BGP / 直连。只要出现A 的下一跳是 BB 的下一跳是 A或者A→B→C→A的闭环环路圈就确定下来了。这一步有个技巧值得说不要只查目标网段的路由还要查默认路由。很多时候明细路由是对的问题出在某一台设备上明细路由缺失流量掉进了默认路由而默认路由恰好指向环路里的下一台设备。所以查路由时要同时看三样目标网段明细、默认路由、以及这条路由的来源协议。5.3 第三轮抓包验证把推断变成证据到这一步环路圈和嫌疑设备都清楚了但下结论之前我习惯再抓一次包因为改动之前留一份证据事后复盘和写变更报告都用得上。抓包时在嫌疑链路做端口镜像过滤目标地址重点看三个特征同一个目的地址的报文在两个方向上反复出现、TTL 值逐次递减 1、源目 MAC 在几个固定组合之间交替。这三个特征同时出现环路就是板上钉钉。下面这张对照表是我这些年排查时用得最多的可以照着症状快速缩小范围症状表现最可能的原因快速验证方式特定网段不通ping 提示 TTL 传输中过期静态路由互指、默认路由互指两端查该网段下一跳是否互指全网间歇抖动路由表频繁变化双点双向重分发未打标记查重分发配置和 route-tag部分网段不通路由表稳定但 CPU 偏高汇总缺 NULL0 兜底查 ABR 是否有汇总对应的丢弃路由路由表完全正常但特定业务不通策略路由或 NAT 回程路径错查 PBR 配置和会话表广播包暴涨、MAC 表震荡二层环路非路由环路查 STP 状态和链路聚合配置6. 收口与预防把环路掐在配置下发之前6.1 静态路由的四条书写规矩静态路由是环路的高发区因为它是人手写的没有协议帮你兜底。我给自己定了四条规矩这些年基本没再写过环路。第一能写明细就不写大网段除非明确知道需要一条汇总路由。第二静态路由一律写出接口加下一跳避免递归查表解析到意外的接口。第三两台设备的默认路由绝不能互指需要主备就用不同的优先级区分需要兜底就用 NULL0。第四任何静态路由变更都要先想清楚回程——去程怎么写回程怎么走两个方向都要在纸上画一遍再下发。第四条听起来啰嗦但它是性价比最高的一条。我见过太多环路事故配置本身没问题问题是只考虑了去程。网络是双向的只规划单向的路由方案等同于埋雷。6.2 重分发必须配齐的三道护栏跨协议重分发是环路的重灾区但只要配齐三道护栏风险能压到很低。第一道优先单向重分发。如果业务允许只从一个协议域向另一个注入路由不要双向。单向注入天然不存在回注问题这一条能省掉后面所有的麻烦。第二道必须双向时统一打 tag。在引入时给路由打上标记在接收侧通过策略拒绝带标记的路由# OSPF 引入外部路由时打 tag ospf 1 import-route bgp tag 200 # 接收侧拒绝带 tag 200 的路由 route-policy DENY-TAG deny node 10 if-match tag 200 route-policy DENY-TAG permit node 20第三道用前缀列表精确控制注入范围。只放行本域真实存在的网段其他一律拒绝。很多重分发环路的根源就是引入时图省事用了 no-advertise 全放结果把邻居域的路由也带进来了。三道护栏配齐之后重分发基本不会再自己长出环路。6.3 汇总和黑洞路由必须成对出现路由汇总的规范很简单凡是有汇总的地方汇总设备上必须有一条指向 NULL0 的丢弃路由。这不是可选项是必选项。除了 NULL0还有两个细节值得注意。一是汇总范围要精确不要用超网图省事比如把两个不连续的网段硬汇总成一个大网段会引入大量无效流量。二是保留关键明细如果某个明细网段有特殊的下一跳需求比如走专线要在汇总的同时把它单独附加上否则会被汇总路由淹没。BGP 场景下还要注意聚合的写法差异aggregate默认会同时发布明细和汇总如果希望只发汇总需要加上抑制明细的参数。这一点在跨域对接时特别重要因为对端往往只想要一条汇总路由。6.4 多实例环境的验证清单虚拟系统、VRF 这类多实例环境建议每次变更后跑一遍固定清单每个实例的默认路由是否指向 NULL0而不是指向别的实例跨实例的引流路由是否写了明确的目的地址两个方向的 traceroute 是否都能走通会话表里有没有长期半开的连接。这张清单看起来只有四条但每一条都对应过真实事故。尤其是第一条我见过太多人习惯性地把默认路由丢给主实例觉得主实例有出口能兜住结果主实例的回程路由又指回来两个实例之间的内部通道被打满整机性能断崖式下跌。7. 在模拟器里复现一次双点双向重分发环路7.1 拓扑搭起来两台边界设备、两个协议域想真正理解环路光看文字不够最好自己复现一次。用模拟器搭一个最小拓扑就行四台设备R1 和 R2 作为双边界左侧跑 OSPF 进程 1右侧跑 OSPF 进程 2用两个进程模拟两个域比搭 ISIS 更省事。核心配置就是两台边界设备各自做双向重分发# R1 ospf 1 import-route ospf 2 ospf 2 import-route ospf 1 # R2 ospf 1 import-route ospf 2 ospf 2 import-route ospf 1搭好之后在任意一台设备上看路由表你会看到右侧网段的路由同时从两个方向学过来而且下一跳互相指向对方。这时候从左侧终端去 ping 右侧一个不存在的地址比如右侧网段里的某个空缺地址就能看到环路现象。7.2 观察现象用三条命令锁定环路复现出来之后别急着改配置先把现象记录下来。三条命令足够# 1. 看路由表里目标网段的下一跳是否互指 display ip routing-table 10.2.2.0 # 2. 看 CPU 和接口速率是否异常 display cpu-usage display interface GigabitEthernet0/0/1 # 3. 做一次 tracert看地址是否循环出现 tracert -a 10.1.1.1 10.2.2.99我第一次做这个实验的时候tracert 输出里 R1、R2 的接口地址连续出现了六次那个画面比任何文字描述都直观。同时打开接口计数器会看到两个方向的速率几乎完全对称——这就是环路的指纹。7.3 修复与回归先打标记再验证修复方式就是前面说的第二道护栏给引入的路由打 tag在接收侧拒绝带标记的路由。改完之后一定要做回归验证重点看三件事目标网段的路由下一跳是否唯一且正确、tracert 路径是否恢复正常、CPU 是否回落到正常水平。我个人的习惯是在模拟器里把这个实验反复做过几次然后故意造一些变种把双向改单向、把 tag 去掉、加一条默认路由互指。每造一个变种就按第 5 节的排查链路走一遍。折腾两三个晚上之后现网再遇到类似问题基本看两眼路由表就能判断方向。模拟器最大的价值就在这里——它允许你把错误犯够而且不花一分钱。最后分享一个小技巧给所有做重分发的边界设备统一加上一句注释写明引入路由的 tag 值和拒绝策略的名称。这个动作看起来无关紧要但当你半年后接手一台别人配的设备或者自己半夜被叫起来排查时这两行注释能省下大量翻配置的时间。环路排查最耗时间的从来不是解决问题而是搞清楚当初为什么这么配。