我想写这个文章的时候,突然觉得哪里不对。为什么我在xlog写的文章,都是日网络的?
其实我并不擅长计算机网络基础,这是我知识领域里很薄弱的一块。日站也好,网络业务也好,虽然我会做, 但是那是很顶层的应用,自顶到下,其实我是并不擅长的。
但是这个,既是原因,也是结果。正因为我不擅长,所以就会遇到“很罕见”的问题,然后这个问题其实对大家来说并不是问题。
这也是一种非舒适区悖论。
nat loopback,也叫 hairpinning。它解决的问题很简单,就是。。。就是。。。。就是nat 的loopback!
你的公网ipv4假设是1.2.3.4,你配置了一个端口转发,从1.2.3.4:1000,转发到192.168.1.2:1000,你的(其中一台)路由器是192.168.1.1。如果你身处内网,以/24为例,你的那网ip是192.168.1.200。这个时候,你访问1.2.3.4:1000,是会遇到问题的。
通常上,路由器的端口转发,背后对应的iptable,是从外部(wan侧)作为开始,向内(lan)侧转发。换句话,wan侧监听,任意的(外部)ip请求端口1000,都会被转发到192.168.1.2:1000。
对应规则
-A zone_wan_prerouting -p tcp -m tcp --dport 1000 -j DNAT --to-destination 192.168.1.2:1000
那么问题来了,如果你并不在外网,而在内网,去访问1.2.3.4:1000会怎么样呢?如果不做额外的设置(但是其实这个额外设置在很多情况下是默认存在的),那么单凭这一条dnat规则是无法连通的。因为限制了入口链在wan侧。
那么,上边说这个问题,是个什么问题呢?nat loopback?
如果你不知道这个问题,那么你就无法提出这个问题。
幸好科技带给了我们新东西。在今天,我们可以通过直接问chatgpt,来得到我们的问题是什么。
问问问题。而不是问问题。Ask what the question is, instead of how to solve it.
这是我对于chatgpt的典型用法。在浩瀚的技术门类中,传统的查询和技术学习路线,去空中楼阁地做一件事情,太需要运气了。你掌握了ABCEFG六门技术,但是你无法将他们联结起来,曾经很多时候只能漫无目的地再尝试/积累HIGK,但是你真正缺少的是D。大模型的生产力其实很有限,很多未知的东西她现在还很难做到,但是这个场景很典型。
其实在现代的路由器系统里,nat loopback早就给你考虑好了。
openwrt里,其背后的设计为reflection。
你会得到这样一个规则。
-A zone_lan_prerouting -s 192.168.1.0/24 -d 192.168.0.3/32 -p tcp -m tcp --dport 1000 -m comment --comment "!fw3: 1000 (reflection)" -j DNAT --to-destination 192.168.1.2:1000
注意标题里的话,因为是非桥接环境,所以此处openwrt的wan环境ip是192.168.0.3
桥接环境,openwrt直接pppoe拨号,并不会导致nat loopback失效。但是在非桥接环境下,也就是光猫路由模式下,会的。
这里的问题在于,openwrt的nat loopback,实际上是断断续续出过问题的,加上版本的碎片化,你在嘈杂的互联网里,很难找到你的错误究竟是哪个。
这直接导致了,问题很多,答案也很多,但是大家的插口并不兼容。
你会搜索到各种的,比如说这个某某版本的openwrt的bug呀,你要配置什么内核flag呀,种种“对”的方法。对,但是解答不了你的问题。
这个问题,大模型目前也帮不了你。非但帮不了,她可太擅长在这个问题上擦边捣乱了。
她最擅长的事情是,搭建草台班子。
问题的根本原因是,这条规则,仅设置了192.168.0.3为入口,而实际上的入口,应该也包括public wan。
-A zone_lan_prerouting -s 192.168.1.0/24 -d 192.168.0.3/32 -p tcp -m tcp --dport 1000 -m comment --comment "!fw3: 1000 (reflection)" -j DNAT --to-destination 192.168.1.2:1000
也就是额外需要
-A zone_lan_prerouting -s 192.168.1.0/24 -d 1.2.3.4/32 -p tcp -m tcp --dport 1000 -j DNAT --to-destination 192.168.1.2:1000
从lan侧,访问pub ip,那么直接转发到目的主机。
但是这样不够优雅,对吧
IP=$(curl -s 4.ipw.cn | grep -oE '\b[0-9]{1,3}(\.[0-9]{1,3}){3}\b' | head -n 1)
iptables -t nat -A PREROUTING -s 192.168.1.0/24 -d $IP/32 -p tcp -m tcp --dport 1000 -j zone_wan_prerouting
所以我们就可以直接添加一个自定义的防火墙规则,如果使用nft,那么大模型可以帮你。其实这个两行,也是chatgpt写的。
获取到public ip之后,其实把请求扔回到zone_wan_prerouting
即可,不需要再重复写一次目的主机。
其实可以更简化/更通用为,不过这样就和openwrt的uci绑定了,毕竟还有padavan呢。
LAN=$(uci show network.lan.ipaddr | cut -d"'" -f2)
IP=$(curl -s 4.ipw.cn | grep -oE '\b[0-9]{1,3}(\.[0-9]{1,3}){3}\b' | head -n 1)
iptables -t nat -A PREROUTING -s $LAN/24 -d $IP/32 -p tcp -m tcp --dport 1000 -j zone_wan_prerouting
padavan下,其实处理的更优雅,并没有flection,而是
-A PREROUTING -d 1.2.3.4/32 -j vserver
-A PREROUTING -d 192.168.0.1/32 -j vserver
定义了vserver
作为外部输入链。