sk_buff数据结构详解

sk_buff 简介

封包存储于struct sk_buff中,所有网络分层都会使用这个结构来储存其报头、有关用户数据的信息,以及协调其工作的其他内部信息。从第二层到第四层都会使用这个数据结构。L4附加一个报头传给L3,L3附加自己的报头传给L2,L2附加自己的报头传给L1转发出去。设备收到sk_buff之后,每层都只处理自己的报头,并将其呈交给上层。

sk_buff数据结构

内核使用一个双向链表来维护sk_buff,链表的开端使用了一个哑元元素sk_buff_head,如下图所示为sk_buff链表:

sk_buff数据结构详解_第1张图片

  sk_buff_head以及sk_buff的数据结构如下:

struct sk_buff_head {
	/* These two members must be first. */
	struct sk_buff	*next;   //第一个元素
	struct sk_buff	*prev;   //最后一个元素
	__u32		qlen;        //表中元素的数目
	spinlock_t	lock;        //防止对表的并发访问
};


struct sk_buff {
	union {
		struct {
			/* These two members must be first. */
			struct sk_buff		*next;
			struct sk_buff		*prev;

			union {
				struct net_device	*dev;
				/* Some protocols might use this space to store information,
				 * while device pointer would be NULL.
				 * UDP receive path is one user.
				 */
				unsigned long		dev_scratch;
			};
		};
		struct rb_node		rbnode; /* used in netem, ip4 defrag, and tcp stack */
		struct list_head	list;
	};
	union {
		struct sock		*sk;
		int			ip_defrag_offset;
	};

	union {
		ktime_t		tstamp;
		u64		skb_mstamp_ns; /* earliest departure time */
	};
	/*
	 * This is the control buffer. It is free to use for every
	 * layer. Please put your private variables there. If you
	 * want to keep them across layers you have to do a skb_clone()
	 * first. This is owned by whoever has the skb queued ATM.
	 */
	char			cb[48] __aligned(8);
	union {
		struct {
			unsigned long	_skb_refdst;
			void		(*destructor)(struct sk_buff *skb);
		};
		struct list_head	tcp_tsorted_anchor;
	};

#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
	unsigned long		 _nfct;
#endif
	unsigned int		len,
				data_len;
	__u16			mac_len,
				hdr_len;
	/* Following fields are _not_ copied in __copy_skb_header()
	 * Note that queue_mapping is here mostly to fill a hole.
	 */
	__u16			queue_mapping;

/* if you move cloned around you also must adapt those constants */
#ifdef __BIG_ENDIAN_BITFIELD
#define CLONED_MASK	(1 << 7)
#else
#define CLONED_MASK	1
#endif
#define CLONED_OFFSET()		offsetof(struct sk_buff, __cloned_offset)
	__u8			__cloned_offset[0];
	__u8			cloned:1,
				nohdr:1,
				fclone:2,
				peeked:1,
				head_frag:1,
				pfmemalloc:1;
#ifdef CONFIG_SKB_EXTENSIONS
	__u8			active_extensions;
#endif
	/* fields enclosed in headers_start/headers_end are copied
	 * using a single memcpy() in __copy_skb_header()
	 */
	/* private: */
	__u32			headers_start[0];
	/* public: */

/* if you move pkt_type around you also must adapt those constants */
#ifdef __BIG_ENDIAN_BITFIELD
#define PKT_TYPE_MAX	(7 << 5)
#else
#define PKT_TYPE_MAX	7
#endif
#define PKT_TYPE_OFFSET()	offsetof(struct sk_buff, __pkt_type_offset)

	__u8			__pkt_type_offset[0];
	__u8			pkt_type:3;
	__u8			ignore_df:1;
	__u8			nf_trace:1;
	__u8			ip_summed:2;
	__u8			ooo_okay:1;

	__u8			l4_hash:1;
	__u8			sw_hash:1;
	__u8			wifi_acked_valid:1;
	__u8			wifi_acked:1;
	__u8			no_fcs:1;
	/* Indicates the inner headers are valid in the skbuff. */
	__u8			encapsulation:1;
	__u8			encap_hdr_csum:1;
	__u8			csum_valid:1;

#ifdef __BIG_ENDIAN_BITFIELD
#define PKT_VLAN_PRESENT_BIT	7
#else
#define PKT_VLAN_PRESENT_BIT	0
#endif
#define PKT_VLAN_PRESENT_OFFSET()	offsetof(struct sk_buff, __pkt_vlan_present_offset)
	__u8			__pkt_vlan_present_offset[0];
	__u8			vlan_present:1;
	__u8			csum_complete_sw:1;
	__u8			csum_level:2;
	__u8			csum_not_inet:1;
	__u8			dst_pending_confirm:1;
#ifdef CONFIG_IPV6_NDISC_NODETYPE
	__u8			ndisc_nodetype:2;
#endif

	__u8			ipvs_property:1;
	__u8			inner_protocol_type:1;
	__u8			remcsum_offload:1;
#ifdef CONFIG_NET_SWITCHDEV
	__u8			offload_fwd_mark:1;
	__u8			offload_l3_fwd_mark:1;
#endif
#ifdef CONFIG_NET_CLS_ACT
	__u8			tc_skip_classify:1;
	__u8			tc_at_ingress:1;
	__u8			tc_redirected:1;
	__u8			tc_from_ingress:1;
#endif
#ifdef CONFIG_TLS_DEVICE
	__u8			decrypted:1;
#endif

#ifdef CONFIG_NET_SCHED
	__u16			tc_index;	/* traffic control index */
#endif

	union {
		__wsum		csum;
		struct {
			__u16	csum_start;
			__u16	csum_offset;
		};
	};
	__u32			priority;
	int			skb_iif;
	__u32			hash;
	__be16			vlan_proto;
	__u16			vlan_tci;
#if defined(CONFIG_NET_RX_BUSY_POLL) || defined(CONFIG_XPS)
	union {
		unsigned int	napi_id;
		unsigned int	sender_cpu;
	};
#endif
#ifdef CONFIG_NETWORK_SECMARK
	__u32		secmark;
#endif

	union {
		__u32		mark;
		__u32		reserved_tailroom;
	};

	union {
		__be16		inner_protocol;
		__u8		inner_ipproto;
	};

	__u16			inner_transport_header;
	__u16			inner_network_header;
	__u16			inner_mac_header;

	__be16			protocol;
	__u16			transport_header;
	__u16			network_header;
	__u16			mac_header;

	/* private: */
	__u32			headers_end[0];
	/* public: */

	/* These elements must be at the end, see alloc_skb() for details.  */
	sk_buff_data_t		tail;
	sk_buff_data_t		end;
	unsigned char		*head,
				*data;
	unsigned int		truesize;
	refcount_t		users;

#ifdef CONFIG_SKB_EXTENSIONS
	/* only useable after checking ->active_extensions != 0 */
	struct skb_ext		*extensions;
#endif
};

sk_buff中的部分字段:

struct sock *sk:指向拥有此缓冲区的套接字sock数据结构。当数据在本地产生或者正由本地进程接受时,就需要这个指针,因为该数据以及套接字相关信息会由L4以及用户应用程序使用。当缓冲区只是被转发时,该指针就是NULL。

unsigned int len:指缓冲区中数据块的大小。当缓冲区从一个网络分层移往下一个网络分层时,其值会变化 

unsigned int data_len:只计算片段中数据的大小

unsigned int mac_len:mac头大小

atomic_t users:使用sk_buff的的实例数目,当值为0时释放掉此缓冲区

unsigned int truesize:代表此缓冲区总的大小,包括sk_buff结构本身。其值为len+sizeof(sk_buff)

unsigned char* head, unsigned char* end, unsigned char* data unsigned* tail:

head和end指向一根陪缓冲区空间的开端和尾端,data和tail指向实际数据的开端和尾端。每一层在head和data之间填上一个协议报头,或在tail和end之间填入新数据。

sk_buff数据结构详解_第2张图片

void (*destructor)(...):缓冲区的析构函数

struct timeval_stamp:用于表示封包何时被接收,由netif_rx函数用net_timestapmp设置。

struct net_device *dev:当收到一个封包,dev表示此接收接口的数据结构的指针。当传输一个封包,dev代表发送封包的设备。

struct net_device *input_dev:表示已被接受的封包来源的设备。

struct net_device *real_dev:代表与虚拟设备所关联的真实设备。

union {...}h、union {...}nh、union {...}mac:h针对L4、nh针对L3、,mac针对L2。

struct dst_entry dst:由路由子系统使用

char cb[40]:控制缓冲区,或说是私有信息的存储空间,维护每一层内部使用 

unsigned char cloned:表示另一个sk_buff缓冲区的克隆

__u32 priority:表示整备传输或转发的封包QoS等级。

unsigned short     protocol:L2层表示下一个较高层的协议

unsigned short security:封包的安全级

sk_buff API使用

alloc_skb 用于分配sk_buff数据结构以及数据缓冲区
dev_alloc_skb 在中断模式下的缓冲区分配,即原子操作的alloc_skb
kfree_skb 释放缓冲区
dev_kfree_skb 释放缓冲区,调用kfree_skb,当skb->users减为0才释放内存
skb_reserve 在缓冲区的头部预留一些空间,允许插入一个报头。此函数只是移动了data和tail指针
skb_put 将 tail  指针向数据区的末尾移动,增加了 len 字段的长度。
skb_push 将 data 指针向数据区的前端移动,增加 了len 字段的长度。
skb_pull 将 data 指针向数据区的末尾移动,减少了len 字段的长度。
skb_clone 克隆sk_buff数据结构,双方的clone位置1,user置1,数据缓冲区内的dataref递增
skb_share_check

检查引用计数skb->users,并且当users字段说明该缓冲区是共享时可以克隆该缓冲区        

skb_copy 拷贝sk_buff以及数据缓冲区
pskb_copy 只拷贝sk_buff

你可能感兴趣的:(Linux网络技术内幕,linux)