skbuff packet sent with zero payload

369 views Asked by At

Using Netfilter's hooks (NF_INET_PRE_ROUTING and NF_INET_POST_ROUTING) I implemented a kernel module that monitors all incoming and outgoing packets for a given host. By looking at the skuffs I can classify packets and identify those that I am interested in. Each time I detect such packets I want to generate my own packet and send it out in the network (note: I am not copying/cloning the original packet but instead create one from "scratch" and send it out using dev_queue_xmit. The only similarity with the original packet is that my packet goes towards the same destination, but has different port/protocol etc.).

The problem is that my generated packets are being sent out with empty payload. Packets successfully reach the destination, the size of the payload is correct, but its content is all set to zeros. Two observations: (1) The function that I am using to construct skbuffs and send out has been tested before and seemed to work. (2) The problem appear only when I classify outgoing packets and attempt to send my own along the way, seemingly the same code works fine when I classify packets using NF_INET_PRE_ROUTING.

Please see the code below:

static struct nf_hook_ops nfout;
static int __init init_main(void) {
        nfout.hook     = hook_func_out;
        nfout.hooknum  = NF_INET_POST_ROUTING;
        nfout.pf       = PF_INET;
        nfout.priority = NF_IP_PRI_FIRST;
        nf_register_hook(&nfout);
        printk(KERN_INFO "Loading Postrouting hook\n");
    return 0;
}
static unsigned int hook_func_out(const struct nf_hook_ops *ops,
        struct sk_buff *skb, const struct net_device *in,
        const struct net_device *out, int (*okfn)(struct sk_buff *)) {    
    if (skb is the packet that I am looking for, omitting details...) {
        generate_send_packet(skb);
    }
    return NF_ACCEPT;
}
void generate_send_packet(struct sk_buff *target_skb) {    
    static struct tcphdr * tcph;
    static struct iphdr * iph;
    static struct ethhdr * ethh;
    struct sk_buff * skb1;
    iph = ip_hdr(target_skb);
    tcph = tcp_hdr(target_skb);
    ethh = eth_hdr(target_skb);
    int payload = 123456789;    
    skb1 = construct_udp_skb(dev,
       dev->dev_addr,  mac,
       iph->saddr, iph->daddr,
       ntohs(tcph->source), dst_port,       
       (unsigned char *)&payload, sizeof(int)       
       );
    if (dev_queue_xmit(skb1) != NET_XMIT_SUCCESS) {
        printk(KERN_INFO "Sending failed\n");
    }
}

struct sk_buff* construct_udp_skb(struct net_device *dev,
    unsigned char * src_mac, unsigned char * dst_mac,
    uint32_t src_ip, uint32_t dst_ip,
    uint32_t src_port, uint32_t dst_port,
    uint32_t ttl, uint32_t tcp_seq,
    unsigned char * usr_data, uint16_t usr_data_len) {

    static struct ethhdr *ethh;
    static struct iphdr *iph;
    struct udphdr * udph;
    static struct sk_buff *skb;
    static uint16_t header_len = 300;
    unsigned char * p_usr_data;
    int udplen;


    skb = alloc_skb(1000, GFP_KERNEL);
    skb_reserve(skb, header_len);
    //-----------------------------------------------
    udph = (struct udphdr*) skb_push(skb, sizeof(struct udphdr));
    iph = (struct iphdr*) skb_push(skb, sizeof(struct iphdr));
    ethh = (struct ethhdr*) skb_push(skb, sizeof(struct ethhdr));

    memset(udph, 0 , sizeof(struct udphdr));
    memset(iph, 0 , sizeof(struct iphdr));

    skb_set_mac_header(skb, 0);
    skb_set_network_header(skb, sizeof(struct ethhdr));
    skb_set_transport_header(skb, sizeof(struct ethhdr) + sizeof(struct iphdr));

    //ETH -------------------------------------------
    memcpy(ethh->h_source, src_mac, 6);
    memcpy(ethh->h_dest, dst_mac, 6);
    ethh->h_proto = htons(ETH_P_IP);
    //IP --------------------------------------------
    iph->ihl = 5;
    iph->version = 4;
    iph->ttl = 128;
    iph->tos = 0; 
    iph->protocol = IPPROTO_UDP;
    iph->saddr = src_ip;
    iph->daddr = dst_ip;

    iph->check = ip_fast_csum((u8 *)iph, iph->ihl);
    iph->id = htons(222);
    iph->frag_off = 0;
    iph->tot_len = htons(sizeof(struct iphdr) + sizeof(struct udphdr) + usr_data_len );
    ip_send_check(iph);

    //UDP--------------------------------------------
    udph->source = htons(src_port);
    udph->dest = htons(dst_port);

    skb->dev = dev;
    skb->protocol = IPPROTO_UDP;
    skb->priority = 0;
    skb->pkt_type = PACKET_OUTGOING;

    p_usr_data = skb_put(skb, usr_data_len);
    printk(KERN_INFO "Sending [%i]\n", *(int*)usr_data);
    skb->csum = csum_and_copy_from_user(usr_data, p_usr_data, usr_data_len, 0, &err);


    udplen = sizeof(struct udphdr) + usr_data_len;
    udph->len = htons(udplen);
    udph->check = udp_v4_check(udplen,
                               iph->saddr, iph->daddr,
                               0);


    return skb;
}

What could be the possible reason why dev_queue_xmit would nullify the payload in the skbuff?

Thanks!

1

There are 1 answers

0
Rami Rosen On

I assume that the result of debug print message for printk(KERN_INFO "Sending [%i]\n", (int)usr_data) is as expected. But what about the skb->data? Can you try to print it after csum_and_copy_from_user() to see whether it is empty or not? It seems very likely that you will see the zero payload at this point already.