开发平台:Ubuntu11.04
编译器: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;
引用计数,避免正在使用时被其它用户所释放掉。