从本地networking环回到转发的公共IP地址 – 发夹式NAT

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

这个问题的一般forms是:

我们有一个与客户端,服务器和NAT路由器的networking。 在路由器上有端口转发到服务器,所以它的一些服务可以在外部使用。 我们有DNS指向外部IP。 本地networking客户端无法连接,但外部工作。

  • 为什么这会失败?
  • 我怎样才能创build一个统一的命名scheme(本地和外部工作的DNS名称)?

这个问题已经回答了其他多个问题。 他们最初引用了FreeBSD,D-Link,Microtik和其他设备。 他们都试图解决同样的问题。

    你在找什么叫做“发夹NAT”。 对于分配给外部接口的IP地址,来自内部接口的请求应该像来自外部接口的NAT一样。

    我根本没有FreeBSD的熟悉程度,但是阅读OpenBSD的“pf”手册( http://www.openbsd.org/faq/pf/rdr.html )提出了水平分割DNS的解决scheme,使用DMZnetworking或TCP代理导致我相信“pf”不支持发夹式NAT。

    我想看看水平分割DNS的路由,而不是在内部使用URL地址,而是使用名字。

    由于这已经被提升为关于发夹NAT规范问题 ,我认为它应该可能有一个比目前被广泛接受的更为普遍的答案。

    这个问题适用于服务器在RFC1918寻址的IPv4networking上提供的服务,这些服务通过在网关处引入目的地NAT(DNAT)而提供给外部用户。 内部用户然后尝试通过外部地址访问这些服务。 他们的数据包从客户端发送到网关设备,网关设备重写目标地址,并立即将其注入内部networking。 正是这个尖锐的转发包在名为发夹NAT的网关上做了,通过与发夹相类比。

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

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

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

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

    在这里输入图像描述

    某些消费者网关设备足够明亮,可以识别需要进行第二个NAT步骤的那些数据包,而这些数据包可能在发夹式NAT场景中可以直接使用。 其他人不是,也不是,他们不可能工作。 讨论哪些消费级设备是服务器故障偏离主题的。

    通常可以告诉适当的networking设备工作,但是 – 因为他们不是在猜测他们的pipe理员 – 他们必须被告知这样做。 Linux使用iptables来执行DNAT:

     iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 192.168.3.11 

    这将启用HTTP端口的简单DNAT到192.168.3.11上的内部服务器。 但是要启用发夹式NAT,还需要一个规则,例如:

     iptables -t nat -A POSTROUTING -d 192.168.3.11 -p tcp --dport 80 -j MASQUERADE 

    请注意,这些规则需要在相关链中正确的位置才能正常工作,并且根据filter链中的设置,可能需要附加规则以允许NATstream量stream动。 所有这些讨论都超出了这个答案的范围。

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

    这里的问题是,你的路由器不会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并超时。

    为什么不使用水平分割dns而不是硬编码IP地址? 你会有ext.yourdomain指向外部217.xxx,然后在内部192.xxx。

    最近回答了一个类似的问题: 思科静态NAT不在LAN侧工作 ,只是意识到这是一个典型问题。 所以请允许我总结一下这里的解决scheme。

    首先:忘记NAT(如果可以的话) – 问题根本不在于configurationNAT。 这是关于从互联网和局域网访问位于NAT后面的服务器。 采用两个DNS区域是一个可行的select,但并不总是解决scheme。 但是解决scheme确实存在并且非常简单(尽pipe可能不完美):

    (1)在服务器上:在服务器的networking接口上添加公网IP地址,使用255.255.255.255掩码(networking服务或任何你想在服务器上监听的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交换机/路由器,请在此(这些)中间交换机/路由器上configuration该主机路由,而不是在客户端。

    对于那些与TCP三次握手有关的人来说:在build议的configuration中,它将工作正常。

    请提供反馈意见(至less是投票)。

    对我的问题回答只是为了给那些有类似问题的人们拓宽视野。

    我联系了我的ISP,并要求他们尝试解决我的问题。 他们提供给我的是另一个公共IP地址,只是为了服务器,现在我在FreeBSD的WAN端有本地stream量,

    如果是原始D-Link路由器(即Virgin Media的Rev. D / Firmware Version 1.00VG),则应该能够调整设置以解决此问题。 (但是,我同意以前的海报对DD-WRT的build议,原因很多!)

    1. login到路由器的Web界面
    2. 点击顶部的高级标签
    3. 点击左侧的防火墙设置标签
    4. 单击TCP Endpoint Filtering下的Endpoint Independent单选button,如下面的屏幕截图所示(或者查看D-Link网站上的路由器仿真器 )
    5. 保存更改; 你完成了

    D-Link路由器Web UI截图

    这个截图来自Rev. C模型; 你的可能会略有不同。

    由于我也问过这个问题(请参阅如何从内部使用外部IP访问NAT防火墙后面的networking服务? ),并在这里redirect,但这里的答案没有提供解决scheme (与通用解释相反),让我的提供我的Linux(特定于iptables )解决scheme,为大家节约几个小时的实验。 这个文件是iptables-restore格式,可以直接读入iptables(编辑IP地址之后)。 这是一个Web服务器(端口80),只适用于IPv4 – IPv6和SSL(端口443)的规则是类似的。

     # Port forwarding for VM / Container access with „hairpin NAT“. *nat :PREROUTING ACCEPT [3:205] :INPUT ACCEPT [59:670] :OUTPUT ACCEPT [16:172] :POSTROUTING ACCEPT [20:257] # This was simple port forwarding - access works from outside but not from inside #-A PREROUTING -4 -p tcp -i eth0 --dport 80 -j DNAT --to web.local:80 # This is real hairpin NAT which allows „web.local“ to access itself via the VM hosts external IP. # First we need to masquerade any traffic going out the external interface: -A POSTROUTING -o eth0 -j MASQUERADE # Then we need to reroute incoming traffic on the public IP to the local IP: -A PREROUTING -4 -p tcp -d web.public.com --dport 80 -j DNAT --to web.local:80 # And finally we need to tell the router that the source IP of any traffic # coming from the LAN must be source-rewritten when going to the web server: -A POSTROUTING -4 -p tcp -s lan.local/24 -d web.local --dport 80 -j SNAT --to-source web.public.com:80 COMMIT 

    lan.localweb.localweb.public.comreplace为您的本地networking(例如10.0.x.0 / 24),您的networking服务器的本地IP(例如10.0.1.2)和您的路由器的公共IP(例如4.5 .6.7)。 -4只是允许在同一个文件中的IPv6和IPv4规则(这样的行被ip6tables忽略)。 另外,请记住在包含端口声明的地方将[IPv6地址]放在[括号]中,例如[fe0a:bd52::2]:80

    这些都是让我在试图在这个问题上实际解释的时候拉我的头发的事情。 我希望我没有留下任何东西。

    我会在这里添加一个答案,因为这里的评论没有解决我的特殊问题。 我怀疑这是因为我碰到了一个令人讨厌的Linux内核错误。 设置是:

     internet <--> modem 1.1.1.1/30 <--> switch <---> LAN 10.1.1.0/24 ^ +----------------------+ | | /--eth0 o <----/ | | | | 10.1.1.1/24 br0 | v (antenna) | 1.1.1.2/30 | | | | \-wlan0 o ----------/ +----------------------+ 

    尽pipe看起来很复杂,但其他评论所涉及的情况唯一相关的变化是增加了软件桥br0。 它在那里,因为网关盒也是局域网的无线接入点。

    我们的网关盒仍然对局域网上的机器执行NAT职责。 因为它只有1个以太网端口,所以它被迫做发夹式NAT。 我怀疑它应该只适用于在这里的其他评论中给出的iptables规则,但在Linux内核4.9,至less它不。 在4.9以下的时候,我们的网关盒子可以访问互联网,而局域网上试图通过NAT访问的机器却不能。

    tcpdump显示响应传入数据包的命中eth0,但不会使其超出br0。 运行此命令可修复:

     ebtables -t brouter -A BROUTING -d 01:00:00:00:00:00/01:00:00:00:00:00 -j ACCEPT ebtables -t brouter -A BROUTING -p IPv4 --ip-dst 10.1.1.0/24 -j ACCEPT ebtables -t brouter -A BROUTING -p IPv4 --ip-src 10.1.1.0/24 -j ACCEPT ebtables -t brouter -A BROUTING -p IPv4 -j DROP 

    在该命令运行之前,传入的数据包将根据内核的默认行为进行处理,即将内容传递给网桥,然后传递给内核的路由模块。 该命令强制不是来自局域网的数据包绕过网桥直接转到路由,这意味着网桥没有机会丢弃它们。 广播和多播地址必须桥接,否则像DHCP和mDNS的东西将无法正常工作。 如果您使用IPv6,则还必须为其添加规则。

    你可能会试图解决这个问题:

     brctl hairpin br0 eth0 on brctl hairpin br0 wlan0 on 

    我当时非常动心 – 这是我第一次尝试。 一旦我做到了,局域网上的机器就可以访问互联网,所以它可以工作一段时间。 然后发生以下事情(我不在乎重复这个实验):

    1. 通过局域网到网关的ping时间大约每隔10秒增加一倍,从0.1ms上升到0.2ms,0.4ms,0.8ms,2ms等等,直到网关不能访问局域网。 它闻起来像是一场数据包风暴,但STP随处可见。
    2. 所有无线接入点死亡后不久。
    3. 在尝试诊断无线networking正在发生什么时,所有IP电话都重新启动。
    4. 此后不久,有线机器就失去了与局域网的所有联系。

    唯一的出路是重新启动大楼内的每一台机器。 一个例外是硬件交换机,它不能被重新启动。 他们必须被重新启动。

    在使用PF的FreeBSD中,很容易(在你的pf.conf文件中):

     extif = "tun0" intif = "em0" {other rules}... nat on $intif from any to 192.168.20.8 port 80 -> ($extif) 

    192.168.20.8将是内部Web服务器。