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

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

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

    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 条附言  ·  353 天前

    花了一个周末的时间对 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 条附言  ·  347 天前

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

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

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

    46 条回复    2023-03-19 00:42:55 +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   ❤️ 2
    感谢楼主 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  
       350 天前
    尝试了下,非常棒,跟 udp2raw 相比损耗的速率非常少,如果能支持 ipv6 就更好了。
    dndx
        35
    dndx  
    OP
       350 天前
    @calcoe IPv6 支持的话,主要是在等 https://github.com/yaa110/tokio-tun/pull/8 这个 PR merge 。其实如果 netfilter 能加上 NAT64 支持,这个问题也基本可以解决。
    dndx
        36
    dndx  
    OP
       347 天前
    @calcoe 最新的 `v0.4.1` 已经加上 TCP 数据包的 IPv6 支持,根据测试性能基本跟 IPv4 模式下没什么区别。
    calcoe
        37
    calcoe  
       347 天前
    Nice !效率真高,感谢楼主,现在有 ipv6 支持对我来说很实用。
    Damn
        38
    Damn  
       242 天前 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  
       225 天前
    @Damn 你写个脚本不是更简单?连 wg set interface 都省了。ip 变化了就用新的参数重启 phantun
    hanssx
        40
    hanssx  
       189 天前
    如果能支持 Windows 客户端就更好啦
    tulongtou
        41
    tulongtou  
       116 天前
    好东西,今天用上了
    kcc2home
        42
    kcc2home  
       26 天前 via iPhone
    支持 macos 吗
    kcc2home
        43
    kcc2home  
       26 天前 via iPhone
    @kcc2home 另外,tcp 阻断可以规避的吗? 佬
    sikeer
        44
    sikeer  
       12 天前
    的确好东西。和 udp2raw 比起来,好象是无状态,不需要握手,不知理解是否正确。
    sikeer
        45
    sikeer  
       12 天前
    @dndx 感谢,我用了好久的 UDP2RAW, 先不说性能,就说 UDP2RAW 握手,不知道为什么经常要好久才能成功。但 phantun 好象不需要握手协议,非常快,请问一下,phantun 是无状态协议,对吗?
    dndx
        46
    dndx  
    OP
       11 天前
    @sikeer 不太清楚,不过 Phantun 跟 udp2raw 这块设计本身就不一样,每个 UDP 会话都有单独的 TCP 连接,udp2raw 是所有会话共享一个 TCP 的。
    关于   ·   帮助文档   ·   博客   ·   nftychat   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   实用小工具   ·   4077 人在线   最高记录 5556   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 56ms · UTC 05:34 · PVG 13:34 · LAX 22:34 · JFK 01:34
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.