Linux tcp_fastopen TFO cookie 与 tcp_fastopen_req 实现TCP Fast OpenTFO通过在 SYN 中携带数据来减少 RTT 延迟。核心数据结构是 tcp_fastopen_req嵌入在 tcp_sock 中用于管理 TFO 客户端状态。tcp_fastopen_cookie 是 TFO cookie 的数据载体由服务端生成并缓存在客户端后续 SYN 中原样回传以证明客户端身份从而避免三次握手后才能发送数据。cstruct tcp_fastopen_req {struct request_sock *saved_req; /* saved for cookie mismatch */u32 snd_una; /* send queue state before first data */u16 mss; /* MSS value from SYN */u8 tfo_state; /* TFO state machine */u8 msg:1; /* MSG_FASTOPEN being used */};struct tcp_fastopen_cookie {union {u8 val[TCP_FASTOPEN_COOKIE_MAX];struct {#if defined(__LITTLE_ENDIAN_BITFIELD)u8 len:4;u8 exp:4;#elif defined(__BIG_ENDIAN_BITFIELD)u8 exp:4;u8 len:4;#endif};};u16 _unused;};客户端 TFO 分为三个状态机阶段由 tfo_state 追踪- TFO_INIT_COOKIE_REQUEST0未获取 cookie需通过 SYN MSG_FASTOPEN 向服务端请求 cookie。- TFO_REQUESTING_COOKIE1cookie 请求已发出等待 SYNACK 中的 cookie 回传。- TFO_COOKIE_ACCEPTED2cookie 已获取后续 SYN 可直接携带数据。- TFO_COOKIE_UNREQUESTED3服务端在 SYNACK 中未携带 cookie不支持或不启用客户端回退到标准三次握手。- TFO_CLIENT_NO_COOKIE4其他原因禁用不再尝试 TFO。服务端在 tcp_fastopen_cookie_gen 中生成 cookie。生成算法基于 AES-128-CMAC输入为客户端 IP 地址和当前密钥。密钥由 sysctl_tcp_fastopen_key 提供支持同时保留主密钥和备份密钥用于密钥轮换场景。服务端在 SYNACK 的 TCP Fast Open 选项kind34中携带 cookie客户端必须保存该 cookie 才能在下次连接中使用。cbool tcp_fastopen_cookie_gen(struct sock *sk,struct tcp_fastopen_cookie *foc){struct tcp_sock *tp tcp_sk(sk);struct tcp_fastopen_context *ctx;u8 key[TCP_FASTOPEN_KEY_LENGTH];bool ok false;rcu_read_lock();ctx rcu_dereference(tp-fastopen_ctx);if (ctx) {memcpy(key, ctx-key, TCP_FASTOPEN_KEY_LENGTH);ok !tcp_fastopen_cmac(key, TCP_FASTOPEN_COOKIE_MAX,sk-sk_rcv_saddr, sizeof(sk-sk_rcv_saddr),foc-val);if (ok)foc-len TCP_FASTOPEN_COOKIE_MIN;}rcu_read_unlock();return ok;}tcp_fastopen_cookie_gen 使用 CMACCipher-based MAC而非简单哈希防止攻击者猜测 cookie。每次连接需计算 CMAC 在 fast path 中引入约 2-3us 延迟但对于节省一个 RTT 的收益可忽略。ctx 是 RCU 保护的指针允许在热替换密钥时不中断正在进行的 TFO 握手。当客户端尝试在 SYN 中携带数据时tcp_sendmsg 检测到 msg-msg_flags MSG_FASTOPEN进入 tcp_try_fastopen 路径。该函数检查 tcp_fastopen_req 的 cookie 状态如果 cookie 尚未就绪将数据入队到 write_queue 但将 skb 标记为 TCPHDR_SYN 且 sacked 位为 TCP_SKB_CB(skb)-tcp_flags 中设置 TCPHDR_ACK 标志。关键限制第一次数据不能超过 ntohs(th-window) 限制因为此时未收到 SYNACK对端窗口未知。cstatic int tcp_try_fastopen(struct sock *sk, struct msghdr *msg, int len,int *copied){struct tcp_sock *tp tcp_sk(sk);struct tcp_fastopen_req *fo tp-fastopen_req;int err 0;if (fo-tfo_state TFO_COOKIE_ACCEPTED) {if (!tp-syn_data_acked) {if (len 0) {err tcp_sendmsg_fastopen(sk, msg, len, copied);if (err -EAGAIN)goto fallback;}}} else if (fo-tfo_state TFO_INIT_COOKIE_REQUEST) {err tcp_send_syn_data(sk, msg, len, copied);}return err;}服务端接收到 SYN data 后在 tcp_conn_request 中验证 TFO cookie。验证通过 tcp_fastopen_cookie_check 完成若 cookie 为空或比对失败则忽略 SYN 中的数据但 SYN 本身有效继续三次握手。注意这里与 SYN cookies 互斥如果 tcp_conn_request 中 want_cookie 为 trueSYN flood 防御SYN 中携带的 TFO data 被安全忽略且不回送 TFO cookie。cbool tcp_fastopen_cookie_check(struct sock *sk, u16 *mss,struct tcp_fastopen_cookie *foc){struct tcp_sock *tp tcp_sk(sk);bool ret false;if (tp-fastopen_ctx NULL)return false;if (foc-len 0) {ret true;goto generate;}if (foc-len TCP_FASTOPEN_COOKIE_MIN ||foc-len TCP_FASTOPEN_COOKIE_MAX) {if (tcp_fastopen_cookie_gen(sk, foc))ret (memcmp(foc-val, foc-val, foc-len) 0);}...}TFO 与 listen socket 的 accept 队列交互当 SYN data 被验证通过后服务端直接构造子 socketchild将 SYN 数据插入到 tcp_inq 和 receive buffer然后子 socket 进入 TCP_SYN_RECV 状态。在完成三次握手的最后一步收到 ACK后子 socket 转入 TCP_ESTABLISHED同时此前收到的 SYN 数据一并交付给 accept 后的进程。这里增加了数据交付的复杂度tcp_rcv_state_process 收到 ACK 后必须将 prequeue 中的 skb 重新排队到 receive buffer。cstatic struct sock *tcp_fastopen_create_child(struct sock *sk,struct sk_buff *skb,struct request_sock *req){struct tcp_sock *tp;struct sock *child;u32 end_seq;child inet_csk_clone_lock(sk, req, GFP_ATOMIC);if (!child)return NULL;tp tcp_sk(child);end_seq TCP_SKB_CB(skb)-seq 1;tcp_create_openreq_child(sk, req, skb);skb_dst_set(skb, dst_clone(sk-sk_rx_dst));skb_queue_tail(tp-ucopy.prequeue, skb);...return child;}边界条件tcp_fastopen_create_child 在 bh_lock_sock 的软中断上下文中分配 child如果内存压力下 inet_csk_clone_lock 返回 NULLSYN 数据被丢弃但 SYN 本身视为有效客户端会收到 SYNACK。在服务端 reach max backlog 时tcp_conn_request 跳过 TFO data 并回退到标准握手——客户端发现 SYNACK 未确认数据后自动重传。TFO cookie 的生命周期管理客户端在 tcp_rcv_synsent_state_process 收到 SYNACK 后调用 tcp_fastopen_cookie_parse 提取 cookie保存在 tp-fastopen_cookie 中。下一次 connect 时通过 getsockopt(TCP_FASTOPEN) 或 MSG_FASTOPEN 传递。内核不负责持久化 cookie应用层需自行保存如浏览器缓存到磁盘。若服务端密钥轮换导致 cookie 失效客户端收到 SYNACK 不带数据确认时通过 tcp_fastopen_skb_steal 回退不产生连接失败。