基于Netfilter框架, 编写自己的hook函数,修改经过hook点的数据包目的地址
程序员文章站
2022-07-13 16:17:27
...
参考连接
http://blog.chinaunix.net/uid-20662820-id-142433.html
修改经过钩子的数据包目的地址:
结果图:
//linux 内核数据包转发模块
/**
myhook_func.c
Makefile
编译:make
加载:sudo insmod myhook_func.ko remark=0 dst=192.168.92.129 //remark默认0,dst 默认127.0.0.1
卸载:sudo rmmod myhook_func
dmesg查看打印
**/
//头文件包含
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/netfilter.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/ctype.h>
#include <linux/inet.h> /*in_aton() 将地址转换为码 */
#include <linux/socket.h> /*PF_INET */
#include <net/checksum.h>
#include <net/tcp.h>
#include <net/udp.h>
#include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_expect.h>
#include <net/netfilter/nf_conntrack_ecache.h>
#include <net/netfilter/nf_conntrack_helper.h>
#include <linux/netfilter/nf_conntrack_ftp.h>
#include <linux/types.h>
#include <linux/skbuff.h> //skb_network_header(skb) CHECKSUM_UNNECESSARY
#include <linux/in.h> //IPPROTO_xxx
#include <linux/timer.h>
#include <net/xfrm.h>
#include <linux/netfilter.h>
#include <linux/netdevice.h> //netif_receive_skb netif_rx
#include <linux/if.h>
#include <linux/inetdevice.h>
#include <net/protocol.h>
#include <linux/netfilter_ipv4.h> /*NF_IP_PRE_FIRST */
#include <linux/netfilter/x_tables.h>
#include <net/netfilter/nf_nat.h>
//#include <net/netfilter/nf_nat_rule.h>
#include <net/netfilter/nf_nat_core.h>
//模块信息
MODULE_LICENSE("GPL");
MODULE_AUTHOR("lxm");
MODULE_DESCRIPTION("myhook.c");
#define NIPQUAD(addr) \
((unsigned char *)&addr)[0], \
((unsigned char *)&addr)[1], \
((unsigned char *)&addr)[2], \
((unsigned char *)&addr)[3]
//全局变量 remark, dst 默认值如下
static int remark=1;
char *dst="127.0.0.1";//转发目的地址
//模块参数
module_param(remark,uint,0644);
module_param(dst,charp,0644);
//描述模块参数
MODULE_PARM_DESC(remark,"Copy the packet with the mark is remark");
//入口函数myhook_func
//函数参数:钩子位置hooknum 收发包缓存结构skb in out
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*))
{
printk(KERN_ALERT "enter myhook_func:===== get hook!\n");
struct iphdr *nowiph;//定义ip头nowiph
nowiph=(struct iphdr*)skb_network_header(skb);//从skb中获取ip头
//获取源,目的ip
__be32 nsip, ndip;
nsip=nowiph->saddr;
ndip=nowiph->daddr;
//get mark
__u32 mark=skb->mark;
__be32 mydst;
mydst = in_aton(dst);//转发地址
//__be32 testip= in_aton("203.208.37.99");
//if(nowiph->saddr==testip&&nowiph->daddr==mydst) printk("OK\n");
printk(KERN_ALERT "mark=%d\n",mark);
// printk(KERN_ALERT "mydst=%d.%d.%d.%d\n " NIPQUAD(mydst));
if(ndip==mydst)//数据包目的地址和要转发的目的地址相同,不需要修改
{
printk("sameid: nsip: %d.%d.%d.%d\n ndip: %d.%d.%d.%d\n mydst: %d.%d.%d.%d\n ",NIPQUAD(nsip), NIPQUAD(ndip), NIPQUAD(mydst) );
//if(mydst==in_aton("127.0.0.1")) skb->pkt_type=5;
return NF_ACCEPT;
}
// printk(KERN_ALERT "mark=%d\n",mark);
if(mark==remark)//数据包的mark标志和我的 remark一样,对数据包进行修改目的地址
{
//printk(KERN_ALERT "OK\n");
//打印数据包的源地址,目的地值和mark
printk("lu before: mark=%d\n source add: %d.%d.%d.%d\n destination add: %d.%d.%d.%d\n mydst: %d.%d.%d.%d\n ",mark, NIPQUAD(nsip), NIPQUAD(ndip),NIPQUAD(mydst));
struct sk_buff *myskb=skb_copy(skb,GFP_ATOMIC);//复制skb
struct iphdr *myiph;
//myskb->pkt_type=PACKET_OTHERHOST;
myiph=(struct iphdr*)skb_network_header(myskb);
if(myiph==NULL)
{
kfree_skb(myskb);
return NF_ACCEPT;
}
myiph->daddr=mydst;//修改ip头部目的地址
printk("lu after: mark=%d\n source add: %d.%d.%d.%d\n destination add: %d.%d.%d.%d\n ",mark, NIPQUAD(myiph->saddr), NIPQUAD(myiph->daddr) );
if(myiph->protocol==IPPROTO_TCP)
{
__wsum csum=0;
struct tcphdr *mytcphdr;
unsigned int tcphoff;
mytcphdr=(struct tcphdr*)(myskb->data+myiph->ihl*4);//检查tcp头
if(mytcphdr==NULL)
{
kfree_skb(myskb);
return NF_ACCEPT;
}
nf_reset(myskb);
tcphoff=myiph->ihl*4;
ip_send_check(myiph);//检验和
mytcphdr->check=0;
csum=skb_checksum(myskb,tcphoff,myskb->len-tcphoff,0);
myskb->csum=csum;
myskb->ip_summed=CHECKSUM_NONE;
/*mytcphdr->check=csum_tcpudp_magic(myiph->saddr,myiph->daddr,myskb->len,IPPROTO_TCP,csum_partial((char *)mytcphdr,myskb->len-tcphoff,0));
mytcphdr->check+=0x1400;*/
mytcphdr->check=0;
mytcphdr->check=tcp_v4_check(myskb->len-tcphoff,myiph->saddr,myiph->daddr,csum_partial(mytcphdr,myskb->len-tcphoff,0));
//printk("%u--%u\n",ntohs(myiph->saddr),ntohs(myiph->daddr));
netif_rx(myskb);
}
else if(myiph->protocol==IPPROTO_UDP)
{
int csum=0;
struct udphdr *myudphdr;
unsigned int udphoff;
myudphdr=(struct udphdr*)(myskb->data+myiph->ihl*4);
if(myudphdr==NULL)
{
kfree_skb(myskb);
return NF_ACCEPT;
}
udphoff=myiph->ihl*4;
myskb->csum=0;
ip_send_check(myiph);
csum=skb_checksum(myskb,udphoff,myskb->len-udphoff,0);
myskb->csum=csum;
myskb->ip_summed=CHECKSUM_NONE;
myudphdr->check=0;
myudphdr->check=csum_tcpudp_magic(myiph->saddr,myiph->daddr,myskb->len-udphoff,IPPROTO_UDP,csum_partial(myudphdr,myskb->len-udphoff,0));
nf_reset(myskb);
netif_rx(myskb);
}
}
return NF_ACCEPT;
}
//定义钩子myhook变量
static struct nf_hook_ops myhook;
//注册钩子
static int __init myhook_init(void)
{
printk(KERN_ALERT " *************myhook init*******************\n");
memset(&myhook,0,sizeof(struct nf_hook_ops));
myhook.hook=myhook_func;
//min myhook.owner=THIS_MODULE;
myhook.pf=PF_INET;
myhook.hooknum=NF_INET_PRE_ROUTING;
//myhook.priority=INT_MAX;
myhook.priority=NF_IP_PRI_NAT_DST;
if(nf_register_hook(&myhook)){
//如果注册失败,打印内核级错误信息
printk(KERN_ERR"myhook init failed=lu xiang min\n");
return -1;
}
printk(KERN_WARNING"myhook init succesful= lu xiangmin\n");
return 0;
}
//注销钩子
static void __exit myhook_exit(void)
{
nf_unregister_hook(&myhook);
printk(KERN_WARNING"****************myhook exit**************\n");
}
//模块加载与卸载
module_init(myhook_init);
module_exit(myhook_exit);
Makefile 文件
ifneq ($(KERNELRELEASE),)
obj-m := myhook_func.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 myhook_func.ko
查看内核模块打印输出:
dmesg
查看模块:
lsmod
卸载模块:
rmmod sample(注意,没有.ko后缀)
消除编译过程生成的文件:
make clean
上一篇: Linux中的管道与连接符号
下一篇: SSH报错处理办法