分类: LINUX
2014-06-24 13:20:47
原文地址:四、Netfilter的HOOK机制 作者:guanglongxishui
Netfilter 中定义了一个全局二维数组,来存放注册了的处理函数。
struct list_head nf_hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS] __read_mostly;
每个hook点上有一个或多个注册上去的处理函数。
每个Netfilter 钩子函数定义如下:
typedef unsigned int nf_hookfn(unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *));
钩子点处理函数的返回值:
#define NF_DROP 0 //丢弃报文
#define NF_ACCEPT 1 //报文通过,继续执行下一个处理
#define NF_STOLEN 2 //报文已经被处理并已经释放
#define NF_QUEUE 3 //报文需要加入队列,直接把报文上送给用户应用进程处理
#define NF_REPEAT 4 //重复执行该处理
#define NF_STOP 5 //结束执行
各个功能的处理函数被封装在结构体 struct nf_hook_ops中,具体定义如下。
struct nf_hook_ops
{
struct list_head list;
nf_hookfn *hook; //钩子处理函数
struct module *owner;
u_int8_t pf; //协议族
unsigned int hooknum; //钩子点
int priority; //钩子函数优先级,值越小优先级越高
};
注册处理函数:
int nf_register_hook(struct nf_hook_ops *reg)
{
struct nf_hook_ops *elem;
int err;
//使用互斥锁nf_hook_mutex来保护二维数组下的hook_ops链表
err = mutex_lock_interruptible(&nf_hook_mutex);
if (err < 0)
return err;
//按照hook_op的优先级来把hook_op注册到全局二维数组中去
list_for_each_entry(elem, &nf_hooks[reg->pf][reg->hooknum], list)
{
if (reg->priority < elem->priority)
break;
}
list_add_rcu(?->list, elem->list.prev);
mutex_unlock(&nf_hook_mutex);
return 0;
}
处理函数已经注册到netfilter 中了,下面我们来看看netfilter是怎么找到并执行该处理函数的。
在协议栈中相应位置嵌入Netfilter的函数NF_HOOK,来拦截报文送到Netfilter中进行处理。
#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)
参数:
pf:协议族
hook:协议定义的hook点
skb:报文
indev:入接口
outdev:出接口
okfn:netfilter处理完成后来处理报文的函数
#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) \
NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, INT_MIN)
#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); //如果netfilter 返回值为1,执行ok函数 \
__ret;})
static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook,
struct sk_buff *skb,
struct net_device *indev,
struct net_device *outdev,
int (*okfn)(struct sk_buff *), int thresh,
int cond)
{
//如果cond为1,直接跳过netfilter的处理
if (!cond)
return 1;
return nf_hook_slow(pf, hook, skb, indev, outdev, okfn, thresh);
}
int nf_hook_slow(u_int8_t pf, unsigned int hook, struct sk_buff *skb,
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;
rcu_read_lock();
//找到hook点的处理函数的链表
elem = &nf_hooks[pf][hook];
next_hook:
//挨个执行处理函数
verdict = nf_iterate(&nf_hooks[pf][hook], skb, hook, indev,
outdev, &elem, okfn, hook_thresh);
if (verdict == NF_ACCEPT || verdict == NF_STOP) {
ret = 1;
} else if (verdict == NF_DROP) {
kfree_skb(skb);
ret = -EPERM;
}
/*如果返回值是要把报文存入用户自定义的队列中(TARGET QUEUE和 NFQUEUE
使用),把verdict字段分成了两个部分,前一部分来定义返回值为入队操作,后半部分
给出入队的队列号*/
else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) {
if (!nf_queue(skb, elem, pf, hook, indev, outdev, okfn,
verdict >> NF_VERDICT_BITS))
goto next_hook;
}
rcu_read_unlock();
return ret;
}
unsigned int nf_iterate(struct list_head *head,
struct sk_buff *skb,
unsigned 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;
list_for_each_continue_rcu(*i, head)
{
struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;
if (hook_thresh > elem->priority)
continue;
//执行注册的hook函数
verdict = elem->hook(hook, skb, indev, outdev, okfn);
if (verdict != NF_ACCEPT)
{
if (verdict != NF_REPEAT)
return verdict;
*i = (*i)->prev;
}
}
return NF_ACCEPT;
}
(未完待续)