1、struct sk_buff结构体解析
struct sk_buff:一个封包就存储在这里。所有网络分层都会使用这个结构来储存其报头、有关用户数据的信息(有效载荷),以及用来协调其工作的其他内部信息。
struct net_device:在Linux内核中每种网络设备都用这个数据结构表示,包括软硬件的配置信息。
struct sk_buff定义在#include <linux/skbuff.h>
struct sk_buff {
union {
struct {
/* These two members must be first. */
struct sk_buff *next; /* 下一个struct sk_buff结构 */
struct sk_buff *prev; /* 前一个struct sk_buff结构 */
union {
/*报文到达或者离开的时间戳; Time we arrived 表示这个skb的接收到的时间, 一般是在包从驱动中往二层发送的接口函数中设置 */
ktime_t tstamp; /* 通常只对一个已接收的封包才有意义。时间戳标记,用于表示封包何时被接收,或者有时用于表示封包预定传输的时间。此字段由netif_rx函数用net_timestamp设置,该函数在接收每个封包之后由设备驱动程序调用 */
struct skb_mstamp skb_mstamp;
};
};
struct rb_node rbnode; /* used in netem & tcp stack */
};
struct sock *sk; /* 用户存储套接字的网络信息,该包属于哪个socket */
struct net_device *dev; /* 收到这个包的网络设备的信息 */
/*
* 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);
unsigned long _skb_refdst;
/* 当此缓冲区被删除时,可以完成某些工作。当此缓冲区不属于一个套接字时,destructor通常不会被初始化。当此缓冲区属于一个套接字时,一般都是设置为sock_rfree或者sock_wfree。 */
void (*destructor)(struct sk_buff *skb);
#ifdef CONFIG_XFRM
struct sec_path *sp;
#endif
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
struct nf_conntrack *nfct;
#endif
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
struct nf_bridge_info *nf_bridge;
#endif
unsigned int len, /* 分片数据区和连续数据区的总长度 */
data_len; /* 分片结构体数据区的总长度 */
__u16 mac_len, /* MAC报头的长度 */
hdr_len; /* 用于clone的时候,它表示clone的skb的头的长度 */
/* Following fields are _not_ copied in __copy_skb_header()
* Note that queue_mapping is here mostly to fill a hole.
*/
kmemcheck_bitfield_begin(flags1);
__u16 queue_mapping; /* 多队列的设备应用,也就是说映射到哪个队列 */
__u8 cloned:1,
nohdr:1,
fclone:2,
peeked:1,
head_frag:1,
xmit_more:1;
/* one bit hole */
kmemcheck_bitfield_end(flags1);
/* 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 pfmemalloc:1;
__u8 ignore_df:1;
__u8 nfctinfo:3;
__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;
__u8 csum_complete_sw:1;
__u8 csum_level:2;
__u8 csum_bad:1;
#ifdef CONFIG_IPV6_NDISC_NODETYPE
__u8 ndisc_nodetype:2;
#endif
__u8 ipvs_property:1;
__u8 inner_protocol_type:1;
__u8 remcsum_offload:1;
/* 3 or 5 bit hole */
#ifdef CONFIG_NET_SCHED
__u16 tc_index; /* traffic control index */
#ifdef CONFIG_NET_CLS_ACT
__u16 tc_verd; /* traffic control verdict */
#endif
#endif
union {
__wsum csum;
struct {
__u16 csum_start;
__u16 csum_offset;
};
};
__u32 priority; /* 优先级,主要用于QOS */
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; /* 实际数据区的起始位置 */
/* 这些字段代表缓冲区的边界以及其中的数据。当每一分层为其工作而准备缓冲区时,可能会为一个报头或更多的数据分配更多的空间。head和end指向已分配缓冲区空间的开端和尾端,而data和tail则指向实际数据的开端和尾端。可以在head和data之间的空隙中填上一个协议报文头 */
/* */
unsigned int truesize;
/* 这个是在缓冲区中实例化的计数。其作用就是在销毁skb结构体时,先查看users是否为0,若不为0,则调用函数递减下引用计数users即可;当销毁时,users为0才真正释放内存空间。有两个操作函数:atomic_inc()计数增加1;atomic_dec()计数jian'qu */
atomic_t users;
};
2、struct sk_buff结构体的管理函数
skb_put之前和之后
skb_push之前和之后
skb_pull
skb_reserver
分配内存:alloc_skb和dev_alloc_skb