VPN 代理加速

本文主要调研游戏网络加速,网络代理的技术方案和实现,可以用于解决网速低、网络信号差等带来的卡顿、丢帧等体验问题

一、整体方案

img

  1. 复杂繁多的运营商:对于没有建立互联的运营商,需要先经数据中心迂回再到上车点
  2. 代理服务器:上车点目前不承担认证协商能力,需要部署代理服务器与 App 之间进行认证协商/加解密,同时为加快代理服务器到上车点的传输,可以部署到同个 IDC
  3. GAAP 专线:使用部署的私有 VPC 在内网传输
  4. 游戏目标服务器:下车点进行 DNAT 转换,为避免伪造攻击,源 IP 需多个

二、Android VPN 加速方案

img

  • 从本地接口的文件描述符(fd)读取传出的 IP 数据包,进行加密并通过 VPN 网关发送到代理服务器,代理服务器寻找加速节点,经过加速专线到达游戏服务器
  • 将传入的数据包(从 VPN 网关接收并解密)写入本地接口的文件描述符(fd)。
  • 5.0 及以上按应用拦截,5.0 以下按目标 IP 地址拦截,(0.0.0.0) 拦截所有流量

方案一:包裹 IP 数据包以 UDP/TCP 数据包转发

通过 UDP/TCP 包裹 IP 数据包转发到代理服务器,代理服务器脱去 UDP/TCP 头获取拦截的 IP 数据包,再进行 NAT IP 地址转换,转发到目标服务器的加速节点。

img

  • TCP/UDP 对 IP 包进行封装,代理服务器再将该 IP 包发到网络协议栈中

优点:

  • 终端只需要透传 IP 报文包,无需关心内部状态

缺点:

  • 传输的包大小增大,多了 udp/tcp 和 ip 的头部

方案二:解析 IP 数据包以 SOCKS 代理协议转发

使用 tun2socks 将 IP 数据包解析成原始数据,再用 SOCKS 协议封装进行转发
img

  • 解析了 IP 数据包后,需要维护 TCP/IP 的协议栈交互
  • 使用 shadowsocks/socks5 等方式协商认证/加密传输到代理服务器
  • 代理服务器转发到加速节点上

优点:

  • 数据包体积较小,没有多余的 ip 和 tcp/udp 头部

缺点:

  • 需要维护与 tun0 的 TCP/IP 协议栈交互
  • lwip 会进行魔改,维护成本高

tun2socks 方案

img

1)与Android VpnService fd 交互

  • IP 读取:fd_handler read fd 后在 device_read_handler_send 中进行分发,tcp 丢给 lwip 的 ip_input 处理,udp 自己处理 process_device_udp_packet
  • IP 写回:BTap_Send write fd ,其中 tcp 通过 netif_output_fn 走 lwip 输出,udp 通过 udpgw_client_handler_received 输出

2)TCP 交互

TCP 读取:

  • 将读取到的 IP 数据包写入 pbuf ,作为 lwip 的 ip_input 输入
  • 监听接入 tcp 的数据报文,进行 socks 封装,通过 BSocksClient 进行发送

TCP 写回:

  • 在 BSocksClient 收到数据解析后,将 tcp 数据通过 tcp_write 进入 lwip
  • lwip 的 tcp 数据经过 ip 报文包装,在 output_fn 中将数据通过 BTap_Send 写回 fd 中。

3)UDP 交互

  • UDP 读取:收到 ip 数据报文后,若是 udp 的报文,则解析数据后,用 socks 封装通过 SocksUdpGwClient 发送
  • UDP 写回:监听 udp 的数据报文,对 socks 进行解析,再封装 ip 报文通过 BTap_Send 写回 fd 中。

Socks5 协议封装

img

  • 认证方法协商
  • 身份信息认证
  • 发起请求携带转发的目标地址和端口

缺点:

  • SOCKS5 连接需要三次握手,有连接延迟
  • SOCKS5 协议只负责传输,不负责加密,如 Http 协议通过 SOCKS5 代理的流量将全是明文,需要用其他方式如 TLS 等来保证的安全性

方案三:解析 IP 数据包以 shadowsocks 协议转发

shadowsocks代理

  • 建立 SOCKS Server,将数据转发到本地建的 shadowsocks local
  • shadowsocks local 对数据进行加密,先通知目标服务器的地址信息,再传输密文和随机数 IV
  • shadowsocks server 对数据进行解密,再进行转发

优点:

  • 数据密文传输,有较强隐蔽性

缺点:

  • 没有协商认证过程,加密方式和加密的用户密码是事先定义好的

shadowsocks 协议加密

img

  • 传输目标地址和端口
  • 使用 AES-256 等算法根据用户密码生成密钥,再根据密钥+随机数IV生成的密文和 IV 进行传输

方案四:解析 IP 数据包直接转发(腾讯云GAAP方案)

img

  • 客户端获取单个 App 的接入点 VIP 和端口,并将数据包转发
  • 接入点进行 NAT 源地址和目标地址转换,目标地址根据 VIP 匹配控制台配置的目标 IP 和端口,客户端源 IP 和端口 TOA 携带

优点:

  • 无需数据包的编解码,速度快
  • 不携带游戏服务器信息

缺点:

  • 不能通用加速,只能配置 VIP 和目标 IP 信息的基础上才能加速
  • 没有认证方式,VIP 和端口容易被攻击

三、报文分析

1、TCP/UDP

IP 报文

ip

  • version:IP协议版本,4bit,0100(IPv4),0110(IPv6)
  • header length:IP包头长度,4bit,包长度可变
  • TOS(Type of Service):服务类型,8bit,3bit用来表示该数据包的优先级, 4bit表示可选的服务类型(最小延迟, 最大吞吐, 最大可靠性, 最低成本),还有1bit保留为0;但未被广泛使用,在RFC2474中 这 8 bit 被重新定义
    • DSCP(DiffServ):区分服务字段,6bit,相比之前3bit 8 类,增长到最多定义 64 个不同的服务类型
    • ECN(Explicit CongestionNotification):显示拥塞通知,2bit,00:不支持,01/10:支持,11:发生拥塞
  • totoal length:总长度,16bit,IP包最大长度65535字节
  • identification:标识符,16bit,配合 flags、fragment offset 使用,当数据包分包后,所有拆开的小包都被标记相同的值,以便目的端区分
  • flags:标记,3bit
    • 第一位是保留位,未使用为0
    • 第二位是DF(Don’t Fragment)位,DF位设为1时表明路由器不能对该数据包分包。如果该数据包无法在不分段的情况下发送,则路由器会丢弃该数据包并返回一个错误信息
    • 第三位是MF(More Fragments)位,当路由器对一个上层数据包分段,则路由器会在除了最后一个分段的IP包的包头中将MF位设为1
  • fragment offset:片偏移,13bit,表示该IP包在该组分片包中位置,接收端靠此来组装还原IP包。
  • TTL:生存时间,8bit,一个数据包在网络上的最多经过多少次转发, 如果超过该数字, 就丢弃该包,当IP包在网络上传送时,每经过一个路由器,TTL就自动减一。值为0时,则丢弃报文。防止报文进入环路
  • protocol:报文协议类型,8bit, 上层可选协议为: TCP, UDP, ICMP, IGMP
  • header checksum:头校验和,16bit,IP 头部的正确性检测,不包含数据部分。路由器会改变 TTL,所以路由器会为每个通过的数据包重新计算这个值
  • source address:源地址,32bit
  • destination address:目的地址,32bit
  • optional:可选,变长

TCP 报文

面向连接的、可靠的、字节流传输协议

  • 可靠性:需要超时、重传、窗口管理、流量控制、拥塞控制等功能来解决比特错误、包乱序、包重复、丢包等传输错误
  • 字节流:需要用 TCP 窗口管理、拥塞控制等功能来对应用层发送的数据进行统一分片或打包发送

tcp

  • 源端口:16bit,发送应用程序对应的端口,端口在0~65535之间,在收到服务请求时,操作系统动态地为客户端的应用程序分配端口号
  • 目的端口:16bit,报文接收计算机上的应用程序地址端口
  • 发送序列号:32bit,TCP 报文中第一个数据字节,标识从TCP源端向TCP目标端发送的数据字节流;当建立一个新的连接时,SYN变为1,此字段包含连接的初始顺序号 ISN
  • 确认序列号:32bit,只有ACK为1时有效,它包含目标端所期望收到源端的下一个数据字节
  • 头部长度:4bit,TCP 头部长度,最大为15/32bit,TCP头部长度默认20字节,最大60字节
  • 保留:4bit,000
  • 标识位:8bit
    • CWR:拥塞窗口减少标识被发送主机的设置,表明它接收到了设置 ECE 标识的TCP包,发送端通过降低发送窗口大小来降低发送速率
    • ECE:ECN响应标志,被用来表明 TCP 3次握手时表明一个 TCP 端是具备 ECN 功能的,并表明接收到的 TCP 包的 IP 头部的 ECN 被设置为 11
    • URG:紧急指针有效
    • ACK:确认序号有效
    • PSH:接收方应该尽快将这个报文段交给应用层
    • RST:重建连接
    • SYN:发起一个连接
    • FIN:释放一个连接
  • 窗口:16bit,流量控制,本机期望一次接收的字节数
  • 校验和:16bit,对整个 TCP 报文段(TCP 头部和TCP 数据进行校验和计算),并由目标端进行验证,对于大的数据包,不能可靠的反应比特错误,应用层需要添加自己的校验方式
  • 紧急指针:16bit,指向后面是优先数据的字节,若没有被设置,则作为填充
  • 选项,长度不变,但必须是 32bit 的整数倍

UDP 报文

无连接,数据包套接字,不保证到达性

udp

  • 源端口:16bit,
  • 目的端口:16bit
  • 长度:16bit
  • 校验:16bit

2、LWIP TCP/IP

LWIP 是一个轻量型的 TCP/IP 协议栈,我们可以在拦截 Android IP 报文包后,将其解析成 TCP 报文桥接到外部使用 Socks 协议发出:

lwip_in

拦截 IP 报文后,输出 tcp_input,我们可以在 tcp_input 中将协议转发到外部。

当收到外部消息后,使用代理 Socks 协议解析成裸数据,然后使用 LWIP tcp_write 写回,并封装成 IP 报文回给 Android VPN。

lwip_out

四、Demo 示例

我们使用 shadowsocks 在服务端部署,并在客户端进行 VPN 代理转发。

服务部署

1)shadowsocks 安装

CentOS:

1
yum install python-setuptools && easy_install pip pip install shadowsocks

如腾讯云上区分 python2或python3,需要这样:

1
pip2 install shadowsocks

2)shadowsocks server 启动

新建一个 shadowsocks.json 配置:

1
2
3
4
5
6
7
8
9
10
{
"server":"0.0.0.0",
"server_port":8388,
"local_address": "127.0.0.1",
"local_port":1080,
"password":"123456",
"timeout":300,
"method":"aes-256-cfb",
"fast_open": false
}
  • server:0.0.0.0,表示本网络中的本机IPV4地址,如果写的其他 IP 会有 socket.error: [Errno 99] Cannot assign requested address 错误
  • server_port:服务端口需要加安全组

启动运行 shadowsocks server

1
2
3
4
5
6
# 运行在前台
ssserver -c shadowsocks.json

# 运行在后台
ssserver -c shadowsocks.json -d start
ssserver -c shadowsocks.json -d stop

本地连接

下载个 shadowsocks app 设置已经部署的 IP:Port 进行连接:

shadowsocks本地连接

知道是不会有人点的,但万一被感动了呢?