云服务器价格_云数据库_云主机【优惠】最新活动-搜集站云资讯

分布式数据库_药物数据库_精选特惠

小七 141 0

野外SYN包处理

在Cloudflare,我们有很多在互联网上操作服务器的经验。但我们一直在提高对黑人艺术的掌握。在这个博客中,我们谈到了互联网协议的多个黑暗角落:比如了解FIN-WAIT-2或接收缓冲区调整。伊萨莫雷诺的CC BY 2.0图像但有一个话题没有引起足够的关注——SYN floods。我们使用Linux,结果发现Linux中的SYN包处理非常复杂。在这篇文章中,我们将对这个问题有所了解。两人排队的故事首先,我们必须了解,处于"侦听"TCP状态的每个绑定套接字都有两个独立的队列:SYN队列接受队列在文献中,这些队列通常被赋予其他名称,如"reqsk_queue"、"ACK backlog"、"listen backlog"甚至"TCP backlog",但为了避免混淆,我将坚持使用上述名称。同步队列SYN队列存储入站SYN数据包[1](具体为:struct inet_request_sock)。它负责发送SYN+ACK包并在超时时重试。在Linux上,重试次数配置为:$系统控制网络ipv4.tcp\u synack\u重试网络ipv4.tcp_synack_retries=5文档描述了这种切换:tcp_synack_retries-整数被动TCP连接尝试的同步次数将被重新传输。不应高于255。违约值为5,相当于31秒,直到最后一秒当前初始RTO为1秒的重传。与这是被动TCP连接的最后超时63秒后发生。在发送SYN+ACK之后,SYN队列等待来自客户机的ACK包-三方握手中的最后一个包。所有接收到的ACK包必须首先与完全建立的连接表相匹配,然后才与相关SYN队列中的数据进行匹配。在SYN Queue match中,内核从SYN队列中删除项,愉快地创建一个完整的连接(特别是:struct inet_sock),并将其添加到Accept队列中。接受队列接受队列包含完全建立的连接:准备由应用程序接收。当进程调用accept()时,套接字将退出队列并传递给应用程序。这是Linux上SYN包处理的简化视图。使用诸如TCP_DEFER_ACCEPT[2]和TCP_FASTOPEN之类的套接字切换,工作原理略有不同。队列大小限制Accept和SYN队列的最大允许长度取自应用程序传递给listen(2)syscall的backlog参数。例如,这会将Accept和SYN队列大小设置为1024:听(sfd,1024)注意:在4.3之前的内核中,SYN队列长度的计算方式不同。这个SYN队列cap过去由网络ipv4.tcp_max_syn_backlog切换,但现在不是这样了。如今net.core.somaxconn为两个队列大小设置上限。在我们的服务器上,我们将其设置为16k:$系统控制net.core.somaxconnnet.core.somaxconn=16384完美积压价值知道了这些,我们可能会问一个问题——理想的backlog参数值是多少?答案是:视情况而定。对于大多数琐碎的TCP服务器来说,这并不重要。例如,在1.11版本之前,Golang不支持定制backlog值。但是,增加该值有合理的理由:当传入连接的速率非常大时,即使使用performant应用程序,入站SYN队列可能需要更多的插槽。backlog值控制SYN队列的大小。这实际上可以理解为"飞行中的ACK包"。客户机的平均往返时间越长。在许多客户机远离服务器的情况下,几百毫秒之外,增加backlog值是有意义的。TCP_DEFER_ACCEPT选项会导致套接字在SYN-RECV状态下保持更长时间,从而导致队列限制。过多的积压也很糟糕:SYN队列中的每个插槽都使用一些内存。在SYN Flood期间,浪费资源存储攻击包是没有意义的。SYN队列中的每个struct inet_request_sock条目在内核4.14上占用256字节的内存。为了窥探Linux上的SYN队列,我们可以使用ss命令并查找SYN-RECV套接字。例如,在Cloudflare的一台服务器上,我们可以看到tcp/80 SYN队列中使用了119个插槽,tcp/443上有78个插槽。$ss-n state syn recv sport=:80 | wc-l119$ss-n state syn recv sport=:443 | wc-l78类似的数据可以用我们的overenginered SystemTap脚本显示:resq.stp公司.缓慢应用如果应用程序不能足够快地调用accept()会发生什么?这就是魔术发生的时候!当接受队列已满(其大小为backlog+1),则:将丢弃到SYN队列的入站SYN数据包。将丢弃到SYN队列的入站ACK数据包。TcpExtListenOverflows/LINUX_MIB_LISTENOVERFLOWS计数器将递增。TcpExtListenDrops/LINUX_MIB_LISTENDROPS计数器将递增。丢弃入站数据包有一个很强的理由:这是一种推回机制。另一方迟早会重新发送SYN或ACK包,希望到那时,缓慢的应用程序就会恢复。这是几乎所有服务器都需要的行为。完整性:可根据全局网络ipv4.tcp"在溢出切换时中止",但最好不要触碰它。如果您的服务器需要处理大量的入站连接,并且在accept()吞吐量方面有困难,请考虑阅读我们的Nginx tuning/Epoll work发行版文章和后续文章,展示有用的SystemTap脚本。通过查看NSAT计数器,可以跟踪接受队列溢出统计信息:$nstat-az TcpExtListenDrops公司TcpExtListenDrops 49199 0.0版这是一个全局计数器。这并不理想-有时我们看到它在增加,而所有的应用程序看起来都很健康!第一步应始终使用ss打印Accept Queue size:$ss-plnt sport=:6443 |猫本地状态接收-Q发送-Q地址:端口对等地址:港口听0 1024*:6443*:*列Recv-Q显示接受队列中的套接字数,Send-Q显示backlog参数。在本例中,我们看到没有未完成的套接字被accept()ed,但ListenDrops计数器仍在增加。结果我们的应用程序被卡住了几分之一秒。这足以让Accept队列在很短的时间内溢出。过了一会儿,它就会恢复。这样的情况很难用ss调试,所以我们编写了一个接受stpSystemTap脚本来帮助我们。它挂接到内核并打印正在丢弃的SYN包:$sudo stap-v接受stp时间(us)acceptq qmax本地地址远程地址1495634198449075 1025 1024 0.0.0.0:6443 10.0.1.92:285851495634198449253 1025 1024 0.0.0.0:6443 10.0.1.92:505001495634198450062 1025 1024 0.0.0.0:6443 10.0.1.92:65434...在这里您可以精确地看到哪些SYN包受到ListenDrops的影响。使用此脚本,了解哪个应用程序正在断开连接是很简单的。由internets_dairy提供的CC BY 2.0图像同步洪流如果有可能使接受队列溢出,那么也必须有可能使SYN队列溢出。在那种情况下会发生什么?这就是SYN Flood攻击的意义所在。在过去,用伪造的伪造SYN包淹没SYN队列是一个真正的问题。在1996年以前,只要填满SYN队列,就可以成功地拒绝几乎任何带宽很少的TCP服务器的服务。解决方案是SYN Cookies。SYN Cookies是一种允许无状态地生成SYN+ACK的结构,而不会实际保存入站SYN和浪费系统内存。SYN Cookies不会破坏合法流量。当另一方是真实的时,它将响应一个有效的ACK包,其中包括反射的序列号,可以通过密码验证。默认情况下,SYN Cookies在需要时启用-对于SYN队列已满的套接字。Linux在SYN Cookies上更新了几个计数器。发送SYN cookie时:tcpexttcpreqfulldocookies/LINUX_MIB_tcpreqfulldocookies递增。TcpExtSyncookiesSent/LINUX_MIB_SYNCOOKIESSENT递增。Linux曾用于增加TcpExtListenDrops,但没有从内核4.7开始。当入站ACK进入SYN队列并使用SYN cookies时:加密验证成功时,TcpExtSyncookiesRecv/LINUX_MIB_SYNCOOKIESRECV将递增。TcpExtSyncookiesFailed/LINUX_MIB_SYNCOOKIESFAILED在加密失败时递增。系统控制网络ipv4.tcp_syncookies可以禁用SYN Cookies或强制启用它们。默认值是好的,不要更改它。SYN Cookies和TCP时间戳SYN Cookies的魔力是有效的,但也不是没有缺点的。主要问题是SYN Cookie中可以保存的数据很少。具体地说,在ACK中只返回序列号的32位。这些位的用法如下:+----------+--------+-------------------+|6位| 2位| 24位||t mod 32 | MSS |哈希(ip,端口,t)|+----------+--------+-------------------+由于MSS设置被截断为只有4个不同的值,Linux不知道另一方的任何可选TCP参数。有关时间戳、ECN、选择性ACK或窗口缩放的信息丢失,并可能导致TCP会话性能下降。幸运的是,Linux有一个解决方案。如果启用了TCP时间戳,内核可以重用时间戳字段中的另一个32位插槽。它包含:+-----------+-------+-------+--------+|26位| 1位| 1位| 4位||时间戳| ECN | SACK | WScale|+-----------+-------+-------+--------+默认情况下应启用TCP时间戳,以验证请参阅sysctl:$系统控制网络ipv4.tcp_时间戳网络ipv4.tcp_时间戳=1历史上有很多关于TCP时间戳有用性的讨论。在过去的时间戳泄露了服务器的正常运行时间(这是否重要是另一个讨论)。这是8个月前修好的。TCP时间戳使用非常多的带宽—每个包上有12个字节。它们可以为数据包校验和添加额外的随机性,这有助于解决某些损坏的硬件。如上所述TCP时间