TCP握手期间可以传输数据吗?我不是说第三次握手的ACK是否能携带数据,显而易见是可以的,也是简单的。我是在问,client发往server的第一个SYN报文,以及server回复client的第二个SYN/ACK报文,能携带数据吗?
可以!不过声明,本文的内容与TCP Fast Open无关,说的是原始TCP。亦与安全无关,安全的归安全。
依据如经典RFC793 3.4节所示:
此外,《TCP/IP详解(第二版) 卷1》13.2节有如下Note:
诸如“TCP为什么要3次握手,2次可以吗?”,“TCP握手的必要性”等,亦参考RFC793 3.4节,两三页胜过大书特书。比如本文不会解释3次握手是为了初始化双向的序列号这种众所周知的事。
没人在SYN报文中携带数据的原因是sockets API不支持这么做,进而TCP协议的实现也就没了支持的必要,以下是Linux TCP处理SYN报文的实现片段:
static void tcp_openreq_init(struct request_sock *req,
const struct tcp_options_received *rx_opt,
struct sk_buff *skb, const struct sock *sk)
{
struct inet_request_sock *ireq = inet_rsk(req);
...
tcp_rsk(req)->rcv_isn = TCP_SKB_CB(skb)->seq;
tcp_rsk(req)->rcv_nxt = TCP_SKB_CB(skb)->seq + 1;
...
Linux TCP server处理SYN报文时忽略了携带的数据。但如RFC 793所述,SYN报文是可以“捎带”数据的,为什么不呢?怎么想都是可以的。
也许MSS尚未协商,也许rwin尚未知晓,但依TCP语义,receiver可尽力接收,只需回复正确的ACK指示sender的UNA。
唯一问题在于内存开销。
若syncookie未启用,TCP在握手尚未完结前,需缓存任何收到的数据,待连接ESTABLISH之后方可push至应用层,期间,过多sender假冒过量连接,将很容易使receiver内存溢出,表现攻击特征。
但安全的归安全,TCP的归TCP。
至少可做配置开关,在额外机制保证安全的情形下支持SYN携带数据。2022年,安全早就从TCP协议分离了,指望TCP协议本身保证安全,属实不现实。
若死活不支持SYN携带数据,便减少了很多可能:
起初是sockets API,导致各TCP实现均不支持SYN携带数据,这反使人们偏见于HTTP,TCP握手带来的额外RTT开销让HTTP协议饱受诟病!最终QUIC宣称的0RTT反而成了一个广而告之的新鲜特征。
握手开销无关HTTP,亦无关TCP协议,而是原生于sockets API约束的TCP实现问题。握手开销竟成TCP污点。
基因决定了死亡方式。TCP有很多致命缺陷,滑动窗口,自时钟都是,但握手开销不是。
关于TCP SYN报文携带Payload的有趣玩法,参见前年写的一篇短文:
https://blog.csdn.net/dog250/article/details/108540823
浙江温州皮鞋湿,下雨进水不会胖。