V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
dndx
V2EX  ›  分享创造

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

  •  1
     
  •   dndx ·
    dndx · 2021-09-19 17:43:46 +08:00 · 12876 次点击
    这是一个创建于 1161 天前的主题,其中的信息可能已经有所发展或是发生改变。

    GitHub: https://github.com/dndx/phantun

    Crates: https://crates.io/crates/phantun

    Phantun 的初衷跟 @wangyucn 的 udp2raw 很类似,都是为了实现一种简单的用户态 TCP 状态机来对 UDP 流量做伪装。主要的目的是希望能让 UDP 流量看起来像是 TCP,又不希望受到 TCP retransmission 或者 congestion control 的影响。

    目标

    • 高性能
    • 低并且可预测的 MTU overhead (用最小的数据包头部,最大限度的为 UDP payload 保留空间)
    • 多核友好
    • 低资源占用

    实现

    整个项目使用 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

    第 1 条附言  ·  2022-04-10 21:45:56 +08:00

    花了一个周末的时间对 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 真香

    Performance comparison

    第 2 条附言  ·  2022-04-16 19:52:45 +08:00

    v0.4.1 版本发布,该版本添加了完整的 IPv6 支持,目前 Phantun 的 UDP 和 TCP 协议栈已经完全兼容 IPv6。

    README.md 一并更新了 IPv6 需要的额外配置(其实跟 IPv4 基本上差不多的,就是地址不一样)。

    根据测试,IPv6 跟 IPv4 模式下性能没有任何区别。

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

    可以用 upx 压缩一下,应该可以小很多;也可以再试试开启 LTO and/or opt-level = "z"
    kkocdko
        7
    kkocdko  
       2021-09-20 01:55:33 +08:00 via Android
    @messense 个人认为 binary 大小不算太大问题,比起 golang 自带 runtime 那动辄 10M+的体积,已经算很理想了。
    lto pgo 之类的楼主这个水平应该会考虑到,optz 就算了,这项目的初衷大概就是性能? optz 开倒车呢
    dndx
        8
    dndx  
    OP
       2021-09-20 01:58:29 +08:00 via iPhone
    @kkocdko
    @messense 目前 release 用 zip 压缩一下两个 binary 一共也不到 4MB,还算可以接受。
    plko345
        9
    plko345  
       2021-09-20 08:24:41 +08:00 via Android   ❤️ 1
    楼主,请教你这些网络编程相关的知识有哪些书推荐,或课程
    codehz
        10
    codehz  
       2021-09-20 10:12:20 +08:00
    @dndx 我是说 cloudflare spectrum 这样的服务,理论上是 layer 4 的反代
    dndx
        11
    dndx  
    OP
       2021-09-20 11:54:09 +08:00 via iPhone
    @codehz 这种肯定没有问题,MTU 调好就行。
    onlyu
        12
    onlyu  
       2021-09-20 12:14:00 +08:00
    很棒啊,这样的分享是最想看到的
    dndx
        13
    dndx  
    OP
       2021-09-20 12:18:29 +08:00
    @codehz 又看了一下,Spectrum 宣称可以提供请求日志,感觉像是在 CF Edge 终结了 TCP 连接,如果是这样的话应该不行,最好实际测一下。
    dndx
        14
    dndx  
    OP
       2021-09-20 14:59:37 +08:00 via iPhone   ❤️ 2
    @plko345 网络编程入门的话我看的是 UNIX Network Programming 。写 Phantun 主要就是看 RFC793 和 API 文档了。
    lookas2001
        15
    lookas2001  
       2021-09-20 18:03:09 +08:00 via Android
    好项目,赞
    vinsoncou
        16
    vinsoncou  
       2021-09-22 12:48:16 +08:00
    @dndx 按步骤测试下来发现 linux server 上没有端口监听,不知道问题出在哪里。
    dndx
        17
    dndx  
    OP
       2021-09-22 13:00:29 +08:00
    @vinsoncou 本身就不是系统的 socket,看不到监听是正常的,只要 tun 设备出现就 ok 了
    vinsoncou
        18
    vinsoncou  
       2021-09-22 13:40:39 +08:00
    @dndx 客户端的 wg 的 endpoint 要改为 Phantun client 的监听 ip,如示例中的 127.0.0.1:1234 么?
    dndx
        19
    dndx  
    OP
       2021-09-22 15:31:35 +08:00
    @vinsoncou 是的,客户端连接 `./phantun_client --local` 设置的端口。
    wangyucn
        20
    wangyucn  
       2021-09-24 01:29:26 +08:00
    来滋瓷一个, 用 tun 实现很不错。
    raysonx
        21
    raysonx  
       2021-09-24 22:03:11 +08:00
    挺有意思的的项目,已关注。后面如有用的话希望能够贡献代码。
    NeedforV2
        22
    NeedforV2  
       2021-09-27 17:03:15 +08:00
    LZ 牛 B,赞一个
    F0nebula
        23
    F0nebula  
       2021-09-30 11:04:12 +08:00 via Android
    以性能为代价可以优化二进制文件大小吗 校园网是百兆 但是路由器 ROM 只有 16MB
    dndx
        24
    dndx  
    OP
       2021-09-30 15:26:25 +08:00   ❤️ 1
    @F0nebula 可以按照 @messense 的建议尝试修改 Rust 的编译选项。再不济也可以下载到 tmpfs 里来执行(如果 RAM 够的话)。
    hronro
        25
    hronro  
       2021-09-30 22:25:58 +08:00
    能实现类似 udp2raw 的那种底层掉线上层不掉线吗?
    xiaoun001
        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&
    vinsoncou
        27
    vinsoncou  
       2021-12-10 19:01:41 +08:00
    @xiaoun001 请问下,这个 ip ( 172.16.0.222 )是啥?
    dndx
        28
    dndx  
    OP
       2021-12-10 21:13:05 +08:00
    @vinsoncou 这个是他自定义的 tun 接口地址,一般情况下不需要改动,如果没有搞什么复杂的防火墙规则用文档里默认的配置就行了。
    vinsoncou
        29
    vinsoncou  
       2021-12-11 10:02:16 +08:00
    @dndx 我测试了好几次,一直都不成功。不知道啥原有。Server 端是阿里云,服务器上看不到公网 IP 。
    xiaoun001
        30
    xiaoun001  
       2022-01-02 14:39:21 +08:00
    @vinsoncou 这个是我的本地服务器 IP 地址啊。就是 phantun_client 客户端所在主机的 IP 地址。不是 tun 地址。
    xiaoun001
        31
    xiaoun001  
       2022-01-02 14:46:50 +08:00
    @vinsoncou 用 TCPDUMP 抓包,一段一段找原因。tun 就是本地用途,需要用防火墙 NAT 规则将其映射出去。

    @dndx 谢谢大神,一直追到最新版本了。两个月了,好稳定,效能高!谢谢。现在就是 phantun 主,udp2raw 备(配置好未用的状态),我不会写程序,但我 fork 了一份,期待这个项目一直存在。
    lysS
        32
    lysS  
       2022-03-16 18:31:27 +08:00
    这种伪装有效吗? NAT 、防火墙、QOS 哪些不是直接看 IP 的 type 字段来区分 UDP/TCP 的吗?
    dndx
        33
    dndx  
    OP
       2022-03-20 11:40:11 +08:00
    @lysS 这种伪装下四层头已经不是 UDP 了,所以防火墙会将其识别成 TCP 流量。
    calcoe
        34
    calcoe  
       2022-04-13 18:15:00 +08:00
    尝试了下,非常棒,跟 udp2raw 相比损耗的速率非常少,如果能支持 ipv6 就更好了。
    dndx
        35
    dndx  
    OP
       2022-04-13 18:20:20 +08:00
    @calcoe IPv6 支持的话,主要是在等 https://github.com/yaa110/tokio-tun/pull/8 这个 PR merge 。其实如果 netfilter 能加上 NAT64 支持,这个问题也基本可以解决。
    dndx
        36
    dndx  
    OP
       2022-04-16 19:53:30 +08:00
    @calcoe 最新的 `v0.4.1` 已经加上 TCP 数据包的 IPv6 支持,根据测试性能基本跟 IPv4 模式下没什么区别。
    calcoe
        37
    calcoe  
       2022-04-17 12:47:56 +08:00
    Nice !效率真高,感谢楼主,现在有 ipv6 支持对我来说很实用。
    Damn
        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 变化可以及时更正。
    smallthing
        39
    smallthing  
       2022-08-17 11:24:19 +08:00
    @Damn 你写个脚本不是更简单?连 wg set interface 都省了。ip 变化了就用新的参数重启 phantun
    hanssx
        40
    hanssx  
       2022-09-21 18:51:35 +08:00
    如果能支持 Windows 客户端就更好啦
    tulongtou
        41
    tulongtou  
       2022-12-03 16:00:34 +08:00
    好东西,今天用上了
    kcc2home
        42
    kcc2home  
       2023-03-03 21:07:14 +08:00 via iPhone
    支持 macos 吗
    kcc2home
        43
    kcc2home  
       2023-03-03 21:10:14 +08:00 via iPhone
    @kcc2home 另外,tcp 阻断可以规避的吗? 佬
    sikeer
        44
    sikeer  
       2023-03-17 21:39:14 +08:00
    的确好东西。和 udp2raw 比起来,好象是无状态,不需要握手,不知理解是否正确。
    sikeer
        45
    sikeer  
       2023-03-17 21:43:01 +08:00
    @dndx 感谢,我用了好久的 UDP2RAW, 先不说性能,就说 UDP2RAW 握手,不知道为什么经常要好久才能成功。但 phantun 好象不需要握手协议,非常快,请问一下,phantun 是无状态协议,对吗?
    dndx
        46
    dndx  
    OP
       2023-03-19 00:42:55 +08:00
    @sikeer 不太清楚,不过 Phantun 跟 udp2raw 这块设计本身就不一样,每个 UDP 会话都有单独的 TCP 连接,udp2raw 是所有会话共享一个 TCP 的。
    kcw294196204
        47
    kcw294196204  
       306 天前
    vsp 的客户端一直报错 ERROR client > Unable to connect to remote 154.18.*.*:4567 怎么破?一直连不上服务端的 IP 和端口
    kcw294196204
        48
    kcw294196204  
       306 天前
    连上了 但是无法上网?
    服务器日志
    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 没问题的
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   924 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 22:12 · PVG 06:12 · LAX 14:12 · JFK 17:12
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.