]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
lua: convert lua flowint functions to lib: suricata.flowintlib
authorJason Ish <jason.ish@oisf.net>
Thu, 1 May 2025 22:33:19 +0000 (16:33 -0600)
committerJason Ish <jason.ish@oisf.net>
Fri, 2 May 2025 15:32:23 +0000 (09:32 -0600)
Ticket: #7487

doc/userguide/lua/libs/flowint.rst [new file with mode: 0644]
doc/userguide/lua/libs/index.rst
src/Makefile.am
src/detect-lua-extensions.c
src/detect-lua.c
src/util-lua-builtins.c
src/util-lua-flowintlib.c [new file with mode: 0644]
src/util-lua-flowintlib.h [new file with mode: 0644]

diff --git a/doc/userguide/lua/libs/flowint.rst b/doc/userguide/lua/libs/flowint.rst
new file mode 100644 (file)
index 0000000..a5f66ac
--- /dev/null
@@ -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.
index 2279b74175517735b13a3aac646df8fd1a390973..ba7e6723600584b705fddc0fb9abbbd6df42a173 100644 (file)
@@ -11,6 +11,7 @@ environment without access to additional modules.
    base64
    dns
    flowlib
+   flowint
    flowvar
    hashlib
    http
index 839e323d74ef57155a0e00c7bd762c9b436c5ee3..193fe49ced6cc2c0c88d4ad4be54b991837154d5 100755 (executable)
@@ -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 \
index e92ca9c3bfe510d9d5b6b41679dbd06366eed7f6..c793828de8c75159b65e6b889071e07f75c2ec85 100644 (file)
@@ -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");
 
index 2854ae5cec154f22c6195ce9f4aaabbd70425979..049142471add388514df9c1c7611a6bb2c697e36 100644 (file)
@@ -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"
index 7a51749f4b9a3bd6d940aba510c29c4e32ad2063..c0898e28eafc2c204a9b9950bdcdbba273f4ee2d 100644 (file)
@@ -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 (file)
index 0000000..ff42d08
--- /dev/null
@@ -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 (file)
index 0000000..9dcfc1a
--- /dev/null
@@ -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 */