就是要你懂TCP–wireshark-dup-ack-issue
问题:
很多同学学会抓包后,经常拿着这样一个抓包来问我是怎么回事:
在wireshark中看到一个tcp会话中的两台机器突然一直互相发dup ack包,但是没有触发重传。每次重复ack都是间隔精确的20秒
如下截图:
client都一直在回复收到2号包(ack=2)了,可是server跟傻了一样居然还发seq=1的包(按理,应该发比2大的包啊)
系统配置:
net.ipv4.tcp_keepalive_time = 20
net.ipv4.tcp_keepalive_probes = 5
net.ipv4.tcp_keepalive_intvl = 3
原因:
抓包不全的话wireshark有缺陷,把keepalive包识别成了dup ack包,看内容这种dup ack和keepalive似乎是一样的,flags都是0x010。keep alive的定义的是后退一格(seq少1)。
2、4、6、8……号包,都有一个“tcp acked unseen segment”。这个一般表示它ack的这个包,没有被抓到。Wirshark如何作出此判断呢?前面一个包是seq=1, len=0,所以正常情况下是ack = seq + len = 1,然而Wireshark看到的确是ack = 2, 它只能判断有一个seq =1, len = 1的包没有抓到。
dup ack也是类似道理,这些包完全符合dup ack的定义,因为“ack = ” 某个数连续多次出现了。
这一切都是因为keep alive的特殊性导致的。打开66号包的tcp层(见后面的截图),可以看到它的 next sequence number = 12583,表示正常情况下server发出的下一个包应该是seq = 12583。可是在下一个包,也就是68号包中,却是seq = 12582。keep alive的定义的确是这样,即后退一格。
Wireshark只有在抓到数据包(66号包)和keep alive包的情况下才有可能正确识别,前面的抓包中恰好在keep alive之前丢失了数据包,所以Wireshark就蒙了。
构造重现
如果用“frame.number >= 68” 过滤这个包,然后File–>export specified packets保存成一个新文件,再打开那个新文件,就会发现Wireshark又蒙了。本来能够正常识别的keep alive包又被错看成dup ack了,所以一旦碰到这种情况不要慌要稳
下面是知识点啦
Keepalive
TCP报文接收方必须回复的场景:
TCP携带字节数据
没有字节数据,携带SYN状态位
没有字节数据,携带FIN状态位
keepalive 提取历史发送的最后一个字节,充当心跳字节数据,依然使用该字节的最初序列号。也就是前面所说的seq回退了一个
对方收到后因为seq小于TCP滑动窗口的左侧,被判定为duplicated数据包,然后扔掉了,并回复一个duplicated ack
所以keepalive跟duplicated本质是一回事,就看wireshark能够正确识别了。
Duplication ack是指:
server收到了3和8号包,但是没有收到中间的4/5/6/7,那么server就会ack 3,如果client还是继续发8/9号包,那么server会继续发dup ack 3#1 ; dup ack 3#2 来向客户端说明只收到了3号包,不要着急发后面的大包,把4/5/6/7给我发过来
TCP Window Update
如上图,当接收方的tcp Window Size不足一个MSS的时候,为了避免 Silly Window Syndrome,Client不再发小包,而是发送探测包(跟keepalive一样,发一个回退一格的包,触发server ack同时server ack的时候会带过来新的window size)探测包间隔时间是200/400/800/1600……ms这样
正常的keep-alive Case:
keep-alive 通过发一个比实际seq小1的包,比如server都已经 ack 12583了,client故意发一个seq 12582来标识这是一个keep-Alive包
Duplication ack是指:
server收到了3和8号包,但是没有收到中间的4/5/6/7,那么server就会ack 3,如果client还是继续发8/9号包,那么server会继续发dup ack 3#1 ; dup ack 3#2 来向客户端说明只收到了3号包,不要着急发后面的大包,把4/5/6/7给我发过来