转自;http://blog.csdn.net/suiyuan19840208/article/details/19684883

-1: 为什么要写这个东西?
最近在找工作,之前netfilter 这一块的代码也认真地研究过,应该每个人都是这样的你懂 不一定你能很准确的表达出来。 故一定要化些时间把这相关的东西总结一下。

0:相关文档

linux 下 nf_conntrack_tuple 跟踪记录  其中可以根据内核提供的数据结构获取连接跟踪记录。

iptables 中的NAT使用总结    iptable的在防火墙上面的应用。

1:iptable中三个tables所挂接的HOOKs

其实这个问题很简单的运行iptables打开看看就知道,此处的hook与内核的hook是对应起来的。

因此在内核中注册的5个HOOK点如下:

enum nf_inet_hooks {
NF_INET_PRE_ROUTING,
NF_INET_LOCAL_IN,
NF_INET_FORWARD,
NF_INET_LOCAL_OUT,
NF_INET_POST_ROUTING,

NF_INET_NUMHOOKS
};

在向下看linux内核中的实现之前在看看一个数据包在进过linux内核中neitfilter的处理过程。

其中这5个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的处理点。
2 :proc文件下的跟踪记录

上面的就是连接跟踪记录,其中记录linux系统建立的每一条连接,其中包括源IP,目的IP,源port,目的port,协议ID,其这些可以称为5元组。有关这个在linux内含中的定义是的结构体 struct nf_conn   中的变量 

/* Connection tracking(链接跟踪)用来跟踪、记录每个链接的信息(目前仅支持IP协议的连接跟踪)。
            每个链接由“tuple”来唯一标识,这里的“tuple”对不同的协议会有不同的含义,例如对tcp,udp
                 来说就是五元组: (源IP,源端口,目的IP, 目的端口,协议号),对ICMP协议来说是: (源IP, 目
            的IP, id, type, code), 其中id,type与code都是icmp协议的信息。链接跟踪是防火墙实现状态检
            测的基础,很多功能都需要借助链接跟踪才能实现,例如NAT、快速转发、等等。*/

/* XXX should I move this to the tail ? - Y.K */
/* These are my tuples; original and reply */
struct nf_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX]; 此变量就保存着上面的跟踪记录,

3:hooks点的定义及注册

其中每个不同协议的不同HOOK点最终都会注册到全局的nf_hooks链表变量之中:同时注册到同一个HOOK的处理函数会根据优先级的不同的进行先后处理。

extern struct list_head nf_hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS];

其中定义的协议如下:

enum {
NFPROTO_UNSPEC =  0,
NFPROTO_IPV4   =  2, //ipV4
NFPROTO_ARP    =  3, //ARP
NFPROTO_BRIDGE =  7, //brigde
NFPROTO_IPV6   = 10,
NFPROTO_DECNET = 12,
NFPROTO_NUMPROTO,

};

NF_MAX_HOOKS 宏的定义已经在前面说明。HOOK点的定义。

与下面的HOOK的struct nf_hook_ops对比一下就可以看到差异:变量pf的值不同,优先级不同,即内核中根据不同的协议类型可以注册不同的挂载点进行不同的优先级数据包的处理。

其注册使用函数为:nf_register_hooks()函数在内核中多个地方出现,因为用户可以根据自己的需要对特定的协议在特定的位置添加HOOK出现函数。

nf_register_hook()函数的实现就是:

  1. int nf_register_hook(struct nf_hook_ops *reg)
  2. {
  3. struct nf_hook_ops *elem;
  4. int err;
  5. err = mutex_lock_interruptible(&nf_hook_mutex);
  6. if (err < 0)
  7. return err;////遍历已经注册的的HOOK,OPS,将新加入的根据优先级添加到链表最后
  8. list_for_each_entry(elem, &nf_hooks[reg->pf][reg->hooknum], list) {
  9. if (reg->priority < elem->priority)
  10. break;
  11. }
  12. list_add_rcu(®->list, elem->list.prev);
  13. mutex_unlock(&nf_hook_mutex);
  14. return 0;
  15. }

上面就是HOOK点的注册函数,即根据协议类型和HOOK点注册到全局数组中nf_hooks[][]中。

4:注册的HOOK点何时被使用?

在linux内核中当需要使用注册的HOOK点时,使用函数:

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

NF_HOOK->NF_HOOK_THRESH->nf_hook_thresh->nf_hook_slow——这个是最终的执行函数。

先看看下面函数都返回值:

/* Responses from hook functions. */
#define NF_DROP 0
#define NF_ACCEPT 1
#define NF_STOLEN 2
#define NF_QUEUE 3
#define NF_REPEAT 4
#define NF_STOP 5
#define NF_MAX_VERDICT NF_STOP

  1. int nf_hook_slow(u_int8_t pf, unsigned int hook, struct sk_buff *skb,
  2. struct net_device *indev,
  3. struct net_device *outdev,
  4. int (*okfn)(struct sk_buff *),
  5. int hook_thresh)
  6. {
  7. struct list_head *elem;
  8. unsigned int verdict;
  9. int ret = 0;
  10. /* We may already have this, but read-locks nest anyway */
  11. rcu_read_lock();
  12. elem = &nf_hooks[pf][hook];//是不是很熟悉就是上面的全局变量专门用来注册全局HOOK点的变量。
  13. next_hook:/* 开始遍历对应的netfilter的规则,即对应的proto和hook挂载点 */
  14. verdict = nf_iterate(&nf_hooks[pf][hook], skb, hook, indev,outdev, &elem, okfn, hook_thresh);
  15. if (verdict == NF_ACCEPT || verdict == NF_STOP) {
  16. ret = 1;
  17. } else if (verdict == NF_DROP) {
  18. kfree_skb(skb);
  19. ret = -EPERM;
  20. } else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) {
  21. if (!nf_queue(skb, elem, pf, hook, indev, outdev, okfn,
  22. verdict >> NF_VERDICT_BITS))
  23. goto next_hook;
  24. }
  25. rcu_read_unlock();
  26. return ret;
  27. }

现在来看看nf_iterate()函数:

  1. unsigned int nf_iterate(struct list_head *head,
  2. struct sk_buff *skb,
  3. unsigned int hook,
  4. const struct net_device *indev,
  5. const struct net_device *outdev,
  6. struct list_head **i,
  7. int (*okfn)(struct sk_buff *),
  8. int hook_thresh)
  9. {
  10. unsigned int verdict;
  11. //其中head就是全局的2维数组nf_hooks,
  12. /*
  13. * The caller must not block between calls to this
  14. * function because of risk of continuing from deleted element.
  15. */
  16. list_for_each_continue_rcu(*i, head) {
  17. struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;
  18. if (hook_thresh > elem->priority)
  19. continue;
  20. /* Optimization: we don't need to hold module
  21. reference here, since function can't sleep. --RR */
  22. verdict = elem->hook(hook, skb, indev, outdev, okfn);//根据协议和HOOK点执行挂在的处理函数
  23. if (verdict != NF_ACCEPT) {//返回结果进行判断。
  24. #ifdef CONFIG_NETFILTER_DEBUG
  25. if (unlikely((verdict & NF_VERDICT_MASK)
  26. > NF_MAX_VERDICT)) {
  27. NFDEBUG("Evil return from %p(%u).\n",
  28. elem->hook, hook);
  29. continue;
  30. }
  31. #endif
  32. if (verdict != NF_REPEAT)
  33. return verdict;
  34. *i = (*i)->prev;
  35. }
  36. }
  37. return NF_ACCEPT;
  38. }

对各个返回值的解释如下:

NF_DROP:直接drop掉这个数据包;
NF_ACCEPT:数据包通过了挂载点的所有规则;
NF_STOLEN:这个还未出现,留在以后解释;
NF_QUEUE:将数据包enque到用户空间的enque handler;
NF_REPEAT:为netfilter的一个内部判定结果,需要重复该条规则的判定,直至不为NF_REPEAT;
NF_STOP:数据包通过了挂载点的所有规则。但与NF_ACCEPT不同的一点时,当某条规则的判定结果为NF_STOP,那么可以直接返回结果NF_STOP,无需进行后面的判定了。而NF_ACCEPT需要所以的规则都为ACCEPT,才能返回NF_ACCEPT。

在数据包流经内核协议栈的整个过程中,在内中定义的HOOK中的如:PRE_ROUTING、LOCAL_IN、FORWARD、LOCAL_OUT和POST_ROUTING会根据数据包的协议簇PF_INET到这些关键点去查找是否注册有钩子函数。如果没有,则直接返回okfn函数指针所指向的函数继续走协议栈;如果有,则调用nf_hook_slow函数,从而进入到Netfilter框架中去进一步调用已注册在该过滤点下的钩子函数,再根据其返回值来确定是否继续执行由函数指针okfn所指向的函数

一个IP数据包的接受过程如下:


 

linux内核netfilter模块分析之:HOOKs点的注册及调用的更多相关文章

  1. Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7)

    http://blog.chinaunix.net/uid-20543672-id-3157283.html Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3 ...

  2. 2018-2019-1 20189221《Linux内核原理与分析》第四周作业

    2018-2019-1 20189221<Linux内核原理与分析>第四周作业 教材学习:<庖丁解牛Linux内核分析> 第 3 章 MenuOS的构造 计算机三大法宝:存储程 ...

  3. Linux内核源码分析--内核启动之(6)Image内核启动(do_basic_setup函数)(Linux-3.0 ARMv7)【转】

    原文地址:Linux内核源码分析--内核启动之(6)Image内核启动(do_basic_setup函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://bl ...

  4. Linux内核源码分析方法_转

    Linux内核源码分析方法 转自:http://www.cnblogs.com/fanzhidongyzby/archive/2013/03/20/2970624.html 一.内核源码之我见 Lin ...

  5. Linux内核源码分析--内核启动之zImage自解压过程

    参考: http://blog.chinaunix.net/uid-20543672-id-3018233.html Linux内核编译流程分析 linux2.6内核启动分析--李枝果(不看是你的损失 ...

  6. Linux内核源码分析--内核启动之zImage自解压过程【转】

    转自:https://www.cnblogs.com/pengdonglin137/p/3838245.html 阅读目录(Content) zImage来历 piggy.gz压缩文件的特点 vmli ...

  7. Linux内核源码分析之setup_arch (三)

    1. 前言 在 Linux内核源码分析之setup_arch (二) 中介绍了当前启动阶段的内存分配函数memblock_alloc,该内存分配函数在本篇将要介绍paging_init中用于页表和内存 ...

  8. 20169212《Linux内核原理与分析》课程总结

    20169212<Linux内核原理与分析>课程总结 每周作业链接汇总 第一周作业:完成linux基础入门实验,了解一些基础的命令操作. 第二周作业:学习MOOC课程--计算机是如何工作的 ...

  9. 20169212《Linux内核原理与分析》第二周作业

    <Linux内核原理与分析>第二周作业 这一周学习了MOOCLinux内核分析的第一讲,计算机是如何工作的?由于本科对相关知识的不熟悉,所以感觉有的知识理解起来了有一定的难度,不过多查查资 ...

随机推荐

  1. spring-dao.xml 模板

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...

  2. 原 nc在centos7上的安装和简单使用

    https://blog.csdn.net/qq_16414307/article/details/50291341 https://www.cnblogs.com/rocky-AGE-24/p/69 ...

  3. Mysql安装(msi版的安装)

    上次使用绿色版的MySQL,现在使用一次安装版的,具体的步骤都是按照别人的,这里就贴一个链接,如果有时间,以后再补充上来截图. 主要参考的是下面的链接: http://www.cnblogs.com/ ...

  4. WebSocket In ASP.NET Core(一)

    .NET-Core Series Server in ASP.NET-Core DI in ASP.NET-Core Routing in ASP.NET-Core Error Handling in ...

  5. jstat命令总结

    jvm统计信息监控工具 一. jstat是什么 jstat是JDK自带的一个轻量级小工具.全称"Java Virtual Machine statistics monitoring tool ...

  6. hdu CA Loves GCD(dp)

    一道我想骂人的题,差点把我气炸了. 题意: 求一个数的集合中(非多重集,每个数只出现一次)所有子集的gcd的和.结果MOD10^8+7输出. 输入输出不说了,自己看吧,不想写了. 当时我真把它当作数论 ...

  7. 【BZOJ-3218】a+b Problem 最小割 + 可持久化线段树

    3218: a + b Problem Time Limit: 20 Sec  Memory Limit: 40 MBSubmit: 1320  Solved: 498[Submit][Status] ...

  8. 【转载】gdi+ 内存泄漏

    [转载]http://issf.blog.163.com/blog/static/1941290822009111894413472/ 最近用GDI+实现了几个自定义控件,但是发现存在内存泄露问题 B ...

  9. [Java]jdbc[转]

    >>http://www.cnblogs.com/xiohao/p/3507483.html >>http://www.cnblogs.com/hongten/archive/ ...

  10. Jmeter关于上传图片接口

    最近接到的一个新的项目,老规矩,开发组开发完接口需要进行接口的测试,其他的很简单,根据限制条件逻辑等设计数据,用浏览器或者工具进行验证就OK. 其中有一个接口涉及到图片的上传,以前没有用过,通过查找资 ...