Phantun - Rust 写的轻量级 UDP -> TCP 混淆器

2021-09-19 17:43:46 +08:00
 dndx

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 要多了不少)。

跟 udp2raw 的主要区别

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

13146 次点击
所在节点    分享创造
48 条回复
Tink
2021-09-19 18:38:55 +08:00
牛啊,这个感觉是 wireguard 绝配
meanmachine
2021-09-19 18:40:51 +08:00
mark... udp2raw 跑单核确实蛋疼了一点
makelove
2021-09-19 19:18:22 +08:00
太牛了,而且这种真是生活必须品。
我当前用 upd2raw,确实对我用来 fq 的垃圾小鸡性能有点压力。
codehz
2021-09-19 20:38:45 +08:00
测试过可以穿透一些常见的 tcp 反代 /负载均衡吗(
dndx
2021-09-19 20:59:21 +08:00
@codehz 7 层反代是不行的,7 层反代的 TCP 栈是操作系统的,这种只能用真的 TCP 来实现,就没有了 Fake TCP 的性能优势。
messense
2021-09-19 21:17:57 +08:00
> 唯一的缺点就是 async Rust 因为 Futures 用的比较多,build 出来的二进制文件也比较大,超过了 6MB

可以用 upx 压缩一下,应该可以小很多;也可以再试试开启 LTO and/or opt-level = "z"
kkocdko
2021-09-20 01:55:33 +08:00
@messense 个人认为 binary 大小不算太大问题,比起 golang 自带 runtime 那动辄 10M+的体积,已经算很理想了。
lto pgo 之类的楼主这个水平应该会考虑到,optz 就算了,这项目的初衷大概就是性能? optz 开倒车呢
dndx
2021-09-20 01:58:29 +08:00
@kkocdko
@messense 目前 release 用 zip 压缩一下两个 binary 一共也不到 4MB,还算可以接受。
plko345
2021-09-20 08:24:41 +08:00
楼主,请教你这些网络编程相关的知识有哪些书推荐,或课程
codehz
2021-09-20 10:12:20 +08:00
@dndx 我是说 cloudflare spectrum 这样的服务,理论上是 layer 4 的反代
dndx
2021-09-20 11:54:09 +08:00
@codehz 这种肯定没有问题,MTU 调好就行。
onlyu
2021-09-20 12:14:00 +08:00
很棒啊,这样的分享是最想看到的
dndx
2021-09-20 12:18:29 +08:00
@codehz 又看了一下,Spectrum 宣称可以提供请求日志,感觉像是在 CF Edge 终结了 TCP 连接,如果是这样的话应该不行,最好实际测一下。
dndx
2021-09-20 14:59:37 +08:00
@plko345 网络编程入门的话我看的是 UNIX Network Programming 。写 Phantun 主要就是看 RFC793 和 API 文档了。
lookas2001
2021-09-20 18:03:09 +08:00
好项目,赞
vinsoncou
2021-09-22 12:48:16 +08:00
@dndx 按步骤测试下来发现 linux server 上没有端口监听,不知道问题出在哪里。
dndx
2021-09-22 13:00:29 +08:00
@vinsoncou 本身就不是系统的 socket,看不到监听是正常的,只要 tun 设备出现就 ok 了
vinsoncou
2021-09-22 13:40:39 +08:00
@dndx 客户端的 wg 的 endpoint 要改为 Phantun client 的监听 ip,如示例中的 127.0.0.1:1234 么?
dndx
2021-09-22 15:31:35 +08:00
@vinsoncou 是的,客户端连接 `./phantun_client --local` 设置的端口。
wangyucn
2021-09-24 01:29:26 +08:00
来滋瓷一个, 用 tun 实现很不错。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/802949

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX