前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Quic 协议详解--包格式

Quic 协议详解--包格式

原创
作者头像
榴莲其实还可以
发布2019-01-25 11:13:19
10K0
发布2019-01-25 11:13:19
举报

前言

上一篇<Quic 协议详解--简介>中简单的讲述了讲述了Quic的一些特性,本篇将讲述quic的包类型与格式。Quic有两种类型的包:特殊包(Specail Packets)和常规包(Regular Packets)。其中特殊包有两类:版本协商包(Version Negotiation Packets)和公共重置包(Public Reset Packets)。 常规包 包含了帧(Frame)信息。所有的Quic 包的大小应该不大于MTU,以避免ip 分片。当前的Quic实现在ipv6环境每个包 最大限制为1350的字节,ipv4环境下为1370,这两个限制都不包含ip 和 udp 头。

1,Quic 公共包头(Public Packet Header)

所有的Quic包都是以一个1~51字节的公共头开始的。其格式如下

0 1 2 3 4 8

+--------+--------+--------+--------+--------+--- ---+

| Public | Connection ID (64) ... | ->

|Flags(8)| (optional) |

+--------+--------+--------+--------+--------+--- ---+

9 10 11 12

+--------+--------+--------+--------+

| QUIC Version (32) | ->

| (optional) |

+--------+--------+--------+--------+

13 14 15 16 17 18 19 20

+--------+--------+--------+--------+--------+--------+--------+--------+

| Diversification Nonce | ->

| (optional) |

+--------+--------+--------+--------+--------+--------+--------+--------+

21 22 23 24 25 26 27 28

+--------+--------+--------+--------+--------+--------+--------+--------+

| Diversification Nonce Continued | ->

| (optional) |

+--------+--------+--------+--------+--------+--------+--------+--------+

29 30 31 32 33 34 35 36

+--------+--------+--------+--------+--------+--------+--------+--------+

| Diversification Nonce Continued | ->

| (optional) |

+--------+--------+--------+--------+--------+--------+--------+--------+

37 38 39 40 41 42 43 44

+--------+--------+--------+--------+--------+--------+--------+--------+

| Diversification Nonce Continued | ->

| (optional) |

+--------+--------+--------+--------+--------+--------+--------+--------+

45 46 47 48 49 50

+--------+--------+--------+--------+--------+--------+

| Packet Number (8, 16, 32, or 48) |

| (variable length) |

+--------+--------+--------+--------+--------+--------+

其中:

public flag:

0x01 = PUBLIC_FLAG_VERSION ,表示版本协商包,

0x02 = PUBLIC_FLAG_RESET 表示 Public Reset包

0x04 表示包头包含了32字节的 Diversification Nonce

0x08 表示header中包含8字节的connection id 这个标志位在所有的包中都应该被设置

在0x30上的两位表示packet number 的直接数,其中

0x30表示packet number 有6字节

0x20表示packet number 有4字节

0x10表示2个字节

0x00表示1个字节

0x40 保留

0x80 未使用

connection ID: 64位无符号整形,由client随机产生。标示一个连接

Quic Version : 32位 表示Quic 协议版本.只有当PUBLIC_FLAG_VERSION 被设置才会存在这个字段。server 只有在client提出的版本不支持的情况下,才会置PUBLIC_FLAG_VERSION。

Packet Number: sender 会给每一个 常规包分配一个packet number,对端发送的第一个包的 number 应该是1,其后的packet number 是递增的

public flag 处理流程

图1 public flag 处理流程
图1 public flag 处理流程

2,特殊包(Special Packets)

2.1 版本协商包(Version Negotiation Packet)

0 1 2 3 4 5 6 7 8

+--------+--------+--------+--------+--------+--------+--------+--------+--------+

| Public | Connection ID (64) | ->

|Flags(8)| |

+--------+--------+--------+--------+--------+--------+--------+--------+--------+

9 10 11 12 13 14 15 16 17

+--------+--------+--------+--------+--------+--------+--------+--------+---...--+

| 1st QUIC version supported | 2nd QUIC version supported | ...

| by server (32) | by server (32) |

+--------+--------+--------+--------+--------+--------+--------+--------+---...--+

版本协商包只会由server发送,且public flag 必须置位PUBLIC_FLAG_VERSION,此外还有8直接的connection id ,余下的部分,每4个字节表示server 所支持的Quic版本。

2.2 公共重置包(PublicResetPacket)

0 1 2 3 4 8

+--------+--------+--------+--------+--------+-- --+

| Public | Connection ID (64) ... | ->

|Flags(8)| |

+--------+--------+--------+--------+--------+-- --+

9 10 11 12 13 14

+--------+--------+--------+--------+--------+--------+---

| Quic Tag (32) | Tag value map ... ->

| (PRST) | (variable length)

+--------+--------+--------+--------+--------+--------+---

这个包格式的解释暂且放下

3,常规包(Regular Packets)

常规包是经过认证和加密的,公共头(Public Header)只经过认证,并没有加密,包的剩余部分都是经过加密的。一个常规包,包含一系列的Frames

帧包(Frame Packet)

帧包包含了一系列的type 前缀的frame, 一般的帧包格式如下:

+--------+---...---+--------+---...---+

| Type | Payload | Type | Payload |

+--------+---...---+--------+---...---+

4,Quic 连接的生命周期

4.1连接的建立

Quic连接的建立由client发起,连接的建立过程中包含了版本协商和加密的握手信息 以减少建连的时延。client发给server的每一个初始包都需要置version flag 并且指定具体使用的版本号,直到收到server的包取消设置version flag 位。server 收到了client发送的第一个未设置version flag 的包后,其它所有置位 version flag 的包都将被忽视。

当server收到一个包 带一个新的 Connection id,他会将client的版本与它所能支持的版本进行比较,如果server支持,那么server在整个连接的生命周期内都将使用这个版本号,其后server发送的所有的包都会取消置位version flag

如果server 不支持 client 的版本号,1个rtt 的时延将会产生。此时server 将会发送一个版本协商包(version negotiation packet)给client。此包将会置位version flag 并且携带一系列server 所支持的版本.

当client收到server的版本协商包的时候,他会选择一个自己能支持的版本,然后使用这个版本重发所有的包。这些包必须置位version flag 并且携带版本号。之后client会收到server的常规包,它将意味着版本协商结束。此后,client发送的包就可以不用置位version flag了。

为了防止降级攻击,client在第一个包中指定的协议版本和server应答的一系列支持的版本号都必须作为加密握手包的数据部分。client需要去验证 握手过程中的server提供的版本列表和版本协议包中的列表是匹配的。server也需要验证,握手过程中client的版本,server确实不支持。此外,在连接建立的过程中,握手的时候必须协商各种传输参数,

4.2,数据传输

Quic实现了连接的可依赖,拥塞控制,流量控制。Quic连接所有数据(Ack除外)的传输,包括握手包都是作为stream的数据部分传输。

4.3,Quic流(stream)的生命周期

流(stream)是双向独立的数据,被封装在stream frame里面。streams 既可以由client创建,也可以由server端创建,也可以和其它的stream交织在一起被发送,甚至可以被取消。stream的创建是隐式的,在指定的流上发送STREAM Frame。为了避免Stream id 冲突,如果是server初始化流,stream id必须是偶数,如果是client初始化流,那么stream id 必须是奇数。0是无效的stream id,1为握手保留,当使用http2 over quic stream id为3 也是保留的。

每个方向的stream id 都必须是单调递增的。比如说stream id 为2 的流可能后与 stream id 为3的流创建,但是stream id 为 7的流,必须先于stream id为 9的流创建。此外对端收到的流可能是乱序的。比如:server 可能先收到了 stream id 为9 的第10个包, 然后再收到 stream 7 第 9个包。

如果对端收到了一个STREAM Frame 但是又不想接收,它可以立马响应一个 RTS_STREAM Frame。此后收到的数据都将被忽视。一旦流被创建,它既可以被用于接收数据,也可以用来发送数据。quic协议的任意一方都可以正常的终止一条流,有如下三种方式终止流:

1,正常终止:因为stream是双向的,所以它可以半关闭和关闭,当一方发送了一个Frame并且在此Frame中置位Fin,该stream 进处于半关闭状态,Fin 意味着发送Fin方,在这条流上不会再有数据发送。当Quic的两端都发送且收到了Fin,此时这条流就处于关闭状态。

2,突然中止 : client和server 可以再任意时刻发送RST_STREAM, RST_STREAM帧包含了一个错误码,表示失败的原因,如果RST_STREAM帧由该流的初创者发送,它表示这条流出错了,后续不会再有数据发送,如果RST_STREAM帧是由流的接收者发送,那么发送方应该停止发送数据。有这么一种情况,发送方已经发送了数据(在路上,未被确认),同时又收到了RST_STREAM, 为了确保连接级别的流量控制,即使收到了RST_STREAM帧,发送者需要确保:Fin或者或有的数据都被对方收好了,或者RST_STREAM帧被对方收到,这也同时以为着,RST_STREAM的发送方需要以适当的WINDOW_UPDATEs 继续响应到来的帧,以保证发送方发送Fin的时候不会被阻塞。

3,当连接断开的时候,流也会终止

4.4,连接终止

连接应该是一直打开的,直到连接空闲时间超过双方协商的时间。当server决定终止一个空闲连接的时候,它不应该通知client,一个Quic连接一旦被建立,可以通过如下两种方式终止

1,显示的关闭(Explicit Shutdown): 一端发送CONNECTION_CLOSE帧给 初始化连接终止的另一端,一端可能会发送GOAWAY 帧给另一端表示连接将会马上终止。GOAWAY帧的接收方依然可以处理任何活跃的流,但是发送方将不会初始化任何新的流,同时也不会接收任何新到来的流。活跃流被终止的时候,可能是收到了CONNECTION_CLOSE如果一方发送了CONNECTION_CLOSE ,但是未终止的流依然是活跃的,对端必须认为这条流是未完成的,是异常终止的

2,隐式的关闭(Implicit Shutdown): Quic连接默认的空闲超时时间是30s, 最大值为10min,如果在超时时间范围内没有活跃的流,连接将会被关闭。默认会发送一个CONNECTION_CLOSE帧。

5,帧类型与格式(Frame Type and formats)

Quic的帧包(Frame Packet)由 frame填充,包有特殊包和常规包,帧类型也有特殊帧(Special Frame Type)和常规帧(Regular Frame Type)。

5.1帧类型

5.1.1 Special Frame Type

+------------------+-----------------------------+

| Type-field value | Control Frame-type |

+------------------+-----------------------------+

| 1fdooossB | STREAM |

| 01ntllmmB | ACK |

| 001xxxxxB | CONGESTION_FEEDBACK |

+------------------+-----------------------------+

5.1.2 Regular Frame Type

+------------------+-----------------------------+

| Type-field value | Control Frame-type |

+------------------+-----------------------------+

| 00000000B (0x00) | PADDING |

| 00000001B (0x01) | RST_STREAM |

| 00000010B (0x02) | CONNECTION_CLOSE |

| 00000011B (0x03) | GOAWAY |

| 00000100B (0x04) | WINDOW_UPDATE |

| 00000101B (0x05) | BLOCKED |

| 00000110B (0x06) | STOP_WAITING |

| 00000111B (0x07) | PING |

+------------------+-----------------------------+

5.2 格式

5.2.1 STREAM Frame

STREAM Frame既用于隐式创建一条流,也用于发送数据,格式如下

0 1 … SLEN

+--------+--------+--------+--------+--------+

|Type (8)| Stream ID (8, 16, 24, or 32 bits) |

| | (Variable length SLEN bytes) |

+--------+--------+--------+--------+--------+

SLEN+1 SLEN+2 … SLEN+OLEN

+--------+--------+--------+--------+--------+--------+--------+--------+

| Offset (0, 16, 24, 32, 40, 48, 56, or 64 bits) (variable length) |

| (Variable length: OLEN bytes) |

+--------+--------+--------+--------+--------+--------+--------+--------+

SLEN+OLEN+1 SLEN+OLEN+2

+-------------+-------------+

| Data length (0 or 16 bits)|

| Optional(maybe 0 bytes) |

+------------+--------------+

STREAM Frame中各个字段的说明如下:

Type: type是一个8bit的值,包含各种flags(1fdooossB).最左边的bit 必须设置为1,表示这是一个STREAM frame, 'f' bit位是 Fin 位,当该位被设置为1时,表示发送方已经发完了,连接将处于半关闭状态。'd' bit表示在STREAM header中是否包含有数据长度,当被设置为0时,表示 知道包尾都是STREAM Frame,后续的三个'ooo'bit表示Offset 长度,分别为 0,16,24,32,40,48,58,64位 ,再接下来的两个'ss'表示stream id的长度,取值为8,16,24,32

Stream ID: 可变的无符号整形,唯一区分这条流

Offset: 可变长度,表示这块数据在整个stream 中的偏移。

Data Length: 可选的16位无符号整形,表示在这个stream frame中数据的长度。

5.2.2 ACK Frame

发送ack frame用来通知对端哪些包收到了,哪些包可能丢失了。ack 帧包括1~256个ack blocks, ack block是包的确认区间,类似于TCP的SACK blocks ,如果一个未发送的包被确认了,发送方应该总是关闭这个链接,这种机制能够防止潜在的攻击。

5.2.3 STOP_WATTING Frame

STOP_WATING Frame 帧告诉对端,它不应该再等包序号低于某个指定值的数据包了,包序号可由1,2,4,6个直接编码表示,和公共包头中的包序号长度相同。帧格式如下:

0 1 2 3 4 5 6

+--------+--------+--------+--------+--------+-------+-------+

|Type (8)| Least unacked delta (8, 16, 32, or 48 bits) |

| | (variable length) |

+--------+--------+--------+--------+--------+--------+------+

Frame Type:必须被设置为0x06表示这个是一个STOP_WATING Frame

Least Unacked Delta:可变长的包序号差值(packet number delta), 用公共头中的包序号减去它可以得到最小的未确认号(the least unacked,这个结果是发送方等待的最小的ack确认号,如果接收方丢失了比这个结果更小值的包序号,接收方应该认为这些包不可撤销的丢失了。

5.2.4 WINDOW_UPDATE Frame

WINDOW_UPDATE Frame用于通告对端接收窗口增加了,如果Stream ID 等于0,表示这是连接级别的流量控制,否则是流级别的流量控制,表示在指定的流上应该增加其流量控制窗口。帧格式如下:

0 1 4 5 12

+--------+--------+-- ... --+-------+--------+-- ... --+-------+

|Type(8) | Stream ID (32 bits) | Byte offset (64 bits) |

+--------+--------+-- ... --+-------+--------+-- ... --+-------+

Frame Type:8bit,必须设置成0x40,表示他是WINDOW_UPDATE Frame

Stream ID: 0表示连接级别的流量控制,非0 指定的流

Byte offset: 64bit 表示指定流上数据的绝对偏移。如果是连接级别的流量控制,表示所有流总数据的绝对偏移

byte offset 表示WINDOW_Update Frame 帧的接收方 在指定的stream 上只能发送这么多的数据,如果发多了,对端可能会关闭连接。如果再指定的stream id 上收到了多个WINDOW_UPDATE帧,接收方只需要记录最大最大byteoffset的帧。流和连接级别的流量控制窗口初始时都是16KB,在握手阶段会增加。在握手的过程中双方会协商SFCW(Stream Flow Control Wind) 和 CFCW(Connection Flow Control Wind)参数。

5.2.5 BLOCK Frame

BLOCK Frame 告诉对端,我有数据要发送,但是被阻塞了,它纯粹是一个通知信息帧,在调试的时候有用。接收方收到BLOCK Frame 后会打印一些日志信息,然后直接丢弃这个包。其帧结构如下:

+--------+--------+--------+--------+--------+

|Type(8) | Stream ID (32 bits) |

+--------+--------+--------+--------+--------+

Frame Type: 必须设置成0x05

5.2.6 RST_STREAM Frame

RST_STREAM Frame可以异常终止一条流,当该帧又流的创建者发送时,表示创建者希望取消这条流,当接收方发送该帧时,表示出现了错误,或者接收者不想接收这条流,应该这条流应该被关闭。帧格式如下:

0 1 4 5 12 8 16

+-------+--------+-- ... ----+--------+-- ... ------+-------+-- ... ------+

|Type(8)| StreamID (32 bits) | Byte offset (64 bits)| Error code (32 bits)|

+-------+--------+-- ... ----+--------+-- ... ------+-------+-- ... ------+

帧的各个字段:

Frame Type: 8bit 必须设置为0x01

Byte offset:最后数据部分在流的绝对偏移

Error Code: 32位错误码

CONNECTION_CLOSE Frame

用来告知对方,连接正在被关闭。如果还有流是活跃的,那么当连接被关闭的时候,所有的流也将会断开。理想情况下,应该发送一个GOAWAY帧 给予足够的时间,让所有的流主动断开。帧格式如下:

0 1 4 5 6 7

+--------+--------+-- ... -----+--------+--------+--------+----- ...

|Type(8) | Error code (32 bits)| Reason phrase | Reason phrase

| | | length (16 bits) | (variable length)

+--------+--------+-- ... -----+--------+--------+--------+----- ...

字段说明:

Frame type: 必须设置为0x02,表示该帧为CONNECTION_CLOSE Frame

Error Code : 关闭的原因

Reason Phase Length: 原因说明字段的长度

Reason Phrase: 原因说明

5.2.7 GOAWAY Frame

告诉对方,连接马上很可能被中断,应该停止使用该连接。活跃的流还可以继续处理,但是不应该再初始化创建任何流了,也不应该再接受新的流了。帧格式如下:

0 1 4 5 6 7 8

+--------+--------+-- ... -----+-------+-------+-------+------+

|Type(8) | Error code (32 bits)| Last Good Stream ID (32 bits)| ->

+--------+--------+-- ... -----+-------+-------+-------+------+

9 10 11

+--------+--------+--------+----- ...

| Reason phrase | Reason phrase

| length (16 bits)|(variable length)

+--------+--------+--------+----- ...

帧的各字段如下:

Frame tyep: 必须设置为0x03

Last Good Stream ID:GOAWAY帧发送者所接受的最后一个Stream ID.没有设置为0.

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 1,Quic 公共包头(Public Packet Header)
  • 2,特殊包(Special Packets)
    • 2.1 版本协商包(Version Negotiation Packet)
      • 2.2 公共重置包(PublicResetPacket)
      • 3,常规包(Regular Packets)
        • 帧包(Frame Packet)
        • 4,Quic 连接的生命周期
          • 4.1连接的建立
            • 4.2,数据传输
              • 4.3,Quic流(stream)的生命周期
                • 4.4,连接终止
                • 5,帧类型与格式(Frame Type and formats)
                  • 5.1帧类型
                    • 5.1.1 Special Frame Type
                    • 5.1.2 Regular Frame Type
                  • 5.2 格式
                    • 5.2.1 STREAM Frame
                      • 5.2.2 ACK Frame
                      • 5.2.3 STOP_WATTING Frame
                      • 5.2.4 WINDOW_UPDATE Frame
                      • 5.2.5 BLOCK Frame
                      • 5.2.6 RST_STREAM Frame
                      • 5.2.7 GOAWAY Frame
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档