![为什么 NAT 不从计算机的 TCP 和 UDP 端口池中保留端口?](https://linux22.com/image/71465/%E4%B8%BA%E4%BB%80%E4%B9%88%20NAT%20%E4%B8%8D%E4%BB%8E%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%9A%84%20TCP%20%E5%92%8C%20UDP%20%E7%AB%AF%E5%8F%A3%E6%B1%A0%E4%B8%AD%E4%BF%9D%E7%95%99%E7%AB%AF%E5%8F%A3%EF%BC%9F.png)
我做了两个实验。这是他们两个的网络:
[private network] [public network]
A -------------------- R ----------------- B
192.168.0.5 192.168.0.1|192.0.2.1 192.0.2.8
A的默认网关是右。右IPv4 转发处于活动状态并且具有以下 iptables 规则:
iptables -t nat -A POSTROUTING -p TCP -j MASQUERADE --to-ports 50000
其目的是,任何来自 TCPA将使用 192.0.2.1 进行掩码右的端口50000。
我在端口 60000 上发布了 TCP 服务乙使用nc -4l 192.0.2.8 60000
。
然后我打开了一个连接A:nc -4 192.0.2.8 60000
A开始发送如下所示的数据包:
192.168.0.5:53269 -> 192.0.2.8:60000
右将其翻译成
192.0.2.1:50000 -> 192.0.2.8:60000
到目前为止,一切都很好。
然后我尝试打开以下客户端右: nc -4 192.0.2.8 60000 -p 50000
。我发了消息,什么也没发生。上看不到任何数据包右的 tcpdump.
因为伪装规则存在,或者至少因为它是活跃的,所以我本来期望右的 nc 失败,并显示错误消息“nc:地址已在使用中”,如果我将两个 nc 绑定到同一端口,就会发生这种情况。
然后我等了一会儿,所以 conntrack 的映射就会消失。
第二个实验是我尝试打开右的客户至上。右开始与乙正好。如果我随后打开连接A,其数据包将被忽略。A的 SYN 到达右,但他们没有得到答复,即使是 ICMP 错误也没有得到答复。我不知道这是不是因为右知道它耗尽了伪装端口,或者因为 Linux 完全混乱了(它在技术上屏蔽了端口,但已经建立的连接以某种方式干扰)。
我觉得NAT的行为是错误的。我可能会意外地为伪装(特别是在 iptables 规则期间未指定)和服务配置端口--to-ports
,并且内核将默默地删除连接。我也没有在任何地方看到任何此类记录。
例如:
- A发出正常请求乙。右使用端口 50k 的掩码。
- A进行 DNS 查询右。就是这样时间是递归的,右(出于巧合,使用临时端口 50k)查询权威名称服务器Z在端口 53 上。
刚刚发生了碰撞;右现在使用端口 50k 进行两个单独的 TCP 连接。
我猜这是因为你通常不会在路由器上发布服务。但话又说回来,当内核主动伪装时,从 TCP 端口池“借用”端口会伤害内核吗?
我知道我可以将我的临时端口与我的--to-ports
.但是,这似乎不是默认行为。 NAT 和临时端口都默认为 32768-61000,这令人毛骨悚然。
(我通过查询 /proc/sys/net/ipv4/ip_local_port_range 找到了临时范围,并通过在单独的实验中简单地 NAT 大量 UDP 请求找到了 NAT 范围 - 并在服务器端打印源端口。我找不到一种使用 iptables 打印范围的方法。)
答案1
当内核主动伪装时,从 TCP 端口池“借用”端口会伤害内核吗?
我想答案是“不,但这并不重要”。
我错误地假设右仅使用响应数据包的目标传输地址来判断它是否前往A或它本身。它实际上似乎使用整个源-目标传输地址元组来标识连接。因此,NAT 使用相同的连接创建多个连接实际上是正常的(右拥有)港口;它不会造成任何混乱。因此,TCP/UDP 端口池并不重要。
现在我想起来,这是非常明显的。
然后我尝试打开以下客户端右:
nc -4 192.0.2.8 60000 -p 50000
。我发了消息,什么也没发生。上看不到任何数据包右的 tcpdump.
这是我搞砸的实验部分。
失败的发生是因为源和目标传输地址相同,不仅仅是因为源地址相同。
如果我这样做,说,nc -4 192.0.2.8 60001 -p 50000
它确实有效。即使它使用与 NAT 掩码相同的端口。
我觉得NAT的行为是错误的。我可能会意外地为伪装(特别是在 iptables 规则期间未指定)和服务配置端口
--to-ports
,并且内核将默默地删除连接。
它不会,因为屏蔽连接和右- 启动的连接很可能有不同的目的地。
因为伪装规则存在,或者至少因为它是活跃的,所以我本来期望右的 nc 失败,并显示错误消息“nc:地址已在使用中”,如果我将两个 nc 绑定到同一端口,就会发生这种情况。
我仍在寻找对此问题的可靠答案,但一切似乎都表明“这是其实施方式的不利后果,而且它很小,我们愿意忍受它。”