如何编写适用于 ebtables 的自定义模块?

4

基本上,我想编写一个内核模块,为ebtables添加一个可能的过滤器。然后,我需要告诉ebtables在我设置的桥上使用我的过滤器。

我需要编写自己的模块的原因是我想在连续的数据包之间引入延迟(出于某些测试原因)。为了说明,我的网络最初的流量如下:

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

其中,+ 表示数据包的流量,- 表示该线路上没有数据包。我希望在两者之间搭建一座桥梁,这样数据包模式将变为:

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

这意味着我将确保每个数据包到达之间存在一定的延迟。
现在,我编写了以下简单代码,基本上是从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);
}

我已经成功加载了模块,但好像它并没有生效。我在matchcheck函数中没有看到日志输出,所以桥明显没有考虑我的过滤器。我做错了什么?

我尝试了许多组合,先加载我的过滤器,先设置桥,或者先设置ebtables规则,但都没有改变什么。

P.S. 桥本身是有效的。 我确定ebtables也在生效,因为如果我添加一个策略来丢弃数据包,我不会在最终计算机上收到它们。 我弄不清楚的是如何告诉ebtables也考虑我的过滤器。

2个回答

8

我已经让它工作了,虽然不是最优雅的方式,但无论如何,我在这里写下来供未来的探险者参考:

假设您的过滤器名称为:“any”

用户空间插件

您需要一些在ebtables源代码之外不可用的头文件。因此,获取源代码,并转到扩展文件夹。在Makefile中,将any添加到EXT_FUNC(即要构建的目标),并编写以下源文件ebt_any.c

#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);
}

注意:如果您有从用户空间到内核空间的数据传输,应编写其他内容而不是struct whatever。我已将其注释掉,因为我没有使用任何东西。
注意:即使您的程序不需要选项(例如我的程序应该匹配所有内容),您仍然需要提供选项,因为这是ebtables知道如何使用您的过滤器的方式。
注意:其中一些函数似乎是不必要的,但如果您不编写它们,则会出现“BUG:bad merge”错误。
内核空间模块更简单:
#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");
}

请注意:如果您在用户空间使用struct whatever,则必须在内核空间中使用相同的内容。
请注意:与使用ebtables头文件/函数的用户空间插件不同,内核模块使用xtables!
编译模块(相当标准)并安装以进行自动加载。或者,在添加/删除ebtables规则之前,您可以自己使用insmodrmmod模块。
如何使ebtables使用您的过滤器:只需添加包含--use-any some_value的规则即可。例如:
ebtables -A FORWARD --use-any 1 -j ACCEPT

注意:--use-any是在用户空间插件中定义的数组_any_opts中给出的ebt_u_match reg.extra_ops选项。


2

要使用内核模块,您还需要为用户空间程序编写适当的插件,然后插入调用它的规则。

如果您没有任何选项,请不要在struct xt_match中指定任何.matchsize参数(相当于指定0)。


非常感谢!我正在寻找如何编写用户空间程序,但是在使用ebtables时,我只能找到这个链接(http://ebtables.sourceforge.net/ebtables-hacking/ebtables-hacking-HOWTO-4.html),它已经过时了。有很多关于iptables的东西存在,但我猜那与我所需的不同。您能否请指导我一个有用的链接或一段代码,以便我可以阅读并尝试弄清楚这个问题? - Shahbaz
也许你可以先阅读 iptables 的免费 PDF 《编写 Netfilter 模块》以获得更好的结果,一旦摄入了足够的咖啡因,再回到 ebtables。你提供的 ebt howto 页面看起来是一个快速入门的准确指南,但请记住,ebtables 是一种旧版 iptables 的分支。 - jørgensen
我按照你说的做了。我读了电子书,实现了模块和用户共享对象。我将匹配命名为“any”,将xt_any.ko放在可加载的某个地方(同时运行depmod),将libxt_any.so放在/lib/xtables中,根据那本书的说明一切都很好。现在,无论我搜索多少次,我都找不到命令告诉ebtables使用我的匹配函数。你有任何想法吗? - Shahbaz
ebtables的man页面说匹配扩展会自动加载,“因此无需像iptables一样需要-m”。好的,不用-m,但是无论我尝试什么组合,ebtables都没有使用我的“any”匹配扩展! - Shahbaz
尝试将它们命名为 ebt_any.ko 和 libebt_any.so(放置在 /lib/ebtables 中),但没有成功。 - Shahbaz
好的,我解决了。明天我会发布整个过程作为更新。它需要混合使用“在 ebtables 源代码内构建”和“在内核模块中使用 xtables”。 - Shahbaz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接