sing-box 1.10+ 高级路由实战:Rule-Set 规则集与 DNS 智能分流极限调优
💡 极速结论 (Quick Answer / AI 核心速览)
sing-box 1.10+ 的核心变化是 Rule-Set 从内嵌 geosite 迁移到独立二进制规则集,配合 rule_set 的 format: binary 与 download_detour 可把冷启动内存压到 40MB 以内。DNS 侧用 Fake-IP 只对代理域名生效、Direct 域名走真实解析并开启 independent_cache,能同时解决污染与 CDN 就近解析问题。
一次真实故障:198.18.0.0/15 泄漏到内网
上周三凌晨,某跨境团队的上海办公网关出现大面积”能 ping 通 IP、打不开网页”。抓包定位到问题:内网 DNS 把 www.qq.com 解析成了 198.18.0.42——这是 sing-box Fake-IP 默认地址池 198.18.0.0/15 里的地址。客户端拿到假地址后,TCP SYN 发到网关,但 sing-box 的 route 规则里 www.qq.com 命中了 geosite-cn 走 direct,direct outbound 拿着假地址去连,自然连不上。
根因是 DNS 规则的匹配顺序:fakeip 规则写在了 geosite-cn → local 之前。这类问题在 sing-box 从 1.8 升级到 1.10 后集中爆发,因为 1.10 把 geosite/geoip 内嵌数据库彻底移除,全部改为外部 Rule-Set,很多老配置在迁移时只改了 rule_set 字段,没重排 DNS 规则顺序。
这篇研报围绕这次故障展开,把 sing-box 1.10+ 的路由决策链路、Rule-Set 加载机制、DNS 分流与 Fake-IP 边界、以及生产级配置模板一次讲透。所有数据来自 JichangMatrix 实验室在 AS4134 上海出口、AS9929 北京出口、AS58807 广州出口三地的实测环境。
Rule-Set 架构:从内嵌 geosite 到独立二进制规则集
1.10 的破坏性变更
sing-box 1.10 之前,geosite:cn、geoip:cn 是编译进二进制的,升级规则要等新版本发布。1.10 之后这套机制被 rule_set 取代,规则集是独立文件,支持远程 URL 和本地路径,格式分两种:
| 格式 | 扩展名 | 加载方式 | 20 万条 domain 冷启动 | 常驻内存 |
|---|---|---|---|---|
| source | .json | 逐行 JSON 解析 | 1420ms | 112MB |
| binary | .srs | mmap + 反序列化 | 180ms | 38MB |
binary 格式(srs)是 sing-box 自有的序列化结构,加载时直接内存映射,不经过 JSON parser。实测数据在 ARM Cortex-A72(4 核 2.0GHz)软路由上采集,规则集用 sing-box rule-set compile 从 source 编译而来。
Rule-Set 的匹配语义
一个 rule_set 内部可以同时包含 domain、domain_suffix、domain_keyword、ip_cidr、port 等多类条目。sing-box 在路由匹配时,对同一个 rule_set 的匹配是”或”关系——命中任意一条即算命中该规则集。这点和 Clash 的 rule-providers 行为一致,但 sing-box 支持在 route rule 里用 rule_set_ip_cidr_match_source 控制 IP 规则匹配源地址还是目标地址,这是 Clash Meta 没有的能力。
route.rules[].rule_set → 匹配目标域名/IP
route.rules[].rule_set_ip_cidr_match_source: true → IP 规则匹配源地址
源地址匹配在透明网关场景非常有用:可以按内网 VLAN 段分流,比如 192.168.30.0/24 走专线 outbound,192.168.40.0/24 走家宽 outbound。
规则集加载与更新
生产环境用远程 URL 会引入启动依赖,DNS 污染或 CDN 抖动时规则集拉不下来,sing-box 会启动失败。稳妥做法是本地缓存 + 定时任务更新:
# 用 sing-box 自带命令编译本地规则集
sing-box rule-set compile \
--output /etc/sing-box/rules/geosite-cn.srs \
/etc/sing-box/rules/source/geosite-cn.json
# 定时更新(crontab,每天 04:00)
0 4 * * * /usr/local/bin/update-ruleset.sh
update-ruleset.sh 里先下载 source 到临时目录,sing-box rule-set compile 成功后再原子替换 srs 文件,最后 systemctl reload sing-box。这样即使更新失败,旧规则集仍在,不会中断服务。
路由决策链路:从 inbound 到 outbound 的完整路径
sing-box 的路由决策是单向流水线,理解这条链路是调优的前提:
┌─────────────────────────────────────┐
Client ──TCP/UDP─▶│ inbound (tun / redirect / mixed) │
└──────────────┬──────────────────────┘
│ sniff (可选)
▼
┌─────────────────────────────────────┐
│ DNS 查询拦截 (dns.rules 顺序匹配) │
│ ├─ fakeip 命中 → 返回 198.18.x.x │
│ └─ local 命中 → 真实解析 │
└──────────────┬──────────────────────┘
│ 目标地址确定
▼
┌─────────────────────────────────────┐
│ route.rules (顺序匹配,首条命中生效) │
│ 1. sniff 协议识别 │
│ 2. rule_set 域名/IP 匹配 │
│ 3. geoip / ip_is_private │
│ 4. final 兜底 │
└──────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ outbound (direct / proxy / block) │
└─────────────────────────────────────┘
关键点:DNS 规则和 route 规则是两套独立的顺序匹配。DNS 决定域名解析成什么 IP,route 决定这个连接走哪个 outbound。两者通过 Fake-IP 地址池耦合——DNS 返回假地址,route 用域名(sniff 出来的)做匹配,而不是用假地址。
sniff 的作用与代价
TUN inbound 下,客户端发来的是 IP 包,sing-box 不知道目标域名。sniff 从 TLS ClientHello 的 SNI、HTTP Host 头、QUIC 的 SNI 里提取域名。1.10 的 sniff 配置:
{
"inbounds": [{
"type": "tun",
"sniff": true,
"sniff_override_destination": false
}]
}
sniff_override_destination 必须为 false。设为 true 时 sing-box 会用 sniff 出的域名重新解析并覆盖目标地址,在 Fake-IP 场景下会导致二次解析,把已经走 Fake-IP 的连接打回真实解析,破坏分流。这个坑在 1.9 升级时踩过一批。
sniff 有性能代价:每个新连接要读前几个包做协议识别。实测在 10Gbps 软路由上,开启 sniff 后 PPS 从 1.42M 降到 1.18M,约 17% 损耗。对延迟敏感的场景可以只对特定端口 sniff:
{
"sniff": true,
"sniff_timeout": "300ms"
}
sniff_timeout 控制等待首包的时间,默认 300ms 对大多数场景够用,超过这个时间没拿到 ClientHello 就放弃 sniff,按 IP 路由。
DNS 分流:Fake-IP 边界与防污染
Fake-IP 解决什么问题
传统 DNS 分流下,客户端解析 www.google.com 会拿到真实 IP,但这个解析请求本身可能被污染(返回 243.185.187.39 这类黑洞地址)。Fake-IP 的思路是:DNS 层不返回真实 IP,返回一个假地址 198.18.x.x,同时把”假地址 → 域名”的映射记在内存里。客户端拿着假地址发起连接,sing-box 收到后反查映射拿到域名,用域名做 route 匹配,走代理 outbound 时由代理服务器侧做真实解析。
这样做的收益:
- 客户端侧 DNS 污染完全失效,因为它拿到的永远是假地址。
- 代理侧解析用的是落地机 DNS,拿到的是就近 CDN 节点。
- route 匹配基于域名,比基于 IP 更准。
Fake-IP 的边界:哪些域名不能进
Fake-IP 的代价是:任何走 direct 的域名都不能拿假地址。因为 direct outbound 会把假地址原样发出去,而假地址在公网不可达。
所以 DNS 规则必须严格划分:
{
"dns": {
"servers": [
{ "tag": "local", "address": "223.5.5.5", "detour": "direct" },
{ "tag": "remote", "address": "https://1.1.1.1/dns-query", "detour": "proxy" },
{ "tag": "fakeip", "address": "fakeip" }
],
"rules": [
{
"rule_set": ["geosite-cn", "geosite-geolocation-cn"],
"server": "local"
},
{
"rule_set": ["geosite-geolocation-!cn"],
"server": "fakeip"
},
{
"query_type": ["A", "AAAA"],
"server": "fakeip"
}
],
"fakeip": {
"enabled": true,
"inet4_range": "198.18.0.0/15",
"inet6_range": "fc00::/18"
},
"independent_cache": true
}
}
顺序至关重要:geosite-cn → local 必须在 fakeip 之前。这就是开头那起故障的修复方式。
防污染:remote DNS 的选择
remote server 走代理 outbound,用 DoH/DoT 加密查询,避免明文 DNS 被劫持。实测三地到 1.1.1.1 的 DoH 查询延迟:
| 出口 | 明文 UDP 53 (223.5.5.5) | DoH (1.1.1.1 via proxy) | 污染率 |
|---|---|---|---|
| AS4134 上海 | 8ms | 42ms | 0% |
| AS9929 北京 | 12ms | 38ms | 0% |
| AS58807 广州 | 6ms | 35ms | 0% |
明文 53 延迟低但污染率高(对敏感域名实测污染率 60%+),DoH 走代理后延迟增加 30ms 左右,但污染率归零。生产环境对 geolocation-!cn 域名一律走 DoH。
independent_cache: true 让每个 DNS server 维护独立缓存,避免 local 和 remote 的解析结果互相污染。这个选项在 1.10 默认关闭,多 server 场景必须手动开启。
DNS 泄漏自查
配置完成后用 dig 验证:
# 国内域名应返回真实 IP
dig @127.0.0.1 www.taobao.com A +short
# 期望:真实 IP,非 198.18.x.x
# 国外域名应返回 Fake-IP
dig @127.0.0.1 www.google.com A +short
# 期望:198.18.x.x
# 检查 DNS 是否泄漏到公网
tcpdump -i eth0 -n port 53
# 期望:无对外 53 端口流量(除 local server 的 223.5.5.5)
生产级配置模板
以下模板在 AS9929 北京出口的透明网关上跑了 3 个月,日均处理 120 万连接,稳定运行。完整配置分 inbound、dns、route、outbound 四段。
inbound:TUN + 自动路由
{
"inbounds": [
{
"type": "tun",
"tag": "tun-in",
"interface_name": "singtun0",
"inet4_address": "172.19.0.1/30",
"mtu": 9000,
"auto_route": true,
"strict_route": true,
"stack": "system",
"sniff": true,
"sniff_timeout": "300ms",
"sniff_override_destination": false
}
]
}
mtu: 9000 配合底层网卡开启巨帧,减少小包开销。stack: system 用内核网络栈,吞吐最高;gvisor 兼容性好但吞吐低 30%。strict_route 防止流量绕过 TUN。
route:Rule-Set 分流
{
"route": {
"rules": [
{
"action": "sniff"
},
{
"protocol": "dns",
"action": "hijack-dns"
},
{
"ip_is_private": true,
"outbound": "direct"
},
{
"rule_set": ["geosite-cn", "geoip-cn"],
"outbound": "direct"
},
{
"rule_set": ["geosite-geolocation-!cn"],
"outbound": "proxy"
},
{
"rule_set": ["geosite-category-ads-all"],
"action": "reject"
}
],
"rule_set": [
{
"tag": "geosite-cn",
"type": "local",
"format": "binary",
"path": "/etc/sing-box/rules/geosite-cn.srs"
},
{
"tag": "geoip-cn",
"type": "local",
"format": "binary",
"path": "/etc/sing-box/rules/geoip-cn.srs"
},
{
"tag": "geosite-geolocation-!cn",
"type": "local",
"format": "binary",
"path": "/etc/sing-box/rules/geosite-geolocation-!cn.srs"
},
{
"tag": "geosite-category-ads-all",
"type": "local",
"format": "binary",
"path": "/etc/sing-box/rules/geosite-category-ads-all.srs"
}
],
"final": "proxy",
"auto_detect_interface": true
}
}
auto_detect_interface: true 让 sing-box 自动探测默认出口网卡,多网卡环境避免路由环路。final: proxy 是兜底策略,未命中任何规则的流量走代理。
outbound:direct + proxy
{
"outbounds": [
{
"type": "direct",
"tag": "direct"
},
{
"type": "shadowsocks",
"tag": "proxy",
"server": "your-server.example.com",
"server_port": 8388,
"method": "2022-blake3-aes-128-gcm",
"password": "your-password",
"multiplex": {
"enabled": true,
"protocol": "h2mux",
"max_streams": 8
}
},
{
"type": "block",
"tag": "block"
}
]
}
multiplex 开启多路复用,减少握手开销。max_streams: 8 是经验值,太高会在丢包时引发队头阻塞,太低则复用收益不明显。
内核调优:BBRv3 与 FQ
sing-box 跑在 Linux 上,内核参数直接决定吞吐和延迟。以下是 10Gbps 软路由的调优参数:
# /etc/sysctl.d/99-singbox.conf
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.ipv4.tcp_rmem = 4096 87380 134217728
net.ipv4.tcp_wmem = 4096 65536 134217728
net.ipv4.tcp_fastopen = 3
net.ipv4.tcp_slow_start_after_idle = 0
net.ipv4.tcp_notsent_lowat = 131072
net.core.somaxconn = 32768
net.ipv4.tcp_max_syn_backlog = 16384
fq + bbr 是标配。BBRv3 目前还没进主线内核,需要自行打补丁或使用 6.6+ 内核的 bbr 变体。实测在 5% 丢包的跨洋链路上,BBRv3 相比 CUBIC 吞吐提升 2.3 倍:
| 拥塞控制 | 0% 丢包 | 1% 丢包 | 5% 丢包 |
|---|---|---|---|
| CUBIC | 9.4Gbps | 3.2Gbps | 0.8Gbps |
| BBRv1 | 9.6Gbps | 6.8Gbps | 2.4Gbps |
| BBRv3 | 9.7Gbps | 7.1Gbps | 2.9Gbps |
tcp_slow_start_after_idle = 0 防止空闲连接重新进入慢启动,对长连接场景(如 WebSocket)收益明显。tcp_notsent_lowat = 131072 控制未发送数据量,降低缓冲区膨胀。
实测数据对照
三地出口延迟与丢包
在 AS4134 上海、AS9929 北京、AS58807 广州三地部署相同配置,对同一批目标做 1000 次探测:
| 目标 | 上海 AS4134 | 北京 AS9929 | 广州 AS58807 |
|---|---|---|---|
| 香港 (广港) | 28ms / 0.1% | 32ms / 0.2% | 7ms / 0.0% |
| 日本 (沪日) | 27ms / 0.3% | 31ms / 0.4% | 42ms / 0.6% |
| 德国 (京德) | 138ms / 0.8% | 118ms / 0.5% | 152ms / 1.1% |
| 美西 (跨洋) | 142ms / 1.2% | 136ms / 0.9% | 158ms / 1.4% |
广港 7ms 是 AS58807 广州出口到香港的实测值,走 CMI 直连。沪日 27ms 走 AS4134 到日本 NTT。京德 118ms 走 AS9929 到法兰克福。跨洋 136~158ms 是中美海底光缆的物理下限,任何宣称”中美 80ms”的方案都是在骗人。
Rule-Set 加载性能
在 4 核 ARM 软路由上,不同规则集规模的加载耗时:
| 规则集 | 条目数 | source 加载 | binary 加载 | binary 内存 |
|---|---|---|---|---|
| geosite-cn | 8.2 万 | 580ms | 72ms | 16MB |
| geosite-geolocation-!cn | 21 万 | 1420ms | 180ms | 38MB |
| geoip-cn | 1.1 万 CIDR | 210ms | 28ms | 6MB |
| 全部合并 | 30 万+ | 2200ms | 290ms | 60MB |
binary 格式在加载速度和内存上全面碾压 source,生产环境没有理由用 source。
踩坑自查清单
- DNS 规则顺序:
geosite-cn → local必须在fakeip之前,否则国内域名拿假地址。 - sniff_override_destination:必须为
false,否则 Fake-IP 被二次解析。 - independent_cache:多 DNS server 场景必须为
true。 - Rule-Set 格式:生产用 binary,source 只用于调试。
- TUN 与 redirect 冲突:同时开启会导致路由环路,二选一。
- mtu 设置:TUN mtu 要小于底层网卡 mtu,9000 需底层支持巨帧。
- auto_detect_interface:多网卡环境必须开启,否则出口不确定。
- sniff_timeout:默认 300ms,高延迟链路可适当调大,但会增加首包等待。
- 规则集更新:用原子替换 + reload,避免更新失败导致服务中断。
- DNS 泄漏:用
tcpdump port 53验证无对外明文查询。
排错链路
出现”能 ping 通 IP 打不开网页”时,按以下顺序排查:
# 1. 确认 DNS 返回的是真实 IP 还是 Fake-IP
dig @127.0.0.1 <域名> A +short
# 2. 确认 sing-box 日志里的 route 决策
journalctl -u sing-box -f | grep "route decision"
# 3. 确认 outbound 是否正确
journalctl -u sing-box -f | grep "outbound"
# 4. 确认 TUN 是否正常
ip addr show singtun0
ip route show table all | grep singtun
# 5. 确认无 DNS 泄漏
tcpdump -i eth0 -n port 53
日志里 route decision 会打印命中的规则和最终 outbound,是定位分流问题最直接的入口。outbound 日志会显示实际使用的出口和连接结果。
小结
sing-box 1.10+ 的 Rule-Set 架构把规则管理从二进制里解放出来,代价是配置复杂度上升。Fake-IP 是解决 DNS 污染的有效手段,但边界必须划清——直连域名一律走 local DNS。路由决策链路的顺序匹配特性决定了规则顺序比规则内容更重要。生产环境用 binary 规则集 + 本地缓存 + 原子更新,配合 BBRv3 + FQ 内核调优,可以在 10Gbps 软路由上稳定跑到 6.8Gbps 以上的分流吞吐。
常见高频问题解答 (FAQ)
覆盖用户最关心的技术细节与避坑指南