在Linux中,有两种方法来处理传入TCP数据段:快速路径(Fast Path)和慢速路径(Slow Path)。使用快速路径只进行最少的处理,如处理数据段、发生ACK、存储时间戳等。使用慢速路径可以处理乱序数据段、PAWS、socket内存管理和紧急数据等。Linux通过预测标志来区分这两种处理模式,预测标志存储在tp->pred_flags,生成这个标志的函数是__tcp_fast_path_on和tcp_fast_path_on,TCP会直接使用这两个函数来生成预测标记,也可以调用tcp_fast_path_check来完成这一任务:
- 613 static inline void __tcp_fast_path_on(struct tcp_sock *tp, u32 snd_wnd)
- 614 {
- 615 tp->pred_flags = htonl((tp->tcp_header_len << 26) |
- 616 ntohl(TCP_FLAG_ACK) |
- 617 snd_wnd);
- 618 }
- 619
- 620 static inline void tcp_fast_path_on(struct tcp_sock *tp)
- 621 {
- 622 __tcp_fast_path_on(tp, tp->snd_wnd >> tp->rx_opt.snd_wscale);
- 623 }
- 624
- 625 static inline void tcp_fast_path_check(struct sock *sk)
- 626 {
- 627 struct tcp_sock *tp = tcp_sk(sk);
- 628
- 629 if (skb_queue_empty(&tp->out_of_order_queue) &&
- 630 tp->rcv_wnd &&
- 631 atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf &&
- 632 !tp->urg_data)
- 633 tcp_fast_path_on(tp);
- 634 }
613-617:__tcp_fast_path_on函数做的工作实际上是在构建TCP首部的第4个字节,即首部长度、标记位、窗口(格式见1.2节)。其中tp->tcp_header_len是首部长度的字节数,TCP首部中记录首部长度的数值位于第4个字节的高4bit,即第28-31bit,而且这个数值乘以4才是首部长度的字节数。故tp->tcp_header_len需要左移28位,再右移2位(除以4),即左移26位。
620-622:tcp_fast_path_on封装了__tcp_fast_path_on函数,它在设置预测标记时会考虑窗口扩大因子的影响
625-633:tcp_fast_path_check会先检查条件是否满足,如果满足再设置预测标记。条件是:
(1)没有乱序数据(629)
(2)接收窗口不为0(630)
(3)接收缓存未耗尽(631)
(4)没有紧急数据(632)
TCP直接调用__tcp_fast_path_on的时机是connect系统调用即将结束时:
- 5291 void tcp_finish_connect(struct sock *sk, struct sk_buff *skb)
- 5292 {
- ...
- 5320 if (!tp->rx_opt.snd_wscale)
- 5321 __tcp_fast_path_on(tp, tp->snd_wnd);
- 5322 else
- 5323 tp->pred_flags = 0;
- ...
TCP直接调用tcp_fast_path_on的时机是收到三次握手中的ACK报文时:
- 5600 int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
- 5601 const struct tcphdr *th, unsigned int len)
- 5602 {
- ...
- 5678 int acceptable = tcp_ack(sk, skb, FLAG_SLOWPATH |
- 5679 FLAG_UPDATE_TS_RECENT) > 0;
- 5680
- 5681 switch (sk->sk_state) {
- 5682 case TCP_SYN_RECV:
- 5683 if (acceptable) {
- ...
- 5745 tcp_fast_path_on(tp);
- 5746 } else {
- 5747 return 1;
- 5748 }
- 5749 break;
- ...
TCP直接调用tcp_fast_path_check的时机有3处:
(1)当读过紧急数据时;紧急数据是由慢速路径处理,需要保持在慢速路径模式直到收完紧急数据,然后就可以开启快速路径模式了。
- 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 {
- ...
- 1874 if (tp->urg_data && after(tp->copied_seq, tp->urg_seq)) {
- 1875 tp->urg_data = 0;
- 1876 tcp_fast_path_check(sk);
- 1877 }
- ...
(2)当发生方收到ACK并调用tcp_ack_update_window更新窗口时;通告窗口发生了变化,则必须更新预测标记,以免后续的输入报文因为窗口不符而进入慢速路径。
- 3218 static int tcp_ack_update_window(struct sock *sk, const struct sk_buff *skb, u32 ack,
- 3219 u32 ack_seq)
- 3220 {
- 3221 struct tcp_sock *tp = tcp_sk(sk);
- 3222 int flag = 0;
- 3223 u32 nwin = ntohs(tcp_hdr(skb)->window);
- 3224
- 3225 if (likely(!tcp_hdr(skb)->syn))
- 3226 nwin <<= tp->rx_opt.snd_wscale;
- 3227
- 3228 if (tcp_may_update_window(tp, ack, ack_seq, nwin)) {
- 3229 flag |= FLAG_WIN_UPDATE;
- 3230 tcp_update_wl(tp, ack_seq);
- 3231
- 3232 if (tp->snd_wnd != nwin) {
- 3233 tp->snd_wnd = nwin;
- 3234
- 3235
-
-
- 3238 tp->pred_flags = 0;
- 3239 tcp_fast_path_check(sk);
- ...
(3)当tcp_data_queue将数据放入接收队列时;这时可用的接收缓存大小发生变化,tcp_fast_path_check会检查这个缓存的变化是否允许开启快速路径模式。
- 4300 static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)
- 4301 {
- ...
- 4321 if (TCP_SKB_CB(skb)->seq == tp->rcv_nxt) {
- 4322 if (tcp_receive_window(tp) == 0)
- 4323 goto out_of_window;
- 4324
- ...
- 4371 tcp_fast_path_check(sk);
- ...
设置了预测标记后,使用它是在处理TCP数据段的唯一入口函数——
tcp_rcv_established:
- 5076 int tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
- 5077 const struct tcphdr *th, unsigned int len)
- 5078 {
- 5079 struct tcp_sock *tp = tcp_sk(sk);
- ...
- 5109 if ((tcp_flag_word(th) & TCP_HP_BITS) == tp->pred_flags &&
- 5110 TCP_SKB_CB(skb)->seq == tp->rcv_nxt &&
- 5111 !after(TCP_SKB_CB(skb)->ack_seq, tp->snd_nxt)) {
- 5112 int tcp_header_len = tp->tcp_header_len;
- 5113
- 5114
-
-
-
- 5118
- 5119
- 5120 if (tcp_header_len == sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED) {
- 5121
- 5122 if (!tcp_parse_aligned_timestamp(tp, th))
- 5123 goto slow_path;
- 5124
- 5125
- 5126 if ((s32)(tp->rx_opt.rcv_tsval - tp->rx_opt.ts_recent) < 0)
- 5127 goto slow_path;
- 5128
- 5129
-
-
-
-
- 5134 }
- 5135
- 5136 if (len <= tcp_header_len) {
- 5137
- 5138 if (len == tcp_header_len) {
- 5139
-
-
-
- 5143 if (tcp_header_len ==
- 5144 (sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED) &&
- 5145 tp->rcv_nxt == tp->rcv_wup)
- 5146 tcp_store_ts_recent(tp);
- 5147
- 5148
-
-
- 5151 tcp_ack(sk, skb, 0);
- 5152 __kfree_skb(skb);
- 5153 tcp_data_snd_check(sk);
- 5154 return 0;
- 5155 } else {
- 5156 TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_INERRS);
- 5157 goto discard;
- 5158 }
- 5159 } else {
- 5160 int eaten = 0;
- 5161 int copied_early = 0;
- 5162 bool fragstolen = false;
- 5163
- 5164 if (tp->copied_seq == tp->rcv_nxt &&
- 5165 len - tcp_header_len <= tp->ucopy.len) {
- 5166 #ifdef CONFIG_NET_DMA
- 5167 if (tp->ucopy.task == current &&
- 5168 sock_owned_by_user(sk) &&
- 5169 tcp_dma_try_early_copy(sk, skb, tcp_header_len)) {
- 5170 copied_early = 1;
- 5171 eaten = 1;
- 5172 }
- 5173 #endif
- 5174 if (tp->ucopy.task == current &&
- 5175 sock_owned_by_user(sk) && !copied_early) {
- 5176 __set_current_state(TASK_RUNNING);
- 5177
- 5178 if (!tcp_copy_to_iovec(sk, skb, tcp_header_len))
- 5179 eaten = 1;
- 5180 }
- 5181 if (eaten) {
- 5182
-
-
-
- 5186 if (tcp_header_len ==
- 5187 (sizeof(struct tcphdr) +
- 5188 TCPOLEN_TSTAMP_ALIGNED) &&
- 5189 tp->rcv_nxt == tp->rcv_wup)
- 5190 tcp_store_ts_recent(tp);
- 5191
- 5192 tcp_rcv_rtt_measure_ts(sk, skb);
- 5193
- 5194 __skb_pull(skb, tcp_header_len);
- 5195 tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
- 5196 NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPHPHITSTOUSER);
- 5197 }
- 5198 if (copied_early)
- 5199 tcp_cleanup_rbuf(sk, skb->len);
- 5200 }
- 5201 if (!eaten) {
- 5202 if (tcp_checksum_complete_user(sk, skb))
- 5203 goto csum_error;
- 5204
- 5205 if ((int)skb->truesize > sk->sk_forward_alloc)
- 5206 goto step5;
- 5207
- 5208
-
-
-
- 5212 if (tcp_header_len ==
- 5213 (sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED) &&
- 5214 tp->rcv_nxt == tp->rcv_wup)
- 5215 tcp_store_ts_recent(tp);
- 5216
- 5217 tcp_rcv_rtt_measure_ts(sk, skb);
- 5218
- 5219 NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPHPHITS);
- 5220
- 5221
- 5222 eaten = tcp_queue_rcv(sk, skb, tcp_header_len,
- 5223 &fragstolen);
- 5224 }
- 5225
- 5226 tcp_event_data_recv(sk, skb);
- 5227
- 5228 if (TCP_SKB_CB(skb)->ack_seq != tp->snd_una) {
- 5229
- 5230 tcp_ack(sk, skb, FLAG_DATA);
- 5231 tcp_data_snd_check(sk);
- 5232 if (!inet_csk_ack_scheduled(sk))
- 5233 goto no_ack;
- 5234 }
- 5235
- 5236 if (!copied_early || tp->rcv_nxt != tp->rcv_wup)
- 5237 __tcp_ack_snd_check(sk, 0);
- 5238 no_ack:
- 5239 #ifdef CONFIG_NET_DMA
- 5240 if (copied_early)
- 5241 __skb_queue_tail(&sk->sk_async_wait_queue, skb);
- 5242 else
- 5243 #endif
- 5244 if (eaten)
- 5245 kfree_skb_partial(skb, fragstolen);
- 5246 sk->sk_data_ready(sk, 0);
- 5247 return 0;
- 5248 }
- 5249 }
- 5250
- 5251 slow_path:
- ...
5109:这样是检查输入的TCP报文是否与预测标志匹配:
- 64 union tcp_word_hdr {
- 65 struct tcphdr hdr;
- 66 __be32 words[5];
- 67 };
- 68
- 69 #define tcp_flag_word(tp) ( ((union tcp_word_hdr *)(tp))->words [3])
- 70
- 71 enum {
- 72 TCP_FLAG_CWR = __constant_cpu_to_be32(0x00800000),
- 73 TCP_FLAG_ECE = __constant_cpu_to_be32(0x00400000),
- 74 TCP_FLAG_URG = __constant_cpu_to_be32(0x00200000),
- 75 TCP_FLAG_ACK = __constant_cpu_to_be32(0x00100000),
- 76 TCP_FLAG_PSH = __constant_cpu_to_be32(0x00080000),
- 77 TCP_FLAG_RST = __constant_cpu_to_be32(0x00040000),
- 78 TCP_FLAG_SYN = __constant_cpu_to_be32(0x00020000),
- 79 TCP_FLAG_FIN = __constant_cpu_to_be32(0x00010000),
- 80 TCP_RESERVED_BITS = __constant_cpu_to_be32(0x0F000000),
- 81 TCP_DATA_OFFSET = __constant_cpu_to_be32(0xF0000000)
- 82 };
而TCP_HP_BITS的定义为:
- 122 #define TCP_HP_BITS (~(TCP_RESERVED_BITS|TCP_FLAG_PSH))
可以看出,5109行的匹配方法是将TCP首部的第4个字节去掉TCP_FLAG_PSH标记后再与预测标记对比,只有首部长度和窗口大小与预测标记一致且标记位仅有TCP_FLAG_ACK的包才能通过。除了预测标记匹配通过外,当前包序列号和确认号还需要满足5110和5111行的要求才能进入快速处理路径。
5120-5127:如果报文中携带时间戳选项,则需要解析时间戳然后检查是否有序列号回绕(PAWS)的问题。如果时间戳选项解析失败或PAWS检查失败,则需要进入慢速路径仔细检查
5136-5157是对没有数据部分的报文的处理。
5143-5144:TCP首部中有时间戳选项
5145:这个条件满足意味着当前包是对最新一次发送的数据包的回应
5146:这时需要更新时间戳信息
5151-5152:调用tcp_ack处理完ACK标记后,这个没有数据的包就已经完成了使命,可以释放了
5153:收到ACK后可能确认了一部分旧的数据,也可能更新了通告窗口,这时需要调用tcp_data_snd_check函数试着发送一下发送队列中的数据
至此,快速处理路径中对无数据的包的处理完毕,下面是对有数据的包的处理。
5164-5199:如果进程使用prequeue收包,则需要试图将数据直接copy到用户缓存;如果copy成功,则需要更新时间戳、设置tp->rcv_nxt等
5201-5223:如果没有数据被直接copy到用户空间,则在进行了检验和校验、接收空间检查、时间戳保存等工作后,调用tcp_queue_rcv函数将skb放入接收队列中:
- 4244 static int __must_check tcp_queue_rcv(struct sock *sk, struct sk_buff *skb, int hdrlen,
- 4245 bool *fragstolen)
- 4246 {
- 4247 int eaten;
- 4248 struct sk_buff *tail = skb_peek_tail(&sk->sk_receive_queue);
- 4249
- 4250 __skb_pull(skb, hdrlen);
- 4251 eaten = (tail &&
- 4252 tcp_try_coalesce(sk, tail, skb, fragstolen)) ? 1 : 0;
- 4253 tcp_sk(sk)->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
- 4254 if (!eaten) {
- 4255 __skb_queue_tail(&sk->sk_receive_queue, skb);
- 4256 skb_set_owner_r(skb, sk);
- 4257 }
- 4258 return eaten;
- 4259 }
可见无论skb是否整合成功,tcp_queue_rcv函数最终会将skb的数据部分放入到sk->sk_receive_queue中。
处理完skb的数据部分后,
tcp_rcv_established函数会调用tcp_event_data_recv函数更新拥塞控制信息(包括拥塞窗口),然后处理ACK标记位:
5228:满足这个判断条件意味着要么ACK会确认新的数据(TCP_SKB_CB(skb)->ack_seq > tp->snd_una),要么是一个旧的ACK(TCP_SKB_CB(skb)->ack_seq < tp->snd_una),这两种情况都需要调用tcp_ack进行处理,然后再试图发送一下发送队列中的数据。
5236-5237:如果没有数据通过DMA直接copy给进程,或接收了新的数据,则发送ACK(立即发送或使用延迟ACK机制)。换个角度考虑,不发送ACK的条件是:有数据通过DMA直接copy给进程,且没有接收新的数据。也就是说,skb中的数据长度为0。但这样的话skb是走不到5159这个分支中的啊。我实在想不出使这个条件为假的情况。
5240-5240:如果有数据通过DMA传输,则将skb放入sk_async_wait_queue中,以防DMA传输失败。
5246:最后,调用sk->sk_data_ready指向的函数通知进程有数据可以接收。
至此,快速路径处理完成,不满足快速处理条件的skb会被送入慢速处理路径。在那里,skb会接受更多更严格的检查与处理。
TCP报文段进入慢速路径处理的条件有:
(1)收到乱序数据或乱序队列非空
(2)收到紧急指针
(3)接收缓存耗尽
(4)收到0窗口通告
(5)收到的报文中含有除了PUSH和ACK之外的标记,如SYN、FIN、RST
(6)报文的时间戳选项解析失败
(7)报文的PAWS检查失败
慢速路径处理的代码如下:
- 5076 int tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
- 5077 const struct tcphdr *th, unsigned int len)
- 5078 {
- 5079 struct tcp_sock *tp = tcp_sk(sk);
- ...
- 5251 slow_path:
- 5252 if (len < (th->doff << 2) || tcp_checksum_complete_user(sk, skb))
- 5253 goto csum_error;
- 5254
- 5255 if (!th->ack && !th->rst)
- 5256 goto discard;
- 5257
- 5258
-
-
- 5261
- 5262 if (!tcp_validate_incoming(sk, skb, th, 1))
- 5263 return 0;
- 5264
- 5265 step5:
- 5266 if (tcp_ack(sk, skb, FLAG_SLOWPATH | FLAG_UPDATE_TS_RECENT) < 0)
- 5267 goto discard;
- 5268
- 5269 tcp_rcv_rtt_measure_ts(sk, skb);
- 5270
- 5271
- 5272 tcp_urg(sk, skb, th);
- 5273
- 5274
- 5275 tcp_data_queue(sk, skb);
- 5276
- 5277 tcp_data_snd_check(sk);
- 5278 tcp_ack_snd_check(sk);
- 5279 return 0;
- ...
tcp_validate_incoming函数用于检查时间戳、序列号等字段:
- 4985 static bool tcp_validate_incoming(struct sock *sk, struct sk_buff *skb,
- 4986 const struct tcphdr *th, int syn_inerr)
- 4987 {
- 4988 struct tcp_sock *tp = tcp_sk(sk);
- 4989
- 4990
- 4991 if (tcp_fast_parse_options(skb, th, tp) && tp->rx_opt.saw_tstamp &&
- 4992 tcp_paws_discard(sk, skb)) {
- 4993 if (!th->rst) {
- 4994 NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSESTABREJECTED);
- 4995 tcp_send_dupack(sk, skb);
- 4996 goto discard;
- 4997 }
- 4998
- 4999 }
- 5000
- 5001
- 5002 if (!tcp_sequence(tp, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq)) {
- 5003
-
-
-
-
-
- 5009 if (!th->rst) {
- 5010 if (th->syn)
- 5011 goto syn_challenge;
- 5012 tcp_send_dupack(sk, skb);
- 5013 }
- 5014 goto discard;
- 5015 }
- 5016
- 5017
- 5018 if (th->rst) {
- 5019
-
-
-
-
-
- 5025 if (TCP_SKB_CB(skb)->seq == tp->rcv_nxt)
- 5026 tcp_reset(sk);
- 5027 else
- 5028 tcp_send_challenge_ack(sk);
- 5029 goto discard;
- 5030 }
- 5031
- 5032
- 5033
- 5034
-
-
- 5037 if (th->syn) {
- 5038 syn_challenge:
- 5039 if (syn_inerr)
- 5040 TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_INERRS);
- 5041 NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPSYNCHALLENGE);
- 5042 tcp_send_challenge_ack(sk);
- 5043 goto discard;
- 5044 }
- 5045
- 5046 return true;
- 5047
- 5048 discard:
- 5049 __kfree_skb(skb);
- 5050 return false;
- 5051 }
4991-4996:如果有时间戳选项的话检查PAWS,不通过则认为是旧包,丢弃之,并立即发送ACK;不过RST包即使PAWS检查不过也是可以接受的,并不丢弃。
5002-5014:tcp_sequence函数用来确保由数据的序列号落入接收窗口之内,否则丢弃之;但如果SYN包的序列号非法则需要发送ACK挑战(原因见RFC5961)。
5018-5046:按照RFC5961的要求处理SYN报文和RST报文,以防止Blind In-Window Attack。
Blind In-Window Attack简介:这个攻击的基本原理是攻击者通过发送伪造的RST包或SYN包导致TCP通信两端中的一个认为包非法而发送RST,从而异常结束连接。而这个攻击能够奏效的前提是所伪造的包的序列号必须在窗口范围内,要保证这一点攻击者必须发送许多攻击包进行猜测,因此得名。防御这种攻击的基本方法是,对于RST包,仔细检查其序列号,只有其序列号真正等于tp->rcv_nxt时才发送RST(攻击者能够伪造出符合这一特征的RST包的概率是很低的);而对于建立状态下SYN包,只是发送ACK,不发RST。
慢速处理路径中ACK的处理、拥塞控制信息的更新以及紧急指针在后续章节中详细讨论。
数据接收处理在tcp_data_queue函数中进行:
- 4300 static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)
- 4301 {
- 4302 const struct tcphdr *th = tcp_hdr(skb);
- 4303 struct tcp_sock *tp = tcp_sk(sk);
- 4304 int eaten = -1;
- 4305 bool fragstolen = false;
- 4306
- 4307 if (TCP_SKB_CB(skb)->seq == TCP_SKB_CB(skb)->end_seq)
- 4308 goto drop;
- 4309
- 4310 skb_dst_drop(skb);
- 4311 __skb_pull(skb, th->doff * 4);
- 4312
- 4313 TCP_ECN_accept_cwr(tp, skb);
- 4314
- 4315 tp->rx_opt.dsack = 0;
- 4316
- 4317
-
-
-
- 4321 if (TCP_SKB_CB(skb)->seq == tp->rcv_nxt) {
- 4322 if (tcp_receive_window(tp) == 0)
- 4323 goto out_of_window;
- 4324
- 4325
- 4326 if (tp->ucopy.task == current &&
- 4327 tp->copied_seq == tp->rcv_nxt && tp->ucopy.len &&
- 4328 sock_owned_by_user(sk) && !tp->urg_data) {
- 4329 int chunk = min_t(unsigned int, skb->len,
- 4330 tp->ucopy.len);
- 4331
- 4332 __set_current_state(TASK_RUNNING);
- 4333
- 4334 local_bh_enable();
- 4335 if (!skb_copy_datagram_iovec(skb, 0, tp->ucopy.iov, chunk)) {
- 4336 tp->ucopy.len -= chunk;
- 4337 tp->copied_seq += chunk;
- 4338 eaten = (chunk == skb->len);
- 4339 tcp_rcv_space_adjust(sk);
- 4340 }
- 4341 local_bh_disable();
- 4342 }
- 4343
- 4344 if (eaten <= 0) {
- 4345 queue_and_out:
- 4346 if (eaten < 0 &&
- 4347 tcp_try_rmem_schedule(sk, skb, skb->truesize))
- 4348 goto drop;
- 4349
- 4350 eaten = tcp_queue_rcv(sk, skb, 0, &fragstolen);
- 4351 }
- 4352 tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
- 4353 if (skb->len)
- 4354 tcp_event_data_recv(sk, skb);
- 4355 if (th->fin)
- 4356 tcp_fin(sk);
- 4357
- 4358 if (!skb_queue_empty(&tp->out_of_order_queue)) {
- 4359 tcp_ofo_queue(sk);
- 4360
- 4361
-
-
- 4364 if (skb_queue_empty(&tp->out_of_order_queue))
- 4365 inet_csk(sk)->icsk_ack.pingpong = 0;
- 4366 }
- 4367
- 4368 if (tp->rx_opt.num_sacks)
- 4369 tcp_sack_remove(tp);
- 4370
- 4371 tcp_fast_path_check(sk);
- 4372
- 4373 if (eaten > 0)
- 4374 kfree_skb_partial(skb, fragstolen);
- 4375 if (!sock_flag(sk, SOCK_DEAD))
- 4376 sk->sk_data_ready(sk, 0);
- 4377 return;
- 4378 }
- 4379
- 4380 if (!after(TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt)) {
- 4381
- 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
- 4394 if (!before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt + tcp_receive_window(tp)))
- 4395 goto out_of_window;
- 4396
- 4397 tcp_enter_quickack_mode(sk);
- 4398
- 4399 if (before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt)) {
- 4400
- 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
-
-
- 4410 if (!tcp_receive_window(tp))
- 4411 goto out_of_window;
- 4412 goto queue_and_out;
- 4413 }
- 4414
- 4415 tcp_data_queue_ofo(sk, skb);
- 4416 }
乱序数据的重组由
tcp_ofo_queue函数完成:
- 4023 static void tcp_ofo_queue(struct sock *sk)
- 4024 {
- 4025 struct tcp_sock *tp = tcp_sk(sk);
- 4026 __u32 dsack_high = tp->rcv_nxt;
- 4027 struct sk_buff *skb;
- 4028
- 4029 while ((skb = skb_peek(&tp->out_of_order_queue)) != NULL) {
- 4030 if (after(TCP_SKB_CB(skb)->seq, tp->rcv_nxt))
- 4031 break;
- 4032
- 4033 if (before(TCP_SKB_CB(skb)->seq, dsack_high)) {
- 4034 __u32 dsack = dsack_high;
- 4035 if (before(TCP_SKB_CB(skb)->end_seq, dsack_high))
- 4036 dsack_high = TCP_SKB_CB(skb)->end_seq;
- 4037 tcp_dsack_extend(sk, TCP_SKB_CB(skb)->seq, dsack);
- 4038 }
- 4039
- 4040 if (!after(TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt)) {
- 4041 SOCK_DEBUG(sk, "ofo packet was already received\n");
- 4042 __skb_unlink(skb, &tp->out_of_order_queue);
- 4043 __kfree_skb(skb);
- 4044 continue;
- 4045 }
- 4046 SOCK_DEBUG(sk, "ofo requeuing : rcv_next %X seq %X - %X\n",
- 4047 tp->rcv_nxt, TCP_SKB_CB(skb)->seq,
- 4048 TCP_SKB_CB(skb)->end_seq);
- 4049
- 4050 __skb_unlink(skb, &tp->out_of_order_queue);
- 4051 __skb_queue_tail(&sk->sk_receive_queue, skb);
- 4052 tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
- 4053 if (tcp_hdr(skb)->fin)
- 4054 tcp_fin(sk);
- 4055 }
- 4056 }
tcp_data_queue_ofo负责将乱序包按照seq顺序放入乱序队列中,并设置SACK选项信息:
- 4121 static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb)
- 4122 {
- 4123 struct tcp_sock *tp = tcp_sk(sk);
- 4124 struct sk_buff *skb1;
- 4125 u32 seq, end_seq;
- 4126
- 4127 TCP_ECN_check_ce(tp, skb);
- 4128
- 4129 if (unlikely(tcp_try_rmem_schedule(sk, skb, skb->truesize))) {
- 4130 NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPOFODROP);
- 4131 __kfree_skb(skb);
- 4132 return;
- 4133 }
- 4134
- 4135
- 4136 tp->pred_flags = 0;
- 4137 inet_csk_schedule_ack(sk);
- 4138
- 4139 NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPOFOQUEUE);
- 4140 SOCK_DEBUG(sk, "out of order segment: rcv_next %X seq %X - %X\n",
- 4141 tp->rcv_nxt, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq);
- 4142
- 4143 skb1 = skb_peek_tail(&tp->out_of_order_queue);
- 4144 if (!skb1) {
- 4145
- 4146 if (tcp_is_sack(tp)) {
- 4147 tp->rx_opt.num_sacks = 1;
- 4148 tp->selective_acks[0].start_seq = TCP_SKB_CB(skb)->seq;
- 4149 tp->selective_acks[0].end_seq =
- 4150 TCP_SKB_CB(skb)->end_seq;
- 4151 }
- 4152 __skb_queue_head(&tp->out_of_order_queue, skb);
- 4153 goto end;
- 4154 }
- 4155
- 4156 seq = TCP_SKB_CB(skb)->seq;
- 4157 end_seq = TCP_SKB_CB(skb)->end_seq;
- 4158
- 4159 if (seq == TCP_SKB_CB(skb1)->end_seq) {
- 4160 bool fragstolen;
- 4161
- 4162 if (!tcp_try_coalesce(sk, skb1, skb, &fragstolen)) {
- 4163 __skb_queue_after(&tp->out_of_order_queue, skb1, skb);
- 4164 } else {
- 4165 kfree_skb_partial(skb, fragstolen);
- 4166 skb = NULL;
- 4167 }
- 4168
- 4169 if (!tp->rx_opt.num_sacks ||
- 4170 tp->selective_acks[0].end_seq != seq)
- 4171 goto add_sack;
- 4172
- 4173
- 4174 tp->selective_acks[0].end_seq = end_seq;
- 4175 goto end;
- 4176 }
- 4177
- 4178
- 4179 while (1) {
- 4180 if (!after(TCP_SKB_CB(skb1)->seq, seq))
- 4181 break;
- 4182 if (skb_queue_is_first(&tp->out_of_order_queue, skb1)) {
- 4183 skb1 = NULL;
- 4184 break;
- 4185 }
- 4186 skb1 = skb_queue_prev(&tp->out_of_order_queue, skb1);
- 4187 }
- 4188
- 4189
- 4190 if (skb1 && before(seq, TCP_SKB_CB(skb1)->end_seq)) {
- 4191 if (!after(end_seq, TCP_SKB_CB(skb1)->end_seq)) {
- 4192
- 4193 NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPOFOMERGE);
- 4194 __kfree_skb(skb);
- 4195 skb = NULL;
- 4196 tcp_dsack_set(sk, seq, end_seq);
- 4197 goto add_sack;
- 4198 }
- 4199 if (after(seq, TCP_SKB_CB(skb1)->seq)) {
- 4200
- 4201 tcp_dsack_set(sk, seq,
- 4202 TCP_SKB_CB(skb1)->end_seq);
- 4203 } else {
- 4204 if (skb_queue_is_first(&tp->out_of_order_queue,
- 4205 skb1))
- 4206 skb1 = NULL;
- 4207 else
- 4208 skb1 = skb_queue_prev(
- 4209 &tp->out_of_order_queue,
- 4210 skb1);
- 4211 }
- 4212 }
- 4213 if (!skb1)
- 4214 __skb_queue_head(&tp->out_of_order_queue, skb);
- 4215 else
- 4216 __skb_queue_after(&tp->out_of_order_queue, skb1, skb);
- 4217
- 4218
- 4219 while (!skb_queue_is_last(&tp->out_of_order_queue, skb)) {
- 4220 skb1 = skb_queue_next(&tp->out_of_order_queue, skb);
- 4221
- 4222 if (!after(end_seq, TCP_SKB_CB(skb1)->seq))
- 4223 break;
- 4224 if (before(end_seq, TCP_SKB_CB(skb1)->end_seq)) {
- 4225 tcp_dsack_extend(sk, TCP_SKB_CB(skb1)->seq,
- 4226 end_seq);
- 4227 break;
- 4228 }
- 4229 __skb_unlink(skb1, &tp->out_of_order_queue);
- 4230 tcp_dsack_extend(sk, TCP_SKB_CB(skb1)->seq,
- 4231 TCP_SKB_CB(skb1)->end_seq);
- 4232 NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPOFOMERGE);
- 4233 __kfree_skb(skb1);
- 4234 }
- 4235
- 4236 add_sack:
- 4237 if (tcp_is_sack(tp))
- 4238 tcp_sack_new_ofo_skb(sk, seq, end_seq);
- 4239 end:
- 4240 if (skb)
- 4241 skb_set_owner_r(skb, sk);
- 4242
4129:如果TCP接收缓存的空间紧张,乱序队列中的数据是可以被删除的(反正也没确认,不删白不删):
- 4061 static int tcp_try_rmem_schedule(struct sock *sk, struct sk_buff *skb,
- 4062 unsigned int size)
- 4063 {
- 4064 if (atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf ||
- 4065 !sk_rmem_schedule(sk, skb, size)) {
- 4066
- 4067 if (tcp_prune_queue(sk) < 0)
- 4068 return -1;
- 4069
- 4070 if (!sk_rmem_schedule(sk, skb, size)) {
- 4071 if (!tcp_prune_ofo_queue(sk))
- 4072 return -1;
- 4073
- 4074 if (!sk_rmem_schedule(sk, skb, size))
- 4075 return -1;
- 4076 }
- 4077 }
- 4078 return 0;
- 4079 }
数据包放入接收队列进程后,进程就可以使用系统调用将包中的数据copy到用户缓存中,最终完成TCP的数据接收。
在慢速处理路径的最后部分,调用tcp_data_snd_check函数发送数据并检查接收缓存空间:
- 4719 static void tcp_new_space(struct sock *sk)
- 4720 {
- 4721 struct tcp_sock *tp = tcp_sk(sk);
- 4722
- 4723 if (tcp_should_expand_sndbuf(sk)) {
- 4724 int sndmem = SKB_TRUESIZE(max_t(u32,
- 4725 tp->rx_opt.mss_clamp,
- 4726 tp->mss_cache) +
- 4727 MAX_TCP_HEADER);
- 4728 int demanded = max_t(unsigned int, tp->snd_cwnd,
- 4729 tp->reordering + 1);
- 4730 sndmem *= 2 * demanded;
- 4731 if (sndmem > sk->sk_sndbuf)
- 4732 sk->sk_sndbuf = min(sndmem, sysctl_tcp_wmem[2]);
- 4733 tp->snd_cwnd_stamp = tcp_time_stamp;
- 4734 }
- 4735
- 4736 sk->sk_write_space(sk);
- 4737 }
- 4738
- 4739 static void tcp_check_space(struct sock *sk)
- 4740 {
- 4741 if (sock_flag(sk, SOCK_QUEUE_SHRUNK)) {
- 4742 sock_reset_flag(sk, SOCK_QUEUE_SHRUNK);
- 4743 if (sk->sk_socket &&
- 4744 test_bit(SOCK_NOSPACE, &sk->sk_socket->flags))
- 4745 tcp_new_space(sk);
- 4746 }
- 4747 }
- 4748
- 4749 static inline void tcp_data_snd_check(struct sock *sk)
- 4750 {
- 4751 tcp_push_pending_frames(sk);
- 4752 tcp_check_space(sk);
- 4753 }
以上简要分析了TCP在慢速路径处理模式下的工作。下节我们着重关注一下TCP发送和接收ACK的细节。