sk_buff数据结构定义:
-
struct sk_buff {
-
/* These two members must be first. */
-
// 用于将该skb链入到双向链表中
-
struct sk_buff *next;
-
struct sk_buff *prev;
-
-
/*接收或发送时间戳,通常在netif_receive_skb()或netif_rx()中设置*/
-
ktime_t tstamp;
-
/*
-
* skb的宿主传输控制块(sock结构),仅当网络报文由本地发送或接收时
-
* 才有效,使传输控制块与套接口(socket)及应用程序相关。当该skb仅在
-
* L2或L3进行转发时(即src和dst ip都不是本机地址),该指针为空。
-
*/
-
struct sock *sk;
-
/*
-
* 网络设备指针。在接收时指向收到网络包的网络设备(net_device结构);
-
* 发送时指向输出包的网络设备,如果是虚拟网卡,则指向该虚拟网卡,
-
* 在发送包时,虚拟设备驱动会在一组设备中选择合适的物理设备,并将
-
* dev指针改写为该设备的net_device结构;在收包时物理设备驱动会根据一定
-
* 策略选择合适的虚拟网络设备,并激dev指针改写。在某些情况下,该指针
-
* 会在包处理过程中改变。
-
*/
-
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.
-
*/
-
// 控制缓存,各层均可使用,存放私有数据的地方,8字节对齐
-
char cb[48] __aligned(8);
-
-
unsigned long _skb_refdst;
-
#ifdef CONFIG_XFRM
-
struct sec_path *sp;
-
#endif
-
/*
-
* skb中的数据长度,包括线性区(data指向)的数据长度和SG模式下各frag
-
* 的数据长度,注意:仅包括数据的长度,并不是分配的数据区的长度。
-
* 该长度随skb的传递而改变,因为各层的头部数据的添加或剥离。
-
* len中包含了协议首部的长度。
-
*/
-
unsigned int len,
-
data_len;/*SG类型和Fraglist类型聚合分散IO存储区中的数据长度*/
-
// L2首部长度
-
__u16 mac_len,
-
hdr_len;
-
/*
-
* 校验和相关,当校验状态为CHECKSUM_NONE时,csum中存放skb中数据部分
-
* 的校验和,当校验状态为CHECKSUM_PARTIAL时,csum_offset记录传输层首部中
-
* 的校验和字段的偏移,两种状态互斥,所以用联合体节省空间
-
*/
-
union {
-
__wsum csum;
-
struct {
-
__u16 csum_start;
-
__u16 csum_offset;
-
};
-
};
-
/*QoS流控使用*/
-
__u32 priority;
-
kmemcheck_bitfield_begin(flags1);
-
/*节省空间,按位利用*/
-
/*标记该skb是否运行在本地分片*/
-
__u8 local_df:1,
-
/*标记该skb是否已被克隆*/
-
cloned:1,
-
ip_summed:2,
-
/*标记payload是否被单独使用,如果不是则不能再修改协议首部,也不能通过skb->data访问协议首部*/
-
nohdr:1,
-
nfctinfo:3;
-
/*帧类型,由L2的目的地址(mac地址)决定*/
-
__u8 pkt_type:3,
-
/*当前克隆状态*/
-
fclone:2,
-
ipvs_property:1,
-
peeked:1,
-
nf_trace:1;
-
kmemcheck_bitfield_end(flags1);
-
/*从L2设备角度看到的上层协议(L3),典型的包括IP/IPv6/ARP*/
-
__be16 protocol;
-
/*
-
* skb析构函数指针,在释放skb时调用,释放后该skb不再属于相应的传输控制块(sock),
-
* 因此需要根据被释放的skb->truesize来调整sock的接收和发送缓存区大小(sk_rmem_alloc和sk_wmem_alloc)
-
*/
-
void (*destructor)(struct sk_buff *skb);
-
/*netfilter相关*/
-
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
-
struct nf_conntrack *nfct;
-
#endif
-
#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED
-
struct sk_buff *nfct_reasm;
-
#endif
-
#ifdef CONFIG_BRIDGE_NETFILTER
-
struct nf_bridge_info *nf_bridge;
-
#endif
-
-
int skb_iif;
-
-
__u32 rxhash;
-
-
__be16 vlan_proto;
-
__u16 vlan_tci;
-
/*tc流控相关*/
-
#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;
-
__u8 l4_rxhash:1;
-
__u8 wifi_acked_valid:1;
-
__u8 wifi_acked:1;
-
__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;
-
/* 7/9 bit hole (depending on ndisc_nodetype presence) */
-
kmemcheck_bitfield_end(flags2);
-
-
#if defined CONFIG_NET_DMA || defined CONFIG_NET_LL_RX_POLL
-
union {
-
unsigned int napi_id;
-
dma_cookie_t dma_cookie;
-
};
-
#endif
-
#ifdef CONFIG_NETWORK_SECMARK
-
__u32 secmark;
-
#endif
-
union {
-
__u32 mark;
-
__u32 dropcount;
-
__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;
-
-
/* RHEL SPECIFIC
-
*
-
* The following padding has been inserted before ABI freeze to
-
* allow extending the structure while preserve ABI. Feel free
-
* to replace reserved slots with required structure field
-
* additions of your backport.
-
*/
-
#ifndef __GENKSYMS__
-
u32 rh_reserved1;
-
u32 rh_reserved2;
-
u32 rh_reserved3;
-
u32 rh_reserved4;
-
#endif
-
-
/* These elements must be at the end, see alloc_skb() for details. */
-
/*线性区中数据结束的地方,tail和end之间可以添加数据*/
-
sk_buff_data_t tail;
-
/*
-
* 线性区的尾部,end之后紧接着存放skb_shared_info数据结构
-
* 用于存放SG相关的信息,如分片(frag)信息
-
*/
-
sk_buff_data_t end;
-
/*线性区的头部,发送时,每一层的协议在head和data之间填充协议首部*/
-
unsigned char *head,
-
*data;/*线性区中数据开始的地方*/
-
/*
-
* sk_buff数据结构长度+len(数据长度)
-
*/
-
unsigned int truesize;
-
/*引用计数,标识引用该skb的数量,在释放时判断,只有在其为0时才真正释放*/
-
atomic_t users;
-
};
skb->data skb的数据区开始
skb_shared_info skb数据区的附加信息,用于分散/聚集(SG)特性(需要网卡硬件支持),放在在skb数据区的末尾,该数据结构放在skb->end指针所指的地址之后,分配skb时,会一起分配:
-
/**
-
* 分配sk_buff结构及其缓冲区。
-
*/
-
struct sk_buff *alloc_skb(unsigned int size, int gfp_mask)
-
{
-
struct sk_buff *skb;
-
u8 *data;
-
-
/* Get the HEAD */
-
/**
-
* 从缓存中获取一个sk_buff结构
-
*/
-
skb = kmem_cache_alloc(skbuff_head_cache,
-
gfp_mask & ~__GFP_DMA);
-
if (!skb)
-
goto out;
-
-
/* Get the DATA. Size must match skb_add_mtu(). */
-
/**
-
* 分配缓冲区.
-
* 在调用 kmalloc 前,size 参数通过 SKB_DATA_ALIGN宏强制对齐。
-
*/
-
size = SKB_DATA_ALIGN(size);
-
/**
-
* skb_shared_info 块主要用来处理 IP 分片
-
*/
-
data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);
-
。。。
skb_shared_info->dataref 数据区引用计数,在skb_clone时增加
skb_shared_info在很多地方用作存放实际的数据,比如xen的pv驱动中。
skb中存放的数据分两种类型:
1、线性区数据,即skb->data指针指向的数据区,为一块连续的内存区,故称线性区。
2、SG(集合分散)IO数据区,也称分片区,或非线性区,用于SG特性下存放数据,其中的数据存放与独立的内存区域中,使用skb_shared_info->frags[]数组指向。
skb_clone 仅复制skb数据结构,并设置skb数据结构相关成员,clone的skb与原skb共享数据区(data)和skb_shared_info共享(同一份,使用指针引用),只是增加相应的引用计数而已。
当一个缓冲区需要被不同的用户独立地操作,而这些用户可能会修改sk_buff中某些变量的值(比如h和nh值)时调用。克隆过程只复制sk_buff结构,同时修改缓冲区的引用计数以避免共享的数据被提前释放.
skb_copy 复制skb数据结构和相应的数据(包括数据区(data)和skb_shared_info)
当一个函数不仅要修改sk_buff,而且要修改缓冲区内容时,就需要同时复制缓冲区。
如果所修改的数据在skb->start和skb->end之间,可以使用pskb_copy来复制这部分数据.
如果同时需要修改分片中的数据,就必须使用skb_copy。
pskb_copy 复制skb数据结构和数据区,共用skb_shared_info,当需要修改复制后的skb数据结构和数据区时,使用此接口。其作用介于skb_clone和sky_copy之间。
通常,使用skb_clone的情况比较多,由于通常的流程中,都不需要修改原skb的数据,而只是修改skb数据结构。
阅读(3730) | 评论(0) | 转发(0) |