Skip to content

Creating a Netfilter kernel module which filters UDP packets

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.

{ 5 } Comments

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

    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.

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

    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?

  3. Ben Evans | January 25, 2010 at 6:16 pm | Permalink

    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?

  4. jashmi | April 20, 2010 at 4:08 pm | Permalink

    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.

  5. prashant kapoor | June 11, 2010 at 3:43 pm | Permalink

    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?

{ 1 } Trackback

  1. [...] Access packet headers within Netfilter hook [...]

Post a Comment

Your email is never published nor shared. Required fields are marked *

Additional comments powered by BackType