摘要

  • RFC 5382 的 REQ-7 明确要求 TCP NAT 不得让不同内部端点同时使用同一个地址与端口映射;一旦两者连接同一外部地址与端口,公网四元组就会碰撞。
  • 端口利用率、映射归属、入站过滤、TCP 建连、日志归因与应用完成是不同证据。节省一个端口,不能以取消会话身份为代价。

区分被藏进了目的地

设想一张 NAT 表。内部端点 A 使用私网地址和端口发起 TCP 连接,转换器把它映射为一个公网地址与端口。随后,内部端点 B 也得到完全相同的公网坐标。只要 A 连接外部服务器 X、B 连接外部服务器 Y,转换器仍可把远端地址与端口放进查找键,在回包时选出正确的内部端点。

从容量统计看,这很漂亮:同一个公网端口承载了两条连接。可是系统并没有创造新的命名空间,只是借用了 X 与 Y 的差异。若 A 和 B 同时访问同一个热门服务,源公网地址相同、源端口相同、目的地址相同、目的端口也相同。外部可见的四元组不再能表示两条独立连接。被藏进目的地的区分消失了。

RFC 5382 第 7.1 节把这种行为称为端口重载。文档指出,共享同一映射的两个内部端点无法同时连接同一个外部端点。REQ-7 因而使用了明确的禁止性规范:TCP NAT 不得采用端口重载。

这不是保守的性能建议,而是对优化权力的限制。公网端口在映射有效期内是一个临时坐标。转换器可以决定如何分配资源,却不能一边把坐标交给多个主体,一边继续让外部系统相信它指向唯一主体。

端点独立映射不是多人共用

RFC 5382 还要求 TCP 使用端点独立映射。字面上看,它似乎鼓励更广泛地复用映射;但关键是“谁在复用”。正确语义是:同一个内部地址与端口连接不同远端时,可以保留同一个外部映射。内部主体没有变,只有对端变化。

端口重载则让不同内部地址与端口成为同一映射的共同占用者。它不是连续性,而是并发共用。为了维持共用,转换器不得不让远端地址与端口承担隐藏身份字段。于是,一个本应不依赖目的地的映射,在区分内部主体时反而依赖了目的地。

过滤策略也不能替代这个判断。过滤回答哪些外部来源可以利用已有映射返回数据;映射归属回答获准的数据应交付给谁。BTW 已有文章专门讨论 NAT64 映射不等于过滤策略。REQ-7 的问题更早:在讨论是否放行以前,系统是否拥有一个唯一接收者?

因此,评审材料应把三件事分开记录:映射属于哪个内部端点;哪些外部端点获准返回;应用最终是否完成工作。稳定映射不能证明过滤,过滤许可不能证明唯一交付,握手成功也不能证明业务完成。

它不是 TCP 同时打开

RFC 5382 也因保护 TCP simultaneous open 而知名。那种场景中,两个对等端都主动发送 SYN,报文在路径中交叉,双方沿着合法状态机建立一条连接。某些 NAT 只学习了典型客户端—服务器顺序,因而把合法的入站 SYN 当成未经请求的流量。现有 BTW 长文已经完整拥有这个历史命题和六秒不确定窗口。

端口重载碰撞不是同一件事。这里有两个不同的内部申请者,他们通常都在连接第三方服务。问题不是 SYN 交叉,也不是谁充当客户端,而是转换器把两个内部源投影成一个公网源。当两者选择同一外部目标,投影失去了可逆性。

两种故障需要不同证据。同时打开要检查 SYN-SENT、SYN-RECEIVED 和入站策略;端口重载要检查分配事件、内部元组、外部元组、远端元组、策略版本与状态所有者。把它们都写成“NAT 不兼容”,会让修复动作对错对象。

稀缺不能授权抹掉身份

IPv4 稀缺是真实约束。运营商需要用有限公网地址服务大量用户,设备厂商会展示每地址会话数、每秒新建连接数和表项上限。端口因此容易被当成可任意切分的容量单位。

但 TCP 消耗的不是孤立号码。连接包含两个端点、序列空间、状态与返回路径。公网地址与端口是转换器对外提供的临时可达声明:在声明有效期间,发往该坐标的报文应抵达正确的内部端点。它不必永久属于用户,却必须在所承诺的范围内含义明确。

资源耗尽时,合规系统可能拒绝新连接。拒绝会影响体验,却是可观测、可计量的事实。运营团队可以扩容、调整端口块、迁移到 IPv6 或改变准入策略。如果系统为了让准入数字好看而把新连接叠到已有 TCP 映射上,最初的计数可能成功,真正冲突却延后发生,甚至伤害原有连接。

所以容量工程要衡量“保持唯一性所需的资源”,而不是宣布唯一性太昂贵。池大小、端口分块、超时与准入控制都是合法杠杆;把同一临时身份同时许给两人不是。

发夹路径让身份期待显形

REQ-8 要求 TCP 支持 hairpinning,并要求回送到内部的报文使用外部源地址与端口。两个内部应用若只知道彼此的公网映射,也应能够通信;收到报文时,它们看到的源要与此前获得的对端坐标一致。

这项要求与端口重载禁令相互独立,却共同揭示了外部坐标的作用。应用不仅接收字节,还会把字节关联到某个对端。即使报文没有真正离开 NAT,转换器也必须维护那个关联。公网元组并非可随时替换的装饰标签。

端口重载从另一侧破坏同样的期待。若一个坐标只有在附加不可见的远端条件后才指向唯一主体,它的可移植性就比表面更弱。它可能只在一台节点、一张内存表、一次故障切换之前有效。运营者需要证明的不是坐标永恒,而是它在映射寿命与使用范围内始终只有一个内部占用者。

错误、静默与修改都不自动获得处置权

RFC 5382 的 ICMP 要求提供了相邻边界:NAT 应翻译相关 Destination Unreachable,但任何 ICMP 消息都不得直接终止对应映射或 TCP 连接。错误消息是路径证据,不是销毁会话的授权书。

空闲时间也不是端点死亡证明。RFC 为无法判断活动性的情形规定了已建立连接两小时四分钟、过渡连接四分钟的最低空闲时间。BTW 的 keepalive 文章已经拥有“静默并非故障”的主题。对本稿而言,结论更窄:只要映射仍有效,近期没有报文就不能被解释为可以把同一 TCP 身份并发借给另一内部端点。

ALG 会产生进一步责任。若 NAT 修改 TCP 序列号,就必须正确处理 SACK。每一次隐藏修改都要在后续报文上保持一致。端口重载会在序列号处理之前制造更根本的问题:这个包究竟属于哪个主体、哪个序列空间?

这里的共同原则是证据与权限相称。ICMP 可以报告,计时器可以触发候选回收,ALG 可以按明确规则转换;它们都不能凭局部信号重写会话身份。

怎样证明没有重载

配置页面显示“已禁用”只是意图。真正测试要重现 REQ-7 所保护的碰撞条件:两个不同内部地址与端口,在第一条映射仍存活时,同时连接同一外部地址与端口。观察两条连接的公网源,它们必须不同;若没有可用资源,第二条必须得到明确失败,而不能悄悄复用第一条身份。

测试应覆盖端口压力、地址池缩减、策略重载与节点切换。每次分配要记录内部源、外部映射、远端目标、协议、创建和删除时间、算法版本、节点身份与故障切换代次。把这些事件同 SYN、SYN-ACK、ACK 关联,再用应用层请求验证实际结果。

归因同样需要边界。公网地址与端口只有结合准确时间窗口才可能对应某项临时声明。若系统还要求远端元组才能找回内部主体,就必须在任何滥用调查或审计中公开这一依赖。REQ-7 的价值正是避免 TCP 同时出现这种共同占用。

来源