在这几天的工作中总是或多或少的接触到了sk_buff结构体。后来我觉得这样时不时地学点sk_buff结构还不如干脆花段时间来研究下这个重要的结构体。所以我就学习了《深入理解linux网络技术内幕》有关sk_buff结构的介绍,这系列博文本来是我根据《深入理解linux网络技术内幕》学习整理而来的,可以算作是笔记吧。后来在看sk_buff克隆和拷贝时,又看了下《linux内核源码剖析:TCP/IP实现》。现在这博文是经过修改的,所以也加进了在《llinux内核源码剖析:TCP/IP实现》学习到的内容。大家也可以看看原文对sk_buff结构体的一些讲解,原文分别在第二章关键数据结构:套接字缓冲区:sk_buff结构和第三章:套接字缓存。如果没有电子书的,可以私信我留下邮箱地址。
我要先感谢下这两本书的作者以及译者,《深入理解linux网络技术内幕》是linux网络中的一本经典之作,对于学习linux网络来说是非常有用的,这是本全面宏观的介绍linux网络的书;《linux内核源码剖析:TCP/IP实现》我开始看的是电子书,这本书吸引我的地方是讲解的非常详细,而且是非常简洁,尤其是对各个知识点的讲解非常透彻。
下面开始正式的讲解sk_buff结构内容,至于sk_buff结构体的重要性以及历史背景之类的我就不过多废话了。
sk_buff结构体:
第一、内核中sk_buff结构体在各层协议之间传输不是用拷贝sk_buff结构体,而是通过增加协议头和移动指针来操作的。如果是从L4传输到L2,则是通过往sk_buff结构体中增加该层协议头来操作;如果是从L4到L2,则是通过移动sk_buff结构体中的data指针来实现,不会删除各层协议头。这样做是为了提高CPU的工作效率。
第二、sk_buff结构体中有很多条件编译,比如:
#ifdef CONFIG_BRIDGE_NETFILTER struct nf_bridge_info*nf_bridge; #endif
因为sk_buff结构体是linux网络代码中最重要的数据结构,是整个网络传输载体。所以sk_buff结构体
里面有很多关于其他功能的成员字段,比如:防火墙,子路由系统,多播等。这些字段并不是一定有的,只有在满足特点条件才有的。所以可以在需要的时候再去关心这些成员字段,现在我们只来讲解下一般的成员字段。
第三、下面就直接来看sk_buff结构体了。为了好理解结构中的一些成员字段,先把后面要讲的内容提前说下。sk_buff结构体关联多个其他结构体,第一是数据区:由sk_buff中head和end指向的数据块,用来存储sk_buff结构的数据也即是存储数据包的内容和各层协议头。第二是分片结构:用来表示IP分片的一个结构体,实则上是和sk_buff结构的数据区相连的,即是end指针的下一个字节开始就是分片结构。也正是此原因,所以分片结构和sk_buff数据区内存分配及销毁时都是一起的。第三个是分片结构指向的数据区,即是IP分片内容。下面开始看sk_buff结构体:
struct sk_buff {
/* These two members must be first. */ struct sk_buff struct sk_buff
*next; // 因为sk_buff结构体是双链*prev; // 这是指向前一个sk_buff结
表,所以有前驱后继。这是个指向后面的sk_buff结构体指针 构体指针
//老版本(2.6以前)应该还有个字段: sk_buff_head *list //struct sock ktime_t
*sk; // 指向拥有此缓冲的套接字tstamp; // 时间戳,表示这个skb的接*dev; // 表示一个网络设备,当skb为
即每个sk_buff结构都有个指针指向头节点 sock结构体,即:宿主传输控制模块
收到的时间,一般是在包从驱动中往二层发送的接口函数中设置
struct net_device
输出/输入时,dev表示要输出/输入到的设备
unsigned long _skb_dst; // 主要用于路由子系统,保存路由有关的东西
度,
char
cb[48]; // 保存每层的控制信息,每一len, // 表示数据区的长度(tail - data)
层的私有信息
unsigned int
与分片结构体数据区的长度之和。其实这个len中数据区长度是个有效长 // 因为不删除协议头,所以只计算有效协议头和包内容。如:当在L3时,不会计算L2的协议头长度。 志
cloned:1, // 为1表示该结构被克隆,
或者自己是个克隆的结构体;同理被克隆时,自身skb和克隆skb的cloned都要置1
ip_summed:2,
nohdr:1, // nohdr标识payload是否被
data_len; // 只表示分片结构体数据区mac_len, // mac报头的长度
hdr_len; // 用于clone时,表示clone
的长度,所以len = (tail - data) + data_len;
__u16
的skb的头长度
// 接下来是校验相关域,这里就不详细讲了。 __u32 __u8
priority; // 优先级,主要用于QOS local_df:1, // 是否可以本地切片的标
kmemcheck_bitfield_begin(flags1);
单独引用,不存在协议首部。 // 如果被引用,则决不能再修改协议首部,也不能通过skb->data来访问协议首部。
__u8
nfctinfo:3;
pkt_type:3, // 标记帧的类型 fclone:2, // 这个成员字段是克隆时ipvs_property:1, peeked:1, nf_trace:1;
protocol:16; // 这是包的协议类型,标
使用,表示克隆状态
__be16
识是IP包还是ARP包或者其他数据包。
kmemcheck_bitfield_end(flags1);
void (*destructor)(struct sk_buff *skb); // 这是析构函
数,后期在skb内存销毁时会用到 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
struct nf_conntrack struct sk_buff
*nfct; *nfct_reasm;
#endif
#ifdef CONFIG_BRIDGE_NETFILTER
struct nf_bridge_info *nf_bridge; int
iif; // 接受设备的index tc_index;
/* traffic control
#endif
#ifdef CONFIG_NET_SCHED
__u16 index */
#ifdef CONFIG_NET_CLS_ACT
__u16
tc_verd;
/* traffic control
verdict */ #endif #endif
kmemcheck_bitfield_begin(flags2); __u16 __u8
queue_mapping:16; ndisc_nodetype:2;
#ifdef CONFIG_IPV6_NDISC_NODETYPE #endif
kmemcheck_bitfield_end(flags2); /* 0/14 bit hole */ dma_cookie_t
dma_cookie;
#ifdef CONFIG_NET_DMA #endif
#ifdef CONFIG_NETWORK_SECMARK
__u32 __u32 __u16
secmark; mark; vlan_tci;
transport_header; // 指向四层帧
#endif
sk_buff_data_t
相关推荐: