sing-box bypass 路由问题怎么排查?先分清 direct 与内核绕过
简答:先判断你要的是普通直连,还是 Linux TUN 场景的内核绕过:多数 Windows、macOS、Android VPNService 用户应使用 action: route + outbound: direct;只有 Linux 且启用 auto_route、auto_redirect 时,才继续排查未指定 outbound 的 action: bypass。若出现断网、回环或超时,先恢复 direct,再检查 debug 日志、nftables、nfqueue、ip rule 和实际路由表。
定义:sing-box 的 bypass 是一种路由规则动作:在满足 Linux 与 auto_redirect 条件的预匹配场景中,让连接在内核层绕过 sing-box,而不是适用于所有平台的直连开关。
30 秒速查:先判断能不能用 bypass
先按下面四种情况定位,不要一开始就修改固定 mark 或清空系统路由。
| 你的环境或现象 | 先用什么 | 下一步 |
|---|---|---|
| Windows、macOS 或普通 Android VPNService | action: route + outbound: direct | 检查规则顺序、出站标签、DNS 解析和已建立连接。 |
| Linux,但版本低于 1.13.0 | route + direct | 先升级或暂时不要使用未指定 outbound 的 bypass。 |
Linux 1.13.0 及以上,且有 auto_route、auto_redirect | 可以测试未指定 outbound 的 action: bypass | 检查 nfqueue、nftables、预匹配和路由表。 |
| IPv4 正常、IPv6 单独超时 | 先查 ip -6 rule 和 table 2022 | 确认标记实际查到的 IPv6 默认路由没有回到 TUN。 |
官方规则动作文档说明,bypass 自 sing-box 1.13.0 起提供,仅支持 Linux,并且需要启用 auto_redirect;未指定 outbound 时,只会在 auto_redirect 预匹配中生效,其他场景会跳过。参见sing-box 规则动作文档。
最安全的第一步:把原来的 bypass 临时改成普通 route,并指定已有的 direct 出站。这样可以先区分“基础连接或 DNS 有问题”和“内核绕过条件不满足”。
{
"route": {
"rules": [
{
"domain_suffix": [
"intranet.example"
],
"action": "route",
"outbound": "direct"
}
],
"final": "proxy",
"auto_detect_interface": true
}
}intranet.example、direct 和 proxy 都是占位值,必须替换为你配置中实际存在的域名和出站标签。这里的 final: proxy 只表示未匹配流量继续交给原来的代理出站,不是完整配置。修改前请备份;如果配置来自订阅,直接改原文件可能在更新时被覆盖,应使用客户端提供的合并或覆盖方式。
action:bypass、direct 与 route_exclude_address 怎么区分
| 写法 | 作用 | 适用边界 | 常见误区 |
|---|---|---|---|
action: route + outbound: direct | 把匹配连接交给 direct 出站处理。 | 适合多数 TUN、桌面系统和 Android VPNService 场景。 | 把 direct 误认为内核层绕过,或忘记检查 direct 标签是否存在。 |
action: bypass,不指定 outbound | 在 Linux auto_redirect 预匹配中,让连接在内核层绕过 sing-box。 | 需要 Linux、sing-box 1.13.0 及以上、auto_route 和 auto_redirect。 | 在 Windows、macOS 或普通 Android VPNService 中照抄。 |
action: bypass,指定 outbound | 在非 auto_redirect 或已建立连接中,行为与指定 outbound 的 route 相同。 | 不要把它理解为所有平台的内核绕过。 | 以为写了 bypass 就一定绕过 TUN。 |
route_exclude_address | 从 TUN 自动路由中排除指定地址或 CIDR。 | 配合 auto_route 使用;适合少量明确网段。 | 把它当作按域名选择 direct 出站。 |
route_exclude_address_set | 按规则集中的 IP CIDR 排除 TUN 路由。 | 官方要求 Linux、auto_route 和 auto_redirect;Android 图形客户端不适合大量此类路由。 | 忽略规则集加载、内核路由和 auto_redirect 条件。 |
官方 TUN 文档还说明,auto_redirect 仅支持 Linux 且要求先启用 auto_route;预匹配需要内核支持 nfnetlink_queue,OpenWrt 下会自动向 fw4 插入兼容规则。该文档也列出了 iproute2_table_index 默认值 2022、Linux 默认输入标记 0x2023 和输出标记 0x2024,但 Android 默认标记不同,配置也可能被自定义。参见sing-box Tun 文档。
按平台处理:Linux、OpenWrt、Windows、macOS 和 Android
| 平台 | 推荐做法 | 检查重点 |
|---|---|---|
| Linux 主机 | 先用 route + direct 验证,再测试 bypass。 | sing-box 版本、auto_route、auto_redirect、nfqueue、nftables、ip rule 和路由表。 |
| OpenWrt 旁路网关 | 先确认 LAN 到目标能通过 direct 连通,再测试内核绕过。 | fw4、masquerade、源地址、LAN 客户端默认网关、回程路由和 IPv6 table 2022。 |
| Windows | 使用 route + direct,不照抄未指定 outbound 的 bypass。 | 规则顺序、DNS、默认网卡绑定和应用是否复用旧连接。 |
| macOS | 使用 route + direct,必要时设置自动检测网卡。 | 出站绑定、DNS 解析和系统路由,不把 Linux auto_redirect 示例直接迁移。 |
| Android | 普通 VPNService 使用 route + direct。 | root 环境的 auto_redirect 要单独核对;它不等同于 bypass 动作获得跨平台支持。 |
官方 TUN 文档将 auto_redirect 的完整支持分为 Linux,以及 Android 图形客户端的 root 服务或 root shell 场景;但规则动作文档对未指定 outbound 的 bypass 仍明确标注为仅支持 Linux。因此 Android root 能使用部分 auto_redirect 能力,并不代表可以把 bypass 当成普通 Android VPNService 的通用规则。
Linux 的最小排障配置可以使用下面结构,但它不是订阅配置的替代品:
{
"log": {
"level": "debug",
"timestamp": true
},
"inbounds": [
{
"type": "tun",
"tag": "tun-in",
"address": [
"172.19.0.1/30",
"fdfe:dcba:9876::1/126"
],
"auto_route": true,
"auto_redirect": true,
"strict_route": false
}
],
"outbounds": [
{
"type": "direct",
"tag": "direct"
}
],
"route": {
"final": "direct",
"auto_detect_interface": true
}
}这里把 strict_route 设为 false,是为了排障时先排除严格路由对不经过 TUN 的流量、局域网访问或部分系统应用的影响。官方文档说明,strict_route 可能使不支持的网络不可达;在 Windows 上还可能影响某些应用,因此测试完成后应根据原配置恢复。TUN 字段说明和路由配置文档可用于核对字段位置。
这份配置中的 final: direct 会让未匹配流量全部直连,只能作为临时诊断状态。测试结束后恢复原来的代理出站和 final,否则可能误以为代理功能失效。
bypass 不生效、断网或回环:按顺序执行检查
- 确认版本和平台。执行
如果不是 Linux,或者版本低于 1.13.0,先不要继续追查未指定 outbound 的 bypass,改用sing-box versionaction: route+outbound: direct。 - 确认 TUN 前置条件。配置中应能找到
auto_route: true和auto_redirect: true。Linux 还要检查内核队列和系统规则。建议分开看命令、正常线索和异常含义:
| 命令 | 正常应看到什么 | 异常说明什么 |
|---|---|---|
test -e /proc/net/netfilter/nfnetlink_queue && echo nfqueue-present || echo nfqueue-missing | 输出 nfqueue-present。 | 内核可能没有加载 NFQUEUE,预匹配无法按预期工作。 |
nft list ruleset | grep -E 'sing-box|nfqueue|queue' | 能看到 sing-box 或 queue 相关规则。 | auto_redirect 未生效、规则未插入,或当前系统后端不同。 |
ip -4 rule show、ip -6 rule show | 能看到与实际配置对应的 fwmark 和查表规则。 | 规则缺失、标记被改写,或不能按固定示例判断。 |
ip route show table 2022、ip -6 route show table 2022 | 存在符合当前网络的默认路由和接口。 | 路由指向 TUN、表为空或 IPv6 默认路由错误。 |
官方文档给出的默认 Linux 输入标记是 0x2023、输出标记是 0x2024,但排查时应先从当前 ip rule show 或 ip -6 rule show 读取实际值,再代入路由查询,不要盲抄固定 mark。
- 打开 debug 日志并只复现一次。在现有
log配置中临时加入或修改为:
{
"log": {
"level": "debug",
"timestamp": true
}
}重启 sing-box 后只访问一次目标地址,搜索日志中的 route、bypass、tun、dns 和 outbound。重点看目标是否命中预期规则、是否被前面的规则截断、出站标签是否存在。日志字段的可用值以sing-box 日志文档为准。
- 重新建立连接。预匹配发生在连接建立之前:TCP 在连接建立前没有可用的连接数据,UDP 在该阶段只能看到首个数据包;如果探测动作需要更多数据,预匹配会在相应规则处停止。不要用已经打开的浏览器连接判断修改是否生效,关闭应用连接或重启应用后再试。参见sing-box 预匹配文档。
- 检查 FakeIP、DNS 和协议探测。官方预匹配文档明确说明,FakeIP 目标需要先执行
resolve,否则连接会被拒绝。UDP 只失败而 TCP 正常时,再检查首个 UDP 包、DNS、QUIC 和 sniff 规则;不要把“域名规则未命中”直接判断为 bypass 故障。协议探测字段和支持的协议见sing-box 协议探测文档。 - 单独验证 IPv4 和 IPv6。执行:
ip route get 1.1.1.1
ip -6 route get 2606:4700:4700::1111
ip -6 rule show
ip -6 route show table 2022
ip -6 route get 2606:4700:4700::1111 mark 0x实际标记最后一行的标记值要替换成你从 ip -6 rule show 读取到的实际值。查询结果如果指向 sing-box 自己的 TUN,而不是上游 WAN 接口,就应先修正 table 2022 的 IPv6 路由或恢复 route + direct 测试,不要继续增加 bypass 规则。
Issue #4645 复现了什么:IPv6 超时时查 table 2022
公开 Issue #4645 的环境是 Linux 路由器 ImmortalWrt 25.12.2、sing-box 1.14.3。报告描述的条件是:IPv6 默认路由带有源地址限定,TUN 的 auto_route 将自身 IPv6 默认路由写入 bypass 使用的 table 2022;带有 fwmark 0x2023 的 IPv6 出站连接随后被送回 TUN,形成黑洞。Issue 中记录的示例规则是 fwmark 0x2023 lookup 2022,并且标记后的 IPv6 查询结果指向 TUN 接口;这些值属于该报告环境,不能替代你本机的 ip -6 rule show 输出。参见sing-box Issue #4645。
| 现象 | 报告中对应的原因 | 先执行什么 |
|---|---|---|
| IPv4 正常、IPv6 出站失败 | Issue 报告称 table 2022 的 IPv4 默认路由正常,而 IPv6 默认路由错误指向 TUN。 | ip -6 route show table 2022 |
| 带标记的 IPv6 路由回到 TUN | 报告示例中 fwmark 0x2023 查询到 sing-box TUN 接口。 | ip -6 rule show 和带实际 mark 的 ip -6 route get |
| 连接没有明显错误日志 | Issue 将该复现描述为 silent blackhole。 | 不要只看日志,同时检查内核路由表。 |
| 重启时报 route already exists | 报告还记录了停止后 table 2022 残留路由的次生问题。 | 先保存路由输出,再按你的系统维护流程清理残留项。 |
这是一份特定 Linux/OpenWrt 环境的公开问题报告,不代表所有 sing-box 版本、系统或网络都会出现同样故障。若你的 IPv6 失败与该条件不符,仍应回到平台、auto_redirect、DNS、规则顺序和上游 IPv6 连通性检查。
OpenWrt 旁路网关:bypass 超时先查 NAT 和回程路由
旁路网关中,内核 bypass 与 route + direct 的路径不同:绕过 TUN 后,数据包可能不再经过 sing-box 的地址处理;如果没有正确的 masquerade,源地址可能仍是 LAN 客户端地址,外部网络的回程就可能不经过旁路网关;如果反向路径过滤认为入接口与源地址不匹配,也可能丢弃返回包。因此“规则命中了但连接超时”不一定是规则语法错误。
在 OpenWrt 网关上先执行只读检查,接口名按实际环境替换:
sysctl net.ipv4.ip_forward
sysctl net.ipv4.conf.all.rp_filter
sysctl net.ipv4.conf.default.rp_filter
nft list table inet fw4
ip route show
ip -6 route show
ip -6 route show table 2022检查结果按“现象→原因→命令”理解:
| 现象 | 优先怀疑 | 对应检查 |
|---|---|---|
| LAN 客户端完全无法转发 | 内核转发未开启或默认网关没有指向旁路网关。 | sysctl net.ipv4.ip_forward;在 LAN 客户端查看默认网关。 |
| 请求发出但返回包丢失 | 没有合适的 masquerade,或回程路径绕开旁路网关。 | nft list table inet fw4、ip route show。 |
| 部分接口访问超时 | rp_filter 对源地址和入接口判断不一致。 | sysctl net.ipv4.conf.all.rp_filter 与接口级设置。 |
| 仅 IPv6 超时 | IPv6 table 2022 中的默认路由回到 TUN,或上游 IPv6 回程异常。 | ip -6 route show table 2022、ip -6 route get。 |
建议顺序是:第一步关闭 bypass,用 route + direct 验证 LAN 到目标的基础连通性;第二步确认 auto_redirect、fw4 和 nfqueue;第三步只测试一个明确的 IP/CIDR;最后才测试未指定 outbound 的 bypass。不要为了验证而直接清空 nftables 或删除系统路由,先保存命令输出,并且每次只改一个条件。
本站是第三方使用指南站,不是 sing-box 官方站点;配置字段、平台支持和当前界面应以你正在使用的版本及官方文档为准。
常见问题
sing-box bypass 在 Windows、macOS 或 Android 上为什么不生效?
官方规则动作文档将未指定 outbound 的 bypass 标注为仅支持 Linux,并要求启用 auto_redirect。Windows、macOS 和普通 Android VPNService 场景应改用 action: route + outbound: direct;Android root 的 auto_redirect 能力不能等同于 bypass 动作获得跨平台支持。参见规则动作文档。
action:bypass 和 direct 出站有什么区别?
direct 是出站,规则把连接交给它处理;bypass 则是在特定 Linux auto_redirect 预匹配中,让连接在内核层绕过 sing-box。若 bypass 指定了 outbound,在非 auto_redirect 或已建立连接中,其行为与 route 相同,不应把两者视为完全等价。
为什么 bypass 规则写在配置里,却像没有匹配?
未指定 outbound 的 bypass 只在 auto_redirect 预匹配中匹配,非 auto_redirect 连接和已建立连接会被跳过。先确认版本、平台、auto_route 和 auto_redirect,再打开 log.level: debug 重现一次,并检查规则前面是否已有更早的匹配项。
route_exclude_address 和 route_exclude_address_set 应该怎么选?
route_exclude_address 适合直接填写少量需要从 TUN 自动路由中排除的地址或 CIDR;route_exclude_address_set 按规则集中的 IP CIDR 匹配,官方要求 Linux、auto_route 和 auto_redirect。若只是少量固定网段,先使用明确地址更容易核验;若使用规则集,还要检查规则集加载和系统路由结果。
IPv6 出站全部超时,是不是一定要把 mark 改成 0x2023?
不是。官方 TUN 文档将 0x2023 列为 Linux 默认 auto_redirect 输入标记,Android 默认值不同,配置也可能自定义。Issue #4645 的复现环境确实出现了 fwmark 0x2023 lookup 2022,但你仍应先从 ip -6 rule show 读取实际标记,再用 ip -6 route get 验证路由是否回到 TUN。
常见问题
sing-box bypass 在 Windows、macOS 或 Android 上为什么不生效?
官方规则动作文档将未指定 outbound 的 bypass 标注为仅支持 Linux,并要求启用 auto_redirect。Windows、macOS 和普通 Android VPNService 场景应改用 action: route + outbound: direct;Android root 的 auto_redirect 能力不能等同于 bypass 动作获得跨平台支持。
action:bypass 和 direct 出站有什么区别?
direct 是出站,规则把连接交给它处理;bypass 则是在特定 Linux auto_redirect 预匹配中,让连接在内核层绕过 sing-box。若 bypass 指定了 outbound,在非 auto_redirect 或已建立连接中,其行为与 route 相同,不应把两者视为完全等价。
为什么 bypass 规则写在配置里,却像没有匹配?
未指定 outbound 的 bypass 只在 auto_redirect 预匹配中匹配,非 auto_redirect 连接和已建立连接会被跳过。先确认版本、平台、auto_route 和 auto_redirect,再打开 log.level: debug 重现一次,并检查规则前面是否已有更早的匹配项。
route_exclude_address 和 route_exclude_address_set 应该怎么选?
route_exclude_address 适合直接填写少量需要从 TUN 自动路由中排除的地址或 CIDR;route_exclude_address_set 按规则集中的 IP CIDR 匹配,官方要求 Linux、auto_route 和 auto_redirect。若只是少量固定网段,先使用明确地址更容易核验;若使用规则集,还要检查规则集加载和系统路由结果。
IPv6 出站全部超时,是不是一定要把 mark 改成 0x2023?
不是。官方 TUN 文档将 0x2023 列为 Linux 默认 auto_redirect 输入标记,Android 默认值不同,配置也可能自定义。Issue #4645 的复现环境确实出现了 fwmark 0x2023 lookup 2022,但你仍应先从 ip -6 rule show 读取实际标记,再用 ip -6 route get 验证路由是否回到 TUN。