3
votes

I have to split large packets into smaller ones in PRE_ROUTING hook. I have done the necessary steps for splitting a packet into smaller ones, creating skb, setting ip and udp header etc, etc. But what I am not understanding is how to route the packets? I can append data in packets now (that can be seen in my previous question: How to append data on a packet from kernel space?). But now I am stuck with routing splitted packets. Thanks in advance.

I am giving my codes (so far I can write) below. Let us imagine the module runs on the server machine. The server runs on port 6000. The client then sends a message "ThisIsUsedForTesting". According to the code, the server should get "ThisI": a smaller packet. I am not concerning with the second packet right now. I can easily mangle packet size. But now there can be two or more packets.

After running this module, the server gets the message: "ThisI". But when echoing it back, the packet does not go out of the box. I write the module for PRE_ROUTING, and the machine should oops right then, but the server process gets the message, and the machine then oops. I do not understand these scenario. Any help/suggestion is welcome. If I can manage the first splitted packet, I think, the rest can be handled automatically, so the code for them is not given here:

    #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>
    #include <linux/netfilter_ipv4.h>

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

    static unsigned int pkt_split_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 skb_print_info(const struct sk_buff *skb);
    static void ip_print_info(struct iphdr *iph);
    static void udp_print_info(struct udphdr *udph);
    static void data_print_info(unsigned char *data, int len);

    static struct nf_hook_ops pkt_split_ops __read_mostly = {
        .pf = NFPROTO_IPV4,
        .priority = 1,
        .hooknum = NF_INET_PRE_ROUTING,
        .hook = pkt_split_begin,
    }; 

static int __init pkt_split_init(void)
{
    printk(KERN_ALERT "\npkt_split module started ...");
    return nf_register_hook(&pkt_split_ops);
}

static void __exit pkt_split_exit(void)
{
    nf_unregister_hook(&pkt_split_ops);
    printk(KERN_ALERT "pkt_split module stopped ...");
}

static unsigned int pkt_split_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 int i;

    unsigned char *temp;
    unsigned char *temp1, *temp2;
    unsigned char *ptr;

    __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 (dst_port == 6000) {
                printk(KERN_ALERT "\nUDP packet goes in");
                data = (unsigned char *) skb_header_pointer (skb, IP_HDR_LEN+UDP_HDR_LEN, 0, NULL);
                data_len = skb->len - TOT_HDR_LEN;

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

                temp1 = kmalloc(50 * sizeof(char), GFP_ATOMIC);
                temp2 = kmalloc(50 * sizeof(char), GFP_ATOMIC);

                unsigned int len1, len2;

                len1 = 5;
                len2 = data_len - len1;

                memcpy(temp1, temp, len1);
                temp1[len1] = '\0';
                printk(KERN_ALERT "temp1: %s", temp1);

                ptr = temp + len1;
                memcpy(temp2, ptr, len2);
                printk(KERN_ALERT "temp2: %s", temp2);

                struct sk_buff *skb1, *skb2;
                struct iphdr *iph1, *iph2;
                struct udphdr *udph1, *udph2;
                unsigned char *data1, *data2;
                int data_len1, data_len2;

                skb1 = skb_copy(skb, GFP_ATOMIC);
                skb2 = skb_copy(skb, GFP_ATOMIC);

                iph1 = (struct iphdr *) skb_header_pointer(skb1, 0, 0, NULL);
                udph1 = (struct udphdr *) skb_header_pointer(skb1, IP_HDR_LEN, 0, NULL);
                data1 = (unsigned char *) skb_header_pointer(skb1, TOT_HDR_LEN, 0, NULL);
                data_len1 = skb1->len - len2 - TOT_HDR_LEN + 1;

                memset(data1, 0, data_len);
                memcpy(data1, temp1, data_len1);
                skb1->len = data_len1 + TOT_HDR_LEN;
                iph1->tot_len = htons(data_len1 + TOT_HDR_LEN);
                udph1->len = htons(data_len1 + UDP_HDR_LEN);
                skb1->tail = skb1->tail - data_len2 + 1;


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

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

                struct sk_buff *tempskb;

                tempskb = skb_copy(skb, GFP_ATOMIC);

                *tempskb = *skb;
                *skb = *skb1;
                *skb1 = *tempskb;

                (*okfn)(skb);

                skb_print_info(skb1);
                ip_print_info(iph1);
                udp_print_info(udph1);
                data_print_info(data1, data_len1);

                kfree_skb(skb1);

            }
        }
    }
    return NF_DROP;
}

static void skb_print_info (const struct sk_buff *skb)
{
    printk(KERN_ALERT "\nPrinting SKB info: ");

    printk(KERN_ALERT "len: %d", skb->len);
    printk(KERN_ALERT "tail: %d", skb->tail);
    printk(KERN_ALERT "end: %d", skb->end);
    printk(KERN_ALERT "head: %x", skb->head);
    printk(KERN_ALERT "data: %x", skb->data);

    printk(KERN_ALERT "\ntail pointer = %x", skb_tail_pointer(skb));
    printk(KERN_ALERT "end pointer = %x", skb_end_pointer(skb));
    printk(KERN_ALERT "\nheadroom = %d", skb_headroom(skb));
    printk(KERN_ALERT "\ntailroom = %d", skb_tailroom(skb));

}

void ip_print_info (struct iphdr *iph)
{
    printk(KERN_ALERT "\nPrinting IP header info:");

    printk(KERN_ALERT "ihl = %d", iph->ihl);
    printk(KERN_ALERT "version = %d", iph->version);
    printk(KERN_ALERT "tos = %d", iph->tos);
    printk(KERN_ALERT "tot_len = %d", ntohs(iph->tot_len));
    printk(KERN_ALERT "id = %d", ntohs(iph->id));
    printk(KERN_ALERT "frag_off = %d", ntohs(iph->frag_off));
    printk(KERN_ALERT "ttl = %d", iph->ttl);
    printk(KERN_ALERT "protocol = %d", iph->protocol);
    printk(KERN_ALERT "check = %x", ntohs(iph->check));
    printk(KERN_ALERT "saddr = %x", ntohl(iph->saddr));
    printk(KERN_ALERT "daddr = %x", ntohl(iph->daddr));
}

void udp_print_info (struct udphdr *udph)
{
    printk(KERN_ALERT "\nPrinting UDP header info: ");

    printk(KERN_ALERT "source = %d", ntohs(udph->source));
    printk(KERN_ALERT "dest = %d", ntohs(udph->dest));
    printk(KERN_ALERT "len = %d", ntohs(udph->len));
    printk(KERN_ALERT "check = %x", ntohs(udph->check));
}


void data_print_info (unsigned char *data, int len)
{
    printk(KERN_ALERT "\nPrinting data info: ");

    printk(KERN_ALERT "Data: %s", data);
    printk(KERN_ALERT "data_len: %d", len);
}

module_init(pkt_split_init);
module_exit(pkt_split_exit);

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

1 Answers

5
votes

This time I found the solution. It is also a simple case like the previous one (http://stackoverflow.com/questions/12529497/how-to-append-data-on-a-packet-from-kernel-space). I am presenting the code. But something I need to explain.

okfn

must be called like

okfn(skb);

And it will free the skb itself. And my problem in the question was about doubly freeing an skb. Look in the following code and it will be understood what is needed to get it done. The code is just used for testing purpose. The first 5 bytes (and plus a '\0') will be used to build a new packet, and rest of the data is used to create the second one. So, here we go. Necessary helper functions for printing skb info, ip header info etc. are provided for convenience (which I used for understanding what is going on).

#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>
#include <linux/netfilter_ipv4.h>

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

static unsigned int pkt_split_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 skb_print_info(const struct sk_buff *skb);
static void ip_print_info(struct iphdr *iph);
static void udp_print_info(struct udphdr *udph);
static void data_print_info(unsigned char *data, int len);

static struct nf_hook_ops pkt_split_ops __read_mostly = {
    .pf = NFPROTO_IPV4,
    .priority = 1,
    .hooknum = NF_INET_PRE_ROUTING,
    .hook = pkt_split_begin,
};

static int __init pkt_split_init(void)
{
    printk(KERN_ALERT "\npkt_split module started ...");
    return nf_register_hook(&pkt_split_ops);
}

static void __exit pkt_split_exit(void)
{
    nf_unregister_hook(&pkt_split_ops);
    printk(KERN_ALERT "pkt_split module stopped ...");
}

static unsigned int pkt_split_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 int i;

    unsigned char *temp;
    unsigned char *temp1, *temp2;
    unsigned char *ptr;

    __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 (dst_port == 6000) {
                printk(KERN_ALERT "\nUDP packet goes in");
                data = (unsigned char *) skb_header_pointer (skb, IP_HDR_LEN+UDP_HDR_LEN, 0, NULL);
                data_len = skb->len - TOT_HDR_LEN;

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

                temp1 = kmalloc(50 * sizeof(char), GFP_ATOMIC);
                temp2 = kmalloc(50 * sizeof(char), GFP_ATOMIC);

                unsigned int len1, len2;

                len1 = 5;
                len2 = data_len - len1;

                memcpy(temp1, temp, len1);
                temp1[len1] = '\0';
                printk(KERN_ALERT "temp1: %s", temp1);

                ptr = temp + len1;
                memcpy(temp2, ptr, len2);
                printk(KERN_ALERT "temp2: %s", temp2);

                struct sk_buff *skb1, *skb2;
                struct iphdr *iph1, *iph2;
                struct udphdr *udph1, *udph2;
                unsigned char *data1, *data2;
                int data_len1, data_len2;

                skb1 = skb_copy(skb, GFP_ATOMIC);
                skb2 = skb_copy(skb, GFP_ATOMIC);

                iph1 = (struct iphdr *) skb_header_pointer(skb1, 0, 0, NULL);
                udph1 = (struct udphdr *) skb_header_pointer(skb1, IP_HDR_LEN, 0, NULL);
                data1 = (unsigned char *) skb_header_pointer(skb1, TOT_HDR_LEN, 0, NULL);
                data_len1 = skb1->len - len2 -TOT_HDR_LEN + 1;

                memset(data1, 0, data_len);
                memcpy(data1, temp1, data_len1);
                skb1->len = data_len1 + TOT_HDR_LEN;
                iph1->tot_len = htons(data_len1 + TOT_HDR_LEN);
                udph1->len = htons(data_len1 + UDP_HDR_LEN);
                skb1->tail = skb1->tail - data_len2 + 1;

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

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

                /* Dealing with the second packet */
                iph2 = (struct iphdr *) skb_header_pointer(skb2, 0, 0, NULL);
                udph2 = (struct udphdr *) skb_header_pointer(skb2, IP_HDR_LEN, 0, NULL);
                data2 = (unsigned char *) skb_header_pointer(skb2, TOT_HDR_LEN, 0, NULL);
                data_len2 = skb2->len - len1 - TOT_HDR_LEN;

                memset(data2, 0, data_len);
                memcpy(data2, temp2, data_len2);
                skb2->len = data_len2 + TOT_HDR_LEN;
                iph2->tot_len = htons(data_len2 + TOT_HDR_LEN);
                udph2->len = htons(data_len2 + UDP_HDR_LEN);
                skb2->tail = skb2->tail - data_len1;

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

                /* Calculation of UDP checksum */
                udph2->check = 0;
                offset = skb_transport_offset(skb1);
                len = skb2->len - offset;
                udph2->check = ~csum_tcpudp_magic((iph2->saddr), (iph2->daddr), len, IPPROTO_UDP, 0);

                okfn(skb1);
                okfn(skb2);
            }
        }
    }
    return NF_DROP;
}

static void skb_print_info (const struct sk_buff *skb)
{
    printk(KERN_ALERT "\nPrinting SKB info: ");

    printk(KERN_ALERT "len: %d", skb->len);
    printk(KERN_ALERT "tail: %d", skb->tail);
    printk(KERN_ALERT "end: %d", skb->end);
    printk(KERN_ALERT "head: %x", skb->head);
    printk(KERN_ALERT "data: %x", skb->data);

    printk(KERN_ALERT "\ntail pointer = %x", skb_tail_pointer(skb));
    printk(KERN_ALERT "end pointer = %x", skb_end_pointer(skb));
    printk(KERN_ALERT "\nheadroom = %d", skb_headroom(skb));
    printk(KERN_ALERT "\ntailroom = %d", skb_tailroom(skb));

}

void ip_print_info (struct iphdr *iph)
{
    printk(KERN_ALERT "\nPrinting IP header info:");

    printk(KERN_ALERT "ihl = %d", iph->ihl);
    printk(KERN_ALERT "version = %d", iph->version);
    printk(KERN_ALERT "tos = %d", iph->tos);
    printk(KERN_ALERT "tot_len = %d", ntohs(iph->tot_len));
    printk(KERN_ALERT "id = %d", ntohs(iph->id));
    printk(KERN_ALERT "frag_off = %d", ntohs(iph->frag_off));
    printk(KERN_ALERT "ttl = %d", iph->ttl);
    printk(KERN_ALERT "protocol = %d", iph->protocol);
    printk(KERN_ALERT "check = %x", ntohs(iph->check));
    printk(KERN_ALERT "saddr = %x", ntohl(iph->saddr));
    printk(KERN_ALERT "daddr = %x", ntohl(iph->daddr));
}

void udp_print_info (struct udphdr *udph)
{
    printk(KERN_ALERT "\nPrinting UDP header info: ");

    printk(KERN_ALERT "source = %d", ntohs(udph->source));
    printk(KERN_ALERT "dest = %d", ntohs(udph->dest));
    printk(KERN_ALERT "len = %d", ntohs(udph->len));
    printk(KERN_ALERT "check = %x", ntohs(udph->check));
}


void data_print_info (unsigned char *data, int len)
{
    printk(KERN_ALERT "\nPrinting data info: ");

    printk(KERN_ALERT "Data: %s", data);
    printk(KERN_ALERT "data_len: %d", len);
}

module_init(pkt_split_init);
module_exit(pkt_split_exit);

MODULE_AUTHOR("Rifat Rahman Ovi: <rifatrahmanovi@gmail.com>");
MODULE_DESCRIPTION("Inward Packet Splitting in kernel space");
MODULE_LICENSE("GPL");

So...As I am new in kernel space programming, any suggestions are welcome for improvement.