How can I safely add data to the sk_buff target for IPTables?

I am working on a Linux kernel module that needs to change the network packets and add an extra header. I've already implemented part of the modification, recalculated the checksums and it worked well. But I don't know how to safely add an additional header. If my input packet looks something like this:

ip-header / tcp-header / data

      

I would like to have an output package like:

ip-header / tcp-header / my-header / data

      

For what I've read, I think I need something like the following code. I wrote my specific questions about the code as comments. My general problem is that the code I write here is memory safe or what should I do to have a storage safe way to add a new header. Also, if I am doing something wrong or there is a better way to do it, I would appreciate a comment as well. I tried to find examples but no luck so far. Here is the code:

static unsigned int my_iptables_target(struct sk_buff *skb, const struct xt_action_param *par) {
    const struct xt_mytarget_info *info = par->targinfo;
    /* Some code ... */

    if (!skb_make_writable(skb, skb->len)) {
        //Drop the packet
        return NF_DROP;
    }

    struct newheader* myheader;
    // Check if there is enough space and do something about it
    if (skb_headroom(skb) < sizeof(struct newheader)) {
        // So there is no enugh space.
        /* I don't know well what to put here. I read that a function called pskb_expand_head might
         * do the job. I do not understand very well how it works, or why it might fail (return value
         * different from zero). Does this code work:
         */
        if (pskb_expand_head(skb, sizeof(struct newheader) - skb_headroom(skb), 0, GPF_ATOMIC) != 0) {
            // What does it mean if the code reaches this point?
            return NF_DROP;
        }
    }
    // At this point, there should be enough space
    skb_push(skb, sizeof(struct newheader));

    /* I also think that skb_push() creates space at the beggining, to open space between the header and
     * the body I guess I must move the network/transport headers up. Perhaps something like this:
     */
    memcpy(skb->data, skb->data + sizeof(struct newheader), size_of_all_headers - sizeof(struct newheader));

    // Then set myheader address and fill data.
    myheader = skb->data + size_of_all_headers;

    //Then just set the new header, and recompute checksums.

    return XT_CONTINUE;
}

      

I assumed the variable size_of_all_headers

contains the network byte size and transport headers. I also think it memcpy

copies bytes in ascending order, so the call shouldn't be a problem. So does this code work? Is it safe for everyone? Are there any better ways to do this? Are there examples (or can you provide them) that do something like this?

+3


source to share


1 answer


I used code similar to the one in the question and it has worked very well so far for the entire test I have done. To answer some of the specific questions, I used something like:

if (skb_headroom(skb) < sizeof(struct newheader)) {
    printk("I got here!\n");
    if (pskb_expand_head(skb, sizeof(struct newheader) - skb_headroom(skb), 0, GPF_ATOMIC) != 0) {
        printk("And also here\n");
        return NF_DROP;
    }
}

      



But none of the print statements were ever executed. My guess is that this is due to the OS reserving enough memory space so there can be no problem with the IP header. But I think it is better to leave this if statement in order to increase the package if needed.

Another difference in the code I've tested and worked is that instead of moving all the other headers to create space for my header, I decided to move the package body down.

0


source







All Articles