From 2d2baf887cbd005dce3092f82dd6e55d05180fbf Mon Sep 17 00:00:00 2001
From: wangjia <809946525@qq.com>
Date: Mon, 22 Jun 2026 08:58:10 +0800
Subject: [PATCH] =?UTF-8?q?docs+test:=20macOS=20=E9=9A=A7=E9=81=93?=
=?UTF-8?q?=E8=B8=A9=E5=9D=91=E5=A4=8D=E7=9B=98/=E8=A7=84=E5=88=99=20+=20V?=
=?UTF-8?q?PN=20=E6=B5=8B=E8=AF=95=E8=B0=83=E7=A0=94=20+=20=E9=BB=91?=
=?UTF-8?q?=E7=9B=92=E6=B5=8B=E8=AF=95=E5=B7=A5=E5=85=B7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- docs/macos-sysext-realize-troubleshooting.html:补"运行时"章节(libbox 空指针、
接口时序、队列死锁、DNS 劫持)+ 运行时 checklist。
- docs/vpn-testing-research.md:VPN 测试调研(行业方法论 + GFW 专项 + Pangolin
分层测试项 + 三层测试工具设计)。
- CLAUDE.md:新增「client/ macOS 原生隧道」铁律章节(构建发版/系统扩展 realize 要求/
libbox+NE 集成/配置由服务端渲染/已知坑)。
- scripts/vpn_test.sh:客户端侧黑盒测试工具(出口 IP/DNS/可达性/延迟/吞吐/IPv6 泄漏,
PASS/WARN/FAIL,可远程跑)。
Co-Authored-By: Claude Opus 4.8 系统扩展能 realize/激活只是第一步。让内嵌的 libbox(sing-box)真正建起隧道、能上网,又踩了四个坑(均在 扩展进程启动 360ms 后自杀(SIGABRT)。把 stderr 重定向到 App Group 容器文件后抓到 Go panic: 此版本 libbox 会解引用第三个 openTun 打点显示卡在 修复:把 libbox 启动整段放进后台队列( 隧道起来了、TCP 能经 REALITY 出海,但域名打不开。box.log 显示发往隧道 DNS 的查询走了直连: 隧道 DNS 地址 172.19.0.2 落在 LAN 直连规则 172.16/12 内,被路由成直连(发往不存在的主机)→ 解析全失败。修复(服务端 注:sing-box 1.13 的 另一个发版必知:每次构建必递增 连通验证(从命令行):5.5 加载之后:从"扩展能起"到"真正连通"(运行时)
+PacketTunnelProvider.swift / 服务端配置):运行时 ① libbox
+startOrReloadService(options:) 传 nil → 空指针崩溃 扩展进程 SIGABRTpanic: runtime error: invalid memory address or nil pointer dereference
+libbox.(*CommandServer).StartOrReloadService(server, {config}, 0x0) command_server.go:175
+options 参数(虽标 _Nullable)。修复:传非空 LibboxOverrideOptions() 而非 nil。运行时 ② 默认接口监控异步返回 →
+no available network interface 启动报错startDefaultInterfaceMonitor 启动 NWPathMonitor 后立即返回,但首个 path 回调是异步晚到的;sing-box 紧接着的网络操作拿到的默认接口索引还是 -1。修复:用信号量**阻塞到首个 path 更新再返回**(带 5s 超时),对齐 sing-box-for-apple。运行时 ③ provider 队列三方死锁 → 隧道永远卡 connecting 最隐蔽
+setTunnelNetworkSettings 的信号量等待,回调永不触发:
+
+startTunnel → 同步调 startOrReloadService(阻塞该队列)sem.wait() 等 setTunnelNetworkSettings 完成DispatchQueue.global().async),startTunnel 立即返回、provider 队列腾出来投递回调。运行时 ④ 缺 DNS 劫持 → 隧道连上但打不开网站 最后一关
+inbound packet connection to 172.19.0.2:53
+router: match ip_cidr=[..172.16.0.0/12..] => route(direct) # DNS 被 LAN 规则吞去直连 → 解析失败
+clientconfig.go):route.rules 首条加 {"action":"hijack-dns","port":[53]}(排在 LAN 规则之前),把 :53 查询交给 sing-box DNS 模块。protocol:"dns" 匹配需先 sniff;按目的端口 53 匹配最稳、不依赖 sniff。CFBundleVersion。 sysextd 按(标识符, 版本)去重;同版本号重装**不会替换**已激活的旧扩展,跑的还是旧代码(排查时极易被误导)。$ curl https://api.ipify.org → 103.119.13.48 # 出口=节点 IP,流量走隧道
+$ curl -o/dev/null -w '%{http_code}' https://github.com → 200
+box.log: router: match[0] port=53 => hijack-dns → dns: exchanged A github.com 140.82.116.3
+
6. 给后人的排查 checklist(Developer ID 网络系统扩展)
systemextensionsctl developer on,或在 15/14 机器上验证。
startOrReloadService(options:) 传**非空** LibboxOverrideOptions(),别传 nil。startTunnel 里的 libbox 启动放**后台队列**(DispatchQueue.global().async),别在 provider 队列同步跑(死锁)。startDefaultInterfaceMonitor **阻塞到首个 path 更新再返回**。action:hijack-dns,按 port 53),排在 LAN/分流规则之前。CFBundleVersion**,否则 sysextd 不更新已激活的扩展。log.output 指向容器文件(writeLogs 回调启动期不触发);Go fatal 走 stderr,需把扩展 stderr 重定向到文件才看得到。改动文件:client/macos/PacketTunnel/Info.plist · client/macos/PacketTunnel/PacketTunnel.entitlements · client/macos/Runner/Release.entitlements · client/macos/PacketTunnel/PacketTunnelProvider.swift · client/macos/Runner.xcodeproj/project.pbxproj(OTHER_LDFLAGS/PRODUCT_NAME/移除 Embed Libbox/加 timestamp)。验证机:cara,干净 macOS 15.3.2 Intel。
改动文件:扩展 Info.plist/entitlements/main.swift/PacketTunnelProvider.swift · Runner/Release.entitlements · Runner.xcodeproj/project.pbxproj(OTHER_LDFLAGS/PRODUCT_NAME/移除 Embed Libbox/--timestamp/版本号) · scripts/local_test.sh · client/macos/sign_libbox.sh · 服务端 server/internal/httpapi/clientconfig.go(DNS 劫持)。验证机:cara,干净 macOS 15.3.2 Intel,出口 IP=节点、国外站可达。