编译器:gcc version 4.5.2 (Ubuntu/Linaro 4.5.2-8ubuntu4)
内核源码:linux-2.6.38.8.tar.bz2
struct sk_buff是Linux操作系统网络相关代码中最重要的结构体之一,用于管理已接收或正要传输的网络数据包。此结构体定义在include/linux/skbuff.h头文件中。
1、结构体成员
struct sk_buff *next;
struct sk_buff *prev;
内核通过一个双向链表来维护所有的sk_buff结构体,所以每个sk_buff结构体都使用next和prev这两个成员来实现与这个双向链表的联系。
ktime_t tstamp;
时间戳记,常用于表示数据包何时被接收。
struct sock *sk;
当数据在本地产生或者正由本地进程接收时,TCP或UDP以及用户程序就会使用sk这个指针。
struct net_device *dev;
当接收数据包时,驱动程序使用接收设备的结构体变量更新此成员;当发送一个数据包时,此成员代表的是发送数据包的设备。
char cb[48] __aligned(8);
控制缓冲区(controlbuffer),每一层都可以使用它来存储私有数据。
unsigned long _skb_refdst;
dst引用计数。
#ifdef CONFIG_XFRM
struct sec_path *sp;
#endif
由IPsec协议组使用,以记录转换信息。
unsigned int len,
data_len;
__u16 mac_len,
hdr_len;
len是缓冲区以及一些片段的总长度,data_len只是一些片段的长度,mac_len是MAC报头的长度,hdr_len是克隆skb时可写报头的长度。
union {
__wsum csum;
struct {
__u16 csum_start;
__u16 csum_offset;
};
};
校验和以及相关的状态标识。
__u32 priority;
此成员表示正被传输或转发的数据包QoS等级。
kmemcheck_bitfield_begin(flags1);
__u8 local_df:1,
cloned:1,
ip_summed:2,
nohdr:1,
nfctinfo:3;
__u8 pkt_type:3,
fclone:2,
ipvs_property:1,
peeked:1,
nf_trace:1;
kmemcheck_bitfield_end(flags1);
kmemcheck_bitfield_begin(flags2);
__u16 queue_mapping:16;
#ifdef CONFIG_IPV6_NDISC_NODETYPE
__u8 ndisc_nodetype:2,
deliver_no_wcard:1;
#else
__u8 deliver_no_wcard:1;
#endif
__u8 ooo_okay:1;
kmemcheck_bitfield_end(flags2);
各种标识(至于这些标识的用法,在以后用到时再加以说明)。
kmemcheck_bitfield_begin和kmemcheck_bitfield_end分别用于定义零长数组,以确定这些成员的起始和终止的位置。 它们的源代码如下所示:
/* linux-2.6.38.8/include/linux/kmemcheck.h */
#define kmemcheck_bitfield_begin(name) \
int name##_begin[0];
#define kmemcheck_bitfield_end(name) \
int name##_end[0];
使用零长数组实现此功能的类似代码如下:
#include <stdio.h>
struct test {
int len1;
char ch_start[0];
char ch1;
char ch2;
char ch_end[0];
int len2;
};
int main(void)
{
char *p;
int i;
struct test test_value = {
.ch1 = 'a',
.len1 = 1,
.len2 = 2,
.ch2 = 'b',
};
for (p = test_value.ch_start, i = 0;
p < test_value.ch_end; p++, i++)
printf("*(p + %d) = %c\n", i, *p);
return 0;
}
例子输出结果:
*(p + 0) = a
*(p + 1) = b
__be16 protocol;
由于每种协议都有自己处理接收数据包的处理函数,因此,驱动程序使用此成员来通知其上层该使用哪个处理函数。
void (*destructor)(struct sk_buff *skb);
当此缓冲区被删除时,用它来完成某些工作。
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
struct nf_conntrack *nfct;
#endif
#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED
struct sk_buff *nfct_reasm;
#endif
#ifdef CONFIG_BRIDGE_NETFILTER
struct nf_bridge_info *nf_bridge;
#endif
这些成员由netfilter相关代码所使用。
int skb_iif;
目的地网络设备的ifindex。
#ifdef CONFIG_NET_SCHED
__u16 tc_index; /* traffic control index */
#ifdef CONFIG_NET_CLS_ACT
__u16 tc_verd; /* traffic control verdict */
#endif
#endif
这些成员由流量控制功能所使用。
__u32 rxhash;
所接收的数据包的哈希表。
#ifdef CONFIG_NET_DMA
dma_cookie_t dma_cookie;
#endif
a cookie to one of several possible DMAoperations done by skb DMA functions.
#ifdef CONFIG_NETWORK_SECMARK
__u32 secmark;
#endif
数据包安全等级标记。
union {
__u32 mark;
__u32 dropcount;
};
通用数据包标记。
__u16 vlan_tci;
VLAN控制信息。
sk_buff_data_t transport_header;
sk_buff_data_t network_header;
sk_buff_data_t mac_header;
指向TCP/IP协议栈各协议报头的指针:transport_header指向传输层,network_header指向网络层,mac_header指向链路层。
sk_buff_data_t tail;
sk_buff_data_t end;
unsigned char *head,
*data;
这些成员代表缓冲区的边界以及其中的数据,head和end指向已分配缓冲区空间的开端和尾端,而data和tail则指向实际数据的开端和尾端,如下图(图片来自《Understanding Linux Network Internals》):
其中,headroom可插入一个协议报头,tailroom可填入其他新的数据。
unsigned int truesize;
此成员代表缓冲区总的大小,包括sk_buff结构本身。
atomic_t users;
引用计数,避免正在使用时被其它用户所释放掉。