2014年10月29日 星期三

SKB(struct sk_buff)数据结构的部分分析

出處
other
http://simohayha.iteye.com/blog/556168
skb function 操作

SKB(struct sk_buff)数据结构的部分分析

我参考的原文地址是http://vger.kernel.org/~davem/skb.html,记录在这里主要是为了帮助自己好理解内核中网络部分的代码。
在Linux内核中,socket buffer(SKB)是Linux内核网络协议代码中最重要的基础。每一个发送或者接收的packet的处理都需要用到该数据结构。
下面结合其数据结构的各成员变量来说明其组成元素和在网络处理过程中发挥的作用:
1
2
3
4
5
6
7
8
9
     struct sk_buff {
/* These two members must be first. */
struct sk_buff *next;
struct sk_buff *prev;
struct sk_buff_head *list;
next 和prev是用来实现list链表的功能。packets在许多类型的链表list和队列queues中出现,比如一个TCP socket的发送队列。list参数来指明当前这个packet是在哪个队列之上。
1
struct sock *sk;
sk用来记录与SKB相关的socket.当一个packet到来或者要被发送出去的时候,与它相关的内存必须传给该socket来进行合适的内存计数。
1
struct timeval stamp;
stamp用来记录其时间戳,或者是到来时候的时间或者是发送出去的时间。但是计算时间的耗费是很大的,因此能避免就避免,除非有必要。net_enable_timestamp()和net_disable_timestamp()是与它相关的两个函数。
stamp在截包进行分析时很有用,还有可以用来实现一些特定的socket选项,还有一些net filter模块也会用到stamp。
1
2
3
4
5
     struct net_device *dev;
struct net_device *input_dev;
struct net_device *real_dev;
这三个变量用来帮助记录与该包相关的设备。之所以有这三个不同的网络设备来记录,是因为通过一个虚拟设备的时候,skb->dev可能会被改变。
因此,如果从一个设备A接收到packet,而这个设备A是一个绑定设备实例的一部分,(这个地方没理解作者原文的意思-_-!)。开始的时候skb->dev指向这个设备A,然后会把skb->dev赋给skb->real_dev,而skb->dev会更新为真正被帮定的设备。
同样的,当物理设备收到一个包的时候,它会将自己记录为skb->input_dev。这样,不管虚拟设备存在多少layers,skb->input_dev都能够被找到并确认为从网络上接收数据的真正设备。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
     union {
struct tcphdr *th;
struct udphdr *uh;
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 *arph;
unsigned char *raw;
} nh;
union {
unsigned char *raw;
} mac;
这里保存的是各个协议层的头指针,在构造发送的包和解析接收到的包的时候极其有用。比如,skb->mac.raw被eth_type_trans()设置,当一个ethernet包被接收到的时候。然后我们就可以用skb->mac.raw来确定起mac地址的头。
1
struct dst_entry *dst;
dst是用来记录该包相关的路由。它告诉我们怎样将一个包送到最终目的地;它记录的不仅仅是输入所需的路由,也包括输出的。该数据结构同样复杂,暂不详述。
1
struct sec_path *sp;
不理解,不解释。
1
char cb[40];
它是SKB的控制块。它属于不透明的存储块(private_data?), 经常被协议和一些设备驱动程序用来存储与每个包相关的私有数据。比如,TCP利用它来存储序列号和帧的重传状态。
1
2
3
4
5
6
7
     unsigned int len,
data_len,
mac_len,
csum;
len是指整个packet的大小。data_len是指当SKB是由多个页面缓冲组成的时候,在页面缓冲区域的字节总数(现在还没有研究它的具体意思,因此理解的不清楚,可能是错误的)。
mac_len保存的是MAC头的长度。一般来说它是没有必要维护的,只有在一些特殊的处理时才需要,比如IPSEC。
csum记录着该包的checksum。当我们构建一个往外发的packet的时候,先把数据从userpaace得到数据,然后计算checksum,它被累计在skb->csum中。这能够帮助我们计算最终的checksum。有时候可以忽略,当硬件设备来完成包的checksum时。
在处理输入的情况时,csum可以用来存储由设备计算出来的checksum值。如果设备提示 skb->ip_summed = CHECKSUM_HW,意味着csum是记录从skb->data开始的数据区域的checksum值。
1
2
3
4
5
6
7
8
9
     unsigned char local_df,
cloned:1,
nohdr:1,
pkt_type,
ip_summed;
cloned用来得到对SKB数据的快速的索引,Linux建立起了cloned的概念。
pkt_type存储了该packet的类型信息,如PACKET_*,它的定义在linux/if_packet.h中。包括,PACKET_HOST, PACKET_BOARDCAST, PACKET_MULTICAST等等。
ip_summed记录进行checksum的方式.
1
__u32 priority;
它被用来实现QoS.
1
unsigned short protocol,     security;
protocol域是由像eth_type_trans()这样的方法来初始化的,它被赋予一个类诉ETH_P_*的值,在头文件linux/if_ether.h中有描述。
1
2
3
4
5
     void (*destructor)(struct sk_buff *skb);
//...
unsigned int truesize;
这两个域用来做socket buffer的计数。
1
atomic_t users;
我们使用users域来记录SKB对象的引用情况。相关的函数有skb_get();kfree_skb();
1
2
3
4
5
6
7
unsigned char *head,
*data,
*tail,
*end;
这4个指针是用来管理一个SKB包的线性存储空间的核心.它们表示了SKB缓冲区和数据部分的边界。
对应区域如下图所示:
+——–+ <—– head
|headroom|
|        |
+——–+ <—– data
|  data  |
|        |
|        |
|        |
+——–+ <—– tail
|tailroom|
|        |
+——–+ <—– end

沒有留言:

張貼留言

有敘述錯誤或者是觀念有問題歡迎指正