简体   繁体   English

如何在内核空间中附加数据包?

[英]How to append data on a packet from kernel space?

I am trying to append some data on a packet from kernel space. 我试图在内核空间的数据包上附加一些数据。 I have an echo client and server. 我有一个echo客户端和服务器。 I type in the command line like: ./client "message" and the server just echoes it back. 我输入命令行,如:./client“message”,服务器只是回复它。 The server was run with ./server . 服务器使用./server运行。

Now, the client and server are on two different machines (may be VMs). 现在,客户端和服务器位于两台不同的计算机上(可能是VM)。 I am writing a kernel module which runs on the client machine. 我正在编写一个在客户机上运行的内核模块。 Its work is to append "12345" after "message" while the packet goes out of the machine. 它的工作是在数据包离开机器时在“消息”之后附加“12345”。 I am presenting the code below. 我将在下面介绍代码。

/*
 * 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");

I want to get the "message" to be "message12345" while sending out of the client machine from kernel space. 我想从内核空间发送客户端机器时将“消息”变为“message12345”。 So that the server will get "message12345" and echo it back, and the client will the read just "message12345" But I am having trouble with skb_put() and skb_add_data() functions. 这样服务器将获得“message12345”并回显它,客户端将只读取“message12345”但是我遇到了skb_put()和skb_add_data()函数的问题。 I do not understand what error was made by me. 我不明白我犯了什么错误。 If anyone can help me with the code, I will be highly grateful. 如果有人可以帮我解决这些问题,我将非常感激。 Thanks in advance. 提前致谢。 I am also giving the Makefile for convenience. 为方便起见,我也给了Makefile。 This is for the distribution kernel, not for a built kernel. 这适用于分发内核,而不适用于构建的内核。

#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

Now I am quite convinced that skb->end - skb->tail is so small that I will have to create new packets in kernel space. 现在我完全相信skb-> end - skb-> tail是如此之小以至于我将不得不在内核空间中创建新的数据包。 I have used alloc_skb() skb_reserve() skb_header_pointer() and other useful skb functions for creating a new skb, but the thing I am running out of idea is that how to route the newly created packet in the packet flowing path. 我已经使用了alloc_skb()skb_reserve()skb_header_pointer()和其他有用的skb函数来创建一个新的skb,但我想到的是如何在数据包流路径中路由新创建的数据包。 How to use 如何使用
ip_route_me_harder() I looked in the xtables-addons package for suggestion, but the function they used is different from the one in linux kernel. ip_route_me_harder()我查看了xtables-addons包中的建议,但是他们使用的函数与linux内核中的函数不同。 Any suggestion is welcomed. 任何建议都受到欢迎。

About one year ago for kernel 2.6.26 I did it like this: 大约一年前的内核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*

}

Don't forget: 别忘了:

  • Recalculate UDP checksum, because you changed data in the transported data. 重新计算UDP校验和,因为您更改了传输数据中的数据。

  • Change the field tot_len (total length) in the ip header, because you added data to the packet. 更改ip标头中的字段tot_len (总长度),因为您向数据包添加了数据。

  • Recalculate the IP header checksum, because you changed the tot_len field. 重新计算IP标头校验和,因为您更改了tot_len字段。

Extra note : This is just a simple thing. 额外注意 :这只是一件简单的事情。 I see in your code you're allocating tmp as a 200 byte array and using that to store the data of your message. 我在你的代码中看到你将tmp分配为一个200字节的数组并使用它来存储你的消息数据。 If you send a bigger packet you'll have a hard time debugging this as kernel crashes due to memory overflows are too painful. 如果你发送一个更大的数据包,你将很难调试这个,因为内存崩溃是因为内存崩溃太痛苦了。

I have solved the problem. 我已经解决了这个问题。 It was trivial. 这是微不足道的。 And all I am going to do is to post my code for future references and discussion. 我要做的就是发布我的代码以供将来参考和讨论。

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/netfilter.h>
#include <linux/netdevice.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <linux/mm.h>
#include <linux/err.h>
#include <linux/crypto.h>
#include <linux/init.h>
#include <linux/crypto.h>
#include <linux/scatterlist.h>
#include <net/ip.h>
#include <net/udp.h>
#include <net/route.h>

#undef __KERNEL__
#include <linux/netfilter_ipv4.h>
#define __KERNEL__

#define IP_HDR_LEN 20
#define UDP_HDR_LEN 8
#define TOT_HDR_LEN 28

static unsigned int pkt_mangle_begin(unsigned int hooknum,
                        struct sk_buff *skb,
                        const struct net_device *in,
                        const struct net_device *out,
                        int (*okfn)(struct sk_buff *));

static struct nf_hook_ops pkt_mangle_ops __read_mostly = {
    .pf = NFPROTO_IPV4,
    .priority = 1,
    .hooknum = NF_IP_LOCAL_OUT,
    .hook = pkt_mangle_begin,
};

static int __init pkt_mangle_init(void)
{
    printk(KERN_ALERT "\npkt_mangle module started ...");
    return nf_register_hook(&pkt_mangle_ops);
}

static void __exit pkt_mangle_exit(void)
{
    nf_unregister_hook(&pkt_mangle_ops);
    printk(KERN_ALERT "pkt_mangle module stopped ...");
} 

static unsigned int pkt_mangle_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 int data_len;
    unsigned char extra_data[] = "12345";
    unsigned char *temp;
    unsigned int extra_data_len;
    unsigned int tot_data_len;

    unsigned int i;

    __u16 dst_port, src_port;

    if (skb) {
        iph = (struct iphdr *) skb_header_pointer (skb, 0, 0, NULL);

        if (iph && iph->protocol &&(iph->protocol == IPPROTO_UDP)) {
            udph = (struct udphdr *) skb_header_pointer (skb, IP_HDR_LEN, 0, NULL);
            src_port = ntohs (udph->source);
            dst_port = ntohs (udph->dest);

            if (src_port == 6000) {
                printk(KERN_ALERT "UDP packet goes out");
                data = (unsigned char *) skb_header_pointer (skb, IP_HDR_LEN+UDP_HDR_LEN, 0, NULL);
                data_len = skb->len - TOT_HDR_LEN;

                    temp = kmalloc(512 * sizeof(char), GFP_ATOMIC);
                memcpy(temp, data, data_len);

                unsigned char *ptr = temp + data_len - 1;
                extra_data_len = sizeof(extra_data);
                memcpy(ptr, extra_data, extra_data_len);
                tot_data_len = data_len + extra_data_len - 1;

                skb_put(skb, extra_data_len - 1);

                memcpy(data, temp, tot_data_len);

                /* Manipulating necessary header fields */
                iph->tot_len = htons(tot_data_len + TOT_HDR_LEN);
                udph->len = htons(tot_data_len + UDP_HDR_LEN);

                /* Calculation of IP header checksum */
                iph->check = 0;
                ip_send_check (iph);

                /* Calculation of UDP checksum */
                udph->check = 0;
                int offset = skb_transport_offset(skb);
                int len = skb->len - offset;
                udph->check = ~csum_tcpudp_magic((iph->saddr), (iph->daddr), len, IPPROTO_UDP, 0);

                 }
        }
    }
    return NF_ACCEPT;
}


module_init(pkt_mangle_init);
module_exit(pkt_mangle_exit);

MODULE_AUTHOR("Rifat Rahman Ovi: <rifatrahmanovi@gmail.com>");
MODULE_DESCRIPTION("Outward Packet Mangling and Decryption in Kernel Space");
MODULE_LICENSE("GPL");

Here the thing is that, I forgot to update the length fields and forgot to update the checksum. 这就是,我忘了更新长度字段而忘记更新校验和。 Now, if I present the code correctly here, all should go well. 现在,如果我在这里正确地呈现代码,一切都应该顺利。 There are some other helper functions which are not included here. 还有一些其他辅助函数不包含在这里。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM