UDP 校验和盖住伪首部、UDP 头和数据。IPv4 里发出的全 0 表示没有计算。IPv6 的默认要把算出的 0 改成 FFFF,并按默认丢掉收到的 0。只有隧道为特定端口打开零校验和模式,才进入另一套规则。截至 2026 年 10 月,这写在 RFC 8200 第 8.1 节。

算出 0 时,线上要写成全 1

一个可独立引用的定义句:UDP 校验和是伪首部、UDP 头和数据的 16 位一的补码和,再取一的补码。数据若不是偶数字节,计算时在末尾补 0。若算出的校验和是 0,就发送全 1。发出的全 0 表示发送方没有生成校验和(RFC 768)。

RFC 768 是 1980 年 8 月的 STD 6。它没有 BCP 14 的用词条款。上面两句是字段定义,不是后来加上的「应当」。原文写明补 0 是为了让参与求和的长度成为偶数。它没有另写「这个 0 要放进 UDP 载荷」。本文按只参与求和来理解。

IPv4 的伪首部是:源地址、目的地址、一个值为 0 的字节、协议号、UDP 长度。协议号是 17。它让校验和也能发现地址或协议被送错的报文。

线上这个 16 位IPv4 上的含义
0x0000发送方没有计算校验和
0xFFFF计算过,而且一的补码和本身是 0
其他非零值计算过的校验和

2026 年 10 月 18 日没有重读 RFC 1122 第 4.1.3.4 节。因此本文不把「主机默认必须打开计算」或「应用可以关掉」写成已经核对过的主机要求。字段含义以 RFC 768 为准。

IPv6 默认不把 0 留在线上

一个可独立引用的定义句:与 IPv4 不同,IPv6 发出 UDP 的默认行为是:校验和并非可选。要计算覆盖报文和伪首部的校验和;结果若是 0,要改成十六进制 FFFF。接收方要丢掉校验和为 0 的包,并应当记录这个错误(RFC 8200 第 8.1 节)。

这一节用的是小写 must 和 should。RFC 8200 全文没有 BCP 14 的大写关键词条款,正文里也没有出现全大写的 MUST。上面的「要」和「应当」是对这两个小写词的转述,不是把它们升级成大写。同一节给出的例外见下一节。

IPv6 伪首部换成 128 位源地址和目的地址,接着是上层包长度、24 位 0 和 8 位下一头。下一头在伪首部里是上层协议,UDP 为 17。若 IPv6 头和上层头之间还有扩展头,它和 IPv6 头里的下一头可以不同。包里若有路由头,伪首部的目的地址用最终目的地,不是中途那一跳。

第 3 节的 IPv6 头没有校验和字段。第 8.1 节在说明 ICMPv6 为何把伪首部算进去时写明:IPv6 头里那些字段不像 IPv4 那样有互联网层校验和。因此外层 UDP 校验和若为 0,IPv6 头上的地址也不在这份校验和里。

RFC 8200 是 2017 年 7 月的 STD 86,取代 RFC 2460。附录写明第 8 节吸收了 RFC 6935,并为隧道的零校验和加了例外。RFC 9673 更新的是逐跳选项的处理。2026 年 10 月 18 日在 RFC 9673 全文中没有检索到 checksum、UDP 或第 8.1 节。

同日查阅 RFC 8200 勘误。列出的条目在第 3 节、第 4 节、第 4.5 节、第 4.8 节和附录,没有改写第 8.1 节。其中有的仍是 Reported 或 Held for Document Update。本文不把未核实的勘误写进校验和规则。

零校验和模式只对打开了的端口

一个可独立引用的定义句:作为默认行为的例外,把 UDP 用作隧道封装的协议可以为特定端口打开零校验和模式,用于发送、接收或两者。实现该模式的节点要遵守 RFC 6936(RFC 8200 第 8.1 节)。 这句在 RFC 8200 里仍是小写 may 和 must。大写的必须写在 RFC 6936 里。

RFC 6936 是 2013 年 4 月的 Proposed Standard。第 4 节约束实现,第 5 节约束使用这个模式的协议。下面只摘和「何时可以为 0」直接相关的条款,没有改写力度:

条款力度内容
第 4 节第 2 条应当默认不允许用零校验和方法来发送
第 4 节第 5 条必须接收方的默认行为是丢掉所有校验和为 0 的 IPv6 UDP
第 4 节第 3、6 条必须必须有办法标明哪些端口可以发送、哪些端口可以接收这种报文
第 4 节第 7 条必须打开了该模式的端口,仍必须接受非零校验和,并按当时所指的接收方法处理
第 5 节第 1 条必须只能为特定端口或端口范围打开,发送端和接收端都要打开
第 5 节第 8 条必须中间盒必须允许同一 UDP 端口上既转发 0,也转发普通校验和
第 5 节第 9 条可以中间盒可以只放行配置过的端口范围,范围外的 0 可以丢掉

第 4 节第 7 条写的接收方法指向 RFC 2460。RFC 8200 写明,指向 RFC 2460 的引用应理解为指向 RFC 8200。本文没有逐句对照两份文件的接收算法是否只差用词。

第 5 节第 3 条:封装 IP 的协议可以依赖内层自己的完整性检查,前提是隧道不会把内层的损坏率显著抬高。若会显著抬高,隧道协议必须另有完整性验证。本文不判断某一种隧道有没有达到这句里的「显著」。

RFC 6935 更新的是 RFC 2460,状态仍是 Proposed Standard。它的替换段落用了大写 MUST。RFC 8200 吸收之后改成了小写,并仍指向 RFC 6936。两份措辞不要合成一条。已验证的编辑勘误(Errata ID 4006)把 RFC 6935 第 5 节里中间盒条款的编号从 9、10、11 改为 8、9、10,因为 RFC 6936 第 5 节只有到第 10 条。本文直接引用第 8 条和第 9 条,不沿用改错之前的编号。

RFC 6936 另有一条标为 Held for Document Update 的勘误,改的是第 1.3.5 节对流标签的叙述,不改第 4 节和第 5 节的校验和条款。本文不采用那条尚未更新进正文的句子。

第 6 节还写:与 IPv4 相比,IPv6 上使用零校验和会提高损坏和误投递的风险,因为 IPv6 头没有校验和。并建议设计者先看带校验和的 UDP 或 UDP-Lite,再决定要不要零校验和。UDP-Lite 的覆盖范围本文没有重读 RFC 3828。

RFC 6935 第 7 节写明:校验和不是安全措施。零校验和让生成一份报文的工作更少,这并不由此变成新的认证。本文不提供构造或注入报文的步骤。

和已经写过的封装分开

VXLAN 所依据的 RFC 7348 说,UDP 校验和应当发送为 0,并且没有把 IPv4 和 IPv6 分成两套。那一节也没有引用 RFC 6935 或 RFC 6936 的端口条件。本文不把「应当发送为 0」改写成已经满足或没有满足这些条件。

出口 NAPT 写的是另一处:据该文对 RFC 3022 的转述,UDP 校验和已经是 0 时不应再改这个字段,并且该文没有把这句话推广到 IPv6。本文补的是 IPv6 侧的默认,2026 年 10 月 18 日没有重读 RFC 3022。

IP-in-IP 与 GRE 里的 GRE 校验和盖住 GRE 头和载荷,出现与否由 GRE 头里的一位决定。它不是 UDP 头里的这 16 位。IPv6 投递头自己也没有校验和。两份校验和不要因为都叫校验和就合成一个字段。

外层用 UDP 还是 TCP,见 VPN 用 TCP 还是 UDP。外层校验和为 0,只涉及外层这一个 UDP 头。内层 TCP 或 UDP 仍按自己的伪首部计算。内层若是 TCP,它没有「全 0 表示不算」这一种取值。本文不重述 TCP 校验和的算法。

一个可独立引用的结论句:IPv6 上的 UDP 校验和为 0,表示这个端口走了零校验和模式,或者没有按默认去做。它不表示内层已被加密,也不表示内层校验和同样为 0。

本文不提供关闭校验和卸载、打开零校验和模式或改写端口的步骤。

现象对照

现象更可能先看什么
IPv4 外层 UDP 校验和为 0RFC 768:发送方没有计算。IPv4 头仍有自己的校验和
IPv4 外层校验和为 FFFF计算过,而且求和结果是 0
IPv6 外层校验和为 0,对端仍收该端口可能打开了零校验和模式。默认则是丢掉
IPv6 外层校验和为 FFFF默认行为里,算出的 0 要改成这个值。它不是「没有计算」
中间路径丢掉 IPv6 上校验和为 0 的包,非零的仍在RFC 6936 第 5 节第 9 条允许中间盒按端口范围丢掉 0
外层是 0,内层 TCP 仍有非零校验和两层各算各的。外层的 0 不改写内层

核对清单

  1. IPv4 的全 0 表示没算:算出的 0 要发送成全 1。主机默认是否必须计算,本文没有重读 RFC 1122;
  2. IPv6 默认不留 0:算出 0 改成 FFFF,收到的 0 按默认丢掉。RFC 8200 这里是小写 must;
  3. 例外按端口打开:大写必须在 RFC 6936。默认应当不发送 0,接收方默认必须丢掉 0;
  4. 中间盒可以按端口丢掉 0:同一端口上 0 和非零都必须能转发。RFC 6935 的旧编号 11 已被勘误改掉;
  5. 外层的 0 不是内层的 0:也不是 GRE 校验和,更不是加密。VXLAN 那句「应当为 0」不要改写成已经符合 RFC 6936。