TCP的两个队列,Slow Accept

-- TOC --

一个是半连接队列,记录收到SYN并发出SYNACK后的连接信息。一个是全连接队列,记录收到最后的ACK后的连接信息,等待accept。

Linux内核中会维护着这两个队列:

two_queue.png

listen接口的backlog参数,同时影响此两个队列的长度,同时也受到内核somaxconn参数的限制,这两个队列是等长的!(tcp_max_syn_backlog是否还有作用,不确定?)

根据backlog的含义,实际上队列的长度是backlog+1。

SYN队列存储了收到SYN包的连接(对应内核代码的结构体:struct inet_request_sock)。它的职责是回复SYNACK包,并且在没有收到ACK包时重传,直到超时。

发送完SYNACK之后,SYN队列等待从客户端发出的ACK包(也即三次握手的最后一个包)。

All received ACK packets must first be matched against the fully established connection table, and only then against data in the relevant SYN Queue. On SYN Queue match, the kernel removes the item from the SYN Queue, happily creates a fully fledged connection (specifically: struct inet_sock), and adds it to the Accept Queue.

The Accept Queue contains fully established connections: ready to be picked up by the application. When a process calls accept(), the sockets are de-queued and passed to the application.

这就是Linux处理SYN包的一个简单描述。顺便一提,当socket开启了TCP_DEFER_ACCEPT和TCP_FASTOPEN时,工作方式将会有细微不同。

Slow Accept

SYN和Accept这两个队列,其实都是Linux内核自行处理的部分,应用程序要用accept接口,取出一个socket连接,然后再进行应用部分的处理。一个有趣的细节是,如果应用运行有点慢,这两个队列都满了,应用来不及accept,这个时候会出现什么情况?

此时,收到的SYN报文会被丢弃,收到的匹配SYN队列的ACK报文也会被丢弃!

There is a strong rationale for dropping inbound packets: it's a push-back mechanism. The other party will sooner or later resend the SYN or ACK packets by which point, the hope is, the slow application will have recovered.

This is a desirable behavior for almost all servers. For completeness: it can be adjusted with the global net.ipv4.tcp_abort_on_overflow toggle, but better not touch it.

正常情况下,一般都是ACCEPT队列出现积压,因为SYN队列中的半连接很快就会被移动到ACCEPT队列中。而当SYN队列出现积压时,要小心SYN Flood攻击了。(面对SYN Flood攻击,Linux默认有SYN Cookie的保护。)

本文链接:https://cs.pynote.net/net/tcp/202303233/

-- EOF --

-- MORE --