天天看點

Linux協定棧(8)——傳輸層實作

ip_local_deliver負責分發IP分組傳輸的資料内容。基于IP的主要傳輸協定分别是UDP和TCP。處理函數分别是udp_rcv和tcp_rcv。

函數tcp_v4_rcv()定義在net/ipv4/tcp_ipv4.c中負責接收來自網絡層的TCP資料包。相比UDP,TCP在核心中實作要困難得多,其狀态轉換如下圖,珍藏多年的一張好圖,高清圖可以從網上下載下傳。

Linux協定棧(8)——傳輸層實作

  tcp報頭定義在<b>include/uapi/linux/tcp.h</b>

struct tcphdr {         

        __be16  source; 

        __be16  dest;   

        __be32  seq;    

        __be32  ack_seq;

#if defined(__LITTLE_ENDIAN_BITFIELD)

        __u16   res1:4, 

                doff:4, 

                fin:1,  

                syn:1,

                rst:1,

                psh:1,  

                ack:1,

                urg:1,  

                ece:1,

                cwr:1;  

#elif defined(__BIG_ENDIAN_BITFIELD)

        __u16   doff:4,

                res1:4, 

                cwr:1,  

                ece:1,  

                ack:1,  

                rst:1,  

                fin:1;  

#else 

#error  "Adjust your &lt;asm/byteorder.h&gt; defines"

#endif                  

        __be16  window; 

        __sum16 check;  

        __be16  urg_ptr;

};

tcp協定對象,位于<b>net/ipv4/af_inet.c</b><b>檔案中</b>

static struct net_protocol tcp_protocol = {

        .early_demux    =      

tcp_v4_early_demux,

        .early_demux_handler =  tcp_v4_early_demux,

        .handler        =      

tcp_v4_rcv,

        .err_handler    =      

tcp_v4_err,

        .no_policy      =      

1,  

        .netns_ok       =      

        .icmp_strict_tag_validation

= 1,

 在inet_init(net/ipv4/af_inet.c)函數中調用inet_add_protocol(net/ipv4/protocol.c)函數來添加協定。

  tcp定時器定義在net/ipv4/tcp_timer.c中,有四個定時器:重傳定時器、延遲确認定時器、存活定時器、零視窗探測定時器。

TCP的接收入口是tcp_v4_rcv,

調用__inet_lookup_skb函數,該函數會調用__inet_lookup,調用

__inet_lookup_established函數,檢測套接字,沒有套接字就調用__inet_lookup_listener.

  根據連結的狀态,如果sk-&gt;sk_state == TCP_LISTEN,調用tcp_v4_do_rcv,進入TCP狀态自動機。

  代碼流程圖如下:

Linux協定棧(8)——傳輸層實作

  udp_rcv是處理UDP資料包的函數,定義在<b>net/ipv4/udp.c</b>,該函數是__udp4_lib_rcv函數的包裝函數。入參包含套接字緩沖區。會調用__udp4_lib_lookup_skb函數在udptable中找套接字,找到則調用udp_queue_rcv_skb函數。

            udp標頭檔案定義在:<b>include/uapi/linux/udp.h</b>

<b>檔案中。</b>

struct udphdr {

        __be16  source;

        __be16  dest;

        __be16  len;

Linux協定棧(8)——傳輸層實作

  tcp分組的發送從tcp_sendmsg函數調用開始。tcp_sendmsg(net/ipv4/tcp.c)函數會首先檢查已經建立的

TCP connection 的狀态,然後擷取該連接配接的MSS,開始發送流程。

  構造 TCP 段的 playload:它在核心空間中建立該

packet 的 sk_buffer 資料結構的執行個體 skb,從

使用者空間的 buffer 中拷貝 packet 的資料到 skb 的

buffer。構造 TCP header。

  計算 TCP 校驗和(checksum)和 順序号 (sequence number)。TCP的校驗和是必需的。然後發到

IP 層處理:調用 IP handler 句柄 ip_queue_xmit,将 skb 傳入 IP 處理流程。

  SOCK_STREAM類socket的TCP層操作函數集執行個體為tcp_prot定義在net/ipv4/tcp_ipv4.c檔案中。之後調用tcp_write_xmit()來把sock發送隊列中的skb盡量地發送出去。

傳輸層協定inet_sendmsg的proto指向的操作也不一樣,而對于TCP協定,inet_sendmsg指向tcp_sendmsg函數,

對于UDP的socket來說,sendto調用,真正去做工作的是udp_sendmsg這個函數,定義在<b>net/ipv4/udp.c檔案中</b>。

繼續閱讀