Chinaunix首页 | 论坛 | 博客
  • 博客访问: 107768
  • 博文数量: 41
  • 博客积分: 2520
  • 博客等级: 少校
  • 技术积分: 440
  • 用 户 组: 普通用户
  • 注册时间: 2010-01-22 16:25
文章分类

全部博文(41)

文章存档

2010年(41)

我的朋友

分类: LINUX

2010-01-22 16:31:54

本章主要说明两个问题:

1.         挂载点的数据结构以及如何挂载;

2.         如何从网络协议栈经挂载点到hook函数;

 

挂载点的数据结构:

 

/* In this code, we can be waiting indefinitely for userspace to

 * service a packet if a hook returns NF_QUEUE.  We could keep a count

 * of skbuffs queued for userspace, and not deregister a hook unless

 * this is zero, but that sucks.  Now, we simply check when the

 * packets come back: if the hook is gone, the packet is discarded. */

struct list_head nf_hooks[NPROTO][NF_MAX_HOOKS];

nf_hooks[][]list_head类型的二维数组。

一维

#define NPROTO                 32

表示目前内核支持的协议类型;

二维

/* Largest hook number + 1 */

#define NF_MAX_HOOKS 8

表示每种协议的HOOK地点。这些点是定义在内核代码中,linux当前是用NF_HOOK()来实现的。对于PF_INET协议,只用到了5HOOK点。

EXPORT_SYMBOL(nf_hooks);                                                                   // 全局的

static DEFINE_SPINLOCK(nf_hook_lock);                                    // 全局锁

 

int nf_register_hook(struct nf_hook_ops *reg)                 // nf_hook_ops定义HOOK点及其操作函数等

{

              struct list_head *i;

 

              spin_lock_bh(&nf_hook_lock);

              list_for_each(i, &nf_hooks[reg->pf][reg->hooknum]) {              // 找到指定协议的指定HOOK点的头指针

                            if (reg->priority < ((struct nf_hook_ops *)i)->priority)  // 遍历链表,按照指定的优先级插入链表

                                          break;                  // 如果要插入的nf_hook_ops的优先级比当前的小,才退出循环。

              }                                                                     // 这样的话,也就是说,相同优先级的nf_hook_ops会按照插入顺序排列

              list_add_rcu(®->list, i->prev);

              spin_unlock_bh(&nf_hook_lock);

 

              synchronize_net();

              return 0;

}

EXPORT_SYMBOL(nf_register_hook);

 

插入举例:

现在有列表,优先级为:1    3     4     5,要再插入优先级为3nf_hook_ops

那么从头开始比较,3 < 1不成立;3 < 3也不成立,3 < 4成立,此时i4这边。

这样的话,插入点是4前面的那个34之间。

 

 

还有一个nf_register_hooks()函数,其实就是简单包装了nf_register_hook()。和函数名有个复数一样,它其实就是对nf_hook_ops的数组来依次注册里面的元素。

int nf_register_hooks(struct nf_hook_ops *reg, unsigned int n)

{

              unsigned int i;

              int err = 0;

 

              for (i = 0; i < n; i++) {

                            err = nf_register_hook(®[i]);

                            if (err)

                                          goto err;

              }

              return err;

 

err:

              if (i > 0)

                            nf_unregister_hooks(reg, i);

              return err;

}

EXPORT_SYMBOL(nf_register_hooks);

例如:

              /* Register hooks */

              ret = nf_register_hooks(ipt_ops, ARRAY_SIZE(ipt_ops));

              if (ret < 0)

                            goto cleanup_table;

 

 


如何从网络协议栈到HOOK函数:

 

首先说明是通过NF_HOOK()宏来实现的,

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

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

 

看看NF_HOOK_THRESH()

#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, 1)) == 1)\

              __ret = (okfn)(skb);                                                                                        \               // 如果上面返回1,那么执行指定的函数

__ret;})                                                                                                                                                                          // 上面的返回值是nf_hook_slow()返回的

                                                                                                                                                                                                    // #define NF_ACCEPT 1

在看看nf_hook_thresh()

/**

 *           nf_hook_thresh - call a netfilter hook

 *          

 *           Returns 1 if the hook has allowed the packet to pass.  The function

 *           okfn must be invoked by the caller in this case.  Any other return

 *           value indicates the packet has been consumed by the hook.

 */

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

                                                         struct sk_buff **pskb,

                                                         struct net_device *indev,

                                                         struct net_device *outdev,

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

                                                         int cond)

{

              if (!cond)                                                        // 上面的调用设置cond1,所以这里恒假

                            return 1;

#ifndef CONFIG_NETFILTER_DEBUG

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

                            return 1;

#endif

              return nf_hook_slow(pf, hook, pskb, indev, outdev, okfn, thresh);    // 最重要的就是这个函数

}

 

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

 * -EPERM for NF_DROP, 0 otherwise. */

int nf_hook_slow(int pf, unsigned int hook, struct sk_buff **pskb,

                             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 = 0;

 

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

              rcu_read_lock();

 

              elem = &nf_hooks[pf][hook];             

pfhook是从NF_HOOK()那里传递过来的。也就是说,是网络协议栈那边写死了的。

比如ip_rcv()里面:

              return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,

                                   ip_rcv_finish);

这样的话,就找到了HOOK点操作结构体链表的表头。

next_hook:

              verdict = nf_iterate(&nf_hooks[pf][hook], pskb, hook, indev,         // 参考下面的详细介绍

                                               outdev, &elem, okfn, hook_thresh);           // 这里会联系到操作数组里面的操作函数.hook

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

                            ret = 1;                                                                          // 上面NF_HOOK_THRESH()会执行指定的函数okfn

                            goto unlock;

              } else if (verdict == NF_DROP) {

                            kfree_skb(*pskb);

                            ret = -EPERM;                                                 // DROP,释放skb

              } else if ((verdict & NF_VERDICT_MASK)  == NF_QUEUE) {      // QUEUE先不考虑

                            NFDEBUG("nf_hook: Verdict = QUEUE.\n");

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

                                                verdict >> NF_VERDICT_BITS))

                                          goto next_hook;

              }

unlock:

              rcu_read_unlock();

              return ret;

}

EXPORT_SYMBOL(nf_hook_slow);


unsigned int nf_iterate(struct list_head *head,                    // HOOK点操作结构体链表的头指针

                                          struct sk_buff **skb,

                                          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);     // 这里就是要找的地方。调用了.hook函数

                            if (verdict != NF_ACCEPT) {              // 如果一个HOOK操作结构体中的.hook接受了包

#ifdef CONFIG_NETFILTER_DEBUG // 那么继续下此链表的一个HOOK操作结构体

                                          if (unlikely((verdict & NF_VERDICT_MASK)     // 注意okfn是传递给了.hook函数

                                                                                                  > NF_MAX_VERDICT)) {

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

                                                                elem->hook, hook);

                                                        continue;

                                          }

#endif

                                          if (verdict != NF_REPEAT)   // 如果不接受,并且不是REPEAT那么返回

                                                        return verdict;

                                          *i = (*i)->prev;                                  // 否则再来一次这个HOOK

                            }

              }

              return NF_ACCEPT;

}

 

对于filter表来说,.hook函数就是:

static struct nf_hook_ops ipt_ops[] = {

              {

                            .hook                   = ipt_hook,

                            .owner                  = THIS_MODULE,

                            .pf                        = PF_INET,

                            .hooknum              = NF_IP_LOCAL_IN,

                            .priority   = NF_IP_PRI_FILTER,

              },

              {

                            .hook                   = ipt_hook,

                            .owner                  = THIS_MODULE,

                            .pf                        = PF_INET,

                            .hooknum              = NF_IP_FORWARD,

                            .priority   = NF_IP_PRI_FILTER,

              },

              {

                            .hook                   = ipt_local_out_hook,

                            .owner                  = THIS_MODULE,

                            .pf                        = PF_INET,

                            .hooknum              = NF_IP_LOCAL_OUT,

                            .priority   = NF_IP_PRI_FILTER,

              },

};

 

 

/* The work comes in here from netfilter.c. */

static unsigned int

ipt_hook(unsigned int hook,

               struct sk_buff **pskb,

               const struct net_device *in,

               const struct net_device *out,

               int (*okfn)(struct sk_buff *))

{

              return ipt_do_table(pskb, hook, in, out, &packet_filter, NULL);              // 已经分析了此函数

}

阅读(429) | 评论(0) | 转发(0) |
0

上一篇:没有了

下一篇:Table Register

给主人留下些什么吧!~~