TCP Analysis Flags 之 TCP Fast Retransmission

news/2024/12/19 15:19:50/

前言

默认情况下,Wireshark 的 TCP 解析器会跟踪每个 TCP 会话的状态,并在检测到问题或潜在问题时提供额外的信息。在第一次打开捕获文件时,会对每个 TCP 数据包进行一次分析,数据包按照它们在数据包列表中出现的顺序进行处理。可以通过 “Analyze TCP sequence numbers” TCP 解析首选项启用或禁用此功能。

TCP 分析展示

在数据包文件中进行 TCP 分析时,关于 “TCP Fast Retransmission” 一般是如下显示的,包括:

  1. Packet List 窗口中的 Info 信息列,以 [TCP Fast Retransmission] 黑底红字进行标注;
  2. Packet Details 窗口中的 TCP 协议树下,在 [SEQ/ACK analysis] -> [TCP Analysis Flags] 中定义该 TCP 数据包的分析说明。

  1. 考虑到 TCP 乱序、重传场景的复杂性,专家信息在重传的判断上,前面都会有一个(suspected),表示疑似,说明并不是百分百正确,属于 Note 注意。
  2. 另在专家信息中,对于 TCP Fast Retransmission 标志位分析同时会增加两种 Note,包括 Fast Retransmission 和 Retransmission。

TCP Fast Retransmission 定义

文档中关于 TCP Fast Retransmission 的定义看起来简单,但实际考虑到 TCP 乱序、重传场景的复杂性,在 TCP 分析中对于 TCP Fast Retransmission 是与 TCP Spurious RetransmissionTCP Out-Of-OrderTCP Retransmission 等在一起判断标记乱序或重传类型,而在不少场景还会有判断出错的问题,当然 Wireshark 考虑到这种情况,也有手动修正的选项,这正好也侧面证明了上面的说法,关于 TCP 乱序、重传的复杂性。

TCP Fast Retransmission 的定义如下,当以下所有条件都为真时设置:

  • 不是 Keep-Alive 数据包
  • 同方向 TCP 段大小大于零或设置了 SYN/FIN 标志位
  • 同方向之前下一个期望的 Seq Num 大于当前数据包的 Seq Num
  • 反方向至少有两个重复 ACK
  • 当前数据包的 Seq Num 等于反方向之前下一个期望的 ACK Num
  • 在不到 20 毫秒前看到最后一次 ACK

替代 Out-Of-OrderRetransmission

Set when all of the following are true:This is not a keepalive packet.
In the forward direction, the segment size is greater than zero or the SYN or FIN is set.
The next expected sequence number is greater than the current sequence number.
We have at least two duplicate ACKs in the reverse direction.
The current sequence number equals the next expected acknowledgment number.
We saw the last acknowledgment less than 20ms ago.Supersedes “Out-Of-Order” and “Retransmission”.

关于 TCP 快速重传,这里相较之前的 TCP 分析标志位,有几个不同的判断条件:

  1. 首先是反方向的重复 ACK,一般来说出现三个 TCP Dup ACK 会触发出标准的 TCP 快速重传(无 SACK 的场景下),但是在 Wireshark 的定义以及分析代码中关于 TCP Fast Retransmission 判断所依赖的 Dup ACK 数量为 2 ,即 Dup ACK 出现 2 次再结合其他条件满足之后,就会标记为快速重传。 切记~

一般来说,如果是标准的 TCP 快速重传,Dup ACK 两次是无法触发生成的,但是这里需要想明白的是因果关系,数据包跟踪文件是已然存在的,2 个 Dup ACK + TCP 重传数据包也是存在的,Wireshark 仅仅是分析工具,它在此认为的一种可能场景是,3 个 Dup ACK 丢失了一个,所以在 Dup ACK 大于等于 2 的时候就会标记该 TCP 重传数据包为 TCP 快速重传数据包。

  1. 以上一直强调说是无 SACK 的场景以及所谓的标准的 TCP 快速重传,是因为区别于有 SACK 的场景下,触发出快速重传的是 SACK 块数,而不是依赖于 Dup ACK 的个数 3,之后会单独文章解释。
  2. Next expected acknowledgment number,这也是在某段时间带给我梦魇的一个定义(官方文档),从 TCP 的角度当时真的没见过对于 ACK Num 也有所谓的下一个期望一说,之前遍寻答案却一无所获,但不知道什么时候就悟了,它实际是指的是反方向之前的 LastACK Num,而且我现在也依然认为描述不是太清晰,或者是我英语太渣。😅
  3. 关于 20ms 的定义,同样貌似在 Linux TCP 实现中没有相关的定义,应该是 Wireshark 判断快速重传的一个时间范围,在 20ms 内的重传再结合其他条件满足之后,就会标记为快速重传,而超过了 20ms,重传在时间上的判断,更加像是超时重传等其他重传,并不会被 Wireshark 认为是快速重传。

具体的代码如下,总的来说这段代码是 Wireshark 中 TCP 分析模块的一部分,用于检测和标识 TCP 数据包中的各种重传类型。它的主要功能是根据当前数据包的序列号、长度、标志位以及之前收到的 TCP 数据包的信息,判断当前数据包是否属于重传,如果是则进一步确定它属于哪种重传类型。

根据分析 TCP 数据包的各种特征,对重传数据包进行分类,有助于更好地理解 TCP 连接中的重传行为,对于诊断网络问题很有帮助。这段代码的主要逻辑如下,如果所有下述条件均满足,则认为该数据包是一个快速重传包。

第一类情况:

  • 序列号未递增;
  • 至少有两个重复 ACK;
  • 当前数据包的 Seq Num 等于反方向之前的 LastACK Num;
  • 时间间隔<20ms,即在不到 20 毫秒前看到最后一次 ACK。

第二类情况(SACK):

  • 序列号未递增;
  • 时间间隔<20ms,即在不到 20 毫秒前看到最后一次 ACK;
  • 至少有两个重复 ACK;
  • 有 SACK 信息,但当前包未被 SACK 确认。
    /* RETRANSMISSION/FAST RETRANSMISSION/OUT-OF-ORDER* If the segment contains data (or is a SYN or a FIN) and* if it does not advance the sequence number, it must be one* of these three.* Only test for this if we know what the seq number should be* (tcpd->fwd->nextseq)** Note that a simple KeepAlive is not a retransmission*/if (seglen>0 || flags&(TH_SYN|TH_FIN)) {gboolean seq_not_advanced = tcpd->fwd->tcp_analyze_seq_info->nextseq&& (LT_SEQ(seq, tcpd->fwd->tcp_analyze_seq_info->nextseq));guint64 t;guint64 ooo_thres;if(tcpd->ta && (tcpd->ta->flags&TCP_A_KEEP_ALIVE) ) {goto finished_checking_retransmission_type;}/* This segment is *not* considered a retransmission/out-of-order if*  the segment length is larger than one (it really adds new data)*  the sequence number is one less than the previous nextseq and*      (the previous segment is possibly a zero window probe)** We should still try to flag Spurious Retransmissions though.*/if (seglen > 1 && tcpd->fwd->tcp_analyze_seq_info->nextseq - 1 == seq) {seq_not_advanced = FALSE;}...nextseq = seq+seglen;gboolean precedence_count = tcp_fastrt_precedence;do {switch(precedence_count) {case TRUE:/* If there were >=2 duplicate ACKs in the reverse direction* (there might be duplicate acks missing from the trace)* and if this sequence number matches those ACKs* and if the packet occurs within 20ms of the last* duplicate ack* then this is a fast retransmission*/t=(pinfo->abs_ts.secs-tcpd->rev->tcp_analyze_seq_info->lastacktime.secs)*1000000000;t=t+(pinfo->abs_ts.nsecs)-tcpd->rev->tcp_analyze_seq_info->lastacktime.nsecs;if( seq_not_advanced&&  tcpd->rev->tcp_analyze_seq_info->dupacknum>=2&&  tcpd->rev->tcp_analyze_seq_info->lastack==seq&&  t<20000000 ) {if(!tcpd->ta) {tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd);}tcpd->ta->flags|=TCP_A_FAST_RETRANSMISSION;goto finished_checking_retransmission_type;}/* Look for this segment in reported SACK ranges,* if not present this might very well be a FAST Retrans,* when the conditions above (timing, number of retrans) are still true */if( seq_not_advanced&&  t<20000000&&  tcpd->rev->tcp_analyze_seq_info->dupacknum>=2&&  tcpd->rev->tcp_analyze_seq_info->num_sack_ranges > 0) {gboolean is_sacked = FALSE;int i=0;while( !is_sacked && i<tcpd->rev->tcp_analyze_seq_info->num_sack_ranges ) {is_sacked = ((seq >= tcpd->rev->tcp_analyze_seq_info->sack_left_edge[i++])&& (nextseq <= tcpd->rev->tcp_analyze_seq_info->sack_right_edge[i]));}/* fine, it's probably a Fast Retrans triggered by the SACK sender algo */if(!is_sacked) {if(!tcpd->ta)tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd);tcpd->ta->flags|=TCP_A_FAST_RETRANSMISSION;goto finished_checking_retransmission_type;}}precedence_count=!precedence_count;break;...}finished_checking_retransmission_type:
  1. next expected sequence number,为 nextseq,定义为 highest seen nextseq。
  2. lastack,定义为 Last seen ack for the reverse flow。
  3. 个人理解,不论是标准或是SACK情况下,Wireshark 对于快速重传的定义和 Linux 实现会有些许不一样, 在之后文章会再展开说明。

Packetdrill 示例

根据上述 TCP Fast Retransmission 定义和代码说明,通过 packetdrill 模拟连续传入数据分段,但丢失了一个,因此会触发出三个 TCP Dup ACK 数据包,之后再传入这个丢失的数据分段,则会认为是 TCP 快速重传数据包。

# cat tcp_fast_retrans.pkt 
0   socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0  setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0  bind(3, ..., ...) = 0
+0  listen(3, 1) = 0+0 < S 0:0(0) win 16000 <mss 1460>
+0 > S. 0:0(0) ack 1 <...>
+0.01 < . 1:1(0) ack 1 win 16000+0 accept(3, ..., ...) = 4
+0 < P. 1:21(20) ack 1 win 15000
+0.01 < P. 41:61(20) ack 1 win 15000
+0.01 < P. 61:81(20) ack 1 win 15000
+0.01 < P. 81:101(20) ack 1 win 15000
+0.01 < P. 21:41(20) ack 1 win 15000
# 

经 Wireshark 展示如下,可以看到满足判断条件后,No.12 标识 [TCP Fast Retransmission] ,是因为客户端发送的数据分段 No.6 Seq Num 41 和 No.4 Next Seq Num 21 之间缺少了一个长度为 20 字节的数据分段,No.6 标识为 [TCP Previous segment not captured] ,而 No.7 标识为 [TCP Dup ACK] ,紧接着之后的 No.8-9、No.10-11 同样,一共三次 [TCP Dup ACK] ,最终客户端重新发出的 No.12 Seq Num 21 + Len 20 数据包,标识为 [TCP Fast Retransmission]。

在这里也可以验证下,如果仅有两个 [TCP Dup ACK] 数据包的情况下,Wireshark 是否仍会标识为 [TCP Fast Retransmission]。代码简单修改,减少传入一次数据分段即可。

# cat tcp_fast_retrans_02.pkt 
0   socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0  setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0  bind(3, ..., ...) = 0
+0  listen(3, 1) = 0+0 < S 0:0(0) win 16000 <mss 1460>
+0 > S. 0:0(0) ack 1 <...>
+0.01 < . 1:1(0) ack 1 win 16000+0 accept(3, ..., ...) = 4
+0 < P. 1:21(20) ack 1 win 15000
+0.01 < P. 41:61(20) ack 1 win 15000
+0.01 < P. 61:81(20) ack 1 win 15000
+0.01 < P. 21:41(20) ack 1 win 15000
# 

可见,在 Wireshark 的 TCP 分析中,满足 [TCP Dup ACK]>= 2 后,即可以满足相对应的判断条件,从而 No.10 标识为 [TCP Fast Retransmission]。

当然以上两个 packetdrill 代码案例实际上并没有体现出系统内核触发快速重传的机制,纯粹是在三个 TCP Dup ACK 后手工注入重传的分段,使得 Wireshark 判断为快速重传。

那么以下再尝试另一种写法,使得内核触发出快速重传。

# cat tcp_fast_retrans_03.pkt 
0   socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0  setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0  bind(3, ..., ...) = 0
+0  listen(3, 1) = 0+0 < S 0:0(0) win 10000 <mss 1000>
+0 > S. 0:0(0) ack 1 <...>
+0.01 < . 1:1(0) ack 1 win 10000+0 accept(3, ..., ...) = 4+0.01 write(4, ..., 7000) = 7000
+0.01 < . 1:1(0) ack 2001 win 10000
+0 < . 1:1(0) ack 2001 win 10000
+0 < . 1:1(0) ack 2001 win 10000
+0 < . 1:1(0) ack 2001 win 10000
# 

在满足三次 TCP Dup ACK 后,系统内核产生 No.12 [TCP Fast Retransmission]

那么再次尝试减少 Dup ACK 的次数为 2 ,如下。

# cat tcp_fast_retrans_04.pkt 
0   socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0  setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0  bind(3, ..., ...) = 0
+0  listen(3, 1) = 0+0 < S 0:0(0) win 10000 <mss 1000>
+0 > S. 0:0(0) ack 1 <...>
+0.01 < . 1:1(0) ack 1 win 10000+0 accept(3, ..., ...) = 4+0.01 write(4, ..., 7000) = 7000
+0.01 < . 1:1(0) ack 2001 win 10000
+0 < . 1:1(0) ack 2001 win 10000
+0 < . 1:1(0) ack 2001 win 10000+0 `sleep 100000`
# 

只有两次 TCP Dup ACK ,无法触发出 [TCP Fast Retransmission],又经过 213ms 后,超时重传 No.11 Seq Num 2001 + Len 1000。

实例

关于 TCP Fast Retransmission 的实例,实际日常抓包中经常会看到,是比较常见的一种 TCP 分析信息,也会伴生着出现像是 TCP Dup ACKTCP ACKed unseen segmentTCP Previous segment not captured 等信息。

  1. 标准 TCP 快速重传

标准的 TCP 快速重传场景,3次 TCP Dup ACK + TCP Fast Retransmission 组合。客户端所发送的 No.16 数据分段在中间传输,丢了一个 Seq Num 4878 + Len 1440 的数据包,之后在服务器上陆续触发出三个 Dup ACK,最后在客户端上产生了 No.24 快速重传数据包,标识 [TCP Fast Retransmission],其中 Seq Num 4878 + Len 1440。

  1. SACK 下的 TCP 快速重传

SACK 下的 TCP 快速重传场景,服务器端所发送的数据分段在中间传输,丢了一个 Seq Num 1062833 + Len 1448 的数据包,之后在客户端上触发出两个 Dup ACK,最后在服务器端上产生了 No.1061 快速重传数据包,标识 [TCP Fast Retransmission],其中 Seq Num 1062833 + Len 1448。

但真实的情况却并不是这样,Wireshark 所判断出来的快速重传和 Linux TCP 协议栈所产生的快速重传,在实现上并不一致。Wireshark 在此认为 No.1061 是快速重传,完全是根据包括 Dup ACK >= 2 以内的逻辑条件所判断得出,而在 Linux 上触发产生 No.1061 却是因为 SACK 所确认的段 >=3,SLE=1064281,SRE=1068625,1068625-1064281/1448 = 3。

  1. TCP 快速重传还是 TCP 乱序

TCP 快速重传和乱序混淆,这似乎在目前的 Wireshark 版本中是经常可以看到的一种场景,如下案例:

  1. 服务器所发的数据包 No.20 前丢了一个 TCP 分段 Seq 9577 ,所以 No.20 标记为 TCP Previous segment not captured
  2. 客户端回应第一个 No.21 DUP ACK 确认还要 Seq 9577 分段(原 ACK 在 No.19);
  3. 服务器下一个数据包 No.22 仍不是 TCP 分段 Seq 9577;
  4. 因此客户端回应第二个 No.23 DUP ACK 确认继续要 Seq 9577 分段;
  5. 此时服务器像是因为 DUP ACK 的原因触发了快速重传,发送了 No.24 Seq 9577 数据包。

Wireshark 在此判断 No.24 为快速重传感觉确实合情合理,因为包括 DUP ACK >=2 等条件综合判断为真时就会认为是快速重传。但是细细一琢磨,会发现里面有些问题:IRTT,IRTT 为 0.103362s ,说明客户端和服务器端 RTT 约为 103ms,如果捕获点在客户端,No.24 和 No.23 之间的时间差值仅为 71ns。试问从客户端发出 No.23 到服务器收到 No.23 之后触发快速重传,再到客户端所捕获,这个 71ns 的时间完全不符合现实。

此时通过 IP.ID 加以佐证,No.24 的 IP ID 为 49749 ,在 No.20 和 No.22 之前,因此 No.24 实际上是乱序,而不是快速重传。

总结

考虑到数据包会出现乱序、重传等各类不同的场景,产生 TCP Fast Retransmission 的情形自然也是五花八门,具体问题具体分析。


http://www.ppmy.cn/news/1556414.html

相关文章

1688商品爬取:商品信息与价格接口获取指南

引言 在电商领域&#xff0c;获取商品信息和价格对于市场分析、价格监控和供应链管理至关重要。1688作为中国领先的B2B电商平台&#xff0c;提供了海量的商品数据。本文将详细介绍如何利用Java爬虫技术合法合规地获取1688商品信息和价格接口数据。 环境准备 在开始之前&…

FPGA高速下载器SZ901

SZ901基于AMD(Xilinx) Virtual Cable协议. 本设备使用千兆网络接口。基于此接口,本设备可以同时支持多达四路FPGA板卡同时调试,每组相互独立,互不干扰。 特点 1,支持JTAG 速度最高53Mb/s&#xff0c;电压范围1.2-3.3V,最高支持200cm排线 2,支持4路JTAG独立使用 3,支持多路…

半导体制造全流程

半导体制造是一个极其复杂且精密的过程&#xff0c;主要涉及将硅片加工成功能强大的芯片。以下是半导体制造的全流程概述&#xff1a; 1. 硅材料制备 硅提纯&#xff1a; 使用冶金级硅&#xff0c;进一步提纯为高纯度硅&#xff08;电子级硅&#xff09;&#xff0c;纯度可达 …

MIT S6081 2024 Lab 1 | Operating System | Notes

目录 安装与下载 实验1 开始我们的实验 sleep&#xff08;简单&#xff09; pingpong&#xff08;简单&#xff09; primes (中等)/(困难) find&#xff08;中等&#xff09; xargs&#xff08;中等&#xff09; finally Reference I. Tools Debian 或 Ubuntu Arch…

ubuntu 上怎么设置应用开机自动启动

方法 1: 使用 “启动应用程序” 工具 打开“启动应用程序”工具&#xff1a; 在 GNOME 桌面环境中&#xff0c;按 Super&#xff08;Windows&#xff09;键 或点击应用菜单&#xff0c;搜索并打开 “启动应用程序” 或 “Startup Applications”。 添加启动程序&#xff1a; 点…

STM32使用SFUD库驱动W25Q64

SFUD简介 SFUD是一个通用SPI Flash驱动库&#xff0c;通过SFUD可以库轻松完成对SPI Flash的读/擦/写的基本操作&#xff0c;而不用自己去看手册&#xff0c;写代码造轮子。但是SFUD的功能不仅仅于此&#xff1a;①通过SFUD库可以实现在一个项目中对多个Flash的同时驱动&#x…

利用开源Stable Diffusion模型实现图像压缩比竞争方法用更低的比特率生成更逼真的图像

概述 论文地址&#xff1a;https://studios.disneyresearch.com/app/uploads/2024/09/Lossy-Image-Compression-with-Foundation-Diffusion-Models-Paper.pdf 迪士尼的研究部门正在提供一种新的图像压缩方法&#xff0c;利用开源Stable Diffusion V1.2 模型&#xff0c;以比竞…

ICCAD 2024新趋势:IP企业携手为汽车和桌面等热点应用打造联合IP解决方案

作者&#xff1a;Imagination Technologies 2024年12月11日-12日&#xff0c;“上海集成电路2024年度产业发展论坛暨中国集成电路设计业展览会&#xff08;ICCAD 2024&#xff09;”在上海圆满落幕&#xff0c;本届大会参与人数超过了7000人&#xff0c;为历届ICCAD大会之最&a…