Skip to content

fix: never bind TUN direct dialer to the easyss TUN device - #119

Merged
nange merged 5 commits into
masterfrom
fix/tun-direct-dialer-interface-binding
Aug 25, 2026
Merged

fix: never bind TUN direct dialer to the easyss TUN device#119
nange merged 5 commits into
masterfrom
fix/tun-direct-dialer-interface-binding

Conversation

@nange

@nange nange commented Aug 25, 2026

Copy link
Copy Markdown
Owner

问题

开启 tun2socks 后出现两类故障:

  1. 请求死循环:所有出口连接(连服务器、直连目标、DNS)陷入 tun2socks 循环,请求永不完成
  2. 网络全断(WSAENETUNREACH):所有连接立即报"网络位置不可达"

均源于今日提交 ebbc18b(macOS sleep/wake TUN 恢复)引入的直连 dialer 接口探测逻辑。

根因

ebbc18b 通过探测 0.0.0.1 来重新发现默认网卡,但实测发现:

  • Windows 内核拒绝为 0.0.0.0/8 选路GetBestRoute2 无条件返回"网络位置不可达",与路由表内容无关——即使临时加入 0.0.0.0/10.0.0.1/32 精确路由依然失败)→ 探测必然失败 → dialer 无绑定 → TUN 激活后所有直连流量落入 TUN 设备,经 tun2socks 转发回本地代理,代理再拨号又落入 TUN……无限循环
  • 探测失败后的回退枚举在多网卡机器上可能选中虚拟网卡(WSL/Hyper-V/VirtualBox/Tailscale)→ 全部连接 WSAENETUNREACH
  • macOS 上探测成功,所以问题只在 Windows(和 Linux)暴露

修复

  1. 脚本(Windows/Linux):TUN 路由从 0.0.0.0/1 改为 1.0.0.0/7 起(与 darwin 对齐),0.0.0.0/8 漏出 TUN 路由,保证 0.0.0.1 探测在 macOS/Linux 上始终解析到物理网卡
  2. Windows 探测GetIpForwardTable 读取 0.0.0.0/0 默认路由(按 metric 排序并跳过 TUN 设备自己的默认路由——netsh 配置静态网关时自动添加),TUN 激活时也能拿到物理网卡
  3. 防护:直连 dialer 永不绑定 easyss TUN 设备——刷新路径探测到 TUN 设备时保留既有物理绑定,启动路径回退物理网卡枚举

验证

  • 本机(Windows)实测:TUN 激活 / 关闭两种状态下 SysDefaultRoute 均返回物理网卡(WLAN),并断言排除 TUN 设备
  • 实测确认 Windows 对 0.0.0.1 无条件拒绝选路(Find-NetRoute 与 go-netroute 双路验证,含 0.0.0.0/10.0.0.1/32 精确路由实验)
  • go test ./... 全绿;win/linux/darwin 三平台交叉编译通过;lint 0 issues

nange added 5 commits August 25, 2026 17:16
Since ebbc18b the direct dialer re-detects the default interface by
probing 0.0.0.1. On Windows that lookup rejects 0.0.0.0/8 destinations
outright, so detection failed and the dialer stayed unbound: with TUN
active every direct dial (server connection, direct targets, DNS) fell
into the TUN device and looped through tun2socks forever. The fallback
interface enumeration could also pick a virtual adapter, breaking all
connections with WSAENETUNREACH.

- route Windows/Linux TUN traffic from 1.0.0.0/7, mirroring darwin, so
  0.0.0.0/8 stays outside the TUN routes for the 0.0.0.1 probe
- on Windows read the 0.0.0.0/0 default route from the routing table
  (GetIpForwardTable), skipping the TUN device's own default route that
  netsh adds while TUN is active
- keep darwin/linux on the 0.0.0.1 probe, which now resolves to the
  physical interface on every platform
- never bind the direct dialer to the easyss TUN device: reject it in
  the refresh path and fall back to physical interface enumeration at
  startup
errNoDefaultRoute is only referenced by sys_windows.go, so on darwin/linux
builds it was an unused package var and failed the lint CI.
tun-easyss/utun9 were hardcoded in both client/tun.New (device creation)
and util.IsTunIface (TUN device recognition); the two could drift apart.
Reference sharedconfig.DefaultTunDeviceName(Darwin) in both places.
Keep the GetIpForwardTable approach (the only viable way to find the
physical default interface on Windows), but improve readability: name
only the used MIB_IPFORWARDROW fields and keep the layout with anonymous
placeholders, move the two-call buffer dance into getIpForwardTable, and
sort candidates with slices.SortFunc.
IsTunIface matched only the current platform's default device name
(tun-easyss on windows/linux, utun9 on darwin), so tests constructing a
tun-easyss interface failed on macOS CI: the refresh path treated the
TUN device as a plain interface and rebound the direct dialer to it.
Match both default names regardless of the platform.
@nange
nange merged commit 08dbe8c into master Aug 25, 2026
6 checks passed
@nange
nange deleted the fix/tun-direct-dialer-interface-binding branch August 25, 2026 10:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant