sk_buff结构体是linux网络代码中最重要的数据结构,是整个网络传输载体。所以sk_buff结构体里面有很多关于其他功能的成员字段,比如:防火墙,子路由系统,多播等。这些字段并不是一定有的,只有在满足特点条件才有的。
sk_buff结构体是双链表结构,sk_buff结构体关联多个其他结构体:
第一是数据区:由sk_buff中head和end指向的数据块,用来存储sk_buff结构的数据也即是存储数据包的内容和各层协议头。
第二是分片结构:用来表示IP分片的一个结构体,实则上是和sk_buff结构的数据区相连的,即是end指针的下一个字节开始就是分片结构。也正是此原因,所以分片结构和sk_buff数据区内存分配及销毁时都是一起的。
第三是分片结构指向的数据区,即是IP分片内容。
数据包在各层间传输时,data指针的变化:sk_buff在各层间传输时,只改变指针和添加协议头,不拷贝也不删除协议头。
假设从收包开始:
(1)开始进入第二层时,这时data指针指向帧头。mac = data,然后操作mac指针已经数据包。当二层操作完后把包往三层传送时,会调用一个函数(具体什么函数后面会详细讲)让data指针指向三层的IP头;
(2)当包进入第三层时,这时data指针已经指向了IP头,让nh = data,然后操作nh指针已经数据包,当三层操作完后把包往四层传送时,同样调用一个函数把data指向四层的TCP头;同理,四层也是一样处理的,只移动指针,不删除协议头。发包时就相反了,只是变成了为每一层添加协议头了。下面是书上的图,供参考。
sk_buff_head结构体和sk_buff结构体关系:
sk_buff结构体数据区:
sk_buff结构体只是网络数据包中的一些配置,真正包含传输内容和传输协议的都是在sk_buff结构体中几个指针所指向的数据区中。这里先简称数据区,数据区的大小是:(skb->end - skb->head);对于每个数据包来说这个大小都是固定的,而且在传输过程中skb->end和skb->head所指向的地址都是不变的。这块数据区是用来存放应用层发下来的数据和各层的协议信息。但在计算数据长度或者操作协议信息时,一般都要和实际的数据存放指针为准。实际数据指针为data和tail,data指向实际数据开始的地方,tail指向实际数据结束的地方。
下面来看下sk_buff结构体中的指针和数据区关系:
(1)sk_buff结构数据区刚被申请好,此时head指针、data指针、tail指针都是指向同一个地方。记住前面讲过的:head指针和end指针指向的位置一直都不变,而对于数据的变化和协议信息的添加都是通过data指针和tail指针的改变来表现的。
(2)开始准备存储应用层下发过来的数据,通过调用函数skb_reserve()来使data指针和tail指针同时向下移动,空出一部分空间来为后期添加协议信息。
(3)开始存储数据了,通过调用函数skb_put()来使tail指针向下移动空出空间来添加数据,此时skb->data和skb->tail之间存放的都是数据信息,无协议信息。
(4)这时就开始调用函数skb_push()来使data指针向上移动,空出空间来添加各层协议信息。直到最后到达二层,添加完帧头然后就开始发包了。
另外一篇文章介绍sk_buff结构体讲解的特别详细
https://blog.csdn.net/shanshanpt/article/details/21024465#commentBox
IP数据包协议的讲解: