/* 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; }