title: How to route the splitted packets using netfilter hooks in kernel space
body: <p>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: <a href="https://stackoverflow.com/questions/12529497/how-to-append-data-on-a-packet-from-kernel-space">How to append data on a packet from kernel space?</a>). But now I am stuck with routing splitted packets. Thanks in advance. </p>
  
  <p>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. </p>
  
  <p>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:</p>
  
  <pre><code>    #include &lt;linux/kernel.h&gt;
      #include &lt;linux/module.h&gt;
      #include &lt;linux/skbuff.h&gt;
      #include &lt;linux/netfilter.h&gt;
      #include &lt;linux/netdevice.h&gt;
      #include &lt;linux/ip.h&gt;
      #include &lt;linux/udp.h&gt;
      #include &lt;linux/mm.h&gt;
      #include &lt;linux/err.h&gt;
      #include &lt;linux/crypto.h&gt;
      #include &lt;linux/init.h&gt;
      #include &lt;linux/crypto.h&gt;
      #include &lt;linux/scatterlist.h&gt;
      #include &lt;net/ip.h&gt;
      #include &lt;net/udp.h&gt;
      #include &lt;net/route.h&gt;
      #include &lt;linux/netfilter_ipv4.h&gt;
  
      #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(&amp;pkt_split_ops);
  }
  
  static void __exit pkt_split_exit(void)
  {
      nf_unregister_hook(&amp;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 &amp;&amp; iph-&gt;protocol &amp;&amp;(iph-&gt;protocol == IPPROTO_UDP)) {
              udph = (struct udphdr *) skb_header_pointer (skb, IP_HDR_LEN, 0, NULL);
              src_port = ntohs (udph-&gt;source);
              dst_port = ntohs (udph-&gt;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-&gt;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-&gt;len - len2 - TOT_HDR_LEN + 1;
  
                  memset(data1, 0, data_len);
                  memcpy(data1, temp1, data_len1);
                  skb1-&gt;len = data_len1 + TOT_HDR_LEN;
                  iph1-&gt;tot_len = htons(data_len1 + TOT_HDR_LEN);
                  udph1-&gt;len = htons(data_len1 + UDP_HDR_LEN);
                  skb1-&gt;tail = skb1-&gt;tail - data_len2 + 1;
  
  
                  /* Calculation of IP header checksum */
                  iph1-&gt;check = 0;
                  ip_send_check (iph1);
  
                  /* Calculation of UDP checksum */
                  udph1-&gt;check = 0;
                  int offset = skb_transport_offset(skb1);
                  int len = skb1-&gt;len - offset;
                  udph1-&gt;check = ~csum_tcpudp_magic((iph1-&gt;saddr), (iph1-&gt;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-&gt;len);
      printk(KERN_ALERT "tail: %d", skb-&gt;tail);
      printk(KERN_ALERT "end: %d", skb-&gt;end);
      printk(KERN_ALERT "head: %x", skb-&gt;head);
      printk(KERN_ALERT "data: %x", skb-&gt;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-&gt;ihl);
      printk(KERN_ALERT "version = %d", iph-&gt;version);
      printk(KERN_ALERT "tos = %d", iph-&gt;tos);
      printk(KERN_ALERT "tot_len = %d", ntohs(iph-&gt;tot_len));
      printk(KERN_ALERT "id = %d", ntohs(iph-&gt;id));
      printk(KERN_ALERT "frag_off = %d", ntohs(iph-&gt;frag_off));
      printk(KERN_ALERT "ttl = %d", iph-&gt;ttl);
      printk(KERN_ALERT "protocol = %d", iph-&gt;protocol);
      printk(KERN_ALERT "check = %x", ntohs(iph-&gt;check));
      printk(KERN_ALERT "saddr = %x", ntohl(iph-&gt;saddr));
      printk(KERN_ALERT "daddr = %x", ntohl(iph-&gt;daddr));
  }
  
  void udp_print_info (struct udphdr *udph)
  {
      printk(KERN_ALERT "\nPrinting UDP header info: ");
  
      printk(KERN_ALERT "source = %d", ntohs(udph-&gt;source));
      printk(KERN_ALERT "dest = %d", ntohs(udph-&gt;dest));
      printk(KERN_ALERT "len = %d", ntohs(udph-&gt;len));
      printk(KERN_ALERT "check = %x", ntohs(udph-&gt;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: &lt;rifatrahmanovi@gmail.com&gt;");
  MODULE_DESCRIPTION("Outward Packet Mangling and Decryption in Kernel Space");
  MODULE_LICENSE("GPL");
  </code></pre>