Is it Still Possible to Extend TCP?¶
这是 MPTCP 设计团队做的测量工作,是 NSDI12-MPTCP 的前作。本文中的结论直接影响了 MPTCP 的标准化设计.
- 本文(IMC11-MPTCP): 测量结果
- NSDI12-MPTCP: 标准化设计
一、核心问题与结论¶
问题:IP options 早已因路由器慢路径处理而无法使用。TCP 的官方扩展机制是 TCP options,它是否也被中间盒(middlebox,例如 NAT、防火墙、性能增强代理 PEP、流量规整器)"僵化"了?如果还能用,新扩展的设计会受到哪些约束?
结论:TCP 仍然可以扩展,但设计空间很窄
新扩展必须满足三点:
- 在 SYN 握手中协商
- 容忍 options 被剥离并回退到普通 TCP
- 不依赖序列号的绝对值、序列空间的连续性和报文段边界
至少 25% 的路径存在超出普通防火墙的 Layer4 (传输层) 干预
二、测量方法¶
工具 TCPExposure:
客户端(发起方,约 3000 行 Python)由志愿者运行,服务器(响应方,约 500 行)部署在作者自己运营的、没有中间盒的网络 sfc.wide.ad.jp
两端都可控,这是本文相对于 Padhye/Floyd、Medina 等"仅客户端"测量的关键优势:只有在两端同时抓包,才能判断选项是否被删、序列号是否被改、报文段是否被重新分段
响应方是无状态的:
它的回复只依赖收到的报文段(收到 SYN 就回 SYN/ACK,ACK 号取收到序列的末尾)。这样便于推理,因为没有隐藏的服务器状态。但这也带来一个局限:无法区分选项是在 SYN 中被剥离,还是在 SYN/ACK 中被剥离
控制命令(放在载荷里):
- just ack:默认命令,响应方回一个纯 ACK
- echo headers:响应方把收到的头部和自己回复的头部都放进载荷返回,客户端据此逐字段比对
- don't advance ack:响应方不推进 ACK,只用于重传测试
参数:
两端 MSS 都设为 512,以避免意外分片。测试三个端口:80、443,以及未分配的 34343,因为中间盒的行为与端口相关
三、各项测试与结果¶
| 测试 | 测什么 | 关键结果(端口 34343 / 80 / 443) | 对扩展设计的启示 |
|---|---|---|---|
| 4.1 选项 | SYN 和数据段中的未知选项(MP_CAPABLE / MP_DATA)、已知选项(TIMESTAMP) | SYN 中未知选项被删的比例为 4% / 14% / 6%。没有任何路径因为选项而丢弃 SYN。凡是在 SYN 中放行未知选项的路径,在数据段中也会放行 | 在 SYN 中协商就足以判断后续能否使用该选项。必须容忍 SYN/ACK 中的选项被剥离 |
| 4.2 序列号改写 | 双向 ISN 是否被改 | 序列号未变的比例为 93% / 82% / 90%。原因是防火墙为弥补可预测 ISN 而重新随机化。部分改写序列号的路径仍然放行未知选项(34343:5/9;80:7/26;443:7/14) | 不能在选项中嵌入绝对序列号,应改用相对连接起点的字节偏移 |
| 4.3 序列空洞 | Data-first:中间盒看到空洞两侧的数据;Ack-first:先看到越过未见数据的 ACK(proactive ack) | Data-first 通过率 97% / 85% / 95%,失败表现为无响应或重复 ACK。Ack-first 通过率只有 76% / 67% / 74%,约 20% 无响应,另有 ACK 被"修正"、中间盒自行重传等情况。约 10% 的家庭网络在 Ack-first 中无响应,尽管这些网络都不剥离选项 | 不能在单条流中留下序列空洞 |
| 4.4 代理确认 | Proxy SYN/ACK(依据 ISN、窗口、MSS、窗口缩放因子的特征值)和 Proxy Data ACK | 两类测试识别出的路径集合完全一致,说明存在完整的 split-TCP 代理 | 代理会剥离选项,扩展会自然回退到普通 TCP |
| 4.5 不一致重传 | 重传时使用不同的载荷(等长、少 16 字节、多 16 字节) | 大多数路径放行(99% / 87% / 97%)。少数路径缓存原始段并重发,即 ACK 推进了但头部没有被回显。端口 80 有一条路径直接 RST | 基本可用,但扩展不能依赖"到达的是原始数据还是新数据" |
| 4.6 重分段 | 拆分(发送满长度段,响应方 MSS 为 512);合并(顺序发送,以及先制造空洞迫使中间盒排队) | 拆分只在代理路径上出现(1 / 9 / 4 条),且都不保留选项。没有任何中间盒在放行选项的同时进行重分段。有一条非代理路径只在不带选项时才合并 | 选项放行时,可以认为它仍然绑定在原来的报文段上 |
| 4.7 智能网卡 | 4 个厂商的 12 款支持 TSO 的网卡,以及 Intel 82598EB 的 LRO | TSO 会把选项复制到每个拆分后的段。LRO 只合并选项值相同的段,合并后只保留一份选项 | 选项会被重复或合并,不能用选项出现的次数做信令 |
四、三个案例分析¶
第 5 节
(1) MPTCP: 设计几乎完全由中间盒的行为决定
- 协商:在 SYN 中携带
MP_CAPABLE- 如果第一个 RTT 内数据段上的选项没有通过,就回退到普通 TCP
- 每个子流使用独立的序列空间
- 因为跨路径条带化会造成序列空洞(数据空洞的不通过率为 2–10%,ACK 空洞约 25%)
- 因此需要额外的数据层序列号(DSN)
- DSN 放在 TCP Options 而不是载荷里
- 放在载荷里需要 TLV 分块,会引入不一致重传的风险,也会迫使中间盒解析载荷
- 采用显式映射(数据序列号起点、子流序列号起点、长度),而不是"每段一个 DSN"
- 以兼容 TSO 的选项复制和 LRO 的合并
- 子流序列号使用相对值,以应对 ISN 改写
- DSN 映射带校验和
- 用来防范 FTP/SIP 等应用层网关改写载荷
- 必要时回退到单路径
- 重传时总是发送原始数据
- 使用显式的数据层 ACK,不从子流 ACK 推断,以防范 proactive ack
(2) TCP Long Option [扩展选项空间]: 按现有形式不安全
- TSO 会让每个拆分段看起来都带有长选项,因此需要像 MPTCP 那样显式标明扩展选项的位置
- 同样需要相对序列号和校验和
- 如果 SACK 放在扩展空间里,会改写序列号的中间盒看不到它,也就无法同步修正
- 重传时扩展选项内容变化,在缓存段的中间盒看来就是不一致重传,可能导致连接失败
XXX is Not an Option 这句话很常见!
笔者见过好多次这类表达, 比如:
(1) Fonseca、Porter、Katz、Shenker、Stoica 2005 年的 Berkeley 技术报告《IP options are not an option》
(2) HotNets'18:《Delay is Not an Option: Low Latency Routing in Space》
(3) 一句更有名的话 "Failure is not an option"。这句话出自电影《阿波罗 13 号》(1995)
这个句式表示: "XXX 不是一个可行的选择"
- 看 (1): 带 IP options 的包会被路由器送进慢路径(由 CPU 软件处理,而不是硬件转发),甚至直接被丢弃。所以这个扩展机制实际上已废
- 看 (2): "对 LEO 星座来说,高时延是不可接受的"。呼应了论文的核心卖点:低轨卫星加上星间激光链路,长距离时延可以低于地面光纤
当你想表示: XXX 已经过时了 / XXX 需要rethinking / ... 就可以采用这个方式! 🚀