From: Jason Ish Date: Thu, 1 May 2025 22:33:19 +0000 (-0600) Subject: lua: convert lua flowint functions to lib: suricata.flowintlib X-Git-Tag: suricata-8.0.0-rc1~389 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=8a5bc12156162ca99ebdaa08e4127ec3a041c131;p=thirdparty%2Fsuricata.git lua: convert lua flowint functions to lib: suricata.flowintlib Ticket: #7487 --- diff --git a/doc/userguide/lua/libs/flowint.rst b/doc/userguide/lua/libs/flowint.rst new file mode 100644 index 0000000000..a5f66acfd0 --- /dev/null +++ b/doc/userguide/lua/libs/flowint.rst @@ -0,0 +1,59 @@ +Flowint Library +############### + +The ``suricata.flowint`` library exposes ``flowint`` variables to Lua +scripts. + +Initialization +************** + +First, the ``flowint`` module must be loaded:: + + local flowintlib = require("suricata.flowint") + +Then in the ``init`` method, any flow integers used in the script +should be registered. This is optional and could be skipped if you +know for sure the flow integers will be registered by some other +means. + +Example:: + + local flowintlib = require("suricata.flowint") + + function init () + flowintlib.register("count") + return {} + end + +Finally, in the ``thread_init`` function a handle is acquired for the +flow integers and stored as a global:: + + function thread_init () + count_flow_int = flowintlib.get("count") + end + +Flow Integer Methods +******************** + +``decr()`` +========== + +Decrement the value of the ``flowint`` by 1. The new value is +returned. If the value is 0, it will remain 0. + +``incr()`` +========== + +Increment the value of the ``flowint`` by 1. The new value is +returned. + +``value()`` +=========== + +Get the current value of the flow integer. Note that ``nil`` may be +returned if the flow integer does not have a value. + +``set(value)`` +=================== + +Set the value of the flowint to the value provided. diff --git a/doc/userguide/lua/libs/index.rst b/doc/userguide/lua/libs/index.rst index 2279b74175..ba7e672360 100644 --- a/doc/userguide/lua/libs/index.rst +++ b/doc/userguide/lua/libs/index.rst @@ -11,6 +11,7 @@ environment without access to additional modules. base64 dns flowlib + flowint flowvar hashlib http diff --git a/src/Makefile.am b/src/Makefile.am index 839e323d74..193fe49ced 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -533,6 +533,7 @@ noinst_HEADERS = \ util-lua-dnp3.h \ util-lua-dnp3-objects.h \ util-lua-dns.h \ + util-lua-flowintlib.h \ util-lua-flowlib.h \ util-lua-flowvarlib.h \ util-lua.h \ @@ -1102,6 +1103,7 @@ libsuricata_c_a_SOURCES = \ util-lua-dnp3.c \ util-lua-dnp3-objects.c \ util-lua-dns.c \ + util-lua-flowintlib.c \ util-lua-flowlib.c \ util-lua-flowvarlib.c \ util-lua-hashlib.c \ diff --git a/src/detect-lua-extensions.c b/src/detect-lua-extensions.c index e92ca9c3bf..c793828de8 100644 --- a/src/detect-lua-extensions.c +++ b/src/detect-lua-extensions.c @@ -29,7 +29,6 @@ #include "detect.h" #include "flow.h" -#include "flow-var.h" #include "util-debug.h" @@ -39,7 +38,6 @@ #include "util-lua.h" #include "util-lua-common.h" -#include "util-lua-http.h" #include "util-lua-ja3.h" #include "util-lua-tls.h" #include "util-lua-smtp.h" @@ -64,183 +62,6 @@ static int GetLuaData(lua_State *luastate, DetectLuaData **ret_ld) return 0; } -static int GetFlow(lua_State *luastate, Flow **ret_f) -{ - Flow *f = LuaStateGetFlow(luastate); - if (f == NULL) { - LUA_ERROR("no flow"); - } - *ret_f = f; - return 0; -} - -static int GetFlowIntById(lua_State *luastate, Flow *f, - FlowVar **ret_fv, bool fv_may_be_null, uint32_t *ret_idx) -{ - DetectLuaData *ld = NULL; - if (ret_idx) - *ret_idx = 0; - *ret_fv = NULL; - - /* need lua data for id -> idx conversion */ - int ret = GetLuaData(luastate, &ld); - if (ret != 0) - return ret; - - if (!lua_isnumber(luastate, 1)) { - LUA_ERROR("flowvar id not a number"); - } - int id = lua_tonumber(luastate, 1); - if (id < 0 || id >= DETECT_LUA_MAX_FLOWVARS) { - LUA_ERROR("flowvar id out of range"); - } - uint32_t idx = ld->flowint[id]; - if (idx == 0) { - LUA_ERROR("flowvar id uninitialized"); - } - FlowVar *fv = FlowVarGet(f, idx); - if (!fv_may_be_null && fv == NULL) { - LUA_ERROR("no flow var"); - } - *ret_fv = fv; - if (ret_idx) - *ret_idx = idx; - return 0; -} - -static int LuaGetFlowint(lua_State *luastate) -{ - Flow *f; - FlowVar *fv; - uint32_t number; - - /* need flow */ - int ret = GetFlow(luastate, &f); - if (ret != 0) - return ret; - - ret = GetFlowIntById(luastate, f, &fv, false, NULL); - if (ret != 0) - return ret; - - number = fv->data.fv_int.value; - - /* return value through luastate, as a luanumber */ - lua_pushnumber(luastate, (lua_Number)number); - return 1; - -} - -static int LuaSetFlowint(lua_State *luastate) -{ - uint32_t idx; - Flow *f; - DetectLuaData *ld; - - /* need lua data for id -> idx conversion */ - int ret = GetLuaData(luastate, &ld); - if (ret != 0) - return ret; - - /* need flow */ - ret = GetFlow(luastate, &f); - if (ret != 0) - return ret; - - /* need flowint idx */ - if (!lua_isnumber(luastate, 1)) { - LUA_ERROR("1st arg not a number"); - } - int id = lua_tonumber(luastate, 1); - if (id < 0 || id >= DETECT_LUA_MAX_FLOWVARS) { - LUA_ERROR("flowint id out of range"); - } - - if (!lua_isnumber(luastate, 2)) { - LUA_ERROR("2nd arg not a number"); - } - lua_Number luanumber = lua_tonumber(luastate, 2); - if (luanumber < 0 || id > (double)UINT_MAX) { - LUA_ERROR("value out of range, " - "value must be unsigned 32bit int"); - } - uint32_t number = (uint32_t)luanumber; - - idx = ld->flowint[id]; - if (idx == 0) { - LUA_ERROR("flowint id uninitialized"); - } - - FlowVarAddInt(f, idx, number); - - SCLogDebug("stored flow:%p idx:%u value:%u", f, idx, number); - return 0; -} - -static int LuaIncrFlowint(lua_State *luastate) -{ - uint32_t idx; - Flow *f; - FlowVar *fv; - uint32_t number; - - /* need flow */ - int ret = GetFlow(luastate, &f); - if (ret != 0) - return ret; - - ret = GetFlowIntById(luastate, f, &fv, true, &idx); - if (ret != 0) - return ret; - - if (fv == NULL) { - number = 1; - } else { - number = fv->data.fv_int.value; - if (number < UINT_MAX) - number++; - } - FlowVarAddIntNoLock(f, idx, number); - - /* return value through luastate, as a luanumber */ - lua_pushnumber(luastate, (lua_Number)number); - SCLogDebug("incremented flow:%p idx:%u value:%u", f, idx, number); - return 1; - -} - -static int LuaDecrFlowint(lua_State *luastate) -{ - uint32_t idx; - Flow *f; - FlowVar *fv; - uint32_t number; - - /* need flow */ - int ret = GetFlow(luastate, &f); - if (ret != 0) - return ret; - - ret = GetFlowIntById(luastate, f, &fv, true, &idx); - if (ret != 0) - return ret; - - if (fv == NULL) { - number = 0; - } else { - number = fv->data.fv_int.value; - if (number > 0) - number--; - } - FlowVarAddIntNoLock(f, idx, number); - - /* return value through luastate, as a luanumber */ - lua_pushnumber(luastate, (lua_Number)number); - SCLogDebug("decremented flow:%p idx:%u value:%u", f, idx, number); - return 1; - -} - static int LuaGetByteVar(lua_State *luastate) { DetectLuaData *ld = NULL; @@ -296,30 +117,6 @@ void LuaExtensionsMatchSetup(lua_State *lua_state, DetectLuaData *ld, */ int LuaRegisterExtensions(lua_State *lua_state) { - lua_pushcfunction(lua_state, LuaGetFlowint); - lua_setglobal(lua_state, "ScFlowintGet"); - - lua_pushcfunction(lua_state, LuaGetFlowint); - lua_setglobal(lua_state, "SCFlowintGet"); - - lua_pushcfunction(lua_state, LuaSetFlowint); - lua_setglobal(lua_state, "ScFlowintSet"); - - lua_pushcfunction(lua_state, LuaSetFlowint); - lua_setglobal(lua_state, "SCFlowintSet"); - - lua_pushcfunction(lua_state, LuaIncrFlowint); - lua_setglobal(lua_state, "ScFlowintIncr"); - - lua_pushcfunction(lua_state, LuaIncrFlowint); - lua_setglobal(lua_state, "SCFlowintIncr"); - - lua_pushcfunction(lua_state, LuaDecrFlowint); - lua_setglobal(lua_state, "ScFlowintDecr"); - - lua_pushcfunction(lua_state, LuaDecrFlowint); - lua_setglobal(lua_state, "SCFlowintDecr"); - lua_pushcfunction(lua_state, LuaGetByteVar); lua_setglobal(lua_state, "SCByteVarGet"); diff --git a/src/detect-lua.c b/src/detect-lua.c index 2854ae5cec..049142471a 100644 --- a/src/detect-lua.c +++ b/src/detect-lua.c @@ -1505,22 +1505,26 @@ static int LuaMatchTest03a(void) /** \test http buffer, flowints */ static int LuaMatchTest04(void) { - const char script[] = "function init (args)\n" - " local needs = {}\n" - " needs[\"flowint\"] = {\"cnt\"}\n" - " return needs\n" + const char script[] = "local flowintlib = require(\"suricata.flowint\")\n" + "function init (args)\n" + " flowintlib.register(\"cnt\")\n" + " return {}\n" + "end\n" + "\n" + "function thread_init (args)\n" + " cnt = flowintlib.get(\"cnt\")\n" "end\n" "\n" "function match(args)\n" " print \"inspecting\"" - " a = ScFlowintGet(0)\n" + " a = cnt:value()\n" " if a then\n" - " ScFlowintSet(0, a + 1)\n" + " cnt:set(a + 1)\n" " else\n" - " ScFlowintSet(0, 1)\n" + " cnt:set(1)\n" " end\n" " \n" - " a = ScFlowintGet(0)\n" + " a = cnt:value()\n" " if a == 2 then\n" " print \"match\"\n" " return 1\n" @@ -1621,22 +1625,26 @@ static int LuaMatchTest04(void) /** \test http buffer, flowints */ static int LuaMatchTest04a(void) { - const char script[] = "function init (args)\n" - " local needs = {}\n" - " needs[\"flowint\"] = {\"cnt\"}\n" - " return needs\n" + const char script[] = "local flowintlib = require(\"suricata.flowint\")\n" + "function init (args)\n" + " flowintlib.register(\"cnt\")\n" + " return {}\n" + "end\n" + "\n" + "function thread_init (args)\n" + " cnt = flowintlib.get(\"cnt\")\n" "end\n" "\n" "function match(args)\n" " print \"inspecting\"" - " a = SCFlowintGet(0)\n" + " a = cnt:value()\n" " if a then\n" - " SCFlowintSet(0, a + 1)\n" + " cnt:set(a + 1)\n" " else\n" - " SCFlowintSet(0, 1)\n" + " cnt:set(1)\n" " end\n" " \n" - " a = SCFlowintGet(0)\n" + " a = cnt:value()\n" " if a == 2 then\n" " print \"match\"\n" " return 1\n" @@ -1739,15 +1747,19 @@ static int LuaMatchTest04a(void) /** \test http buffer, flowints */ static int LuaMatchTest05(void) { - const char script[] = "function init (args)\n" - " local needs = {}\n" - " needs[\"flowint\"] = {\"cnt\"}\n" - " return needs\n" + const char script[] = "local flowintlib = require(\"suricata.flowint\")\n" + "function init (args)\n" + " flowintlib.register(\"cnt\")\n" + " return {}\n" + "end\n" + "\n" + "function thread_init (args)\n" + " cnt = flowintlib.get(\"cnt\")\n" "end\n" "\n" "function match(args)\n" " print \"inspecting\"" - " a = ScFlowintIncr(0)\n" + " a = cnt:incr()\n" " if a == 2 then\n" " print \"match\"\n" " return 1\n" @@ -1850,15 +1862,19 @@ static int LuaMatchTest05(void) /** \test http buffer, flowints */ static int LuaMatchTest05a(void) { - const char script[] = "function init (args)\n" - " local needs = {}\n" - " needs[\"flowint\"] = {\"cnt\"}\n" - " return needs\n" + const char script[] = "local flowintlib = require(\"suricata.flowint\")\n" + "function init (args)\n" + " flowintlib.register(\"cnt\")\n" + " return {}\n" + "end\n" + "\n" + "function thread_init (args)\n" + " cnt = flowintlib.get(\"cnt\")\n" "end\n" "\n" "function match(args)\n" " print \"inspecting\"" - " a = SCFlowintIncr(0)\n" + " a = cnt:incr()\n" " if a == 2 then\n" " print \"match\"\n" " return 1\n" @@ -1961,20 +1977,24 @@ static int LuaMatchTest05a(void) /** \test http buffer, flowints */ static int LuaMatchTest06(void) { - const char script[] = "function init (args)\n" - " local needs = {}\n" - " needs[\"flowint\"] = {\"cnt\"}\n" - " return needs\n" + const char script[] = "local flowintlib = require(\"suricata.flowint\")\n" + "function init (args)\n" + " flowintlib.register(\"cnt\")\n" + " return {}\n" + "end\n" + "\n" + "function thread_init (args)\n" + " cnt = flowintlib.get(\"cnt\")\n" "end\n" "\n" "function match(args)\n" " print \"inspecting\"" - " a = ScFlowintGet(0)\n" + " a = cnt:value()\n" " if a == nil then\n" " print \"new var set to 2\"" - " ScFlowintSet(0, 2)\n" + " cnt:set(2)\n" " end\n" - " a = ScFlowintDecr(0)\n" + " a = cnt:decr()\n" " if a == 0 then\n" " print \"match\"\n" " return 1\n" @@ -2077,20 +2097,24 @@ static int LuaMatchTest06(void) /** \test http buffer, flowints */ static int LuaMatchTest06a(void) { - const char script[] = "function init (args)\n" - " local needs = {}\n" - " needs[\"flowint\"] = {\"cnt\"}\n" - " return needs\n" + const char script[] = "local flowintlib = require(\"suricata.flowint\")\n" + "function init (args)\n" + " flowintlib.register(\"cnt\")\n" + " return {}\n" + "end\n" + "\n" + "function thread_init (args)\n" + " cnt = flowintlib.get(\"cnt\")\n" "end\n" "\n" "function match(args)\n" " print \"inspecting\"" - " a = SCFlowintGet(0)\n" + " a = cnt:value()\n" " if a == nil then\n" " print \"new var set to 2\"" - " SCFlowintSet(0, 2)\n" + " cnt:set(2)\n" " end\n" - " a = SCFlowintDecr(0)\n" + " a = cnt:decr()\n" " if a == 0 then\n" " print \"match\"\n" " return 1\n" diff --git a/src/util-lua-builtins.c b/src/util-lua-builtins.c index 7a51749f4b..c0898e28ea 100644 --- a/src/util-lua-builtins.c +++ b/src/util-lua-builtins.c @@ -20,6 +20,7 @@ #include "util-lua-base64lib.h" #include "util-lua-dataset.h" #include "util-lua-dnp3.h" +#include "util-lua-flowintlib.h" #include "util-lua-flowvarlib.h" #include "util-lua-http.h" #include "util-lua-dns.h" @@ -37,6 +38,7 @@ static const luaL_Reg builtins[] = { { "suricata.dnp3", SCLuaLoadDnp3Lib }, { "suricata.dns", SCLuaLoadDnsLib }, { "suricata.flow", LuaLoadFlowLib }, + { "suricata.flowint", LuaLoadFlowintLib }, { "suricata.flowvar", LuaLoadFlowvarLib }, { "suricata.hashlib", SCLuaLoadHashlib }, { "suricata.http", SCLuaLoadHttpLib }, diff --git a/src/util-lua-flowintlib.c b/src/util-lua-flowintlib.c new file mode 100644 index 0000000000..ff42d08ba5 --- /dev/null +++ b/src/util-lua-flowintlib.c @@ -0,0 +1,189 @@ +/* Copyright (C) 2025 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include "suricata-common.h" +#include "app-layer-protos.h" +#include "flow-var.h" +#include "lauxlib.h" +#include "util-debug.h" +#include "util-lua-common.h" +#include "util-lua-flowintlib.h" +#include "util-lua.h" +#include "util-var-name.h" +#include "detect-lua.h" +#include "detect-lua-extensions.h" + +static const char suricata_flowint_mt[] = "suricata:flowint:mt"; + +static DetectLuaData *GetLuaData(lua_State *luastate) +{ + DetectLuaData *ld; + lua_pushlightuserdata(luastate, (void *)&luaext_key_ld); + lua_gettable(luastate, LUA_REGISTRYINDEX); + ld = lua_touserdata(luastate, -1); + return ld; +} + +/** + * \brief Register a flowvar. + * + * Ensures that a flowvar exists for the provided name, will be + * created if needed. + * + * The flowvar ID is returned, however as this is most likely to be + * used in the scripts "init" method, this ID is unlikely to be used. + */ +static int LuaFlowintRegister(lua_State *L) +{ + DetectLuaData *ld = GetLuaData(L); + const char *name = luaL_checkstring(L, 1); + uint32_t *flowvar_id = lua_newuserdata(L, sizeof(*flowvar_id)); + *flowvar_id = VarNameStoreRegister(name, VAR_TYPE_FLOW_INT); + if (*flowvar_id == 0) { + return luaL_error(L, "failed to register flowvar"); + } + ld->flowint[ld->flowints++] = *flowvar_id; + + luaL_getmetatable(L, suricata_flowint_mt); + lua_setmetatable(L, -2); + + return 1; +} + +static int LuaFlowintGet(lua_State *L) +{ + const char *name = luaL_checkstring(L, 1); + uint32_t *flowvar_id = lua_newuserdata(L, sizeof(*flowvar_id)); + *flowvar_id = VarNameStoreLookupByName(name, VAR_TYPE_FLOW_INT); + if (*flowvar_id == 0) { + return luaL_error(L, "flowvar does not exist"); + } + + luaL_getmetatable(L, suricata_flowint_mt); + lua_setmetatable(L, -2); + + return 1; +} + +static int LuaFlowintValue(lua_State *L) +{ + uint32_t *flowvar_id = luaL_checkudata(L, 1, suricata_flowint_mt); + Flow *f = LuaStateGetFlow(L); + if (f == NULL) { + return LuaCallbackError(L, "flow is NULL"); + } + FlowVar *fv = FlowVarGet(f, *flowvar_id); + if (fv == NULL) { + lua_pushnil(L); + } else { + lua_pushnumber(L, (lua_Number)fv->data.fv_int.value); + } + return 1; +} + +static int LuaFlowintSet(lua_State *L) +{ + const int value = luaL_checkinteger(L, 2); + uint32_t *flowvar_id = luaL_checkudata(L, 1, suricata_flowint_mt); + Flow *f = LuaStateGetFlow(L); + if (f == NULL) { + return luaL_error(L, "no flow"); + } + + FlowVarAddInt(f, *flowvar_id, value); + + return 1; +} + +static int LuaFlowintIncr(lua_State *L) +{ + uint32_t *flowvar_id = luaL_checkudata(L, 1, suricata_flowint_mt); + Flow *f = LuaStateGetFlow(L); + if (f == NULL) { + return luaL_error(L, "no flow"); + } + + FlowVar *fv = FlowVarGet(f, *flowvar_id); + uint32_t value; + if (fv == NULL) { + value = 1; + } else { + value = fv->data.fv_int.value; + if (value < UINT32_MAX) { + value++; + } + } + + FlowVarAddInt(f, *flowvar_id, value); + lua_pushnumber(L, (lua_Number)value); + + return 1; +} + +static int LuaFlowintDecr(lua_State *L) +{ + uint32_t *flowvar_id = luaL_checkudata(L, 1, suricata_flowint_mt); + Flow *f = LuaStateGetFlow(L); + if (f == NULL) { + return luaL_error(L, "no flow"); + } + + FlowVar *fv = FlowVarGet(f, *flowvar_id); + uint32_t value; + if (fv == NULL) { + value = 0; + } else { + value = fv->data.fv_int.value; + if (value > 0) { + value--; + } + } + + FlowVarAddInt(f, *flowvar_id, value); + lua_pushnumber(L, (lua_Number)value); + + return 1; +} + +static const luaL_Reg flowvarlib[] = { + // clang-format off + { "register", LuaFlowintRegister, }, + { "get", LuaFlowintGet }, + { NULL, NULL, }, + // clang-format on +}; + +static const luaL_Reg flowvarmt[] = { + // clang-format off + { "value", LuaFlowintValue, }, + { "set", LuaFlowintSet, }, + { "incr", LuaFlowintIncr, }, + { "decr", LuaFlowintDecr, }, + { NULL, NULL, }, + // clang-format on +}; + +int LuaLoadFlowintLib(lua_State *L) +{ + luaL_newmetatable(L, suricata_flowint_mt); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + luaL_setfuncs(L, flowvarmt, 0); + + luaL_newlib(L, flowvarlib); + return 1; +} diff --git a/src/util-lua-flowintlib.h b/src/util-lua-flowintlib.h new file mode 100644 index 0000000000..9dcfc1a0e0 --- /dev/null +++ b/src/util-lua-flowintlib.h @@ -0,0 +1,25 @@ +/* Copyright (C) 2025 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef SURICATA_UTIL_LUA_FLOWINTLIB_H +#define SURICATA_UTIL_LUA_FLOWINTLIB_H + +#include "lua.h" + +int LuaLoadFlowintLib(lua_State *L); + +#endif /* SURICATA_UTIL_LUA_FLOWINTLIB_H */