选择性应答(SACK)是 TCP 的一项可选特性,可以提高某些网络中所有可用带宽的使用效率。虽然 SACK 可以提高吞吐量,但事实证明,对于 TCP 发送方来说,处理这种类型的应答严重占用 CPU。这个弱点在商业网点中可能会被一些恶意的同行利用。本文进行了一些实验性评测,展示这种问题在 Linux® TCP 协议栈中的影响程度。大多数发行版在默认情况下都会启用 SACK 功能。
在最近几个月 Internet 上对 Linux® 开发的日常讨论中,一个重要的话题就是 Linux 的 TCP SACK(Selective Acknowledgment)实现。讨论主要集中在处理某些 SACK 事件时 TCP 协议栈的性能方面,有一些人认为这会带来安全隐患。
我对这些讨论非常感兴趣,但是这些讨论似乎缺乏有力的数据支持。他们讨论的是哪些特定情形?这只是一个小的性能缺陷,还是完全有可能使服务器受到拒绝服务攻击?
我收集了一些有关此主题的引述(参见 参考资料 中这些引述的链接):
David Miller:“现在,基本上每个 TCP 协议栈都存在此问题,处理无效或恶意创建的 SACK 块会消耗大量 CPU 资源。”
Ilpo Jarvinen [1]:“但是,只要在 sacktag 中存在对 skb 的 fack_count 的依赖,即使在 RB-tree 的情况下,CPU 也存在处理攻击的空间,因为在慢速道上只能步行”。
NC State University:“我们在这个试验中展示了 SACK 处理的效率。正如我们看到的许多情形,TCP 不能很好地处理大型窗口拖放,尤其是大量数据的缓冲。”
CHC IT: “最后,对 2.4 和 2.6 版提出一个警告:对于 TCP 窗口大于 20 MB 的大型 BDP 路径,可能会遇到 Linux SACK 实现问题。如果 Linux 收到一个 SACK 事件时有大量包在传递,它会花费很长的时间定位经过 SACK 处理的包,而且将会导致 TCP 超时,CWND 将返回到第一个包。”
本文将观察 SACK 实现及其在非理想条件下的性能,我们将从 Linux 2.6.22 入手,它目前是 Ubuntu 7.10 的普通发行版内核。该内核在几个月之前发行,自从它发布以来没有出现什么问题,开发人员一直在为其编写代码。当前的开发内核为 2.6.25,其中包含 Ilpo Jarvinen 提供的一系列补丁,用于处理 SACK 性能。本文最后将查看这些代码如何发挥作用,并简略讨论进一步的更改。
SACK 由 RFC 2018、2883 和 3517 定义(参见 参考资料 中这些 RFC 的链接)。普通 TCP(即未提供 SACK 特性)应答是严格累积的 — 对 N 的应答意味着字节 N 和所有之前的字节都已经收到。SACK 要解决的问题普通累积式应答的 “全有或全无” 性质。
例 如,即使包 2(假设从 0 到 9 的序列)是在传送过程中惟一丢失的包,接收方也只能对包 1 发出一个普通的 ACK,因为这是连续接收到的包中的最后一个。另一方面,SACK 接收方可以发出包 1 的 ACK 和包 3 到包 9 的 SACK 选项。这种附加信息可以帮助发送方确定丢失的包最少,只需重新传送很少的数据。如果没有这种附加信息,它需要重新传送大量的数据,这样会降低传送速率,从 而适应高丢包率的网络。
在高延迟的连接中,SACK 对于有效利用所有可用带宽尤其重要。高延迟会导致在任何给定时刻都有大量正在传送的包在等待应答。在 Linux 中,除非得到应答或不再需要,这些包将一直存放在重传队列中。这些包按照序列编号排队,但不存在任何形式的索引。当需要处理一个收到的 SACK 选项时,TCP 协议栈必须在重传队列中找到应用了 SACK 的包。重传队列越长,找到所需的数据就越困难。
每个包中的最多包含 4 个 SACK 选项。
本文转自:IBM developerWorks 中国