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。
花了一个周末的时间对 Phantun 又做了更细致的优化,主要是去除了 AsyncMutex
的使用以及 spawn 更多的 Tokio task 以充分利用多核心。
跟 v0.2.x
版本比较,单 UDP 连接性能提升大约 200%,多 UDP 连接性能提升大约 20%。
在 AWS 上开了 2 个 t4g.xlarge
4 核心 ARM 机器做性能测试,与目前最新版的 udp2raw 相比,Phantun 单连接性能大概快 67%,多连接性能快 210%。
最新的 v0.3.2
版本已经发布,包含了这些优化。欢迎大家继续提供使用反馈。
总结:Rust 真香
1
Tink 2021-09-19 18:38:55 +08:00 via Android
牛啊,这个感觉是 wireguard 绝配
|
2
meanmachine 2021-09-19 18:40:51 +08:00
mark... udp2raw 跑单核确实蛋疼了一点
|
3
makelove 2021-09-19 19:18:22 +08:00
太牛了,而且这种真是生活必须品。
我当前用 upd2raw,确实对我用来 fq 的垃圾小鸡性能有点压力。 |
4
codehz 2021-09-19 20:38:45 +08:00 via Android
测试过可以穿透一些常见的 tcp 反代 /负载均衡吗(
|
5
dndx OP @codehz 7 层反代是不行的,7 层反代的 TCP 栈是操作系统的,这种只能用真的 TCP 来实现,就没有了 Fake TCP 的性能优势。
|
6
messense 2021-09-19 21:17:57 +08:00
> 唯一的缺点就是 async Rust 因为 Futures 用的比较多,build 出来的二进制文件也比较大,超过了 6MB
可以用 upx 压缩一下,应该可以小很多;也可以再试试开启 LTO and/or opt-level = "z" |
7
kkocdko 2021-09-20 01:55:33 +08:00 via Android
@messense 个人认为 binary 大小不算太大问题,比起 golang 自带 runtime 那动辄 10M+的体积,已经算很理想了。
lto pgo 之类的楼主这个水平应该会考虑到,optz 就算了,这项目的初衷大概就是性能? optz 开倒车呢 |
8
dndx OP |
9
plko345 2021-09-20 08:24:41 +08:00 via Android 1
楼主,请教你这些网络编程相关的知识有哪些书推荐,或课程
|
12
onlyu 2021-09-20 12:14:00 +08:00
很棒啊,这样的分享是最想看到的
|
13
dndx OP @codehz 又看了一下,Spectrum 宣称可以提供请求日志,感觉像是在 CF Edge 终结了 TCP 连接,如果是这样的话应该不行,最好实际测一下。
|
14
dndx OP @plko345 网络编程入门的话我看的是 UNIX Network Programming 。写 Phantun 主要就是看 RFC793 和 API 文档了。
|
15
lookas2001 2021-09-20 18:03:09 +08:00 via Android
好项目,赞
|
18
vinsoncou 2021-09-22 13:40:39 +08:00
@dndx 客户端的 wg 的 endpoint 要改为 Phantun client 的监听 ip,如示例中的 127.0.0.1:1234 么?
|
20
wangyucn 2021-09-24 01:29:26 +08:00
来滋瓷一个, 用 tun 实现很不错。
|
21
raysonx 2021-09-24 22:03:11 +08:00
挺有意思的的项目,已关注。后面如有用的话希望能够贡献代码。
|
22
NeedforV2 2021-09-27 17:03:15 +08:00
LZ 牛 B,赞一个
|
23
F0nebula 2021-09-30 11:04:12 +08:00 via Android
以性能为代价可以优化二进制文件大小吗 校园网是百兆 但是路由器 ROM 只有 16MB
|
24
dndx OP |
25
hronro 2021-09-30 22:25:58 +08:00
能实现类似 udp2raw 的那种底层掉线上层不掉线吗?
|
26
xiaoun001 2021-11-06 20:04:46 +08:00 3
感谢楼主 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& |
28
dndx OP @vinsoncou 这个是他自定义的 tun 接口地址,一般情况下不需要改动,如果没有搞什么复杂的防火墙规则用文档里默认的配置就行了。
|
30
xiaoun001 2022-01-02 14:39:21 +08:00
@vinsoncou 这个是我的本地服务器 IP 地址啊。就是 phantun_client 客户端所在主机的 IP 地址。不是 tun 地址。
|
31
xiaoun001 2022-01-02 14:46:50 +08:00
|
32
lysS 2022-03-16 18:31:27 +08:00
这种伪装有效吗? NAT 、防火墙、QOS 哪些不是直接看 IP 的 type 字段来区分 UDP/TCP 的吗?
|
34
calcoe 2022-04-13 18:15:00 +08:00
尝试了下,非常棒,跟 udp2raw 相比损耗的速率非常少,如果能支持 ipv6 就更好了。
|
35
dndx OP @calcoe IPv6 支持的话,主要是在等 https://github.com/yaa110/tokio-tun/pull/8 这个 PR merge 。其实如果 netfilter 能加上 NAT64 支持,这个问题也基本可以解决。
|
36
dndx OP @calcoe 最新的 `v0.4.1` 已经加上 TCP 数据包的 IPv6 支持,根据测试性能基本跟 IPv4 模式下没什么区别。
|
37
calcoe 2022-04-17 12:47:56 +08:00
Nice !效率真高,感谢楼主,现在有 ipv6 支持对我来说很实用。
|
38
Damn 2022-07-30 14:47:34 +08:00 via iPhone
@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 变化可以及时更正。 |
39
smallthing 2022-08-17 11:24:19 +08:00
@Damn 你写个脚本不是更简单?连 wg set interface 都省了。ip 变化了就用新的参数重启 phantun
|
40
hanssx 2022-09-21 18:51:35 +08:00
如果能支持 Windows 客户端就更好啦
|
41
tulongtou 2022-12-03 16:00:34 +08:00
好东西,今天用上了
|
42
kcc2home 2023-03-03 21:07:14 +08:00 via iPhone
支持 macos 吗
|
44
sikeer 2023-03-17 21:39:14 +08:00
的确好东西。和 udp2raw 比起来,好象是无状态,不需要握手,不知理解是否正确。
|
45
sikeer 2023-03-17 21:43:01 +08:00
@dndx 感谢,我用了好久的 UDP2RAW, 先不说性能,就说 UDP2RAW 握手,不知道为什么经常要好久才能成功。但 phantun 好象不需要握手协议,非常快,请问一下,phantun 是无状态协议,对吗?
|
46
dndx OP @sikeer 不太清楚,不过 Phantun 跟 udp2raw 这块设计本身就不一样,每个 UDP 会话都有单独的 TCP 连接,udp2raw 是所有会话共享一个 TCP 的。
|
47
kcw294196204 297 天前
vsp 的客户端一直报错 ERROR client > Unable to connect to remote 154.18.*.*:4567 怎么破?一直连不上服务端的 IP 和端口
|
48
kcw294196204 297 天前
连上了 但是无法上网?
服务器日志 INFO fake_tcp > Sent SYN + ACK to client INFO fake_tcp > Connection from 42.51.*.*:12122 established INFO server > New connection: (Fake TCP connection from 192.168.201.2:4567 to 42.51.*.*:12122) INFO server > No traffic seen in the last 180s, closing connection INFO fake_tcp > Fake TCP connection to (Fake TCP connection from 192.168.201.2:4567 to 42.51.*.*:62333) closed 客户端日志 INFO client > Remote address is: 154.18.*.*:4567 INFO client > 1 cores available INFO client > Created TUN device tun0 INFO client > New UDP client from 127.0.0.1:59487 INFO fake_tcp > Sent SYN to server INFO fake_tcp > Connection to 154.18.*.*:4567 established 客户端 ping ping: www.google.com: Temporary failure in name resolution DNS 没问题的 |