struct sk_buff是linux网络系统中的核心结构体,linux网络中的所有数据包的封装以及解封装都是在这个结构体的基础上进行。
sk_buff是Linux网络中最核心的结构体,它用来管理和控制接收或发送数据包的信息。各层协议都依赖于sk_buff而存在。
内核中sk_buff结构体在各层协议之间传输不是用拷贝sk_buff结构体,而是通过增加协议头和移动指针来操作的。
如果是从L4传输到L2,则是通过往sk_buff结构体中增加该层协议头来操作;
如果是从L4到L2,则是通过移动sk_buff结构体中的data指针来实现,不会删除各层协议头。
这样做是为了提高CPU的工作效率。
1 struct sk_buff_head
2 {
3 struct sk_buff *next;
4 struct sk_buff *prev;
5
6 __u32 qlen;
7 spinlock_t lock;
8 }
9
10
11 struct sk_buff
12 {
13 struct sk_buff *next;
14 struct sk_buff *prev;
15 struct sock *sock ; //struct sock是socket在网络层的表示,其中存放了网络层的信息
16
17 unsigned int len; //下面有介绍
18 unsigned int data_len; //下面有介绍
19 __u16 mac_len ; //数路链路层的头长度
20 __u16 hdr_len ; //writable header length of cloned skb
21 unsigned int truesize ; //socket buffer(套接字缓存区的大小)
22 atomic_t users ; //对当前的struct sk_buff结构体的引用次数;
23 __u32 priority ; //这个struct sk_buff结构体的优先级
24
25 sk_buff_data_t transport_header ; //传输层头部的偏移量
26 sk_buff_data_t network_header ; //网络层头部的偏移量
27 sk_buff_data_t mac_header ; //数据链路层头部的偏移量
28
29 char *data ; //socket buffer中数据的起始位置;
30 sk_buff_data_t tail ; //socket buffer中数据的结束位置;
31 char *head ; //socket buffer缓存区的起始位置;
32 sk_buffer_data_t end ; //socket buffer缓存区的终止位置;
33
34 struct net_device *dev; //将要发送struct sk_buff结构体的网络设备或struct sk_buff的接收
35 //网络设备
36 int iif; //网络设备的接口索引号;
37
38
39 struct timeval tstamp ; //用于存放接受的数据包的到达时间;
40
41 __u8 local_df : 1 , //allow local fragmentaion;
42 cloned : 1 , // head may be cloned
43 ;
44
45 __u8 pkt_type : 3 , //数据包的类型;
46 fclone : 2, // struct sk_buff clone status
47
48 }
1 /* include/linux/skbuff.h */
2 struct sk_buff {
3 union {
4 struct {
5 /* These two members must be first.
6 这两个域是用来连接相关的skb的(如果有分片的话,可以通过它们将分片链接到一起),sk_buff是双链表结构。
7 */
8 struct sk_buff *next; /*链表中的下一个skb*/
9 struct sk_buff *prev; /*链表中的上一个skb*/
10
11 union {
12 ktime_t tstamp; /*记录接受或者传输报文的时间戳*/
13 struct skb_mstamp skb_mstamp;
14 };
15 };
16 struct rb_node rbnode; /* 红黑树,used in netem, ip4 defrag, and tcp stack */
17 };
18
19 union {
20 struct sock *sk; /*指向报文所属的套接字指针*/
21 int ip_defrag_offset;
22 };
23
24 struct net_device *dev; /*记录接受或发送报文的网络设备*/
25
26 /*
27 * This is the control buffer. It is free to use for every
28 * layer. Please put your private variables there. If you
29 * want to keep them across layers you have to do a skb_clone()
30 * first. This is owned by whoever has the skb queued ATM.
31 */
32 char cb[48] __aligned(8); /*保存与协议相关的控制信息,每个协议可能独立使用这些信息*/
33
34 unsigned long _skb_refdst; /*主要用于路由子系统,保存路由相关的东西*/
35 void (*destructor)(struct sk_buff *skb);
36 #ifdef CONFIG_XFRM
37 struct sec_path *sp;
38 #endif
39 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
40 struct nf_conntrack *nfct;
41 #endif
42 #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
43 struct nf_bridge_info *nf_bridge;
44 #endif
45 unsigned int len,
46 /*整个数据区域的长度,
47 这里的len = length(实际线性数据,不包括头空间和尾空间) + length(非线性数据)
48 len = (tail - data) + data_len
49 这个len中数据区长度是个有效长度,因为不删除协议头,
50 所以只计算有效协议头和包内容。如:当在L3时,不会计算L2的协议头长度。*/
51 data_len; /*非线性数据,length(实际线性数据 = skb->len - skb->data_len)*/
52 __u16 mac_len, /*mac层报头的长度*/
53 hdr_len; /*用于clone时,表示clone的skb的头长度*/
54
55 /* Following fields are _not_ copied in __copy_skb_header()
56 * Note that queue_mapping is here mostly to fill a hole.
57 */
58 kmemcheck_bitfield_begin(flags1);
59 __u16 queue_mapping;
60
61 /* if you move cloned around you also must adapt those constants */
62 #ifdef __BIG_ENDIAN_BITFIELD
63 #define CLONED_MASK (1 << 7)
64 #else
65 #define CLONED_MASK 1
66 #endif
67 #define CLONED_OFFSET() offsetof(struct sk_buff, __cloned_offset)
68
69 __u8 __cloned_offset[0];
70 __u8 cloned:1,
71 nohdr:1,
72 fclone:2,
73 peeked:1,
74 head_frag:1,
75 xmit_more:1,
76 pfmemalloc:1;
77 kmemcheck_bitfield_end(flags1);
78
79 /* fields enclosed in headers_start/headers_end are copied
80 * using a single memcpy() in __copy_skb_header()
81 */
82 /* private: */
83 __u32 headers_start[0];
84 /* public: */
85
86 /* if you move pkt_type around you also must adapt those constants */
87 #ifdef __BIG_ENDIAN_BITFIELD
88 #define PKT_TYPE_MAX (7 << 5)
89 #else
90 #define PKT_TYPE_MAX 7
91 #endif
92 #define PKT_TYPE_OFFSET() offsetof(struct sk_buff, __pkt_type_offset)
93
94 __u8 __pkt_type_offset[0];
95 __u8 pkt_type:3; /*标记帧的类型*/
96 __u8 ignore_df:1;
97 __u8 nfctinfo:3;
98 __u8 nf_trace:1;
99
100 __u8 ip_summed:2;
101 __u8 ooo_okay:1;
102 __u8 l4_hash:1;
103 __u8 sw_hash:1;
104 __u8 wifi_acked_valid:1;
105 __u8 wifi_acked:1;
106 __u8 no_fcs:1;
107
108 /* Indicates the inner headers are valid in the skbuff. */
109 __u8 encapsulation:1;
110 __u8 encap_hdr_csum:1;
111 __u8 csum_valid:1;
112 __u8 csum_complete_sw:1;
113 __u8 csum_level:2;
114 __u8 csum_bad:1;
115 #ifdef CONFIG_IPV6_NDISC_NODETYPE
116 __u8 ndisc_nodetype:2;
117 #endif
118 __u8 ipvs_property:1;
119
120 __u8 inner_protocol_type:1;
121 __u8 remcsum_offload:1;
122 #ifdef CONFIG_NET_SWITCHDEV
123 __u8 offload_fwd_mark:1;
124 #endif
125 /* 2, 4 or 5 bit hole */
126
127 #ifdef CONFIG_NET_SCHED
128 __u16 tc_index; /* traffic control index */
129 #ifdef CONFIG_NET_CLS_ACT
130 __u16 tc_verd; /* traffic control verdict */
131 #endif
132 #endif
133
134 union {
135 __wsum csum;
136 struct {
137 __u16 csum_start;
138 __u16 csum_offset;
139 };
140 };
141 __u32 priority; /*优先级,主要用于QOS*/
142 int skb_iif; /*接收设备的index*/
143 __u32 hash;
144 __be16 vlan_proto;
145 __u16 vlan_tci;
146 #if defined(CONFIG_NET_RX_BUSY_POLL) || defined(CONFIG_XPS)
147 union {
148 unsigned int napi_id;
149 unsigned int sender_cpu;
150 };
151 #endif
152 #ifdef CONFIG_NETWORK_SECMARK
153 __u32 secmark;
154 #endif
155
156 union {
157 __u32 mark;
158 __u32 reserved_tailroom;
159 };
160
161 union {
162 __be16 inner_protocol;
163 __u8 inner_ipproto;
164 };
165
166 __u16 inner_transport_header;
167 __u16 inner_network_header;
168 __u16 inner_mac_header;
169
170 __be16 protocol; /*协议类型*/
171 __u16 transport_header; /*传输层头部的偏移量*/
172 __u16 network_header; /*网络层头部的偏移量*/
173 __u16 mac_header; /*数据链路层头部的偏移量*/
174
175 /* private: */
176 __u32 headers_end[0];
177 /* public: */
178
179 /* These elements must be at the end, see alloc_skb() for details. */
180 sk_buff_data_t tail; /*指向数据区中实际数据结束的位置*/
181 sk_buff_data_t end; /*指向数据区中结束的位置(非实际数据区域结束位置)*/
182
183 unsigned char *head, /* 指向数据区中开始的位置(非实际数据区域开始位置)*/
184 *data; /*指向数据区中实际数据开始的位置*/
185 unsigned int truesize;
186 atomic_t users; /*缓冲区总长度*/
187 };
struct sk_buff结构体中的pkt_type字段的取值为:
通过struct sk_buff中的pkt_type字段中的值,可以判断出接收到的数据包是不是发送给本机的数据包。
如果pkt_type == PACKET_HOST,说明收到的数据包是发送给本机的单播数据包
如果pkt_type == PACKET_BROADCAST,说明收到的数据包是发送给本机的广播数据包
如果pkt_type == PACKET_MULTICAST,说明收到的数据包是发送给本机的组播数据包
如果pkt_type == PACKET_OTHERHOST,说明收到的数据包不是发送给本机的数据包,需要转发出去。
1.1、重要的长度len的解析
(1)、线性数据:head - end
(2)、实际线性数据:data - tail,不包含线性数据中的头空间和尾空间。
skb->data_len: skb中的分片数据(非线性数据)的长度。
skb->len: skb中的数据块的总长度,数据块包括实际线性数据和非线性数据,非线性数据为data_len,所以skb->len= (data - tail) + data_len。
skb->truesize: skb的总长度,包括sk_buff结构和数据部分,skb=sk_buff控制信息 + 线性数据(包括头空间和尾空间) + skb_shared_info控制信息 + 非线性数据,
所以:skb->truesize = sizeof(struct sk_buff) + (head - end) + sizeof(struct skb_shared_info) + data_len。
1.2、sk_buff数据区
sk_buff结构体中的都是sk_buff的控制信息,是网络数据包的一些配置,真正储存数据的是sk_buff结构体中几个指针指向的数据区中,
线性数据区的大小 = (skb->end - skb->head),对于每个数据包来说这个大小都是固定不变的,
在传输过程中skb->end和skb->head所指向的地址都是不变的,这里要注意这个地址不是本机的地址,
如果是本机的地址那么数据包传到其他主机上这个地址就是无效的,所以这个地址是这个skb缓冲区的相对地址。
线性数据区是用来存放各层协议头部和应用层发下来的数据。各层协议头部相关信息放在线性数据区中。
实际数据指针为data和tail,data指向实际数据开始的地方,tail指向实际数据结束的地方。
用一张图来表示sk_buff和数据区的关系:
1.2、线性数据区
这一节介绍先行数据区在sk_buff创建过程中的变化,图中暂时省略了非线性数据区:
(1)、sk_buff结构数据区刚被申请好,此时 head 指针、data 指针、tail 指针都是指向同一个地方。记住前面讲过的:head 指针和 end 指针指向的位置一直都不变,而对于数据的变化和协议信息的添加都是通过 data 指针和 tail 指针的改变来表现的。
初始化sk_buff
(2)、开始准备存储应用层下发过来的数据,通过调用函数 skb_reserve(m) 来使 data 指针和 tail 指针同时向下移动,空出一部分空间来为后期添加协议信息。m一般为最大协议头长度,内核中定义。
初始定位skb_reserve(m)
(3)、开始存储数据了,通过调用函数 skb_put() 来使 tail 指针向下移动空出空间来添加数据,此时 skb->data 和 skb->tail 之间存放的都是数据信息,无协议信息。
储存应用层数据skb_put()
(4)、这时就开始调用函数 skb_push() 来使 data 指针向上移动,空出空间来添加各层协议信息,添加协议信息也是用skb_put()。直到最后到达二层,添加完帧头然后就开始发包了。
添加协议头
1.3、非线性数据区
1.2中所讲的都是线性数据区中的相关的配置,当线性数据区不够用的时候就会启用非线性数据区作为数据区域的扩展,skb中用skb_shared_info分片结构体来配置非线性数据。
skb_shared_info结构体是和skb中的线性数据区一体的,所以在skb的各种操作时都会把这两个结构看作是一个结构来操作。如:
(1)、当sk_buff结构的线性数据区申请和释放空间时,分片结构会跟着数据区一起分配和释放。
(2)、克隆skb时,sk_buff的线性数据区和分片结构都由分片结构中的dataref成员字段来标识是否被引用。
sk_buff与数据区的关系
从上图中可以看出来非线性数据区接到skb->end的位置后,skb->end的下一个字节就作为非线性数据区的开始。
end指针的下个字节可以作为分片结构的开始,获取end指针的位置要强行转成分片结构,内核中有定义好的宏:
1 #define skb_shinfo(SKB) ((struct skb_shared_info *)(skb_end_pointer(SKB)))
skb_shared_info结构:
1 /* include/linux/skbuff.h */
2 struct skb_shared_info {
3 unsigned char nr_frags; /*表示有多少分片结构*/
4 __u8 tx_flags;
5 unsigned short gso_size;
6 /* Warning: this field is not always filled in (UFO)! */
7 unsigned short gso_segs;
8 unsigned short gso_type;
9 struct sk_buff *frag_list; /*一种类型的分配数据*/
10 struct skb_shared_hwtstamps hwtstamps;
11 u32 tskey;
12 __be32 ip6_frag_id;
13
14 /*
15 * Warning : all fields before dataref are cleared in __alloc_skb()
16 */
17 atomic_t dataref; /*用于引用计数,克隆一个skb结构体时会增加一个引用计数*/
18
19 /* Intermediate layers must ensure that destructor_arg
20 * remains valid until skb destructor */
21 void * destructor_arg;
22
23 /* must be last field, see pskb_expand_head() */
24 skb_frag_t frags[MAX_SKB_FRAGS]; /*保存分页数据,skb->data_len = 所有数组数据长度之和*/
25 };
非线性数据区有两种不同的构成数据的方式
(1)用数组存储的分片数据区,采用是是结构体中的frags[MAX_SKB_FRAGS]
对于frags[]一般用在当数据比较多,在线性数据区装不下的时候,skb_frag_t中是一页一页的数据,skb_frag_struct结构体如下:
1 /* include/linux/skbuff.h */
2 typedef struct skb_frag_struct skb_frag_t;
3
4 struct skb_frag_struct {
5 struct {
6 struct page *p; /*指向分片数据区的指针,类似于sk_buff中的data指针*/
7 } page;
8 #if (BITS_PER_LONG > 32) || (PAGE_SIZE >= 65536)
9 __u32 page_offset;
10 __u32 size;
11 #else
12 __u16 page_offset; /*偏移量,表示相对开始位置的页偏移量*/
13 __u16 size; /*page中的数据长度*/
14 #endif
15 };
下图显示了frags是怎么分配分片数据的:
数组存储分片指针类型
rag_list指针来指向的分片数据类型