抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

前言

虽然自行搭建一个高带宽的代理已经非常简单快捷,但是低延迟的游戏加速器却并不简单。在这里,我们首先分析一下游戏加速服务器需要哪些能力:

  • 低延迟

对于低延迟这一需求,通常需要分析我们需要访问的游戏区服。通常情况下亚洲区服主要为 港服(港澳台)、日服、新加坡服(东南亚)。对于港服/新加坡服,我们的落地服务器通常为香港,而日服通常是落地日本。
如果选择直连方案,香港和日本的服务器本身并不便宜,尤其是如果需要高品质网络,为了拿到诸如 CN2 之类的线路有需要增加预算。因此不如迷信一下同一家服务器提供商的大陆机器与落地机器之间的特殊优化,采用中转的方案。
对于中转方案,推荐使用 广州/深圳 + 香港 或者 上海 + 日本 的中转方案。

  • 不丢包

众所周知,打游戏是需要使用 UDP 的,所以代理需要支持 UDP,最好不要是 UDP over TCP。于是 SS / vmess mkcp / Wireguard 就进入了视野。
但是 UDP 传输本身就会被 QoS,尤其是在跨境传输的情况下。因此我们需要一些手段来骗过运营商,这里一个好用的方式是 UDP2RAW,一种将 UDP 伪装成 TCP 的工具。

  • NAT 1

NAT 类型是游戏非常重要的一部分,其原理可以看这篇文章一个臭打游戏的对 fullcone 的高谈阔论,讲得比我好多了。
由于 Linux 的 NAT 实现本身存在一些问题,我们需要使用这个项目netfilter-full-cone-nat来实现真正的 Full Cone NAT。原理详见这篇文章从DNAT到netfilter内核子系统,浅谈Linux的Full Cone NAT实现

综合上面的讨论,这里采用 Wireguard + UDP2RAW 的方案将跨境的两台服务器连接,创造一个伪 IPLC 隧道。Client 机器通过 SS 经过中转机的隧道到达落地机并最终落地。

Steps

下文均默认默认已经有基础的 Wireguard 使用能力及 Linux 使用能力。

Full Cone NAT

我们只需要在落地机上实现 FullCone NAT。

编译并安装 kennel module & iptables

此处可参考这篇文章自建wireguard加速游戏并实现NAT1(fullcone)

这里摘录一下相关步骤,减少跳转不同页面的成本。

  1. 安装完整的内核

    1
    apt install linux-image-$(uname -r)

    若无法下到一般是 outdated 了,apt upgrade 更新一下,或者自行想办法下载。

  2. 安装相关依赖

1
apt install gcc autoconf autogen libtool pkg-config libgmp3-dev -y
  1. clone 代码
1
2
3
4
5
6
7
8
cd ${your_temp_dir}

mkdir fullcone
cd fullcone
git clone git://git.netfilter.org/libmnl
git clone git://git.netfilter.org/libnftnl.git
git clone git://git.netfilter.org/iptables.git
git clone https://github.com/Chion82/netfilter-full-cone-nat.git
  1. 编译libmnl
1
2
3
4
5
cd ${your_temp_dir}/fullcone/libmbl
sh autogen.sh
./configure
make
make install

然后运行

1
2
3
4
whereis libmnl

> libmnl: /usr/local/lib/libmnl.so /usr/local/lib/libmnl.la /usr/include/libmnl

获取到路径后

1
ldd /usr/local/lib/libmnl.so
  1. 编译libnftnl
1
2
3
4
5
cd ${your_temp_dir}/fullcone/libnftnl
sh autogen.sh
./configure
make
make install
  1. 编译和临时启用 netfilter-full-cone-nat
1
2
3
cd ${your_temp_dir}/fullcone/netfilter-full-cone-nat
make
insmod xt_FULLCONENAT.ko

如果编译模块报错,请安装 kernel-devel ,载入模块报错 Unknown symbol in module ,先 modprobe nf_nat 再载入模块。

  1. 编译和替换 iptables
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
cp ${your_temp_dir}/fullcone/netfilter-full-cone-nat/libipt_FULLCONENAT.c ${your_temp_dir}/fullcone/iptables/extensions/
ln -sfv /usr/sbin/xtables-multi /usr/bin/iptables-xml
./autogen.sh
PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
export PKG_CONFIG_PATH
./configure
make
make install

# 先关闭 iptables
systemctl stop iptables
# 进入相应目录,并覆盖相关文件
cd /usr/local/sbin
cp /usr/local/sbin/iptables /sbin/
cp /usr/local/sbin/iptables-restore /sbin/
cp /usr/local/sbin/iptables-save /sbin/
  1. 检测编译与替换结果
1
2
iptables -t nat -A POSTROUTING -o ${YOUR_ETH_CARD} -j FULLCONENAT # 没有错误
lsmod | grep xt_FULLCONENAT # 有结果

VPS 的网卡可以通过 ip route 查看 default 路由获得

  1. 开启自动挂载
1
2
3
mv ${your_temp_dir}/fullcone/netfilter-full-cone-nat/xt_FULLCONENAT.ko  /lib/modules/$(uname -r)/

depmod

增加相关 conf

1
echo "xt_FULLCONENAT" > /etc/modules-load.d/fullconenat.conf

重启后验证

1
2
reboot
lsmod | grep xt_FULLCONENAT # 有输出

若重启之后验证失败,可以参考以下操作

1
2
3
4
cd ${your_temp_dir}/fullcone/netfilter-full-cone-nat
make
mv ${your_temp_dir}/fullcone/netfilter-full-cone-nat/xt_FULLCONENAT.ko /lib/modules/$(uname -r)/
depmod
  1. 配置 iptables

这一步可能不是必须的,请在操作之前确认你对 iptables 的基础知识

由于其他文章中出现过 netfilter-full-cone-nat 与 DROP 规则可能有化学反应,因此请检查你的路由表。

1
2
iptables -L
iptables -t nat -L

若你没有其他自定义规则,也没用 docker 之类的,建议在检查过路由表之后直接清理原有逻辑。

1
2
iptables -F
iptables -Z

注意:请在有相关知识,确认路由表可以修改后再进行

增加 FULLCONENAT 相关配置

1
2
iptables -t nat -A PREROUTING -i ${YOUR_ETH_CARD} -j FULLCONENAT
iptables -t nat -A POSTROUTING -o ${YOUR_ETH_CARD} -j FULLCONENAT

保存所有更改

1
iptables-save

检测 NAT 类型

这里使用 pystun3 进行 NAT 类型检测

  1. 安装 pystun3
1
pip3 install pystun3
  1. 运行 pystun3
    1
    2
    3
    4
    5
    pystun3

    > NAT Type: Full Cone
    > External IP: {your_ip}
    > External Port: {your_random_port}

    若需要使用国内的 NAT 检测提供商,请参考其官方 Document,更改服务商
    若发现此时依然是 Restrict NAT,请先确认 VPS 供应商侧的防火墙配置,推荐将 10000-65535 的 UDP 端口入站放行

Wireguard

那么重头戏就是 wireguard 的配置了。这里首先给出中转机和落地机的配置,再针对这之中的一些细节进行补充

为了方便说明,这里中转机称为 A,落地机称为 B

中转机配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[Interface]
Address = {YOUR_A_WIREGUARD_IP}/{YOUR_MASK}
PrivateKey = {YOUR_A_PRIVKEY}
PreUp = ip route add {YOUR_B_IP} via {YOUR_GATEWAY_IP}
PreUp = source {YOUR_ADD_IP_SHELL}
PreUp = udp2raw -c -l 127.0.0.1:{YOUR_A_UDP2RAW_PORT} -r {YOUR_B_SERVER_IP}:{YOUR_B_UDP2RAW_PORT} -k {YOUR_PASSWORD} --raw-mode faketcp -a &
PreDown = source {YOUR_REMOVE_IP_SHELL}
PostDown = killall udp2raw
PostDown = ip route delete {YOUR_B_IP} via {YOUR_GATEWAY_IP}
MTU = 1250
ListenPort = {YOUR_A_WIREGUARD_PORT}

[Peer]
PublicKey = {YOUR_B_PEER_PUBKEY}
EndPoint = 127.0.0.1:{YOUR_A_UDP2RAW_PORT}
AllowedIPs=0.0.0.0/0
PersistentKeepalive = 20

WARNING: 直接照抄可能会导致 SSH 断连且无法简单恢复
请先阅读并理解为什么这么做,再进行你的修改与部署

  • PreUp = ip route add {YOUR_B_IP} via {YOUR_GATEWAY_IP}

实际上这并不是必须的,但是由于最终我们需要将中转机的数据都转发到落地机,也就是 AllowedIPs=0.0.0.0/0 ,为了安全起见,先添加你的落地机的 IP 路由,防止直接断网。
{YOUR_GATEWAY_IP} 可以简单的通过以下方式获取:

1
ip route | awk '$1=="default" {print $3}'
  • PreUp = source {YOUR_ADD_IP_SHELL}

由于 Wireguard 通过其 AllowedIPs 配置来设定路由,而实际上我们只希望出/入站为非中国大陆的流量才被代理,所以我们需要排除掉部分IP。实际上在我们已知 IP 段的情况下,可以通过这个计算器 WIREGUARD ALLOWEDIPS CALCULATOR来算出最终的规则,但是众所周知,中国的 IP 段非常多,这样做实在是工作量巨大。于是我们可以像上面那条这样,通过脚本将所有的中国 IP 段的路由设定为不通过 wireguard 来达成同样的目的。
shell 脚本的生成可以参考这个仓库 wireguard-configuration 实现。同时也应生成删除路由规则的 shell 脚本,以用于关闭 wireguard 时的钩子函数调用

  • PreUp = udp2raw -c -l 127.0.0.1:{YOUR_A_UDP2RAW_PORT} -r {YOUR_B_SERVER_IP}:{YOUR_B_UDP2RAW_PORT} -k {YOUR_PASSWORD} --raw-mode faketcp -a &

这里的 PreUp 钩子是用于同时打开 udp2raw 客户端以将 UDP 流量伪装为 TCP 流量发送,降低被 QoS 的情况。此处建立了到远程落地机 udp2raw 服务端的连接并将所有指定本地端口上的所有 UDP 数据包伪装为 TCP 后发送。

  • MTU = 1250

由于 UDP2RAW 会使用部分包头,为了防止整体包大小过大,必须把 wireguard 本身的包大小减少

  • EndPoint = 127.0.0.1:{YOUR_A_UDP2RAW_PORT}

中转机作为 UDP2RAW 的 client,需要将落地机Peer 的 EndPoint 指向 UDP2RAW 的本地端口才能正常工作

落地机配置

1
2
3
4
5
6
7
8
9
10
11
12
13
[Interface]
Address = {YOUR_WIREGUARD_B_IP}/{YOUR_MASK}
PreUp = udp2raw -s -l 0.0.0.0:{YOUR_B_UDP2RAW_PORT} -r 127.0.0.1:{YOUR_B_WIREGUARD_PORT} -k {YOUR_PASSWORD} --raw-mode faketcp -a &
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o {YOUR_ETH_CARD} -j FULLCONENAT
PreDown = killall udp2raw
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o {YOUR_ETH_CARD} -j FULLCONENAT
PrivateKey = {YOUR_B_PRIVKEY}
MTU = 1250
ListenPort = {YOUR_B_WIREGUARD_PORT}

[Peer]
PublicKey = {YOUR_A_PUBKEY}
AllowedIPs={YOUR_A_WIREGUARD_IP}/{YOUR_MASK}

落地机相比较而言非常简单。只需要简单关注 iptables 相关配置。

备注

中转机由于需要处理路由问题会比较复杂。此外这里备注一个救援方式:

  1. 在不确定是否安全的情况下,AllowedIPs 建议先设置为 wireguard IP 段
  2. 建立 wireguard 连接后,在落地机 ssh 到中转机的 wireguard IP,确认可以成功连接
  3. 在这个情况下,就算后续将 AllowedIPs 设置为 0.0.0.0/0 后出现问题,也可以在落地机 ssh 回去

Client <-> 中转机

Client 到中转机的隧道就十分简单了,无论是在中转机上加一个新的 wireguard peer,client 简单配一下 AllowedIPs 来走代理,还是在中转机上搭建一个 SS 等直接支持 UDP 的正统过墙协议都没有问题。

参考文献

评论