![带有 iptables 端口转发的源地址](https://linux22.com/image/648686/%E5%B8%A6%E6%9C%89%20iptables%20%E7%AB%AF%E5%8F%A3%E8%BD%AC%E5%8F%91%E7%9A%84%E6%BA%90%E5%9C%B0%E5%9D%80.png)
我使用 iptables 设置了 80 端口的端口转发:
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 10080
此外,我已启用 IP 转发来路由来自不同子网的连接。
我在同一台机器的端口 10080 上运行我自己的标准套接字服务器,当我收到端口 80 的传入连接时,它会按预期转发到我的服务器。
我的问题是,当我使用 getsockname 和 getpeername 打印出套接字信息时,源地址始终是服务器的。如果我通过不受 iptables 规则影响的端口直接连接到服务器,我会看到预期的客户端地址。为什么端口转发会影响源地址?
答案1
我认为在这种情况下重定向目标不是正确的选择,动态 NAT 目标更合适。
如果您的内部网络上有一台服务器,并且希望将其对外开放,则可以使用
-j DNAT
NAT 中的 PREROUTING 链的目标来指定目标 IP 地址和端口,以便转发请求连接到内部服务的传入数据包。例如,如果您想将传入的 HTTP 请求转发到位于 172.31.0.23 的专用 Apache HTTP Server 服务器系统,请运行以下命令:iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j DNAT \ --to 172.31.0.23:80
答案2
我的问题是,当我使用 getsockname 和 getpeername 打印出套接字信息时,源地址始终是服务器的。如果我通过不受 iptables 规则影响的端口直接连接到服务器,我会看到预期的客户端地址。为什么端口转发会影响源地址?
我会解释,但请注意,这将是一个详细解释。
首先让我们了解一下什么是TCP 连接(IP,Port)
:它是‘发起元组’和‘终止元组’之间的有状态的面向连接的数据交换(IP,port)
。
换句话说,TCP 连接由四重的(Client_IP,Client_Port,Server_IP,Server_Port)
。
这意味着,该 TCP 连接的所有信息传输都需要满足四重定义。
现在,我们假设您的服务器位于 1.1.1.1,而客户端位于 2.2.2.2。客户端打开到您服务器端口 80 的连接。假设客户端的临时端口为 34567,则我们有四个端口 (2.2.2.2,34567,1.1.1.1,80)。
在您的服务器上,数据包被重定向到 1.1.1.1:10080。但这会导致不同的四重连接!例如,(2.2.2.2,34567,1.1.1.1,10080)。
那么实际的监听器会做什么呢?为什么它会直接响应 2.2.2.2:34567,导致数据包被客户端丢弃,因为客户端有 (2.2.2.2,34567,1.1.1.1,80) 四个字节,而没有 (2.2.2.2,34567,1.1.1.1,10080)。
因此,在这种情况下,REDIRECT
目标必须创建一个临时的四重映射:
(2.2.2.2,34567,1.1.1.1,80) --> (1.1.1.1,x,1.1.1.1,10080)
(x 是一个临时的、短暂的端口,用于建立 TCP 连接)。
通过这种临时映射,10080 处的服务器响应netfilter
,并且发生反向映射:
(1.1.1.1,x,1.1.1.1,10080) --> (2.2.2.2,34567,1.1.1.1,80)
netfilter
将已经反向映射的数据包发送给客户端,一切正常。
因此,如果您需要知道客户端的 IP 地址,您可以不能使用REDIRECT
目标。相反,将实际服务器放在不同的子网中,并使用DNAT
目标,让 Linux 服务器充当完整路由器。假设实际服务器的本地 LAN 地址为 192.168.1.51,则映射将是:
(2.2.2.2,34567,1.1.1.1,80) --> (2.2.2.2,34567,192.168.1.51,80)
192.168.1.51的服务器会回复2.2.2.2,路由器会进行反向映射:
(2.2.2.2,34567,192.168.1.51,80) --> (2.2.2.2,34567,1.1.1.1,80)
一切都好。