TCP 窗口缩放、时间戳和 SACK( 四 )


TCP 时间戳的另一个更晦涩的用例与 TCP syn cookie 功能有关 。
在服务器端建立 TCP 连接
当连接请求到达的速度快于服务器应用程序可以接受新的传入连接的速度时 , 连接积压最终将达到其极限 。 这可能是由于系统配置错误或应用程序中的错误引起的 。 当一个或多个客户端发送连接请求而不对 “SYN ACK” 响应做出反应时 , 也会发生这种情况 。 这将用不完整的连接填充连接队列 。 这些条目需要几秒钟才会超时 。 这被称为 “同步泛洪攻击”(syn flood attack)
TCP 时间戳和 TCP Syn Cookie
即使队列已满 , 某些 TCP 协议栈也允许继续接受新连接 。 发生这种情况时 , Linux 内核将在系统日志中打印一条突出的消息:
端口 P 上可能发生 SYN 泛洪 。 正在发送 Cookie 。 检查 SNMP 计数器 。
此机制将完全绕过连接队列 。 通常存储在连接队列中的信息被编码到 SYN/ACK 响应 TCP 序列号中 。 当 ACK 返回时 , 可以根据序列号重建队列条目 。
序列号只有有限的空间来存储信息 。 因此 , 使用 “TCP Syn Cookie” 机制建立的连接不能支持 TCP 选项 。
但是 , 对两个对等点都通用的 TCP 选项可以存储在时间戳中 。 ACK 数据包在回显时间戳字段中反映了该值 , 这也允许恢复已达成共识的 TCP 选项 。 否则 , cookie 连接受标准的 64KB 接收窗口限制 。
常见误区 —— 时间戳不利于性能
不幸的是 , 一些指南建议禁用 TCP 时间戳 , 以减少内核访问时间戳时钟来获取当前时间所需的次数 。 这是不正确的 。 如前所述 , RTT 估算是 TCP 的必要部分 。 因此 , 内核在接收/发送数据包时总是采用微秒级的时间戳 。
在包处理步骤的其余部分中 , Linux 会重用 RTT 估算所需的时钟时间戳 。 这还避免了将时间戳添加到传出 TCP 数据包的额外时钟访问 。
整个时间戳选项在每个数据包中仅需要 10 个字节的 TCP 选项空间 , 这不会显著减少可用于数据包有效负载的空间 。
常见误区 —— 时间戳是个安全问题
一些安全审计工具和(较旧的)博客文章建议禁用 TCP 时间戳 , 因为据称它们泄露了系统正常运行时间:这样一来 , 便可以估算系统/内核的补丁级别 。 这在过去是正确的:时间戳时钟基于不断增加的值 , 该值在每次系统引导时都以固定值开始 。 时间戳值可以估计机器已经运行了多长时间(正常运行时间 uptime) 。
从 Linux 4.12 开始 , TCP 时间戳不再显示正常运行时间 。 发送的所有时间戳值都使用对等设备特定的偏移量 。 时间戳值也每 49 天回绕一次 。
换句话说 , 从地址 “A” 出发 , 或者终到地址 “A” 的连接看到的时间戳与到远程地址 “B” 的连接看到的时间戳不同 。
运行 sysctl net.ipv4.tcp_timeamp=2 以禁用随机化偏移 。 这使得分析由诸如 wireshark 或 tcpdump 之类的工具记录的数据包跟踪变得更容易 —— 从主机发送的数据包在其 TCP 选项时间戳中都具有相同的时钟基准 。 因此 , 对于正常操作 , 默认设置应保持不变 。
选择性确认如果同一数据窗口中的多个数据包丢失了 , TCP 将会出现问题 。 这是因为 TCP 确认是累积的 , 但仅适用于按顺序到达的数据包 。 例如:

  • 发送方发送段 s_1、s_2、s_3、... s_n
  • 发送方收到 s_2 的 ACK
  • 这意味着 s_1 和 s_2 都已收到 , 并且发送方不再需要保留这些段 。
  • s_3 是否应该重新发送? s_4 呢? s_n?
发送方等待 “重传超时” 或 “重复 ACK” 以使 s_2 到达 。 如果发生重传超时或到达了 s_2 的多个重复 ACK , 则发送方再次发送 s_3 。
如果发送方收到对 s_n 的确认 , 则 s_3 是唯一丢失的数据包 。 这是理想的情况 。 仅发送单个丢失的数据包 。
如果发送方收到的确认段小于 s_n , 例如 s_4 , 则意味着丢失了多个数据包 。 发送方也需要重传下一个数据段 。


推荐阅读