在 macOS 终端里查 VPN 状态:scutil、路由还是 ping
服务状态、内核路由和一次真实请求回答三个不同的问题。脚本要认服务标识符,也要检查路由。
某个部署脚本里,在开始复制之前会检查隧道是否可用。可用的答案有三个,含义并不相同:scutil 报告的服务状态、内核选中的路由,以及对端是否真的回应。把三者对照,才是能预测复制结果的检查与只给日志添彩的检查之间的区别。
「VPN 通了吗」的三个答案
服务状态是控制面的事实。macOS 认为某个配置已连接,是因为它的客户端进程这么说。路由是数据面的事实:内核会把目标从那个接口发出去。可达性是端到端的事实,它可能因为另外两者都看不到的原因而失败。
| Signal | Answers | Blind spot |
|---|---|---|
scutil --nc status | Is the service connected? | Third-party clients, wrong routes |
route -n get | Where would this go? | The peer may be down |
| A request to the far side | Does it work now? | Slow, and it changes state |
有用的检查把前两者合起来,再用第三项确认前两者没有说谎。任何单独一项,都只是带时间戳的猜测。
读标识符,不要读显示名
scutil --nc list
scutil --nc status "Office"
列表为每个服务打印一行,每行先是一个括号里的状态,然后是服务标识符、类型和显示名。脚本要认标识符,因为显示名是某人在系统设置里敲进去的,随时可能变。status 命令打出一个单词,某些服务类型后面还跟着统计;比对那个首词,而不要在整段输出里搜名字——名字在别处也会出现。
启动服务可能弹出授权请求,配置由管理描述文件下发时还会被直接拒绝。这种拒绝不是隧道故障,而是关于谁能拉起它的策略声明。
第三方客户端不在这个列表里
列表为空不代表没有 VPN。系统自身网络设置里创建的配置会出现在这里;厂商客户端、管理描述文件或第三方应用可能维持着一条这个命令永远看不到的隧道。自己的服务以自家工具为准。向 scutil 询问一个从未向它注册的客户端会返回空,而空并不等于已断开。
列表为空但流量明显在加密时,去找接口:一个处在预期网段里的 utun 或 ppp 设备,就是某物建立了隧道的证据。这个信号比服务状态弱,所以要与路由检查配对,而不是把接口当成身份证明。多个客户端可以创建多个这样的接口,其中只有一个属于你要找的那条隧道。
状态不等于路由
已连接的服务说明隧道存在,并不说明你的目标走了它。先读地址,再直接问内核:
ifconfig | grep -A4 utun
netstat -rn -f inet
route -n get 10.40.0.20
全隧道的客户端会安装一条经隧道的默认路由;分流隧道的客户端会安装特定的私有前缀。两者都报告 Connected,只查状态的脚本会心安理得地走错路。停掉一条隧道并不一定删除另一个客户端装的路由,这也是一台机器同时出现已连接和陈旧路由的一种来由。要看你在意的那个目标,而不是整张路由表。
命名是另一个问题
地址能用,别的机器却用名字连不上 Mac,于是被报成 VPN 故障。本地主机名、主机名和 DNS 记录是三件不同的事。scutil --get LocalHostName 显示 Bonjour 名;sudo scutil --set HostName mac-office 设置系统上报的名字。
这两项设置都不会在办公区里生成记录。如果其他主机必须通过 DNS 解析这个名字,就得由区负责人加上记录,否则脚本应改用地址。设置一个稳定的名字能让通告保持一致,但不会让解析器给出答案。
把服务标识符和预期的路由形态一起写进脚本,并在复制之前把两者都查一遍。隧道通了但路由走错方向,失败的样子和没有隧道一模一样,而那行写着 Connected 的日志会把下一个人支到错误的地方。