歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

探討Linux kernel中對序列號超前的ACK包的處理

在開發的內核模塊中遇到這樣一個問題:一個數據包有多個請求,每次只讓服務器處理一個請求,所以在將請求交到上層的時候需要拆包,只將部分數據交到上層。為了防止客戶端重傳數據包,要預先給客戶端發送一個對完整數據包的確認。這樣就會造成一個問題,客戶端發送的ACK包的序列號,會比協議棧中期望的序列號大。

假設完整數據包的起始序列號分別為1883458390、1883458821,上層協議棧拿到的數據包的起始序列號為1883458390、1883458476,這時服務器端sock結構的rcv_nxt應該為1883458476,但是由於我們事先多發送了一個ACK,所以這時客戶端的ACK包的序列號為1883458821,而不是1883458476。下面是抓包的部分截圖,根據抓包情況來看,這樣的ACK包,內核接受了這樣的確認包,如圖所示(客戶端:192.168.9.188;服務器端:192.168.9.191):

OK,現在我們開始來看看內核中是如何處理這樣的數據包,為什麼會接受這樣的ACK包。

TCP協議的接收函數為tcp_v4_rcv(),該函數SKB進行必要的檢查,如數據的長度、校驗和初始化等,初始化TCP控制塊中的值;接著會調用__inet_lookup_skb()函數在tcp_hashinfo散列表中來查找是否存在對應的傳輸控制塊。如果找到,則調用tcp_v4_do_rcv()來處理(這裡我們忽略了Netfilter、IPSec、DMA等細節,這些跟我們探討的內容無關),基本的代碼流程如下圖所示:

我們的ACK包的校驗和、首部長度是沒有問題,所以它可以輕松的通過tcp_v4_rcv()的檢查,接下來看tcp_v4_do_rcv()中處理。tcp_v4_do_rcv()中會首先檢查SKB包對應的sock結構的狀態,如果是ESTABLISHED狀態,則會調用tcp_rcv_established()來處理,代碼片段如下:

int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
{
    ......

 if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */
  TCP_CHECK_TIMER(sk);
  if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) {
   rsk = sk;
   goto reset;
  }
  TCP_CHECK_TIMER(sk);
  return 0;
 }
 
 ......
}

Copyright © Linux教程網 All Rights Reserved