本文研究一种利用国外大带宽VPS,并且借用线路加密的方式完成内网穿透的方案。

阅读本文之前,需要了解至少一种常用的反向代理工具(例如FRP),至少一种底层隧道(例如Wireguard、IPSec),熟悉TCP/IP协议。

本文是基于之前一篇文章使用RouterOS建立PPP虚拟线路进行内网穿透替代FRP的升级。之前的方案实施条件比较苛刻,众所周知,国内网络受到严格的审查,因此所有节点必须使用国内供应商的产品,并且需要进行备案。

上述方案使用至今,我的评价是非常之稳定,体验相比FRP提升几个量级。目前唯一的不足就是受限于VPS带宽,大文件传输会显得捉襟见肘(不论使用何种方案都一样),因此考虑使用国际产品,所以不得不考虑如何应对审查问题。

目前主流工具为V2ray,很多服务提供商也是使用此种协议,让我们比较容易获得优质的线路。因此本文也选择使用V2Ray来进行说明。

原方案简述

原方案中涉及三种终端,一个具有公网IP的节点A(轻量应用服务器,比较便宜的价格获得5Mbps带宽),一个位于NAT后提供服务的主机B(NAS等家庭网关设备),一个任意地点的访问设备(手机、电脑、电视等等任何可以上网的终端)。

在A与B之间,使用隧道协议(本文所有隧道均指比较底层的协议,如二层隧道L2TP)建立虚拟链路,在此链路上通过路由、NAT等技术,完成C->A->B流量的转发。

新方案改动

新方案在原有方案的基础上,增加的一个P节点,此节点用于对A-B之间的流量进行加密。P节点可以是独立存在的,也可以与A共用,我更倾向于选择前者,原因有二,一可以增加对抗风险的能力,P节点在服务上进行购买,有众多线路可以切换;而是可以减少带宽损耗(最后再详细描述,能否减少带宽损耗取决于A与B节点谁会造成带宽瓶颈)。而后者一旦被封禁,A节点(P节点)的访问也跟着一起受限。

新方案分两个区域,内部区域和外部区域,在同一个区域内的通讯不会被审查,但是跨区域通信的流量有被识别的风险。

仍然是在A与B之间建立隧道,但是中间经过P节点进行中转,形成B-P-A,B与P的连接需要跨区域,因此使用V2ray协议作为外部协议,隐藏内部的隧道协议,P-A之间的连接为裸隧道协议。其他方面与原方案无二致。

实施思路

由于只是在之前的基础上多了一层套娃,只需要改动B节点的配置,所以只讲一下V2ray配置的部分。

通过Docker在群晖NAS上运行V2Ray(群晖内网地址192.168.88.88,当然,在OpenWrt,甚至RouterOS上运行V2Ray也无不可,请尽情发挥想象力),使用V2Ray的dokodemo-door协议,将访问本地端口192.168.88.88:5555,通过P.example.com节点的代理连接到A.example.com。V2Ray配置文件如下。

{
  "outbound": {
    "settings": {
      "vnext": [
        {
          "port": 443,
          "users": [
            {
              "id": "56f686c3-59f6-30f7-9hbf-7542a980491f"
            }
          ],
          "address": "P.example.com"
        }
      ]
    },
    "protocol": "vmess",
    "streamSettings": {
      "network": "tcp"
    }
  },
  "inbound": {
    "listen": "0.0.0.0",
    "port": 5555,
    "protocol": "dokodemo-door",
    "settings": {
      "address": "A.example.com",
      "port": 5555,
      "network": "udp",
      "followRedirect": false,
      "timeout": 0
    }
  }
}

而后需要改动隧道连接的地址为本地代理地址。A.example.com -> 192.168.88.88。以RouterOS WireGuard为例,唯一变动的只有连接地址和端口。

/interface wireguard peer add
interface=wg-out-cloud public-key="***" \
    endpoint-address=192.168.88.88 endpoint-port=5555 \
    allowed-address=0.0.0.0/0 persistent-keepalive=10s

谈谈流量损耗问题

这个问题需要一些计算,首先分为两个前提,A与P节点部署方式,独立和共用。

独立P节点前提下:

一般这种情况下,P节点都是我们购买的服务,提供的带宽非常之大,能够满足家庭宽带的全速上传(一般都是30-50Mbps),所以P的带宽我们无需考虑。此时实际C终端能够获得的速度是A与B中速率较小的一方,它的速率减去与P之间协议传输的额外开销。

举个比较夸张的例子,V2Ray协议开销较大,我们设定为20%(开销来源于协议头、填充加密等),Wireguard开销较小,我们设定为5%。A上下行对等30Mbps,C、P带宽看作不限,B上行50Mbps,下行200Mbps。在C终端下载情况下,B->P之间的最大带宽为B的上行带宽减去Wireguard协议开销,P->A之间最大带宽为A下行传带宽减去两种协议开销,A->C取决于C的行带宽。

数据流向:B--(50Mbps75%)--P--(30Mbps95%)--A--(30Mbps)--C,木桶原理,C访问的实际速度就是30Mbps*95%。

共用A和P节点前提下:

实际上并没有什么不同,只是P节点的带宽不能看作是无限的了,而是与A共享,所以相同的条件计算如下。

数据流向:B--(30Mbps75%)--A&P--(30Mbps)--C,木桶原理,C访问的实际速度就是30Mbps75%。

结论就是,B上行高于A的带宽时,B的带宽浪费有点严重。

谈谈延迟的问题

协议等级越高,延迟和损耗也就越高,所以在有了VMess作为外层协议的情况下,内部的协议要尽可能的简单,选用Wireguard主要看中它的部署方便。

在我的实践中,P选用服务上提供的,与A同区域的节点,尽可能降低A-P之间延迟。而又由于这些服务供应商大都使用了国内节点中转的方式,P与B的连接延迟也非常之低,甚至比我在A上直接运行P延迟更低,猜想是这些服务供应商应该是有IPLC之类的优质线路。

B--(250ms)--A&P--(45ms)--C
在我自建的情况下,测试的延迟大概在300ms左右浮动,下载峰值速度14Mbps左右,损耗比较大。

B--(20ms)--P1--(服务商中转线路)--P2--(同区估测10ms以内)--A--(45ms)--C
在使用服务供应商提供的节点,测试延迟在100ms左右。其中B到服务商之间的延迟无法直接测试,但可以猜测出来25ms左右。下载峰值速度25Mbps左右,接近A的最大带宽,损耗非常小。

戏说套娃

这种套娃可玩的花样还是非常多的,这篇文章当是抛砖引玉了。套娃固然有趣,但是也不能忘了我们最终的目的,我只是想尽量利用带宽,在能满足这一目的的前提下,尽量减少各种协议嵌套带来的开销才是正解。

最后修改:2023 年 05 月 18 日
如果觉得我的文章对你有用,请随意赞赏