]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
examples: add custom logging plugin
authorJason Ish <jason.ish@oisf.net>
Mon, 26 Aug 2024 22:12:16 +0000 (16:12 -0600)
committerVictor Julien <victor@inliniac.net>
Sat, 31 Aug 2024 08:53:59 +0000 (10:53 +0200)
Add an example custom logger that hooks into the low level packet and
flow logging callbacks.

Ticket: #7227

.github/workflows/builds.yml
configure.ac
examples/plugins/c-custom-loggers/.gitignore [new file with mode: 0644]
examples/plugins/c-custom-loggers/Makefile.in [new file with mode: 0644]
examples/plugins/c-custom-loggers/README.md [new file with mode: 0644]
examples/plugins/c-custom-loggers/custom-logger.c [new file with mode: 0644]

index f91effdc89c7f8ee3c3e21beeb35aeb9776e7aec..0d86c6857b7ecdca763d31a3342a654f8719504e 100644 (file)
@@ -194,6 +194,10 @@ jobs:
         working-directory: examples/plugins/c-json-filetype
         run: make clean all
 
+      - name: Build example C custom logger plugin
+        working-directory: examples/plugins/c-custom-loggers
+        run: make clean all
+
       - name: Install Suricata and library
         run: make install install-headers install-library
 
index 24dc01430a358c2f1a68683b7ed3d78da790cc7e..03ffadd89b27581fed7217ccc68f1b715029a36d 100644 (file)
@@ -2505,6 +2505,7 @@ AC_CONFIG_FILES(python/Makefile python/suricata/config/defaults.py)
 AC_CONFIG_FILES(ebpf/Makefile)
 AC_CONFIG_FILES(libsuricata-config)
 AC_CONFIG_FILES(examples/plugins/c-json-filetype/Makefile)
+AC_CONFIG_FILES(examples/plugins/c-custom-loggers/Makefile)
 AC_CONFIG_FILES(examples/plugins/ci-capture/Makefile)
 AC_CONFIG_FILES(examples/lib/simple/Makefile examples/lib/simple/Makefile.example)
 AC_CONFIG_FILES(plugins/Makefile)
diff --git a/examples/plugins/c-custom-loggers/.gitignore b/examples/plugins/c-custom-loggers/.gitignore
new file mode 100644 (file)
index 0000000..53ceb6a
--- /dev/null
@@ -0,0 +1,3 @@
+!/Makefile.in
+*.la
+*.so
diff --git a/examples/plugins/c-custom-loggers/Makefile.in b/examples/plugins/c-custom-loggers/Makefile.in
new file mode 100644 (file)
index 0000000..a30bc8a
--- /dev/null
@@ -0,0 +1,32 @@
+# If building a plugin out of the Suricata source tree, you can use
+# libsuricata-config --cflags.
+#LIBSURICATA_CONFIG ?=  libsuricata-config
+#CPPFLAGS +=            `$(LIBSURICATA_CONFIG) --cflags`
+
+# But as this is an example in the Suricata source tree we'll look for
+# includes in the source tree.
+CPPFLAGS +=     -I@top_srcdir@/src -DHAVE_CONFIG_H
+
+# Currently the Suricata logging system requires this to be even for
+# plugins.
+CPPFLAGS +=    "-D__SCFILENAME__=\"$(*F)\""
+
+all: Makefile custom-logger.so
+
+custom-logger.so: custom-logger.c
+       $(CC) $(CPPFLAGS) -fPIC -shared -o $@ $^
+
+clean:
+       rm -f *.so *.o *.lo
+       rm -rf .deps
+
+distclean: clean
+       rm -f Makefile.am
+
+# Regenerate Makefile on change of Makefile.in since we're not using
+# Makefile.am.
+Makefile: Makefile.in
+       cd @top_builddir@ && ./config.status examples/plugins/c-custom-logger/Makefile
+
+# Dummy rules to satisfy make dist.
+dist distdir:
diff --git a/examples/plugins/c-custom-loggers/README.md b/examples/plugins/c-custom-loggers/README.md
new file mode 100644 (file)
index 0000000..412cc4f
--- /dev/null
@@ -0,0 +1,55 @@
+# Example Custom Logging Plugin
+
+This is an example of a low level logging plugin.
+
+Currently implemented are packet and flow loggers.
+
+## Building
+
+If in the Suricata source directory, this plugin can be built by
+running `make`'.
+
+## Building Standalone
+
+This Makefile is not generated by automake so it can serve as an
+example for plugins created outside of the Suricata source tree.
+
+Building a standalone plugin has the following dependencies:
+
+- Suricata is installed
+- The Suricata library is installed: `make install-library`
+- The Suricata development headers are installed: `make install-headers`
+- The program `libsuricata-config` is in your path (installed with
+  `make install-library`)
+
+Modify the Makefile to use `libsuricata-config`.
+
+Before building this plugin you will need to build and install Suricata from the
+git master branch and install the development tools and headers:
+
+- `make install-library`
+- `make install-headers`
+
+then make sure the newly installed tool `libsuricata-config` can be
+found in your path, for example:
+```
+libsuricata-config --cflags
+```
+
+Then a simple `make` should build this plugin.
+
+Or if the Suricata installation is not in the path, a command like the following
+can be used:
+
+```
+PATH=/opt/suricata/bin:$PATH make
+```
+
+## Usage
+
+To run the plugin, first add the path to the plugin you just compiled to
+your `suricata.yaml`, for example:
+```
+plugins:
+  - /usr/lib/suricata/plugins/c-custom-loggers/custom-loggers.so
+```
diff --git a/examples/plugins/c-custom-loggers/custom-logger.c b/examples/plugins/c-custom-loggers/custom-logger.c
new file mode 100644 (file)
index 0000000..a9cca80
--- /dev/null
@@ -0,0 +1,110 @@
+/* Copyright (C) 2023-2024 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.
+ */
+
+#include "suricata-common.h"
+#include "suricata-plugin.h"
+
+#include "output-packet.h"
+#include "output-flow.h"
+#include "util-print.h"
+
+static int CustomPacketLogger(ThreadVars *tv, void *thread_data, const Packet *p)
+{
+    char src_ip[46] = { 0 }, dst_ip[46] = { 0 };
+
+    if (PacketIsIPv4(p)) {
+        PrintInet(AF_INET, (const void *)&(p->src.addr_data32[0]), src_ip, sizeof(src_ip));
+        PrintInet(AF_INET, (const void *)&(p->dst.addr_data32[0]), dst_ip, sizeof(dst_ip));
+    } else if (PacketIsIPv6(p)) {
+        PrintInet(AF_INET6, (const void *)&(p->src.address), src_ip, sizeof(src_ip));
+        PrintInet(AF_INET6, (const void *)&(p->dst.address), dst_ip, sizeof(dst_ip));
+    } else {
+        SCLogNotice("Packet is not IP");
+        return 0;
+    }
+    SCLogNotice("Packet: %s -> %s", src_ip, dst_ip);
+    return 0;
+}
+
+static bool CustomPacketLoggerCondition(ThreadVars *tv, void *thread_data, const Packet *)
+{
+    /* Always true for this example. */
+    return true;
+}
+
+static int CustomFlowLogger(ThreadVars *tv, void *thread_data, Flow *f)
+{
+    char src_ip[46] = { 0 }, dst_ip[46] = { 0 };
+    Port sp, dp;
+
+    if ((f->flags & FLOW_DIR_REVERSED) == 0) {
+        if (FLOW_IS_IPV4(f)) {
+            PrintInet(AF_INET, (const void *)&(f->src.addr_data32[0]), src_ip, sizeof(src_ip));
+            PrintInet(AF_INET, (const void *)&(f->dst.addr_data32[0]), dst_ip, sizeof(dst_ip));
+        } else if (FLOW_IS_IPV6(f)) {
+            PrintInet(AF_INET6, (const void *)&(f->src.address), src_ip, sizeof(src_ip));
+            PrintInet(AF_INET6, (const void *)&(f->dst.address), dst_ip, sizeof(dst_ip));
+        }
+        sp = f->sp;
+        dp = f->dp;
+    } else {
+        if (FLOW_IS_IPV4(f)) {
+            PrintInet(AF_INET, (const void *)&(f->dst.addr_data32[0]), src_ip, sizeof(src_ip));
+            PrintInet(AF_INET, (const void *)&(f->src.addr_data32[0]), dst_ip, sizeof(dst_ip));
+        } else if (FLOW_IS_IPV6(f)) {
+            PrintInet(AF_INET6, (const void *)&(f->dst.address), src_ip, sizeof(src_ip));
+            PrintInet(AF_INET6, (const void *)&(f->src.address), dst_ip, sizeof(dst_ip));
+        }
+        sp = f->dp;
+        dp = f->sp;
+    }
+
+    SCLogNotice("Flow: %s:%u -> %s:%u", src_ip, sp, dst_ip, dp);
+
+    return 0;
+}
+
+static TmEcode ThreadInit(ThreadVars *tv, const void *initdata, void **data)
+{
+    return TM_ECODE_OK;
+}
+
+static TmEcode ThreadDeinit(ThreadVars *tv, void *data)
+{
+    // Nothing to do. If we allocated data in ThreadInit we would free
+    // it here.
+    return TM_ECODE_OK;
+}
+
+static void Init(void)
+{
+    OutputRegisterPacketLogger(LOGGER_USER, "custom-packet-logger", CustomPacketLogger,
+            CustomPacketLoggerCondition, NULL, ThreadInit, ThreadDeinit, NULL);
+    OutputRegisterFlowLogger("custom-flow-logger", CustomFlowLogger, NULL, ThreadInit, ThreadDeinit);
+}
+
+const SCPlugin PluginRegistration = {
+    .name = "CustomLogger",
+    .author = "Firstname Lastname",
+    .license = "GPLv2",
+    .Init = Init,
+};
+
+const SCPlugin *SCPluginRegister(void)
+{
+    return &PluginRegistration;
+}