TCP/IP源码学习(55)——skb接收层次间的关键结构

740阅读 0评论2013-11-08 491357604
分类:LINUX

作者:gfree.wind@gmail.com
博客:blog.focus-linux.net   linuxfocus.blog.chinaunix.net
 
 
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
======================================================================================================
众所周知,OSI的网络结构虽然被定义为7层,然后应用广泛的TCP/IP却只有四层或五层——关于TCP/IP是四层,还是五层,各有说法。

那么对于Linux Kernel来说,我个人认为四层的划分比较合适,分别Driver Interface Layer,Internet Layer,Transport Layer,和 Socket Application Layer。那么skb数据包是如何在各层次之间进行上下流动,传递的呢。以前我画过一个简单发送流程图,其流程还是比较简单的。基本上就是socket-》指定协议的发送函数-》IP层的发送函数-》路由-》Neighbour-》网卡。

今天看一下skb数据包如何从下向上传递,其层次间的关键结构

需要说明的是:
1. skb的起点以net_receive_skb为开始;
2. 这个流程图省略了很多中间的步骤和检查,如netfilter,等;

下面是流程说明:
1. 进入net_receive_skb,这时skb刚刚从网卡的缓存中读取出来,也就是说刚刚有了一个skb;
2. 根据L2层的数据包格式,得到L3层的数据包类型,如IPv4,IPv6或者x.25等;
3. 根据L2的类型,从ptype_base中得到对应L3类型,如ip_packet_type;
4. ip_packet_type的成员函数func为处理IP包的handler,即ip_rcv;
5. 查找路由,根据路由结果dst,通过dst->input得到接收包的处理函数即ip_local_deliver;
6. 根据L3的类型,得到L4的协议类型,从inet_protos得到对应L4类型,如udp_protocol;
7. L4类型有自己的处理函数,对应udp_protocol来说,即udp_rcv;
8. 对应UDP或者TCP,都是维护了该协议的socket的hash表,通过五元组信息tuple,找到对应的socket。

下面看一下这一流程中,衔接各层次间的关键结构:
1. ptype_base是一个struct list_head的数组;
2. ip_packet_type:

  1. static struct packet_type ip_packet_type __read_mostly = {
  2.     .type = cpu_to_be16(ETH_P_IP), //3层类型
  3.     .func = ip_rcv,  //3层的接收函数
  4.     //下面这三个函数无需关心,为对网卡feature的一种支持
  5.     .gso_send_check = inet_gso_send_check,
  6.     .gso_segment = inet_gso_segment,
  7.     .gro_receive = inet_gro_receive,
  8.     .gro_complete = inet_gro_complete,
  9. };
3. inet_protos为一个struct net_protocol类型的数组
4. 以udp_protocol为例

  1. static const struct net_protocol udp_protocol = {
  2.     .handler = udp_rcv, //4层dup的接收报文入口
  3.     .err_handler = udp_err,
  4.     .gso_send_check = udp4_ufo_send_check,
  5.     .gso_segment = udp4_ufo_fragment,
  6.     .no_policy = 1,
  7.     .netns_ok = 1,
  8. };

上面基本就是skb接收过程中,连接各层次的关键结构。不知道大家有没有想过,为什么ptype_base是使用链表,而inet_protos使用的是数组。

我认为是因为对于L2层来说,要支持的L3层协议类型,由于历史原因并没有得到统一的定义。这样造成了类型的数值有重合。所以只能使用链表来实现。而对于inet_protos来说,作为IP协议的L4协议容器。IP协议对于自己的L4协议类型,自然可以保证其值不冲突。因此一个普通数组即可完成任务。

上一篇:TCP/IP源码学习(54)——TCP的连接过程的实现(3)
下一篇:TCP/IP源码学习(51)——socket与VFS的write关联