网络驱动移植之sk_buff结构体及其相关操作函数(上)

时间:2021-05-01 11:02:43

    开发平台: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》):

 网络驱动移植之sk_buff结构体及其相关操作函数(上)

    其中,headroom可插入一个协议报头,tailroom可填入其他新的数据。 

	unsigned int		truesize;

    此成员代表缓冲区总的大小,包括sk_buff结构本身。 

	atomic_t		users;

    引用计数,避免正在使用时被其它用户所释放掉。