OpenClash
OpenClash copied to clipboard
[Bug] OpenClash 未正确获取并排除路由器非 PPPoE 的 IPv4 WAN 地址,导致 UDP 流量转发在 TProxy 模式下出现回环卡死
Verify Steps
- [X] Tracker 我已经在 Issue Tracker 中找过我要提出的问题
- [X] Latest 我已经使用最新 Dev 版本测试过,问题依旧存在
- [X] Core 这是 OpenClash 存在的问题,并非我所使用的 Clash 或 Meta 等内核的特定问题
- [X] Meaningful 我提交的不是无意义的 催促更新或修复 请求
OpenClash Version
v0.45.121-beta
Bug on Environment
Official OpenWrt
Bug on Platform
Linux-arm64
To Reproduce
让路由器通过 非 PPPoE 方式(如静态分配或 DHCP)获得一个 非保留 IP 段 的 IPv4 WAN 地址(约等于公网地址),在 Fake-IP + TProxy 模式下打开 UDP 流量转发,向这个 IPv4 WAN 地址发送 UDP 包,此时 Clash 会占用 100% 处理器资源卡死。
Describe the Bug
OpenClash 启动时 /etc/init.d/openclash 会 在 mangle 表中加入 UDP TPROXY 目标 (L2380) 并排除本机网络地址 (L2354) (这些规则在 PREROUTING 链中被调用),而本机网络地址则包括 保留 IP 段和 WAN 地址 (L2188),IPv4 WAN 地址又是 通过 openclash_get_network.lua 脚本获取的 。
这个脚本 只读取了 PPPoE 分配的 WAN 地址,不包括其他方式,导致本机的 WAN 地址没有被排除,发往该地址的数据包会被 TPROXY 目标捕获并发向 Clash 内核,Clash 内核会将这个包(通过回环接口)重新发往本机 WAN 地址,然后再次被捕获,重复以上过程并卡死。
执行 ipset list localnetwork
查看 OpenClash 添加的本机 IPv4 地址,可以发现仅有保留地址段:
Name: localnetwork
Type: hash:net
Revision: 6
Header: family inet hashsize 1024 maxelem 65536
Size in memory: 1032
References: 5
Number of entries: 9
Members:
224.0.0.0/4
10.0.0.0/8
169.254.0.0/16
172.16.0.0/12
0.0.0.0/8
240.0.0.0/4
192.168.0.0/16
127.0.0.0/8
100.64.0.0/10
TCP 包不受此影响,因为 TCP 的转发规则是通过 nat 表的 PREROUTING 链调用的,从回环接口传入的包不会经过 nat 表的 PREROUTING 链。IPv6 的 UDP 转发也不存在这个问题,因为 IPv6 的 WAN 地址是通过 ifconfig 获取的 。
执行 ipset list localnetwork6
查看本机 IPv6 地址,包括了 WAN 地址:
Name: localnetwork6
Type: hash:net
Revision: 6
Header: family inet6 hashsize 1024 maxelem 65536
Size in memory: 2760
References: 4
Number of entries: 15
Members:
::ffff:0.0.0.0/96
2002::/16
100::/64
fc00::/7
2001::/32
::1
fd7a:115c:a1e0::e
2001:db8::/32
2001:20::/28
::
64:ff9b::/96
fe80::/10
ff00::/8
::ffff:0:0:0/96
fe80::/64
*IPv6_WAN_IP*
修改 openclash_get_network.lua ,使其能够正确读取 IPv4 WAN 地址,重新启动 OpenClash ,卡死现象消失, Clash 能够正常工作。再次执行 ipset list localnetwork
查看本机网络地址:
Name: localnetwork
Type: hash:net
Revision: 6
Header: family inet hashsize 1024 maxelem 65536
Size in memory: 1096
References: 5
Number of entries: 10
Members:
0.0.0.0/8
192.168.0.0/16
100.64.0.0/10
127.0.0.0/8
169.254.0.0/16
240.0.0.0/4
172.16.0.0/12
224.0.0.0/4
10.0.0.0/8
*IPv4_WAN_IP*
尽管没有测试,但我怀疑这个问题也可能在 LAN 或者其他接口/网络上出现,毕竟本质上和网络区域无关,只要本机使用的地址没有被彻底排除掉,应该都会触发这个问题,例如路由拿到的是一个公网 IP 段,在 LAN 接口上没有 NAT 到一个保留地址段,而是也分配了一个公网地址,只不过可能对于大部分用户而言在 IPv4 上很难遇到这种场景。
TUN 模式没有测试,也没有细看对应的 iptables 规则,不清楚是否存在类似问题。
OpenClash Log
在 Clash 核心日志中可以看到 Clash 在大量转发本机 WAN 地址上的 UDP 数据包。
[INFO] [UDP] IPV4_WAN_IP:47904 --> IPV4_WAN_IP:53478 match GeoIP(CN) using 直连[DIRECT] [INFO] [UDP] IPV4_WAN_IP:49804 --> IPV4_WAN_IP:46216 match GeoIP(CN) using 直连[DIRECT] [INFO] [UDP] IPV4_WAN_IP:48211 --> IPV4_WAN_IP:53478 match GeoIP(CN) using 直连[DIRECT] [INFO] [UDP] IPV4_WAN_IP:58416 --> IPV4_WAN_IP:46216 match GeoIP(CN) using 直连[DIRECT] [INFO] [UDP] IPV4_WAN_IP:40292 --> IPV4_WAN_IP:53478 match GeoIP(CN) using 直连[DIRECT] [INFO] [UDP] IPV4_WAN_IP:34777 --> IPV4_WAN_IP:46216 match GeoIP(CN) using 直连[DIRECT] [INFO] [UDP] IPV4_WAN_IP:58908 --> IPV4_WAN_IP:53478 match GeoIP(CN) using 直连[DIRECT] [INFO] [UDP] IPV4_WAN_IP:40106 --> IPV4_WAN_IP:46216 match GeoIP(CN) using 直连[DIRECT] [INFO] [UDP] IPV4_WAN_IP:40687 --> IPV4_WAN_IP:53478 match GeoIP(CN) using 直连[DIRECT] [INFO] [UDP] IPV4_WAN_IP:54192 --> IPV4_WAN_IP:46216 match GeoIP(CN) using 直连[DIRECT]
OpenClash Config
No response
Expected Behavior
修复后 OpenClash 应当能够完整获取 IPv4 WAN 地址并从 UDP TProxy 中排除,避免出现死循环。或许可采用与 IPv6 相似的方法通过 ifconfig 获取地址。
Screenshots
No response
基于楼主的提示,我在基于nftables的fw4环境下也测了一下
(没看源代码,直接用nft list table inet fw4
导出了启动openclash后的整个防火墙设置)
应该说不管是哪个模式,openclash都是通过“把wan口ip加入名为localnetwork的ipset/nftset”的方法来防回环的。
~~不过非pppoe还能拿到公网ipv4。。莫非是sjtu的在校学生?~~
怎么分辨是不是公网ip
基于楼主的提示,我在基于nftables的fw4环境下也测了一下 (没看源代码,直接用
nft list table inet fw4
导出了启动openclash后的整个防火墙设置) 应该说不管是哪个模式,openclash都是通过“把wan口ip加入名为localnetwork的ipset/nftset”的方法来防回环的。~不过非pppoe还能拿到公网ipv4。。莫非是sjtu的在校学生?~
其实可以在局域网手动分配公网 ip 吧,只不过不规范以及从外面路由不到里面来罢了,正常情况应该都不会这么干。
怎么分辨是不是公网ip
我觉得核心不是分辨公网 ip ,而是让发往本机的包不要再过 TProxy 进 Clash ,所以应该可以直接像 v6 一样用 ifconfig 把本机所有网卡的 ip 都加到 localnetwork ipset 里面排除掉,这样这些包完全不会进 Clash ;或者在 openclash 链里加一个条件,排除掉从回环接口进来的包 (iptables -t mangle -A openclash -i lo -j RETURN
) ,这样发往本机的包最多只会在 Clash 里过一次,不会无限循环。