由前文可知,TCP的序列号(seq)与确认号(ack_seq)都是无符号32的整数。seq用于为每一字节TCP数据编号(SYN和FIN标记位也会占用一个seq);ack_seq用于告知数据发送端:“接收端已经收到了ack_seq - 1号数据,请发送ack_seq号数据”。下面我们重温一下TCP连接建立和数据传输过程,在这个过程中探究一下seq和ack_seq是如何使用的。首先来介绍一下tcp_sock中与seq和ack_seq相关的主要成员:
34 struct tcp_sock {
135 /* inet_connection_sock has to be the first member of tcp_sock */
136 struct inet_connection_sock inet_conn;
...
151 u32 rcv_nxt; /* What we want to receive next */
152 u32 copied_seq; /* Head of yet unread data */
153 u32 rcv_wup; /* rcv_nxt on last window update sent */
154 u32 snd_nxt; /* Next sequence we send */
155
156 u32 snd_una; /* First byte we want an ack for */
157 u32 snd_sml; /* Last byte of the most recently transmitted small packet */
...
182 u32 snd_wl1; /* Sequence for window update */
...
240 u32 write_seq; /* Tail(+1) of data held in tcp send buffer */
241 u32 pushed_seq; /* Last pushed seq, required to talk to windows */
...
281 u32 urg_seq; /* Seq of received urgent pointer */
...
这些成员的作用都有英文注释来说明,就不翻译了。
TCP连接的首个SYN包中的序列号被称为初始序列号(ISN),来看看ISN是如何生成的,先看发送SYN时:
142 int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
143 {
...
237 if (!tp->write_seq && likely(!tp->repair))
238 tp->write_seq = secure_tcp_sequence_number(inet->inet_saddr,
239 inet->inet_daddr,
240 inet->inet_sport,
241 usin->sin_port); //生成初始序列号
...
ISN的生成用到了源|目的IP和源|目的端口,这样就保证了“连接不同但ISN相同”的概率是很低的。来看secure_tcp_sequence_number函数:
105 __u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr,
106 __be16 sport, __be16 dport)
107 {
108 u32 hash[MD5_DIGEST_WORDS];
109
110 hash[0] = (__force u32)saddr;
111 hash[1] = (__force u32)daddr;
112 hash[2] = ((__force u16)sport << 16) + (__force u16)dport;
113 hash[3] = net_secret[15];
114
115 md5_transform(hash, net_secret);
116
117 return seq_scale(hash[0]);
118 }
其中,md5_transform函数所使用的net_secret是个全局变量,其中的数据由net_secret_init函数来随机填充:
13 static u32 net_secret[MD5_MESSAGE_BYTES / 4] ____cacheline_aligned;
14
15 void net_secret_init(void)
16 {
17 get_random_bytes(net_secret, sizeof(net_secret));
18 }
net_secret_init函数由build_ehash_secret函数调用,而socket系统调用在使用inet_create函数时会调用build_ehash_secret:
277 static int inet_create(struct net *net, struct socket *sock, int protocol,
278 int kern)
279 {
280 struct sock *sk;
281 struct inet_protosw *answer;
282 struct inet_sock *inet;
283 struct proto *answer_prot;
284 unsigned char answer_flags;
285 char answer_no_check;
286 int try_loading_module = 0;
287 int err;
288
289 if (unlikely(!inet_ehash_secret))
290 if (sock->type != SOCK_RAW && sock->type != SOCK_DGRAM)
291 build_ehash_secret();
...
这样对于任意一个TCP scoket,其ISN与另一个TCP socket相同的概率都是很低的。如果攻击者需要猜对ISN才能实施攻击的话,这个特性会大大增加攻击难度,因为攻报文的seq不合法会被丢弃。
接下来我们看ISN是如何使用的:
2925 int tcp_connect(struct sock *sk)
2926 {
2927 struct tcp_sock *tp = tcp_sk(sk);
2928 struct sk_buff *buff;
2929 int err;
2930
2931 tcp_connect_init(sk);
...
2945 tcp_init_nondata_skb(buff, tp->write_seq++, TCPHDR_SYN);
...
2947 tcp_connect_queue_skb(sk, buff); //将SYN放入队列中
...
2951 err = tp->fastopen_req ? tcp_send_syn_data(sk, buff) :
2952 tcp_transmit_skb(sk, buff, 1, sk->sk_allocation); //发送SYN
2953 if (err == -ECONNREFUSED)
2954 return err;
2955
2956 /* We change tp->snd_nxt after the tcp_transmit_skb() call
2957 * in order to make this packet get counted in tcpOutSegs.
2958 */
2959 tp->snd_nxt = tp->write_seq; //snd_nxt = ISN + 1
2960 tp->pushed_seq = tp->write_seq;
...
tcp_connect_init函数中会初始化一些与seq相关的成员:
2752 void tcp_connect_init(struct sock *sk)
2753 {
...
2803 tcp_init_wl(tp, 0); //tp->snd_wl1 = 0
2804 tp->snd_una = tp->write_seq;
2805 tp->snd_sml = tp->write_seq;
2806 tp->snd_up = tp->write_seq;
2807 tp->snd_nxt = tp->write_seq;
...
tcp_init_nondata_skb函数会将seq记录在skb中:
356 static void tcp_init_nondata_skb(struct sk_buff *skb, u32 seq, u8 flags)
357 {
...
368 TCP_SKB_CB(skb)->seq = seq;
369 if (flags & (TCPHDR_SYN | TCPHDR_FIN))
370 seq++; //SYN和FIN都会占用一个seq
371 TCP_SKB_CB(skb)->end_seq = seq;
372 }
tcp_connect_queue_skb函数不只是将skb加入到发送队列中:
2821 static void tcp_connect_queue_skb(struct sock *sk, struct sk_buff *skb)
2822 {
2823 struct tcp_sock *tp = tcp_sk(sk);
2824 struct tcp_skb_cb *tcb = TCP_SKB_CB(skb);
2825
2826 tcb->end_seq += skb->len; //如果有数据则将数据编号;end_seq实际上是下一个字节数据的seq
2827 skb_header_release(skb);
2828 __tcp_add_write_queue_tail(sk, skb); //加入到发送队列
2829 sk->sk_wmem_queued += skb->truesize; //更新队列中缓存占用总大小
2830 sk_mem_charge(sk, skb->truesize); //更新socket中已申请内存的信息
2831 tp->write_seq = tcb->end_seq; //记录已经写入发送缓存的最后一个字节的seq
2832 tp->packets_out += tcp_skb_pcount(skb); //记录TCP发送出去但未被接收的报文的数量
2833 }
tcp_transmit_skb函数在发送skb时会将seq和ack_seq写入报文:
828 static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
829 gfp_t gfp_mask)
830 {
831 const struct inet_connection_sock *icsk = inet_csk(sk);
...
898 th->seq = htonl(tcb->seq);
899 th->ack_seq = htonl(tp->rcv_nxt);
...
client发送的SYN到达server端后,server的TCP也要选择序列号作为SYN|ACK的ISN,同时需要设置ack_seq。看看这些是怎么做的:
1465 int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
1466 {
...
1523 tcp_openreq_init(req, &tmp_opt, skb);
...
1576 isn = tcp_v4_init_sequence(skb);
1577 }
1578 tcp_rsk(req)->snt_isn = isn;
...
1598 skb_synack = tcp_make_synack(sk, dst, req,
1599 fastopen_cookie_present(&valid_foc) ? &valid_foc : NULL);
...
在连接建立完成之前,server端保存连接信息的是request_sock,tcp_openreq_init函数负责对其进行初始化:
1075 static inline void tcp_openreq_init(struct request_sock *req,
1076 struct tcp_options_received *rx_opt,
1077 struct sk_buff *skb)
1078 {
1079 struct inet_request_sock *ireq = inet_rsk(req);
...
1083 tcp_rsk(req)->rcv_isn = TCP_SKB_CB(skb)->seq;
1084 tcp_rsk(req)->rcv_nxt = TCP_SKB_CB(skb)->seq + 1; //期望接收SYN的下一个字节
...
ISN由tcp_v4_init_sequence函数生成:
101 static inline __u32 tcp_v4_init_sequence(const struct sk_buff *skb)
102 {
103 return secure_tcp_sequence_number(ip_hdr(skb)->daddr,
104 ip_hdr(skb)->saddr,
105 tcp_hdr(skb)->dest,
106 tcp_hdr(skb)->source);
107 }
可见ISN的生成方法与生成SYN时是一样的。
tcp_make_synack函数生成SYN|ACK报文:
2654 struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
2655 struct request_sock *req,
2656 struct tcp_fastopen_cookie *foc)
2657 {
...
2726 tcp_init_nondata_skb(skb, tcp_rsk(req)->snt_isn,
2727 TCPHDR_SYN | TCPHDR_ACK);
2728
2729 th->seq = htonl(TCP_SKB_CB(skb)->seq);
2730 /* XXX data is queued and acked as is. No buffer/window check */
2731 th->ack_seq = htonl(tcp_rsk(req)->rcv_nxt);
...
这样ISN和ack_seq就写入到SYN|ACK报文中。
收到SYN|ACK时:
5373 static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
5374 const struct tcphdr *th, unsigned int len)
5375 {
...
5385 if (th->ack) {
...
5437 tcp_init_wl(tp, TCP_SKB_CB(skb)->seq);
5438 tcp_ack(sk, skb, FLAG_SLOWPATH);
5439
5440 /* Ok.. it's good. Set up sequence numbers and
5441 * move to established.
5442 */
5443 tp->rcv_nxt = TCP_SKB_CB(skb)->seq + 1;
5444 tp->rcv_wup = TCP_SKB_CB(skb)->seq + 1;
...
5476 tp->copied_seq = tp->rcv_nxt;
...
5438:慢速路径下tcp_ack会调用tcp_ack_update_window更新snd_una:
3325 static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
3326 {
3327 struct inet_connection_sock *icsk = inet_csk(sk);
3328 struct tcp_sock *tp = tcp_sk(sk);
3329 u32 prior_snd_una = tp->snd_una;
3330 u32 ack_seq = TCP_SKB_CB(skb)->seq;
3331 u32 ack = TCP_SKB_CB(skb)->ack_seq;
...
3392 flag |= tcp_ack_update_window(sk, skb, ack, ack_seq);
...
3218 static int tcp_ack_update_window(struct sock *sk, const struct sk_buff *skb, u32 ack,
3219 u32 ack_seq)
3220 {
...
3248 tp->snd_una = ack;
3249
3250 return flag;
3251 }
即tp->snd_una = TCP_SKB_CB(skb)->ack_seq。
发送ACK时会使用tcp_send_ack函数:
3027 void tcp_send_ack(struct sock *sk)
3028 {
...
3050 tcp_init_nondata_skb(buff, tcp_acceptable_seq(sk), TCPHDR_ACK); //将tcp_acceptable_seq(sk)的返回值作为ACK的seq
...
3054 tcp_transmit_skb(sk, buff, 0, sk_gfp_atomic(sk, GFP_ATOMIC)); //ACK的ack_seq会使用tp->rcv_nxt
3055 }
tcp_acceptable_seq函数得到一个可用的seq:
94 static inline __u32 tcp_acceptable_seq(const struct sock *sk)
95 {
96 const struct tcp_sock *tp = tcp_sk(sk);
97
98 if (!before(tcp_wnd_end(tp), tp->snd_nxt)) //snd_nxt <= 窗口右边缘
99 return tp->snd_nxt;
100 else
101 return tcp_wnd_end(tp);
102 }
发送无数据的ACK时tp->snd_nxt不需要增加,因为AK标记不占用seq。
综上可知,TCP连接双方的ISN是独立选择的,从而seq空间也是独立的,但ack_seq与对端的seq相关。也就是说,一个TCP通信端自己的seq与ack_seq是无关的。
下面来看数据的发送和接收过程中seq和ack_seq是如何使用的。首先是数据发送:
1016 int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
1017 size_t size)
1018 {
...
1133 skb_entail(sk, skb); //将skb放入发送队列
...
1192 tp->write_seq += copy; //写入了copy个字节
1193 TCP_SKB_CB(skb)->end_seq += copy; //skb增加了copy个字节长的数据
...
如果TCP选择已经在队列中但还未发送且有剩余空间的skb来写入数据,则不会调用skb_entail函数。但1192和1193行的代码会调到。
新申请skb时则会调用skb_entail函数:
596 static inline void skb_entail(struct sock *sk, struct sk_buff *skb)
597 {
598 struct tcp_sock *tp = tcp_sk(sk);
599 struct tcp_skb_cb *tcb = TCP_SKB_CB(skb);
600
601 skb->csum = 0;
602 tcb->seq = tcb->end_seq = tp->write_seq; //设置seq的值,end_seq会在copy完数据后更新
603 tcb->tcp_flags = TCPHDR_ACK; //设置ACK标记位
604 tcb->sacked = 0;
605 skb_header_release(skb);
606 tcp_add_write_queue_tail(sk, skb); //将skb加入到发送队列中
607 sk->sk_wmem_queued += skb->truesize;
608 sk_mem_charge(sk, skb->truesize);
609 if (tp->nonagle & TCP_NAGLE_PUSH)
610 tp->nonagle &= ~TCP_NAGLE_PUSH;
611 }
调用tcp_write_xmit发送数据时:
1811 static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
1812 int push_one, gfp_t gfp)
1813 {
...
1832 while ((skb = tcp_send_head(sk))) { //sk->sk_send_head指针非空,则意味着有skb尚未发送
...
1884 if (unlikely(tcp_transmit_skb(sk, skb, 1, gfp)))
1885 break;
... //发送成功
1891 tcp_event_new_data_sent(sk, skb);
...
tcp_transmit_skb发送数据时会用TCP_SKB_CB(skb)->seq作为包的seq,用tp->rcv_nxt作为ack_seq。
tcp_event_new_data_sent函数处理发送数据成功的事件:
72 static void tcp_event_new_data_sent(struct sock *sk, const struct sk_buff *skb)
73 {
74 struct inet_connection_sock *icsk = inet_csk(sk);
75 struct tcp_sock *tp = tcp_sk(sk);
76 unsigned int prior_packets = tp->packets_out;
77
78 tcp_advance_send_head(sk, skb); //sk->sk_send_head指针指向发送队列中下一个skb
79 tp->snd_nxt = TCP_SKB_CB(skb)->end_seq; //更新snd_nxt,因为skb中的数据已经发送出去
80
81 tp->packets_out += tcp_skb_pcount(skb); //有更多的包发送出去了,更新一下“在网络中”的包数
82 if (!prior_packets || icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS ||
83 icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) {
84 tcp_rearm_rto(sk); //重设重传定时器,更新RTO估计量
85 }
86 }
TCP收到数据或ACK时:
1961 int tcp_v4_rcv(struct sk_buff *skb)
1962 {
...
1994 TCP_SKB_CB(skb)->seq = ntohl(th->seq);
1995 TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin +
1996 skb->len - th->doff * 4); //IP报文段数据长度减去TCP报头长度为TCP数据长度
1997 TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq);
...
需要检查序列号是否合法:
4985 static bool tcp_validate_incoming(struct sock *sk, struct sk_buff *skb,
4986 const struct tcphdr *th, int syn_inerr)
4987 {
...
5001 /* Step 1: check sequence number */
5002 if (!tcp_sequence(tp, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq)) {
5003 /* RFC793, page 37: "In all states except SYN-SENT, all reset
5004 * (RST) segments are validated by checking their SEQ-fields."
5005 * And page 69: "If an incoming segment is not acceptable,
5006 * an acknowledgment should be sent in reply (unless the RST
5007 * bit is set, if so drop the segment and return)".
5008 */
5009 if (!th->rst) {
5010 if (th->syn)
5011 goto syn_challenge;
5012 tcp_send_dupack(sk, skb);
5013 }
5014 goto discard;
5015 }
...
一个合法数据段的seq和end_seq之间的数据必须有一部分在窗口之中:
3738 static inline bool tcp_sequence(const struct tcp_sock *tp, u32 seq, u32 end_seq)
3739 {
3740 return !before(end_seq, tp->rcv_wup) && //end_seq >= rcv_wup
3741 !after(seq, tp->rcv_nxt + tcp_receive_window(tp)); //seq <= rcv_nxt + rcv_win
3742 }
end_seq < rcv_wup意味着报文的全部数据都是旧的,seq > rcv_nxt + rcv_win则是数据全部位于窗口之外,这两种报文是必须丢弃的。
如果seq通过了tcp_sequence的检查则会进入tcp_data_queue函数;如果seq等于rcv_nxt则tcp_data_queue函数会设置tp->rcv_nxt,否则会执行更严格的检查:
4300 static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)
4301 {
...
4321 if (TCP_SKB_CB(skb)->seq == tp->rcv_nxt) { //skb的seq正好就是期望接收的seq
...
4352 tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq; //更新rcv_nxt
...
4380 if (!after(TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt)) { //end_seq <= rcv_nxt
4381 /* A retransmit, 2nd most common case. Force an immediate ack. */
4382 NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_DELAYEDACKLOST);
4383 tcp_dsack_set(sk, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq);
4384
4385 out_of_window:
4386 tcp_enter_quickack_mode(sk);
4387 inet_csk_schedule_ack(sk);
4388 drop:
4389 __kfree_skb(skb);
4390 return;
4391 }
4392
4393 /* Out of window. F.e. zero window probe. */
4394 if (!before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt + tcp_receive_window(tp))) //seq >= rcv_nxt + rcv_win
4395 goto out_of_window;
4396
4397 tcp_enter_quickack_mode(sk);
4398
4399 if (before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt)) { //seq < rcv_nxt;这时skb中有一部分数据是旧的,一部分是新的
4400 /* Partial packet, seq < rcv_next < end_seq */
4401 SOCK_DEBUG(sk, "partial packet: rcv_next %X seq %X - %X\n",
4402 tp->rcv_nxt, TCP_SKB_CB(skb)->seq,
4403 TCP_SKB_CB(skb)->end_seq);
4404
4405 tcp_dsack_set(sk, TCP_SKB_CB(skb)->seq, tp->rcv_nxt);
4406
4407 /* If window is closed, drop tail of packet. But after
4408 * remembering D-SACK for its head made in previous line.
4409 */
4410 if (!tcp_receive_window(tp))
4411 goto out_of_window;
4412 goto queue_and_out; //也会去更新rcv_nxt
4413 }
4414 //seq > tp->rcv_nxt,且报文没有在窗口之外,则是乱序数据
4415 tcp_data_queue_ofo(sk, skb);
4416 }
应用进程在使用tcp_recvmsg函数读取数据时会利用tp->copied_seq记录下一次要copy的数据的seq:
1545 int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
1546 size_t len, int nonblock, int flags, int *addr_len)
1547 {
1548 struct tcp_sock *tp = tcp_sk(sk);
1549 int copied = 0;
1550 u32 peek_seq;
1551 u32 *seq;
...
1588 seq = &tp->copied_seq;
...
1781 if (tp->rcv_nxt == tp->copied_seq && //没有待copy的数据了
1782 !skb_queue_empty(&tp->ucopy.prequeue)) {
1783 do_prequeue:
1784 tcp_prequeue_process(sk);
...
1855 {
1856 err = skb_copy_datagram_iovec(skb, offset,
1857 msg->msg_iov, used);
1858 if (err) {
1859 /* Exception. Bailout! */
1860 if (!copied)
1861 copied = -EFAULT;
1862 break;
1863 }
1864 }
1865 }
1866
1867 *seq += used; //已经copy完了used字节
1868 copied += used;
...
1889 found_fin_ok:
1890 /* Process the FIN. */
1891 ++*seq; //读取FIN时copied_seq也要加1
...
tcp_ack函数会对报文的ack_seq进行检查:
3325 static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
3326 {
3327 struct inet_connection_sock *icsk = inet_csk(sk);
3328 struct tcp_sock *tp = tcp_sk(sk);
3329 u32 prior_snd_una = tp->snd_una;
3330 u32 ack_seq = TCP_SKB_CB(skb)->seq;
3331 u32 ack = TCP_SKB_CB(skb)->ack_seq;
...
3343 if (before(ack, prior_snd_una)) { //ack_seq < tp->snd_una
3344 /* RFC 5961 5.2 [Blind Data Injection Attack].[Mitigation] */
3345 if (before(ack, prior_snd_una - tp->max_window)) {
3346 tcp_send_challenge_ack(sk);
3347 return -1;
3348 }
3349 goto old_ack;
3350 }
3351
3352 /* If the ack includes data we haven't sent yet, discard
3353 * this segment (RFC793 Section 3.9).
3354 */
3355 if (after(ack, tp->snd_nxt)) //ack_seq > tp->snd_nxt
3356 goto invalid_ack;
...
3380 tp->snd_una = ack;
...
一个合法的ack_seq必须满足:snd_una < ack_seq <= snd_nxt。ack_seq <= snd_una则是重复的ACK。ack_seq < tp->snd_una则ack_seq太旧了;ack_seq > tp->snd_nxt则是ack_seq确认了尚未发送的数据,也是非法的。如果ack_seq合法则tcp_ack会设置tp->snd_una = ack_seq。
TCP使用before和after来比较seq和ack_seq的大小:
300 static inline bool before(__u32 seq1, __u32 seq2)
301 {
302 return (__s32)(seq1-seq2) < 0;
303 }
304 #define after(seq2, seq1) before(seq1, seq2)
before为真则可以认为seq1 < seq2,after为真则可以认为seq2 > seq1。由于seq是无符号32位整数,seq大于2^32 - 1会出现回绕,所以如果seq1 < seq2 < (seq1 + 2^31) mod 2^32 - 1,则before(seq1, seq2)为真;否则如果seq2 < seq1 < (seq2 + 2^31) mod 2^32 - 1,则before(seq2, seq1)为真.也就是说,32bit的序列号空间只有一半(2^31)的序列号可用。当数据发送很快时,序列号会迅速出现回绕,这时靠before和after这种单纯比较序列号来判断报文新旧的方法容易出现错误,这个问题需要靠时间戳来解决。
判断报文段中的数据是否合法还需要知道接收窗口的大小。关于窗口下节详细分析。