在 macOS 上同时使用公司 Wi-Fi、家庭 L2TP VPN 和 Clash Verge Rev 时,很容易遇到一种看起来十分混乱的问题:
L2TP 能连接,但访问不了家里的局域网设备;
手动添加路由后,家里设备可以访问,但 Clash 又失效;
终端执行 curl 可以访问 Google,浏览器却打不开;
L2TP 每次断开并重新连接后,之前添加的路由都会消失;
Clash Verge Rev 界面显示“系统代理已开启”,但 macOS 实际并没有启用代理。
这类问题表面上像是 Clash、L2TP、DNS、浏览器一起坏了,实际上主要涉及三个相互独立的机制:
macOS 路由表;
PPP/L2TP 点对点隧道;
macOS 按网络服务维护的系统代理设置。
本文记录一次完整排查过程,并给出一套最终稳定方案。
一、网络环境与目标 本次环境如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 公司 Wi-Fi: 物理网卡:en0 默认网关:192.168.1.254 家庭 L2TP: 通过公网域名连接 PPP 本地地址:10.7.7.10 PPP 对端地址:10.1.0.1 PPP 接口:ppp0 家庭局域网: 192.168.9.0/24 Clash Verge Rev: 使用 Mihomo 内核 最终采用系统代理模式 TUN 模式关闭
期望的最终流量路径是:
flowchart LR
A[macOS 应用程序]
A -->|访问 192.168.9.0/24| B[静态路由]
B --> C[ppp0]
C --> D[10.1.0.1]
D --> E[家庭局域网]
A -->|浏览器访问 Google| F[macOS 系统代理]
F --> G[Clash Verge Rev]
G --> H[代理节点]
H --> I[en0 / 公司 Wi-Fi]
A -->|普通直连流量| I
也就是:
1 2 3 192.168.9.0/24 → 10.1.0.1 → ppp0 → 家庭局域网 Google 等代理流量 → Clash → en0 → 代理节点 普通公网流量 → en0
二、先理解两个容易混淆的地址 1. L2TP 公网域名 L2TP 配置里的域名只负责定位家庭网络的公网入口。
例如:
它解决的是:
1 Mac 如何通过互联网找到家里的 L2TP 服务端
域名只参与隧道建立阶段,不应该作为访问家庭局域网时的静态路由网关。
2. PPP 本地地址与对端地址 L2TP 连接成功后,可以执行:
本次输出中关键部分类似:
1 inet 10.7.7.10 --> 10.1.0.1
含义是:
1 2 Mac 在隧道内的地址:10.7.7.10 L2TP 点对点对端:10.1.0.1
PPP 是点对点链路,本地地址和对端地址不要求像普通以太网那样处在同一个 /24 网段。因此:
并不异常。
访问家庭局域网时,应把家庭网段路由到 PPP 对端:
1 192.168.9.0/24 → 10.1.0.1
三、故障一:L2TP 已连接,但访问不了家庭设备 连接 L2TP 后执行:
1 route -n get 192.168.9.1
故障状态为:
1 2 route to: 192.168.9.1 interface: en0
这表示访问 192.168.9.1 的流量仍然从公司 Wi-Fi 发出,没有进入 L2TP。
只要目标接口是 en0,家庭设备就不可能访问成功。
为什么会走 en0 因为 L2TP 默认只建立了 PPP 隧道,并不一定会自动把家庭局域网 192.168.9.0/24 写入 macOS 路由表。
系统只知道:
但不知道:
1 192.168.9.0/24 也应该通过 10.1.0.1 到达
因此需要显式添加家庭局域网路由。
四、正确添加家庭局域网路由 1. 先确认 PPP 对端可达
如果 10.1.0.1 可以 ping 通,说明:
这段链路已经建立。
2. 删除可能存在的旧路由 1 2 sudo route -n delete -net 192.168.9.0/24 2>/dev/nullsudo route -n delete -host 192.168.9.1 2>/dev/null
删除失败不影响后续操作,因为旧路由可能本来就不存在。
3. 添加家庭网段路由 1 sudo route -n add -net 192.168.9.0/24 10.1.0.1
这里使用的是 PPP 对端地址 10.1.0.1,而不是强行写死 ppp0。
这样做更稳妥,因为:
ppp0 只在 L2TP 成功连接后存在;
macOS 重新连接时接口编号可能发生变化;
PPP 本质是点对点链路,以对端地址作为下一跳更符合路由语义。
4. 验证路由 1 route -n get 192.168.9.1 | egrep 'gateway|interface'
正确结果应为:
1 2 gateway: 10.1.0.1 interface: ppp0
然后测试:
如果目标设备不响应 ICMP,可以改用端口测试:
1 2 nc -vz 192.168.9.1 80 nc -vz 192.168.9.1 443
也可以测试具体的 NAS、ESXi 或服务器:
1 2 nc -vz 192.168.9.100 22 nc -vz 192.168.9.100 443
五、为什么 L2TP 重连后路由会消失 下面这条命令添加的是内核临时路由:
1 sudo route -n add -net 192.168.9.0/24 10.1.0.1
当 ppp0 断开时,相关路由会失效或被系统清理。
因此每次执行:
都可能需要重新添加:
1 sudo route -n add -net 192.168.9.0/24 10.1.0.1
这不是命令执行失败,而是临时路由本身就不会自动永久保存。
六、持久化方案一:为 L2TP 网络服务添加附加路由 先查看 macOS 中的网络服务名称:
1 networksetup -listallnetworkservices
假设 L2TP 服务名称为:
先检查已有附加路由:
1 networksetup -getadditionalroutes "Home L2TP"
如果没有需要保留的其他附加路由,可以设置:
1 2 sudo networksetup -setadditionalroutes "Home L2TP" \ 192.168.9.0 255.255.255.0 10.1.0.1
验证:
1 networksetup -getadditionalroutes "Home L2TP"
预期结果:
1 2 3 Destination: 192.168.9.0 Subnet Mask: 255.255.255.0 Gateway: 10.1.0.1
重新连接 L2TP 后再次检查:
1 route -n get 192.168.9.1 | egrep 'gateway|interface'
如果该方式在当前 macOS 版本或 VPN 服务上未生效,可以使用 PPP 的 ip-up 脚本。
七、持久化方案二:通过 /etc/ppp/ip-up 自动添加路由 PPP 建链成功后,pppd 可以调用 /etc/ppp/ip-up。
创建脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 sudo mkdir -p /etc/pppsudo tee /etc/ppp/ip-up >/dev/null <<'EOF' HOME_NET="192.168.9.0/24" VPN_PEER="10.1.0.1" /sbin/route -n delete -net "$HOME_NET " 2>/dev/null /sbin/route -n add -net "$HOME_NET " "$VPN_PEER " exit 0 EOF
增加执行权限:
1 sudo chmod 755 /etc/ppp/ip-up
之后每次 L2TP 成功建立 PPP 链路时,系统都会尝试自动执行:
1 route -n add -net 192.168.9.0/24 10.1.0.1
为了方便排查,也可以增加日志:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 sudo tee /etc/ppp/ip-up >/dev/null <<'EOF' HOME_NET="192.168.9.0/24" VPN_PEER="10.1.0.1" LOG_FILE="/tmp/l2tp-ip-up.log" { echo "[$(date '+%Y-%m-%d %H:%M:%S') ] PPP connected" echo "interface=$1 local=$4 remote=$5 " } >> "$LOG_FILE " /sbin/route -n delete -net "$HOME_NET " 2>/dev/null /sbin/route -n add -net "$HOME_NET " "$VPN_PEER " >> "$LOG_FILE " 2>&1exit 0 EOFsudo chmod 755 /etc/ppp/ip-up
查看日志:
八、故障二:连接 L2TP 后 Clash 失效 家庭路由修好后,又出现了另一个现象:
1 2 3 4 5 关闭 L2TP: 浏览器可以访问 Google 连接 L2TP: 浏览器不能访问 Google
但检查公网路由:
1 route -n get 8.8.8.8 | egrep 'gateway|interface'
结果是:
1 2 gateway: 192.168.1.254 interface: en0
这说明公网默认路由并没有被 L2TP 抢走。
既然公网仍然走 en0,浏览器却无法访问 Google,就不能再把问题归因于默认路由。
真正的关键检查是:
或者只查看 HTTP/HTTPS:
1 2 scutil --proxy | egrep \'HTTPEnable|HTTPProxy|HTTPPort|HTTPSEnable|HTTPSProxy|HTTPSPort'
故障状态为:
1 2 HTTPEnable : 0 HTTPSEnable : 0
这表示:
1 macOS 当前有效的系统 HTTP/HTTPS 代理处于关闭状态
浏览器不会再把请求发给 Clash,而是直接访问 Google,因此失败。
九、为什么 Clash Verge 显示已开启,但系统代理实际是关闭的 macOS 的代理配置是按网络服务维护的。
例如:
1 2 3 4 Wi-Fi Home L2TP USB Ethernet Thunderbolt Bridge
这些服务可以拥有不同的:
HTTP 代理;
HTTPS 代理;
SOCKS 代理;
DNS;
代理绕过规则。
Clash Verge Rev 开启系统代理时,通常会给当前网络服务写入:
但连接 L2TP 后,macOS 当前有效网络服务和代理计算结果可能发生变化。
于是可能出现:
1 2 Clash Verge Rev 界面:系统代理已开启 macOS 实际状态:HTTPEnable = 0
判断系统代理是否真正生效,不能只看 Clash 界面的开关,应以:
为准。
十、为什么 curl 成功,浏览器却失败 故障过程中还出现过:
1 curl -I --connect-timeout 10 https://www.google.com
可以成功,但浏览器打不开 Google。
这是因为 curl 和浏览器可能使用了不同的代理来源。
浏览器 Chrome、Safari 等浏览器通常读取 macOS 当前系统代理:
当:
1 2 HTTPEnable : 0 HTTPSEnable : 0
时,浏览器会直连。
curl curl 可能读取终端环境变量:
1 env | grep -iE '^(http_proxy|https_proxy|all_proxy|no_proxy)='
例如:
1 2 3 http_proxy=http://127.0.0.1:7897 https_proxy=http://127.0.0.1:7897 all_proxy=socks5://127.0.0.1:7897
因此即使系统代理已经关闭,curl 仍然可能通过 Clash 访问 Google。
如果 curl 输出:
1 HTTP/1.1 200 Connection established
通常意味着 curl 已经连接到 HTTP 代理,并由代理建立 HTTPS CONNECT 隧道。
所以:
这是本次排查中最容易造成误判的一点。
十一、恢复 macOS 系统代理 保持以下状态:
1 2 3 L2TP:已连接 Clash TUN:关闭 Clash 系统代理:准备开启
在 Clash Verge Rev 中执行:
然后验证:
1 2 scutil --proxy | egrep \'HTTPEnable|HTTPProxy|HTTPPort|HTTPSEnable|HTTPSProxy|HTTPSPort'
正确结果应类似:
1 2 3 4 5 6 7 HTTPEnable : 1 HTTPProxy : 127.0.0.1 HTTPPort : 7897 HTTPSEnable : 1 HTTPSProxy : 127.0.0.1 HTTPSPort : 7897
这里的端口应以 Clash Verge Rev 实际配置为准,不一定是 7897。
可以查看 Clash 当前端口:
1 2 grep -E '^(mixed-port|port|socks-port):' \ "$HOME /Library/Application Support/io.github.clash-verge-rev.clash-verge-rev/clash-verge.yaml"
注意:
通常是 Clash Verge Rev 自动生成的最终配置,不建议直接修改。
十二、给 L2TP 网络服务配置系统代理 如果每次连接 L2TP 后系统代理都会变成关闭状态,可以直接给 L2TP 网络服务配置代理。
先查看服务名:
1 networksetup -listallnetworkservices
假设:
1 2 VPN_SERVICE="Home L2TP" CLASH_PORT=7897
设置 HTTP 代理:
1 2 3 4 5 sudo networksetup -setwebproxy \ "$VPN_SERVICE " 127.0.0.1 "$CLASH_PORT " sudo networksetup -setwebproxystate \ "$VPN_SERVICE " on
设置 HTTPS 代理:
1 2 3 4 5 sudo networksetup -setsecurewebproxy \ "$VPN_SERVICE " 127.0.0.1 "$CLASH_PORT " sudo networksetup -setsecurewebproxystate \ "$VPN_SERVICE " on
如有必要,再设置 SOCKS:
1 2 3 4 5 sudo networksetup -setsocksfirewallproxy \ "$VPN_SERVICE " 127.0.0.1 "$CLASH_PORT " sudo networksetup -setsocksfirewallproxystate \ "$VPN_SERVICE " on
设置家庭网络绕过代理:
1 2 3 4 5 6 7 sudo networksetup -setproxybypassdomains \ "$VPN_SERVICE " \ localhost \ 127.0.0.1 \ "*.local" \ "192.168.9.*" \ "10.1.0.1"
完成后重新连接 L2TP,并检查:
十三、Clash Verge Rev 最终配置 本次最终没有使用 TUN 模式。
Clash 的扩展配置中只保留了一行:
作用是:
1 强制 Mihomo 的代理节点连接通过 en0 出站
避免 L2TP 建立后,Mihomo 错误选择 ppp0 作为代理节点出口。
Clash Verge Rev 配置目录 macOS 下常见目录:
1 ~/Library/Application Support/io.github.clash-verge-rev.clash-verge-rev/
可直接打开:
1 open "$HOME /Library/Application Support/io.github.clash-verge-rev.clash-verge-rev"
常见文件:
1 2 3 4 verge.yaml profiles.yaml clash-verge.yaml profiles/
不要直接修改:
因为它属于自动生成的最终配置,切换订阅或重启内核后可能被覆盖。
应从 Clash Verge Rev 界面进入:
1 2 3 订阅 → 当前订阅 → 编辑扩展配置 / 编辑合并配置
最终内容:
十四、为什么没有继续使用 TUN TUN 会创建虚拟网卡并主动修改路由表。
在同时存在:
的环境中,TUN 需要额外处理:
默认出口选择;
家庭网段排除;
PPP 接口排除;
DNS 劫持;
代理节点自回环;
macOS 网络服务切换。
本次需求只是:
1 2 浏览器等常见应用通过 Clash 访问互联网 同时访问家庭局域网
系统代理模式已经足够。
最终稳定方案是:
1 2 Clash 系统代理:开启 Clash TUN:关闭
如果没有必须代理那些完全不支持系统代理的软件,就没有必要额外启用 TUN。
TUN 在这里不是“高级模式”,更像是又请来了一位喜欢抢方向盘的司机。
十五、最终稳定配置 L2TP 1 2 3 4 公网入口:域名 PPP 本地地址:10.7.7.10 PPP 对端:10.1.0.1 发送所有流量:关闭
家庭局域网路由 1 192.168.9.0/24 → 10.1.0.1 → ppp0
临时设置:
1 sudo route -n add -net 192.168.9.0/24 10.1.0.1
持久化优先尝试:
1 2 sudo networksetup -setadditionalroutes "Home L2TP" \ 192.168.9.0 255.255.255.0 10.1.0.1
未生效时使用:
Clash Verge Rev 1 2 3 系统代理:开启 TUN:关闭 扩展配置:interface-name: en0
扩展配置内容:
macOS 有效系统代理 1 2 3 4 HTTPEnable : 1 HTTPSEnable : 1 HTTPProxy : 127.0.0.1 HTTPSProxy : 127.0.0.1
十六、一键检查脚本 可以保存为:
内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 #!/bin/bash HOME_TEST_IP="192.168.9.1" VPN_PEER="10.1.0.1" echo "========================================" echo "1. PPP 接口" echo "========================================" if ifconfig ppp0 >/dev/null 2>&1; then ifconfig ppp0 | grep 'inet ' else echo "ppp0 不存在,L2TP 可能尚未连接" fi echo echo "========================================" echo "2. PPP 对端路由" echo "========================================" route -n get "$VPN_PEER " 2>/dev/null | egrep 'route to|gateway|interface' echo echo "========================================" echo "3. 公网路由" echo "========================================" route -n get 8.8.8.8 2>/dev/null | egrep 'route to|gateway|interface' echo echo "========================================" echo "4. 家庭局域网路由" echo "========================================" route -n get "$HOME_TEST_IP " 2>/dev/null | egrep 'route to|gateway|interface' echo echo "========================================" echo "5. macOS 系统代理" echo "========================================" scutil --proxy | egrep 'HTTPEnable|HTTPProxy|HTTPPort|HTTPSEnable|HTTPSProxy|HTTPSPort|SOCKSEnable|SOCKSProxy|SOCKSPort' echo echo "========================================" echo "6. Shell 代理环境变量" echo "========================================" env | grep -iE '^(http_proxy|https_proxy|all_proxy|no_proxy)=' || echo "未发现代理环境变量" echo echo "========================================" echo "7. 家庭网络连通性" echo "========================================" ping -c 2 "$VPN_PEER " ping -c 2 "$HOME_TEST_IP " echo echo "检查完成"
增加执行权限:
1 chmod +x check-l2tp-clash.sh
运行:
十七、最终验证标准 公网默认路由 1 route -n get 8.8.8.8 | egrep 'gateway|interface'
正确:
1 2 gateway: 192.168.1.254 interface: en0
家庭局域网路由 1 route -n get 192.168.9.1 | egrep 'gateway|interface'
正确:
1 2 gateway: 10.1.0.1 interface: ppp0
系统代理 1 2 scutil --proxy | egrep \'HTTPEnable|HTTPProxy|HTTPPort|HTTPSEnable|HTTPSProxy|HTTPSPort'
正确:
1 2 3 4 5 6 7 HTTPEnable : 1 HTTPProxy : 127.0.0.1 HTTPPort : Clash实际端口 HTTPSEnable : 1 HTTPSProxy : 127.0.0.1 HTTPSPort : Clash实际端口
浏览器与 curl 浏览器访问:
curl 测试:
1 curl -Iv --connect-timeout 10 https://www.google.com
注意,curl 成功不能替代浏览器测试,因为两者可能使用不同的代理来源。
十八、排查结论 本次问题不是单一故障,而是两个问题同时发生:
问题一:家庭路由缺失
修复:
1 192.168.9.0/24 → 10.1.0.1 → ppp0
问题二:连接 L2TP 后系统代理被关闭 1 2 HTTPEnable : 0 HTTPSEnable : 0
修复:
1 2 重新启用 Clash 系统代理 或给 L2TP 网络服务单独配置 HTTP/HTTPS 代理
最终要同时满足:
1 2 3 4 家庭网段走 ppp0 公网路由走 en0 浏览器系统代理走 Clash Clash 代理节点从 en0 出口访问
只盯着 Clash 配置文件、只盯着路由表,或者只看 curl 是否成功,都容易误判。
macOS 网络排障最重要的是分层验证:
1 2 3 4 5 6 第一层:PPP 隧道是否建立 第二层:家庭网段路由是否正确 第三层:公网默认路由是否正确 第四层:系统代理是否真正启用 第五层:Clash 到代理节点是否正常 第六层:浏览器和终端是否使用同一代理来源
路由正确、Clash 正常、浏览器使用了 Clash 是三件不同的事情。把这三层拆开看,问题就不会再像一锅网络玄学。