QUIC协议解析与部署

HNUlixinze 2022-01-18 23:59:24

一、QUIC协议简述

Quic 全称是“快速 UDP 互联网连接”,是由 google 提出的使用 UDP 进行多路并发传输的协议。可以用一个公式大致概括:TCP + TLS + HTTP2 = UDP + QUIC + HTTP2’s API,由公式可以看出Quic 相比现在广泛应用的 http2+tcp+tls 协议有如下优势:减少了 TCP 三次握手及 TLS 握手时间。改进的拥塞控制。避免队头阻塞的多路复用。实现了TLS的保密功能,同时又使用更少的RTT建立安全的会话。连接迁移。前向冗余纠错。QUICP协议在两个端点间创建连接,且支持多路复用连接。在设计之初,QUIC希望能够提供等同于SSL/TLS层级的网络安全保护,减少数据传输及创建连接时的延迟时间,双向控制带宽,以避免网络拥塞。Google希望使用这个协议来取代TCP协议,使网页传输速度加快,计划将QUIC提交至互联网工程任务小组(IETF),让它成为下一代的正式网络规范。

二、QUIC协议的目标

是为了整合TCP协议的可靠性和UDP协议的速度和效率。众所周知UDP比TCP传输速度快,TCP是可靠协议,但是代价就是 双方确认数据而衍生的一系列消耗。其次TCP是系统内核实现的,如果升级TCP协议,就得让用户升级系统,这个的门槛比较高,而QUIC在UPD基础上由客户端自由发挥,只要有服务器能对接就可以。

三、QUIC协议的特性

1.低延迟连接的建立

QUIC在握手过程中使用Diffie-Hellman算法协商初始密钥,初始密钥依赖于服务器存储的一组配置参数,该参数会周期性的更新。初始密钥协商成功后,服务器会提供一个临时随机数,双方根据这个数再生成会话密钥。

具体握手过程如下:

    (1) 客户端判断本地是否已有服务器的全部配置参数,如果有则直接跳转到(5),否则继续

    (2) 客户端向服务器发送inchoate client hello(CHLO)消息,请求服务器传输配置参数

    (3) 服务器收到CHLO,回复rejection(REJ)消息,其中包含服务器的部分配置参数

    (4) 客户端收到REJ,提取并存储服务器配置参数,跳回到(1) 

    (5) 客户端向服务器发送full client hello消息,开始正式握手,消息中包括客户端选择的公开数。此时客户端根据获取的服务器配置参数和自己选择的公开数,可以计算出初始密钥。

    (6) 服务器收到full client hello,如果不同意连接就回复REJ,同(3);如果同意连接,根据客户端的公开数计算出初始密钥,回复server hello(SHLO)消息,SHLO用初始密钥加密,并且其中包含服务器选择的一个临时公开数。

    (7) 客户端收到服务器的回复,如果是REJ则情况同(4);如果是SHLO,则尝试用初始密钥解密,提取出临时公开数

    (8) 客户端和服务器根据临时公开数和初始密钥,各自基于SHA-256算法推导出会话密钥

    (9) 双方更换为使用会话密钥通信,初始密钥此时已无用,QUIC握手过程完毕。之后会话密钥更新的流程与以上过程类似,只是数据包中的某些字段略有不同。

如上所述, DH密钥协商需要通行双方各自生成自己的非对称公私钥对。server端与客户端的关系是1对N的关系,明显server端生成一份公私钥对, 让N个客户端公用, 能明显减少生成开销, 降低管理成本。server端的这份公私钥对就是专门用于握手使用的,客户端一经获取,就可以缓存下来后续建连时继续使用, 这个就是达成0-RTT握手的关键, 因此server生成的这份公钥称为0-RTT握手公钥。client端首次握手时对server一无所知,需要1个RTT来询问server端的握手公钥(实际的握手交互还会发送诸如版本等其他数据)并缓存下来,本步骤只在首次建连时发生(0-RTT握手公钥的过期也会导致需要重走这一步),但这种情况很少发生。 

另外,前面提到在初始密钥后,还会再协商出一个最终的会话密钥,这么做的目的是为了获取所谓的前向安全特性: 因为server端的后面生成的这份公私钥是临时生成的,不会保存下来,也就杜绝了密钥泄漏导致会话数据被恶意收集后的被解密掉的风险。

 

 

2.改进的拥塞控制

QUIC协议当前默认使用TCP协议的Cubic拥塞控制算法。看似QUIC协议只是吧TCP的拥塞算法重新实现了一遍,其实不然。QUIC协议在TCP拥塞算法基础上做了些改进:

(1)可插拔

应用程序层面就能实现不同的拥塞控制算法,不需要操作系统或内核支持。单个应用程序的不同连接也能支持配置不同的拥塞控制。不需要停机和升级就能实现拥塞控制的变更。

(2)单调递增的Packet Number

单纯依靠严格递增的 Packet Number 肯定是无法保证数据的顺序性和可靠性。QUIC 又引入了一个 Stream Offset 的概念。即一个 Stream 可以经过多个 Packet 传输,Packet Number 严格递增,没有依赖。但是 Packet 里的 Payload 如果是 Stream 的话,就需要依靠 Stream 的 Offset 来保证应用数据的顺序。如错误! 未找到引用源。所示,发送端先后发送了 Pakcet N 和 Pakcet N+1,Stream 的 Offset 分别是 x 和 x+y。假设 Packet N 丢失了,发起重传,重传的 Packet Number 是 N+2,但是它的 Stream 的 Offset 依然是 x,这样就算 Packet N + 2 是后到的,依然可以将 Stream x 和 Stream x+y 按照顺序组织起来,交给应用程序处理。

(3)更多的ACK块

QUIC ACK帧支持256个ACK块,TCP 的 Sack 选项能够告诉发送方已经接收到的连续 Segment 的范围,方便发送方进行选择性重传。由于 TCP 头部最大只有 60 个字节,标准头部占用了 20 字节,所以 Tcp Option 最大长度只有 40 字节,再加上 Tcp Timestamp option 占用了 10 个字节 [25],所以留给 Sack 选项的只有 30 个字节。每一个 Sack Block 的长度是 8 个,加上 Sack Option 头部 2 个字节,也就意味着 Tcp Sack Option 最大只能提供 3 个 Block。但是 Quic Ack Frame 可以同时提供 256 个 Ack Block,在丢包率比较高的网络下,更多的 Sack Block 可以提升网络的恢复速度,减少重传量。

(4)精确计算RTT时间

QUIC ACK包同时携带了从收到包到回复ACK的延时,这样结合递增的包序号,能够精确的计算RTT时间

3.无队头阻塞的多路复用

HTTP2的最大特性就是多路复用,而HTTP2最大的问题就是队头阻塞。首先了解下为什么会出现队头阻塞。比如HTTP2在一个TCP连接上同时发送3个stream,其中第2个stream丢了一个Packet,TCP为了保证数据可靠性,需要发送端重传丢失的数据包,虽然这时候第3个数据包已经到达接收端,但被阻塞了。这就是所谓的队头阻塞。而QUIC多路复用可以避免这个问题,因为QUIC的丢包、流控都是基于stream的,所有stream是相互独立的,一条stream上的丢包,不会影响其他stream的数据传输。

4.前向纠错

QUIC使用了FEC(前向纠错码)来恢复数据,FEC采用简单异或的方式,每发送一组数据,包括若干个数据包后,并对这些数据包依次做异或运算,最后的结果作为一个FEC包再发送出去。接收方收到一组数据后,根据数据包和FEC包即可以进行校验和纠错。比如:10个包,编码后会增加2个包,接收端丢失第2和第3个包,仅靠剩下的10个包就可以解出丢失的包,不必重新发送,但这样也是有代价的,每个UDP数据包会包含比实际需要更多的有效载荷,增加了冗余和CPU编解码的消耗。

5.连接迁移

TCP的连接是基于4元组的,而QUIC使用64为的Connection ID进行唯一识别客户端和服务器的逻辑连接,这就意味着如果一个客户端改变IP地址或端口号,TCP连接不再有效,而QUIC层的逻辑连接维持不变,仍然采用老的Connection ID。

四、QUIC建立

1.数据加密

为了保证安全,QUIC也是加密传输数据的,所以在QUIC的建连过程中也需要双方协商出一个加密私钥。但与TLS不同,QUIC采用的加密算法仅需要一个RTT就能实现密钥交换,并且该算法也被用于目前正在草案阶段的TLS1.3协议。该就是Diffie-Hellman密钥交换算法。

可以看到,客户端和服务端各自保留了自己的私钥a和b,通过交换各自的公钥B和A,以及基底G和很大的质数P,双方就能计算出相等的私钥S,这个S就是加密传输的对称密钥。另外,根据离散对数的不可逆,即使拿到G,P,和质数B,也很难推导出私钥b(同理私钥a),也就保证了计算密钥的安全。

2.QUIC建连过程

1、客户端发起Inchoate client hello

2、服务器返回Rejection,包括密钥交换算法的公钥信息,算法信息,证书信息等被放到server config中传给客户端

3、客户端发起client hello,包括客户端公钥信息

此时,双方各自计算出了对称密钥。QUIC的1RTT建连过程结束,平均只耗时100ms以内。后续发起连接的过程中,一旦客户端缓存或持久化了server config,就可以复用并结合本地生成的私钥进行加密数据传输了,不需要再次握手,从而实现0RTT建立连接。

 

五、QUIC部署

1.部署环境

部署环境ubuntu 20.04.3

2.部署文件结构

 3.部署过程

下载chromium项目源码

git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git

在~/.bashrc中添加

export PATH="$PATH:/home/lllxxxzzz/program/quic_dev/depot_tools"

更新配置

source ~/.bashrc

mkdir chromium && cd chromium

fetch --nohooks chromium

编译出quic_server和quic_client

cd src

sudo build/install-build-deps.sh

 gclient runhooks 

gn gen out/Default 

ninja -C out/Default quic_server quic_client 

此时我们已经得到quic_server和quic_client

 在浏览器中添加证书

这里需要cd /tmp/quic-data/www.example.org , 然后 sudo vim index.html , 在header里加上 X-Original-Url: https://www.example.org/

cd net/tools/quic/certs

./generate-certs.sh

cd -

然后下载chrome浏览器,在地址栏里输入chrome://settings/search#ssl 然后选择 授权中心,点击导入 ,将net/tools/quic/certs/out/2048-sha256-root.pem导入 ,最后勾选第一项,确定即可

下一步就是跑server

./out/Default/quic_server \

  --quic_response_cache_dir=/tmp/quic-data/www.example.org \

  --certificate_file=net/tools/quic/certs/out/leaf_cert.pem \

  --key_file=net/tools/quic/certs/out/leaf_cert.pkcs8

 然后是Client

./out/Debug/quic_client --host=127.0.0.1 --port=6121 https://www.example.org/

 

作者:NP279-李昕泽

...全文
4281 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

571

社区成员

发帖
与我相关
我的任务
社区描述
软件工程教学新范式,强化专项技能训练+基于项目的学习PBL。Git仓库:https://gitee.com/mengning997/se
软件工程 高校
社区管理员
  • 码农孟宁
加入社区
  • 近7日
  • 近30日
  • 至今

试试用AI创作助手写篇文章吧