钩子函数的调用

1、nf_hook_slow函数

Hook被注册后,它就会在那里守株待兔,等待自动送上门的数据包,那么内核是如何调用到注册的Hook的呢?在分析NF_HOOK的时候说过,如果指定协议的指定钩子类型上注册了钩子函数数,会调用nf_hook_slow函数:

[Copy to clipboard] [ - ]
CODE:
/* 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();

#ifdef CONFIG_NETFILTER_DEBUG
        if (unlikely((*pskb)->nf_debug & (1 << hook))) {
                printk("nf_hook: hook %i already set./n", hook);
                nf_dump_skb(pf, *pskb);
        }
        (*pskb)->nf_debug |= (1 << hook);
#endif
        /*取得对应的链表首部*/
        elem = &nf_hooks[pf][hook];
next_hook:
        /*调用对应的钩子函数*/
        verdict = nf_iterate(&nf_hooks[pf][hook], pskb, hook, indev,
                             outdev, &elem, okfn, hook_thresh);
        /*判断返回值,做相应的处理*/
if (verdict == NF_ACCEPT || verdict == NF_STOP) {
                ret = 1;                /*前面提到过,返回1,则表示装继续调用okfn函数指针*/
                goto unlock;
        } else if (verdict == NF_DROP) {
                kfree_skb(*pskb);                /*删除数据包,需要释放skb*/
                ret = -EPERM;
        } else if (verdict == NF_QUEUE) {
                NFDEBUG("nf_hook: Verdict = QUEUE./n");
                if (!nf_queue(*pskb, elem, pf, hook, indev, outdev, okfn))
                        goto next_hook;
        }
unlock:
        rcu_read_unlock();
        return ret;
}

内核的数据转发函数,调用宏NF_HOOK时,会告诉它协议簇和Hook类型,nf_hook_slow函数根据这两个要素,可以很轻易地从nf_hooks 中取得对应的前面注册好的Hook链表的首部:
elem = &nf_hooks[pf][hook];
然后,就调用函数nf_iterate ,遍历Hook链表,调用链用上所有的Hook函数

2、nf_iterate 函数

[Copy to clipboard] [ - ]
CODE:
static unsigned int nf_iterate(struct list_head *head,
                               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) {                        /*遍历所有注册的Hook*/
                /*取得当前遍历的Hook*/
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 */
                /*调用Hook 的函数*/
`        verdict = elem->hook(hook, skb, indev, outdev, okfn);
                if (verdict != NF_ACCEPT) {
#ifdef CONFIG_NETFILTER_DEBUG
                        if (unlikely(verdict > NF_MAX_VERDICT)) {
                                NFDEBUG("Evil return from %p(%u)./n",
                                        elem->hook, hook);
                                continue;
                        }
#endif
                        if (verdict != NF_REPEAT)
                                return verdict;
                        *i = (*i)->prev;
                }
        }
        return NF_ACCEPT;
}

我们可以看到,函数在一个链表的遍历过程中,不断地调用对应的Hook函数:
verdict = elem->hook(hook, skb, indev, outdev, okfn);
以执行我们注册好的Hook,对数据包进行我们想要的处理。函数的返回值verdict决定了封包的命令,接受或丢弃,Netfilter共有6 个值:

[Copy to clipboard] [ - ]
CODE:
/* Responses from hook functions. */
#define NF_DROP 0                                 丢弃该数据包
#define NF_ACCEPT 1                                 保留该数据包
#define NF_STOLEN 2                                 记掉该数据包
#define NF_QUEUE 3                                 将该数据包插入到用户空间
#define NF_REPEAT 4              再次调用该Hook函数
#define NF_STOP 5                                 停止检测,不再进行下一个Hook函数

逐 个分析每一个Hook函数,就可以了解整个Netfilter的运行机理,这是我后面的重点内容。现在需要回答的问题是,如果同时注册了N个Hook,它 们对封包的处理,有的是ACCEPT,有的是DROP,那么如果前一个DROP了它,后面的Hook函数还会被执行吗?来看返回值的处理代码:

[Copy to clipboard] [ - ]
CODE:
if (verdict != NF_ACCEPT)
{
        if (verdict != NF_REPEAT)
                        return verdict;
        *i = (*i)->prev;
}

很明显,如果是ACCEPT动作,那么还会继续调用下一个Hook,否则,当是REPEAT时,再返回前一个Hook,至于其它动作,则直接返回,不再断续调用下一个Hook,换句话讲,就是如果数据包已经被前一个Hook函数丢弃,当然不会再被交给下一个Hook函数。

你可能感兴趣的:(Unix/Linux)