New Lua lib, "suricata.flowvar" for working with flowvars from Lua.
Replaces functions:
- SCFlowvarGet (and ScFlowvarGet)
- SCFlowvarSet (and SCFlowvarSet)
Of note, the DetectLuaData has been made available to the init and
thread_init methods, instead of just the match. This is due to an
issue that if a flow variable is not registered in init, it will not
be logged, registering in thread_init is too late.
Ticket: #7486
--- /dev/null
+Flowvar
+#######
+
+The ``suricata.flowvar`` library exposes flow variables to Lua
+scripts.
+
+Initialization
+--------------
+
+First, the ``flowvar`` lib module must be loaded::
+
+ local flowvarlib = require("suricata.flow")
+
+Then in the ``init`` method, any flow variables used in the script
+should be registered. This is optional and could be skipped if you
+know for sure the flow variable will be registered by some other
+means.
+
+Example::
+
+ local flowvarlib = require("suricata.flow")
+
+ function init ()
+ flowvarlib.register("count")
+ return {}
+ end
+
+Finally, in the ``thread_init`` function a handle is acquired for the
+flow variables and stored as a global::
+
+ function thread_init ()
+ count_flow_var = flowvarlib.get("count")
+ end
+
+Flow Variable Methods
+---------------------
+
+``value()``
+^^^^^^^^^^^
+
+Get the current value of the flow variable as a string. Note that
+``nil`` may be returned if the flow variable does not have a value.
+
+``set(value, len)``
+^^^^^^^^^^^^^^^^^^^
+
+Set the value of the flow variable to the value provided. The length
+of the value must also be provided.
+
+Example
+-------
+
+::
+
+ local flowvarlib = require("suricata.flowvar")
+
+ function init ()
+ flowvarlib.register("count")
+ return {}
+ end
+
+ function thread_init ()
+ count_var = flowvarlib.get("count")
+ end
+
+ function match ()
+ local value = count_var:value()
+ if value == nil then
+ -- Initialize value to 1.
+ value = tostring(1)
+ count_var:set(value, #value)
+ else
+ value = tostring(tonumber(value) + 1)
+ count_var:set(value, #value)
+ fi
+
+ -- Return 1 or 0 based on your own logic.
+ return 1
+ end
base64
dns
flowlib
+ flowvar
hashlib
http
packetlib
Decrement Flowint at index given by the first parameter.
-SCFlowvarGet
-~~~~~~~~~~~~
-
-Get the Flowvar at index given by the parameter.
-
-SCFlowvarSet
-~~~~~~~~~~~~
-
-Set a Flowvar. First parameter is the index, second is the data
-and third is the length of data.
-
-You can use it to set string
-
-::
-
- function init (args)
- local needs = {}
- needs["http.request_headers"] = tostring(true)
- needs["flowvar"] = {"cnt"}
- return needs
- end
-
- function match(args)
- a = SCFlowvarGet(0);
- if a then
- a = tostring(tonumber(a)+1)
- SCFlowvarSet(0, a, #a)
- else
- a = tostring(1)
- SCFlowvarSet(0, a, #a)
- end
-
Misc
----
util-lua-dnp3-objects.h \
util-lua-dns.h \
util-lua-flowlib.h \
+ util-lua-flowvarlib.h \
util-lua.h \
util-lua-hashlib.h \
util-lua-hassh.h \
util-lua-dnp3-objects.c \
util-lua-dns.c \
util-lua-flowlib.c \
+ util-lua-flowvarlib.c \
util-lua-hashlib.c \
util-lua-hassh.c \
util-lua-http.c \
#include "util-lua-dnp3.h"
#include "detect-lua-extensions.h"
-static const char luaext_key_ld[] = "suricata:luadata";
+/* Lua registry key for DetectLuaData. */
+const char luaext_key_ld[] = "suricata:luadata";
static int GetLuaData(lua_State *luastate, DetectLuaData **ret_ld)
{
return 0;
}
-static int GetFlowVarById(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->flowvar[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 GetFlowVarByKey(lua_State *luastate, Flow *f, FlowVar **ret_fv)
-{
- *ret_fv = NULL;
-
- if (!lua_isstring(luastate, 1)) {
- LUA_ERROR("flowvar key not a string");
- }
- const char *keystr = lua_tostring(luastate, 1);
- if (keystr == NULL) {
- LUA_ERROR("key is null");
- }
- if (!lua_isnumber(luastate, 2)) {
- LUA_ERROR("key length not specified");
- }
- int keylen = lua_tonumber(luastate, 2);
- if (keylen < 0 || keylen > 0xff) {
- LUA_ERROR("key len out of range: max 256");
- }
-
- FlowVar *fv = FlowVarGetByKey(f, (const uint8_t *)keystr, (uint8_t)keylen);
- if (fv == NULL) {
- LUA_ERROR("no flow var");
- }
- *ret_fv = fv;
- return 0;
-}
-
static int GetFlowIntById(lua_State *luastate, Flow *f,
FlowVar **ret_fv, bool fv_may_be_null, uint32_t *ret_idx)
{
return 0;
}
-static int LuaGetFlowvar(lua_State *luastate)
-{
- Flow *f;
- FlowVar *fv;
- int ret;
-
- /* need flow */
- ret = GetFlow(luastate, &f);
- if (ret != 0)
- return ret;
-
- if (lua_isnumber(luastate, 1)) {
- ret = GetFlowVarById(luastate, f, &fv, false, NULL);
- if (ret != 0 || fv == NULL)
- return ret;
- } else if (lua_isstring(luastate, 1)) {
- ret = GetFlowVarByKey(luastate, f, &fv);
- if (ret != 0 || fv == NULL)
- return ret;
- } else {
- LUA_ERROR("invalid data type as first argument");
- }
-
- LuaPushStringBuffer(luastate,
- (const uint8_t *)fv->data.fv_str.value,
- (size_t)fv->data.fv_str.value_len);
- return 1;
-}
-
-static int LuaSetFlowvarById(lua_State *luastate)
-{
- uint32_t idx = 0;
- Flow *f;
- const char *str;
- int len;
- uint8_t *buffer;
- FlowVar *fv = NULL;
-
- /* need flow */
- int ret = GetFlow(luastate, &f);
- if (ret != 0)
- return ret;
-
- ret = GetFlowVarById(luastate, f, &fv, true, &idx);
- if (ret != 0)
- return ret;
-
- if (!lua_isstring(luastate, 2)) {
- LUA_ERROR("buffer not a string");
- }
- str = lua_tostring(luastate, 2);
- if (str == NULL) {
- LUA_ERROR("buffer is null");
- }
-
- if (!lua_isnumber(luastate, 3)) {
- LUA_ERROR("buffer length not specified");
- }
- len = lua_tonumber(luastate, 3);
- if (len < 0 || len > 0xffff) {
- LUA_ERROR("len out of range: max 64k");
- }
-
- buffer = SCMalloc(len+1);
- if (unlikely(buffer == NULL)) {
- LUA_ERROR("out of memory");
- }
- memcpy(buffer, str, len);
- buffer[len] = '\0';
-
- FlowVarAddIdValue(f, idx, buffer, (uint16_t)len);
- return 0;
-}
-
-static int LuaSetFlowvarByKey(lua_State *luastate)
-{
- Flow *f;
- const char *str;
- int len;
- uint8_t *buffer;
-
- /* need flow */
- int ret = GetFlow(luastate, &f);
- if (ret != 0)
- return ret;
-
- const char *keystr = NULL;
- int keylen = 0;
-
- keystr = lua_tostring(luastate, 1);
- if (keystr == NULL) {
- LUA_ERROR("key is null");
- }
- if (!lua_isnumber(luastate, 2)) {
- LUA_ERROR("key length not specified");
- }
- keylen = lua_tonumber(luastate, 2);
- if (keylen < 0 || keylen > 0xff) {
- LUA_ERROR("key len out of range: max 255");
- }
-
- if (!lua_isstring(luastate, 3)) {
- LUA_ERROR("buffer not a string");
- }
- str = lua_tostring(luastate, 3);
- if (str == NULL) {
- LUA_ERROR("buffer is null");
- }
-
- if (!lua_isnumber(luastate, 4)) {
- LUA_ERROR("buffer length not specified");
- }
- len = lua_tonumber(luastate, 4);
- if (len < 0 || len > 0xffff) {
- LUA_ERROR("len out of range: max 64k");
- }
-
- buffer = SCMalloc(len+1);
- if (unlikely(buffer == NULL)) {
- LUA_ERROR("out of memory");
- }
- memcpy(buffer, str, len);
- buffer[len] = '\0';
-
- uint8_t *keybuf = SCMalloc(keylen+1);
- if (unlikely(keybuf == NULL)) {
- SCFree(buffer);
- LUA_ERROR("out of memory");
- }
- memcpy(keybuf, keystr, keylen);
- keybuf[keylen] = '\0';
- FlowVarAddKeyValue(f, keybuf, (uint8_t)keylen, buffer, (uint16_t)len);
-
- return 0;
-}
-
-static int LuaSetFlowvar(lua_State *luastate)
-{
- if (lua_isnumber(luastate, 1)) {
- return LuaSetFlowvarById(luastate);
- } else {
- return LuaSetFlowvarByKey(luastate);
- }
-}
-
static int LuaGetFlowint(lua_State *luastate)
{
Flow *f;
{
SCLogDebug("det_ctx %p, f %p", det_ctx, f);
- /* lua keyword data */
- lua_pushlightuserdata(lua_state, (void *)&luaext_key_ld);
- lua_pushlightuserdata(lua_state, (void *)ld);
- lua_settable(lua_state, LUA_REGISTRYINDEX);
-
LuaStateSetSignature(lua_state, s);
-
LuaStateSetFlow(lua_state, f);
LuaStateSetDetCtx(lua_state, det_ctx);
*/
int LuaRegisterExtensions(lua_State *lua_state)
{
- lua_pushcfunction(lua_state, LuaGetFlowvar);
- lua_setglobal(lua_state, "ScFlowvarGet");
-
- lua_pushcfunction(lua_state, LuaGetFlowvar);
- lua_setglobal(lua_state, "SCFlowvarGet");
-
- lua_pushcfunction(lua_state, LuaSetFlowvar);
- lua_setglobal(lua_state, "ScFlowvarSet");
-
- lua_pushcfunction(lua_state, LuaSetFlowvar);
- lua_setglobal(lua_state, "SCFlowvarSet");
-
lua_pushcfunction(lua_state, LuaGetFlowint);
lua_setglobal(lua_state, "ScFlowintGet");
#ifndef SURICATA_DETECT_LUA_EXT_H
#define SURICATA_DETECT_LUA_EXT_H
+extern const char luaext_key_ld[];
+
int LuaRegisterExtensions(lua_State *);
void LuaExtensionsMatchSetup(lua_State *lua_state, DetectLuaData *, DetectEngineThreadCtx *det_ctx,
}
}
+static void LuaStateSetDetectLuaData(lua_State *state, DetectLuaData *data)
+{
+ lua_pushlightuserdata(state, (void *)&luaext_key_ld);
+ lua_pushlightuserdata(state, (void *)data);
+ lua_settable(state, LUA_REGISTRYINDEX);
+}
+
/**
* \brief Common function to run the Lua match function and process
* the return value.
}
LuaRegisterExtensions(t->luastate);
+ LuaStateSetDetectLuaData(t->luastate, lua);
/* hackish, needed to allow unittests to pass buffers as scripts instead of files */
#ifdef UNITTESTS
} else {
SCLuaSbLoadLibs(luastate);
}
+ LuaStateSetDetectLuaData(luastate, ld);
/* hackish, needed to allow unittests to pass buffers as scripts instead of files */
#ifdef UNITTESTS
{
SCConfSetFinal("security.lua.allow-rules", "true");
- const char script[] = "function init (args)\n"
- " local needs = {}\n"
- " needs[\"flowvar\"] = {\"cnt\"}\n"
- " return needs\n"
+ const char script[] = "local flowvarlib = require(\"suricata.flowvar\")\n"
+ "function init (args)\n"
+ " flowvarlib.register(\"cnt\")\n"
+ " return {}\n"
+ "end\n"
+ "function thread_init (args)\n"
+ " cnt = flowvarlib.get(\"cnt\")\n"
"end\n"
"\n"
"function match(args)\n"
- " a = ScFlowvarGet(0)\n"
+ " a = cnt:value()\n"
" if a then\n"
" a = tostring(tonumber(a)+1)\n"
" print (a)\n"
- " ScFlowvarSet(0, a, #a)\n"
+ " cnt:set(a, #a)\n"
" else\n"
" a = tostring(1)\n"
" print (a)\n"
- " ScFlowvarSet(0, a, #a)\n"
+ " cnt:set(a, #a)\n"
" end\n"
" \n"
" print (\"pre check: \" .. (a))\n"
static int LuaMatchTest01a(void)
{
- const char script[] = "function init (args)\n"
- " local needs = {}\n"
- " needs[\"flowvar\"] = {\"cnt\"}\n"
- " return needs\n"
+ const char script[] = "local flowvarlib = require(\"suricata.flowvar\")\n"
+ "function init (args)\n"
+ " flowvarlib.register(\"cnt\")\n"
+ " return {}\n"
+ "end\n"
+ "function thread_init (args)\n"
+ " cnt = flowvarlib.get(\"cnt\")\n"
"end\n"
"\n"
"function match(args)\n"
- " a = SCFlowvarGet(0)\n"
+ " a = cnt:value(0)\n"
" if a then\n"
" a = tostring(tonumber(a)+1)\n"
" print (a)\n"
- " SCFlowvarSet(0, a, #a)\n"
+ " cnt:set(a, #a)\n"
" else\n"
" a = tostring(1)\n"
" print (a)\n"
- " SCFlowvarSet(0, a, #a)\n"
+ " cnt:set(a, #a)\n"
" end\n"
" \n"
" print (\"pre check: \" .. (a))\n"
/** \test payload buffer */
static int LuaMatchTest02(void)
{
- const char script[] = "function init (args)\n"
+ const char script[] = "local flowvarlib = require(\"suricata.flowvar\")\n"
+ "function init (args)\n"
+ " flowvarlib.register(\"cnt\")\n"
" local needs = {}\n"
" needs[\"payload\"] = tostring(true)\n"
- " needs[\"flowvar\"] = {\"cnt\"}\n"
" return needs\n"
"end\n"
+ "function thread_init (args)\n"
+ " cnt = flowvarlib.get(\"cnt\")\n"
+ "end\n"
"\n"
"function match(args)\n"
- " a = ScFlowvarGet(0)\n"
+ " a = cnt:value()\n"
" if a then\n"
" a = tostring(tonumber(a)+1)\n"
" print (a)\n"
- " ScFlowvarSet(0, a, #a)\n"
+ " cnt:set(a, #a)\n"
" else\n"
" a = tostring(1)\n"
" print (a)\n"
- " ScFlowvarSet(0, a, #a)\n"
+ " cnt:set(a, #a)\n"
" end\n"
" \n"
" print (\"pre check: \" .. (a))\n"
/** \test payload buffer */
static int LuaMatchTest02a(void)
{
- const char script[] = "function init (args)\n"
+ const char script[] = "local flowvarlib = require(\"suricata.flowvar\")\n"
+ "function init (args)\n"
+ " flowvarlib.register(\"cnt\")"
" local needs = {}\n"
" needs[\"payload\"] = tostring(true)\n"
- " needs[\"flowvar\"] = {\"cnt\"}\n"
" return needs\n"
"end\n"
+ "function thread_init (args)\n"
+ " cnt = flowvarlib.get(\"cnt\")"
+ "end\n"
"\n"
"function match(args)\n"
- " a = SCFlowvarGet(0)\n"
+ " a = cnt:value()\n"
" if a then\n"
" a = tostring(tonumber(a)+1)\n"
" print (a)\n"
- " SCFlowvarSet(0, a, #a)\n"
+ " cnt:set(a, #a)\n"
" else\n"
" a = tostring(1)\n"
" print (a)\n"
- " SCFlowvarSet(0, a, #a)\n"
+ " cnt:set(a, #a)\n"
" end\n"
" \n"
" print (\"pre check: \" .. (a))\n"
/** \test packet buffer */
static int LuaMatchTest03(void)
{
- const char script[] = "function init (args)\n"
+ const char script[] = "local flowvarlib = require(\"suricata.flowvar\")\n"
+ "function init (args)\n"
+ " flowvarlib.register(\"cnt\")\n"
" local needs = {}\n"
" needs[\"packet\"] = tostring(true)\n"
- " needs[\"flowvar\"] = {\"cnt\"}\n"
" return needs\n"
"end\n"
"\n"
+ "function thread_init (args)\n"
+ " cnt = flowvarlib.get(\"cnt\")\n"
+ "end\n"
+ "\n"
"function match(args)\n"
- " a = ScFlowvarGet(0)\n"
+ " a = cnt:value()\n"
" if a then\n"
" a = tostring(tonumber(a)+1)\n"
" print (a)\n"
- " ScFlowvarSet(0, a, #a)\n"
+ " cnt:set(a, #a)\n"
" else\n"
" a = tostring(1)\n"
" print (a)\n"
- " ScFlowvarSet(0, a, #a)\n"
+ " cnt:set(a, #a)\n"
" end\n"
" \n"
" print (\"pre check: \" .. (a))\n"
/** \test packet buffer */
static int LuaMatchTest03a(void)
{
- const char script[] = "function init (args)\n"
+ const char script[] = "local flowvarlib = require(\"suricata.flowvar\")\n"
+ "function init (args)\n"
+ " flowvarlib.register(\"cnt\")\n"
" local needs = {}\n"
" needs[\"packet\"] = tostring(true)\n"
- " needs[\"flowvar\"] = {\"cnt\"}\n"
" return needs\n"
"end\n"
"\n"
+ "function thread_init (args)\n"
+ " cnt = flowvarlib.get(\"cnt\")\n"
+ "end\n"
+ "\n"
"function match(args)\n"
- " a = SCFlowvarGet(0)\n"
+ " a = cnt:value()\n"
" if a then\n"
" a = tostring(tonumber(a)+1)\n"
" print (a)\n"
- " SCFlowvarSet(0, a, #a)\n"
+ " cnt:set(a, #a)\n"
" else\n"
" a = tostring(1)\n"
" print (a)\n"
- " SCFlowvarSet(0, a, #a)\n"
+ " cnt:set(a, #a)\n"
" end\n"
" \n"
" print (\"pre check: \" .. (a))\n"
#include "util-lua-base64lib.h"
#include "util-lua-dataset.h"
#include "util-lua-dnp3.h"
+#include "util-lua-flowvarlib.h"
#include "util-lua-http.h"
#include "util-lua-dns.h"
#include "util-lua-ssh.h"
{ "suricata.dnp3", SCLuaLoadDnp3Lib },
{ "suricata.dns", SCLuaLoadDnsLib },
{ "suricata.flow", LuaLoadFlowLib },
+ { "suricata.flowvar", LuaLoadFlowvarLib },
{ "suricata.hashlib", SCLuaLoadHashlib },
{ "suricata.http", SCLuaLoadHttpLib },
{ "suricata.packet", LuaLoadPacketLib },
--- /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-flowvarlib.h"
+#include "util-lua.h"
+#include "util-var-name.h"
+#include "detect-lua.h"
+#include "detect-lua-extensions.h"
+
+static const char suricata_flowvar_mt[] = "suricata:flowvar: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 LuaFlowvarRegister(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_VAR);
+ if (*flowvar_id == 0) {
+ return luaL_error(L, "failed to register flowvar");
+ }
+ ld->flowvar[ld->flowvars++] = *flowvar_id;
+
+ luaL_getmetatable(L, suricata_flowvar_mt);
+ lua_setmetatable(L, -2);
+
+ return 1;
+}
+
+static int LuaFlowvarGet(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_VAR);
+ if (*flowvar_id == 0) {
+ return luaL_error(L, "flowvar does not exist");
+ }
+
+ luaL_getmetatable(L, suricata_flowvar_mt);
+ lua_setmetatable(L, -2);
+
+ return 1;
+}
+
+static int LuaFlowvarValue(lua_State *L)
+{
+ uint32_t *flowvar_id = luaL_testudata(L, 1, suricata_flowvar_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 {
+ LuaPushStringBuffer(
+ L, (const uint8_t *)fv->data.fv_str.value, (size_t)fv->data.fv_str.value_len);
+ }
+ return 1;
+}
+
+static int LuaFlowvarSet(lua_State *L)
+{
+ const char *value = luaL_checkstring(L, 2);
+ const int len = luaL_checkinteger(L, 3);
+ uint32_t *flowvar_id = luaL_checkudata(L, 1, suricata_flowvar_mt);
+ Flow *f = LuaStateGetFlow(L);
+ if (f == NULL) {
+ return luaL_error(L, "no flow");
+ }
+
+ uint8_t *buf = SCMalloc(len + 1);
+ memcpy(buf, value, len);
+ buf[len] = '\0';
+ FlowVarAddIdValue(f, *flowvar_id, buf, (uint16_t)len);
+
+ return 1;
+}
+
+static const luaL_Reg flowvarlib[] = {
+ // clang-format off
+ { "register", LuaFlowvarRegister, },
+{ "get", LuaFlowvarGet },
+ { NULL, NULL, },
+ // clang-format on
+};
+
+static const luaL_Reg flowvarmt[] = {
+ // clang-format off
+ { "value", LuaFlowvarValue, },
+ { "set", LuaFlowvarSet, },
+ { NULL, NULL, },
+ // clang-format on
+};
+
+int LuaLoadFlowvarLib(lua_State *L)
+{
+ luaL_newmetatable(L, suricata_flowvar_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_FLOWVARLIB_H
+#define SURICATA_UTIL_LUA_FLOWVARLIB_H
+
+#include "lua.h"
+
+int LuaLoadFlowvarLib(lua_State *L);
+
+#endif /* SURICATA_UTIL_LUA_FLOWVARLIB_H */