From 38965fcc0455ab101603f3e35d5bdbd2a5bd6962 Mon Sep 17 00:00:00 2001 From: Mohammad Shehar Yaar Tausif Date: Mon, 29 Jul 2024 18:13:28 +0530 Subject: [PATCH] add userspace library skeleton Signed-off-by: Mohammad Shehar Yaar Tausif --- README.md | 12 +++ usr/lib/xtable/Makefile | 27 ++++++ usr/lib/xtable/libxtable.c | 183 +++++++++++++++++++++++++++++++++++++ 3 files changed, 222 insertions(+) create mode 100644 usr/lib/xtable/Makefile create mode 100644 usr/lib/xtable/libxtable.c diff --git a/README.md b/README.md index cd79313e..fe5686a5 100644 --- a/README.md +++ b/README.md @@ -1203,6 +1203,18 @@ netfilter hooks to Lua. * `"LOCAL_OUT"`: `NF_INET_LOCAL_OUT`. The packet is generated by the local system. * `"POST_ROUTING"`: `NF_INET_POST_ROUTING`. The packet is about to be sent out. +### xtable userspace library (libxtable) + +The `libxtable` [userspace library](usr/lib/xtable) provides support for generating userspace code for [xtable extensions](https://inai.de/projects/xtables-addons/). The user can modify the generated lua code to implement the userspace handlers for the corresponding xtable extension. + +To generate the library, the following steps are required: + +1. Go to `usr/lib/xtable` and create a `libxt_.lua` file. +2. Register your callbacks for the xtable extension by importing the library (`libxtable`) in the created file. +3. Run `LUAXTABLE_MODULE= make` to build the extension and `LUAXTABLE_MODULE= make install` (as root) to install the userspace plugin to the system. + +Now load the extension normally using `iptables`. + # Examples ### spyglass diff --git a/usr/lib/xtable/Makefile b/usr/lib/xtable/Makefile new file mode 100644 index 00000000..8605ba25 --- /dev/null +++ b/usr/lib/xtable/Makefile @@ -0,0 +1,27 @@ +# SPDX-FileCopyrightText: (c) 2024 Mohammad Shehar Yaar Tausif +# SPDX-License-Identifier: MIT OR GPL-2.0-only + +LUAXTABLE_MODULE ?= test + +CFLAGS = -DLUAXTABLE_MODULE=\"${LUAXTABLE_MODULE}\" -O2 -Wall -I../../../lib +XTABLES_SO_DIR = $(shell pkg-config xtables --variable xtlibdir) +LUA_FLAGS = -llua -lm + +all: + make libxt_${LUAXTABLE_MODULE}.so + +install: + sudo cp libxt_${LUAXTABLE_MODULE}.so ${XTABLES_SO_DIR} + +uninstall: + sudo rm -f ${XTABLES_SO_DIR}/libxt_${LUAXTABLE_MODULE}.so + +clean: + rm -f libxt_*.so libxt_*.o + +lib%.so: lib%.o + gcc -shared -fPIC -o libxt_${LUAXTABLE_MODULE}.so libxt_${LUAXTABLE_MODULE}.o ${LUA_FLAGS}; + +lib%.o: libxtable.c + gcc ${CFLAGS} ${LUA_FLAGS} -D_INIT=lib$*_init -fPIC -c -o libxt_${LUAXTABLE_MODULE}.o $<; + diff --git a/usr/lib/xtable/libxtable.c b/usr/lib/xtable/libxtable.c new file mode 100644 index 00000000..03a9d370 --- /dev/null +++ b/usr/lib/xtable/libxtable.c @@ -0,0 +1,183 @@ +/* +* SPDX-FileCopyrightText: (c) 2024 Mohammad Shehar Yaar Tausif +* SPDX-License-Identifier: MIT OR GPL-2.0-only +*/ + +#include +#include +#include + +#include +#include +#include + +#include + +#include "luaxtable.h" + +#ifndef LUAXTABLE_MODULE +#error "LUAXTABLE_MODULE is not defined" +#endif + +static lua_State *L = NULL; +const char *libxtable_match_key = "libxtable_match"; +const char *libxtable_target_key = "libxtable_target"; + +int luaopen_libxtable(lua_State *L); + +static inline int libxtable_run(lua_State *L, const char *func_name, const char *key, int nargs, int nresults) +{ + int ret = 0; + int base = lua_gettop(L) - nargs; + lua_rawgetp(L, LUA_REGISTRYINDEX, key); + + if (lua_getfield(L, -1, func_name) != LUA_TFUNCTION) { + fprintf(stderr, "Function %s not found\n", func_name); + goto restore; + } + + lua_insert(L, base + 1); /* func */ + lua_pop(L, 1); /* table */ + + if (lua_pcall(L, nargs, nresults, 0) != LUA_OK) { + fprintf(stderr, "Failed to call Lua function %s: %s\n", func_name, + lua_tostring(L, -1)); + goto restore; + } + + if (nresults == 1) + ret = lua_toboolean(L, -1); + +restore: + lua_settop(L, base); + return ret; +} + +// stubs for xtables functions +#define LIBXTABLE_HELPER_CB(hook) \ +static void libxtable_##hook##_help(void) \ +{ \ + libxtable_run(L, "help", libxtable_##hook##_key, 0, 0); \ +} + +#define LIBXTABLE_INITER_CB(hook) \ +static void libxtable_##hook##_init(struct xt_entry_##hook *hook) \ +{ \ + libxtable_run(L, "init", libxtable_##hook##_key, 1, 0); \ +} + +#define LIBXTABLE_PARSER_CB(hook) \ +static int libxtable_##hook##_parse(int c, char **argv, int invert, unsigned int *flags, \ + const void *entry, struct xt_entry_##hook **hook) \ +{ \ + return libxtable_run(L, "parse", libxtable_##hook##_key, 0, 1); \ +} + +#define LIBXTABLE__FINALCHECKER_CB(hook) \ +static void libxtable_##hook##_finalcheck(unsigned int flags) \ +{ \ + libxtable_run(L, "final_check", libxtable_##hook##_key, 0, 0); \ +} + +#define LIBXTABLE_PRINTER_CB(hook) \ +static void libxtable_##hook##_print(const void *entry, const struct xt_entry_##hook *hook, int numeric) \ +{ \ + libxtable_run(L, "print", libxtable_##hook##_key, 0, 0); \ +} + +#define LIBXTABLE_SAVER_CB(hook) \ +static void libxtable_##hook##_save(const void *entry, const struct xt_entry_##hook *hook) \ +{ \ + libxtable_run(L, "save", libxtable_##hook##_key, 0, 0); \ +} + +#define LIBXTABLE_NEWCB(hook) \ + LIBXTABLE_HELPER_CB(hook) \ + LIBXTABLE_INITER_CB(hook) \ + LIBXTABLE_PARSER_CB(hook) \ + LIBXTABLE__FINALCHECKER_CB(hook) \ + LIBXTABLE_PRINTER_CB(hook) \ + LIBXTABLE_SAVER_CB(hook) + +LIBXTABLE_NEWCB(match); +LIBXTABLE_NEWCB(target); + + +#define LIBXTABLE_NEWREG(hook) \ +static struct xtables_##hook libxtable_##hook##_reg = { \ + .version = XTABLES_VERSION, \ + .name = LUAXTABLE_MODULE, \ + .size = XT_ALIGN(sizeof(luaxtable_info_t)), \ + .userspacesize = 0, \ + .help = libxtable_##hook##_help, \ + .init = libxtable_##hook##_init, \ + .parse = libxtable_##hook##_parse, \ + .final_check = libxtable_##hook##_finalcheck, \ + .print = libxtable_##hook##_print, \ + .save = libxtable_##hook##_save \ +} + +LIBXTABLE_NEWREG(match); +LIBXTABLE_NEWREG(target); + +static inline int libxtable_checkint(lua_State *L, int idx, const char *key) +{ + if (lua_getfield(L, idx, key) != LUA_TNUMBER) { + fprintf(stderr, "invalid \'%s\' in match\n", key); + return 0; + } + int ret = lua_tointeger(L, -1); + lua_pop(L, 1); + return ret; +} + +#define LIBXTABLE_LIB_CB(hook) \ +static int libxtable_##hook##_lib(lua_State *L) \ +{ \ + luaL_checktype(L, 1, LUA_TTABLE); \ + \ + libxtable_##hook##_reg.revision = libxtable_checkint(L, 1, "revision"); \ + libxtable_##hook##_reg.family = libxtable_checkint(L, 1, "family"); \ + xtables_register_##hook(&libxtable_##hook##_reg); \ + lua_pushvalue(L, 1); \ + lua_rawsetp(L, LUA_REGISTRYINDEX, libxtable_##hook##_key); \ + return 0; \ +} + +LIBXTABLE_LIB_CB(match); +LIBXTABLE_LIB_CB(target); + +static const luaL_Reg libxtable_lib[] = { + {"match", libxtable_match_lib}, + {"target", libxtable_target_lib}, + {NULL, NULL} +}; + +int luaopen_libxtable(lua_State *L) +{ + luaL_newlib(L, libxtable_lib); + return 1; +} + +static int __attribute__((constructor)) _init(void) +{ + if ((L = luaL_newstate()) == NULL) { + fprintf(stderr, "Failed to create Lua state\n"); + return -1; + } + luaL_openlibs(L); + luaL_requiref(L, "libxtable", luaopen_libxtable, 1); + + if (luaL_dofile(L, "libxt_"LUAXTABLE_MODULE".lua") != LUA_OK) { + fprintf(stderr, "Failed to load Lua script: %s\n", lua_tostring(L, -1)); + return -1; + } + + return 0; +} + +static void __attribute__((destructor)) _fini(void) +{ + if (L) + lua_close(L); +}