封包存储于struct sk_buff中,所有网络分层都会使用这个结构来储存其报头、有关用户数据的信息,以及协调其工作的其他内部信息。从第二层到第四层都会使用这个数据结构。L4附加一个报头传给L3,L3附加自己的报头传给L2,L2附加自己的报头传给L1转发出去。设备收到sk_buff之后,每层都只处理自己的报头,并将其呈交给上层。
内核使用一个双向链表来维护sk_buff,链表的开端使用了一个哑元元素sk_buff_head,如下图所示为sk_buff链表:
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之间填入新数据。
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:封包的安全级
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 |