为什么在TCP上使用TCP不是一个好主意(2001)

2020-11-13 19:38:25

IP隧道应用的一个经常出现的想法是在基于TCP的连接上运行像PPP这样的协议,该协议以适合流传输(如调制解调器线路)的格式封装IP分组。通过在SSH上运行PPP来加密隧道将是一个简单的解决方案,已经有几个推荐(一个在Linux HOWTO基础上,一个在我自己的网站上,当然还有其他几个)。这也是压缩任意IP流量的一种简单方法,而基于数据报的压缩很难克服效率限制。不幸的是,它的效果并不好。预计会出现长时间延迟和频繁的连接中止。这就是为什么。TCP将数据流划分为多个数据段,这些数据段作为单独的IP数据报发送。数据段携带序列号和确认号,序列号对流中的字节进行编号,确认号告诉另一端最后接收到的序列号。[RFC793]由于IP数据报可能丢失、重复或重新排序,因此使用序列号重新组装流。确认号间接告知发送方数据段是否丢失:当最近发送的数据段的确认在一定时间内未到达时,发送方假定数据包丢失并重新发送该数据段。许多其他使用类似方法的协议大多设计为在带宽相对固定的线路上使用,它们有固定或可配置的一定时间量。然而,在互联网中,带宽、延迟和丢失率等参数在不同的连接之间差别很大,甚至在单个连接上会随着时间的推移而变化。在快速局域网上将超时固定在秒范围内是不合适的,在拥塞的国际链路上也同样不合适。事实上,这会加剧交通拥堵,并导致一种被称为“崩溃”的效应。因此,TCP对所有与计时相关的参数使用自适应超时。它们从保守的估计开始,并随着每个接收到的数据段而动态变化。在[RFC2001]中描述了实际使用的算法。这里的细节并不重要,但有一个关键属性:当段超时时,以下超时会增加(事实上,这是指数级的,因为这已被证明可以避免熔毁效应)。在各种不同的连接特征上,TCP超时策略在Internet中运行良好。因为TCP非常努力地不中断连接,所以超时可能会增加到几分钟的范围。这正是无人值守批量数据传输的明智之举。(对于交互式应用程序,这种慢速连接当然是不可取的,而且很可能会被用户终止。)。在将一个TCP连接堆叠在另一个连接之上时,这种可靠性优化会中断,这是TCP设计者从未预料到的。但在SSH或其他基于TCP的协议上运行PPP时会发生这种情况,因为PPP封装的IP数据报可能承载基于TCP的有效负载,如下所示:请注意,上层和下层的TCP具有不同的计时器。当上层连接快速启动时,其计时器也会很快。现在可能发生的情况是,较低的连接具有较慢的计时器,可能是基础连接较慢或不可靠的一段时间的遗留问题。想象一下,在这种情况下,当基本连接开始丢失数据包时会发生什么。较低层的TCP将重新传输排队并增加其超时。由于连接在这段时间内被阻塞,上层(即有效负载)的TCP无法及时获得ACK,并且还将排队重新传输。由于超时仍然小于下层超时,因此上层将排队更多的重传,其速度快于下层处理它们的速度。这会使上层连接很快停止,每次重新传输都会增加问题--内部熔断效应。TCP可靠性条款在这里适得其反。上层重传是完全没有必要的,因为运营商保证了传输--但是上层的TCP不知道这一点,因为TCP总是假设一个不可靠的运营商。整个问题是启动CIPE项目的最初动机,因为我使用了一段时间的PPP over SSH解决方案,结果证明它相当不可用。当时,它必须经过一条频繁丢包的光纤链路,有时会在很长一段时间内丢失10%-20%。使用普通的TCP,这是可以忍受的(因为链路并不拥塞),但是使用堆叠的协议,连接将变得非常慢,然后非常频繁地中断。这就是CIPE使用数据报载体的详细原因。(选择UDP而不是像IPsec那样的另一个IP级协议有几个原因:这允许通过端口号来区分隧道,并且它增加了在SOCKS上运行的能力。)。数据报载体具有与普通IP完全相同的特性,而TCP是为运行OVE而设计的