我试图在内核空间的数据包上附加一些数据。我有一个回显客户端和服务器。我在命令行中键入:./client“ message”,服务器将其回显。服务器使用./server运行。
现在,客户端和服务器位于两台不同的计算机(可能是VM)上。我正在编写一个在客户端计算机上运行的内核模块。它的工作是在数据包离开机器时在“消息”之后附加“ 12345”。我在下面介绍代码。
/* * This is ibss_obsf_cat.c */ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> #include <linux/netfilter.h> #include <linux/skbuff.h> #include <linux/netdevice.h> #include <linux/udp.h> #include <linux/ip.h> #undef __KERNEL__ #include <linux/netfilter_ipv4.h> #define __KERNEL__ /* * Function prototypes ... */ static unsigned int cat_obsf_begin (unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)); static void hex_dump (char str[], int len) { } /* * struct nf_hook_ops instance initialization */ static struct nf_hook_ops cat_obsf_ops __read_mostly = { .pf = NFPROTO_IPV4, .priority = 1, .hooknum = NF_IP_POST_ROUTING, .hook = cat_obsf_begin, }; /* * Module init and exit functions. * No need to worry about that. */ static int __init cat_obsf_init (void) { printk(KERN_ALERT "cat_obsf module started...\n"); return nf_register_hook(&cat_obsf_ops); } static void __exit cat_obsf_exit (void) { nf_unregister_hook(&cat_obsf_ops); printk(KERN_ALERT "cat_obsf module stopped...\n"); } /* * Modification of the code begins here. * Here are all the functions and other things. */ static unsigned int cat_obsf_begin (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; struct udphdr *udph; unsigned char *data; unsigned char dt[] = "12345"; unsigned char *tmp; unsigned char *ptr; int i, j, len; if (skb){ iph = ip_hdr(skb); if (iph && iph->protocol && (iph->protocol == IPPROTO_UDP)){ udph = (struct udphdr *) ((__u32 *)iph + iph->ihl); data = (char *)udph + 8; if(ntohs(udph->dest) == 6000){ for (i=0; data[i]; i++); len = i; //printk(KERN_ALERT "\nData length without skb: %d", len); //printk(KERN_ALERT "Data is: %s", data); //printk(KERN_ALERT "dt size: %lu", sizeof(dt)); //printk(KERN_ALERT "skb->len: %d", skb->len); tmp = kmalloc(200*sizeof(char), GFP_KERNEL); memcpy(tmp, data, len); ptr = tmp + len; memcpy(ptr, dt, sizeof(dt)); printk(KERN_ALERT "tmp: %s", tmp); printk(KERN_ALERT "skb->tail: %d", skb->tail); //skb_put(skb, sizeof(dt)); printk(KERN_ALERT "skb->end: %d", skb->end); printk(KERN_ALERT "skb->tail: %d", skb->tail); printk(KERN_ALERT "skb->tail(int): %d", (unsigned int)skb->tail); //memset(data, 0, len + sizeof(dt)); //memcpy(data, tmp, len + sizeof(dt)); //skb_add_data(skb, tmp, len+sizeof(dt)); printk(KERN_ALERT "Now data is: %s", data); for(i=0; data[i]; i++); printk(KERN_ALERT "data length: %d", i); kfree(tmp); } } } return NF_ACCEPT; } /* * Nothing to be touched hereafter */ module_init(cat_obsf_init); module_exit(cat_obsf_exit); MODULE_AUTHOR("Rifat"); MODULE_DESCRIPTION("Module for packet mangling"); MODULE_LICENSE("GPL");
我希望从内核空间发送出客户端计算机时将“ message”设置为“ message12345”。这样服务器将获得“ message12345”并回显,而客户端将仅读取“ message12345”,但是我在使用skb_put()和skb_add_data()函数时遇到了麻烦。我不明白我犯了什么错误。如果有人可以帮助我编写代码,我将不胜感激。提前致谢。为了方便起见,我也提供Makefile。这是针对分发内核的,而不是针对构建内核的。
#If KERNELRELEASE is defined, we've been invoked from the #kernel build system and use its language ifneq ($(KERNELRELEASE),) obj-m := ibss_obsf_cat.o #Otherwise we were called directly from the command #line; invoke the kernel build system. else KERNELDIR ?= /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules endif
现在,我非常确信skb-> end-skb-> tail很小,我将不得不在内核空间中创建新的数据包。我已经使用alloc_skb()skb_reserve()skb_header_pointer()和其他有用的skb函数来创建新的skb,但我的想法已荡然无存,那就是如何在数据包流动路径中路由新创建的数据包。如何使用 ip_route_me_harder()我在xtables-addons包中寻找了建议,但是它们使用的功能不同于linux内核中的功能。任何建议都欢迎。
大约一年前,对于内核2.6.26,我这样做是这样的:
// Do we need extra space? if(len - skb_tailroom(skb) > 0){ // Expand skb tail until we have enough room for the extra data if (pskb_expand_head(skb, 0, extra_data_len - skb_tailroom(skb), GFP_ATOMIC)) { // allocation failed. Do whatever you need to do } // Allocation succeeded // Reserve space in skb and return the starting point your_favourite_structure* ptr = (your_favourite_structure*) skb_push(skb, sizeof(*ptr)); // Now either set each field of your structure or memcpy into it. // Remember you can use a char* }
不要忘记:
重新计算UDP校验和,因为您更改了传输数据中的数据。
更改tot_lenip标头中的字段(总长度),因为您已向数据包中添加了数据。
tot_len
重新计算IP标头校验和,因为您更改了该tot_len字段。
附加说明 :这只是简单的事情。我在您的代码中看到您正在分配tmp一个200字节的数组,并使用该数组存储消息的数据。如果发送更大的数据包,将很难调试,因为由于内存溢出而导致的内核崩溃太痛苦了。
tmp