http://www.skynet.org.cn/viewthread.php?tid=7&extra=page%3D2
第二章 钩子函数的调用
作者:kendo
出处:SecPower[www.skynet.org.cn]
版权所有,转载请注明作者与出处。
1、nf_hook_slow函数
Hook被注册后,它就会在那里守株待兔,等待自动送上门的数据包,那么内核是如何调用到注册的Hook的呢?在分析NF_HOOK的时候说过,如果指定协议的指定钩子类型上注册了钩子函数数,会调用nf_hook_slow函数:
内核的数据转发函数,调用宏NF_HOOK时,会告诉它协议簇和Hook类型,nf_hook_slow函数根据这两个要素,可以很轻易地从nf_hooks 中取得对应的前面注册好的Hook链表的首部:
elem = &nf_hooks[pf][hook];
然后,就调用函数nf_iterate ,遍历Hook链表,调用链用上所有的Hook函数
2、nf_iterate 函数
我们可以看到,函数在一个链表的遍历过程中,不断地调用对应的Hook函数:
verdict = elem->hook(hook, skb, indev, outdev, okfn);
以执行我们注册好的Hook,对数据包进行我们想要的处理。函数的返回值verdict决定了封包的命令,接受或丢弃,Netfilter共有6 个值:
逐 个分析每一个Hook函数,就可以了解整个Netfilter的运行机理,这是我后面的重点内容。现在需要回答的问题是,如果同时注册了N个Hook,它 们对封包的处理,有的是ACCEPT,有的是DROP,那么如果前一个DROP了它,后面的Hook函数还会被执行吗?来看返回值的处理代码:
很明显,如果是ACCEPT动作,那么还会继续调用下一个Hook,否则,当是REPEAT时,再返回前一个Hook,至于其它动作,则直接返回,不再断续调用下一个Hook,换句话讲,就是如果数据包已经被前一个Hook函数丢弃,当然不会再被交给下一个Hook函数。
作者:kendo
出处:SecPower[www.skynet.org.cn]
版权所有,转载请注明作者与出处。
1、nf_hook_slow函数
Hook被注册后,它就会在那里守株待兔,等待自动送上门的数据包,那么内核是如何调用到注册的Hook的呢?在分析NF_HOOK的时候说过,如果指定协议的指定钩子类型上注册了钩子函数数,会调用nf_hook_slow函数:
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;
}
* -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 函数
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;
}
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 个值:
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函数
#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函数还会被执行吗?来看返回值的处理代码:
CODE:
if (verdict != NF_ACCEPT)
{
if (verdict != NF_REPEAT)
return verdict;
*i = (*i)->prev;
}
{
if (verdict != NF_REPEAT)
return verdict;
*i = (*i)->prev;
}
很明显,如果是ACCEPT动作,那么还会继续调用下一个Hook,否则,当是REPEAT时,再返回前一个Hook,至于其它动作,则直接返回,不再断续调用下一个Hook,换句话讲,就是如果数据包已经被前一个Hook函数丢弃,当然不会再被交给下一个Hook函数。