我试图了解使用 iptables DNAT 和环回地址的局限性。假设我们有一个只能连接到 127.0.0.1 的应用程序;使服务器和客户端在不同节点上工作的明显解决方案是使用 NAT,如下所示:
确保所有传出的数据包都有主客户端主机的 IP 地址:
iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to-source <CLIENT_IP>
现在让我们尝试欺骗客户端并通过 DNAT 与外界建立连接:
iptables -t nat -A OUTPUT -d 127.0.0.1/32 -p tcp -m tcp --dport <SERVICE_PORT> -j DNAT --to-destination <SERVER_IP>
不幸的是,这根本不起作用,当尝试连接到127.0.0.1:<SERVICE_PORT>
程序时,它只是挂在connect
系统调用上。有趣的是,我在任何接口上都看不到任何 SYN 数据包(tcpdump -qn -i any port <SERVICE_PORT>
),但是我在查看 iptables 统计信息时可以看到数据包计数器增加(iptables -nvL -t nat
)。
通过谷歌搜索解决方案,我找到了一个内核构建选项CONFIG_IP_NF_NAT_LOCAL
,该选项在内核 2.6.0-2.6.10 中用于解决使用 NAT 进行本地连接的问题。不幸的是,当前的 git 内核树保存了 2.6.11 及更高版本的信息,因此我在这里遇到了死胡同。
进一步看,我遇到了此主题,涉及修复 2.6.11 中的一些 DNAT 环回问题;其中一个补丁删除了CONFIG_IP_NF_NAT_LOCAL
构建选项并无条件启用代码(这是实际的区别)。
但是,这个问题的现状对我来说还不太清楚。我想找到一些解释或参考资料来证明这是一个内核错误或一些不太详细的文档功能。我知道网络 127.0.0.0/8 路由由内核以特殊方式处理,但从 iptables 文档中可以清楚地看出,nat 表 OUTPUT 链是唯一对此类规则有意义的地方,并且没有说明任何例外或任何有关 127/8 网络的说明。
请不要建议使用第三方工具来解决一些问题,我已经有一些了,我只是想解释一下为什么这个确切的配置不起作用以及为什么它不应该起作用。欢迎提供任何有关如何使用任何其他 iptables 规则完成此 DNAT 的示例。
上述配置在 Debian squeeze 和 wheezy 上进行了测试,内核为 2.6.32、3.1.0、3.2.0。
答案1
深入挖掘后可以证实上述行为是完全正确的。
根据 RFC 5735,网络 127.0.0.0/8 不应路由到主机本身之外:
127.0.0.0/8 - 此地址块被指定为 Internet 主机环回地址。由高级协议发送到此地址块内任意地址的数据报都会在主机内环回。这通常仅使用 127.0.0.1/32 进行环回。如 [RFC1122] 第 3.2.1.3 节所述,整个 127.0.0.0/8 地址块内的地址不会合法地出现在任何网络上。
RFC 1700,第 5 页还指出这些地址“绝不应出现在主机之外”。
RFC 1812,第 5.3.7 节 Martian 地址过滤声明“路由器不应该转发,除非通过环回接口,任何源地址在网络 127 上的数据包”,这正是我的情况。
关于这些参考,我推测第一篇文章中的案例确实是一个特点,而相反的行为将违反标准。