I did not get this work in the most elegant way, but in any case, I am writing it here for the 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 the extensions folder. In the Makefile, add any to EXT_FUNC (these are the goals you need to create) and write the source ebt_any.c file as follows:
#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 coming from user space to kernel space, write something instead of struct whatever . I commented on this because I am not using anything.
Note. , even if your program does not require an option (for example, mine, which should have fit all), you need to specify the parameter anyway, because the way ebtables knows to use your filter.
Note: some of these functions seem unnecessary, but if you do not write them, you will get the error "BUG: bad merge".
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(®); printk("Bridge initializing...done!\n"); return ret; } void cleanup_module(void) { printk("Bridge exiting...\n"); xt_unregister_match(®); printk("Bridge exiting...done!\n"); }
Note: if you use struct whatever in user space, you must use the same in kernel space.
Note: unlike a user-space plugin using ebtables headers / functions, the kernel module uses xtables instead instead !!
Compile the module (fairly standard) and install it for automatic loading. In addition, you can insmod and rmmod use the module yourself before adding / after removing ebtables rules.
How to make ebtables use your filter
Just add a rule containing --use-any some_value and you are kind. For instance:
ebtables -A FORWARD --use-any 1 -j ACCEPT
Note: this --use-any is the option that was specified in ebt_u_match reg.extra_ops (which was defined in the _any_opts array) in the user space plugin.