ip_local_deliver負責分發IP分組傳輸的資料内容。基于IP的主要傳輸協定分别是UDP和TCP。處理函數分别是udp_rcv和tcp_rcv。
函數tcp_v4_rcv()定義在net/ipv4/tcp_ipv4.c中負責接收來自網絡層的TCP資料包。相比UDP,TCP在核心中實作要困難得多,其狀态轉換如下圖,珍藏多年的一張好圖,高清圖可以從網上下載下傳。
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 <asm/byteorder.h> 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->sk_state == TCP_LISTEN,調用tcp_v4_do_rcv,進入TCP狀态自動機。
代碼流程圖如下:
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;
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>。