suricata
util-lua-flowlib.c
Go to the documentation of this file.
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 static const char suricata_flow[] = "suricata:flow";
37 
38 /* no stored pointer: the accessors act on the flow the state is
39  * running on (LuaStateGetFlow), so a retained handle can never
40  * dangle */
41 struct LuaFlow {
42 };
43 
44 /** \internal
45  * \brief fill lua stack with flow id
46  * \param luastate the lua state
47  * \retval cnt number of data items placed on the stack
48  *
49  * Places: flow id (number)
50  */
51 static int LuaFlowId(lua_State *luastate)
52 {
53  SCLuaCheckUserData(luastate, 1, suricata_flow, SCLUA_TUD_FLOW);
54  Flow *f = LuaStateGetFlow(luastate);
55  if (f == NULL) {
56  LUA_ERROR("failed to get flow");
57  }
58 
59  int64_t id = (int64_t)FlowGetId(f);
60  lua_pushinteger(luastate, id);
61  return 1;
62 }
63 
64 static void PushAlprotoString(lua_State *luastate, AppProto alproto)
65 {
66  const char *s = AppProtoToString(alproto);
67  lua_pushstring(luastate, s ? s : "unknown");
68 }
69 
70 /** \internal
71  * \brief fill lua stack with AppLayerProto
72  * \param luastate the lua state
73  * \retval cnt number of data items placed on the stack
74  *
75  * Places: alproto as string (string), alproto_ts as string (string),
76  * alproto_tc as string (string), alproto_orig as string (string),
77  * alproto_expect as string (string)
78  */
79 static int LuaFlowAppLayerProto(lua_State *luastate)
80 {
81  SCLuaCheckUserData(luastate, 1, suricata_flow, SCLUA_TUD_FLOW);
82  Flow *f = LuaStateGetFlow(luastate);
83  if (f == NULL) {
84  LUA_ERROR("failed to get flow");
85  }
86  /* unknown alprotos have no name: report "unknown" as documented */
87  PushAlprotoString(luastate, f->alproto);
88  PushAlprotoString(luastate, f->alproto_ts);
89  PushAlprotoString(luastate, f->alproto_tc);
90  PushAlprotoString(luastate, f->alproto_orig);
91  PushAlprotoString(luastate, f->alproto_expect);
92  return 5;
93 }
94 
95 /** \internal
96  * \brief fill lua stack with flow has alerts
97  * \param luastate the lua state
98  * \retval cnt number of data items placed on the stack
99  *
100  * Places: alerts (bool)
101  */
102 static int LuaFlowHasAlerts(lua_State *luastate)
103 {
104  SCLuaCheckUserData(luastate, 1, suricata_flow, SCLUA_TUD_FLOW);
105  Flow *f = LuaStateGetFlow(luastate);
106  if (f == NULL) {
107  LUA_ERROR("failed to get flow");
108  }
109  lua_pushboolean(luastate, FlowHasAlerts(f));
110  return 1;
111 }
112 
113 /** \internal
114  * \brief fill lua stack with flow stats
115  * \param luastate the lua state
116  * \retval cnt number of data items placed on the stack
117  *
118  * Places: ts pkts (number), ts bytes (number), tc pkts (number), tc bytes (number)
119  */
120 static int LuaFlowStats(lua_State *luastate)
121 {
122  SCLuaCheckUserData(luastate, 1, suricata_flow, SCLUA_TUD_FLOW);
123  Flow *f = LuaStateGetFlow(luastate);
124  if (f == NULL) {
125  LUA_ERROR("failed to get flow");
126  }
127  lua_pushinteger(luastate, f->todstpktcnt);
128  lua_pushinteger(luastate, f->todstbytecnt);
129  lua_pushinteger(luastate, f->tosrcpktcnt);
130  lua_pushinteger(luastate, f->tosrcbytecnt);
131  return 4;
132 }
133 
134 /** \internal
135  * \brief fill lua stack with flow timestamps
136  * \param luastate the lua state
137  * \retval cnt number of data items placed on the stack
138  *
139  * Places: seconds (number), seconds (number), microseconds (number),
140  * microseconds (number)
141  */
142 static int LuaFlowTimestamps(lua_State *luastate)
143 {
144  SCLuaCheckUserData(luastate, 1, suricata_flow, SCLUA_TUD_FLOW);
145  Flow *f = LuaStateGetFlow(luastate);
146  if (f == NULL) {
147  LUA_ERROR("failed to get flow");
148  }
149  lua_pushnumber(luastate, (double)SCTIME_SECS(f->startts));
150  lua_pushnumber(luastate, (double)SCTIME_SECS(f->lastts));
151  lua_pushnumber(luastate, (double)SCTIME_USECS(f->startts));
152  lua_pushnumber(luastate, (double)SCTIME_USECS(f->lastts));
153  return 4;
154 }
155 
156 static int LuaFlowTimestringIso8601(lua_State *luastate)
157 {
158  SCLuaCheckUserData(luastate, 1, suricata_flow, SCLUA_TUD_FLOW);
159  Flow *f = LuaStateGetFlow(luastate);
160  if (f == NULL) {
161  LUA_ERROR("failed to get flow");
162  }
163  char timebuf[64];
164  CreateIsoTimeString(f->startts, timebuf, sizeof(timebuf));
165  lua_pushstring(luastate, timebuf);
166  return 1;
167 }
168 
169 /** \internal
170  * \brief legacy format as used by fast.log, etc.
171  */
172 static int LuaFlowTimestringLegacy(lua_State *luastate)
173 {
174  SCLuaCheckUserData(luastate, 1, suricata_flow, SCLUA_TUD_FLOW);
175  Flow *f = LuaStateGetFlow(luastate);
176  if (f == NULL) {
177  LUA_ERROR("failed to get flow");
178  }
179  char timebuf[64];
180  CreateTimeString(f->startts, timebuf, sizeof(timebuf));
181  lua_pushstring(luastate, timebuf);
182  return 1;
183 }
184 
185 /** \internal
186  * \brief fill lua stack with header info
187  * \param luastate the lua state
188  * \retval cnt number of data items placed on the stack
189  *
190  * Places: ipver (number), src ip (string), dst ip (string), protocol (number),
191  * sp or icmp type (number), dp or icmp code (number).
192  */
193 static int LuaFlowTuple(lua_State *luastate)
194 {
195  SCLuaCheckUserData(luastate, 1, suricata_flow, SCLUA_TUD_FLOW);
196  Flow *f = LuaStateGetFlow(luastate);
197  if (f == NULL) {
198  LUA_ERROR("failed to get flow");
199  }
200  int ipver = 0;
201  if (FLOW_IS_IPV4(f)) {
202  ipver = 4;
203  } else if (FLOW_IS_IPV6(f)) {
204  ipver = 6;
205  }
206  lua_pushinteger(luastate, ipver);
207  if (ipver == 0)
208  return 1;
209 
210  char srcip[46] = "", dstip[46] = "";
211  if (FLOW_IS_IPV4(f)) {
212  PrintInet(AF_INET, (const void *)&(f->src.addr_data32[0]), srcip, sizeof(srcip));
213  PrintInet(AF_INET, (const void *)&(f->dst.addr_data32[0]), dstip, sizeof(dstip));
214  } else if (FLOW_IS_IPV6(f)) {
215  PrintInet(AF_INET6, (const void *)&(f->src.address), srcip, sizeof(srcip));
216  PrintInet(AF_INET6, (const void *)&(f->dst.address), dstip, sizeof(dstip));
217  }
218 
219  lua_pushstring(luastate, srcip);
220  lua_pushstring(luastate, dstip);
221 
222  /* proto and ports (or type/ code) */
223  lua_pushinteger(luastate, f->proto);
224  if (f->proto == IPPROTO_TCP || f->proto == IPPROTO_UDP) {
225  lua_pushinteger(luastate, f->sp);
226  lua_pushinteger(luastate, f->dp);
227  } else if (f->proto == IPPROTO_ICMP || f->proto == IPPROTO_ICMPV6) {
228  lua_pushinteger(luastate, f->icmp_s.type);
229  lua_pushinteger(luastate, f->icmp_s.code);
230  } else {
231  lua_pushinteger(luastate, 0);
232  lua_pushinteger(luastate, 0);
233  }
234  return 6;
235 }
236 
237 static int LuaFlowGet(lua_State *luastate)
238 {
239  Flow *f = LuaStateGetFlow(luastate);
240  if (f == NULL) {
241  LUA_ERROR("failed to get flow");
242  }
243 
244  struct LuaFlow *s = SCLuaNewUserData(luastate, SCLUA_TUD_FLOW, sizeof(*s));
245  if (s == NULL) {
246  LUA_ERROR("failed to allocate userdata");
247  }
248  luaL_getmetatable(luastate, suricata_flow);
249  lua_setmetatable(luastate, -2);
250  return 1;
251 }
252 
253 static const luaL_Reg flowlib[] = {
254  // clang-format off
255  { "get", LuaFlowGet },
256  { NULL, NULL }
257  // clang-format on
258 };
259 
260 static const luaL_Reg flowlib_meta[] = {
261  // clang-format off
262  { "id", LuaFlowId },
263  { "app_layer_proto", LuaFlowAppLayerProto },
264  { "has_alerts", LuaFlowHasAlerts },
265  { "stats", LuaFlowStats },
266  { "timestamps", LuaFlowTimestamps },
267  { "timestring_iso8601", LuaFlowTimestringIso8601 },
268  { "timestring_legacy", LuaFlowTimestringLegacy },
269  { "tuple", LuaFlowTuple },
270  { NULL, NULL }
271  // clang-format on
272 };
273 
274 int LuaLoadFlowLib(lua_State *luastate)
275 {
276  luaL_newmetatable(luastate, suricata_flow);
277  lua_pushvalue(luastate, -1);
278  lua_setfield(luastate, -2, "__index");
279  luaL_setfuncs(luastate, flowlib_meta, 0);
280 
281  luaL_newlib(luastate, flowlib);
282  return 1;
283 }
LUA_ERROR
#define LUA_ERROR(msg)
Definition: util-lua-common.h:195
FLOW_IS_IPV6
#define FLOW_IS_IPV6(f)
Definition: flow.h:175
util-lua-common.h
CreateIsoTimeString
void CreateIsoTimeString(const SCTime_t ts, char *str, size_t size)
Definition: util-time.c:209
Flow_::startts
SCTime_t startts
Definition: flow.h:498
Flow_::proto
uint8_t proto
Definition: flow.h:381
util-lua.h
AppProto
uint16_t AppProto
Definition: app-layer-protos.h:87
Flow_
Flow data structure.
Definition: flow.h:359
AppProtoToString
const char * AppProtoToString(AppProto alproto)
Maps the ALPROTO_*, to its normalized string equivalent.
Definition: app-layer-protos.c:53
Flow_::alproto_orig
AppProto alproto_orig
Definition: flow.h:461
Flow_::dp
Port dp
Definition: flow.h:375
lua_State
struct lua_State lua_State
Definition: suricata-common.h:527
Flow_::tosrcbytecnt
uint64_t tosrcbytecnt
Definition: flow.h:503
Flow_::dst
FlowAddress dst
Definition: flow.h:362
util-debug.h
Flow_::todstpktcnt
uint32_t todstpktcnt
Definition: flow.h:500
Flow_::lastts
SCTime_t lastts
Definition: flow.h:423
util-print.h
PrintInet
const char * PrintInet(int af, const void *src, char *dst, socklen_t size)
Definition: util-print.c:238
SCLuaCheckUserData
void * SCLuaCheckUserData(lua_State *L, int idx, const char *mt, uint32_t type_id)
Definition: util-lua-common.c:101
Flow_::todstbytecnt
uint64_t todstbytecnt
Definition: flow.h:502
FLOW_IS_IPV4
#define FLOW_IS_IPV4(f)
Definition: flow.h:173
util-lua-flowlib.h
Flow_::alproto_expect
AppProto alproto_expect
Definition: flow.h:464
LuaFlow
Definition: util-lua-flowlib.c:41
Flow_::src
FlowAddress src
Definition: flow.h:362
SCLuaNewUserData
void * SCLuaNewUserData(lua_State *L, uint32_t type_id, size_t size)
Definition: util-lua-common.c:42
LuaLoadFlowLib
int LuaLoadFlowLib(lua_State *luastate)
Definition: util-lua-flowlib.c:274
Flow_::icmp_s
struct Flow_::@126::@132 icmp_s
suricata-common.h
SCTIME_SECS
#define SCTIME_SECS(t)
Definition: util-time.h:57
SCLUA_TUD_FLOW
@ SCLUA_TUD_FLOW
Definition: util-lua-common.h:59
Flow_::alproto_ts
AppProto alproto_ts
Definition: flow.h:456
FlowAddress_::address
union FlowAddress_::@125 address
LuaStateGetFlow
Flow * LuaStateGetFlow(lua_State *luastate)
get flow pointer from lua state
Definition: util-lua.c:260
app-layer-protos.h
FlowHasAlerts
int FlowHasAlerts(const Flow *f)
Check if flow has alerts.
Definition: flow.c:162
Flow_::sp
Port sp
Definition: flow.h:364
Flow_::alproto_tc
AppProto alproto_tc
Definition: flow.h:457
Flow_::alproto
AppProto alproto
application level protocol
Definition: flow.h:455
Flow_::tosrcpktcnt
uint32_t tosrcpktcnt
Definition: flow.h:501
CreateTimeString
void CreateTimeString(const SCTime_t ts, char *str, size_t size)
Definition: util-time.c:272
SCTIME_USECS
#define SCTIME_USECS(t)
Definition: util-time.h:56
f
Flow f
Definition: fuzz_dataset.c:32