examples: minimal example capture plugin for ci

Create a mininal capture plugin that injects one packet. While it can
also be a template, we should be able to run this in CI to test the
loading and registration of the capture plugin mechanisms.
pull/10552/head
Jason Ish 1 year ago committed by Victor Julien
parent acfc1c9395
commit 0dc3de332a

@ -280,7 +280,7 @@ jobs:
- name: Building Rust documentation - name: Building Rust documentation
run: make doc run: make doc
working-directory: rust working-directory: rust
- run: make install - run: make install install-conf
- run: suricatasc -h - run: suricatasc -h
- run: suricata-update -V - run: suricata-update -V
- name: Check if Suricata-Update example configuration files are installed - name: Check if Suricata-Update example configuration files are installed
@ -292,6 +292,14 @@ jobs:
test -e /usr/local/lib/suricata/python/suricata/update/configs/threshold.in test -e /usr/local/lib/suricata/python/suricata/update/configs/threshold.in
test -e /usr/local/lib/suricata/python/suricata/update/configs/update.yaml test -e /usr/local/lib/suricata/python/suricata/update/configs/update.yaml
- name: Test capture plugin
working-directory: examples/plugins/ci-capture
run: |
make
../../../src/suricata -S /dev/null --set plugins.0=./capture.so --capture-plugin=ci-capture --runmode=single -l . -c ../../../suricata.yaml
cat eve.json | jq -c 'select(.dns)'
test $(cat eve.json | jq -c 'select(.dns)' | wc -l) = "1"
- name: Test library build in tree - name: Test library build in tree
working-directory: examples/lib/simple working-directory: examples/lib/simple
run: make clean all run: make clean all

@ -2641,6 +2641,7 @@ AC_CONFIG_FILES(python/Makefile python/suricata/config/defaults.py)
AC_CONFIG_FILES(ebpf/Makefile) AC_CONFIG_FILES(ebpf/Makefile)
AC_CONFIG_FILES(libsuricata-config) AC_CONFIG_FILES(libsuricata-config)
AC_CONFIG_FILES(examples/plugins/c-json-filetype/Makefile) AC_CONFIG_FILES(examples/plugins/c-json-filetype/Makefile)
AC_CONFIG_FILES(examples/plugins/ci-capture/Makefile)
AC_CONFIG_FILES(examples/lib/simple/Makefile examples/lib/simple/Makefile.example) AC_CONFIG_FILES(examples/lib/simple/Makefile examples/lib/simple/Makefile.example)
AC_OUTPUT AC_OUTPUT

@ -4,3 +4,8 @@
An example plugin of an EVE/JSON filetype plugin. This type of plugin An example plugin of an EVE/JSON filetype plugin. This type of plugin
is useful if you want to send EVE output to custom destinations. is useful if you want to send EVE output to custom destinations.
## ci-capture
A minimal capture plugin that can be used as a template, but also used
for testing capture plugin loading and registration in CI.

@ -0,0 +1,4 @@
!/Makefile.in
*.o
*.so
*.la

@ -0,0 +1,41 @@
SRCS := plugin.c \
runmode.c \
source.c
# 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)\""
OBJS := $(SRCS:.c=.o)
all: Makefile capture.so
%.o: %.c
$(CC) $(CPPFLAGS) -fPIC -c $< -o $@
capture.so: $(OBJS)
$(CC) $(CPPFLAGS) -fPIC -shared -o $@ $(OBJS)
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/ci-capture/Makefile
# Dummy rules to satisfy make dist.
dist distdir:

@ -0,0 +1,2 @@
# Minimal Capture Plugin for CI

@ -0,0 +1,56 @@
/* Copyright (C) 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-plugin.h"
#include "suricata-common.h"
#include "util-debug.h"
#include "runmode.h"
#include "source.h"
static void InitCapturePlugin(const char *args, int plugin_slot, int receive_slot, int decode_slot)
{
SCLogNotice("...");
CiCaptureIdsRegister(plugin_slot);
TmModuleReceiveCiCaptureRegister(receive_slot);
TmModuleDecodeCiCaptureRegister(decode_slot);
}
static void SCPluginInit(void)
{
SCLogNotice("...");
SCCapturePlugin *plugin = SCCalloc(1, sizeof(SCCapturePlugin));
if (plugin == NULL) {
FatalError("Failed to allocate memory for capture plugin");
}
plugin->name = "ci-capture";
plugin->Init = InitCapturePlugin;
plugin->GetDefaultMode = CiCaptureIdsGetDefaultRunMode;
SCPluginRegisterCapture(plugin);
}
const SCPlugin PluginRegistration = {
.name = "ci-capture",
.author = "OISF Developer",
.license = "GPL-2.0-only",
.Init = SCPluginInit,
};
const SCPlugin *SCPluginRegister()
{
return &PluginRegistration;
}

@ -0,0 +1,73 @@
/* Copyright (C) 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 "runmodes.h"
#include "tm-threads.h"
#include "util-affinity.h"
#include "runmode.h"
const char *CiCaptureIdsGetDefaultRunMode(void)
{
return "autofp";
}
static int RunModeSingle(void)
{
SCLogNotice("...");
char thread_name[TM_THREAD_NAME_MAX];
snprintf(thread_name, sizeof(thread_name), "%s#01", thread_name_single);
ThreadVars *tv = TmThreadCreatePacketHandler(
thread_name, "packetpool", "packetpool", "packetpool", "packetpool", "pktacqloop");
if (tv == NULL) {
SCLogError("TmThreadCreatePacketHandler failed");
return -1;
}
TmModule *tm_module = TmModuleGetByName("ReceiveCiCapture");
if (tm_module == NULL) {
FatalError("TmModuleGetByName failed for ReceiveCiCapture");
}
TmSlotSetFuncAppend(tv, tm_module, NULL);
tm_module = TmModuleGetByName("DecodeCiCapture");
if (tm_module == NULL) {
FatalError("TmModuleGetByName DecodeCiCapture failed");
}
TmSlotSetFuncAppend(tv, tm_module, NULL);
tm_module = TmModuleGetByName("FlowWorker");
if (tm_module == NULL) {
FatalError("TmModuleGetByName for FlowWorker failed");
}
TmSlotSetFuncAppend(tv, tm_module, NULL);
TmThreadSetCPU(tv, WORKER_CPU_SET);
if (TmThreadSpawn(tv) != TM_ECODE_OK) {
FatalError("TmThreadSpawn failed");
}
return 0;
}
void CiCaptureIdsRegister(int slot)
{
RunModeRegisterNewRunMode(slot, "single", "Single threaded", RunModeSingle, NULL);
}

@ -0,0 +1,24 @@
/* Copyright (C) 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.
*/
#ifndef CI_CAPTURE_PLUGIN_RUNMODE_H
#define CI_CAPTURE_PLUGIN_RUNMODE_H
const char *CiCaptureIdsGetDefaultRunMode(void);
void CiCaptureIdsRegister(int slot);
#endif /* CI_CAPTURE_PLUGIN_RUNMODE_H */

@ -0,0 +1,154 @@
/* Copyright (C) 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.h"
#include "threadvars.h"
#include "tm-modules.h"
#include "tm-threads-common.h"
#include "tm-threads.h"
#include "source.h"
/* DNS request for suricata.io. */
static const unsigned char DNS_REQUEST[94] = {
0xa0, 0x36, 0x9f, 0x4c, 0x4c, 0x28, 0x50, 0xeb, /* .6.LL(P. */
0xf6, 0x7d, 0xea, 0x54, 0x08, 0x00, 0x45, 0x00, /* .}.T..E. */
0x00, 0x50, 0x19, 0xae, 0x00, 0x00, 0x40, 0x11, /* .P....@. */
0x4a, 0xc4, 0x0a, 0x10, 0x01, 0x0b, 0x0a, 0x10, /* J....... */
0x01, 0x01, 0x95, 0x97, 0x00, 0x35, 0x00, 0x3c, /* .....5.< */
0x90, 0x6e, 0xdb, 0x12, 0x01, 0x20, 0x00, 0x01, /* .n... .. */
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x73, /* .......s */
0x75, 0x72, 0x69, 0x63, 0x61, 0x74, 0x61, 0x02, /* uricata. */
0x69, 0x6f, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, /* io...... */
0x00, 0x29, 0x04, 0xd0, 0x00, 0x00, 0x00, 0x00, /* .)...... */
0x00, 0x0c, 0x00, 0x0a, 0x00, 0x08, 0x88, 0x51, /* .......Q */
0x20, 0xaf, 0x46, 0xc5, 0xdc, 0xce /* .F... */
};
static TmEcode ReceiveThreadInit(ThreadVars *tv, const void *initdata, void **data)
{
SCLogNotice("...");
return TM_ECODE_OK;
}
static TmEcode ReceiveThreadDeinit(ThreadVars *tv, void *data)
{
SCLogNotice("...");
return TM_ECODE_OK;
}
static TmEcode ReceiveLoop(ThreadVars *tv, void *data, void *slot)
{
SCLogNotice("...");
if (suricata_ctl_flags & SURICATA_STOP) {
SCReturnInt(TM_ECODE_OK);
}
TmSlot *s = (TmSlot *)slot;
/* Notify we are running and processing packets. */
TmThreadsSetFlag(tv, THV_RUNNING);
PacketPoolWait();
Packet *p = PacketGetFromQueueOrAlloc();
if (unlikely(p == NULL)) {
return TM_ECODE_FAILED;
}
PKT_SET_SRC(p, PKT_SRC_WIRE);
struct timeval now;
gettimeofday(&now, NULL);
p->ts = SCTIME_FROM_TIMEVAL(&now);
p->datalink = LINKTYPE_ETHERNET;
p->flags |= PKT_IGNORE_CHECKSUM;
if (unlikely(PacketCopyData(p, DNS_REQUEST, sizeof(DNS_REQUEST)) != 0)) {
TmqhOutputPacketpool(tv, p);
return TM_ECODE_FAILED;
}
if (TmThreadsSlotProcessPkt(tv, s, p) != TM_ECODE_OK) {
return TM_ECODE_FAILED;
}
EngineStop();
return TM_ECODE_DONE;
}
static void ReceiveThreadExitPrintStats(ThreadVars *tv, void *data)
{
SCLogNotice("...");
}
static TmEcode DecodeThreadInit(ThreadVars *tv, const void *initdata, void **data)
{
SCLogNotice("...");
DecodeThreadVars *dtv = DecodeThreadVarsAlloc(tv);
if (dtv == NULL) {
SCReturnInt(TM_ECODE_FAILED);
}
DecodeRegisterPerfCounters(dtv, tv);
*data = (void *)dtv;
return TM_ECODE_OK;
}
static TmEcode DecodeThreadDeinit(ThreadVars *tv, void *data)
{
SCLogNotice("...");
if (data != NULL) {
DecodeThreadVarsFree(tv, data);
}
SCReturnInt(TM_ECODE_OK);
return TM_ECODE_OK;
}
static TmEcode Decode(ThreadVars *tv, Packet *p, void *data)
{
SCLogNotice("...");
DecodeLinkLayer(tv, data, p->datalink, p, GET_PKT_DATA(p), GET_PKT_LEN(p));
return TM_ECODE_OK;
}
void TmModuleReceiveCiCaptureRegister(int slot)
{
tmm_modules[slot].name = "ReceiveCiCapture";
tmm_modules[slot].ThreadInit = ReceiveThreadInit;
tmm_modules[slot].Func = NULL;
tmm_modules[slot].PktAcqLoop = ReceiveLoop;
tmm_modules[slot].PktAcqBreakLoop = NULL;
tmm_modules[slot].ThreadExitPrintStats = ReceiveThreadExitPrintStats;
tmm_modules[slot].ThreadDeinit = ReceiveThreadDeinit;
tmm_modules[slot].cap_flags = 0;
tmm_modules[slot].flags = TM_FLAG_RECEIVE_TM;
}
void TmModuleDecodeCiCaptureRegister(int slot)
{
tmm_modules[slot].name = "DecodeCiCapture";
tmm_modules[slot].ThreadInit = DecodeThreadInit;
tmm_modules[slot].Func = Decode;
tmm_modules[slot].ThreadExitPrintStats = NULL;
tmm_modules[slot].ThreadDeinit = DecodeThreadDeinit;
tmm_modules[slot].cap_flags = 0;
tmm_modules[slot].flags = TM_FLAG_DECODE_TM;
}

@ -0,0 +1,24 @@
/* Copyright (C) 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.
*/
#ifndef CI_CAPTURE_PLUGIN_SOURCE_H
#define CI_CAPTURE_PLUGIN_SOURCE_H
void TmModuleReceiveCiCaptureRegister(int slot);
void TmModuleDecodeCiCaptureRegister(int slot);
#endif /* CI_CAPTURE_PLUGIN_SOURCE_H */
Loading…
Cancel
Save