1.        前言
Netfilter作为目前进行包过滤,连接跟踪,地址转换等的主要实现框架,了解其内部机制对于我们更好的利用Netfilter进行设计至关重要,因此本文通过阅读内核源码2.6.21.2,根据自身的分析总结出Netfilter的大致实现机制,由于自身水平有限,且相关的参考资料较少,因此其中的结论不能保证完全正确,如果在阅读本文的过程中发现了问题欢迎及时与作者联系。
2.        规则的存储与遍历机制
        规则的存储机制
在Netfilter中规则是顺序存储的,一条规则主要包括三个部分:ipt_entry、ipt_entry_matches、ipt_entry_target。ipt_entry_matches由多个ipt_entry_match组成,ipt_entry结构主要保存标准匹配的内容,ipt_entry_match结构主要保存扩展匹配的内容,ipt_entry_target结构主要保存规则的动作。在ipt_entry中还保存有与遍历规则相关的变量target_offset与next_offset,通过target_offset可以找到规则中动作部分ipt_entry_target的位置,通过next_offset可以找到下一条规则的位置。规则的存储如下图2-1所示。

图2-1 规则的存储

ipt_entry结构如下图2-2所示,其成员ip指向结构ipt_ip,该结构主要保存规则中标准匹配的内容(IP、mask、interface、proto等),target_offset的值等于ipt_entry的长度与ipt_entry_matches的长度之和,next_offset的值等于规则中三个部分的长度之和。通过target_offset与next_offset可以实现规则的遍历。

图2-2 ipt_entry结构

ipt_entry_match主要保存规则中扩展匹配内容(tos、ttl、time等),其是Netfilter中内核与用户态交互的关键数据结构,在其内核部分由一个函数指针指向一个ipt_match结构,该结构体中包含了对包做匹配的函数,是真正对包做匹配的地方。ipt_entry_target结构与ipt_entry_match结构很类似。

图2-3 ipt_entry_match结构

图2-4 ipt_entry_target结构
        
       规则的遍历机制
在Netfilter中,函数ipt_do_table()实现了规则的遍历,该函数根据传入的参数table和hook找到相应的规则起点,即第一个ipt_entry的位置,主要通过函数get_entry()实现。
private = table->private;
table_base = (void *)private->entries[smp_processor_id();
e = get_entry(table_base, private->hook_entry[hook);

标准匹配是通过函数ip_packet_match()实现的,该函数主要对包的五元组信息进行匹配,扩展匹配则通过宏IPT_MATCH_ITERATE实现,该宏的定义为:

#define IPT_MATCH_ITERATE(e, fn, args...)    \
({                        \
    unsigned int __i;            \
    int __ret = 0;                \
    struct ipt_entry_match *__match;    \
                        \
    for (__i = sizeof(struct ipt_entry);    \
     __i < (e)->target_offset;        \
     __i += __match->u.match_size) {    \
        __match = (void *)(e) + __i;    \
                        \
        __ret = fn(__match , ## args);    \
        if (__ret != 0)            \
            break;            \
    }                    \
    __ret;                    \
})

宏IPT_MATCH_ITERATE依次调用各个ipt_entry_match所指向的ipt_match中match()处理数据包,在for循环中使用了terget_offset位置变量查找match的位置。
        在对数据包进行了匹配后,接着需要进行相应的动作处理,通过函数ipt_get_target()获取规则动作ipt_entry_target的位置:

static __inline__ struct ipt_entry_target *
ipt_get_target(struct ipt_entry *e)
{
    return (void *)e + e->target_offset;
}

如果还需要继续遍历下一条规则,则继续执行以下语句以找到下一条规则的开始位置:

e = (void *)e + e->next_offset;

3.        表、匹配、动作存储及管理机制
       表、匹配、动作的存储机制
规则中所使用到的match、target、table使用全局变量xt_af所指向的相应链表保存,这些链表是在对Netfilter进行初始化或匹配模块扩展时进行更新的,在初始化时,默认的表及动作则添加到相应的链表中。Netfilter实现了很好的扩展性,如需要对数据包的时间进行匹配,则在match的链表中需要首先增加time扩展匹配模块,在相应的规则中则通过指向该time模块所对应的函数match()以进行时间的匹配。xt_af是个一维数组,其按照协议族的不同分别存储,目前我们常用的协议族主要是AF_INET。

图3-1 match,target,table的全局存储
        
match、target、table的全局存储如上图3-1所示,以下为各部分的详细的结构表示。当扩展一个匹配模块时,其会注册一个ipt_match结构到match链表中,该结构的主要变量值如下图所示,name表示扩展模块的名字,match()是该模块最主要的函数,其主要对数据包进行相应的比较,checkentry()主要对包进行相应的完整性检验,destroy()在对模块进行撤销时调用。如果需要自己新加一个扩展模块,则需要构造一个ipt_match结构并注册到相应的链表中。ipt_target的结构与ipt_match相似,其最主要的函数是target()。

图3-2 ipt_match结构的存储


图3-3 ipt_target结构的存储

table主要是用来对规则进行管理,通过table中的相应参数可以找到相应的规则所处的入口位置。

图3-4 ipt_table结构的存储
   
       表、匹配、动作的管理机制
match、target、table的注册分别调用xt_register_match()、xt_register_target()、xt_register_table()实现,前两个注册函数很相似,xt_register_table()则稍微复杂些。撤销时则分别调用相应的unregister函数实现。xt_register_match()函数的定义如下(xt_match与ipt_match是一样的):

int
xt_register_match(struct xt_match *match)
{
    int ret, af = match->family;

ret = mutex_lock_interruptible(&xt[af.mutex);
    if (ret != 0)
        return ret;

list_add(&match->list, &xt[af.match);
    mutex_unlock(&xt[af.mutex);

return ret;
}

xt_register_table()函数的定义如下(xt_table与ipt_table是一样的),因为一个xt_table结构中还指向另一结构xt_table_info,该结构主要描述表的相关信息,所以对表注册时需要对这两类结构体进行定义。

int xt_register_table(struct xt_table *table,
         struct xt_table_info *bootstrap,
         struct xt_table_info *newinfo)
{
    int ret;
    struct xt_table_info *private;
    struct xt_table *t;

ret = mutex_lock_interruptible(&xt[table->af.mutex);
    if (ret != 0)
        return ret;

/* Don't autoload: we'd eat our tail... */
    list_for_each_entry(t, &xt[table->af.tables, list) {
        if (strcmp(t->name, table->name) == 0) {
            ret = -EEXIST;
            goto unlock;
        }
    }

/* Simplifies replace_table code. */
    table->private = bootstrap;
    rwlock_init(&table->lock);
    if (!xt_replace_table(table, 0, newinfo, &ret))
        goto unlock;

private = table->private;
    duprintf("table->private->number = %u\n", private->number);

/* save number of initial entries */
    private->initial_entries = private->number;

list_add(&table->list, &xt[table->af.tables);

ret = 0;
unlock:
    mutex_unlock(&xt[table->af.mutex);
    return ret;
}

4.        钩子函数的存储及管理机制
        钩子函数的存储机制
钩子函数由一个全局二维链表nf_hooks保存,其按照协议族归类存储,在每个协议族中,根据钩子点顺序排列,在钩子点内则根据钩子函数的优先级依次排列。钩子函数的存储图如下图4-1所示,链表中的每个元素都是指向结构体nf_hook_ops中的hook()函数的指针,nf_hook_ops实际存储了钩子函数的内容,其结构如图4-2所示。在相应的钩子点调用钩子函数时,则根据协议族和钩子点找到相应的链表入口,然后依次调用该链中的每一个钩子函数对数据包进行操作。


图4-1 钩子函数的全局存储


图4-2 钩子函数的链表
        钩子函数的管理机制
如果需要在相应的钩子点挂载钩子函数,则需要首先定义一个nf_hook_ops结构,在其中实现实际的钩子函数,再调用函数nf_register_hook()将该钩子函数注册到图4-1所示的二维链表中,nf_register_hook()函数的定义如下:

int nf_register_hook(struct nf_hook_ops *reg)
{
    struct list_head *i;
    int err;

err = mutex_lock_interruptible(&nf_hook_mutex);
    if (err < 0)
        return err;
    list_for_each(i, &nf_hooks[reg->pf[reg->hooknum) {
        if (reg->priority < ((struct nf_hook_ops *)i)->priority)
            break;
    }
    list_add_rcu(&reg->list, i->prev);
    mutex_unlock(&nf_hook_mutex);
    return 0;
}

5.        Netfilter的流程框架
      在Netfilter中的不同钩子点调用了不同的钩子函数,这些钩子函数的调用如图4-1所示,其调用的流程框架如下图5-1所示。


图5-1 Netfilter中hook函数的调用流程

Netfilter中默认表filter在建立时则在NF_IP_LOCAL_IN,NF_IP_FORWARD钩子点注册了钩子函数ipt_hook(),在NF_IP_LOCAL_OUT这个点注册了钩子函数ipt_local_out_hook(),两个钩子函数都会调用ipt_do_table()对相对应的表和钩子点的规则进行遍历。调用的流程如下图5-2所示。

图5-2 Netfilter中规则的调用流程
6.        总结
以上只是简单分析了Netfilter的整体框架,主要描述了其中的实现机制。在这个机制上已经实现了很多功能,除了对基本的功能进行完善和改进外,还出现了很多新的扩展功能。如在此架构上实现的连接跟踪机制和NAT机制,以及结合连接跟踪机制与Netfilter框架实现的Layer7扩展匹配模块等。对此框架的了解,有助于我们更好的利用Netfilter框架实现我们的设计,鉴于自身水平有限,因此以上的分析不能保证全部正确。希望各位批评指正。

由于主要是为了描绘出整个Netfilter的框架,故对其中较细节的的内容有所忽略而未深入分析,如规则的另外一个动作ipt_standard_target,table表注册时的初始化等,但这并不影响对整个框架的了解。至于Netfilter在链路层的实现机制此处也并未分析,因为其实现较简单,且我们大部分是在网络层利用Netfilter架构。分析中也未涉及到用户态规则与内核态规则之间的关系,对于iptables如何操作内核中的规则并未介绍,以后有机会再做详细分析。

Netfilter实现机制分析 原作者:Minit相关推荐

  1. 【转】Linux Netfilter实现机制和扩展技术

    转自https://www.ibm.com/developerworks/cn/linux/l-ntflt/index.html 作者: 杨沙洲 2.4.x的内核相对于2.2.x在IP协议栈部分有比较 ...

  2. Linux系统调用详解(实现机制分析)

    为什么需要系统调用   linux内核中设置了一组用于实现系统功能的子程序,称为系统调用.系统调用和普通库函数调用非常相似,只是系统调用由操作系统核心提供,运行于内核态,而普通的函数调用由函数库或用户 ...

  3. Linux Netfilter实现机制和扩展技术

    Linux Netfilter实现机制和扩展技术 杨沙洲 ( pubb@163.net)国防科技大学计算机学院 简介: 本文从Linux网络协议栈中报文的流动过程分析开始,对Linux 2.4.x内核 ...

  4. Google Test(GTest)使用方法和源码解析——结果统计机制分析

    在分析源码之前,我们先看一个例子.以<Google Test(GTest)使用方法和源码解析--概况 >一文中最后一个实例代码为基准,修改最后一个"局部测试"结果为错误 ...

  5. Linux内核抢占实现机制分析【转】

    Linux内核抢占实现机制分析 转自:http://blog.chinaunix.net/uid-24227137-id-3050754.html [摘要]本文详解了Linux内核抢占实现机制.首先介 ...

  6. C++ 异常机制分析

    C++ 异常机制分析 参考文章: (1)C++ 异常机制分析 (2)https://www.cnblogs.com/QG-whz/p/5136883.html 备忘一下.

  7. Linux cgroup机制分析之cpuset subsystem

    ------------------------------------------ 本文系本站原创,欢迎转载! 转载请注明出处:http://ericxiao.cublog.cn/ -------- ...

  8. libpcap捕包机制分析(三)

    目前,在linux操作系统下的网络数据包捕获系统普遍是建立在libpcap捕包平台上的,libpcap的英文意思是Library of Packet Capture,即数据包捕获函数库.该库提供的C函 ...

  9. 【Binder 机制】Native 层 Binder 机制分析 ( binder_loop | svcmgr_handler | binder.c | binder_parse )

    文章目录 前言 一.binder_loop 方法调用 二.binder_loop 方法参数 svcmgr_handler 三.binder_loop 方法 四.binder_parse 方法 前言 在 ...

  10. JDK源码分析——Java的SPI机制分析与实战

    重点提示:在我博客中的所有的源码分析的实例,我都将会放到github上,感兴趣的朋友可以下载下来调试运行,我相信还是可以有所收获的.我的目的是让所有读到我博客的朋友都可以了解到有价值的东西,学习到ja ...

最新文章

  1. js控制表格隔行变色
  2. python中chr的用法_python中chr()函数和ord()函数的用法
  3. 华 为 路 由 器 命 令 大 全
  4. 计算机论文搜索技巧【二】
  5. 千寻和省cors精度对比_测量员新手上路攻略:解析省CORS和千寻CORS账号区别及其如何选择运用...
  6. centos 7.2安装 lnmp一键安装
  7. 网络嗅探器如何嗅探_SQL Server中的运行时常量嗅探
  8. Google 重磅发布 Flutter 2 !一套代码横扫 5 大系统
  9. 华为鸿蒙系统支持什么手机_华为手机支持升级鸿蒙OS的EMUI 11系统55款机型名单公布...
  10. Windows核心编程笔记
  11. 题目1439:Least Common Multiple
  12. 【图像融合】基于matlab GUI像素点图像融合【含Matlab源码 783期】
  13. LoadRunner 压力测试
  14. 黑苹果 macos 教程
  15. 打工人准时下班踩点利器——python写一个自动关机程序并打包成exe文件
  16. ios 视频选择封面功能
  17. chrome浏览器最新离线版下载 30-72版本全
  18. linux 内核调试 booting the kernel.,Uncompressing Linux....... done, booting the kernel就不动了的一个可能原因...
  19. HCNP RoutingSwitching之MAC地址防漂移
  20. html项目的致谢词,毕业论文致谢词范文200字(精选10篇)

热门文章

  1. “adb server is out of date.
  2. 【转】正则表达式括号的作用
  3. 百度编辑器ueditor获取不到内容?请把form放在table等其他元素最外面
  4. 笔记:Hadoop权威指南 第9章 构建Hadoop集群
  5. 手机通过笔记本电脑上网
  6. 退出myeclipse 8.5配置中心
  7. 浅谈JS中的原型对象和原型链
  8. 安防的未来五年 如何把握机遇深耕市场?
  9. 前端学习总结【103天】:CSS——不用JavaScript实现tab标签切换的两种方法
  10. idea exclude from compile 再加回来