SagerNet / SagerNet/sing-box

auto_detect_interface 将 loopback UDP 出站绑定到默认网卡

Open
#4,423 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
38.2k
Forks
4.6k
Avg merge
19d 15h
Merged PRs (30d)
1

Description

操作系统

Linux

系统版本

Ubuntu 24.04.4 LTS Linux 6.8.0-101-generic x86_64

安装类型

sing-box 原始命令行程序

如果您使用图形客户端程序,请提供该程序版本。

No response

版本
sing-box version 1.14.0-beta.15

Environment: go1.26.6 linux/amd64
Tags:
with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,with_clo
udflared,with_naive_outbound,with_usbip,with_openvpn,with_openconnect,badlinkname,tfogo_checklinkname0,with_musl
Revision: e98ae31a7d9a558d1b2dd982c9f1f9c4b8bbff87
CGO: enabled
描述

在 Linux 上启用 route.auto_detect_interface=true 后,
Shadowsocks relay 将 UDP 数据转发到本机 127.0.0.1 destination 时,
UDP 包会被绑定到默认物理网卡,而不是通过 lo 发送到本机 loopback socket。

环境中的链路:

Shadowsocks relay inbound
  -> destination 127.0.0.1:38080
  -> 本地 Shadowsocks inbound
  -> 实际 UDP 目标

sing-box 日志显示 relay 已经将数据路由到
127.0.0.1:38080,但是本地 Shadowsocks inbound 收不到 UDP 包。

tcpdump 显示数据包实际从 eth0 发出:

eth0 Out:
10.42.2.41:<ephemeral> -> 127.0.0.1:38080 UDP

而在 lo 上抓不到对应的数据包。

这会导致本地二级 Shadowsocks relay 无法收到 UDP,
上层 DNS 查询持续超时并产生大量重试。

为 loopback destination 增加一个 bind_interface=lo 的 direct outbound,
并为 127.0.0.0/8 和 ::1/128 增加路由规则后,问题消失。

重现方式

以下是已经在同一台 Linux 主机上验证过的最小化复现方式。

不依赖远程服务器、不依赖 TUN、不依赖图形客户端。
测试使用两个临时 sing-box 进程。

1. Relay 配置

注意:该配置没有 route.rules,也没有 route.final。

{
  "log": {
    "level": "debug",
    "timestamp": true
  },
  "inbounds": [
    {
      "type": "shadowsocks",
      "tag": "relay-in",
      "listen": "::",
      "listen_port": 55517,
      "method": "2022-blake3-aes-128-gcm",
      "password": "ArTR9gokhcj3C95CVIPNDw==",
      "destinations": [
        {
          "name": "local",
          "server": "127.0.0.1",
          "server_port": 5380,
          "password": "YF+PqRw6PDNPlqHPfztgKg=="
        }
      ]
    },
    {
      "type": "shadowsocks",
      "tag": "down-in",
      "listen": "127.0.0.1",
      "listen_port": 5380,
      "method": "2022-blake3-aes-128-gcm",
      "password": "YF+PqRw6PDNPlqHPfztgKg=="
    }
  ],
  "outbounds": [
    {
      "type": "direct",
      "tag": "direct"
    }
  ],
  "route": {
    "auto_detect_interface": true
  }
}

2. Client 配置

{
 "log": {
   "level": "debug",
   "timestamp": true
 },
 "inbounds": [
   {
     "type": "direct",
     "tag": "udp-in",
     "listen": "127.0.0.1",
     "listen_port": 55553,
     "network": "udp",
     "override_address": "1.1.1.1",
     "override_port": 53
   }
 ],
 "outbounds": [
   {
     "type": "shadowsocks",
     "tag": "ss-out",
     "server": "127.0.0.1",
     "server_port": 55517,
     "method": "2022-blake3-aes-128-gcm",
     "password": "ArTR9gokhcj3C95CVIPNDw==:YF+PqRw6PDNPlqHPfztgKg=="
   }
 ],
 "route": {
   "rules": [
     {
       "inbound": [
         "udp-in"
       ],
       "outbound": "ss-out"
     }
   ]
 }
}

3. 启动

分别启动两个 sing-box 进程:

sing-box run -c relay.json
sing-box run -c client.json

4. 发送 UDP 数据包

printf 'repro-payload' | nc -u -w 1 127.0.0.1 55553

5. 观察结果

Relay 日志显示:

inbound/shadowsocks[relay-in]:
[local] inbound packet connection to 127.0.0.1:5380

outbound/direct[direct]:
outbound packet connection

但是 downstream 的 down-in 没有收到对应的:

inbound/shadowsocks[down-in]:
inbound packet connection

Client 日志显示:

inbound/direct[udp-in]:
inbound packet connection to 1.1.1.1:53

outbound/shadowsocks[ss-out]:
outbound packet connection to 1.1.1.1:53

但 UDP 数据无法继续到达本机的 downstream Shadowsocks inbound。

6. 生产环境抓包结果

在实际生产配置中,抓包结果为:

eth0 Out IP 10.42.2.41.46742 > 127.0.0.1.38080:
UDP, length 886

在 lo 上抓包:

tcpdump -ni lo 'udp port 38080'

0 packets captured

7. Workaround

增加 loopback 专用 outbound:

{
 "type": "direct",
 "tag": "direct-loopback",
 "bind_interface": "lo"
}

并在通用规则前增加:

{
 "ip_cidr": [
   "127.0.0.0/8",
   "::1/128"
 ],
 "outbound": "direct-loopback"
}

增加后,数据包恢复为:

lo:
127.0.0.1:<ephemeral> -> 127.0.0.1:38080 UDP
127.0.0.1:38080 -> 127.0.0.1:<ephemeral> UDP

downstream Shadowsocks inbound 可以正常收到 UDP。

日志
# 复现时 Relay 日志

+0800 2026-08-16 00:36:58 INFO [4107452948 0ms] inbound/shadowsocks[relay-in]: [local] inbound packet connection from
127.0.0.1:59368
+0800 2026-08-16 00:36:58 INFO [4107452948 0ms] inbound/shadowsocks[relay-in]: [local] inbound packet connection to
127.0.0.1:5380
+0800 2026-08-16 00:36:58 INFO [4107452948 0ms] outbound/direct[direct]: outbound packet connection

# 复现时 Client 日志

+0800 2026-08-16 00:36:58 INFO [824732556 0ms] inbound/direct[udp-in]: inbound packet connection from 127.0.0.1:59368
+0800 2026-08-16 00:36:58 INFO [824732556 0ms] inbound/direct[udp-in]: inbound packet connection to 1.1.1.1:53
+0800 2026-08-16 00:36:58 DEBUG [824732556 0ms] router: match[0] inbound=udp-in => route(ss-out)
+0800 2026-08-16 00:36:58 INFO [824732556 0ms] outbound/shadowsocks[ss-out]: outbound packet connection to 1.1.1.1:53

# 复现时 downstream 日志

没有出现 inbound/shadowsocks[down-in] 的 UDP 接收记录。

# 生产环境错误路径抓包

eth0 Out IP 10.42.2.41.46742 > 127.0.0.1.38080:
UDP, length 886

# 修复后的抓包

lo IP 127.0.0.1.47152 > 127.0.0.1.38080:
UDP, length 607

lo IP 127.0.0.1.38080 > 127.0.0.1.47152:
UDP, length 875
支持我们
完整性要求
  • 我保证阅读了文档,了解所有我编写的配置文件项的含义,而不是大量堆砌看似有用的选项或默认值。
  • 我保证提供了可以在本地重现该问题的服务器、客户端配置文件与流程,而不是一个脱敏的复杂客户端配置文件。
  • 我保证提供了可用于重现我报告的错误的最简配置,而不是依赖远程服务器、TUN、图形界面客户端或者其他闭源软件。
  • 我保证提供了完整的配置文件与日志,而不是出于对自身智力的自信而仅提供了部分认为有用的部分。

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with route.auto_detect_interface and the direct outbound's bind_interface behavior on Linux, using the two-process relay configuration and the provided nc command to reproduce the UDP path. Compare the failing default route with the direct-loopback workaround; done means 127.0.0.0/8 and ::1/128 UDP destinations reach the local downstream inbound without manual routing rules.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, linux
Domain
networking
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.