Writing your own netfilter match

ArticleCategory:

AuthorImage:

AboutTheAuthor:

Nicolas is a young warrior in the free software community.
He's a gnu/linux addict since the day he installed it on his computer in 1998.
He spend his time studying the linux networking stack, writing free softwares
and attending at linux related conference like the OLS.
When he's not in front of his computer, he likes watching sci-fi movies,
playing chess and listening Richard Stallman's talk.

Abstract:

The iptables/netfilter framework gives us the possibility to add features.
To do so, we write kernel modules that registers against this framework.
Also, depending on the feature's category, we write an iptables module.
By writing your new extension, you can match, mangle, give faith and track a
given packet. In fact, you can do almost everything you want in this filtering
world.
Beware that a little error in a kernel module can severly crash the computer.

For the sake of simplicity, I will explain a skeleton match that I wrote. This way,
I hope to make the interactions with the framework a little easier to understand.
Here, I'll assume you already know a bit about iptables and that you know C programming.

This example will show you how to match a packet according to the source and/or destination address ip.

ArticleIllustration:

ArticleBody:

Description

The general steps around creating an iptables/netfilter's match module are:

You want to match a specific situation.

Write the user-space part which will handle arguments.

Write the kernel-space part which will analyze packets and says to match or not.

1.0 The iptables module

The purpose of an iptables library is basically to interact with the user. It
will handle the arguments the user want the kernel-part to take in
consideration.

1.1 Structures and Functions available

At first, some basic structures.
<iptables/include/iptables.h>
We will see later in this text what's the purpose of each field.

'Help' is called when a user enter 'iptables -m module -h'. 'Parse' is
called when you enter a new rule, its duty is to validate the arguments.
In the case of 'print', its called by 'iptables -L' to
show previously enterd rules.

The iptables infrastructure can support multiple shared libraries. Each
library must register to iptables by calling 'register_match()', defined
into <iptables/iptables.c>. This function is called when the
module is loaded by iptables.
For more information about it: 'man dlopen'.

void _init(void)
{
register_match(&ipaddr);
}

1.2.2 save function

If we have a ruleset that we want to save, iptables provide the tool 'iptables-save' which dumps all your rules.
It obviously needs your extension's help to dump proper rules. This is done by calling this function.

1.2.3 print function

In the same philosophy of the previous one, this function aims to print information about the rule.
It's called by 'iptables -L'. We will see later in this text what's the purpose of 'ipt_entry_match *match', but
you certainly already have a little idea about it.

1.2.5 parse function

This is the most important function because it's here that we verify if
arguments are used correctly
and set informations we will share with the kernel-part. It is called
each time an argument is found, so if the user provides two arguments, it
will be called twice
with the argument code into the var 'c'.

We use this special structure to keep informations we will share with the kernel-part. 'Match' pointer is passed to a couple
of functions so we can work on the same data structure. Once the rule is loaded, this pointer is copied to the kernel-part.
This way, the kernel module knows what the user asks to analyze (and that's the point, no?).

Each arguments correspond to an single value, so we can do
specific actions according to the inputed arguments. We will see later
in this text how we map arguments to values.

switch(c) {

First, we check if the argument has been used more than once. If
it appears to be the case, we call 'exit_error()' defined in <iptables/iptables.c>,
which will exit immediatly with the status flag 'PARAMETER_PROBLEM' defined in <iptables/include/iptables_common.h>.
Else, we set 'flags' and 'info->flags' to the 'IPADDR_SRC' value
defined in our header's file. We will see this header's file later.

Although both var flags seems to have the same purpose, they really
don't. The scope of 'flags' is only this function, and 'info->flags'
is a field part of our structure which will be shared with the
kernel-part.

We verify if the invert flag, '!', has been inputed and then set appropriate information into the 'info->flags'.Next,
we call 'parse_ipaddr', an internal function written for this skeleton,
to convert the ip address string to a 32bits value.

1.2.6 options structure

We have discussed earlier that every arguments are mapped to a single
value. The 'struct option' is the better way to achieve it. For more
information about this structure, I strongly suggest you read 'man 3
getopt'.

1.3 Summary chapter 1

In the first part, we discussed the purpose of the
iptables library. We covered the internals of each function and how the
main structure 'ipt_ipaddr_info' is used to keep information that will
be copied to the kernel-part for further consideration. We also look at
the iptables structure and how to register our new library.
You should keep in mind that this is only a skeleton example to help me
to show you how the framework is working. Furthermore,
'ipt_ipaddr_info' and things like that are not part of the
iptables/netfilter but part of this example.

2.0 The netfilter module

The duty of a match module is to inspect each packet received and to
decide if it matches or not according to our criteria. The module has the
following means to do that:

Receive each packet hitting the table related with the match module

Tell netfilter if our module match the packet

2.1 Structures and Functions available

At first some basic structures. This structure is defined in <linux/netfilter_ipv4/ip_tables.h>.
If you are interested in learning more about this structure and
the previous one presented for iptables, you should look at the netfilter hacking howto written by Rusty Russell and Harald Welte.

We hand them functions that will be called at the loading and unloading of the module.

module_init(init);
module_exit(fini);

2.2.2 match function

The linux tcp/ip stack is sprink of five netfilter's hooks. Thus when a packet
walks in, the stack passes the packet to the appropriate hook which iterates
through each table which iterates through each rules. When it's the
time to your module to have the packet, it can finally do its job.

If the '--ipsrc' argument has been inputed we look if the source
address match with the one specified in the rule. We don't forget to
take in consideration the invert flag: '!'. If we don't match: we
return the verdict; 0.

2.3 Summary chapter 2

In this second part, we covered the netfilter's
module and how to register it by using a specific structure. In
addition, we discussed how to match a specific situation according to
criteria provided by the user-space part.

3.0 Playing with iptables/netfilter

We have seen how to write
a new iptables/netfilter's match module. Now, we would like to add it
in our kernel to play with it. Here, I assume that you know how to
build/compile a kernel. First, get the skeletons's match files from the download page for this article.

3.2 kernel

First, you have to copy 'ipt_ipaddr.c' in <linux/net/ipv4/netfilter/> and 'ipt_ipaddr.h' into <linux/include/linux/netfilter_ipv4/>. Some of you are still using linux 2.4, so I'll present both 2.4 and 2.6 files to edit.

For 2.4, edit <linux/net/ipv4/netfilter/Config.in> and add the bold line.