这一节讨论sk_buff的主要字段,而这些字段都与特定的内核功能无关。
struct timeval stamp;
通常只对一个已经接收的封包才有意义,这是一个时间戳,用于避哦啊是封包何时被接收,或者有时候用于表示封包预定传输的时间,此字段由netif_rx 函数用net_timestamp设置,而该函数在接收每个封包之后由设备驱动程序调用,对比在第二十一章给出了描述。
struct net_device *dev;
此段描述一个网络设备,其类型net_device 将在本章稍后做出详细说明,由dev所代表的设备的角色,依赖于存储在该缓冲区内的封包是即将传输的还是刚被接收的而定。
当接收到一个封包时,设备驱动程序会用代表接收接口的数据结构的指针更新此字段,如下列取自vortex_rx的代码片段所示,当接收到一帧时,3c95x系列的以太网卡的驱动程序就会调用该函数(在 drivers/net/3c59x.c中)
static int vortex_rx(struct net_device *dev)
{
skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);//把封包传到较高的分层。
}
当传输一个封包时,此参数代表的是发送封包的设备,设置此值的代码要比接收封包更为复杂,所以把相关讨论推迟到二十一章和三十五章进行。
有些网络功能允许一些设备按组集合起来代表转移的虚拟借口,由一个虚拟设备驱动程序提供接口服务,当该设备驱动程序被调用时,dev参数会指向此虚拟设备的net_device 数据结构,驱动程序会从其群组中选择一个特定的设备,然后把dev参数改为指向该设备net_devoce数据结构,因此,在这些情况下,在封包处理期间传输设备的指针可能会变化。
struct net_device *input_dev
这是已经被接收的封包所源自的设备,当封包是由本地产生时,其为NULL指针,就Ethernet 设备而言,字段在eth_type_trans中初始化,此字段主要由流量控制traffic control使用。
struct net_device *real_dev
此字段只对虚拟设备有意义,代表的是与虚拟设备所关联的真实设备,例如,Bonding和VLAN接口使用该字段,用以记下真实设备的输入流量是从什么地方接收来的。
union {...} h
union {...} nh
union {...} mac
这些是指向TCP/IP协议栈的报头的指针,h针对L4, nh针对L3, 而mac针对L2,每个字段都指向一个由各种结构组成的联合,每个协议的结构都是由内核中该曾来解析,例如h是一个联合,内核所解释的每个L4协议在报头h中都有一个字段,每个联合都有一个名为raw的成员用于初始化,而后续所有的访问都是通过协议指定的成员。
当接收一个数据封包时,负责处理第n层报头的函数,会从第n-1层接收一个缓冲区,而该缓冲区的skb->data 指向第n层报头的开端,处理第n层的函数会为该曾初始化适当的指针,skb->nh 。用以保存skb->data 字段,因为在下一层进行处理时,skb->data 会设成缓冲区内另一个不同的偏移量。这个指针的内容就会丢失,接着,此函数完成第n层的处理,把封包传给第n+1层的处理函数钱,先更新skb->data, 使其指向第n层报头的尾端,也就是第n + 1层报头的开始。
传送一个封包就是此过程的逆过程,而所增加的复杂度就是在每一个层上增加一个新的报头。
struct dst_entry dst
这个结构由路由子系统使用。由于这个数据结构相当复杂,需要了解其他子系统如何工作的知识,因此,推迟到第七部分再予以描述。
char cb[40]
这是一个控制缓冲区control buffer, 或者说是私有信息的存储空间,为每一个层内部使用起维护的作用,该字段在sk_buff结构内静态分配(目前的大小是40个字节),而且容量足以容纳每个层所需要的私有数据。在每一个层的代码中都是通过宏进行访问的,这样使得代码更具有可读性。例如,tcp使用这个空间来存储一个tcp_skb_cb数据结构,该数据结构定义在include /net/tcp.h中。
struct tcp_skb_cb {
__u32 seq;//起始序列号编号
__u32 end_seq; //SEQ + FIN +SYN + datalen
__u32 wlen;//用于计算rtt's
__u8 flags;//TCP报头标志
}
以下是TCP代码用于访问该结构的宏,该宏由一些简单的指针组成。
#define TCP_SKB_CB(__skb) ((struct tcp_skb_cb*)& ((__skb))->cb[0])
以下是TCP子系统在段的收条中填写此结构的案例
int tcp_v4_rcv(struct sk_buff *skb)
{
th = skb->h.th;
TCP_SKB_CB(skb)->seq = ntohl(th->seq);
TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin + skb->len - th->doff * 4);
TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq);
TCP_SKB_CB(skb)->wlen = 0;
TCP_SKB_CB(skb)->flags = skb->nh.iph->tos;
TCP_SKB_CB(skb)->sacked = 0;
}
为了了解cb缓冲区中的参数是如何找回的,可以看一下net/ipv4/tcp_output.c中的tcp_transmit_skb 。tcp用该函数把一个数据段压入IP层以便于传输。
在第二十二章你还将了解IPV4如何采用cb存储有关IP分片的信息。
unsigned int csum
unsigned char ip_sumed
这些代表校验和(checksum)以及相关联的状态标识,其用法将在第十九章予以描述。
unsigned char cloned
当一个boolean 标识置位时,表示该结构是另一个sk_buff 缓冲区的克隆,参见稍后 缓冲区的克隆和拷贝一节。
unsigned char pkt_type
此字段会根据帧的L2目的地址进行类型划分,可能的取值列于include/linux/if_packet.h 中,对Ethernet 设备而言,此参数由函数eth_type_trans 进行初始化,在第十三章中将予以描述。
PACKET_HOST
已经收帧的目的地址,即接收接口,换句话说, 封包已经达到目的地。
PACKET_MULTICAST
已接收的帧的目的地址是该接口已经注册的多播地址之一。
PACKET_BRODCAST
已接收的帧的目的地址是接收接口的广播地址。
PACKET_OTHERHOST
已经接收的帧的目的地址不属于与该接口相匹配的地址,多播以及广播。因此如果转发机制使能,该帧不得不被转发,否则就会被丢弃。
PACKET_OUTGOING
封包正被发送,此标识的用户是Decnet 协议,并且给每个分流器一个输出封包副本的函数。
PACKET_LOOPBACK
封包正传送到回环设备,由于此标识,当处理回环设备时,内核可以跳过一些真实设备所需要的操作。
PACKET_FASTROUTE
用fastroute 功能路由封包,
第十三章详细描述了这些值如何根据L2目的地址而设置。
__u32 priority
此字段表示正被传输或者转发的封包Qos 登记,如果该封包由本地产生,套接字层灰定义优先级的值,相反的,如果此封包正被转发,则函数rt_tos2priority 会根据IP报头本身的Tos 字段的值而定义此字段的值,此函数的值和第十八章所描述的DSCP 无关,将在第二十六章的ip_forward 函数,一节中讨论其角色。
unsigned short protocol
从L2层的设备驱动程序的角度看,就是用在下一个较高层的协议,典型的协议有IP,IPV6以及ARP,完整的列表可以在include/linux/if_ether.h中找到,由于每种协议都有自己的函数处理例程来处理输入的封包,因此,驱动程序使用这个字段通知其上层使用哪个处理例程,每个驱动程序会调用netif_rx来启动上面的网络 分层的处理例程,所以,在该函数调用前protocol字段必须初始化,更多细节参见第十章和第十三章。
unsigned short security
这是封包的安全级,最初引入这个字段是为了IPsec, 现在已经不再使用了。