Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write custom module for ebtables?

Basically, I want to write a kernel module that adds a possible filter to ebtables. Then I need to tell ebtables to use my filter on a bridge I have set up.

The reason I need to write my own module is that I want to introduce delay between consecutive packages (for some testing reason). To demonstrate, my network originally has a traffic like this:

+++-----------------+++-----------------+++-----------------+++-----------------

where + shows traffic of a package and - means no package on the line. I want to put a bridge in between so that the pattern of the packets would change to this:

+----+----+---------+----+----+---------+----+----+---------+----+----+---------

This means that I would make sure there would be a certain amount of delay between arrival of each packet.

Now I have written the following simple code which I basically took from linux-source/net/bridge/netfilter/ebt_ip.c:

static bool match(const struct sk_buff *skb, const struct xt_match_param *par)
{
    printk(KERN_INFO"match called\n");
    return true;  // match everything!
}

static bool check(const struct xt_mtchk_param *par)
{
    printk(KERN_INFO"check called\n");
    return true;  // pass everything!
}

static struct xt_match reg __read_mostly = {
    .name = "any",   // I made this up, but I tried also putting ip for example which didn't change anything.
    .revision = 0,
    .family = NFPROTO_BRIDGE,
    .match = match,
    .checkentry = check,
    .matchsize = XT_ALIGN(4),  // don't know what this is, so I just gave it an `int`
    .me = THIS_MODULE
};

int init_module(void)
{
    return xt_register_match(&reg);
}

void cleanup_module(void)
{
    xt_unregister_match(&reg);
}

I successfully load the module. But it's as if it's not there. I'm not getting the logs inside match and check functions so the bridge is clearly not considering my filter. What am I doing wrong?

I have tried many combinations of loading my filter first, setting up the bridge first or setting ebtables rules first, but none of them change anything.

P.S. The bridge itself works. I am certain that ebtables is also in effect because if I add a policy to drop packages, I don't receive them on the final computer. What I can't figure out is how to tell ebtables to consider my filter also.

like image 459
Shahbaz Avatar asked Feb 22 '23 11:02

Shahbaz


1 Answers

I got this working, not in the most elegant way, but anyway, I am writing it here for a future wanderer:

Let's say your filter name is: "any"

User-space plugin

You need headers that are not available outside the ebtables source. So, get the source code, and go to extensions folder. In the Makefile, add any to EXT_FUNC (that is targets to be built) and write the source file ebt_any.c like the following:

#include <stdio.h>
#include <getopt.h>
#include "../include/ebtables_u.h"

/*struct whatever
{
        int a;
};*/

static struct option _any_opts[] =
{
        {"use-any", required_argument, 0, 0},
        {'\0'}
};

static void _any_help(void)
{
        printf("any match options: nothing!\n");
}

static void _any_init(struct ebt_entry_match *match)
{
        printf("any_init\n");
}

static void _any_check(const struct ebt_u_entry *entry, const struct ebt_entry_match *match, const char *name,
        unsigned int hookmask, unsigned int time)
{
        printf("any_check\n");
}

static int _any_parse(int c, char **argv, int argc, const struct ebt_u_entry *entry, unsigned int *flags, struct ebt_entry_match **match)
{
        printf("any_parse: %d\n", c);
        if (c == 0)
                return 1;
        return 0;       // return true for anything
}
        
static int _any_compare(const struct ebt_entry_match *m1, const struct ebt_entry_match *m2)
{
/*      struct whatever *w1 = (struct whatever *)m1->data;
        struct whatever *w2 = (struct whatever *)m2->data;
        if (w1->a != w2->a)
                return 0;*/
        return 1;
}

static void _any_print(const struct ebt_u_entry *entry, const struct ebt_entry_match *match)
{       
        printf("any_print");
}

static struct ebt_u_match _reg = {
        .name           = "any",
//      .size           = sizeof(struct whatever),
        .help           = _any_help,
        .init           = _any_init,
        .parse          = _any_parse,
        .final_check    = _any_check,
        .print          = _any_print,
        .compare        = _any_compare,
        .extra_ops      = _any_opts,
};

void _init(void)
{
        ebt_register_match(&_reg);
}

Note: if you have data going from user-space to kernel space, write something instead of struct whatever. I have commented it out because I am not using anything.

Note: even if your program doesn't require an option (such as mine which was supposed to match everything), you need to give an option anyway because that's how ebtables knows to use your filter.

Note: some of these functions seem unnecessary, but if you don't write them, you get a "BUG: bad merge" error.

Kernel-space module

The kernel-space module is simpler:

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

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Shahbaz Youssefi");
MODULE_ALIAS("ebt_any");

/*struct whatever
{
        int a;
};*/
    
static bool match(const struct sk_buff *skb, const struct xt_match_param *par)
{
        printk(KERN_INFO"Matching\n");
        return true;
}

static bool check(const struct xt_mtchk_param *par)
{
        printk(KERN_INFO"Checking\n");
        return true;
}

static struct xt_match reg __read_mostly = {
        .name           = "any",
        .match          = match,
//      .matchsize      = sizeof(struct whatever),
        .checkentry     = check,
        .me             = THIS_MODULE
};

int init_module(void)
{       
        int ret = 0;
        printk("Bridge initializing...\n");
        ret = xt_register_match(&reg);
        printk("Bridge initializing...done!\n");
        return ret;
}

void cleanup_module(void)
{
        printk("Bridge exiting...\n");
        xt_unregister_match(&reg);
        printk("Bridge exiting...done!\n");
}

Note: if you use struct whatever in user-space, you must use the same in kernel-space.

Note: unlike user-space plugin that use ebtables headers/functions, the kernel module uses xtables instead!!

Compile the module (fairly standard) and install it for automatic loading. Alternatively, you can insmod and rmmod the module yourself before adding/after removing ebtables rules.

How to make ebtables use your filter

Just add a rule that contains --use-any some_value and you're good. For example:

ebtables -A FORWARD --use-any 1 -j ACCEPT

Note: this --use-any is the option that was given in ebt_u_match reg.extra_ops (which was defined in array _any_opts) in the user space plugin.

like image 77
Shahbaz Avatar answered Mar 05 '23 17:03

Shahbaz