修正TUN下NetworkManager的网络状态
不知从何时开始,当启用代理软件并使用TUN虚拟网卡时,NetworkManager会错误地提示「网络连接受限」,但事实上网络能够正常使用。这不仅会让人困惑,还会影响到依赖网络状态通知的应用(例如调度网络服务)。
解决方法
将connectivity对应服务器的IP地址绕过TUN网卡即可。Arch Linux所使用的默认服务器为ping.archlinux.org,其IP地址为95.216.195.133和2a01:4f9:c010:2636::1。其他发行版可能需要根据实际情况调整。对于sing-box配置文件:
在入站的tun网卡配置中加入:
1 | "route_exclude_address_set": ["arch-linux-ping"] |
同时添加对应的规则集:
1 | { |
探究过程
在Arch Linux上,与网络连通性相关的配置文件位于/usr/lib/NetworkManager/conf.d/20-connectivity.conf,其中指定了uri为http://ping.archlinux.org/nm-check.txt。根据NetworkManager的文档,程序会使用SO_BINDDEVICE检查网络是否通畅[1]。源代码中,其nm-connectivity.c中调用libcurl相关函数,使用curl_easy_setopt(ehandle, CURLOPT_INTERFACE, cb_data->ifspec)绑定网络接口,随后检查是否能收到响应[2]。而libcurl会调用setsockopt,以实现绑定[3]。使用strace跟踪系统调用时,发现其调用了setsockopt,和先前分析一致:
1 | 821 setsockopt(30, SOL_SOCKET, SO_BINDTODEVICE, "enp55s0\0", 8) = 0 |
依此,我编写了一个类似的测试程序:
1 |
|
使用tcpdump监听网卡流量时,我发现TUN接口sing-box上存在与deepchirp.com相关的多路流量,但物理网卡wlan0上似乎并无相关流量。而在关闭sing-box后,wlan0上则能捕获到相关流量。由此可见,sing-box会将流量劫持到其TUN网卡上,导致绑定物理网卡的setsockopt调用失效,从而使NetworkManager误判网络状态。
因此,只需将检查连通性的相关流量绕过TUN网卡即可。我一开始考虑使用策略路由,为ping.archlinux.org的流量打上路由标记(使用default_mark即可),并使用nftables及ip rule将其路由到物理网卡,但感觉不够优雅:因为当sing-box退出后,相关的路由规则无用但仍存在。后来发现,ping.archlinux.org的IP地址似乎并不常变动——至少2022年8月的IP地址即与今相同[4]——因此选择直接将其IP地址绕过TUN网卡。不过,其他发行版的连通性检查服务器地址可能不同,IP地址若时常变动,可能还是使用策略路由更加方便。
NetworkManager. NetworkManager.conf — NetworkManager configuration file [EB/OL]. (n.d.)[2025-10-20]. ↩︎
NetworkManager. src/core/nm-connectivity.c — apertis/v2026pre, pkg/network-manager (GitLab) [EB/OL]. (n.d.)[2025-10-20]. ↩︎
Android Open Source Project. lib/connect.c — platform/external/curl (main) [EB/OL]. (n.d.)[2025-10-20]. ↩︎
Andreiva. [SOLVED] Arch makes many reverse dns lookups for redirect.archlinux.org [EB/OL]. Arch Linux Forums, (2022-08-22)[2025-10-20]. ↩︎
