GitHub: https://github.com/dndx/phantun
Crates: https://crates.io/crates/phantun
Phantun 的初衷跟 @wangyucn 的 udp2raw 很类似,都是为了实现一种简单的用户态 TCP 状态机来对 UDP 流量做伪装。主要的目的是希望能让 UDP 流量看起来像是 TCP,又不希望受到 TCP retransmission 或者 congestion control 的影响。
整个项目使用 Rust 实现,没有额外的依赖。I/O 部分使用了久经考验的 Tokio。在可能的情况下尽量避免了多进程之间的锁争抢(比如使用原子操作来增加 TCP SEQ/ACK )。
TCP 状态机部分做成了一个单独的库作为 Phantun 的依赖,方便在别的项目里集成:
https://crates.io/crates/fake-tcp
这是我第一个用 async Rust 写的程序,不得不说 async Rust 和 Tokio 的性能大大超出了我的预期。目前这个只有最基本优化的版本在单 CPU 和多 CPU 的情况下性能均超过了基于 libev 和 C++ 的 udp2raw 。Rust 的安全检查也非常的舒服,基本上编译通过了以后不会有任何内存问题的可能。Rust 的 Drop
支持对管理 TCP 的状态也是帮助非常大。总体来说,这个代码量不大的项目的开发效率,执行效率和稳定性都大大超出了我的预期。
目前 Phantun 在几台机器上跑了一段时间,没有出现任何不稳定的现象,任何时候内存占用都未超过 2MB 。核心之间的 CPU 占用也很均匀。用 perf
看了一下火焰图,90% 以上的 CPU 时间都花在了内核态,说明 Tokio 的 runtime 实现是非常的高效的。
Rust 的 build 环境简直不要太舒服,交叉编译不同的架构也就是两行命令的事情,跟原来写 C 比起来开发体验上了一个档次。
唯一的缺点就是 async Rust 因为 Futures 用的比较多,build 出来的二进制文件也比较大,超过了 6MB 。对比之下 udp2raw 的二进制文件只有 5 MB (而且代码量比 Phantun 要多了不少)。
Phantun 的目标不是为了替代 udp2raw,从一开始 Phantun 就希望设计足够的简单高效,所以 udp2raw 支持的 ICMP 隧道,加密,防止重放等等功能 Phantun 都选择不实现。
Phantun 假设 UDP 协议本身已经解决了这些问题,所以整个转发过程就是简单的明文换头加上一些必要的 TCP 状态控制信息。对于我日常使用的 WireGuard 来说,Phantun 这种设计是足够安全的,因为 WireGuard 的协议已经更好的实现了这些安全功能。
Phantun 使用 TUN 接口来收发 3 层数据包,udp2raw 使用 Raw Socket + BFP 过滤器。个人感觉基于 TUN 的实现要稍微的 clean 一点,而且跨平台移植也要更容易(不过目前只做了 Linux 的支持)。
Phantun 的 TCP 连接是按需创建的,只启动 Client 不会主动去连接服务器,需要第一个数据包到达了后才会按需创建。每个 UDP 流都有自己独立的 TCP 连接。这一点跟 udp2raw 很不一样,udp2raw 所有的 UDP 连接共用一个 TCP 连接。这样做的坏处就是 udp2raw 需要额外的头部信息来区分连接,更加增加了头部的开销。跟纯 UDP 比较,Phantun 每个数据包的额外头部开销是 12 byte,udp2raw 根据我的测试达到了 44 bytes 。
跟 udp2raw 的详细功能和性能比较,请查看 README.md。
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.