linuxip协议栈源代码分析pdfWord格式文档下载.docx
- 文档编号:17156012
- 上传时间:2022-11-28
- 格式:DOCX
- 页数:7
- 大小:19.52KB
linuxip协议栈源代码分析pdfWord格式文档下载.docx
《linuxip协议栈源代码分析pdfWord格式文档下载.docx》由会员分享,可在线阅读,更多相关《linuxip协议栈源代码分析pdfWord格式文档下载.docx(7页珍藏版)》请在冰豆网上搜索。
详细地说,netfilter/iptables的体系结构可以分为三个大部分:
filter的hook机制
netfilter的通用框架不依赖于具体的协议,而是为每种网络协议定义一套hook函数。
这些hook函数在数据报经过协议栈的几个关键点时被调用,在这几个点中,协议栈将数据报及hook函数标号作为参数,传递给netfilter框架。
对于它在网络堆栈中增加的这些hook,内核的任何模块可以对每种协议的一个或多个hook进行注册,实现挂接。
这样当某个数据报被传递给netfilter框架时,内核能检测到是否有任何模块对该协议和hook函数进行了注册。
若注册了,则调用该模块的注册时使用的回调函数,这样这些模块就有机会检查、修改、丢弃该数据报及指示netfilter将该数据报传入用户空间的队列。
这样,hook提供了一种方便的机制:
在数据报通过linux内核的不同位置上截获和操作处理数据报。
2.iptables基础模块
iptables基础模块实现了三个表来筛选各种数据报,具体地讲,linux2.4内核提供的这三种数据报的处理功能是相互间独立的模块,都基于netfilter的hook函数和各种表、链实现。
这三个表包括:
filter表,nat表以及mangle表。
3.具体功能模块
1.
1.数据报过滤模块
2.连接跟踪模块(conntrack)
3.网络地址转换模块(nat)
4.数据报修改模块(mangle)
5.其它高级功能模块
于是,netfilter/iptables总体架构如图
三、hook的实现
filter-ipv4中的hook
netfilter模块需要使用hook来启用函数的动态钩接,它在ipv4中定义了五个hook(位于文件include/linux/netfilter_ipv4.h,line39),分别对应0-4的hooknum
简单地说,数据报经过各个hook的流程如下:
数据报从进入系统,进行ip校验以后,首先经过第一个hook函数
nF_ip_pRe_Routing进行处理;
然后就进入路由代码,其决定该数据报是需要转发还是发给本机的;
若该数据报是发被本机的,则该数据经过hook函数nF_ip_local_in处理以后然后传递给上层协议;
若该数据报应该被转发则它被nF_ip_FoRwaRd处理;
经过转发的数据报经过最后一个hook函数nF_ip_post_Routing处理以后,再传输到网络上。
本地产生的数据经过hook函数nF_ip_local_out处理后,进行路由选择处理,然后经过nF_ip_post_Routing处理后发送出去。
总之,这五个hook所组成的netfilter-ipv4数据报筛选体系如图
详细地说,各个hook及其在ip数据报传递中的具体位置如图
nF_ip_pRe_Routing(0)
数据报在进入路由代码被处理之前,数据报在ip数据报接收函数ip_rcv()(位于net/ipv4/ip_input.c,line379)的最后,也就是在传入的数据报被处理之前经过这个hook。
在ip_rcv()中挂接这个hook之前,进行的是一些与类型、长度、版本有关的检查。
经过这个hook处理之后,数据报进入ip_rcv_finish()(位于
net/ipv4/ip_input.c,line306),进行查路由表的工作,并判断该数据报是发给本地机器还是进行转发。
在这个hook上主要是对数据报作报头检测处理,以捕获异常情况。
涉及功能(优先级顺序):
conntrack(-200)、mangle(-150)、dnat(-100)
nF_ip_local_in
(1)
目的地为本地主机的数据报在ip数据报本地投递函数ip_local_deliver()(位于net/ipv4/ip_input.c,line290)的最后经过这个hook。
经过这个hook处理之后,数据报进入ip_local_deliver_finish()(位于net/ipv4/ip_input.c,line219)
这样,iptables模块就可以利用这个hook对应的input规则链表来对数据报进行规则匹配的筛选了。
防火墙一般建立在这个hook上。
涉及功能:
mangle(-150)、filter(0)、snat(100)、conntrack(int_max-1)
nF_ip_FoRwaRd
(2)
目的地非本地主机的数据报,包括被nat修改过地址的数据报,都要在ip数据报转发函数ip_forward()(位于net/ipv4/ip_forward.c,line73)的最后经过这个hook。
经过这个hook处理之后,数据报进入ip_forward_finish()(位于
net/ipv4/ip_forward.c,line44)
另外,在net/ipv4/ipmr.c中的ipmr_queue_xmit()函数(line1119)最后也会经过这个hook。
(ipmr为多播相关,估计是在需要通过路由转发多播数据时的处理)
这样,iptables模块就可以利用这个hook对应的FoRwaRd规则链表来对数据报进行规则匹配的筛选了。
mangle(-150)、filter(0)
nF_ip_local_out(3)
本地主机发出的数据报在ip数据报构建/发送函数ip_queue_xmit()(位于net/ipv4/ip_output.c,line339)、以及ip_build_and_send_pkt()(位于
net/ipv4/ip_output.c,line122)的最后经过这个hook。
(在数据报处理中,前者最为常用,后者用于那些不传输有效数据的syn/ack包)
经过这个hook处理后,数据报进入ip_queue_xmit2()(位于
net/ipv4/ip_output.c,line281)
另外,在ip_build_xmit_slow()(位于net/ipv4/ip_output.c,line429)和ip_build_xmit()(位于net/ipv4/ip_output.c,line638)中用于进行错误检测;
在igmp_send_report()(位于net/ipv4/igmp.c,line195)的最后也经过了这个hook,进行多播时相关的处理。
这样,iptables模块就可以利用这个hook对应的output规则链表来对数据报进行规则匹配的筛选了。
conntrack(-200)、mangle(-150)、dnat(-100)、filter(0)
nF_ip_post_Routing(4)
所有数据报,包括源地址为本地主机和非本地主机的,在通过网络设备离开本地主机之前,在ip数据报发送函数ip_finish_output()(位于net/ipv4/ip_output.c,line184)的最后经过这个hook。
经过这个hook处理后,数据报进入ip_finish_output2()(位于
net/ipv4/ip_output.c,line160)另外,在函数ip_mc_output()(位于
net/ipv4/ip_output.c,line195)中在克隆新的网络缓存skb时,也经过了这个hook进行处理。
mangle(-150)、snat(100)、conntrack(int_max)
其中,入口为net_rx_action()(位于net/core/dev.c,line1602),作用是将数据报一个个地从cpu的输入队列中拿出,然后传递给协议处理例程。
出口为dev_queue_xmit()(位于net/core/dev.c,line1035),这个函数被高层协议的实例使用,以数据结构structsk_buff*skb的形式在网络设备上发送数据报。
2.hook的调用
hook的调用是通过宏nF_hook实现的,其定义位于
include/linux/netfilter.h,line122:
#definenF_hook(pf,hook,skb,indev,outdev,okfn)/
(list_empty(/*userid*/
structlist_headlist;
}
3.
针对上述user_queue链表,
要求以队列方式向其中依次添加10个类型为structuser的宿主节点,并要求这10个宿主节点的id依次为1-10
4.依次遍历输出这10个宿主节点的id
5.从队列中删除首个宿主节点,然后依次遍历该队列并输出余下
各宿主节点的id值
四、
实现原理
linux的内核源文件list.h提供了所有的链表定义、各类操作接口及其实现。
其中
创建链表的方法如下:
list_head(my_list);
内核源文件list.h中定义了以下若干接口,用于对通用链表进行各类操作:
1)
在指定的head后插入新节点,常用于堆栈数据结构的实现//@new:
即将添加的新链表节点
//@head:
在此节点后添加
list_add(structlist_head*new,structlist_head*head);
2)
在指定的head前插入新节点,常用于队列数据结构的实现//@new:
在此节点前添加
list_add_tail(structlist_head*new,structlist_head*head)3)
从链表中删除一个指定节点
//@entry:
要从链表中删除的链表节点
list_del(structlist_head*entry)
4)
根据当前链表节点指针ptr获得宿主节点指针
//*@ptr:
structlist_head类型的指针
//*@type:
链表节点所在的宿主节点的类型
//*@member:
嵌入宿主的链表节点的变量名
list_entry(ptr,type,member)
5)
遍历链表
//@pos:
遍历链表时用于指示正在遍历的链表节点的指针//@head:
链表头
list_for_each(pos,head)
五、
实现代码和运行结果
#include
#include"
list.h"
list_head(user_quene);
intmain()
{
structuseruid[10];
structlist_head*pos;
inti;
for(i=0;
i list_add_tail(
uid[i].id=i+1;
list_for_each(pos,
printf("
\n"
);
list_del(
return0;
}structuser{intid;
/*userid*/structlist_headlist;
};
实验二、
实验项目名称:
一、
实验目的和要求:
学习linux内核的通用哈希链表的设计原理,
熟练掌握linux内核通用哈希链表的使用。
二、
实验内容
1.掌握linux通用哈希链表的创建
2.掌握通用哈希表增加元素、查找元素的方法。
三、
实验要求
待创建的哈希表头数组为structhlist_headuser_hash[16],要求对哈希表宿主元素的
name成员的值进行散列,并将散列值作为哈希表宿主元素的key。
2.
作为哈希表元素的宿主节点类型定义如下:
structusermap{
structhlist_nodehlist;
unsignedcharname[8];
};
针对上述user_hash哈希表,
要求向其中添加3个类型为structusermap的宿主元素,并要求这3个宿主元素的name成员分别为"
smith"
"
john"
bob"
。
4.
向哈希表user_hash中添加第4个宿主元素。
若新宿主元素的name成员已经存在
(例如"
)
,则提示已经存在该用户,否则向哈希表中添加该宿主元素。
四、
linux的内核源文件list.h提供了哈希表各类操作接口及其实现。
其中创建具有16
个key值的哈希表的方法如下:
structhlist_headuser_hash[16];
在上述user_hash数组的16个元素中存放的哈希表头元素定义如下:
structhlist_head{
structhlist_node*first;
哈希表节点元素定义如下:
structhlist_node{
structhlist_node*next,**pprev;
本实验对哈希表宿主元素节点的name值进行散列的算法如下:
unsignedintbkdRhash(unsignedchar*str)
unsignedintseed=131;
unsignedinthash=0;
while(*str){
hash=hash*seed+(*str++);
return(hash
//返回此*str所对应的哈希值
于是,本实验中对一个字符串name求最终哈希值hash的方法如下:
unsignedinthash=bkdRhash(name)*prev;
内核把sk_buff组织成为双向链表,为了每个skb能被头部快速找到,在第一个skb节点的前面会插入另一个辅助的sk_buff_head结构的头结点。
structsk_buff_head{
__u32qlen;
//skb链表的节点数,队列长度/*thesetwomembersmustbefirst.*/structsk_buffstructsk_buff*next;
*prev;
spinlock_tlock;
//对链表并发操作需要自旋锁
数据存储相关
structsock*sk;
skb的宿主传输控制块。
skb在由本地产生或者本地接收的时候才有效。
当skb在2层或者3层转发的时候,没有意义,null。
unsignedint
len,mac_len,data_lenlen:
skb中数据部分的长度。
mac_len:
以太帧首部长度data_len:
sg类型和FRaglist类型聚合分散i/o存储区中的数据长度。
users;
atomic_t
引用计数,标识有多少个实体在使用skb,以此来确定释放skb的时机。
truesize;
unsignedint
skb的实际长度,包括skb描述符和数据缓存区的长度。
如果申请了一个len字节的缓存区,alloc_skb()会将truesize初始化成len+sizeof(sk_buff)unsignedchar
*head,*data,*tail,*end;
发送数据时,每一层协议会在head与data之间填充协议首部。
通用的成员变量
structskb_timeval
tstamp;
接收时间戳或者发送时间戳,一般在网络设备收到数据包以后通过netif_receive_skb()调用net_timestamp()进行设置
structnet_device*dev;
网络设备指针,该字段的设置与skb是发送包还是接收包有关。
在初始化网络设备驱动的时候会分配接受缓存队列,将该指针指向接收到数据包的网络设备。
发送数据包时这个字段的设置要复杂的多,见后面。
linux支持虚拟网络设备,dev可能会指向虚拟网络设备。
数据包在输入或者输出的时候dev的指针可能会在包处理过程中被改变。
structnet_device*input_dev;
接收报文最原始的设备,本地生成为null。
union{}h、union{}nh、union{}mac
分别指向四层、三层、二层协议的首部,联合体内表示能解析的协议。
报文从二层向三层传递时指针的变化:
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- linuxip 协议 源代码 分析 pdf
![提示](https://static.bdocx.com/images/bang_tan.gif)