netfilter的实现及数据包的修改
的有关信息介绍如下:通俗的说,netfilter的架构就是在整个网络流程的若干位置放置了一些检测点(HOOK),而在每个检测点上登记了一些处理函数进行处理(如包过滤,NAT等,甚至可以是 用户自定义的功能)。
IP层的五个HOOK点的位置如下图所示
enumnf_inet_hooks{
NF_INET_PRE_ROUTING,
NF_INET_LOCAL_IN,
NF_INET_FORWARD,
NF_INET_LOCAL_OUT,
NF_INET_POST_ROUTING,
NF_INET_NUMHOOKS
};
:NF_IP_PRE_ROUTING:刚刚进入网络层的数据包通过此点(刚刚进行完版本号,校验
和等检测), 目的地址转换在此点进行;
:NF_IP_LOCAL_IN:经路由查找后,送往本机的通过此检查点,INPUT包过滤在此点进行;
:NF_IP_FORWARD:要转发的包通过此检测点,FORWARD包过滤在此点进行;
:NF_IP_POST_ROUTING:所有马上便要通过网络设备出去的包通过此检测点,内置的源地址转换功能(包括地址伪装)在此点进行;
:NF_IP_LOCAL_OUT:本机进程发出的包通过此检测点,OUTPUT包过滤在此点进行。
首先,定义一个nf_hook_ops结构体,选定要挂取的钩子点
static struct nf_hook_ops nfho = {
.hook = my_hookfn,
.pf = PF_INET,
.hooknum = NF_INET_PRE_ROUTING,
.priority = NF_IP_PRI_FIRST,
.owner = THIS_MODULE,
};
第二步,写好my_hookfn这个hook函数,即要实现的功能(此处是将其接收包的源IP地址改成100.100.100.100.
unsigned int my_hookfn(unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct iphdr *iph;
iph = ip_hdr(skb);
/* log the original src IP */
printk(KERN_INFO"src IP %pI4\n", &iph->saddr);
/* modify the packet's src IP */
iph->saddr = in_aton("100.100.100.100");
return NF_ACCEPT;
}
注册函数会把nf_hook_ops放入nf_hooks相应的位置中。
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); /* 把netfilter实例添加到队列中 */
mutex_unlock(&nf_hook_mutex);
return 0;
}
注销函数
void nf_unregister_hook(struct nf_hook_ops *reg)
{
mutex_lock(&nf_hook_mutex);
list_del_rcu(®->list); /* 把netfilter实例从队列中删除 */
mutex_unlock(&nf_hook_mutex);
synchronize_net();
}
然后进行模块的注册与注销
static int __init http_init(void)
{
if (nf_register_hook(&nfho)) {
printk(KERN_ERR"nf_register_hook() failed\n");
return -1;
}
return 0;
}
static void __exit http_exit(void)
{
nf_unregister_hook(&nfho);
}
这是完整的源代码,并取名http.c
#include
#include
#include
#include
#include
#include
/**
* Hook function to be called.
* We modify the packet's src IP.
*/
unsigned int my_hookfn(unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct iphdr *iph;
iph = ip_hdr(skb);
/* log the original src IP */
printk(KERN_INFO"src IP %pI4\n", &iph->saddr);
/* modify the packet's src IP */
iph->saddr = in_aton("100.100.100.100");
return NF_ACCEPT;
}
/* A netfilter instance to use */
static struct nf_hook_ops nfho = {
.hook = my_hookfn,
.pf = PF_INET,
.hooknum = NF_INET_PRE_ROUTING,
.priority = NF_IP_PRI_FIRST,
.owner = THIS_MODULE,
};
static int __init http_init(void)
{
if (nf_register_hook(&nfho)) {
printk(KERN_ERR"nf_register_hook() failed\n");
return -1;
}
return 0;
}
static void __exit http_exit(void)
{
nf_unregister_hook(&nfho);
}
module_init(http_init);
module_exit(http_exit);
MODULE_AUTHOR("flyking");
MODULE_LICENSE("GPL");
然后编写Makefile,此处是Makefile源码
ifneq ($(KERNELRELEASE),)
obj-m += http.o
else
PWD := $(shell pwd)
KVER := $(shell uname -r)
KDIR := /lib/modules/$(KVER)/build
default:
$(MAKE) -C $(KDIR) M=$(PWD) modules
all:
make -C $(KDIR) M=$(PWD) modules
clean:
rm -rf *.o *.mod.c *.ko *.symvers *.order *.makers
endif
编写好了http.c和Makefile,将其用动态模块的方式挂载在Linux系统内核。进行编译make,即可生成所需要的http.ko文件,如图所示
然后动态加载insmod这个模块,lsmod查看即可看来系统内有这个模块,如图所示
然后我们用wireshark抓了上百度的过程,就看到了我们的接收的包源地址被改成了100.100.100.100.