免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1268 | 回复: 0
打印 上一主题 下一主题

HOOK [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-01-22 16:31 |只看该作者 |倒序浏览

本章主要说明两个问题:
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协议,只用到了5个HOOK点。
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 priority)  // 遍历链表,按照指定的优先级插入链表
                                          break;                  // 如果要插入的nf_hook_ops的优先级比当前的小,才退出循环。
              }                                                                     // 这样的话,也就是说,相同优先级的nf_hook_ops会按照插入顺序排列
              list_add_rcu(&reg->list, i->prev);
              spin_unlock_bh(&nf_hook_lock);

              synchronize_net();
              return 0;
}
EXPORT_SYMBOL(nf_register_hook);

插入举例:
现在有列表,优先级为:1    3     4     5,要再插入优先级为3的nf_hook_ops。
那么从头开始比较,3 不成立;3 也不成立,3 成立,此时i在4这边。
这样的话,插入点是4前面的那个3和4之间。


还有一个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
                            err = nf_register_hook(&reg);
                            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
                            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)                                                        // 上面的调用设置cond为1,所以这里恒假
                            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];              
pf和hook是从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);              // 已经分析了此函数
}


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/110937/showart_2157902.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP