ip_local_deliver负责分发IP分组传输的数据内容。基于IP的主要传输协议分别是UDP和TCP。处理函数分别是udp_rcv和tcp_rcv。
函数tcp_v4_rcv()定义在net/ipv4/tcp_ipv4.c中负责接收来自网络层的TCP数据包。相比UDP,TCP在内核中实现要困难得多,其状态转换如下图,珍藏多年的一张好图,高清图可以从网上 https://download.csdn.net/download/notbaron/10283521下载。
tcp报头定义在include/uapi/linux/tcp.h
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,
urg:1,
ack:1,
psh:1,
rst:1,
syn:1,
fin:1;
#else
#error "Adjust your
#endif
__be16 window;
__sum16 check;
__be16 urg_ptr;
};
tcp协议对象,位于net/ipv4/af_inet.c文件中
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 = 1,
.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数据包的函数,定义在net/ipv4/udp.c,该函数是__udp4_lib_rcv函数的包装函数。入参包含套接字缓冲区。会调用__udp4_lib_lookup_skb函数在udptable中找套接字,找到则调用udp_queue_rcv_skb函数。
udp包头文件定义在:include/uapi/linux/udp.h 文件中。
struct udphdr {
__be16 source;
__be16 dest;
__be16 len;
__sum16 check;
};
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函数,