https://www.v2ex.com/t/802949
https://github.com/dndx/phantun
作者: dndx
将 UDP 流量伪装成 TCP 流量的主流工具是 udp2raw
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 。
https://icloudnative.io/posts/wireguard-over-tcp-using-phantun/
https://icloudnative.io/posts/custom-derp-servers/
0. 编译
… 过程省略 .. 参考 rust 相关帖子
1. 在服务器上配置,运行
设置转发 iptables -t nat -A PREROUTING -p tcp -i eth0 --dport 4567 -j DNAT --to-destination 192.168.201.2 运行 RUST_LOG=info ./server --local 4567 --remote 127.0.0.1:1234 INFO server > Remote address is: 127.0.0.1:1234 INFO server > 1 cores available INFO server > Created TUN device tun0 INFO server > Listening on 4567
创建tun
tun0 inet 192.168.201.1 peer 192.168.201.2/32 scope global tun0
让第三方的 UDP 服务,监听在 127.0.0.1的1234 端口
2. 客户端配置,运行
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE 运行 RUST_LOG=info ./client --local 127.0.0.1:1234 --remote server_ip:4567
监听在本地的 127.0.0.1的1234端口
第3方客户端, 直接连接到 127.0.0.1:1234 就相当于 连接到 远程的UDP服务
建立一个tun0
tun0: inet 192.168.200.1 peer 192.168.200.2/32 scope global tun0
phantun监听在本地的 127.0.0.1:1234 UDP, 同时创建了一个 tun0设备, tun0设备的本端ip为 192.168.200.1, 对端ip为
192.168.200.2
来自于 123 udp端口的的 数据, 都被以 192.168.200.2的IP, 连接到 server_ip:4567 用tcp发送出去
但这个tcp连接, 又被 iptable改写了 源地址, 从 192.168.200.2 改成 了 客户端自己的主机地址
3. 测试程序
#!/usr/bin/env python import socket import sys import argparse from time import sleep host = '127.0.0.1' payload_size = 2048 def echo_server(peer, bind_port, to_port): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) server_address = (host, bind_port) print ("Starting up echo server on %s port %s" % server_address) sock.bind(server_address) sock.settimeout(5.0) index = 1 while True: try: print ("Waiting to receive message from client") data, address = sock.recvfrom(payload_size) print ("received %s bytes from %s" % (len(data), address)) print ("Data: %s" %data) if data: #data = b'###...' + data + b'...###' data = b'### %i' % index sent = sock.sendto(data, address) print ("sent %s bytes back to %s" % (sent, address)) index = index + 1 sleep(1) except socket.timeout: #addr = (peer, to_port) #data = b'count %i' % index #sent = sock.sendto(data, addr) #print ("sent %s bytes hello to %s" % (sent, addr)) index = index + 1 if __name__ == '__main__': parser = argparse.ArgumentParser(description='Socket Server Example') parser.add_argument('--host', action="store", dest="host", type=str, required=True) parser.add_argument('--bind', action="store", dest="bind_port", type=int, required=True) parser.add_argument('--to', action="store", dest="to_port", type=int, required=True) given_args = parser.parse_args() peer = given_args.host local_port = given_args.bind_port remote_port = given_args.to_port echo_server(peer, local_port, remote_port)
在服务端运行
python3 ttt_echo_server.py --host cliet_peer_ip --bind 1234 --to 9999
监听在udp的1234端口
在客户端运行
python3 ttt_echo_server.py --host 127.0.0.1 --bind 9999 --to 1234
它会往 本地 127.0.0.1 的1234 udp端口发送数据, 从而fake tcp转发给 服务器
INFO fake_tcp > Sent SYN to server
INFO fake_tcp > Connection to 111.222.333.444:4567 established
4. 关于MTU
WireGuard MTU = Interface MTU – IPv4 header (20 bytes) – TCP header (20 bytes) – WireGuard overhead (32 bytes)
IPv4: 1500 – 20 – 20 – 32 = 1428
phantun作者介绍的,应该这么算, 但我实际设置为 1480 也是通的
he tb ipv6后
ping -c 2 -M do -4 -s 1364 10.0.0.1
ipv4 直连 (MTU居然只有1400)
ping -c 2 -M do -4 -s 1372 server_public_ip 1372(1400)
ipv4 udp wireguard直连
ping -c 2 -M do -4 -s 1452 10.0.0.1 1452(1480)
ipv4 tcp wireguard
ping -c 2 -M do -4 -s 1396 10.0.0.1
实际浪费56个字节
IPV4 20
IPCP 16
Usually the MTU of most networks is 1500. So the MTU of IPv6 tunnel should be 1480 by removing the 20 bytes IPv4 header. To set a proper MTU that neither cause IPv6 packet be truncated nor be too small for IPv6, You’d better find the IPv4 MTU from you to your tunnel relay server by your self and set the IPv6 MTU by minus 20 bytes.
5. 几个坑点
(1) 在服务器上,如果运行的是
./server --local 4567 --remote 127.0.0.1:1234
也就是转发给 127.0.0.1:1234 这个 udp监听端口, 必须严格监听在 127.0.0.1:1234
监听在 0.0.0.0:1234 是不能正常工作的
(2)客户端, iptable命令的出口 网卡名字必须正确指定
iptables -t nat -A POSTROUTING -o br0 -j MASQUERADE
如果用了网桥, 必须设置为网桥的名字