基于quic-go的quic协议分析

见素抱补 2022-01-19 10:32:34

一、QUIC协议概述

1.1 QUIC协议简述

Quic 全称 quick udp internet connection,“快速 UDP 互联网连接”,(和英文 quick 谐音,简称“快”)是由 google 提出的使用 udp 进行多路并发传输的协议。

1.2 QUIC协议特性

Quic 相比现在广泛应用的 http2+tcp+tls 协议有如下优势:

  1. 减少了 TCP 三次握手及 TLS 握手时间。
    QUIC实现传输层0RTT建立连接,加密层实现0RTT建立加密连接。
  2. 改进的拥塞控制。
    QUIC 协议当前默认使用了 TCP 协议的 Cubic 拥塞控制算法 [6],同时也支持 CubicBytes, Reno, RenoBytes, BBR, PCC 等拥塞控制算法。
  3. 避免队头阻塞的多路复用。
    QUIC 一个连接上的多个 stream 之间没有依赖。这样假如 stream2 丢了一个 udp packet,也只会影响 stream2 的处理。不会影响 stream2 之前及之后的 stream 的处理。
    QUIC 最基本的传输单元是 Packet,不会超过 MTU 的大小,整个加密和认证过程都是基于 Packet 的,不会跨越多个 Packet。这样就能避免 TLS 协议存在的队头阻塞。
  4. 连接迁移。
    任何一条 QUIC 连接不再以 IP 及端口四元组标识,而是以一个 64 位的随机数作为 ID 来标识,这样就算 IP 或者端口发生变化时,只要 ID 不变,这条连接依然维持着,上层业务逻辑感知不到变化,不会中断,也就不需要重连。
    由于这个 ID 是客户端随机产生的,并且长度有 64 位,所以冲突概率非常低。
  5. 前向冗余纠错。
    在重要的包比如握手消息发生丢失时,能够根据冗余信息还原出握手消息。

1.3 quic协议分层

img

二、quic-go源码编译与测试

编译及测试环境:mac os + go@1.16

2.1 源码编译

  1. 安装go 1.16环境
    brew search go
    brew install go@1.16
    echo 'export PATH="/usr/local/opt/go@1.16/bin:$PATH"' >> ~/.zshrc
    source .zshrc
    
  2. 下载quic-go库,并进行编译
    git clone https://github.com/lucas-clemente/quic-go.git
    cd quic-go/example
    //server
    go build main.go
    ./main -qlog -v -tcp
    //client
    cd client
    go build main.go
    

2.2 测试

2.2.1 服务器端
浏览器访问
  1. firefox 浏览器打开about:config修改HTTP3值为TRUE
  2. 开启server端
    cd quic-go/example
    ./main -qlog -v -tcp
    
  3. 访问https://localhost:6121/demo/tile,打开调试工具查看request详情,第一次访问网站使用TCP协议(HTTP1.1),同时在请求头带上Alt-Svc字段,通知浏览器该服务器支持HTTP3协议

    img


    刷新页面再查看请求,即可发现协议已经使用HTTP3协议

    img

2.2.2 客户端
浏览器访问
cd quic-go/example/client
./main -v -insecure -keylog ssl.log https://quic.rocks:4433/
测试结果

img

key.log

查看key.log文件

CLIENT_HANDSHAKE_TRAFFIC_SECRET e65a96f4182847c7b5bbaa3af2e209b96d9bf35aff8c772c395f90fadcd91bef 737a56ede644a2d584be8ae45acffed9e4582a8caecd5154d503264800c0a13d
SERVER_HANDSHAKE_TRAFFIC_SECRET e65a96f4182847c7b5bbaa3af2e209b96d9bf35aff8c772c395f90fadcd91bef 071d2846bde495ddc663cbcff856580b4fd1da2c537299af8ebc26c7bf697f81
CLIENT_TRAFFIC_SECRET_0 e65a96f4182847c7b5bbaa3af2e209b96d9bf35aff8c772c395f90fadcd91bef 4fd462c58beaaef3f838a3364bbd34d84eb622f471f82ce101188c152140f607
SERVER_TRAFFIC_SECRET_0 e65a96f4182847c7b5bbaa3af2e209b96d9bf35aff8c772c395f90fadcd91bef d0ab6cc075b5f95d50511c66c25d236332850aafda8ac7e032295bfa09b86d5d

三、源码分析

3.1 header格式

根据QUIC Transport Draft可以知道quic协议使用的长短header格式如下:

img

查看源码

headers := []wire.Header{
        { // Initial without token
            IsLongHeader:     true,
            SrcConnectionID:  protocol.ConnectionID(getRandomData(3)),
            DestConnectionID: protocol.ConnectionID(getRandomData(8)),
            Type:             protocol.PacketTypeInitial,
            Length:           protocol.ByteCount(rand.Intn(1000)),
            Version:          version,
        },
        { // Initial without token, with zero-length src conn id
            IsLongHeader:     true,
            DestConnectionID: protocol.ConnectionID(getRandomData(8)),
            Type:             protocol.PacketTypeInitial,
            Length:           protocol.ByteCount(rand.Intn(1000)),
            Version:          version,
        },
        { // Initial with Token
            IsLongHeader:     true,
            SrcConnectionID:  protocol.ConnectionID(getRandomData(10)),
            DestConnectionID: protocol.ConnectionID(getRandomData(19)),
            Type:             protocol.PacketTypeInitial,
            Length:           protocol.ByteCount(rand.Intn(1000)),
            Version:          version,
            Token:            getRandomData(25),
        },
        { // Handshake packet
            IsLongHeader:     true,
            SrcConnectionID:  protocol.ConnectionID(getRandomData(5)),
            DestConnectionID: protocol.ConnectionID(getRandomData(10)),
            Type:             protocol.PacketTypeHandshake,
            Length:           protocol.ByteCount(rand.Intn(1000)),
            Version:          version,
        },
        { // Handshake packet, with zero-length src conn id
            IsLongHeader:     true,
            DestConnectionID: protocol.ConnectionID(getRandomData(12)),
            Type:             protocol.PacketTypeHandshake,
            Length:           protocol.ByteCount(rand.Intn(1000)),
            Version:          version,
        },
        { // 0-RTT packet
            IsLongHeader:     true,
            SrcConnectionID:  protocol.ConnectionID(getRandomData(8)),
            DestConnectionID: protocol.ConnectionID(getRandomData(9)),
            Type:             protocol.PacketType0RTT,
            Length:           protocol.ByteCount(rand.Intn(1000)),
            Version:          version,
        },
        { // Retry Packet, with empty orig dest conn id
            IsLongHeader:     true,
            SrcConnectionID:  protocol.ConnectionID(getRandomData(8)),
            DestConnectionID: protocol.ConnectionID(getRandomData(9)),
            Type:             protocol.PacketTypeRetry,
            Token:            getRandomData(1000),
            Version:          version,
        },
        { // Short-Header
            DestConnectionID: protocol.ConnectionID(getRandomData(8)),
        },
    }

3.2 数据流建立

QUIC协议中数据流建立的状态机如下图

img

查看crypto_stream.go可以见到

type cryptoStream interface {
    // for receiving data
    HandleCryptoFrame(*wire.CryptoFrame) error
    GetCryptoData() []byte
    Finish() error
    // for sending data
    io.Writer
    HasData() bool
    PopCryptoFrame(protocol.ByteCount) *wire.CryptoFrame
}

3.4 QUIC请求流程

首先查看cryptoStreamManager

type cryptoStreamManager struct {
    cryptoHandler cryptoDataHandler

    initialStream   cryptoStream
    handshakeStream cryptoStream
    oneRTTStream    cryptoStream
}

分为三种数据流,初始化、握手和1RTT数据流。

QUIC协议请求流程如下图

img

四、QUIC协议仍有不足

4.1 使⽤UDP不⼀定会⽐TCP快

客户端可以使用TCP和QUIC竞速

4.2 ⼈们还没有为QUIC的到来做好准备

运营商针对UDP通道⽀持不⾜,表现为不稳定
NAT局域⽹路由、交换机、防⽕墙等会禁⽌UDP 443同⾏

4.3 链路调试成本⾼

调试复杂:测试环境需要预埋证书,只能看到前期明⽂握⼿,链路调试抓包可能性为零
Wireshark + ⽇志输出
高阶调试:中间人代理 + 证书

作者NP567

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

571

社区成员

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

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