Skip to content

[New Protocol] VLESS BETA:性能至上、可扩展性空前,目标是全场景终极协议。 #2636

@RPRX

Description

@RPRX

官网文档:https://www.v2fly.org/config/protocols/vless.html

终极配置:VLESS over TCP with XTLS + 回落 & 分流 to WHATEVER


2020-07-31 更新:VLESS PREVIEW 1.1 已于两天前合并进 v2ray-core 公测,将出现在 v4.27.0 版本中。

2020-07-23 更新:加入了判断首包长度(原创)的协议回落模式,PREVIEW 版本将很快发布(在此之后,BETA 系列仍会继续)。


这份说明本来应该于一周前 VLESS BETA 发布时就写好的,但因为实在是很麻烦(每次写说明比写代码麻烦多了),所以就鸽到了今天。。。不过上次也差不多。与上一份说明 #2583 不同,这次更加注重 VLESS 本身的设计。

代码在 rprx/v2ray-vless,它是 v2fly/v2ray-core 的超集,只是多了 VLESS 协议,其它的完全一样。releases 已补充历版 VLESS Changes,有已编译好的 Windows & Linux x64 版本,交叉编译可参考 v2ray/discussion#756

若你正在使用 TLS,简单地将 VMess 改为 VLESS 就可以获得性能提升:

  1. 替换服务端和客户端的可执行文件。
  2. 将两边的协议都改为 "vless",具体参考 VLESS 配置文档,相较于 VMess 的只有少许改动。

这只是 VLESS 的短期意义,它的长期意义是:可扩展性空前,适合随意组合、全场景广泛使用,符合很多人的设想、几乎所有人的需求,足以成为 v2ray 的下一代主要协议,乃至整个 XX 界的终极协议。

那么,是时候更新一下对 VLESS 的认知了。

Request & Response

1 字节 16 字节 1 字节 M 字节 1 字节 2 字节 1 字节 S 字节 X 字节
协议版本 等价 UUID 附加信息长度 M 附加信息 ProtoBuf 指令 端口 地址类型 地址 请求数据
1 字节 1 字节 N 字节 Y 字节
协议版本,与请求的一致 附加信息长度 N 附加信息 ProtoBuf 响应数据

VLESS 早在第二个测试版 ALPHA 2 时就已经是上述结构了(BETA 是第五个测试版):

“响应认证”被替换为“协议版本”并移至最前,使 VLESS 可以升级换代,同时消除了生成伪随机数的开销。混淆相关结构被替换为附加信息(ProtoBuf)并前移,赋予协议本身可扩展性,相关开销也极小(gogo/protobuf),若无附加信息则无相关开销。

我一直觉得“响应认证”不是必要的,ALPHA 时为了提升生成随机数的性能,还用 math/rand 替换 crypto/rand,而现在都不需要了。

“协议版本”不仅能起到“响应认证”的作用,还赋予了 VLESS 无痛升级协议结构的能力,带来无限的可能性。
“协议版本”在测试版本中均为 0,正式版本中为 1,以后若有不兼容的协议结构变更则应升级版本。

VLESS 服务端的设计是 switch version,即同时支持所有 VLESS 版本。若需要升级协议版本(可能到不了这一步),推荐的做法是服务端提前一个月支持,一个月后再改客户端。VMess 请求也有协议版本,但它的认证信息在外面,指令部分则高度耦合且有固定加密,导致里面的协议版本毫无意义,服务端也没有进行判断,响应则没有协议版本。Trojan 的协议结构中没有协议版本。

接下来是 UUID,我本来觉得 16 字节有点长,曾经考虑过缩短它,但后来看到 Trojan 用了 56 个可打印字符(56 字节),就彻底打消了这个念头。服务端每次都要验证 UUID,所以性能也很重要:VLESS 的 Validator 经历了多次重构/升级,相较于 VMess,它十分简洁且耗资源很少,可以同时支持非常多的用户,性能也十分强悍,验证速度极快(sync.Map)。API 动态增删用户则更高效顺滑。

引入 ProtoBuf 是一个创举,等下会详细讲解。“指令”到“地址”的结构目前与 VMess 完全相同,同样支持 Mux。

总体上,ALPHA 2 到 BETA 主要是:结构进化、清理整合、性能提升、更加完善。这些都是一点一滴的,详见 VLESS Changes

ProtoBuf

似乎只有 VLESS 可选内嵌 ProtoBuf,它是一种数据交换格式,信息被紧密编码成二进制,TLV 结构(Tag Length Value)。

起因是我看到一篇文章称 SS 有一些缺点,如没有设计错误回报机制,客户端没办法根据不同的错误采取进一步的动作。
(但我并不认同所有错误都要回报,不然防不了主动探测。下一个测试版中,服务器可以返回一串自定义信息。)
于是想到一个可扩展的结构是很重要的,未来它也可以承载如动态端口指令。不止响应,请求也需要类似的结构。
本来打算自己设计 TLV,接着发觉 ProtoBuf 就是此结构、现成的轮子,完全适合用来做这件事,各语言支持等也不错。

目前“附加信息”只有 Scheduler 和 SchedulerV,它们是 MessName 和 MessSeed 的替代者,当你不需要它们时,“附加信息长度”为 0,也就不会有 ProtoBuf 序列化/反序列化的开销。其实我更愿意称这个过程为“拼接”,因为 pb 实际原理上也只是这么做而已,相关开销极小。拼接后的 bytes 十分紧凑,和 ALPHA 的方案相差无几,有兴趣的可以分别输出并对比。

为了指示对附加信息(Addons,也可以理解成插件,以后可以有很多个插件)的不同支持程度,下个测试版会在“附加信息长度”前新增“附加信息版本”。256 - 1 = 255 字节是够用且合理的(65535 就太多了,还可能有人恶意填充),现有的只用了十分之一,以后也不会同时有那么多附加信息,且大多数情况下是完全没有附加信息的。真不够用的话,可以升级 VLESS 版本。

为了减少逻辑判断等开销,暂定 Addons 不使用多级结构。一个月前出现过“可变协议格式”的想法,pb 是可以做到打乱顺序,但没必要,因为现代加密的设计不会让旁观者看出两次传输的头部相同。

下面介绍 Schedulers 和 Encryption 的构想,它们都是可选的,一个应对流量时序特征问题,一个应对密码学上的问题。

Schedulers Flow

中文名暂称:流量调度器(2020-09-03 更新:中文名确定为“流控”),指令由 ProtoBuf 承载,控制的是数据部分。

我之前发现,VMess 原有的 shake “元数据混淆”在 TLS 上完全不会带来有意义的改变,只会降低性能,所以 VLESS 弃用了它。并且,“混淆”这个表述容易被误解成伪装,也弃用了。顺便一提,我一直是不看好伪装的:做不到完全一样,那不就是强特征吗?做得到完全一样,那为什么不直接用伪装目标?我一开始用的是 SSR,后来发现它只是表面伪装骗运营商,就再也没用过了。

那么,“流量调度器”要解决什么问题?它影响的是宏观流量时序特征,而不是微观特征,后者是加密要解决的事情。流量时序特征可以是协议带来的,比如 Socks5 over TLS 时的 Socks5 握手 ,TLS 上不同的这种特征对于监测者来说就是不同的协议,此时无限 Schedulers 就相当于无限协议(重新分配每次发送的数据量大小等)。流量时序特征也可以是行为带来的,比如访问 Google 首页时加载了多少文件、顺序、每个文件的大小,多套一层加密并不能有效掩盖这些信息。

Schedulers 没必要像下面的 Encryption 一样整个套在外面,因为头部的一丁点数据相对于后面的数据量来说太微不足道了。

BETA 2 预计推出两个初级的 Scheduler:Zstd 压缩、数据量动态扩充。进阶操作才是从宏观层面来控制、分配,暂时咕咕。

Encryption

与 VMess 的高度耦合不同,VLESS 的服务端、客户端不久后可以提前约定好加密方式,仅在外面套一层加密。这有点类似于使用 TLS,不影响承载的任何数据,也可以理解成底层就是从 TLS 换成预设约定加密。相对于高度耦合,这种方式更合理且灵活:一种加密方式出了安全性问题,直接扔掉并换用其它的就行了,十分方便。VLESS 服务端还会允许不同的加密方式共存。

对比 VMess,VLESS 相当于把 security 换成 encryption,把 disableInsecureEncryption 换成 decryption,就解决了所有问题。目前 encryption 和 decryption 只接受 "none" 且不能留空(即使以后有连接安全性检查),详见 VLESS 配置文档。encryption 并不需要往外移一级,一是因为无法复用很多代码,二是因为会影响控制粒度,看未来的应用就明白了。

加密支持两类形式,一类是加密完全独立,需要额外密码,适合私用,另一类是结合已有的 UUID 来加密,适合公用。
(若用第一类加密形式,且密码是以某种形式公开的,比如多人共用,那么中间人攻击就不远了)
重新设计的动态端口可能会随加密同时推出,指令由 ProtoBuf 承载,具体实现和 VMess 的动态端口也会有很多不同。

套现成加密是件很简单的事情,也就多一层 writer & reader。BETA 3 预计支持 SS 的 aes-128-gcm 和 chacha20-ietf-poly1305:
客户端的 encryption 可以填 “auto: ss_aes-128-gcm_0_123456, ss_chacha20-ietf-poly1305_0_987654”,auto 会选择最适合当前机器的,0 代表测试版,最后的是密码。服务端的 decryption 也是类似填法,收到请求时会逐一尝试解密。

并不是所有组合都需逐一尝试:VMess 的加密分为三段,第一段是认证信息,结合了 UUID、alterId、时间因素,第二段是指令部分,以固定算法加密,指令中含有数据部分使用的加密算法,第三段才是重要的数据部分。可以看出,VMess 的加解密方式实际上是多对一(服务端适配),而不仅是结合 UUID。但仅是结合 UUID 来加密也是件相对麻烦的事情,短时间内不会出,鉴于我们现在有 VMessAEAD 可用,也并不着急。若 VLESS 推出了结合 UUID 的加密方式,相当于重构了整个 VMess。

UDP issues

由于 VLESS 是对 VMess 的精简且同样存在于 v2ray 中,目前 VLESS 的 UDP 能力与 VMess 完全相同,也就是:

  1. 整个 v2ray 对 UDP 的处理广泛存在问题,这是影响 FullCone 实现的障碍之一,参考 对上游应用实现 Cone NAT 提供支持 #1429 (comment)
  2. VMess 没有为 UDP 建立持久的隧道,这是影响 FullCone 实现的障碍之二,参考 https://trojan-gfw.github.io/trojan/protocol
  3. VMess 只支持 UDP over TCP,不支持直接 UDP(至于 mKCP、QUIC,它们不属于 VMess,且 UDP 实际上经历了两次转换)

对于第一个问题,要改 v2ray 很多地方的代码,希望 @xiaokangwang 大佬可以解决。

对于第二个问题,我先几句话讲清突破本地的层层 NAT 限制实现 FullCone 的原理:

简单来说就是把远端 VPS 的一些 UDP 端口“映射”到本机,使其效果完全等同于本机的端口(但本机的端口并不一定被实际占用)。因为 VPS 一般都是有公网 IP 的,NAT 环境最佳,核心原理就是利用 VPS 的公网 IP 来提升本机的 NAT 等级,或者某个游戏/应用的实际 NAT 等级。此时它们使用的实际上是远端 VPS 的端口,就绕过了本地的层层 NAT 限制。而要做到保持映射,至少连接要保持住。

VLESS BETA 系列后期,预计客户端可选:

"udp": {
    "nat": "origin" / "tunnel" / "tunnels",
    "tcp": true / false
}

Sharing link

为了避免 VMess 分享链接生态混乱的情况,VLESS 要求图形客户端等仅支持官方分享链接标准,即将出炉。

一开始,我只是简化了 VMess,并推出 VLESS 协议,追求性能极限,不断进行优化,同时注重可扩展性,为下一步铺路。

而现在,不同于前几个版本主要做减法,BETA 系列的目标是好好利用可扩展性,做可选的加法,成为终极协议。

我一直在强调,这些加法都是可选的,如果你不需要那就不用开启,并不影响性能极限,这也是最重要的、贯穿始终的。

到处可扩展、可组合、层层完美解耦不失性能,并不只是我一个人的设想,很多人都是这么想的,这个解决方案可以使所有人满意。

而 VLESS 将会成为有史以来可解耦性(性能)、可扩展性(功能)、安全性等都最强大、完美的终极协议,一个协议解决所有问题。

当然,这也离不开 v2ray 已有的模块,感谢大家的贡献。我相信对于 v2ray 来说,这也会是一个全新的开始。

若还有什么没考虑到的情况,欢迎提出改进建议,这个 issue 还会持续跟进 VLESS 的更新。

LESS IS MORE.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions