A blog on Computer Science, Security, Programming, and more...

HeapSpray Blog » Programming » View Post

02
Apr
2015

Writing a Netfilter Linux Kernel Module for 3.x.x Kernels

Written by Matt

The Linux kernel API sure changes a lot. Functions just magically disappear, or they change in subtle ways like a pointer becoming a pointer to a pointer and vice versa - which causes only a warning when compiled but will panic your kernel if you try to load in that module. Thank you, Linux Kernel devs.

Most of the code (or all of the code?) that specifies how to use Netfilter on the internet right now is outdated and no longer works on 3.x.x kernels. It will just not compile or crash your machine if you try to use it. This post contains updated example code that will use Netfilter's kernel-space API to drop all incoming packets - please don't compile and load this module on a remote server, as you will lose access to it completely. Netfilter's hooks have the capability to run well before the regular iptables stages and even SELinux's processing hooks. You are working in kernel space, so you are not limited in the normal sense. Be aware that in kernel-space programming libc is not available.

Note: if this gives you a warning when you try to compile it on your kernel, you should look in your netfilter kernel headers to see whether the function signature is different from your kernel. The kernel code is in /lib/modules/`uname -r`/source - if you have the appropriate headers installed. Depending on your kernel version, the first argument of nk_hook_ex's signature might have to be different. It changed sometime in the middle of 3.x.x's development, the very latest kernels require "const struct nf_hook_ops *ops" instead of "unsigned int" as the first argument to an nf_hookfn.

The Code

The code is fairly simple and terse. Just use it as a base to go off of. Write the following to a file and name it nkmod.c (important).

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>

static struct nf_hook_ops hk; 

/* if you get a warning about this function, change the first argument to "const struct nf_hook_ops *ops" - it is basically a copy of the variables fed into 'hk' below */
unsigned int nf_hook_ex(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)){

        if (skb != NULL)
                printk(KERN_INFO "Dropping received packet\n");

        return NF_DROP;
}

/* Called when module loaded using 'insmod' */
int kmod_init(void){
        /* Just some fancy C to copy an inline struct */
        hk = (struct nf_hook_ops){
                .hook = nf_hook_ex, /* this is important, this variable is of type nf_hookfn - the signature of the function MUST match this type */
                .hooknum = NF_INET_PRE_ROUTING, /* Prerouting is the first netfilter stage */
                .pf = PF_INET, /* Just hook for Internet Packets */
                .priority = NF_IP_PRI_FIRST /* Run this hook before any other hook */
        };
        nf_register_hook(&hk);

  return 0;
}

/* Called when module unloaded using 'rmmod' */
void kmod_exit(void){
        nf_unregister_hook(&hk);
}

/* Some standard macros to pass the kernel compile script some information */
module_init(kmod_init);
module_exit(kmod_exit);
MODULE_LICENSE("GPL");

Here is the accompanying Makefile - it references nkmod.o and it passes that to the kernel's automatic kbuild makefiles. The file in the directory from where you run "make" must then be called nkmod.c.

obj-m += nkmod.o

all:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

Just run "make" in the directory that holds both these files. If the build fails, it means you don't have kernel headers installed. If running on a yum based distro, then run "yum install kernel-devel-`uname -r`", on an apt distro run "apt-get install linux-headers-`uname -r`" instead.

Once the module is built, just load it by running "insmod nkmod.ko" as root. All traffic will now be dropped until you unload the module with "rmmod nkmod" - likewise as root. No application will be able to see any packets at all, this hooks the very first stage in the kernel after the network device driver processes the packets on the physical interface.

Please note the use of printk instead of printf - if you use printf you will likely cause a kernel panic. The kernel has its own set of functions for I/O, and printk prints to /var/log/messages, or to journalctl if you use systemd.

That's all there is to it. The definitions for netfilter are in /lib/modules/`uname -r`/source/include/linux/netfilter.h -- open that file and just browse the referenced source. sk_buff, the actual structure that contains the packets, changes ridiculously often and there's not even any point in trying to write a how-to for it, since it will become obsolete within a month at best.

The sk_buff structure is defined in /lib/modules/`uname -r`/source/include/linux/skbuff.h - it is a large structure, but you can look at source of other kernel modules to see what you should reference for your use case.

  • Name and Email fields are optional
  • Your email will not be public, only the administrator can see it
  • You are rate limited to one comment for every 10 minutes