今天在论坛上看别人分享了一篇 TCP 三次握手和四次挥手的文章,本来这篇文章平平,几乎可以说是抄教科书的,甚至于可以说是节选教科书的,因为里面的内容很简单,也很少。在平常的普通论坛,这篇帖子可能很快也就沉下去了,但是,我逛的这个帖子的同学数量虽少,但是,愤青不少,所以下面就开喷了。其中,有一个回帖说到 TCP 三次握手有什么必要性和局限性。这是个大问题啊,后面也陆续有同学参与进来讨论了,我也从同学们的讨论中学到了很多套路,其中这个 TFO(TCP Fast Open)就让我收获很大,这里就以自己的学习和知识积累总结一番。

TFO 简介

TFO 是在原始的 TCP 基础上进行扩展的协议,原文在 RFC7413 上,它基于 TCP 的改良之处在于三次握手期间是可以传输应用数据的。虽然在 TCP 中,三次握手包也是可以传输数据,但是 RFC793 中说了,即使你传了,也不会将这些数据上传给应用层,所以传了也没用。所以 TFO 就基于此,对 TCP 的三次握手进行了改良,从而提高了网络带宽的使用率,同时,也顺带解决了一些问题。

TFO 交互图

15003959058292

TFO 的三次握手肯定和 TCP 的三次握手时不同的,但是,过程相似,其中有一些细节需要说明:

首先是整体实现思路,TFO 的整体实现思路为:

  1. 先使用普通的 TCP 三次握手建立连接一次,但是这个过程中 TCP Server 会分配一个 cookie 给 Client,Client 将这个 Cookie 保存起来
  2. TCP Client 使用 Cookie 构造 TFO 握手包,这个时候正式开始 TFO 的流程
  3. TCP Server 需要对 Client 的 Cookie 进行验证,验证通过则正常交流,不通过则忽略

从图中可以看到,第一阶段就是一个普通的三次握手包,但是在请求中多了一个 CookieOpt 的标志;第二阶段的通信从第一个握手包就开始传输数据 DATA_A 了。

TFO 的 Cookie 是用于快速打开的关键,所以有一些限制是需要遵守的:

  1. Cookie 的长度必须是偶数,且长度是 0 或者介于 4 ~ 16

    为什么可以是 0?看第一张图的第一阶段,可以发现有一个 CookieOpt = Nil 的普通 TCP 握手包,这个时候是还没有 Cookie 的,所以应该是 0

  2. TFO Server 生成 Cookie 需要快速,生成的 Cookie 有时效性

    • 一个简单的实现就是直接将客户端的地址进行 AES-128 加密,然后截断为 64位传回给客户端,下次直接对客户端的 IP 进行同样的操作,然后对比结果即可。
    • 至于时效性的话,可以定时周期得更换 Server 端的 key,这样以前的 Cookie 都失效了
  3. TFO Client Cookie 处理

    • TFO 客户端应该将 Cookie 保存下来,如果是多实例的客户端,那么针对每个客户端都应该保存一份
    • TFO Client 尽量将 MSS 也保存下来,这样下次使用的时候第一次就可以知道传多少数据合适,而不用等 ACK 传回 MSS 才知道

TFO 的用处

从上面的示例中可以看到 TFO 的其中一个优点就是可以提高网络的利用率,尤其是有频繁网络建立的情景,TFO 的优势尤其明显。但是,除此之外,TFO 还有一个很大的优点就是可以防止 SYN泛洪攻击

因为咱们前面说了,TFO 的客户端是需要针对 Client 进行 Cookie 验证的,验证的基础就是时间戳和 IP,如果使用 SYN Flood 攻击,那么就需要使用正确的 Cookie,只要 Cookie 不对,Server 就可以忽略掉这个连接,不会占用到连接队列。当然,这只是其中一个考虑,这个问题比较复杂,先挖个坑,后面专门找个坑来填。

总结

本文简单快速得介绍了一下 TFO 的概念了一些细节,但是并没有很深入得完全讲解以及介绍一些实际实践。所以在后面的参考文献中给出了 RFC7413 的地址,感兴趣的同学可以自行阅读参考。最后再归纳一下,TFO 的两大好处:

  1. 提高网络利用率
  2. 提高网络安全性

Reference

  1. RFC 7413
  2. TCP快速打开
  3. Speed up web delivery with Nginx and TFO
  4. TCP Fast Open