从本地网络环回转发的公共 IP 地址 - 发夹式 NAT

ado*_*lot 55 networking nat internet port-forwarding

这是一个关于发夹式 NAT(环回 NAT)的规范问题

这个问题的一般形式是:

我们有一个包含客户端、服务器和 NAT 路由器的网络。路由器上有到服务器的端口转发,因此它的一些服务可从外部使用。我们有指向外部 IP 的 DNS。本地网络客户端无法连接,但外部工作。

  • 为什么这会失败?
  • 如何创建统一的命名方案(在本地和外部均可使用的 DNS 名称)?

这个问题的答案是从多个其他问题合并而来的。他们最初引用了 FreeBSD、D-Link、Microtik 和其他设备。然而,他们都试图解决同样的问题。

Mad*_*ter 60

由于这已被提升为关于发夹式 NAT规范问题,我认为它可能应该有一个比当前接受的答案更普遍有效的答案,该答案(尽管非常好)特别与 FreeBSD 相关。

此问题适用于 RFC1918 寻址的 IPv4 网络上的服务器提供的服务,这些服务通过在网关处引入目标 NAT (DNAT) 来提供给外部用户。然后内部用户尝试通过外部地址访问这些服务。他们的数据包从客户端传到网关设备,网关设备重写目标地址并立即将其注入内部网络。正是数据包在网关处进行的这种急转弯产生了名称hairpin NAT,类似于hairpin turn

当网关设备重写目的地址而不是源地址时,就会出现问题。然后服务器收到一个带有内部目标地址(它自己的)和一个内部源地址(客户端的)的数据包;它知道它可以直接回复这样的地址,所以它会这样做。由于该回复是直接的,它不会通过网关,因此永远没有机会通过重写返回数据包的源地址来平衡入站目标 NAT 对初始数据包的影响。

因此,客户端将数据包发送到外部IP 地址,但会从内部IP 地址获得回复。它不知道这两个数据包是同一个对话的一部分,所以没有对话发生。

解决方案是,对于需要这样的目的NAT,并且从内部网络到达网关的数据包,也对入站数据包进行源NAT(SNAT),通常通过将源地址重写为网关的源地址。服务器然后认为客户端是网关本身,并直接回复它。这反过来又使网关有机会通过在返回数据包上重写源地址和目标地址来平衡 DNAT 和 SNAT 对入站数据包的影响。

客户端认为它正在与外部服务器通话。服务器认为它正在与网关设备通话。各方都很高兴。在这一点上,图表可能会有所帮助:

在此处输入图片说明

一些消费者网关设备足够亮,可以识别那些需要第二个 NAT 步骤的数据包,这些数据包可能会在发夹式 NAT 场景中开箱即用。其他人不是,所以不会,而且不太可能使它们起作用。讨论哪些消费级设备属于 Server Fault 是题外话。

适当的网络设备通常可以被告知工作,但是 - 因为他们不是在事后猜测他们的管理员 - 他们必须被告知这样做。Linux 用于iptables执行 DNAT,因此:

iptables -t nat -A PREROUTING  -p tcp --dport 80 -j DNAT --to-destination 192.168.3.11
Run Code Online (Sandbox Code Playgroud)

这将为 HTTP 端口启用简单的 DNAT,到192.168.3.11. 但是要启用发夹式 NAT,还需要一个规则,例如:

iptables -t nat -A POSTROUTING -d 192.168.3.11 -p tcp --dport 80 -j MASQUERADE
Run Code Online (Sandbox Code Playgroud)

请注意,此类规则需要位于相关链中的正确位置才能正常工作,并且根据filter链中的设置,可能需要额外的规则来允许 NATted 流量流动。所有此类讨论都超出了本答案的范围。

但正如其他人所说,正确启用发夹式 NAT 并不是解决问题的最佳方法。最好的是水平分割 DNS,您的组织根据请求客户端的位置为原始查找提供不同的答案,或者通过为内部用户和外部用户使用不同的物理服务器,或者通过配置 DNS 服务器根据不同的响应请求客户端的地址。

  • 这是一个很棒的答案。我基本上总是有一个紧迫的问题“为什么在路由器中打开端口转发后,我不能在浏览器中向我的公共 IP 发出 HTTP 请求来测试它?” 这就是答案。令人恼火的是,有些 ISP 没有做到这一点。当我更换 ISP 后,我就无法进行这样的测试了。我了解到新的 ISP“不支持发夹 NAT”。现在,我意识到他们实际上正在使用发夹 NAT,而不是像您图中那样的“正确的发夹 NAT”。如果他们根本不使用它,或者使用它并且做得正确,那么它就会起作用。 (3认同)
  • 我假设您指的是最后一种情况,“正确的发夹 NAT”。关键是重写入站数据包的源地址,使其返回到路由器,然后路由器可以反转 DNAT 和 SNAT,从而避免问题。路由器使用众多地址中的哪一个来执行此操作更像是一个品味问题,如果您使用“iptables”执行此操作,那么您当然可以选择配置。 (2认同)

Eva*_*son 19

您正在寻找的称为“发夹式 NAT”。来自内部接口的对分配给外部接口的 IP 地址的请求应该经过 NAT 处理,就好像它们来自外部接口一样。

我根本不熟悉 FreeBSD,但阅读 OpenBSD ( http://www.openbsd.org/faq/pf/rdr.html ) 的“pf”手册,建议的水平分割 DNS 解决方案,使用DMZ 网络或 TCP 代理让我相信“pf”不支持发夹式 NAT。

我会考虑采用水平分割 DNS 的路线,而不是在内部使用 URL 中的 IP 地址,而是使用名称。


PEr*_*Era 10

这里的问题是,您的路由器没有对您的内部客户端地址进行 NAT。因此,TCP 握手失败。

让我们假设以下 IP

  • 客户端:192.168.1.3
  • 服务器:192.168.1.2
  • 路由器内部:192.168.1
  • 路由器外部:123.123.123.1

这是正在发生的事情:

  1. 客户端 (192.168.1.3) 将 TCP-SYN 发送到您的外部 IP、端口 80 (123.123.123.1:80)
  2. 路由器看到端口转发规则并将数据包转发到服务器(192.168.1.2:80)而不改变源IP(192.168.1.3)
  3. 客户端等待来自外部 IP 的 SYN-ACK
  4. 服务器直接将他的回答发送回客户端,因为它在同一个子网上。它不会将数据包发送到路由器,这会反转 NAT。
  5. 客户端从 192.168.1.2 而不是 123.123.123.1 收到 SYN-ACK。并丢弃它。
  6. 客户端仍在等待来自 123.123.123.1 的 SYN-ACK 并超时。