fix: 彻底修复 stop 后 TUN 残留导致断网
根因: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 文件不准杀不掉) 两版已做语法检查与入口冒烟测试。
This commit is contained in:
@@ -142,10 +142,10 @@ network_leftovers() {
|
|||||||
[[ "$dev" != utun* ]] && continue
|
[[ "$dev" != utun* ]] && continue
|
||||||
local idx="${dev#utun}"
|
local idx="${dev#utun}"
|
||||||
[[ "$idx" != <-> ]] && continue
|
[[ "$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
|
return 0
|
||||||
fi
|
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
|
return 0
|
||||||
fi
|
fi
|
||||||
(( idx >= 1024 )) && return 0
|
(( idx >= 1024 )) && return 0
|
||||||
@@ -181,16 +181,19 @@ network_restore() {
|
|||||||
rm -f "$PID_FILE"
|
rm -f "$PID_FILE"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 2. 清理 mihomo 残留 TUN(按 fake-IP 网关 198.18.0.1 / 路由特征识别,
|
# 2. 清理 mihomo 残留 TUN / 全量劫持路由
|
||||||
# macOS 上 mihomo 常创建 utun7/utun8 这类低编号,不能只认 >=1024)
|
# 放宽识别: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 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=""
|
local mihomo_tuns=""
|
||||||
for dev in ${(s: :)tun_list}; do
|
for dev in ${(s: :)tun_list}; do
|
||||||
[[ "$dev" != utun* ]] && continue
|
[[ "$dev" != utun* ]] && continue
|
||||||
local idx="${dev#utun}"
|
local idx="${dev#utun}"
|
||||||
[[ "$idx" != <-> ]] && continue
|
[[ "$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"
|
mihomo_tuns+=" $dev"
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
@@ -200,11 +203,19 @@ network_restore() {
|
|||||||
fi
|
fi
|
||||||
(( idx >= 1024 )) && mihomo_tuns+=" $dev"
|
(( idx >= 1024 )) && mihomo_tuns+=" $dev"
|
||||||
done
|
done
|
||||||
for dev in ${(s: :)mihomo_tuns}; do
|
|
||||||
$verbose && echo -n " 清理残留 TUN $dev ... "
|
# 无条件清理全量劫持路由(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 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 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
|
|
||||||
|
for dev in ${(s: :)mihomo_tuns}; do
|
||||||
|
$verbose && echo -n " 清理残留 TUN $dev ... "
|
||||||
sudo ifconfig "$dev" down 2>/dev/null
|
sudo ifconfig "$dev" down 2>/dev/null
|
||||||
sudo ifconfig "$dev" destroy 2>/dev/null
|
sudo ifconfig "$dev" destroy 2>/dev/null
|
||||||
$verbose && echo "${GREEN}✓${NC}"
|
$verbose && echo "${GREEN}✓${NC}"
|
||||||
@@ -329,8 +340,11 @@ cmd_start() {
|
|||||||
echo ""
|
echo ""
|
||||||
echo -n " 启动中 ... "
|
echo -n " 启动中 ... "
|
||||||
nohup sudo "$MIHOMO_BIN" -d "$CONFIG_DIR" -f "$CONFIG_FILE" > "$LOG_FILE" 2>&1 &
|
nohup sudo "$MIHOMO_BIN" -d "$CONFIG_DIR" -f "$CONFIG_FILE" > "$LOG_FILE" 2>&1 &
|
||||||
echo $! > "$PID_FILE"
|
local sudo_pid=$!
|
||||||
sleep 1
|
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
|
if is_running; then
|
||||||
ok "已就绪"
|
ok "已就绪"
|
||||||
@@ -366,11 +380,13 @@ cmd_stop() {
|
|||||||
local pid=$(cat "$PID_FILE")
|
local pid=$(cat "$PID_FILE")
|
||||||
echo ""
|
echo ""
|
||||||
echo -n " 停止中 ... "
|
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
|
for i in {1..10}; do
|
||||||
if ! pgrep -F "$PID_FILE" &>/dev/null 2>&1; then
|
if ! pgrep -x mihomo >/dev/null 2>&1; then
|
||||||
ok "已停止 (PID: $pid)"
|
ok "已停止"
|
||||||
rm -f "$PID_FILE"
|
rm -f "$PID_FILE"
|
||||||
echo ""
|
echo ""
|
||||||
network_restore false
|
network_restore false
|
||||||
@@ -382,11 +398,11 @@ cmd_stop() {
|
|||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
warn "超时,强制终止..."
|
warn "超时,强制终止..."
|
||||||
sudo kill -9 "$pid" 2>/dev/null
|
sudo pkill -9 -x mihomo 2>/dev/null
|
||||||
sleep 0.5
|
sleep 0.5
|
||||||
rm -f "$PID_FILE"
|
rm -f "$PID_FILE"
|
||||||
|
|
||||||
if pgrep -l mihomo >/dev/null 2>&1; then
|
if pgrep -x mihomo >/dev/null 2>&1; then
|
||||||
err "停止失败,残留进程仍在运行"
|
err "停止失败,残留进程仍在运行"
|
||||||
dim "试试: sudo pkill -9 mihomo"
|
dim "试试: sudo pkill -9 mihomo"
|
||||||
return 1
|
return 1
|
||||||
|
|||||||
@@ -168,8 +168,8 @@ function Test-NetworkLeftovers {
|
|||||||
Where-Object { $_.InterfaceDescription -like "*Wintun*" -or $_.InterfaceDescription -like "*Mihomo*" }
|
Where-Object { $_.InterfaceDescription -like "*Wintun*" -or $_.InterfaceDescription -like "*Mihomo*" }
|
||||||
if ($adapters) { return $true }
|
if ($adapters) { return $true }
|
||||||
|
|
||||||
# 3. 残留路由(指向 198.18.0.1)
|
# 3. 残留路由(网关落在 198.18.x fake-IP 段)
|
||||||
$routes = Get-NetRoute -ErrorAction SilentlyContinue | Where-Object { $_.NextHop -eq "198.18.0.1" }
|
$routes = Get-NetRoute -ErrorAction SilentlyContinue | Where-Object { $_.NextHop -match '^198\.18\.' }
|
||||||
if ($routes) { return $true }
|
if ($routes) { return $true }
|
||||||
|
|
||||||
# 4. DNS 劫持(指向 127.0.0.1 或 198.18.x)
|
# 4. DNS 劫持(指向 127.0.0.1 或 198.18.x)
|
||||||
@@ -196,20 +196,23 @@ function Invoke-NetworkRestore {
|
|||||||
Remove-Item $PidFile -Force -ErrorAction SilentlyContinue
|
Remove-Item $PidFile -Force -ErrorAction SilentlyContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
# 2. 清理残留 wintun 网卡
|
# 2. 清理残留 wintun 网卡 + 其上的全部路由
|
||||||
$adapters = Get-NetAdapter -ErrorAction SilentlyContinue |
|
$adapters = Get-NetAdapter -ErrorAction SilentlyContinue |
|
||||||
Where-Object { $_.InterfaceDescription -like "*Wintun*" -or $_.InterfaceDescription -like "*Mihomo*" }
|
Where-Object { $_.InterfaceDescription -like "*Wintun*" -or $_.InterfaceDescription -like "*Mihomo*" }
|
||||||
foreach ($adapter in $adapters) {
|
foreach ($adapter in $adapters) {
|
||||||
if ($ShowProgress) { Write-Host -NoNewline " 清理残留 TUN $($adapter.Name) ... " }
|
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
|
netsh interface set interface "$($adapter.Name)" admin=disable 2>$null | Out-Null
|
||||||
if ($ShowProgress) { Write-Host "${Green}✓${NC}" }
|
if ($ShowProgress) { Write-Host "${Green}✓${NC}" }
|
||||||
}
|
}
|
||||||
|
|
||||||
# 3. 清理残留路由
|
# 3. 清理残留路由(网关落在 198.18.x fake-IP 段的,无条件删除)
|
||||||
$mihomoRoutes = Get-NetRoute -ErrorAction SilentlyContinue |
|
$mihomoRoutes = Get-NetRoute -ErrorAction SilentlyContinue |
|
||||||
Where-Object { $_.NextHop -eq "198.18.0.1" }
|
Where-Object { $_.NextHop -match '^198\.18\.' }
|
||||||
foreach ($route in $mihomoRoutes) {
|
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 缓存
|
# 4. 刷新 DNS 缓存
|
||||||
@@ -399,6 +402,8 @@ function Invoke-CmdStop {
|
|||||||
Write-Host ""
|
Write-Host ""
|
||||||
W-Warn "超时,强制终止..."
|
W-Warn "超时,强制终止..."
|
||||||
Stop-Process -Id $procId -Force -ErrorAction SilentlyContinue
|
Stop-Process -Id $procId -Force -ErrorAction SilentlyContinue
|
||||||
|
# 兜底:按进程名强杀,防止 PID 文件不准导致杀不掉
|
||||||
|
Get-Process -Name mihomo -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
|
||||||
Start-Sleep -Milliseconds 500
|
Start-Sleep -Milliseconds 500
|
||||||
Remove-Item $PidFile -Force -ErrorAction SilentlyContinue
|
Remove-Item $PidFile -Force -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user