Coverage Report

Created: 2026-09-28 07:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata8/src/util-lua-flowlib.c
Line
Count
Source
1
/* Copyright (C) 2025 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17
18
/**
19
 * \file
20
 *
21
 * Flow API fow Lua.
22
 *
23
 * local flow = require("suricata.flow")
24
 */
25
26
#include "suricata-common.h"
27
28
#include "util-lua-flowlib.h"
29
30
#include "app-layer-protos.h" /* Required by util-lua-common. */
31
#include "util-lua-common.h"
32
#include "util-lua.h"
33
#include "util-debug.h"
34
#include "util-print.h"
35
36
/* key for f (flow) pointer */
37
extern const char lua_ext_key_f[];
38
static const char suricata_flow[] = "suricata:flow";
39
40
struct LuaFlow {
41
    Flow *f;
42
};
43
44
static int LuaFlowGC(lua_State *luastate)
45
0
{
46
0
    SCLogDebug("gc:start");
47
0
    struct LuaFlow *s = (struct LuaFlow *)luaL_testudata(luastate, 1, suricata_flow);
48
0
    if (s != NULL) {
49
0
        SCLogDebug("flow %p", s->f);
50
0
        s->f = NULL;
51
0
    }
52
0
    SCLogDebug("gc:done");
53
0
    return 0;
54
0
}
55
56
/** \internal
57
 *  \brief fill lua stack with flow id
58
 *  \param luastate the lua state
59
 *  \retval cnt number of data items placed on the stack
60
 *
61
 *  Places: flow id (number)
62
 */
63
static int LuaFlowId(lua_State *luastate)
64
0
{
65
0
    struct LuaFlow *s = (struct LuaFlow *)luaL_checkudata(luastate, 1, suricata_flow);
66
0
    if (s->f == NULL) {
67
0
        LUA_ERROR("failed to get flow");
68
0
    }
69
70
0
    Flow *f = s->f;
71
72
0
    int64_t id = (int64_t)FlowGetId(f);
73
0
    lua_pushinteger(luastate, id);
74
0
    return 1;
75
0
}
76
77
/** \internal
78
 *  \brief fill lua stack with AppLayerProto
79
 *  \param luastate the lua state
80
 *  \retval cnt number of data items placed on the stack
81
 *
82
 *  Places: alproto as string (string), alproto_ts as string (string),
83
 *          alproto_tc as string (string), alproto_orig as string (string),
84
 *          alproto_expect as string (string)
85
 */
86
static int LuaFlowAppLayerProto(lua_State *luastate)
87
0
{
88
0
    struct LuaFlow *s = (struct LuaFlow *)luaL_checkudata(luastate, 1, suricata_flow);
89
0
    if (s->f == NULL) {
90
0
        LUA_ERROR("failed to get flow");
91
0
    }
92
93
0
    Flow *f = s->f;
94
0
    lua_pushstring(luastate, AppProtoToString(f->alproto));
95
0
    lua_pushstring(luastate, AppProtoToString(f->alproto_ts));
96
0
    lua_pushstring(luastate, AppProtoToString(f->alproto_tc));
97
0
    lua_pushstring(luastate, AppProtoToString(f->alproto_orig));
98
0
    lua_pushstring(luastate, AppProtoToString(f->alproto_expect));
99
0
    return 5;
100
0
}
101
102
/** \internal
103
 *  \brief fill lua stack with flow has alerts
104
 *  \param luastate the lua state
105
 *  \retval cnt number of data items placed on the stack
106
 *
107
 *  Places: alerts (bool)
108
 */
109
static int LuaFlowHasAlerts(lua_State *luastate)
110
0
{
111
0
    struct LuaFlow *s = (struct LuaFlow *)luaL_checkudata(luastate, 1, suricata_flow);
112
0
    if (s->f == NULL) {
113
0
        LUA_ERROR("failed to get flow");
114
0
    }
115
116
0
    Flow *f = s->f;
117
0
    lua_pushboolean(luastate, FlowHasAlerts(f));
118
0
    return 1;
119
0
}
120
121
/** \internal
122
 *  \brief fill lua stack with flow stats
123
 *  \param luastate the lua state
124
 *  \retval cnt number of data items placed on the stack
125
 *
126
 *  Places: ts pkts (number), ts bytes (number), tc pkts (number), tc bytes (number)
127
 */
128
static int LuaFlowStats(lua_State *luastate)
129
0
{
130
0
    struct LuaFlow *s = (struct LuaFlow *)luaL_checkudata(luastate, 1, suricata_flow);
131
0
    if (s->f == NULL) {
132
0
        LUA_ERROR("failed to get flow");
133
0
    }
134
135
0
    Flow *f = s->f;
136
0
    lua_pushinteger(luastate, f->todstpktcnt);
137
0
    lua_pushinteger(luastate, f->todstbytecnt);
138
0
    lua_pushinteger(luastate, f->tosrcpktcnt);
139
0
    lua_pushinteger(luastate, f->tosrcbytecnt);
140
0
    return 4;
141
0
}
142
143
/** \internal
144
 *  \brief fill lua stack with flow timestamps
145
 *  \param luastate the lua state
146
 *  \retval cnt number of data items placed on the stack
147
 *
148
 *  Places: seconds (number), seconds (number), microseconds (number),
149
 *          microseconds (number)
150
 */
151
static int LuaFlowTimestamps(lua_State *luastate)
152
0
{
153
0
    struct LuaFlow *s = (struct LuaFlow *)luaL_checkudata(luastate, 1, suricata_flow);
154
0
    if (s->f == NULL) {
155
0
        LUA_ERROR("failed to get flow");
156
0
    }
157
158
0
    Flow *f = s->f;
159
0
    lua_pushnumber(luastate, (double)SCTIME_SECS(f->startts));
160
0
    lua_pushnumber(luastate, (double)SCTIME_SECS(f->lastts));
161
0
    lua_pushnumber(luastate, (double)SCTIME_USECS(f->startts));
162
0
    lua_pushnumber(luastate, (double)SCTIME_USECS(f->lastts));
163
0
    return 4;
164
0
}
165
166
static int LuaFlowTimestringIso8601(lua_State *luastate)
167
0
{
168
0
    struct LuaFlow *s = (struct LuaFlow *)luaL_checkudata(luastate, 1, suricata_flow);
169
0
    if (s->f == NULL) {
170
0
        LUA_ERROR("failed to get flow");
171
0
    }
172
173
0
    Flow *f = s->f;
174
0
    char timebuf[64];
175
0
    CreateIsoTimeString(f->startts, timebuf, sizeof(timebuf));
176
0
    lua_pushstring(luastate, timebuf);
177
0
    return 1;
178
0
}
179
180
/** \internal
181
 *  \brief legacy format as used by fast.log, http.log, etc.
182
 */
183
static int LuaFlowTimestringLegacy(lua_State *luastate)
184
0
{
185
0
    struct LuaFlow *s = (struct LuaFlow *)luaL_checkudata(luastate, 1, suricata_flow);
186
0
    if (s->f == NULL) {
187
0
        LUA_ERROR("failed to get flow");
188
0
    }
189
190
0
    Flow *f = s->f;
191
0
    char timebuf[64];
192
0
    CreateTimeString(f->startts, timebuf, sizeof(timebuf));
193
0
    lua_pushstring(luastate, timebuf);
194
0
    return 1;
195
0
}
196
197
/** \internal
198
 *  \brief fill lua stack with header info
199
 *  \param luastate the lua state
200
 *  \retval cnt number of data items placed on the stack
201
 *
202
 *  Places: ipver (number), src ip (string), dst ip (string), protocol (number),
203
 *          sp or icmp type (number), dp or icmp code (number).
204
 */
205
static int LuaFlowTuple(lua_State *luastate)
206
0
{
207
0
    struct LuaFlow *s = (struct LuaFlow *)luaL_checkudata(luastate, 1, suricata_flow);
208
0
    if (s->f == NULL) {
209
0
        LUA_ERROR("failed to get flow");
210
0
    }
211
0
    Flow *f = s->f;
212
0
    int ipver = 0;
213
0
    if (FLOW_IS_IPV4(f)) {
214
0
        ipver = 4;
215
0
    } else if (FLOW_IS_IPV6(f)) {
216
0
        ipver = 6;
217
0
    }
218
0
    lua_pushinteger(luastate, ipver);
219
0
    if (ipver == 0)
220
0
        return 1;
221
222
0
    char srcip[46] = "", dstip[46] = "";
223
0
    if (FLOW_IS_IPV4(f)) {
224
0
        PrintInet(AF_INET, (const void *)&(f->src.addr_data32[0]), srcip, sizeof(srcip));
225
0
        PrintInet(AF_INET, (const void *)&(f->dst.addr_data32[0]), dstip, sizeof(dstip));
226
0
    } else if (FLOW_IS_IPV6(f)) {
227
0
        PrintInet(AF_INET6, (const void *)&(f->src.address), srcip, sizeof(srcip));
228
0
        PrintInet(AF_INET6, (const void *)&(f->dst.address), dstip, sizeof(dstip));
229
0
    }
230
231
0
    lua_pushstring(luastate, srcip);
232
0
    lua_pushstring(luastate, dstip);
233
234
    /* proto and ports (or type/ code) */
235
0
    lua_pushinteger(luastate, f->proto);
236
0
    if (f->proto == IPPROTO_TCP || f->proto == IPPROTO_UDP) {
237
0
        lua_pushinteger(luastate, f->sp);
238
0
        lua_pushinteger(luastate, f->dp);
239
0
    } else if (f->proto == IPPROTO_ICMP || f->proto == IPPROTO_ICMPV6) {
240
0
        lua_pushinteger(luastate, f->icmp_s.type);
241
0
        lua_pushinteger(luastate, f->icmp_s.code);
242
0
    } else {
243
0
        lua_pushinteger(luastate, 0);
244
0
        lua_pushinteger(luastate, 0);
245
0
    }
246
0
    return 6;
247
0
}
248
249
static int LuaFlowGet(lua_State *luastate)
250
0
{
251
0
    Flow *f = LuaStateGetFlow(luastate);
252
0
    if (f == NULL) {
253
0
        LUA_ERROR("failed to get flow");
254
0
    }
255
256
0
    struct LuaFlow *s = (struct LuaFlow *)lua_newuserdata(luastate, sizeof(*s));
257
0
    if (s == NULL) {
258
0
        LUA_ERROR("failed to allocate userdata");
259
0
    }
260
0
    s->f = f;
261
0
    luaL_getmetatable(luastate, suricata_flow);
262
0
    lua_setmetatable(luastate, -2);
263
0
    return 1;
264
0
}
265
266
static const luaL_Reg flowlib[] = {
267
    // clang-format off
268
    { "get", LuaFlowGet },
269
    { NULL, NULL }
270
    // clang-format on
271
};
272
273
static const luaL_Reg flowlib_meta[] = {
274
    // clang-format off
275
    { "id", LuaFlowId },
276
    { "app_layer_proto", LuaFlowAppLayerProto },
277
    { "has_alerts", LuaFlowHasAlerts },
278
    { "stats", LuaFlowStats },
279
    { "timestamps", LuaFlowTimestamps },
280
    { "timestring_iso8601", LuaFlowTimestringIso8601 },
281
    { "timestring_legacy", LuaFlowTimestringLegacy },
282
    { "tuple", LuaFlowTuple },
283
    { "__gc", LuaFlowGC },
284
    { NULL, NULL }
285
    // clang-format on
286
};
287
288
int LuaLoadFlowLib(lua_State *luastate)
289
0
{
290
0
    luaL_newmetatable(luastate, suricata_flow);
291
0
    lua_pushvalue(luastate, -1);
292
0
    lua_setfield(luastate, -2, "__index");
293
0
    luaL_setfuncs(luastate, flowlib_meta, 0);
294
295
0
    luaL_newlib(luastate, flowlib);
296
0
    return 1;
297
0
}