当网络自己跟自己不一致
六个真相来源,一个答案,以及当它们互相矛盾时该怎么办
Michel Wijnberg
Osprey 从六个地方了解一张网络:
| 来源 | 告诉您什么 |
|---|---|
| OSPF / IS-IS LSDB | 每个协议认为拓扑是什么样 |
| SNMP 路由表 | 一台路由器实际装了什么 |
| BMP Adj-RIB-In | 每个 BGP 对等体向这台路由器通告了什么,以及来自谁 |
| LLDP / CDP | 物理上什么和什么之间有线 |
| IP-MIB | 哪个地址住在哪个端口上 |
| BGP 会话表 | 谁和谁建了对等关系 |
它们每一个都在某件事上是权威。没有一个在所有事情上都是权威。而且它们并不总是一致。
天真的做法是全局挑一个赢家(“拓扑信 LLDP,BGP 信 BMP”),然后继续往前走。这在遇到第一 张 LLDP 在链路一端已经过期的网络时就会失败,或者在两台自以为直连的路由器之间夹着一个桥 的时候失败。
我最后落到的答案更无聊,但管用:按断言给证据排序,并记录每个断言来自哪里。
端口:四种情形,四种不同的答案
这个问题最尖锐的版本,是给一次 eBGP 转换标注物理端口。两台位于不同自治系统的路由器之间 有一条会话。它跨的是哪根线?
Osprey 区分四种情形,并给每一种一种不同类型的答案:
**一对链路,没有 fabric 替代路径。**端口作为事实陈述:
jfk1-gw1 Et1/1 → i-jfk1-gw1 Et1/2
**并行链路。**具体是哪一对无法确定,所以它不去断言任何一对:
3 parallel L2 links: Gi2↔Et1/0, … (session link undetermined)
两端边界都在一个共享桥上,没有直连链路。它报告桥的存在以及接入端口,也就是”存在 一条穿过它的通路”这个陈述,绝不断言这条会话就走了那里:
shared L2 fabric: ix-sw (wn-edge Et0/3 ↔ ix-sw Et0/1; ix-sw Et0/2 ↔ nn-edge Et0/3) (session path unconfirmed)
**既有直连链路又有共享 fabric。**明确点名的歧义,两个都不选。
第四种情形才是让这套设计值回票价的地方。它在交换中心确实常见,仅凭二层信息确实无法判定, 而且恰恰是一个想显得自信的工具会挑那条直连链路、然后悄悄搞错的地方。
把猜测变成事实的那一阶
有一条出路,而且它不是启发式。如果两个 BGP 会话地址都能通过设备的 IP-MIB 绑定解析到 具体端口,那么这些端口就不再是推断出来的。是路由器自己告诉了您哪个接口拥有那个地址。这份 证据压过那些歧义闸门:
eBGP transition 200 → 300 jfk1-gw1 Et1/1 → i-jfk1-gw1 Et1/2 (bmp-peer+l2; ip-bound)
请把那个括号读作一条证据保管链:
bmp-peer:两者之间有一条已建立的 eBGP 会话,通过 BMP 看到+l2:远端是通过两台边界设备的 LLDP/CDP 邻接匹配上的ip-bound:两个会话地址都经由 IP-MIB 解析到了那两个确切端口
三个互相独立的来源彼此吻合。一个断言的强度取决于它所依赖的最弱的那一环,而这个记号把那一 环是什么说了出来。
新鲜度也是证据的一部分
二层数据会过期,而过期的二层数据比没有更糟,因为它看起来和新鲜的二层数据一模一样。所以支撑 用的那些记录带着规则:
- 记录必须比 13 小时更新,才有资格支撑一个端口断言
- 超过 6 小时会披露其年龄
- 仅凭 sysname 匹配上的桥(而不是解析出的设备身份)会否决一个端口断言,同时自己永远 不会作为事实被展示出来
最后这一条很微妙,我很喜欢:弱证据被允许摧毁信心,但不被允许制造信心。一个只识别了 一半的桥,足以成为停止断言某个直连端口对的理由,却远远不足以成为断言那个桥的理由。
溯源:同一张表,两种不同的血统
在第二篇文章里我展示过 BGP 对等体表:57 个对等体上的 378 条会话。 那张截图没有展示的是,它们并非都是同一条路来的:
| 来源 | 会话数 |
|---|---|
| BMP:路由器推送自己的 Adj-RIB-In(RFC 7854) | 24 |
| SNMP:轮询时刻对 BGP4-MIB 做一次遍历 | 354 |
两者都是真的。它们并不一样好。BMP 实时地带来对等体通告过的每一条路径,还有路由器视角下的 Peer Up 和 Peer Down。SNMP 遍历给您的是上一次轮询那一刻的会话表,中间发生了什么则一无所知。
值得把是哪一份 RIB 说清楚,因为它决定了您可以断言什么。RFC 7854 监控的是 Adj-RIB-In: 从每个对等体那里到达的东西,在这台路由器做出任何选择之前。路由器自己选出的那张表,也就是 Loc-RIB,属于后来另立的扩展(RFC 9069),而这里的导出器并不发送它。所以这个实验室可以用路 由器的权威说”对等体 X 向我提供了这条路径”,而”这是路由器装上的那条路径”则是 Osprey 在那些 候选之上自己算出来的。这是两个不同的断言,也被分别标注。
因此每一行都用一个 source 列记录自己属于哪一种,而这个值是承重的,不是装饰。当 Osprey 拼
接一条跨 AS 路径、需要一跳跨域转发时,一条 eBGP 会话是带着自己的出处被计为证据的:
bmp-peer 和 snmp-peer 是阶梯上不同的两阶,而路径标注会说明它当时站在哪一阶上。
路径引擎里还埋着一条我很喜欢的相关规则。要把一条 BGP 路由分类为内部还是外部,显而易见的启
发式是去看 AS_PATH:只有一个元素的路径意味着 iBGP。Osprey 不会先这么干。它会把这条路由与会
话表里真实的对等体类型关联起来(实时来自 bgp_peer,历史时刻来自 bgp_peer_history),只
有在解析不出源对等体时才退回到 AS_PATH 启发式。那个启发式几乎总是对的。而”几乎总是”正是整个
系列所讲的那种失效模式。
什么都对不上的时候:就说出来
有时候来源不只是彼此矛盾,而是干脆用尽了。跨域路径拼接会用一条严格的阶梯给每一段评级:
| 阶 | 证据 | 置信度 |
|---|---|---|
| a | 某个边界上有一条覆盖该目的地的 RIB 记录 | resolved |
| a2 | 某个 route server 自己的 BMP RIB 经由下一个 AS 覆盖了它 | inferred(bmp-rib-rs) |
| b | 朝向下一个 AS 有一条已建立的 eBGP 会话 | inferred |
| c | 什么都没有 | opaque:一个带注记的段,一跳都不画 |
而且评级是按段给的,不是按整条路径,因为一条路径可能在这个 AS 里是确定的、到了下一个 AS 就成了猜测。这也正是上面那张跨域截图会在同一个答案的两半上带着两个不同徽标的原因。
第 (a2) 阶的存在有一个值得点名的具体理由:透明 route server 从来不会出现在 AS_PATH 里。不去读 route server 自己的 RIB,就根本没有办法看到一次经由 IX 的绕行。那条路径看起来会像直连,而它并 不是。真实互联网拓扑里有一整类结构,除非您专门去找那个把它藏起来的东西,否则它是不可见的。
那条规则
当两个来源彼此矛盾时,您有三种做法:
- **挑一个。**快,看起来果断,会在不知道什么时候悄悄出错。
- **取平均。**得到一个不描述任何真实存在之物的数字。
- 给它们排序,并说明您用了哪一个。
Osprey 处处都用第三种,而这也是我更信任它的输出、而不是我自己对上周二那张网络长什么样的记忆 的原因。
代价是界面上要比竞争对手多背一些词:resolved、inferred、opaque、ip-bound、
session link undetermined、bmp-peer 与 snmp-peer 之别。这里的每一个,都是一个更漂亮的产
品本可以画上一条干净直线的地方。
一条干净的线是一个断言。如果您撑不起它,就别画它。
下一篇,是这套哲学反噬其作者的那一篇:我发现自己的工具一直在画虚构路径的那一天: 我的拓扑骗了我的那一天。