/src/suricata/src/util-lua-packetlib.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 | | * Packet API for Lua. |
22 | | * |
23 | | * local packet = require("suricata.packet") |
24 | | */ |
25 | | |
26 | | #include "suricata-common.h" |
27 | | |
28 | | #include "util-lua-packetlib.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_packet[] = "suricata:packet"; |
37 | | |
38 | | /* no stored pointer: the accessors act on the packet the state is |
39 | | * running on (LuaStateGetPacket), so a retained handle can never |
40 | | * dangle */ |
41 | | struct LuaPacket { |
42 | | }; |
43 | | |
44 | | static int LuaPacketPayload(lua_State *luastate) |
45 | 0 | { |
46 | 0 | SCLuaCheckUserData(luastate, 1, suricata_packet, SCLUA_TUD_PACKET); |
47 | 0 | Packet *p = LuaStateGetPacket(luastate); |
48 | 0 | if (p == NULL) { |
49 | 0 | LUA_ERROR("failed to get packet"); |
50 | 0 | } |
51 | | |
52 | 0 | LuaPushStringBuffer(luastate, (const uint8_t *)p->payload, (size_t)p->payload_len); |
53 | 0 | return 1; |
54 | 0 | } |
55 | | |
56 | | static int LuaPacketPacket(lua_State *luastate) |
57 | 0 | { |
58 | 0 | SCLuaCheckUserData(luastate, 1, suricata_packet, SCLUA_TUD_PACKET); |
59 | 0 | Packet *p = LuaStateGetPacket(luastate); |
60 | 0 | if (p == NULL) { |
61 | 0 | LUA_ERROR("failed to get packet"); |
62 | 0 | } |
63 | | |
64 | 0 | LuaPushStringBuffer(luastate, (const uint8_t *)GET_PKT_DATA(p), (size_t)GET_PKT_LEN(p)); |
65 | 0 | return 1; |
66 | 0 | } |
67 | | |
68 | | static int LuaPacketPcapCnt(lua_State *luastate) |
69 | 0 | { |
70 | 0 | SCLuaCheckUserData(luastate, 1, suricata_packet, SCLUA_TUD_PACKET); |
71 | 0 | Packet *p = LuaStateGetPacket(luastate); |
72 | 0 | if (p == NULL) { |
73 | 0 | LUA_ERROR("failed to get packet"); |
74 | 0 | } |
75 | | |
76 | 0 | lua_pushinteger(luastate, PcapPacketCntGet(p)); |
77 | 0 | return 1; |
78 | 0 | } |
79 | | |
80 | | /** \internal |
81 | | * \brief legacy format as used by fast.log, etc. |
82 | | */ |
83 | | static int LuaPacketTimestringLegacy(lua_State *luastate) |
84 | 0 | { |
85 | 0 | SCLuaCheckUserData(luastate, 1, suricata_packet, SCLUA_TUD_PACKET); |
86 | 0 | Packet *p = LuaStateGetPacket(luastate); |
87 | 0 | if (p == NULL) { |
88 | 0 | LUA_ERROR("failed to get packet"); |
89 | 0 | } |
90 | | |
91 | 0 | char timebuf[64]; |
92 | 0 | CreateTimeString(p->ts, timebuf, sizeof(timebuf)); |
93 | 0 | lua_pushstring(luastate, timebuf); |
94 | 0 | return 1; |
95 | 0 | } |
96 | | |
97 | | static int LuaPacketTimestringIso8601(lua_State *luastate) |
98 | 0 | { |
99 | 0 | SCLuaCheckUserData(luastate, 1, suricata_packet, SCLUA_TUD_PACKET); |
100 | 0 | Packet *p = LuaStateGetPacket(luastate); |
101 | 0 | if (p == NULL) { |
102 | 0 | LUA_ERROR("failed to get packet"); |
103 | 0 | } |
104 | | |
105 | 0 | char timebuf[64]; |
106 | 0 | CreateIsoTimeString(p->ts, timebuf, sizeof(timebuf)); |
107 | 0 | lua_pushstring(luastate, timebuf); |
108 | 0 | return 1; |
109 | 0 | } |
110 | | |
111 | | static int LuaPacketTimestamp(lua_State *luastate) |
112 | 0 | { |
113 | 0 | SCLuaCheckUserData(luastate, 1, suricata_packet, SCLUA_TUD_PACKET); |
114 | 0 | Packet *p = LuaStateGetPacket(luastate); |
115 | 0 | if (p == NULL) { |
116 | 0 | LUA_ERROR("failed to get packet"); |
117 | 0 | } |
118 | | |
119 | 0 | lua_pushnumber(luastate, (double)SCTIME_SECS(p->ts)); |
120 | 0 | lua_pushnumber(luastate, (double)SCTIME_USECS(p->ts)); |
121 | 0 | return 2; |
122 | 0 | } |
123 | | |
124 | | /** \internal |
125 | | * \brief fill lua stack with header info |
126 | | * \param luastate the lua state |
127 | | * \retval cnt number of data items placed on the stack |
128 | | * |
129 | | * Places: ipver (number), src ip (string), dst ip (string), protocol (number), |
130 | | * sp or icmp type (number), dp or icmp code (number). |
131 | | */ |
132 | | static int LuaPacketTuple(lua_State *luastate) |
133 | 0 | { |
134 | 0 | SCLuaCheckUserData(luastate, 1, suricata_packet, SCLUA_TUD_PACKET); |
135 | 0 | Packet *p = LuaStateGetPacket(luastate); |
136 | 0 | if (p == NULL) { |
137 | 0 | LUA_ERROR("failed to get packet"); |
138 | 0 | } |
139 | | |
140 | 0 | int ipver = 0; |
141 | 0 | if (PacketIsIPv4(p)) { |
142 | 0 | ipver = 4; |
143 | 0 | } else if (PacketIsIPv6(p)) { |
144 | 0 | ipver = 6; |
145 | 0 | } |
146 | 0 | lua_pushinteger(luastate, ipver); |
147 | 0 | if (ipver == 0) |
148 | 0 | return 1; |
149 | | |
150 | 0 | char srcip[46] = "", dstip[46] = ""; |
151 | 0 | if (PacketIsIPv4(p)) { |
152 | 0 | PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), srcip, sizeof(srcip)); |
153 | 0 | PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), dstip, sizeof(dstip)); |
154 | 0 | } else if (PacketIsIPv6(p)) { |
155 | 0 | PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), srcip, sizeof(srcip)); |
156 | 0 | PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), dstip, sizeof(dstip)); |
157 | 0 | } |
158 | |
|
159 | 0 | lua_pushstring(luastate, srcip); |
160 | 0 | lua_pushstring(luastate, dstip); |
161 | | |
162 | | /* proto and ports (or type/code) */ |
163 | 0 | lua_pushinteger(luastate, p->proto); |
164 | 0 | if (p->proto == IPPROTO_TCP || p->proto == IPPROTO_UDP) { |
165 | 0 | lua_pushinteger(luastate, p->sp); |
166 | 0 | lua_pushinteger(luastate, p->dp); |
167 | |
|
168 | 0 | } else if (p->proto == IPPROTO_ICMP || p->proto == IPPROTO_ICMPV6) { |
169 | 0 | lua_pushinteger(luastate, p->icmp_s.type); |
170 | 0 | lua_pushinteger(luastate, p->icmp_s.code); |
171 | 0 | } else { |
172 | 0 | lua_pushinteger(luastate, 0); |
173 | 0 | lua_pushinteger(luastate, 0); |
174 | 0 | } |
175 | |
|
176 | 0 | return 6; |
177 | 0 | } |
178 | | |
179 | | /** \internal |
180 | | * \brief get tcp/udp/sctp source port |
181 | | * \param luastate the lua state |
182 | | */ |
183 | | static int LuaPacketSport(lua_State *luastate) |
184 | 0 | { |
185 | 0 | SCLuaCheckUserData(luastate, 1, suricata_packet, SCLUA_TUD_PACKET); |
186 | 0 | Packet *p = LuaStateGetPacket(luastate); |
187 | 0 | if (p == NULL) { |
188 | 0 | LUA_ERROR("failed to get packet"); |
189 | 0 | } |
190 | | |
191 | 0 | switch (p->proto) { |
192 | 0 | case IPPROTO_TCP: |
193 | 0 | case IPPROTO_UDP: |
194 | 0 | case IPPROTO_SCTP: |
195 | 0 | lua_pushinteger(luastate, p->sp); |
196 | 0 | break; |
197 | 0 | default: |
198 | 0 | LUA_ERROR("sp only available for tcp, udp and sctp"); |
199 | 0 | } |
200 | | |
201 | 0 | return 1; |
202 | 0 | } |
203 | | |
204 | | /** \internal |
205 | | * \brief get tcp/udp/sctp dest port |
206 | | * \param luastate the lua state |
207 | | */ |
208 | | static int LuaPacketDport(lua_State *luastate) |
209 | 0 | { |
210 | 0 | SCLuaCheckUserData(luastate, 1, suricata_packet, SCLUA_TUD_PACKET); |
211 | 0 | Packet *p = LuaStateGetPacket(luastate); |
212 | 0 | if (p == NULL) { |
213 | 0 | LUA_ERROR("failed to get packet"); |
214 | 0 | } |
215 | | |
216 | 0 | switch (p->proto) { |
217 | 0 | case IPPROTO_TCP: |
218 | 0 | case IPPROTO_UDP: |
219 | 0 | case IPPROTO_SCTP: |
220 | 0 | lua_pushinteger(luastate, p->dp); |
221 | 0 | break; |
222 | 0 | default: |
223 | 0 | LUA_ERROR("dp only available for tcp, udp and sctp"); |
224 | 0 | } |
225 | | |
226 | 0 | return 1; |
227 | 0 | } |
228 | | |
229 | | static int LuaPacketGet(lua_State *luastate) |
230 | 0 | { |
231 | 0 | Packet *p = LuaStateGetPacket(luastate); |
232 | 0 | if (p == NULL) { |
233 | 0 | LUA_ERROR("failed to get packet"); |
234 | 0 | } |
235 | | |
236 | 0 | struct LuaPacket *s = SCLuaNewUserData(luastate, SCLUA_TUD_PACKET, sizeof(*s)); |
237 | 0 | if (s == NULL) { |
238 | 0 | LUA_ERROR("failed to get userdata"); |
239 | 0 | } |
240 | 0 | luaL_getmetatable(luastate, suricata_packet); |
241 | 0 | lua_setmetatable(luastate, -2); |
242 | 0 | return 1; |
243 | 0 | } |
244 | | |
245 | | static const luaL_Reg packetlib[] = { |
246 | | // clang-format off |
247 | | { "get", LuaPacketGet }, |
248 | | { NULL, NULL } |
249 | | // clang-format on |
250 | | }; |
251 | | |
252 | | static const luaL_Reg packetlib_meta[] = { |
253 | | // clang-format off |
254 | | { "packet", LuaPacketPacket }, |
255 | | { "payload", LuaPacketPayload }, |
256 | | { "pcap_cnt", LuaPacketPcapCnt }, |
257 | | { "timestring_legacy", LuaPacketTimestringLegacy }, |
258 | | { "timestring_iso8601", LuaPacketTimestringIso8601 }, |
259 | | { "timestamp", LuaPacketTimestamp }, |
260 | | { "tuple", LuaPacketTuple }, |
261 | | { "sp", LuaPacketSport }, |
262 | | { "dp", LuaPacketDport }, |
263 | | { NULL, NULL } |
264 | | // clang-format on |
265 | | }; |
266 | | |
267 | | int LuaLoadPacketLib(lua_State *luastate) |
268 | 0 | { |
269 | 0 | luaL_newmetatable(luastate, suricata_packet); |
270 | 0 | lua_pushvalue(luastate, -1); |
271 | 0 | lua_setfield(luastate, -2, "__index"); |
272 | 0 | luaL_setfuncs(luastate, packetlib_meta, 0); |
273 | |
|
274 | 0 | luaL_newlib(luastate, packetlib); |
275 | 0 | return 1; |
276 | 0 | } |