]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
lua: add initial suricata.packet lib
authorVictor Julien <vjulien@oisf.net>
Tue, 21 Jan 2025 19:03:17 +0000 (20:03 +0100)
committerVictor Julien <victor@inliniac.net>
Wed, 12 Feb 2025 16:08:32 +0000 (17:08 +0100)
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.

src/Makefile.am
src/util-lua-builtins.c
src/util-lua-packetlib.c [new file with mode: 0644]
src/util-lua-packetlib.h [new file with mode: 0644]

index 5bfa9de62d8904868a38708a78f74f86cb38365f..7b71196fb71c4f9f9664c1d5c3f3d0f7399353d2 100755 (executable)
@@ -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 \
index c826df4d9f6d55376a16fd9f524dd3472b663ea5..565eb98bee595b2dc624c1853f21b9f6698cf602 100644 (file)
 #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 (file)
index 0000000..1d96210
--- /dev/null
@@ -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 (file)
index 0000000..8d9393e
--- /dev/null
@@ -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 */