Avanti Indietro Indice

6. Netfilter Extensions

6.1 Netfilter Extensions

This is the lowest layer of hooking into the kernel stack

You can write your own kernel modules which hook directly into the IP stack (or others)


/* Rusty's Dumb netfilter hook example */
#include <linux/config.h>
#include <linux/module.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>

/* The work comes in here from netfilter.c. */
static unsigned int
stupid_hook(unsigned int hook,
           struct sk_buff **pskb,
           const struct net_device *indev,
           const struct net_device *outdev,
           int (*okfn)(struct sk_buff *))
{
       if ((*pskb)->len == 200)
               return NF_DROP;

       return NF_ACCEPT;
}

static struct nf_hook_ops stupid_ops
= { { NULL, NULL }, stupid_hook, PF_INET, NF_IP_POST_ROUTING, 0 };

static int __init init(void)
{
        return nf_register_hook(&stupid_ops);
}

static void __exit fini(void)
{
        nf_unregister_hook(&linuxmag_ops);
}

module_init(init);
module_exit(fini);

6.2 Extensions In Userspace (cont.)

You can use the QUEUE target to pass packets to an asynchronous handler

The most common handler is James Morris' ip_queue handler


/* Stupidity in userspace */
#include <sys/types.h>
#include <limits.h>
#include <net/if.h>
#include <netinet/ip.h>
#include <linux/netfilter_ipv4.h>

#include "libipq/libipq.h"

int main(void)
{
        struct ipq_handle *h;
        unsigned char packet[65536];

        if ((h = ipq_create_handle(0)) == NULL)
                exit(1);

        /* Tell the queue to send packets up to size BUFSIZE */
        rval = ipq_set_mode(h, IPQ_COPY_META, 0);
        if (rval < 0)
                goto die;

        while ((rval = ipq_read(h, packet, sizeof(packet), 0)) >= 0) {
                if (ipq_message_type(packet) == IPQM_PACKET) {
                        ipq_packet_msg_t *msg = ipq_get_packet(packet);
                        int verdict = NF_ACCEPT;

                        if (m->data_len == 200)
                                verdict = NF_DROP;
                        ipq_set_verdict(h, m->packet_id,
                                        verdict, m->data_len, m->payload);
                } else {
                        fprintf(stderr, "Received error message %d\n",
                                ipq_get_msgerr(packet));
                        break;
                }
        }
die:
        ipq_destroy_handle(h);
        exit(1);
}

6.3 iptables Extensions

We've seen the limited REJECT target extension.

Let's write a `length' match extension (James Morris)


#ifndef _IPT_LENGTH_H
#define _IPT_LENGTH_H

struct ipt_length_info {
        u_int16_t length;
};
#endif /*_IPT_LENGTH_H*/

 /* Kernel module to match packet length. */
#include <linux/module.h>
#include <linux/skbuff.h>

#include <linux/netfilter_ipv4/ipt_length.h>
#include <linux/netfilter_ipv4/ip_tables.h>

static int
match(const struct sk_buff *skb,
      const struct net_device *in,
      const struct net_device *out,
      const void *matchinfo,
      int offset,
      const void *hdr,
      u_int16_t datalen,
      int *hotdrop)
{
        const struct ipt_length_info *info = matchinfo;

        return skb->len == info->length;
}

static int
checkentry(const char *tablename,
           const struct ipt_ip *ip,
           void *matchinfo,
           unsigned int matchsize,
           unsigned int hook_mask)
{
        if (matchsize != IPT_ALIGN(sizeof(struct ipt_length_info)))
	         return 0;
        return 1;
}

static struct ipt_match length_match
 = { { NULL, NULL }, "length", &match, &checkentry, NULL, THIS_MODULE };

static int __init init(void)
{
        return ipt_register_match(&length_match);
}

static void __exit fini(void)
{
        ipt_unregister_match(&length_match);
}

module_init(init);
module_exit(fini);

6.4 iptables Extensions (cont.)

We need a shared library to supplement command line:


/* Shared library add-on to iptables to add packet length matching support. */
#include <stdio.h>
#include <netdb.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h>
#include <iptables.h>
#include <linux/netfilter_ipv4/ipt_length.h>

/* Function which prints out usage message. */
static void
help(void)
{
        printf("length v%s options:\n"
              "--length length    Match this packet length\n",
              NETFILTER_VERSION);
}

static struct option opts[] = {
        { "length", 1, 0, '1' },
        {0}
};

/* Initialize the match. */
static void
init(struct ipt_entry_match *m, unsigned int *nfcache)
{
        *nfcache |= NFC_UNKNOWN;
}

/* Function which parses command options; returns true if it
   ate an option */
static int
parse(int c, char **argv, int invert, unsigned int *flags,
      const struct ipt_entry *entry,
      unsigned int *nfcache,
      struct ipt_entry_match **match)
{
       struct ipt_length_info *info = (struct ipt_length_info *)(*match)->data;
       int len;

        switch (c) {
                case '1':
                        if (*flags)
                                exit_error(PARAMETER_PROBLEM,
                                           "length: `--length' may only be "
                                           "specified once");
                       len = string_to_number(argv[optind-1], 0, 0xFFFF);
                       if (len == -1)
                               exit_error(PARAMETER_PROBLEM,
                                          "length invalid: `%s'\n", s);
                        *flags = 1;
                        break;

                default:
                        return 0;
        }
        return 1;
}

/* Final check; must have specified --length. */
static void
final_check(unsigned int flags)
{
        if (!flags)
                exit_error(PARAMETER_PROBLEM,
                           "length: You must specify `--length'");
}

/* Common match printing code. */
static void
print_length(struct ipt_length_info *info)
{
        printf("%u ", info->length);
}

/* Prints out the matchinfo. */
static void
print(const struct ipt_ip *ip,
      const struct ipt_entry_match *match,
      int numeric)
{
        printf("length ");
        print_length((struct ipt_length_info *)match->data);
}

/* Saves the union ipt_matchinfo in parsable form to stdout. */
static void
save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
{
        printf("--length ");
        print_length((struct ipt_length_info *)match->data);
}

struct iptables_match length
= { NULL,
    "length",
    NETFILTER_VERSION,
    sizeof(struct ipt_length_info),
    sizeof(struct ipt_length_info),
    &help,
    &init,
    &parse,
    &final_check,
    &print,
    &save,
    opts
};

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

Using it is now simple a matter of

iptables -m length --length 100 -j DROP

6.5 Other Extensions

Both the connection tracking code and NAT code can be taught about new transport protocols (eg. PPTP)

Both can also have `helpers' to assist with particular application protocols (eg. ftp, irc, etc).

6.6 Summary

The framework is very extensible

Even without altering the core code, alot of flexibility is given at various levels

Documentation is fairly good (netfilter hacking HOWTO)

Flexibility should increase design life


Avanti Indietro Indice                                 LINUXCARE