linux诡异的半连接(SYN_RECV)队列长度(一)

最近在学习TCP方面的基础知识,对于古老的SYN Flood也有了更多认识。SYN Flood利用的是TCP协议缺陷,发送大量伪造的TCP连接请求,从而使得被攻击方资源耗尽(CPU满负荷或内存不足)的攻击方式。
SYN Flood的原理简单,实现也不复杂,而且网上有许多现成的程序。

我在两台虚拟机上(虚拟机C攻击虚拟机S)做测试,S上跑了apache监听80端口,用C对S的80端口发送SYN Flood,在无任何防护的情况下攻击效果显著。用netstat可以看见80端口存在大量的半连接状态(SYN_RECV),用tcpdump抓包可以看见大量伪造IP发来的SYN连接,S也不断回复SYN+ACK给对方,可惜对方并不存在(如果存在则S会收到RST这样就失去效果了),所以会超时重传。
这个时候如果有正常客户A请求S的80端口,它的SYN包就被S丢弃了,因为半连接队列已经满了,达到攻击目的。

对于SYN Flood的防御一般会提到修改 net.ipv4.tcp_synack_retries, net.ipv4.tcp_syncookies, net.ipv4.tcp_max_syn_backlog
目的就是减小SYN+ACK重传次数,增加半连接队列长度,启用syn cookie。
当S开启syn cookie的时候情况会缓解,一旦半连接队列满了系统就会启用syn cookie功能,同时在/var/log/messages记录kernel: possible SYN flooding on port 80. Sending cookies.
但也不是可以完全防御的,如果说攻击瞬间并发量足够大,毕竟S的CPU内存有限,一般大公司都有专业的防火墙设备来应对。

其中对于net.ipv4.tcp_max_syn_backlog的描述一般都称为半连接队列的长度,但在我实际测试的过程中却发现SYN_RECV状态的数量与net.ipv4.tcp_max_syn_backlog设置的值相差甚远。
S系统配置如下:
net.ipv4.tcp_synack_retries = 5
net.ipv4.tcp_syncookies = 0
net.ipv4.tcp_max_syn_backlog = 4096
但SYN_RECV状态的数量却只有256

于是就开始相关资料,首先想到的是TCP/IP详解卷1中提到的backlog,man 2 listen:
int listen(int sockfd, int backlog);
The backlog parameter defines the maximum length the queue of pending connections may grow to. If a connection request arrives with
the queue full the client may receive an error with an indication of ECONNREFUSED or, if the underlying protocol supports retransmis-
sion, the request may be ignored so that retries succeed.

NOTES
The behaviour of the backlog parameter on TCP sockets changed with Linux 2.2. Now it specifies the queue length for completely estab-
lished sockets waiting to be accepted, instead of the number of incomplete connection requests. The maximum length of the queue for
incomplete sockets can be set using the tcp_max_syn_backlog sysctl. When syncookies are enabled there is no logical maximum length
and this sysctl setting is ignored. See tcp(7) for more information.

可见backlog在Linux 2.2之后表示的是已完成三次握手但还未被应用程序accept的队列长度。

man 7 tcp:
tcp_max_syn_backlog (integer; default: see below)
The maximum number of queued connection requests which have still not received an acknowledgement from the connecting client.
If this number is exceeded, the kernel will begin dropping requests. The default value of 256 is increased to 1024 when the
memory present in the system is adequate or greater (>= 128Mb), and reduced to 128 for those systems with very low memory (<=
32Mb). It is recommended that if this needs to be increased above 1024, TCP_SYNQ_HSIZE in include/net/tcp.h be modified to
keep TCP_SYNQ_HSIZE*16<=tcp_max_syn_backlog, and the kernel be recompiled.

可见tcp_max_syn_backlog确实是半连接队列的长度,那为何会不准呢?
这时候正好让同事也在两台机器上测试了一下,得到的数据居然与tcp_max_syn_backlog完全一致。
开始怀疑是系统哪个地方配置有问题,又发现一个可疑的配置 net.core.somaxconn 它是listen的第二个参数int backlog的上限值,如果程序里的backlog大于
net.core.somaxconn的话就会取net.core.somaxconn的值。S系统的net.core.somaxconn = 128

//file:net/socket.c
 
SYSCALL_DEFINE2(listen, int, fd, int, backlog)
{
	struct socket *sock;
	int err, fput_needed;
	int somaxconn;
 
	sock = sockfd_lookup_light(fd, &err, &fput_needed);
	if (sock) {
		somaxconn = sock_net(sock->sk)->core.sysctl_somaxconn;
		//上限不超过somaxconn
		if ((unsigned)backlog > somaxconn)
			backlog = somaxconn;
 
		err = security_socket_listen(sock, backlog);
		if (!err)
			err = sock->ops->listen(sock, backlog);
 
		fput_light(sock->file, fput_needed);
	}
	return err;
}

查了apache文档关于ListenBackLog 指令的说明,默认值是511. 可见最终全连接队列(backlog)应该是net.core.somaxconn = 128

证实这点比较容易,用慢连接攻击测试观察到虚拟机S的80端口ESTABLISHED状态最大数量384
正好等于256(apache prefork模式MaxClients即apache可以响应的最大并发连接数) + 128(backlog即已完成三次握手等待apache accept的最大连接数)。说明全连接队列长度等于min(backlog,somaxconn);

未完,见(二)


http://www.piao2010.com/linux%E8%AF%A1%E5%BC%82%E7%9A%84%E5%8D%8A%E8%BF%9E%E6%8E%A5syn_recv%E9%98%9F%E5%88%97%E9%95%BF%E5%BA%A6%E4%B8%80


TCP连接过程中,SYN_RECVSYN_SENT状态分别表示服务器端和客户端正在处理三次握手的中间状态。当使用`ss -ltnp | grep 38002`命令时,发现SYN_RECV队列大小为51,SYN_SENT队列大小为50,这表明当前存在大量未完成的连接请求。这种情况可能由多种原因引起,包括但不限于服务器资源不足、网络延迟过高或恶意攻击(如SYN洪水攻击)。 ### 1. 调整SYN半连接队列大小 SYN_RECV队列是半连接队列部分,用于存放尚未完成三次握手的连接请求。如果该队列过大,可能意味着队列容量不足,导致连接请求被丢弃。可以通过调整`net.ipv4.tcp_max_syn_backlog`参数来增加SYN半连接队列的大小。例如: ```bash sysctl -w net.ipv4.tcp_max_syn_backlog=2048 ``` 此操作会临时修改系统设置。为了使更改永久生效,可以将该参数写入`/etc/sysctl.conf`文件中: ```bash echo "net.ipv4.tcp_max_syn_backlog=2048" >> /etc/sysctl.conf sysctl -p ``` ### 2. 启用SYN Cookies SYN Cookies是种用于防御SYN洪水攻击的技术。当启用SYN Cookies后,系统不会立即为每个SYN请求分配资源,而是通过计算个特殊的序列号来验证客户端的响应。如果检测到SYN_RECV队列异常增长,可以启用SYN Cookies来缓解问题: ```bash sysctl -w net.ipv4.tcp_syncookies=1 ``` 同样,为了使更改永久生效,可以将该参数写入`/etc/sysctl.conf`文件中: ```bash echo "net.ipv4.tcp_syncookies=1" >> /etc/sysctl.conf sysctl -p ``` ### 3. 检查并优化应用程序性能 如果SYN_RECVSYN_SENT队列持续增长,可能是由于应用程序处理连接请求的速度过慢。可以检查应用程序的日志,确认是否存在性能瓶颈或错误处理。此外,确保应用程序能够及时调用`accept()`函数来处理已完成的连接请求,避免全连接队列积压。 ### 4. 监控网络流量和连接状态 使用`ss`或`netstat`命令定期监控网络连接状态,确保SYN_RECVSYN_SENT队列的大小处于合理范围内。例如,使用以下命令查看当前的连接状态: ```bash ss -ltnp | grep 38002 ``` 此外,可以使用`netstat -s`命令查看TCP协议栈的统计信息,确认是否存在连接请求被丢弃的情况: ```bash netstat -s | grep "failed connection attempts" ``` ### 5. 防火墙和DDoS防护 如果SYN_RECV队列的异常增长是由恶意攻击引起的,可以考虑配置防火墙规则来限制SYN请求的速率。例如,使用`iptables`限制每秒的SYN请求数量: ```bash iptables -A INPUT -p tcp --syn -m limit --limit 1/s -j ACCEPT iptables -A INPUT -p tcp --syn -j DROP ``` 此规则将每秒的SYN请求限制为1个,超出限制的请求将被丢弃。可以根据实际情况调整限制值。 ### 6. 调整全连接队列大小 除了SYN_RECV队列外,还需要确保全连接队列(accept queue)的大小足够大,以容纳已完成的连接请求。可以通过调整`net.core.somaxconn`参数来增加全连接队列的最大长度: ```bash sysctl -w net.core.somaxconn=2048 ``` 同样,为了使更改永久生效,可以将该参数写入`/etc/sysctl.conf`文件中: ```bash echo "net.core.somaxconn=2048" >> /etc/sysctl.conf sysctl -p ``` ### 7. 检查网络延迟和丢包情况 网络延迟过高或丢包也可能导致SYN_RECVSYN_SENT队列的增长。可以使用`ping`或`traceroute`命令检查网络延迟和路径问题: ```bash ping <目标IP> traceroute <目标IP> ``` 如果发现网络延迟过高或存在丢包,可以联系网络管理员进行进步排查。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值