新旧公网/24子网1:1 NAT代理(firewall-cmd/iptables)配置求助
看起来你在做新旧公网子网的1:1 NAT迁移,这个需求其实很常见,但iptables/firewalld的规则链逻辑确实容易搞混,我来帮你梳理下问题出在哪,以及正确的配置方向。
先说说你当前配置的问题
你遇到的Invalid argument错误,核心原因是在PREROUTING链用了SNAT规则——这完全不符合iptables的规则链逻辑:
- PREROUTING链属于nat表的"路由前"阶段,只能修改数据包的目标地址(用DNAT),用来把外部访问的旧IP导向新IP;
- SNAT是修改数据包的源地址,必须在POSTROUTING链(路由后阶段)使用,通常用于隐藏内部源IP。
所以你直接在PREROUTING里加SNAT,iptables肯定会报错拒绝。
另外,你当前只针对单个VIP(XXX.XXX.XXX.99)配置了规则,但你的需求是整个/24子网的1:1映射,单个IP的规则显然不高效,这里我们可以用iptables专门的NETMAP目标来实现网段级的1:1映射,比写一堆单个IP的规则方便太多。
概念先理清楚
先明确几个关键NAT类型的区别,避免再搞混:
- DNAT:修改数据包的目标地址,用于把外部请求的旧IP映射到新IP,在PREROUTING链处理;
- SNAT:修改数据包的源地址,用于把新子网返回的数据包的源地址改回旧IP,让外部客户端能正确接收响应,在POSTROUTING链处理;
- NETMAP:iptables的特殊目标,专门用于同掩码网段的1:1地址映射(比如XXX.XXX.XXX.x → YYY.YYY.YYY.x,主机位完全对应),完美匹配你的需求。
正确的配置步骤
1. 永久启用IP转发
临时写/proc/sys/net/ipv4/ip_forward重启会失效,建议写到sysctl配置里:
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf sysctl -p # 立即生效
2. 启用Masquerade并刷新规则
这一步你之前做的是对的,确保永久生效:
firewall-cmd --permanent --zone=public --add-masquerade firewall-cmd --reload
3. 配置网段级1:1 NAT映射
用NETMAP实现整个旧子网到新子网的1:1映射:
第一步:配置DNAT,把旧IP的请求转发到新IP
firewall-cmd --direct --permanent --add-rule ipv4 nat PREROUTING 0 -d XXX.XXX.XXX.0/24 -j NETMAP --to YYY.YYY.YYY.0/24
这条规则的意思是:所有目标地址是旧子网XXX.XXX.XXX.0/24的数据包,自动把目标地址映射到新子网对应的IP(比如XXX.XXX.XXX.9 → YYY.YYY.YYY.9,主机位完全对应)。
第二步:配置SNAT,让新子网的返回包能正确回到客户端
新子网服务器返回的数据包源地址是新IP,需要改成对应的旧IP才能让客户端正常接收(因为客户端原本请求的是旧IP):
firewall-cmd --direct --permanent --add-rule ipv4 nat POSTROUTING 0 -s YYY.YYY.YYY.0/24 -j NETMAP --to XXX.XXX.XXX.0/24
4. 生效规则
执行reload让所有永久规则生效:
firewall-cmd --reload
关键前提提醒
你之前给代理服务器加了旧子网的VIP(XXX.XXX.XXX.99),但还要确保整个旧子网的流量都能到达这个代理服务器:
- 要么让旧子网的上游路由器把
XXX.XXX.XXX.0/24的所有路由指向代理服务器; - 要么在代理服务器上配置旧子网的整个段作为VIP(可以用ip addr add批量添加,比如
for i in {1..254}; do ip addr add XXX.XXX.XXX.$i/24 dev eth0; done)。
只有旧子网的流量能到代理服务器,NAT规则才能生效。
验证配置
- 检查IP转发是否生效:
sysctl net.ipv4.ip_forward
返回net.ipv4.ip_forward = 1就是正确的。
- 检查firewalld的direct规则:
firewall-cmd --direct --get-rules ipv4 nat PREROUTING firewall-cmd --direct --get-rules ipv4 nat POSTROUTING
能看到你添加的两条NETMAP规则就说明配置成功。
- 抓包验证:
在代理服务器上执行tcpdump -i any host XXX.XXX.XXX.9,然后从外部设备访问XXX.XXX.XXX.9,就能看到数据包被转发到YYY.YYY.YYY.9的流量。
备注:内容来源于stack exchange,提问作者Max Cuttins

