逐跳的真相
您的路径工具画的是一条走廊。路由器却是逐跳转发的。这是对这道鸿沟做的 4,032 对测量。
Michel Wijnberg
我用过的几乎每一款网络路径工具(包括不久之前的我自己那款)都是这么干的:
- 取源路由器。
- 在拓扑上跑 Dijkstra。
- 把算出来的最短路径画出来。
那是一条以源为根的走廊,而它是一个微妙地错误的 IP 转发模型。真实的转发里没有走 廊。每一台路由器都独立地用目的地址去匹配自己那张路由表,然后挑一个下一跳。只 有当每一台中间路由器的决策规则和输入集合都与源相同时,这两个答案才会一致。
在 OSPF 里,它们经常不一致。
一个永远到不了的报文
这是我实验室里的一个真实案例,在每一台涉及的路由器上用 show ip route 实测得来,是从
设备上读出来的,不是建模算出来的:
| 路由器 | 对该目的地的说法 | 转发给 |
|---|---|---|
dfw1-gw2 | metric 20031, type inter area | dfw1-cr1 |
dfw1-cr1 | metric 30001, type intra area | ord1-cr1 ← 不是 dfw2-cr1 |
ord1-cr1 | metric 20001, type intra area | jfk1-cr1 |
jfk1-cr1 | metric 10001, type intra area | lhr1-cr1 |
lhr1-cr1 | metric 1, connected | 送达 |
以源为根的走廊画出来的是这个:
dfw1-gw2 → dfw1-cr1 → dfw2-cr1 → iad2-cr1 → ams1-cr1 → lhr1-cr1
报文根本不会到 dfw2-cr1。这条走廊确实更便宜。它只是没有任何一台路由器会真的构建出
来。有两条互相独立的 RFC 2328 规则让 dfw1-cr1 拒绝了它:
§16 步骤 (3) / §16.2 第 1 段:骨干区闸门。连接到多个区域的路由器只检查来自骨干
区的 summary-LSA。dfw2-cr1 那条开销为 20011 的更便宜的 summary 确实存在于 dfw1-cr1
的 4.6.23.0 区域数据库里(我用 show ip ospf database summary 确认过),但它不是候选
项,因为 dfw1-cr1 是 ABR,而那条 summary 不是从区域 0 进来的。RFC 3509 §2.1/§2.2
(这些设备上实际运行的 Cisco ABR 行为)把这一条件挂在”有一条活跃的骨干连接”之上,而
dfw1-cr1 正好有。
§16.2 步骤 (6):区域内胜过区域间,没有例外。“If the paths present in the table are
intra-area paths, do nothing with the LSA (intra-area paths are always preferred).”
dfw1-cr1 手里已经有一条度量值为 30001 的区域内路径。那条 20011 的 summary 输给了它。
度量值在这里毫不相干:这是一条关于路径类型的规则,开销比较根本不会发生。
这两条规则中的任意一条单独就足以杀死那条走廊。一个只从源跑一次 SPF 的工具两条都看不
见,因为它从来没问过 dfw1-cr1 自己是怎么想的。
这件事到底多常发生?
这才是我在意的部分,因为”你的模型在理论上不够精确”是一个比数字弱得多的说法。
所以我把 AS 200 里全部 64 台路由器的完整 OSPF 路由表都读了出来,逐一比对了 4,032 个有序的源/目的对:
| 测量项 | 结果 |
|---|---|
| 走廊画出了 ≥1 个没有任何路由器会走的跳的跨区域对 | 2,839 中的 360(12.68%) |
| 有同样缺陷的区域内对 | 1,193 中的 0 |
| 总体 | 4,032 中的 360(8.93%) |
区域内为零是那个理智检查。在同一个区域内,每台路由器都在一份完全相同的 LSDB 上跑 SPF,而一条最短路径从它上面的任何一点看出去仍然是最短的,所以这两个模型完全有理由重 合。关键在于这个重合是被测出来的,而不是被假定的:1,193 对,零分歧。而只要 ABR 和 summary-LSA 一进场,八条路径里就有一条是虚构的。
不是”略微次优”。是虚构,里面有一台报文可被证明从未经过的路由器。如果您拿这条路径去安排 维护窗口、或者去解释一次故障,那您推理的是一条并不存在的路由。
修法:挨个问路由器
Osprey 现在会走完整条链。在每一跳,它都用那台路由器自己的 LSDB、自己的区域归属、自己的 ABR 身份和自己的管理距离组合,去评估那台路由器自己安装的、指向该目的地址的路由,然 后沿着它真正选出的下一跳走下去,如此往复。
拿同样这 4,032 对做验证:
| 检查项 | 结果 |
|---|---|
| 路由器自己的度量值被精确复现 | 4,032 / 4,032 |
| 路径类型(区域内 / 区域间 / 外部)完全一致 | 4,032 / 4,032 |
| 已安装的完整 ECMP 下一跳集合完全一致 | 4,032 / 4,032 |
链在请求的目的地以 delivered 终止 | 4,032 / 4,032 |
不是”接近”。是每一对都完全一致,而且包含完整的等价下一跳集合,而不是随便挑出来的一个成 员。
在画布上看起来是这样:正向路径橙色,反向蓝色,不在路径上的一切都暗下去,衬出一条走廊:
注意 4 equal-cost paths 选择器和 per-flow hash 标签。Osprey 会把完整的 ECMP 集合枚举
出来,而不是挑一个成员当作那条路径,因为您的流量是按哈希分散在这四条上的,而一次只检
查其中一条的排障过程,四次里有三次会什么问题都查不出来。
同一条路径展开之后是这样:
每一跳都带着做决定的那台路由器、它是在哪个区域里做的决定、进出的物理接口及其地址,还有开
销。ABR 徽标是从 LSDB 推导出来的,不是从主机名。下面那条区域色带显示路径穿过
12.1.24.0 → 0.0.0.0 → 6.18.1.0,这正是 RFC 2328 对跨区域流量所要求的骨干区中转。
而显示出来的总开销 30061,是源自身安装的度量值。它就是 show ip route 在 lax1-gw1
上打印出来的那个数字,含 stub 开销在内,而不是 Osprey 把自己中意的链路加起来算出的和。
那段解释是生成的,不是装饰:
- Route type: O IA (inter-area): Source 172.16.2.33 and destination 172.16.5.23 have no shared area, routing via backbone (area 0.0.0.0)
- Path traverses: area 12.1.24.0 (cost 10) → area 0.0.0.0 (cost 30040) → area 6.18.1.0 (cost 10)
- Total cost: 30061 via 10 hops
- ECMP: 4 equal-cost paths available
跨越 AS 边界
在单一 IGP 内部,这条链是可计算的,因为 LSDB 是完整的。一旦跨过 AS 边界,什么都不完整了。 没有共享的数据库,没有共享的度量空间,也没有共享的管理距离策略。
诚实的做法不是放弃,更绝不是假装这些度量值可以相加。诚实的做法是拼接证据。
从 AS 200(租户 Harrier-Broadband,OSPF)的 lax1-gw1 到 AS 300(租户 Merlin-Carrier,
IS-IS)的 i-sin1-gw1,共十五跳。看它做了什么:
- 第 8 跳
jfk1-gw1带着BGP徽标:这里是 IGP 结束、BGP 决策接手的地方。 - 第 9 跳
i-jfk1-gw1带着IS-IS徽标:完全不同的协议、不同的地址规划、不同的租户。 - 页脚写着 “Per-segment costs (never summed) | 15 hops”。20051 的 OSPF 开销和 1320 的
IS-IS 开销是两个毫无关联的度量空间里的数字。把它们相加会得到
21371,而那不是一个存在 的量。Osprey 拒绝把它打印出来。
生成的解释在每一步都点名自己的证据:
- BGP border selection:
jfk1-gw1holds 10.66.0.0/15 for 10.66.15.3 (AS path: 300)- AS 200: IGP segment:
lax1-gw1→jfk1-gw1, cost 20051- eBGP transition 200 → 300:
jfk1-gw1 Et1/1 → i-jfk1-gw1 Et1/2(bmp-peer+l2; ip-bound)- AS 300: IGP segment:
i-jfk1-gw1→i-sin1-gw1, cost 1320
第 3 步是我在这个产品里最喜欢的一行。它没有说”这两台路由器大概以某种方式连着”。它点出了 两端的物理端口,然后在那个括号里把自己的推导过程摊开:
bmp-peer:两者之间有一条已建立的 eBGP 会话,通过 BMP 看到+l2:远端是通过两台边界设备的 LLDP/CDP 邻接匹配上的ip-bound:两个会话地址都通过设备的 IP-MIB 绑定解析到了那两个具体端口
三份互相独立的证据彼此吻合。当它们不吻合时,产品就会照实说出来,而那正是 下一篇文章的全部主题。
为什么我更在乎这件事,而不是功能清单
一张路径图是一个断言:断言您的网络会拿一个报文怎么办。如果这个断言八次里错一次,而屏幕上 没有任何东西能把错的那次和对的那七次区分开,那么这张图就比没用还糟。它是自信满满地没 用,而且恰恰会在”搞错了代价很高”的那一刻被人相信。
从”看起来说得通”走到”精确复现全部 64 张路由表,4,032 比 4,032”,靠的是一套验证工装、一个 我能从中读到基准真相的实验室,以及愿意发现自己的工具已经画了 360 次虚构路径的心态。
实验室就是干这个用的。
下一篇是同一枚硬币的另一面:Osprey 算不出答案、并且照实说出来的那些地方: Osprey 拒绝猜测的东西。