UDP 4500 上,IKE 以四个 0 开头,ESP 直接从 SPI 开始。SPI 不得为 0,两者因此分得开。单独一个 0xFF 是 NAT 保活,收到它不能判断对端还活着。格式在 2005 年 1 月的 RFC 3948,IKEv2 的对应句子在 RFC 7296 第 2.23 节。
前四个字节决定是谁
一个可独立引用的定义句:非 ESP 标记是四个值为 0 的字节,与 ESP 包的 SPI 字段对齐。在 UDP 4500 上,IKE 载荷必须以此开头;ESP 的 SPI 不得为 0(RFC 3948 第 2.2 节、第 2.1 节)。
RFC 7296 第 2 节有同一条必须:从端口 4500 发出的、含 IKE 报文的 UDP 载荷,必须以四个 0 开头,否则接收方不知道怎样处理。第 2.23 节写明,把 IKE 装进 UDP 4500 时,是在 IKE 头前加上四个 0;把 ESP 装进去时,ESP 头紧跟 UDP 头。ESP 头的前四个字节是 SPI,而 SPI 不能合法地为 0。
| UDP 载荷 | 开头 | 其余 |
|---|---|---|
| ESP | SPI,不得为 0 | ESP 头和受保护的数据 |
| IKE | 四个 0 | 四个 0 之后才是 IKE 头 |
| NAT 保活 | 一个字节,值是 0xFF | 没有后续字段 |
相对没有 UDP 的 ESP,RFC 3948 第 3.2 节和第 3.4 节做的是插入一份 UDP 头,并改写外层 IPv4 头里的总长度、协议和头校验和,使它们与插入之后的包一致。这两节没有把协议号 17 印出来。传输模式和隧道模式差在有几层 IP 头,见 传输模式与隧道模式。本文不把那一篇的模式表再列一遍。
IKE 通常收发于 UDP 500,也可以在 4500 上收到,格式略有不同。不得在端口 500 上做 UDP 封装。发现 NAT 之后,支持 NAT 穿越的两端都必须对 ESP 使用 UDP 封装。第 2.23 节写明,这一节里的「必须」只约束支持 NAT 穿越的实现;支持本身是可选的。发起方即使还没有 NAT,也可以一开始就用 4500 同时跑 IKE 和 ESP。只要有一侧在用 4500,理解收到的 UDP 封装 ESP 是必需的;是否发送这种封装,则不因此成为必需,除非已经发现 NAT。
看见目的端口 4500,因此只说明用的是这个端口,不说明路径上已经发现了 NAT。
2026 年 10 月 21 日下载的 IANA 服务名登记 里,UDP 4500 的服务名是 ipsec-nat-t,描述是 IPsec NAT-Traversal,修改日期列是 2017-07-07,参考文献是 RFC 3948 和 RFC 7296。TCP 4500 是另一行,修改日期列是 2022-09-15,参考文献是 RFC 9329。本文没有重读 RFC 9329,不把 UDP 载荷前的四个 0 写成也适用于 TCP。
关键词在 RFC 3948 里按 RFC 2119 解释。这份 2005 年的文本没有加上后来常见的「只在全大写时才算」。下文的「必须」对应所引原文的大写词。RFC 7296 的元数据在 2026 年 10 月 21 日把状态标成 INTERNET STANDARD,更新者是 RFC 7427、7670、8247、8983、9370 和 9827。标题分别涉及签名认证、原始公钥、算法实现要求、地址共存通知、多次密钥交换,以及 ESN 变换类型的改名。本文没有重读这些正文,因此不判断它们有没有改写第 2.23 节。RFC 3948 的元数据当日没有列出更新者。
已验证勘误 6774(Editorial,2022-01-27)只把引言里对 RFC 3715 的引用从 case i 改成 case j。它不改四个 0、SPI 或 0xFF。
保活不证明对端还活着
一个可独立引用的定义句:NAT 保活的唯一目的,是在对等体之间的连接持续期间维持 NAT 映射。收到这种包,不得用来判断连接是否仍然活着。发送方必须使用长度为 1 字节、值为 0xFF 的载荷。接收方应当忽略收到的 NAT 保活(RFC 3948 第 2.3 节、第 4 节)。
对等体可以在阶段一或阶段二的安全关联仍然存在时发送,或者在这种关联至多于 N 分钟前还存在时发送。N 是本地可配置参数,默认 5 分钟。若按 RFC 3947 已经看出需要保活,并且已经有 M 秒没有向对端发送任何其他包,则应当发送。M 也是本地可配置参数,默认 20 秒。这是 2005 年文本里的默认,不是 2026 年 10 月对某一客户端的测量。
RFC 7296 第 2.23 节把「应当开始发送 RFC 3948 所定义的保活」写在一种具体情形上:NAT_DETECTION_DESTINATION_IP 的散列与外层包对不上,表示接收这份通知的系统自己在 NAT 后面。不支持 NAT 穿越时,也可以拒绝这次连接。本文不提供构造这份 SHA-1 通知或改写映射的步骤。
保活与重连 讨论的是隧道怎样发现路径已经空了。0xFF 不承担那个判断。收到它,按第 4 节应当忽略。NAT 穿越 说明地址和端口会被改写;本文只写改写之后、4500 上的三种载荷怎样区分。
发出时,IKE 报文的源端口和目的端口都必须是 500 或 4500。收到时必须接受任何源端口,应答必须回到报文到来的那个端口。RFC 7296 把理由写成:NAT 可能改掉端口。这句不把「任何源端口」推广成「目的端口也可以离开 4500」。
校验和与内层地址各算一层
ESP 和 NAT 保活的 IPv4 UDP 校验和应当发送为 0。接收方不得依赖它一定是 0。IKE 这一份,RFC 3948 写明自己不给校验和另加要求。条款是按 IPv4 头写的。引言说没有技术理由不能用 IPv6 当外层或内层头,但没有因此给出一套 IPv6 校验和。零校验和在 IPv6 上的默认和例外,见 UDP 校验和。本文不把「应当发送为 0」改写成 IPv6 的规则。
传输模式穿过 NAT 之后,内层 TCP 或 UDP 的校验和可能与改写后的地址不再一致。第 3.1.2 节按本地策略必须做下面三件事中的一件:用 IKE 得到的真实地址做增量更正;或者重新计算校验和;或者在内层是 UDP 时把校验和置 0。第三件只应当用于传输模式,并且包要有完整性保护。隧道模式的 TCP 校验和必须核验。RFC 7296 第 2.23 节写明,传输模式做这次更正时,原始地址来自流量选择符,而且选择符里必须正好有一个 IP 地址。2026 年 10 月 21 日没有重读第 2.23.1 节,因此不复述那一节的细节。TCP 校验和 说明这份和盖住哪些字段。本文不提供更正校验和的步骤。
隧道模式还有另一类冲突。第 5.1 节警告:远程对等体可能协商出在安全网关上重叠的表项,网关会分不清该把回来的包送往哪一个内部地址。实现必须设法避免。原文建议的办法包括分配本地唯一的地址,或在转发前做地址转换。这是实现者的义务,不是某一产品已经这样做的证据。出口上的地址和端口改写见 出口 NAPT。本文不提供分配内部地址或合并安全关联的步骤。
认证头不在这份封装里。RFC 3948 的引言把理由写成:AH 对外部 IP 地址的保护与 NAT 不相容。AH 覆盖到前面的 IP 头,范围在传输模式那一篇所指出的 RFC 4302。本文只写 ESP 前面这四个字节。
L2TP/IPsec 提到 UDP 4500 用于 NAT 穿越。那一篇说的是层次和端口。四个 0 与 0xFF 的差别在本文,不在那张端口表里。
现象对照
| 现象 | 更可能先看什么 |
|---|---|
| 目的端口 4500,前四个字节全 0 | IKE。四个 0 之后才是 IKE 头 |
| 目的端口 4500,前四个字节不是 0 | 按 ESP 解释。这四字节是 SPI |
| 载荷只有一个 0xFF | NAT 保活。应当忽略,不得用来判断连接还活着 |
| 目的端口 500,载荷却像 ESP 或四个 0 | 与「不得在 500 上做 UDP 封装」不符 |
| IPv4 的 UDP 校验和不是 0 | 接收方不得依赖它必须是 0。发送时的建议才是 0 |
| 两端都在 4500 上,路径上却没有 NAT | 允许。4500 不证明已经发现 NAT |
核对清单
- 三个开头不要看错:四个 0 是 IKE,非零的四个字节是 SPI,单独的 0xFF 是保活。SPI 不得为 0;
- 500 与 4500:不得在 500 上做 UDP 封装。发现 NAT 之后,支持 NAT 穿越的两端才必须给 ESP 加这层 UDP;
- 保活的默认不是测量:N 默认 5 分钟,M 默认 20 秒,两者都是本地参数。收到 0xFF 不得当成对端还活着;
- 校验和条款是 IPv4:应当发送 0,接收方不得依赖 0。不要把这句直接用于 IPv6。TCP 4500 的 RFC 9329 没有读;
- 第 2.23 节的更新者没有重读:2026 年 10 月 21 日的元数据列出六份更新 RFC 7296 的文件。勘误 6774 只改了一处引用,不改标记。