分类: 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协议,只用到了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 < ((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,要再插入优先级为3的nf_hook_ops。
那么从头开始比较,3 < 1不成立;3 < 3也不成立,3 < 4成立,此时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 < 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) // 上面的调用设置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); // 已经分析了此函数
}