From e94e38e39ca84fe6c7274a00936fc59ef67e1080 Mon Sep 17 00:00:00 2001 From: lukeopen Date: Mon, 10 Aug 2026 17:29:24 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=BD=BB=E5=BA=95=E4=BF=AE=E5=A4=8D=20s?= =?UTF-8?q?top=20=E5=90=8E=20TUN=20=E6=AE=8B=E7=95=99=E5=AF=BC=E8=87=B4?= =?UTF-8?q?=E6=96=AD=E7=BD=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根因:mihomo 退出后 0.0.0.0/1 + 128.0.0.0/1 黑洞路由未清掉, 所有流量指向已消失的 TUN 网关 198.18.0.1 → 断网。 macOS (clash): - start 解析 mihomo 真实 PID 写入 PID 文件(原记的是 sudo PID) - cmd_stop 直接 pkill -TERM/-9 mihomo,不再依赖 sudo PID - network_restore 无条件删除 0.0.0.0/1 + 128.0.0.0/1 黑洞路由, 并放宽 TUN 识别到整个 198.18.x fake-IP 段(不再只认 198.18.0.1) - network_leftovers 检测同步放宽 Windows (clash.ps1): - NetworkRestore 删除 wintun 网卡上的全部路由(不限网段)+ 清理 网关落在 198.18.x 的所有路由 - Test-NetworkLeftovers 路由检测放宽到 198.18.x - CmdStop 强制终止时增加按进程名强杀兜底(防止 PID 文件不准杀不掉) 两版已做语法检查与入口冒烟测试。 --- clash | 46 +++++++++++++++++++++++++++++++--------------- clash.ps1 | 17 +++++++++++------ 2 files changed, 42 insertions(+), 21 deletions(-) diff --git a/clash b/clash index e4d63b4..5f40a30 100755 --- a/clash +++ b/clash @@ -142,10 +142,10 @@ network_leftovers() { [[ "$dev" != utun* ]] && continue local idx="${dev#utun}" [[ "$idx" != <-> ]] && continue - if ifconfig "$dev" 2>/dev/null | grep -q "inet 198.18.0.1 "; then + if ifconfig "$dev" 2>/dev/null | grep -qE "inet 198\.18\."; then return 0 fi - if netstat -rn -f inet 2>/dev/null | grep -qE "198\.18\.0\.1[[:space:]]+.*[[:space:]]+$dev"; then + if netstat -rn -f inet 2>/dev/null | grep -qE "198\.18\.[[:space:]]+.*[[:space:]]+$dev"; then return 0 fi (( idx >= 1024 )) && return 0 @@ -181,16 +181,19 @@ network_restore() { rm -f "$PID_FILE" fi - # 2. 清理 mihomo 残留 TUN(按 fake-IP 网关 198.18.0.1 / 路由特征识别, - # macOS 上 mihomo 常创建 utun7/utun8 这类低编号,不能只认 >=1024) + # 2. 清理 mihomo 残留 TUN / 全量劫持路由 + # 放宽识别:fake-IP 网关只要是 198.18.x 段都算(不限于 198.18.0.1), + # 并无条件删除 0.0.0.0/1 + 128.0.0.0/1 这两条黑洞路由,防止残留导致断网 local tun_list=$(ifconfig -l 2>/dev/null) - local route_devs=$(netstat -rn -f inet 2>/dev/null | awk '$2=="198.18.0.1" && $4 ~ /^utun[0-9]+$/ {print $4}' | sort -u) + # fake-IP 网关段(默认 198.18.x,放宽不再只认 198.18.0.1) + local fake_gws=$(netstat -rn -f inet 2>/dev/null | awk '$4 ~ /^utun[0-9]+$/ && $2 ~ /^198\.18\./ {print $2}' | sort -u) + local route_devs=$(netstat -rn -f inet 2>/dev/null | awk '$4 ~ /^utun[0-9]+$/ && $2 ~ /^198\.18\./ {print $4}' | sort -u) local mihomo_tuns="" for dev in ${(s: :)tun_list}; do [[ "$dev" != utun* ]] && continue local idx="${dev#utun}" [[ "$idx" != <-> ]] && continue - if ifconfig "$dev" 2>/dev/null | grep -q "inet 198.18.0.1 "; then + if ifconfig "$dev" 2>/dev/null | grep -qE "inet 198\.18\."; then mihomo_tuns+=" $dev" continue fi @@ -200,11 +203,19 @@ network_restore() { fi (( idx >= 1024 )) && mihomo_tuns+=" $dev" done + + # 无条件清理全量劫持路由(0.0.0.0/1 + 128.0.0.0/1),几乎必定是 mihomo 创建 + for gw in ${(s: :)fake_gws}; do + sudo route -n delete -net 0.0.0.0/1 "$gw" 2>/dev/null + sudo route -n delete -net 128.0.0.0/1 "$gw" 2>/dev/null + sudo route -n delete -net 198.18.0.0/16 "$gw" 2>/dev/null + done + # 兜底:常见默认网关组合,防止识别遗漏留下黑洞 + sudo route -n delete -net 0.0.0.0/1 198.18.0.1 2>/dev/null + sudo route -n delete -net 128.0.0.0/1 198.18.0.1 2>/dev/null + for dev in ${(s: :)mihomo_tuns}; do $verbose && echo -n " 清理残留 TUN $dev ... " - sudo route -n delete -net 0.0.0.0/1 198.18.0.1 2>/dev/null - sudo route -n delete -net 128.0.0.0/1 198.18.0.1 2>/dev/null - sudo route -n delete -net 198.18.0.0/16 198.18.0.1 2>/dev/null sudo ifconfig "$dev" down 2>/dev/null sudo ifconfig "$dev" destroy 2>/dev/null $verbose && echo "${GREEN}✓${NC}" @@ -329,8 +340,11 @@ cmd_start() { echo "" echo -n " 启动中 ... " nohup sudo "$MIHOMO_BIN" -d "$CONFIG_DIR" -f "$CONFIG_FILE" > "$LOG_FILE" 2>&1 & - echo $! > "$PID_FILE" + local sudo_pid=$! sleep 1 + # 解析 mihomo 真实 PID(sudo 的子进程),PID 文件记它,stop 才能精确杀 + local mihomo_pid=$(pgrep -P "$sudo_pid" 2>/dev/null | head -1) + echo "${mihomo_pid:-$sudo_pid}" > "$PID_FILE" if is_running; then ok "已就绪" @@ -366,11 +380,13 @@ cmd_stop() { local pid=$(cat "$PID_FILE") echo "" echo -n " 停止中 ... " - sudo kill "$pid" 2>/dev/null + # 直接给 mihomo 发 SIGTERM(PID 文件记的是 sudo,按进程名杀最稳) + sudo pkill -TERM -x mihomo 2>/dev/null + sudo kill "$pid" 2>/dev/null # 兜底:也通知 sudo 进程 for i in {1..10}; do - if ! pgrep -F "$PID_FILE" &>/dev/null 2>&1; then - ok "已停止 (PID: $pid)" + if ! pgrep -x mihomo >/dev/null 2>&1; then + ok "已停止" rm -f "$PID_FILE" echo "" network_restore false @@ -382,11 +398,11 @@ cmd_stop() { echo "" warn "超时,强制终止..." - sudo kill -9 "$pid" 2>/dev/null + sudo pkill -9 -x mihomo 2>/dev/null sleep 0.5 rm -f "$PID_FILE" - if pgrep -l mihomo >/dev/null 2>&1; then + if pgrep -x mihomo >/dev/null 2>&1; then err "停止失败,残留进程仍在运行" dim "试试: sudo pkill -9 mihomo" return 1 diff --git a/clash.ps1 b/clash.ps1 index 3a07280..bd8f5ea 100644 --- a/clash.ps1 +++ b/clash.ps1 @@ -168,8 +168,8 @@ function Test-NetworkLeftovers { Where-Object { $_.InterfaceDescription -like "*Wintun*" -or $_.InterfaceDescription -like "*Mihomo*" } if ($adapters) { return $true } - # 3. 残留路由(指向 198.18.0.1) - $routes = Get-NetRoute -ErrorAction SilentlyContinue | Where-Object { $_.NextHop -eq "198.18.0.1" } + # 3. 残留路由(网关落在 198.18.x fake-IP 段) + $routes = Get-NetRoute -ErrorAction SilentlyContinue | Where-Object { $_.NextHop -match '^198\.18\.' } if ($routes) { return $true } # 4. DNS 劫持(指向 127.0.0.1 或 198.18.x) @@ -196,20 +196,23 @@ function Invoke-NetworkRestore { Remove-Item $PidFile -Force -ErrorAction SilentlyContinue } - # 2. 清理残留 wintun 网卡 + # 2. 清理残留 wintun 网卡 + 其上的全部路由 $adapters = Get-NetAdapter -ErrorAction SilentlyContinue | Where-Object { $_.InterfaceDescription -like "*Wintun*" -or $_.InterfaceDescription -like "*Mihomo*" } foreach ($adapter in $adapters) { if ($ShowProgress) { Write-Host -NoNewline " 清理残留 TUN $($adapter.Name) ... " } + # 删除该网卡上的所有路由(不限网段),避免黑洞残留 + Get-NetRoute -InterfaceIndex $adapter.InterfaceIndex -ErrorAction SilentlyContinue | + Remove-NetRoute -Confirm:$false -ErrorAction SilentlyContinue netsh interface set interface "$($adapter.Name)" admin=disable 2>$null | Out-Null if ($ShowProgress) { Write-Host "${Green}✓${NC}" } } - # 3. 清理残留路由 + # 3. 清理残留路由(网关落在 198.18.x fake-IP 段的,无条件删除) $mihomoRoutes = Get-NetRoute -ErrorAction SilentlyContinue | - Where-Object { $_.NextHop -eq "198.18.0.1" } + Where-Object { $_.NextHop -match '^198\.18\.' } foreach ($route in $mihomoRoutes) { - Remove-NetRoute -DestinationPrefix $route.DestinationPrefix -NextHop "198.18.0.1" -Confirm:$false -ErrorAction SilentlyContinue + Remove-NetRoute -DestinationPrefix $route.DestinationPrefix -NextHop $route.NextHop -Confirm:$false -ErrorAction SilentlyContinue } # 4. 刷新 DNS 缓存 @@ -399,6 +402,8 @@ function Invoke-CmdStop { Write-Host "" W-Warn "超时,强制终止..." Stop-Process -Id $procId -Force -ErrorAction SilentlyContinue + # 兜底:按进程名强杀,防止 PID 文件不准导致杀不掉 + Get-Process -Name mihomo -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue Start-Sleep -Milliseconds 500 Remove-Item $PidFile -Force -ErrorAction SilentlyContinue