运维面试八股--TCP的半连接队列与全连接队列的全部面试题都在这了

关于TCP的半连接队列与全连接队列的所有面试题

TCP半连接与全连接队列的面试题,虽然是一个大的知识点,但是面试题是很多的,要知道问题不一样,考察的内容是一致的哦,在文章中,也会在相应的位置提示面试题。

在这里插入图片描述

一线大厂面试真题:

大厂面试必问:CPU使用率100%怎么办?运维高手这样回答!
字节面试真题–用户反馈网页访问慢,可能会有哪些原因
字节面试真题–TCP建立连接为什么要三次握手?为什么不是两次或者四次?
面试八股(五) TCP的三次握手与四次挥手过程【是详述可不是简单几句话哦】
面试八股(六) 腾讯面试真题:close_wait状态的原因是什么?该怎么办?
面试八股(七) TCP Keepalive和HTTP Keep-Alive有什么区别?
面试八股(八) Linux服务器拔网线或者断电,已建立的TCP连接会中断吗?
面试八股(九) 关于TCP TIME_WAIT状态的所有面试题都在这里了
面试八股(11) 字节真题,关于TCP半连接与全连接队列的所有面试题
面试八股(12) 大厂真题,你了解synflood攻击吗?该如何应对
服务端处于TCP TIME_WAIT状态的连接,客户端重用这个连接还能连上吗?

还有更多一线大厂的面试真题,关注我的公众号:Linux运维实战派

在这里插入图片描述

一、什么是TCP的半连接和全连接队列

半连接队列也叫SYN队列,TCP三次握手的过程中,服务端接受到SYN包后,会将这个连接放入到SYN队列,并发送SYN+ACK报文

全连接队列也叫Accept队列,三次握手完成之后,连接会从SYN队列转移到Accept队列,等待进程执行accept()调用,将连接取走。

在这里插入图片描述

二、半连接队列及全连接队列溢出会发生什么

如上图,我们不难看出来,SYN队列是靠着三次握手最后一个ACK才完成转移,那如果最后一个ACK迟迟不到,甚至是永远也不会到,会发生什么?

Accept 队列是靠着进程执行accept()调用,完成转移,那如果业务迟迟不执行accept(),全连接队列满了会怎么样?

答案是:

半连接队列满了之后,如果没有开启syncookie则会直接丢弃SYN包

全连接队列满了之后,会直接丢弃(客户端反应就是连接超时)或者RST(客户端反应是 Connection reset by peer)具体是直接丢弃还是RST,取决于服务端的net.ipv4.tcp_abort_on_overflow的配置

  • tcp_abort_on_overflow == 0 的时候,会直接丢弃客户端发过来的数据包。这个是内核的默认行为,好处是丢弃客户端的报文,客户端会进行重传,假设全连接队列有空闲,请求就会被正常处理,可以增加客户端的成功率
  • tcp_abort_on_overflow == 1 的时候,会直接回复客户端RST,重置连接。客户端会立马收到响应

三、全连接队列的大小配置

在Linux中全连接队列的计算逻辑为min(somaxconn, backlog)net.core.somaxconn 内核参数与listen调用时传的backlog的最小值

  • somaxconn 由net.core.somaxconn 这个sysctl参数配置,默认值是128
  • backlog 是listen调用的时候传递的backlog大小,如nginx默认值是511,nginx在配置的时listen 80 backlog=16384; 通过这样的方式配置

3.1 全连接队列大小的查询

我们可以通过netstatss 命令来查询连接生效的全连接队列的大小,但是需要注意的是,这两个命令查询出来的结果会有不同。

ss 命令
ss -ant

State   Recv-Q Send-Q  Local Address:Port  Peer Address:Port 
LISTEN  0      128     0.0.0.0:22          0.0.0.0:*
ESTAB   0      52      10.230.1.153:22     88.88.88.88:19912

ss命令中的Send-Q 在连接处于LISTEN状态时,这个值就表示全连接队列大小;如果是ESTABLISHED状态的连接,这个值表示已经发送还没有被对端确认的数据大小

netstat 命令
netstat -ant
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN
tcp        0   2828 10.230.15.76:22         10.230.1.153:41644      ESTABLISHED

netstat命令中的Send-Q 不管什么状态,都表示已经发送,但没有被确认的数据大小

不管是netstat 还是ss Recv-Q都表示接收缓冲区内还没有被应用取走的数据大小

3.2 全连接满该如何监测

netstat -s |grep -i overflowed

如果没有出现全连接队列满的情况,这个命令执行会没有输出,如果这个输出的数字在一直增长,则表明当前有因为全连接队列满引起的丢包

面试题:如果监控报ListenOverFlow的错误,是什么原因,该怎么办?

3.3 全连接队列满该如何处理

从连接转移过程可以看出,全连接队列满主要是应用不及时调用accept() 引起的。所以,根本的解决办法在于加速应用程序的处理能力。

正产环境下的实践是,默认128是太小,推荐调整到4096及以上,在一些高并发的场景,如Nginx负载均衡,推荐调整到16384。需要注意的是,这个参数需要同时调整应用的Listen backlogsomaxconn的大小。

四、半连接队列

半连接队列是接收到SYN包之后,处于SYN_RECV状态的数量。如果无法及时收到客户端发来的ack报文,SYN的半连接队列就会溢出。

4.1 半连接队列的大小

首先必须明确的是,内核参数中的tcp_max_syn_backlog并不等于半连接队列,半连接的最大值受到多个参数的共同影响。

注意:以下逻辑适用于内核 4.2以上版本,在这之前计算逻辑是比较复杂的

SYN包正常的判断条件如下:

1)半连接队列大小不超过“全连接队列最大值【min(somaxconn, backlog)】”

2)全连接队列大小,不超过全连接队列最大值

3)半连接队列大小不超过tcp_max_syn_backlogtcp_max_syn_backlog/4的差【tcp_max_syn_backlog - (tcp_max_syn_backlog>>2)】

所以综上,如果一定要说SYN半连接的最大值,公式应该是

min(min(somaxconn, backlog), (max_syn_backlog - max_syn_backlog/4))

4.2 半连接队列的调整

在生产环境中一般性我们都会将somaxconn, listen backlogmax_syn_backlog 配置成一样的值,如16384。

如果想配置半连接队列与全连接队列最大值相等的话,tcp_max_syn_backlog 配置为: 4*全连接队列最大值/3

echo "net.core.somaxconn=16384" >> /etc/sysctl.d/99-sysctl.conf
echo "net.ipv4.tcp_max_syn_backlog=16384" >> /etc/sysctl.d/99-sysctl.conf
sysctl -p

4.3 半连接队列溢出

半连接队列溢出(即上边三个条件不满足任意一个),如果没有开启syncookies,那客户端的SYN包会被直接丢弃。

查看当前半连接大小
ss -ant | grep -i SYN-RECV | wc -l
半连接队列溢出监测
netstat -s |grep -i "SYNs to LISTEN sockets dropped"

判断这个输出如果有值,并且数值一直增长,则表明SYN半连接队列溢出

面试题:监控发现有报kernel: Possible SYN flooding Sending cookies. Check SNMP counters. 相关的报错,是什么原因,该怎么办

4.4 半连接队列溢出该如何处理

1)适当调大TCP队列的配置,包括somaxconn,listen backlog,max_syn_backlog

2)降低syn-ack的重传次数

echo "net.ipv4.tcp_synack_retries=1" >> /etc/sysctl.d/99-sysctl.conf
sysctl -p

3)开启syncookies,如果使用了LVS,支持synproxy的话,开启synproxy

echo "net.ipv4.tcp_syncookies" >> /etc/sysctl.d/99-sysctl.conf
sysctl -p

五、源码分析

推荐大家在线看kernel源码的网站:https://elixir.bootlin.com/linux/v5.10/source

5.1 全连接队列大小

// kernel 5.10 net/socket.c
int __sys_listen(int fd, int backlog)
{
    struct socket *sock;
    int somaxconn;
    sock = sockfd_lookup_light(fd, &err, &fput_needed);
    if (sock) {
       somaxconn = sock_net(sock->sk)->core.sysctl_somaxconn;
       // min(backlog, somaxconn)
       if ((unsigned int)backlog > somaxconn)
           backlog = somaxconn;
       ....
}

5.2 半连接大小限制

// kernel 5.10 net/ipv4/tcp_input.c
int tcp_conn_request(struct request_sock_ops *rsk_ops,
		     const struct tcp_request_sock_ops *af_ops,
		     struct sock *sk, struct sk_buff *skb)
{
    // syncookies没有开启的话,半连接队列溢出的话,则会丢弃
	if ((net->ipv4.sysctl_tcp_syncookies == 2 ||
	     inet_csk_reqsk_queue_is_full(sk)) && !isn) {
		want_cookie = tcp_syn_flood_action(sk, rsk_ops->slab_name);
		if (!want_cookie)
			goto drop;
	}
	// 全连接队列溢出,丢弃
	if (sk_acceptq_is_full(sk)) {
		NET_INC_STATS(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
		goto drop;
	}
	if (!want_cookie && !isn) {
        // 没有开syncookies, 且(max_syn_backlog - 当前半连接大小)<(max_syn_backlog>>2)
        // ==> 当前半连接队列 > (max_syn_backlog-max_syn_backlog>>2)
        // C中无符号整数>>2等于除以4
		if (!net->ipv4.sysctl_tcp_syncookies &&
		    (net->ipv4.sysctl_max_syn_backlog - inet_csk_reqsk_queue_len(sk) <
		     (net->ipv4.sysctl_max_syn_backlog >> 2)) &&
		    !tcp_peer_is_proven(req, dst)) {
            .....
			goto drop_and_release;
		}
	}
    .....
}

5.3 全连接与半连接队列溢出的判断

//kernel 5.10 net/inet_connection_sock.h#L279
static inline int inet_csk_reqsk_queue_is_full(const struct sock *sk)
{
    // 判断半连接队列溢出,实际上4.2以上版本都是直接与全连接队列最大值做的判断
    // sk_max_ack_backlog是全连接队列最大值, == min(backlog,somaxconn)
	return inet_csk_reqsk_queue_len(sk) >= sk->sk_max_ack_backlog;
}
// kernel 5.0 include/net/sock.h#L931
static inline bool sk_acceptq_is_full(const struct sock *sk)
{
    // sk_ack_backlog当前全连接队列大小
    // sk_max_ack_backlog 全连接队列大小的最大值
	return READ_ONCE(sk->sk_ack_backlog) > READ_ONCE(sk->sk_max_ack_backlog);
}

六、写在最后

因为TCP三次握手的设计,假设黑客伪造客户端IP,向服务端发起大量的SYN包,建立连接。那很快服务端的半连接队列(SYN队列)就会溢出,这就是synflood攻击。

synflood攻击该如何防护?syncookies的原理又是什么?我们下回再说

帅锅,看到这里了,点个赞,给个关注再走呗。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值