套接字缓存之sk_buff结构
sk_buff结构用来描述已接收或者待发送的数据报文信息;skb在不同网络协议层之间传递,可被用于不同网络协议,如二层的以太网协议,三层的ip协议,四层的tcp或者udp协议,其中某些成员变量会在该结构从一层向另一层传递时发生改变,从上层向下层传递需要添加首部,从下层向上层传递需要移除首部;
多个skb通过sk_buff_head表头部结构的next和prev指针连接成双向链表;头部还包含了链表中skb节点的总数量;
1 /* skb头结构 */ 2 struct sk_buff_head { 3 /* These two members must be first. */ 4 /* 通过下面两个指针成员将skb连接成双向链表 */ 5 struct sk_buff *next; /* 指向后一个skb */ 6 struct sk_buff *prev; /* 指向前一个skb */ 7 8 __u32 qlen; /* 链表中元素个数 */ 9 spinlock_t lock; /* 自旋锁 */ 10 };
下面为具体的sk_buff结构,部分成员加了注释,在后续代码阅读过程中会持续更新该结构注释;
1 /* skb结构 */ 2 struct sk_buff { 3 union { 4 struct { 5 /* These two members must be first. */ 6 struct sk_buff *next; 7 struct sk_buff *prev; 8 9 /* 报文到达或者离开的时间戳 */ 10 union { 11 ktime_t tstamp; 12 struct skb_mstamp skb_mstamp; 13 }; 14 }; 15 /* 红黑树的节点,用在netem和tcp协议栈 */ 16 struct rb_node rbnode; /* used in netem & tcp stack */ 17 }; 18 19 /* 20 指向缓冲区的套接字sock数据结构。当数据在本地产生或者正由本地进程接收时, 21 该数据以及套接字相关信息会被L4(tcp或者udp)以及用户应用程序使用 22 当缓冲区只是被转发时(本地机器不是来源也不是目的地),该指针为NULL 23 */ 24 struct sock *sk; 25 26 union { 27 /* 报文到达或者离开时的网络设备 */ 28 struct net_device *dev; 29 /* Some protocols might use this space to store information, 30 * while device pointer would be NULL. 31 * UDP receive path is one user. 32 */ 33 unsigned long dev_scratch; 34 }; 35 /* 36 * This is the control buffer. It is free to use for every 37 * layer. Please put your private variables there. If you 38 * want to keep them across layers you have to do a skb_clone() 39 * first. This is owned by whoever has the skb queued ATM. 40 */ 41 /* 42 控制缓冲区,用于存储私有信息,每层协议自己维护并使用, 43 并且只在本层有有效 44 */ 45 char cb[48] __aligned(8); 46 47 /* 路由缓存,输入或者输出报文都要查询到目的路由缓存项,才能确定流向 */ 48 unsigned long _skb_refdst; 49 50 /* 51 当缓冲区被删除时,可以完成某些清理工作 52 当缓冲区不属于一个套接字时,该函数通常不被初始化 53 属于一个套接字时,通常设置为sock_rfree或sock_wfree 54 sock_xxx函数用于更新套接字队列中所持有的内存 55 */ 56 void (*destructor)(struct sk_buff *skb); 57 #ifdef CONFIG_XFRM 58 /* ipsec用于跟踪传输信息 */ 59 struct sec_path *sp; 60 #endif 61 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) 62 /* 连接跟踪 */ 63 unsigned long _nfct; 64 #endif 65 #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) 66 /* 桥接帧的相关信息 */ 67 struct nf_bridge_info *nf_bridge; 68 #endif 69 /* 70 缓冲区的数据区块大小,该长度包括主缓冲区(head指针指向)的数据 71 以及一些片段(fragment)的数据,当缓冲区从一个网络分层移动到下一个 72 网络分层时,该值会发生变化,因为在协议栈中向上层移动时报头会被丢弃 73 向下层移动时报头会添加,len也会把协议报头算在内,与"数据预留和对齐"操作 74 */ 75 unsigned int len, 76 /* 片段(fragment)中的数据大小 */ 77 data_len; 78 /* mac报头大小 */ 79 __u16 mac_len, 80 /* 克隆skb时可写报文头部长度 */ 81 hdr_len; 82 83 /* Following fields are _not_ copied in __copy_skb_header() 84 * Note that queue_mapping is here mostly to fill a hole. 85 */ 86 kmemcheck_bitfield_begin(flags1); 87 /* 多设备的队列映射 */ 88 __u16 queue_mapping; 89 90 /* if you move cloned around you also must adapt those constants */ 91 #ifdef __BIG_ENDIAN_BITFIELD 92 #define CLONED_MASK (1 << 7) 93 #else 94 #define CLONED_MASK 1 95 #endif 96 #define CLONED_OFFSET() offsetof(struct sk_buff, __cloned_offset) 97 98 __u8 __cloned_offset[0]; 99 /* 表示该skb是另外一个skb的克隆 */ 100 __u8 cloned:1, 101 /* 102 payload是否被单独引用,不存在协议首部,如果被引用,则不能修改协议首部, 103 也不能通过skb->data来访问协议首部 104 */ 105 nohdr:1, 106 /* 107 当前克隆状态 108 SKB_FCLONE_UNAVAILABLE-skb未被克隆 109 SKB_FCLONE_ORIG-在skbuff_fclone_cache分配的父skb,可以被克隆 110 SKB_FCLONE_CLONE-在skbuff_fclone_cache分配的子skb,从父skb克隆得到 111 */ 112 fclone:2, 113 peeked:1, 114 /* 通过page_fragment_alloc分配内存 */ 115 head_frag:1, 116 xmit_more:1, 117 __unused:1; /* one bit hole */ 118 kmemcheck_bitfield_end(flags1); 119 120 /* fields enclosed in headers_start/headers_end are copied 121 * using a single memcpy() in __copy_skb_header() 122 */ 123 /* private: */ 124 __u32 headers_start[0]; 125 /* public: */ 126 127 /* if you move pkt_type around you also must adapt those constants */ 128 #ifdef __BIG_ENDIAN_BITFIELD 129 #define PKT_TYPE_MAX (7 << 5) 130 #else 131 #define PKT_TYPE_MAX 7 132 #endif 133 #define PKT_TYPE_OFFSET() offsetof(struct sk_buff, __pkt_type_offset) 134 135 __u8 __pkt_type_offset[0]; 136 /* 137 此字段根据l2的目的地址进行划分 138 PACKET_HOST-mac地址与接收设备mac地址相等,说明是发给该主机的 139 PACKET_BROADCAST-mac地址是接收设备的广播地址 140 PACKET_MULTICAST-mac地址接收改设备注册的多播地址之一 141 PACKET_OTHERHOST-mac地址不属于接收设备的地址,启用转发则转发,否则丢弃 142 PACKET_OUTGOING-数据包将被发出,用到这个标记的功能包括decnet,或者为每个 143 网络tab都复制一份发出包的函数 144 PACKET_LOOPBACK-数据包发往回环设备,有此标识,处理回环设备时, 145 可以跳过一些真实设备所需的操作 146 PACKET_USER-发送到用户空间,netlink使用 147 PACKET_KERNEL-发送到内核空间,netlink使用 148 PACKET_FASTROUTE-未使用 149 */ 150 __u8 pkt_type:3; 151 /* PFMEMALLOC内存分配标记 */ 152 __u8 pfmemalloc:1; 153 __u8 ignore_df:1; 154 155 __u8 nf_trace:1; 156 /* 157 CHECKSUM_NONE-硬件不支持,完全由软件执行校验和 158 CHECKSUM_PARTIAL-由硬件来执行校验和 159 CHECKSUM_UNNECESSARY-没必要执行校验和 160 CHECKSUM_COMPLETE-已完成执行校验和 161 */ 162 __u8 ip_summed:2; 163 __u8 ooo_okay:1; 164 __u8 l4_hash:1; 165 __u8 sw_hash:1; 166 __u8 wifi_acked_valid:1; 167 __u8 wifi_acked:1; 168 169 __u8 no_fcs:1; 170 /* Indicates the inner headers are valid in the skbuff. */ 171 __u8 encapsulation:1; 172 __u8 encap_hdr_csum:1; 173 __u8 csum_valid:1; 174 __u8 csum_complete_sw:1; 175 __u8 csum_level:2; 176 __u8 csum_bad:1; 177 178 __u8 dst_pending_confirm:1; 179 #ifdef CONFIG_IPV6_NDISC_NODETYPE 180 __u8 ndisc_nodetype:2; 181 #endif 182 __u8 ipvs_property:1; 183 __u8 inner_protocol_type:1; 184 __u8 remcsum_offload:1; 185 #ifdef CONFIG_NET_SWITCHDEV 186 __u8 offload_fwd_mark:1; 187 #endif 188 #ifdef CONFIG_NET_CLS_ACT 189 __u8 tc_skip_classify:1; 190 __u8 tc_at_ingress:1; 191 __u8 tc_redirected:1; 192 __u8 tc_from_ingress:1; 193 #endif 194 195 #ifdef CONFIG_NET_SCHED 196 __u16 tc_index; /* traffic control index */ 197 #endif 198 199 union { 200 /* 校验和,必须包含csum_start和csum_offset */ 201 __wsum csum; 202 struct { 203 /* 校验开始位置,相对于header */ 204 __u16 csum_start; 205 /* 校验和存储位置,相对于csum_start */ 206 __u16 csum_offset; 207 }; 208 }; 209 /* 210 正在被传输的数据包QoS等级 211 数据包由本地产生,套接字会定义优先级的值 212 数据包在被转发,则在调用ip_forward函数时,会根据 213 ip头本身的ToS字段定义该值 214 */ 215 __u32 priority; 216 /* 数据包接收时的网络设备索引号 */ 217 int skb_iif; 218 /* 数据包的hash值 */ 219 __u32 hash; 220 /* vlan封装协议 */ 221 __be16 vlan_proto; 222 /* vlan标签控制信息 */ 223 __u16 vlan_tci; 224 #if defined(CONFIG_NET_RX_BUSY_POLL) || defined(CONFIG_XPS) 225 union { 226 unsigned int napi_id; 227 unsigned int sender_cpu; 228 }; 229 #endif 230 #ifdef CONFIG_NETWORK_SECMARK 231 __u32 secmark; 232 #endif 233 234 union { 235 __u32 mark; 236 __u32 reserved_tailroom; 237 }; 238 239 /* 封装的协议 */ 240 union { 241 __be16 inner_protocol; 242 __u8 inner_ipproto; 243 }; 244 /* 封装的传输层头部相对于head的偏移 */ 245 __u16 inner_transport_header; 246 /* 封装的网络层头部相对于head的偏移 */ 247 __u16 inner_network_header; 248 /* 封装的链路层头部相对于head的偏移 */ 249 __u16 inner_mac_header; 250 251 /* 252 l3层协议值 253 如ETH_P_IP-ipv4报文 254 ETH_P_ARP-arp报文等 255 */ 256 __be16 protocol; 257 /* 传输层头部相对于head的偏移 */ 258 __u16 transport_header; 259 /* 网络层头部相对于head的偏移 */ 260 __u16 network_header; 261 /* 链路层头部相对于head的偏移 */ 262 __u16 mac_header; 263 264 /* private: */ 265 __u32 headers_end[0]; 266 /* public: */ 267 268 /* These elements must be at the end, see alloc_skb() for details. */ 269 /* 实际数据的尾部 */ 270 sk_buff_data_t tail; 271 /* 缓冲区的尾部 */ 272 sk_buff_data_t end; 273 /* 缓冲区的头部 */ 274 unsigned char *head, 275 /* 实际数据的头部 */ 276 *data; 277 /* 278 缓冲区的总大小,包括skb本身和实际数据len大小,alloc_skb函数将 279 该字段设置为len+sizeof(sk_buff) 280 每当len值更新,该值也要对应更新 281 */ 282 unsigned int truesize; 283 284 /* 285 引用计数,在使用该skb缓冲区的实例个数,当引用计数为0时,skb才能被释放 286 skb_get()获取操作中会增加引用计数,kfree_skb释放过程中检查引用计数, 287 引用计数为0时,才真正释放skb 288 该计数器只计算sk_buff结构引用计数,缓冲区包含的实际数据由 289 skb_shared_info->dataref字段记录 290 */ 291 atomic_t users; 292 };