本次的任务是要在linux下利用netfilter往内核挂钩子,截获数据包。
这几天的练习,发现很多结构体以及技术涉及到内核版本,如果你发现有些结构体的成员什么的未定义,大多是这种问题。本机内核版本2.6.32-21-generic。查看内核版本命令uanme -r。
通俗的说,netfilter的架构就是在整个网络流程的若干位置放置了一些检测点(HOOK),而在每个检测点上登记了一些处理函数进行处理(如包过滤,NAT等,甚至可以是 用户自定义的功能)。
IP层的五个HOOK点的位置如下所示
[1]:NF_IP_PRE_ROUTING:刚刚进入网络层的数据包通过此点(刚刚进行完版本号,校验和等检测), 目的地址转换在此点进行;
[2]:NF_IP_LOCAL_IN:经路由查找后,送往本机的通过此检查点,INPUT包过滤在此点进行;
[3]:NF_IP_FORWARD:要转发的包通过此检测点,FORWORD包过滤在此点进行;
[4]:NF_IP_POST_ROUTING:所有马上便要通过网络设备出去的包通过此检测点,内置的源地址转换功能(包括地址伪装)在此点进行;
[5]:NF_IP_LOCAL_OUT:本机进程发出的包通过此检测点,OUTPUT包过滤在此点进行。
进入HOOK点,执行通过nf_register_hook()登记的功能,如果我们想加入自己的代码,便要用nf_register_hook函数,其函数原型为:
int nf_register_hook(struct nf_hook_ops *reg)。 我们考察一下struct nf_hook_ops结构:
struct nf_hook_ops
{
struct list_head list; //链表成员
/* User fills in from here down. */
nf_hookfn *hook; //钩子函数指针
struct module *owner;
int pf; //协议簇,对于ipv4而言,是PF_INET
int hooknum; //hook类型
/* Hooks are ordered in ascending priority. */
int priority; //优先级
};
我们的工作便是生成一个struct nf_hook_ops结构的实例,并用nf_register_hook将其HOOK上。其中list项我们总要初始化为{NULL,NULL};由于一般在IP层工作,pf总是PF_INET;hooknum就是我们选择的HOOK点;一个HOOK点可能挂多个处理函数,谁先谁后,便要看优先级,即priority的指定了。netfilter_ipv4.h中用一个枚举类型指定了内置的处理函数的优先级:hook是提供的处理函数,也就是我们的主要工作.它的五个参数将由NFHOOK宏传进去。
本例简单地截获所有的数据包,并丢弃,这样就阻断了系统的网络链接,网页、ping等都收不到数据包。
#include <stdio.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
/* 用于注册我们的函数的数据结构 */
static struct nf_hook_ops nfho;
int count=0;
/* 注册的hook函数的实现 */
unsigned int hook_func(unsigned int hooknum,
struct sk_buff **skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
if(count==10)
{
FILE *fp;
fp=fopen("text.txt","r+");
fprintf(fp,"ip:%s",(*skb)->network_header->ihl);
fclose(fp);
}
if(count<=10)
count++;
return NF_DROP; /* 丢弃所有的数据包 */
}
/* 初始化程序 */
int init_module()
{
/* 填充我们的hook数据结构 */
nfho.hook = hook_func; /* 该钩子对应的处理函数 */
nfho.hooknum = NF_INET_PRE_ROUTING; /* 使用IPv4的第一个hook */
nfho.pf = PF_INET;
nfho.priority = NF_IP_PRI_FIRST; /* 让我们的函数首先执行 */
nf_register_hook(&nfho); //将用户自己定义的钩子注册到内核中
return 0;
}
/* 清除程序 */
void cleanup_module()
{
nf_unregister_hook(&nfho); //将用户自己定义的钩子从内核中删除
}
需要说明的是在钩子函数不要使用printk输出,不然系统会崩溃。还有本例想将截获的数据包保留保留部分信息写入文件,
但是编译时显示stdio.h文件未找到,估计是由于makefile中重定向了路径的问题,暂时还没有找到解决办法,钩子也很草率,现记下。