基于Linux-2.6.30版本,具体实现net\ipv4\netfilter目录下,入口文件为net\ipv4\netfilter\iptable_filter.c,入口/出口函数为模块的init函数iptable_filter_init()和uninit函数iptable_filter_fini()

iptable_filter_init()函数流程如下

1、register_pernet_subsys(&iptable_filter_net_ops),其作用初步看是用于注册报文匹配目标规则,暂不分析。

2、nf_register_hooks(ipt_ops, ARRAY_SIZE(ipt_ops)); 该调用既是将filter类型table的hook这侧到netfilter的核心框架中。其中ipt_ops既是记录了具体hook处理实现内容,如下

 static struct nf_hook_ops ipt_ops[] __read_mostly = {

 {

 .hook = ipt_local_in_hook,

 .owner = THIS_MODULE,

 .pf = PF_INET,

 .hooknum = NF_INET_LOCAL_IN,

 .priority = NF_IP_PRI_FILTER,

 },

 {

 .hook = ipt_hook,

 .owner = THIS_MODULE,

 .pf = PF_INET,

 .hooknum = NF_INET_FORWARD,

 .priority = NF_IP_PRI_FILTER,

 },

 {

 .hook = ipt_local_out_hook,

 .owner = THIS_MODULE,

 .pf = PF_INET,

 .hooknum = NF_INET_LOCAL_OUT,

 .priority = NF_IP_PRI_FILTER,

 },

 };

以chain INPUT的实现为例,

hook成员即表示具体的hook处理函数,当报文匹配上本规则后,在报文向上层protocol layer上送之前,会被调用。具体见后面进一步分析。

pf即protocol family,表示处理报文的协议类型。

hooknum其实表示本pf下的hook类型,此处处理入方向的报文,与iptables命令工具中INPUT、FORWARD、OUTPUT基本相对应。

priority表示本条chain的优先级。

需要特别注意一下,后续实际注册chain处理规则时,既是利用pf、hooknum、priority将各个chain保存到nf_hooks中的对应位置。后续在netfilter的核心系统中,即根据pf/hooknum查找与之对应的hook,并按照priority指定的优先级一次调用各个hook函数。

现在看看具体filter hook函数是怎么调用的,对net/ipv4/中的代码进行grep,结果如下

 [root@arch ipv4]# grep -n NF_HOOK *.c

 arp.c::      NF_HOOK(NFPROTO_ARP, NF_ARP_OUT, skb, NULL, skb->dev, dev_queue_xmit);

 arp.c::      return NF_HOOK(NFPROTO_ARP, NF_ARP_IN, skb, dev, NULL, arp_process);

 ip_forward.c::       return NF_HOOK(PF_INET, NF_INET_FORWARD, skb, skb->dev, rt->u.dst.dev,

 ip_input.c:: return NF_HOOK(PF_INET, NF_INET_LOCAL_IN, skb, skb->dev, NULL,

 ip_input.c:: return NF_HOOK(PF_INET, NF_INET_PRE_ROUTING, skb, dev, NULL,

 ip_output.c::                                NF_HOOK(PF_INET, NF_INET_POST_ROUTING, newskb,

 ip_output.c::                        NF_HOOK(PF_INET, NF_INET_POST_ROUTING, newskb, NULL,

 ip_output.c::        return NF_HOOK_COND(PF_INET, NF_INET_POST_ROUTING, skb, NULL, skb->dev,

 ip_output.c::        return NF_HOOK_COND(PF_INET, NF_INET_POST_ROUTING, skb, NULL, dev,

 ipmr.c::    NF_HOOK(PF_INET, NF_INET_FORWARD, skb, skb->dev, dev,

 raw.c::      err = NF_HOOK(PF_INET, NF_INET_LOCAL_OUT, skb, NULL, rt->u.dst.dev,

 xfrm4_input.c::       NF_HOOK(PF_INET, NF_INET_PRE_ROUTING, skb, skb->dev, NULL,

 xfrm4_output.c::      return NF_HOOK_COND(PF_INET, NF_INET_POST_ROUTING, skb,

 [root@arch ipv4]# pwd

 /root/linux-2.6./net/ipv4

 [root@arch ipv4]#

补充说明:NF_HOOK宏既是netfilter系统在报文处理的地方,插入hook的功能宏,其实现如下

 #ifdef CONFIG_NETFILTER

 #define NF_HOOK(pf, hook, skb, indev, outdev, okfn) \

 NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, INT_MIN)

 #else

 #define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb)

 #endif

在上面的grep结果中,ip_input.c对INPUT方向中,利用NF_HOOK放置的报文处理hook实现为

 /*

  *  Deliver IP Packets to the higher protocol layers.

  */

 int ip_local_deliver(struct sk_buff *skb)

 {

 /*

  * Reassemble IP fragments.

  */

 if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) {

 if (ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER))

 return ;

 }

 return NF_HOOK(PF_INET, NF_INET_LOCAL_IN, skb, skb->dev, NULL,

        ip_local_deliver_finish);

 }

 NF_HOOK_THRESH宏实现如下

 #ifdef CONFIG_NETFILTER

 #define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)        \

 ({int __ret;        \

 if ((__ret=nf_hook_thresh(pf, hook, (skb), indev, outdev, okfn, thresh, )) == )\

 __ret = (okfn)(skb);        \

 __ret;})

 #endif

可见,协议栈是在将ip报文向上一层的协议处理层上送报文的时刻,调用netfilter的hook函数的,可见若系统没有配置netfilter,NF_HOOK实际将直接调用协议扎自身的上送函数,反之若配置了netfilter,则先经过netfilter处理之后再根据结果做区分处理。

Nf_hook_thresh()的实现又做了进一步区分实现,如下

 #ifdef CONFIG_NETFILTER

 static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook,

  struct sk_buff *skb,

  struct net_device *indev,

  struct net_device *outdev,

  int (*okfn)(struct sk_buff *), int thresh,

  int cond)

 {

 if (!cond)

 return ;

 #ifndef CONFIG_NETFILTER_DEBUG

 if (list_empty(&nf_hooks[pf][hook]))

 return ;

 #endif

 return nf_hook_slow(pf, hook, skb, indev, outdev, okfn, thresh);

 }

 #else

 static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook,

  struct sk_buff *skb,

  struct net_device *indev,

  struct net_device *outdev,

  int (*okfn)(struct sk_buff *), int thresh,

  int cond)

 {

 return okfn(skb);

 }

 #endif

可见最终在netfilter系统中,hook调用的入口是nf_hook_slow,其实现很直观,如下

 /* Returns 1 if okfn() needs to be executed by the caller,

  * -EPERM for NF_DROP, 0 otherwise. */

 int nf_hook_slow(u_int8_t pf, unsigned int hook, struct sk_buff *skb,

  struct net_device *indev,

  struct net_device *outdev,

  int (*okfn)(struct sk_buff *),

  int hook_thresh)

 {

 struct list_head *elem;

 unsigned int verdict;

 int ret = ;

 /* We may already have this, but read-locks nest anyway */

 rcu_read_lock();

 elem = &nf_hooks[pf][hook]; // pf -> NFPROTO_IPV4, NFPROTO_ARP,NFPROTO_BRIDGE,PF_INET etc

 next_hook: // hook -> 0~7

 verdict = nf_iterate(&nf_hooks[pf][hook], skb, hook, indev,

      outdev, &elem, okfn, hook_thresh);

 if (verdict == NF_ACCEPT || verdict == NF_STOP) {

 ret = ;

 } else if (verdict == NF_DROP) {

 kfree_skb(skb);

 ret = -EPERM;

 } else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) {

 if (!nf_queue(skb, elem, pf, hook, indev, outdev, okfn,

       verdict >> NF_VERDICT_BITS))

 goto next_hook;

 }

 rcu_read_unlock();

 return ret;

 }

基本流程就是利用pf/hook在nf_hooks[][]中找到前期注册的hook,然后遍历调用hook中各个处理函数,根据hook处理函数返回结果,对当前报文做区分处理。其实按优先级进行遍历,不体现在调用的地方,而是在注册的地方,调用既是按照优先级排序好的顺序依次调用各个函数而已。nf_iterate()函数实现如下

 unsigned int nf_iterate(struct list_head *head,

 struct sk_buff *skb,

 unsigned int hook,

 const struct net_device *indev,

 const struct net_device *outdev,

 struct list_head **i,

 int (*okfn)(struct sk_buff *),

 int hook_thresh)

 {

 unsigned int verdict;

 /*

  * The caller must not block between calls to this

  * function because of risk of continuing from deleted element.

  */

 list_for_each_continue_rcu(*i, head) {

 struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;

 if (hook_thresh > elem->priority)

 continue;

 /* Optimization: we don't need to hold module

    reference here, since function can't sleep. --RR */

 verdict = elem->hook(hook, skb, indev, outdev, okfn);

 if (verdict != NF_ACCEPT) {

 #ifdef CONFIG_NETFILTER_DEBUG

 if (unlikely((verdict & NF_VERDICT_MASK)

 > NF_MAX_VERDICT)) {

 NFDEBUG("Evil return from %p(%u).\n",

 elem->hook, hook);

 continue;

 }

 #endif

 if (verdict != NF_REPEAT)

 return verdict;

 *i = (*i)->prev;

 }

 }

 return NF_ACCEPT;

 }

注意红色部分,可以看出,仅当是每一个规则返回结果为NF_ACCEPT时,才继续处理本类型hook中下一个优先级的;若本次的hook函数返回NF_REPEAT,则将当前packet的在本次hook函数上再执行依次;其他情况直接返回hook函数执行结果。

Nf_iterate()返回到nf_hook_slow()函数之后,nf_hook_slow即根据执行结果,做区分处理, 若是ACCEPT或STOP,本packet在netfilter处理过程完毕,后续继续调用packet上报函数

若是DROP,本packet将被释放丢弃,不再调用packet上报函数

若包含QUEUE,则将packet送入当前chain对应的队列中,再做对应的处理,在iptables的命令帮助中,说明如下

QUEUE means to pass the packet to userspace. (How the packet can be received by a userspace process differs by the particular queue handler. 2.4.x and 2.6.x kernels up to 2.6.13 include the ip_queue queue handler. Kernels 2.6.14 and later additionally include the nfnetlink_queue queue handler. Packets with a target of QUEUE will be sent to queue number '0' in this case. Please also see the NFQUEUE target as described later in this man page.)

基本的报文处理流程既是如上面所述,接下来是具体过滤处理实现流程,以决定对应对packet的处理结果,以IPv4报文入方向过滤处理为例,具体既是

 static struct nf_hook_ops ipt_ops[] __read_mostly = {

 {

 .hook = ipt_local_in_hook,

 .owner = THIS_MODULE,

 .pf = PF_INET,

 .hooknum = NF_INET_LOCAL_IN,

 .priority = NF_IP_PRI_FILTER,

 }

 ...

 };

即ipt_local_in_hook函数,其实现既将报文针对之前添加的规则,与packet做匹配检查,并返回预先设置的结果。

netfilter-IPv4实现框架分析(一)的更多相关文章

  1. Android/Linux下CGroup框架分析及其使用

    1 cgroup介绍 CGroup是control group的简称,它为Linux kernel提供一种任务聚集和划分的机制,可以限制.记录.隔离进程组(process groups)所使用的资源( ...

  2. 几款开源的hybird移动app框架分析

    几款开源的Hybrid移动app框架分析 Ionic Onsen UI 与 ionic 相比 jQuery Mobile Mobile Angular UI 结论 很多移动开发者喜欢使用原生代码开发, ...

  3. 深入浅出 - Android系统移植与平台开发(十一) - Sensor HAL框架分析之一

    作者:唐老师,华清远见嵌入式学院讲师. 1. Sensor的概念 Sensor即传感器,在当前智能手机上大量存在:G-Sensor.LightsSensor. ProximitySensor.Temp ...

  4. 深入浅出 - Android系统移植与平台开发(八)- HAL Stub框架分析

    作者:唐老师,华清远见嵌入式学院讲师. 1. HAL Stub框架分析 HAL stub的框架比较简单,三个结构体.两个常量.一个函数,简称321架构,它的定义在:@hardware/libhardw ...

  5. openwrt: Makefile 框架分析

    openwrt: Makefile 框架分析 原文链接:blog.chinaunix.net/uid-26675482-id-4704952.html 本篇的主要目的是想通过分析Makefile,了解 ...

  6. Android 核心分析 之六 IPC框架分析 Binder,Service,Service manager

    IPC框架分析 Binder,Service,Service manager 我首先从宏观的角度观察Binder,Service,Service Manager,并阐述各自的概念.从Linux的概念空 ...

  7. VS2010/MFC编程入门之四(MFC应用程序框架分析)

    VS2010/MFC编程入门之四(MFC应用程序框架分析)-软件开发-鸡啄米 http://www.jizhuomi.com/software/145.html   上一讲鸡啄米讲的是VS2010应用 ...

  8. Yii PHP 框架分析(二)

    Yii PHP 框架分析(二)作者:wdy http://hi.baidu.com/delphiss/blog/item/54597af595085ad3f3d38552.html Yii是基于组件( ...

  9. Yii PHP 框架分析 (一)

    Yii PHP 框架分析 (一)作者:wdy http://hi.baidu.com/delphiss/blog/item/f7da86d787adb72506088b4b.html 基于yii1.0 ...

随机推荐

  1. CentOS 7安装Mysql并设置开机自启动

    CentOS 7不带Mysql数据库了,默认的数据库是MariaDB(Mysql的一个分支). 可以按照以下步骤手动安装Mysql数据库. 1. 下载rpm安装文件 wget http://repo. ...

  2. 微信小程序-视图

    数据绑定 <!--wxml--> <view> {{message}} </view> // page.js Page({ data: { message: 'He ...

  3. javascript事件之:谈谈自定义事件

    对于JavaScript自定义事件,印象最深刻的是用jQuery在做图片懒加载的时候.给需要懒加载的图片定义一个appear事件.当页面图片开始出现时候,触发这个自定义的appear事件(注意,这里只 ...

  4. iOS开发 ReactiveCocoa入门教程 第二部分

    ReactiveCocoa 是一个框架,它允许你在你的iOS程序中使用函数响应式(FRP)技术.加上第一部分的讲解,你将会学会如何使用信号量(对事件发出数据流)如何替代标准的动作和事件处理逻辑.你也会 ...

  5. 课时9—popup

    内容比较多的时候可以全屏显示,用第一种实现方式,如图1 图1 内容较少的话可以使用第二种实现方式如图2:图2 具体的实现代码如下: .header,.footer,.wrap-page{ positi ...

  6. jquery 获取选择符

    1.工厂函数$() 标签名:$('p') 取得文档中的所有段落 ID:$('#some-id') 取得文档中具有对应的some-id ID的一个元素 类:$('.some-class') 取得文档中带 ...

  7. python无意中发现的

    http://www.zhihu.com/question/37904398?sort=created&page=2 >>> a = [[1,2],[3,4],[5,6,7] ...

  8. 日期转换时Safari中返回Invalid Date

    问题: 进行日期转换时,Safari中会返回Invalid Date, 而IE 9, Firefox, Chrome and Opera显示正常,代码如下所示: var d = new Date(&q ...

  9. HTML5的 2D SVG和SVG DOM的学习笔记(2)---SVG动画

    SVG支持动画.可以通过以下几种方法获得动画效果: 使用SVG动画元素.SVG可以描述随时间变化的图形对象,使用不同的动画元素可以定义运动路径,淡入淡出效果和对象的膨胀.收缩.旋转和变换颜色. 使用S ...

  10. EXE捆绑器

    释放的方法真没想到  太神奇了 // 文件捆绑器Dlg.cpp : 实现文件 // #include "stdafx.h" #include "文件捆绑器.h" ...