after close VPN, analyze why no Net
Mac 在关闭 VPN 后,由于系统 DNS 配置残留或未正确还原导致的断网故障
前置知识
VPN原理 VPN 改动 DNS服务器地址 虚拟DNS 服务器, 所有的 DNS请求都会走虚拟DNS服务器, 相当于 代理工具 接管系统DNS服务器,再由 虚拟DNS服务器 处理 发往真正的DNS服务器 关闭代理工具, 但 虚拟DNS服务器 按理来说是不存在,应当消失, 回归 系统DNS服务器;
常用命令
# 查看当前DNS服务器地址
cat /etc/resolv.conf
scutil --dns
# 查看默认的DNS服务器
scutil --dns | grep nameserver
# 路由信息查看
netstat -r
# DNS 解析域名
dig @[DNS服务器] [域名]
连接VPN 前
❯ cat /etc/resolv.conf ─╯
#
# macOS Notice
#
# This file is not consulted for DNS hostname resolution, address
# resolution, or the DNS query routing mechanism used by most
# processes on this system.
#
# To view the DNS configuration used by this system, use:
# scutil --dns
#
# SEE ALSO
# dns-sd(1), scutil(8)
#
# This file is automatically generated.
#
nameserver 192.168.231.1
nameserver 192.168.1.1DNS 指向局域网网关
192.168.231.1和192.168.1.1
scutil --dns ─╯
DNS configuration
resolver #1
nameserver[0] : 192.168.231.1
nameserver[1] : 192.168.1.1
if_index : 7 (en0)
flags : Request A records
reach : 0x00000002 (Reachable)
resolver #2
domain : local
options : mdns
timeout : 5
flags : Request A records
reach : 0x00000000 (Not Reachable)
order : 300000
resolver #3
domain : 254.169.in-addr.arpa
options : mdns
timeout : 5
flags : Request A records
reach : 0x00000000 (Not Reachable)
order : 300200
resolver #4
domain : 8.e.f.ip6.arpa
options : mdns
timeout : 5
flags : Request A records
reach : 0x00000000 (Not Reachable)
order : 300400
resolver #5
domain : 9.e.f.ip6.arpa
options : mdns
timeout : 5
flags : Request A records
reach : 0x00000000 (Not Reachable)
order : 300600
resolver #6
domain : a.e.f.ip6.arpa
options : mdns
timeout : 5
flags : Request A records
reach : 0x00000000 (Not Reachable)
order : 300800
resolver #7
domain : b.e.f.ip6.arpa
options : mdns
timeout : 5
flags : Request A records
reach : 0x00000000 (Not Reachable)
order : 301000
DNS configuration (for scoped queries)
resolver #1
nameserver[0] : 192.168.231.1
nameserver[1] : 192.168.1.1
if_index : 7 (en0)
flags : Scoped, Request A records
reach : 0x00000002 (Reachable)❯ scutil --dns | grep nameserver ─╯
nameserver[0] : 192.168.231.1
nameserver[1] : 192.168.1.1
nameserver[0] : 192.168.231.1
nameserver[1] : 192.168.1.1说明 DNS …
连接VPN 后
❯ cat /etc/resolv.conf ─╯
#
# macOS Notice
#
# This file is not consulted for DNS hostname resolution, address
# resolution, or the DNS query routing mechanism used by most
# processes on this system.
#
# To view the DNS configuration used by this system, use:
# scutil --dns
#
# SEE ALSO
# dns-sd(1), scutil(8)
#
# This file is automatically generated.
#
nameserver 198.18.0.2DNS 依然被锁定在 198.18.0.2(这通常是 Clash 等代理/VPN 工具使用的 Fake IP 或者是其内置 TUN 模式的 DNS 服务地址) 前后比对,DNS解析地址 被替换, 路由信息被更新“ 当 VPN 关闭后,这个 IP 变得不可达,导致你的 Mac 无法解析任何域名,从而“断网”。
命令方式解析 dig @[DNS服务器] [域名]
dig @114.114.114.114 www.baidu.com一、 命令行定位与分析步骤 既然怀疑是 DNS 问题,我们可以通过以下命令行组合进行精准排查:
- 验证是否只是 DNS 解析失败(排除物理断网) 在终端中尝试直接 ping 一个公网 IP(如 Google 或 腾讯的公共 DNS),再 ping 一个域名:
Bash
1. 测试网络层是否通畅
ping -c 3 114.114.114.114
2. 测试域名解析是否失败
ping -c 3 baidu.com 分析: 如果 ping 114.114.114.114 能通,但 ping baidu.com 提示 cannot resolve baidu.com: Unknown host,说明网络连接完全正常,100% 是 DNS 的锅。
- 查看 macOS 真正的 DNS 配置 正如你截图中 macOS 自带的警告所说,macOS 并不真正依赖 /etc/resolv.conf,它有自己的系统配置框架(Configuration Daemon)。 执行以下命令查看当前生效的 DNS:
Bash scutil –dns 分析: 检查输出中的 nameserver[0]。你会发现即使关闭了 VPN,这里可能依然残留着 198.18.0.2。这是因为 VPN 客户端退出时,未能正常调用系统 API 将修改的 DNS 还原。
- 查看网络服务(以 Wi-Fi 为例)的硬件 DNS 设置 Bash networksetup -getdnsservers Wi-Fi 分析: 如果这里输出了 198.18.0.2 或提示存在特定的 DNS,说明 VPN 直接修改了你网卡的硬配置。如果输出 There aren’t any DNS Servers set on Wi-Fi.,说明走的是 DHCP 自动获取(这是正常的,不正常的配置残留在内存的 scutil 守护进程中)。
二、 核心原理分析 为什么关闭 VPN 后,DNS 会被“卡死”在 198.18.0.2?
接管机制: 当你开启 VPN 或 Clash 的 TUN 模式时,客户端为了接管所有的网络流量,会利用 macOS 的 SystemConfiguration 框架,将系统全局 DNS 强行修改为代理软件的内部 DNS 服务器(如 198.18.0.2)。
异常退出/恢复失败: 当你关闭 VPN 时,如果软件发生崩溃、强制退出、或者因为权限问题未能成功触发“善后逻辑(Clean up)”,它向系统注册的临时 DNS 配置就不会被注销。
macOS 的 DNS 缓存: macOS 内部的 mDNSResponder 守护进程会一直坚守这个错误的 DNS 地址,直到你手动干预或重启电脑。
三、 命令行解决方案 请按以下步骤依次尝试,通常第一步或第二步就能直接解决问题。
方案 1:强制刷新 macOS DNS 缓存与重置 mDNSResponder 这是最快、最有效的方法,重新初始化系统的网络解析守护进程:
Bash sudo killall -HUP mDNSResponder 输入密码后回车。此命令会强制重启 DNS 响应服务,通常会让系统重新向路由器读取正确的 DNS。
方案 2:使用 networksetup 强制清空并重置网卡 DNS 如果 VPN 修改了网络接口的硬配置,可以通过命令行将其恢复为“自动获取(DHCP)”:
Bash
1. 将 Wi-Fi 的 DNS 清空,恢复为默认的从路由器获取
sudo networksetup -setdnsservers Wi-Fi Empty
2. (可选)如果你用的是有线网卡(如以太网),同样清空
sudo networksetup -setdnsservers “AX88179A Triple Speed New” Empty 2>/dev/null || true 执行后,再次运行 scutil –dns 查看是否已经变回你的局域网网关(如 192.168.1.1)。
方案 3:通过 scutil 动态修改残留的配置(高级) 如果方案 1 和 2 都不管用,可以直接进入系统的配置字典里把残留的配置删掉:
Bash sudo scutil 进入交互式命令行后,依次输入以下命令(每行回车):
Plaintext
open d.init d.add ServerAddresses * 192.168.1.1 114.114.114.114 set State:/Network/Global/DNS quit 这会强行将全局 DNS 临时扭转回 192.168.1.1 和 114.114.114.114。
💡 治本建议(防止下次再犯) 规范关闭软件: 尽量不要通过 kill -9 或在活动监视器里强制退出 VPN 软件,在软件菜单栏中选择 Quit/退出,给它留出还原系统网络配置的时间。
检查权限: 部分 VPN 软件(如 Clash Verge / Nyatwork)需要安装 Service Mode(服务模式) 才能拥有足够的系统权限在退出时清理 DNS。检查软件设置中是否正确安装了 Helper 工具。
❯ ping -c 3 114.114.114.114 ─╯
PING 114.114.114.114 (114.114.114.114): 56 data bytes
64 bytes from 114.114.114.114: icmp_seq=0 ttl=64 time=3.109 ms
64 bytes from 114.114.114.114: icmp_seq=1 ttl=64 time=1.630 ms
64 bytes from 114.114.114.114: icmp_seq=2 ttl=64 time=1.009 ms
--- 114.114.114.114 ping statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 1.009/1.916/3.109/0.881 ms
~ ────────────────────────────────────────────────────── system Node at 08:45:41 AM ─╮
❯ ping -c 3 baidu.com ─╯
PING baidu.com (124.237.177.164): 56 data bytes
64 bytes from 124.237.177.164: icmp_seq=0 ttl=64 time=4.223 ms
64 bytes from 124.237.177.164: icmp_seq=1 ttl=64 time=1.734 ms
64 bytes from 124.237.177.164: icmp_seq=2 ttl=64 time=0.817 ms
--- baidu.com ping statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.817/2.258/4.223/1.439 ms
~ ────────────────────────────────────────────────────── system Node at 08:45:49 AM ─╮
❯ scutil --dns ─╯
DNS configuration
resolver #1
nameserver[0] : 198.18.0.2
if_index : 24 (utun11)
flags : Supplemental, Request A records, Request AAAA records
reach : 0x00000003 (Reachable,Transient Connection)
order : 102200
resolver #2
nameserver[0] : 198.18.0.2
if_index : 24 (utun11)
flags : Request A records, Request AAAA records
reach : 0x00000003 (Reachable,Transient Connection)
order : 200000
resolver #3
domain : local
options : mdns
timeout : 5
flags : Request A records, Request AAAA records
reach : 0x00000000 (Not Reachable)
order : 300000
resolver #4
domain : 254.169.in-addr.arpa
options : mdns
timeout : 5
flags : Request A records, Request AAAA records
reach : 0x00000000 (Not Reachable)
order : 300200
resolver #5
domain : 8.e.f.ip6.arpa
options : mdns
timeout : 5
flags : Request A records, Request AAAA records
reach : 0x00000000 (Not Reachable)
order : 300400
resolver #6
domain : 9.e.f.ip6.arpa
options : mdns
timeout : 5
flags : Request A records, Request AAAA records
reach : 0x00000000 (Not Reachable)
order : 300600
resolver #7
domain : a.e.f.ip6.arpa
options : mdns
timeout : 5
flags : Request A records, Request AAAA records
reach : 0x00000000 (Not Reachable)
order : 300800
resolver #8
domain : b.e.f.ip6.arpa
options : mdns
timeout : 5
flags : Request A records, Request AAAA records
reach : 0x00000000 (Not Reachable)
order : 301000
DNS configuration (for scoped queries)
resolver #1
nameserver[0] : 192.168.231.1
nameserver[1] : 192.168.1.1
if_index : 7 (en0)
flags : Scoped, Request A records
reach : 0x00000002 (Reachable)
resolver #2
nameserver[0] : 198.18.0.2
if_index : 24 (utun11)
flags : Scoped, Request A records, Request AAAA records
reach : 0x00000003 (Reachable,Transient Connection)
~ ────────────────────────────────────────────────────── system Node at 08:45:55 AM ─╮
❯ ping baidu.com ─╯
^C
~ ──────────────────────────────────── INT ✘ took 7s system Node at 08:49:05 AM ─╮
❯ ─╯
cat /etc/resolv.conf
#
# macOS Notice
#
# This file is not consulted for DNS hostname resolution, address
# resolution, or the DNS query routing mechanism used by most
# processes on this system.
#
# To view the DNS configuration used by this system, use:
# scutil --dns
#
# SEE ALSO
# dns-sd(1), scutil(8)
#
# This file is automatically generated.
#
nameserver 192.168.231.1
nameserver 192.168.1.1
~ ────────────────────────────────────────────────────── system Node at 08:49:16 AM ─╮
❯ ping baidu.com ─╯
^C
~ ──────────────────────────────────── INT ✘ took 6s system Node at 08:49:28 AM ─╮
❯ dig @192.168.231.1 www.baidu.com ─╯
;; communications error to 192.168.231.1#53: timed out
;; communications error to 192.168.231.1#53: timed out
;; communications error to 192.168.231.1#53: timed out
; <<>> DiG 9.20.24 <<>> @192.168.231.1 www.baidu.com
; (1 server found)
;; global options: +cmd
;; no servers could be reached
~ ─────────────────────────────────────────── took 15s system Node at 08:49:59 AM ─╮
❯ ─╯
cat /etc/resolv.conf
#
# macOS Notice
#
# This file is not consulted for DNS hostname resolution, address
# resolution, or the DNS query routing mechanism used by most
# processes on this system.
#
# To view the DNS configuration used by this system, use:
# scutil --dns
#
# SEE ALSO
# dns-sd(1), scutil(8)
#
# This file is automatically generated.
#
nameserver 192.168.231.1
nameserver 192.168.1.1
~ ────────────────────────────────────────────────────── system Node at 08:52:48 AM ─╮
❯ ping -c 3 192.168.231.1 ─╯
PING 192.168.231.1 (192.168.231.1): 56 data bytes
64 bytes from 192.168.231.1: icmp_seq=0 ttl=63 time=4.427 ms
64 bytes from 192.168.231.1: icmp_seq=1 ttl=63 time=1.954 ms
64 bytes from 192.168.231.1: icmp_seq=2 ttl=63 time=2.018 ms
--- 192.168.231.1 ping statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 1.954/2.800/4.427/1.151 ms
~ ────────────────────────────────────────────────────── system Node at 08:52:54 AM ─╮
❯ netstat -nr | grep default ─╯
default 192.168.1.1 UGScg en0
default fe80::%utun0 UGcIg utun0
default fe80::%utun1 UGcIg utun1
default fe80::%utun2 UGcIg utun2
default fe80::%utun3 UGcIg utun3
default fe80::%utun12 UGcIg utun12
default fe80::%utun13 UGcIg utun13
~ ────────────────────────────────────────────────────── system Node at 08:53:20 AM ─╮
❯ sudo route -n delete default 192.168.1.1 ─╯
Password:
delete net default: gateway 192.168.1.1
~ ──────────────────────────────────────────── took 4s system Node at 08:56:11 AM ─╮
❯ sudo route -n add default 192.168.231.1 ─╯
add net default: gateway 192.168.231.1
~ ────────────────────────────────────────────────────── system Node at 08:56:25 AM ─╮
❯ cat /etc/resolv.conf ─╯
#
# macOS Notice
#
# This file is not consulted for DNS hostname resolution, address
# resolution, or the DNS query routing mechanism used by most
# processes on this system.
#
# To view the DNS configuration used by this system, use:
# scutil --dns
#
# SEE ALSO
# dns-sd(1), scutil(8)
#
# This file is automatically generated.
#
nameserver 192.168.231.1
nameserver 192.168.1.1
~ ────────────────────────────────────────────────────── system Node at 08:58:04 AM ─╮
❯ netstat -nr | grep default ─╯
default 192.168.1.1 UGScg en0
default fe80::%utun0 UGcIg utun0
default fe80::%utun1 UGcIg utun1
default fe80::%utun2 UGcIg utun2
default fe80::%utun3 UGcIg utun3
default fe80::%utun12 UGcIg utun12
default fe80::%utun13 UGcIg utun13
~ ────────────────────────────────────────────────────── system Node at 08:58:14 AM ─╮
❯ sudo route -n delete default 192.168.1.1 ─╯
delete net default: gateway 192.168.1.1
~ ────────────────────────────────────────────────────── system Node at 08:58:23 AM ─╮
❯ sudo route -n add default 192.168.231.1 ─╯
route: writing to routing socket: Network is unreachable
add net default: gateway 192.168.231.1: Network is unreachable
~ ────────────────────────────────────────────────────── system Node at 08:58:31 AM ─╮
❯ cat /etc/resolv.conf ─╯
#
# macOS Notice
#
# This file is not consulted for DNS hostname resolution, address
# resolution, or the DNS query routing mechanism used by most
# processes on this system.
#
# To view the DNS configuration used by this system, use:
# scutil --dns
#
# SEE ALSO
# dns-sd(1), scutil(8)
#
# This file is automatically generated.
#
nameserver 192.168.231.1
nameserver 192.168.1.1
~ ────────────────────────────────────────────────────── system Node at 08:58:40 AM ─╮
❯ netstat -nr | grep default ─╯
default fe80::%utun0 UGcIg utun0
default fe80::%utun1 UGcIg utun1
default fe80::%utun2 UGcIg utun2
default fe80::%utun3 UGcIg utun3
default fe80::%utun12 UGcIg utun12
default fe80::%utun13 UGcIg utun13
❯ ping -c 3 192.168.231.1 ─╯
PING 192.168.231.1 (192.168.231.1): 56 data bytes
64 bytes from 192.168.231.1: icmp_seq=0 ttl=63 time=4.427 ms
64 bytes from 192.168.231.1: icmp_seq=1 ttl=63 time=1.954 ms
64 bytes from 192.168.231.1: icmp_seq=2 ttl=63 time=2.018 ms
— 192.168.231.1 ping statistics —
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 1.954/2.800/4.427/1.151 ms
~ ────────────────────────────────────────────────────── system Node at 08:52:54 AM ─╮
❯ netstat -nr | grep default ─╯
default 192.168.1.1 UGScg en0
default fe80::%utun0 UGcIg utun0
default fe80::%utun1 UGcIg utun1
default fe80::%utun2 UGcIg utun2
default fe80::%utun3 UGcIg utun3
default fe80::%utun12 UGcIg utun12
default fe80::%utun13 UGcIg utun13
破案了!这是一起非常典型的多网卡/双网关路由冲突引发的“高级故障”。
你的测试结果给出了最关键的线索:
ping 192.168.231.1 可以通,说明物理链路和该网段是通的。
netstat -nr | grep default 暴露了致命问题:你的 IPv4 默认网关(default)指向的是 192.168.1.1,走的是 en0 接口。
一、 深度原理分析:为什么会断网? 回到你上一张图的 /etc/resolv.conf:
Plaintext nameserver 192.168.231.1 nameserver 192.168.1.1 你当前系统里其实并存了两个物理网络(或者一个多网段的复杂网络),一个是 192.168.231.x,另一个是 192.168.1.x。
数据包的迷路过程: 当你执行 dig @192.168.231.1 www.baidu.com 时,系统查路由表发现,要前往 192.168.231.1 不需要走默认网关,因为它属于本地直连网段。 但是,你的默认路由(负责所有外网流量)被死死绑定在了 192.168.1.1 上。当 VPN 关闭后,192.168.1.1 这个网关可能根本没有通往外网的权限,或者它是个死网关。
总结: 你的 Mac 目前误把没有外网能力的 192.168.1.1 当作了主力出口,而把真正能上网的 192.168.231.1 晾在了一边。
二、 命令行解决方案 我们需要明确告诉 macOS:把外网出口(默认网关)切换回 192.168.231.1。
步骤 1:手动修正 IPv4 默认路由 强行删除错误的 192.168.1.1 默认网关,并指向正确的 192.168.231.1:
Bash
1. 删除当前指向 192.168.1.1 的默认路由
sudo route -n delete default 192.168.1.1
2. 添加真正能上网的 192.168.231.1 作为默认网关
sudo route -n add default 192.168.231.1 步骤 2:彻底清除残留的 utun 虚拟接口路由(可选) 你的输出中有一大堆 utun0 到 utun13 的 IPv6 残留。虽然它们是 IPv6,但为了防止干扰,可以直接让网络服务重新加载:
Bash
触发网络接口配置更新(将 en0 软重启)
sudo ifconfig en0 down && sudo ifconfig en0 up 步骤 3:调整 macOS 网络服务优先级(长久之计) 如果你的 Mac 同时连接了多个网络接口(比如 Wi-Fi 和以太网,或者两个不同的虚拟网络),你需要让提供 192.168.231.x 的那个接口在系统里处于最高优先级。
先查看你所有的网络服务名称:
Bash networksetup -listnetworkserviceorder 假设你的以太网叫 USB 10/100/1000 LAN,Wi-Fi 叫 Wi-Fi。如果你希望以太网优先,可以使用以下命令调整顺序(把你希望优先的服务写在最前面):
Bash
示例命令,请根据你上一步查到的实际名称修改
sudo networksetup -ordernetworkservices “USB 10/100/1000 LAN” “Wi-Fi” “Bluetooth PAN” 三、 验证网络 完成路由调整后,请再次测试:
Bash
1. 再次检查默认网关是否已变为 192.168.231.1
netstat -nr | grep default
2. 测试域名解析
dig www.baidu.com
3. 测试外网是否畅通
ping -c 3 baidu.com
当 VPN 关闭后发生断网,其根本原因通常是 VPN 软件未能干净地撤销它对系统网络栈(DNS、路由表、虚拟网卡、防火墙)的接管。按照一个标准的数据包从“域名解析”到“物理网卡发出”的 正常网络访问链路,我们可以从上到下、由表及里地梳理出一套完整的命令行(CLI)排查与验证指南。🌐 正常网络访问链路排查总览为了方便记忆,排查链路分为以下 5 个核心节点:应用层/DNS 映射(/etc/resolv.conf)系统 DNS 解析引擎(mDNSResponder / scutil)网络层路由转发(netstat / route)链路层/物理网卡状态(ifconfig / networksetup)系统防火墙与流量拦截(pfctl)🛠️ 各链路节点排查与命令总结节点 1:配置文件层(检查基础 DNS 地址)数据包发出的第一步,是检查系统基础的域名解析服务器指向。查看命令:Bashcat /etc/resolv.conf 异常表现: nameserver 后面依然残留 VPN 的私有 DNS(如 198.18.0.2 等 Fake IP 映射地址)。正常表现: 应该指向你的局域网网关(如 192.168.1.1)或公共 DNS(如 114.114.114.114)。节点 2:系统 DNS 解析层(检查实际 DNS 路由与解析能力)macOS 并不直接依赖上一步的配置文件,而是通过配置画布(Configuration Daemon)管理。查看实际生效 DNS:Bashscutil –dns 检查 nameserver[0] 是否为有效的外网 DNS。测试指定 DNS 接口解析:Bash# 强制使用你本地的网关(如 192.168.231.1)去解析外网域名 dig @192.168.231.1 www.baidu.com 测试系统默认 DNS 解析:Bashdig www.baidu.com 异常表现: dig 提示 timed out 或 no servers could be reached(即使网关能 ping 通)。此节点修复命令(刷新 DNS 缓存):Bashsudo killall -HUP mDNSResponder 节点 3:网络层路由转发(检查默认网关)DNS 解析成功(或直接访问 IP)后,数据包需要知道从哪一个“出口”发往外网。查看全局路由表(特别是默认网关):Bashnetstat -nr | grep default 测试物理网关的连通性:Bash# 替换为你实际的网关 IP ping -c 3 192.168.231.1 异常表现: 1. 存在多个 default 路由,导致路由冲突。2. default 错误的指向了已关闭的 VPN 虚拟网卡(如 utunX),或者指向了一个没有外网能力的死网关(如多网卡环境下的错误内网网关)。此节点修复命令(重置默认路由):Bash# 删除错误的网关,并添加正确的物理网关 sudo route -n delete default 192.168.1.1 sudo route -n add default 192.168.231.1 节点 4:链路层与硬件接口(检查物理网卡状态)确保你的物理网卡(以太网/Wi-Fi)处于激活状态,且获得了正确的 IP 分配。查看所有网络接口状态:Bashifconfig 检查你的物理网卡(如 en0)是否带有 status: active 以及正确的 inet (IP 地址)。查看硬件端口与服务的绑定关系:Bashnetworksetup -listallhardwareports 此节点修复命令(网卡软重启/重新触发 DHCP):Bash# 以 en0 为例,先禁用再启用,强制重新向路由器申请 IP 和路由 sudo ifconfig en0 down && sudo ifconfig en0 up 节点 5:防火墙与安全策略层(检查流量拦截)很多现代 VPN 软件(如 Clash 增强模式、Surge、Tailscale 等)会写入内核级别的防火墙(PF)转发规则。软件关闭时如果未清理,会把所有非 VPN 流量直接丢弃(Drop)。查看防火墙状态与规则:Bashsudo pfctl -s rules 异常表现: 存在大量指向 127.0.0.1 或是虚拟网卡的 redirect (rdr) 转发规则。此节点修复命令(强制关闭 PF 防火墙拦截):Bashsudo pfctl -d 📋 快速排查口诀表排查顺序检查目标核心 CLI 命令正常标准Step 1配置文件cat /etc/resolv.confnameserver 为物理网关或公网 DNSStep 2DNS 解析dig www.baidu.com能正确返回 A 记录(IP 地址)Step 3路由网关netstat -nr | grep defaultdefault 唯一且指向真正能上网的物理网关Step 4物理链路ping -c 3 [网关IP]0% packet loss(延迟通常在几毫秒内)Step 5流量拦截sudo pfctl -d彻底关闭可能残留的防火墙拦截规则
终极病灶分析:什么是 link#27? 看看你之前的路由表输出:
Plaintext default link#27 UCSg utun16 在 macOS 中,link#X(如 link#27)代表的是系统内核直接绑定的链路层接口(Link-layer Interface)。 当 VPN 软件(特别是开启了 TUN 模式、Enhance 模式或智能路由的客户端)运行时,它会在内核中创建一个名为 utun16 的虚拟网卡,并把全局默认路由的下一跳直接绑死在物理链路 link#27 上。
这就是为什么你断开 VPN 后依然无法访问的原因: 普通的 sudo route delete default 只能删除常规的 IP 路由,但这种由 VPN 强行写入内核、绑定在 link# 上的链路层条件路由(UCSg 标记)具有极高的优先级,它像胶水一样粘在系统里。即使你以为 VPN 关了,系统在发送 SSH 数据包时,依然通过 link#27 丢给了已经失效的 utun16,导致 Codeberg 的服务器直接断开或根本收不到正确的握手包。
🛠️ 终极命令行解决方案 我们必须用点“暴力”手段,将这些残留的虚拟网卡接口和内核路由彻底抹去。
步骤 1:强行卸载/关停残留的 utun16 接口 既然流量总是被吸进 utun16,我们直接在链路层把它禁用:
Bash sudo ifconfig utun16 down (如果执行后路由表里还有别的 utunX 占领 default,可以针对性地将其 down 掉)
步骤 2:强制刷新 macOS 内核路由表(终极重置) 既然常规命令删不掉 link#27,我们直接通过重启 macOS 网络核心配置框架,逼迫系统重新扫描物理网卡:
Bash
1. 强制清理并重置系统的网络配置守护进程(这会顺便冲刷掉内核残留的 link 路由)
sudo configd -b -d
2. 软重启你的物理网卡 en0,让它重新拿回唯一的 default 控制权
sudo ifconfig en0 down && sudo ifconfig en0 up 步骤 3:验证路由表是否恢复纯净 现在执行以下命令,观察输出:
Bash netstat -nr | grep default 【正确标准】: 此时输出应该有且只有一行,且必须指向你的物理网关,绝对不能再出现 link# 或 utun:
Plaintext default 192.168.1.1 UGSc en0 🧪 最终验证 当路由表被彻底清洗干净,只剩下物理网卡 en0 后,再次发起冲击:
Bash ssh -vT git@codeberg.org 这一次,数据包终于能顺着真正的物理网线飞往 Codeberg 了!
从这份最新的路由表来看,你的 IPv4 路由表现在是非常干净且正确的:
只有一个 IPv4 默认网关 default 指向 192.168.1.1,并且成功绑定在物理网卡 en0 上。
之前残留的 link#27 和 utun16 的 IPv4 拦截已经彻底消失了。
剩下的 utun0 到 utun13 都是 IPv6 的本地链路多播路由(fe80:: 开头),属于 macOS 的正常系统组件(或 OrbStack 的内部通信),不会影响你的 IPv4 外网访问。
既然 IPv4 路由表已经完全正常,为什么 ssh -vT git@codeberg.org 依然卡在 kex_exchange_identification?
这就排除了路由表的嫌疑,问题被锁死在以下两个最后可能的原因:
🔍 终极原因定位与验证
- 终端的环境变量中残留了代理 虽然你的 VPN 软件关闭了,路由也清干净了,但如果你当前使用的这个终端窗口是在关闭 VPN 之前打开的,它里面可能依然保留着旧的 ALL_PROXY 或 HTTP_PROXY 环境变量。SSH 会尝试去走这个已经死掉的代理端口,导致连接直接被挂断。
验证与解决命令: 在当前终端直接执行以下命令,清空所有可能的代理变量:
Bash unset http_proxy https_proxy all_proxy HTTP_PROXY HTTPS_PROXY ALL_PROXY 或者,直接新开一个全新的终端标签页(Terminal Tab)来测试。
- 运营商或本地宽带网络对 Codeberg 的 22 端口进行了封锁 Codeberg(217.197.84.140)的服务器在海外。在不挂 VPN 的纯国内物理网络下,部分地区的运营商(如移动、长城宽带等)常常会直接拦截或干扰境外非主流 Git 平台的 22 端口(SSH 握手包被审查直接阻断)。
验证命令(测试 22 端口是否能正常建立流传输):
Bash nc -vzt codeberg.org 22 或者使用带有探测性质的 curl:
Bash curl -v ssh://codeberg.org:22 分析: 如果执行 nc 很久没反应,或者显示 Connection refused,说明你的物理宽带网络在没有 VPN 的情况下,本身就无法直接连接 Codeberg 的 SSH 服务。
🛠️ 终极解决方案 如果你清空了环境变量(或换了新终端),并且发现不挂 VPN 就是连不上 Codeberg 的 22 端口,请使用以下两个终极对策:
对策 A:使用 Codeberg 官方提供的 SSH 备用端口(443) 为了应对 22 端口被防火墙或运营商封锁的情况,Codeberg 官方支持通过 443 端口(通常用于 HTTPS,不易被封)来走 SSH 协议。
修改你的 ~/.ssh/config,将 codeberg 的配置改成这样:
Plaintext Host codeberg.org HostName altssh.codeberg.org User git Port 443 IdentityFile ~/.ssh/id_ed25519_codeberg IdentitiesOnly yes 修改完后,直接测试:
Bash ssh -vT git@codeberg.org 这个方法非常有效,它依然走的是你的 SSH 密钥认证,但完美绕过了被运营商封锁的 22 端口。
对策 B:切换为 HTTPS 协议克隆/推送 如果 SSH 无论如何都不通,最省心的办法就是放弃 SSH 协议,改用 HTTPS 协议(通过 Codeberg 的账号 + Token/密码)进行代码管理:
Bash
修改你本地 Git 仓库的远程连接地址为 HTTPS
git remote set-url origin https://codeberg.org/你的用户名/你的仓库名.git
❯ ping baidu.com ─╯ ping: cannot resolve baidu.com: Unknown host
~ ❯ cat /etc/resolv.conf ─╯
macOS Notice
This file is not consulted for DNS hostname resolution, address
resolution, or the DNS query routing mechanism used by most
processes on this system.
To view the DNS configuration used by this system, use:
scutil –dns
SEE ALSO
dns-sd(1), scutil(8)
This file is automatically generated.
nameserver 223.5.5.5
~ ────────── system Node at 10:39:09 AM ─╮ ❯ dig @223.5.5.5 www.baidu.com ─╯ ;; communications error to 223.5.5.5#53: timed out ;; communications error to 223.5.5.5#53: timed out ;; communications error to 223.5.5.5#53: timed out
; <<>> DiG 9.20.24 <<>> @223.5.5.5 www.baidu.com ; (1 server found) ;; global options: +cmd ;; no servers could be reached
现在的问题和之前的 Hosts 已经没有关系了。这就是最典型、最致命的 “VPN 关闭/残留引发的系统全局 DNS 死锁”。
看这三个连续的致命报错:
Could not resolve host: mirrors.aliyun.com(连国内的阿里云都无法解析)
ping: cannot resolve baidu.com: Unknown host(百度无法解析)
dig @223.5.5.5 www.baidu.com … timed out(直接向阿里公共 DNS 53端口发包全完超时)
🔍 根源分析:你的 Mac 经历了什么? 虽然你的 /etc/resolv.conf 里写着 nameserver 223.5.5.5,但因为你之前开启过 VPN(TUN 模式、增强模式或系统代理),VPN 在断开或者关闭时,没有干净地把接管的系统底层路由表(Routing Table)和虚拟网卡(utun)卸载掉。
导致的结果是:你的 Mac 依然盲目地把所有 DNS 流量(53端口)丢给一个已经不存在的 VPN 虚拟网关,导致你现在连国内网络、DNS 服务器全部失联。
强行重置 macOS 网络层
🛠️ 针对以太网的终极修复步骤
找出你当前以太网的准确“服务名称”
不同的扩展坞,以太网在系统里的名字不一样。我们先用这个命令列出你所有的网络接口:
networksetup -listallnetworkservices
❯ networksetup -listallnetworkservices ─╯
An asterisk (*) denotes that a network service is disabled.
Ethernet
Thunderbolt Bridge
Wi-Fi
Shadowrocket看一眼输出结果:
你会看到类似 AX88179A、USB 10/100/1000 LAN、Thunderbolt Ethernet 或 Ethernet 的名字。请记住这个名字(下面以 Ethernet 为例,如果你的叫别的,请自行替换)。
强行给以太网注入国内骨干 DNS
用官方命令强行把你的有线网络 DNS 冲刷干净,彻底摆脱 VPN 残留:
# "Ethernet"
sudo networksetup -setdnsservers "Ethernet" 223.5.5.5 119.29.29.29物理层重启以太网卡(重洗路由表)
我们需要让你的有线网卡重新向路由器申请一次内网 IP 和网关。
查明以太网的物理设备名:
networksetup -listallhardwareports
找到你以太网服务对应的 Device(比如 en0 或 en2)。
强行断开并重启该网卡:
Bash
# 是 en0
sudo ifconfig en0 down && sudo ifconfig en0 up
# 定位到是en0
❯ networksetup -listallhardwareports ─╯
Hardware Port: Ethernet
Device: en0
Ethernet Address: d0:11:e5:8a:4f:6c
Hardware Port: Ethernet Adapter (en5)
Device: en5
Ethernet Address: 9a:86:59:a9:af:e2
Hardware Port: Ethernet Adapter (en6)
Device: en6
Ethernet Address: 9a:86:59:a9:af:e3
Hardware Port: Ethernet Adapter (en7)
Device: en7
Ethernet Address: 9a:86:59:a9:af:e5
Hardware Port: Thunderbolt Bridge
Device: bridge0
Ethernet Address: 36:fd:e1:09:fc:c0
Hardware Port: Wi-Fi
Device: en1
Ethernet Address: d0:11:e5:79:c5:4f
Hardware Port: Thunderbolt 1
Device: en2
Ethernet Address: 36:fd:e1:09:fc:c0
Hardware Port: Thunderbolt 2
Device: en3
Ethernet Address: 36:fd:e1:09:fc:c4
Hardware Port: Thunderbolt 4
Device: en4
Ethernet Address: 36:fd:e1:09:fc:cc
VLAN Configurations
===================瞬间冲刷全网 DNS 缓存
sudo killall -HUP mDNSResponder🧪 验证网络
现在,拔掉再重新插一下你的以太网网线,让它亮起绿灯。然后再次在终端测试:
ping -c 3 baidu.com
dig @ 223.5.5.5 www.baidu.com
只要 ping 能够通,你的系统网络就彻底复活了!这时候你再运行 brew update(或者 bu),Homebrew 就能顺着你刚刚配好的物理线缆和 Hosts 节点秒速完成了。