摘要

  • RFC 1241 把未改动的 “Clear Datagram” 放入外层 IP 报头和 8 字节封装协议报头,在独立的 Encapsulation Space 中转送。
  • 32 位 Flow ID 只在一台封装器或解封器内有效;路径映射及其维护由未定义的上层机制负责。
  • 隧道内部的 ICMP 错误只能引用外层 IP 报头与完整的封装协议报头,无法带回 Clear Datagram;故障证据因而可能失去原始原因。

两个地址空间,也有两套事实

1991 年 7 月,Robert A. Woodburn 与 David L. Mills 发布实验性协议 RFC 1241。文件把原始 IP 数据报称为 Clear Datagram,把它所在的普通网络称为 User Space;封装器与解封器使用的地址和路由则属于另一个 Encapsulation Space。

设计目标颇有吸引力。遇到路由故障、失效网关或不愿穿越的域时,封装可以把数据报送往一个本来不会由内层目的地址选中的中间节点。源主机不必掌握中间拓扑,也不必理解试验中的路由算法。解封后,原始数据报仍以原来的源地址继续转发。

但“源端无需知道”不等于系统无需知道。选择哪些 Clear Header 进入哪条路径,需要封装器维护映射;若一条路径由多组封装器与解封器接力,每个节点都要理解下一段和上一段。RFC 1241 把这条路径叫作 Flow,也称 tunnel。

Flow ID 有 32 位,却不是全球标识。同一条 Flow 在相邻节点上可以拥有不同编号。错误要沿原路返回,当前节点就必须保存前一封装器的地址,以及那台封装器眼中的 Flow ID;每退一跳,再翻译一次。

文件没有规定谁来建立、刷新或核对这些表,只把职责交给“上层实体”,并说试验阶段甚至可以使用 ASCII 文件。于是,一个 Flow ID 命中本地表,只能证明本地存在一条记录。它不能证明记录仍然新鲜、下一跳采用相同意图,也不能证明反向错误链仍完整。

包装没有改内层字节,却改了数据报的命运

封装报头由新的外层 IP 报头和 8 字节协议报头组成。后者保存版本、消息类型、原因码、校验和与 Flow ID。Clear Datagram 跟在后面,内层字节本身不变。

不过,网络处理的是变大的外层数据报。外层源地址变成封装器,目的地址变成解封器;优先级与服务质量位可复制,时间戳、记录路由和源路由选项不复制。内层 TTL 不搬到外层,但在封装前仍要递减;若解封器利用 Flow ID 绕过普通 IP 转发,它也必须接手下一次封装前的 TTL 处理。

最直接的代价是 MTU。源接口能够容纳的 Clear Datagram,加上新报头之后可能超过下一段链路的上限。RFC 1241 允许外层 IP 分片,同时承认分片低效;如果封装器禁止分片,封装空间的有效 MTU就比物理 MTU 小,原始源端还必须获得有意义的反馈。

分类也会被分片拆开。映射表可以查看 TCP 端口,甚至区分一条连接。可是 IP 数据报若已在 User Space 被分片,只有第一片带 TCP 报头,后续分片只剩 IP 字段。一条依赖端口的规则可能只认出第一片。RFC 1241 没有声称这必然导致某次事故;它揭示的是:完整报头上的精确规则,到了碎片上已不再精确。

RFC 1191 当时已经用 DF 位和 “fragmentation needed” ICMP 建立 Path MTU Discovery。隧道改变了反馈对象。内部路由器看到的外层源地址是封装器,错误自然回到封装器,而不是 Clear Datagram 的原始源端。

ICMP 带回了信封,没有带回信件

RFC 792 要求错误消息带回出错的 IP 报头以及其后至少 64 位数据。对 RFC 1241 而言,这 64 位恰好就是完整的 8 字节封装协议报头。引文到此为止,Clear Datagram 的内容“完全丢失”。

封装器能看到出错的外层目的地址和 Flow ID,却看不到原始内层 IP 源地址,更看不到 TCP 报头。一个 Flow 又可能对应多种 Clear Header,因此无法从 Flow ID 精确反推原始报头。正向的透明性靠隐藏隧道实现;反向的不可见性也由同一次隐藏造成。

RFC 1241 设计了两类内部错误。未知 Flow ID 可报告给前一封装器和表管理实体;一般 ICMP 可包装后沿 Flow 逆行,并逐跳改写为前一节点使用的本地编号。但搬运 ICMP 对象不能补回已丢的内层字节。若“发送错误”本身再次失败,协议不再产生新错误。

文件甚至提出一种折中:内部 ICMP 先给 Flow 打标,等下一份匹配数据报到来时,再由封装器向原始源端生成一个新错误。这样,后一包替前一包触发告警。它反映的是路径状态,不是前一包的逐包回执;文件也只认为这种做法对部分错误类型“可能安全”。

后来的文件没有让证据缺口自动消失

后续 RFC 不能证明 RFC 1241 曾大规模部署,也不能把所有隧道写成一条直系谱系。不过,RFC 1853 明确区分了简单 IP-in-IP 与 RFC 1241 的额外协议报头,并承认其篇章结构和部分文字受 RFC 1241 影响。

标准轨文件 RFC 2003 再次指出:ICMP 引用的 8 字节不足以容纳内层 IP 报头。它要求封装器维护隧道 MTU、TTL 与可达性的软状态,再用后续数据报与这些状态比对。这提高了生成有用错误的机会,却仍不把每个外层错误变成某个内层数据报的一一证明。

RFC 4459 后来把网内隧道的报文尺寸称为常见且并不简单的问题:分片外层、通知源端缩小、预留足够 MTU,或先分片内层,每种办法都把成本与责任移到不同位置。

RFC 1241 的历史价值正在于它没有把“透明”写成“无需记录”。能够证明的,是封装格式、本地编号、逐跳翻译和短 ICMP 引文造成的上下文丢失。不能仅凭规范证明的,是某个实现被部署、某个内层包抵达,或某个源端收到了等价错误。

因此,入口处的 Clear Header、外层报头、本地 Flow ID、表版本和时间必须绑定保存;出口处要另行记录解封结果;ICMP 也应保留实际引用的字节。只有这些证据能够关联时,才能谈内层结果。隧道送达了外层包,不代表它也把原问题带回了家。

资料来源