TUN 模式与系统代理的工作机制对比:流量到底在哪一层被接管
对比系统代理依赖应用主动读取代理设置、TUN 通过虚拟网卡接管全部 IP 流量的差异,说明命令行工具、游戏与 UDP 应用为何在两种模式下表现不同,以及各自适合的场景。
先把结论摆在前面
系统代理和 TUN 模式不在同一层。系统代理是应用层约定:操作系统把代理地址写进设置,应用主动来读,读了才走代理。TUN 模式是网络层接管:mihomo 创建一块虚拟网卡,内核把符合条件的 IP 包全部送进网卡,应用不知道代理存在,也无法绕过。
一句话:系统代理靠应用配合,TUN 模式靠内核路由。两者在命令行、游戏、UDP 场景下表现完全不同,根源就在这里。
系统代理:应用主动读取,代理才生效
开启系统代理后,Windows 在「设置」→「网络和 Internet」→「代理」写入地址,macOS 在「系统设置」→「网络」→「代理」写入同一份信息。mihomo 默认在 127.0.0.1:7890 监听混合端口,同时接受 HTTP 与 SOCKS5 连接。
关键在第二步:应用必须主动向系统查询代理设置,并真正用这个地址建立连接。于是出现四类分化:
- 浏览器与多数 Electron 应用走系统网络 API,会读取,系统代理对它们有效。
- 游戏引擎常直接创建 socket,不查系统代理,直接裸连。
curl、wget、git在 Windows 与 macOS 上默认不读系统代理,只认http_proxy/https_proxy环境变量。- 所有 UDP 流量:HTTP 代理只承载 TCP;SOCKS5 虽有 UDP ASSOCIATE 扩展,绝大多数应用并不实现。
所以「开了代理,终端里 curl 还是直连」不是配置坏了,是系统代理的机制边界。对照如下:
| 流量类型 | 系统代理 | TUN 模式 |
|---|---|---|
| 浏览器 HTTP/HTTPS | 接管 | 接管 |
| 命令行 curl / git / brew | 不接管(需环境变量) | 接管 |
| 游戏与语音 UDP | 不接管 | 接管 |
| QUIC / HTTP/3 | 不接管(浏览器降级 TCP) | 接管 |
| 局域网设备访问 | 不受影响 | 需排除内网段 |
TUN 模式:虚拟网卡接管 IP 层
TUN 是内核提供的虚拟网络设备。mihomo 开启 TUN 后创建虚拟网卡——Windows 用 wintun 驱动,macOS 用 utun,Linux 用 tun——再修改路由表,把非本机地址的 IP 包指向这块网卡。
流量路径因此改变:
- 应用把数据交给内核;
- 内核按路由表送入虚拟网卡;
- mihomo 在用户态读到完整 IP 包;
- 解出 TCP 或 UDP,按规则匹配;
- 经节点转发出去。
接管层级是网络层(L3),不是应用层(L7)。mihomo 不关心应用怎么发数据,只看到一个个 IP 包。三个直接结果:应用完全不知道自己在被代理;TCP 与 UDP 一视同仁;无论进程用什么语言、什么网络库,都逃不出路由表。
代价同样明确:创建虚拟网卡需要管理员或 root 权限;路由表被改动,内网与局域网访问需要额外放行。
DNS 是 TUN 模式容易忽略的另一半。TUN 配置里的 dns-hijack 把所有发往 53 端口的查询劫持到本地解析,配合 enhanced-mode: fake-ip,域名解析也走代理链路,避免 DNS 泄漏。以 Clash Verge 默认配置为例:
tun:
enable: true
stack: mixed
dns-hijack:
- any:53
dns:
enable: true
enhanced-mode: fake-ip
nameserver:
- https://doh.pub/dns-query
开启后,解析无法直连的域名返回的是 198.18.0.0/16 段的 Fake-IP,真实解析由远端 DoH 完成——这是判断 TUN 是否真正生效的直观信号。
命令行、游戏与 UDP:两种模式的分水岭
命令行工具
git clone、curl、brew、npm、go install 在系统代理模式下全部直连。只想开系统代理就覆盖它们,需要手动导出环境变量:
export https_proxy=http://127.0.0.1:7890
export http_proxy=http://127.0.0.1:7890
export all_proxy=socks5://127.0.0.1:7890
7890 是混合端口,HTTP 与 SOCKS5 共用,上面三行让 git、curl、npm 走同一个入口。TUN 模式下不需要这一步,残留的环境变量反而会造成重复代理,建议清掉。
游戏与 UDP
游戏匹配、语音、状态同步大量使用 UDP。系统代理在协议层面就不承载 UDP;即便开启 SOCKS5,游戏客户端也不会去实现 UDP ASSOCIATE。于是常见「能登录、匹配不到人」「语音时断时续」的半通状态。TUN 把 UDP 包原样收进虚拟网卡再按规则转发,表现接近直连。
QUIC 与 HTTP/3
Chrome、Safari 对支持 HTTP/3 的站点优先尝试 QUIC(UDP/443)。系统代理下浏览器检测到代理不支持 UDP,自动降级回 TCP;TUN 下 QUIC 流量直接被接管,不降级。
场景对照:什么时候用哪个
| 场景 | 推荐模式 | 理由 |
|---|---|---|
| 日常浏览网页 | 系统代理 | 无需管理员权限,资源占用低 |
| 开发与命令行 | TUN 模式 | 一次性覆盖 git / npm / curl |
| 游戏、语音、视频通话 | TUN 模式 | UDP 必须由网络层接管 |
| 公司内网与代理并存 | 系统代理 + 规则 | 内网段不接管,策略更可控 |
| 不允许程序申请管理员权限 | 系统代理 | TUN 创建网卡需要提权 |
| 全局抓包、全量测试 | TUN 模式 | 所有进程统一路径 |
切换前的检查清单
- 连通性:用
curl -I https://www.gstatic.com/generate_204验证,别用 ping——ICMP 是否被接管取决于节点与栈,不能作为代理生效的依据。 - DNS:解析一个无法直连的域名,返回
198.18.x.x说明 Fake-IP 生效;返回真实 IP 则检查dns-hijack与enhanced-mode。 - 局域网:打印机、NAS、路由器后台在 TUN 下可能被截走,在
tun段加route-exclude-address: 192.168.0.0/16,或用规则直连。 - Windows 兼容:游戏或 P2P 连不上时,依次切换
stack: system→gvisor→mixed再测。 - 残留环境变量:系统代理模式下,终端执行
env | grep -i proxy,有残留就unset,否则命令行会继续走旧代理。
最后提醒:两种模式不是二选一的单选题。日常浏览用系统代理,开发与游戏场景临时切到 TUN,是多数用户最省心的组合;如果网络环境固定、又不想每次手动切换,也可以把 TUN 常开,再在规则里把内网与国内域名直连,兼顾接管范围与访问速度。切换后记得用上面的清单复核一遍,确认 DNS、局域网与命令行三个环节都符合预期。
常见误区:开 TUN 后又开系统代理。两者不会叠加,浏览器流量只是多绕一步进 7890 再出来。TUN 模式下把系统代理开关关掉,路径最短,排查问题也更干净。