原文 https://datatracker.ietf.org/doc/html/rfc6298 Computing TCP's Retransmission Timer 计算TCP重传计时器
概述
本文档定义了传输控制协议 (TCP) 发送方用于计算和管理其重传计时器的标准算法。 它扩展了 RFC 1122 第 4.2.3.1 节中的讨论,并将支持算法的要求从 SHOULD 升级为 MUST。 本文档废弃了 RFC 2988。
1. 简介
传输控制协议 (TCP) [Pos81] 使用重传计时器来确保在没有来自远程数据接收器的任何反馈的情况下传输数据。该计时器的持续时间称为 RTO(重传超时)。 RFC 1122 [Bra89] 规定 RTO 应该按照 [Jac88] 中的概述进行计算。
本文档编写了设置 RTO 的算法。此外,本文档扩展了 RFC 1122 第 4.2.3.1 节中的讨论,并将支持算法的要求从应该(SHOULD)升级为必须(MUST)。 RFC 5681 [APB09] 概述了 TCP 用于在 RTO 到期并发送重传后开始发送的算法。本文档不会改变 RFC 5681[APB09] 中概述的行为。
在某些情况下,TCP 发送方比本文档中详述的算法所允许的更保守可能是有益的。但是,TCP 不得比以下算法允许的更激进。本文档废弃了 RFC 2988 [PA00]。
2. 基本算法
为了计算当前 RTO,TCP 发送方维护两个状态变量,SRTT(平滑往返时间)和 RTTVAR(往返时间变化)。此外,我们假设时钟粒度为 G 秒。
SRTT、RTTVAR 和 RTO 的计算规则如下:
(2.1) 在对发送方和接收方之间发送的段进行往返时间 (RTT) 测量之前,发送方应该
设置 RTO <- 1 秒,尽管 (5.5) 中讨论的重复重传的“退避”仍然适用。
请注意,本文档的先前版本使用了 3 秒的初始 RTO [PA00]。 TCP 实现可能仍使用此值(或任何其他值 > 1 秒)。附录 A 中进一步详细讨论了初始 RTO 下限的这种变化。
(2.2) 当第一次 RTT 测量 R 进行时,主机必须设置
(2.3) 当进行后续的 RTT 测量 R' 时,主机必须设置
更新到 RTTVAR 时使用的 SRTT 的值是它在使用第二次分配更新 SRTT 之前的值。 也就是说,更新 RTTVAR 和 SRTT 必须按上述顺序计算。
以上应该使用 alpha=1/8 和 beta=1/4 计算(如 [JK88] 中所建议的)。
计算完成后,主机必须更新
(2.4) 每当计算 RTO 时,如果它小于 1 秒,那么 RTO 应该四舍五入到 1 秒。
传统上,TCP 实现使用粗粒度时钟来测量 RTT 并触发 RTO,这对 RTO 施加了很大的最小值。研究表明,需要一个大的最小 RTO 来保持 TCP 保守并避免虚假重传 [AP99]。因此,该规范需要较大的最小 RTO 作为保守方法,同时承认在未来某个时刻,研究可能表明较小的最小 RTO 是可以接受的或更好的。
(2.5) 一个最大值可以放在 RTO 上,只要它至少是 60 秒。
3. 获取 RTT 样本
TCP 必须使用 Karn 算法 [KP87] 来获取 RTT 样本。也就是说,RTT 样本不得使用重传的段进行(因此,回复是针对数据包的第一个实例还是后来的实例是不明确的)。 TCP 可以安全地从重传段中获取 RTT 样本的唯一情况是使用 TCP 时间戳选项 [JBB92] 时,因为时间戳选项消除了关于哪个数据段实例触发了确认的歧义。
传统上,TCP 实现一次进行一次 RTT 测量(通常,每个 RTT 测量一次)。但是,当使用时间戳选项时,每个 ACK 都可以用作一个 RTT 样本。 RFC 1323 [JBB92] 建议使用大拥塞窗口的 TCP 连接应该为每个数据窗口采集许多 RTT 样本,以避免估计 RTT 中的混叠效应。 TCP 实现必须对每个 RTT 至少进行一次 RTT 测量(除非根据 Karn 算法不可能这样做)。
对于相当适中的拥塞窗口大小,研究表明对每个段进行计时不会导致更好的 RTT 估计器 [AP99]。
此外,当每个 RTT 采集多个样本时,第 2 节中定义的 alpha 和 beta 可能会保留不充分的 RTT 历史记录。改变这些常数的方法目前是一个开放的研究问题。
4. 时钟粒度
对用于计算 RTT 测量和不同状态变量的时钟粒度 G 没有要求。但是,如果 RTO 计算中的 K*RTTVAR 项等于零,则方差项必须四舍五入到 G 秒(即,使用步骤 2.3 中给出的等式)。
经验表明,更细的时钟粒度(<= 100 毫秒)比粗粒度的性能要好一些。
请注意,[Jac88] 概述了几个巧妙的技巧,可用于从粗粒度计时器中获得更好的精度。这些变化在当前的 TCP 实现中被广泛实现。
5. 管理 RTO 定时器
一个实现必须以这样一种方式来管理重传计时器,即一个片段永远不会过早重传,即在该片段的前一次传输之后少于一个 RTO。
下面是管理重传定时器的推荐算法:
(5.1) 每次发送包含数据的数据包(包括重传),如果计时器未运行,则启动它运行,使其在 RTO 秒后到期(对于 RTO 的当前值)。
(5.2) 当所有未完成的数据都得到确认后,关闭重传定时器。
(5.3) 当收到确认新数据的 ACK 时,重新启动重传定时器,使其在 RTO 秒后到期(对于 RTO 的当前值)。
当重传定时器超时时,执行以下操作:
(5.4) 重传尚未被 TCP 接收方确认的最早段。
(5.5) 主机必须设置 RTO <- RTO * 2(“退避计时器”)。上面(2.5)中讨论的最大值可用于提供此加倍运算的上限。
(5.6) 启动重传计时器,使其在 RTO 秒后到期(对于 5.5 中概述的加倍操作后的 RTO 值)。
(5.7) 如果定时器超时等待 SYN 段的 ACK 并且 TCP 实现正在使用小于 3 秒的 RTO,则当数据传输开始时(即,在三路握手之后),RTO 必须重新初始化为 3 秒完成)。
这代表了对本文档 [PA00] 先前版本的更改,并在附录 A 中进行了讨论。
请注意,重传后,一旦获得新的 RTT 测量值(只有在发送并确认新数据时才会发生),将执行第 2 节中概述的计算,包括 RTO 的计算,这可能导致“崩溃”RTO在受到指数回退后回退(规则 5.5)。
请注意,TCP 实现可能会在多次退出计时器后清除 SRTT 和 RTTVAR,因为在这种情况下,当前的 SRTT 和 RTTVAR 很可能是假的。一旦 SRTT 和 RTTVAR 被清除,它们应该使用根据 (2.2) 而不是使用 (2.3) 获取的下一个 RTT 样本进行初始化。
6. 安全考虑
本文档要求 TCP 在重新传输未确认的段之前等待给定的时间间隔。攻击者可以通过向定时数据包的延迟或其确认延迟添加延迟,使 TCP 发送方计算出较大的 RTO 值。然而,增加数据包延迟的能力通常与导致数据包丢失的能力同时发生,因此很难看出攻击者可能从这种攻击中获得什么,这可能比简单地丢弃一些TCP连接的数据包造成更大的损害。
互联网在很大程度上依赖于 RTO 算法(以及 RFC 5681 中描述的那些)的正确实现,以保持网络稳定性并避免拥塞崩溃。攻击者可以通过在接收方实际接收到数据之前伪造段的确认,使 TCP 端点在拥塞时做出更积极的响应,从而将 RTO 降低到不安全的值。但是要做到这一点需要正确地欺骗确认,这很困难,除非攻击者可以监控发送方和接收方之间路径上的流量。
此外,即使攻击者可以使发送方的 RTO 达到太小的值,攻击者似乎也无法利用这一点进行大量攻击(与如果他们可以欺骗属于该连接的数据包相比,他们可以造成的其他损害) ,因为发送 TCP 仍然会在面对由于实际拥塞而导致错误传输的数据包丢失时退出其计时器。
RFC 5681 [APB09] 中的安全考虑也适用于本文档。
7. RFC 2988 的变化
本文档将初始 RTO 从前 3 秒 [PA00] 减少到 1 秒,除非 SYN 或 SYN 的 ACK 丢失,在这种情况下,默认 RTO 在数据传输开始前恢复为 3 秒。
8. 致谢
本文中描述的 RTO 算法由 Van Jacobson 在 [Jac88] 中提出。
促使将初始 RTO 从 3 秒更改为 1 秒的大部分数据来自 Robert Love、Andre Broido 和 Mike Belshe。