1. sk_buff 结构体

可以看出 sk_buff 结构体很重要,

sk_buff --- 套接字缓冲区,用来在linux网络子系统中各层之间数据传递,起到了“神经中枢”的作用。

当发送数据包时,linux内核的网络模块必须建立一个包含要传输的数据包的sk_buff,然后将sk_buff传递给下一层,各层在 sk_buff 中添加不同的协议头,直到交给网络设备发送。

同样,当接收数据包时,网络设备从物理媒介层接收到数据后,他必须将接收到的数据转换为sk_buff,并传递给上层,各层剥去相应的协议头后直到交给用户。

sk_buff结构如下图所示:


sk_buff定义如下:

[cpp]  view plain copy
  1. /**  
  2.  *  struct sk_buff - socket buffer 
  3.  *  @next: Next buffer in list 
  4.  *  @prev: Previous buffer in list 
  5.  *  @sk: Socket we are owned by 
  6.  *  @tstamp: Time we arrived 
  7.  *  @dev: Device we arrived on/are leaving by 
  8.  *  @transport_header: Transport layer header 
  9.  *  @network_header: Network layer header 
  10.  *  @mac_header: Link layer header 
  11.  *  @_skb_refdst: destination entry (with norefcount bit) 
  12.  *  @sp: the security path, used for xfrm 
  13.  *  @cb: Control buffer. Free for use by every layer. Put private vars here 
  14.  *  @len: Length of actual data 
  15.  *  @data_len: Data length 
  16.  *  @mac_len: Length of link layer header 
  17.  *  @hdr_len: writable header length of cloned skb 
  18.  *  @csum: Checksum (must include start/offset pair) 
  19.  *  @csum_start: Offset from skb->head where checksumming should start 
  20.  *  @csum_offset: Offset from csum_start where checksum should be stored 
  21.  *  @local_df: allow local fragmentation 
  22.  *  @cloned: Head may be cloned (check refcnt to be sure) 
  23.  *  @nohdr: Payload reference only, must not modify header 
  24.  *  @pkt_type: Packet class 
  25.  *  @fclone: skbuff clone status 
  26.  *  @ip_summed: Driver fed us an IP checksum 
  27.  *  @priority: Packet queueing priority 
  28.  *  @users: User count - see {datagram,tcp}.c 
  29.  *  @protocol: Packet protocol from driver 
  30.  *  @truesize: Buffer size  
  31.  *  @head: Head of buffer 
  32.  *  @data: Data head pointer 
  33.  *  @tail: Tail pointer 
  34.  *  @end: End pointer 
  35.  *  @destructor: Destruct function 
  36.  *  @mark: Generic packet mark 
  37.  *  @nfct: Associated connection, if any 
  38.  *  @ipvs_property: skbuff is owned by ipvs 
  39.  *  @peeked: this packet has been seen already, so stats have been 
  40.  *      done for it, don't do them again 
  41.  *  @nf_trace: netfilter packet trace flag 
  42.  *  @nfctinfo: Relationship of this skb to the connection 
  43.  *  @nfct_reasm: netfilter conntrack re-assembly pointer 
  44.  *  @nf_bridge: Saved data about a bridged frame - see br_netfilter.c 
  45.  *  @skb_iif: ifindex of device we arrived on 
  46.  *  @rxhash: the packet hash computed on receive 
  47.  *  @queue_mapping: Queue mapping for multiqueue devices 
  48.  *  @tc_index: Traffic control index 
  49.  *  @tc_verd: traffic control verdict 
  50.  *  @ndisc_nodetype: router type (from link layer) 
  51.  *  @dma_cookie: a cookie to one of several possible DMA operations 
  52.  *      done by skb DMA functions 
  53.  *  @secmark: security marking 
  54.  *  @vlan_tci: vlan tag control information 
  55.  */  
  56.   
  57. struct sk_buff {  
  58.     /* These two members must be first. */  
  59.     struct sk_buff      *next;    //链表指针,指向后一个和前一个  
  60.     struct sk_buff      *prev;  
  61.   
  62.     ktime_t         tstamp;   //socket 到达时的时间戳  
  63.   
  64.     struct sock     *sk;      //socket的所有者  
  65.     struct net_device   *dev;     //发送或接受该缓冲区的网络设备  
  66.   
  67.     /* 
  68.      * This is the control buffer. It is free to use for every 
  69.      * layer. Please put your private variables there. If you 
  70.      * want to keep them across layers you have to do a skb_clone() 
  71.      * first. This is owned by whoever has the skb queued ATM. 
  72.      */  
  73.     char            cb[48] __aligned(8);  
  74.   
  75.     unsigned long       _skb_refdst;  
  76. #ifdef CONFIG_XFRM  
  77.     struct  sec_path    *sp;  
  78. #endif  
  79.     unsigned int        len,  
  80.                 data_len;  
  81.     __u16           mac_len,  
  82.                 hdr_len;  
  83.     union {  
  84.         __wsum      csum;  
  85.         struct {  
  86.             __u16   csum_start;  
  87.             __u16   csum_offset;  
  88.         };  
  89.     };  
  90.     __u32           priority;  
  91.     kmemcheck_bitfield_begin(flags1);  
  92.     __u8            local_df:1,  
  93.                 cloned:1,  
  94.                 ip_summed:2,   //对数据包的校验策略  
  95.                 nohdr:1,  
  96.                 nfctinfo:3;  
  97.     __u8            pkt_type:3,      
  98.                 fclone:2,  
  99.                 ipvs_property:1,  
  100.                 peeked:1,  
  101.                 nf_trace:1;  
  102.     kmemcheck_bitfield_end(flags1);  
  103.     __be16          protocol;  
  104.   
  105.     void            (*destructor)(struct sk_buff *skb);  
  106. #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)  
  107.     struct nf_conntrack *nfct;  
  108.     struct sk_buff      *nfct_reasm;  
  109. #endif  
  110. #ifdef CONFIG_BRIDGE_NETFILTER  
  111.     struct nf_bridge_info   *nf_bridge;  
  112. #endif  
  113.   
  114.     int         skb_iif;  
  115. #ifdef CONFIG_NET_SCHED  
  116.     __u16           tc_index;   /* traffic control index */  
  117. #ifdef CONFIG_NET_CLS_ACT  
  118.     __u16           tc_verd;    /* traffic control verdict */  
  119. #endif  
  120. #endif  
  121.   
  122.     __u32           rxhash;  
  123.   
  124.     kmemcheck_bitfield_begin(flags2);  
  125.     __u16           queue_mapping:16;  
  126. #ifdef CONFIG_IPV6_NDISC_NODETYPE  
  127.     __u8            ndisc_nodetype:2,  
  128.                 deliver_no_wcard:1;  
  129. #else  
  130.     __u8            deliver_no_wcard:1;  
  131. #endif  
  132.     kmemcheck_bitfield_end(flags2);  
  133.   
  134.     /* 0/14 bit hole */  
  135.   
  136. #ifdef CONFIG_NET_DMA  
  137.     dma_cookie_t        dma_cookie;  
  138. #endif  
  139. #ifdef CONFIG_NETWORK_SECMARK  
  140.     __u32           secmark;  
  141. #endif  
  142.     union {  
  143.         __u32       mark;  
  144.         __u32       dropcount;  
  145.     };  
  146.   
  147.     __u16           vlan_tci;  
  148.   
  149.     sk_buff_data_t      transport_header;   //传输层协议头  
  150.     sk_buff_data_t      network_header;     //网络层协议头  
  151.     sk_buff_data_t      mac_header;         //链路层协议头  
  152.     /* These elements must be at the end, see alloc_skb() for details.  */  
  153.     sk_buff_data_t      tail;  
  154.     sk_buff_data_t      end;  
  155.     unsigned char       *head,  
  156.                 *data;  
  157.     unsigned int        truesize;  
  158.     atomic_t        users;  
  159. };  
sk_buff主要成员如下:

1.1 各层协议头:

--- transport_header : 传输层协议头,如 TCP, UDP , ICMP, IGMP等协议头

--- network_header : 网络层协议头, 如IP, IPv6, ARP 协议头

--- mac_header : 链路层协议头

--- sk_buff_data_t 原型就是一个char 指针

[cpp]  view plain copy
  1. #ifdef NET_SKBUFF_DATA_USES_OFFSET  
  2. typedef unsigned int sk_buff_data_t;  
  3. #else  
  4. typedef unsigned char *sk_buff_data_t;  
  5. #endif  
1.2  数据缓冲区指针  head, data, tail, end

--- *head : 指向内存中已分配的用于存放网络数据缓冲区的起始地址,  sk_buff和相关数据被分配后,该指针值就固定了

--- *data : 指向对应当前协议层有效数据的起始地址。

每个协议层的有效数据内容不一样,各层有效数据的内容如下:

a. 对于传输层,有效数据包括用户数据和传输层协议头

b. 对于网络层,有效数据包括用户数据、传输层协议和网络层协议头。

c. 对于数据链路层,有效数据包括用户数据、传输层协议、网络层协议和链路层协议。

因此,data指针随着当前拥有sk_buff的协议层的变化而进行相应的移动。

--- tail : 指向对应当前协议层有效数据的结尾地址,与data指针相对应。

--- end : 指向内存中分配的网络数据缓冲区的结尾,与head指针相对应。和head一样,sk_buff被分配后,end指针就固定了。

head, data, tail, end 关系如下图所示:


1.3 长度信息 len, data_len, truesize

--- len : 指网络数据包的有效数据的长度,包括协议头和负载(payload).

--- data_len : 记录分片的数据长度

--- truesize : 表述缓存区的整体长度, 一般为 sizeof(sk_buff).

1.4 数据包类型 

--- pkt_type : 指定数据包类型。驱动程序负责将其设置为:

PACKET_HOST --- 该数据包是给我的。

PACKET_OTHERHOST --- 该数据包不是给我的。

PACKET_BROADCAST --- 广播类型的数据包

PACKET_MULTICAST --- 组播类型的数据包

驱动程序不必显式的修改pkt_type,因为eth_type_trans会完成该工作。

2. 套接字缓冲区的操作

2.1 分配套接字缓冲区

struct sk_buff *alloc_skb(unsigned intlen, int priority);

alloc_skb()函数 分配一个套接字缓冲区和一个数据缓冲区。

--- len : 为数据缓冲区的大小

--- priority : 内存分配的优先级

[cpp]  view plain copy
  1. static inline struct sk_buff *alloc_skb(unsigned int size,  
  2.                     gfp_t priority)  
  3. {  
  4.     return __alloc_skb(size, priority, 0, NUMA_NO_NODE);  
  5. }  
[cpp]  view plain copy
  1. /** 
  2.  *  __alloc_skb -   allocate a network buffer 
  3.  *  @size: size to allocate 
  4.  *  @gfp_mask: allocation mask 
  5.  *  @fclone: allocate from fclone cache instead of head cache 
  6.  *      and allocate a cloned (child) skb 
  7.  *  @node: numa node to allocate memory on 
  8.  * 
  9.  *  Allocate a new &sk_buff. The returned buffer has no headroom and a 
  10.  *  tail room of size bytes. The object has a reference count of one. 
  11.  *  The return is the buffer. On a failure the return is %NULL. 
  12.  * 
  13.  *  Buffers may only be allocated from interrupts using a @gfp_mask of 
  14.  *  %GFP_ATOMIC. 
  15.  */  
  16. struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,  
  17.                 int fclone, int node)  
  18. {  
  19.     struct kmem_cache *cache;  
  20.     struct skb_shared_info *shinfo;  
  21.     struct sk_buff *skb;  
  22.     u8 *data;  
  23.   
  24.     cache = fclone ? skbuff_fclone_cache : skbuff_head_cache;  
  25.   
  26.     /* Get the HEAD */  
  27.     skb = kmem_cache_alloc_node(cache, gfp_mask & ~__GFP_DMA, node);  //分配套接字缓冲区  
  28.     if (!skb)  
  29.         goto out;  
  30.     prefetchw(skb);  
  31.   
  32.     size = SKB_DATA_ALIGN(size);  
  33.     data = kmalloc_node_track_caller(size + sizeof(struct skb_shared_info),   //分配数据缓冲区  
  34.             gfp_mask, node);  
  35.     if (!data)  
  36.         goto nodata;  
  37.     prefetchw(data + size);  
  38.   
  39.     /* 
  40.      * Only clear those fields we need to clear, not those that we will 
  41.      * actually initialise below. Hence, don't put any more fields after 
  42.      * the tail pointer in struct sk_buff! 
  43.      */  
  44.     memset(skb, 0, offsetof(struct sk_buff, tail));  
  45.     skb->truesize = size + sizeof(struct sk_buff);  
  46.     atomic_set(&skb->users, 1);  
  47.     skb->head = data;  
  48.     skb->data = data;  
  49.     skb_reset_tail_pointer(skb);  
  50.     skb->end = skb->tail + size;  
  51. #ifdef NET_SKBUFF_DATA_USES_OFFSET  
  52.     skb->mac_header = ~0U;  
  53. #endif  
  54.   
  55.     /* make sure we initialize shinfo sequentially */  
  56.     shinfo = skb_shinfo(skb);  
  57.     memset(shinfo, 0, offsetof(struct skb_shared_info, dataref));  
  58.     atomic_set(&shinfo->dataref, 1);  
  59.   
  60.     if (fclone) {  
  61.         struct sk_buff *child = skb + 1;  
  62.         atomic_t *fclone_ref = (atomic_t *) (child + 1);  
  63.   
  64.         kmemcheck_annotate_bitfield(child, flags1);  
  65.         kmemcheck_annotate_bitfield(child, flags2);  
  66.         skb->fclone = SKB_FCLONE_ORIG;  
  67.         atomic_set(fclone_ref, 1);  
  68.   
  69.         child->fclone = SKB_FCLONE_UNAVAILABLE;  
  70.     }  
  71. out:  
  72.     return skb;  
  73. nodata:  
  74.     kmem_cache_free(cache, skb);  
  75.     skb = NULL;  
  76.     goto out;  
  77. }  
  78. EXPORT_SYMBOL(__alloc_skb);  
struct sk_buff *dev_alloc_skb(unsignedint len);

dev_alloc_skb()函数以GFP_ATOMIC 优先级调用上面的alloc_skb()函数。

并保存skb->dead 和 skb->data之间的16个字节

[cpp]  view plain copy
  1. /** 
  2.  *  dev_alloc_skb - allocate an skbuff for receiving 
  3.  *  @length: length to allocate 
  4.  * 
  5.  *  Allocate a new &sk_buff and assign it a usage count of one. The 
  6.  *  buffer has unspecified headroom built in. Users should allocate 
  7.  *  the headroom they think they need without accounting for the 
  8.  *  built in space. The built in space is used for optimisations. 
  9.  * 
  10.  *  %NULL is returned if there is no free memory. Although this function 
  11.  *  allocates memory it can be called from an interrupt. 
  12.  */  
  13. struct sk_buff *dev_alloc_skb(unsigned int length)  
  14. {  
  15.     /* 
  16.      * There is more code here than it seems: 
  17.      * __dev_alloc_skb is an inline 
  18.      */  
  19.     return __dev_alloc_skb(length, GFP_ATOMIC);  
  20. }  
  21. EXPORT_SYMBOL(dev_alloc_skb);  
[cpp]  view plain copy
  1. /** 
  2.  *  __dev_alloc_skb - allocate an skbuff for receiving 
  3.  *  @length: length to allocate 
  4.  *  @gfp_mask: get_free_pages mask, passed to alloc_skb 
  5.  * 
  6.  *  Allocate a new &sk_buff and assign it a usage count of one. The 
  7.  *  buffer has unspecified headroom built in. Users should allocate 
  8.  *  the headroom they think they need without accounting for the 
  9.  *  built in space. The built in space is used for optimisations. 
  10.  * 
  11.  *  %NULL is returned if there is no free memory. 
  12.  */  
  13. static inline struct sk_buff *__dev_alloc_skb(unsigned int length,  
  14.                           gfp_t gfp_mask)  
  15. {  
  16.     struct sk_buff *skb = alloc_skb(length + NET_SKB_PAD, gfp_mask);  
  17.     if (likely(skb))  
  18.         skb_reserve(skb, NET_SKB_PAD);  
  19.     return skb;  
  20. }  

2.2 释放套接字缓冲区

void kfree_skb(struct sk_buff *skb);

[cpp]  view plain copy
  1. /** 
  2.  *  kfree_skb - free an sk_buff 
  3.  *  @skb: buffer to free 
  4.  * 
  5.  *  Drop a reference to the buffer and free it if the usage count has 
  6.  *  hit zero. 
  7.  */  
  8. void kfree_skb(struct sk_buff *skb)  
  9. {  
  10.     if (unlikely(!skb))  
  11.         return;  
  12.     if (likely(atomic_read(&skb->users) == 1))  
  13.         smp_rmb();  
  14.     else if (likely(!atomic_dec_and_test(&skb->users)))  
  15.         return;  
  16.     trace_kfree_skb(skb, __builtin_return_address(0));  
  17.     __kfree_skb(skb);  
  18. }  
  19. EXPORT_SYMBOL(kfree_skb);  
--- kfree_skb() 函数只能在内核内部使用,网络设备驱动中必须使用dev_kfree_skb()、dev_kfree_skb_irq() 或 dev_kfree_skb_any().

void dev_kfree_skb(struct sk_buff *skb);

--- dev_kfree_skb()用于非中断上下文。

[cpp]  view plain copy
  1. #define dev_kfree_skb(a)    consume_skb(a)  
[cpp]  view plain copy
  1. /** 
  2.  *  consume_skb - free an skbuff 
  3.  *  @skb: buffer to free 
  4.  * 
  5.  *  Drop a ref to the buffer and free it if the usage count has hit zero 
  6.  *  Functions identically to kfree_skb, but kfree_skb assumes that the frame 
  7.  *  is being dropped after a failure and notes that 
  8.  */  
  9. void consume_skb(struct sk_buff *skb)  
  10. {  
  11.     if (unlikely(!skb))  
  12.         return;  
  13.     if (likely(atomic_read(&skb->users) == 1))  
  14.         smp_rmb();  
  15.     else if (likely(!atomic_dec_and_test(&skb->users)))  
  16.         return;  
  17.     trace_consume_skb(skb);  
  18.     __kfree_skb(skb);  
  19. }  
  20. EXPORT_SYMBOL(consume_skb);  
void dev_kfree_skb_irq(struct sk_buff *skb);

--- dev_kfree_skb_irq() 用于中断上下文。

[cpp]  view plain copy
  1. void dev_kfree_skb_irq(struct sk_buff *skb)  
  2. {  
  3.     if (atomic_dec_and_test(&skb->users)) {  
  4.         struct softnet_data *sd;  
  5.         unsigned long flags;  
  6.   
  7.         local_irq_save(flags);  
  8.         sd = &__get_cpu_var(softnet_data);  
  9.         skb->next = sd->completion_queue;  
  10.         sd->completion_queue = skb;  
  11.         raise_softirq_irqoff(NET_TX_SOFTIRQ);  
  12.         local_irq_restore(flags);  
  13.     }  
  14. }  
  15. EXPORT_SYMBOL(dev_kfree_skb_irq);  
void dev_kfree_skb_any(struct sk_buff *skb);

--- dev_kfree_skb_any() 在中断或非中断上下文中都能使用。

[cpp]  view plain copy
  1. void dev_kfree_skb_any(struct sk_buff *skb)  
  2. {  
  3.     if (in_irq() || irqs_disabled())  
  4.         dev_kfree_skb_irq(skb);  
  5.     else  
  6.         dev_kfree_skb(skb);  
  7. }  
  8. EXPORT_SYMBOL(dev_kfree_skb_any);  
2.3 移动指针
Linux套接字缓冲区中的指针移动操作有:put(放置), push(推), pull(拉) 和 reserve(保留) 等。

2.3.1 put操作

unsigned char *skb_put(struct sk_buff *skb, unsigned int len); 

将 tail 指针下移,增加 sk_buff 的 len 值,并返回 skb->tail 的当前值。

将数据添加在buffer的尾部。

[cpp]  view plain copy
  1. /** 
  2.  *  skb_put - add data to a buffer 
  3.  *  @skb: buffer to use 
  4.  *  @len: amount of data to add 
  5.  * 
  6.  *  This function extends the used data area of the buffer. If this would 
  7.  *  exceed the total buffer size the kernel will panic. A pointer to the 
  8.  *  first byte of the extra data is returned. 
  9.  */  
  10. unsigned char *skb_put(struct sk_buff *skb, unsigned int len)  
  11. {  
  12.     unsigned char *tmp = skb_tail_pointer(skb);    // tmp = skb->tail  
  13.     SKB_LINEAR_ASSERT(skb);  
  14.     skb->tail += len;  
  15.     skb->len  += len;  
  16.     if (unlikely(skb->tail > skb->end))  
  17.         skb_over_panic(skb, len, __builtin_return_address(0));   //检测放入缓冲区的数据  
  18.     return tmp;  
  19. }  
  20. EXPORT_SYMBOL(skb_put);  
[cpp]  view plain copy
  1. static inline unsigned char *skb_tail_pointer(const struct sk_buff *skb)  
  2. {  
  3.     return skb->tail;  
  4. }  
unsigned char *__skb_put(struct sk_buff *skb, unsigned int len);

__skb_put() 与 skb_put()的区别在于 skb_put()会检测放入缓冲区的数据, 而__skb_put()不会检查

[cpp]  view plain copy
  1. static inline unsigned char *__skb_put(struct sk_buff *skb, unsigned int len)  
  2. {  
  3.     unsigned char *tmp = skb_tail_pointer(skb);  
  4.     SKB_LINEAR_ASSERT(skb);  
  5.     skb->tail += len;  
  6.     skb->len  += len;  
  7.     return tmp;  
  8. }  

2.3.2 push操作:

unsigned char *skb_push(struct sk_buff *skb, unsigned int len);

skb_push()会将data指针上移,也就是将数据添加在buffer的起始点,因此也要增加sk_buff的len值。

[cpp]  view plain copy
  1. /** 
  2.  *  skb_push - add data to the start of a buffer 
  3.  *  @skb: buffer to use 
  4.  *  @len: amount of data to add 
  5.  * 
  6.  *  This function extends the used data area of the buffer at the buffer 
  7.  *  start. If this would exceed the total buffer headroom the kernel will 
  8.  *  panic. A pointer to the first byte of the extra data is returned. 
  9.  */  
  10. unsigned char *skb_push(struct sk_buff *skb, unsigned int len)  
  11. {  
  12.     skb->data -= len;  
  13.     skb->len  += len;  
  14.     if (unlikely(skb->data<skb->head))  
  15.         skb_under_panic(skb, len, __builtin_return_address(0));  
  16.     return skb->data;  
  17. }  
  18. EXPORT_SYMBOL(skb_push);  
unsigned char *__skb_push(struct sk_buff *skb, unsigned int len);
[cpp]  view plain copy
  1. static inline unsigned char *__skb_push(struct sk_buff *skb, unsigned int len)  
  2. {  
  3.     skb->data -= len;  
  4.     skb->len  += len;  
  5.     return skb->data;  
  6. }  
__skb_push()和skb_push()的区别 与 __skb_put() 和 skb_put()的区别一样。

push操作在缓冲区的头部增加一段可以存储网络数据包的空间,而put操作在缓冲区的尾部增加一段可以存储网络数据包的空间。
2.3.3 pull操作:

unsigned char *skb_pull(struct sk_buff *skb, unsigned int len);

skb_pull()将data指针下移,并减少skb的len值, 这个操作与skb_push()对应。

这个操作主要用于下层协议向上层协议移交数据包,使data指针指向上一层协议头

[cpp]  view plain copy
  1. /** 
  2.  *  skb_pull - remove data from the start of a buffer 
  3.  *  @skb: buffer to use 
  4.  *  @len: amount of data to remove 
  5.  * 
  6.  *  This function removes data from the start of a buffer, returning 
  7.  *  the memory to the headroom. A pointer to the next data in the buffer 
  8.  *  is returned. Once the data has been pulled future pushes will overwrite 
  9.  *  the old data. 
  10.  */  
  11. unsigned char *skb_pull(struct sk_buff *skb, unsigned int len)  
  12. {  
  13.     return skb_pull_inline(skb, len);  
  14. }  
  15. EXPORT_SYMBOL(skb_pull);  
[cpp]  view plain copy
  1. static inline unsigned char *skb_pull_inline(struct sk_buff *skb, unsigned int len)  
  2. {  
  3.     return unlikely(len > skb->len) ? NULL : __skb_pull(skb, len);  
  4. }  
[cpp]  view plain copy
  1. static inline unsigned char *__skb_pull(struct sk_buff *skb, unsigned int len)  
  2. {  
  3.     skb->len -= len;  
  4.     BUG_ON(skb->len < skb->data_len);  
  5.     return skb->data += len;  
  6. }  
2.3.4 reserve 操作

void skb_reserve(struct sk_buff *skb,  unsigned int len);

skb_reserve()将data指针 和 tail 指针同时下移。

这个操作用于在缓冲区头部预留len长度的空间

[cpp]  view plain copy
  1. /** 
  2.  *  skb_reserve - adjust headroom 
  3.  *  @skb: buffer to alter 
  4.  *  @len: bytes to move 
  5.  * 
  6.  *  Increase the headroom of an empty &sk_buff by reducing the tail 
  7.  *  room. This is only allowed for an empty buffer. 
  8.  */  
  9. static inline void skb_reserve(struct sk_buff *skb, int len)  
  10. {  
  11.     skb->data += len;  
  12.     skb->tail += len;  
  13. }  
3. 例子: 

Linux处理 一个UDP数据包的接收流程,来说明对sk_buff的操作过程。

这一过程绝大部分工作会在内核完成,驱动中只需要完成涉及数据链路层部分。

假设网卡收到一个UDP数据包,Linux处理流程如下:

3.1 网卡收到一个UDP数据包后,驱动程序需要创建一个sk_buff结构体和数据缓冲区,将接收到的数据全部复制到data指向的空间,并将skb->mac_header指向data。 

此时有效数据的开始位置data是一个以太网头部,即链路层协议头。

示例代码如下:

//分配新的套接字缓冲区和数据缓冲区

[cpp]  view plain copy
  1. skb = dev_alloc_skb(length + 2);  
  2. if(skb == NULL) {  
  3.     ... //分配失败  
  4.     return ;  
  5. }  
  6.   
  7. skb_reserve(skb, 2);  //在缓冲区头部预留空间,以使网络层协议头对齐。  
  8.   
  9. //将硬件接收到的数据复制到数据缓冲区  
  10. readwords(ioaddr, RX_FRAME_PORT, skb_put(skb, length), length >> 1);  
  11. if(length & 1){  
  12.     skb->data[length - 1] = readword(ioaddr, RX_FRAME_PORT);  
  13. }  

工作内容如下图所示:


3.2 数据链路层通过调用 skb_pull() 剥掉以太网协议头,向网络层IP传送数据包。

在剥离过程中,data指针会下移 一个 以太网头部的长度 sizeof(struct ethhdr), 而len 也减去 sizeof(struct ethhdr)长度。

此时有效数据的开始位置是一个IP协议头,skb->network_head指向data,即IP协议头, 而 skb->mac_header 依旧指向以太网头, 即链路层协议头。

内容如下图所示:


3.3 网络层通过skb_pull()剥掉IP协议头,向UDP传输层传递数据包。

剥离过程中,data指针会下移一个IP协议头长度 sizeof(struct iphdr), 而len也会减少sizeof(struct iphdr)长度。

此时有效数据开始位置是一个UDP协议头, skb->transport_header指向data,即UDP协议头。

而skb->network_header继续指向IP协议头, skb->mac_header 继续指向链路层协议头。

如下图所示:


3.4 应用程序在调用 recv() 接收数据时,从 skb->data + sizeof(struct udphdr) 的位置开始复制到应用层缓冲区。

可见,UPD协议头到最后也没有被剥离。

GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐