linux tcpip协议栈分析Word下载.docx
- 文档编号:8165582
- 上传时间:2023-05-10
- 格式:DOCX
- 页数:19
- 大小:214.92KB
linux tcpip协议栈分析Word下载.docx
《linux tcpip协议栈分析Word下载.docx》由会员分享,可在线阅读,更多相关《linux tcpip协议栈分析Word下载.docx(19页珍藏版)》请在冰点文库上搜索。
#ifdefCONFIG_NET_CLS_ACT
tc_verd;
tc_classid;
#endif
}
它表明,tc_index只有在编译时定义了CONFIG_NET_SCHED符号才有效。
这个符号可以通过选择特定的编译选项来定义(例如:
"
DeviceDriversNetworkingsupportNetworkingoptionsQoSand/orfairqueueing"
)。
这些编译选项可以由管理员通过makeconfig来选择,或者通过一些自动安装工具来选择。
前面的例子有两个嵌套的选项:
CONFIG_NET_CLS_ACT(包分类器)只有在选择支持“QoSand/orfairqueueing”时才能生效。
顺便提一下,QoS选项不能被编译成内核模块。
原因就是,内核编译之后,由某个选项所控制的数据结构是不能动态变化的。
一般来说,如果某个选项会修改内核数据结构(比如说,在sk_buff
里面增加一个项tc_index),那么,包含这个选项的组件就不能被编译成内核模块。
你可能经常需要查找是哪个makeconfig编译选项或者变种定义了某个#ifdef标记,以便理解内核中包含的某段代码。
在2.6内核中,最快的,查找它们之间关联关系的方法,就是查找分布在内核源代码树中的kconfig文件中是否定义了相应的符号(每个目录都有一个这样的文件)。
在
2.4内核中,你需要查看Documentation/Configure.help文件。
2.LayoutFields
有些sk_buff成员变量的作用是方便查找或者是连接数据结构本身。
内核可以把sk_buff组织成一个双向链表。
当然,这个链表的结构要比常见的双向链表的结构复杂一点。
就像任何一个双向链表一样,sk_buff中有两个指针next和prev,其中,next指向下一个节点,而
prev指向上一个节点。
但是,这个链表还有另一个需求:
每个sk_buff结构都必须能够很快找到链表头节点。
为了满足这个需求,在第一个节点前面会插入另一个结构sk_buff_head,这是一个辅助节点,它的定义如下:
structsk_buff_head{
/*Thesetwomembersmustbefirst.*/
structsk_buff
*next;
*prev;
qlen;
spinlock_t
lock;
};
qlen代表链表元素的个数。
lock用于防止对链表的并发访问。
sk_buff和sk_buff_head的前两个元素是一样的:
next和prev指针。
这使得它们可以放到同一个链表中,尽管sk_buff_head要比sk_buff小得多。
另外,相同的函数可以同样应用于sk_buff和sk_buff_head。
为了使这个数据结构更灵活,每个sk_buff结构都包含一个指向sk_buff_head的指针。
这个指针的名字是list。
图1会帮助你理解它们之间的关系。
Figure1.Listofsk_buffelements
其他有趣的成员变量如下:
structsock*sk
这是一个指向拥有这个sk_buff的sock结构的指针。
这个指针在网络包由本机发出或者由本机进程接收时有效,因为插口相关的信息被L4(TCP或UDP)或者用户空间程序使用。
如果sk_buff只在转发中使用(这意味着,源地址和目的地址都不是本机地址),这个指针是NULL。
unsignedintlen
这是缓冲区中数据部分的长度。
它包括主缓冲区中的数据长度(data指针指向它)和分片中的数据长度。
它的值在缓冲区从一个层向另一个层传递时改变,因为往上层传递,旧的头部就没有用了,而往下层传递,需要添加本层的头部。
len同样包含了协议头的长度。
unsignedintdata_len
和len不同,data_len只计算分片中数据的长度。
unsignedintmac_len
这是mac头的长度。
atomic_tusers
这是一个引用计数,用于计算有多少实体引用了这个sk_buff缓冲区。
它的主要用途是防止释放sk_buff后,还有其他实体引用这个sk_buff。
因此,每个引用这个缓冲区的实体都必须在适当的时候增加或减小这个变量。
这个计数器只保护sk_buff结构本身,而缓冲区的数据部分由类似的计数器(dataref)来保护.
有时可以用atomic_inc和atomic_dec函数来直接增加或减小users,但是,通常还是使用函数skb_get和kfree_skb来操作这个变量。
unsignedinttruesize
这是缓冲区的总长度,包括sk_buff结构和数据部分。
如果申请一个len字节的缓冲区,alloc_skb函数会把它初始化成len+sizeof(sk_buff)。
structsk_buff*alloc_skb(unsignedintsize,intgfp_mask)
{
skb->
truesize=size+sizeof(structsk_buff);
当skb->
len变化时,这个变量也会变化。
unsignedchar*head
unsignedchar*end
unsignedchar*data
unsignedchar*tail
它们表示缓冲区和数据部分的边界。
在每一层申请缓冲区时,它会分配比协议头或协议数据大的空间。
head和end指向缓冲区的头部和尾部,而data和tail指向实际数据的头部和尾部,参见图2。
每一层会在head和data之间填充协议头,或者在tail和end之间添加新的协议数据。
图2中右边数据部分会在尾部包含一个附加的头部。
Figure2.head/endversusdata/tailpointers
void(*destructor)(...)
这个函数指针可以初始化成一个在缓冲区释放时完成某些动作的函数。
如果缓冲区不属于一个socket,这个函数指针通常是不会被赋值的。
如果缓冲区属于一个socket,这个函数指针会被赋值为sock_rfree或sock_wfree(分别由skb_set_owner_r或skb_set_owner_w函数初始化)。
这两个sock_xxx函数用于更新socket的队列中的内存容量。
3.GeneralFields
本节描述sk_buff的主要成员变量,这些成员变量与特定的内核功能无关:
structtimevalstamp
这个变量只对接收到的包有意义。
它代表包接收时的时间戳,或者有时代表包准备发出时的时间戳。
它在netif_rx里面由函数net_timestamp设置,而netif_rx是设备驱动收到一个包后调用的函数。
structnet_device*dev
这个变量的类型是net_device,net_device它代表一个网络设备。
dev的作用与这个包是准备发出的包还是刚接收的包有关。
当收到一个包时,设备驱动会把sk_buff的dev指针指向收到这个包的设备的数据结构,就像下面的vortex_rx里的一段代码所做的一样,这个函数属于3c59x系列以太网卡驱动,用于接收一个帧。
(drivers/net/3c59x.c):
staticintvortex_rx(structnet_device*dev)
dev=dev;
protocol=eth_type_trans(skb,dev);
netif_rx(skb);
/*Passthepackettothehigherlayer*/
}
当一个包被发送时,这个变量代表将要发送这个包的设备。
在发送网络包时设置这个值的代码要比接收网络包时设置这个值的代码复杂。
有些网络功能可以把多个网络设备组成一个虚拟的网络设备(也就是说,这些设备没有和物理设备直接关联),并由一个虚拟网络设备驱动管理。
当虚拟设备被使用时,dev指针指向虚拟设备的net_device结构。
而虚拟设备驱动会在一组设备中选择一个设备并把dev指针修改为这个设备的net_device结构。
因此,在某些情况下,指向传输设备的指针会在包处理过程中被改变。
structnet_device*input_dev
这是收到包的网络设备的指针。
如果包是本地生成的,这个值为NULL。
对以太网设备来说,这个值由eth_type_trans初始化,它主要被流量控制代码使用。
structnet_device*real_dev
这个变量只对虚拟设备有意义,它代表与虚拟设备关联的真实设备。
例如,Bonding和VLAN设备都使用它来指向收到包的真实设备。
union{...}h
union{...}nh
union{...}mac
这些是指向TCP/IP各层协议头的指针:
h指向L4,nh指向L3,mac指向L2。
每个指针的类型都是一个联合,包含多个数据结构,每一个数据结构都表示内核在这一层可以解析的协议。
例如,h是一个包含内核所能解析的L4协议的数据结构的联合。
每一个联合都有一个raw变量用于初始化,后续的访问都是通过协议相关的变量进行的。
当接收一个包时,处理n层协议头的函数从n-1层收到一个缓冲区,它的skb->
data指向n层协议的头。
处理n层协议的函数把本层的指针(例如,L3对应的是skb->
nh指针)初始化为skb->
data,因为这个指针的值会在处理下一层协议时改变(skb->
data将被初始化成缓冲区里的其他地址)。
在处理n层协议的函数结束时,在把包传递给n+1层的处理函数前,它会把skb->
data指针指向n层协议头的末尾,这正好是n+1层协议的协议头(参见图3)。
发送包的过程与此相反,但是由于要为每一层添加新的协议头,这个过程要比接收包的过程复杂。
Figure3.Header'
spointerinitializationswhilemovingfromlayertwotolayerthree
structdst_entrydst
这个变量在路由子系统中使用。
charcb[40]
这是一个“controlbuffer”,或者说是一个私有信息的存储空间,由每一层自己维护并使用。
它在分配sk_buff结构时分配(它目前的大小是40字节,已经足够为每一层存储必要的私有信息了)。
在每一层中,访问这个变量的代码通常用宏实现以增强代码的可读性。
例如,TCP用这个变量存储tcp_skb_cb结构,这个结构在include/net/tcp.h中定义:
structtcp_skb_cb{
seq;
/*Startingsequencenumber*/
end_seq;
/*SEQ+FIN+SYN+datalen*/
when;
/*usedtocomputertt'
s
*/
__u8
flags;
/*TCPheaderflags.
};
下面这个宏被TCP代码用来访问cb变量。
在这个宏里面,有一个简单的类型转换:
#defineTCP_SKB_CB(__skb)
((structtcp_skb_cb*)&
((__skb)->
cb[0]))
下面的例子是TCP子系统在收到一个分段时填充相关数据结构的代码:
inttcp_v4_rcv(structsk_buff*skb)
th=skb->
h.th;
TCP_SKB_CB(skb)->
seq=ntohl(th->
seq);
end_seq=(TCP_SKB_CB(skb)->
seq+th->
syn+th->
fin+
len-th->
doff*4);
ack_seq=ntohl(th->
ack_seq);
when=0;
flags=skb->
nh.iph->
tos;
sacked=0;
如果想要了解cb中的参数是如何被取出的,可以查看net/ipv4/tcp_output.c中的tcp_transmit_skb函数。
这个函数被TCP用于向IP层发送一个分段。
unsignedintcsum
unsignedcharip_summed
表示校验和以及相关状态标记。
unsignedcharcloned
一个布尔标记,当被设置时,表示这个结构是另一个sk_buff的克隆。
在“克隆和拷贝缓冲区”一节中有描述。
unsignedcharpkt_type
这个变量表示帧的类型,分类是由L2的目的地址来决定的。
可能的取值都在include/linux/if_packet.h中定义。
对以太网设备来说,这个变量由eth_type_trans函数初始化。
类型的可能取值如下:
PACKET_HOST
包的目的地址与收到它的网络设备的L2地址相等。
换句话说,这个包是发给本机的。
.PACKET_MULTICAST
包的目的地址是一个多播地址,而这个多播地址是收到这个包的网络设备所注册的多播地址。
PACKET_BROADCAST
包的目的地址是一个广播地址,而这个广播地址也是收到这个包的网络设备的广播地址。
PACKET_OTHERHOST
包的目的地址与收到它的网络设备的地址完全不同(不管是单播,多播还是广播),因此,如果本机的转发功能没有启用,这个包会被丢弃。
PACKET_OUTGOING
这个包将被发出。
用到这个标记的功能包括Decnet协议,或者是为每个网络tap都复制一份发出包的函数。
PACKET_LOOPBACK
这个包发向loopback设备。
由于有这个标记,在处理loopback设备时,内核可以跳过一些真实设备才需要的操作。
PACKET_FASTROUTE
这个包由快速路由代码查找路由。
快速路由功能在2.6内核中已经去掉了。
__u32priority
这个变量描述发送或转发包的QoS类别。
如果包是本地生成的,socket层会设置priority变量。
如果包是将要被转发的,rt_tos2priority函数会根据ip头中的Tos域来计算赋给这个变量的值。
这个变量的值与DSCP(DiffServCodePoint)没有任何关系。
unsignedshortprotocol
这个变量是高层协议从二层设备的角度所看到的协议。
典型的协议包括IP,IPV6和ARP。
完整的列表在include/linux/if_ether.h中。
由于每个协议都有自己的协议处理函数来处理接收到的包,因此,这个域被设备驱动用于通知上层调用哪个协议处理函数。
每个网络驱动都调用netif_rx来通知上层网络协议的协议处理函数,因此protocol变量必须在这些协议处理函数调用之前初始化。
unsignedshortsecurity
这是包的安全级别。
这个变量最初由IPSec子系统使用,但现在已经作废了。
4.Feature-SpecificFields
linux内核是模块化的,你可以选择包含或者删除某些功能。
因此,sk_buff结构里面的一些成员变量只有在内核选择支持某些功能时才有效,比如防火墙(netfilter)或者qos:
unsignedlongnfmark
__u32nfcache
__u32nfctinfo
structnf_conntrack*nfct
unsignedintnfdebug
structnf_bridge_info*nf_bridge
这些变量被netfilter使用(防火墙代码),内核编译选项是“DeviceDrivers->
Networkingsupport->
Networkingoptions->
Networkpacketfiltering”和两个子选项“Networkpacketfilteringdebugging”和“BridgedIP/ARPpacketsfiltering”
union{...}private
这个联合结构被高性能并行接口(HIPPI)使用。
相应的内核编译选项是“Device->
Drivers->
Networkingsupport->
Networkdevicesupport->
HIPPIdriversupport”
__u32tc_index
__u32tc_verd
__u32tc_classid
这些变量被流量控制代码使用,内核编译选项是“DeviceDrivers->
Networking->
support->
Networkingoptions->
QoSand/orfairqueueing”和它的子选项“PacketclassifierAPI”
structsec_path*sp
这个变量被IPSec协议用于跟踪传输的信息。
5.ManagementFunctions
有很多函数,通常都比较短小而且简单,内核用这些函数操作sk_buff的成员变量或者sk_buff
链表。
图4会帮助我们理解其中几个重要的函数。
我们首先来看分配和释放缓冲区的函数,然后是一些通过移动指针在缓冲区的头部或尾部预留空间的函数。
如果你看过include/linux/skbuff.h和net/core/skbuff.c中的函数,你会发现,基本上每个函数都有两个版本,名字分别是do_something和__do_something。
通常第一种函数是一个包装函数,它会在第二种函数的基础上增加合法性检查或者锁。
一般来说,类似__do_something的函数不能被直接调用(除非满足特定的条件,比如说锁)。
那些违反这条规则而直接引用这些函数的不良代码会最终被更正。
Figure4.Beforeandafter:
(a)skb_put,(b)skb_push,(c)skb_pull,and(d)skb_reserve
5.1.Allocatingmemory:
alloc_skbanddev_alloc_skb
alloc_skb是net/core/skbuff.c里面定义的,用于分配缓冲区的函数。
我们已经知道,数据缓冲区和缓冲区的描述结构(sk_buff结构)是两种不同的实体,这就意味着,在分配一个缓冲区时,需要分配两块内存(一个是缓冲区,一个是缓冲区的描述结构sk_buff)。
alloc_skb调用函数kmem_cache_alloc从缓存中获取一个sk_buff结构,并调用kmalloc分配缓冲区(如果有缓存的话,它同样从缓存中获取内存)。
简化后的代码如下:
skb=kmem_cache_alloc(skbuff_head_cache,gfp_mask&
~__GFP_DMA);
size=SKB_DATA_ALIGN(size);
data=kmalloc(size+sizeof(structskb_shared_info),gfp_mask);
在调用kmalloc前,size参数通过SKB_DATA_ALIGN宏强制对齐。
在函数返回前,它会初始化结构中的一些变量,最后的结构如图5所示。
在图5右边所示的内存块的底部,你能看到对齐操作所带来的填充区域。
Figure5.alloc_skbfunction
dev_alloc_skb也是一个缓冲区分配函数,它主要被设备驱动使用,通常用在
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- linux tcpip协议栈分析 tcpip 协议 分析
![提示](https://static.bingdoc.com/images/bang_tan.gif)