sing-box 能不能用 eBPF(本机 CachyOS 现状实测)
结论时间:2026-10-02 方法:全部为本机真实二进制 / 真实内核实测,不是推断。
对象:当时正在跑的 sing-box 1.14.2(/etc/sing-box/config.json,mixed-in + tun-in)。
📌 2026-10-02 晚复核(现状,先读这条):本文开头几节的”用不了 eBPF”是自建包之前的状态,已被推翻。
现在/usr/bin/sing-box就是 fork 构建(sing-box-ebpf 1.14.2.ref1nd-2,Tags: …,with_ebpf),
配置是mixed-in+ebpf-in(tun-in已移除),服务正跑在 eBPF 模式下:local=cgroup✅、shared=packet_rewriteonvirbr0✅(共享附件以 TCX 挂载,api ebpf实测role=shared mechanism=tcx framing=ethernet)。
下面凡说”现在用不了 eBPF / 现在的二进制不行”的段落,都按这条读。
0. 一句话结论
你现在装的 sing-box 用不了 eBPF;不是配置问题,是二进制里没有这个入站。
❌ 已被推翻(2026-10-02 晚):上面这句说的是当时装的 Arch 官方包。现在装的是自建
sing-box-ebpf
包(fork 构建,Tags含with_ebpf),eBPF 入站正在用。本节其余事实性描述仍然成立。
eBPF 入站是reF1nd/sing-box分支的实验特性(编译标签with_ebpf,仅 Linux / Android),
上游SagerNet/sing-box没有这个功能。Android 上的 NetProxy 模块正是内置了这个分支内核。你这台机器的内核完全够用——我已经现场编译出带
with_ebpf的 Linux 版二进制,
它能直接读你现有的/etc/sing-box配置(checkexit=0),并对本机跑了内核能力预检。而且不必用 alpha:分支在 stable 基线
v1.14.2-reF1nd(与你现装的 1.14.2 同基线)上同样带
eBPF,我也编好并验证过了(check -C /etc/sing-boxexit=0,且没有 1.15 的tun.stack弃用告警)。
| 目标 | 能否用 eBPF | 依据 |
|---|---|---|
| 当时的 sing-box 1.14.2(Arch 官方包,已卸) | ❌ 不能 | 实测 FATAL: unknown inbound type: ebpf(exit=1) |
现装的 sing-box-ebpf 1.14.2.ref1nd-2(自建包,fork 构建) |
✅ 已在用 | sing-box version 的 Tags 含 with_ebpf;api ebpf 显示 local(cgroup) + shared(tcx) 均已挂载(2026-10-02 晚实测) |
| 本机换用 reF1nd 分支二进制 | ✅ 可以(内核已具备,二进制已编好,见第 4 节) | 见第 4、5 节 |
| Android + NetProxy 模块 | ✅ 设计如此(eBPF 是唯一数据面) | 模块无 TPROXY / REDIRECT 回退,内核不行就起不来 |
1. 为什么当时的二进制不行(实测)
📌 2026-10-02 晚追注(本节保留原文,属历史记录):下面是 Arch 官方包的实测。现在装的
sing-box-ebpf
包Tags里有with_ebpf(sing-box version实测尾部…,badlinkname,tfogo_checklinkname0,with_ebpf),
所以”check报 unknown inbound type”这个故障在当前二进制上不会复现。
$ cat > /tmp/ebpf-test.json # 官方样例的 ebpf 入站
$ sing-box check -c /tmp/ebpf-test.json
FATAL[0000] decode config at /tmp/ebpf-test.json: inbounds[0]: unknown inbound type: ebpf
exit=1
当时(Arch 官方包)二进制的编译标签里没有 with_ebpf:
Tags: with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,
with_tailscale,with_ccm,with_ocm,with_cloudflared,with_naive_outbound,with_usbip,
with_openvpn,with_openconnect,badlinkname,tfogo_checklinkname0
2. eBPF 入站到底是谁的功能
| 项目 | 情况 |
|---|---|
| 上游 SagerNet/sing-box | ❌ 没有:docs/configuration/inbound/ 下无 ebpf.md(404),入站列表只有 tun / redirect / tproxy / mixed / socks / http … |
reF1nd/sing-box(分支 reF1nd-testing) |
✅ 有:eBPF 入站文档,标注”sing-box 1.15.0 中的更改 / 实验功能”,//go:build with_ebpf && (linux || android) |
| eBPF 程序本体 | 在独立依赖 CHIZI-0618/sing-ebpf 里(internal/bpfgen/*_bpfel.o、*_bpfeb.o 预编译内嵌)→ 普通构建不需要 NDK / clang,只要 Go |
| Fanju6/NetProxy-Magisk(Android) | 内置的”sing-box 内核”就是上面这个分支,构建标签含 with_ebpf(见其 .github/resources.json) |
| 分支发布产物 | GitHub Releases 为空(只有 tag;2026-10-02 晚复核仍为 0)→ Linux / Android 二进制都要自己编,Arch 仓库更没有(本机已用自建包 ~/pkgbuild-source/packages/sing-box-ebpf 解决,见 4.1) |
分支自述:“本分支现已加入实验性的 eBPF 入站支持,可在 Linux 与 Root Android 上通过内核 eBPF 程序接管网络连接,
无需创建 TUN 设备,也不依赖传统的 iptables/nftables REDIRECT 或 TPROXY 规则”。
2.1 “alpha 才有?有没有编好的二进制?”——逐条实测
| 问题 | 答案 | 证据 |
|---|---|---|
| eBPF 只在 alpha? | ❌ 不是。1.14 stable 基线的分支 tag 一样有 | v1.14.0-reF1nd / v1.14.1-reF1nd / v1.14.2-reF1nd 的 include/ebpf.go 全部 HTTP 200;v1.14.2-reF1nd 的 option/ebpf.go 里 local/shared/cgroup/packet_rewrite/include_uid 字段与新版一致,protocol/ebpf/ 有 54 个文件 |
| 上游 alpha 有预编译二进制? | ✅ 有,但装了也没 eBPF | 上游 v1.15.0-alpha.9 是 prerelease,带 167 个资产(linux/darwin/windows 各架构、SFA-*.apk、deb/rpm…);但 eBPF 入站源码在上游根本不存在(docs/configuration/inbound/ebpf.md → 404) |
| 分支(reF1nd)有预编译二进制? | ❌ 一个都没有 | GET /repos/reF1nd/sing-box/releases?per_page=100 → count: 0。只有 tag,无任何 asset |
| NetProxy 模块的核是哪来的? | 它自己编的 | 其 .github/workflows/update-resources.yml 用 TAGS=${{ … }} + make -C .sing-box-source build,然后 go version -m .sing-box-source/sing-box | grep -q 'with_ebpf' 自检 |
| 分支自己的 Docker 镜像? | ❌ 不能用 | docker.yml 用的标签集是 release/DEFAULT_BUILD_TAGS*(不含 with_ebpf),且只在 release 事件触发——而分支没有 release |
第三方 Docker Hub eyaoyi/sing-box-ref1nd-stable |
⚠️ 别用 | 最后更新 2026-08,停在 v1.13.19;非官方,且 eBPF 在容器里还要额外处理 cgroup/权限 |
结论:想要 eBPF,二进制只能自己编(或自己打包)。 幸运的是构建只要 Go(BPF 对象是CHIZI-0618/sing-ebpf 模块里预编译内嵌的 *_bpfel.o),
不需要 NDK / clang / CGO。这正好适合你 ~/pkgbuild-source 那套 CI。
3. 你这台机器的内核体检(全部实测通过)
Linux 7.2.8-1-cachyos-bore-lto #1 SMP PREEMPT_DYNAMIC x86_64
| 要求(来自分支的 eBPF 内核要求文档) | 你的机器 | 结果 |
|---|---|---|
CONFIG_BPF / CONFIG_BPF_SYSCALL / CONFIG_BPF_JIT |
y / y / y | ✅ |
CONFIG_CGROUP_BPF(local cgroup 数据面默认) |
y | ✅ |
CONFIG_NET_CLS_BPF / CONFIG_NET_SCH_INGRESS / CONFIG_NET_CLS_ACT |
=m / =m / y |
✅(ko 在 /lib/modules/…/net/sched/,按需加载) |
CONFIG_VETH(local tc 数据面才需要) |
=m |
✅ |
CONFIG_BPF_STREAM_PARSER(SOCKMAP 加速) |
y | ✅ |
| cgroup v2 统一挂载 | cgroup2 /sys/fs/cgroup cgroup2 rw,… |
✅ |
| bpffs | bpf /sys/fs/bpf bpf rw,… |
✅ |
| LPM trie 内核缺陷(6.6.0–6.6.46) | 7.2.8,不在受影响区间 | ✅ |
| 现有 tc 钩子冲突 | tc filter show 所有网卡 无任何 filter(dae 残留早已清净) |
✅(2026-10-02 晚复核:virbr0 上有 libvirt 自己的 htb qdisc + 一条 u32 filter(csum 动作),不是 eBPF 钩子;shared 走的是 TCX,与这类 filter 不冲突) |
3.1 root 预检正式结果:全绿通过 ✅
sudo ~/.cache/sing-box-bin/sing-box-v1.14.2-reF1nd-with_ebpf \
tools ebpf status --mode all --interface virbr0 --json
"result": "preflight_passed",
"preflight": true, "exact_object_load": true,
"summary": { "pass": 51, "warn": 0, "fail": 0, "unknown": 0,
"required_failures": 0, "required_unknowns": 0, "required_issues": 0 }
含义(不是”参数看着对”,是真的把对象喂给 verifier 并加载了):
| 关键 PASS 项 | 说明 |
|---|---|
selected cgroup eBPF object |
真加载了 cgroup 程序与其 map 规格(未挂 hook);coarse-time UDP 变体可用 |
selected packet-rewrite eBPF object |
真加载了 shared 报文改写程序(未挂 classifier) |
BPF map type Array / Hash / LRUHash / LPMTrie / PerCPUArray |
五类 map 全部可建 |
CGroupSockAddr + bpf_get_current_uid_gid |
本机 UID 级(分应用)接管能力就位 |
| IPv4/IPv6 透明 TCP/UDP、original-dst、packet-info | 透明 socket 全套通过 |
BPF JIT |
bpf_jit_enable=1 |
locked-memory limit |
RLIMIT_MEMLOCK 自动调成 unlimited |
TC interface framing virbr0 |
ether 帧 ✅ → 虚拟机共享路径可用(2026-10-02 晚已端到端实测通过,见 3.2 追注) |
LPM trie policy updates |
7.2.8 不在缺陷区间,PASS |
⚠️ 一个必须注意的细节:探测里那条
cgroup path报的是/sys/fs/cgroup/user.slice/…/kitty-30065-0.scope—— 那是”探测进程自己“所在的 cgroup
(我从你终端里跑的),不是接管范围。真正全局接管要么省略local.cgroup_path(挂 cgroup v2 根层级),
要么显式写/sys/fs/cgroup;写成上面那个 scope 就只会代理你终端里跑的程序。
官方也明说”attach permission and exclusivity are still checked during startup“——
挂载权限只在真实启动时校验,所以第一次起服务才是真考验(见 5.2 / 5.4 的兜底方案)。
之前非 root 探测时看到的
UNKNOWN/FATAL: map create: operation not permitted不是内核不支持,是权限不够:/proc/sys/kernel/unprivileged_bpf_disabled = 2,非 root 连一个 map 都建不了。
即 eBPF 相关的check、探测、启动都必须 root。
3.2 A/B 试跑结果(2026-10-02 08:54,同一套判据)
用 trial/run-ebpf-trial.sh 实测(脚本先停 TUN 服务、前台起 eBPF 核心、跑完恢复;脚本在仓库 trial/ 下):
| 判据 | TUN 基线 | eBPF 阶段 | 结论 |
|---|---|---|---|
tun0_state |
present | absent | 真的没有 TUN 设备 |
proxy_exit_ip |
无 TUN 仍在走代理 ✅ | ||
ipv6_http_code |
200 | 200 | IPv6 正常 ✅ |
ipv6_blackhole |
0 | 0 | 无黑洞规则 ✅ |
direct_isp_ip |
国内仍直连 ✅ | ||
resolve_github |
ok | ok | DNS 通 ✅ |
ads_dns_answer |
[](被拦) |
⚠️ 127.0.0.53 timed out(瞬时) |
见下 |
结论:eBPF 数据面在这台机器上完整可用。 尤其重要的是,3.1 里唯一”没被预验证”的点
——root 挂 cgroup v2 根层级的权限/独占性——实际启动时通过了(日志 sing-box started (0.13s),
且流量确实被接管)。分应用所需的 UID hook 也已在预检中确认就位。
两处尚需一次干净重跑才能定论:
ads_dns_answer那次超时:切换瞬间 systemd-resolved 的上游(TUN 的172.19.0.2)刚消失,
属于瞬时;且该判据当时还受 systemd-resolved 自身缓存干扰(脚本 v2 已改为dig @1.1.1.1+
每轮flush-caches)。- 第二轮
SHARED_IFACE=virbr0的”基线”其实是无代理直连(因为第一轮恢复失败),
所以那份 diff 无参考价值;虚拟机共享路径仍未端到端验证(当时virbr0是 DOWN,没有虚拟机在跑)。
✅ 2026-10-02 晚追注:这一条已经补上了。
shared(packet_rewriteonvirbr0)已做端到端实测
(netns + veth 接到virbr0):直连与走代理两条路都通过——Clash API 命中链路=Proxy → Auto → 新加坡Z02,virbr0抓包 11 SYN / 11 SYN-ACK,curl200。
另外,local的tc数据面实测不可用(register TC eBPF TCP listener: operation not supported),
所以 5.2 里”cgroup 被拒就换 tc”的兜底不成立(见 5.2 / 6 的追注)。
🔒 本节表格里 TUN 基线的出口 IP 原文是真实代理出口地址,公开仓库已脱敏为<proxy-exit-ip>。
4. 我现场编译出来的二进制(已放好)
用 podman 里的 Go 1.26.8 编的分支 reF1nd-testing(HEAD bccceeb):
podman run --rm -v /tmp/sb-fork:/src -v gomodcache:/go/pkg/mod -w /src \
-e CGO_ENABLED=0 -e GOOS=linux -e GOARCH=amd64 \
docker.io/library/golang:1.26 sh -c "go build -trimpath -tags '$TAGS' \
-ldflags \"-s -w -X 'github.com/sagernet/sing-box/constant.Version=v1.15.0-alpha.9-reF1nd'\" \
-o /src/sing-box-ebpf ./cmd/sing-box"
产物(两个版本都编好并验证过,可任选;推荐 stable 基线的那个):
| 路径 | 版本 | sha256 | 大小 |
|---|---|---|---|
~/.cache/sing-box-bin/sing-box-v1.14.2-reF1nd-with_ebpf |
v1.14.2-reF1nd(与你现在同基线) |
87583b8695369f6a340fb1a4fe27dc652b59e02ff59d3a50d658fc2e632164f3 |
86.8 MB |
~/.cache/sing-box-bin/sing-box-v1.15.0-alpha.9-reF1nd-with_ebpf |
v1.15.0-alpha.9-reF1nd |
8a55dfe9125164de26afb7a889bcf6458922fbfd76d34046f51a9695d2dee1e4 |
88.5 MB |
实测结果(都在你本机跑的):
| 实测项 | v1.14.2-reF1nd |
v1.15.0-alpha.9-reF1nd |
|---|---|---|
version |
go1.26.8 linux/amd64 ✅ |
go1.26.8 linux/amd64 ✅ |
编译标签含 with_ebpf |
✅ | ✅ |
check -C /etc/sing-box(你现在的 TUN 配置) |
exit=0,零告警 ✅ | exit=0,唯一告警:tun.stack 在 1.15 弃用 |
check -c <tun 换成 ebpf 入站的配置> |
非 root 下 exit=1,原因是 map create: operation not permitted —— 权限门,不是配置错误(官方样例配置在该权限下同样失败) |
同上 |
tools ebpf status 内核预检 |
命令可用,能逐项报能力 | 同左 |
注意一个反直觉点:这个分支的
sing-box check遇到 ebpf 入站会真的去建 eBPF map,
所以”非 root 时check失败”≠ 配置写错。字段名我已对齐option/ebpf.go,无 unknown field。
4.1 用你自己的 ~/pkgbuild-source 打包(已完成)
✅ 已落地:
~/pkgbuild-source/packages/sing-box-ebpf/(PKGBUILD + .SRCINFO)。
容器内真实makepkg构建通过(产物 25.5 MB,含二进制 + 两个 systemd 单元 + license),
namcap 仅两条 Go 静态二进制常态告警(lacks FULL RELRO/lacks PIE,退出码 0,不会中断 CI)。
配套的管理界面见docs/03-面板指南.md(源码在~/pkgbuild-source/packages/sing-box-panel/)。
你那个仓库(CI 从 packages/<name>/{PKGBUILD,.SRCINFO} 构建 → 发到 repo 分支 → pacman 直接装)
正好解决”没有预编译二进制”这件事:一次打包,以后 pacman 升级,不用手工编。packages/daed-emo/ 就是现成模板(同样是 Go 构建 + 自带 systemd 单元)。
建议新增 packages/sing-box-ebpf/,要点:
source=('git+https://github.com/reF1nd/sing-box.git#tag=v1.14.2-reF1nd') # stable 基线;要 1.15 特性再换 tag
makedepends=('base-devel' 'git' 'go')
provides=('sing-box') # 装的是 /usr/bin/sing-box,沿用发行版单元
conflicts=('sing-box' 'sing-box-git')
# 构建标签务必含 with_ebpf(否则就是普通的 sing-box,白装):
# with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,
# with_tailscale,with_ccm,with_ocm,with_cloudflared,with_usbip,with_openvpn,
# with_openconnect,badlinkname,tfogo_checklinkname0,with_ebpf
with_naive_outbound不要带(Linux +CGO_ENABLED=0编不过,见第 8 节)。conflicts=('sing-box')是刻意的:两个透明代理不能共存,装这个就等于顶掉官方包。- 打包时顺手把发行版单元的 capability 限制补上
CAP_BPF CAP_PERFMON(见 5.2),否则服务起不来。
(2026-10-02 晚实测:自建包最终用的是User=root+ 不设CapabilityBoundingSet+LimitMEMLOCK=infinity,
这样最省事,不需要逐个补 CAP。)
我没有直接改你的仓库——你说一声我就按仓库约定加上
PKGBUILD+.SRCINFO(并用makepkg --printsrcinfo自检)。
5. 真要切到 eBPF,需要改什么
5.1 配置:tun-in → ebpf-in(mixed-in 保留)
{
"type": "ebpf",
"tag": "ebpf-in",
"network": ["tcp", "udp"],
"local": {
"enabled": true,
"data_plane": "cgroup",
"dns_mode": "respect_policy",
"bypass_private_address": true,
"ipv6": true,
"exclude_uid": [ "<可选:不需要走代理的 UID>" ]
},
"shared": { "enabled": false }
}
端口 53:由
dns_mode在内核侧接管(hijack先接管再筛 UID,respect_policy先筛再接管),
替代 TUN 时代的hijack-dns路由动作;原规则留着无害,可删。IPv6:由
local.ipv6显式开关,不再需要为了 v6 去给 TUN 配地址,也不会再出现ip -6 rule里那条unreachable黑洞(那是 TUN 模式的防泄露设计)。分应用(Linux 上 TUN 做不到的事):
include_uid/exclude_uid/*_uid_range;include_package/exclude_package是 Android 专用,桌面别用。局域网 / 虚拟机共享:
shared填的是下游接口,不是上游!
你这台机器上默认出口是wlan0,所以不要填 wlan0;要让 libvirt 的虚拟机也走代理,应填virbr0:"shared": { "enabled": true, "data_plane": "packet_rewrite", "interface": ["virbr0"] }且 shared 模式自己不做 NAT / DHCP / IPv6 RA(那些仍由 libvirt dnsmasq 负责),
只支持以太网帧接口(raw-IP / PPP / 隧道链路要换socket_assign)。一个实例只能有一个 ebpf 入站做 local 接管;绝对不要 TUN 与 eBPF 同时开(等于两个透明代理)。
5.2 systemd:当时的发行版单元跑不了 eBPF
✅ 2026-10-02 晚复核:这条建议已经落地了。 现在用的是自建包自带的单元
(/usr/lib/systemd/system/sing-box.service,来自sing-box-ebpf),实测就是User=root+ 完全不限制CapabilityBoundingSet+LimitMEMLOCK=infinity;systemctl show sing-box显示ActiveState=active、NRestarts=0,不需要逐个补CAP_BPF/CAP_PERFMON。
下面这段是旧单元的体检记录(保留原文)。
发行版单元是 User=sing-box + CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
——没有 CAP_BPF/CAP_PERFMON,而且 local cgroup 数据面要挂到 cgroup v2 根层级(特权操作)。
建议照 daed 的老路:用 root 跑(User=root 或干脆去掉 User=),capability 限制放宽或至少补CAP_BPF CAP_PERFMON CAP_NET_ADMIN CAP_SYS_ADMIN;ExecStart 的 -D /var/lib/sing-box -C /etc/sing-box 不用动。
若坚持非 root,cgroup_path 指到自己被 systemd 委派的子树,但那样只能接管该子树内的进程,不是全局透明。
兜底(root 挂根 cgroup 被拒时):把 local.data_plane 从 cgroup 换成 tc
(跟随默认接口,预检里 veth / 策略路由 / socket lookup 能力都已 PASS),代价是依赖默认接口、需要建 veth。
❌ 2026-10-02 晚推翻:这条兜底不成立。 本机
local的tc数据面实测起不来:FATAL start service: start inbound/ebpf[ebpf-in]: register TC eBPF TCP listener: operation not supported。
教训:预检(不挂 hook 的加载探测)PASS 不等于能挂上并跑通;cgroup挂载若被拒,没有tc这条退路。
5.3 预检(已跑过:preflight_passed,51 PASS / 0 fail)
sudo ~/.cache/sing-box-bin/sing-box-v1.14.2-reF1nd-with_ebpf \
tools ebpf status --mode all --interface virbr0 --json
结果见 3.1。FAIL 才是真缺能力;UNKNOWN 表示探测被安全策略挡住,需要换权限重跑。
注意删掉 --interface virbr0 就没探测虚拟机共享路径,反之不想要共享路径可只测 --mode local。
5.4 切换顺序与回滚(已于 2026-10-02 10:37 执行成功,脚本:switch-to-ebpf.sh)
✅ 现成脚本(仓库
scripts/目录):sudo scripts/switch-to-ebpf.sh(可选--shared virbr0、--shared-plane packet_rewrite|socket_assign)。
它做完整安全管线:内核预检 → tun→ebpf 改配置 →check→ 备份 → 原子替换 → 重启 →
eBPF 专属健康检查(api ebpf附件状态 + 不经代理的请求是否走代理 + 国内是否仍直连 + DNS 是否仍拦)
→ 任一失败自动回滚到 TUN。下面是不用脚本时的等价手工步骤。
sudo cp -a /etc/sing-box/config.json /etc/sing-box/config.json.pre-ebpf.$(date +%Y%m%d-%H%M%S) # 1 备份
sudo scripts/switch-to-local-rules.sh --dry-run # 2 仍用旧的 TUN 脚本预览
sudo systemctl stop sing-box # 3 停服务
# 4 用新二进制 + ebpf 配置在前台手跑,盯日志;出问题 Ctrl-C 即回到无代理状态
sudo ~/.cache/sing-box-bin/sing-box-v1.15.0-alpha.9-reF1nd-with_ebpf -D /var/lib/sing-box -C /etc/sing-box run
# 5 没问题再换 systemd 单元(第 5.2 节)并 systemctl start sing-box
验证四条(与现有审计报告同一套判据):
curl -s https://myip.ipip.net # 期望:你的电信 IP(直连)
curl -s https://api.ipify.org # 期望:代理节点 IP
curl -6 -s -o /dev/null -w '%{http_code}\n' https://ipv6.google.com # 期望 200
ip -6 rule show | grep -c unreachable # 期望 0
回滚:systemctl stop sing-box → 把 config.json.pre-ebpf.* 覆盖回 /etc/sing-box/config.json →
换回 /usr/bin/sing-box(1.14.2 + TUN)与发行版单元 → systemctl start sing-box。
6. 代价 / 什么时候值得
| 说明 | |
|---|---|
| ⚠️ 版本 | 分支基线的 tag 可选 stable(v1.14.2-reF1nd,与你现装同基线)或 alpha(v1.15.0-alpha.9-reF1nd);eBPF 本身不是 alpha 专属 |
| ⚠️ 维护 | 无 Linux release 产物、无 Arch 包 → 每次升级都要自己 podman/Go 重编;建议用 ~/pkgbuild-source 打包解决(见 4.1) |
| ⚠️ 覆盖面 | local cgroup 只管本机 socket;转发流量(libvirt VM)不在内,那要 shared + virbr0(已端到端实测 ✅)。❌ 原文”含 rootless podman”是错的:2026-10-02 晚实测 rootless podman(pasta)容器仍走不了代理——境外失败、国内直连与 DNS 劫持正常、--network=host 可用;pasta 属用户态转发,local 与 shared 两条路都不覆盖它。而 TUN 的 auto_route 是一把全包住 |
| ⚠️ 首次启动 | root 挂根 cgroup 的权限/独占性只在真启动时校验(3.1)——实际启动通过了(api ebpf 可见 role=local mechanism=cgroup,服务 NRestarts=0);tc 数据面tc 不可用(见 5.2 追注),没有这条退路 |
| ✅ 收益 1 | 分应用分流(include_uid/exclude_uid)——这是 TUN 在 Linux 上给不了的,且预检确认 bpf_get_current_uid_gid 可用 |
| ✅ 收益 2 | 不建 TUN 设备、无 TUN 拷贝,理论上更省;不用 iptables/nftables REDIRECT/TPROXY |
| ✅ 收益 3 | type: "group" 故障转移组用上type: "group" 在 1.14.2-reF1nd 上就能用——线上配置的 dns-proxy / dns-direct 正是 type: "group",不需要 1.15 |
| ✅ 现状对比 | 你这套 TUN 已经过完整审计:DNS 劫持完整、v6 已修、podman 透明、并发/大文件都过。没有刚需就别换;当年 daed 的 eBPF 正是把 DNS 和 IPv6 TCP 弄坏才被换掉的 |
建议(预检通过后的版本):
能力门已经过了(51 PASS / 0 fail,连 cgroup 与 packet_rewrite 对象都真加载过),所以剩下的是行为风险,不是”能不能跑”。
顺序:先打包(4.1)→ 再备份/前台试跑/回滚(5.4);只在你要”分应用”或”去掉 TUN”时才真正切。
只是想尝鲜的话,成本最低的试法是:不开机自启,前台跑一次、跑完立刻 Ctrl-C,然后用审计报告里那套判据(DNS 四源一致、
国内直连 IP、curl -6200、ip -6 rule无 unreachable、podman 出口 IP)打一遍分。✅ 2026-10-02 晚复核:切换已经做了(见 5.4),当日晚又补完了 shared 的端到端验证;服务至今
ActiveState=active、NRestarts=0。唯一没被”审计判据”覆盖的是 rootless podman(pasta):
它在 eBPF 下仍走不了代理(见上表”覆盖面”行)。
7. Android(NetProxy 模块)怎么判断能不能用
- 模块只提供 eBPF 透明代理入站,没有 TPROXY/REDIRECT 回退:内核能力不足 → 透明代理根本起不来。
- 厂商内核可能关掉/回移部分 BPF 能力,不能只看 Android 或内核版本号,要用模块自带探测:
su -c '/data/adb/modules/netproxy/netproxyctl ebpf status configured'
su -c '/data/adb/modules/netproxy/netproxyctl ebpf status all --raw'
- 默认数据面:本机
cgroup(EBPF_LOCAL_DATA_PLANE=cgroup),共享网络packet_rewrite(EBPF_SHARED_ENABLED=0默认关)。 - 需要:Magisk / KernelSU / APatch + Root;内核支持 BPF、TC classifier、透明 socket、socket lookup;本机路径还要 veth 与策略路由(未复核:本机实测
tc数据面在 7.2.8 上不可用、而cgroup路径不依赖 veth,这句是照模块文档转述的,没有实机验证)。 - 它的”sing-box 配置”与你的
/etc/sing-box/config.json不是一份东西:它用runtime/现生成的 Provider + eBPF 配置,
静态主配置在config/singbox/config.json。你的 TUN 配置思路(规则集、DoH、selector)可以照搬,但tun入站整段要去掉。
8. 复现 / 清理
# 重编(缓存已在 podman 卷里,所以很快;需要 podman,不需要装 Go)
TAGS="with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,with_cloudflared,with_usbip,with_openvpn,with_openconnect,badlinkname,tfogo_checklinkname0,with_ebpf"
# stable 基线(推荐;/tmp/sb-fork-1142 已存在,可直接复用)
git clone --depth 1 --branch v1.14.2-reF1nd https://github.com/reF1nd/sing-box.git /tmp/sb-fork-1142
# 或者最新分支:--branch reF1nd-testing
cd /tmp/sb-fork-1142 && podman run --rm -v "$PWD":/src -v gomodcache:/go/pkg/mod \
-v gomodcache-build:/root/.cache/go-build -w /src \
-e CGO_ENABLED=0 -e GOOS=linux -e GOARCH=amd64 -e GOTOOLCHAIN=local \
docker.io/library/golang:1.26 sh -c "go build -trimpath -ldflags '-s -w' -tags '$TAGS' -o /src/sing-box-ebpf ./cmd/sing-box"
# 清理我留下的构建缓存(约 2 GB,放着你下次重编会快很多)
podman volume rm gomodcache gomodcache-build
podman rmi docker.io/library/golang:1.26
坑:
with_naive_outbound在 Linux 上CGO_ENABLED=0编不过
(cronet-go/lib/linux_amd64: build constraints exclude all Go files),所以标签里没带它——
你的订阅是 Shadowsocks,用不到 naive。
9. 踩坑记录:cache.db 属主(试跑时踩到,已修)
📌 2026-10-02 晚追注(本节保留原文,属历史记录):现行单元是
User=root(自建sing-box-ebpf包),/var/lib/sing-box/cache.db现为root:root 644,所以”被改成 emo 属主导致服务写不了”这个具体故障
在当前单元下不会复现;但下面两条铁律(别拿线上配置跑非 rootcheck、active≠ 就绪)仍然成立。
⚠️ 另:本次没能复现”check初始化 cache-file”这一步——把cache_file.path指到可写临时目录后sing-box checkexit=0 且没有生成文件;带ebpf入站时则更早地在map create: operation not permitted
失败。所以”check走到服务初始化”是真的(会去建 eBPF map),但”它顺手重建了cache.db“这一环存疑。
症状:systemctl restart sing-box 后服务 active 一秒即挂,无限重启循环,tun0 消失:
FATAL[0000] start service: initialize cache-file: open /var/lib/sing-box/cache.db: permission denied
根因:/etc/sing-box/config.json 里 experimental.cache_file.path 是绝对路径/var/lib/sing-box/cache.db,而非 root 身份跑 sing-box check -c/-C <线上配置> 也会真的初始化
cache-file(这个 fork 的 check 会走到服务初始化)。于是该文件被重建为 emo:emo 644,
而服务用户是 sing-box(目录 755,只有属主可写)→ 服务再也写不了自己的缓存。
当时老进程还握着旧 inode,所以表面无恙,直到重启才炸。
修复(一条命令,缓存可丢弃):
sudo chown sing-box:sing-box /var/lib/sing-box/cache.db && sudo systemctl restart sing-box
# 或者干脆:sudo rm -f /var/lib/sing-box/cache.db && sudo systemctl restart sing-box
两条铁律:
- 不要用非 root 对线上配置跑
sing-box check/run。要验证就先复制一份,并把experimental.cache_file.path改到/tmp(试跑脚本 v2 已内置这个隔离)。 systemctl报active≠ TUN 已就绪:实测有 1 秒级竞态,is-active为真时tun0
可能还没建出来。判断健康要is-active+ip link show tun0一起看(脚本 v2 已改成轮询 10s)。
(eBPF 模式下没有tun0,判据要换成api ebpf的附件状态 + “不设代理的出口 == 经代理的出口”,见 09 文档 §9。)










