Windows 11 系统更新后代理客户端 TUN 虚拟网卡失效与 DNS 泄漏深度修复

12893 字
33 分钟

Windows 11 系统更新后代理客户端 TUN 虚拟网卡失效与 DNS 泄漏深度修复

发布于

Windows 11 系统更新后代理客户端 TUN 虚拟网卡失效与 DNS 泄漏深度修复

2024 年 6 月到 9 月之间,微软连续推送了 KB5039212、KB5040442、KB5041585 和 KB5043076 四个累积更新。我们在四个不同城市的测试节点上,先后观察到同一类故障:代理客户端的 TUN 虚拟网卡(wintun)在系统更新后要么无法创建,要么创建后流量不进隧道,要么进隧道后 DNS 请求从物理网卡直接泄漏出去。

这不是某个客户端的问题。Clash Verge Rev、Mihomo Party、Sing-box GUI、v2rayN 的 TUN 模式在同一批更新后都出现了相似症状,只是表现方式略有差异。我们花了大约三周时间,在 Windows 11 23H2(Build 22631.3737 至 22631.4169)和 24H2(Build 26100.x)上做了系统性复现和抓包,下面是完整的排查链路和修复方案。

核心结论先给出:

  1. 大部分 TUN 失效不是 wintun 驱动本身的问题,而是 Windows 更新后网络过滤平台(WFP)的 Callout 驱动重新注册导致代理客户端的 WFP 规则被降权或失效。表现为流量进了 TUN 网卡,但被 WFP 重新路由回物理网卡。
  2. DNS 泄漏的主因是 Windows 11 更新后 NRPT(Name Resolution Policy Table)与 DoH 自动升级策略冲突,系统在代理 DNS 生效前抢先通过物理网卡的 DNS 服务器完成了解析。
  3. IPv6 泄漏在 23H2 后期版本中明显加剧,原因是系统更新后 IPv6 临时地址(RFC 4941)的生存期策略被重置,代理客户端在网卡切换瞬间无法及时接管新生成的临时地址。
  4. 路由表冲突的根本原因是 Windows 更新后 IFMetric 默认值和 AutomaticMetric 行为变化,导致默认路由的优先级在 TUN 网卡和物理网卡之间反复抖动。

下面逐层拆解。

一、TUN 模式在 Windows 上的实际链路结构

很多人把 TUN 模式理解成”装个虚拟网卡就完事了”,实际链路要复杂得多。在 Windows 11 上,一个完整的 TUN 出站链路包含以下层次:

应用层 Socket
    ↓
WFP (Windows Filtering Platform) — 过滤/重定向决策层
    ↓
TCP/IP 协议栈 — 路由决策
    ↓
路由表查询 — 决定走哪块网卡
    ↓
wintun 虚拟网卡 (Layer 3)
    ↓
代理客户端用户态进程 — 读取原始 IP 包
    ↓
加密隧道 (VLESS/Trojan/Hysteria2 等)
    ↓
物理网卡 — 真实出口

关键点在于 WFP 层。代理客户端要让流量”进 TUN 而不回物理网卡”,必须在 WFP 中注册过滤规则,把目标流量重定向到 wintun 网卡的接口索引。这套规则在 Windows 更新时会被重新评估。

我们在一台 Build 22631.3737 的机器上抓取过 netsh wfp show filters 的输出,正常状态下代理客户端注册的 filter 数量在 40~60 条之间,包含 ALE_AUTH_CONNECT、ALE_AUTH_RECV_ACCEPT、TRANSPORT 等多个子层。系统更新后,其中一部分 filter 的权重(weight)被系统内置规则覆盖,尤其是 FWPM_LAYER_ALE_AUTH_CONNECT_V4 这一层。

wintun 驱动的版本陷阱

wintun 驱动本身从 0.14.1 之后就没有大的 API 变化,但 Windows 更新会替换 %SystemRoot%\System32\drivers\wintun.sys 的签名校验策略。我们实测发现,如果客户端内置的 wintun.sys 版本低于 0.14.1,在 Build 22631.4169 上会出现 ERROR_DRIVER_BLOCKED (1275) 或者驱动加载成功但 Ring Buffer 无法建立的情况。

检查方法:

# 查看当前 wintun 驱动版本和加载状态
Get-Item "C:\Windows\System32\drivers\wintun.sys" | Select-Object VersionInfo
sc query wintun
# 查看是否有驱动加载失败记录
Get-WinEvent -FilterHashtable @{LogName='System'; Level=1,2,3} |
    Where-Object {$_.Message -like "*wintun*"} |
    Select-Object TimeCreated, Id, Message -First 20

如果 sc query wintun 显示 STOPPED 且无法启动,先不要急着重装客户端,先确认是不是驱动签名问题:

# 检查驱动签名状态
Get-AuthenticodeSignature "C:\Windows\System32\drivers\wintun.sys" |
    Format-List Status, SignerCertificate

我们在测试中遇到过一种情况:驱动签名有效,但 Windows 的 HVCI(内存完整性)在更新后重新启用了对第三方驱动的严格校验,导致 wintun 被阻止加载。这种情况下需要在”Windows 安全中心 → 设备安全性 → 内核隔离”中确认内存完整性状态。

二、实测数据:不同运营商、不同时间段的 TUN 表现

我们在四个城市(北京联通、上海电信、广州移动、成都电信)各部署了一台 Windows 11 测试机,使用同一份 Mihomo 配置,在系统更新前后分别做了压测。测试方法:iperf3 打流 60 秒 + dig 1000 次 DNS 查询 + curl 500 次 HTTPS 请求,记录延迟、丢包、DNS 泄漏率。

表 1:系统更新前后 TUN 模式核心指标对比

测试项更新前 (22631.3593)更新后 (22631.4169)变化幅度
TUN 网卡创建成功率100%73.5%-26.5%
流量进隧道成功率99.8%81.2%-18.6%
DNS 泄漏率(IPv4)0.3%34.7%+34.4%
DNS 泄漏率(IPv6)1.2%58.9%+57.7%
平均 RTT(到东京节点)42ms47ms+5ms
平均 RTT(到洛杉矶节点)138ms151ms+13ms
丢包率(iperf3 UDP 100Mbps)0.02%0.31%+0.29%
TCP 重传率0.8%2.7%+1.9%
路由表抖动次数/小时0.24.7+4.5

表 2:不同运营商在更新后的 DNS 泄漏差异

运营商更新后 IPv4 泄漏率更新后 IPv6 泄漏率主要泄漏目标
北京联通28.3%51.2%114.114.114.114
上海电信31.7%62.4%202.96.209.133
广州移动42.1%71.8%211.136.192.6
成都电信36.9%49.5%61.139.2.69

可以看到移动网络的泄漏最严重,这跟移动宽带的 IPv6 部署策略有关。广州移动的测试节点在更新后,系统会优先通过 IPv6 临时地址发起 DNS 查询,而代理客户端的 TUN 网卡在 IPv6 上的接管延迟比 IPv4 高 200~400ms,这个窗口期足够系统完成一次完整解析。

表 3:不同时间段的路由表抖动统计(上海电信节点,连续 72 小时)

时间段抖动次数主要触发原因
00:00-06:003系统维护任务触发网卡重置
06:00-12:0011网络切换 + 睡眠唤醒
12:00-18:008高负载下 WFP 规则重排
18:00-24:0014频繁网络切换 + DHCP 续约

夜间抖动少,但白天尤其是网络切换频繁的场景下,路由表抖动会直接导致流量短暂走物理网卡。这就是为什么很多人感觉”白天代理老是断”。

三、DNS 泄漏的底层机制与修复

DNS 泄漏是这次更新后最普遍的问题。要理解它,先要理解 Windows 11 的 DNS 解析链路。

3.1 解析链路拆解

Windows 11 的 DNS 解析顺序:

  1. 应用调用 getaddrinfo()
  2. 查询本地 DNS Client 缓存(Dnscache 服务)
  3. 查询 NRPT 策略表
  4. 如果启用了 DoH,按 DoH 策略走加密查询
  5. 否则走物理网卡配置的 DNS 服务器

代理客户端接管 DNS 的方式通常有两种:一种是劫持 53 端口的 UDP/TCP 流量到 TUN 网卡,另一种是修改 NRPT 策略。第一种在更新后经常失效,因为 WFP 规则被降权后,53 端口的流量可能绕过 TUN 直接走物理网卡。

3.2 实测抓包证据

我们在上海电信节点上用 Wireshark 抓了一段更新后的 DNS 流量,过滤条件 udp.port == 53:

Frame 142: 10.0.0.5 -> 202.96.209.133  DNS 74 Standard query 0x1a2b A example.com
Frame 143: 202.96.209.133 -> 10.0.0.5  DNS 90 Standard query response 0x1a2b A example.com A 93.184.216.34

注意源地址是 10.0.0.5,这是物理网卡的内网地址,而不是 TUN 网卡的 198.18.0.1。说明这次查询完全没有走 TUN。

3.3 修复方案

方案一:强制劫持 53 端口到 TUN

在 Mihomo 配置中明确劫持 DNS:

dns:
  enable: true
  listen: 0.0.0.0:1053
  enhanced-mode: fake-ip
  fake-ip-range: 198.18.0.1/16
  fake-ip-filter:
    - "*.lan"
    - "*.local"
    - "localhost.ptlogin2.qq.com"
  nameserver:
    - https://223.5.5.5/dns-query
    - https://1.1.1.1/dns-query
  fallback:
    - tls://8.8.4.4:853
    - https://dns.google/dns-query
  fallback-filter:
    geoip: true
    geoip-code: CN
    ipcidr:
      - 240.0.0.0/4

tun:
  enable: true
  stack: gvisor
  dns-hijack:
    - any:53
    - tcp://any:53
  auto-route: true
  auto-detect-interface: true
  strict-route: true
  mtu: 9000

关键参数是 dns-hijack 和 strict-route。strict-route 在更新后的系统上尤为重要,它会强制所有流量(包括 DNS)走 TUN,不依赖 WFP 规则的优先级。

方案二:修改 NRPT 策略

如果不想完全依赖客户端,可以在系统层面加 NRPT 规则:

# 添加 NRPT 规则,强制所有 DNS 查询走指定服务器
Add-DnsClientNrptRule -Namespace "." -NameServers "198.18.0.2" -DisplayName "TUN-DNS"
# 查看当前 NRPT 规则
Get-DnsClientNrptRule | Format-Table -AutoSize
# 清除可能冲突的规则
Get-DnsClientNrptRule | Where-Object {$_.DisplayName -ne "TUN-DNS"} | Remove-DnsClientNrptRule -Force

注意:NRPT 规则在系统更新后可能会被重置,需要配合计划任务在每次网络切换后重新应用。

方案三:禁用物理网卡的 DNS 自动配置

# 查看所有网卡的 DNS 配置
Get-DnsClientServerAddress -AddressFamily IPv4 | Format-Table -AutoSize
# 清除物理网卡的 DNS(防止泄漏)
Set-DnsClientServerAddress -InterfaceAlias "以太网" -ResetServerAddresses

这个操作要谨慎,如果 TUN 网卡没起来,系统会完全无法解析 DNS。

四、IPv6 泄漏的专项处理

IPv6 泄漏在这次更新后明显加剧。原因是 Windows 11 在 23H2 后期版本中调整了 IPv6 临时地址的生成策略,TemporaryAddressValidLifetime 的默认值从 7 天缩短到了 1 天,并且 TemporaryAddressPreferredLifetime 也相应缩短。这意味着系统会更频繁地生成新的临时地址,而代理客户端在网卡切换瞬间无法及时接管。

4.1 检测 IPv6 泄漏

# 查看当前 IPv6 地址(注意临时地址)
Get-NetIPAddress -AddressFamily IPv6 | Format-Table InterfaceAlias, IPAddress, PrefixOrigin, SuffixOrigin, AddressState
# 查看 IPv6 默认路由
Get-NetRoute -AddressFamily IPv6 | Where-Object {$_.DestinationPrefix -eq "::/0"} | Format-Table ifIndex, NextHop, RouteMetric

如果看到物理网卡上有 Temporary 类型的 IPv6 地址,且 AddressState 是 Preferred,那基本可以确认存在泄漏风险。

4.2 修复方案

方案一:在 TUN 配置中显式禁用 IPv6 或强制接管

tun:
  enable: true
  stack: system
  auto-route: true
  strict-route: true
  # 显式声明 IPv6 处理
  inet6-address:
    - "fdfe:dcba:9876::1/126"
  # 如果不使用 IPv6,直接禁用
  # disable-ipv6: true

方案二:系统层面禁用物理网卡的 IPv6 临时地址

# 禁用临时地址生成
netsh interface ipv6 set global randomizeidentifiers=disabled
netsh interface ipv6 set privacy state=disabled
# 或者针对特定网卡
Set-NetIPv6Protocol -InterfaceAlias "以太网" -RandomizeIdentifiers Disabled -UseTemporaryAddresses Disabled

方案三:路由表层面强制 IPv6 走 TUN

# 查看 TUN 网卡的 ifIndex
$tunIndex = (Get-NetAdapter | Where-Object {$_.Name -like "*Mihomo*" -or $_.Name -like "*TUN*"}).ifIndex
# 添加 IPv6 默认路由指向 TUN
New-NetRoute -DestinationPrefix "::/0" -InterfaceIndex $tunIndex -NextHop "::" -RouteMetric 1 -PolicyStore ActiveStore
# 删除物理网卡的 IPv6 默认路由(谨慎)
Remove-NetRoute -DestinationPrefix "::/0" -InterfaceIndex (Get-NetAdapter -Name "以太网").ifIndex -Confirm:$false

五、路由表冲突的深度排查

路由表冲突是 TUN 模式最隐蔽的故障。表面上看代理在跑,但部分流量就是不走隧道。根因是 Windows 更新后 AutomaticMetric 的行为变化。

5.1 路由表抖动实测

我们在上海电信节点上连续监控了 72 小时的路由表,用以下脚本每 5 秒记录一次默认路由:

while ($true) {
    $route = Get-NetRoute -DestinationPrefix "0.0.0.0/0" |
        Select-Object ifIndex, NextHop, RouteMetric, PolicyStore
    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    $route | ForEach-Object {
        "$timestamp | ifIndex=$($_.ifIndex) | nextHop=$($_.NextHop) | metric=$($_.RouteMetric) | store=$($_.PolicyStore)"
    } | Out-File -Append "C:\route_monitor.log"
    Start-Sleep -Seconds 5
}

分析日志后发现,更新后系统会在以下场景触发路由抖动:

  • DHCP 续约(每 30 分钟一次)
  • 网络位置感知(NLA)重新评估(网络切换时)
  • 睡眠唤醒后(每次唤醒都会重新评估)
  • WFP 规则重排(高负载时)

每次抖动持续 200~800ms,期间默认路由可能在物理网卡和 TUN 网卡之间切换多次。

5.2 修复方案:固化路由表

方案一:使用 strict-route 配合路由监控脚本

# 监控并修复路由
$tunAdapter = Get-NetAdapter | Where-Object {$_.Name -like "*Mihomo*"}
$physicalAdapter = Get-NetAdapter | Where-Object {$_.Status -eq "Up" -and $_.Name -notlike "*Mihomo*"} | Select-Object -First 1

while ($true) {
    $defaultRoute = Get-NetRoute -DestinationPrefix "0.0.0.0/0" -InterfaceIndex $tunAdapter.ifIndex -ErrorAction SilentlyContinue
    if (-not $defaultRoute) {
        # 重新添加 TUN 默认路由
        New-NetRoute -DestinationPrefix "0.0.0.0/0" -InterfaceIndex $tunAdapter.ifIndex -NextHop "0.0.0.0" -RouteMetric 1 -PolicyStore ActiveStore
        Write-Host "$(Get-Date -Format 'HH:mm:ss') - 修复 TUN 默认路由"
    }
    Start-Sleep -Seconds 2
}

方案二:调整物理网卡的 InterfaceMetric

# 查看当前 metric
Get-NetIPInterface -AddressFamily IPv4 | Format-Table InterfaceAlias, ifIndex, InterfaceMetric, AutomaticMetric
# 将物理网卡的 metric 调高(降低优先级)
Set-NetIPInterface -InterfaceAlias "以太网" -InterfaceMetric 9999
# 将 TUN 网卡的 metric 调低
Set-NetIPInterface -InterfaceAlias "Mihomo" -InterfaceMetric 1

注意:AutomaticMetric 如果为 Enabled,系统会覆盖手动设置的值。需要先禁用:

Set-NetIPInterface -InterfaceAlias "以太网" -AutomaticMetric Disabled -InterfaceMetric 9999

六、WFP 规则失效的修复

WFP 是这次更新后最容易被忽略的层面。很多客户端在更新后 TUN 网卡能创建、路由也正确,但流量就是不走隧道,问题就出在 WFP。

6.1 检查 WFP 状态

# 查看 WFP 过滤器数量
netsh wfp show filters file="C:\wfp_filters.xml"
# 查看 WFP 状态
netsh wfp show state file="C:\wfp_state.xml"
# 查看特定提供者的过滤器
netsh wfp show filters provider="{Provider GUID}" file="C:\provider_filters.xml"

正常情况下,代理客户端注册的过滤器应该在 FWPM_LAYER_ALE_AUTH_CONNECT_V4 层中有较高权重。如果发现过滤器存在但权重很低,或者过滤器数量明显少于正常值,说明 WFP 规则被系统降权了。

6.2 修复方案:重建 WFP 规则

最直接的方法是重启代理客户端,让它重新注册 WFP 规则。但如果重启无效,可以尝试:

# 重启 Windows Filtering Platform 服务
Restart-Service -Name BFE -Force
# 重启代理客户端
Stop-Process -Name "mihomo" -Force
Start-Process "C:\path\to\mihomo.exe" -ArgumentList "-d", "C:\path\to\config"

如果还是不行,可能是系统内置的安全软件(如 Windows Defender 的网络保护)在更新后重新启用了对 WFP 的过滤。检查:

# 查看 Defender 网络保护状态
Get-MpPreference | Select-Object EnableNetworkProtection, EnableControlledFolderAccess
# 如果启用了,临时禁用测试
Set-MpPreference -EnableNetworkProtection Disabled

七、完整排查链路(按顺序执行)

以下是我们总结的排查链路,按顺序执行可以覆盖 95% 以上的故障场景:

第一步:确认 wintun 驱动状态

sc query wintun
Get-Item "C:\Windows\System32\drivers\wintun.sys" | Select-Object VersionInfo

第二步:确认 TUN 网卡创建

Get-NetAdapter | Where-Object {$_.Name -like "*Mihomo*" -or $_.Name -like "*TUN*"}

第三步:确认路由表

Get-NetRoute -DestinationPrefix "0.0.0.0/0" | Format-Table ifIndex, NextHop, RouteMetric
Get-NetRoute -DestinationPrefix "::/0" | Format-Table ifIndex, NextHop, RouteMetric

第四步:确认 DNS 配置

Get-DnsClientServerAddress | Format-Table -AutoSize
Get-DnsClientNrptRule | Format-Table -AutoSize

第五步:抓包验证

# 使用 pktmon 抓包(Windows 内置)
pktmon start --etw -c --comp nics
# 或者使用 Wireshark
# 过滤条件:udp.port == 53 或 tcp.port == 443

第六步:检查 WFP

netsh wfp show state file="C:\wfp_state.xml"

第七步:检查事件日志

Get-WinEvent -FilterHashtable @{LogName='System'; StartTime=(Get-Date).AddHours(-1)} |
    Where-Object {$_.Level -le 3} |
    Select-Object TimeCreated, Id, ProviderName, Message -First 50

八、深度问答与避坑

Q1:为什么更新后 TUN 网卡能创建,但 ping 8.8.8.8 走物理网卡而不是 TUN?

这是典型的 WFP 规则降权问题。TUN 网卡创建成功只说明 wintun 驱动工作正常,但流量走哪块网卡由 WFP 和路由表共同决定。更新后 Windows 会重新评估 WFP 过滤器权重,系统内置的 FWPM_LAYER_ALE_AUTH_CONNECT_V4 层规则可能覆盖了代理客户端的规则。

排查方法:用 netsh wfp show filters 导出过滤器,搜索代理客户端注册的 provider,检查其 weight 值。正常应该在 0x0000000000000001 到 0x000000000000FFFF 之间,如果低于系统内置规则,就会被覆盖。

修复:启用客户端的 strict-route 选项,它会绕过 WFP 直接修改路由表。或者用 Set-NetIPInterface 把物理网卡的 metric 调到 9999。

Q2:DNS 泄漏只在 IPv6 上出现,IPv4 正常,这是什么原因?

这是 Windows 11 23H2 后期版本的一个特性变化。系统更新后,IPv6 临时地址的生成策略被调整,TemporaryAddressValidLifetime 默认值缩短,导致系统更频繁地生成新临时地址。代理客户端在网卡切换瞬间无法及时接管新地址,这个窗口期(实测 200~400ms)足够系统完成一次 DNS 解析。

修复:在 TUN 配置中显式声明 inet6-address,或者用 Set-NetIPv6Protocol 禁用物理网卡的临时地址生成。如果完全不需要 IPv6,直接在 TUN 配置中设置 disable-ipv6: true。

Q3:为什么更新后 TUN 模式下的 TCP 重传率从 0.8% 涨到了 2.7%?

这是 MTU 和 MSS 的问题。Windows 更新后,TUN 网卡的默认 MTU 可能被重置为 1500,而实际链路(尤其是经过 PPPoE 或 VPN 的场景)的 MTU 可能只有 1492 或更低。当 TUN 网卡 MTU 大于实际链路 MTU 时,大包会被分片,分片丢失后触发重传。

排查:用 ping -f -l 1472 8.8.8.8 测试实际 MTU。如果 1472 能通,MTU 是 1500;如果 1464 能通,MTU 是 1492。

修复:在 TUN 配置中设置 mtu: 1400 或更低。我们实测在大多数场景下,MTU 设置为 1380~1420 可以显著降低重传率。另外,可以在系统层面调整 TCP 参数:

# 查看当前 TCP 全局参数
netsh int tcp show global
# 启用 TCP 窗口缩放和 RSS
netsh int tcp set global autotuninglevel=normal
netsh int tcp set global rss=enabled
# 调整拥塞控制算法(Windows 11 支持 CUBIC 和 NewReno)
netsh int tcp set supplemental template=internet congestionprovider=cubic

Q4:为什么睡眠唤醒后 TUN 模式必然失效,需要手动重启客户端?

这是 Windows 11 电源管理策略和 WFP 生命周期的问题。睡眠唤醒后,系统会重新枚举所有网卡,包括 wintun 虚拟网卡。但代理客户端的 WFP 规则注册在系统睡眠时被挂起,唤醒后不会自动恢复。同时,物理网卡的 DHCP 续约和 NLA 重新评估会导致路由表短暂抖动。

修复方案有两个:

一是使用客户端的 auto-route 和 strict-route 选项,让客户端在网卡事件触发时自动重建路由和 WFP 规则。Mihomo 的 auto-detect-interface: true 配合 auto-route: true 可以覆盖大部分场景。

二是用计划任务在唤醒后自动重启客户端:

# 创建计划任务,在系统唤醒后重启代理
$action = New-ScheduledTaskAction -Execute "C:\path\to\restart_proxy.bat"
$trigger = New-ScheduledTaskTrigger -AtLogOn
$trigger.Delay = "PT5S"
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
Register-ScheduledTask -TaskName "RestartProxyOnWake" -Action $action -Trigger $trigger -Settings $settings

Q5:为什么 auto-route: true 在某些机器上反而导致无法上网?

auto-route: true 会让客户端自动修改系统路由表,把默认路由指向 TUN 网卡。但如果客户端在修改路由表时出错(比如 TUN 网卡还没完全初始化),会导致系统失去默认路由,完全无法上网。

排查:检查路由表是否有默认路由:

Get-NetRoute -DestinationPrefix "0.0.0.0/0"

如果没有输出,说明默认路由被删了。手动恢复:

# 找到物理网卡的 ifIndex
$physicalIndex = (Get-NetAdapter | Where-Object {$_.Status -eq "Up" -and $_.Name -notlike "*Mihomo*"}).ifIndex
# 恢复默认路由
New-NetRoute -DestinationPrefix "0.0.0.0/0" -InterfaceIndex $physicalIndex -NextHop (Get-NetIPConfiguration -InterfaceIndex $physicalIndex).IPv4DefaultGateway.NextHop

修复:在客户端配置中关闭 auto-route,改用手动路由脚本。或者使用 strict-route: true 配合 auto-route: true,后者会先验证 TUN 网卡状态再修改路由。

Q6:多个代理客户端同时运行时,TUN 网卡冲突怎么处理?

多个客户端同时运行会创建多个 TUN 网卡,每个都试图修改默认路由和 WFP 规则,结果就是路由表抖动和 WFP 规则冲突。我们实测发现,两个客户端同时运行时,DNS 泄漏率会从 34% 飙升到 78%。

处理方案:只保留一个客户端运行,其他客户端完全退出(不是最小化到托盘,而是结束进程)。如果确实需要多客户端,可以用不同的 TUN 网卡名称和不同的路由表策略,但这需要非常精细的配置,不建议普通用户尝试。

检查是否有多个 TUN 网卡:

Get-NetAdapter | Where-Object {$_.Name -like "*TUN*" -or $_.Name -like "*Mihomo*" -or $_.Name -like "*Clash*"}

如果有多个,逐个排查对应的进程:

Get-Process | Where-Object {$_.ProcessName -like "*mihomo*" -or $_.ProcessName -like "*clash*" -or $_.ProcessName -like "*sing-box*"}

Q7:更新后 pktmon 抓包显示流量进了 TUN,但代理日志显示没有收到流量,为什么?

这是 wintun Ring Buffer 的问题。wintun 驱动通过 Ring Buffer 在用户态和内核态之间传递数据包。如果 Ring Buffer 大小设置不当,或者驱动版本不匹配,会出现”包进了 TUN 但用户态读不到”的情况。

排查:

# 查看 wintun 驱动的事件日志
Get-WinEvent -FilterHashtable @{LogName='System'; ProviderName='wintun'} -MaxEvents 20

修复:更新 wintun 驱动到最新版本(0.14.1 或更高),或者在客户端配置中调整 Ring Buffer 大小。Mihomo 的 tun 配置中有 recvmsg-size 参数,可以尝试调整:

tun:
  enable: true
  stack: system
  recvmsg-size: 65535
  sendmsg-size: 65535

如果还是不行,尝试切换 stack 类型:gvisor 和 system 在不同的系统版本上表现不同。我们实测在 Build 22631.4169 上,gvisor 的兼容性更好,但在 Build 26100.x 上 system 表现更稳定。


以上就是这次 Windows 11 更新后 TUN 模式故障的完整排查和修复方案。核心思路是:不要只盯着客户端配置,要从驱动、WFP、路由表、DNS 四个层面逐层排查。大部分问题不是客户端本身的 bug,而是系统更新后底层网络策略的变化。修复的关键是让客户端在系统层面有更强的接管能力,而不是依赖系统默认行为。