青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

大龍的博客

常用鏈接

統計

最新評論

Linux TCP數據包接收處理 --- 轉

在接收流程一節中可以看到數據包在讀取到用戶空間前,都要經過tcp_v4_do_rcv處理,從而在receive queue中排隊。

在該函數中,我們只分析當連接已經建立后的數據包處理流程,也即tcp_rcv_established函數。

 

tcp_rcv_established函數的工作原理是把數據包的處理分為2類:fast path和slow path,其含義顯而易見。這樣分類

的目的當然是加快數據包的處理,因為在正常情況下,數據包是按順序到達的,網絡狀況也是穩定的,這時可以按照fast path

直接把數據包存放到receive queue了。而在其他的情況下則需要走slow path流程了。

 

在協議棧中,是用頭部預測來實現的,每個tcp sock有個pred_flags成員,它就是判別的依據。

  1. static inline void __tcp_fast_path_on(struct tcp_sock *tp, u32 snd_wnd)  
  2. {  
  3.     tp->pred_flags = htonl((tp->tcp_header_len << 26) |  
  4.                    ntohl(TCP_FLAG_ACK) |  
  5.                    snd_wnd);  
  6. }  

 

可以看出頭部預測依賴的是頭部長度字段和通告窗口。也就是說標志位除了ACK和PSH外,如果其他的存在的話,就不能用

fast path處理,其揭示的含義如下:

Either the data transaction is taking place in only one direction (which means that we are the receiver

and not transmitting any data) or in the case where we are sending out data also, the window advertised

 from the other end is constant. The latter means that we have not transmitted any data from our side for

quite some time but are receiving data from the other end. The receive window advertised by the other end is constant.

 

2. Other than PSH|ACK flags in the TCP header, no other flag is set (ACK is set for each TCP segment). 

This means that if any other flag is set such as URG, FIN, SYN, ECN, RST, and CWR, we
know that something important is there to be attended and we need to move into the SLOW path.

 

3. The header length has unchanged. If the TCP header length remains unchanged,
we have not added/reduced any TCP option and we can safely assume that
there is nothing important to be attended, if the above two conditions are TRUE.

 

fast path工作的條件

[c-sharp] view plaincopy
  1. static inline void tcp_fast_path_check(struct sock *sk)  
  2. {  
  3.     struct tcp_sock *tp = tcp_sk(sk);  
  4.   
  5.     if (skb_queue_empty(&tp->out_of_order_queue) &&  
  6.         tp->rcv_wnd &&  
  7.         atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf &&  
  8.         !tp->urg_data)  
  9.         tcp_fast_path_on(tp);  
  10. }  

 

 

1 沒有亂序數據包

2 接收窗口不為0

3 還有接收緩存空間

4 沒有緊急數據

 

反之,則進入slow path處理;另外當連接新建立時處于slow path。

 

從fast path進入slow path的觸發條件(進入slow path 后pred_flags清除為0):

1 在tcp_data_queue中接收到亂序數據包

2 在tcp_prune_queue中用完緩存并且開始丟棄數據包

3 在tcp_urgent_check中遇到緊急指針

4 在tcp_select_window中發送的通告窗口下降到0.

 

從slow_path進入fast_path的觸發條件:

1 When we have read past an urgent byte in tcp_recvmsg() . Wehave gotten an urgent byte and we remain

  in the slow path mode until we receive the urgent byte because it is handled in the slow path in
  tcp_rcv_established() .

2 當在tcp_data_queue中亂序隊列由于gap被填充而處理完畢時,運行tcp_fast_path_check。

3 tcp_ack_update_window()中更新了通告窗口。

 

fast path處理流程

A 判斷能否進入fast path

  1. if ((tcp_flag_word(th) & TCP_HP_BITS) == tp->pred_flags &&  
  2.         TCP_SKB_CB(skb)->seq == tp->rcv_nxt) {  

 

TCP_HP_BITS的作用就是排除flag中的PSH標志位。只有在頭部預測滿足并且數據包以正確的順序(該數據包的第一個序號就是下個要接收

的序號)到達時才進入fast path。

[c-sharp] view plaincopy
  1. int tcp_header_len = tp->tcp_header_len;  
  2.   
  3. /* Timestamp header prediction: tcp_header_len 
  4.  * is automatically equal to th->doff*4 due to pred_flags 
  5.  * match. 
  6.  */  
  7.   
  8. /* Check timestamp */  
  9. if (tcp_header_len == sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED) {  
  10.     /* No? Slow path! */  
  11.     if (!tcp_parse_aligned_timestamp(tp, th))  
  12.         goto slow_path;  
  13.   
  14.     /* If PAWS failed, check it more carefully in slow path */  
  15.     if ((s32)(tp->rx_opt.rcv_tsval - tp->rx_opt.ts_recent) < 0)  
  16.         goto slow_path;  
  17.   
  18.     /* DO NOT update ts_recent here, if checksum fails 
  19.      * and timestamp was corrupted part, it will result 
  20.      * in a hung connection since we will drop all 
  21.      * future packets due to the PAWS test. 
  22.      */  
  23. }  

 

該代碼段是依據時戳選項來檢查PAWS(Protect Against Wrapped Sequence numbers)。

 

如果發送來的僅是一個TCP頭的話(沒有捎帶數據或者接收端檢測到有亂序數據這些情況時都會發送一個純粹的ACK包)

[c-sharp] view plaincopy
  1. /* Bulk data transfer: sender */  
  2. if (len == tcp_header_len) {  
  3.     /* Predicted packet is in window by definition. 
  4.      * seq == rcv_nxt and rcv_wup <= rcv_nxt. 
  5.      * Hence, check seq<=rcv_wup reduces to: 
  6.      */  
  7.     if (tcp_header_len ==  
  8.         (sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED) &&  
  9.         tp->rcv_nxt == tp->rcv_wup)  
  10.         tcp_store_ts_recent(tp);  
  11.   
  12.     /* We know that such packets are checksummed 
  13.      * on entry. 
  14.      */  
  15.     tcp_ack(sk, skb, 0);  
  16.     __kfree_skb(skb);  
  17.     tcp_data_snd_check(sk);  
  18.     return 0;  
  19. else { /* Header too small */  
  20.     TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_INERRS);  
  21.     goto discard;  
  22. }  

 

主要的工作如下:

1 保存對方的最近時戳 tcp_store_ts_recent。通過前面的if判斷可以看出tcp總是回顯2次時戳回顯直接最先到達的數據包的時戳,

  rcv_wup只在發送數據(這時回顯時戳)時重置為rcv_nxt,所以接收到前一次回顯后第一個數據包后,rcv_nxt增加了,但是

  rcv_wup沒有更新,所以后面的數據包處理時不會調用該函數來保存時戳。

2 ACK處理。這個函數非常復雜,包含了擁塞控制機制,確認處理等等。

3 檢查是否有數據待發送 tcp_data_snd_check。

 

如果該數據包中包含了數據的話

  1.         } else {  
  2.             int eaten = 0;  
  3.             int copied_early = 0;  
  4.                         /* 此數據包剛好是下一個讀取的數據,并且用戶空間可存放下該數據包*/  
  5.             if (tp->copied_seq == tp->rcv_nxt &&  
  6.                 len - tcp_header_len <= tp->ucopy.len) {  
  7. #ifdef CONFIG_NET_DMA  
  8.                 if (tcp_dma_try_early_copy(sk, skb, tcp_header_len)) {  
  9.                     copied_early = 1;  
  10.                     eaten = 1;  
  11.                 }  
  12. #endif                          /* 如果該函數在進程上下文中調用并且sock被用戶占用的話*/  
  13.                 if (tp->ucopy.task == current &&  
  14.                     sock_owned_by_user(sk) && !copied_early) {  
  15.                                         /* 進程有可能被設置為TASK_INTERRUPTIBLE */  
  16.                     __set_current_state(TASK_RUNNING);  
  17.                                         /* 直接copy數據到用戶空間*/  
  18.                     if (!tcp_copy_to_iovec(sk, skb, tcp_header_len))  
  19.                         eaten = 1;  
  20.                 }  
  21.                 if (eaten) {  
  22.                     /* Predicted packet is in window by definition. 
  23.                      * seq == rcv_nxt and rcv_wup <= rcv_nxt. 
  24.                      * Hence, check seq<=rcv_wup reduces to: 
  25.                      */  
  26.                     if (tcp_header_len ==  
  27.                         (sizeof(struct tcphdr) +  
  28.                          TCPOLEN_TSTAMP_ALIGNED) &&  
  29.                         tp->rcv_nxt == tp->rcv_wup)  
  30.                         tcp_store_ts_recent(tp);  
  31.                                         /* 更新RCV RTT,Dynamic Right-Sizing算法*/  
  32.                     tcp_rcv_rtt_measure_ts(sk, skb);  
  33.   
  34.                     __skb_pull(skb, tcp_header_len);  
  35.                     tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq;  
  36.                     NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPHPHITSTOUSER);  
  37.                 }  
  38.                 if (copied_early)  
  39.                     tcp_cleanup_rbuf(sk, skb->len);  
  40.             }  
  41.             if (!eaten) { /* 沒有直接讀到用戶空間*/  
  42.                 if (tcp_checksum_complete_user(sk, skb))  
  43.                     goto csum_error;  
  44.   
  45.                 /* Predicted packet is in window by definition. 
  46.                  * seq == rcv_nxt and rcv_wup <= rcv_nxt. 
  47.                  * Hence, check seq<=rcv_wup reduces to: 
  48.                  */  
  49.                 if (tcp_header_len ==  
  50.                     (sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED) &&  
  51.                     tp->rcv_nxt == tp->rcv_wup)  
  52.                     tcp_store_ts_recent(tp);  
  53.   
  54.                 tcp_rcv_rtt_measure_ts(sk, skb);  
  55.   
  56.                 if ((int)skb->truesize > sk->sk_forward_alloc)  
  57.                     goto step5;  
  58.   
  59.                 NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPHPHITS);  
  60.   
  61.                 /* Bulk data transfer: receiver */  
  62.                 __skb_pull(skb, tcp_header_len);  
  63.                                 /* 進入receive queue 排隊,以待tcp_recvmsg讀取*/  
  64.                 __skb_queue_tail(&sk->sk_receive_queue, skb);  
  65.                 skb_set_owner_r(skb, sk);  
  66.                 tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq;  
  67.             }  
  68.                         /* 數據包接收后續處理*/  
  69.             tcp_event_data_recv(sk, skb);  
  70.                         /* ACK 處理*/  
  71.             if (TCP_SKB_CB(skb)->ack_seq != tp->snd_una) {  
  72.                 /* Well, only one small jumplet in fast path... */  
  73.                 tcp_ack(sk, skb, FLAG_DATA);  
  74.                 tcp_data_snd_check(sk);  
  75.                 if (!inet_csk_ack_scheduled(sk))  
  76.                     goto no_ack;  
  77.             }  
  78.                         /* ACK發送處理*/  
  79.             if (!copied_early || tp->rcv_nxt != tp->rcv_wup)  
  80.                 __tcp_ack_snd_check(sk, 0);  
  81. no_ack:  
  82. #ifdef CONFIG_NET_DMA  
  83.             if (copied_early)  
  84.                 __skb_queue_tail(&sk->sk_async_wait_queue, skb);  
  85.             else  
  86. #endif                    
  87.                         /* eaten為1,表示數據直接copy到了用戶空間,這時無需提醒用戶進程數據的到達,否則需調用sk_data_ready來通知,因為此時數據到達了receive queue*/  
  88.             if (eaten)  
  89.                 __kfree_skb(skb);  
  90.             else  
  91.                 sk->sk_data_ready(sk, 0);  
  92.             return 0;  
  93.         }  

 

 

tcp_event_data_recv函數

[c-sharp] view plaincopy
  1. static void tcp_event_data_recv(struct sock *sk, struct sk_buff *skb)  
  2. {  
  3.     struct tcp_sock *tp = tcp_sk(sk);  
  4.     struct inet_connection_sock *icsk = inet_csk(sk);  
  5.     u32 now;  
  6.         /* 接收到了數據,設置ACK需調度標志*/  
  7.     inet_csk_schedule_ack(sk);  
  8.   
  9.     tcp_measure_rcv_mss(sk, skb);  
  10.   
  11.     tcp_rcv_rtt_measure(tp);  
  12.   
  13.     now = tcp_time_stamp;  
  14.         /* 以下為根據接收間隔更新icsk_ack.ato,該值主要用于判斷pingpong模式見函數tcp_event_data_sent */  
  15.     if (!icsk->icsk_ack.ato) {  
  16.         /* The _first_ data packet received, initialize 
  17.          * delayed ACK engine. 
  18.          */  
  19.         tcp_incr_quickack(sk);  
  20.         icsk->icsk_ack.ato = TCP_ATO_MIN;  
  21.     } else {  
  22.         int m = now - icsk->icsk_ack.lrcvtime;  
  23.   
  24.         if (m <= TCP_ATO_MIN / 2) {  
  25.             /* The fastest case is the first. */  
  26.             icsk->icsk_ack.ato = (icsk->icsk_ack.ato >> 1) + TCP_ATO_MIN / 2;  
  27.         } else if (m < icsk->icsk_ack.ato) {  
  28.             icsk->icsk_ack.ato = (icsk->icsk_ack.ato >> 1) + m;  
  29.             if (icsk->icsk_ack.ato > icsk->icsk_rto)  
  30.                 icsk->icsk_ack.ato = icsk->icsk_rto;  
  31.         } else if (m > icsk->icsk_rto) {  
  32.             /* Too long gap. Apparently sender failed to 
  33.              * restart window, so that we send ACKs quickly. 
  34.              */  
  35.             tcp_incr_quickack(sk);  
  36.             sk_mem_reclaim(sk);  
  37.         }  
  38.     }  
  39.     icsk->icsk_ack.lrcvtime = now;  
  40.   
  41.     TCP_ECN_check_ce(tp, skb);  
  42.         /* 每次接收到來自對方的一個TCP數據報,且數據報長度大于128字節時,我們需要調用tcp_grow_window,增加rcv_ssthresh的值,一般每次為rcv_ssthresh增長兩倍的mss,增加的條件是rcv_ssthresh小于window_clamp,并且 rcv_ssthresh小于接收緩存剩余空間的3/4,同時tcp_memory_pressure沒有被置位(即接收緩存中的數據量沒有太大)。 tcp_grow_window中對新收到的skb的長度還有一些限制,并不總是增長rcv_ssthresh的值*/  
  43.     if (skb->len >= 128)  
  44.         tcp_grow_window(sk, skb);  
  45. }  

 

rcv_ssthresh是當前的接收窗口大小的一個閥值,其初始值就置為rcv_wnd。它跟rcv_wnd配合工作,

當本地socket收到數據報,并滿足一定條件時,增長rcv_ssthresh的值,在下一次發送數據報組建TCP首部時,

需要通告對方當前的接收窗口大小,這時需要更新rcv_wnd,此時rcv_wnd的取值不能超過rcv_ssthresh的值。

兩者配合,達到一個滑動窗口大小緩慢增長的效果。

__tcp_ack_snd_check用來判斷ACK的發送方式

[c-sharp] view plaincopy
  1. /* 
  2.  * Check if sending an ack is needed. 
  3.  */  
  4. static void __tcp_ack_snd_check(struct sock *sk, int ofo_possible)  
  5. {  
  6.     struct tcp_sock *tp = tcp_sk(sk);  
  7.   
  8.         /* More than one full frame received... */  
  9.     if (((tp->rcv_nxt - tp->rcv_wup) > inet_csk(sk)->icsk_ack.rcv_mss  
  10.          /* ... and right edge of window advances far enough. 
  11.           * (tcp_recvmsg() will send ACK otherwise). Or... 
  12.           */  
  13.          && __tcp_select_window(sk) >= tp->rcv_wnd) ||  
  14.         /* We ACK each frame or... */  
  15.         tcp_in_quickack_mode(sk) ||  
  16.         /* We have out of order data. */  
  17.         (ofo_possible && skb_peek(&tp->out_of_order_queue))) {  
  18.         /* Then ack it now */  
  19.         tcp_send_ack(sk);  
  20.     } else {  
  21.         /* Else, send delayed ack. */  
  22.         tcp_send_delayed_ack(sk);  
  23.     }  
  24. }  

 

注釋很清楚,無需解釋。

 

 

這里有個疑問,就是當ucopy應用讀到需要讀取到的數據包后,也即在一次處理中

  1. if (tp->copied_seq == tp->rcv_nxt &&  
  2.                 len - tcp_header_len <= tp->ucopy.len) {  

 

 

的第二個條件的等號為真 len - tcp_header_len == tp->ucopy.len,然后執行流程到后面eaten為1,所以函數以釋放skb結束,沒有

調用sk_data_ready函數。假設這個處理調用流程如下:

tcp_recvmsg-> sk_wait_data  -> sk_wait_event -> release_sock -> __release_sock-> sk_backlog_rcv-> tcp_rcv_established

那么即使此時用戶得到了所需的數據,但是在tcp_rcv_established返回前沒有提示數據已得到,

 

  1. #define sk_wait_event(__sk, __timeo, __condition)           /  
  2.     ({  int __rc;                       /  
  3.         release_sock(__sk);                 /  
  4.         __rc = __condition;                 /  
  5.         if (!__rc) {                        /  
  6.             *(__timeo) = schedule_timeout(*(__timeo));  /  
  7.         }                           /  
  8.         lock_sock(__sk);                    /  
  9.         __rc = __condition;                 /  
  10.         __rc;                           /  
  11.     })  

 

但是在回到sk_wait_event后,由于__condition為 !skb_queue_empty(&sk->sk_receive_queue),所以還是會調用schedule_timeout

來等待。這點顯然是浪費時間,所以這個condition應該考慮下這個數據已經讀滿的情況,而不能光靠觀察receive queue來判斷是否等待。

 

接下來分析slow path

  1. slow_path:  
  2.     if (len < (th->doff << 2) || tcp_checksum_complete_user(sk, skb))  
  3.         goto csum_error;  
  4.   
  5.     /* 
  6.      *  Standard slow path. 
  7.      */  
  8.         /* 檢查到達的數據包 */  
  9.     res = tcp_validate_incoming(sk, skb, th, 1);  
  10.     if (res <= 0)  
  11.         return -res;  
  12.   
  13. step5:  /* 如果設置了ACK,則調用tcp_ack處理,后面再分析該函數*/  
  14.     if (th->ack)  
  15.         tcp_ack(sk, skb, FLAG_SLOWPATH);  
  16.   
  17.     tcp_rcv_rtt_measure_ts(sk, skb);  
  18.   
  19.     /* Process urgent data. */  
  20.     tcp_urg(sk, skb, th);  
  21.   
  22.     /* step 7: process the segment text */  
  23.     tcp_data_queue(sk, skb);  
  24.           
  25.     tcp_data_snd_check(sk);  
  26.     tcp_ack_snd_check(sk);  
  27.     return 0;  

 

 

先看看tcp_validate_incoming函數,在slow path處理前檢查輸入數據包的合法性。

  1. /* Does PAWS and seqno based validation of an incoming segment, flags will 
  2.  * play significant role here. 
  3.  */  
  4. static int tcp_validate_incoming(struct sock *sk, struct sk_buff *skb,  
  5.                   struct tcphdr *th, int syn_inerr)  
  6. {  
  7.     struct tcp_sock *tp = tcp_sk(sk);  
  8.   
  9.     /* RFC1323: H1. Apply PAWS check first. */  
  10.     if (tcp_fast_parse_options(skb, th, tp) && tp->rx_opt.saw_tstamp &&  
  11.         tcp_paws_discard(sk, skb)) {  
  12.         if (!th->rst) {  
  13.             NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSESTABREJECTED);  
  14.             tcp_send_dupack(sk, skb);  
  15.             goto discard;  
  16.         }  
  17.         /* Reset is accepted even if it did not pass PAWS. */  
  18.     }  
  19.   
  20.     /* Step 1: check sequence number */  
  21.     if (!tcp_sequence(tp, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq)) {  
  22.         /* RFC793, page 37: "In all states except SYN-SENT, all reset 
  23.          * (RST) segments are validated by checking their SEQ-fields." 
  24.          * And page 69: "If an incoming segment is not acceptable, 
  25.          * an acknowledgment should be sent in reply (unless the RST 
  26.          * bit is set, if so drop the segment and return)". 
  27.          */  
  28.         if (!th->rst)  
  29.             tcp_send_dupack(sk, skb);  
  30.         goto discard;  
  31.     }  
  32.   
  33.     /* Step 2: check RST bit */  
  34.     if (th->rst) {  
  35.         tcp_reset(sk);  
  36.         goto discard;  
  37.     }  
  38.   
  39.     /* ts_recent update must be made after we are sure that the packet 
  40.      * is in window. 
  41.      */  
  42.     tcp_replace_ts_recent(tp, TCP_SKB_CB(skb)->seq);  
  43.   
  44.     /* step 3: check security and precedence [ignored] */  
  45.   
  46.     /* step 4: Check for a SYN in window. */  
  47.     if (th->syn && !before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt)) {  
  48.         if (syn_inerr)  
  49.             TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_INERRS);  
  50.         NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPABORTONSYN);  
  51.         tcp_reset(sk);  
  52.         return -1;  
  53.     }  
  54.   
  55.     return 1;  
  56.   
  57. discard:  
  58.     __kfree_skb(skb);  
  59.     return 0;  
  60. }  

 

 

第一步:檢查PAWS tcp_paws_discard

[c-sharp] view plaincopy
  1. static inline int tcp_paws_discard(const struct sock *sk,  
  2.                    const struct sk_buff *skb)  
  3. {  
  4.     const struct tcp_sock *tp = tcp_sk(sk);  
  5.     return ((s32)(tp->rx_opt.ts_recent - tp->rx_opt.rcv_tsval) > TCP_PAWS_WINDOW &&  
  6.         get_seconds() < tp->rx_opt.ts_recent_stamp + TCP_PAWS_24DAYS &&  
  7.         !tcp_disordered_ack(sk, skb));  
  8. }  

 

 

 PAWS丟棄數據包要滿足以下條件

1 The difference between the timestamp value obtained in the current segmentand last seen timestamp on

the incoming TCP segment should be more than TCP_PAWS_WINDOW (= 1), which means that if the segment that was
transmitted 1 clock tick before the segment that reached here earlier TCP seq should be acceptable.

It may be because of reordering of the segments that the latter reached earlier.

2 the 24 days have not elapsed since last time timestamp was stored,

3 tcp_disordered_ack返回0.

 

以下轉載自CU論壇http://linux.chinaunix.net/bbs/viewthread.php?tid=1130308

 


 

 

 

在實際進行PAWS預防時,Linux是通過如下代碼調用來完成的
tcp_rcv_established
    |
    |-->tcp_paws_discard
          |
          |-->tcp_disordered_ack
其中關鍵是local方通過tcp_disordered_ack函數對一個剛收到的數據分段進行判斷,下面我們對該函數的判斷邏輯進行下總結:
大前提:該收到分段的TS值表明有回繞現象發生
a)若該分段不是一個純ACK,則丟棄。因為顯然這個分段所攜帶的數據是一個老數據了,不是local方目前希望接收的(參見PAWS的處理依據一節)
b)若該分段不是local所希望接收的,則丟棄。這個原因很顯然
c)若該分段是一個純ACK,但該ACK并不是一個重復ACK(由local方后續數據正確到達所引發的),則丟棄。因為顯然該ACK是一個老的ACK,并不是由于為了加快local方重發而在每收到一個丟失分段后的分段而發出的ACK。
d)若該分段是一個ACK,且為重復ACK,并且該ACK的TS值超過了local方那個丟失分段后的重發rto,則丟棄。因為顯然此時local方已經重發了那個導致此重復ACK產生的分段,因此再收到此重復ACK就可以直接丟棄。
e)若該分段是一個ACK,且為重復ACK,但是沒有超過一個rto的時間,則不能丟棄,因為這正代表peer方收到了local方發出的丟失分段后的分段,local方要對此ACK進行處理(例如立刻重傳)

這里有一個重要概念需要理解,即在出現TS問題后,純ACK和帶ACK的數據分段二者是顯著不同的,對于后者,可以立刻丟棄掉,因為從一個窗口的某個seq到下一個窗口的同一個seq過程中,一定有窗口變化曾經發生過,從而TS記錄值ts_recent也一定更新過,此時一定可以通過PAWS進行丟棄處理。但是對于前者,一個純ACK,就不能簡單丟棄了,因為有這樣一個現象是合理的,即假定local方的接收緩存很大,并且peer方在發送時很快就回繞了,于是在local方的某個分段丟失后,peer方需要在每收到的后續分段時發送重復ACK,而此時該重發ACK的ack_seq就是這個丟失分段的序號,而該重發ACK的seq已經是回繞后的重復序號了,盡管此時到底是回繞后的那個重復ACK還是之前的那個同樣序號seq的重復ACK,對于local方來都需要處理(立刻啟動重發動作),而不能簡單丟棄掉。

 


 

 第2步 檢查數據包的序號是否正確,該判斷失敗后調用tcp_send_dupack發送一個duplicate acknowledge(未設置RST標志位時)。

  1. static inline int tcp_sequence(struct tcp_sock *tp, u32 seq, u32 end_seq)  
  2. {  
  3.     return  !before(end_seq, tp->rcv_wup) &&  
  4.         !after(seq, tp->rcv_nxt + tcp_receive_window(tp));  
  5. }  

 

由rcv_wup的更新時機(發送ACK時的tcp_select_window)可知位于序號rcv_wup前面的數據都已確認,所以待檢查數據包的結束序號至少

要大于該值;同時開始序號要落在接收窗口內。

 

第3步 如果設置了RST,則調用tcp_reset處理

第4步 更新ts_recent,

第5步 檢查SYN,因為重發的SYN和原來的SYN之間不會發送數據,所以這2個SYN的序號是相同的,如果不滿足則reset連接。

 

接下來重點分析tcp_data_queue函數,這里就是對數據包的處理了。

 

 

  1. static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)  
  2. {  
  3.     struct tcphdr *th = tcp_hdr(skb);  
  4.     struct tcp_sock *tp = tcp_sk(sk);  
  5.     int eaten = -1;  
  6.         /* 沒有數據處理*/  
  7.     if (TCP_SKB_CB(skb)->seq == TCP_SKB_CB(skb)->end_seq)  
  8.         goto drop;  
  9.         /* 跳過tcp頭部*/  
  10.     __skb_pull(skb, th->doff * 4);  
  11.         /* 如果收到對方發來的CWR,則本地TCP發送時不在設置ECE*/  
  12.     TCP_ECN_accept_cwr(tp, skb);  
  13.         /* 初始化Duplicate SACK*/  
  14.     if (tp->rx_opt.dsack) {  
  15.         tp->rx_opt.dsack = 0;  
  16.         tp->rx_opt.eff_sacks = tp->rx_opt.num_sacks;  
  17.     }  

 

 

如果該數據包剛好是下一個要接收的數據,則可以直接copy到用戶空間(如果存在且可用),否則排隊到receive queue

  1. /*  Queue data for delivery to the user. 
  2.  *  Packets in sequence go to the receive queue. 
  3.  *  Out of sequence packets to the out_of_order_queue. 
  4.  */  
  5. if (TCP_SKB_CB(skb)->seq == tp->rcv_nxt) {  
  6.     if (tcp_receive_window(tp) == 0)  
  7.         goto out_of_window;  
  8.   
  9.     /* Ok. In sequence. In window. */  
  10.     if (tp->ucopy.task == current &&  
  11.         tp->copied_seq == tp->rcv_nxt && tp->ucopy.len &&  
  12.         sock_owned_by_user(sk) && !tp->urg_data) {  
  13.         int chunk = min_t(unsigned int, skb->len,  
  14.                   tp->ucopy.len);  
  15.   
  16.         __set_current_state(TASK_RUNNING);  
  17.                        /* 這里的下半部開關的作用不解*/  
  18.         local_bh_enable();  
  19.         if (!skb_copy_datagram_iovec(skb, 0, tp->ucopy.iov, chunk)) {  
  20.             tp->ucopy.len -= chunk;  
  21.             tp->copied_seq += chunk;  
  22.             eaten = (chunk == skb->len && !th->fin);  
  23.             tcp_rcv_space_adjust(sk);  
  24.         }  
  25.         local_bh_disable();  
  26.     }  
  27.   
  28.     if (eaten <= 0) {  
  29. ueue_and_out:  
  30.         if (eaten < 0 &&  
  31.                            /* 該函數用于判斷是否有接收緩存,在tcp內存管理中將分析*/  
  32.             tcp_try_rmem_schedule(sk, skb->truesize))  
  33.             goto drop;  
  34.   
  35.         skb_set_owner_r(skb, sk);  
  36.         __skb_queue_tail(&sk->sk_receive_queue, skb);  
  37.     }  
  38.     tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq;  
  39.     if (skb->len)  
  40.         tcp_event_data_recv(sk, skb);  
  41.     if (th->fin)  
  42.         tcp_fin(skb, sk, th);  
  43.                /* 到達的數據包喲可能填充了亂序隊列中的hole */  
  44.     if (!skb_queue_empty(&tp->out_of_order_queue)) {  
  45.         tcp_ofo_queue(sk);  
  46.   
  47.         /* RFC2581. 4.2. SHOULD send immediate ACK, when 
  48.          * gap in queue is filled. 
  49.          */  
  50.                        /*關閉乒乓模式,在quick計數沒消耗完時則可立即發送ACK,見tcp_in_quickack_mode*/  
  51.         if (skb_queue_empty(&tp->out_of_order_queue))  
  52.             inet_csk(sk)->icsk_ack.pingpong = 0;  
  53.     }  
  54.                /* 新數據到達導致返回給對方的SACK Block 調整*/  
  55.     if (tp->rx_opt.num_sacks)  
  56.         tcp_sack_remove(tp);  
  57.                /* 在當前slow path,檢測是否可以進入fast path*/  
  58.     tcp_fast_path_check(sk);  
  59.   
  60.     if (eaten > 0)  
  61.         __kfree_skb(skb);  
  62.     else if (!sock_flag(sk, SOCK_DEAD))  
  63.         sk->sk_data_ready(sk, 0);  
  64.     return;  
  65. }  

 

下面看看函數tcp_ofo_queue,也即out-of-order queue的處理

  1. /* This one checks to see if we can put data from the 
  2.  * out_of_order queue into the receive_queue. 
  3.  */  
  4. static void tcp_ofo_queue(struct sock *sk)  
  5. {  
  6.     struct tcp_sock *tp = tcp_sk(sk);  
  7.     __u32 dsack_high = tp->rcv_nxt;  
  8.     struct sk_buff *skb;  
  9.   
  10.     while ((skb = skb_peek(&tp->out_of_order_queue)) != NULL) {  
  11.                 /* 當前hole未覆蓋,則處理結束*/  
  12.         if (after(TCP_SKB_CB(skb)->seq, tp->rcv_nxt))  
  13.             break;  
  14.                 /* DSACK處理*/  
  15.         if (before(TCP_SKB_CB(skb)->seq, dsack_high)) {  
  16.             __u32 dsack = dsack_high;  
  17.             if (before(TCP_SKB_CB(skb)->end_seq, dsack_high))  
  18.                 dsack_high = TCP_SKB_CB(skb)->end_seq;  
  19.             tcp_dsack_extend(sk, TCP_SKB_CB(skb)->seq, dsack);  
  20.         }  
  21.                 /* 該亂序數據包完全被到達的數據包覆蓋,則從亂序隊列中刪除之,并釋放該數據包*/  
  22.         if (!after(TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt)) {  
  23.             SOCK_DEBUG(sk, "ofo packet was already received /n");  
  24.             __skb_unlink(skb, &tp->out_of_order_queue);  
  25.             __kfree_skb(skb);  
  26.             continue;  
  27.         }  
  28.         SOCK_DEBUG(sk, "ofo requeuing : rcv_next %X seq %X - %X/n",  
  29.                tp->rcv_nxt, TCP_SKB_CB(skb)->seq,  
  30.                TCP_SKB_CB(skb)->end_seq);  
  31.                 /* hole被填充,取出該亂序數據包到receive queue中排隊,并更新rcv_nxt */  
  32.         __skb_unlink(skb, &tp->out_of_order_queue);  
  33.         __skb_queue_tail(&sk->sk_receive_queue, skb);  
  34.         tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq;  
  35.         if (tcp_hdr(skb)->fin)  
  36.             tcp_fin(skb, sk, tcp_hdr(skb));  
  37.     }  
  38. }  

 

這里DSACK的處理中為什么即使dsack比end_seq大,還是用dsack作為右邊界呢

 

[c-sharp] view plaincopy
  1. /* 該數據包的數據已經完全存在,則發送DSACK,并進入快速ACK模式,調度ACK發送*/    
  2. if (!after(TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt)) {  
  3.         /* A retransmit, 2nd most common case.  Force an immediate ack. */  
  4.         NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_DELAYEDACKLOST);  
  5.         tcp_dsack_set(sk, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq);  
  6.   
  7. out_of_window:  
  8.         tcp_enter_quickack_mode(sk);  
  9.         inet_csk_schedule_ack(sk);  
  10. drop:  
  11.         __kfree_skb(skb);  
  12.         return;  
  13.     }  
  14.   
  15.     /* Out of window. F.e. zero window probe. */  
  16.     if (!before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt + tcp_receive_window(tp)))  
  17.         goto out_of_window;  
  18.           
  19.     tcp_enter_quickack_mode(sk);  
  20.         /* 部分數據已存在,則設置正確的DSACK,然后排隊到receive queue*/  
  21.     if (before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt)) {  
  22.         /* Partial packet, seq < rcv_next < end_seq */  
  23.         SOCK_DEBUG(sk, "partial packet: rcv_next %X seq %X - %X/n",  
  24.                tp->rcv_nxt, TCP_SKB_CB(skb)->seq,  
  25.                TCP_SKB_CB(skb)->end_seq);  
  26.   
  27.         tcp_dsack_set(sk, TCP_SKB_CB(skb)->seq, tp->rcv_nxt);  
  28.   
  29.         /* If window is closed, drop tail of packet. But after 
  30.          * remembering D-SACK for its head made in previous line. 
  31.          */  
  32.         if (!tcp_receive_window(tp))  
  33.             goto out_of_window;  
  34.         goto queue_and_out;  
  35.     }  

 

 

  1.     TCP_ECN_check_ce(tp, skb); /* 檢查ECE是否設置 */  
  2.         /* 以下則把數據包排隊到失序隊列 */  
  3.         /* 同樣先判斷內存是否滿足 */  
  4.     if (tcp_try_rmem_schedule(sk, skb->truesize))  
  5.         goto drop;  
  6.   
  7.     /* Disable header prediction. */  
  8.     tp->pred_flags = 0;  
  9.     inet_csk_schedule_ack(sk);  
  10.   
  11.     SOCK_DEBUG(sk, "out of order segment: rcv_next %X seq %X - %X/n",  
  12.            tp->rcv_nxt, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq);  
  13.   
  14.     skb_set_owner_r(skb, sk);  
  15.         /* 該數據包是失序隊列的第一個數據包*/  
  16.     if (!skb_peek(&tp->out_of_order_queue)) {  
  17.         /* Initial out of order segment, build 1 SACK. */  
  18.         if (tcp_is_sack(tp)) {  
  19.             tp->rx_opt.num_sacks = 1;  
  20.             tp->rx_opt.dsack     = 0;  
  21.             tp->rx_opt.eff_sacks = 1;  
  22.             tp->selective_acks[0].start_seq = TCP_SKB_CB(skb)->seq;  
  23.             tp->selective_acks[0].end_seq =  
  24.                         TCP_SKB_CB(skb)->end_seq;  
  25.         }  
  26.         __skb_queue_head(&tp->out_of_order_queue, skb);  
  27.     } else {  
  28.         struct sk_buff *skb1 = tp->out_of_order_queue.prev;  
  29.         u32 seq = TCP_SKB_CB(skb)->seq;  
  30.         u32 end_seq = TCP_SKB_CB(skb)->end_seq;  
  31.                 /*剛好與失序隊列最后一個數據包數據銜接*/  
  32.         if (seq == TCP_SKB_CB(skb1)->end_seq) {  
  33.             __skb_queue_after(&tp->out_of_order_queue, skb1, skb);  
  34.                         /*如果沒有sack block或者當前數據包開始序號不等于第一個block右邊界*/  
  35.             if (!tp->rx_opt.num_sacks ||  
  36.                 tp->selective_acks[0].end_seq != seq)  
  37.                 goto add_sack;  
  38.                         /*該數據包在某個hole后是按序到達的,所以可以直接擴展第一個sack*/    
  39.             /* Common case: data arrive in order after hole. */  
  40.             tp->selective_acks[0].end_seq = end_seq;  
  41.             return;  
  42.         }  
  43.   
  44.         /* Find place to insert this segment. */  
  45.                 /* 該循環找到一個開始序號不大于該數據包開始序號的失序隊列中的數據包*/  
  46.         do {  
  47.             if (!after(TCP_SKB_CB(skb1)->seq, seq))  
  48.                 break;  
  49.         } while ((skb1 = skb1->prev) !=  
  50.              (struct sk_buff *)&tp->out_of_order_queue);  
  51.   
  52.         /* Do skb overlap to previous one? 檢查與前個數據包是否有重疊*/  
  53.         if (skb1 != (struct sk_buff *)&tp->out_of_order_queue &&  
  54.             before(seq, TCP_SKB_CB(skb1)->end_seq)) {  
  55.             if (!after(end_seq, TCP_SKB_CB(skb1)->end_seq)) {  
  56.                 /* All the bits are present. Drop. */  
  57.                 __kfree_skb(skb);  
  58.                 tcp_dsack_set(sk, seq, end_seq);  
  59.                 goto add_sack;  
  60.             }  
  61.             if (after(seq, TCP_SKB_CB(skb1)->seq)) {  
  62.                 /* Partial overlap. */  
  63.                 tcp_dsack_set(sk, seq,  
  64.                           TCP_SKB_CB(skb1)->end_seq);  
  65.             } else {  
  66.                 skb1 = skb1->prev;  
  67.             }  
  68.         }  
  69.                 /* 排隊到失序隊列*/  
  70.         __skb_queue_after(&tp->out_of_order_queue, skb1, skb);  
  71.   
  72.         /* And clean segments covered by new one as whole. 檢測與后面的數據包重疊*/  
  73.         while ((skb1 = skb->next) !=  
  74.                (struct sk_buff *)&tp->out_of_order_queue &&  
  75.                after(end_seq, TCP_SKB_CB(skb1)->seq)) {  
  76.             if (before(end_seq, TCP_SKB_CB(skb1)->end_seq)) {  
  77.                 tcp_dsack_extend(sk, TCP_SKB_CB(skb1)->seq,  
  78.                          end_seq);  
  79.                 break;  
  80.             }  
  81.             __skb_unlink(skb1, &tp->out_of_order_queue);  
  82.             tcp_dsack_extend(sk, TCP_SKB_CB(skb1)->seq,  
  83.                      TCP_SKB_CB(skb1)->end_seq);  
  84.             __kfree_skb(skb1);  
  85.         }  
  86.   
  87. add_sack:  
  88.         if (tcp_is_sack(tp))  
  89.                         /* 根據失序隊列的現狀更新SACK的blocks */  
  90.             tcp_sack_new_ofo_skb(sk, seq, end_seq);  
  91.     }  
  92. }  

 

@import url(http://m.shnenglu.com/cutesoft_client/cuteeditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);

posted on 2013-02-18 14:23 大龍 閱讀(6951) 評論(0)  編輯 收藏 引用


只有注冊用戶登錄后才能發表評論。
網站導航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            久久九九精品| 亚洲少妇一区| 欧美精品久久久久久久免费观看 | 日韩视频在线永久播放| 欧美国产日韩一二三区| 亚洲第一福利视频| 亚洲高清资源综合久久精品| 亚洲激情av在线| 正在播放日韩| 午夜精品久久久久影视| 久久精品国产一区二区三区免费看| 亚洲欧美精品伊人久久| 久久激五月天综合精品| 免费在线亚洲| 国产精品亚洲综合色区韩国| 国产手机视频精品| 亚洲黄色三级| 亚洲一区免费| 欧美成人亚洲成人| 99精品视频免费| 久久久久久久激情视频| 欧美日韩在线综合| 黄色亚洲在线| 亚洲视频在线观看网站| 欧美专区第一页| 亚洲国产精品成人| 香蕉免费一区二区三区在线观看| 美女露胸一区二区三区| 国产精品成人免费精品自在线观看| 国产一区二区丝袜高跟鞋图片| 亚洲美女av黄| 久久久高清一区二区三区| 亚洲第一成人在线| 午夜视黄欧洲亚洲| 欧美日韩精品综合在线| 韩国成人福利片在线播放| 亚洲视频在线观看三级| 牛人盗摄一区二区三区视频| 亚洲在线视频免费观看| 欧美日韩成人激情| 亚洲激情婷婷| 老鸭窝亚洲一区二区三区| 亚洲视屏一区| 欧美连裤袜在线视频| 狠狠色综合网| 久久久国产视频91| 在线综合亚洲欧美在线视频| 久久久999国产| 国产精品网红福利| 亚洲影院色无极综合| 最近中文字幕mv在线一区二区三区四区| 午夜欧美精品久久久久久久| 国产精品欧美久久久久无广告| 亚洲精品免费看| 美女国产一区| 久久久久99| 一区二区三区在线不卡| 久久久久国产精品人| 亚洲一区二区黄| 亚洲影视中文字幕| 欧美日韩视频| 亚洲午夜电影网| 一区二区激情视频| 午夜精品视频| 欧美成人久久| 美女脱光内衣内裤视频久久网站| 国产亚洲va综合人人澡精品| 欧美一区二区视频在线观看| 亚洲一区在线播放| 国产欧美综合在线| 久久久www免费人成黑人精品 | 国产一区二区三区久久悠悠色av| 欧美一区二区啪啪| 久久精品最新地址| 在线成人免费视频| 亚洲高清三级视频| 欧美日韩一区二区三区在线视频| 一区二区三区欧美| 亚洲私拍自拍| 国产一区二区三区av电影| 久久久久久久综合| 免费亚洲一区| 亚洲午夜一区| 午夜一区不卡| 亚洲国产欧美在线| 亚洲免费观看视频| 国产视频久久网| 欧美国产日本| 欧美新色视频| 久久国产精品色婷婷| 久久久亚洲综合| 夜夜精品视频一区二区| 在线综合亚洲| 亚洲国产视频一区| 在线一区二区三区四区| 激情欧美一区二区三区在线观看| 亚洲国产欧美一区二区三区同亚洲| 欧美日韩黄色一区二区| 久久精品视频播放| 欧美韩国一区| 久久青草久久| 欧美三日本三级少妇三2023| 久久久久国产精品厨房| 欧美久久久久久久| 久久精品av麻豆的观看方式| 欧美国产欧美亚洲国产日韩mv天天看完整 | 一本色道久久88综合日韩精品 | 久久精品国产免费看久久精品| 亚洲黄一区二区| 中文有码久久| 亚洲欧洲日本在线| 亚洲一区中文字幕在线观看| 亚洲美女av电影| 麻豆成人精品| 国产精品久久一区二区三区| 亚洲最新视频在线| 久久综合色婷婷| 欧美午夜精品久久久久久久 | 国内自拍视频一区二区三区| 亚洲精品中文字| 亚洲激情网站免费观看| 久久久99精品免费观看不卡| 午夜精品福利视频| 欧美日韩国产一级| 亚洲黄页一区| 亚洲片国产一区一级在线观看| 久久福利资源站| 欧美主播一区二区三区| 欧美日韩人人澡狠狠躁视频| 欧美高清视频一区二区三区在线观看 | 美乳少妇欧美精品| 国产精品男女猛烈高潮激情| 亚洲欧洲日韩综合二区| 激情五月综合色婷婷一区二区| 一区二区三区日韩欧美精品| 韩日午夜在线资源一区二区| 一区二区三区你懂的| 亚洲免费观看| 欧美夫妇交换俱乐部在线观看| 噜噜噜在线观看免费视频日韩| 国产视频一区二区在线观看 | 欧美亚洲免费电影| 国产精品毛片在线看| 99视频精品全部免费在线| 亚洲美女电影在线| 老司机免费视频久久| 欧美成人精品福利| 久久国产精品久久精品国产| 在线一区观看| 欧美三级视频在线| 亚洲一区二区三区精品动漫| 欧美在线视频导航| 伊人成综合网伊人222| 免费一区视频| 亚洲美女在线看| 亚洲午夜伦理| 国产乱码精品一区二区三区五月婷| 亚洲色在线视频| 欧美一区二区三区免费观看| 国产一区二区三区高清在线观看 | 国产欧美精品久久| 久久久欧美精品sm网站| 欧美sm视频| 欧美中文字幕| 亚洲高清123| 欧美久久九九| 亚洲最新在线| 麻豆9191精品国产| 亚洲免费av片| 国产午夜精品视频免费不卡69堂| 久久久亚洲人| 一本色道久久综合狠狠躁篇怎么玩| 欧美一区二区三区在线观看| 亚洲高清视频一区二区| 欧美日韩国产综合在线| 欧美一区二区视频免费观看| 欧美国产综合视频| 亚洲欧美日韩一区在线| 国产一区日韩二区欧美三区| 麻豆国产va免费精品高清在线| 一区二区久久久久| 美女久久一区| 欧美一区二区视频免费观看| 亚洲精品国精品久久99热| 国产欧美日本一区二区三区| 欧美精品久久久久久| 久久精精品视频| 一本不卡影院| 亚洲第一在线综合网站| 久久激情五月丁香伊人| 一区二区免费在线播放| 激情一区二区三区| 国产精品午夜久久| 欧美日韩视频专区在线播放| 久久久久这里只有精品| 亚洲欧美综合网| 在线亚洲一区观看| 亚洲精选在线观看| 亚洲国产精品黑人久久久 | 亚洲精品无人区|