mirror of
https://github.com/OISF/suricata.git
synced 2026-05-28 04:32:12 -04:00
plugins: add --plugin command line option to load plugins
Add --plugin <PATH> to load an additional plugin from the command line. This is more convenient than "--set plugins.X" especially when you may already have a plugins loaded and you want to load an additional one. Ticket: 8463
This commit is contained in:
parent
aa9c60993b
commit
b3fb3518f5
8 changed files with 103 additions and 68 deletions
8
.github/workflows/builds.yml
vendored
8
.github/workflows/builds.yml
vendored
|
|
@ -187,7 +187,7 @@ jobs:
|
|||
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
|
||||
../../../src/suricata -S /dev/null --plugin ./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"
|
||||
|
||||
|
|
@ -195,7 +195,7 @@ jobs:
|
|||
working-directory: examples/plugins/altemplate
|
||||
run: |
|
||||
cargo build
|
||||
../../../src/suricata -S altemplate.rules --set plugins.0=./target/debug/libsuricata_altemplate.so --runmode=single -l . -c altemplate.yaml -k none -r ../../../rust/src/applayertemplate/template.pcap
|
||||
../../../src/suricata -S altemplate.rules --plugin ./target/debug/libsuricata_altemplate.so --runmode=single -l . -c altemplate.yaml -k none -r ../../../rust/src/applayertemplate/template.pcap
|
||||
cat eve.json | jq -c 'select(.altemplate)'
|
||||
test $(cat eve.json | jq -c 'select(.altemplate)' | wc -l) = "3"
|
||||
# we get 2 alerts and 1 altemplate events
|
||||
|
|
@ -419,7 +419,7 @@ jobs:
|
|||
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
|
||||
../../../src/suricata -S /dev/null --plugin ./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"
|
||||
|
||||
|
|
@ -427,7 +427,7 @@ jobs:
|
|||
working-directory: examples/plugins/altemplate
|
||||
run: |
|
||||
cargo build
|
||||
../../../src/suricata -S altemplate.rules --set plugins.0=./target/debug/libsuricata_altemplate.so --runmode=single -l . -c altemplate.yaml -k none -r ../../../rust/src/applayertemplate/template.pcap
|
||||
../../../src/suricata -S altemplate.rules --plugin ./target/debug/libsuricata_altemplate.so --runmode=single -l . -c altemplate.yaml -k none -r ../../../rust/src/applayertemplate/template.pcap
|
||||
cat eve.json | jq -c 'select(.altemplate)'
|
||||
test $(cat eve.json | jq -c 'select(.altemplate)' | wc -l) = "3"
|
||||
# we get 2 alerts and 1 altemplate events
|
||||
|
|
|
|||
|
|
@ -272,6 +272,14 @@
|
|||
|
||||
.. Advanced options.
|
||||
|
||||
.. option:: --plugin <path>
|
||||
|
||||
Load a plugin from *path* in addition to the plugins listed in the
|
||||
configuration file. This option can be specified multiple times.
|
||||
|
||||
If *path* is a directory, Suricata will attempt to load each
|
||||
``.so`` file in that directory.
|
||||
|
||||
.. option:: --set <key>=<value>
|
||||
|
||||
Set a configuration value. Useful for overriding basic
|
||||
|
|
|
|||
|
|
@ -9,14 +9,15 @@ optionally the `--with-napatech-includes` and
|
|||
## Running
|
||||
```
|
||||
/usr/local/suricata/bin/suricata \
|
||||
--set plugins.0=/usr/local/lib/suricata/napatech.so \
|
||||
--plugin /usr/local/lib/suricata/napatech.so \
|
||||
--capture-plugin=napatech
|
||||
```
|
||||
|
||||
### --set plugins.0=/usr/local/lib/suricata/napatech.so
|
||||
### --plugin /usr/local/lib/suricata/napatech.so
|
||||
|
||||
This command line option tells Suricata about this plugin. This could also
|
||||
be done in `suricata.yaml` with the following section:
|
||||
This command line option tells Suricata about this plugin in addition to any
|
||||
plugins listed in `suricata.yaml`. This could also be done in `suricata.yaml`
|
||||
with the following section:
|
||||
```
|
||||
plugins:
|
||||
- /usr/local/lib/suricata/napatech.so
|
||||
|
|
|
|||
|
|
@ -9,15 +9,16 @@ optionally the `--with-libpfring-includes` and
|
|||
## Running
|
||||
```
|
||||
/usr/local/suricata/bin/suricata \
|
||||
--set plugins.0=/usr/local/lib/suricata/pfring.so \
|
||||
--capture-plugin=pfring-plugin \
|
||||
--plugin /usr/local/lib/suricata/pfring.so \
|
||||
--capture-plugin=pfring \
|
||||
--set pfring.0.interface=eno1
|
||||
```
|
||||
|
||||
### --set plugins.0=/usr/local/lib/suricata/pfring.so
|
||||
### --plugin /usr/local/lib/suricata/pfring.so
|
||||
|
||||
This command line option tells Suricata about this plugin. This could also
|
||||
be done in `suricata.yaml` with the following section:
|
||||
This command line option tells Suricata about this plugin in addition to any
|
||||
plugins listed in `suricata.yaml`. This could also be done in `suricata.yaml`
|
||||
with the following section:
|
||||
```
|
||||
plugins:
|
||||
- /usr/local/lib/suricata/pfring.so
|
||||
|
|
|
|||
|
|
@ -644,6 +644,7 @@ static void PrintUsage(const char *progname)
|
|||
printf("\t--runmode <runmode_id> : specific runmode modification the engine should run. The argument\n"
|
||||
"\t supplied should be the id for the runmode obtained by running\n"
|
||||
"\t --list-runmodes\n");
|
||||
printf("\t--plugin <path> : load plugin in addition to config\n");
|
||||
|
||||
printf("\n Capture and IPS:\n");
|
||||
|
||||
|
|
@ -1382,6 +1383,38 @@ static bool IsLogDirectoryWritable(const char* str)
|
|||
return access(str, W_OK) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper functions to append option values to an array where the
|
||||
* option is allowed multiple times. For example:
|
||||
* - --include
|
||||
* - --plugin
|
||||
*/
|
||||
static void AddCommandLineOptionValue(
|
||||
const char ***values, const char *value, const char *description)
|
||||
{
|
||||
if (*values == NULL) {
|
||||
*values = SCCalloc(2, sizeof(char *));
|
||||
if (*values == NULL) {
|
||||
FatalError("Failed to allocate memory for %s: %s", description, strerror(errno));
|
||||
}
|
||||
(*values)[0] = value;
|
||||
} else {
|
||||
for (int i = 0;; i++) {
|
||||
if ((*values)[i] == NULL) {
|
||||
const char **new_values = SCRealloc(*values, (i + 2) * sizeof(char *));
|
||||
if (new_values == NULL) {
|
||||
FatalError(
|
||||
"Failed to allocate memory for %s: %s", description, strerror(errno));
|
||||
}
|
||||
*values = new_values;
|
||||
(*values)[i] = value;
|
||||
(*values)[i + 1] = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern int g_skip_prefilter;
|
||||
|
||||
TmEcode SCParseCommandLine(int argc, char **argv)
|
||||
|
|
@ -1434,6 +1467,7 @@ TmEcode SCParseCommandLine(int argc, char **argv)
|
|||
{"no-random", 0, &g_disable_randomness, 1},
|
||||
{"strict-rule-keywords", optional_argument, 0, 0},
|
||||
|
||||
{"plugin", required_argument, 0, 0},
|
||||
{"capture-plugin", required_argument, 0, 0},
|
||||
{"capture-plugin-args", required_argument, 0, 0},
|
||||
|
||||
|
|
@ -1550,6 +1584,8 @@ TmEcode SCParseCommandLine(int argc, char **argv)
|
|||
"to pass --enable-pfring to configure when building.");
|
||||
return TM_ECODE_FAILED;
|
||||
#endif /* HAVE_PFRING */
|
||||
} else if (strcmp((long_opts[option_index]).name, "plugin") == 0) {
|
||||
AddCommandLineOptionValue(&suri->additional_plugins, optarg, "additional plugins");
|
||||
} else if (strcmp((long_opts[option_index]).name, "capture-plugin") == 0) {
|
||||
suri->run_mode = RUNMODE_PLUGIN;
|
||||
suri->capture_plugin_name = optarg;
|
||||
|
|
@ -1870,32 +1906,8 @@ TmEcode SCParseCommandLine(int argc, char **argv)
|
|||
FatalError("failed to duplicate 'strict' string");
|
||||
}
|
||||
} else if (strcmp((long_opts[option_index]).name, "include") == 0) {
|
||||
if (suri->additional_configs == NULL) {
|
||||
suri->additional_configs = SCCalloc(2, sizeof(char *));
|
||||
if (suri->additional_configs == NULL) {
|
||||
FatalError(
|
||||
"Failed to allocate memory for additional configuration files: %s",
|
||||
strerror(errno));
|
||||
}
|
||||
suri->additional_configs[0] = optarg;
|
||||
} else {
|
||||
for (int i = 0;; i++) {
|
||||
if (suri->additional_configs[i] == NULL) {
|
||||
const char **additional_configs =
|
||||
SCRealloc(suri->additional_configs, (i + 2) * sizeof(char *));
|
||||
if (additional_configs == NULL) {
|
||||
FatalError("Failed to allocate memory for additional configuration "
|
||||
"files: %s",
|
||||
strerror(errno));
|
||||
} else {
|
||||
suri->additional_configs = additional_configs;
|
||||
}
|
||||
suri->additional_configs[i] = optarg;
|
||||
suri->additional_configs[i + 1] = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
AddCommandLineOptionValue(
|
||||
&suri->additional_configs, optarg, "additional configuration files");
|
||||
} else if (strcmp((long_opts[option_index]).name, "firewall-rules-exclusive") == 0) {
|
||||
if (suri->firewall_rule_file != NULL) {
|
||||
SCLogError("can't have multiple --firewall-rules-exclusive options");
|
||||
|
|
@ -2828,7 +2840,7 @@ int PostConfLoadedSetup(SCInstance *suri)
|
|||
SigTableInit();
|
||||
|
||||
#ifdef HAVE_PLUGINS
|
||||
SCPluginsLoad(suri->capture_plugin_name, suri->capture_plugin_args);
|
||||
SCPluginsLoad(suri->capture_plugin_name, suri->capture_plugin_args, suri->additional_plugins);
|
||||
#endif
|
||||
|
||||
LiveDeviceFinalize(); // must be after EBPF extension registration
|
||||
|
|
|
|||
|
|
@ -176,6 +176,7 @@ typedef struct SCInstance_ {
|
|||
const char *progname; /**< pointer to argv[0] */
|
||||
const char *conf_filename;
|
||||
const char **additional_configs;
|
||||
const char **additional_plugins;
|
||||
char *strict_rule_parsing_string;
|
||||
|
||||
const char *capture_plugin_name;
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ bool RegisterPlugin(SCPlugin *plugin, void *lib)
|
|||
return true;
|
||||
}
|
||||
|
||||
static void InitPlugin(char *path)
|
||||
static void InitPlugin(const char *path)
|
||||
{
|
||||
void *lib = dlopen(path, RTLD_NOW);
|
||||
if (lib == NULL) {
|
||||
|
|
@ -99,37 +99,48 @@ static void InitPlugin(char *path)
|
|||
}
|
||||
}
|
||||
|
||||
void SCPluginsLoad(const char *capture_plugin_name, const char *capture_plugin_args)
|
||||
static void LoadPluginsFromPath(const char *plugin_path)
|
||||
{
|
||||
SCConfNode *conf = SCConfGetNode("plugins");
|
||||
if (conf == NULL) {
|
||||
struct stat statbuf;
|
||||
if (stat(plugin_path, &statbuf) == -1) {
|
||||
SCLogError("Bad plugin path: %s: %s", plugin_path, strerror(errno));
|
||||
return;
|
||||
}
|
||||
SCConfNode *plugin = NULL;
|
||||
TAILQ_FOREACH(plugin, &conf->head, next) {
|
||||
struct stat statbuf;
|
||||
if (stat(plugin->val, &statbuf) == -1) {
|
||||
SCLogError("Bad plugin path: %s: %s", plugin->val, strerror(errno));
|
||||
continue;
|
||||
if (S_ISDIR(statbuf.st_mode)) {
|
||||
// coverity[toctou : FALSE]
|
||||
DIR *dir = opendir(plugin_path);
|
||||
if (dir == NULL) {
|
||||
SCLogError("Failed to open plugin directory %s: %s", plugin_path, strerror(errno));
|
||||
return;
|
||||
}
|
||||
if (S_ISDIR(statbuf.st_mode)) {
|
||||
// coverity[toctou : FALSE]
|
||||
DIR *dir = opendir(plugin->val);
|
||||
if (dir == NULL) {
|
||||
SCLogError("Failed to open plugin directory %s: %s", plugin->val, strerror(errno));
|
||||
continue;
|
||||
struct dirent *entry = NULL;
|
||||
char path[PATH_MAX];
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
if (strstr(entry->d_name, ".so") != NULL) {
|
||||
snprintf(path, sizeof(path), "%s/%s", plugin_path, entry->d_name);
|
||||
InitPlugin(path);
|
||||
}
|
||||
struct dirent *entry = NULL;
|
||||
char path[PATH_MAX];
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
if (strstr(entry->d_name, ".so") != NULL) {
|
||||
snprintf(path, sizeof(path), "%s/%s", plugin->val, entry->d_name);
|
||||
InitPlugin(path);
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
} else {
|
||||
InitPlugin(plugin->val);
|
||||
}
|
||||
closedir(dir);
|
||||
} else {
|
||||
InitPlugin(plugin_path);
|
||||
}
|
||||
}
|
||||
|
||||
void SCPluginsLoad(const char *capture_plugin_name, const char *capture_plugin_args,
|
||||
const char **additional_plugins)
|
||||
{
|
||||
SCConfNode *conf = SCConfGetNode("plugins");
|
||||
if (conf != NULL) {
|
||||
SCConfNode *plugin = NULL;
|
||||
TAILQ_FOREACH (plugin, &conf->head, next) {
|
||||
LoadPluginsFromPath(plugin->val);
|
||||
}
|
||||
}
|
||||
|
||||
if (additional_plugins != NULL) {
|
||||
for (int i = 0; additional_plugins[i] != NULL; i++) {
|
||||
LoadPluginsFromPath(additional_plugins[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@
|
|||
|
||||
#include "suricata-plugin.h"
|
||||
|
||||
void SCPluginsLoad(const char *capture_plugin_name, const char *capture_plugin_args);
|
||||
void SCPluginsLoad(const char *capture_plugin_name, const char *capture_plugin_args,
|
||||
const char **additional_plugins);
|
||||
SCCapturePlugin *SCPluginFindCaptureByName(const char *name);
|
||||
|
||||
bool RegisterPlugin(SCPlugin *, void *);
|
||||
|
|
|
|||
Loading…
Reference in a new issue