声明:写这篇可有可无的文章的起因是:我对 sk_buff 结构体的 一些 具体操作的困惑,一直到再看到 Understanding Linux Network Internals 的19章,才恍然大悟。因此记下来,本文并不代表什么,只是释惑而已。
sk_buff穿越链路层时的包头结构的变化
困惑:以前在看书时,一看到下面这幅图,就以为sk_buff的结构都会填充好了,对head和end指针的操作也是不甚了解。
一直以为在sk_buff穿越网络协议栈时,只有data在变,而h、nh、mac包头是在初始化分配时就确定的。data在变,我是明白的,因为data就是协议栈每一层所处理面对的对象,而每一层的对象在变(从MAC帧到IP包等),所以data的指向就会不断的变化,是相对的量 ;而 h、nh、mac包头是绝对的量,指向的包头都是确定唯一的。所以我一直不明白一句话——“h和nh说指向的数据头可能会是一样的 ”。
最近在看 Understanding Linux Network Internals 这本书,才明白了关于sk_buff在穿越TCP/IP协议栈时的变化操作。为了便于说明问题,我就以穿越链路层为例吧。
下面这幅图,也是很清晰的描述了data的变化情况。
(a)Before:此时在链路层的sk_buff,data和mac都是指向链路层帧头的,这点毫无疑问。
(b)After:在到达IP层后,data转而指向IP头,而此时nh也隆重出场,也同时指向了IP包头。所以说,nh并不是一开始就是赋好值,指向IP包头的 。但,此先的nh有没有赋给值,还是为NULL,这个问题可以看下图。
这个图,就十分清楚了。由此可见,nh是有赋值,或者是nh不为空。至于赋的值,我大胆猜测一下(没看源码),就是mac值。
由此,我们得出一个结论:如果sk_buff在TCP/IP协议栈的某一层(处理层),那么上层所有的包头指针都指向当前的处理层。 比如,如果sk_buff在链路层,那么nh和h就指向mac头;如果sk_buff在IP层,那么h就会指向IP层。至于为什么这样处理,我想是因为,处理数据包就是要层层剥开各种包头,每一层都有各自的函数处理,所以sk_buff各种包头的指针就是层层赋值的。如果一下子就把各层的包头指针一次搞定,这就违背了分层简化处理的思想。
这样一来, “h和nh说指向的数据头可能会是一样的 ”这句话就得到了解释。
另外,对于head和end,可以看到,它们之间的空间是分配好的了,一般情况都是足够使用。在数据包向上层递交时,随着各种包头的剥离,headroom是在不断的收留那些被剥离的数据包头;而在数据包向下发送时,随着各种数据包头的添加,tailroom使在不断的腾出空间,以供使用。