zl程序教程

您现在的位置是:首页 >  云平台

当前栏目

传输层 TCP 三次握手中性能优化 SYN_RCV 状态/syn攻击

攻击性能状态TCP 优化 三次 握手 传输层
2023-09-14 09:15:15 时间

服务器三次握手流程示例


当理解了三次握手当中的状态变迁之后,就可以进一步的去探讨客户端与服务器在实现当中的一些特点。这样我们就能够直到如何进行性能优化,以及握手当中的一些安全攻击问题。

首先以服务器为例,来看看三次握手的流程。

首先客户端发来syn分组到达了服务器,服务器内核,也就是对tcp的实现,会将syn分组插入到syn对列当中,同时会发出syn+ack,这个时候连接的状态为syn-received的状态,什么时候会从syn-received状态变为establish的状态呢?它会在客户端再次发来ack分组之后就会进入establish状态了。

但实际上在操作系统内核当中会将syn从syn队列当中移出,再塞入accept队列,这样我们的应用程序nginx  tomcat在调用accept的方法的时候就从accept队列当中将之前的连接拿出来给相关的方法去使用了。

所以在这个过程当中可以看到这个syn的队列长度和accept队列长度和我们的负载都是相关的。

如果面对的是每秒几十万请求高负载的机器,那么对syn队列和accept队列也是需要延长的。

 

 

 

超时时间与缓冲队列


怎么样对syn accept队列进行延长呢?

客户端优化

应用层 connect 超时时间调整(客户端也可以调整,客户端调用connect方法的时候,通常会有一个connect_timeout,去设置超时时间)
服务端优化
操作系统内核限制调整
           • 服务器端 SYN_RCV 状态
                         • net.ipv4.tcp_max_syn_backlogSYN_RCVD 状态连接的最大个数(调整sync队列的大小)
/proc/sys/net/ipv4/tcp_max_syn_backlog 此参数表示设置SYN队列最大长度,默认为1024,建议加大队列长度为8192, 可以容纳更多等待连接的网络连接数。
它其实是使用服务器内存来换取更大等待队列的长度。同时让攻击数据包不占满连接导致正常用户没有办法完成握手,所以消耗操作系统部分内存资源。
                         • /proc/sys/net/ipv4/tcp_syn_retries: 此参数表示对于一个新建连接,内核要发送多少个SYN连接请求才决定放弃,此值不应该大于255,默认值是5,建议设置为2。
如果设置的较大,那么要尝试很多次,在连接数较大的web服务器上面,这个值越小越好。
echo 2 > /proc/sys/net/ipv4/tcp_syn_retries
                           • net.ipv4.tcp_synack_retries:被动建立连接时,发SYN/ACK的重试次数(当我们发送SYN/ACK之后,长时间没有得到ack响应,我们可能要重发SYN/ACK的重试次数
 
net.ipv4.tcp_synack_retries用来降低服务器SYN+ACK报文重试次数(默认是5次),尽快释放等待资源。
 
                       
客户端 SYN_SENT 状态
• net.ipv4.tcp_syn_retries = 6 主动建立连接时,发 SYN 的重试次数(客户端发完syn,其实就进入了syn_sent状态,在syn_sent状态的时候,对于linux上面的负载均衡,或者向其他服务器建立大量连接的时候,也可以通过syn_retries来设置重发syn的重试次数)
net.ipv4.ip_local_port_range = 32768 60999 建立连接时的本地端口可用范围(划定更大的源端口的范围)
• ACCEPT队列设置(一样可以通过backlog设置)

当你的linux作为web服务器去使用,那么基于网络的内核参数是必须要优化的。优化网络内核参数可以最大限度的发挥web服务器的网络连接性能。下面是常用的网络连接的内核参数。

/proc/sys/net/ipv4/tcp_syncookies 此参数表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭,建议打开。(缓解服务器资源的一个压力)

echo 1 >/proc/sys/net/ipv4/ tcp_syncookies

是当前最流行的ddos,拒绝服务攻击,它利用了TCP协议的缺陷,发送大量的伪造的TCP连接请求,假冒的ip号会发来海量的请求,第一个握手的包叫做syn包,被攻击服务器回应第二个握手包,这个包叫做syn+ack的一个包,因为对方是伪造的IP,不会回应第三个握手的包,这样会导致被攻击服务器保持大量的半连接的状态并且默认重试net.ipv4.tcp_synack_retries是5次,然后再回复第二个握手的包,直到塞满tcp syn等待的队列,这样资源就会被耗尽,比如cpu满负荷或者内存不足,这样就会使得正常的业务连接请求进不来,这就是典型的拒绝服务攻击,syn攻击属于ddos攻击的一种类型。

针对syn攻击可以启用cookie来处理,然后设置syn队列的最大长度,以及设置syn/ack最大的重试次数,通过这种方式去最大程度缓解这种攻击。

通过前面参数的设置,它会在一定程度上减轻syn的攻击,但是也是一把双刃剑,如果设置的过大,它可能会消耗服务器更多的内存资源,甚至影响正常用户建立tcp连接,关于网络内核参数的调优需要评估服务器硬件的一个资源以及攻击力度,去谨慎的设置和选择。

 /proc/sys/net/ipv4/ tcp_tw_recycle  此参数表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭,普通web服务器建议设置为1,开启。

 

Fast Open 降低时延


三次握手当中有没有进一步的优化空间呢?

比如发起http get的请求,需要进行三次握手,发出syn 收到一个syn+ack,下一个ack当中带一个http get请求,这里其实是用了两个rtt的时间。

tcp提供了一个叫fast open的功能,它在做什么样的事情呢?第一次去建立连接的时候,server会给他生成一个cookie,在发送syn+sck的时候,将cookie给了我们的client,client会缓存这个cookie,所以第一次发起连接的时候还是要提供两个rtt才能将get请求发出去,以及收到对方的响应。

但是在第二次发送请求的时候,我们就跳过了三次握手,因为cookie当中维护了上一次建立连接当中相关的信息,比如窗口,时间戳等等tcp的选项参数。

直接将cooki在syn当中发给server,而且可以将get请求也发给server,这样就使用了tcp fsat open。实际上就消除了三次握手rtt的时延。

Linux上打开TCP Fast Open


net.ipv4.tcp_fastopen:系统开启 TFO 功能

0:关闭
1:作为客户端时可以使用 TFO
2:作为服务器时可以使用 TFO
3:无论作为客户端还是服务器,都可以使用 TFO

如何应对 SYN 攻击?


当在服务器端做应用开发的时候,必须考虑应对syn攻击

攻击者短时间伪造不同 IP 地址的 SYN 报文,快速占满 backlog 队列,使服务器不能为正常用户服务,可以通过下面的参数控制队列的大小,以及队列满的时候应该应用怎么一种行为。

net.core.netdev_max_backlog
接收自网卡、但未被内核协议栈处理的报文队列长度
net.ipv4.tcp_max_syn_backlog
SYN_RCVD 状态连接的最大个数
net.ipv4.tcp_abort_on_overflow
超出处理能力时,对新来的 SYN 直接回包 RST,丢弃连接

tcp_syncookies


回顾一下syn攻击时如何进行的, 一次正常的请求是收到syn,放到syn队列当中,当收到ack之后,我们再将这个连接从syn队列当中移出放在accept队列当中,应用程序就可以调用accept方法从accept队列当中将连接取出来。

 如果应用程序过慢就会导致accept队列满。

如果收到syn攻击的时候会导致syn队列满,怎么解决syn队列满呢?这里就有cookie了。

当队列的满的时候,Linux就不会给它塞到syn队列当中,而是直接生成了一个cookie,连着cookie一起发给了客户端,当客户端再来恢复连接的时候,它将cookie带进来了,那么这个时候连接就建立成功了。

也就是syn队列满的时候,我们仍然可以建立连接,但这里有个问题,TCP的报文头部是20个字节,所以启用cookie的时候,占用了一些序列号的空间,以及使得我们很多的可选功能失效了,比如扩充窗口和时间戳。

TCP_DEFER_ACCEPT


当服务器收到ack分组以后,2.1,其实它将连接放到accept队列当中了,此时内核需不需要激活相应的应用程序呢?如果这个时候并不激活应用程序,直到收到了实际的data分组,也就是实际的消息报文之后,我们再去激活应用程序,这样使得应用程序效率最高。