QUIC ACK frame解析
笔者在开发quic相关代码的时候,查阅了网上关于QUIC ACK frame解析的博文,发现大部分分析有想当然的嫌疑,解析过程和真正的实现差距较大,具体细节没有体现,在这里根据RFC 9000及具体的实现代码(基于XQUIC)给出真正的解析流程。
我们先看一下RFC9000对QUIC ACK frame的定义:
Type(i): 表示该帧类型为ACK,其中0x02为一般ACK frame,0x03则还需要包含到此为止在连接上接收到的带有相关ECN标记的QUIC包的累计计数。
Largest Acknowledged(i): 用于表示对端已经确认的最大包号,一般来讲是对端在生成ACK frame的时候接收到的最大包号。
ACK Delay(i): 表示编码ACK的时延,单位是毫秒,注意,这个值不是最终值,而是需要将其乘以2的ack_delay_exponent次幂,才能得到最终的
ACK Delay,这个ack_delay_exponent是本端发送给对端的一个用于编码ack delay的参数。
ACK Range Count(i): 表示当前ACK Frame中ACK Range域的数量。
ACK Range(…): 表示当前ACK Frame额外确认的范围数量,换句话说,如果一个ACK frame确认了n个连续的包号范围(例如:1-3、5-8、11-20),其ACK Range Count为n-1(例如:3-1=2)
每一个ACK Range中的元素表示一个连续的被确认的包号范围,其包含两个成员变量,Gap(i)表示距离上一个元素的距离,ACK Range Length(i)表示本元素确认的包号范围的长度。
其中,Gap是一个大于等于0的整数,当Gap=n时,表示距离上一个元素的距离为n+1,这很重要。比如,当Gap=0时,表示距离上一个元素距离为1,为什么要这么设计呢?笔者认为原因有两点,首先,距离上一个元素的距离不可能为零,否则应该在同一个ack range中。其次,这样可以让Gap可以表示的范围+1。
所以,每一个连续确认包号范围的上界应该如下计算:
而根据ACK Range Count真实值应该为每个ACK range元素确认的连续的数据包数量-1,因为根据RFC要求,根据ack range计算出来的每一个连续数据包范围均应该为闭区间:
换句话说,如果一个ACK range元素确认了17-20号,4个数据包,其ACK Range Length应为为3.
First ACK Range(i): 表示第一个被确认的连续数据包范围的长度,和ACK Range Length同理。
注意,ACK frame的确认顺序为倒序,也就是每一个ACK frame均从最大包号的数据包开始确认,直到最小值。
举例,如果一个数据包要确认的包号范围为:[1,3], [5,9], [13,20],其ack frame结构应该为:
ACK Frame{
Type: 0x02;
Largest Acknowledged: 20;
ACK Delay: 2;
ACK Range Count: 2;
First ACK Range: 7;
ACK Range{
Gap: 2;
ACK Range Length: 4;
Gap: 0;
ACK Range Length: 2;
}
}
QUIC ACK_MP frame解析
ACK_MP frame是用来辅助完成QUIC多路径传输的ACK frame扩展,在多路径传输中,如果不区分来自不同path的数据包,会导致错误的rtt计算以及双端混乱的包处理。
我们来看一下ACK_MP frame的格式:
与ACK frame不同,ACK_MP frame的Type域标记为0xbaba00和0xbaba01,被标记为0xbaba01的ACK_MP frame同样需要包含到此为止在连接上接收到的带有相关ECN标记的QUIC包的累计计数。
相比于上文提到的ACK frame,ACK_MP frame增加了Packet Number Space identifier,该标识符为本路径的DCID(不同路径的DCID不相同),如果DCID尚未被初始化,Packet Number Space identifier应该被置为0.