• Creating a Netfilter kernel module which filters UDP packets

    by  • November 2, 2009 • kernel module, linux, netfilter, networking • 15 Comments

    Last time we created a Netfilter kernel module which simply dropped all packets which gave the structure and functions that need to be implemented for a Netfilter module to work.

    This time, we’ll extend the functionality to poke into the IP header, specifically the protocol field, in order to perform functionality specific to a packet type. I used an old article as the basis of this which can be found at http://www.linuxjournal.com/article/7184, but as you’ll see in this article, access to packet headers has changed significantly between Linux kernel 2.4 and 2.6. This tutorial is written for Ubuntu 8.04, with the development environment set up as specified in part 1, so for those of you with other distributions, YMMV.

    Specifically, in kernel 2.6, access to network, transport and mac headers are now not through the fields nh, h and mac on the skbuff structure any more, but through new accessors: skb_network_header, skb_transport_header and skb_mac_header. To determine this, I had to look at the header files for the given data structure, in this case, skbuff. Having installed the linux-headers package earlier, we simply navigate to the installation folder, /lib/modules/2.6.24-23-generic/build/include/linux, and checking out skbuff.h we see that in the definition of the sk_buff data structure that nh, h and mac fields no longer exist. What we do see though are several accessor methods:

    static inline unsigned char *skb_transport_header(const struct sk_buff *skb)
    { ... }

    static inline unsigned char *skb_network_header(const struct sk_buff *skb)
    { ... }

    static inline unsigned char *skb_mac_header(const struct sk_buff *skb)
    { ... }
    .

    In this example, we will use the skb_network_header accessor method to access the ip packet’s protocol number. If the packet’s protocol number corresponds to UDP (17, from http://www.iana.org/assignments/protocol-numbers/), I log this fact in the /var/log/messages. Additional header files are included to cast the return values from skb_network_header and skb_transport_header to the appropriate iphdr and udphdr structures. Note in this example no operations are performe udp packet header we cast, but you can access any of the fields available to you — check out udp.h and ip.h for the available fields.

    So, without further ado, the complete code:

    1. //’Hello World’ v2 netfilter hooks example
    2. //For any packet, get the ip header and check the protocol field
    3. //if the protocol number equal to UDP (17), log in var/log/messages
    4. //default action of module to let all packets through
    5.  
    6. #include <linux/kernel.h>
    7. #include <linux/module.h>
    8. #include <linux/netfilter.h>
    9. #include <linux/netfilter_ipv4.h>
    10. #include <linux/skbuff.h>
    11. #include <linux/udp.h>
    12. #include <linux/ip.h>
    13.  
    14. static struct nf_hook_ops nfho;   //net filter hook option struct
    15. struct sk_buff *sock_buff;
    16. struct udphdr *udp_header;          //udp header struct (not used)
    17. struct iphdr *ip_header;            //ip header struct
    18.  
    19. unsigned int hook_func(unsigned int hooknum, struct sk_buff **skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
    20. {
    21.         sock_buff = *skb;
    22.  
    23.         ip_header = (struct iphdr *)skb_network_header(sock_buff);    //grab network header using accessor
    24.        
    25.         if(!sock_buff) { return NF_ACCEPT;}
    26.  
    27.         if (ip_header->protocol==17) {
    28.                 udp_header = (struct udphdr *)skb_transport_header(sock_buff);  //grab transport header
    29.  
    30.                 printk(KERN_INFO "got udp packet \n");     //log we’ve got udp packet to /var/log/messages
    31.                 return NF_DROP;
    32.         }
    33.                
    34.         return NF_ACCEPT;
    35. }
    36.  
    37. int init_module()
    38. {
    39.         nfho.hook = hook_func;
    40.         nfho.hooknum = NF_IP_PRE_ROUTING;
    41.         nfho.pf = PF_INET;
    42.         nfho.priority = NF_IP_PRI_FIRST;
    43.  
    44.         nf_register_hook(&nfho);
    45.        
    46.         return 0;
    47. }
    48.  
    49. void cleanup_module()
    50. {
    51.         nf_unregister_hook(&nfho);     
    52. }
    53.  

    So whats changed from the example in part 2?

    • now using header files, skbuff.h, ip.h and udp.h, which allows us to cast the relevant data structures we need to poke around
    • new logic in hook_func to get the protocol number, and if this is equal to 17, to log that we have received a UDP packet to /var/log/messages

    In the final part, we will seperate the kernel module into a user space daemon and a smaller kernel module. This brings many advantages, such as easier debugging and less likelihood of causing a kernel panic from the seperation of logic (such as from common programming mistakes leading to seg faults). To acheive this communication between the user space and kernel module, we will use the standard mechanism: netlink.

    About

    .NET developer at thetrainline.com, previously web developer at MRM Meteorite. Awarded a PhD in misbehaviour detection in wireless ad-hoc networks.A keen C# ASP.net developer bridging the gap with APIs and JavaScript frameworks, one web app at a time.

    http://www.paulkiddie.com

    15 Responses to Creating a Netfilter kernel module which filters UDP packets

    1. Pingback: Paul Kiddie’s Blog : Creating a “hello world” kernel module in linux

    2. Srudeep K
      November 2, 2009 at 7:30 pm

      Hi Paul,
      Thank you very much for the kind reply. Your second article helped me a lot. I have small doubt, if I want to acess the source and destination address from the ip header of the packet, is there any way to do it? As we are looking for INET protocol family (pf = PF_INET) we should capture all ip packets and all packets should contain source address. With this back ground I have tried using the saddr variable which is in iphdr structure (iphdr->saddr) but it gave me segmentation fault. I am not able to debug this fault, my system just hangs.

    3. Srudeep K
      November 2, 2009 at 7:35 pm

      Is there any other way to do it? Is acess to the saddr field of iphdr structure is legal? or is there any other method to access it?

    4. Ben Evans
      January 25, 2010 at 6:16 pm

      Hi Paul,

      Thanks for this series of articles – they’ve been very useful so far.

      One thing, is that neither this one nor the previous seem to work on Ubuntu 9 (2.6.28-16 kernel). The errors look like this:

      bje@bje-desktop:~/projects/netfilter-test$ make
      make -C /lib/modules/2.6.28-16-generic/build SUBDIRS=/home/bje/projects/netfilter-test modules
      make[1]: Entering directory `/usr/src/linux-headers-2.6.28-16-generic’
      CC [M] /home/bje/projects/netfilter-test/nfudp.o
      /home/bje/projects/netfilter-test/nfudp.c: In function ‘init_module’:
      /home/bje/projects/netfilter-test/nfudp.c:42: warning: assignment from incompatible pointer type
      /home/bje/projects/netfilter-test/nfudp.c:43: error: ‘NF_IP_PRE_ROUTING’ undeclared (first use in this function)
      /home/bje/projects/netfilter-test/nfudp.c:43: error: (Each undeclared identifier is reported only once
      /home/bje/projects/netfilter-test/nfudp.c:43: error: for each function it appears in.)
      make[2]: *** [/home/bje/projects/netfilter-test/nfudp.o] Error 1
      make[1]: *** [_module_/home/bje/projects/netfilter-test] Error 2
      make[1]: Leaving directory `/usr/src/linux-headers-2.6.28-16-generic’
      make: *** [default] Error 2

      Examining linux/netfilter_ipv4.h seems to indicate that an #ifndef __KERNEL__ now extends further than it used to and is preventing NF_IP_PRE_ROUTING from being visible to the module. Something similar is suggested by:

      http://markmail.org/message/kvzvzc32v3htduin#query:netfilter_ipv4.h%20changes%20kernel+page:1+mid:chddq75k2cxjzuqr+state:results

      Any thoughts as to what needs to be done to change these examples to work with a 2.6.28-16 or thereabouts kernel?

    5. jashmi
      April 20, 2010 at 4:08 pm

      Hi paul,
      I am a beginer .I tried inserting this module and module insertion was successfull.But wen i sent traffic , system hangs.Any suggestion on what can be done for this problem.I changed NF_IP_PRE_ROUTING as NF_INET_PRE_ROUTING.

    6. prashant kapoor
      June 11, 2010 at 3:43 pm

      Hi Paul

      Congrats , I think you have the best tutorial explaining netfilter kernel module.

      If we make drastic changes to the packet that the packet is not at all recognizable and we want to send the packets back to the pre-routing, do we have to do something special?

    7. Maaz Khan
      September 25, 2010 at 11:18 pm

      Hi I suppose my reply is very late but if anyone is still having problem: ‘NF_IP_PRE_ROUTING’ undeclared (first use in this function)

      try using 0 instead of ‘NF_IP_PRE_ROUTING’ because NF_IP_PRE_ROUTING=0;

      Also when i run you UDP capture example the system freezes any suggestion… i m using ubuntu 8.04

      thanks

    8. saurabh
      October 7, 2010 at 1:00 pm

      Similar freeze for me on ubuntu 10.04. I am using a similar code. To me it looks like whenever I try to read “ip_header” extracted from sk_buff, i get the freeze.

    9. sunil shah
      October 18, 2010 at 3:08 pm

      to get rid of freezing problem change
      sk_buff **skb
      to
      sk_buff *skb

      and
      sock_buff = * skb to
      sock_buff = skb

      and for nfho.hooknum = NF_IP_PRE_ROUTING error
      either set it to 0
      or change it to
      nfho.hooknum = NF_INET_PRE_ROUTING; ( not sure)

    10. Karol Sabat
      December 6, 2010 at 8:20 am

      Hey Folks,

      Had the same problem so, do the trick;)

      #undef __KERNEL__
      #include /* for constants like NF_IP_PRI_FIRST */
      #define __KERNEL__

      Regards,
      Karol

    11. October 14, 2011 at 8:04 am

      I try to do something more : modify tcp header and then calculate tcp checksum again by using Netfilter. Can you help me to do with this.

    12. Kesava
      November 24, 2011 at 7:18 am

      HI Paul,

      Thank You Very Much for the Information. It helped me a lot. I am trying to add an Extra IP header over the Received Ping Packet. I have increased the size of skb by 20 bytes and filled the IP Header Fields accordingly.

      After adding the IP header ; I have return NF_STOLEN. Please let me know ; What is the Function that I should call to forward the STOLEN Packet?? OR Please Point me to some Documentation from where I can get the necessary information ???

      -Thanks in Advance,
      VKS

    13. February 20, 2012 at 11:35 pm

      Paul, thanks for the simple yet detailed article! It helped a lot.
      And thanks Sunil Shah for his tip that helped me get over the freeze issue (2.6 kernal).

    14. divya
      March 13, 2012 at 4:37 pm

      paul i have modified ur code to print source and destination address of the packet….now i’m trying to get read the data from the packet but havent got any way i tried to print the data using sk_buff->data but even if i put it in loop skb_underpannic is called…. any suggestions how to access the data?

    15. Maksim
      July 7, 2012 at 7:06 pm

      Hi,Paul. Thanks for the great article. Where could somebody find the final part(as was mentioned above),using netlink?

    Leave a Reply

    Your email address will not be published. Required fields are marked *