1 连接迁移条件判断
函数 ngx_quic_handle_frames
中处理完一个数据包中的所有帧之后, 满足以下两个条件,则表示发生了连接迁移。
- 当前数据包中没有探测帧(有探测帧表示是一个探测包,只是测试如果发生连接迁移路径是否可用,而不是真正发生连接迁移)
- 判断当前数据包的路径(pkt->path)和连接中保存的经过验证的路径(qc->path)不同
满足以上两个条件之后,调用函数
ngx_quic_handle_migration
进行下一步处理。
2 pkt->path 如何取值
函数 ngx_quic_handle_datagram
处理一个UDP数据包,这个UDP数据报中的所有的QUIC数据包有相同的客户端地址,在处理第一个QUIC数据包时,pkt->path为NULL,对其进行初始化之后,其他的QUIC数据包的path设置为第一个pkt的path。
函数 ngx_quic_handle_payload
用于处理一个QUIC数据包中的载荷,在对数据包中载荷成功解密之后,判断pkt->path是否为空,如果为空,设置pkt->path取值。
为什么不在处理QUIC数据包头中对pkt->path进行初始化?
为了在对载荷成功解密,即验证载荷正确加密之后判断是否发生了路径迁移,出于安全考虑。
pkt->path 的取值逻辑如下:
- 如果当前数据包是当前连接上的第一个包,则pkt->path = qc->path
- 如果当前数据包的客户端地址信息和经过验证或正在验证路径中的客户端地址信息相同,则pkt->path=qc->paths中的某一个
- 当前数据包的客户端地址是新的,创建一个新的path1,设置tag为PROBE(验证中),pkt->path = path1,将之前处于PROBE的路径释放
3 连接迁移处理
函数 ngx_quic_handle_migration
的处理逻辑如下:
- 当前收到的包如果不是已经收到的最大数据包号,则忽略该连接迁移,保证连接迁移之后,比发生连接迁移的数据包号更大的数据包都发向新的客户端
- 如果当前数据包的客户端地址发生变化,但是目的连接ID没有发生变化,则可能是NAT网络的原因,即非客户端主动发起的连接迁移,服务端向原来的客户端地址发起通道验证,验证老的路径现在是否可用,验证通过则设置validated = 1,否则验证失败validated = 0,并释放path
- 如果老的路径原来是经过验证的(validated = 1),则设置老的路径tag = BACKUP
- 设置qc->path = 新的路径,tag = ACTIVE,validated = 0
- 向新的路径发起通道验证,后续如果新的路径通道验证通过,则设置validated = 1,如果验证失败,释放新路径,如果有BACKUP的路径,设置BACKU路径为qc->path,tag = ACTIVE
综上:
- qc->paths中只可能有一个PROBE路径,只可能有一个BACKUP路径,只可能有一个ACTIVE路径,即正在使用的路径
- 发生连接迁移的时候,如果新路径验证失败,则获得一个之前已经验证过的BACKUP路径作为新路径,如果没有BACKUP路径可用,则无法和对端通信
- 如果因为NAT重绑定的原因发生了路径迁移,服务端会验证之前的客户端地址是否依然可用,并且先将其作为备用路径,该路径验证失败之前,如果新路径先验证失败,则将该路径设置为qc->path,如果再发现该路径验证失败,则释放该路径,无路径可用,无法和对端通信;如果该路径验证成功,则依旧使用该路径和对端通信。