Linux下Netfilter创建自己的Hook函数,让数据包可以发送到用户层,然后统计节点的负载信息
写在最前面,我的linux内核版本是4.4.0-31-generic,版本是Ubuntu 16.04.1 LTS
希望实现的功能如题目所示,该功能实际分为几个步骤
- 在用户空间编写自己的Hook函数C文件,并将该文件编译成内核模块(后缀为.ko),然后加载到内核中,并测试Hook函数是否起作用
- 在用户空间编写C文件,接收内核传过来的数据包,统计负载信息,然后回传给内核
一、在用户空间编写自己的Hook函数C文件,编译生成.ko模块,并动态加载到内核中
Makefile内容为:
obj-m := myhook.o
all:
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
linux头文件在下面这个目录中可以找到
/usr/src/linux-headers-4.4.0-31-generic(内核版本)/include/
然后如果是
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/ip.h>
#include <asm/atomic.h>
#include <linux/version.h>
#include <linux/skbuff.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/moduleparam.h>
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter_bridge.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Wang");
MODULE_DESCRIPTION("Myhook");
static int pktcnt = 0;
//我们自己定义的hook回调函数
static unsigned int myhook_func(unsigned int hooknum, struct sk_buff **skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
{
const struct iphdr *iph = ip_hdr(skb);
return NF_QUEUE;
}
static struct nf_hook_ops nfho =
{
.hook=myhook_func, //回调函数是myhook_func
.pf=PF_INET, //协议类型
.hooknum=NF_BR_POST_ROUTING,//挂载点
.priority=NF_IP_PRI_FIRST,//优先级
};
static int __init myhook_init(void)
{
return nf_register_hook(&nfho);
}
static void __exit myhook_fini(void)
{
nf_unregister_hook(&nfho);
}
module_init(myhook_init);
module_exit(myhook_fini);
把makefile和myhook.c放在同一目录下,然后make即可,会生成myhook.ko文件。
动态加载内核模块命令为 sudo insmod myhook.ko
动态卸载内核模块命令为 sudo rmmod myhook.ko
在加载这个模块之后,你会发现自己机器无法上网了,这是因为所有的数据包都被排队到了用户空间中,需要在用户空间中进行处理,也就是我们的第二部分需要做的内容。
二、在用户空间编写C文件,接收内核传过来的数据包,统计负载信息,然后回传给内核
之前网络上很多教程说要用ip_queue模块,但实际上这个模块在新版本内核中已经没有了,所以很多程序的头文件
include <linux/netfilter_ipv4/ip_queue.h>
是已经不存在了的,没有办法使用。
所以在新版本内核中,需要用到三个库,下载地址为https://netfilter.org/projects/libnetfilter_queue/
1、libmnl
2、libnfnetlink
3、libnetfilter_queue
下载源代码后,先安装libmnl,然后安装libnfnetlink,再安装libnetfilter_queue,编译时configure, make ,make install (注意make install的时候需要sudo)
libnetfilter_queue的示例代码在libnetfilter_queue-1.0.3/examples/nf-queue和libnetfilter_queue-1.0.3/utils/nfqnl_test下都有
我们测试验证一下功能:
1、动态加载第一步中的内核模块;sudo insmod myhook.ko,把hook挂在post_routing上
2、终端执行ping www.baidu.com命令,命令没反应,发不出去数据包,因为数据包在post_routing处被hook送到用户空间了
3、运行sudo ./nf-queue 0,这里的0表示queue_num是0,目前我仅实现了hook挂在后把数据包传到第0个queue_num上,按道理应该是可以更改queue_num的,需要进一步研究。
4、此时终端会显示nf-queue收到了数据包,并且可以看到payload len,然后将数据包回传给内核,内核可以继续发送
5、关闭nf-queue,此时ping命令无法继续发送数据包
利用payload len我们就可以统计负载信息了