IPsec 协议族是企业网络跨越不可信公共互联网构建加密通道的关键工业标准。在 IPsec 最初设计的年代,架构假定互联网上的每个端点都具备全球可路由的公网 IPv4 地址。为了提供机密性、源身份验证与防重放保护,IPsec 严重依赖封装安全载荷(ESP,IP 协议号 50)。然而,随着 IPv4 地址枯竭,网络地址转换(NAT)与端口地址转换(PAT)在全球网络中无处不在。由于纯 ESP 协议直接承载于网络层 IP 之上,缺乏传输层 L4 端口信息,中间 NAT 网关无法跟踪或分发返回的加密数据包。IETF 颁布的 RFC 3947(IKE 中的 NAT 遍历协商)与 RFC 3948(IPsec ESP 报文的 UDP 封装)通过将 ESP 载荷封装在 UDP 4500 端口报文中,彻底解决了这一底层协议兼容矛盾。
原生 ESP 架构(在 PAT/NAT 网关下失效):
[IP 报头 (协议 50)] [ESP 报头: SPI (4B), 序号 (4B)] [加密载荷数据]
└──> 无 UDP/TCP 端口!PAT 网关无法向内部分发回包。
NAT-T 封装架构(RFC 3948 基于 UDP 4500 端口):
[IP 报头 (协议 17)] [UDP 报头 (源 4500, 目的 4500)] [ESP 报头] [载荷]
└──> 具备 L4 端口,中间网关可维持稳定的会话映射。
PAT 端口转换困局:原生 ESP 协议为何在 NAT 下失效
要理解 IPsec NAT 穿越(NAT-Traversal,简称 NAT-T)的必要性,必须深入剖析状态化 NAT 和 PAT 网关解析处理数据报头的方式。
传输层端口缺失:ESP 协议无源/目的端口特性
当家庭路由器或移动运营商级 NAT(CGNAT)后的多台私网设备访问外部服务时,网关会将它们不同的私网 IP 映射为同一个公网 IP。为了准确识别回包属于内网哪一台设备,网关会为每个出站 TCP 或 UDP 连接分配一个独一无二的传输层源端口号。
然而,ESP 位于网络层,协议号被固定为 50。与 TCP(协议 6)或 UDP(协议 17)不同,ESP 根本不存在端口概念。ESP 报头的前 4 个字节为安全参数索引(SPI),这是接收方分配用于标识安全关联(SA)的 32 位标识符:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 安全参数索引 Security Parameters Index (SPI) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 序号 Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 有效载荷数据 Payload Data |
当出站的 ESP 数据包到达 PAT 网关时,网关能重写源 IP,却无法分配转换端口。当远端服务器的 ESP 响应包返回时,PAT 网关检查报头发现缺失目的端口,便无法确定应将数据包转发给哪台内部主机。尽管部分家用路由器尝试使用所谓“SPI 穿透”,但一旦内网有多台设备同时连接同一远端 VPN 节点,这种启发式猜测就会彻底失效。
网络路径改写与 TCP/UDP 校验和失效难题
第二个严重问题发生在传输模式(Transport Mode)下。TCP 和 UDP 的伪报头校验和算法涵盖源 IP、目的 IP、协议号与分段长度。当中间 NAT 网关修改数据包的 IP 地址时,发送端主机预先计算的传输层校验和便会失效。由于载荷已被 ESP 加密,中间 NAT 网关无法解密并重新计算正确的校验和。当接收端节点解密还原出内部 TCP/UDP 数据时,校验和比对失败,操作系统内核将静默丢弃该数据包。
RFC 3947 NAT-Discovery 探测机制:动态识别中间件
为了动态解决 NAT 兼容性难题,RFC 3947(现已整合入现代 IKEv2 RFC 7296)在安全关联建立的初始阶段确立了自动化的 NAT 发现(NAT-D)流程。
IKE_SA_INIT 中的 NAT-D 载荷与哈希比对
在基于 UDP 500 端口执行的 IKE_SA_INIT 阶段,发起方与响应方会交换 NAT-D 载荷(NAT_DETECTION_SOURCE_IP 与 NAT_DETECTION_DESTINATION_IP)。每个载荷中都包含针对通信双方 SPI、IP 地址与 UDP 端口计算生成的 SHA-1 哈希值:
$$\text{Hash} = \text{SHA-1}(\text{SPI}_i ,|, \text{SPI}_r ,|, \text{IP} ,|, \text{Port})$$
探测过程从两个独立维度展开校验:
- 源地址哈希判定:发起方根据本地内网 IP 与源端口(500)计算哈希。当响应方收到该数据包时,使用外层 IP 报头中实际呈现的公网 IP 和端口计算哈希。若二者不匹配,说明发起方的出站链路上存在 NAT 网关改写了地址。
- 目的地址哈希判定:发起方对预设的目标 IP 与端口计算哈希。响应方对照自身实际绑定的网络接口地址进行核对。若不一致,说明响应方处于目的 NAT 之后。
NAT-Discovery 探测交互时序:
发起端(内网 IP: 192.168.1.50) 响应端(公网 IP: 203.0.113.1)
│ │
├─ IKE_SA_INIT (UDP 500) ────────────────────────────>│ 中途网关将源 IP 改写为 198.51.100.2
│ NAT-D (源: 192.168.1.50:500 的哈希) │ 响应端计算 Hash(198.51.100.2:500)
│ NAT-D (目的: 203.0.113.1:500 的哈希) │ 哈希值不匹配 => 确诊存在 NAT!
│<─ IKE_SA_INIT Response (UDP 500) ───────────────────┤
▼ ▼
双方确认存在 NAT 中间件 => 通信端点即刻浮动至 UDP 4500 端口。
端口浮动:平滑切换至 UDP 4500
一旦通信双方确认中间链路存在 NAT 设备,立即触发“端口浮动”:
- 后续的
IKE_AUTH身份验证过程全部迁移至 UDP 4500 端口进行。 - 随后生成的所有子安全关联(Child SA)数据载荷(ESP)均统一封装进发往 4500 端口的 UDP 报文中。
端口浮动避免了与部分路由器针对 500 端口设置的死板规则冲突,并为混合传输信令与加密载荷开辟了通道。
RFC 3948 封装架构:4 字节 Non-ESP Marker 解析
RFC 3948 规定了标准在线报文格式,用于在单个 UDP 4500 套接字上同时复用 IKE 控制信令和 ESP 数据载荷。
RFC 3948 UDP 封装格式拓扑:
+---------------------------------------------------------------+
| IPv4 或 IPv6 报头 |
+---------------------------------------------------------------+
| UDP 报头 (源端口, 目的端口 4500) |
+---------------------------------------------------------------+
| 解复用决策分流点 |
| [前 4 字节 == 0x00000000] | [前 4 字节 != 0x000000] |
| │ | │ |
| ▼ | ▼ |
| Non-ESP Marker | ESP 报头 (SPI) |
| (IKE 控制信令包) | (加密数据有效载荷) |
+-----------------------------------+---------------------------+
区分 IKE 控制流量与 ESP 数据载荷
由于 IKE 会话维护报文(重协商密钥、保活心跳、删除 SA)与用户数据流量共用 UDP 4500 端口,操作系统内核必须在极低延迟下完成数据流分流,而无需解析深层协议结构。
RFC 3948 确立了巧妙的架构分流规则:
- IKE 的 Non-ESP Marker:在 UDP 4500 端口上传输的任何 IKE 控制报文,都在紧随 UDP 报头处强制插入 4 个全零字节(
0x00000000)。 - ESP 的 SPI 偏移对齐:封装后的 ESP 报文直接将 32 位安全参数索引(SPI)置于 UDP 报头正后方。
根据 RFC 4301 规范,SPI 值 0(0x00000000)被强制保留,禁止作为任何有效 SA 的标识符。因此接收端内核只需执行一次常数时间复杂度 $O(1)$ 的快速判定:
- 若前 4 字节完全等于
0x00000000,说明该包是 IKE 信令,内核剥离该 4 字节标记后,将其分发给用户态 IKE 守护进程处理。 - 若前 4 字节非零,该包即为加密 ESP 流量。内核直接将此 32 位值视作 SPI,在安全关联数据库(SAD)中高速匹配规则,并送交内核密码学硬件解密。
这一设计避免了数据平面发生不必要的上下文切换,确保了穿透环境下的传输性能。
NAT 保活心跳机制与状态化转换定时器
状态化防火墙与 CGNAT 设备会在内存中维护动态转换表。当内网终端发起一次 UDP 请求时,网关建立映射表项。如果一定时间内(在移动蜂窝网络中往往只有 30 至 60 秒)没有新的数据包流经该映射,防火墙便会自动释放该端口。一旦映射过期,远端 VPN 节点推送的数据包将被丢弃。
RFC 3948 定义了专用的 NAT 保活报文(Keepalive):
- 数据格式:紧随 UDP 4500 报头之后填入一个字节,数值固定为
0xFF。 - 有效载荷:仅有 1 字节。
- 发送周期:在连接闲置时,通常以 20 至 25 秒为间隔自动发射。
RFC 3948 保活报文结构:
+-----------------------------------------------+
| IP 报头 (协议号 17) |
+-----------------------------------------------+
| UDP 报头 (源 4500, 目的 4500, 长度 9) |
+-----------------------------------------------+
| 0xFF (单字节载荷) |
+-----------------------------------------------+
当网关接收到 UDP 载荷长度恰好为 1 且数值等于 0xFF 的数据包时,会直接丢弃该数据,不触发任何错误日志。这种心跳报文的核心使命就是刷新中间 NAT 网关的活跃定时器,让回程数据通道保持畅通。
隧道封装开销、路径 MTU 探测与 TCP MSS 钳制
将 ESP 载荷套入 UDP 报文会引入额外的报头开销,挤压有效数据载荷空间。
在标准的 1500 字节以太网链路上:
- IPv4 原生 ESP 的加密开销大约为 50 至 60 字节(具体取决于加密套件选择)。
- 叠加 UDP 封装后会多占用 8 字节,导致隧道整体报头开销达到 62 至 72 字节。
- 内部 TCP 最大报文段长度(MSS)必须从常规的 1460 字节主动压制(Clamping)至 1360–1380 字节左右。
若未正确执行 TCP MSS 钳制,超出路径 MTU 的大型数据包将触发分片。而由于大量运营商中间件经常静默丢弃 IP 报文分片,错误的 MTU 策略会导致严重的 TCP 传输停顿与通信异常。
运营商级 CGNAT 韧性:深度融合 RiceGuard
VPNRice 的底层安全网络引擎 RiceGuard 深度实现了 RFC 3948 NAT-T 标准,保障连接在多层级 NAT、双重路由器架构与严苛企业防火墙中稳定运行。
RiceGuard 零信任 NAT-T 架构拓扑:
移动端设备 / 漫游终端
│ (位于运营商级 CGNAT 或双重 NAT 之后)
│ 通过 UDP 500 端口发起 IKE_SA_INIT
▼
自动化 NAT-D 校验
│ - 敏锐捕捉公网 IP 与端口改写
│ - 瞬时无缝切换至 UDP 4500 端口
│ - 为控制帧精准注入 4 字节 Non-ESP Marker
▼
RiceGuard 高性能 Anycast 接入网关
│ - 承载 UDP 4500 封装的 ESP 数据流
│ - 基于 Linux XFRM 内核接口实现微秒级 SAD 匹配
│ - 根据链路遥测数据动态调整保活心跳频率
▼
企业安全主干骨干网 / 互联网络通信
- 确定性端口浮动:RiceGuard 在建连阶段自动探知 NAT 拓扑,主动浮动至 UDP 4500,规避运营商对 500 端口实施的单向阻断。
- 自适应保活算法:摒弃死板的固定心跳间隔,RiceGuard 能根据当前网络类型动态微调发送频率,在有效保护移动设备电池续航的同时杜绝映射超时。
- 内核级快速解复用:借助 Linux XFRM 套接字与原生 UDP 卸载能力,将加密数据直接推送至硬件加速流水线,并将控制帧无缝分流至管理守护进程。
运维诊断验证:检查 NAT-T 套接字与内核 SAD 状态
系统工程师可以在 Linux 网关上使用原生网络诊断工具,验证 NAT-T 会话的运行健康度。
检验 UDP 4500 套接字侦听状态
确认 IPsec 守护进程已成功在 4500 端口创建监听:
# 查看监听 IKE 与 NAT-T 流量的 UDP 套接字
ss -uan '( sport = :500 or sport = :4500 )'
输出示例:
State Recv-Q Send-Q Local Address:Port Peer Address:Port
UNCONN 0 0 0.0.0.0:500 0.0.0.0:*
UNCONN 0 0 0.0.0.0:4500 0.0.0.0:*
查看 Linux 内核 XFRM 安全关联封装参数
查询内核安全关联数据库(SAD)中的具体封装规则:
# 显示内核中活跃的 IPsec SA 详细状态与封装信息
ip xfrm state show
关键输出片段:
src 198.51.100.2 dst 203.0.113.1
proto esp spi 0xc7a41289 reqid 1 mode tunnel
replay-window 64 flag af-unspec
auth-trunc hmac(sha256) 0x6e7a... 128
enc cbc(aes) 0x4f12...
encap type espinudp sport 4500 dport 4500 addr 0.0.0.0
sel src 0.0.0.0/0 dst 0.0.0.0/0
其中的 encap type espinudp 字段证实:Linux 内核正对发往远端的 ESP 数据流施行 UDP 4500 封装,并预期接收相同格式的数据包,保障流量在任意网络中平稳流通。
RFC 3948 是确保现代 IPsec 能在充满 NAT 的网络环境中生存的核心技术。通过将无状态 ESP 报文装载入标准 UDP 4500 框架,并以 Non-ESP Marker 清晰划定控制分界,NAT 穿越为跨越复杂网络边界的加密通信提供了坚实保障。
若需构建能够从容适应复杂网络中间件的零信任加密网络,推荐获取 VPNRice。



