首页 > 代码库 > TCP/IP 协议数据收发流程

TCP/IP 协议数据收发流程

先弄清楚重要的数据结构

两个全局的变量 struct socket* socket[NR_SOCKET];  struct proto_ops* pops[NR_PROTOCOL];

bsd socket 层

struct socket *sock  /  struct proto_ops *ops 

===========================

inet 层  struct proto_ops inet_proto_ops = { inet_creat, inet_read, .... } 作为socket层的ops操作函数

struct sock* sk / struct proto *protocol  

===========================

tcp / udp / icmp 层

struct proto tcp_prot = { tcp_read, tcp_write, ..., ip_queue_transmit } 做为sock 的protocol 的操作函数, 在sock_create()时初始化绑定

strcut proto udp_prot

===========================

ip / arp 层

struct inet_protocol inet_protocol_base = { &tcp_protocol, &udp_protocol, &icmp_protocol } 在初始化的时候绑定到inet_protocol_base中

===========================

dev 层

struct packet_type ptype_base = {&ip_packet, &arp_packet}

 

初始化流程 sock_init() ->ddi_init() ->inet_proto_init() -> socket_register()

 

数据包的接收

数据包异步到达网卡, 网卡会做MAC地址检查是否是发往本机的数据包, 如果不是则丢弃, 如果是则发网卡中断信息, CPU 执行网卡驱动的中断程序ei_interrrupt()  读取网卡寄存器的状态等一系列工作, 调用ei_receive() 从网卡数据寄存器中读取数据并封装成 struct sk_buff的结构, 然后调用netrx_if() 将skb 数据包插入到名backlog的skb队列中skb_queue_tail(skb), 标记中断下半部,内核在适当的时候执行中断下半部的程序inet_bh().

在inet_bh()中, 循环读取backlog队列 dequeue_skb(backlog), 调用dev->type_transmit(skb), 获取packet的类型 0x800(IP_PACKET), 0x806(ARP_PACKET), 根据packet类型调用 struct packet_type ptype_base[type]的func() 来处理相应的数据包. 以IP packek为例,则调用ip_recv(skb)来处理, 调用address_check()如果数据包中的des ip不是发往本机的ip地址, 则转到ip_forward() 来处理数据转发处理; 否则判断数据包是否有做ip fragment处理, 如果有则做ip_defragment() 数据包的重组处理, 没有则判断上一层的协议类型(TCP, UDP, ICMP), 分别调用相应的协议处理程序 struct inet_protocol  inet_proto_base[type]->handler(), 对于tcp来说则是tcp_recv(skb),  根据tcp头部中的端口号, 找到tcp_array[]中的struct sock* sk, 根据sk中的状态做TCP的状态处理, 此时已经是established 则调用tcp_data(), 将skb插入到sk->rqueue队列中, 等待上层调用read/recv/recvfrom从接受队列rqueue中读取。 

调用顺序就是 ei_interrupt() -> ei_receive() -> netif_rx() -> inet_bh() -> packet_type_base[type]->func() (ip_recv) -> inet_proto_base[type]->handler() (tcp_recv) -> tcp_data()

 

数据包的发送

通过write()/send() 等系统调用到socket层 sock_write/sock_send 通过struct socket的 struct proto_ops的操作函数调到inet层的inet_write/inet_send, 然后通过struct sock中的struct proto 的操作函数, 对于tcp来说是tcp_write() 构造skb先调用ip_build_header()构造ip header, 然后构造tcp header, 根据tcp的状态做作态处理 调用到tcp_send_skb(skb) 根据mss的大小做tcp 分段处理, 调用struct proto中的queue_transmit即 ip_queue_transmit(skb) 检查数据包与MTU的大小来据顶是否要做分片处理, 是则转到ip_fragment(skb) 否则查找route_table来决定下一跳的mac 地址并构造dev->build_header生成以太网头部, 然后调用dev_queue_transmit(skb)  调用dev->hard_start_xmit 对应到设备驱动程序中的ei_start_xmit() 发送数据, 如果发送失败则将skb插入到dev->buff[3]中, 等待在inet_bh()中调用dev_transmit()->dev_queue_transmit() 来发送。

调用顺序 write()/send() -> sock_write()/sock_send() ->inet_write() / inet_send() -> tcp_write() -> tcp_send_skb() -> ip_queue_transmit() -> dev_queue_transmit() -> ei_start_xmit()

TCP/IP 协议数据收发流程