/src/suricata8/src/runmodes.c
Line | Count | Source |
1 | | /* Copyright (C) 2007-2024 Open Information Security Foundation |
2 | | * |
3 | | * You can copy, redistribute or modify this Program under the terms of |
4 | | * the GNU General Public License version 2 as published by the Free |
5 | | * Software Foundation. |
6 | | * |
7 | | * This program is distributed in the hope that it will be useful, |
8 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | | * GNU General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU General Public License |
13 | | * version 2 along with this program; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
15 | | * 02110-1301, USA. |
16 | | */ |
17 | | |
18 | | /** \file |
19 | | * |
20 | | * \author Victor Julien <victor@inliniac.net> |
21 | | * |
22 | | * Pre-cooked threading runmodes. |
23 | | */ |
24 | | |
25 | | #include "suricata-common.h" |
26 | | #include "detect-engine.h" |
27 | | #include "app-layer-parser.h" |
28 | | #include "util-debug.h" |
29 | | #include "util-affinity.h" |
30 | | #include "conf.h" |
31 | | #include "log-flush.h" |
32 | | #include "runmodes.h" |
33 | | #include "runmode-af-packet.h" |
34 | | #include "runmode-af-xdp.h" |
35 | | #include "runmode-dpdk.h" |
36 | | #include "runmode-erf-dag.h" |
37 | | #include "runmode-erf-file.h" |
38 | | #include "runmode-ipfw.h" |
39 | | #include "runmode-lib.h" |
40 | | #include "runmode-netmap.h" |
41 | | #include "runmode-nflog.h" |
42 | | #include "runmode-nfq.h" |
43 | | #include "runmode-pcap.h" |
44 | | #include "runmode-pcap-file.h" |
45 | | #include "runmode-unix-socket.h" |
46 | | #include "runmode-windivert.h" |
47 | | #include "util-unittest.h" |
48 | | #include "util-misc.h" |
49 | | #include "util-plugin.h" |
50 | | |
51 | | #include "output.h" |
52 | | |
53 | | #include "tmqh-flow.h" |
54 | | #include "flow-manager.h" |
55 | | #include "flow-bypass.h" |
56 | | #include "counters.h" |
57 | | |
58 | | #include "suricata-plugin.h" |
59 | | #include "util-device-private.h" |
60 | | |
61 | | int debuglog_enabled = 0; |
62 | | bool threading_set_cpu_affinity = false; |
63 | | uint64_t threading_set_stack_size = 0; |
64 | | |
65 | | /* Runmode Global Thread Names */ |
66 | | const char *thread_name_autofp = "RX"; |
67 | | const char *thread_name_single = "W"; |
68 | | const char *thread_name_workers = "W"; |
69 | | const char *thread_name_verdict = "TX"; |
70 | | const char *thread_name_flow_mgr = "FM"; |
71 | | const char *thread_name_flow_rec = "FR"; |
72 | | const char *thread_name_flow_bypass = "FB"; |
73 | | const char *thread_name_unix_socket = "US"; |
74 | | const char *thread_name_detect_loader = "DL"; |
75 | | const char *thread_name_counter_stats = "CS"; |
76 | | const char *thread_name_counter_wakeup = "CW"; |
77 | | const char *thread_name_heartbeat = "HB"; |
78 | | |
79 | | /** |
80 | | * \brief Holds description for a runmode. |
81 | | */ |
82 | | typedef struct RunMode_ { |
83 | | /* the runmode type */ |
84 | | enum SCRunModes runmode; |
85 | | const char *name; |
86 | | const char *description; |
87 | | /* runmode function */ |
88 | | int (*RunModeFunc)(void); |
89 | | int (*RunModeIsIPSEnabled)(void); |
90 | | } RunMode; |
91 | | |
92 | | typedef struct RunModes_ { |
93 | | int cnt; |
94 | | RunMode *runmodes; |
95 | | } RunModes; |
96 | | |
97 | | static RunModes runmodes[RUNMODE_USER_MAX]; |
98 | | |
99 | | static char *active_runmode; |
100 | | |
101 | | /* free list for our outputs */ |
102 | | typedef struct OutputFreeList_ { |
103 | | OutputModule *output_module; |
104 | | OutputCtx *output_ctx; |
105 | | |
106 | | TAILQ_ENTRY(OutputFreeList_) entries; |
107 | | } OutputFreeList; |
108 | | static TAILQ_HEAD(, OutputFreeList_) output_free_list = |
109 | | TAILQ_HEAD_INITIALIZER(output_free_list); |
110 | | |
111 | | /** |
112 | | * \internal |
113 | | * \brief Translate a runmode mode to a printable string. |
114 | | * |
115 | | * \param runmode Runmode to be converted into a printable string. |
116 | | * |
117 | | * \retval string Printable string. |
118 | | */ |
119 | | static const char *RunModeTranslateModeToName(int runmode) |
120 | 0 | { |
121 | 0 | switch (runmode) { |
122 | 0 | case RUNMODE_PCAP_DEV: |
123 | 0 | return "PCAP_DEV"; |
124 | 0 | case RUNMODE_PCAP_FILE: |
125 | 0 | return "PCAP_FILE"; |
126 | 0 | case RUNMODE_PLUGIN: |
127 | 0 | return "PLUGIN"; |
128 | 0 | case RUNMODE_NFQ: |
129 | 0 | return "NFQ"; |
130 | 0 | case RUNMODE_NFLOG: |
131 | 0 | return "NFLOG"; |
132 | 0 | case RUNMODE_IPFW: |
133 | 0 | return "IPFW"; |
134 | 0 | case RUNMODE_ERF_FILE: |
135 | 0 | return "ERF_FILE"; |
136 | 0 | case RUNMODE_DAG: |
137 | 0 | return "ERF_DAG"; |
138 | 0 | case RUNMODE_UNITTEST: |
139 | 0 | return "UNITTEST"; |
140 | 0 | case RUNMODE_AFP_DEV: |
141 | 0 | return "AF_PACKET_DEV"; |
142 | 0 | case RUNMODE_AFXDP_DEV: |
143 | 0 | return "AF_XDP_DEV"; |
144 | 0 | case RUNMODE_NETMAP: |
145 | | #ifdef HAVE_NETMAP |
146 | | return "NETMAP"; |
147 | | #else |
148 | 0 | return "NETMAP(DISABLED)"; |
149 | 0 | #endif |
150 | 0 | case RUNMODE_UNIX_SOCKET: |
151 | 0 | return "UNIX_SOCKET"; |
152 | 0 | case RUNMODE_WINDIVERT: |
153 | | #ifdef WINDIVERT |
154 | | return "WINDIVERT"; |
155 | | #else |
156 | 0 | return "WINDIVERT(DISABLED)"; |
157 | 0 | #endif |
158 | 0 | case RUNMODE_DPDK: |
159 | | #ifdef HAVE_DPDK |
160 | | return "DPDK"; |
161 | | #else |
162 | 0 | return "DPDK(DISABLED)"; |
163 | 0 | #endif |
164 | 0 | case RUNMODE_LIB: |
165 | 0 | return "LIB"; |
166 | | |
167 | 0 | default: |
168 | 0 | FatalError("Unknown runtime mode. Aborting"); |
169 | 0 | } |
170 | 0 | } |
171 | | |
172 | | /** |
173 | | * \internal |
174 | | * \brief Dispatcher function for runmodes. Calls the required runmode function |
175 | | * based on runmode + runmode_custom_id. |
176 | | * |
177 | | * \param runmode The runmode type. |
178 | | * \param runmode_custom_id The runmode custom id. |
179 | | */ |
180 | | static RunMode *RunModeGetCustomMode(enum SCRunModes runmode, const char *custom_mode) |
181 | 607 | { |
182 | 607 | if (runmode < RUNMODE_USER_MAX) { |
183 | 1.04k | for (int i = 0; i < runmodes[runmode].cnt; i++) { |
184 | 447 | if (strcmp(runmodes[runmode].runmodes[i].name, custom_mode) == 0) |
185 | 9 | return &runmodes[runmode].runmodes[i]; |
186 | 447 | } |
187 | 607 | } |
188 | 598 | return NULL; |
189 | 607 | } |
190 | | |
191 | | |
192 | | /** |
193 | | * Return the running mode |
194 | | * |
195 | | * The returned string must not be freed. |
196 | | * |
197 | | * \return a string containing the current running mode |
198 | | */ |
199 | | char *RunmodeGetActive(void) |
200 | 0 | { |
201 | 0 | return active_runmode; |
202 | 0 | } |
203 | | |
204 | | bool RunmodeIsWorkers(void) |
205 | 0 | { |
206 | 0 | return RunmodeGetActive() && (strcmp(RunmodeGetActive(), "workers") == 0); |
207 | 0 | } |
208 | | |
209 | | bool RunmodeIsAutofp(void) |
210 | 0 | { |
211 | 0 | return RunmodeGetActive() && (strcmp(RunmodeGetActive(), "autofp") == 0); |
212 | 0 | } |
213 | | |
214 | | /** |
215 | | * Return the running mode |
216 | | * |
217 | | * The returned string must not be freed. |
218 | | * |
219 | | * \return a string containing the current running mode |
220 | | */ |
221 | | const char *RunModeGetMainMode(void) |
222 | 0 | { |
223 | 0 | int mainmode = SCRunmodeGet(); |
224 | |
|
225 | 0 | return RunModeTranslateModeToName(mainmode); |
226 | 0 | } |
227 | | |
228 | | /** |
229 | | * \brief Register all runmodes in the engine. |
230 | | */ |
231 | | void RunModeRegisterRunModes(void) |
232 | 20 | { |
233 | 20 | memset(runmodes, 0, sizeof(runmodes)); |
234 | | |
235 | 20 | RunModeIdsPcapRegister(); |
236 | 20 | RunModeFilePcapRegister(); |
237 | 20 | RunModeIpsNFQRegister(); |
238 | 20 | RunModeIpsIPFWRegister(); |
239 | 20 | RunModeErfFileRegister(); |
240 | 20 | RunModeErfDagRegister(); |
241 | 20 | RunModeIdsAFPRegister(); |
242 | 20 | RunModeIdsAFXDPRegister(); |
243 | 20 | RunModeIdsNetmapRegister(); |
244 | 20 | RunModeIdsNflogRegister(); |
245 | 20 | RunModeUnixSocketRegister(); |
246 | 20 | RunModeIpsWinDivertRegister(); |
247 | 20 | RunModeDpdkRegister(); |
248 | 20 | SCRunModeLibIdsRegister(); |
249 | | #ifdef UNITTESTS |
250 | | UtRunModeRegister(); |
251 | | #endif |
252 | 20 | } |
253 | | |
254 | | /** |
255 | | * \brief Lists all registered runmodes. |
256 | | */ |
257 | | void RunModeListRunmodes(void) |
258 | 0 | { |
259 | 0 | printf("------------------------------------- Runmodes -------------------" |
260 | 0 | "-----------------------\n"); |
261 | |
|
262 | 0 | printf("| %-17s | %-17s | %-10s \n", |
263 | 0 | "RunMode Type", "Custom Mode ", "Description"); |
264 | 0 | printf("|-----------------------------------------------------------------" |
265 | 0 | "-----------------------\n"); |
266 | 0 | int i = RUNMODE_UNKNOWN + 1; |
267 | 0 | int j = 0; |
268 | 0 | for ( ; i < RUNMODE_USER_MAX; i++) { |
269 | 0 | int mode_displayed = 0; |
270 | 0 | for (j = 0; j < runmodes[i].cnt; j++) { |
271 | 0 | if (mode_displayed == 1) { |
272 | 0 | printf("| ----------------------------------------------" |
273 | 0 | "-----------------------\n"); |
274 | 0 | RunMode *runmode = &runmodes[i].runmodes[j]; |
275 | 0 | printf("| %-17s | %-17s | %-27s \n", |
276 | 0 | "", |
277 | 0 | runmode->name, |
278 | 0 | runmode->description); |
279 | 0 | } else { |
280 | 0 | RunMode *runmode = &runmodes[i].runmodes[j]; |
281 | 0 | printf("| %-17s | %-17s | %-27s \n", |
282 | 0 | RunModeTranslateModeToName(runmode->runmode), |
283 | 0 | runmode->name, |
284 | 0 | runmode->description); |
285 | 0 | } |
286 | 0 | if (mode_displayed == 0) |
287 | 0 | mode_displayed = 1; |
288 | 0 | } |
289 | 0 | if (mode_displayed == 1) { |
290 | 0 | printf("|-----------------------------------------------------------------" |
291 | 0 | "-----------------------\n"); |
292 | 0 | } |
293 | 0 | } |
294 | 0 | } |
295 | | |
296 | | static const char *RunModeGetConfOrDefault(int capture_mode, const char *capture_plugin_name) |
297 | 4 | { |
298 | 4 | const char *custom_mode = NULL; |
299 | 4 | const char *val = NULL; |
300 | 4 | if (SCConfGet("runmode", &val) != 1) { |
301 | 4 | custom_mode = NULL; |
302 | 4 | } else { |
303 | 0 | custom_mode = val; |
304 | 0 | } |
305 | | |
306 | 4 | if ((custom_mode == NULL) || (strcmp(custom_mode, "auto") == 0)) { |
307 | 4 | switch (capture_mode) { |
308 | 0 | case RUNMODE_PCAP_DEV: |
309 | 0 | custom_mode = RunModeIdsGetDefaultMode(); |
310 | 0 | break; |
311 | 4 | case RUNMODE_PCAP_FILE: |
312 | 4 | custom_mode = RunModeFilePcapGetDefaultMode(); |
313 | 4 | break; |
314 | 0 | case RUNMODE_PLUGIN: { |
315 | 0 | #ifdef HAVE_PLUGINS |
316 | 0 | SCCapturePlugin *plugin = SCPluginFindCaptureByName(capture_plugin_name); |
317 | 0 | if (plugin == NULL) { |
318 | 0 | FatalError("No capture plugin found with name %s", capture_plugin_name); |
319 | 0 | } |
320 | 0 | custom_mode = (const char *)plugin->GetDefaultMode(); |
321 | 0 | #endif |
322 | 0 | break; |
323 | 0 | } |
324 | 0 | case RUNMODE_NFQ: |
325 | 0 | custom_mode = RunModeIpsNFQGetDefaultMode(); |
326 | 0 | break; |
327 | 0 | case RUNMODE_IPFW: |
328 | 0 | custom_mode = RunModeIpsIPFWGetDefaultMode(); |
329 | 0 | break; |
330 | 0 | case RUNMODE_ERF_FILE: |
331 | 0 | custom_mode = RunModeErfFileGetDefaultMode(); |
332 | 0 | break; |
333 | 0 | case RUNMODE_DAG: |
334 | 0 | custom_mode = RunModeErfDagGetDefaultMode(); |
335 | 0 | break; |
336 | 0 | case RUNMODE_AFP_DEV: |
337 | 0 | custom_mode = RunModeAFPGetDefaultMode(); |
338 | 0 | break; |
339 | 0 | case RUNMODE_AFXDP_DEV: |
340 | 0 | custom_mode = RunModeAFXDPGetDefaultMode(); |
341 | 0 | break; |
342 | 0 | case RUNMODE_NETMAP: |
343 | 0 | custom_mode = RunModeNetmapGetDefaultMode(); |
344 | 0 | break; |
345 | 0 | case RUNMODE_UNIX_SOCKET: |
346 | 0 | custom_mode = RunModeUnixSocketGetDefaultMode(); |
347 | 0 | break; |
348 | 0 | case RUNMODE_NFLOG: |
349 | 0 | custom_mode = RunModeIdsNflogGetDefaultMode(); |
350 | 0 | break; |
351 | | #ifdef WINDIVERT |
352 | | case RUNMODE_WINDIVERT: |
353 | | custom_mode = RunModeIpsWinDivertGetDefaultMode(); |
354 | | break; |
355 | | #endif |
356 | | #ifdef HAVE_DPDK |
357 | | case RUNMODE_DPDK: |
358 | | custom_mode = RunModeDpdkGetDefaultMode(); |
359 | | break; |
360 | | #endif |
361 | 0 | case RUNMODE_LIB: |
362 | 0 | custom_mode = SCRunModeLibGetDefaultMode(); |
363 | 0 | break; |
364 | 0 | default: |
365 | 0 | return NULL; |
366 | 4 | } |
367 | 4 | } else { |
368 | | /* Add compatibility with old 'worker' name */ |
369 | 0 | if (!strcmp("worker", custom_mode)) { |
370 | 0 | SCLogWarning("'worker' mode have been renamed " |
371 | 0 | "to 'workers', please modify your setup."); |
372 | 0 | custom_mode = "workers"; |
373 | 0 | } |
374 | 0 | } |
375 | | |
376 | 4 | return custom_mode; |
377 | 4 | } |
378 | | |
379 | | int RunModeEngineIsIPS(int capture_mode, const char *runmode, const char *capture_plugin_name) |
380 | 11 | { |
381 | 11 | if (runmode == NULL) { |
382 | 11 | runmode = RunModeGetConfOrDefault(capture_mode, capture_plugin_name); |
383 | 11 | if (runmode == NULL) // non-standard runmode |
384 | 2 | return 0; |
385 | 11 | } |
386 | | |
387 | 9 | RunMode *mode = RunModeGetCustomMode(capture_mode, runmode); |
388 | 9 | if (mode == NULL) { |
389 | 0 | return 0; |
390 | 0 | } |
391 | | |
392 | 9 | int ips_enabled = 0; |
393 | 9 | if (mode->RunModeIsIPSEnabled != NULL) { |
394 | 0 | ips_enabled = mode->RunModeIsIPSEnabled(); |
395 | 0 | if (ips_enabled == 1) { |
396 | 0 | extern uint16_t g_livedev_mask; |
397 | 0 | if (g_livedev_mask != 0 && LiveGetDeviceCount() > 0) { |
398 | 0 | SCLogWarning("disabling livedev.use-for-tracking with IPS mode. See ticket #6726."); |
399 | 0 | g_livedev_mask = 0; |
400 | 0 | } |
401 | 0 | } |
402 | 0 | } |
403 | | |
404 | 9 | return ips_enabled; |
405 | 9 | } |
406 | | |
407 | | /** |
408 | | */ |
409 | | void RunModeDispatch(int runmode, const char *custom_mode, const char *capture_plugin_name, |
410 | | const char *capture_plugin_args) |
411 | 0 | { |
412 | 0 | char *local_custom_mode = NULL; |
413 | |
|
414 | 0 | if (custom_mode == NULL) { |
415 | 0 | custom_mode = RunModeGetConfOrDefault(runmode, capture_plugin_name); |
416 | 0 | if (custom_mode == NULL) |
417 | 0 | FatalError("Unknown runtime mode. Aborting"); |
418 | 0 | } |
419 | | |
420 | 0 | RunMode *mode = RunModeGetCustomMode(runmode, custom_mode); |
421 | 0 | if (mode == NULL) { |
422 | 0 | SCLogError("The custom type \"%s\" doesn't exist " |
423 | 0 | "for this runmode type \"%s\". Please use --list-runmodes to " |
424 | 0 | "see available custom types for this runmode", |
425 | 0 | custom_mode, RunModeTranslateModeToName(runmode)); |
426 | 0 | exit(EXIT_FAILURE); |
427 | 0 | } |
428 | | |
429 | | /* Export the custom mode */ |
430 | 0 | if (active_runmode) { |
431 | 0 | SCFree(active_runmode); |
432 | 0 | } |
433 | 0 | active_runmode = SCStrdup(custom_mode); |
434 | 0 | if (unlikely(active_runmode == NULL)) { |
435 | 0 | FatalError("Unable to dup active mode"); |
436 | 0 | } |
437 | | |
438 | 0 | if (strcasecmp(active_runmode, "autofp") == 0) { |
439 | 0 | TmqhFlowPrintAutofpHandler(); |
440 | 0 | } |
441 | |
|
442 | 0 | mode->RunModeFunc(); |
443 | |
|
444 | 0 | if (local_custom_mode != NULL) |
445 | 0 | SCFree(local_custom_mode); |
446 | | |
447 | | /* Check if the alloted queues have at least 1 reader and writer */ |
448 | 0 | TmValidateQueueState(); |
449 | |
|
450 | 0 | if (runmode != RUNMODE_UNIX_SOCKET) { |
451 | | /* spawn management threads */ |
452 | 0 | FlowManagerThreadSpawn(); |
453 | 0 | FlowRecyclerThreadSpawn(); |
454 | 0 | if (RunModeNeedsBypassManager()) { |
455 | 0 | BypassedFlowManagerThreadSpawn(); |
456 | 0 | } |
457 | 0 | StatsSpawnThreads(); |
458 | 0 | LogFlushThreads(); |
459 | 0 | TmThreadsSealThreads(); |
460 | 0 | } |
461 | 0 | } |
462 | | |
463 | | static int g_runmode_needs_bypass = 0; |
464 | | |
465 | | void RunModeEnablesBypassManager(void) |
466 | 0 | { |
467 | 0 | g_runmode_needs_bypass = 1; |
468 | 0 | } |
469 | | |
470 | | int RunModeNeedsBypassManager(void) |
471 | 0 | { |
472 | 0 | return g_runmode_needs_bypass; |
473 | 0 | } |
474 | | |
475 | | |
476 | | |
477 | | /** |
478 | | * \brief Registers a new runmode. |
479 | | * |
480 | | * \param runmode Runmode type. |
481 | | * \param name Custom mode for this specific runmode type. Within each |
482 | | * runmode type, each custom name is a primary key. |
483 | | * \param description Description for this runmode. |
484 | | * \param RunModeFunc The function to be run for this runmode. |
485 | | */ |
486 | | void RunModeRegisterNewRunMode(enum SCRunModes runmode, const char *name, const char *description, |
487 | | int (*RunModeFunc)(void), int (*RunModeIsIPSEnabled)(void)) |
488 | 279 | { |
489 | 279 | if (RunModeGetCustomMode(runmode, name) != NULL) { |
490 | 0 | FatalError("runmode '%s' has already " |
491 | 0 | "been registered. Please use an unique name.", |
492 | 0 | name); |
493 | 0 | } |
494 | | |
495 | 279 | void *ptmp = SCRealloc(runmodes[runmode].runmodes, |
496 | 279 | (runmodes[runmode].cnt + 1) * sizeof(RunMode)); |
497 | 279 | if (ptmp == NULL) { |
498 | 0 | SCFree(runmodes[runmode].runmodes); |
499 | 0 | runmodes[runmode].runmodes = NULL; |
500 | 0 | exit(EXIT_FAILURE); |
501 | 0 | } |
502 | 279 | runmodes[runmode].runmodes = ptmp; |
503 | | |
504 | 279 | RunMode *mode = &runmodes[runmode].runmodes[runmodes[runmode].cnt]; |
505 | 279 | runmodes[runmode].cnt++; |
506 | 279 | memset(mode, 0x00, sizeof(*mode)); |
507 | | |
508 | 279 | mode->runmode = runmode; |
509 | 279 | mode->name = SCStrdup(name); |
510 | 279 | if (unlikely(mode->name == NULL)) { |
511 | 0 | FatalError("Failed to allocate string"); |
512 | 0 | } |
513 | 279 | mode->description = SCStrdup(description); |
514 | 279 | if (unlikely(mode->description == NULL)) { |
515 | 0 | FatalError("Failed to allocate string"); |
516 | 0 | } |
517 | 279 | mode->RunModeFunc = RunModeFunc; |
518 | 279 | mode->RunModeIsIPSEnabled = RunModeIsIPSEnabled; |
519 | 279 | } |
520 | | |
521 | | /** |
522 | | * Setup the outputs for this run mode. |
523 | | * |
524 | | * \param tv The ThreadVars for the thread the outputs will be |
525 | | * appended to. |
526 | | */ |
527 | | static void RunOutputFreeList(void) |
528 | 0 | { |
529 | 0 | OutputFreeList *output; |
530 | 0 | while ((output = TAILQ_FIRST(&output_free_list))) { |
531 | 0 | TAILQ_REMOVE(&output_free_list, output, entries); |
532 | |
|
533 | 0 | SCLogDebug("output %s %p %p", output->output_module->name, output, output->output_ctx); |
534 | 0 | if (output->output_ctx != NULL && output->output_ctx->DeInit != NULL) |
535 | 0 | output->output_ctx->DeInit(output->output_ctx); |
536 | 0 | SCFree(output); |
537 | 0 | } |
538 | 0 | } |
539 | | |
540 | | static int file_logger_count = 0; |
541 | | static int filedata_logger_count = 0; |
542 | | |
543 | | int RunModeOutputFiledataEnabled(void) |
544 | 4 | { |
545 | 4 | return filedata_logger_count > 0; |
546 | 4 | } |
547 | | |
548 | | bool IsRunModeSystem(enum SCRunModes run_mode_to_check) |
549 | 0 | { |
550 | 0 | switch (run_mode_to_check) { |
551 | 0 | case RUNMODE_PCAP_FILE: |
552 | 0 | case RUNMODE_ERF_FILE: |
553 | 0 | case RUNMODE_ENGINE_ANALYSIS: |
554 | 0 | return false; |
555 | 0 | break; |
556 | 0 | default: |
557 | 0 | return true; |
558 | 0 | } |
559 | 0 | } |
560 | | |
561 | | bool IsRunModeOffline(enum SCRunModes run_mode_to_check) |
562 | 0 | { |
563 | 0 | switch(run_mode_to_check) { |
564 | 0 | case RUNMODE_CONF_TEST: |
565 | 0 | case RUNMODE_PCAP_FILE: |
566 | 0 | case RUNMODE_ERF_FILE: |
567 | 0 | case RUNMODE_ENGINE_ANALYSIS: |
568 | 0 | case RUNMODE_UNIX_SOCKET: |
569 | 0 | return true; |
570 | 0 | break; |
571 | 0 | default: |
572 | 0 | return false; |
573 | 0 | } |
574 | 0 | } |
575 | | |
576 | | /** |
577 | | * Cleanup the run mode. |
578 | | */ |
579 | | void RunModeShutDown(void) |
580 | 0 | { |
581 | 0 | RunOutputFreeList(); |
582 | |
|
583 | 0 | OutputPacketShutdown(); |
584 | 0 | OutputTxShutdown(); |
585 | 0 | OutputFileShutdown(); |
586 | 0 | OutputFiledataShutdown(); |
587 | 0 | OutputStreamingShutdown(); |
588 | 0 | OutputStatsShutdown(); |
589 | 0 | OutputFlowShutdown(); |
590 | |
|
591 | 0 | OutputClearActiveLoggers(); |
592 | | |
593 | | /* Reset logger counts. */ |
594 | 0 | file_logger_count = 0; |
595 | 0 | filedata_logger_count = 0; |
596 | 0 | } |
597 | | |
598 | | /** \internal |
599 | | * \brief add Sub RunModeOutput to list for Submodule so we can free |
600 | | * the output ctx at shutdown and unix socket reload */ |
601 | | static void AddOutputToFreeList(OutputModule *module, OutputCtx *output_ctx) |
602 | 124 | { |
603 | 124 | OutputFreeList *fl_output = SCCalloc(1, sizeof(OutputFreeList)); |
604 | 124 | if (unlikely(fl_output == NULL)) |
605 | 0 | return; |
606 | 124 | fl_output->output_module = module; |
607 | 124 | fl_output->output_ctx = output_ctx; |
608 | 124 | TAILQ_INSERT_TAIL(&output_free_list, fl_output, entries); |
609 | 124 | } |
610 | | |
611 | | /** \brief Turn output into thread module */ |
612 | | static void SetupOutput( |
613 | | const char *name, OutputModule *module, OutputCtx *output_ctx, LoggerId *logger_bits) |
614 | 62 | { |
615 | | /* flow logger doesn't run in the packet path */ |
616 | 62 | if (module->FlowLogFunc) { |
617 | 4 | SCOutputRegisterFlowLogger(module->name, module->FlowLogFunc, output_ctx, |
618 | 4 | module->ThreadInit, module->ThreadDeinit); |
619 | 4 | return; |
620 | 4 | } |
621 | | /* stats logger doesn't run in the packet path */ |
622 | 58 | if (module->StatsLogFunc) { |
623 | 0 | OutputRegisterStatsLogger(module->name, module->StatsLogFunc, output_ctx, |
624 | 0 | module->ThreadInit, module->ThreadDeinit); |
625 | 0 | return; |
626 | 0 | } |
627 | | |
628 | 58 | if (module->logger_id == LOGGER_ALERT_DEBUG) { |
629 | 0 | debuglog_enabled = 1; |
630 | 0 | } |
631 | | |
632 | 58 | if (module->PacketLogFunc) { |
633 | 8 | SCLogDebug("%s is a packet logger", module->name); |
634 | 8 | SCOutputRegisterPacketLogger(module->logger_id, module->name, module->PacketLogFunc, |
635 | 8 | module->PacketConditionFunc, output_ctx, module->ThreadInit, module->ThreadDeinit); |
636 | 50 | } else if (module->TxLogFunc) { |
637 | 46 | SCLogDebug("%s is a tx logger", module->name); |
638 | 46 | SCOutputRegisterTxLogger(module->logger_id, module->name, module->alproto, |
639 | 46 | module->TxLogFunc, output_ctx, module->tc_log_progress, module->ts_log_progress, |
640 | 46 | module->TxLogCondition, module->ThreadInit, module->ThreadDeinit); |
641 | | /* Not used with wild card loggers */ |
642 | 46 | if (module->alproto != ALPROTO_UNKNOWN) { |
643 | 44 | logger_bits[module->alproto] |= BIT_U32(module->logger_id); |
644 | 44 | } |
645 | 46 | } else if (module->FiledataLogFunc) { |
646 | 2 | SCLogDebug("%s is a filedata logger", module->name); |
647 | 2 | SCOutputRegisterFiledataLogger(module->logger_id, module->name, module->FiledataLogFunc, |
648 | 2 | output_ctx, module->ThreadInit, module->ThreadDeinit); |
649 | 2 | filedata_logger_count++; |
650 | 2 | } else if (module->FileLogFunc) { |
651 | 2 | SCLogDebug("%s is a file logger", module->name); |
652 | 2 | SCOutputRegisterFileLogger(module->logger_id, module->name, module->FileLogFunc, output_ctx, |
653 | 2 | module->ThreadInit, module->ThreadDeinit); |
654 | 2 | file_logger_count++; |
655 | 2 | } else if (module->StreamingLogFunc) { |
656 | 0 | SCLogDebug("%s is a streaming logger", module->name); |
657 | 0 | SCOutputRegisterStreamingLogger(module->logger_id, module->name, module->StreamingLogFunc, |
658 | 0 | output_ctx, module->stream_type, module->ThreadInit, module->ThreadDeinit); |
659 | 0 | } else { |
660 | 0 | SCLogError("Unknown logger type: name=%s", module->name); |
661 | 0 | } |
662 | 58 | } |
663 | | |
664 | | static void RunModeInitializeEveOutput( |
665 | | SCConfNode *conf, OutputCtx *parent_ctx, LoggerId *logger_bits) |
666 | 4 | { |
667 | 4 | SCConfNode *types = SCConfNodeLookupChild(conf, "types"); |
668 | 4 | SCLogDebug("types %p", types); |
669 | 4 | if (types == NULL) { |
670 | 0 | return; |
671 | 0 | } |
672 | 4 | if (!SCConfNodeIsSequence(types)) { |
673 | 0 | FatalError("output types should be a sequence"); |
674 | 0 | } |
675 | | |
676 | 4 | SCConfNode *type = NULL; |
677 | 96 | TAILQ_FOREACH(type, &types->head, next) { |
678 | 96 | int sub_count = 0; |
679 | 96 | char subname[256]; |
680 | | |
681 | 96 | if (strcmp(type->val, "ikev2") == 0) { |
682 | 0 | SCLogWarning("eve module 'ikev2' has been replaced by 'ike'"); |
683 | 0 | strlcpy(subname, "eve-log.ike", sizeof(subname)); |
684 | 96 | } else { |
685 | 96 | snprintf(subname, sizeof(subname), "eve-log.%s", type->val); |
686 | 96 | } |
687 | | |
688 | 96 | SCLogConfig("enabling 'eve-log' module '%s'", type->val); |
689 | | |
690 | 96 | SCConfNode *sub_output_config = SCConfNodeLookupChild(type, type->val); |
691 | 96 | if (sub_output_config != NULL) { |
692 | 24 | const char *enabled = SCConfNodeLookupChildValue(sub_output_config, "enabled"); |
693 | 24 | if (enabled != NULL && !SCConfValIsTrue(enabled)) { |
694 | 0 | continue; |
695 | 0 | } |
696 | 24 | } |
697 | | |
698 | | /* Now setup all registers logger of this name. */ |
699 | 96 | OutputModule *sub_module; |
700 | 5.80k | TAILQ_FOREACH(sub_module, &output_modules, entries) { |
701 | 5.80k | if (strcmp(subname, sub_module->conf_name) == 0) { |
702 | 104 | sub_count++; |
703 | | |
704 | 104 | if (sub_module->parent_name == NULL || |
705 | 104 | strcmp(sub_module->parent_name, "eve-log") != 0) { |
706 | 0 | FatalError("bad parent for %s", subname); |
707 | 0 | } |
708 | 104 | if (sub_module->InitSubFunc == NULL) { |
709 | 0 | FatalError("bad sub-module for %s", subname); |
710 | 0 | } |
711 | | |
712 | | /* pass on parent output_ctx */ |
713 | 104 | OutputInitResult result = |
714 | 104 | sub_module->InitSubFunc(sub_output_config, parent_ctx); |
715 | 104 | if (!result.ok || result.ctx == NULL) { |
716 | 0 | FatalError("unable to initialize sub-module %s", subname); |
717 | 0 | } |
718 | | |
719 | 104 | AddOutputToFreeList(sub_module, result.ctx); |
720 | 104 | SetupOutput(sub_module->name, sub_module, result.ctx, logger_bits); |
721 | 104 | } |
722 | 5.80k | } |
723 | | |
724 | | /* Error is no registered loggers with this name |
725 | | * were found .*/ |
726 | 96 | if (!sub_count) { |
727 | 0 | FatalErrorOnInit("No output module named %s", subname); |
728 | 0 | continue; |
729 | 0 | } |
730 | 96 | } |
731 | 4 | } |
732 | | |
733 | | static void RunModeInitializeLuaOutput( |
734 | | SCConfNode *conf, OutputCtx *parent_ctx, LoggerId *logger_bits) |
735 | 0 | { |
736 | 0 | OutputModule *lua_module = OutputGetModuleByConfName("lua"); |
737 | 0 | BUG_ON(lua_module == NULL); |
738 | | |
739 | 0 | SCConfNode *scripts = SCConfNodeLookupChild(conf, "scripts"); |
740 | 0 | BUG_ON(scripts == NULL); //TODO |
741 | | |
742 | 0 | OutputModule *m; |
743 | 0 | TAILQ_FOREACH(m, &parent_ctx->submodules, entries) { |
744 | 0 | SCLogDebug("m %p %s:%s", m, m->name, m->conf_name); |
745 | |
|
746 | 0 | SCConfNode *script = NULL; |
747 | 0 | TAILQ_FOREACH(script, &scripts->head, next) { |
748 | 0 | SCLogDebug("script %s", script->val); |
749 | 0 | if (strcmp(script->val, m->conf_name) == 0) { |
750 | 0 | break; |
751 | 0 | } |
752 | 0 | } |
753 | 0 | BUG_ON(script == NULL); |
754 | | |
755 | | /* pass on parent output_ctx */ |
756 | 0 | OutputInitResult result = m->InitSubFunc(script, parent_ctx); |
757 | 0 | if (!result.ok || result.ctx == NULL) { |
758 | 0 | continue; |
759 | 0 | } |
760 | | |
761 | 0 | AddOutputToFreeList(m, result.ctx); |
762 | 0 | SetupOutput(m->name, m, result.ctx, logger_bits); |
763 | 0 | } |
764 | 0 | } |
765 | | |
766 | | extern bool g_file_logger_enabled; |
767 | | extern bool g_filedata_logger_enabled; |
768 | | |
769 | | /** |
770 | | * Initialize the output modules. |
771 | | */ |
772 | | void RunModeInitializeOutputs(void) |
773 | 6 | { |
774 | 6 | SCConfNode *outputs = SCConfGetNode("outputs"); |
775 | 6 | if (outputs == NULL) { |
776 | | /* No "outputs" section in the configuration. */ |
777 | 2 | return; |
778 | 2 | } |
779 | | |
780 | 4 | SCConfNode *output, *output_config; |
781 | 4 | const char *enabled; |
782 | 4 | char tls_log_enabled = 0; |
783 | 4 | char tls_store_present = 0; |
784 | | |
785 | | // g_alproto_max is set to its final value |
786 | 4 | LoggerId logger_bits[g_alproto_max]; |
787 | 4 | memset(logger_bits, 0, g_alproto_max * sizeof(LoggerId)); |
788 | 18 | TAILQ_FOREACH(output, &outputs->head, next) { |
789 | | |
790 | 18 | output_config = SCConfNodeLookupChild(output, output->val); |
791 | 18 | if (output_config == NULL) { |
792 | | /* Shouldn't happen. */ |
793 | 0 | FatalError("Failed to lookup configuration child node: %s", output->val); |
794 | 0 | } |
795 | | |
796 | 18 | if (strcmp(output->val, "tls-store") == 0) { |
797 | 0 | tls_store_present = 1; |
798 | 0 | } |
799 | | |
800 | 18 | enabled = SCConfNodeLookupChildValue(output_config, "enabled"); |
801 | 18 | if (enabled == NULL || !SCConfValIsTrue(enabled)) { |
802 | 0 | continue; |
803 | 0 | } |
804 | | |
805 | 18 | if (strcmp(output->val, "file-log") == 0) { |
806 | 0 | SCLogWarning("file-log is no longer supported," |
807 | 0 | " use eve.files instead " |
808 | 0 | "(see ticket #2376" |
809 | 0 | " for an explanation)"); |
810 | 0 | continue; |
811 | 18 | } else if (strncmp(output->val, "unified-", sizeof("unified-") - 1) == 0) { |
812 | 0 | SCLogWarning("Unified1 is no longer supported," |
813 | 0 | " use Unified2 instead " |
814 | 0 | "(see ticket #353" |
815 | 0 | " for an explanation)"); |
816 | 0 | continue; |
817 | 18 | } else if (strncmp(output->val, "unified2-", sizeof("unified2-") - 1) == 0) { |
818 | 0 | SCLogWarning("Unified2 is no longer supported."); |
819 | 0 | continue; |
820 | 18 | } else if (strcmp(output->val, "dns-log") == 0) { |
821 | 0 | SCLogWarning("dns-log is not longer available as of Suricata 5.0"); |
822 | 0 | continue; |
823 | 18 | } else if (strcmp(output->val, "tls-log") == 0) { |
824 | 4 | tls_log_enabled = 1; |
825 | 4 | } |
826 | | |
827 | 18 | OutputModule *module; |
828 | 18 | int count = 0; |
829 | 1.08k | TAILQ_FOREACH(module, &output_modules, entries) { |
830 | 1.08k | if (strcmp(module->conf_name, output->val) != 0) { |
831 | 1.07k | continue; |
832 | 1.07k | } |
833 | | |
834 | 16 | count++; |
835 | | |
836 | 16 | OutputCtx *output_ctx = NULL; |
837 | 16 | if (module->InitFunc != NULL) { |
838 | 16 | OutputInitResult r = module->InitFunc(output_config); |
839 | 16 | if (!r.ok) { |
840 | 0 | FatalErrorOnInit("output module \"%s\": setup failed", output->val); |
841 | 0 | continue; |
842 | 16 | } else if (r.ctx == NULL) { |
843 | 0 | continue; |
844 | 0 | } |
845 | 16 | output_ctx = r.ctx; |
846 | 16 | } else if (module->InitSubFunc != NULL) { |
847 | 0 | SCLogInfo("skipping submodule"); |
848 | 0 | continue; |
849 | 0 | } |
850 | | |
851 | | // TODO if module == parent, find it's children |
852 | 16 | if (strcmp(output->val, "eve-log") == 0) { |
853 | 4 | RunModeInitializeEveOutput(output_config, output_ctx, logger_bits); |
854 | | |
855 | | /* add 'eve-log' to free list as it's the owner of the |
856 | | * main output ctx from which the sub-modules share the |
857 | | * LogFileCtx */ |
858 | 4 | AddOutputToFreeList(module, output_ctx); |
859 | 12 | } else if (strcmp(output->val, "lua") == 0) { |
860 | 0 | SCLogDebug("handle lua"); |
861 | 0 | if (output_ctx == NULL) |
862 | 0 | continue; |
863 | 0 | RunModeInitializeLuaOutput(output_config, output_ctx, logger_bits); |
864 | 0 | AddOutputToFreeList(module, output_ctx); |
865 | 12 | } else { |
866 | 12 | AddOutputToFreeList(module, output_ctx); |
867 | 12 | SetupOutput(module->name, module, output_ctx, logger_bits); |
868 | 12 | } |
869 | 16 | } |
870 | 18 | if (count == 0) { |
871 | 2 | FatalErrorOnInit("No output module named %s", output->val); |
872 | 2 | continue; |
873 | 2 | } |
874 | 18 | } |
875 | | |
876 | | /* Backward compatibility code */ |
877 | 4 | if (!tls_store_present && tls_log_enabled) { |
878 | | /* old YAML with no "tls-store" in outputs. "tls-log" value needs |
879 | | * to be started using 'tls-log' config as own config */ |
880 | 4 | SCLogWarning("Please use 'tls-store' in YAML to configure TLS storage"); |
881 | | |
882 | 18 | TAILQ_FOREACH(output, &outputs->head, next) { |
883 | 18 | output_config = SCConfNodeLookupChild(output, output->val); |
884 | | |
885 | 18 | if (strcmp(output->val, "tls-log") == 0) { |
886 | | |
887 | 4 | OutputModule *module = OutputGetModuleByConfName("tls-store"); |
888 | 4 | if (module == NULL) { |
889 | 0 | SCLogWarning("No output module named %s, ignoring", "tls-store"); |
890 | 0 | continue; |
891 | 0 | } |
892 | | |
893 | 4 | OutputCtx *output_ctx = NULL; |
894 | 4 | if (module->InitFunc != NULL) { |
895 | 4 | OutputInitResult r = module->InitFunc(output_config); |
896 | 4 | if (!r.ok) { |
897 | 0 | FatalErrorOnInit("output module setup failed"); |
898 | 0 | continue; |
899 | 4 | } else if (r.ctx == NULL) { |
900 | 0 | continue; |
901 | 0 | } |
902 | 4 | output_ctx = r.ctx; |
903 | 4 | } |
904 | | |
905 | 4 | AddOutputToFreeList(module, output_ctx); |
906 | 4 | SetupOutput(module->name, module, output_ctx, logger_bits); |
907 | 4 | } |
908 | 18 | } |
909 | 4 | } |
910 | | |
911 | | /* register the logger bits to the app-layer */ |
912 | 4 | AppProto a; |
913 | 166 | for (a = 0; a < g_alproto_max; a++) { |
914 | 162 | if (AppLayerParserSupportsFiles(IPPROTO_TCP, a)) { |
915 | 32 | if (g_file_logger_enabled) |
916 | 32 | logger_bits[a] |= BIT_U32(LOGGER_FILE); |
917 | 32 | if (g_filedata_logger_enabled) |
918 | 32 | logger_bits[a] |= BIT_U32(LOGGER_FILEDATA); |
919 | 32 | SCLogDebug("IPPROTO_TCP::%s: g_file_logger_enabled %d g_filedata_logger_enabled %d -> " |
920 | 32 | "%08x", |
921 | 32 | AppProtoToString(a), g_file_logger_enabled, g_filedata_logger_enabled, |
922 | 32 | logger_bits[a]); |
923 | 32 | } |
924 | 162 | if (AppLayerParserSupportsFiles(IPPROTO_UDP, a)) { |
925 | 4 | if (g_file_logger_enabled) |
926 | 4 | logger_bits[a] |= BIT_U32(LOGGER_FILE); |
927 | 4 | if (g_filedata_logger_enabled) |
928 | 4 | logger_bits[a] |= BIT_U32(LOGGER_FILEDATA); |
929 | 4 | } |
930 | | |
931 | 162 | if (logger_bits[a] == 0) |
932 | 74 | continue; |
933 | | |
934 | 88 | const int tcp = AppLayerParserProtocolHasLogger(IPPROTO_TCP, a) | (g_file_logger_enabled) | |
935 | 88 | (g_filedata_logger_enabled); |
936 | 88 | const int udp = AppLayerParserProtocolHasLogger(IPPROTO_UDP, a) | (g_file_logger_enabled) | |
937 | 88 | (g_filedata_logger_enabled); |
938 | 88 | SCLogDebug("tcp %d udp %d", tcp, udp); |
939 | | |
940 | 88 | SCLogDebug("logger for %s: %s %s", AppProtoToString(a), |
941 | 88 | tcp ? "true" : "false", udp ? "true" : "false"); |
942 | | |
943 | 88 | SCLogDebug("logger bits for %s: %08x", AppProtoToString(a), logger_bits[a]); |
944 | 88 | if (tcp) |
945 | 88 | AppLayerParserRegisterLoggerBits(IPPROTO_TCP, a, logger_bits[a]); |
946 | 88 | if (udp) |
947 | 88 | AppLayerParserRegisterLoggerBits(IPPROTO_UDP, a, logger_bits[a]); |
948 | 88 | } |
949 | 4 | OutputSetupActiveLoggers(); |
950 | 4 | } |
951 | | |
952 | | float threading_detect_ratio = 1; |
953 | | |
954 | | /** |
955 | | * Initialize multithreading settings. |
956 | | */ |
957 | | void RunModeInitializeThreadSettings(void) |
958 | 2 | { |
959 | 2 | int affinity = 0; |
960 | 2 | if ((SCConfGetBool("threading.set-cpu-affinity", &affinity)) == 0) { |
961 | 2 | threading_set_cpu_affinity = false; |
962 | 2 | } else { |
963 | 0 | threading_set_cpu_affinity = affinity == 1; |
964 | 0 | } |
965 | | |
966 | | /* try to get custom cpu mask value if needed */ |
967 | 2 | if (threading_set_cpu_affinity) { |
968 | 0 | AffinitySetupLoadFromConfig(); |
969 | 0 | } |
970 | 2 | if ((SCConfGetFloat("threading.detect-thread-ratio", &threading_detect_ratio)) != 1) { |
971 | 2 | if (SCConfGetNode("threading.detect-thread-ratio") != NULL) |
972 | 0 | WarnInvalidConfEntry("threading.detect-thread-ratio", "%s", "1"); |
973 | 2 | threading_detect_ratio = 1; |
974 | 2 | } |
975 | | |
976 | 2 | SCLogDebug("threading.detect-thread-ratio %f", threading_detect_ratio); |
977 | | |
978 | | /* |
979 | | * Check if there's a configuration setting for the per-thread stack size |
980 | | * in case the default per-thread stack size is to be adjusted |
981 | | */ |
982 | 2 | const char *ss = NULL; |
983 | 2 | if ((SCConfGet("threading.stack-size", &ss)) == 1) { |
984 | 0 | if (ss != NULL) { |
985 | 0 | if (ParseSizeStringU64(ss, &threading_set_stack_size) < 0) { |
986 | 0 | FatalError("Failed to initialize thread_stack_size output, invalid limit: %s", ss); |
987 | 0 | } |
988 | 0 | } |
989 | 2 | } else { |
990 | 2 | pthread_attr_t attr; |
991 | 2 | pthread_attr_init(&attr); |
992 | 2 | size_t size; |
993 | 2 | if (pthread_attr_getstacksize(&attr, &size) == 0 && size < 512 * 1024) { |
994 | 0 | threading_set_stack_size = 512 * 1024; |
995 | 0 | SCLogNotice("thread stack size of %" PRIuMAX " too small: setting to 512k", |
996 | 0 | (uintmax_t)size); |
997 | 0 | } |
998 | 2 | } |
999 | | |
1000 | 2 | SCLogDebug("threading.stack-size %" PRIu64, threading_set_stack_size); |
1001 | 2 | } |