Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Packet Processing in NetFilter Hooks ?

Looking for Information on NF_STOLEN.

After Stealing Packet from NET-FILTER ; am adding IP Header by expanding SKUBUFFER. After that; want to send the Packet to the corresponding Interface . What is the function that we should use to do this step ??? OR if we say; NF_ACCEPT., can we ensure that Manipulated Packet will be processed by the Kernel correctly forwarding it to the right interface ??

Looking forward for the reply !!!

-Thanks in Advance, VKS

like image 426
Kesav Avatar asked May 12 '26 20:05

Kesav


1 Answers

Yes, if you alter the skbuff structure and compute the appropriate checksums, you just need to return NF_ACCEPT. The kernel will deal with the rest for you.

I've done this in my thesis. Here's some code I've done (it does not extend or trim the skbuff, but it changes a field in the application payload. However, I've also done some code which extends the skbuff and the theory is the same):

unsigned int pre_routing_hook(filter_specs* sp, 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* udp;
    __tp(pdu)* pdu;

    /*Omitted some sanity checks */

    iph = ip_hdr(skb);
    udp = (struct udphdr*) (((char*) iph) + (iph->ihl << 2));

    //I didn't care about udp checksum so I've stored zero in this field.
    udp->check = 0;

    switch (iph->protocol) {
    case IPPROTO_UDP:
          pdu = (__tp(pdu)*) (((char*) iph) + (iph->ihl << 2)
                               + sizeof(struct udphdr));

          swap_pdu_byte_order(pdu);

          pdu->timestamp = get_kernel_current_time() - 
                           (pdu->timestamp + pdu->rtt * 1000); 

          swap_pdu_byte_order(pdu);
          break;
    default:
            printk("\tProtocol not supported.\n");
    }
    return NF_ACCEPT;
}

EDIT: I've took a look at the code you've posted and here's what I came up with. It works for me:

#include <linux/ip.h>
#include <linux/in.h>


static uint16_t csum(uint16_t* buff, int nwords) {
    uint32_t sum;
    for (sum = 0; nwords > 0; nwords--)
            sum += *buff++;
    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);

    return ((uint16_t) ~sum);
}

//I'm assuming this will run in PRE_ROUTING                                                                                                  
unsigned int main_hook(unsigned int hooknum,
            struct sk_buff *skb, const struct net_device *in,
                   const struct net_device *out, int(*okfn)(struct sk_buff*)) {
  uint16_t lets_keep_the_original_size;
  struct in_device* ipa;

  iph = ip_hdr(sock_buff);

  lets_keep_the_original_size = ntohs(iph->tot_len);

  if(iph->protocol == 1) {
    if(skb_headroom(skb) < sizeof(struct iphdr)) {
      if( 0 != pskb_expand_head(skb, (sizeof(struct iphdr))- 
                                skb_headroom(skb),0,GFP_ATOMIC) ){
        kfree_skb(skb);
        return NF_STOLEN;
    }
  }

  iph = (struct iphdr*) skb_push(skb, sizeof(struct iphdr)); 
  iph->proto = IPPROTO_IP;
  iph->ihl = 5;
  iph->version = 4;
  iph->tos = 0;
  iph->tot_len = htons(lets_keep_the_original_size + sizeof(struct iphdr));
  iph->id = 0;
  iph->frag_off = 0;
  iph->ttl = 60;

  //This must be zero to be able to calculate it with csum above.                                                                          
  iph->check = 0;
  //in is the interface where the packet arrived, provided by netfilters. 
  //In PRE_ROUTING there is no out interface yet so you'll need to add 
  //the ip manually:                                                                                                                          
  ipa = (struct in_device*) in->ip_ptr;
  iph->saddr = ipa->ifa_list->ifa_address;

  //in_aton already gives you the address in network byte order .                                                                          
  //You can just add an integer, but I've chosen to use in_aton                                                                            
  //so the code is more readable                                                                                                           
  iph->daddr = in_aton("192.168.1.1");

  //Here is the important part.                                                                                                            
  iph->check = csum((uint16_t*) iph, (iph->ihl << 1));

  //Let the kernel deal with the rest for us.                                                                                              
  return NF_ACCEPT;
 }
 return NF_ACCEPT;
}

Let me know if there's anything I can help you with.

like image 71
Fred Avatar answered May 15 '26 17:05

Fred



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!