Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1732188
  • 博文数量: 782
  • 博客积分: 2455
  • 博客等级: 大尉
  • 技术积分: 4140
  • 用 户 组: 普通用户
  • 注册时间: 2011-04-06 21:37
个人简介

Linux ,c/c++, web,前端,php,js

文章分类

全部博文(782)

文章存档

2015年(8)

2014年(28)

2013年(110)

2012年(307)

2011年(329)

分类:

2011-10-19 08:57:17

原文地址:SK_BUFF结构分析 作者:spike8800

 

前言:

以下是根据《深入理解Linux网络技术内幕》对sk_buff的相关总结,由于是刚刚看这本书(太厚了),不免在前期出现错误,随着对此书的深入我会在修改前面的错误,也希望各位牛人给予指点。帮助我成长。

sk_buff分析:

sk_buffLinux网络代码中最重要的结构体之一。它是Linux在其协议栈里传送的结构体,也就是所谓的“包”,在他里面包含了各层协议的头部,比如ethernet, ip ,tcp ,udp等等。也有相关的操作等。熟悉他是进一步了解Linux网络协议栈的基础。

      此结构定义在头文件中,结构体布局大致可分为以下四部分:

l       布局(layout

l       通用(general

l       功能专用(feature-specific

l       管理函数(management functions

网络选项以及内核结构

      我们可以看到在此结构体里有很多预处理,他是在需要指定相应功能时才起作用,我们在这里先对通用的作出分析。

布局字段:

      sk_buff是一个复杂的双向链表,在他结构中有nextprev指针,分别指向链表的下一个节点和前一个节点。并且为了某些需求(不知道是哪些目前)需要很快定位到链表头部,所以还有一个指向链表头部的指针list(我在2.6.25内核没有发现这个指针)

sk_buff_head结构是:

struct sk_buff_head {

      /* These two members must be first. */

      struct sk_buff *next;

      struct sk_buff *prev;

      __u32       qlen; //代表元素节点数目

      spinlock_t      lock; //加锁,防止对表的并发访问

};


  

struct sock *sk

这个指针指向一个套接字sock数据结构。当数据在本地产生或者本地进程接受时,需要这个指针;里面的数据会有tcp/udp和用户态程序使用。如果是转发此指针为NULL

unsigned int len

缓冲区中数据块大小。长度包括:主要缓冲区(head所指)的数据以及一些片断(fragment)的数据。当包在协议栈向上或向下走时,其大小会变,因为有头部的丢弃和添加。

unsigned int data_len

片段中数据大小

unsigned int mac_len

mac包头大小

atomic_t users

      引用计数,使用这个sk_buff的使用者的数目,可能有多个函数要使用同一个sk_buff所以防止提前释放掉,设置此计数

unsigned int truesize

      此缓冲区总大小,包括sk_buffsk_buff只不过是个指针的集合,他所指的才是真正的数据区,所以是两部分。(见下图)

sk_buff_data_t          tail;

sk_buff_data_t          end;

unsigned char      *head, *data;

      这些指针很重要,他们指向的是真正的数据区,他们的边界。headend指向的是数据区的开端和尾端(注意和datatail区别)如下图,datatail指向的是实际数据的开头和结尾。

      因为数据区在协议栈走的时候要一层层添加或去掉一些数据(比如报头)所以申请一块大的足够的内存,然后在往里放东西。真实的实际数据可能用不了这么多,所以用data,tail指向真实的,head,tail指向边界。刚开始没填充数据时前三个指针指向的是一个地方。

 

               

void (*destructor) (…….)

      此函数指针被初始化一个函数,当此缓冲区删除时,完成某些工作。

通用字段

struct timeval stamp2.6.25没有,估计是ktime_t tstamp

      时间戳,表示何时被接受或有时表示包预定的传输时间

struct net_device *dev

      描述一个网络设备,我会以后分析他。

sk_buff_data_t          transport_header; //L4

sk_buff_data_t          network_header; //L3

sk_buff_data_t          mac_header; //L2

这些指针分别指向报文头部,和2.4版本比较有了变化,不再是联合体,使用更加方便了,Linux给出了很方便的函数直接定位到各层的头部。下图是2.4版本的,只是说明一下。

 


struct dst_entry dst

      路由子系统使用。目前不知道怎么回事呢。据说比较复杂。

char cb[40]

      缓冲控制区,用来存储私有信息的空间。比如tcp用这个空间存储一个结构体tcp_skb_cb ,可以用宏TCP_SKB_CB(__skb)定位到他,然后使用里面的变量。

ip_summed:2

__wsum   csum;

      校验和

unsigned char pkt_type

      根据L2层帧的目的地址进行类型划分。

unsigned char cloned

      表示该结构是另一个sk_buff克隆的。

__u32            priority;

      QoS等级

__be16                protocol;

      L2层设备驱动看使用在下一个较高层的协议。

功能专用字段

Linux是模块化的,你编译时可以带上特定功能,比如netfilter等,相应的字段才会生效。应该是那些预定义控制的。

 

管理函数

下面这个图是:a*skb_put; (b*) skb_push; (c*) skb_pull (d*) skb_reserve的使用,主要是对skb_buf所指向的数据区的指针移动。(数据预留以及对齐)

 

 

下图是用skb_reserve函数,把一个14字节的ethernet帧拷贝到缓冲区。skb_reserve(skb, 2) 2表示16字节对齐。14+2=16

 

下图是穿过协议栈从tcp层向下到链路层的过程

 

 

分配内存:

alloc_skb 分配缓冲区和一个sk_buff结构

dev_alloc_skb 设备驱动程序使用的缓冲区分配函数

释放内存:

kfree_skb 只有skb->users计数器为1时才释放

dev_kfree_skb

缓冲区克隆函数 skb_clone

 

列表管理函数:

skb_queue_head_init

      队列初始化

skb_queue_head , skb_queue_tail

      把一个缓冲区添加到队列头或尾

skb_dequeue, skb_dequeue_tail

      从头或尾去掉

skb_queue_purge

      把队列变空

skb_queue_walk

      循环队列每个元素

  1. 内核也新增了几个函数,来提供获取这些偏移的接口:
  2. #ifdef NET_SKBUFF_DATA_USES_OFFSET
  3. 如果使用了offset来表示偏移的话,就是说是一个相对偏移的情况:
  4. static inline unsigned char *skb_transport_header(const struct sk_buff *skb)
  5. {
  6.  return skb->head + skb->transport_header;
  7. }
  8. static inline void skb_reset_transport_header(struct sk_buff *skb)
  9. {
  10.  skb->transport_header = skb->data - skb->head;
  11. }
  12. static inline void skb_set_transport_header(struct sk_buff *skb,
  13.          const int offset)
  14. {
  15.  skb_reset_transport_header(skb);
  16.  skb->transport_header += offset;
  17. }
  18. static inline unsigned char *skb_network_header(const struct sk_buff *skb)
  19. {
  20.  return skb->head + skb->network_header;
  21. }
  22. static inline void skb_reset_network_header(struct sk_buff *skb)
  23. {
  24.  skb->network_header = skb->data - skb->head;
  25. }
  26. static inline void skb_set_network_header(struct sk_buff *skb, const int offset)
  27. {
  28.  skb_reset_network_header(skb);
  29.  skb->network_header += offset;
  30. }
  31. static inline unsigned char *skb_mac_header(const struct sk_buff *skb)
  32. {
  33.  return skb->head + skb->mac_header;
  34. }
  35. static inline int skb_mac_header_was_set(const struct sk_buff *skb)
  36. {
  37.  return skb->mac_header != ~0U;
  38. }
  39. static inline void skb_reset_mac_header(struct sk_buff *skb)
  40. {
  41.  skb->mac_header = skb->data - skb->head;
  42. }
  43. static inline void skb_set_mac_header(struct sk_buff *skb, const int offset)
  44. {
  45.  skb_reset_mac_header(skb);
  46.  skb->mac_header += offset;
  47. }
  48. #else /* NET_SKBUFF_DATA_USES_OFFSET */
  49. 不使用相对偏移的情况
  50. static inline unsigned char *skb_transport_header(const struct sk_buff *skb)
  51. {
  52.  return skb->transport_header;
  53. }
  54. static inline void skb_reset_transport_header(struct sk_buff *skb)
  55. {
  56.  skb->transport_header = skb->data;
  57. }
  58. static inline void skb_set_transport_header(struct sk_buff *skb,
  59.          const int offset)
  60. {
  61.  skb->transport_header = skb->data + offset;
  62. }
  63. static inline unsigned char *skb_network_header(const struct sk_buff *skb)
  64. {
  65.  return skb->network_header;
  66. }
  67. static inline void skb_reset_network_header(struct sk_buff *skb)
  68. {
  69.  skb->network_header = skb->data;
  70. }
  71. static inline void skb_set_network_header(struct sk_buff *skb, const int offset)
  72. {
  73.  skb->network_header = skb->data + offset;
  74. }
  75. static inline unsigned char *skb_mac_header(const struct sk_buff *skb)
  76. {
  77.  return skb->mac_header;
  78. }
  79. static inline int skb_mac_header_was_set(const struct sk_buff *skb)
  80. {
  81.  return skb->mac_header != NULL;
  82. }
  83. static inline void skb_reset_mac_header(struct sk_buff *skb)
  84. {
  85.  skb->mac_header = skb->data;
  86. }
  87. static inline void skb_set_mac_header(struct sk_buff *skb, const int offset)
  88. {
  89.  skb->mac_header = skb->data + offset;
  90. }
  91. #endif /* NET_SKBUFF_DATA_USES_OFFSET */

  92. 1、TCP层获取相关偏移的函数
  93. static inline struct tcphdr *tcp_hdr(const struct sk_buff *skb)
  94. {
  95.  return (struct tcphdr *)skb_transport_header(skb);
  96. }
  97. 这个函数用来获得sk_buff结构中TCP头的指针
  98. static inline unsigned int tcp_hdrlen(const struct sk_buff *skb)
  99. {
  100.  return tcp_hdr(skb)->doff * 4;
  101. }
  102. 这个函数用来获得TCP头的长度
  103. static inline unsigned int tcp_optlen(const struct sk_buff *skb)
  104. {
  105.  return (tcp_hdr(skb)->doff - 5) * 4;
  106. }
  107. 获取tcp option的长度
  108. 2、IP相关的函数
  109. static inline struct iphdr *ip_hdr(const struct sk_buff *skb)
  110. {
  111.  return (struct iphdr *)skb_network_header(skb);
  112. }
  113. 该函数获得ip头
  114. static inline struct iphdr *ipip_hdr(const struct sk_buff *skb)
  115. {
  116.  return (struct iphdr *)skb_transport_header(skb);
  117. }
  118. 该函数获得ipip头,实际上偏移已经跑到了传输层的开始
  119. 3、MAC相关函数
  120. static inline struct ebt_802_3_hdr *ebt_802_3_hdr(const struct sk_buff *skb)
  121. {
  122.  return (struct ebt_802_3_hdr *)skb_mac_header(skb);
  123. }
  124. 获取802.3MAC头指针。

  125. static inline struct ethhdr *eth_hdr(const struct sk_buff *skb)
  126. {
  127.     return (struct ethhdr *)skb_mac_header(skb);
  128. }
  129. 获取以太网MAC头指针。以太网头指针结构体:
  130. struct ethhdr {
  131.     unsigned char h_dest[ETH_ALEN]; /* destination eth addr */
  132.     unsigned char h_source[ETH_ALEN]; /* source ether addr */
  133.     __be16 h_proto; /* packet type ID field */
  134. } __attribute__((packed));

  135. 内核中网络地址转化为字符串形式的IP地址的宏定义:
  136. #define NIPQUAD(addr) \
  137. ((unsigned char *)&addr)[0], \
  138. ((unsigned char *)&addr)[1], \
  139. ((unsigned char *)&addr)[2], \
  140. ((unsigned char *)&addr)[3]
  141. #define NIPQUAD_FMT "%u.%u.%u.%u"

 

写完了,O(_)O~

阅读(604) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~