某业务运行在服务器 A(10.0.0.2:80),后来将其迁移至服务器 B(10.0.0.3:80),为了保证平滑迁移,服务器 A 需要将 80 端口的入口流量转发至服务器 B 的 80 端口。Iptables 强大的端口转发功能可以很好的满足该需求,因为服务器 A 需要将入口流量转发,因此使用 DNAT官网文档的步骤如下:

开启转发功能:

$ sysctl -w net.ipv4.ip_forward=1

设置如下转发规则,即把 A 的 80 端口的流量转发到服务器 B 的 80 端口。

$ iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to  10.0.0.3:80

然后从客户端(10.0.0.10)访问服务器 A 的 80 端口,但是没有回应,原因是超时。

$ curl http://10.0.0.2:80
curl: (7) Failed to connect to 10.0.0.2 port 80: Operation timed out

服务器 A 的抓包如下,由第一行可知服务器 A 的 80 端口收到请求,第二行表示服务器 A 将 80 端口的流量转发至服务器 B 的 80 端口(请注意相同的 TCP 序列号)。

$ tcpdump port 80
... IP 10.0.0.10.52799 > 10.0.0.2.80 Flags [S], seq 100071038, ... length 0
... IP 10.0.0.10.52799 > 10.0.0.3.80 Flags [S], seq 100071038, ... length 0
... (repeat the two lines above)

服务器 B 的抓包如下,第一行表示服务器 B 收到 TCP 三次握手中的 sync 报文,之后服务器回送 sync 报文(第二行),第三行表示收到客户端 reset 报文。客户端之所以发送 reset 报文,是因为客户端是和服务器 A 而非 B 建立的 TCP 链接,所以服务器 B 回送 sync 报文时,客户端并不认识服务器 B,故客户端重置连接。

$ tcpdump port 80
... IP 10.0.0.10.52799 > 10.0.0.3.80: Flags [S], seq 100071038 ... length 0
... IP 10.0.0.3.80 > 10.0.0.10.52799: Flags [S.], seq 2076971142, ack 100071039 ... length 0
... IP 10.0.0.10.52799 > 10.0.0.3.80: Flags [R], seq 100071039 ... length 0

分析清楚原因后,对于该问题,解决的办法有多种,最为便捷的是在服务器 A 对 80 端口的流量上再做 SNAT。

$ iptables -t nat -A POSTROUTING -d 10.0.0.3 -p tcp --dport 80 -j SNAT --to-source 10.0.0.2

Serverfault 亦有人提出类似问题 how-to-do-the-port-forwarding-from-one-ip-to-another-ip-in-same-network