可恶,又被小林装到了!

简介: 可恶,又被小林装到了!

0.png作者:小林coding

图解网站:https://xiaolincoding.com/

大家好,我是小林。

昨天群里有个读者问了一个很有意思的问题。


-.png

9.png

他抓到一个抓包图,客户端和服务端四次挥手后,客户端在 17 秒内又复用了与上一次连接相同的端口,向服务端发起了 SYN 报文, 并成功建立了连接。

他觉得服务端应该还是处于 TIME_WAIT 状态(因为 Linux 操作系统中,2MSL 的时间是 60 秒,也就是 TIME_WAIT 状态的持续时间),为什么收到客户端的  SYN 报文后可以正常建立连接?

抓包图手机端不好看,为了方便大家看,我画了一个图:

8.png

简单来说,这个问题就是,为什么处于 TIME_WAIT 状态的 TCP 连接,收到相同四元组的 SYN 报文后,可以正常建立连接。

可能有人问,这个问题小林不是写过吗?对的,我之前是写过一篇:处于 TIME_WAIT 状态的连接,收到相同四元组的 SYN 后会发生什么?

当时文章给出的结论是:

  • 如果 SYN 报文的「序列号+时间戳」都是合法的话,就会重新建立连接;
  • 如果 SYN 报文的「序列号+时间戳」其中一个不合法的话,就会回 RST。

这位读者也看了这篇文章的,他觉得他抓包图中客户端的 SYN 报文的序列号是不合法的,所以应该是回 RST 才对,但是现象却是是可以正常建立连接。

7.jpg

到这,我开始慌了,难道,我之前的文章写错了?难道处于 TIME_WAIT 状态的连接,只要收到 SYN 报文,不管合不合法,都会重新建立连接?

我先不着急说结论,我先带大家分析一波,从这个抓包图的信息,分析内核有没有走 TIME_WAIT 状态收到 SYN 报文后,重新建立连接的逻辑。


分析一波内核源码


在 Linux 内核中,处于 TIME_WAIT 状态的连接,收到 SYN 报文后,有这么一个逻辑:

6.png

大概就是,如果报文是 SYN 包,时间戳+序列号都是合法的,那么就会允许在 TIME_WAIT 状态下重新建立连接。

抓包图中的客户端是在 17 秒后重用端口发起 SYN 报文的,所以时间戳肯定相比于历史连接是递增的,所以时间戳是合法的。

接下来,我们重点分析 SYN 报文中的序列号是否合法的。

首先,内核是这样判断的:

//如果after函数返回 1, 则说明合法,否则不合法
after(TCP_SKB_CB(skb)->seq, tcptw->tw_rcv_nxt)

如果after函数返回 1, 则说明收到报文的序列号是合法的,否则不合法。after 函数的参数分别表示:

  • TCP_SKB_CB(skb)->seq,这个是 SYN 报文的序列号;
  • tcptw->tw_rcv_nxt,这个是 TIME_WAIT 状态期望下一次收到的序列号,其实也就是第四次挥手中 ACK 报文中的 ack num 值。

根据抓包图,我们可以得出,seq = 3145977016tw_rcv_nxt = 40088018880

5.png

after 这个函数实现很短,我贴出来给大家看:

static inline bool before(unsigned int  seq1, unsigned int seq2)
{
        return (int)(seq1-seq2) < 0;
 }
#define after(seq2, seq1)   before(seq1, seq2)

然后,我写了个代码来验证 after 函数返回的值是什么:

4.png

可以发现,after 函数返回的是 0 ,说明抓包图中 SYN 报文的序列号是不合法的,所以根本就没有进入到 TIME_WAIT 状态重建连接的逻辑

还有一个角度可以证明,此抓包图没有中 TIME_WAIT 状态重建连接的逻辑。

因为当 TIME_WAIT 状态允许重建连接时,服务端第二次握手的初始序列号是这样计算的 tcptw->tw_snd_nxt + 65535 + 2,其中 tw_snd_nxt 表示服务端 TIME_WAIT 状态下最后一个发出报文的序列号。

3.png

根据抓包图,可以得出 tw_snd_nxt 是 1082535342。

2.png

如果走了TIME_WAIT 状态重建连接的逻辑,那么服务端的第二次握手中的序列号应该是 1082535342+ 65535 + 2,而抓包图中显示的服务端第二次握手的序列号为 2175872083这两个值并不相同,所以从这个角度,也可以证明,此抓包图没有中 TIME_WAIT 状态重建连接的逻辑

当时,我也在群里说了这个结论。

111.jpg


被我分析出来了


经过上面的分析,如果服务端还是处于 TIME_WAIT 状态的话,那么收到不合法的 SYN 报文,肯定是回 RST 的,这一点不用怀疑。

所以,我开始考虑是不是因为服务端开启了某些 TCP 内核参数,导致 TIME_WAIT 状态的连接被快速回收了,从而使得客户端后面发起的 SYN 报文,可以正常建立连接。

这里先跟大家说下,有哪些 TCP 内核参数会导致 TIME_WAIT 状态被快速回收:

  • 参数一:net.ipv4.tcp_tw_reuse,如果开启该选项的话,客户端(连接发起方) 在调用 connect() 函数时,内核会随机找一个 TIME_WAIT 状态超过 1 秒的连接给新的连接复用,所以该选项只适用于连接发起方。
  • 参数二:net.ipv4.tcp_tw_recycle,如果开启该选项的话,允许处于 TIME_WAIT 状态的连接被快速回收。

从抓包图可以看出,服务端主动发起的 FIN 报文,所以是服务端处于 TIME_WAIT 状态,所以 tcp_tw_reuse 这个参数不会是导致 TIME_WAIT 状态被快速回收的原因,因为这个参数是用于连接发起方,也就是客户端处于 TIME_WAIT 状态,在发起连接的时候,可以复用 TIME_WAIT 状态。

所以,排除参数一的可能性。

我当时就怀疑是因为服务端开启了 tcp_tw_recycle 参数,导致服务端的 TIME_WAIT 状态被快速回收了,并没有经过完整的 2MSL (60秒)时长的 TIME_WAIT 状态。

所以, 我就让读者去确认下,服务端是否开启了 tcp_tw_recycle 参数。

111.jpg

好家伙,经过读者的确认后,发现服务端真的开启了 tcp_tw_recycle 参数。


11.jpg


那么抓包图的现象就可以很好解释了,就是因为服务端开启了 tcp_tw_recycle 这个参数,导致服务端的 TIME_WAIT 状态被快速回收了,可能经过不到几秒,服务端就进入到 CLOSED 状态了。然后,17 秒后客户端发起的相同四元组的 SYN 报文,就正常建立连接了,因为服务端并没有处于 TIME_WAIT 状态


总结


最后总结下,我的分析思路。

通过抓包图的序列号信息,确认客户端发起的  SYN 报文的序列号是不合法的,所以如果服务端还是处于 TIME_WAIT 状态的话, 收到这个不合法的 SYN 报文,应该是回 RST 的,而抓包图的现象却是正常建立了连接。所以从这个分析中,我确认了服务端的 TIME_WAIT 状态可能是被快速回收了。

然后,想到了 Linux 两个快速回收  TIME_WAIT 状态的参数 tcp_tw_reuse 和 tcp_tw_recycle,其中 tcp_tw_reuse 参数是用于连接放起方,而本次的案例 TIME_WAIT 状态是在服务端,而不是客户端,所以可以排除这个参数的可能性。

于是,就让读者确认是否开启了 tcp_tw_recycle 参数,因为开启了这个参数后,不管是服务端还是客户端,处于 TIME_WAIT 状态的连接,都会被快速回收,然后 TCP 连接就会进入到 CLOSE 状态。

最终,经过读者确认后,发现服务端确实开启 tcp_tw_recycle 参数。

不过,tcp_tw_recycle 状态还是不建议大家开启的,因为在 NAT 的网络下是不安全的,在 Linux 4.12 版本后,直接取消了这一参数。


通过这次的分析案例,大家是不是体验到了「八股文」 的力量,就从一个抓包图的现象,就能分析出是什么导致的。


1.jpg


怎么样,这一波被我装到了


相关文章
|
存储 安全 Python
python多线程------>这个玩意很哇塞,你不来看看吗
python多线程------>这个玩意很哇塞,你不来看看吗
|
前端开发 数据库
贼无聊的文章
贼无聊的文章
41 0
用“三国杀”讲“分布式算法”,这下舒适了吧?
前言 《三国杀》是一款热门的卡牌游戏,结合中国三国时期背景,以身份为线索,以卡牌为形式,益智休闲,老少皆宜。 东汉末年,袁绍作为盟主,汇合了十八路诸侯一起攻打董卓。 在讲解之前,我们先聊下分布式协议和算法整体脉络。 现在很多开发同学对分布式的组件怎么使用都有一定经验,也知道 CAP 理论和 BASE 理论的大致含义。但认真去看分布式算法的真的很少,原因有三:
真香!端午节到来,我用Python画了几个粽子送给女票,女票差点吃了我的电脑...
真香!端午节到来,我用Python画了几个粽子送给女票,女票差点吃了我的电脑...
|
Linux 虚拟化
听说你要删库跑路了?这篇Linux脚本请收好
本文所写的均为作者在vmware中测试使用,未在工作中用到,学习也只是,对付不正当行为,给大学生临时工一个安慰自己的理由罢了,希望得到大家的三连!
275 0
听说你要删库跑路了?这篇Linux脚本请收好
|
消息中间件 存储 缓存
你管这“破玩意儿”叫锁
你管这“破玩意儿”叫锁
你管这“破玩意儿”叫锁
|
设计模式 XML 存储
三歪手把手教你干掉if else
今天想来跟大家讨论一下怎么干掉if else。
156 0
三歪手把手教你干掉if else
|
Linux Windows
唉,被坑惨了!
32 位系统,用户态的虚拟空间只有 3G,如果创建线程时分配的栈空间是 10M,那么一个进程最多只能创建 300 个左右的线程。 64 位系统,用户态的虚拟空间大到有 128T,理论上不会受虚拟内存大小的限制,而会受系统的参数或性能限制。
唉,被坑惨了!
|
前端开发 小程序 Java
一款神仙接私活儿的软件,吊到不行!
一款神仙接私活儿的软件,吊到不行!
155 0
一款神仙接私活儿的软件,吊到不行!
|
存储 程序员 C语言
指针不过如此,看完后我差点飘了(一)
指针不过如此,看完后我差点飘了
157 0
指针不过如此,看完后我差点飘了(一)