Skip to content

Commit f9578ed

Browse files
committed
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
1 parent 8d254f5 commit f9578ed

8 files changed

Lines changed: 103 additions & 68 deletions

File tree

.github/workflows/builds.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -187,15 +187,15 @@ jobs:
187187
working-directory: examples/plugins/ci-capture
188188
run: |
189189
make
190-
../../../src/suricata -S /dev/null --set plugins.0=./capture.so --capture-plugin=ci-capture --runmode=single -l . -c ../../../suricata.yaml
190+
../../../src/suricata -S /dev/null --plugin ./capture.so --capture-plugin=ci-capture --runmode=single -l . -c ../../../suricata.yaml
191191
cat eve.json | jq -c 'select(.dns)'
192192
test $(cat eve.json | jq -c 'select(.dns)' | wc -l) = "1"
193193
194194
- name: Test app-layer plugin
195195
working-directory: examples/plugins/altemplate
196196
run: |
197197
cargo build
198-
../../../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
198+
../../../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
199199
cat eve.json | jq -c 'select(.altemplate)'
200200
test $(cat eve.json | jq -c 'select(.altemplate)' | wc -l) = "3"
201201
# we get 2 alerts and 1 altemplate events
@@ -419,15 +419,15 @@ jobs:
419419
working-directory: examples/plugins/ci-capture
420420
run: |
421421
make
422-
../../../src/suricata -S /dev/null --set plugins.0=./capture.so --capture-plugin=ci-capture --runmode=single -l . -c ../../../suricata.yaml
422+
../../../src/suricata -S /dev/null --plugin ./capture.so --capture-plugin=ci-capture --runmode=single -l . -c ../../../suricata.yaml
423423
cat eve.json | jq -c 'select(.dns)'
424424
test $(cat eve.json | jq -c 'select(.dns)' | wc -l) = "1"
425425
426426
- name: Test app-layer plugin
427427
working-directory: examples/plugins/altemplate
428428
run: |
429429
cargo build
430-
../../../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
430+
../../../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
431431
cat eve.json | jq -c 'select(.altemplate)'
432432
test $(cat eve.json | jq -c 'select(.altemplate)' | wc -l) = "3"
433433
# we get 2 alerts and 1 altemplate events

doc/userguide/partials/options.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,14 @@
272272

273273
.. Advanced options.
274274
275+
.. option:: --plugin <path>
276+
277+
Load a plugin from *path* in addition to the plugins listed in the
278+
configuration file. This option can be specified multiple times.
279+
280+
If *path* is a directory, Suricata will attempt to load each
281+
``.so`` file in that directory.
282+
275283
.. option:: --set <key>=<value>
276284

277285
Set a configuration value. Useful for overriding basic

plugins/napatech/README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@ optionally the `--with-napatech-includes` and
99
## Running
1010
```
1111
/usr/local/suricata/bin/suricata \
12-
--set plugins.0=/usr/local/lib/suricata/napatech.so \
12+
--plugin /usr/local/lib/suricata/napatech.so \
1313
--capture-plugin=napatech
1414
```
1515

16-
### --set plugins.0=/usr/local/lib/suricata/napatech.so
16+
### --plugin /usr/local/lib/suricata/napatech.so
1717

18-
This command line option tells Suricata about this plugin. This could also
19-
be done in `suricata.yaml` with the following section:
18+
This command line option tells Suricata about this plugin in addition to any
19+
plugins listed in `suricata.yaml`. This could also be done in `suricata.yaml`
20+
with the following section:
2021
```
2122
plugins:
2223
- /usr/local/lib/suricata/napatech.so

plugins/pfring/README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,16 @@ optionally the `--with-libpfring-includes` and
99
## Running
1010
```
1111
/usr/local/suricata/bin/suricata \
12-
--set plugins.0=/usr/local/lib/suricata/pfring.so \
13-
--capture-plugin=pfring-plugin \
12+
--plugin /usr/local/lib/suricata/pfring.so \
13+
--capture-plugin=pfring \
1414
--set pfring.0.interface=eno1
1515
```
1616

17-
### --set plugins.0=/usr/local/lib/suricata/pfring.so
17+
### --plugin /usr/local/lib/suricata/pfring.so
1818

19-
This command line option tells Suricata about this plugin. This could also
20-
be done in `suricata.yaml` with the following section:
19+
This command line option tells Suricata about this plugin in addition to any
20+
plugins listed in `suricata.yaml`. This could also be done in `suricata.yaml`
21+
with the following section:
2122
```
2223
plugins:
2324
- /usr/local/lib/suricata/pfring.so

src/suricata.c

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,7 @@ static void PrintUsage(const char *progname)
644644
printf("\t--runmode <runmode_id> : specific runmode modification the engine should run. The argument\n"
645645
"\t supplied should be the id for the runmode obtained by running\n"
646646
"\t --list-runmodes\n");
647+
printf("\t--plugin <path> : load plugin in addition to config\n");
647648

648649
printf("\n Capture and IPS:\n");
649650

@@ -1382,6 +1383,38 @@ static bool IsLogDirectoryWritable(const char* str)
13821383
return access(str, W_OK) == 0;
13831384
}
13841385

1386+
/**
1387+
* Helper functions to append option values to an array where the
1388+
* option is allowed multiple times. For example:
1389+
* - --include
1390+
* - --plugin
1391+
*/
1392+
static void AddCommandLineOptionValue(
1393+
const char ***values, const char *value, const char *description)
1394+
{
1395+
if (*values == NULL) {
1396+
*values = SCCalloc(2, sizeof(char *));
1397+
if (*values == NULL) {
1398+
FatalError("Failed to allocate memory for %s: %s", description, strerror(errno));
1399+
}
1400+
(*values)[0] = value;
1401+
} else {
1402+
for (int i = 0;; i++) {
1403+
if ((*values)[i] == NULL) {
1404+
const char **new_values = SCRealloc(*values, (i + 2) * sizeof(char *));
1405+
if (new_values == NULL) {
1406+
FatalError(
1407+
"Failed to allocate memory for %s: %s", description, strerror(errno));
1408+
}
1409+
*values = new_values;
1410+
(*values)[i] = value;
1411+
(*values)[i + 1] = NULL;
1412+
break;
1413+
}
1414+
}
1415+
}
1416+
}
1417+
13851418
extern int g_skip_prefilter;
13861419

13871420
TmEcode SCParseCommandLine(int argc, char **argv)
@@ -1434,6 +1467,7 @@ TmEcode SCParseCommandLine(int argc, char **argv)
14341467
{"no-random", 0, &g_disable_randomness, 1},
14351468
{"strict-rule-keywords", optional_argument, 0, 0},
14361469

1470+
{"plugin", required_argument, 0, 0},
14371471
{"capture-plugin", required_argument, 0, 0},
14381472
{"capture-plugin-args", required_argument, 0, 0},
14391473

@@ -1550,6 +1584,8 @@ TmEcode SCParseCommandLine(int argc, char **argv)
15501584
"to pass --enable-pfring to configure when building.");
15511585
return TM_ECODE_FAILED;
15521586
#endif /* HAVE_PFRING */
1587+
} else if (strcmp((long_opts[option_index]).name, "plugin") == 0) {
1588+
AddCommandLineOptionValue(&suri->additional_plugins, optarg, "additional plugins");
15531589
} else if (strcmp((long_opts[option_index]).name, "capture-plugin") == 0) {
15541590
suri->run_mode = RUNMODE_PLUGIN;
15551591
suri->capture_plugin_name = optarg;
@@ -1870,32 +1906,8 @@ TmEcode SCParseCommandLine(int argc, char **argv)
18701906
FatalError("failed to duplicate 'strict' string");
18711907
}
18721908
} else if (strcmp((long_opts[option_index]).name, "include") == 0) {
1873-
if (suri->additional_configs == NULL) {
1874-
suri->additional_configs = SCCalloc(2, sizeof(char *));
1875-
if (suri->additional_configs == NULL) {
1876-
FatalError(
1877-
"Failed to allocate memory for additional configuration files: %s",
1878-
strerror(errno));
1879-
}
1880-
suri->additional_configs[0] = optarg;
1881-
} else {
1882-
for (int i = 0;; i++) {
1883-
if (suri->additional_configs[i] == NULL) {
1884-
const char **additional_configs =
1885-
SCRealloc(suri->additional_configs, (i + 2) * sizeof(char *));
1886-
if (additional_configs == NULL) {
1887-
FatalError("Failed to allocate memory for additional configuration "
1888-
"files: %s",
1889-
strerror(errno));
1890-
} else {
1891-
suri->additional_configs = additional_configs;
1892-
}
1893-
suri->additional_configs[i] = optarg;
1894-
suri->additional_configs[i + 1] = NULL;
1895-
break;
1896-
}
1897-
}
1898-
}
1909+
AddCommandLineOptionValue(
1910+
&suri->additional_configs, optarg, "additional configuration files");
18991911
} else if (strcmp((long_opts[option_index]).name, "firewall-rules-exclusive") == 0) {
19001912
if (suri->firewall_rule_file != NULL) {
19011913
SCLogError("can't have multiple --firewall-rules-exclusive options");
@@ -2828,7 +2840,7 @@ int PostConfLoadedSetup(SCInstance *suri)
28282840
SigTableInit();
28292841

28302842
#ifdef HAVE_PLUGINS
2831-
SCPluginsLoad(suri->capture_plugin_name, suri->capture_plugin_args);
2843+
SCPluginsLoad(suri->capture_plugin_name, suri->capture_plugin_args, suri->additional_plugins);
28322844
#endif
28332845

28342846
LiveDeviceFinalize(); // must be after EBPF extension registration

src/suricata.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ typedef struct SCInstance_ {
176176
const char *progname; /**< pointer to argv[0] */
177177
const char *conf_filename;
178178
const char **additional_configs;
179+
const char **additional_plugins;
179180
char *strict_rule_parsing_string;
180181

181182
const char *capture_plugin_name;

src/util-plugin.c

Lines changed: 38 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ bool RegisterPlugin(SCPlugin *plugin, void *lib)
7676
return true;
7777
}
7878

79-
static void InitPlugin(char *path)
79+
static void InitPlugin(const char *path)
8080
{
8181
void *lib = dlopen(path, RTLD_NOW);
8282
if (lib == NULL) {
@@ -99,37 +99,48 @@ static void InitPlugin(char *path)
9999
}
100100
}
101101

102-
void SCPluginsLoad(const char *capture_plugin_name, const char *capture_plugin_args)
102+
static void LoadPluginsFromPath(const char *plugin_path)
103103
{
104-
SCConfNode *conf = SCConfGetNode("plugins");
105-
if (conf == NULL) {
104+
struct stat statbuf;
105+
if (stat(plugin_path, &statbuf) == -1) {
106+
SCLogError("Bad plugin path: %s: %s", plugin_path, strerror(errno));
106107
return;
107108
}
108-
SCConfNode *plugin = NULL;
109-
TAILQ_FOREACH(plugin, &conf->head, next) {
110-
struct stat statbuf;
111-
if (stat(plugin->val, &statbuf) == -1) {
112-
SCLogError("Bad plugin path: %s: %s", plugin->val, strerror(errno));
113-
continue;
109+
if (S_ISDIR(statbuf.st_mode)) {
110+
// coverity[toctou : FALSE]
111+
DIR *dir = opendir(plugin_path);
112+
if (dir == NULL) {
113+
SCLogError("Failed to open plugin directory %s: %s", plugin_path, strerror(errno));
114+
return;
114115
}
115-
if (S_ISDIR(statbuf.st_mode)) {
116-
// coverity[toctou : FALSE]
117-
DIR *dir = opendir(plugin->val);
118-
if (dir == NULL) {
119-
SCLogError("Failed to open plugin directory %s: %s", plugin->val, strerror(errno));
120-
continue;
116+
struct dirent *entry = NULL;
117+
char path[PATH_MAX];
118+
while ((entry = readdir(dir)) != NULL) {
119+
if (strstr(entry->d_name, ".so") != NULL) {
120+
snprintf(path, sizeof(path), "%s/%s", plugin_path, entry->d_name);
121+
InitPlugin(path);
121122
}
122-
struct dirent *entry = NULL;
123-
char path[PATH_MAX];
124-
while ((entry = readdir(dir)) != NULL) {
125-
if (strstr(entry->d_name, ".so") != NULL) {
126-
snprintf(path, sizeof(path), "%s/%s", plugin->val, entry->d_name);
127-
InitPlugin(path);
128-
}
129-
}
130-
closedir(dir);
131-
} else {
132-
InitPlugin(plugin->val);
123+
}
124+
closedir(dir);
125+
} else {
126+
InitPlugin(plugin_path);
127+
}
128+
}
129+
130+
void SCPluginsLoad(const char *capture_plugin_name, const char *capture_plugin_args,
131+
const char **additional_plugins)
132+
{
133+
SCConfNode *conf = SCConfGetNode("plugins");
134+
if (conf != NULL) {
135+
SCConfNode *plugin = NULL;
136+
TAILQ_FOREACH (plugin, &conf->head, next) {
137+
LoadPluginsFromPath(plugin->val);
138+
}
139+
}
140+
141+
if (additional_plugins != NULL) {
142+
for (int i = 0; additional_plugins[i] != NULL; i++) {
143+
LoadPluginsFromPath(additional_plugins[i]);
133144
}
134145
}
135146

src/util-plugin.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020

2121
#include "suricata-plugin.h"
2222

23-
void SCPluginsLoad(const char *capture_plugin_name, const char *capture_plugin_args);
23+
void SCPluginsLoad(const char *capture_plugin_name, const char *capture_plugin_args,
24+
const char **additional_plugins);
2425
SCCapturePlugin *SCPluginFindCaptureByName(const char *name);
2526

2627
bool RegisterPlugin(SCPlugin *, void *);

0 commit comments

Comments
 (0)