freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

TCP四次挥手不同情况的研究(正常状态,三次挥手,以及CLOSING和RST)
2021-10-21 12:45:11

建议有些网络基础的人查看此文档,当然了新手仔细看也能看懂,大佬直接跳过新手加油站

1.新手加油站

1.1 TCP四次握手流程图

首先我们知道传输层TCP协议为了确保可靠连接,会有连接时的三次握手和断开连接时的四次挥手的机制

我们这里不讨论掉三次握手,来看一下正常四次挥手的流程(如下图所示)

注意:实际上client和server端都可以先发出断开连接请求,只不过下图演示的是客户端先发起关闭请求

image-20211018112539626


1.2 TCP头部

新手可能看到上面那个图一脸蒙圈,那么我来说明一下,看完说明再理解能更加明了一些(大佬直接跳过)。

首先图的左右两次代表了客户端和服务端,两方都可以先发起请求,不过演示的图片是客户端先发起的。

左边和右边表示各种状态(像终止等待,关闭等待),下边会列举。

中间的FIN,ACK,和seq和ack需要了解TCP的头部和数据部分(详解如下)

image-20211018113059154

1.首先我们发送TCP请求的时候,不光有数据,还有一些TCP协议每次发送都固定必须有的数据,这些数据存在TCP首部中,我们这里不用全知道,只做简单了解。

2.seq就是头部中的序号------可以简单理解成,假设有1万个数据,太大我们不能一次发送,所以seq可以理解成我这次发送是从这1w个里面的第几个开始的

3.ack就是对方想要的数据--------假设我们这次发送的是从500开始的,那么seq也就是500,并且假设发送了10个数据,那么对方接收到第509个数据(从500开始,发10个,500也算这次发的,所以最后是509),那么对方现在会告诉你我接到你刚才的数据了,我希望你下次发从第510个数据开始,这时ack为510

4.大写的FIN和ACK是上图头部中的6个标志位(上图的那六个字母)中的两个,它们只能为0,1。当FIN为1的时候代表这个包对方是想要跟你断开连接的包,通知你一声。而ACK代表抓个包是对方对你刚才发个它的包的回复。


1.3 四次握手流程详解

好了,简单知道了上述内容就可以看我们四次握手的流程了,分为以下四步

1.双方都是建立连接状态,客户端想要跟服务器断开连接,于是发了个包,这个包的两个标志为FIN和ACK为1,FIN为1表示想要关闭连接,而ACK为1代表是回复它上个包(因为之前他们肯定进行过数据交互,所以服务器肯定有包发送给客户端,就算没数据包发送,三次握手的时候也是有包的),同样的因为之前有过交互所以这里seq和ack的值不确定,所以用u和v来表示。

2.服务器告诉对方接收到了。但是此时还没断开连接,双方仍然可以传递数据。

3.等到服务器这时候该传的数据传完了,同样的告诉客户端我也可以跟你关闭连接了。

4.客户端回复收到了。

状态:

​ 上方是流程的一个详解,我们这里主要讨论状态,因为这跟后边四次握手的不同情况有关系。

FIN-WAIT-1:表示想主动关闭连接。向对方发送FIN报文后会进入到FIN-WAIT-1状态。

CLOSE-WAIT:表示在等待关闭。当对方发送FIN给自己,自己会回应一个ACK报文,此时进入CLOSE——WAIT状态。在此状态下,是需要考虑自己还有没有数据要发给对方,如果没有就发送FIN报文给对方。

**FIN-WAIT-2:**接收到了对方的ACK确认后就会进入该状态,并等待对方发送FIN报文。

​ 如果接收到了对方同时带FIN,和ACK的报文,就可以直接进入到了TIME-WAIT状态,而无需经过FIN-WAIT-2状态

**LAST-ACK:**被动关闭方发送FIN报文后,等待对方的ACK报文,当收到对方的ack报文后进入到close状态。

TIME-WAIT:表示主动方收到了对方的FIN报文,并发送了ACK报文,在等待2MSL后即可进入到CLOSED状态了。

​ MSL:(Maximum Segment Lifetime,最大分段生存期),是TCP报文在internet上的最长存活时间,每个TCP实现都需要一个具体的MSL,RFC 1122建议是2分钟。所以2MSL就是4分钟。

**CLOSED:**关闭状态

注意:

这里解释的状态只是正常的挥手中出现的,还有别的状态下方会讨论到。


2.针对挥手不同情况的实验和研究

​ 以上只是通常情况,四次握手会根据实际的不同,发生不同的变化,下面用Wireshark抓包分析一下。

2.1 正常情况

​ 框起来的是四次挥手,至于上边那三个,有基础的应该知道那是三次握手。可以看到 步骤和我们的之前描述的四次挥手的流程一模一样。

image-20211018133510082

1.客户端想要和服务器断开连接,标志位:FIN,ACK置为1。这里seq是1,ack是1.

2.服务器回复客户端接收到了你的请求所以标志位ACK为1。因为上个请求对方ack为1,证明想要我从第一个数据开始发,所以这次我的seq必为1,而对方上次seq是1所以这次我希望它从第2个数据开始发了,所以这次的ack为2.

3.服务器感觉事情都忙完了,发给客户端可以断开连接,FIN,ACK置为1。seq和ack都和第二步一样,因为这次他们的值还是根据第一步来的,所以值和第二步是一样一样的。

4.客户端回复服务器接收到了。因为上个服务器发来的包ack为2,所以这次我的seq为2,因为上次服务器的seq为1,所以这次我希望它从第2个数据开始发,所以ack为2。


2.2 三次挥手情况

注意:

这里我写的三次挥手情况,它只是在表面上看来是三次挥手,实际上的步骤没有改变,只不过把第二次和第三次合并了。下方是我用WireShark抓包的截图,其中中间标红的忽略掉只看绿色部分。

image-20211018120834171

我们会发现,正常的三次挥手到这里怎么变成了四次了,第二次挥手怎么没了?

​ 实际上它还是存在的,就像我上方所说,这种情况是将第二次和第三次合并了,也就是说当客户端发送了关闭连接的请求的时候,服务器因为之前跟你建立连接并且已经把你之前想要的数据传给你了,然后没事了一直等你消息,你一直没动静,早就等的不耐烦了,这时它收到了你的关闭连接请求,好家伙,爷早就不想干了,它快速反应过来我这边数据已经处理完了,于是在回复你消息收到了的同时也告诉你它也想跟你关闭连接。


2.3 CLOSING状态

参考:https://blog.csdn.net/dog250/article/details/52070680

注意:

这里重头戏来了,我们之前看了正常TCP断开连接的四次挥手过程。

当然还有看上去是经历三次挥手,实际上跟正常的四次挥手过程类似,不过是将第2,3次合并了,具体流程其实还是可以算是一样的。

特殊情况:

​ 那么还有一种情况,就是假如在相同的时间,比如12点整,这个时候客户端和服务器都想和对方断开连接,同时发给对方一个FIN包,那么两边会出现什么情况?

客户端肯定是发了一个FIN包,这时没有接到对面的ACK包,而是接到了对面的FIN包。

服务器端也是一样的,发了一个FIN包,等对面的ACK包,不过ACK包没等到,直接等到了个对方发过来的FIN包。

这时会出现一个新状态→CLOSING:

​ 一种罕见的状态,双方同时都想和对方断开连接,也就是当你发送FIN报文后,没有收到对方的ACK报文反而也收到了对方的FIN报文,那么双方都会进入到CLOSING状态,等待对方的ack报文,如果等待到了对方的ack报文,就直接关闭。

抓包分析:

​ 这里因为要构造TCP连接,所以用了packetdrill,只能在Linux系统中使用,所以人生苦短我用Kali

注意:

​ 脚本不难,但是注意抓包的时候抓的网卡一定要对,我这里抓的是any,我也不知道any是什么,但是看它的线路跟eth0几乎一样,在根据英文,盲猜就是任何网卡的包都抓的意思

脚本:

0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
0.000 bind(3, ..., ...) = 0
0.000 listen(3, 1) = 0

0.100 < S 0:0(0) win 32792 <mss 1460,sackOK,nop,nop,nop,wscale 7>
0.100 > S. 0:0(0) ack 1<...>
0.200 < . 1:1(0) ack 1 win 257
0.200 accept(3, ..., ...) = 4

// 象征性写入一些数据,装的像一点一个正常的TCP连接:握手-传输-挥手
0.250 write(4, ..., 3000) = 3000

0.300 < . 1:1(0) ack 3001 win 257

// 主动断开,发送FIN
0.400 close(4) = 0
// 在未对上述close的FIN进行ACK前,先FIN
0.500 < F. 1:1(0) ack 3001 win 260
// 至此,成功进入同时关闭的CLOSING状态。


// 由于packetdrill不能用dup调用,也好用多线程,为了维持进程不退出,只能等待
10000.000 close(4) = 0

结果如图所示:

​ kali里面自带wireshark,抓包结果如下,可以看见出现了两个FIN,ACK

这里详解一下下面几个问题:

​ 假设43515端口是客户端,8080端口是服务器

image-20211021102714278

image-20211021105418170

超时重传问题

​ 正常当同时出现FIN的时候,双方等待对方的ack并且如果接收到ACK就进入TIME-WAIT状态,等过了时间就关闭了,如果没接收到对方的ACK,就会重传FIN包。图中明显是一方没接收到ACK包,一直在重传,这个不用深究,看了大佬的文章总结出来应该是操作系统内核的BUG,了解一下就好

三次挥手和CLOSING状态的区分

​ 之前我们讲过三次挥手状态是将第二次挥手,和第三次挥手合并了,那么这两种情况好像啊,怎么区分呢?其实很简单,三次挥手虽然简化了但是步骤没省略所有的seq和ack都是正常的。

​ 而CLOSING状态,看好两个FIN包,正常第一个FIN包服务器发送seq 1001给客户端,客户端期望的下次数据是什么?是不是应该是1002,而我们图中第二个FIN包是多少,是不是1001很明显它不是接收到服务器的FIN包后才发回来的,而还停留在上个服务器发的包呢。

seq和ack对应不上问题

​ 看懂了上面区分问题,这个问题就简单了。客户端发的FIN包,ack是1001,但是服务器给的ACK包的seq确实1002这是为什么?,因为双方都知道,奥,我们的FIN包同时发的,那么按照正常挥手情况你的ACK应该是1002啊,不过现在咱俩冲突了,那他很聪明,就直接按照正常情况给你发了seq为1002的包

2.4 强制关闭

​ 这个就没啥好说的了,基本上就是两种情况

1.客户端和服务器连接但是客户端直接关了,没有FIN包,这时候我们的java(我的scoket代码用的java写的)还会负责人的给服务器发个RST,强制断开TCP连接

image-20211021104502921

2.客户端发了FIN,服务器也回了,但是服务器那边没写关闭scoket的代码,所以客户端发FIN,服务器ACK,但是服务器发不了FIN,所以服务器那边也是直接来了个RST

image-20211021104931284

# 网络安全技术
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录