在编写linux内核中的网络模块时,用到了钩子函数也就是hook函数。现在来看看linux是如何实现hook函数的。
先介绍一个结构体:
struct nf_hook_ops,这个结构体是实现钩子函数必须要用到的结构体,所在文件:linux/netfilter.h 定义为:
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 *));
struct nf_hook_ops {
struct list_head list;
/* User fills in from here down. */
nf_hookfn *hook;
struct module *owner;
u_int8_t pf;
unsigned int hooknum;
/* Hooks are ordered in ascending priority. */
int priority;
};
其中的成员信息为:
hook :是一个函数指针,可以将自定义的函数赋值给它,来实现当有数据包到达是调用你自定义的函数。自定义函数的返回值为:
/* Responses from hook functions. */
#define NF_DROP 0
#define NF_ACCEPT 1
#define NF_STOLEN 2
#define NF_QUEUE 3
#define NF_REPEAT 4
#define NF_STOP 5
#define NF_MAX_VERDICT NF_STOP
owner:是模块的所有者,一般owner = THIS_MODULE ;
pf :是protocol flags,其取值范围为:
enum {
NFPROTO_UNSPEC = 0,
NFPROTO_IPV4 = 2,
NFPROTO_ARP = 3,
NFPROTO_BRIDGE = 7,
NFPROTO_IPV6 = 10,
NFPROTO_DECNET = 12,
NFPROTO_NUMPROTO,
};
hooknum :中存放的是用户自定义的钩子函数的调用时机,其取值为:
enum nf_inet_hooks {
NF_INET_PRE_ROUTING,
NF_INET_LOCAL_IN,
NF_INET_FORWARD,
NF_INET_LOCAL_OUT,
NF_INET_POST_ROUTING,
NF_INET_NUMHOOKS
};
其中的每个值的含义为:
priority : 为所定义的钩子函数的优先级,其取值为份两种:分别为IPV4 和 IPV6;
priority 的IPV4取值为:
enum nf_ip_hook_priorities {
NF_IP_PRI_FIRST = INT_MIN,
NF_IP_PRI_CONNTRACK_DEFRAG = -400,
NF_IP_PRI_RAW = -300,
NF_IP_PRI_SELINUX_FIRST = -225,
NF_IP_PRI_CONNTRACK = -200,
NF_IP_PRI_MANGLE = -150,
NF_IP_PRI_NAT_DST = -100,
NF_IP_PRI_FILTER = 0,
NF_IP_PRI_SECURITY = 50,
NF_IP_PRI_NAT_SRC = 100,
NF_IP_PRI_SELINUX_LAST = 225,
NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX,
NF_IP_PRI_LAST = INT_MAX,
};
所在文件:linux\netfilter_ipv4.h
priority 的IPV6取值为:
enum nf_ip6_hook_priorities {
NF_IP6_PRI_FIRST = INT_MIN,
NF_IP6_PRI_CONNTRACK_DEFRAG = -400,
NF_IP6_PRI_RAW = -300,
NF_IP6_PRI_SELINUX_FIRST = -225,
NF_IP6_PRI_CONNTRACK = -200,
NF_IP6_PRI_MANGLE = -150,
NF_IP6_PRI_NAT_DST = -100,
NF_IP6_PRI_FILTER = 0,
NF_IP6_PRI_SECURITY = 50,
NF_IP6_PRI_NAT_SRC = 100,
NF_IP6_PRI_SELINUX_LAST = 225,
NF_IP6_PRI_LAST = INT_MAX,
};
以上是对struct nf_hook_ops结构体中的每个字段的详解;
具体实例
struct nf_hook_ops my_hook = {
.hook = myfunction,
.owner = THIS_MODULE,
.pf = NFPROTO_IPV4,
.hooknum = NET_INET_FORWARD,
.priority = NF_IP4_PRI_FIRST
};
unsigned int myfunction( unsigend int hooknum, struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{}
如上面的代码一样,当定义一个struct nf_hook_ops结构体,并且对其完成了初始化以后,需要将这个结构体进行注册,之后这个结构体以及其中的自定义函数才会其作用。
注册一个struct nf_hook_ops需要用到的函数为:
int nf_register_hook(struct nf_hook_ops *reg)
其实这个 int nf_register_hook()函数在内核中的实现也没有多么的复杂,
来看看它是如何实现的:
struct list_head nf_hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS] __read_mostly;
EXPORT_SYMBOL(nf_hooks);
static DEFINE_MUTEX(nf_hook_mutex);
int nf_register_hook(struct nf_hook_ops *reg)
{
struct nf_hook_ops *elem;
int err;
err = mutex_lock_interruptible(&nf_hook_mutex);
if (err < 0)
return err;
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;
}
EXPORT_SYMBOL(nf_register_hook);
所在文件:net\netfilter\core.c
当不再需要使用这个struct nf_hook_ops时,需要注销这个结构体,其可用的函数为:
void nf_unregister_hook(struct nf_hook_ops *reg)
{
mutex_lock(&nf_hook_mutex);
list_del_rcu(®->list);
mutex_unlock(&nf_hook_mutex);
synchronize_net();
}
EXPORT_SYMBOL(nf_unregister_hook);
当一次需要注册多个struct nf_hook_ops结构体,如:
struct nf_hook_ops myhooks[n]时,使用:
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);
同样,当一次需要注销多个struct nf_hook_ops结构体是,使用:
void nf_unregister_hooks(struct nf_hook_ops *reg, unsigned int n)
{
while (n-- > 0)
nf_unregister_hook(®[n]);
}
EXPORT_SYMBOL(nf_unregister_hooks);
总结:
struct nf_hook_ops
int nf_register_hook( struct nf_hook_ops *reg );
void nf_unregister_hook( struct nf_hook_ops *reg );
int nf_register_hooks( struct nf_hook_ops *regs, unsigend int n );
void nf_unregister_hooks( struct nf_hook_ops *regs, unsigned int n );