如何在 PowerShell 中核查 Windows 上的 VPN 实际路由了什么
已连接的 VPN 磁贴并不能证明覆盖范围。请按顺序读取已保存的配置文件标志、实时路由表和 NRPT 规则。
我在一台 Windows 11 笔记本上连上了 VPN 客户端,磁贴显示 Connected,内部文件共享可以打开,而一个公网站点仍然直接从办公室的 Wi-Fi 加载。这种混合现象看起来像隧道坏了。其实这是一个把部分流量送进 VPN、其余流量留在物理网卡上的配置文件的正常输出。强制隧道改变的是 Windows 偏好哪条默认路由;它不会删除更具体的路由,也不承诺连接断开时流量会停止。下面是我在客户端声称全覆盖时遵循的顺序:客户端状态、已保存的配置文件、已应用的路由、解析器,最后是真实流量。
从客户端开始,然后别再相信它
客户端之所以是起点,只是因为它能说明哪个配置文件处于活动状态。打开连接属性,记下确切的配置文件名称、它是为当前用户还是为本机所有用户创建,以及连接后 ncpa.cpl 中出现哪块网卡。在我的测试机上,网卡沿用了配置文件名称,这让路由输出更容易读。在其他机器上它显示为 WAN Miniport 条目,那个名字什么也告诉不了你。
客户端无法告诉你的是覆盖范围。已连接的磁贴描述的是隧道状态,而不是目的地清单。第三方客户端也会保留自己的配置,因此原生 Windows 配置文件以及你从中读到的每个属性,都可能与那个客户端安装的东西无关。如果 VPN 是通过 AnyConnect、OpenVPN GUI、WireGuard 或厂商代理接入的,下面的 PowerShell 检查大多不适用,你应当改读该客户端自己的拆分隧道和路由设置。
读已保存的配置文件,而不是磁贴
对于原生 Windows 配置文件(IKEv2、L2TP、SSTP 或由 MDM 推送的配置),已保存的拆分隧道决定只有一项属性:
Get-VpnConnection -Name 'Office' | Select-Object Name, ServerAddress, SplitTunneling, DnsSuffix
当配置文件是为设备上所有用户创建时,加上 -AllUserConnection。否则你读到的是当前用户的列表,它可能为空,也可能包含另一条名称相近的条目。False 值出现在 SplitTunneling 中,意味着配置文件按完整隧道保存:Windows 会通过 VPN 网卡安装一条默认路由。True 意味着拆分隧道,此时覆盖范围完全取决于该配置文件推送的路由和名称解析规则。
把这个值当作配置,而不是观测。它记录的是连接时配置文件要求了什么。Windows 最终可能应用不同的路由,原因可能是另一块网卡上更具体的路由、策略设置或上一次连接残留的条目,所以这个布尔值从来不能单独回答这个问题。
把已保存的值与已应用的路由表对比
连接之后,列出隧道网卡上的路由,而不是翻遍整张表:
$vpn = Get-NetAdapter | Where-Object InterfaceDescription -match 'VPN|WAN Miniport'
Get-NetRoute -InterfaceIndex $vpn.ifIndex | Sort-Object DestinationPrefix
Find-NetRoute -RemoteIPAddress 10.20.4.15 | Select-Object -First 1
Find-NetRoute 才是有用的那一半。它回答了客户端永远不会回答的问题:对于这个目的地,Windows 实际会使用哪块网卡和哪个源地址?对内网主机、公网主机以及任何你视为有意例外的目标都跑一遍。完整隧道配置文件对公网地址应当显示隧道网卡。拆分隧道配置文件通常在那里显示物理网卡,而内网前缀仍然解析到 VPN。
两个陷阱解释了大多数「它路由错了方向」的报告。最具体的前缀总是获胜,所以物理网卡上的 /24 会击败 VPN 上的默认路由,无论隧道度量值多低。度量值只在同一前缀的多条路由之间打破平局。而 IPv4 与 IPv6 是分开决定的:覆盖 0.0.0.0/0 的配置文件可能把 IPv6 留在本地链路上,这会让双栈站点看起来只被隧道覆盖了一半。在断定隧道没问题之前,先检查 Get-NetRoute -AddressFamily IPv6。
| What you see | Most likely cause | Check next |
|---|---|---|
| Public traffic leaves the physical adapter while SplitTunneling reads False | A more specific route on that adapter wins over the tunnel default | Find-NetRoute for the exact destination, then look for a /24 or host route on the physical interface |
| Internal name fails, internal IP works | No NRPT rule for that namespace, or a broader rule matches first | Get-DnsClientNrptPolicy and the namespace order |
| A dual-stack site opens directly while IPv4 uses the tunnel | The profile covers only the IPv4 default route | The IPv6 route table on both interfaces |
| Routes look right but one app still goes direct | The app uses its own proxy, or the client keeps a per-app list | The app's proxy settings and the client's application list |
用 NRPT 单独检查名称解析
内网名称失败而内网 IP 地址可用,指向的是解析器策略而不是路由。Windows 通过名称解析策略表按命名空间决定 DNS,该表独立于路由表:
Get-DnsClientNrptPolicy -Effective | Select-Object Namespace, NameServers
Get-DnsClientServerAddress -InterfaceIndex $vpn.ifIndex
当规则匹配 *.corp.example 时,该命名空间的查询会发往隧道的 DNS 服务器,即使在拆分隧道模式下通往这些服务器的数据路径仍留在物理网卡上。设计就是如此,这也是为什么打开公网站点的浏览器测试对内网名称毫无说明。如果网卡列出了预期的服务器但查询仍然去了别处,就比较 NRPT 规则的顺序,确认更具体的命名空间是否先匹配。
核实流量,然后再判断你实际要求了什么
按固定顺序走一遍目的地,让结果在机器之间可比:一个公网地址、一个内网地址、一个内网名称,以及一个有意设置的例外,比如本地打印机或视频通话。记下 Find-NetRoute 为每一项返回的网卡和源地址。任何策略变更之后都要重新连接并重复这四项检查;重连后的路由表是新配置文件已生效的唯一证据。
然后把路由与强制执行分开。强制隧道在隧道处于活动状态时,会为被覆盖的流量优先选择隧道。它不是断路器。如果要求是隧道断开时机器上什么都不许出去,那必须来自阻断式设计:绑定隧道状态的防火墙配置文件、带流量过滤器的常开配置文件,或厂商客户端自带的失败即关闭选项。强制隧道还必须让 VPN 服务器自身的地址在隧道之外仍可访问,而以「强制一切」之名把它阻断,是把自己锁在所试图强制的那条连接之外的可靠办法。
在改动任何东西之前,先走一遍这份清单。每一项失败都会缩小真正问题所在的方位。
- 已确认配置文件名称和范围,对整机配置文件使用了
-AllUserConnection。 - 已读取
SplitTunneling,并把它当作配置,而不是覆盖范围的证据。 - 已分别列出隧道网卡在 IPv4 和 IPv6 下的路由。
- 已对一个公网、一个内网和一个例外目的地运行
Find-NetRoute。 - 在把名称失败归咎于路由之前,已检查 NRPT 规则。
- 已重读阻断要求:优先路由并不等同于已执行的阻断。
完整隧道还是拆分隧道的决定属于配置文件的拥有者,而 Microsoft 的路由说明描述了配置文件设定该值时 Windows 实际实现的行为。Microsoft:强制与拆分隧道 是我在读取输出时保持打开的那份参考。