http://fcns.eu/2010/02/netfilter-hooks/
keyword:write new netfilter module
Note: This article was inspired by the lack of updated documentation on how to write proper netfilter kernel modules. At the time I’m writing this article, the latest stable release was 2.6.32.8. I am also assuming you are familiar with how LKMs (Loadable Kernel Modules) work. If you are not, then you might want to check this article first: tldp.org/HOWTO/Module-HOWTO/
In this article (Part 1) I will present how to create a simple Linux kernel module that implements a netfilter hook for a generic transport protocol (not one of the usual ones).
In Part 2, I plan to connect the module to the iptables rules generated on the userspace side.
What is netfilter?
For those of you who are not familiar with netfilter, all I can say is that it is actually a framework for packet mangling, outside the normal Berkeley socket interface. It’s the engine behind iptables – the popular firewall solution for Linux. It has four parts. Firstly, each protocol defines “hooks” (IPv4 defines 5) which are well-defined points in a packet’s traversal of that protocol stack. At each of these points, the protocol will call the netfilter framework with the packet and the hook number.
Secondly, parts of the kernel can register to listen to the different hooks for each protocol. So when a packet is passed to the netfilter framework, it checks to see if anyone has registered for that protocol and hook; if so, they each get a chance to examine (and possibly alter) the packet in order, then discard the packet (NF_DROP), allow it to pass (NF_ACCEPT), tell netfilter to forget about the packet (NF_STOLEN), or ask netfilter to queue the packet for userspace (NF_QUEUE).
The third part is that packets that have been queued are collected (by the ip_queue driver) for sending to userspace; these packets are handled asynchronously.
Netfilter Hooks in the Linux Kernel.
Netfilter modules can be loaded into the Linux kernel at runtime, so we need hooks in the actual routing code to enable dynamic hooking of functions. An integer identifier is allocated to each of these netfilter hooks. The identifiers of all hooks for each supported protocol are defined in the protocol-specific header file (<linux/netfilter_ipv4.h> or <linux/netfilter_ipv6.h>). The following five hooks are defined for IP Version 4 in <linux/netfilter_ipv4.h>:
Calling the NF_HOOK macro causes the routing code to process the filter functions hooked into a netfilter hook. More specifically, the NF_HOOK macro has the following arguments:
The packet-filter functions that are actually hooked into the netfilter hooks are so-called hook functions of the type nf_hookfn. The signature of a hook function is defined in <linux/netfilter.h> as follows:
typedef unsigned int nf_hookfn(unsigned int hooknum,
struct sk_buff **skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn) (struct sk_buff *));
The return value of a packet-filter function specifies what should happen to the packet. It is of the typeunsigned int and can take any of the following values, defined in <linux/netfilter.h>:
nf_register_hook(), nf_unregister_hook() registers and unregisters a packet-filter function with the Linux kernel. The parameter passed is a nf_hook_ops structure, which includes all information required.
To register a new packet-filter function with the Linux kernel, we first have to initialize a structure of the type nf_hook_ops (linux/netfilter.h) with all of the management information required:
struct nf_hook_ops
{
struct list_head list;
/* User fills in from here down. */
nf_hookfn *hook;
int pf;
int hooknum;
/* Hooks are ordered in ascending priority. */
int priority;
};
The fields of this structure have the following meaning:
Now that you’ve got the main idea on how netfilter hooks work, I suggest we check an example. The following code belongs to a personal project involving the design of a little protocol. The source code for the .c file can be found here, while the code for the .h file is here.
The basic idea behind this generic protocol is that it uses a header made of a port number and a type of message, both defined in the header file. My module is supposed to identify the protocol (based on the protocol field of the IP header) and process only packets arriving for this protocol. Once the match is made, the next thing to do is to recover the port number and type of message from our protocol’s header. Once we have all data, we can call our callback function to do whatever we want next.
There have been a couple of important changes since 2.6.22 (which most guides are written for):
One more important aspect you need to take into consideration is that both skb_network_header andskb_transport_header point to the same memory address. This means that if you want to access the transport header information, you will need to add (skip) to the location of the header in the memory by adding the IP header length (e.g.. rpmp_header = (struct rpmphdr *)(skb_transport_header(sock_buff)+sizeof(struct iphdr));). I don’t know why this happens, but it happens.
Anyway, here is the code:
Source code for my_module.h:
#include <linux/types.h> #define DRIVER_AUTHOR "Andrei SAMBRA <[email protected]>" #define DRIVER_DESC "Generic Protocol" #define IPPROTO_RPMP 150 struct rpmphdr { __be16 dport ; __u16 type ; } ;
Source code for my_module.c:
/* * Author: [email protected] * GPLv3 License applies to this code. * * */ #include <linux/module.h> /* Needed by all modules */ #include <linux/kernel.h> /* Needed for KERN_INFO */ #include <linux/init.h> /* Needed for the macros */ #include <linux/skbuff.h> #include <linux/netfilter.h> #include <linux/netfilter_ipv4.h> #include <linux/ip.h> #include <linux/udp.h> #include "rpmp.h" /* Needed for our structures */ #define DEBUG 0 struct sk_buff *sock_buff ; struct iphdr *ip_header ; struct udphdr *udp_header ; struct rpmphdr *rpmp_header ; static struct nf_hook_ops nfho ; static 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 * ) ) { sock_buff = skb ; if ( !sock_buff ) { return NF_ACCEPT ; } else { ip_header = ( struct iphdr * )skb_network_header (sock_buff ) ; if ( !ip_header ) { return NF_ACCEPT ; } else { if (ip_header ->protocol == IPPROTO_RPMP ) { rpmp_header = ( struct rpmphdr * ) (skb_transport_header (sock_buff ) + sizeof ( struct iphdr ) ) ; #if DEBUG > 0 printk (KERN_INFO "[RPMP] DEBUG: th: 0p%p\n" , rpmp_header ) ; printk (KERN_INFO "[RPMP] DEBUG: nh: 0p%p\n" ,skb_network_header (sock_buff ) ) ; printk (KERN_INFO "[RPMP] DEBUG: mh: 0p%p\n" ,skb_mac_header (sock_buff ) ) ; printk (KERN_INFO "[RPMP] DEBUG: Length: rpmp_header=%d | dport=%d | type=%d.\n" , sizeof (rpmp_header ) , sizeof (rpmp_header ->dport ) , sizeof (rpmp_header ->type ) ) ; printk (KERN_INFO "[RPMP] DEBUG: From IP address: %d.%d.%d.%dn" , ip_header -saddr & 0x000000FF , (ip_header ->saddr & 0x0000FF00 ) >> 8 , (ip_header ->saddr & 0x00FF0000 ) >> 16 , (ip_header ->saddr & 0xFF000000 ) >> 24 ) ; #endif printk (KERN_INFO "[RPMP] Got a RPMP packet for port=%d (type:%d).\n" , ntohs (rpmp_header ->dport ) , ntohs (rpmp_header ->type ) ) ; /* Callback function here*/ return NF_DROP ; } else { return NF_ACCEPT ; } } } } static int __init init_main ( void ) { nfho. hook = hook_func ; nfho. hooknum = 1 ; nfho. pf = PF_INET ; nfho. priority = NF_IP_PRI_FIRST ; nf_register_hook ( &nfho ) ; #if DEBUG > 0 printk (KERN_INFO "[RPMP] Successfully inserted protocol module into kernel.\n" ) ; #endif return 0 ; } static void __exit cleanup_main ( void ) { nf_unregister_hook ( &nfho ) ; #if DEBUG > 0 printk (KERN_INFO "[RPMP] Successfully unloaded protocol module.\n" ) ; #endif } module_init (init_main ) ; module_exit (cleanup_main ) ; /* * Declaring code as GPL. */ MODULE_LICENSE ( "GPLv3" ) ; MODULE_AUTHOR (DRIVER_AUTHOR ) ; /* Who wrote this module? */ MODULE_DESCRIPTION (DRIVER_DESC ) ; /* What does this module do */