使用udp2raw封装WireGuard UDP流量
0x00 前言
本文是上一篇《RouterOS 基于 WireGuard 与动态 DNS 列表的策略分流》的进阶篇。上一篇已经实现 RouterOS 通过 WireGuard 连接 HK VPS,并基于 DNS 解析结果做 address-list 和策略路由分流。
本文不改原来的 DNS 分流逻辑,只在 WireGuard 外层增加 udp2raw 封装。通过 udp2raw,隐藏原始 WireGuard UDP 端口和协议特征,绕过一些简单的 UDP 限速或 UDP 阻断,让公网链路表现为类似 TCP 的流量。
内层 WireGuard 仍然按 UDP 工作,外层 faketcp 只是运输封装。最终链路是:RouterOS CHR -> Debian 旁路节点 udp2raw client -> HK VPS udp2raw server -> VPS 本机 wg0。
0x01 udp2raw 介绍
udp2raw 是什么?
udp2raw 是一个把 UDP 数据包封装进 raw packet 的隧道工具。它常用于把原本的 UDP 应用流量伪装成其他形态,例如:faketcp,udp,icmp。
但要注意:udp2raw faketcp 不是 HTTPS/TLS 伪装,udp2raw faketcp 不等于真实 TCP 应用,强 DPI 仍可能识别它不是正常 TCP 流量。
faketcp 是怎么做到无状态的?
普通 TCP 是由操作系统内核维护状态的,比如 SYN、SYN/ACK、ACK、ESTABLISHED、seq/ack、重传、拥塞控制、连接关闭。
udp2raw faketcp 的思路不一样:程序使用 raw socket 自己构造 IP/TCP 头,不调用内核的 TCP listen/connect/accept。外层包长得像 TCP,但不是内核 TCP 栈建立出来的真实连接。
因为系统内核并不知道这个“TCP 连接”,如果收到对端 faketcp 包,可能会认为这是不存在的连接,然后自动回 RST。所以 udp2raw 使用 -a 自动添加 iptables 规则,把相关包在进入内核 TCP 栈前 DROP 掉。这样 raw socket 能收到包,内核 TCP 栈不会插手回 RST。
这里说的“无状态”不是完全没有状态,而是:不是内核 TCP 状态,不是完整 TCP 连接,只是用户态用 raw packet 模拟出足够像 TCP 的外观。
当前使用的模式
我这里使用的是:--raw-mode faketcp、--cipher-mode aes128cfb、--auth-mode hmac_sha1。
--cipher-mode xor 更省 CPU,但更像简单混淆。虽然内层 WireGuard 本身已经有强加密,但我这边 Debian 旁路节点和 VPS 都是 x86_64,目前没有明显 CPU 瓶颈,所以先保留 aes128cfb + hmac_sha1。如果后面测速发现 CPU 成为瓶颈,再考虑两端同时改成 --cipher-mode xor。
注意:udp2raw 两端的 raw-mode、cipher-mode、auth-mode 必须一致。
0x02 当前拓扑
我这里 RouterOS CHR 跑在 PVE 上,另外在 PVE 里起了一台很小的 Debian VM,专门跑 udp2raw client。
当前地址规划:RouterOS WireGuard 地址是 10.252.252.2/30,Debian 旁路节点是 172.17.10.254/24。只要 RouterOS 能访问这台 Debian 即可,具体放在哪个网段按自己的网络规划来。Debian 上 udp2raw client 监听 0.0.0.0:15182/udp,转发到 VPS 的 119.28.68.138:35827/faketcp。VPS 上 udp2raw server 监听 0.0.0.0:35827/faketcp,再转发给本机 127.0.0.1:443/udp。
flowchart LR
LAN["LAN / DNS 分流流量"] --> ROS["RouterOS CHR<br/>WG: 10.252.252.2/30"]
ROS -->|"WG UDP<br/>172.17.10.254:15182"| DEBIAN["Debian 旁路节点<br/>udp2raw client"]
DEBIAN -->|"faketcp<br/>119.28.68.138:35827"| VPS_RAW["HK VPS<br/>udp2raw server"]
VPS_RAW -->|"UDP<br/>127.0.0.1:443"| VPS_WG["wg0<br/>10.252.252.1/30"]
VPS_WG --> WAN["VPS 出口"]
我的 VPS 上 nginx/docker 占用的是 TCP 443,WireGuard 实际监听的是 UDP 443,两者不冲突。udp2raw 外层单独使用 TCP 35827。
0x03 Debian 旁路节点配置
Debian VM 很小就够用,只要 RouterOS 到 Debian 网络可达即可。建议配置:1 core CPU,256MB 或 512MB 内存,4GB 硬盘。
安装依赖:
root@pve-debian:~# apt update
root@pve-debian:~# apt install -y curl tar iptables ca-certificates tcpdump openssl
下载 udp2raw:
root@pve-debian:~# cd /tmp
root@pve-debian:/tmp# curl -fsSL -o udp2raw.tar.gz https://github.com/wangyu-/udp2raw/releases/download/20230206.0/udp2raw_binaries.tar.gz
root@pve-debian:/tmp# mkdir -p /tmp/udp2raw
root@pve-debian:/tmp# tar -xzf udp2raw.tar.gz -C /tmp/udp2raw
root@pve-debian:/tmp# install -m 0755 /tmp/udp2raw/udp2raw_amd64 /usr/local/bin/udp2raw
生成 key:
root@pve-debian:~# mkdir -p /etc/udp2raw
root@pve-debian:~# openssl rand -base64 32 > /etc/udp2raw/key
root@pve-debian:~# chmod 600 /etc/udp2raw/key
root@pve-debian:~# cat /etc/udp2raw/key
<UDP2RAW_KEY>
这个 key 两端必须一致,后面 VPS 也要填同一个。
创建 client 配置:
root@pve-debian:~# vi /etc/udp2raw/client.conf
-c
-l 0.0.0.0:15182
-r 119.28.68.138:35827
-k <UDP2RAW_KEY>
--raw-mode faketcp
--cipher-mode aes128cfb
--auth-mode hmac_sha1
-a
创建 systemd 服务:
root@pve-debian:~# vi /etc/systemd/system/udp2raw-wg-client.service
[Unit]
Description=udp2raw WireGuard wrapper client
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/local/bin/udp2raw --conf-file /etc/udp2raw/client.conf
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target
启动:
root@pve-debian:~# systemctl daemon-reload
root@pve-debian:~# systemctl enable --now udp2raw-wg-client
root@pve-debian:~# systemctl status udp2raw-wg-client
0x04 VPS 配置 udp2raw server
VPS 上先确认 WireGuard 实际监听端口:
root@itho:~# wg show wg0
interface: wg0
listening port: 443
我这里 wg0 实际监听的是 UDP 443,所以 udp2raw server 要转发到 127.0.0.1:443。如果你的 WireGuard 是 51820,这里就改成 127.0.0.1:51820。
安装依赖和 udp2raw:
root@itho:~# apt update
root@itho:~# apt install -y curl tar iptables ca-certificates tcpdump
root@itho:~# cd /tmp
root@itho:/tmp# curl -fsSL -o udp2raw.tar.gz https://github.com/wangyu-/udp2raw/releases/download/20230206.0/udp2raw_binaries.tar.gz
root@itho:/tmp# mkdir -p /tmp/udp2raw
root@itho:/tmp# tar -xzf udp2raw.tar.gz -C /tmp/udp2raw
root@itho:/tmp# install -m 0755 /tmp/udp2raw/udp2raw_amd64 /usr/local/bin/udp2raw
创建 server 配置:
root@itho:~# mkdir -p /etc/udp2raw
root@itho:~# vi /etc/udp2raw/server.conf
-s
-l 0.0.0.0:35827
-r 127.0.0.1:443
-k <UDP2RAW_KEY>
--raw-mode faketcp
--cipher-mode aes128cfb
--auth-mode hmac_sha1
-a
创建 systemd 服务:
root@itho:~# vi /etc/systemd/system/udp2raw-wg-server.service
[Unit]
Description=udp2raw WireGuard wrapper server
After=network-online.target wg-quick@wg0.service
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/local/bin/udp2raw --conf-file /etc/udp2raw/server.conf
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target
启动:
root@itho:~# systemctl daemon-reload
root@itho:~# systemctl enable --now udp2raw-wg-server
root@itho:~# systemctl status udp2raw-wg-server
安全组需要放行 TCP 35827 入站。
我一开始就卡在这里,VPS 上 tcpdump 抓不到任何 35827 的包。后面放行安全组后,udp2raw 才进入 client_ready / server_ready。
VPS 上可以这样看:
root@itho:~# journalctl -u udp2raw-wg-server -f
正常会看到:
received syn,sent syn ack back
changed state to server_ready
0x05 RouterOS 修改 WireGuard Peer
原来的 WireGuard peer 是直接指向 VPS 公网 IP 的,现在改成本地 Debian 旁路节点。我这里 peer 名字是 peer-119:
[admin@HOME2-ROS] > /interface/wireguard/peers/set [find name="peer-119"] endpoint-address=172.17.10.254 endpoint-port=15182 persistent-keepalive=5s
注意:RouterOS 填的是 172.17.10.254:15182,不是 VPS 的 119.28.68.138:35827。
15182 是 RouterOS 到 Debian 的本地 UDP 端口。
35827 是 Debian 到 VPS 的公网 faketcp 端口。
0x06 MTU 调整
这里是一个重点坑。刚开始隧道已经通了,RouterOS ping VPS 的 WireGuard 地址也正常,但是客户端网页打不开。这个现象很容易误判成 DNS、路由或者 NAT 问题,实际上是 MTU 太大。
WireGuard 外面又套了一层 udp2raw faketcp,有效 MTU 会变小。如果两端 WireGuard 还保持默认较大的 MTU,就可能出现:ping 正常、小包正常,但是网页打不开、HTTPS/TLS 握手卡住,或者视频加载异常。
我这边最终把 WireGuard 两端 MTU 都设置成 1320,稳定了。
RouterOS 端:
[admin@HOME2-ROS] > /interface/wireguard/set wireguard2 mtu=1320
VPS 端运行时设置:
root@itho:~# ip link set mtu 1320 dev wg0
VPS 持久化写入 /etc/wireguard/wg0.conf:
[Interface]
Address = 10.252.252.1/30
ListenPort = 443
PrivateKey = ...
MTU = 1320
然后重启 WireGuard:
root@itho:~# systemctl restart wg-quick@wg0
RouterOS 也建议加 MSS clamp:
/ip/firewall/mangle/add chain=forward out-interface=wireguard2 protocol=tcp tcp-flags=syn action=change-mss new-mss=clamp-to-pmtu passthrough=yes comment="clamp MSS for WG over udp2raw"
0x07 测试验证
RouterOS 测试
RouterOS 上 ping VPS 的 WireGuard 地址:
[admin@HOME2-ROS] > /ping 10.252.252.1 src-address=10.252.252.2
SEQ HOST SIZE TTL TIME STATUS
0 10.252.252.1 56 64 39ms891us
1 10.252.252.1 56 64 39ms729us
之前直连 WireGuard 大概 35ms,现在套了一层 udp2raw 后大概 39-40ms,多了约 5ms。
VPS 测试
VPS 上看 wg show:
root@itho:~# wg show wg0
interface: wg0
listening port: 443
peer: XvnO1bNLgGGjBqRaKN9w03U2N9v0okLvKT9Cb1HB6g0=
endpoint: 127.0.0.1:50473
latest handshake: 11 seconds ago
这里最关键的是 endpoint: 127.0.0.1:50473。这说明 VPS 的 WireGuard 收到的是本机 udp2raw server 转发进来的包,不再是公网直接 UDP 进 wg0。
抓包验证
Debian 上抓外层 faketcp:
root@pve-debian:~# tcpdump -ni ens18 host 119.28.68.138 and tcp port 35827
可以看到 172.17.10.254.xxxxx > 119.28.68.138.35827 和 119.28.68.138.35827 > 172.17.10.254.xxxxx 这样的双向流量。
到这里,WireGuard over udp2raw 已经完成。
0x08 踩坑记录
1、端口不能写 151820
一开始我把 Debian 本地监听端口写成了 151820,udp2raw 直接报错:
invalid port: 151820
端口最大只能到 65535,所以后面改成了 15182。
2、VPS TCP 443 已经被 nginx 占用
我的 VPS 上 nginx/docker 已经占用了 TCP 443,所以 udp2raw 不能再监听 TCP 443。
最后选择了 udp2raw faketcp:35827。
注意 WireGuard 用的是 UDP 443,nginx 用的是 TCP 443,这两个不冲突。
3、安全组必须放行 TCP 35827
VPS 本机服务已经启动,但安全组没放行时,VPS 上抓不到任何包。放行 TCP 35827 后,udp2raw 立刻握手成功。
root@itho:~# tcpdump -ni eth0 tcp port 35827
0 packets captured
4、MTU 太大导致网页打不开
这是最隐蔽的问题。隧道 ping 通,不代表网页一定能正常打开。套了 udp2raw 后,我这里 WireGuard 两端 MTU 设置为 1320 才稳定。
0x09 总结
这套方案最大的优点是:不破坏原来的 RouterOS DNS 分流结构。RouterOS 还是继续负责 DNS FWD、address-list、mangle、策略路由和 WireGuard。
Debian 旁路节点只负责把 RouterOS 发来的 WireGuard UDP 包封装成 faketcp,再转给 VPS。
最终公网链路从 RouterOS -> VPS WireGuard UDP 变成 RouterOS -> Debian -> udp2raw faketcp -> VPS -> wg0。
缺点也很明确:多了一层用户态封装,延迟增加了约 5ms,并且需要处理 MTU 问题。
参考:udp2raw 项目地址 https://github.com/wangyu-/udp2raw 。上一篇是《RouterOS 7 + Ubuntu VPS:基于 WireGuard 与动态 DNS 列表的策略分流》。
routeros ros wireguard udp2raw faaketcp udp debian