Last Updated: 2023-03-24 07:43:48 Friday
-- TOC --
在面对Syn-Flood攻击的时候,服务器给TCP连接的资源会被大量消耗,可能会无法再响应正常的连接请求。当服务器TCP的半连接队列被SYN攻击请求塞满后,SYN Cookie就成为一种可能还可以接收正常请求的机制。
Linux文档中说明,SYN Cookie机制只是用来应对攻击,如果没有攻击,只是服务器负担过重,不建议使用这个功能。因为这个功能不是TCP标准,通过Cookie建立的TCP连接,不支持TCP扩展功能。但是,tcp_syncookies默认开启,设置为1,在SYN队列被塞满后开始工作。
Syn-Flood攻击成立的关键在于服务器资源的有限性,服务器收到请求会分配资源。通常来说,服务器用这些资源保存此次请求的关键信息,包括连接五元组,TCP选项,如最大报文段长度MSS、时间戳timestamp、选择应答使能Sack、窗口缩放因子Wscale等等。当后续的ACK报文到达,三次握手完成,全连接成功创建,这些信息会被复制到连接结构中,用来指导后续的报文收发。
那么现在的问题就是,服务器如何在没有资源可分的情况下:
SYN Cookie算法解决了问题(1)和问题(2)的一部分。
我们知道,TCP连接建立时,双方的起始报文序号是任意的。SYN cookies利用这一点,按照以下规则构造初始序列号:
则初始序列号n为:
+----------+--------+-------------------+
| 6 bits | 2 bits | 24 bits |
| t mod 32 | MSS | hash(ip, port, t) |
+----------+--------+-------------------+
当客户端收到此SYN+ACK报文后,根据TCP标准,它会回复ACK报文,且报文中ack = n + 1,那么在服务器收到它时,将 ack-1 就可以拿回当初发送的SYN+ACK报文中的序号了!服务器巧妙地通过这种方式间接保存了一部分SYN报文的信息。
接下来,服务器需要对 ack-1 这个序号进行检查:
到此,连接就可以顺利建立了,并且没有占用半连接SYN队列的资源。
既然SYN Cookies可以跳过资源分配环节,那为什么没有被纳入TCP标准呢?原因是SYN Cookies也是有代价的:
/*
* MSS Values are chosen based on the 2011 paper
* 'An Analysis of TCP Maximum Segement Sizes' by S. Alcock and R. Nelson.
* Values ..
* .. lower than 536 are rare (< 0.2%)
* .. between 537 and 1299 account for less than < 1.5% of observed values
* .. in the 1300-1349 range account for about 15 to 20% of observed mss values
* .. exceeding 1460 are very rare (< 0.04%)
*
* 1460 is the single most frequently announced mss value (30 to 46% depending
* on monitor location). Table must be sorted.
*/
static __u16 const msstab[] = {
536,
1300,
1440, /* 1440, 1452: PPPoE */
1460,
};
Linux doesn't know any optional TCP parameters of the other party. Information about Timestamps, ECN, Selective ACK, or Window Scaling is lost, and can lead to degraded TCP session performance.
Linux中的SYN Cookie与上文描述的稍有不同,其初始序列号的生成:
seq = hash(saddr, daddr, sport, dport, 0, 0) + req.th.seq + t << 24 + (hash(saddr, daddr, sport, dport, t, 1) + mss_ind) & 0x00FFFFFF
内核编译需要打开 CONFIG_SYN_COOKIES,内核参数:
net.ipv4.tcp_syncookie
代码线索:
tcp_conn_request
|-- cookie_init_sequence
|-- cookie_v4_init_sequence
|-- __cookie_v4_init_sequence
|-- secure_tcp_syn_cookie
承接前面所述SYN Cookie的缺点,Fortunately Linux has a work around. If TCP Timestamps are enabled, the kernel can reuse another slot of 32 bits in the Timestamp field. It contains:
+-----------+-------+-------+--------+
| 26 bits | 1 bit | 1 bit | 4 bits |
| Timestamp | ECN | SACK | WScale |
+-----------+-------+-------+--------+
如果服务器和客户端都打开了时间戳选项(Linux默认打开),那么服务器可以将客户端在SYN报文中携带了TCP选项的部分使能情况暂时保存在时间戳中。当前使用了低 6 位,分别保存Wscale、SACK和ECN。
客户端会在ACK的TSecr字段,把这些值带回来。
accept会将全连接队列中的ESTABLISHED的连接取出来,如果不调用accept,多个client调用connect后,可以达到让server的队列满的测试效果。这个队列的长度,由listen接口的backlog参数控制。
虽然net.ipv4.tcp_timestamps
默认是打开的,它在SYN Cookie启用的时候,可以带来一些好处,但它也会给每个报文增加12byte的长度,non-trivial amount of bandwidth。当然,tcp_timestamps的作用一开始并不是为了SYN Cookie,它还有别的重要功能。
The Linux SYN packet handling landscape is constantly evolving. Until recently SYN Cookies were slow, due to an old fashioned lock in the kernel. This was fixed in 4.4 and now you can rely on the kernel to be able to send millions of SYN Cookies per second, practically solving the SYN Flood problem for most users. With proper tuning it's possible to mitigate even the most annoying SYN Floods without affecting the performance of legitimate connections.
本文链接:https://cs.pynote.net/net/tcp/202205052/
-- EOF --
-- MORE --