From e10d107415b0da4cdcb02aeba6e19906a9decc47 Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Sat, 4 Jul 2020 00:33:08 -0600 Subject: [PATCH] plugins: support for capture plugins Allow a plugin to register itself as a capture source. This isn't that much different than how current sources register, it just happens a little later on during startup. One "slot" is reserved for capture plugins, but multiple plugins implementing a capture can be loaded. The --capture-plugin command line option must be used to tell Suricata which plugin to use. This is still very much a work in progress, but can load PF_RING as a capture plugin. --- src/decode.h | 4 ++++ src/runmode-unix-socket.c | 2 +- src/runmodes.c | 17 ++++++++++++++++- src/runmodes.h | 3 ++- src/suricata-plugin.h | 15 +++++++++++++++ src/suricata.c | 15 +++++++++++++-- src/suricata.h | 3 +++ src/tm-modules.c | 2 ++ src/tm-threads-common.h | 2 ++ src/util-plugin.c | 32 +++++++++++++++++++++++++++++++- src/util-plugin.h | 3 ++- 11 files changed, 91 insertions(+), 7 deletions(-) diff --git a/src/decode.h b/src/decode.h index 0424b487b7..9e868dcc35 100644 --- a/src/decode.h +++ b/src/decode.h @@ -28,6 +28,7 @@ #define COUNTERS #include "suricata-common.h" +#include "suricata-plugin.h" #include "threadvars.h" #include "util-debug.h" #include "decode-events.h" @@ -479,6 +480,9 @@ typedef struct Packet_ WinDivertPacketVars windivert_v; #endif /* WINDIVERT */ + /* A chunk of memory that a plugin can use for its packet vars. */ + uint8_t plugin_v[PLUGIN_VAR_SIZE]; + /** libpcap vars: shared by Pcap Live mode and Pcap File mode */ PcapPacketVars pcap_v; }; diff --git a/src/runmode-unix-socket.c b/src/runmode-unix-socket.c index 656ebc1474..f4bbe1e659 100644 --- a/src/runmode-unix-socket.c +++ b/src/runmode-unix-socket.c @@ -573,7 +573,7 @@ static TmEcode UnixSocketPcapFilesCheck(void *data) PreRunInit(RUNMODE_PCAP_FILE); PreRunPostPrivsDropInit(RUNMODE_PCAP_FILE); - RunModeDispatch(RUNMODE_PCAP_FILE, NULL); + RunModeDispatch(RUNMODE_PCAP_FILE, NULL, NULL, NULL); /* Un-pause all the paused threads */ TmThreadWaitOnThreadInit(); diff --git a/src/runmodes.c b/src/runmodes.c index 688a2496fc..b64f1d4e96 100644 --- a/src/runmodes.c +++ b/src/runmodes.c @@ -38,6 +38,7 @@ #include "runmodes.h" #include "util-unittest.h" #include "util-misc.h" +#include "util-plugin.h" #include "output.h" @@ -54,6 +55,8 @@ #include "flow-bypass.h" #include "counters.h" +#include "suricata-plugin.h" + int debuglog_enabled = 0; int threading_set_cpu_affinity = FALSE; @@ -122,6 +125,8 @@ static const char *RunModeTranslateModeToName(int runmode) #else return "PFRING(DISABLED)"; #endif + case RUNMODE_PLUGIN: + return "PLUGIN"; case RUNMODE_NFQ: return "NFQ"; case RUNMODE_NFLOG: @@ -275,7 +280,8 @@ void RunModeListRunmodes(void) /** */ -void RunModeDispatch(int runmode, const char *custom_mode) +void RunModeDispatch(int runmode, const char *custom_mode, + const char *capture_plugin_name, const char *capture_plugin_args) { char *local_custom_mode = NULL; @@ -301,6 +307,15 @@ void RunModeDispatch(int runmode, const char *custom_mode) custom_mode = RunModeIdsPfringGetDefaultMode(); break; #endif + case RUNMODE_PLUGIN: { + SCCapturePlugin *plugin = SCPluginFindCaptureByName(capture_plugin_name); + if (plugin == NULL) { + FatalError(SC_ERR_PLUGIN, "No capture plugin found with name %s", + capture_plugin_name); + } + custom_mode = (const char *)plugin->GetDefaultMode(); + break; + } case RUNMODE_NFQ: custom_mode = RunModeIpsNFQGetDefaultMode(); break; diff --git a/src/runmodes.h b/src/runmodes.h index b437079009..63b75d2bd6 100644 --- a/src/runmodes.h +++ b/src/runmodes.h @@ -40,6 +40,7 @@ enum RunModes { RUNMODE_NAPATECH, RUNMODE_UNIX_SOCKET, RUNMODE_WINDIVERT, + RUNMODE_PLUGIN, RUNMODE_USER_MAX, /* Last standard running mode */ RUNMODE_LIST_KEYWORDS, RUNMODE_LIST_APP_LAYERS, @@ -77,7 +78,7 @@ char *RunmodeGetActive(void); const char *RunModeGetMainMode(void); void RunModeListRunmodes(void); -void RunModeDispatch(int, const char *); +void RunModeDispatch(int, const char *, const char *capture_plugin_name, const char *capture_plugin_args); void RunModeRegisterRunModes(void); void RunModeRegisterNewRunMode(enum RunModes, const char *, const char *, int (*RunModeFunc)(void)); diff --git a/src/suricata-plugin.h b/src/suricata-plugin.h index 233c5d82e1..c399640da4 100644 --- a/src/suricata-plugin.h +++ b/src/suricata-plugin.h @@ -25,6 +25,12 @@ #include "conf.h" +/** + * The size of the data chunk inside each packet structure a plugin + * has for private data (Packet->plugin_v). + */ +#define PLUGIN_VAR_SIZE 64 + /** * Structure to define a Suricata plugin. */ @@ -50,4 +56,13 @@ typedef struct SCPluginFileType_ { bool SCPluginRegisterFileType(SCPluginFileType *); +typedef struct SCCapturePlugin_ { + char *name; + void (*Init)(const char *args, int plugin_slot, int receive_slot, int decode_slot); + const char *(*GetDefaultMode)(void); + TAILQ_ENTRY(SCCapturePlugin_) entries; +} SCCapturePlugin; + +int SCPluginRegisterCapture(SCCapturePlugin *); + #endif /* __SURICATA_PLUGIN_H */ diff --git a/src/suricata.c b/src/suricata.c index 6c96302e03..372c82a402 100644 --- a/src/suricata.c +++ b/src/suricata.c @@ -1202,6 +1202,9 @@ static TmEcode ParseCommandLine(int argc, char** argv, SCInstance *suri) {"no-random", 0, &g_disable_randomness, 1}, {"strict-rule-keywords", optional_argument, 0, 0}, + {"capture-plugin", required_argument, 0, 0}, + {"cpature-plugin-args", required_argument, 0, 0}, + #ifdef BUILD_UNIX_SOCKET {"unix-socket", optional_argument, 0, 0}, #endif @@ -1293,6 +1296,13 @@ static TmEcode ParseCommandLine(int argc, char** argv, SCInstance *suri) return TM_ECODE_FAILED; #endif /* HAVE_PFRING */ } + else if (strcmp((long_opts[option_index]).name , "capture-plugin") == 0){ + suri->run_mode = RUNMODE_PLUGIN; + suri->capture_plugin_name = optarg; + } + else if (strcmp((long_opts[option_index]).name , "capture-plugin-args") == 0){ + suri->capture_plugin_args = optarg; + } else if (strcmp((long_opts[option_index]).name , "af-packet") == 0) { if (ParseCommandLineAfpacket(suri, optarg) != TM_ECODE_OK) { @@ -2550,7 +2560,7 @@ int PostConfLoadedSetup(SCInstance *suri) FeatureTrackingRegister(); /* must occur prior to output mod registration */ RegisterAllModules(); - SCPluginsLoad(); + SCPluginsLoad(suri->capture_plugin_name, suri->capture_plugin_args); AppLayerHtpNeedFileInspection(); @@ -2792,7 +2802,8 @@ int SuricataMain(int argc, char **argv) } SCSetStartTime(&suricata); - RunModeDispatch(suricata.run_mode, suricata.runmode_custom_mode); + RunModeDispatch(suricata.run_mode, suricata.runmode_custom_mode, + suricata.capture_plugin_name, suricata.capture_plugin_args); if (suricata.run_mode != RUNMODE_UNIX_SOCKET) { UnixManagerThreadSpawnNonRunmode(); } diff --git a/src/suricata.h b/src/suricata.h index 7bd87a0e16..43bb8ab0c5 100644 --- a/src/suricata.h +++ b/src/suricata.h @@ -158,6 +158,9 @@ typedef struct SCInstance_ { const char *progname; /**< pointer to argv[0] */ const char *conf_filename; char *strict_rule_parsing_string; + + const char *capture_plugin_name; + const char *capture_plugin_args; } SCInstance; diff --git a/src/tm-modules.c b/src/tm-modules.c index 9f0ec6e7df..5d0f59d1af 100644 --- a/src/tm-modules.c +++ b/src/tm-modules.c @@ -213,6 +213,8 @@ const char * TmModuleTmmIdToString(TmmId id) CASE_CODE (TMM_DECODEPCAPFILE); CASE_CODE (TMM_RECEIVEPFRING); CASE_CODE (TMM_DECODEPFRING); + CASE_CODE (TMM_RECEIVEPLUGIN); + CASE_CODE (TMM_DECODEPLUGIN); CASE_CODE (TMM_RESPONDREJECT); CASE_CODE (TMM_DECODEIPFW); CASE_CODE (TMM_VERDICTIPFW); diff --git a/src/tm-threads-common.h b/src/tm-threads-common.h index 665d52135c..be8c103c97 100644 --- a/src/tm-threads-common.h +++ b/src/tm-threads-common.h @@ -41,6 +41,8 @@ typedef enum { TMM_DECODEPCAPFILE, TMM_RECEIVEPFRING, TMM_DECODEPFRING, + TMM_RECEIVEPLUGIN, + TMM_DECODEPLUGIN, TMM_RESPONDREJECT, TMM_DECODEIPFW, TMM_VERDICTIPFW, diff --git a/src/util-plugin.c b/src/util-plugin.c index a4c6501eac..8774881601 100644 --- a/src/util-plugin.c +++ b/src/util-plugin.c @@ -26,6 +26,8 @@ static TAILQ_HEAD(, SCPluginFileType_) output_types = TAILQ_HEAD_INITIALIZER(output_types); +static TAILQ_HEAD(, SCCapturePlugin_) capture_plugins = TAILQ_HEAD_INITIALIZER(capture_plugins); + static void InitPlugin(char *path) { void *lib = dlopen(path, RTLD_NOW); @@ -48,7 +50,7 @@ static void InitPlugin(char *path) } } -void SCPluginsLoad(void) +void SCPluginsLoad(const char *capture_plugin_name, const char *capture_plugin_args) { ConfNode *conf = ConfGetNode("plugins"); if (conf == NULL) { @@ -82,6 +84,16 @@ void SCPluginsLoad(void) InitPlugin(plugin->val); } } + + if (run_mode == RUNMODE_PLUGIN) { + SCCapturePlugin *capture = SCPluginFindCaptureByName(capture_plugin_name); + if (capture == NULL) { + FatalError(SC_ERR_PLUGIN, "No capture plugin found with name %s", + capture_plugin_name); + } + capture->Init(capture_plugin_args, RUNMODE_PLUGIN, TMM_RECEIVEPLUGIN, + TMM_DECODEPLUGIN); + } } /** @@ -139,6 +151,24 @@ SCPluginFileType *SCPluginFindFileType(const char *name) return NULL; } +int SCPluginRegisterCapture(SCCapturePlugin *plugin) +{ + TAILQ_INSERT_TAIL(&capture_plugins, plugin, entries); + SCLogNotice("Capture plugin registered: %s", plugin->name); + return 0; +} + +SCCapturePlugin *SCPluginFindCaptureByName(const char *name) +{ + SCCapturePlugin *plugin = NULL; + TAILQ_FOREACH(plugin, &capture_plugins, entries) { + if (strcmp(name, plugin->name) == 0) { + return plugin; + } + } + return plugin; +} + #else void PluginsLoad(void) diff --git a/src/util-plugin.h b/src/util-plugin.h index 55c21e911f..f15821f532 100644 --- a/src/util-plugin.h +++ b/src/util-plugin.h @@ -20,7 +20,8 @@ #include "suricata-plugin.h" -void SCPluginsLoad(void); +void SCPluginsLoad(const char *capture_plugin_name, const char *capture_plugin_args); SCPluginFileType *SCPluginFindFileType(const char *name); +SCCapturePlugin *SCPluginFindCaptureByName(const char *name); #endif /* __UTIL_PLUGIN_H__ */