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