游戏卡顿白皮书 › 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 后忘了恢复,结果也一样。
出处
- RFC 2018: TCP Selective Acknowledgment Options IETF
没有 SACK、只靠累积 ACK 时,每个往返只能得知一个丢失的包 - RFC 7323: TCP Extensions for High Performance IETF
没有窗口缩放选项时,窗口最大为 2^16 = 64 KiB - RFC 8985: The RACK-TLP Loss Detection Algorithm for TCP IETF
RACK-TLP 必须使用 SACK - net/ipv4/tcp_output.c Linux kernel
Linux 只在使用 SACK 的连接上安排 TLP - IP Sysctl Linux kernel
tcp_sack 默认 1(开启) - misc/ss.c iproute2
ss -ti 根据连接使用的选项显示 ts、sack、wscale:发送,接收 - tcp: limit payload size of sacked skbs Linux kernel
2019 年 SACK 处理漏洞(CVE-2019-11477)的修复提交 - Linux and FreeBSD Kernel: Multiple TCP-based remote denial of service vulnerabilities (NFLX-2019-001) Netflix
当时给出的临时对策是设置 tcp_sack=0(关闭 SACK 处理) - SNMP counter Linux kernel
TcpExtTCPRenoRecovery(不带 SACK 开始恢复)、TcpExtTCPSackRecovery(借助 SACK 开始恢复),TcpExtTCPSACKDiscard(无效 SACK 块数) - Display Filter Reference: Transmission Control Protocol Wireshark
tcp.options.sack_perm(SYN 中的 SACK 允许选项)显示过滤器
相关原因
同一层:TCP 重传的根本原因
其他层中同样导致“卡住”的原因
查看含图示和实验的原卡片