摘要

  • RFC 1185 的计算显示,在 1 Gbit/s 的设定下,TCP 序列号关键的半圈约 17 秒就能走完。迟到的旧包可能带着再次看起来合理的号码出现。
  • RFC 1323 的 PAWS 用 TS.Recent 比较入站时间戳,在一条现存连接内丢弃旧重复包;若连接空闲超过 24 天,接收方必须让那份旧记录失效,以便真正的新包能唤醒连接。

TCP 给传输中的字节编号,号码用完就从头再来。线路慢时,上一轮编号对应的数据包早已离开网络。线路快起来,号码转得比迟到包消失的速度还快。守门人看到熟悉的号码,难免把昨天的访客当成今天的。

1990 年 10 月的 RFC 1185把这个麻烦写成了算式。它的表格给出:以 1 Gbit/s 传输时,相关序列号空间的半圈大约只需 17 秒。文中拿来比较的 TCP 报文最长存活时间是两分钟。速度提高了号码回来的频率,却不会顺手清空网络里每一个迟到的报文。序列号之外,数据包还需要第二张证件。

1992 年 5 月的 RFC 1323选了 TCP 时间戳。这个选项一身两职:估计往返时间,也为 PAWS 提供防范序列号回绕的证据。在一条已经建立的连接里,接收方保存一个较近的时间戳 TS.Recent。新报文若带来比它更小的 TSval,且旧值仍然有效,接收方便回送确认并丢弃该报文。通过时间戳检查的包,仍要接受普通 TCP 接收窗口的检验。旧包能撞上当前序列号,时间戳却会露馅。

两端不用对着同一只钟校表。发送方负责让自己的时间戳单调前进,接收方在 32 位模空间中比较。TS.Recent 同时供往返时间测量使用,节省了一份状态。PAWS 检查这条连接收到的数据包和确认包,拦的是同一条开放连接内的旧重复,而不是给所有历史连接发一张通用通行证。

麻烦是守门人的笔记也会老。RFC 1323 允许时间戳时钟从每秒跳一次到每毫秒跳一次。取最快速度,32 位时间戳的有符号半圈大约 24.8 天。设想连接一直开着,却长时间没有流量;对端的时钟继续向前。它重新发包时,真正的新时间戳可能在模比较中看起来低于旧 TS.Recent。若接收方永远相信那页旧记录,就会把后来的每个包都挡在门外,连接由此冻结。

RFC 1323 的解法是明确要求遗忘。连接空闲超过 24 天,PAWS 实现必须让 TS.Recent 失效。遇到看似更小的入站时间戳,接收方先检查手中记录是否还值得信任。记录已经过期,报文就能通过 PAWS 这一关,并为后续比较留下新参考;普通序列号窗口检查依然照常进行。扔掉旧笔记,不等于撤掉门卫。

作者考虑过偶尔发送保活报文来刷新记忆,最后选择失效规则,因为 TCP 规范并未规定必须发送保活。接收方也不知道对端时钟究竟跳得多快,只能按最快情形定界。这里的 24 天计的是空闲时间,不是连接的寿命。持续传输的连接会不断更新参考值;睡了很久的连接需要一条醒来的路。

后来的 RFC 7323保留了这套过期时间戳的解释和 24 天失效要求。1990 到 1992 年间,TCP 的两种“旧”碰了面:高速让序列号变旧得太快,长期安静又让拦旧包的时间戳参考值老得太久。PAWS 教会守门人识别迟到者,也教他何时把自己的名单扔进碎纸机。