From: Victor Julien Date: Tue, 21 Jan 2025 19:03:17 +0000 (+0100) Subject: lua: add initial suricata.packet lib X-Git-Tag: suricata-8.0.0-beta1~449 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=fbb6b0de61c99da3c359a116fdc5f987dcd98971;p=thirdparty%2Fsuricata.git lua: add initial suricata.packet lib Example: ``` local packet = require "suricata.packet" function init (args) local needs = {} return needs end function match (args) p = packet.get() payload = p:payload() ts = p:timestring() for line in payload:gmatch("([^\r\n]*)[\r\n]+") do if line == "GET /index.html HTTP/1.0" then ipver, srcip, dstip, proto, sp, dp = p:tuple() SCLogNotice(string.format("%s %s->%s %d->%d (pcap_cnt:%d) match! %s", ts, srcip, dstip, sp, dp, p:pcap_cnt(), line)); return 1 end end return 0 end ``` Methods: `get` creates the packet object. `payload` returns the packet payload as a buffer `packet` returns the whole packet (includes headers) `pcap_cnt` returns the `pcap_cnt` (pcap file mode only) `tuple` returns various fields: srcip, dstip, proto, sp, dp `sp` returns source port `dp` returns destination port `timestamp` returns time as 2 numbers: seconds and microseconds `timestring_legacy` returns a timestamp as a string (like fastlog) `timestring_iso8601` returns a iso8601 compat timestring (like eve) Ticket: #7488. --- diff --git a/src/Makefile.am b/src/Makefile.am index 5bfa9de62d..7b71196fb7 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -521,6 +521,7 @@ noinst_HEADERS = \ util-lua-hassh.h \ util-lua-http.h \ util-lua-ja3.h \ + util-lua-packetlib.h \ util-lua-sandbox.h \ util-lua-smtp.h \ util-lua-ssh.h \ @@ -1075,6 +1076,7 @@ libsuricata_c_a_SOURCES = \ util-lua-hassh.c \ util-lua-http.c \ util-lua-ja3.c \ + util-lua-packetlib.c \ util-lua-sandbox.c \ util-lua-smtp.c \ util-lua-ssh.c \ diff --git a/src/util-lua-builtins.c b/src/util-lua-builtins.c index c826df4d9f..565eb98bee 100644 --- a/src/util-lua-builtins.c +++ b/src/util-lua-builtins.c @@ -19,12 +19,14 @@ #include "util-lua-builtins.h" #include "util-lua-hashlib.h" #include "util-lua-dataset.h" +#include "util-lua-packetlib.h" #include "lauxlib.h" static const luaL_Reg builtins[] = { { "suricata.hashlib", SCLuaLoadHashlib }, { "suricata.dataset", LuaLoadDatasetLib }, + { "suricata.packet", LuaLoadPacketLib }, { NULL, NULL }, }; diff --git a/src/util-lua-packetlib.c b/src/util-lua-packetlib.c new file mode 100644 index 0000000000..1d9621021a --- /dev/null +++ b/src/util-lua-packetlib.c @@ -0,0 +1,282 @@ +/* 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. + */ + +/** + * \file + * + * Packet API for Lua. + * + * local packet = require("suricata.packet") + */ + +#include "suricata-common.h" + +#include "util-lua-packetlib.h" + +#include "app-layer-protos.h" /* Required by util-lua-common. */ +#include "util-lua-common.h" +#include "util-lua.h" +#include "util-debug.h" +#include "util-print.h" + +/* key for p (packet) pointer */ +extern const char lua_ext_key_p[]; +static const char suricata_packet[] = "suricata:packet"; + +struct LuaPacket { + Packet *p; +}; + +static int LuaPacketGC(lua_State *luastate) +{ + SCLogDebug("gc:start"); + struct LuaPacket *s = (struct LuaPacket *)lua_touserdata(luastate, 1); + SCLogDebug("packet %p", s->p); + s->p = NULL; + SCLogDebug("gc:done"); + return 0; +} + +static int LuaPacketPayload(lua_State *luastate) +{ + struct LuaPacket *s = (struct LuaPacket *)lua_touserdata(luastate, 1); + if (s == NULL || s->p == NULL) { + LUA_ERROR("failed to get packet"); + } + + LuaPushStringBuffer(luastate, (const uint8_t *)s->p->payload, (size_t)s->p->payload_len); + return 1; +} + +static int LuaPacketPacket(lua_State *luastate) +{ + struct LuaPacket *s = (struct LuaPacket *)lua_touserdata(luastate, 1); + if (s == NULL || s->p == NULL) { + LUA_ERROR("failed to get packet"); + } + + LuaPushStringBuffer(luastate, (const uint8_t *)GET_PKT_DATA(s->p), (size_t)GET_PKT_LEN(s->p)); + return 1; +} + +static int LuaPacketPcapCnt(lua_State *luastate) +{ + struct LuaPacket *s = (struct LuaPacket *)lua_touserdata(luastate, 1); + if (s == NULL || s->p == NULL) { + LUA_ERROR("failed to get packet"); + } + + lua_pushinteger(luastate, s->p->pcap_cnt); + return 1; +} + +/** \internal + * \brief legacy format as used by fast.log, http.log, etc. + */ +static int LuaPacketTimestringLegacy(lua_State *luastate) +{ + struct LuaPacket *s = (struct LuaPacket *)lua_touserdata(luastate, 1); + if (s == NULL || s->p == NULL) { + LUA_ERROR("failed to get packet"); + } + + char timebuf[64]; + CreateTimeString(s->p->ts, timebuf, sizeof(timebuf)); + lua_pushstring(luastate, timebuf); + return 1; +} + +static int LuaPacketTimestringIso8601(lua_State *luastate) +{ + struct LuaPacket *s = (struct LuaPacket *)lua_touserdata(luastate, 1); + if (s == NULL || s->p == NULL) { + LUA_ERROR("failed to get packet"); + } + + char timebuf[64]; + CreateIsoTimeString(s->p->ts, timebuf, sizeof(timebuf)); + lua_pushstring(luastate, timebuf); + return 1; +} + +static int LuaPacketTimestamp(lua_State *luastate) +{ + struct LuaPacket *s = (struct LuaPacket *)lua_touserdata(luastate, 1); + if (s == NULL || s->p == NULL) { + LUA_ERROR("failed to get packet"); + } + + lua_pushnumber(luastate, (double)SCTIME_SECS(s->p->ts)); + lua_pushnumber(luastate, (double)SCTIME_USECS(s->p->ts)); + return 2; +} + +/** \internal + * \brief fill lua stack with header info + * \param luastate the lua state + * \retval cnt number of data items placed on the stack + * + * Places: ipver (number), src ip (string), dst ip (string), protocol (number), + * sp or icmp type (number), dp or icmp code (number). + */ +static int LuaPacketTuple(lua_State *luastate) +{ + struct LuaPacket *s = (struct LuaPacket *)lua_touserdata(luastate, 1); + if (s == NULL || s->p == NULL) { + LUA_ERROR("failed to get packet"); + } + Packet *p = s->p; + + int ipver = 0; + if (PacketIsIPv4(p)) { + ipver = 4; + } else if (PacketIsIPv6(p)) { + ipver = 6; + } + lua_pushinteger(luastate, ipver); + if (ipver == 0) + return 1; + + char srcip[46] = "", dstip[46] = ""; + if (PacketIsIPv4(p)) { + PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), srcip, sizeof(srcip)); + PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), dstip, sizeof(dstip)); + } else if (PacketIsIPv6(p)) { + PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), srcip, sizeof(srcip)); + PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), dstip, sizeof(dstip)); + } + + lua_pushstring(luastate, srcip); + lua_pushstring(luastate, dstip); + + /* proto and ports (or type/code) */ + lua_pushinteger(luastate, p->proto); + if (p->proto == IPPROTO_TCP || p->proto == IPPROTO_UDP) { + lua_pushinteger(luastate, p->sp); + lua_pushinteger(luastate, p->dp); + + } else if (p->proto == IPPROTO_ICMP || p->proto == IPPROTO_ICMPV6) { + lua_pushinteger(luastate, p->icmp_s.type); + lua_pushinteger(luastate, p->icmp_s.code); + } else { + lua_pushinteger(luastate, 0); + lua_pushinteger(luastate, 0); + } + + return 6; +} + +/** \internal + * \brief get tcp/udp/sctp source port + * \param luastate the lua state + */ +static int LuaPacketSport(lua_State *luastate) +{ + struct LuaPacket *s = (struct LuaPacket *)lua_touserdata(luastate, 1); + if (s == NULL || s->p == NULL) { + LUA_ERROR("failed to get packet"); + } + Packet *p = s->p; + + switch (p->proto) { + case IPPROTO_TCP: + case IPPROTO_UDP: + case IPPROTO_SCTP: + lua_pushinteger(luastate, p->sp); + break; + default: + LUA_ERROR("sp only available for tcp, udp and sctp"); + } + + return 1; +} + +/** \internal + * \brief get tcp/udp/sctp dest port + * \param luastate the lua state + */ +static int LuaPacketDport(lua_State *luastate) +{ + struct LuaPacket *s = (struct LuaPacket *)lua_touserdata(luastate, 1); + if (s == NULL || s->p == NULL) { + LUA_ERROR("failed to get packet"); + } + Packet *p = s->p; + + switch (p->proto) { + case IPPROTO_TCP: + case IPPROTO_UDP: + case IPPROTO_SCTP: + lua_pushinteger(luastate, p->dp); + break; + default: + LUA_ERROR("dp only available for tcp, udp and sctp"); + } + + return 1; +} + +static int LuaPacketGet(lua_State *luastate) +{ + Packet *p = LuaStateGetPacket(luastate); + if (p == NULL) { + LUA_ERROR("failed to get packet"); + } + + struct LuaPacket *s = (struct LuaPacket *)lua_newuserdata(luastate, sizeof(*s)); + if (s == NULL) { + LUA_ERROR("failed to get userdata"); + } + s->p = p; + luaL_getmetatable(luastate, suricata_packet); + lua_setmetatable(luastate, -2); + return 1; +} + +static const luaL_Reg packetlib[] = { + // clang-format off + { "get", LuaPacketGet }, + { NULL, NULL } + // clang-format on +}; + +static const luaL_Reg packetlib_meta[] = { + // clang-format off + { "packet", LuaPacketPacket }, + { "payload", LuaPacketPayload }, + { "pcap_cnt", LuaPacketPcapCnt }, + { "timestring_legacy", LuaPacketTimestringLegacy }, + { "timestring_iso8601", LuaPacketTimestringIso8601 }, + { "timestamp", LuaPacketTimestamp }, + { "tuple", LuaPacketTuple }, + { "sp", LuaPacketSport }, + { "dp", LuaPacketDport }, + { "__gc", LuaPacketGC }, + { NULL, NULL } + // clang-format on +}; + +int LuaLoadPacketLib(lua_State *luastate) +{ + luaL_newmetatable(luastate, suricata_packet); + lua_pushvalue(luastate, -1); + lua_setfield(luastate, -2, "__index"); + luaL_setfuncs(luastate, packetlib_meta, 0); + + luaL_newlib(luastate, packetlib); + return 1; +} diff --git a/src/util-lua-packetlib.h b/src/util-lua-packetlib.h new file mode 100644 index 0000000000..8d9393edee --- /dev/null +++ b/src/util-lua-packetlib.h @@ -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_PACKET_H +#define SURICATA_UTIL_LUA_PACKET_H + +#include "lua.h" + +int LuaLoadPacketLib(lua_State *luastate); + +#endif /* SURICATA_UTIL_LUA_DATASET_H */