Total Pageviews

Monday, 10 October 2022

使用Phantun,把WireGuard的UDP流量伪装成TCP流量

 

WireGuard 作为一个更先进、更现代的 VPN 协议,比起传统的 IPSec、OpenVPN 等实现,效率更高,配置更简单,并且已经合并入 Linux 内核,使用起来更加方便,简直就是 VPN 中的战斗机。越来越多的高人利用 WireGuard 实现很多奇奇怪怪的需求。例如国内与国外机器通过 WireGuard 打通隧道,变成伪 IPLC 专线;或者打通本地与 Kubernetes 集群的网络。

但是 WireGuard 在国内网络环境下会遇到一个致命的问题:UDP 封锁/限速。虽然通过 WireGuard 可以在隧道内传输任何基于 IP 的协议(TCP、UDP、ICMP、SCTP、IPIP、GRE 等),但 WireGuard 隧道本身是通过 UDP 协议进行通信的,而国内运营商根本没有能力和精力根据 TCP 和 UDP 的不同去深度定制不同的 QoS 策略,几乎全部采取一刀切的手段:对 UDP 进行限速甚至封锁

鲁迅先生说过:羊毛出在羊身上!突破口还是在运营商身上:虽然对 UDP 不友好,但却无力深度检测 TCP 连接的真实性

这就好办了,既然你对 TCP 连接睁一只眼闭一只眼,那我将 UDP 连接伪装成 TCP 连接不就蒙混过关了。目前支持将 UDP 流量伪装成 TCP 流量的主流工具是 udp2raw,相信很多小伙伴对这个工具都轻车熟路了,但是很遗憾,今天的主角不是它,而是另一款比它更强大的新工具: Phantun

Phantun介绍

Phantun 整个项目完全使用 Rust 实现,性能吊打 udp2raw。它的初衷和 udp2raw 类似,都是为了实现一种简单的用户态 TCP 状态机来对 UDP 流量做伪装。主要的目的是希望能让 UDP 流量看起来像是 TCP,又不希望受到 TCP retransmission 或者 congestion control 的影响。

需要申明的是,Phantun 的目标不是为了替代 udp2raw,从一开始 Phantun 就希望设计足够的简单高效,所以 udp2raw 支持的 ICMP 隧道,加密,防止重放等等功能 Phantun 都选择不实现。

Phantun 假设 UDP 协议本身已经解决了这些问题,所以整个转发过程就是简单的明文换头加上一些必要的 TCP 状态控制信息。对于我日常使用的 WireGuard 来说,Phantun 这种设计是足够安全的,因为 WireGuard 的协议已经更好的实现了这些安全功能。

Phantun 使用 TUN 接口来收发 3 层数据包,udp2raw 使用 Raw Socket + BFP 过滤器。个人感觉基于 TUN 的实现要稍微的优雅一点,而且跨平台移植也要更容易。

Phantun 的 TCP 连接是按需创建的,只启动 Client 不会主动去连接服务器,需要第一个数据包到达了后才会按需创建。每个 UDP 流都有自己独立的 TCP 连接。这一点跟 udp2raw 很不一样,udp2raw 所有的 UDP 连接共用一个 TCP 连接。这样做的坏处就是 udp2raw 需要额外的头部信息来区分连接,更加增加了头部的开销。跟纯 UDP 比较,Phantun 每个数据包的额外头部开销是 12 byte,udp2raw 根据我的测试达到了 44 bytes 。

下面是 Phantun 与 udp2raw 的详细对比:


Phantun udp2raw
UDP over FakeTCP 混淆
UDP over ICMP 混淆
UDP over UDP 混淆
多线程
吞吐量 Better Good
三层转发模式 TUN interface Raw sockets + BPF
隧道 MTU 开销 12 bytes 44 bytes
每个 UDP 流都有自己独立的 TCP 连接 Client/Server Server only
防止重放,加密
IPv6

Phantun工作原理


 

Phantun 分为服务端和客户端,服务端会监听一个端口,比如 4567(通过 --local 参数指定),并将 UDP 数据包转发到 UDP 服务(这里指的就是服务端 WireGuard 的监听端口和地址,通过 --remote 参数指定)。

客户端也会监听一个端口,比如 127.0.0.1:4567(通过 --local 参数指定),并且通过 --remote 参数与服务端(比如 10.0.0.1:4567)建立连接。

客户端与服务端都会创建一个 TUN 网卡,客户端 TUN 网卡默认分配的 IPv4/IPv6 地址分别是 192.168.200.2fcc8::2,服务端 TUN 网卡默认分配的 IPv4/IPv6 地址分别是 192.168.201.2fcc9::2

客户端与服务端都需要开启 IP forwarding,并且需要创建相应的 NAT 规则。客户端在流量离开物理网卡之前,需要对 IP 192.168.200.2 进行 SNAT;服务端在流量进入网卡之前,需要将 IP DNAT 为 192.168.201.2

Phantun 配置步骤

接下来我会通过一个示例来演示如何使用 Phantun 将 WireGuard 的 UDP 流量伪装成 TCP。我们需要在服务端和客户端分别安装 phantun,可以到 release 页面下载,推荐下载静态编译版本 phantun_x86_64-unknown-linux-musl.zip

服务端

假设服务端的公网 IP 地址是 121.36.134.95,WireGuard 监听端口是 51822。首先修改配置文件 /etc/wireguard/wg0.conf,在 [Interface] 中添加以下配置:

MTU = 1300
PreUp = iptables -t nat -A PREROUTING -p tcp -i eth0 --dport 4567 -j DNAT --to-destination 192.168.201.2
PreUp = RUST_LOG=info phantun_server --local 4567 --remote 127.0.0.1:51822 &> /var/log/phantun_server.log &
PostDown = iptables -t nat -D PREROUTING -p tcp -i eth0 --dport 4567 -j DNAT --to-destination 192.168.201.2
PostDown = killall phantun_server || true

你需要将 eth0 替换为你服务端的物理网卡名。MTU 值先不管,后面再告诉大家调试方法。

PreUp = iptables -t nat -A PREROUTING -p tcp -i eth0 --dport 4567 -j DNAT --to-destination 192.168.201.2

这条 iptables 规则表示将 4567 端口的入站流量 DNAT 为 TUN 网卡的 IP 地址。

PreUp = RUST_LOG=info phantun_server --local 4567 --remote 127.0.0.1:51822 &> /var/log/phantun_server.log &

这里会启动 phantun_server,监听在 4567 端口,并将 UDP 数据包转发到 WireGuard。

服务端完整的 WireGuard 配置:

# local settings for Endpoint B
[Interface]
PrivateKey = QH1BJzIZcGo89ZTykxls4i2DKgvByUkHIBy3BES2gX8= 
Address = 10.0.0.2/32
ListenPort = 51822
MTU = 1300
PreUp = iptables -t nat -A PREROUTING -p tcp -i eth0 --dport 4567 -j DNAT --to-destination 192.168.201.2
PreUp = RUST_LOG=info phantun_server --local 4567 --remote 127.0.0.1:51822 &> /var/log/phantun_server.log &
PostDown = iptables -t nat -D PREROUTING -p tcp -i eth0 --dport 4567 -j DNAT --to-destination 192.168.201.2
PostDown = killall phantun_server || true

# remote settings for Endpoint A
[Peer]
PublicKey = wXtD/VrRo92JHc66q4Ypmnd4JpMk7b1Sb0AcT+pJfwY= 
AllowedIPs = 10.0.0.1/32

最后重启 WireGuard 即可:

$ systemctl restart wg-quick@wg0

客户端

假设客户端的 WireGuard 监听端口是 51821。首先修改配置文件 /etc/wireguard/wg0.conf,在 [Interface] 中添加以下配置:

MTU = 1300
PreUp = iptables -t nat -A POSTROUTING -o eth0 -s 192.168.200.2 -j MASQUERADE
PreUp = RUST_LOG=info phantun_client --local 127.0.0.1:4567 --remote 121.36.134.95:4567 &> /var/log/phantun_client.log &
PostDown = iptables -t nat -D POSTROUTING -o eth0 -s 192.168.200.2 -j MASQUERADE
PostDown = killall phantun_client || true

你需要将 eth0 替换为你服务端的物理网卡名。

PreUp = iptables -t nat -A POSTROUTING -o eth0 -s 192.168.200.2 -j MASQUERADE

这条 iptables 规则表示对来自 192.168.200.2(TUN 网卡) 的出站流量进行 MASQUERADE。

PreUp = RUST_LOG=info phantun_client --local 127.0.0.1:4567 --remote 121.36.134.95:4567 &> /var/log/phantun_client.log &

这里会启动 phantun_client,监听在 4567 端口,并与服务端建立连接,将伪装的 TCP 数据包传送给服务端。

除此之外还需要修改 WireGuard peer 的 Endpoint,将其修改为 127.0.0.1:4567。

Endpoint = 127.0.0.1:4567

客户端完整的 WireGuard 配置:

# local settings for Endpoint A
[Interface]
PrivateKey = 0Pyz3cIg2gRt+KxZ0Vm1PvSIU+0FGufPIzv92jTyGWk=
Address = 10.0.0.1/32
ListenPort = 51821
MTU = 1300
PreUp = iptables -t nat -A POSTROUTING -o eth0 -s 192.168.200.2 -j MASQUERADE
PreUp = RUST_LOG=info phantun_client --local 127.0.0.1:4567 --remote 121.36.134.95:4567 &> /var/log/phantun_client.log &
PostDown = iptables -t nat -D POSTROUTING -o eth0 -s 192.168.200.2 -j MASQUERADE
PostDown = killall phantun_client || true

# remote settings for Endpoint B
[Peer]
PublicKey = m40NDb5Cqtb78b1DVwY1+kxbG2yEcRhxlrLm/DlPpz8=
Endpoint = 127.0.0.1:4567
AllowedIPs = 10.0.0.2/32
PersistentKeepalive = 25

最后重启 WireGuard 即可:

$ systemctl restart wg-quick@wg0

查看 phantun_client 的日志:

$ tail -f /var/log/phantun_client.log
 INFO  client > Remote address is: 121.36.134.95:4567
 INFO  client > 1 cores available
 INFO  client > Created TUN device tun0
 INFO  client > New UDP client from 127.0.0.1:51821
 INFO  fake_tcp > Sent SYN to server
 INFO  fake_tcp > Connection to 121.36.134.95:4567 established

查看 wg0 接口:

$ wg show wg0
interface: wg0
  public key: wXtD/VrRo92JHc66q4Ypmnd4JpMk7b1Sb0AcT+pJfwY=
  private key: (hidden)
  listening port: 51821

peer: m40NDb5Cqtb78b1DVwY1+kxbG2yEcRhxlrLm/DlPpz8=
  endpoint: 127.0.0.1:4567
  allowed ips: 10.0.0.2/32
  latest handshake: 1 minute, 57 seconds ago
  transfer: 184 B received, 648 B sent
  persistent keepalive: every 25 seconds

测试连通性:

$ ping 10.0.0.2 -c 3
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=13.7 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=14.4 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=15.0 ms

--- 10.0.0.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2005ms
rtt min/avg/max/mdev = 13.718/14.373/15.047/0.542 ms

客户端(多服务端)

如果客户端想和多个服务端建立连接,则新增的服务端配置如下:

PreUp = RUST_LOG=info phantun_client --local 127.0.0.1:4568 --remote xxxx:4567 --tun-local=192.168.202.1 --tun-peer=192.168.202.2 &> /var/log/phantun_client.log &
PostDown = iptables -t nat -D POSTROUTING -o eth0 -s 192.168.202.2 -j MASQUERADE

本地监听端口需要选择一个与之前不同的端口,同理,TUN 网卡的地址也需要修改。最终的配置如下:

# local settings for Endpoint A
[Interface]
PrivateKey = 0Pyz3cIg2gRt+KxZ0Vm1PvSIU+0FGufPIzv92jTyGWk=
Address = 10.0.0.1/32
ListenPort = 51821
MTU = 1300
PreUp = iptables -t nat -A POSTROUTING -o eth0 -s 192.168.200.2 -j MASQUERADE
PreUp = RUST_LOG=info phantun_client --local 127.0.0.1:4567 --remote 121.36.134.95:4567 &> /var/log/phantun_client.log &
PreUp = RUST_LOG=info phantun_client --local 127.0.0.1:4568 --remote xxxx:4567 --tun-local=192.168.202.1 --tun-peer=192.168.202.2 &> /var/log/phantun_client.log &
PostDown = iptables -t nat -D POSTROUTING -o eth0 -s 192.168.200.2 -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -o eth0 -s 192.168.202.2 -j MASQUERADE
PostDown = killall phantun_client || true

# remote settings for Endpoint B
[Peer]
PublicKey = m40NDb5Cqtb78b1DVwY1+kxbG2yEcRhxlrLm/DlPpz8=
Endpoint = 127.0.0.1:4567
AllowedIPs = 10.0.0.2/32
PersistentKeepalive = 25

MTU 调优

如果你使用 ping 或者 dig 等工具(小数据包)测试 WireGuard 隧道能够正常工作,但浏览器或者远程桌面(大数据包)却无法正常访问,很有可能是 MTU 的问题,你需要将 MTU 的值调小一点。

Phantun 官方建议将 MTU 的值设为 1428(假设物理网卡的 MTU 是 1500),但经我测试是有问题的。建议直接将 MTU 设置为最低值 1280,然后渐渐增加,直到无法正常工作为止,此时你的 MTU 就是最佳值。

你学废了吗?

项目地址:https://github.com/dndx/phantun 

----------------------------------------------------------

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

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

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

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

 -----------------------------------

Transforms UDP stream into (fake) TCP streams that can go through Layer 3 & Layer 4 (NAPT) firewalls/NATs.  

Phantun

A lightweight and fast UDP to TCP obfuscator.

GitHub Workflow Status docs.rs

Table of Contents

Latest release

v0.6.0

Overview

Phantun is a project that obfuscated UDP packets into TCP connections. It aims to achieve maximum performance with minimum processing and encapsulation overhead.

It is commonly used in environments where UDP is blocked/throttled but TCP is allowed through.

Phantun simply converts a stream of UDP packets into obfuscated TCP stream packets. The TCP stack used by Phantun is designed to pass through most L3/L4 stateful/stateless firewalls/NAT devices. It will not be able to pass through L7 proxies. However, the advantage of this approach is that none of the common UDP over TCP performance killer such as retransmissions and flow control will occur. The underlying UDP properties such as out-of-order delivery are fully preserved even if the connection ends up looking like a TCP connection from the perspective of firewalls/NAT devices.

Phantun means Phantom TUN, as it is an obfuscator for UDP traffic that does just enough work to make it pass through stateful firewall/NATs as TCP packets.

Phantun is written in 100% safe Rust. It has been optimized extensively to scale well on multi-core systems and has no issue saturating all available CPU resources on a fast connection. See the Performance section for benchmarking results.

Phantun benchmark results Traffic flow diagram

Usage

For the example below, it is assumed that Phantun Server listens for incoming Phantun Client connections at port 4567 (the --local option for server), and it forwards UDP packets to UDP server at 127.0.0.1:1234 (the --remote option for server).

It is also assumed that Phantun Client listens for incoming UDP packets at 127.0.0.1:1234 (the --local option for client) and connects to Phantun Server at 10.0.0.1:4567 (the --remote option for client).

Phantun creates TUN interface for both the Client and Server. For Client, Phantun assigns itself the IP address 192.168.200.2 and fcc8::2 by default. For Server, it assigns 192.168.201.2 and fcc9::2 by default. Therefore, your Kernel must have IPv4/IPv6 forwarding enabled and setup appropriate iptables/nftables rules for NAT between your physical NIC address and Phantun's Tun interface address.

You may customize the name of Tun interface created by Phantun and the assigned addresses. Please run the executable with -h options to see how to change them.

Another way to help understand this network topology (please see the diagram above for an illustration of this topology):

Phantun Client is like a machine with private IP address (192.168.200.2/fcc8::2) behind a router. In order for it to reach the Internet, you will need to SNAT the private IP address before it's traffic leaves the NIC.

Phantun Server is like a server with private IP address (192.168.201.2/fcc9::2) behind a router. In order to access it from the Internet, you need to DNAT it's listening port on the router and change the destination IP address to where the server is listening for incoming connections.

In those cases, the machine/iptables running Phantun acts as the "router" that allows Phantun to communicate with outside using it's private IP addresses.

As of Phantun v0.4.1, IPv6 is fully supported for both TCP and UDP sides. To specify an IPv6 address, use the following format: [::1]:1234 with the command line options. Resolving AAAA record is also supported. Please run the program with -h to see detailed options on how to control the IPv6 behavior.

Back to TOC

1. Enable Kernel IP forwarding

Edit /etc/sysctl.conf, add net.ipv4.ip_forward=1 and run sudo sysctl -p /etc/sysctl.conf.

IPv6 specific config

Back to TOC

2. Add required firewall rules

Client

Client simply need SNAT enabled on the physical interface to translate Phantun's address into one that can be used on the physical network. This can be done simply with masquerade.

Note: change eth0 to whatever actual physical interface name is

Back to TOC

Using nftables

table inet nat {
    chain postrouting {
        type nat hook postrouting priority srcnat; policy accept;
        iifname tun0 oif eth0 masquerade
    }
}

Note: The above rule uses inet as the table family type, so it is compatible with both IPv4 and IPv6 usage.

Back to TOC

Using iptables

iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
ip6tables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

Back to TOC

Server

Server needs to DNAT the TCP listening port to Phantun's TUN interface address.

Note: change eth0 to whatever actual physical interface name is and 4567 to actual TCP port number used by Phantun server

Back to TOC

Using nftables

table inet nat {
    chain prerouting {
        type nat hook prerouting priority dstnat; policy accept;
        iif eth0 tcp dport 4567 dnat ip to 192.168.201.2
        iif eth0 tcp dport 4567 dnat ip6 to fcc9::2
    }
}

Back to TOC

Using iptables

iptables -t nat -A PREROUTING -p tcp -i eth0 --dport 4567 -j DNAT --to-destination 192.168.201.2
ip6tables -t nat -A PREROUTING -p tcp -i eth0 --dport 4567 -j DNAT --to-destination fcc9::2

Back to TOC

3. Run Phantun binaries as non-root (Optional)

It is ill-advised to run network facing applications as root user. Phantun can be run fully as non-root user with the cap_net_admin capability.

sudo setcap cap_net_admin=+pe phantun_server
sudo setcap cap_net_admin=+pe phantun_client

Back to TOC

4. Start Phantun daemon

Note: Run Phantun executable with -h option to see full detailed options.

Back to TOC

Server

Note: 4567 is the TCP port Phantun should listen on and must corresponds to the DNAT rule specified above. 127.0.0.1:1234 is the UDP Server to connect to for new connections.

RUST_LOG=info /usr/local/bin/phantun_server --local 4567 --remote 127.0.0.1:1234

Or use host name with --remote:

RUST_LOG=info /usr/local/bin/phantun_server --local 4567 --remote example.com:1234

Note: Server by default assigns both IPv4 and IPv6 private address to the Tun interface. If you do not wish to use IPv6, you can simply skip creating the IPv6 DNAT rule above and the presence of IPv6 address on the Tun interface should have no side effect to the server.

Back to TOC

Client

Note: 127.0.0.1:1234 is the UDP address and port Phantun should listen on. 10.0.0.1:4567 is the Phantun Server to connect.

RUST_LOG=info /usr/local/bin/phantun_client --local 127.0.0.1:1234 --remote 10.0.0.1:4567

Or use host name with --remote:

RUST_LOG=info /usr/local/bin/phantun_client --local 127.0.0.1:1234 --remote example.com:4567
IPv6 specific config

Back to TOC

MTU overhead

Phantun aims to keep tunneling overhead to the minimum. The overhead compared to a plain UDP packet is the following (using IPv4 below as an example):

Standard UDP packet: 20 byte IP header + 8 byte UDP header = 28 bytes

Obfuscated packet: 20 byte IP header + 20 byte TCP header = 40 bytes

Note that Phantun does not add any additional header other than IP and TCP headers in order to pass through stateful packet inspection!

Phantun's additional overhead: 12 bytes. I other words, when using Phantun, the usable payload for UDP packet is reduced by 12 bytes. This is the minimum overhead possible when doing such kind of obfuscation.

Packet header diagram

Back to TOC

MTU calculation for WireGuard

For people who use Phantun to tunnel WireGuard® UDP packets, here are some guidelines on figuring out the correct MTU to use for your WireGuard interface.

WireGuard MTU = Interface MTU - IPv4 header (20 bytes) - TCP header (20 bytes) - WireGuard overhead (32 bytes)

or

WireGuard MTU = Interface MTU - IPv6 header (40 bytes) - TCP header (20 bytes) - WireGuard overhead (32 bytes)

For example, for a Ethernet interface with 1500 bytes MTU, the WireGuard interface MTU should be set as:

IPv4: 1500 - 20 - 20 - 32 = 1428 bytes IPv6: 1500 - 40 - 20 - 32 = 1408 bytes

The resulted Phantun TCP data packet will be 1500 bytes which does not exceed the interface MTU of 1500. Please note it is strongly recommended to use the same interface MTU for both ends of a WireGuard tunnel, or unexpected packet loss may occur and these issues are generally very hard to troubleshoot.

Back to TOC

Version compatibility

While the TCP stack is fairly stable, the general expectation is that you should run same minor versions of Server/Client of Phantun on both ends to ensure maximum compatibility.

Back to TOC

Documentations

For users who wish to use fake-tcp library inside their own project, refer to the documentations for the library at: https://docs.rs/fake-tcp.

Back to TOC

Performance

Performance was tested on 2 AWS t4g.xlarge instances with 4 vCPUs and 5 Gb/s NIC over LAN. nftables was used to redirect UDP stream of iperf3 to go through the Phantun/udp2raw tunnel between two test instances and MTU has been tuned to avoid fragmentation.

Phantun v0.3.2 and udp2raw_arm_asm_aes 20200818.0 was used. These were the latest release of both projects as of Apr 2022.

Test command: iperf3 -c <IP> -p <PORT> -R -u -l 1400 -b 1000m -t 30 -P 5

Mode Send Speed Receive Speed Overall CPU Usage
Direct (1 stream) 3.00 Gbits/sec 2.37 Gbits/sec 25% (1 core at 100%)
Phantun (1 stream) 1.30 Gbits/sec 1.20 Gbits/sec 60% (1 core at 100%, 3 cores at 50%)
udp2raw (cipher-mode=none auth-mode=none disable-anti-replay) (1 stream) 1.30 Gbits/sec 715 Mbits/sec 40% (1 core at 100%, 1 core at 50%, 2 cores idling)
Direct connection (5 streams) 5.00 Gbits/sec 3.64 Gbits/sec 25% (1 core at 100%)
Phantun (5 streams) 5.00 Gbits/sec 2.38 Gbits/sec 95% (all cores utilized)
udp2raw (cipher-mode=none auth-mode=none disable-anti-replay) (5 streams) 5.00 Gbits/sec 770 Mbits/sec 50% (2 cores at 100%)

Writeup on some of the techniques used in Phantun to achieve this performance result: Writing Highly Efficient UDP Server in Rust.

Back to TOC

Future plans

  • Load balancing a single UDP stream into multiple TCP streams
  • Integration tests
  • Auto insertion/removal of required firewall rules

Back to TOC

Compariation to udp2raw

udp2raw is another popular project by @wangyu- that is very similar to what Phantun can do. In fact I took inspirations of Phantun from udp2raw. The biggest reason for developing Phantun is because of lack of performance when running udp2raw (especially on multi-core systems such as Raspberry Pi). However, the goal is never to be as feature complete as udp2raw and only support the most common use cases. Most notably, UDP over ICMP and UDP over UDP mode are not supported and there is no anti-replay nor encryption support. The benefit of this is much better performance overall and less MTU overhead because lack of additional headers inside the TCP payload.

Here is a quick overview of comparison between those two to help you choose:


Phantun udp2raw
UDP over FakeTCP obfuscation
UDP over ICMP obfuscation
UDP over UDP obfuscation
Multi-threaded
Throughput Better Good
Layer 3 mode TUN interface Raw sockets + BPF
Tunneling MTU overhead 12 bytes 44 bytes
Seprate TCP connections for each UDP connection Client/Server Server only
Anti-replay, encryption
IPv6

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

--------------------------------------

相关帖子:

https://briteming.blogspot.com/2022/01/udp2rawispqoswireguard.html

 

 

 

 

 

 

 

 

 

 

 

 

 

No comments:

Post a Comment