【Linux 驱动】netfilter/iptables (三) 注册和注销Netfilter hook
继前面介绍了 netfilter hook,这里我们开始进行实例讲解:wqlkp.c:#include <linux/module.h>#include <linux/kernel.h>#include <linux/netfilter.h>#include <linux/netfilter_ipv4.h>MODULE_LICENSE("Dual BSD/GPL");static stru
继前面介绍了 netfilter hook,这里我们开始进行简单的实例讲解,主要是Netfilter hook的注册与注销:
wqlkp.c:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
MODULE_LICENSE("Dual BSD/GPL");
static struct nf_hook_ops nfho;
//钩子函数,注意参数格式与开发环境源码树保持一致
unsigned int hook_func(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
return NF_DROP;//丢弃所有的数据包
}
static int __init hook_init(void)
{
nfho.hook = hook_func;//关联对应处理函数
nfho.hooknum = NF_INET_PRE_ROUTING;//2.6.22及以后的内核中,内核态编程需要使用这个
nfho.pf = PF_INET;//ipv4,所以用这个
nfho.priority = NF_IP_PRI_FIRST;//优先级,第一顺位,首先执行我们定义的函数
nf_register_hook(&nfho);//注册
return 0;
}
static void __exit hook_exit(void)
{
nf_unregister_hook(&nfho);//注销
}
module_init(hook_init);
module_exit(hook_exit);
上面涉及到的数据结构及函数接口,请见上一篇博文。
Makefile:
ifneq ($(KERNELRELEASE),)
obj-m := wqlkp.o
else
KERNELDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
$(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules
endif
clean:
rm -f *.o *.ko *.mod.c .wqlkp*
加载模块后的结果就是,你访问不了网络了,要解除的话,卸载这个模块就行了。
为啥会出现上面的结果?主要看这两个
nfho.hook = hook_func;//关联的这个函数直接返回NF_DROP(看前篇博文),丢弃所有数据包,不再传输
nfho.hooknum = NF_INET_PRE_ROUTING;//这个告诉我们处理(关卡)的地点在哪,
//结合上面的处理函数,就是数据包刚通过链路层解包,进入网络层之后,它就被“关卡拦截”丢弃了,
//不再在协议栈中传输了,上层的传输层应用层均收不到数据包。
ok,老规矩,知其然还要知其所以然,我们去窥探下内核源码(Linux kernel 3.14):
注册函数:int nf_register_hook(struct nf_hook_ops *reg)
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);//释放锁
#if defined(CONFIG_JUMP_LABEL)
static_key_slow_inc(&nf_hooks_needed[reg->pf][reg->hooknum]);//忽略...
#endif
return 0;
}
从上面的代码就可以看出,所有的 struct nf_hook_ops 都以指针的形式记录在一个列表中,其保存的顺序就是按照优先级的顺序。这个表是一个二维数组,用来存储不同协议过滤点的回调处理函数。通过协议族类型和hook类型定位。如下图所示:
extern struct list_head nf_hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS];
注销函数:void nf_unregister_hook(struct nf_hook_ops *reg)
void nf_unregister_hook(struct nf_hook_ops *reg)
{
mutex_lock(&nf_hook_mutex);//获取互斥锁,不可中断
list_del_rcu(®->list);//删除指定对象
mutex_unlock(&nf_hook_mutex);//释放锁
#if defined(CONFIG_JUMP_LABEL)
static_key_slow_dec(&nf_hooks_needed[reg->pf][reg->hooknum]);
#endif
/**
* synchronize_net - Synchronize with packet receive processing
*
* Wait for packets currently being received to be done.
* Does not block later packets from starting.
*/
synchronize_net();
}
注销一个 Netfilter hook 其实就是把我们之前注册插入的 struct nf_hook_ops 指针对象从链表中删除,从代码可以看出,它并没有销毁这个对象。
在后面还有个 synchronize_net(); 这个函数可能会引起睡眠,其目的是等待处理完数据包接收过程。
参考资料:
《深入Linux网络核心堆栈》
Linux kernel 3.14 sourcecode
更多推荐
所有评论(0)