文章插图
从上面的代码中,我们可以算出 max_qlen_log 是 8,于是代入到 检测半连接队列是否满的函数 reqsk_queue_is_full :
文章插图
也就是 qlen >> 8 什么时候为 1 就代表半连接队列满了 。这计算并不难,很明显是当 qlen 为 256 时,256 >> 8 = 1 。
至此,总算知道为什么上面模拟测试 SYN 攻击的时候,服务端处于 SYN_RECV 连接最大只有 256 个 。
可见,半连接队列最大值不是单单由 max_syn_backlog 决定,还跟 somaxconn 和 backlog 有关系 。
在 Linux 2.6.32 内核版本,它们之间的关系,总体可以概况为:
文章插图
- 当 max_syn_backlog > min(somaxconn, backlog) 时,半连接队列最大值 max_qlen_log = min(somaxconn, backlog) * 2;
- 当 max_syn_backlog < min(somaxconn, backlog) 时,半连接队列最大值 max_qlen_log = max_syn_backlog * 2;
依然很遗憾,并不是 。
max_qlen_log 是理论半连接队列最大值,并不一定代表服务端处于 SYN_REVC 状态的最大个数 。
在前面我们在分析 TCP 第一次握手(收到 SYN 包)时会被丢弃的三种条件:
- 如果半连接队列满了,并且没有开启 tcp_syncookies,则会丢弃;
- 若全连接队列满了,且没有重传 SYN+ACK 包的连接请求多于 1 个,则会丢弃;
- 如果没有开启 tcp_syncookies,并且 max_syn_backlog 减去 当前半连接队列长度小于 (max_syn_backlog >> 2),则会丢弃;
似乎很难理解,我们继续接着做实验,实验见真知 。
服务端环境如下:
文章插图
配置完后,服务端要重启 Nginx,因为全连接队列最大和半连接队列最大值是在 listen 函数初始化 。
根据前面的源码分析,我们可以计算出半连接队列 max_qlen_log 的最大值为 256:
文章插图
客户端执行 hping3 发起 SYN 攻击:
文章插图
服务端执行如下命令,查看处于 SYN_RECV 状态的最大个数:
文章插图
可以发现,服务端处于 SYN_RECV 状态的最大个数并不是 max_qlen_log 变量的值 。
这就是前面所说的原因:如果当前半连接队列的长度 「没有超过」理论半连接队列最大值 max_qlen_log,那么如果条件 3 成立,则依然会丢弃 SYN 包,也就会使得服务端处于 SYN_REVC 状态的最大个数不会是理论值 max_qlen_log 。
我们来分析一波条件 3 :
文章插图
从上面的分析,可以得知如果触发「当前半连接队列长度 > 192」条件,TCP 第一次握手的 SYN 包是会被丢弃的 。
在前面我们测试的结果,服务端处于 SYN_RECV 状态的最大个数是 193,正好是触发了条件 3,所以处于 SYN_RECV 状态的个数还没到「理论半连接队列最大值 256」,就已经把 SYN 包丢弃了 。
所以,服务端处于 SYN_RECV 状态的最大个数分为如下两种情况:
- 如果「当前半连接队列」没超过「理论半连接队列最大值」,但是超过 max_syn_backlog - (max_syn_backlog >> 2),那么处于 SYN_RECV 状态的最大个数就是 max_syn_backlog - (max_syn_backlog >> 2);
- 如果「当前半连接队列」超过「理论半连接队列最大值」,那么处于 SYN_RECV 状态的最大个数就是「理论半连接队列最大值」;
在上面我们是针对 Linux 2.6.32 版本分析的「理论」半连接最大值的算法,可能每个版本有些不同 。
比如在 Linux 5.0.0 的时候,「理论」半连接最大值就是全连接队列最大值,但依然还是有队列溢出的三个条件:
文章插图
推荐阅读
- 司机高速上睡了半小时,车内“无人驾驶”48公里,让人无法相信
- 茶树的形状乔木型,半乔木型茶树先容
- 下半身减肥运动有什么?
- 无线网络连接怎么设置
- httpclient连接池管理,你用对了?
- 发酵茶怎么发酵,生普属于半发酵茶么
- 说起来 TCP 的连接与释放真是个浪漫的故事呢!
- Win10电脑蓝牙连接手机播放音乐
- 东半山忙蚌普洱茶介绍,忙蚌茶区
- 春天喝什么茶?茶叶入菜香自来!