基于Netfilter框架, 编写自己的hook函数,获取经过hook点的数据包的IP地址

简介

1.Netfilter

Netfilter是linux内核的一个子系统。它作为一个通用的、抽象的框架,提供一整套的hook函数的管理机制。netfilter提供了一系列的接口,将一个到达本机的数据包或者经本机转发的数据包流程中添加了一些可供用户操作的点,这些点被称为HOOK点。
Netfilter采用模块化设计,具有良好的可扩充性。其重要工具模块IPTables从用户态的iptables连接到内核态的Netfilter的架构中,Netfilter与IP协议栈是无缝契合的,使得诸如数据包过滤、网络地址转换(NAT)和基于协议类型的连接跟踪成为了可能。当我们希望过滤特定的数据包或者需要修改数据包的内容再发送出去,这些动作主要都在netfilter中完成。
IPTables工具就是用户空间和内核的Netfilter模块通信的手段,IPTables命令提供很多选项来实现过滤数据包的各种操作,所以,我们在定义数据包过滤规则时,并不需要去直接修改内核中的netfilter模块。Netfilter的实质就是定义一系列的hook点(挂钩),每个hook点上可以挂载多个hook函数,hook函数中就实现了我们要对数据包的内容做怎样的修改、以及要将数据包放行还是过滤掉。数据包进入netfilter框架后,实际上就是依次经过所有hook函数的处理。
基于Netfilter框架, 编写自己的hook函数,获取经过hook点的数据包的IP地址_第1张图片
Netfilter主要通过表、链实现规则,Netfilter是表的容器,表是链的容器,链是规则的容器,最终形成对数据报处理规则的实现。
基于Netfilter框架, 编写自己的hook函数,获取经过hook点的数据包的IP地址_第2张图片

表:

Filter 过滤数据,用来控制让哪些数据可以通过,哪些数据不能通过
NAT 处理网络地址转换的,控制要不要进行地址转换,以及怎样修改源地址或目的地址,从而影响数据包的路由,达到连通的目的,这是家用路由器必备的功能。
Mangle 修改IP数据包头,比如修改TTL值,同时也用于给数据包添加一些标记,从而便于后续其它模块对数据包进行处理(这里的添加标记是指往内核skb结构中添加标记,而不是往真正的IP数据包上加东西)。
Raw 给数据包打标记,从而控制哪些数据包不被connection tracking所追踪。
Security 里面的rule跟SELinux有关,主要是在数据包上设置一些SELinux的标记,便于跟SELinux相关的模块来处理该数据包。

chains

分别对应5个钩子:
基于Netfilter框架, 编写自己的hook函数,获取经过hook点的数据包的IP地址_第3张图片
基于Netfilter框架, 编写自己的hook函数,获取经过hook点的数据包的IP地址_第4张图片

rule

rule包含两部分信息:
Matching如何匹配一个数据包,匹配条件很多,比如协议类型、源/目的IP、源/目的端口、in/out接口、包头里面的数据以及连接状态等,
Targets 找到匹配的数据包之后怎么办,常见的有下面几种:
DROP:直接将数据包丢弃,不再进行后续的处理
RETURN: 跳出当前chain,该chain里后续的rule不再执行
QUEUE: 将数据包放入用户空间的队列,供用户空间的程序处理
ACCEPT: 同意数据包通过,继续执行后续的rule
跳转到其它用户自定义的chain继续执行

代码:打印出经过钩子函数的包的源,目的IP地址

//打印出经过钩子函数的包的源目的ip地址
//sample.c

//头文件包含
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

//模块信息
MODULE_LICENSE("GPL");
MODULE_AUTHOR("luxiangmin");
MODULE_DESCRIPTION("sample");

#define NIPQUAD(addr) \
  ((unsigned char *)&addr)[0], \
  ((unsigned char *)&addr)[1], \
  ((unsigned char *)&addr)[2], \
  ((unsigned char *)&addr)[3]

//入口:sample函数 
static unsigned int sample(unsigned int hooknum, struct sk_buff * skb, const struct net_device *in, const struct net_device *out, int (*okfn) (struct sk_buff *))
{ 
    __be32 sip,dip;
 if(skb){
   struct sk_buff *sb = NULL;
   sb = skb;//获取收发包的缓冲区域
   struct iphdr *ipiph;
   ipiph  = ip_hdr(sb);//获取ip头部
   sip = ipiph->saddr;//取出ip头部的源ip地址
   dip = ipiph->daddr;//取出ip头部的目的ip地址
//打印ip地址
   printk("lu xianmin source add: %d.%d.%d.%d\n lu xiangmin destination add: %d.%d.%d.%d\n ", NIPQUAD(sip), NIPQUAD(dip));
	}
//同意数据包通过,继续执行后续rule
 return NF_ACCEPT;
}

//1.定义自己的钩子函数:nf_hook_ops类型的结构体 sample_ops 
 struct nf_hook_ops sample_ops = {
   .list =  {NULL,NULL},
   .hook = sample,//定义的钩子函数名称
   .pf = PF_INET,//协议族:ipv4
   .hooknum = NF_INET_PRE_ROUTING,//钩子的位置:
   .priority = NF_IP_PRI_FILTER+2, //钩子优先级
 };

//2定义 钩子 注册函数
static int __init sample_init(void) {
  if(nf_register_hook(&sample_ops)){
        //如果注册失败,打印内核级错误信息
	printk(KERN_ERR"sample_ops nf_register_hook() failed=lu xiang min\n");
return -1;
	}
printk(KERN_WARNING"sample zhu ce cheng gong  lu xiangmin\n");
  return 0;
}

 //3.定义 钩子 解除注册函数
static void __exit sample_exit(void) {
  nf_unregister_hook(&sample_ops);
printk(KERN_WARNING"sample exit  lu xiangmin\n");
}

 //在模块加载时(即执行sudo insmod sample时),对钩子函数进行注册:调用注册函数
 module_init(sample_init);
//在模块卸载时(即执行sudo rmmod sample 时),对注册的钩子函数进行解注册:调用解注册函数
 module_exit(sample_exit); 

对应的Makefile文件:

Makefile文件,注意,命令行以Tab键开始

ifneq ($(KERNELRELEASE),)
obj-m := sample.o
else
        KERNELBUILD :=/lib/modules/$(shell uname -r)/build
        PWD :=$(shell pwd)
all:
        make -C $(KERNELBUILD) M=$(PWD) modules
clean:
        rm -rf *.o *.ko *.mod.c .*.cmd *.markers *.order *.symvers .tmp_versions
endif

写好源文件和Makefile文件之后,在其所在目录下,开始编译:
make
编译完成,将.ko文件加载到内核中:
insmod sample.ko
查看内核模块打印输出:
dmesg
查看模块:
lsmod
卸载模块:
rmmod sample(注意,没有.ko后缀)
消除编译过程生成的文件:
make clean

打印出经过钩子函数的包的源,目的IP地址:
基于Netfilter框架, 编写自己的hook函数,获取经过hook点的数据包的IP地址_第5张图片

你可能感兴趣的:(Linux,netfilter,hook)