TUN 协议栈
开启 TUN 后,操作系统把所有 IP 包交给 QuicProxy。协议栈负责把这些 IP 包还原成一条条 TCP 连接和 UDP 报文,再交给分流和代理。协议栈的好坏直接决定了 TUN 模式下的速度、耗电和内存。
通过 TUN 入站的 tun_stack 选择协议栈:
| 协议栈 | 实现方式 | 说明 |
|---|---|---|
native | QuicProxy 自研的纯 Rust 用户态协议栈 | 实验性,需要手动开启 |
system | TCP 交给系统内核,UDP 在用户态处理 | 移动端默认 |
lwip | 基于 C 语言的 lwIP | 桌面端默认 |
smoltcp | 基于 Rust 的 smoltcp | 可选 |
native 栈为什么更省电
空闲时零唤醒
手机锁屏后,大部分连接都处在空闲状态。这时候协议栈如果还在定时醒来检查,就会不断打断 CPU 休眠。
| 协议栈 | 没有流量时的唤醒次数 |
|---|---|
| native | 0 次/秒:只在收到数据、应用读写或某个计时器真正到期时才醒 |
| system | 0 次/秒(每 30 秒清理一次 NAT 表) |
| lwip | 4 次/秒:固定每 250ms 检查一次计时器,永不停止 |
| smoltcp | 最多 200 次/秒:没有计时器时也用 5ms 兜底轮询 |
native 栈把所有连接的计时器放在一起,按最早到期的时间睡眠;没有计时器就一直睡到下一个包到来。
数据少进出几次内核
system 栈的 TCP 交给系统内核处理:一个数据段要先从 TUN 读出来、改写地址再写回 TUN,然后由内核 TCP 处理,最后再从本地 socket 读出来,前后要穿越内核 3 到 4 次。native 栈在用户态直接终结 TCP 连接,每个包只读一次、写一次。CPU 做的事更少,耗电自然更低。
内存按需分配
收发缓冲按需增长,用完即释放,不像 smoltcp 那样给每条连接预先分配固定大小的缓冲区。移动端使用更小的一套参数:接收窗口从 32KB 起步,最大 256KB,最多 1024 条连接,确保在 iOS Network Extension 的 50MB 内存限制之内。
其他优势
- 不依赖系统行为:system 栈需要在 TUN 网段里借一个假地址、把监听 socket 绑到 utun,在 Windows 上还可能触发防火墙。native 栈不需要这些,各平台行为一致。
- 弱网和丢包下更快:支持 SACK 选择确认、快速恢复和尾部丢包探测,一次丢包不必等 300ms 以上的重传超时。
- ping 可用:直接在本地回复 ICMP / ICMPv6 Echo,
ping不再超时。system 栈会直接丢弃 ping。 - IPv6 更省事:TUN 只分到一个 IPv6 地址(如
/128)时,system 栈的 IPv6 TCP 无法工作,native 栈不受影响。 - 纯 Rust:没有 C 代码依赖,跨平台编译更简单,也更安全。
实测数据
测试环境:Linux 真实 TUN 设备,MTU 1500,系统内核 TCP 作为客户端,iperf3 和 curl 经过 native 栈访问本机服务,并人为注入随机丢包。
| 场景 | 上行(客户端发送) | 下行(客户端接收) |
|---|---|---|
| 无丢包 | 约 3.1 Gbps | 约 2.9 Gbps |
| 1% 丢包 | 约 1.4 Gbps | 约 480 Mbps(8 条并发约 1.9 Gbps) |
| 5% 丢包 | 约 25 Mbps | 约 67 Mbps |
- 50MB 文件在 0% 到 5% 丢包下下载,校验和都完全一致。
- 3% 丢包下,800 条半关闭和服务端先关的连接全部正确完成,没有连接泄漏。
- 空闲 10 秒内,进程上下文切换为 0。
上行由客户端的系统内核负责发送,下行由 native 栈负责发送。5% 丢包时,native 栈的下行速度高于系统内核自身的上行速度。
当前限制
native 栈目前仍是实验性功能:
- 还没有在 iOS / Android 真机上做能耗实测。上面的省电分析基于设计和 Linux 上的测试。
- 不重组 IPv4 分片,也不解析 IPv6 扩展头,这两类包会被丢弃。
- 目前收到 SYN 后立即完成握手;如果代理端连接失败,应用看到的是连接被重置,而不是连接超时。
如何启用
在 TUN 入站中设置 tun_stack:
{
"type": "tun",
"tun_address": ["172.19.0.1/24"],
"mtu": 1384,
"auto_route": true,
"tun_stack": "native"
}
遇到问题时,把 tun_stack 改回 system 或 lwip 即可。