IPsec 开销:模式、NAT-T 和加密算法都会改变它
一个公开的开销数字只描述一条路径上的一种封装。把你隧道真正协商到的头部加起来,然后再去测量这条路径。
某本指南里有个数字,通常在六十到八十字节之间,是某人从 1500 里减掉、用来得到接口 MTU 的。那个数字描述的只是某一条路径上的某一种封装。换掉模式、外部地址族、加密算法,或者改变有没有 NAT,这套算术就不再适用于你的隧道。
这个数字来自另一条隧道
开销不是 IPsec 在抽象意义上的属性。它是真正被构造出来的那个数据包的属性:协商好的模式、外层头部、保护头部、填充、完整性字段,可能还有一层额外的 UDP 封装。
针对 IPv4、没有 NAT 的传输模式给出的数字,描述的包比 NAT 穿越下的 IPv6 隧道模式更小。两者在各自的场景里都没错。把其中任何一个照搬到另一种部署,就是人们最后拿到一个会弄坏大流量传输的 MTU 的原因。故障是安静的,因为小请求一直能正常工作。
把真正存在的各层加起来
从外层 IP 头部开始,向内侧推进。隧道模式下原始数据包在里面,所以外层头部是追加,而不是替换。 RFC 4303 描述的是 ESP 头部、填充以及紧随其后的完整性数据。
| Layer | What it adds | When it applies |
|---|---|---|
| Outer IP header | 20 bytes for IPv4, 40 for IPv6 | Always, and 40 rather than 20 if the outer path is v6 |
| ESP header | 8 bytes of SPI and sequence number | Always |
| Block cipher IV | 8 to 16 bytes depending on the transform | When the negotiated cipher uses one |
| Padding and trailer | Padding to the block size, plus two trailer bytes | Always, and the amount varies with payload length |
| Integrity data | 12 to 16 bytes for common authenticated ciphers | When integrity protection is negotiated |
| UDP encapsulation | 8 bytes | NAT traversal on port 4500, which is common for clients behind NAT |
这些行没有一行在各部署之间是恒定的,填充那一行甚至在同一次部署里都不恒定。一个公开的开销数字之所以让人失望,原因全在这里。
两个站点可能对同一条隧道有分歧
受保护的数据包仍然要穿过外层路径,而外层路径并非处处相同。PPPoE 接入线路承载的比干净的 Ethernet 段更少。移动网络可能进一步收缩,而把自己流量再隧道化的运营商又会加上一层没人提过的封装。一条链路能通、另一条链路失败的隧道,描述的是第二条链路,而不是协议。
两个方向也可能不同。非对称路由可能给你一个能用的上传和一个卡住的下载,这看起来像应用问题,直到你去看数据包大小而不是可达性。小 ping 能通,对满尺寸的回应几乎没有证明力。
去测量,而不是去减
找出能穿过这条路径的最大数据报。然后把它和接口报告的值比较:
ip link show eth0 | grep -o 'mtu [0-9]*' ip xfrm state list ping -M do -s 1400 198.51.100.7 ping -M do -s 1372 198.51.100.7 tcpdump -ni any -c 20 'esp or udp port 4500'
把 ping 尺寸降下来,直到有回应,并把能用的最大值当作这条路径的预算。xfrm 的输出会告诉你协商了什么模式和什么算法,那才是任何诚实计算的输入。抓包能确认线上是 ESP 还是被 UDP 包住的 ESP,这就把 NAT 的问题定了下来,不用猜。
也给控制平面留一个值。IKE 握手包的结构和数据包不同,所以从握手抓包里推出来的尺寸不是数据平面的 MTU。
把数字放在它该在的地方
对于穿过隧道的 TCP,限制 MSS 往往是最小侵入的控制手段,因为它减少端点发送的量,而不改变隧道本身。对于基于路由的隧道,要有意设置隧道接口的 MTU,并记录原因。两件事一起做然后测量,你无法知道是哪一件起了作用。
把协商好的模式、外层地址族、NAT 情况和可复现的症状一起留在工单里。一组记录下来的四条事实胜过记住的一个常数,而且只有这种形式能扛过下一次客户端升级。