TCP Analysis Flags 之 TCP ACKed unseen segment

前言

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

TCP 分析展示

在数据包文件中进行 TCP 分析时,关于 "TCP ACKed unseen segment" 一般是如下显示的,包括:

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

TCP ACKed unseen segment 定义

实际在 TCP 分析中,关于 TCP ACKed unseen segment 的定义非常简单,当为反方向设置了期望的下一个确认号并且它小于当前确认号时设置。

Set when the expected next acknowledgment number is set for the reverse direction and it's less than the current acknowledgment number.

具体的代码如下,涉及到 TCP 分析逻辑还是稍复杂,毕竟涉及到不同方向的 Seq 和 Ack Num 计算,其中还涉及零窗口恢复时候的的一个特殊场景。总之,代码通过识别和处理 TCP 数据流中的 "被 ACK 的丢失数据包" 情况,并调整 maxseqtobeacked变量来反映被 ACK 的最大序列号,从而准确跟踪分析 TCP 连接的状态。

    /* ACKED LOST PACKET
     * If this segment acks beyond the 'max seq to be acked' in the other direction
     * then that means we have missed packets going in the
     * other direction.
     * It might also indicate we are resuming from a Zero Window,
     * where a Probe is just followed by an ACK opening again the window.
     * See issue 8404.
     *
     * We only check this if we have actually seen some seq numbers
     * in the other direction.
     */
    if( tcpd->rev->tcp_analyze_seq_info->maxseqtobeacked
    &&  GT_SEQ(ack, tcpd->rev->tcp_analyze_seq_info->maxseqtobeacked)
    &&  (flags&(TH_ACK))!=0 ) {
        if(!tcpd->ta) {
            tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd);
        }

        /* resuming from a Zero Window Probe which re-opens the window,
         * mark it as a Window Update
         */
        if(EQ_SEQ(ack,tcpd->fwd->tcp_analyze_seq_info->lastack+1)
        && (seq==tcpd->fwd->tcp_analyze_seq_info->nextseq)
        && (tcpd->rev->lastsegmentflags&TCP_A_ZERO_WINDOW_PROBE) ) {
            tcpd->rev->tcp_analyze_seq_info->nextseq=ack;
            tcpd->rev->tcp_analyze_seq_info->maxseqtobeacked=ack;
            tcpd->ta->flags|=TCP_A_WINDOW_UPDATE;
        }
        /* real ACKED LOST PACKET */
        else {
            /* We ensure there is no matching packet waiting in the unacked list,
             * and take this opportunity to push the tail further than this single packet
             */

            guint32 tail_le = 0, tail_re = 0;
            for(ual=tcpd->rev->tcp_analyze_seq_info->segments; ual; ual=ual->next) {

                if(tail_le == tail_re) { /* init edge values */
                    tail_le = ual->seq;
                    tail_re = ual->nextseq;
                }

                /* Only look at what happens above the current ACK value,
                 * as what happened before is definetely ACKed here and can be
                 * safely ignored. */
                if(GE_SEQ(ual->seq,ack)) {

                    /* if the left edge is contiguous, move the tail leftward */
                    if(EQ_SEQ(ual->nextseq,tail_le)) {
                        tail_le = ual->seq;
                    }

                    /* otherwise, we have isolated segments above what is being ACKed here,
                     * and we reinit the tails with the current values */
                    else {
                        tail_le = ual->seq;
                        tail_re = ual->nextseq; // move the end tail
                    }
                }
            }

            /* a tail was found and we can push the maxseqtobeacked further */
            if(EQ_SEQ(ack,tail_le) && GT_SEQ(tail_re, ack)) {
                tcpd->rev->tcp_analyze_seq_info->maxseqtobeacked=tail_re;
            }

            /* otherwise, just take into account the value being ACKed now */
            else {
                tcpd->rev->tcp_analyze_seq_info->maxseqtobeacked=ack;
            }

            tcpd->ta->flags|=TCP_A_ACK_LOST_PACKET;
        }
    }
  1. next expected sequence number,为 nextseq,定义为 highest seen nextseq。
  2. lastack,定义为 Last seen ack for the reverse flow。
  3. maxseqtobeacked,定义为 highest seen continuous seq number (without hole in the stream) from the fwd party, this is the maximum seq number that can be acked by the rev party in normal case.If the rev party sends an ACK beyond this seq number it indicates TCP_A_ACK_LOST_PACKET condition。

Packetdrill 示例

根据上述 TCP ACKed unseen segment 定义和代码说明,对于通过 packetdrill 模拟 TCP 连接通讯,仔细琢磨了下,发现不好直接模拟出来,因为 TCP ACKed unseen segment 的现象较为普遍的场景是没有捕获到被 ACK 的数据分段,重点是没有捕获到,而实际端到端 TCP 通讯是正常收到数据分段并确认的。

因此首先第一步简单通过 packetdrill 模拟出一个完整的数据通讯即可,并通过 tcpdump 捕获数据包后,经 Wireshark 展示如下。

# cat tcp_acked_unseen_segment.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 < P. 21:41(20) ack 1 win 15000
+0 < P. 41:61(20) ack 1 win 15000
+0.1 read(4, ..., 60) = 60 
# 

第二步通过 Wireshark 忽略掉 No.6 数据包即可,也就是说这个 TCP 流中没有捕获到该分段,这样在 No.7 数据包中会判断为 [TCP ACKed unseen segment],因为在反方向最大的可确认的 Seq Num 是 No.4 中的 21 ,小于 No.7 的 ACK Num 41 。

实例

关于 TCP ACKed unseen segment 的实例,实际日常抓包中经常会看到,是比较常见的一种 TCP 分析信息,多数情况下可认为就是没有捕获到相应的数据分段。当然不同的场景,也会伴生着出现像是 TCP Previous segment not caputredTCP Spurious Retransmission 等信息。

  1. 未捕获到数据分段

最常见的未捕获到一个或多个数据分段时的情景,在这个 TCP 流中没有捕获到反方向从 Seq 1 NextSeq 2401 的分段,这样在 No.120 数据包中会判断为 [TCP ACKed unseen segment],因为在反方向最大的可确认的 Seq Num 是 No.119 中的 1 ,小于 No.120 的 ACK Num 2401 。

  1. 零窗口的特例

关于从零窗口探测 (Zero Window Probe) 状态恢复的情况,代码判断如果满足以下三个条件:

  1. ACK 值正好是上次 ACK 值加 1。
  2. 当前序列号等于期望的下一个序列号。
  3. 之前的数据段是一个零窗口探测包。

那么就将当前包标记为窗口更新 (Window Update),并更新一些序列号相关的变量。这种情况下不认为是 "被ACK的丢失数据包" 。

以上是关于零窗口探测状态恢复的代码部分解读,结合以下这个实际案例来看,实际是没有出现未捕获数据包的情形的,也因此一开始我对 No.12 标识成 [TCP Window Update] 会感觉疑惑,因为首先需要满足 GT_SEQ(ack, tcpd->rev->tcp_analyze_seq_info->maxseqtobeacked),也就是 ack num 15622 要大于 maxseqtobeacked,而 maxseqtobeacked 不就应该是 No.11 中的 NextSeq Num 15622 嘛??? 既然相等,也就没有后面代码执行的逻辑,也就不会判断标识成 [TCP Window Update] 了。。。

但为什么实际会出现 [TCP Window Update] 呢?各方寻求帮助,再加上不断找寻案例,最后在代码中找到了相关说明,确实是零窗口的特例,简单来说就是 [TCP ZeroWindowProbe] 的 NextSeqNum 会被排除,不会更新 maxseqtobeacked ,因此对于上面的案例,maxseqtobeacked 的值仍是 No.5 的 NextSeqNum 15621,所以 GT_SEQ 成立。

    /* Store the highest continuous seq number seen so far for 'max seq to be acked',
     * so we can detect TCP_A_ACK_LOST_PACKET condition.
     * If this ever happens, this boundary value can "jump" further in order to
     * avoid duplicating multiple messages for the very same lost packet. See later
     * how ACKED LOST PACKET are handled.
     * Zero Window Probes are logically left out at this moment, but if their data
     * really were to be ack'ed, then it will be done later when analyzing their
     * Probe ACK (be it a real Probe ACK, or an ordinary ACK moving the RCV Window).
     */
    if(EQ_SEQ(seq, tcpd->fwd->tcp_analyze_seq_info->maxseqtobeacked) || !tcpd->fwd->tcp_analyze_seq_info->maxseqtobeacked) {
        if( !tcpd->ta || !(tcpd->ta->flags&TCP_A_ZERO_WINDOW_PROBE) ) {
            tcpd->fwd->tcp_analyze_seq_info->maxseqtobeacked=tcpd->fwd->tcp_analyze_seq_info->nextseq;
        }
    }
  1. TCP Keep-Alive 的特例

实际并没有未捕获到数据分段 ,但仍判断为 [TCP ACKed unseen segment] 的特殊场景。

判断为 [TCP ACKed unseen segment] ,是因为在反方向最大的可确认的 Seq Num 是 No.1 中的 1 ,小于 No.2 的 ACK Num 2 ,但如一开始所述,它仅仅是符合了 [TCP ACKed unseen segment] 的代码逻辑,但真实情况并非这样。

细看下边的数据包,可以发现 No.1 和 No.2 实际是一对 [TCP Keep-Alive][TCP Keep-Alive ACK] ,通过 ip.id 辅助查看,也可以发现 No.1 和 No.3 是连续的数据包,中间并没有多余的数据包,也就是说并不存在数据分段未被捕获到的情形。

该案例的详细情况,可见《Wireshark TS | 丢包?不要轻易下结论续》

  1. 消失的 TCP ACKed unseen segment

对于这一种案例,从目前的代码上来说,对于 No.6 确实不会标识成 [TCP ACKed unseen segment] ,因为满足不了 GT_SEQ 的判断。但如下图示,可以很容易看到未捕获到 No.4 和 No.5 之间理应存在的两个分段 Seq 2401 NextSeq 4801,但是在 No.6 ACK 却确认了 6001 以前的所有数据,这不就是一种很明显的 ACKed unseen segment 的情形嘛。

目前我无法确认是否还有一些特殊的情况考虑在内,在之后的某一天也许可以就这样的案例发起一个 TCP 解析器增强功能的请求。

总结

总的来说,[TCP ACKed unseen segment] 实质上没有任何真实的业务影响,你是否明白了其中的真正原理呢。

相关推荐
黑客Ash1 小时前
【D01】网络安全概论
网络·安全·web安全·php
->yjy1 小时前
计算机网络(第一章)
网络·计算机网络·php
摘星星ʕ•̫͡•ʔ2 小时前
计算机网络 第三章:数据链路层(关于争用期的超详细内容)
网络·计算机网络
.Ayang3 小时前
SSRF漏洞利用
网络·安全·web安全·网络安全·系统安全·网络攻击模型·安全架构
好想打kuo碎3 小时前
1、HCIP之RSTP协议与STP相关安全配置
网络·安全
虚拟网络工程师4 小时前
【网络系统管理】Centos7——配置主从mariadb服务器案例(下半部分)
运维·服务器·网络·数据库·mariadb
JosieBook5 小时前
【网络工程】查看自己电脑网络IP,检查网络是否连通
服务器·网络·tcp/ip
inter_peng6 小时前
[Docker-显示所有容器IP] 显示docker-compose.yml中所有容器IP的方法
tcp/ip·docker·eureka
黑客Ash6 小时前
计算机中的网络安全
网络·安全·web安全