← 全部文章

逐跳的真相

您的路径工具画的是一条走廊。路由器却是逐跳转发的。这是对这道鸿沟做的 4,032 对测量。

Michel Wijnberg

我用过的几乎每一款网络路径工具(包括不久之前的我自己那款)都是这么干的:

  1. 取源路由器。
  2. 在拓扑上跑 Dijkstra。
  3. 把算出来的最短路径画出来。

那是一条以源为根的走廊,而它是一个微妙地错误的 IP 转发模型。真实的转发里没有走 廊。每一台路由器都独立地用目的地址去匹配自己那张路由表,然后挑一个下一跳。只 有当每一台中间路由器的决策规则和输入集合都与源相同时,这两个答案才会一致。

在 OSPF 里,它们经常不一致。


一个永远到不了的报文

这是我实验室里的一个真实案例,在每一台涉及的路由器上用 show ip route 实测得来,是从 设备上读出来的,不是建模算出来的:

路由器对该目的地的说法转发给
dfw1-gw2metric 20031, type inter areadfw1-cr1
dfw1-cr1metric 30001, type intra areaord1-cr1不是 dfw2-cr1
ord1-cr1metric 20001, type intra areajfk1-cr1
jfk1-cr1metric 10001, type intra arealhr1-cr1
lhr1-cr1metric 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

不是”接近”。是每一对都完全一致,而且包含完整的等价下一跳集合,而不是随便挑出来的一个成 员。

在画布上看起来是这样:正向路径橙色,反向蓝色,不在路径上的一切都暗下去,衬出一条走廊:

一条画在拓扑上的 OSPF 路径,带正反向叠加层和四条等价路径
一条画在拓扑上的 OSPF 路径,带正反向叠加层和四条等价路径

注意 4 equal-cost paths 选择器和 per-flow hash 标签。Osprey 会把完整的 ECMP 集合枚举 出来,而不是挑一个成员当作那条路径,因为您的流量是按哈希分散在这四条上的,而一次只检 查其中一条的排障过程,四次里有三次会什么问题都查不出来。

同一条路径展开之后是这样:

一条 10 跳的 OSPF 路径,带逐跳表格、ABR 徽标、入向/出向端口和编号解释
一条 10 跳的 OSPF 路径,带逐跳表格、ABR 徽标、入向/出向端口和编号解释

每一跳都带着做决定的那台路由器、它是在哪个区域里做的决定、进出的物理接口及其地址,还有开 销。ABR 徽标是从 LSDB 推导出来的,不是从主机名。下面那条区域色带显示路径穿过 12.1.24.00.0.0.06.18.1.0,这正是 RFC 2328 对跨区域流量所要求的骨干区中转。

而显示出来的总开销 30061,是源自身安装的度量值。它就是 show ip routelax1-gw1 上打印出来的那个数字,含 stub 开销在内,而不是 Osprey 把自己中意的链路加起来算出的和。

那段解释是生成的,不是装饰:

  1. 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)
  2. Path traverses: area 12.1.24.0 (cost 10) → area 0.0.0.0 (cost 30040) → area 6.18.1.0 (cost 10)
  3. Total cost: 30061 via 10 hops
  4. ECMP: 4 equal-cost paths available

跨越 AS 边界

在单一 IGP 内部,这条链是可计算的,因为 LSDB 是完整的。一旦跨过 AS 边界,什么都不完整了。 没有共享的数据库,没有共享的度量空间,也没有共享的管理距离策略。

诚实的做法不是放弃,更绝不是假装这些度量值可以相加。诚实的做法是拼接证据

一条从 AS 200 进入 AS 300 的 15 跳路径,带分段开销和一次 eBGP 转换
一条从 AS 200 进入 AS 300 的 15 跳路径,带分段开销和一次 eBGP 转换

从 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 拒绝把它打印出来。

生成的解释在每一步都点名自己的证据:

  1. BGP border selection: jfk1-gw1 holds 10.66.0.0/15 for 10.66.15.3 (AS path: 300)
  2. AS 200: IGP segment: lax1-gw1jfk1-gw1, cost 20051
  3. eBGP transition 200 → 300: jfk1-gw1 Et1/1 → i-jfk1-gw1 Et1/2 (bmp-peer+l2; ip-bound)
  4. AS 300: IGP segment: i-jfk1-gw1i-sin1-gw1, cost 1320

第 3 步是我在这个产品里最喜欢的一行。它没有说”这两台路由器大概以某种方式连着”。它点出了 两端的物理端口,然后在那个括号里把自己的推导过程摊开:

  • bmp-peer:两者之间有一条已建立的 eBGP 会话,通过 BMP 看到
  • +l2:远端是通过两台边界设备的 LLDP/CDP 邻接匹配上的
  • ip-bound:两个会话地址都通过设备的 IP-MIB 绑定解析到了那两个具体端口

三份互相独立的证据彼此吻合。当它们不吻合时,产品就会照实说出来,而那正是 下一篇文章的全部主题。


为什么我更在乎这件事,而不是功能清单

一张路径图是一个断言:断言您的网络会拿一个报文怎么办。如果这个断言八次里错一次,而屏幕上 没有任何东西能把错的那次和对的那七次区分开,那么这张图就比没用还糟。它是自信满满地没 用,而且恰恰会在”搞错了代价很高”的那一刻被人相信。

从”看起来说得通”走到”精确复现全部 64 张路由表,4,032 比 4,032”,靠的是一套验证工装、一个 我能从中读到基准真相的实验室,以及愿意发现自己的工具已经画了 360 次虚构路径的心态。

实验室就是干这个用的。


下一篇是同一枚硬币的另一面:Osprey 算不出答案、并且照实说出来的那些地方: Osprey 拒绝猜测的东西