laforge@gnumonks.org
This document gives a brief description how to write netfilter = connection=20 tracking helper and nat helper modules. Netfilter is the packet = filtering / NAT=20 infrastructure provided by the Linux 2.4.x kernel.=20
I strongly recommend You reading Rusty Russel's `netfilter hacking = howto'=20 which is available from the netfilter project homepage at=20 http://netfilter.kernelnotes.org=20
The duty of a connection tracking module is to specify which packtets = belong=20 to an already established connection. The module has the following means = to do=20 that:=20
At first some basic structures=20
`struct ip_conntrack_tuple' (just printed the fields valid for TCP)=20
the source IP address
the TCP source port
the destination IP address
the protocol (IPPROTO_TCP, ...)
the TCP destination port
Your kernel module's init function has to call=20 `ip_conntrack_helper_register()' with a pointer to a `struct=20 ip_conntrack_helper'. This struct has the following fields:=20
This is the header for the linked list. Netfilter handles this list = internally. Just initialize it with { NULL, NULL }
This is a `struct ip_conntrack_tuple' which specifies the packets = our=20 conntrack helper module is interested in.
Again a `struct ip_conntrack_tuple'. This mask specifies which bits =
of=20
tuple
are valid.
The function which netfilter should call for each packet matching=20 tuple+mask
#define FOO_PORT 111
static int foo_help(const struct iphdr *iph, size_t len,=20
struct ip_conntrack *ct,=20
enum ip_conntrack_info ctinfo)
{
/* analyze the data passed on this connection and=20
decide how related packets will look like */
if (there_will_be_new_packets_related_to_this_connection)
{
t = new_tuple_specifying_related_packets;
ip_conntrack_expect_related(ct, &t);
=20
/* save information important for NAT in
ct->help.ct_foo_info; */
=20
}
return NF_ACCEPT;
} =20
static struct ip_conntrack_helper foo;
static int __init init(void)
{
memset(&foo, 0, sizeof(struct ip_conntrack_helper);
/* we are interested in all TCP packets with destport 111 */
foo.tuple.dst.protonum = IPPROTO_TCP;
foo.tuple.dst.u.tcp.port = htons(FOO_PORT);
foo.mask.dst.protonum = 0xFFFF;
foo.mask.dst.u.tcp.port = 0xFFFF;
foo.help = foo_help;
return ip_conntrack_helper_register(&foo); =20
}
static void __exit fini(void)
{
ip_conntrack_helper_unregister(&foo);
}
NAT helper modules do some application specific NAT handling. Usually = this=20 includes on-the-fly manipulation of data. Think about the PORT command = in FTP,=20 where the client tells the server which ip/port to connect to. = Thererfore a FTP=20 helper module has to replace the ip/port after the PORT command in the = FTP=20 control connection.=20
If we are dealing with TCP, things get slightly more complicated. The = reason=20 is a possible change of the packet size (FTP example: The length of the = string=20 representing an IP/port tuple after the PORT command has changed). If we = had to=20 change the packet size, we have a syn/ack difference between left and = right side=20 of the NAT box. (i.e. if we had extended one packet by 4 octets, we have = to add=20 this offset to the TCP sequence number of each following packet)=20
Special NAT handling of all related packets is required, too. Take as = example=20 again FTP, where all incoming packets of the DATA connection have to be = NATed to=20 the ip/port given by the client with the PORT command on the control = connection.=20
Your nat helper module's `init()' function has to call=20 `ip_nat_helper_register()' with a pointer to a `struct ip_nat_helper'. = This=20 struct hast the following members:=20
Just again the list header for netfilters internal use. Initialize = this=20 with { NULL, NULL }.
a `struct ip_conntrack_tuple' describing which packets our nat = helper is=20 interested in.
a `struct ip_conntrack_tuple', telling netfilter which bits of=20
tuple
are valid.
The help function which is called for each packet matching = tuple+mask.
The uniqe name this nat helper is identified by.
#define FOO_PORT 111
static int foo_nat_expected(struct sk_buff **pksb,
unsigned int hooknum,
struct ip_conntrack *ct,
struct ip_nat_info *info,
struct ip_conntrack *master,
struct ip_nat_info *masterinfo,
unsigned int *verdict)
/* called whenever a related packet (as specified in the =
connectiontracking
module) arrives
params: pksb packet buffer
hooknum HOOK the call comes from (POST_ROUTING, =
PRE_ROUTING)
ct information about this (the related) connection
info (STATE: established, related, new)
master information about the master connection
masterinfo (STATE: established, related, new) of =
master */
{
/* basically just change ip/port of the packet to the =
masqueraded
values (read from master->tuplehash) */
} =20
static int foo_help(struct ip_conntrack *ct, =20
struct ip_nat_info *info,
enum ip_conntrack_info ctinfo,
unsigned int hooknum,
struct sk_buff **pksb)
/* called for the packet causing related packets=20
params: ct information about tracked connection
info (STATE: related, new, established, ... )
hooknum HOOK the call comes from (POST_ROUTING, =
PRE_ROUTING)
pksb packet buffer
*/
{
/* extract information about future related packets,
exchange address/port with masqueraded values,
insert tuple about related packets */
}
static struct ip_nat_expect foo_expect = {
{ NULL, NULL },
foo_nat_expected };
static struct ip_nat_helper hlpr;
static int __init(void)
{
if (ip_nat_expect_register(&foo_expect))
{
memset(&hlpr, 0, sizeof(struct ip_nat_helper));
hlpr.list = { NULL, NULL };
hlpr.tuple.dst.protonum = IPPROTO_TCP;
hlpr.tuple.dst.u.tcp.port = htons(FOO_PORT);
hlpr.mask.dst.protonum = 0xFFFF;
hlpr.mask.dst.u.tcp.port = 0xFFFF;
hlpr.help = foo_help;
ip_nat_helper_register(hlpr);
}
}
static void __exit(void)
{
ip_nat_expect_unregister(&foo_expect);
ip_nat_helper_unregister(&hlpr);
}
I want to thank all the great netfilter folks, especially Rusty = Russel, for=20 providing us (the Linux community) with this neat infrastructure.=20
|