to process. We do not want these modifications to be seen by packet sniffers and the like. So we use this 'nohdr' field and a special bit in the data area reference count to keep track of whether the device needs to replace the data area before making the packet header modifications.
简单来说,nohdr代表了skb_shared_info里的dataref有没有被分成两部分: ? ?
nohdr = 0:dataref代表整个skb数据区的引用计数;
nohdr = 1:dataref的高16bits代表skb数据区“payload部分”的引用计数,低16bits代表整个skb数据区的引用计数。
与之相关的函数有:skb_header_cloned(), skb_header_release()等。
“cloned”代表skb是否被clone,为了能迅速的引用一个 SKB 的数据,当 clone 一个已存在的 SKB 时,会产生一个新的 SKB,但是这个 SKB 会共享已有 SKB 的数据区。当一个 SKB 被 clone 后,原来的 SKB 和新的 SKB 结构中,‘cloned’都要被设置为1。
“users”:是sk_buff结构本身的引用计数,在kfree_skb时会先先使用原子操作对users计数-1,如果=0,才调用__kfree_skb真正去释放skb。 ? ?
skb_shared()检查skb->users是否为1;
skb_shared_check():如果skb->users > 1,则clone一个新的。
有关dataref,clone,users这些引用计数的区别和应用场景请见后续的“skb共享复制相关操作”一节。
“nfctinfo”:用于netfilter子系统中的conntrack模块记录连接状态,可取值为enum变量: enum ip_conntrack_info {
/* Part of an established connection (either direction). */ */ */ };
IP_CT_NEW, IP_CT_IS_REPLY,
IP_CT_NUMBER = IP_CT_IS_REPLY * 2 - 1 /* >= this indicates reply direction */
/* Number of distinct IP_CT types (no NEW in reply dirn). */
IP_CT_RELATED,
/* Started a new connection to track (only IP_CT_DIR_ORIGINAL); may be a retransmission.
IP_CT_ESTABLISHED,
/* Like NEW, but related to an existing connection, or ICMP error (in either direction).
pkt_type, fclone, ipvs_property
__u8
pkt_type:3,
fclone:2, ipvs_property:1;
“pkt_type”表示根据L2的目的地址得到包类型,可能取值在include/linux/if_packet.h,以太网设备中使用eth_type_trans函数来初始化这个值:
? PACKET_HOST:
? ? ? ? ? ?
PACKET_MULTICAST: PACKET_BROADCAST: PACKET_OTHERHOST: PACKET_OUTGOING: PACKET_LOOPBACK: PACKET_FASTROUTE:
“fclone”:是较新版kernel中添加的一个特性,以前版本的kernel中skb在分配的时候都是从后备高速缓存(lookaside cache)skbuff_head_cache中获取sk_buff的结构;而现在可以在调用alloc_skb(),通过fclone参数选择从skbuff_head_cache或者skbuff_fclone_cache中分配。两者的区别在于skbuff_head_cache在创建时指定的单位内存区域的大小是
sizeof(structsk_buff),可以容纳任意数目的struct sk_buff,而skbuff_fclone_cache在创建时指定的单位内存区域大小是2*sizeof(struct sk_buff)+sizeof(atomic_t),它的最小区域单位是一对strcut sk_buff和一个引用计数,这一对sk_buff是克隆的,即它们指向同一个数据缓冲区,引用计数值是0(SKB_FCLONE_UNAVAILABLE),1(SKB_FCLONE_ORIG)或2(SKB_FCLONE_CLONE),表示这一对中有几个sk_buff已被使用: ? ?
分配skb时,skb->fclone = SKB_FCLONE_ORIG; atomic_set(fclone_ref, 1);
child->fclone = SKB_FCLONE_UNAVAILABLE; skb_clone时,如下处理 struct sk_buff *n; n = skb + 1;
if (skb->fclone == SKB_FCLONE_ORIG && n->fclone == SKB_FCLONE_UNAVAILABLE) { }
atomic_t *fclone_ref = (atomic_t *) (n + 1); n->fclone = SKB_FCLONE_CLONE; atomic_inc(fclone_ref);
n = kmem_cache_alloc(skbuff_head_cache, gfp_mask); if (!n)
return NULL;
n->fclone = SKB_FCLONE_UNAVAILABLE;
} else {
即如果设置了fclone,第一次clone采用快速clone,直接使用child_skb,并且增加fclone_ref,child_skb->fclone = SKB_FCLONE_CLONE;其他情况都从skbuff_head_cache里分配新的skb; ?
在释放skb结构时如下处理
switch (skb->fclone) { case SKB_FCLONE_UNAVAILABLE:
kmem_cache_free(skbuff_head_cache, skb); break;
case SKB_FCLONE_ORIG:
fclone_ref = (atomic_t *) (skb + 2); if (atomic_dec_and_test(fclone_ref)) kmem_cache_free(skbuff_fclone_cache, skb);
break;
case SKB_FCLONE_CLONE:
fclone_ref = (atomic_t *) (skb + 1); other = skb - 1;
/* The clone portion is available for * fast-cloning again. */
skb->fclone = SKB_FCLONE_UNAVAILABLE; if (atomic_dec_and_test(fclone_ref))
kmem_cache_free(skbuff_fclone_cache, other); break;
};
? ?
SKB_FCLONE_UNAVAILABLE:直接归还给skbuff_head_cache;(虽然没有经过clone的child_skb->fclone也是这个标志,但没有被clone也不会被释放)
SKB_FCLONE_ORIG:代表释放的是从skbuff_fclone_cache里分配的skb,如果fclone_ref = 1(未被clone过)则归还skb给skbuff_fclone_cache;否则(已经被clone过)只是将fclone_ref--; ?
SKB_FCLONE_CLONE:代表释放的代表释放的是从skbuff_fclone_cache里分配的skb经过clone操作后得到的新skb,则把fclone标志置为SKB_FCLONE_UNAVAILABLE,留作下次快速clone使用。如果fclone_ref--=0,代表这个skb的兄弟skb已经被释放了,则归还skb给skbuff_fclone_cache。
根据skb->fclone标志:
“ipvs_property”是为ip_vs模块添加的变量,置为1代表已经被ip_vs某个部分处理过,后续不需要再处理。可以参考ip_vs_out()函数。
head/data/tail/end指针,h/nh/mac指针
unsigned char
*head, *data, *tail, *end;
*th; *uh;
union {
struct tcphdr struct udphdr
struct icmphdr *icmph; struct igmphdr *igmph; struct iphdr *ipiph; struct ipv6hdr *ipv6h; unsigned char
*raw;
} h; union {
struct iphdr *iph; struct ipv6hdr *ipv6h; struct arphdr unsigned char
*arph; *raw;
} nh; union {
head, data, tail, end四个指针分别指向data buffer的不同位置,如下图:
unsigned char *raw; } mac;
这四个指针的移动,最常用的四个函数为:(a)skb_put, (b)skb_push, (c)skb_pull, (d)skb_reserve ,如下图所示。
h,nh,mac分别指向各层网络协议的头部,下面讨论各个指针在接收时都在哪些地方被初始化: ? { ?? }
static inline struct ethhdr *eth_hdr(const struct sk_buff *skb)
skb->mac.raw = skb->data; skb_pull(skb, ETH_HLEN); eth = eth_hdr(skb);
mac为L2的头部指针,在eth_type_trans里初始化
__be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)
??
百度搜索“77cn”或“免费范文网”即可找到本站免费阅读全部范文。收藏本站方便下次阅读,免费范文网,提供经典小说综合文库sk_buff详解(2)在线全文阅读。
相关推荐: