一、前言
很多 RouterOS(ROS)玩家在搭建家庭网络或工作室机房时,经常会遇到一个问题:明明已经配置好了外网端口映射(Port Forwarding),用手机的流量访问自己的公网域名时一切正常;可一旦连上家里的内网 WiFi,再去访问同一个域名,网页却一直在转圈圈,怎么也打不开。
“诶,为什么外网能访问,内网反倒访问不了自己的公网域名呢?”
遇到这种情况,千万别怀疑是自己的端口映射做错了。这其实是因为路由器默认不知道该如何处理这种“从内网发往自己公网 IP,又要折返回内网”的数据包。为了解决这个痛点,我们需要配置 Hairpin NAT(回环 NAT / 发卡 NAT) 。
二、Hairpin NAT(回环NAT)原理
Hairpin 网络地址转换(Hairpin NAT,又称 NAT 回环 / NAT Loopback)是指局域网(LAN)上的设备可以通过网关路由器的公网 IP 地址访问同一局域网上的另一台机器。
2.1 外部访问示例
在上述示例中,网关路由器配置了以下 dst-nat(目标地址转换)规则:
/ip firewall nat add chain=dstnat action=dst-nat dst-address=172.16.16.1 dst-port=443 to-addresses=10.0.0.3 to-ports=443 protocol=tcp
当用户从家里的 PC 与 Web 服务器建立连接时,路由器会按照配置执行目标地址转换(DST NAT):
- 客户端发送一个数据包,源 IP 地址为 192.168.88.1,目标 IP 地址为 172.16.16.1,端口为 443,以请求某些 Web 资源;
- 路由器对该数据包进行目标地址转换(DST NAT),将其指向 10.0.0.3,并相应地替换数据包中的目标 IP 地址。源 IP 地址保持不变,仍为 192.168.88.1;
- 服务器对客户端的请求作出响应,回复的数据包中源 IP 地址为 10.0.0.3,目标 IP 地址为 192.168.88.1;
- 路由器判定该数据包属于先前的连接,从而撤销目标地址转换,将原始的目标 IP 地址填入源 IP 地址字段。此时,目标 IP 地址为 192.168.88.1,源 IP 地址为 172.16.16.1;
- 客户端收到了它所期望的回复数据包,连接成功建立。
2.2 内部访问的问题
但是,当与 Web 服务器处于同一网络的客户端请求连接 Web 服务器的公网 IP 地址时,就会出现问题:
- 客户端发送一个数据包,源 IP 地址为 10.0.0.2,目标 IP 地址为 172.16.16.1,端口为 443,以请求某些 Web 资源;
- 路由器对该数据包进行目标地址转换,将其指向 10.0.0.3,并相应地替换数据包中的目标 IP 地址。源 IP 地址保持不变,仍为 10.0.0.2;
- 服务器对客户端的请求作出响应。然而,由于请求的源 IP 地址与 Web 服务器位于同一子网中,Web 服务器不会将回复发回给路由器,而是直接发回给 10.0.0.2,该回复数据包的源 IP 地址为 10.0.0.3;
- 客户端收到了回复数据包,但会将其丢弃,因为它期望收到的是来自 172.16.16.1 的数据包,而不是来自 10.0.0.3 的数据包。
2.3 解决方案:配置 Hairpin NAT 规则
为了解决这个问题,我们需要配置一条新的 src-nat(源地址转换)规则(即Hairpin NAT 规则),如下所示:
/ip firewall nat add action=masquerade chain=srcnat dst-address=10.0.0.3 out-interface=LAN protocol=tcp src-address=10.0.0.0/24
配置上述规则后,流程如下:
- 客户端发送一个数据包,源 IP 地址为 10.0.0.2,目标 IP 地址为 172.16.16.1,端口为 443,以请求某些 Web 资源;
- 路由器对该数据包进行目标地址转换(DST NAT),将其指向 10.0.0.3,并相应地替换数据包中的目标 IP 地址。同时,它还会对该数据包进行源地址转换(SRC NAT),将数据包中的源 IP 地址替换为其 LAN 接口的 IP 地址。此时,目标 IP 地址为 10.0.0.3,源 IP 地址为 10.0.0.1;
- Web 服务器对请求作出响应,并将源 IP 地址为 10.0.0.3 的回复数据包发送回路由器的 LAN 接口 IP 地址 10.0.0.1;
- 路由器判定该数据包属于先前的连接,从而同时撤销源地址和目标地址转换,将原始的目标 IP 地址 10.0.0.3 填入源 IP 地址字段,并将原始的源 IP 地址 172.16.16.1 填入目标 IP 地址字段。
三、PPPoE 动态公网Hairpin NAT 配置指南
注意:在配置 Hairpin NAT 之前,请确保你已经成功配置好了常规的端口映射(dst-nat),即外网已经可以通过公网 IP 正常访问你的内网服务。
3.1 添加内网、外网 地址列表(Address Lists)
3.1.1 添加内网地址
/ip firewall address-list
add address=192.168.12.0/24 list=LANs
3.1.2 配置 PPPoE Profile 自动更新外网地址
为了应对 PPPoE 动态拨号 IP 变化的问题,我们需要在 /ppp profile 中新建(或修改)一个配置文件,利用 On Up 和 On Down 脚本自动将当前的公网 IP 同步到 WANs 地址列表中。
On Up 脚本(拨号成功时执行):
:local Name [/interface pppoe-client get $interface name]
/ip firewall address-list remove [find where list=WANs comment=$Name]
/ip firewall address-list add list=WANs address=$"local-address" comment=$Name
On Down 脚本(断开拨号时执行):
:local Name [/interface pppoe-client get $interface name]
/ip firewall address-list remove [find where list=WANs comment=$Name]
3.1.3 在PPPoE接口中 选择 新建的配置文件
3.2 配置 Hairpin NAT 核心防火墙规则
提示:请将
192.168.12.0/24 替换为你路由器实际使用的内网网段(例如 RouterOS 默认的 192.168.88.0/24 )。
3.2.1 配置 Mangle 规则:建立 Hairpin NAT 连接标记
/ip firewall mangle
add action=mark-connection chain=prerouting comment="Mark connections for hairpin NAT" dst-address-list=WANs new-connection-mark="Hairpin NAT" passthrough=no \
src-address-list=LANs
3.2.2 配置 SRC-NAT 规则:执行源地址伪装 (Masquerade)
/ip firewall nat
add action=masquerade chain=srcnat comment="Hairpin NAT" connection-mark="Hairpin NAT"
到此为止,针对 PPPoE 动态公网 IP 的 Hairpin NAT 核心规则已经全部配置完毕。现在,拿起你的手机或电脑,尝试在局域网内再次通过公网 IP(或绑定的 DDNS 域名)去访问你的内网服务器,你会发现一切都已经畅通无阻了。
附录:参考链接