Linux Kernel Communication — Netfilter Hooks

GoldenOak
InfoSec Write-ups
Published in
5 min readJan 22, 2019

--

I have been always interested in how things work, especially how computers work. About a year ago I found myself studying the Linux kernel, or more accurately, I found myself studying how to create my own Loadable Kernel Module(LKM) so I could do anything (well, almost anything).

The first thing I wanted to do is to build an LKM that would get any command from outside, parse it, and then execute it. At first, I thought of using a socket that would act as a listener, on a single port, using a single protocol so that would be my communication tool; But then something else came to my mind — why not monitor every packet that enters the machine? This way I don’t need to worry about creating more sockets for more ports or protocols.

The Kernel

The kernel is the centre of every operating system. It contains all the definitions and instructions for the machine to know how to manage its resources.

The memory (RAM) of a Linux machine is separated into two spaces, The kernel-space, and the user-space. In a Linux machine, the CPU has two execution modes, the kernel-mode, and the user-mode. The user mode is a non-privileged (i.e. Can only access to the user-space of the memory) mode for user programs, while the kernel mode is a privileged mode for any kernel purpose. When in kernel mode, the CPU assumes that the kernel knows what it is doing, and thus executes every single instruction that is being told to without any question asked.

For more reading, see reference [1]

Netfilter Hooks

What is a netfilter?

From the netfilter project documentation:

netfilter is a framework for packet mangling, outside the normal Berkeley socket interface. 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.

In other words, netfilter is a tool that gives you the power to use callbacks to parse, change or use a packet.

Netfilter offers something called netfilter hooks, which is a way to use callbacks in order to filter packets inside the kernel. There are 5 different kinds of netfilter hooks:

A Packet Traversing the Netfilter System:   --->[1]--->[ROUTE]--->[3]--->[4]--->
| ^
| |
| [ROUTE]
v |
[2] [5]
| ^
| |
v |
  1. NF_IP_PER_ROUNTING — This hook is called when a packet arrives into the machine.
  2. NF_IP_LOCAL_IN — This hook is called when a packet is destined to the machine itself.
  3. NF_IP_FORWARD — This hook is called when a packet is destined to another interface.
  4. NF_IP_POST_ROUTING — This is called when a packet is on its way back to the wire and outside the machine.
  5. NF_IP_LOCAL_OUT — When a packet is created locally and is destined out, this hook is called.

Using Netfilter Hooks inside the kernel

To use netfilter hooks inside the kernel, you’ll need to create a hook function, and register it using the nf_register_hook which receives struct nf_hooks_ops* or the nf_register_net_hook which receives struct net* and struct nf_hooks_ops. You need to choose the function depending on the kernel version. This struct contains the following fields:

struct nf_hook_ops {
/* User fills in from here down. */
nf_hookfn *hook;
struct net_device *dev;
void *priv;
u_int8_t pf;
unsigned int hooknum;
/* Hooks are ordered in ascending priority. */
int priority;
};
  • hook — A pointer to a function that is called as soon as the hook is triggered. This function is from type nf_hookfn which has different signatures in different versions of the kernel. I recommend you to search for the right signature according to the kernel version you work on (see reference [2]). Be sure this function returns NF_DROP (drop the packet), NF_ACCEPT (let the packet continue in its journey) or NF_QUEUE (if you want to queue the packet to user-space handling).
  • hooknum — One of the hooks identifiers (e.g. NF_IP_POST_ROUTING).
  • pf — Protocol family identifier (e.g. PF_INET for IPv4).
  • priority — The priority of the hook (in case that other hooks are registered in the system). This priority can be one of the priorities defined in the enum nf_ip_hook_priorities, which is defined in the netfilter_ipv4.h file (e.g. NF_IP_PRI_FIRST, NF_IP_PRI_RAW).
  • For now, you can ignore the *dev and *priv fields.
    Just for the fun of it, I am adding a quote directly from the Linux kernel source code documentation —
    struct net_device — The DEVICE structure.
    Actually, this whole structure is a big mistake. It mixes I/O
    data with strictly “high-level” data, and it has to know about
    almost every data structure used in the INET module.”

Code Example

In this example, I’ll show you a simple LKM that drops any UDP packet (except for UDP packets that are destined to port 53 — DNS), and accepts any TCP packet. Any other packet will be dropped.

Code Overview

  • Line 21 — Checks if the skb (sk_buf — socket buffer) is empty, and if so, lets this packet continue on its routing.
  • Line 24 — Extracts the IP protocol header, so we could later use it to get more details on the packet.
  • Lines 25 and 31 — Uses the IP header we extracted before, and checks what kind of protocol was used for this packet.
  • Line 27 — Checks the port of the UDP header.
  • Lines 48 and 53 — Registers and unregisters the hook.

Makefile

To compile an LKM, you can use something called Makefile, which is a file that contains instructions and settings that will be later read and executed by the make command in the bash. For this code, I wrote the following Makefile:

obj-m := LKM.o
LKM-objs += simple_netfilter_LKM.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules
rm -r -f *.mod.c .*.cmd *.symvers *.o
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean

Compiling and Testing

To compile and insert the LKM in bash:

# make
# insmod LKM.ko

And rmmod LKM to remove it.

I highly recommend using Wireshark or another sniffing tool to see how the module affects the network traffic on your machine.

References

[1] About the kernel — The Linux Information Project
[2] bootlin — kernel source code by versions
[3] “Hello World” LKM tutorial
[4] Compiling a basic LKM
[5] Wireshark official website

--

--