--- /dev/null
+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.
base64
dns
flowlib
+ flowint
flowvar
hashlib
http
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 \
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 \
#include "detect.h"
#include "flow.h"
-#include "flow-var.h"
#include "util-debug.h"
#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"
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;
*/
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");
/** \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"
/** \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"
/** \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"
/** \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"
/** \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"
/** \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"
#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"
{ "suricata.dnp3", SCLuaLoadDnp3Lib },
{ "suricata.dns", SCLuaLoadDnsLib },
{ "suricata.flow", LuaLoadFlowLib },
+ { "suricata.flowint", LuaLoadFlowintLib },
{ "suricata.flowvar", LuaLoadFlowvarLib },
{ "suricata.hashlib", SCLuaLoadHashlib },
{ "suricata.http", SCLuaLoadHttpLib },
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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 */