连接|TCP半连接队列和全连接队列满了,怎么破( 四 )


文章图片

客户端执行hping3发起SYN攻击:服务端执行如下命令,查看处于SYN_RECV状态的最大个数:
可以发现,服务端处于SYN_RECV状态的最大个数并不是max_qlen_log变量的值。
这就是前面所说的原因:如果当前半连接队列的长度「没有超过」理论半连接队列最大值max_qlen_log,那么如果条件3成立,则依然会丢弃SYN包,也就会使得服务端处于SYN_REVC状态的最大个数不会是理论值max_qlen_log。
我们来分析一波条件3:

 连接|TCP半连接队列和全连接队列满了,怎么破
文章图片

从上面的分析,可以得知如果触发「当前半连接队列长度>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状态的最大个数就是「理论半连接队列最大值」;
6、每个Linux内核版本「理论」半连接最大值计算方式会不同。
在上面我们是针对Linux2.6.32版本分析的「理论」半连接最大值的算法,可能每个版本有些不同。
比如在Linux5.0.0的时候,「理论」半连接最大值就是全连接队列最大值,但依然还是有队列溢出的三个条件:

 连接|TCP半连接队列和全连接队列满了,怎么破
文章图片

7、如果SYN半连接队列已满,只能丢弃连接吗?并不是这样,开启syncookies功能就可以在不使用SYN半连接队列的情况下成功建立连接,在前面我们源码分析也可以看到这点,当开启了syncookies功能就不会丢弃连接。
syncookies是这么做的:服务器根据当前状态计算出一个值,放在己方发出的SYN+ACK报文中发出,当客户端返回ACK报文时,取出该值验证,如果合法,就认为连接建立成功,如下图所示。

 连接|TCP半连接队列和全连接队列满了,怎么破
文章图片

开启syncookies功能syncookies参数主要有以下三个值:
0值,表示关闭该功能;
1值,表示仅当SYN半连接队列放不下时,再启用它;
2值,表示无条件开启功能;
那么在应对SYN攻击时,只需要设置为1即可:
8、如何防御SYN攻击?
这里给出几种防御SYN攻击的方法:
增大半连接队列;
开启tcp_syncookies功能;
减少SYN+ACK重传次数。
(1)方式一:增大半连接队列
在前面源码和实验中,得知要想增大半连接队列,我们得知不能只单纯增大tcp_max_syn_backlog的值,还需一同增大somaxconn和backlog,也就是增大全连接队列。否则,只单纯增大tcp_max_syn_backlog是无效的。
增大tcp_max_syn_backlog和somaxconn的方法是修改Linux内核参数:
增大backlog的方式,每个Web服务都不同,比如Nginx增大backlog的方法如下:

 连接|TCP半连接队列和全连接队列满了,怎么破
文章图片

最后,改变了如上这些参数后,要重启Nginx服务,因为半连接队列和全连接队列都是在listen()初始化的。(2)方式二:开启tcp_syncookies功能
开启tcp_syncookies功能的方式也很简单,修改Linux内核参数:
(3)方式三:减少SYN+ACK重传次数
当服务端受到SYN攻击时,就会有大量处于SYN_REVC状态的TCP连接,处于这个状态的TCP会重传SYN+ACK,当重传超过次数达到上限后,就会断开连接。
那么针对SYN攻击的场景,我们可以减少SYN+ACK的重传次数,以加快处于SYN_REVC状态的TCP连接断开。
参考:
[1]系统性能调优必知必会.陶辉.极客时间.
【 连接|TCP半连接队列和全连接队列满了,怎么破】[2]https://blog.cloudflare.com/syn-packet-handling-in-the-wild


推荐阅读