写在前边#
我想写这个文章的时候,突然觉得哪里不对。为什么我在 xlog 写的文章,都是日网络的?
其实我并不擅长计算机网络基础,这是我知识领域里很薄弱的一块。日站也好,网络业务也好,虽然我会做, 但是那是很顶层的应用,自顶到下,其实我是并不擅长的。
但是这个,既是原因,也是结果。正因为我不擅长,所以就会遇到 “很罕见” 的问题,然后这个问题其实对大家来说并不是问题。
这也是一种非舒适区悖论。
nat loopback#
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?#
那么,上边说这个问题,是个什么问题呢?nat loopback?
如果你不知道这个问题,那么你就无法提出这个问题。
幸好科技带给了我们新东西。在今天,我们可以通过直接问 chatgpt,来得到我们的问题是什么。
问问问题。而不是问问题。Ask what the question is, instead of how to solve it.
这是我对于 chatgpt 的典型用法。在浩瀚的技术门类中,传统的查询和技术学习路线,去空中楼阁地做一件事情,太需要运气了。你掌握了 ABCEFG 六门技术,但是你无法将他们联结起来,曾经很多时候只能漫无目的地再尝试 / 积累 HIGK,但是你真正缺少的是 D。大模型的生产力其实很有限,很多未知的东西她现在还很难做到,但是这个场景很典型。
nat loopback!#
其实在现代的路由器系统里,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
links#
problem!#
桥接环境,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,那么直接转发到目的主机。
但是这样不够优雅,对吧
done#
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
作为外部输入链。