这是关于发夹 NAT(环回 NAT)的规范问题。
这个问题的一般形式是:
我们有一个包含客户端、服务器和 NAT 路由器的网络。路由器上有到服务器的端口转发,因此它的一些服务可以在外部使用。我们有指向外部 IP 的 DNS。本地网络客户端无法连接,但外部工作。
- 为什么会失败?
- 如何创建统一的命名方案(在本地和外部都有效的 DNS 名称)?
这个问题的答案是从多个其他问题合并而来的。他们最初引用了 FreeBSD、D-Link、Microtik 和其他设备。然而,他们都试图解决同样的问题。
这是关于发夹 NAT(环回 NAT)的规范问题。
这个问题的一般形式是:
我们有一个包含客户端、服务器和 NAT 路由器的网络。路由器上有到服务器的端口转发,因此它的一些服务可以在外部使用。我们有指向外部 IP 的 DNS。本地网络客户端无法连接,但外部工作。
这个问题的答案是从多个其他问题合并而来的。他们最初引用了 FreeBSD、D-Link、Microtik 和其他设备。然而,他们都试图解决同样的问题。
由于这已被提升为关于发夹 NAT 的规范问题,我认为它可能应该有一个比当前接受的答案更普遍有效的答案,该答案(尽管非常好)专门与 FreeBSD 相关。
此问题适用于 RFC1918 寻址的 IPv4 网络上的服务器提供的服务,这些服务通过在网关处引入目标 NAT (DNAT) 提供给外部用户。然后内部用户尝试通过外部地址访问这些服务。他们的数据包从客户端发送到网关设备,网关设备重写目标地址并立即将其注入内部网络。正是数据包在网关上的这种急剧转向产生了发夹式 NAT的名称,类似于发夹式转向。
当网关设备重写目标地址而不是源地址时,就会出现问题。然后服务器接收一个带有内部目标地址(它自己的)和一个内部源地址(客户端的)的数据包;它知道它可以直接回复这样的地址,所以它这样做了。由于该回复是直接的,它不会通过网关,因此网关永远没有机会通过重写返回数据包的源地址来平衡入站目标 NAT 对初始数据包的影响。
客户端因此向外部IP 地址发送数据包,但从内部IP 地址获得回复。它不知道这两个数据包是同一个对话的一部分,所以没有对话发生。
解决方案是,对于需要这种目的NAT,并且从内部网络到达网关的数据包,对入站数据包也执行源NAT(SNAT),通常通过将源地址重写为网关的地址。服务器然后认为客户端是网关本身,并直接回复它。这反过来又使网关有机会通过重写返回数据包上的源地址和目标地址来平衡 DNAT 和 SNAT 对入站数据包的影响。
客户端认为它正在与外部服务器通信。服务器认为它正在与网关设备通信。各方皆大欢喜。在这一点上,图表可能会有所帮助:
一些消费者网关设备足够明亮,可以识别需要第二个 NAT 步骤的数据包,并且这些数据包可能在发夹式 NAT 场景中开箱即用。其他人不是,所以也不会,而且不太可能使它们起作用。讨论哪些消费级设备是服务器故障的主题。
通常可以告诉适当的网络设备工作,但是 - 因为他们不从事事后猜测他们的管理员的业务 - 他们必须被告知这样做。Linux 用于
iptables
执行 DNAT,因此:这将为 HTTP 端口启用简单的 DNAT 到
192.168.3.11
. 但要启用发夹式 NAT,还需要一条规则,例如:请注意,这些规则需要在相关链中的正确位置才能正常工作,并且根据
filter
链中的设置,可能需要额外的规则来允许经过 NAT 的流量流动。所有此类讨论都超出了此答案的范围。但正如其他人所说,正确启用发夹 NAT 并不是解决问题的最佳方法。最好的是水平分割 DNS,您的组织根据请求客户端的位置为原始查找提供不同的答案,或者通过为内部用户和外部用户使用不同的物理服务器,或者通过配置 DNS 服务器以根据不同的响应做出不同的响应请求客户端的地址。
您正在寻找的是所谓的“发夹 NAT”。来自内部接口对分配给外部接口的 IP 地址的请求应该进行 NAT,就好像它们来自外部接口一样。
我根本不熟悉 FreeBSD,但是阅读 OpenBSD 的“pf”手册(http://www.openbsd.org/faq/pf/rdr.html)提出了水平分割 DNS 的解决方案,使用DMZ 网络或 TCP 代理让我相信“pf”不支持发夹式 NAT。
我会考虑采用水平分割 DNS 的路线,而不是在内部使用 URL 中的 IP 地址,而是使用名称。
这里的问题是,您的路由器不会对您的内部客户端地址进行 NAT。因此,TCP 握手失败。
让我们假设以下IP
这是正在发生的事情:
为什么不使用水平分割 dns 而不是到处硬编码 IP 地址?您将让 ext.yourdomain 在外部指向 217.xxx,然后在内部指向 192.xxx。
生病回答我的问题只是为了拓宽那些有类似问题的人的视野。
我联系了我的 ISP 并要求他们尝试解决我的问题。他们为我提供的是另一个仅用于服务器的公共 IP 地址,现在我在 FreeBSD 的 WAN 端有本地流量,我们制作了特定的管道,以提高本地流量到服务器公共 IP 的吞吐量
如果它是原始的 D-Link 路由器(即,不是来自 Virgin Media 的 Rev. D / 固件版本 1.00VG),您应该能够调整设置以解决此问题。(不过,我同意之前发帖人对DD-WRT的建议,还有很多其他的原因!)
此屏幕截图来自 Rev. C 模型;你的可能略有不同。
最近回答了一个类似的问题:Cisco static NAT not working on LAN side,刚刚意识到这是一个规范问题。所以请允许我在这里总结一下解决方案。
首先:忘记 NAT(如果可以的话)——问题根本不在于配置 NAT。它是关于从 Internet 和 LAN 访问位于 NAT 后面的服务器。使用两个 DNS 区域是一种可行的替代方案,但并不总是解决方案。但是解决方案确实存在并且非常简单(尽管可能并不完美):
(1) 在服务器上:将公共 IP 地址作为辅助 IP 地址添加到服务器的网络接口上,掩码为 255.255.255.255(服务器上的 Web 服务或您想要的任何东西也应该监听这个 IP 地址);所有现代操作系统都允许您执行此操作(或者可以使用分配了公共 IP 地址的环回接口,而不是向主接口添加辅助 IP)。
(2)在局域网主机上:为公网IP地址添加主机路由,例如对于Windows主机使用如下命令:route -p add 203.0.113.130 mask 255.255.255.255 192.168.1.11(也可以使用DHCP”静态路由”选项来分配路由)。或者,如果在客户端和面向 Internet 的路由器之间存在 (a) L3 交换机/路由器,则在此(这些)中间交换机/路由器上配置该主机路由,而不是在客户身上。
对于那些与 TCP 三向握手有关的人:它在建议的配置中可以正常工作。
请提供反馈(至少,投票)。
从技术角度来看,该问题的最佳解决方案是在您的网络上启用 IPv6。启用 IPv6 后,您需要为您的域创建 AAAA 记录。保持现有的 A 记录指向路由器的外部 IPv4 。创建指向服务器IPv6 地址的 AAAA 记录。
IPv6 有足够的地址来避免 NAT,因此 IPv6 不需要发夹式 NAT。一旦您启用了 IPv6 并创建了 AAAA 记录,任何支持RFC 8305的客户端都会在 IPv4 之前尝试 IPv6。这意味着 IPv4 也不需要发夹式 NAT,因为客户端不会使用它。
在世界大部分地区也启用 IPv6 之前,您仍然需要现有的 IPv4 NAT 用于传出连接和端口转发用于传入连接。
它也更快。
使用 IPv6 将为您提供比发夹式 NAT 更好的性能。
使用发夹 NAT,您的客户端将通过交换机将数据包发送到路由器,然后路由器将执行两轮转换,最后通过交换机将数据包发送到服务器。从服务器到客户端的数据包将反向通过整个路径。
使用 IPv6,您可以避免 NAT,而是直接通过客户端和服务器之间的交换机发送数据包。这意味着在往返过程中,您将通过交换机的次数从 4 次减少到 2 次,并且避免了 2 次通过路由器的行程以及路由器将执行的 4 次转换。这转化为更好的性能。
即使您使用内置在与路由器相同的盒子中的交换机也是如此。
如果 ISP 没有 IPv6 怎么办?
如果您使用的是不支持 IPv6 的 ISP,我会质疑您是否应该在该网络上托管服务器。如果 ISP 当前不支持 IPv6,这些是我的建议。
首先告诉 ISP 你需要IPv6。也许提醒他们 IPv6 协议已经存在 20 年了,所以他们早就应该支持它了。如果这还不足以让 ISP 认真对待您,请开始寻找其他 ISP。
如果您找到支持 IPv6 的 ISP,您可以在过渡期内与这两个 ISP 一起运行。在连接到新 ISP 的路由器上,您可以在 LAN 端禁用 IPv4,然后将两个路由器的 LAN 端连接到同一个交换机。IPv4 和 IPv6 是两个独立的协议,因此如果这些连接通过不同的路由器完全没有问题。作为一个附带好处,如果其中一个连接中断,它会为您提供一些冗余。
如果您找不到支持 IPv6 的 ISP,您应该考虑将您的服务器移至托管设施。使用托管设施中的服务器,您将减少对地理位置的依赖,因此供应商之间存在更多竞争,这将有助于确保有一个满足您的需求。
将服务器移动到托管设施不会为您的客户端提供 IPv6,但移动服务器意味着您不再需要发夹式 NAT 来访问它。
你不应该做什么
如果您无法路由 IPv6 流量,请不要打开 IPv6 并创建 AAAA 记录。如果您的 ISP 不支持 IPv6,但您选择在 LAN 上启用 IPv6(可能使用 RFC 4193 地址)并创建 AAAA 记录,它将适用于 LAN 上的客户端到达 LAN 上的服务器。但是您的 LAN 与外部世界之间的通信将首先尝试 IPv6(这不起作用),并且您将依赖回退到 IPv4,这充其量是慢一点,或者最坏的情况不会发生。
因为我也问过这个问题(请参阅如何使用外部 IP 从内部访问 NATed 防火墙后面的网络服务?)并被重定向到这里,但这里的答案没有提供解决方案(与一般解释相反)让我在这里提供我的 Linux(
iptables
特定)解决方案,以节省大家几个小时的实验时间。这个文件是有iptables-restore
格式的,可以直接读入iptables(当然是在编辑IP地址之后)。这适用于 Web 服务器(端口 80)并且仅适用于 IPv4 - IPv6 和 SSL(端口 443)的规则类似。将
lan.local
和替换web.local
为web.public.com
您的本地网络(例如 10.0.x.0/24)、您的网络服务器的本地 IP(例如 10.0.1.2)和您的路由器的公共 IP(例如 4.5.6.7)。这-4
只是为了允许同一文件中的 IPv6 和 IPv4 规则(这些行被 忽略ip6tables
)。此外,当 IPv6 地址包含端口声明时,请记住将它们放在 [括号] 中,例如[fe0a:bd52::2]:80
.这些都是导致我在尝试实际实施这个问题的解释时拔出头发的所有事情。我希望我什么都没遗漏。
我会在这里添加一个答案,因为这里的评论没有解决我的特定问题。我怀疑那是因为我遇到了一个讨厌的 linux 内核错误。设置是:
尽管图片看起来很复杂,但与其他评论中所涵盖情况的唯一相关更改是添加了软件桥 br0。它存在是因为网关盒也是 LAN 的无线接入点。
我们的网关盒仍在为 LAN 上的机器执行 NAT 任务。因为它只有 1 个以太网端口,所以它被迫进行发夹式 NAT。我怀疑它应该只适用于此处其他评论中给出的 iptables 规则,但在 Linux 内核 4.9 上至少它没有。在 4.9 下,虽然我们的网关盒可以访问互联网,但 LAN 上的机器试图通过 NAT 访问它却不能。
tcpdump
显示对到达 eth0 的传入数据包的响应,但它们不会超出 br0。运行此命令可修复:在该命令运行之前,传入的数据包会根据内核的默认行为进行处理,即将它们提供给网桥,然后将它们传递给内核的路由模块。该命令强制不是来自 LAN 的数据包绕过网桥并直接进入路由,这意味着网桥没有机会丢弃它们。必须桥接广播和多播地址,否则 DHCP 和 mDNS 之类的东西将不起作用。如果您使用的是 IPv6,则还必须为其添加规则。
您可能很想使用以下方法解决问题:
我当然很受诱惑——这是我的第一次尝试。一旦我这样做了,局域网上的机器就可以访问互联网,所以它可以工作一段时间。然后发生了以下情况(我不想重复实验):
唯一的出路是重新启动建筑物中的每台机器。一个例外是硬件开关,无法重新启动。他们必须重新上电。