Seiry

Seiry

非桥接拨号的openwrt中存在的nat loopback问题

写在前边#

我想写这个文章的时候,突然觉得哪里不对。为什么我在 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

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作为外部输入链。

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。