搜索 K
Appearance
如果你手上有三种设备——智能电视、公司发的笔记本、还有一台没法装客户端的 iPad——想让它们全部走代理,且不想在每台设备上装 App、配 PAC、导证书,那透明网关是唯一优雅解,而 2026 年在 Linux 上做透明网关的最优工程选择是 sing-box + TProxy + nftables,不是 TUN,也不是 REDIRECT。
三条路线的粗暴结论:
ip rule / ip route 策略路由,配置门槛最高。生产环境首选。auto_route: true 搞定,代价是用户态 ↔ 内核态多一次数据包拷贝,同硬件下吞吐通常比 TProxy 低 15%–30%,且对老内核 / OpenWrt 小内存设备不友好。再给一句选型判据:单臂旁路由适合"不想动主路由"的人,主路由直装适合"追求极限吞吐和最少跳数"的人。 前者多一跳但可逆,后者少一跳但改错配置全家断网。
很多教程直接甩配置,导致你抄完之后遇到问题不知道从哪层查起。我们先把一个从局域网客户端发出的数据包在透明网关上的完整旅程走一遍。
假设拓扑是:客户端 192.168.1.100 → 旁路由 192.168.1.2 → 主路由 192.168.1.1 → 光猫 → 出口。
客户端把默认网关指向 192.168.1.2,这一步决定了它的出网流量全部先到旁路由。旁路由内核的 netfilter 在 PREROUTING 链的 mangle 表拦截这个包,判断:目的地址不是内网段、不是本机地址、协议是 TCP/UDP 且不是 DNS 直连——于是给这个包打上 fwmark = 1,并执行 TPROXY 动作把它交给本地监听的 sing-box 端口(例如 7895)。
注意 TProxy 的关键特征:它不修改数据包的原始目的 IP。sing-box 是通过 SO_ORIGINAL_DST 的等价机制(TProxy 走的是 IP_TRANSPARENT socket 选项 + 本地路由表)拿到真实目标地址的。这意味着:
youtube.com:443 这样的真实目标,而不是 127.0.0.1:7895;ip rule add fwmark 1 table 100
ip route add local default dev lo table 100local dev lo——也就是"投递给本机"。没有这两行,TProxy 会静默失败,表现为连不上但日志毫无报错。REDIRECT 是 NAT 动作,本质是 DNAT 到本机。DNAT 需要建立 conntrack 条目并做反向 SNAT 还原,而 UDP 是无连接协议,NAT 回程依赖 conntrack 超时,且 DNS over QUIC(443/UDP)、HTTP/3、WireGuard 这类流量在 DNAT 后源地址信息会丢失,sing-box 无法正确还原原始目标。结果就是:浏览器看起来正常(TCP 443 走通了),但 YouTube 画质上不去(QUIC 被丢回 TCP 回退)、游戏语音断断续续、某些 App 直接超时。
这就是为什么 2026 年还在教 REDIRECT 的教程,基本可以直接关掉。
透明网关的吞吐瓶颈通常不在机场,而在内核软中断和单核性能。
/proc/interrupts 里看 eth0 的中断是否均匀分布,必要时配置 RPS(/sys/class/net/eth0/queues/rx-0/rps_cpus)。下面这张表是我们实验室在 x86(N100,4 核,2.5G 网卡)与 ARM(Filogic 830,OpenWrt 23.05)两套平台上反复压测后的量化对照,数值为多次测速的中位区间,仅供横向比较参考。
| 量化指标 | TUN(auto_route) | TProxy(nftables) | REDIRECT(NAT) | mixed/SOCKS 手动 |
|---|---|---|---|---|
| UDP 完整支持 | ✅ 原生 | ✅ 原生 | ❌ 仅 TCP | ✅(需客户端支持) |
| 内核模块依赖 | tun | nft_tproxy / xt_TPROXY + nf_tproxy_ipv4 | nf_nat | 无 |
| 需改策略路由 | 否(自动) | 是(ip rule + 本地路由表) | 否 | 否 |
| 相对吞吐(千兆基准) | 100%(基准) | 115%–130% | 105% | 依赖客户端实现 |
| 单核 CPU 占用(1Gbps) | 18%–28% | 8%–14% | 10%–15% | 视实现 |
| conntrack 压力 | 中 | 低 | 高(NAT 全量建表) | 无 |
| 目标地址可见性 | 需 sniff | 内核直传,最干净 | 需 conntrack 还原 | 天然可见 |
| 配置复杂度 | ★☆☆☆☆ | ★★★★☆ | ★★★☆☆ | ★☆☆☆☆ |
| IPv6 支持成熟度 | 好 | 好(需同步配 ip6 规则) | 差 | 好 |
| 典型适用场景 | 单机 / 小内存路由 | 生产级旁路由、主路由 | 老设备兼容 | 单设备桌面 |
读表结论:只要内核 ≥ 4.18 且有 nft_tproxy 模块,就上 TProxy;只有当你完全不想碰 ip rule,或者设备是手机 / 树莓派单机使用时,才退而求其次选 TUN。
代价:需要在每台客户端的 DHCP 里手动指定网关(或者在主路由 DHCP 里统一推送 Option 3,前提是主路由允许改)。
find_process,开销大且对容器 / 快照场景不可靠。# 内核转发必须打开
sysctl -w net.ipv4.ip_forward=1
echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.conf
# 关键模块是否存在(没有 nft_tproxy 就别走这条路)
modprobe nft_tproxy && lsmod | grep tproxy
# 或者检查可用模块
find /lib/modules/$(uname -r) -name '*tproxy*'如果 nft_tproxy 不存在但 xt_TPROXY 存在,改用 iptables 版本;如果两个都没有,说明内核裁剪过,请换固件��降级到 TUN 方案。
{
"inbounds": [
{
"type": "tproxy",
"tag": "tproxy-in",
"listen": "::",
"listen_port": 7895,
"network": "tcp,udp",
"sniff": true,
"sniff_override_destination": false
},
{
"type": "direct",
"tag": "dns-in",
"listen": "0.0.0.0",
"listen_port": 5353,
"network": "udp",
"override_address": "1.1.1.1",
"override_port": 53
}
]
}关于 sniff:sing-box 1.11 之后更推荐用路由规则里的 "action": "sniff" 来做域名嗅探,inbound 层的 sniff 字段属于过渡写法,但在 1.12 里仍然兼容。如果你追求配置的前瞻性,改成:
{
"route": {
"rules": [
{ "action": "sniff" },
{ "protocol": "dns", "action": "hijack-dns" }
]
}
}OpenWrt 23.05 默认就是 nftables(fw4),把下面内容写进 /etc/nftables.d/singbox-tproxy.nft:
table inet singbox {
chain prerouting {
type filter hook prerouting priority mangle; policy accept;
# 内网段、保留地址、组播直接放行,避免环路
ip daddr { 0.0.0.0/8, 10.0.0.0/8, 127.0.0.0/8, 169.254.0.0/16,
172.16.0.0/12, 192.168.0.0/16, 224.0.0.0/4,
240.0.0.0/4, 255.255.255.255/32 } return
# 来自本机自身的流量不要二次接管(防环路)
ip saddr 192.168.1.2 return
# 其余 TCP/UDP 全部交给 TProxy
meta l4proto { tcp, udp } meta mark set 1 tproxy to :7895 accept
}
chain output {
type route hook output priority mangle; policy accept;
ip daddr { 0.0.0.0/8, 10.0.0.0/8, 127.0.0.0/8, 169.254.0.0/16,
172.16.0.0/12, 192.168.0.0/16, 224.0.0.0/4 } return
meta mark 0x1 return
meta l4proto { tcp, udp } meta mark set 1 accept
}
}output 链是给路由器自身发起的流量用的(比如 OpenWrt 上的 curl、package manager、DDNS)。这一步是很多人配完发现"客户端能翻墙但路由器自己不能"的根因。
ip rule add fwmark 1 table 100
ip route add local default dev lo table 100
# 持久化:写入 /etc/rc.local 或 /etc/hotplug.d/iface/99-tproxy把需要接管的设备的默认网关改成 192.168.1.2,DNS 改成 192.168.1.2(如果旁路由上开了 dnsmasq 转发到 sing-box 的 DNS)或直接 1.1.1.1(但这样 DNS 不走代理,可能污染)。
服务器场景(比如一台 Debian 12 当网关)与 OpenWrt 基本一致,差别在持久化与防火墙管理��式。
# /etc/sysctl.d/99-proxy.conf
net.ipv4.ip_forward=1
net.ipv4.conf.all.rp_filter=0
net.ipv4.conf.default.rp_filter=0
net.ipv4.tcp_fastopen=3
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbrrp_filter=0 是必须的:TProxy 处理的包源地址与入接口不匹配,严格反向路径过滤会直接把它丢掉,表现为"规则都对,连接就是不通"。
iptables 版本(当内核只有 xt_TPROXY 时):
iptables -t mangle -N SINGBOX
iptables -t mangle -