套接口缓存,主要用途是保存在进程和网络接口之间相互传递的用户数据以及其他的一些信息
来自Linux-3.14.17源码
struct sk_buff {
/* These two members must be first. */
struct sk_buff *next;
struct sk_buff *prev;
ktime_t tstamp; //接收时间戳或者发送时间戳
struct sock *sk; //sk是SKB的宿主传输控制块,在由本地发出或者本地接收时才有效,使传输控制块与套接口及用户应用程序相关。
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); //SKB信息控制块,是每层协议的私有信息存储空间,由每一层协议自己维护并使用,并只在本层有效。
unsigned long _skb_refdst; //目的地
#ifdef CONFIG_XFRM
struct sec_path *sp; //IPSec协议用来跟踪传输的信息
#endif
unsigned int len, //SKB中数据部分长度,包括现行缓存区中的数据长度,data_len以及协议首部长度
data_len; //SG类型和FRAGLIST类型聚合分散I/O存储区中的数据长度
__u16 mac_len, //链路层报头的长度。
hdr_len; //克隆skb时可写报头的长度
union { //校验
__wsum csum;
struct {
__u16 csum_start;
__u16 csum_offset;
};
};
__u32 priority; //数据包队列的优先级
kmemcheck_bitfield_begin(flags1);
__u8 local_df:1, //表示该SKB在本地允许分片
cloned:1, //标记所属SKB是否已经克隆
ip_summed:2, //标记传输层校验和的状态
nohdr:1, //标记payload是否被单独饮用
nfctinfo:3; //skb与连接的信息关系
__u8 pkt_type:3, //帧类型,是由二层目的地址决定的
fclone:2, //当前克隆状态
ipvs_property:1, //SKB是否属于虚拟服务器
peeked:1, //包已经被抓到了,不需要再次抓取了
nf_trace:1; //netfilter数据包跟踪标识
kmemcheck_bitfield_end(flags1);
__be16 protocol; //上层协议,典型的包括IP,IPv6,ARP
void (*destructor)(struct sk_buff *skb); //类似析构函数
#if defined( ) || defined(CONFIG_NF_CONNTRACK_MODULE)
struct nf_conntrack *nfct; //相关联的连接(如果有的话)
#endif
#ifdef CONFIG_BRIDGE_NETFILTER
struct nf_bridge_info *nf_bridge; //关于桥接帧保存的数据
#endif
int skb_iif; //目的地网络设备的接口索引
__u32 rxhash; //包的哈希值
__be16 vlan_proto; //虚拟局域网封装协议
__u16 vlan_tci; //虚拟局域网标签控制信息
#ifdef CONFIG_NET_SCHED
__u16 tc_index; /* traffic control index */ //用于输入流量控制
#ifdef CONFIG_NET_CLS_ACT
__u16 tc_verd; /* traffic control verdict */ //用于输入流量控制
#endif
#endif
__u16 queue_mapping; //多设备的队列映射
kmemcheck_bitfield_begin(flags2);
#ifdef CONFIG_IPV6_NDISC_NODETYPE
__u8 ndisc_nodetype:2; //路由的类型(从链路层开始)
#endif
__u8 pfmemalloc:1;
__u8 ooo_okay:1; //允许socket的映射队列改变
__u8 l4_rxhash:1; //说明rxhash是个四元组的哈希值
__u8 wifi_acked_valid:1; //设置wifi_acked
__u8 wifi_acked:1; //wifi的帧是否ack
__u8 no_fcs:1; //帧校验序列
__u8 head_frag:1;
/* Encapsulation protocol and NIC drivers should use
* this flag to indicate to each other if the skb contains
* encapsulated packet or not and maybe use the inner packet
* headers if needed
*/
__u8 encapsulation:1;
/* 6/8 bit hole (depending on ndisc_nodetype presence) */
kmemcheck_bitfield_end(flags2);
#if defined CONFIG_NET_DMA || defined CONFIG_NET_RX_BUSY_POLL
union {
unsigned int napi_id; //这个SKB的NAPI的ID
dma_cookie_t dma_cookie; //DMA操作的一个cookie
};
#endif
#ifdef CONFIG_NETWORK_SECMARK
__u32 secmark; //安全标识
#endif
union {
__u32 mark; //通用分组标记
__u32 dropcount; //sk_receive_queue溢出的数量
__u32 reserved_tailroom; //
};
__be16 inner_protocol; //封装的协议
__u16 inner_transport_header; //封装的内部传输报头
__u16 inner_network_header; //封装的网络层报头
__u16 inner_mac_header; //封装的链路层报头
__u16 transport_header; //传输层报头
__u16 network_header; //网络层报头
__u16 mac_header; //链路层报头
/* 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; //整个数据缓存区的总长度
atomic_t users; //引用计数,用来标识有多少实体引用了该SKB
};
由下图可以看出skb在协议栈中的地位
第一层:链路层 netif
第二层:Ip,Arp
第三层:icmp.igmp,udp,tcp
第四层:skb_queue
//获取以太网卡头部
sturct eth_hdr *ehdr=eth_hdr(skb);
//获取arp头部
sturct arphdr *arp = arp_hdr(skb);
//获取IP头部
struct iphdr *iph=ip_hdr(skb);
//获取UDP头部
struct udphdr *uh = udp_hdr(skb);
//获取TCP头部
struct tcphdr *th = tcp_hdr(skb);
struct ethhdr {
unsigned char h_dest[ETH_ALEN]; /* destination eth addr */
unsigned char h_source[ETH_ALEN]; /* source ether addr */
__be16 h_proto; /* packet type ID field */
} __attribute__((packed));
struct arphdr {
__be16 ar_hrd; /* format of hardware address */
__be16 ar_pro; /* format of protocol address */
unsigned char ar_hln; /* length of hardware address */
unsigned char ar_pln; /* length of protocol address */
__be16 ar_op; /* ARP opcode (command) */
#if 0
/*
* Ethernet looks like this : This bit is variable sized however...
*/
unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */
unsigned char ar_sip[4]; /* sender IP address */
unsigned char ar_tha[ETH_ALEN]; /* target hardware address */
unsigned char ar_tip[4]; /* target IP address */
#endif
};
struct iphdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u8 ihl:4,
version:4;
#elif defined (__BIG_ENDIAN_BITFIELD)
__u8 version:4,
ihl:4;
#else
#error "Please fix <asm/byteorder.h>"
#endif
__u8 tos;
__be16 tot_len;
__be16 id;
__be16 frag_off;
__u8 ttl;
__u8 protocol;
__sum16 check;
__be32 saddr;
__be32 daddr;
/*The options start here. */
};
struct udphdr {
__be16 source;
__be16 dest;
__be16 len;
__sum16 check;
};
struct tcphdr {
__be16 source;
__be16 dest;
__be32 seq;
__be32 ack_seq;
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u16 res1:4,
doff:4,
fin:1,
syn:1,
rst:1,
psh:1,
ack:1,
urg:1,
ece:1,
cwr:1;
#elif defined(__BIG_ENDIAN_BITFIELD)
__u16 doff:4,
res1:4,
cwr:1,
ece:1,
urg:1,
ack:1,
psh:1,
rst:1,
syn:1,
fin:1;
#else
#error "Adjust your <asm/byteorder.h> defines"
#endif
__be16 window;
__sum16 check;
__be16 urg_ptr;
};