版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、Netfilter框架的设计与实现1. 什么是NetfilerLinux 从2.4.X 开始,引入了Netfilter,代替了原来的ipchain,什么是Netfilter呢?有人将它称为“Linux下一个优秀的防火墙工具”,这样 讲,有一定的道理,但是却是很片面的。Netfilter 更准确地讲是Linux 内核中,一个包过滤框架,默认地,它在这个框架上实现了包过滤、状态检测、网络地址转换和包标记等多种功能,因为它设计的开放性,任何有内核开发经验的开 发人员,也可以很容易地利用它提供接口,在内核的数据链路层、网络层,实现自己的功能模块。Netfilter的用户空间管理工具,是著名的 ipta
2、bles 工具套件。Netfilter框架之所以能实现许多强大的功能,是因为它在内核若干网络转发的关键函数,设计了许多巧妙的钩子函数,比如数据转发,由两个主要函数A 和B函数实现,流程为A->B ,现在改变为A->钩子函数->B,就这么简单,在本章里,就让我们来看看Netfilter框架的设计与实现。2. 从NF_HOOK 谈起在整个Netfilter中,NF_HOOK宏占有重要的作用,它定义在Netfilter.h中:CODE:/* This is gross, but inline doesn't cut it for avoiding the function
3、call in fast path: gcc doesn't inline (needs value tracking?). -RR */#ifdef CONFIG_NETFILTER_DEBUG#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (int _ret; if (_ret=nf_hook_slow(pf, hook, &(skb), indev, outdev, okfn, INT_MIN) = 1) _ret = (okfn)(skb); _ret;)#define NF_HOOK_THRESH(pf, hoo
4、k, skb, indev, outdev, okfn, thresh) (int _ret; if (_ret=nf_hook_slow(pf, hook, &(skb), indev, outdev, okfn, thresh) = 1) _ret = (okfn)(skb); _ret;)#else#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (int _ret; if (list_empty(&nf_hookspfhook) | (_ret=nf_hook_slow(pf, hook, &(skb), i
5、ndev, outdev, okfn, INT_MIN) = 1) _ret = (okfn)(skb); _ret;)#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh) (int _ret; if (list_empty(&nf_hookspfhook) | (_ret=nf_hook_slow(pf, hook, &(skb), indev, outdev, okfn, thresh) = 1) _ret = (okfn)(skb); _ret;)#endif一开始就摆出这么长一个宏出来,这个
6、宏稍长了一点,缩短来看:CODE:#ifdef CONFIG_NETFILTER_DEBUG #else #endif我们暂且忽略用于调试作用的语句,把 #else中NF_HOOK的定义提取出来:CODE:#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)
7、160; (int _ret;
8、if (list_empty(&nf_hookspfhook) | (_ret=nf_hook_slow(pf, hook, &(skb), indev, outdev, okfn, INT_MIN) =
9、 1) _ret = (okfn)(skb); _ret;)首 先来看这个宏里边的二维数组nf_hookspfhook ,其中pf对应协
10、议簇,hook对应了某个hook点,比如ipv4 协议簇(PF_INET)下有一个钩子NF_IP_PRE_ROUTING(路由查找之前),那么这个Hook点对应的这个二维数组中的元素就是 nf_hooksPF_INET NF_IP_PRE_ROUTING。nf_hooks数组是一个struct nf_hooks_ops 结构,这个结构有一个hook成员,指向这个hook点的hook函数。另一方面,同一个hook点,可能同时注册了多个hook,所以,结构 struct nf_hooks_ops 中有一个list成员,用来维护一个hook点的链表。该结构定义如下:CODE:struct nf_ho
11、ok_ops struct list_head list; /链表成员 /* User fills in from here down. */ nf_hookfn *hook; /链子函数指针 struct module *owner; int pf; /协议簇,对于ipv4而言,是PF_INET int hooknum; /hook类型 /* Hooks are ordered in ascending priority. */ int priority; /优先级;在一个结构中,内嵌一个struct list_head 类型的list成员,用来维护一个双向链表,是Linux 内核双向链表的
12、标准用法。而list_empty函数用以判断这个链表是否为空。所以,整个NF_HOOK,其含义为:如果当前pf协议的当前hook类型没有定义钩子函数,则直接执行okfn指向的函数,否则,就调用函数nf_hook_slow函数,当该函数返回值为1,仍继续调用okfn指向的函数举个例子来讲,ip_input.c中,ip_rcv 函数用于处理本机接受的数据包,处理完成后,按照正常顺序会调用 ip_rcv_finish函数,我们来看ip_rcv的最后一句:CODE:return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,ip_rcv_fini
13、sh);对照上面分析的NF_HOOK的实现,我们可以得出以下结论:在内核收到一个发往本机的数据包,会判断PF_INET 协议的NF_IP_PRE_ROUTING HOOK类型下是否注册有钩子函数,如果没有,则直接继续执行ip_rcv_finish,如果有,则调用nf_hook_slow函数,进而进一步调用 已注册的钩子函数,再根据其返回值,看是否还需要继续执行ip_rcv_finish。这就是整个Linux内核Netfilter Hook的含义。仔细阅读内核源码,可以发现,不仅在ip_input.c中,内核在每一个数据转发的关键节点,都放置了NF_HOOK。其中,Hook的类型,可以分为五类:
14、CODE:/* IP Hooks */* After promisc drops, checksum checks. */#define NF_IP_PRE_ROUTING 0 /路由前,进入本机的数据/* If the packet is destined for this box. */#define NF_IP_LOCAL_IN 1
15、 /路由后,进入本机的数据/* If the packet is destined for another interface. */#define NF_IP_FORWARD 2 /路由后,本机转发的数据/* Packets coming from a local process. */#define NF_IP_LOCAL_OUT
16、160; 3 /路由前,本机本地进程发出的数据/* Packets about to hit the wire. */#define NF_IP_POST_ROUTING 4 /路由后,本机发出的数据我们可以从上图中看出,我们常用的数据包过滤的表filter,在NF_IP_LOCAL_IN、NF_IP_FORWARD、NF_IP_LOCAL_OUT三处注册了钩子函数,以实现三种不同类型的包过滤,另外两个表也与此类似。3.
17、 Filter表及钩子函数的注册内核在网络堆栈的重要节点,引入了NF_HOOK宏,搭起了整个Netfilter的框架,但是NF_HOOK宏事实上仅是一个转向,更重要的内容是, “内核是如何注册钩子函数以及如何使用它们?”。内核默认的三个表,从框架的角度上来看,这些动作都是一致,我们以filter表为例,来回答这个问题。要在内核中使用filter表,首先要向内核注册这个表,然后该表在NF_IP_LOCAL_IN、NF_IP_FORWARD、 NF_IP_LOCAL_OUT三个Hook点,注册相应的钩子函数,在内核filter模块的初始化函数(iptable_filter.c),完成了 这一功能:
18、CODE:static int _init init(void) int ret; if (forward < 0 | forward > NF_MAX_VERDICT) printk("iptables forward must be 0 or 1n"); return -EINVAL; /* Entry 1 is the FORWARD hook */ initial_table.entries1.target.verdict = -forward - 1; /* 注册filter 表*/ ret = ipt_register_table(&pack
19、et_filter, &initial_table.repl); if (ret < 0) return ret; /*依次注册filter表的三个钩子*/ ret = nf_register_hook(&ipt_ops0); if (ret < 0) goto cleanup_table; ret = nf_register_hook(&ipt_ops1); if (ret < 0) goto cleanup_hook0; ret = nf_register_hook(&ipt_ops2); if (ret < 0) goto clea
20、nup_hook1; return ret;cleanup_hook1: nf_unregister_hook(&ipt_ops1);cleanup_hook0: nf_unregister_hook(&ipt_ops0);cleanup_table: ipt_unregister_table(&packet_filter); return ret;表的注册表的注册,是通过调用ipt_register_table 函数来实现的,我们先来看它的两个参数packet_filter和initial_table.repl。packet_filter的定义和赋值:CODE:sta
21、tic struct ipt_table packet_filter = .name = "filter", .valid_hooks = FILTER_VALID_HOOKS, .lock = RW_LOCK_UNLOCKED, .me = THIS_MODULE;它是一个struct ipt_table类型,内核用结构struct ipt_table(ip_tables.h)来描述表:CODE:struct ipt_table /*链表成员*/ struct list_head list; /* 表名,如"filter"、"nat"
22、;等,为了满足自动模块加载的设计,包含该表的模块应命名为iptable_'name'.o */ char nameIPT_TABLE_MAXNAMELEN; /* 位向量,表示当前表影响了哪些(个)HOOK 类型*/ unsigned int valid_hooks; /* 读写锁,初始为打开状态 */ rwlock_t lock; /* iptable的数据区*/ struct ipt_table_info *private; /* 是否在模块中定义,若否,则为NULL */ struct module *me;先来看成员valid_hook,它 表示当前表影响了哪些hook
23、 类型,我们前边谈到,filter 表影响了NF_IP_LOCAL_IN等三个Hook点,这里给它赋的值是FILTER_VALID_HOOKS,来看这个宏的实现:CODE:#define FILTER_VALID_HOOKS (1 << NF_IP_LOCAL_IN) | (1 << NF_IP_FORWARD) | (1 << NF_IP_LOCAL_OUT)一个简单的按位或,对应了我们提到的filter表所影响的三个Hook点。这个结构包含了表的名称,以及表所影响的Hook类型外,并没有其它实质性的东西,诸如我们关心的过滤规则等等,所以,private成
24、员就成一个让人感兴趣的目标。Private是一个struct ipt_table_info结构类型,该结构是实际描述表具体属性的数据结构(net/ipv4/netfilter/ip_tables.c):CODE:/* The table itself */struct ipt_table_info /* 每个表的大小 */ unsigned int size; /* 表中的规则数 */ unsigned int number; /* 初始的规则数,用于模块计数 */ unsigned int initial_entries; /* 记录所影响的HOOK的规则入口相对于下面的entries变量的
25、偏移量*/ unsigned int hook_entryNF_IP_NUMHOOKS; /* 与hook_entry相对应的规则表上限偏移量,当无规则录入时,相应的hook_entry和underflow均为0 */ unsigned int underflowNF_IP_NUMHOOKS; /* 每个CPU的Hook点规则表入口 */ char entries0 _cacheline_aligned;ipt_register_table 函数调用中的第二个变量initial_table,它是表的初始化时使用的模板,其体体积相当的庞大,这里暂时只用到了其repl成员,它是一个struct i
26、pt_replace结构,我们先来看这个成员赋值:CODE:static struct struct ipt_replace repl; struct ipt_standard entries3; struct ipt_error term; initial_table _initdata = "filter", FILTER_VALID_HOOKS, 4, sizeof(struct ipt_standard) * 3 + sizeof(struct ipt_error), NF_IP_LOCAL_IN = 0, NF_IP_FORWARD = sizeof(struct
27、 ipt_standard), NF_IP_LOCAL_OUT = sizeof(struct ipt_standard) * 2 , NF_IP_LOCAL_IN = 0, NF_IP_FORWARD = sizeof(struct ipt_standard), NF_IP_LOCAL_OUT = sizeof(struct ipt_standard) * 2 ,0, NULL, ,struct ipt_replace是我们要提到的第三个重要的数据结构,除了这里讲到的注册表时作为初始化模板,用户空间通过系统调用来操作表的时候也要用到这个结构类型的变量做为参数:CODE:struct ipt_
28、replace /*前面的部份,类似于ipt_table_info*/ /* Which table. */ char nameIPT_TABLE_MAXNAMELEN; /* Which hook entry points are valid: bitmask. You can't change this. */ unsigned int valid_hooks; /* Number of entries */ unsigned int num_entries; /* Total size of new entries */ unsigned int size; /* Hook en
29、try points. */ unsigned int hook_entryNF_IP_NUMHOOKS; /* Underflow points. */ unsigned int underflowNF_IP_NUMHOOKS; /* 这个结构不同于ipt_table_info之处在于,它还要保存表的旧的规则信息: */ /* Number of counters (must be equal to current number of entries). */ unsigned int num_counters; /* The old entries' counters. */ st
30、ruct ipt_counters _user *counters; /* The entries (hang off end: not really an array). */ struct ipt_entry entries0;结合上面的初始化赋值,我们可以对应过来:name :"filter"/filter表所影响的Hook类型valid_hooks:FILTER_VALID_HOOKS/初始化的规则数为4条,每个Hook类型(对应用户空间的“链”)初始化一条,并以一条“错误的规则”表示结束num_entries:4/标准的规则用struct ipt_standard
31、,错误的规则用struct ipt_error描述size:sizeof(struct ipt_standard) * 3 + sizeof(struct ipt_error)/计算初始的各Hook点对应的初始规则的偏移值hook_entryNF_IP_NUMHOOKS: NF_IP_LOCAL_IN = 0, NF_IP_FORWARD = sizeof(struct ipt_standard), NF_IP_LOCAL_OUT =
32、 sizeof(struct ipt_standard) * 2 underflowNF_IP_NUMHOOKS: NF_IP_LOCAL_IN = 0, NF_IP_FORWARD = sizeof(struct ipt_standard), NF_IP_LOCAL_OUT = sizeof(struct ipt_standard) * 2 /因为是初始化模块,不存在旧表,所以,这些保存旧表信息的参数均为空num_counters:
33、0counters:NULLentries0: OK,ipt_register_table函数的两个参数,前一个包含了表的基本信息,包含了表名,所影响的Hook类型,规则长度,入口等等,后一个是一个表的模块,即初始化的规则等等,有了这些基础,我们来看表的注册的实现:CODE:int ipt_register_table(struct ipt_table *table, const struct ipt_replace *repl) int ret; struct ipt_table_info *newinfo; static struct ipt_table_info bootstrap =
34、0, 0, 0, 0 , 0 , ; /*为每个CPU分配规则空间*/ newinfo = vmalloc(sizeof(struct ipt_table_info) + SMP_ALIGN(repl->size) * num_possible_cpus(); if (!newinfo) return -ENOMEM; /*将规则项拷贝到新表项的第一个cpu空间里面*/ memcpy(newinfo->entries, repl->entries, repl->size); /* translate_table函数将newinfo表示的table的各个规则进行边界检查,
35、然后对于* newinfo所指的ipt_talbe_info结构中的hook_entries和underflows赋予正确的值,最* 后将表项向其他cpu拷贝*/ ret = translate_table(table->name, table->valid_hooks, newinfo, repl->size, repl->num_entries, repl->hook_entry, repl->underflow); if (ret != 0) vfree(newinfo); return ret; ret = down_interruptible(&a
36、mp;ipt_mutex); if (ret != 0) vfree(newinfo); return ret; /* 如果注册的table已经存在,释放空间 并且递减模块计数 */ if (list_named_find(&ipt_tables, table->name) ret = -EEXIST; goto free_unlock; /* 用新的table项替换旧的table项 */ table->private = &bootstrap; if (!replace_table(table, 0, newinfo, &ret) goto free_un
37、lock; duprintf("table->private->number = %un", table->private->number); /* 保存初始规则计数器 */ table->private->initial_entries = table->private->number; rwlock_init(&table->lock); /*将表添加进表的链表当中*/ list_prepend(&ipt_tables, table);unlock: up(&ipt_mutex); retur
38、n ret;free_unlock: vfree(newinfo); goto unlock;这里调用的一些重要的函数,我们后面会陆续分析到,实际上表的注册,就是一个建立/维护表的链表的过程,是最终通过调用内核链表函数 list_prepend 来实现的,经过这样的注册后,初始的filter表,就被添加至表的链表中了,链表首部是全局变量ipt_tables。同样地,在NAT表的初始化函数ip_nat_rule_init中,有:ret = ipt_register_table(&nat_table, &nat_initial_table.repl);表mangle的初始化函数init中,有:ret = ipt_register_table(&packet_mangler, &initial_table.repl);注册表的钩子函数向内核注册了表后,下一步就是注册表的Hook点对应的钩子函数:CODE:static struct nf_hook_ops ipt_ops = .hook = ipt_hook, .owner = THIS_MODULE, .pf = PF_INET, .hooknum = NF_IP_LOCAL_IN, .priority
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 聊城职业技术学院《的分层开发技术》2023-2024学年第一学期期末试卷
- 丽江师范高等专科学校《工程制图Ⅱ》2023-2024学年第一学期期末试卷
- 江西司法警官职业学院《学术论文写作(1)》2023-2024学年第一学期期末试卷
- 江汉艺术职业学院《健身俱乐部经营与管理》2023-2024学年第一学期期末试卷
- 湖北大学知行学院《山地户外运动》2023-2024学年第一学期期末试卷
- 自贡职业技术学院《商业银行与业务经营》2023-2024学年第一学期期末试卷
- 周口师范学院《教育历史与比较研究》2023-2024学年第一学期期末试卷
- 重庆科技学院《工程管理软件与BM技术应用》2023-2024学年第一学期期末试卷
- 浙江树人学院《图像处理软件应用》2023-2024学年第一学期期末试卷
- 长江大学文理学院《材料力学B(外)》2023-2024学年第一学期期末试卷
- 2023年高考数学专项练习痛点问题之概率统计经典解答题含解析
- 物业管理劳务外包合同范本
- 消费者心理与行为分析PPT(第四版)完整全套教学课件
- 小学四年级语文下册全书背诵内容
- 2023-2024学年福建省漳州市初中语文七年级上册期末模考试题
- 全国小学语文研究《低年级作文 》精品课件写话教学课件
- 附录常见感叹词及用法
- GB/T 21709.5-2008针灸技术操作规范第5部分:拔罐
- 大三上-诊断学复习重点
- 应收账款的管理培训课件
- 股东变更情况报告表
评论
0/150
提交评论