한국어English日本語简体中文繁體中文DeutschไทยTiếng ViệtРусскийPortuguês (Brasil)EspañolBahasa Indonesia

游戏卡顿白皮书 › TCP 重传的根本原因

中间设备剥离 TCP 选项 Middlebox strips TCP options

原因 ID rt-sack-stripped · 主责 运维团队·网络运维 · 配合 运维团队·系统运维

在含图示和实验的完整版中打开此卡片 →

部分防火墙或加速设备删除或改写 TCP 选项后,丢多个包时每个往返只能恢复一个,或者窗口(一次能发送的量)变小,速度变慢。

起因 防火墙的“TCP 规范化”、老旧的加速设备剥离 SACK、时间戳、窗口缩放选项 → 结果 丢了多个包时每个往返只恢复一个,窗口被限制在 64 KB → 画面表现 每次丢包卡住的时间长得多(没有 SACK 就用不了 RACK-TLP),恢复后表现为快进。下载更新包这类大流量传输也很慢

症状
卡住, 快进
因素
停顿, 延迟
谁会遇到
特定地区/运营商, 全服
何时出现
一直
负责方
主责 运维团队·网络运维 · 配合 运维团队·系统运维
运维团队要做的事
网络:关闭相关设备的 TCP 规范化设置;同时检查防火墙的序列号随机化;在两端抓包比较 SYN 中的选项。服务器/OS:在 ss -ti 中确认缺少 sack、wscale 标记的连接是否集中在特定路径(Windows PC 按设置可能不使用 ts,所以只缺 ts 可能是正常的);确认服务器的 net.ipv4.tcp_sack 为 1。
监控图上
一直偏高 · 不带 SACK 开始的恢复次数(TcpExtTCPRenoRecovery)
查看位置
在 ss -ti 中看每个连接有没有 sack、wscale 标记,看 nstat 中 TcpExtTCPRenoRecovery(不带 SACK 开始的恢复)与 TcpExtTCPSackRecovery 的比例,以及 TcpExtTCPSACKDiscard(因对不上而丢弃的 SACK 块数)。对可疑路径在两端抓 SYN,比较其中的选项(Wireshark 的 tcp.options.sack_perm 等)
确认依据
只有经过特定路径或设备的连接缺少 sack、wscale,TcpExtTCPRenoRecovery 占比高。发送端 SYN 里有的 SACK 允许选项,接收端收到的 SYN 里没有。如果原因是序列号随机化,选项还在,但 TcpExtTCPSACKDiscard 增加
排除依据
所有连接都缺少 sack 时,先查服务器的 net.ipv4.tcp_sack 值。选项完整,TcpExtTCPSACKDiscard 也不变,则恢复慢另有原因(“thin stream 恢复慢”)
确认手段
运维工具即可确认(无需游戏代码)
深入了解
即使选项还在,SACK 也可能失效。防火墙的序列号随机化(sequence randomization)只改头部的序列号、不改 SACK 里的序列号时,发送方会丢弃对不上的 SACK。2019 年爆出 SACK 安全漏洞时,在服务器上用 tcp_sack=0 关掉 SACK 后忘了恢复,结果也一样。

出处

  1. RFC 2018: TCP Selective Acknowledgment Options IETF
    没有 SACK、只靠累积 ACK 时,每个往返只能得知一个丢失的包
  2. RFC 7323: TCP Extensions for High Performance IETF
    没有窗口缩放选项时,窗口最大为 2^16 = 64 KiB
  3. RFC 8985: The RACK-TLP Loss Detection Algorithm for TCP IETF
    RACK-TLP 必须使用 SACK
  4. net/ipv4/tcp_output.c Linux kernel
    Linux 只在使用 SACK 的连接上安排 TLP
  5. IP Sysctl Linux kernel
    tcp_sack 默认 1(开启)
  6. misc/ss.c iproute2
    ss -ti 根据连接使用的选项显示 ts、sack、wscale:发送,接收
  7. tcp: limit payload size of sacked skbs Linux kernel
    2019 年 SACK 处理漏洞(CVE-2019-11477)的修复提交
  8. Linux and FreeBSD Kernel: Multiple TCP-based remote denial of service vulnerabilities (NFLX-2019-001) Netflix
    当时给出的临时对策是设置 tcp_sack=0(关闭 SACK 处理)
  9. SNMP counter Linux kernel
    TcpExtTCPRenoRecovery(不带 SACK 开始恢复)、TcpExtTCPSackRecovery(借助 SACK 开始恢复),TcpExtTCPSACKDiscard(无效 SACK 块数)
  10. Display Filter Reference: Transmission Control Protocol Wireshark
    tcp.options.sack_perm(SYN 中的 SACK 允许选项)显示过滤器

相关原因

同一层:TCP 重传的根本原因

其他层中同样导致“卡住”的原因

查看含图示和实验的原卡片