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

大龍的博客

常用鏈接

統(tǒng)計(jì)

最新評(píng)論

Linux TCP數(shù)據(jù)包接收處理 --- 轉(zhuǎn)

在接收流程一節(jié)中可以看到數(shù)據(jù)包在讀取到用戶空間前,都要經(jīng)過tcp_v4_do_rcv處理,從而在receive queue中排隊(duì)。

在該函數(shù)中,我們只分析當(dāng)連接已經(jīng)建立后的數(shù)據(jù)包處理流程,也即tcp_rcv_established函數(shù)。

 

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

的目的當(dāng)然是加快數(shù)據(jù)包的處理,因?yàn)樵谡G闆r下,數(shù)據(jù)包是按順序到達(dá)的,網(wǎng)絡(luò)狀況也是穩(wěn)定的,這時(shí)可以按照fast path

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

 

在協(xié)議棧中,是用頭部預(yù)測來實(shí)現(xiàn)的,每個(gè)tcp sock有個(gè)pred_flags成員,它就是判別的依據(jù)。

  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. }  

 

可以看出頭部預(yù)測依賴的是頭部長度字段和通告窗口。也就是說標(biāo)志位除了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 沒有亂序數(shù)據(jù)包

2 接收窗口不為0

3 還有接收緩存空間

4 沒有緊急數(shù)據(jù)

 

反之,則進(jìn)入slow path處理;另外當(dāng)連接新建立時(shí)處于slow path。

 

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

1 在tcp_data_queue中接收到亂序數(shù)據(jù)包

2 在tcp_prune_queue中用完緩存并且開始丟棄數(shù)據(jù)包

3 在tcp_urgent_check中遇到緊急指針

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

 

從slow_path進(jìn)入fast_path的觸發(fā)條件:

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 當(dāng)在tcp_data_queue中亂序隊(duì)列由于gap被填充而處理完畢時(shí),運(yùn)行tcp_fast_path_check。

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

 

fast path處理流程

A 判斷能否進(jìn)入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標(biāo)志位。只有在頭部預(yù)測滿足并且數(shù)據(jù)包以正確的順序(該數(shù)據(jù)包的第一個(gè)序號(hào)就是下個(gè)要接收

的序號(hào))到達(dá)時(shí)才進(jìn)入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. }  

 

該代碼段是依據(jù)時(shí)戳選項(xiàng)來檢查PAWS(Protect Against Wrapped Sequence numbers)。

 

如果發(fā)送來的僅是一個(gè)TCP頭的話(沒有捎帶數(shù)據(jù)或者接收端檢測到有亂序數(shù)據(jù)這些情況時(shí)都會(huì)發(fā)送一個(gè)純粹的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 保存對(duì)方的最近時(shí)戳 tcp_store_ts_recent。通過前面的if判斷可以看出tcp總是回顯2次時(shí)戳回顯直接最先到達(dá)的數(shù)據(jù)包的時(shí)戳,

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

  rcv_wup沒有更新,所以后面的數(shù)據(jù)包處理時(shí)不會(huì)調(diào)用該函數(shù)來保存時(shí)戳。

2 ACK處理。這個(gè)函數(shù)非常復(fù)雜,包含了擁塞控制機(jī)制,確認(rèn)處理等等。

3 檢查是否有數(shù)據(jù)待發(fā)送 tcp_data_snd_check。

 

如果該數(shù)據(jù)包中包含了數(shù)據(jù)的話

  1.         } else {  
  2.             int eaten = 0;  
  3.             int copied_early = 0;  
  4.                         /* 此數(shù)據(jù)包剛好是下一個(gè)讀取的數(shù)據(jù),并且用戶空間可存放下該數(shù)據(jù)包*/  
  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                          /* 如果該函數(shù)在進(jìn)程上下文中調(diào)用并且sock被用戶占用的話*/  
  13.                 if (tp->ucopy.task == current &&  
  14.                     sock_owned_by_user(sk) && !copied_early) {  
  15.                                         /* 進(jìn)程有可能被設(shè)置為TASK_INTERRUPTIBLE */  
  16.                     __set_current_state(TASK_RUNNING);  
  17.                                         /* 直接copy數(shù)據(jù)到用戶空間*/  
  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.                                 /* 進(jìn)入receive queue 排隊(duì),以待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.                         /* 數(shù)據(jù)包接收后續(xù)處理*/  
  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發(fā)送處理*/  
  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,表示數(shù)據(jù)直接copy到了用戶空間,這時(shí)無需提醒用戶進(jìn)程數(shù)據(jù)的到達(dá),否則需調(diào)用sk_data_ready來通知,因?yàn)榇藭r(shí)數(shù)據(jù)到達(dá)了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函數(shù)

[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.         /* 接收到了數(shù)據(jù),設(shè)置ACK需調(diào)度標(biāo)志*/  
  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.         /* 以下為根據(jù)接收間隔更新icsk_ack.ato,該值主要用于判斷pingpong模式見函數(shù)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.         /* 每次接收到來自對(duì)方的一個(gè)TCP數(shù)據(jù)報(bào),且數(shù)據(jù)報(bào)長度大于128字節(jié)時(shí),我們需要調(diào)用tcp_grow_window,增加rcv_ssthresh的值,一般每次為rcv_ssthresh增長兩倍的mss,增加的條件是rcv_ssthresh小于window_clamp,并且 rcv_ssthresh小于接收緩存剩余空間的3/4,同時(shí)tcp_memory_pressure沒有被置位(即接收緩存中的數(shù)據(jù)量沒有太大)。 tcp_grow_window中對(duì)新收到的skb的長度還有一些限制,并不總是增長rcv_ssthresh的值*/  
  43.     if (skb->len >= 128)  
  44.         tcp_grow_window(sk, skb);  
  45. }  

 

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

當(dāng)本地socket收到數(shù)據(jù)報(bào),并滿足一定條件時(shí),增長rcv_ssthresh的值,在下一次發(fā)送數(shù)據(jù)報(bào)組建TCP首部時(shí),

需要通告對(duì)方當(dāng)前的接收窗口大小,這時(shí)需要更新rcv_wnd,此時(shí)rcv_wnd的取值不能超過rcv_ssthresh的值。

兩者配合,達(dá)到一個(gè)滑動(dòng)窗口大小緩慢增長的效果。

__tcp_ack_snd_check用來判斷ACK的發(fā)送方式

[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. }  

 

注釋很清楚,無需解釋。

 

 

這里有個(gè)疑問,就是當(dāng)ucopy應(yīng)用讀到需要讀取到的數(shù)據(jù)包后,也即在一次處理中

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

 

 

的第二個(gè)條件的等號(hào)為真 len - tcp_header_len == tp->ucopy.len,然后執(zhí)行流程到后面eaten為1,所以函數(shù)以釋放skb結(jié)束,沒有

調(diào)用sk_data_ready函數(shù)。假設(shè)這個(gè)處理調(diào)用流程如下:

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

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

 

  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),所以還是會(huì)調(diào)用schedule_timeout

來等待。這點(diǎn)顯然是浪費(fèi)時(shí)間,所以這個(gè)condition應(yīng)該考慮下這個(gè)數(shù)據(jù)已經(jīng)讀滿的情況,而不能光靠觀察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.         /* 檢查到達(dá)的數(shù)據(jù)包 */  
  9.     res = tcp_validate_incoming(sk, skb, th, 1);  
  10.     if (res <= 0)  
  11.         return -res;  
  12.   
  13. step5:  /* 如果設(shè)置了ACK,則調(diào)用tcp_ack處理,后面再分析該函數(shù)*/  
  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函數(shù),在slow path處理前檢查輸入數(shù)據(jù)包的合法性。

  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丟棄數(shù)據(jù)包要滿足以下條件

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.

 

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

 


 

 

 

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

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

 


 

 第2步 檢查數(shù)據(jù)包的序號(hào)是否正確,該判斷失敗后調(diào)用tcp_send_dupack發(fā)送一個(gè)duplicate acknowledge(未設(shè)置RST標(biāo)志位時(shí))。

  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的更新時(shí)機(jī)(發(fā)送ACK時(shí)的tcp_select_window)可知位于序號(hào)rcv_wup前面的數(shù)據(jù)都已確認(rèn),所以待檢查數(shù)據(jù)包的結(jié)束序號(hào)至少

要大于該值;同時(shí)開始序號(hào)要落在接收窗口內(nèi)。

 

第3步 如果設(shè)置了RST,則調(diào)用tcp_reset處理

第4步 更新ts_recent,

第5步 檢查SYN,因?yàn)橹匕l(fā)的SYN和原來的SYN之間不會(huì)發(fā)送數(shù)據(jù),所以這2個(gè)SYN的序號(hào)是相同的,如果不滿足則reset連接。

 

接下來重點(diǎn)分析tcp_data_queue函數(shù),這里就是對(duì)數(shù)據(jù)包的處理了。

 

 

  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.         /* 沒有數(shù)據(jù)處理*/  
  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.         /* 如果收到對(duì)方發(fā)來的CWR,則本地TCP發(fā)送時(shí)不在設(shè)置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.     }  

 

 

如果該數(shù)據(jù)包剛好是下一個(gè)要接收的數(shù)據(jù),則可以直接copy到用戶空間(如果存在且可用),否則排隊(duì)到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.                        /* 這里的下半部開關(guān)的作用不解*/  
  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.                            /* 該函數(shù)用于判斷是否有接收緩存,在tcp內(nèi)存管理中將分析*/  
  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.                /* 到達(dá)的數(shù)據(jù)包喲可能填充了亂序隊(duì)列中的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.                        /*關(guān)閉乒乓模式,在quick計(jì)數(shù)沒消耗完時(shí)則可立即發(fā)送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.                /* 新數(shù)據(jù)到達(dá)導(dǎo)致返回給對(duì)方的SACK Block 調(diào)整*/  
  55.     if (tp->rx_opt.num_sacks)  
  56.         tcp_sack_remove(tp);  
  57.                /* 在當(dāng)前slow path,檢測是否可以進(jìn)入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. }  

 

下面看看函數(shù)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.                 /* 當(dāng)前hole未覆蓋,則處理結(jié)束*/  
  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.                 /* 該亂序數(shù)據(jù)包完全被到達(dá)的數(shù)據(jù)包覆蓋,則從亂序隊(duì)列中刪除之,并釋放該數(shù)據(jù)包*/  
  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被填充,取出該亂序數(shù)據(jù)包到receive queue中排隊(duì),并更新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. /* 該數(shù)據(jù)包的數(shù)據(jù)已經(jīng)完全存在,則發(fā)送DSACK,并進(jìn)入快速ACK模式,調(diào)度ACK發(fā)送*/    
  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.         /* 部分?jǐn)?shù)據(jù)已存在,則設(shè)置正確的DSACK,然后排隊(duì)到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是否設(shè)置 */  
  2.         /* 以下則把數(shù)據(jù)包排隊(duì)到失序隊(duì)列 */  
  3.         /* 同樣先判斷內(nèi)存是否滿足 */  
  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.         /* 該數(shù)據(jù)包是失序隊(duì)列的第一個(gè)數(shù)據(jù)包*/  
  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.                 /*剛好與失序隊(duì)列最后一個(gè)數(shù)據(jù)包數(shù)據(jù)銜接*/  
  32.         if (seq == TCP_SKB_CB(skb1)->end_seq) {  
  33.             __skb_queue_after(&tp->out_of_order_queue, skb1, skb);  
  34.                         /*如果沒有sack block或者當(dāng)前數(shù)據(jù)包開始序號(hào)不等于第一個(gè)block右邊界*/  
  35.             if (!tp->rx_opt.num_sacks ||  
  36.                 tp->selective_acks[0].end_seq != seq)  
  37.                 goto add_sack;  
  38.                         /*該數(shù)據(jù)包在某個(gè)hole后是按序到達(dá)的,所以可以直接擴(kuò)展第一個(gè)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.                 /* 該循環(huán)找到一個(gè)開始序號(hào)不大于該數(shù)據(jù)包開始序號(hào)的失序隊(duì)列中的數(shù)據(jù)包*/  
  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? 檢查與前個(gè)數(shù)據(jù)包是否有重疊*/  
  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.                 /* 排隊(duì)到失序隊(duì)列*/  
  70.         __skb_queue_after(&tp->out_of_order_queue, skb1, skb);  
  71.   
  72.         /* And clean segments covered by new one as whole. 檢測與后面的數(shù)據(jù)包重疊*/  
  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.                         /* 根據(jù)失序隊(duì)列的現(xiàn)狀更新SACK的blocks */  
  90.             tcp_sack_new_ofo_skb(sk, seq, end_seq);  
  91.     }  
  92. }  

 

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

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


只有注冊用戶登錄后才能發(fā)表評(píng)論。
網(wǎng)站導(dǎo)航: 博客園   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>
            翔田千里一区二区| 欧美激情一区二区三区全黄| 欧美激情中文字幕乱码免费| 亚洲欧美激情诱惑| 国产精品久久久久三级| 一区二区三区 在线观看视频| 亚洲高清不卡| 久久精品国产欧美亚洲人人爽| 国产精品国产三级国产专播精品人 | 亚洲全黄一级网站| 免费在线欧美黄色| 亚洲黄色精品| 亚洲福利视频免费观看| 麻豆精品传媒视频| 亚洲精品一区二区三| 欧美激情视频一区二区三区免费 | 午夜一级在线看亚洲| 99在线精品观看| 国产精品v欧美精品v日本精品动漫 | 亚洲最新色图| 国产精品乱看| 亚洲精品一区二区三区婷婷月| 亚洲国产色一区| 欧美精品一区在线播放| 亚洲精品一区二区三区蜜桃久| 亚洲精品免费一区二区三区| 国产精品hd| 欧美在线free| 久久美女性网| 亚洲免费观看视频| 99精品热6080yy久久| 国产日韩av在线播放| 久久久国产精品一区二区三区| 欧美一区二区三区成人| 在线免费精品视频| 亚洲精选在线| 国产亚洲一区在线| 亚洲国产乱码最新视频| 国产精品jvid在线观看蜜臀| 久久噜噜亚洲综合| 欧美精品v国产精品v日韩精品| 亚洲一区二区欧美日韩| 香蕉尹人综合在线观看| 亚洲美女av在线播放| 性欧美8khd高清极品| 亚洲东热激情| 国产精品99久久久久久久久| 亚洲成人影音| 国产精品99久久不卡二区| 极品尤物一区二区三区| 亚洲自拍另类| 亚洲影院污污.| 欧美激情视频一区二区三区不卡| 久久久久综合一区二区三区| 欧美亚洲成人网| 日韩午夜在线电影| a4yy欧美一区二区三区| 免费观看成人网| 欧美aⅴ99久久黑人专区| 韩国成人福利片在线播放| 校园春色国产精品| 欧美在线观看视频一区二区| 国产精品国产三级国产专区53| 亚洲日本欧美日韩高观看| 91久久精品国产91久久| 久久亚洲一区二区| 欧美国产日韩精品| 亚洲国产老妈| 欧美激情亚洲激情| 亚洲人成高清| 亚洲影院污污.| 国产精品免费在线| 亚洲午夜视频在线| 久久狠狠一本精品综合网| 国产精品一区二区你懂的| 亚洲欧美激情在线视频| 久久福利精品| 在线播放亚洲| 欧美激情按摩| 99视频在线观看一区三区| 亚洲一区二区三区中文字幕在线 | 国产自产精品| 久久精品国产69国产精品亚洲| 国产精品久久久久婷婷| 亚洲综合成人婷婷小说| 欧美亚洲尤物久久| 国产综合第一页| 免费看的黄色欧美网站| 亚洲精品在线观| 欧美一级片一区| 在线观看三级视频欧美| 欧美精品激情在线观看| 一区二区三区四区在线| 久久激情五月激情| 亚洲国产精品一区在线观看不卡| 欧美成人精品影院| 亚洲影院一区| 蜜桃伊人久久| 亚洲视频中文字幕| 好看的日韩视频| 欧美精品一区二区三区一线天视频| 一本综合久久| 免费看亚洲片| 亚洲视频一区二区在线观看| 国产亚洲一二三区| 欧美日韩精品久久| 欧美中文字幕久久| a4yy欧美一区二区三区| 久久亚洲一区二区三区四区| 亚洲精品一品区二品区三品区| 国产精品自拍视频| 欧美freesex8一10精品| 亚洲欧美成人综合| 亚洲精品久久7777| 看片网站欧美日韩| 亚洲一区三区视频在线观看| 伊人久久亚洲影院| 国产精品久久网| 欧美精品国产一区| 欧美专区日韩视频| 亚洲图色在线| 亚洲精品视频免费在线观看| 久久偷看各类wc女厕嘘嘘偷窃| 亚洲少妇诱惑| 亚洲精品久久久久久下一站| 国产一区二区精品丝袜| 欧美性一区二区| 欧美福利网址| 免费91麻豆精品国产自产在线观看| 在线一区二区日韩| 亚洲精品免费看| 欧美激情一区在线观看| 久久久www成人免费无遮挡大片 | 亚洲国产黄色片| 国产一区二区三区视频在线观看 | 欧美日韩国产首页在线观看| 麻豆久久精品| 开元免费观看欧美电视剧网站| 亚洲影院色在线观看免费| 日韩写真视频在线观看| 亚洲二区三区四区| 欧美成人中文字幕在线| 老司机精品视频网站| 久久久久久久久久久久久久一区| 午夜精品免费在线| 亚洲欧美国产不卡| 亚洲主播在线播放| 亚洲一区中文| 午夜亚洲福利| 欧美一区二区视频97| 久久国产88| 久久色中文字幕| 免费精品99久久国产综合精品| 久久夜色精品国产欧美乱极品| 久久久久.com| 久久久一本精品99久久精品66| 欧美一区三区三区高中清蜜桃| 香蕉av777xxx色综合一区| 亚洲宅男天堂在线观看无病毒| 亚洲综合精品四区| 性色av一区二区三区在线观看 | 亚洲精品一区二区网址 | 国产欧亚日韩视频| 狠狠综合久久av一区二区老牛| 黄色av成人| 亚洲区一区二区三区| 亚洲一区二区精品| 久久国产黑丝| 欧美+日本+国产+在线a∨观看| 亚洲第一综合天堂另类专| 亚洲人成人77777线观看| 这里是久久伊人| 久久国产精品99久久久久久老狼 | 99精品国产一区二区青青牛奶| 亚洲性视频网站| 久久久久久亚洲综合影院红桃| 欧美激情黄色片| 国产精品免费一区二区三区在线观看| 国产在线观看精品一区二区三区| 亚洲国产一区二区三区a毛片| 亚洲一区三区在线观看| 久久精品一区二区三区四区| 欧美激情影音先锋| 亚洲一区二区三区激情| 久久噜噜噜精品国产亚洲综合| 欧美国产日韩一区| 国产三级精品在线不卡| 亚洲美女视频在线观看| 久久国产视频网| 亚洲经典一区| 久久激情婷婷| 国产精品扒开腿爽爽爽视频| 狠狠色狠狠色综合系列| 一本久久综合亚洲鲁鲁| 久久综合九色综合欧美就去吻| 一本色道久久99精品综合 | 亚洲深夜福利视频| 嫩草成人www欧美| 国产一区二区三区精品欧美日韩一区二区三区 | 免费在线成人av|