摘要

  • RFC 894 规定 IPv4 的 EtherType 为十六进制 0800;若以太网数据字段不足 46 字节,发送端以零补足。这些字节会出现在链路上,却不属于 IP 包,也不计入 IPv4 的 Total Length。
  • 接收端因此必须同时尊重两种长度:用链路实际交付的大小准备缓冲区,用 Total Length 决定 IP 解析边界。RFC 6274 后来明确要求链路载荷不得短于 IP 声称的长度,同时承认更长的载荷可能来自合法填充,也可能来自恶意输入。
  • “不属于 IP”不等于“没有风险”。CERT/CC 曾记录驱动程序用旧缓冲区内容作填充而泄露内存;RFC 894 原文中的“minimum 1500”错误也说明,原始记录与经核验的更正必须并存,不能互相冒充。

0800 先决定怎样读

1984 年 4 月发布的 RFC 894 给 10 Mb/s、48 位地址的以太网规定了一种标准 IP 封装。帧头的 Type 字段必须写十六进制 0800,数据字段紧接着放置 IP 头部和 IP 数据。

这两个字节承担的是“选择语法”的工作。十六进制 0800 等于十进制 2048,但它绝不是“后面有 2048 字节”的声明。接收者看到它之后,才知道应该进入 IPv4 头部,寻找 IHL、Total Length、Protocol 等字段。

RFC 1122 后来解释了这项数值分界的历史背景。同一根物理电缆可以混合 Ethernet 与 IEEE 802.3 帧,而相同位置的两字节字段在 802.3 中是长度,在 Ethernet 中是 EtherType。小于等于 1500 的值是长度;有效 EtherType 大于 1500。2048 因而明确落在“类型”一侧。

外层字段只负责选择解释规则。选中 IPv4 以后,数据报的终点由内部 Total Length 决定。不能因为网卡交给软件的缓冲区还有剩余字节,就把缓冲区末尾改称 IP 末尾。

二十字节的数据报要坐进四十六字节的车厢

RFC 791 把 Total Length 定义为整个 IP 数据报的字节数,包含 IP 头部和 IP 数据。IHL 则以 32 位字为单位描述头部长度,因此指向数据的开始位置。两者不是同一把尺:IHL 说明头部在哪结束,Total Length 说明数据报在哪结束。

一个没有选项、没有数据的 IPv4 数据报可以只有 20 字节。但 RFC 894 规定以太网数据字段至少有 46 字节。差出的 26 字节不能由 IP 应用凭空“拥有”,也不能被写进 Total Length,否则链路层就替上层创造了并不存在的数据。

RFC 894 的做法是以零补齐,并立即声明这些填充不属于 IP 包、不计入 Total Length。于是链路实际长度可以大于内部对象长度:

IHL × 4 <= IP Total Length <= 链路层载荷大小

第二个小于号严格成立时,空隙正是合法填充,不是格式错误。

不同以太网仍遵守同一个归属规则

同月发布的 RFC 895 处理更早的 3 Mb/s 实验以太网。它使用 8 位地址、不同的 Type 值,允许 1536 字节的最大 IP 数据报。与 RFC 894 的 1500 不同。

但 RFC 895 对最小帧的处理完全一致:链路若需要填充,所加字节仍不属于 IP,仍不进入 Total Length。这组对照很重要。1500、1536、地址宽度与类型值都是具体链路的条件;“IP 以自身长度字段界定对象”才是跨链路保留的合同。

1988 年的 RFC 1042 又把 IP 与 ARP 放进 IEEE 802.2 LLC 和 SNAP。LLC 与 SNAP 合计占八字节,SNAP 最后 16 位继续携带 EtherType:IP 为 2048,ARP 为 2054。IEEE 802 若有最小尺寸要求,仍以零填充,仍排除在 IP Total Length 之外。

RFC 1042 所说的最小 28 字节,是 20 字节 IPv4 头部加 8 字节 LLC/SNAP,不含 MAC 头。它不是 RFC 894 所说的 46 字节以太网数据字段下限。两个数字计算的边界不同。

RFC 1122 随后把 RFC 894 规定为 10 Mb/s Ethernet 主机必须发送、必须接收的格式;RFC 1042 格式应当能接收、可以发送。能发送两种格式的主机必须提供选择,并以 RFC 894 为默认。它还给出 Ethernet MTU 1500、802.3 MTU 1492。少掉的八字节是 LLC/SNAP 的外层开销,不是从 IP 头部或 Total Length 中删掉的八字节。

“minimum 1500”把证据问题写进了正文

RFC 894 原文在正确写出 46 字节最低值之后,又说以太网数据字段的“minimum”是 1500 字节,并由此推出 IP 数据报最大为 1500。前后逻辑不可能同时成立,错误的是一个词。

经核验的技术性 Erratum 570 将 “minimum”改为“maximum”,该记录在 2001 年提出。Erratum 5141 于 2017 年再次报告同一问题,2024 年完成核验。2024 年没有出现新的以太网物理上限;变化的是更正记录的状态。

RFC Editor 的勘误政策 刻意不把这两层合并。已发布 RFC 永不改写,经核验的勘误被视为准确,但不会写回 TXT、PDF 或 XML。原文回答“当时印了什么”,勘误回答“这一处应怎样理解”。

若本地镜像悄悄把 minimum 换掉,读者更顺畅,却失去变更来源;若只认原文而拒绝勘误,则保住了字节,却破坏了技术含义。可审计的做法是保留两份事实和它们之间的连接。

填充不是 trailer

同样在 1984 年 4 月,RFC 893 描述了 trailer encapsulation。它为某些接收者的内存对齐与复制成本,把可变的高层头部移动到数据之后;发送者必须知道链路邻居能够理解这种排列。

RFC 894 的填充没有移动任何 IP 结构。IP 头部仍在最前,IP 数据紧随其后,零只出现在 Total Length 指定的终点之后。接收者无需重组,只需在正确位置停止。

因此,链路填充、trailer、IP option 与 FCS 不能因为都靠近“尾部”就混为一谈。它们属于不同语法,受不同字段授权。

外层缓冲区与内层对象需要两次判断

2011 年的 RFC 6274 把这条历史边界写成安全检查。IP 模块可能收到比 Total Length 更大的链路载荷,合法链路填充通常会造成这种差值,攻击者也可能故意制造。

因此,接收缓冲区应按链路层报告的载荷大小分配。这样做是为了安全容纳外层容器,不是把容器的每一字节都授予 IP。进入 IP 解析后,Total Length 仍是终点。

反方向没有合法填充可解释。若链路交付的字节少于 Total Length 声称的数量,就应丢弃并记录。IHL 乘四也不得超过 Total Length。不得把相邻内存补成“缺失数据”,更不能让上层先读再说。

一把尺无法代替这两项职责。只按 Total Length 分配接收空间,可能装不下实际链路载荷;只按链路缓冲区末尾解析,则会吞进不属于 IP 的字节。

IP 不解释的字节仍能泄密

2003 年 1 月,CERT/CC VU#412115 记录了多种网卡驱动的填充泄露。它们没有写入零,而是复用了旧帧缓冲区内容。视实现而定,远端可能看到内核内存、驱动静态内存或网卡硬件缓冲区中的残留。

这些残留不会因被发出就变成合法 IP payload。正确的 IP 解析仍在 Total Length 停下。但以太网邻居能在物理帧中读取它们。语义排除保护了 IP 的对象边界,却不能自动清空硬件输出。

CERT 的供应商记录包括受影响、不受影响与未知状态,不能把漏洞推广为所有设备的共同事实。可以确认的是:不清零的实现确实把链路填充变成了远程信息泄露面。

谁添加字节,谁就承担这些字节的后果。驱动不能一面把旧内存送上网,一面以“Total Length 没有包含它”为理由否认输出。

来源