这篇讨论TCP的协议头,以及网络对TCP协议头产生的固化效果。

TCP协议头

TCP的协议头在RFC793定义的,可以说非常之精简:

    0                   1                   2                   3   
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          Source Port          |       Destination Port        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                        Sequence Number                        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Acknowledgment Number                      |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  Data |           |U|A|P|R|S|F|                               |
   | Offset| Reserved  |R|C|S|S|Y|I|            Window             |
   |       |           |G|K|H|T|N|N|                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           Checksum            |         Urgent Pointer        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Options                    |    Padding    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                             data                              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

下面简单介绍下TCP协议头各个字段的作用:

  • Source Port,Destination Port,Checksum这三个是和UDP的协议头类似,就不赘述了
  • Data Offset的单位是4字节。TCP头的固定长度是20个字节,如果不带Option,Data Offset的值就是20/4=5;如果带Option,那么Option的字节数的计算公式是Data Offset x 4 - 20。
  • Window是用来做流控的,接收端通过这个字段来告诉发送端接收窗口的大小。
  • Sequence Number和Acknowledge是用来做确保数据的可靠性。
  • URG/ACK/PSH/RST/SYN/FIN几个控制位是用来管理TCP连接的。
  • Urgent point是TCP设计的一个小败笔吧,RFC6093已经不推荐使用TCP的Urgent机制了,留着这个字段,只为保持向后兼容性。

除了以上的固定字段以外,TCP还有很多Options用来扩展协议头的功能,由于不是本文的重点,所以就不一一列举了。

从网络视角看TCP

从TCP连接的两端看,网络好像是一条双向的快车道。但实际上不是这样子的,网络是由一个一个节点(路由器和交换机)构成的,其中很多节点会对TCP的头进行检测和修改,如下图所示:

                 +--------+      +--------+                    
                 | Middle |      | Middle |                    
             />>>| Box 1  |>>>>>>| Box 2  |>>>>>>>\            
            /    +--------+      +--------+        \           
  +--------+                                        +--------+ 
  | Server |                                        | Client | 
  +--------+                                        +--------+ 
           \             +--------+                /           
            \<<<<<<<<<<<<| Middle |<<<<<<<<<<<<<<</            
                         | Box 3  |                            
                         +--------+                            

对上图的一些解释。首先网络具有上下行不对称性。对于双向的TCP连接来说,上行和下行在网络中走的路径可能完全不一致。在上图中位于上方的路径表示从Server到Client;位于下方的路径表示从Client到Server。不管是上行的路径还是下行的路径,都是Middlebox的存在,对TCP的协议头进行检测和修改。这些Middlebox有不同的目的,有的是为了增加TCP的性能,而其他的则可能是为了阻止和过滤某些TCP流量。Middlebox的种类非常多,RFC 3234 - Middleboxes: Taxonomy and Issues对各种Middlebox进行了专门的介绍。

固化问题

TCP的协议头是暴露给网络的,就算使用TLS加密,能被加密的也只是TCP搭载的数据,而不是TCP的协议头。现在问题来了,如果网络的Middlebox对TCP的协议头做了各种各样的动作,那么TCP的协议头就会被固化住,很难修改和增加其他功能。因为一旦TCP协议头进行了修改,必须得让这些Middlebox认识和学会如何处理修改后的TCP协议头才行。在现实情况下,这基本是不太可能的,因为Middlebox是归属于不同群体,想让大家统一行动,那得费多大的力气啊。

这就给TCP带来了一个大难题,但凡TCP要做任何的改进,它就要取得太多太多人的认可,极大地阻碍了TCP的演进。有一个英文术语专门用来描述这个问题,叫做ossifying ,指的就是TCP协议头固化的问题。

Is it still possible to extend TCP?这篇论文里面对Middlebox对TCP协议的影响进行探究,得出了一个结论:虽然很难,TCP依然可以扩展,但是必须要考虑非常非常多东西,感兴趣的可以去看一下这篇论文。

总结和展望

TCP暴露了太多信息在协议头,所以导致的现在的困境,这个问题已经被大家伙所认识。未来的传输协议,如Google的QUIC,就吸取了经验和教训,对协议头里面的大部分信息都进行了加密,使之无法被检测和篡改,保留了协议演进的可能性。

(完)