sing-box 1.14.0-alpha.39 的核心变化是:TUN、WireGuard、Tailscale 这类 L3 inbound 的 TCP/UDP 流量,可以在 pre-match 阶段直接转到 WireGuard / Tailscale endpoint。你现在要排查的第一件事:连接有没有在 L3 被转走,而不是先改 DNS、MTU 或整份订阅。

先把边界说清楚:本文按 github.com release 和文档推导配置检查口径,未在 root Android / jailbroken iOS 复测。1.14.0-alpha.391.14.0-alpha.40 都是预发布线;生产环境先保留 1.13 稳定配置,另存一份最小 JSON 做验证。不要在唯一可用配置上直接动刀。

你现在到底是在排哪一种问题?

同样是“开 TUN 后 WireGuard 不通”,原因可能完全不同。先把现象压成一类,别一边改 endpoint key,一边改 DNS,一边换 route final。

错误表现更可能原因先检查什么
sing-box check 直接报 unknown fieldcore 版本不是 1.14 alpha,或配置写了当前版本不认识的字段sing-box version 和报错字段所在段落
ping 某个 WireGuard allowed IP 有回应,网页域名不通ICMP 已能走 L3,但 TCP/UDP 没在 pre-match 命中route rule 的 networkpreferred_by、目标 tag
IP 直连通,域名访问失败DNS / FakeIP / resolve 顺序问题debug 日志里是否出现 resolve 或 DNS 查询
TUN 开启后所有连接卡住路由回环或 WireGuard 握手流量也进了 TUNWG endpoint 地址是否被排除,默认接口是否正确
日志显示走了 proxyselectorsocks-out你走的是普通 proxy outbound,不是 L3 forwardingroute 命中的 outbound tag 类型
1.14.0-alpha.40 配置里用了 bridge,但 WireGuard 仍不通bridge 只解决直接从 L3 出接口,不等于 WG endpoint 配好先回到 WireGuard endpoint 的 key、address、allowed_ips

不看日志的排查都是玄学。这里至少要拿到三样东西:sing-box versionsing-box check -c 输出、一次固定目标的 debug 日志。

1.14 alpha 和 1.13 稳定线差在哪里?

官方 release notes 的时间线很短,但别读错:

版本官方变化排查含义
1.13.0route rule 的 network 增加 icmp,L3 inbound 的 ICMP 可被路由到 Direct、WireGuard、Tailscaleping 通了,不代表 TCP/UDP 也在 L3 forwarding
1.14.0-alpha.39在 1.13.0 ICMP proxy support 基础上,TUN / WireGuard / Tailscale 的 TCP、UDP 可直接 forward 到 WireGuard / Tailscale endpoint本文的核心功能点从这里开始
1.14.0-alpha.40新增 bridge outbound,作为 direct 的 L3 对应物用于把 L3 流量直接发出接口,不是 WireGuard endpoint 的替代品

所以第一条命令必须先跑:

sing-box version

看到 1.13.x 时,别继续排 TCP/UDP 的 L3 forwarding。你最多验证 ICMP 相关行为,生产配置继续留在 1.13 稳定线更稳。看到 1.14.0-alpha.39 或更新,才进入下面的 pre-match 和 route 排查。

怎么判断自己真的走了 L3 forwarding?

L3 forwarding 的证据不是“能打开网页”。网页能打开也可能只是 TUN 把连接转成 L4 proxy 后走了普通 outbound。

更可靠的判断是三条:

  1. 入口来自 L3 inbound:inbounds[].typetun,或来源是 WireGuard / Tailscale inbound。
  2. route 在 pre-match 阶段就能决定去向:规则只依赖当时可用的信息,例如 networkip_cidrpreferred_by、FakeIP resolve 后得到的目的地址。
  3. 目标是支持 L3 forwarding 的 endpoint 或 outbound:WireGuard / Tailscale endpoint,或者 alpha.40 之后的 bridge outbound。

下面这份 JSON 是排查用骨架。把 endpoint 地址、key、allowed IP 换成你自己的值;不要把示例私钥拿去运行。

{
  "log": {
    "level": "debug",
    "timestamp": true
  },
  "inbounds": [
    {
      "type": "tun",
      "tag": "tun-in",
      "interface_name": "sb-tun0",
      "address": [
        "172.19.0.1/30"
      ],
      "mtu": 1408,
      "auto_route": true,
      "strict_route": true,
      "stack": "system"
    }
  ],
  "endpoints": [
    {
      "type": "wireguard",
      "tag": "wg-ep",
      "system": false,
      "mtu": 1408,
      "address": [
        "10.66.66.2/32"
      ],
      "private_key": "REPLACE_WITH_BASE64_PRIVATE_KEY",
      "peers": [
        {
          "address": "198.51.100.10",
          "port": 51820,
          "public_key": "REPLACE_WITH_BASE64_PUBLIC_KEY",
          "allowed_ips": [
            "10.66.66.0/24",
            "192.0.2.0/24"
          ],
          "persistent_keepalive_interval": 25
        }
      ]
    }
  ],
  "outbounds": [
    {
      "type": "direct",
      "tag": "direct"
    }
  ],
  "route": {
    "auto_detect_interface": true,
    "rules": [
      {
        "inbound": [
          "tun-in"
        ],
        "network": [
          "tcp",
          "udp",
          "icmp"
        ],
        "preferred_by": [
          "wg-ep"
        ],
        "action": "route",
        "outbound": "wg-ep"
      }
    ],
    "final": "direct"
  }
}

这段里最值得看的是 preferred_by: ["wg-ep"]。官方 route rule 文档写到,preferred_by 对 WireGuard 会匹配 peer 的 allowed IP。也就是说,你不是用域名规则硬塞进 WG,而是让 WireGuard endpoint 自己的路由偏好参与匹配。

如果你的配置里 route 命中的是 selectorsockshttpshadowsocks 这类普通 outbound,那就不是这次 release notes 说的 L3 forwarding。它可能能用,但排查方向不同。

为什么 ping 通了,TCP/UDP 还是不通?

ping 只能说明 ICMP 这条线有信号。官方 route rule 文档在 1.13.0 处写得很明确:ICMP echo 可以通过新的 icmp network 匹配,并从 TUN / WireGuard / Tailscale inbound 路由到 Direct、WireGuard、Tailscale outbound。

1.14.0-alpha.39 才把 TCP/UDP 也放进 L3 forwarding 的讨论里。排查时别把 ping 10.66.66.1 当成全协议通过。

验证命令分三类跑,目标要固定:

sing-box check -c ./tun-wg-l3.json
ping -c 4 10.66.66.1
curl -4 --connect-timeout 5 http://192.0.2.10:8080/

ping 通但 curl 不通时,先看 network 规则有没有包含 tcp / udp,再看 debug 日志里 TCP 连接有没有在 pre-match 阶段命中 wg-ep。如果日志直接落到 final: direct,不是 WireGuard key 的问题,是 route 没匹配。

DNS 和 FakeIP 会怎样搅乱判断?

Pre-match 的限制是:连接还没建立。TCP 没有连接数据,UDP 只有第一个包。如果规则需要更多数据,pre-match 会停在那条规则。

这就是 DNS / FakeIP 容易误导人的地方。官方 Pre-match 文档还专门提醒:FakeIP 目的地址需要在 pre-match 做 resolve,否则连接会被拒绝。

排查时可以先避开域名,拿 WireGuard peer 的 allowed IP 里一个明确地址测试。等 IP 路径确认后,再加域名和 FakeIP。

如果必须测域名,route 里要让 resolve 出现在会影响 L3 forwarding 的规则之前。配置写法会随你的 DNS 结构变化,下面只展示顺序意图:

{
  "route": {
    "rules": [
      {
        "domain_suffix": [
          "example.net"
        ],
        "action": "resolve"
      },
      {
        "domain_suffix": [
          "example.net"
        ],
        "action": "route",
        "outbound": "wg-ep"
      }
    ],
    "final": "direct"
  }
}

这不是让你把所有域名都强行 resolve。它的用途是排除一种误判:你以为 WireGuard endpoint 不工作,其实连接在 FakeIP 阶段已经被拒了。

怎么分清普通 proxy outbound 和 WireGuard endpoint?

看配置位置。当前 sing-box 里 WireGuard 有两套容易混淆的文档:

写法位置当前判断
outbounds[].type = "wireguard"configuration/outbound/wireguard/官方页面已标废弃,排查新配置时只拿来理解旧文件
endpoints[].type = "wireguard"configuration/endpoint/wireguard/1.11.0 起的 endpoint 模型,本文按这个口径排查
outbounds[].type = "bridge"configuration/outbound/bridge/alpha.40 新增,直接把 L3 流量从接口发出
outbounds[].type = "socks" / http / 其它 proxyoutbounds[]这是 L4 proxy 路径,不是 alpha.39 说的 L3 forwarding

最小判断方法:route 命中的目标 tag 如果只存在于 endpoints[],并且是 wireguard / tailscale,才继续按 L3 forwarding 查。命中的目标如果是普通 outbound,就按传统 proxy 路径查 DNS、selector、节点连通性。

这里不要被字段名骗了。outbound 这个 rule action 字段是历史命名,官方 Pre-match 文档在支持目标里写的是 WireGuard / Tailscale endpoints。排查时以 tag 实际指向的对象为准。

路由回环先查哪里?

TUN 到 WireGuard 最常见的硬卡点是回环:WireGuard 握手包本来要去 peer 的公网地址,却也被 TUN 默认路由抓进 sing-box,最后自己套自己。

先查三项:

  1. route.auto_detect_interface 是否开启,或你是否用 default_interface / bind_interface 指定了真实物理接口。
  2. WireGuard peer 的 address 是否被 TUN 路由覆盖。
  3. route_exclude_address 或系统路由里,是否有 peer endpoint 的明确出口。

Linux 上可以看:

ip route get 198.51.100.10
ip rule

macOS 上可以看:

route -n get 198.51.100.10
netstat -rn

Windows 上可以看:

route print

198.51.100.10 是示例 peer 地址,换成你的 WireGuard server IP。期望结果是:去 peer 公网地址的握手流量走物理网卡,不先进 sb-tun0。如果这一步错了,后面所有 TCP/UDP L3 forwarding 都会像坏了一样。

日志里要抓哪几行?

把日志临时调到 debug,只复现一个目标。别把整晚日志塞进来,排错的人看不动,你自己也看不动。

{
  "log": {
    "level": "debug",
    "output": "tun-wg-l3.log",
    "timestamp": true
  }
}

抓 30 秒就够,重点搜这些词:

日志线索它说明什么下一步
wg-eproute 最终命中了 WireGuard endpoint再查 peer allowed_ips 和握手
direct连接落到了兜底preferred_byip_cidrnetwork
resolvepre-match 做了 DNS 解析看 FakeIP 是否仍被拒
sniff 后停住TCP pre-match 拿不到足够数据暂时移走依赖 sniff 的规则
no such outboundtag 引用不存在route.rules[].outbound 拼写
operation not permitted权限不足查管理员、root、网络扩展或 VPN 授权

如果你看到 selector 或普通 outbound tag,先别继续改 WireGuard。那说明当前连接没有走到 endpoint;把 route 规则改到最小,再复现同一个 IP。

生产环境怎么保留 1.13 配置?

不要把 1.14 alpha 当作“升级越新越好”。正确做法是两份文件并行:

config-1.13-stable.json
config-1.14-alpha-l3-test.json

config-1.13-stable.json 只做日常使用,不动。config-1.14-alpha-l3-test.json 只放一个 TUN inbound、一个 WireGuard endpoint、一个 direct fallback、一个 route 规则。它能通过 sing-box check,再去看日志和路由表。

最后再做一次降级验证:

sing-box run -c ./config-1.13-stable.json

只要 1.14 alpha 的排查还没完成,1.13 稳定配置就是你的退路。alpha.40 的 bridge outbound 值得单独测,但别把它和 WireGuard endpoint 问题混在同一次修改里。

相关阅读

这篇到这里就够了。下一步只做一件事:拿你的 sing-box version、最小 JSON、sing-box check 输出和 30 秒 debug 日志,对照上面的三条证据线判断当前路径。先证明它走没走 L3,再谈要不要调 DNS、MTU 或 route。