Chinaunix首页 | 论坛 | 博客
  • 博客访问: 999426
  • 博文数量: 442
  • 博客积分: 1146
  • 博客等级: 少尉
  • 技术积分: 1604
  • 用 户 组: 普通用户
  • 注册时间: 2010-11-04 12:52
个人简介

123

文章分类

全部博文(442)

文章存档

2017年(3)

2016年(15)

2015年(132)

2014年(52)

2013年(101)

2012年(110)

2011年(29)

分类: LINUX

2014-05-28 13:45:57

作者:gfree.wind@gmail.com
博客:blog.focus-linux.net   linuxfocus.blog.chinaunix.net
 
 
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
======================================================================================================

相信不少同学都用过iptables,即使没有用过,也基本上都听过。今天开始学习一下iptables在内核部分的实现,即netfilter。

在学习内核代码的过程中,我有一个经验——如果可以把内核对应的应用搞清楚,再去学习内核代码会容易很多。所以,如果没有使用iptables的经验的同学,最好可以先用一用iptables。

我也写过几篇关于iptables的博客,第一篇为http://blog.chinaunix.net/space.php?uid=23629988&do=blog&id=85894

好,开始正题!

我看代码习惯从使用的地方开始。对应netfilter来说,使用它的地方,就是netfilter的挂载点。这方面的介绍可以参见http://blog.chinaunix.net/space.php?uid=23629988&do=blog&id=85894

NF_HOOK为netfilter的挂载点的调用函数,在kernel中搜索该关键字,搜索结果如下:


从函数的名字以及第三列的代码,可以看出netfilter确实只有五个挂载点,prerouting,local in,forward,local out 和postrouting。不过针对不同的使用,似乎有不同的五个挂载点的定义。如对应brige来说,有NF_BR_PRE_ROUTING,NF_BR_PRE_ROUTING等,而ARP也有自己的NF_ARP_IN,NF_ARP_IN等定义。

为了搞清楚这个,让我们去看NF_HOOK的代码。NF_HOOK->NF_HOOK_THRESH->nf_hook_thresh->nf_hook_slow——这个是最终的执行函数。
  1. /* Returns 1 if okfn() needs to be executed by the caller,
  2.  * -EPERM for NF_DROP, 0 otherwise. */
  3. int nf_hook_slow(u_int8_t pf, unsigned int hook, struct sk_buff *skb,
  4.          struct net_device *indev,
  5.          struct net_device *outdev,
  6.          int (*okfn)(struct sk_buff *),
  7.          int hook_thresh)
  8. {
  9.     struct list_head *elem;
  10.     unsigned int verdict;
  11.     int ret = 0;

  12.     /* We may already have this, but read-locks nest anyway */
  13.     rcu_read_lock();
     /*
     根据协议pf和挂载点类型,取得第一个元素。
     nf_hooks为一个二维数组,其类型为一个双链表list_head。它最大行数为NFPROTO_NUMPROTO,即支持的协议      最大个数,分别为NFPROTO_UNSPEC,NFPROTO_IPV4,NFPROTO_ARP,NFPROTO_BRIDGE,NFPROTO_IPV6,N
     FPROTO_DECNET。而最大列数为NF_MAX_HOOKS,即为最大挂载点个数+1。

     看到这里,我来猜测一下为什么netfilter要区分协议:
     1. 通过区分协议,简化了数据包的parse过程;
     2. 指定协议,也方便用户配置。
     */
  1.     elem = &nf_hooks[pf][hook];
  2. next_hook:
  3.     /* 开始遍历对应的netfilter的规则,即对应的proto和hook挂载点 */
  4.     verdict = nf_iterate(&nf_hooks[pf][hook], skb, hook, indev,
  5.              outdev, &elem, okfn, hook_thresh);
  6.     if (verdict == NF_ACCEPT || verdict == NF_STOP) {
  7.         /* 判定结果为NF_ACCEPT和NF_STOP,返回结果。即pass所有规则 */
  8.         ret = 1;
  9.     } else if (verdict == NF_DROP) {
  10.         /* 要drop掉这个数据包 */
  11.         kfree_skb(skb);
  12.         ret = -EPERM;
  13.     } else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) {
  14.         /* 
  15.         判定结果有enque,即将数据包传给用户空间的queue handler。
  16.         这个的verdict被重用了。低16位被用于存储判定结果,而高16位用于存储enque的数量。
  17.         */
  18.         if (!nf_queue(skb, elem, pf, hook, indev, outdev, okfn,
  19.              verdict >> NF_VERDICT_BITS))
  20.             goto next_hook; //只有在这个elem无效时,nf_queue才会返回0,继续下面的hook判定。
  21.     }
  22.     rcu_read_unlock();
  23.     return ret;
  24. }
下面进入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.     /*
  12.      * The caller must not block between calls to this
  13.      * function because of risk of continuing from deleted element.
  14.      */
  15.     list_for_each_continue_rcu(*i, head) {
  16.         struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;
         /*
         规则的优先级判断,目前从NF_HOOK到这里的优先级为INT_MIN,即最小。这种情况下,所以的规则都会被检          查。
         这个hook_thresh用于保证某些规则在某些挂载点不起作用。
         搜索NF_HOOK_THRESH关键字,可以发现对于协议NFPROTO_BRIDGE的挂载点NF_BR_PRE_ROUTING,其thr          eash被设为1,这样保证仅部分规则起作用。
         */
  1.         if (hook_thresh > elem->priority)
  2.             continue;

  3.         /* Optimization: we don't need to hold module
  4.          reference here, since function can't sleep. --RR */
  5.         verdict = elem->hook(hook, skb, indev, outdev, okfn);
  6.         if (verdict != NF_ACCEPT) {
  7.             /* 不等于ACCEPT,就可能直接返回判定结果 */
  8. #ifdef CONFIG_NETFILTER_DEBUG
  9.             if (unlikely((verdict & NF_VERDICT_MASK)
  10.                             > NF_MAX_VERDICT)) {
  11.                 NFDEBUG("Evil return from %p(%u).\n",
  12.                     elem->hook, hook);
  13.                 continue;
  14.             }
  15. #endif
  16.             /* 还需要不能等于NF_REPEAT。也就是说既不能等于NF_ACCEPT和NF_REPEAT,即可直接返回判定结
  17.             果,无需后面的判定 */
  18.             if (verdict != NF_REPEAT)
  19.                 return verdict;
             /* 判定结果为NF_REPEAT,则重复这个规则的判定 */
  1.             *i = (*i)->prev;
  2.         }
  3.     }
     
     /* 所有判定结果都为NF_ACCEPT,才可返回NF_ACCEPT */
  1.     return NF_ACCEPT;
  2. }
从今天的这两个函数的学习,可以理解netfilter的判定结果的含义了。
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。


阅读(855) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~