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

13149 次点击
所在节点    分享创造
48 条回复
raysonx
2021-09-24 22:03:11 +08:00
挺有意思的的项目,已关注。后面如有用的话希望能够贡献代码。
NeedforV2
2021-09-27 17:03:15 +08:00
LZ 牛 B,赞一个
F0nebula
2021-09-30 11:04:12 +08:00
以性能为代价可以优化二进制文件大小吗 校园网是百兆 但是路由器 ROM 只有 16MB
dndx
2021-09-30 15:26:25 +08:00
@F0nebula 可以按照 @messense 的建议尝试修改 Rust 的编译选项。再不济也可以下载到 tmpfs 里来执行(如果 RAM 够的话)。
hronro
2021-09-30 22:25:58 +08:00
能实现类似 udp2raw 的那种底层掉线上层不掉线吗?
xiaoun001
2021-11-06 20:04:46 +08:00
感谢楼主 github 中不厌其烦的解释,前后折腾很久,终于用上了。话说真的真稳定,也非常不错。之前一直不通,楼主指导用 TCPDUMP 抓包,终于找到原因了。
注意两个坑:1 、服务端、客户端地址本地有效,最直观用途就是用来做 NAT 转发的。是无法 ping 通对端的。
2 、Linux tun 设备是成对出现的,因此系统 tun0 地址并不是实际发包的地址(用 TCPDUMP 抓包才看到)。
tun0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST> mtu 1500
inet 192.168.200.1 netmask 255.255.255.255 destination 192.168.200.2
此例,发包的地址是 192.168.200.2 ,也就是 snat 目标地址,而不是 200.1 。
3 、phantun 在 bash 下运行的很好,在 sh 下面运行报错。因此,注意 shell 解释器选择。之前在我的客户机 rc.local 就这句不行,服务器就可以。不经意发现客户机的 rc.local 靠头是
#!/bin/sh 改成 bash 正常。测试,的确在 sh 下运行报错。
4\ 楼主说明书客户端 那条比较宽泛 iptables 命令,对我的环境 多出口无效(策略路由环境),其实只要明白 iptables 是为了解决本地 tun0 接口出去,回来,就是让通过 tun0 口 192.168.200.2 能够上网,问题迎刃而解。若您有同样问题,请参考我下面的配置。

用上了,很好。楼主的 phantun ,速率接近 UDP 速率,差不多是 4/5 左右. 王宇大神的 udp2raw 高峰期差不多 2/3 。两个产品都很好,所以我同时用上了。我自己不会写网络程序,感谢两位大神。感谢楼主 isuse 不厌其烦解释。

phantun 配置
服务端:
/sbin/iptables -t nat -A POSTROUTING -s 192.168.201.1/24 -j SNAT –to-source 服务器 IP
/usr/local/bin/phantun_server -l 3389 -r 127.0.0.1:3389 2>1&
/sbin/iptables -t nat -A PREROUTING -p tcp -i eth0 –dport 3389 -j DNAT –to-destination 192.168.201.2


客户端
/sbin/iptables/iptables -t nat -A POSTROUTING -s 192.168.200.1/24 -j SNAT –to-source 172.16.0.222
/usr/local/bin/phantun_client -l 127.0.0.1:3389 -r 服务器 IP:3389 2>1&
vinsoncou
2021-12-10 19:01:41 +08:00
@xiaoun001 请问下,这个 ip ( 172.16.0.222 )是啥?
dndx
2021-12-10 21:13:05 +08:00
@vinsoncou 这个是他自定义的 tun 接口地址,一般情况下不需要改动,如果没有搞什么复杂的防火墙规则用文档里默认的配置就行了。
vinsoncou
2021-12-11 10:02:16 +08:00
@dndx 我测试了好几次,一直都不成功。不知道啥原有。Server 端是阿里云,服务器上看不到公网 IP 。
xiaoun001
2022-01-02 14:39:21 +08:00
@vinsoncou 这个是我的本地服务器 IP 地址啊。就是 phantun_client 客户端所在主机的 IP 地址。不是 tun 地址。
xiaoun001
2022-01-02 14:46:50 +08:00
@vinsoncou 用 TCPDUMP 抓包,一段一段找原因。tun 就是本地用途,需要用防火墙 NAT 规则将其映射出去。

@dndx 谢谢大神,一直追到最新版本了。两个月了,好稳定,效能高!谢谢。现在就是 phantun 主,udp2raw 备(配置好未用的状态),我不会写程序,但我 fork 了一份,期待这个项目一直存在。
lysS
2022-03-16 18:31:27 +08:00
这种伪装有效吗? NAT 、防火墙、QOS 哪些不是直接看 IP 的 type 字段来区分 UDP/TCP 的吗?
dndx
2022-03-20 11:40:11 +08:00
@lysS 这种伪装下四层头已经不是 UDP 了,所以防火墙会将其识别成 TCP 流量。
calcoe
2022-04-13 18:15:00 +08:00
尝试了下,非常棒,跟 udp2raw 相比损耗的速率非常少,如果能支持 ipv6 就更好了。
dndx
2022-04-13 18:20:20 +08:00
@calcoe IPv6 支持的话,主要是在等 https://github.com/yaa110/tokio-tun/pull/8 这个 PR merge 。其实如果 netfilter 能加上 NAT64 支持,这个问题也基本可以解决。
dndx
2022-04-16 19:53:30 +08:00
@calcoe 最新的 `v0.4.1` 已经加上 TCP 数据包的 IPv6 支持,根据测试性能基本跟 IPv4 模式下没什么区别。
calcoe
2022-04-17 12:47:56 +08:00
Nice !效率真高,感谢楼主,现在有 ipv6 支持对我来说很实用。
Damn
2022-07-30 14:47:34 +08:00
@dndx 楼主可以加上漫游功能么?
动态 ip 配 wireguard ,可以每几分钟解析一次对方的 a 记录,然后通过 wg show endpoint 与当前使用的 ip 比较,如果变化了则通过 wg set interface pubkey endpoint newip:port 将连接指向最新的 ip 。

wireguard 前面套一层 phantun ,如果对方 ip 变化了 phantun 就瞎了。希望可以像 wireguard 那样,有个 daemon 或者自己用 shell 写个 daemon ,监测到对方 ip 变化可以及时更正。
smallthing
2022-08-17 11:24:19 +08:00
@Damn 你写个脚本不是更简单?连 wg set interface 都省了。ip 变化了就用新的参数重启 phantun
hanssx
2022-09-21 18:51:35 +08:00
如果能支持 Windows 客户端就更好啦

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

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

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

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

© 2021 V2EX