]> git.ipfire.org Git - people/ms/suricata.git/blob - src/detect-engine.c
detect-engine: convert unittests to FAIL/PASS APIs
[people/ms/suricata.git] / src / detect-engine.c
1 /* Copyright (C) 2007-2021 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 /**
19 * \file
20 *
21 * \author Victor Julien <victor@inliniac.net>
22 */
23
24 #include "suricata-common.h"
25 #include "suricata.h"
26 #include "debug.h"
27 #include "detect.h"
28 #include "flow.h"
29 #include "flow-private.h"
30 #include "flow-util.h"
31 #include "flow-worker.h"
32 #include "conf.h"
33 #include "conf-yaml-loader.h"
34 #include "datasets.h"
35
36 #include "app-layer-parser.h"
37 #include "app-layer-htp.h"
38
39 #include "detect-parse.h"
40 #include "detect-engine-sigorder.h"
41
42 #include "detect-engine-siggroup.h"
43 #include "detect-engine-address.h"
44 #include "detect-engine-port.h"
45 #include "detect-engine-prefilter.h"
46 #include "detect-engine-mpm.h"
47 #include "detect-engine-iponly.h"
48 #include "detect-engine-tag.h"
49
50 #include "detect-engine-file.h"
51
52 #include "detect-engine.h"
53 #include "detect-engine-state.h"
54 #include "detect-engine-payload.h"
55 #include "detect-byte-extract.h"
56 #include "detect-content.h"
57 #include "detect-uricontent.h"
58 #include "detect-tcphdr.h"
59 #include "detect-engine-threshold.h"
60 #include "detect-engine-content-inspection.h"
61
62 #include "detect-engine-loader.h"
63
64 #include "util-classification-config.h"
65 #include "util-reference-config.h"
66 #include "util-threshold-config.h"
67 #include "util-error.h"
68 #include "util-hash.h"
69 #include "util-byte.h"
70 #include "util-debug.h"
71 #include "util-unittest.h"
72 #include "util-action.h"
73 #include "util-magic.h"
74 #include "util-signal.h"
75 #include "util-spm.h"
76 #include "util-device.h"
77 #include "util-var-name.h"
78 #include "util-profiling.h"
79 #include "util-validate.h"
80
81 #include "tm-threads.h"
82 #include "runmodes.h"
83
84 #include "reputation.h"
85
86 #define DETECT_ENGINE_DEFAULT_INSPECTION_RECURSION_LIMIT 3000
87
88 static int DetectEngineCtxLoadConf(DetectEngineCtx *);
89
90 static DetectEngineMasterCtx g_master_de_ctx = { SCMUTEX_INITIALIZER,
91 0, 99, NULL, NULL, TENANT_SELECTOR_UNKNOWN, NULL, NULL, 0};
92
93 static uint32_t TenantIdHash(HashTable *h, void *data, uint16_t data_len);
94 static char TenantIdCompare(void *d1, uint16_t d1_len, void *d2, uint16_t d2_len);
95 static void TenantIdFree(void *d);
96 static uint32_t DetectEngineTentantGetIdFromLivedev(const void *ctx, const Packet *p);
97 static uint32_t DetectEngineTentantGetIdFromVlanId(const void *ctx, const Packet *p);
98 static uint32_t DetectEngineTentantGetIdFromPcap(const void *ctx, const Packet *p);
99
100 static DetectEngineAppInspectionEngine *g_app_inspect_engines = NULL;
101 static DetectEnginePktInspectionEngine *g_pkt_inspect_engines = NULL;
102
103 SCEnumCharMap det_ctx_event_table[] = {
104 #ifdef UNITTESTS
105 { "TEST", DET_CTX_EVENT_TEST },
106 #endif
107 { "NO_MEMORY", FILE_DECODER_EVENT_NO_MEM },
108 { "INVALID_SWF_LENGTH", FILE_DECODER_EVENT_INVALID_SWF_LENGTH },
109 { "INVALID_SWF_VERSION", FILE_DECODER_EVENT_INVALID_SWF_VERSION },
110 { "Z_DATA_ERROR", FILE_DECODER_EVENT_Z_DATA_ERROR },
111 { "Z_STREAM_ERROR", FILE_DECODER_EVENT_Z_STREAM_ERROR },
112 { "Z_BUF_ERROR", FILE_DECODER_EVENT_Z_BUF_ERROR },
113 { "Z_UNKNOWN_ERROR", FILE_DECODER_EVENT_Z_UNKNOWN_ERROR },
114 { "LZMA_DECODER_ERROR", FILE_DECODER_EVENT_LZMA_DECODER_ERROR },
115 { "LZMA_MEMLIMIT_ERROR", FILE_DECODER_EVENT_LZMA_MEMLIMIT_ERROR },
116 { "LZMA_OPTIONS_ERROR", FILE_DECODER_EVENT_LZMA_OPTIONS_ERROR },
117 { "LZMA_FORMAT_ERROR", FILE_DECODER_EVENT_LZMA_FORMAT_ERROR },
118 { "LZMA_DATA_ERROR", FILE_DECODER_EVENT_LZMA_DATA_ERROR },
119 { "LZMA_BUF_ERROR", FILE_DECODER_EVENT_LZMA_BUF_ERROR },
120 { "LZMA_UNKNOWN_ERROR", FILE_DECODER_EVENT_LZMA_UNKNOWN_ERROR },
121 {
122 "TOO_MANY_BUFFERS",
123 DETECT_EVENT_TOO_MANY_BUFFERS,
124 },
125 { NULL, -1 },
126 };
127
128 /** \brief register inspect engine at start up time
129 *
130 * \note errors are fatal */
131 void DetectPktInspectEngineRegister(const char *name,
132 InspectionBufferGetPktDataPtr GetPktData,
133 InspectionBufferPktInspectFunc Callback)
134 {
135 DetectBufferTypeRegister(name);
136 const int sm_list = DetectBufferTypeGetByName(name);
137 if (sm_list == -1) {
138 FatalError(SC_ERR_INITIALIZATION,
139 "failed to register inspect engine %s", name);
140 }
141
142 if ((sm_list < DETECT_SM_LIST_MATCH) || (sm_list >= SHRT_MAX) ||
143 (Callback == NULL))
144 {
145 SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid arguments");
146 BUG_ON(1);
147 }
148
149 DetectEnginePktInspectionEngine *new_engine = SCCalloc(1, sizeof(*new_engine));
150 if (unlikely(new_engine == NULL)) {
151 FatalError(SC_ERR_INITIALIZATION,
152 "failed to register inspect engine %s: %s", name, strerror(errno));
153 }
154 new_engine->sm_list = sm_list;
155 new_engine->sm_list_base = sm_list;
156 new_engine->v1.Callback = Callback;
157 new_engine->v1.GetData = GetPktData;
158
159 if (g_pkt_inspect_engines == NULL) {
160 g_pkt_inspect_engines = new_engine;
161 } else {
162 DetectEnginePktInspectionEngine *t = g_pkt_inspect_engines;
163 while (t->next != NULL) {
164 t = t->next;
165 }
166
167 t->next = new_engine;
168 }
169 }
170
171 /** \brief register inspect engine at start up time
172 *
173 * \note errors are fatal */
174 void DetectAppLayerInspectEngineRegister2(const char *name,
175 AppProto alproto, uint32_t dir, int progress,
176 InspectEngineFuncPtr2 Callback2,
177 InspectionBufferGetDataPtr GetData)
178 {
179 BUG_ON(progress >= 48);
180
181 DetectBufferTypeRegister(name);
182 const int sm_list = DetectBufferTypeGetByName(name);
183 if (sm_list == -1) {
184 FatalError(SC_ERR_INITIALIZATION,
185 "failed to register inspect engine %s", name);
186 }
187
188 if ((alproto >= ALPROTO_FAILED) ||
189 (!(dir == SIG_FLAG_TOSERVER || dir == SIG_FLAG_TOCLIENT)) ||
190 (sm_list < DETECT_SM_LIST_MATCH) || (sm_list >= SHRT_MAX) ||
191 (progress < 0 || progress >= SHRT_MAX) ||
192 (Callback2 == NULL))
193 {
194 SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid arguments");
195 BUG_ON(1);
196 } else if (Callback2 == DetectEngineInspectBufferGeneric && GetData == NULL) {
197 SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid arguments: must register "
198 "GetData with DetectEngineInspectBufferGeneric");
199 BUG_ON(1);
200 }
201
202 int direction;
203 if (dir == SIG_FLAG_TOSERVER) {
204 direction = 0;
205 } else {
206 direction = 1;
207 }
208
209 DetectEngineAppInspectionEngine *new_engine = SCMalloc(sizeof(DetectEngineAppInspectionEngine));
210 if (unlikely(new_engine == NULL)) {
211 exit(EXIT_FAILURE);
212 }
213 memset(new_engine, 0, sizeof(*new_engine));
214 new_engine->alproto = alproto;
215 new_engine->dir = direction;
216 new_engine->sm_list = sm_list;
217 new_engine->sm_list_base = sm_list;
218 new_engine->progress = progress;
219 new_engine->v2.Callback = Callback2;
220 new_engine->v2.GetData = GetData;
221
222 if (g_app_inspect_engines == NULL) {
223 g_app_inspect_engines = new_engine;
224 } else {
225 DetectEngineAppInspectionEngine *t = g_app_inspect_engines;
226 while (t->next != NULL) {
227 t = t->next;
228 }
229
230 t->next = new_engine;
231 }
232 }
233
234 /* copy an inspect engine with transforms to a new list id. */
235 static void DetectAppLayerInspectEngineCopy(
236 DetectEngineCtx *de_ctx,
237 int sm_list, int new_list,
238 const DetectEngineTransforms *transforms)
239 {
240 const DetectEngineAppInspectionEngine *t = g_app_inspect_engines;
241 while (t) {
242 if (t->sm_list == sm_list) {
243 DetectEngineAppInspectionEngine *new_engine = SCCalloc(1, sizeof(DetectEngineAppInspectionEngine));
244 if (unlikely(new_engine == NULL)) {
245 exit(EXIT_FAILURE);
246 }
247 new_engine->alproto = t->alproto;
248 new_engine->dir = t->dir;
249 new_engine->sm_list = new_list; /* use new list id */
250 new_engine->sm_list_base = sm_list;
251 new_engine->progress = t->progress;
252 new_engine->v2 = t->v2;
253 new_engine->v2.transforms = transforms; /* assign transforms */
254
255 if (de_ctx->app_inspect_engines == NULL) {
256 de_ctx->app_inspect_engines = new_engine;
257 } else {
258 DetectEngineAppInspectionEngine *list = de_ctx->app_inspect_engines;
259 while (list->next != NULL) {
260 list = list->next;
261 }
262
263 list->next = new_engine;
264 }
265 }
266 t = t->next;
267 }
268 }
269
270 /* copy inspect engines from global registrations to de_ctx list */
271 static void DetectAppLayerInspectEngineCopyListToDetectCtx(DetectEngineCtx *de_ctx)
272 {
273 const DetectEngineAppInspectionEngine *t = g_app_inspect_engines;
274 while (t) {
275 DetectEngineAppInspectionEngine *new_engine = SCCalloc(1, sizeof(DetectEngineAppInspectionEngine));
276 if (unlikely(new_engine == NULL)) {
277 exit(EXIT_FAILURE);
278 }
279 new_engine->alproto = t->alproto;
280 new_engine->dir = t->dir;
281 new_engine->sm_list = t->sm_list;
282 new_engine->sm_list_base = t->sm_list;
283 new_engine->progress = t->progress;
284 new_engine->v2 = t->v2;
285
286 if (de_ctx->app_inspect_engines == NULL) {
287 de_ctx->app_inspect_engines = new_engine;
288 } else {
289 DetectEngineAppInspectionEngine *list = de_ctx->app_inspect_engines;
290 while (list->next != NULL) {
291 list = list->next;
292 }
293
294 list->next = new_engine;
295 }
296
297 t = t->next;
298 }
299 }
300
301 /* copy an inspect engine with transforms to a new list id. */
302 static void DetectPktInspectEngineCopy(
303 DetectEngineCtx *de_ctx,
304 int sm_list, int new_list,
305 const DetectEngineTransforms *transforms)
306 {
307 const DetectEnginePktInspectionEngine *t = g_pkt_inspect_engines;
308 while (t) {
309 if (t->sm_list == sm_list) {
310 DetectEnginePktInspectionEngine *new_engine = SCCalloc(1, sizeof(DetectEnginePktInspectionEngine));
311 if (unlikely(new_engine == NULL)) {
312 exit(EXIT_FAILURE);
313 }
314 new_engine->sm_list = new_list; /* use new list id */
315 new_engine->sm_list_base = sm_list;
316 new_engine->v1 = t->v1;
317 new_engine->v1.transforms = transforms; /* assign transforms */
318
319 if (de_ctx->pkt_inspect_engines == NULL) {
320 de_ctx->pkt_inspect_engines = new_engine;
321 } else {
322 DetectEnginePktInspectionEngine *list = de_ctx->pkt_inspect_engines;
323 while (list->next != NULL) {
324 list = list->next;
325 }
326
327 list->next = new_engine;
328 }
329 }
330 t = t->next;
331 }
332 }
333
334 /* copy inspect engines from global registrations to de_ctx list */
335 static void DetectPktInspectEngineCopyListToDetectCtx(DetectEngineCtx *de_ctx)
336 {
337 const DetectEnginePktInspectionEngine *t = g_pkt_inspect_engines;
338 while (t) {
339 SCLogDebug("engine %p", t);
340 DetectEnginePktInspectionEngine *new_engine = SCCalloc(1, sizeof(DetectEnginePktInspectionEngine));
341 if (unlikely(new_engine == NULL)) {
342 exit(EXIT_FAILURE);
343 }
344 new_engine->sm_list = t->sm_list;
345 new_engine->sm_list_base = t->sm_list;
346 new_engine->v1 = t->v1;
347
348 if (de_ctx->pkt_inspect_engines == NULL) {
349 de_ctx->pkt_inspect_engines = new_engine;
350 } else {
351 DetectEnginePktInspectionEngine *list = de_ctx->pkt_inspect_engines;
352 while (list->next != NULL) {
353 list = list->next;
354 }
355
356 list->next = new_engine;
357 }
358
359 t = t->next;
360 }
361 }
362
363 /** \internal
364 * \brief append the stream inspection
365 *
366 * If stream inspection is MPM, then prepend it.
367 */
368 static void AppendStreamInspectEngine(Signature *s, SigMatchData *stream, int direction, uint32_t id)
369 {
370 bool prepend = false;
371
372 DetectEngineAppInspectionEngine *new_engine = SCCalloc(1, sizeof(DetectEngineAppInspectionEngine));
373 if (unlikely(new_engine == NULL)) {
374 exit(EXIT_FAILURE);
375 }
376 if (SigMatchListSMBelongsTo(s, s->init_data->mpm_sm) == DETECT_SM_LIST_PMATCH) {
377 SCLogDebug("stream is mpm");
378 prepend = true;
379 new_engine->mpm = true;
380 }
381 new_engine->alproto = ALPROTO_UNKNOWN; /* all */
382 new_engine->dir = direction;
383 new_engine->stream = true;
384 new_engine->sm_list = DETECT_SM_LIST_PMATCH;
385 new_engine->sm_list_base = DETECT_SM_LIST_PMATCH;
386 new_engine->smd = stream;
387 new_engine->v2.Callback = DetectEngineInspectStream;
388 new_engine->progress = 0;
389
390 /* append */
391 if (s->app_inspect == NULL) {
392 s->app_inspect = new_engine;
393 new_engine->id = DE_STATE_FLAG_BASE; /* id is used as flag in stateful detect */
394 } else if (prepend) {
395 new_engine->next = s->app_inspect;
396 s->app_inspect = new_engine;
397 new_engine->id = id;
398
399 } else {
400 DetectEngineAppInspectionEngine *a = s->app_inspect;
401 while (a->next != NULL) {
402 a = a->next;
403 }
404
405 a->next = new_engine;
406 new_engine->id = id;
407 }
408 SCLogDebug("sid %u: engine %p/%u added", s->id, new_engine, new_engine->id);
409 }
410
411 /**
412 * \note for the file inspect engine, the id DE_STATE_ID_FILE_INSPECT
413 * is assigned.
414 */
415 int DetectEngineAppInspectionEngine2Signature(DetectEngineCtx *de_ctx, Signature *s)
416 {
417 const int nlists = s->init_data->smlists_array_size;
418 SigMatchData *ptrs[nlists];
419 memset(&ptrs, 0, (nlists * sizeof(SigMatchData *)));
420
421 const int mpm_list = s->init_data->mpm_sm ?
422 SigMatchListSMBelongsTo(s, s->init_data->mpm_sm) :
423 -1;
424
425 const int files_id = DetectBufferTypeGetByName("files");
426
427 /* convert lists to SigMatchData arrays */
428 int i = 0;
429 for (i = DETECT_SM_LIST_DYNAMIC_START; i < nlists; i++) {
430 if (s->init_data->smlists[i] == NULL)
431 continue;
432
433 ptrs[i] = SigMatchList2DataArray(s->init_data->smlists[i]);
434 SCLogDebug("ptrs[%d] is set", i);
435 }
436
437 /* set up pkt inspect engines */
438 const DetectEnginePktInspectionEngine *e = de_ctx->pkt_inspect_engines;
439 while (e != NULL) {
440 SCLogDebug("e %p sm_list %u nlists %u ptrs[] %p", e, e->sm_list, nlists, e->sm_list < nlists ? ptrs[e->sm_list] : NULL);
441 if (e->sm_list < nlists && ptrs[e->sm_list] != NULL) {
442 bool prepend = false;
443
444 DetectEnginePktInspectionEngine *new_engine = SCCalloc(1, sizeof(DetectEnginePktInspectionEngine));
445 if (unlikely(new_engine == NULL)) {
446 exit(EXIT_FAILURE);
447 }
448 if (mpm_list == e->sm_list) {
449 SCLogDebug("%s is mpm", DetectBufferTypeGetNameById(de_ctx, e->sm_list));
450 prepend = true;
451 new_engine->mpm = true;
452 }
453
454 new_engine->sm_list = e->sm_list;
455 new_engine->sm_list_base = e->sm_list_base;
456 new_engine->smd = ptrs[new_engine->sm_list];
457 new_engine->v1 = e->v1;
458 SCLogDebug("sm_list %d new_engine->v1 %p/%p/%p",
459 new_engine->sm_list, new_engine->v1.Callback,
460 new_engine->v1.GetData, new_engine->v1.transforms);
461
462 if (s->pkt_inspect == NULL) {
463 s->pkt_inspect = new_engine;
464 } else if (prepend) {
465 new_engine->next = s->pkt_inspect;
466 s->pkt_inspect = new_engine;
467 } else {
468 DetectEnginePktInspectionEngine *a = s->pkt_inspect;
469 while (a->next != NULL) {
470 a = a->next;
471 }
472 new_engine->next = a->next;
473 a->next = new_engine;
474 }
475 }
476 e = e->next;
477 }
478
479 bool head_is_mpm = false;
480 uint32_t last_id = DE_STATE_FLAG_BASE;
481 const DetectEngineAppInspectionEngine *t = de_ctx->app_inspect_engines;
482 while (t != NULL) {
483 bool prepend = false;
484
485 if (t->sm_list >= nlists)
486 goto next;
487
488 if (ptrs[t->sm_list] == NULL)
489 goto next;
490
491 SCLogDebug("ptrs[%d] is set", t->sm_list);
492
493 if (t->alproto == ALPROTO_UNKNOWN) {
494 /* special case, inspect engine applies to all protocols */
495 } else if (s->alproto != ALPROTO_UNKNOWN && !AppProtoEquals(s->alproto, t->alproto))
496 goto next;
497
498 if (s->flags & SIG_FLAG_TOSERVER && !(s->flags & SIG_FLAG_TOCLIENT)) {
499 if (t->dir == 1)
500 goto next;
501 } else if (s->flags & SIG_FLAG_TOCLIENT && !(s->flags & SIG_FLAG_TOSERVER)) {
502 if (t->dir == 0)
503 goto next;
504 }
505 DetectEngineAppInspectionEngine *new_engine = SCCalloc(1, sizeof(DetectEngineAppInspectionEngine));
506 if (unlikely(new_engine == NULL)) {
507 exit(EXIT_FAILURE);
508 }
509 if (mpm_list == t->sm_list) {
510 SCLogDebug("%s is mpm", DetectBufferTypeGetNameById(de_ctx, t->sm_list));
511 prepend = true;
512 head_is_mpm = true;
513 new_engine->mpm = true;
514 }
515
516 new_engine->alproto = t->alproto;
517 new_engine->dir = t->dir;
518 new_engine->sm_list = t->sm_list;
519 new_engine->sm_list_base = t->sm_list_base;
520 new_engine->smd = ptrs[new_engine->sm_list];
521 new_engine->progress = t->progress;
522 new_engine->v2 = t->v2;
523 SCLogDebug("sm_list %d new_engine->v2 %p/%p/%p",
524 new_engine->sm_list, new_engine->v2.Callback,
525 new_engine->v2.GetData, new_engine->v2.transforms);
526
527 if (s->app_inspect == NULL) {
528 s->app_inspect = new_engine;
529 if (new_engine->sm_list == files_id) {
530 SCLogDebug("sid %u: engine %p/%u is FILE ENGINE", s->id, new_engine, new_engine->id);
531 new_engine->id = DE_STATE_ID_FILE_INSPECT;
532 } else {
533 new_engine->id = DE_STATE_FLAG_BASE; /* id is used as flag in stateful detect */
534 }
535
536 /* prepend engine if forced or if our engine has a lower progress. */
537 } else if (prepend || (!head_is_mpm && s->app_inspect->progress > new_engine->progress)) {
538 new_engine->next = s->app_inspect;
539 s->app_inspect = new_engine;
540 if (new_engine->sm_list == files_id) {
541 SCLogDebug("sid %u: engine %p/%u is FILE ENGINE", s->id, new_engine, new_engine->id);
542 new_engine->id = DE_STATE_ID_FILE_INSPECT;
543 } else {
544 new_engine->id = ++last_id;
545 }
546
547 } else {
548 DetectEngineAppInspectionEngine *a = s->app_inspect;
549 while (a->next != NULL) {
550 if (a->next && a->next->progress > new_engine->progress) {
551 break;
552 }
553
554 a = a->next;
555 }
556
557 new_engine->next = a->next;
558 a->next = new_engine;
559 if (new_engine->sm_list == files_id) {
560 SCLogDebug("sid %u: engine %p/%u is FILE ENGINE", s->id, new_engine, new_engine->id);
561 new_engine->id = DE_STATE_ID_FILE_INSPECT;
562 } else {
563 new_engine->id = ++last_id;
564 }
565 }
566
567 SCLogDebug("sid %u: engine %p/%u added", s->id, new_engine, new_engine->id);
568
569 s->init_data->init_flags |= SIG_FLAG_INIT_STATE_MATCH;
570 next:
571 t = t->next;
572 }
573
574 if ((s->init_data->init_flags & SIG_FLAG_INIT_STATE_MATCH) &&
575 s->init_data->smlists[DETECT_SM_LIST_PMATCH] != NULL)
576 {
577 /* if engine is added multiple times, we pass it the same list */
578 SigMatchData *stream = SigMatchList2DataArray(s->init_data->smlists[DETECT_SM_LIST_PMATCH]);
579 BUG_ON(stream == NULL);
580 if (s->flags & SIG_FLAG_TOSERVER && !(s->flags & SIG_FLAG_TOCLIENT)) {
581 AppendStreamInspectEngine(s, stream, 0, last_id + 1);
582 } else if (s->flags & SIG_FLAG_TOCLIENT && !(s->flags & SIG_FLAG_TOSERVER)) {
583 AppendStreamInspectEngine(s, stream, 1, last_id + 1);
584 } else {
585 AppendStreamInspectEngine(s, stream, 0, last_id + 1);
586 AppendStreamInspectEngine(s, stream, 1, last_id + 1);
587 }
588
589 if (s->init_data->init_flags & SIG_FLAG_INIT_NEED_FLUSH) {
590 SCLogDebug("set SIG_FLAG_FLUSH on %u", s->id);
591 s->flags |= SIG_FLAG_FLUSH;
592 }
593 }
594
595 #ifdef DEBUG
596 const DetectEngineAppInspectionEngine *iter = s->app_inspect;
597 while (iter) {
598 SCLogDebug("%u: engine %s id %u progress %d %s", s->id,
599 DetectBufferTypeGetNameById(de_ctx, iter->sm_list), iter->id,
600 iter->progress,
601 iter->sm_list == mpm_list ? "MPM":"");
602 iter = iter->next;
603 }
604 #endif
605 return 0;
606 }
607
608 /** \brief free app inspect engines for a signature
609 *
610 * For lists that are registered multiple times, like http_header and
611 * http_cookie, making the engines owner of the lists is complicated.
612 * Multiple engines in a sig may be pointing to the same list. To
613 * address this the 'free' code needs to be extra careful about not
614 * double freeing, so it takes an approach to first fill an array
615 * of the to-free pointers before freeing them.
616 */
617 void DetectEngineAppInspectionEngineSignatureFree(DetectEngineCtx *de_ctx, Signature *s)
618 {
619 int nlists = 0;
620
621 DetectEngineAppInspectionEngine *ie = s->app_inspect;
622 while (ie) {
623 nlists = MAX(ie->sm_list + 1, nlists);
624 ie = ie->next;
625 }
626 DetectEnginePktInspectionEngine *e = s->pkt_inspect;
627 while (e) {
628 nlists = MAX(e->sm_list + 1, nlists);
629 e = e->next;
630 }
631 if (nlists == 0) {
632 BUG_ON(s->pkt_inspect);
633 return;
634 }
635
636 SigMatchData *ptrs[nlists];
637 memset(&ptrs, 0, (nlists * sizeof(SigMatchData *)));
638
639 /* free engines and put smd in the array */
640 ie = s->app_inspect;
641 while (ie) {
642 DetectEngineAppInspectionEngine *next = ie->next;
643 BUG_ON(ptrs[ie->sm_list] != NULL && ptrs[ie->sm_list] != ie->smd);
644 ptrs[ie->sm_list] = ie->smd;
645 SCFree(ie);
646 ie = next;
647 }
648 e = s->pkt_inspect;
649 while (e) {
650 DetectEnginePktInspectionEngine *next = e->next;
651 ptrs[e->sm_list] = e->smd;
652 SCFree(e);
653 e = next;
654 }
655
656 /* free the smds */
657 for (int i = 0; i < nlists; i++)
658 {
659 if (ptrs[i] == NULL)
660 continue;
661
662 SigMatchData *smd = ptrs[i];
663 while(1) {
664 if (sigmatch_table[smd->type].Free != NULL) {
665 sigmatch_table[smd->type].Free(de_ctx, smd->ctx);
666 }
667 if (smd->is_last)
668 break;
669 smd++;
670 }
671 SCFree(ptrs[i]);
672 }
673 }
674
675 /* code for registering buffers */
676
677 #include "util-hash-lookup3.h"
678
679 static HashListTable *g_buffer_type_hash = NULL;
680 static int g_buffer_type_id = DETECT_SM_LIST_DYNAMIC_START;
681 static int g_buffer_type_reg_closed = 0;
682
683 static DetectEngineTransforms no_transforms = {
684 .transforms[0] = {0, NULL},
685 .cnt = 0,
686 };
687
688 int DetectBufferTypeMaxId(void)
689 {
690 return g_buffer_type_id;
691 }
692
693 static uint32_t DetectBufferTypeHashFunc(HashListTable *ht, void *data, uint16_t datalen)
694 {
695 const DetectBufferType *map = (DetectBufferType *)data;
696 uint32_t hash = 0;
697
698 hash = hashlittle_safe(map->string, strlen(map->string), 0);
699 hash += hashlittle_safe((uint8_t *)&map->transforms, sizeof(map->transforms), 0);
700 hash %= ht->array_size;
701
702 return hash;
703 }
704
705 static char DetectBufferTypeCompareFunc(void *data1, uint16_t len1, void *data2,
706 uint16_t len2)
707 {
708 DetectBufferType *map1 = (DetectBufferType *)data1;
709 DetectBufferType *map2 = (DetectBufferType *)data2;
710
711 int r = (strcmp(map1->string, map2->string) == 0);
712 r &= (memcmp((uint8_t *)&map1->transforms, (uint8_t *)&map2->transforms, sizeof(map2->transforms)) == 0);
713 return r;
714 }
715
716 static void DetectBufferTypeFreeFunc(void *data)
717 {
718 DetectBufferType *map = (DetectBufferType *)data;
719
720 if (map == NULL) {
721 return;
722 }
723
724 /* Release transformation option memory, if any */
725 for (int i = 0; i < map->transforms.cnt; i++) {
726 if (map->transforms.transforms[i].options == NULL)
727 continue;
728 if (sigmatch_table[map->transforms.transforms[i].transform].Free == NULL) {
729 SCLogError(SC_ERR_UNIMPLEMENTED,
730 "%s allocates transform option memory but has no free routine",
731 sigmatch_table[map->transforms.transforms[i].transform].name);
732 continue;
733 }
734 sigmatch_table[map->transforms.transforms[i].transform].Free(NULL, map->transforms.transforms[i].options);
735 }
736
737 SCFree(map);
738 }
739
740 static int DetectBufferTypeInit(void)
741 {
742 BUG_ON(g_buffer_type_hash);
743 g_buffer_type_hash = HashListTableInit(256,
744 DetectBufferTypeHashFunc,
745 DetectBufferTypeCompareFunc,
746 DetectBufferTypeFreeFunc);
747 if (g_buffer_type_hash == NULL)
748 return -1;
749
750 return 0;
751 }
752 #if 0
753 static void DetectBufferTypeFree(void)
754 {
755 if (g_buffer_type_hash == NULL)
756 return;
757
758 HashListTableFree(g_buffer_type_hash);
759 g_buffer_type_hash = NULL;
760 return;
761 }
762 #endif
763 static int DetectBufferTypeAdd(const char *string)
764 {
765 DetectBufferType *map = SCCalloc(1, sizeof(*map));
766 if (map == NULL)
767 return -1;
768
769 map->string = string;
770 map->id = g_buffer_type_id++;
771
772 BUG_ON(HashListTableAdd(g_buffer_type_hash, (void *)map, 0) != 0);
773 SCLogDebug("buffer %s registered with id %d", map->string, map->id);
774 return map->id;
775 }
776
777 static DetectBufferType *DetectBufferTypeLookupByName(const char *string)
778 {
779 DetectBufferType map = { (char *)string, NULL, 0, 0, 0, 0, false, NULL, NULL, no_transforms };
780
781 DetectBufferType *res = HashListTableLookup(g_buffer_type_hash, &map, 0);
782 return res;
783 }
784
785 int DetectBufferTypeRegister(const char *name)
786 {
787 BUG_ON(g_buffer_type_reg_closed);
788 if (g_buffer_type_hash == NULL)
789 DetectBufferTypeInit();
790
791 DetectBufferType *exists = DetectBufferTypeLookupByName(name);
792 if (!exists) {
793 return DetectBufferTypeAdd(name);
794 } else {
795 return exists->id;
796 }
797 }
798
799 void DetectBufferTypeSupportsPacket(const char *name)
800 {
801 BUG_ON(g_buffer_type_reg_closed);
802 DetectBufferTypeRegister(name);
803 DetectBufferType *exists = DetectBufferTypeLookupByName(name);
804 BUG_ON(!exists);
805 exists->packet = true;
806 SCLogDebug("%p %s -- %d supports packet inspection", exists, name, exists->id);
807 }
808
809 void DetectBufferTypeSupportsMpm(const char *name)
810 {
811 BUG_ON(g_buffer_type_reg_closed);
812 DetectBufferTypeRegister(name);
813 DetectBufferType *exists = DetectBufferTypeLookupByName(name);
814 BUG_ON(!exists);
815 exists->mpm = true;
816 SCLogDebug("%p %s -- %d supports mpm", exists, name, exists->id);
817 }
818
819 void DetectBufferTypeSupportsTransformations(const char *name)
820 {
821 BUG_ON(g_buffer_type_reg_closed);
822 DetectBufferTypeRegister(name);
823 DetectBufferType *exists = DetectBufferTypeLookupByName(name);
824 BUG_ON(!exists);
825 exists->supports_transforms = true;
826 SCLogDebug("%p %s -- %d supports transformations", exists, name, exists->id);
827 }
828
829 int DetectBufferTypeGetByName(const char *name)
830 {
831 DetectBufferType *exists = DetectBufferTypeLookupByName(name);
832 if (!exists) {
833 return -1;
834 }
835 return exists->id;
836 }
837
838 const char *DetectBufferTypeGetNameById(const DetectEngineCtx *de_ctx, const int id)
839 {
840 BUG_ON(id < 0 || (uint32_t)id >= de_ctx->buffer_type_map_elements);
841 BUG_ON(de_ctx->buffer_type_map == NULL);
842
843 if (de_ctx->buffer_type_map[id] == NULL)
844 return NULL;
845
846 return de_ctx->buffer_type_map[id]->string;
847 }
848
849 static const DetectBufferType *DetectBufferTypeGetById(const DetectEngineCtx *de_ctx, const int id)
850 {
851 BUG_ON(id < 0 || (uint32_t)id >= de_ctx->buffer_type_map_elements);
852 BUG_ON(de_ctx->buffer_type_map == NULL);
853
854 return de_ctx->buffer_type_map[id];
855 }
856
857 void DetectBufferTypeSetDescriptionByName(const char *name, const char *desc)
858 {
859 DetectBufferType *exists = DetectBufferTypeLookupByName(name);
860 if (!exists) {
861 return;
862 }
863 exists->description = desc;
864 }
865
866 const char *DetectBufferTypeGetDescriptionById(const DetectEngineCtx *de_ctx, const int id)
867 {
868 const DetectBufferType *exists = DetectBufferTypeGetById(de_ctx, id);
869 if (!exists) {
870 return NULL;
871 }
872 return exists->description;
873 }
874
875 const char *DetectBufferTypeGetDescriptionByName(const char *name)
876 {
877 const DetectBufferType *exists = DetectBufferTypeLookupByName(name);
878 if (!exists) {
879 return NULL;
880 }
881 return exists->description;
882 }
883
884 bool DetectBufferTypeSupportsPacketGetById(const DetectEngineCtx *de_ctx, const int id)
885 {
886 const DetectBufferType *map = DetectBufferTypeGetById(de_ctx, id);
887 if (map == NULL)
888 return false;
889 SCLogDebug("map %p id %d packet? %d", map, id, map->packet);
890 return map->packet;
891 }
892
893 bool DetectBufferTypeSupportsMpmGetById(const DetectEngineCtx *de_ctx, const int id)
894 {
895 const DetectBufferType *map = DetectBufferTypeGetById(de_ctx, id);
896 if (map == NULL)
897 return false;
898 SCLogDebug("map %p id %d mpm? %d", map, id, map->mpm);
899 return map->mpm;
900 }
901
902 void DetectBufferTypeRegisterSetupCallback(const char *name,
903 void (*SetupCallback)(const DetectEngineCtx *, Signature *))
904 {
905 BUG_ON(g_buffer_type_reg_closed);
906 DetectBufferTypeRegister(name);
907 DetectBufferType *exists = DetectBufferTypeLookupByName(name);
908 BUG_ON(!exists);
909 exists->SetupCallback = SetupCallback;
910 }
911
912 void DetectBufferRunSetupCallback(const DetectEngineCtx *de_ctx,
913 const int id, Signature *s)
914 {
915 const DetectBufferType *map = DetectBufferTypeGetById(de_ctx, id);
916 if (map && map->SetupCallback) {
917 map->SetupCallback(de_ctx, s);
918 }
919 }
920
921 void DetectBufferTypeRegisterValidateCallback(const char *name,
922 bool (*ValidateCallback)(const Signature *, const char **sigerror))
923 {
924 BUG_ON(g_buffer_type_reg_closed);
925 DetectBufferTypeRegister(name);
926 DetectBufferType *exists = DetectBufferTypeLookupByName(name);
927 BUG_ON(!exists);
928 exists->ValidateCallback = ValidateCallback;
929 }
930
931 bool DetectBufferRunValidateCallback(const DetectEngineCtx *de_ctx,
932 const int id, const Signature *s, const char **sigerror)
933 {
934 const DetectBufferType *map = DetectBufferTypeGetById(de_ctx, id);
935 if (map && map->ValidateCallback) {
936 return map->ValidateCallback(s, sigerror);
937 }
938 return true;
939 }
940
941 int DetectBufferSetActiveList(Signature *s, const int list)
942 {
943 BUG_ON(s->init_data == NULL);
944
945 if (s->init_data->list && s->init_data->transforms.cnt) {
946 return -1;
947 }
948 s->init_data->list = list;
949 s->init_data->list_set = true;
950
951 return 0;
952 }
953
954 int DetectBufferGetActiveList(DetectEngineCtx *de_ctx, Signature *s)
955 {
956 BUG_ON(s->init_data == NULL);
957
958 if (s->init_data->list && s->init_data->transforms.cnt) {
959 if (s->init_data->list == DETECT_SM_LIST_NOTSET ||
960 s->init_data->list < DETECT_SM_LIST_DYNAMIC_START) {
961 SCLogError(SC_ERR_INVALID_SIGNATURE, "previous transforms not consumed "
962 "(list: %u, transform_cnt %u)", s->init_data->list,
963 s->init_data->transforms.cnt);
964 SCReturnInt(-1);
965 }
966
967 SCLogDebug("buffer %d has transform(s) registered: %d",
968 s->init_data->list, s->init_data->transforms.cnt);
969 int new_list = DetectBufferTypeGetByIdTransforms(de_ctx, s->init_data->list,
970 s->init_data->transforms.transforms, s->init_data->transforms.cnt);
971 if (new_list == -1) {
972 SCReturnInt(-1);
973 }
974 SCLogDebug("new_list %d", new_list);
975 s->init_data->list = new_list;
976 s->init_data->list_set = false;
977 // reset transforms now that we've set up the list
978 s->init_data->transforms.cnt = 0;
979 }
980
981 SCReturnInt(0);
982 }
983
984 void InspectionBufferClean(DetectEngineThreadCtx *det_ctx)
985 {
986 /* single buffers */
987 for (uint32_t i = 0; i < det_ctx->inspect.to_clear_idx; i++)
988 {
989 const uint32_t idx = det_ctx->inspect.to_clear_queue[i];
990 InspectionBuffer *buffer = &det_ctx->inspect.buffers[idx];
991 buffer->inspect = NULL;
992 }
993 det_ctx->inspect.to_clear_idx = 0;
994
995 /* multi buffers */
996 for (uint32_t i = 0; i < det_ctx->multi_inspect.to_clear_idx; i++)
997 {
998 const uint32_t idx = det_ctx->multi_inspect.to_clear_queue[i];
999 InspectionBufferMultipleForList *mbuffer = &det_ctx->multi_inspect.buffers[idx];
1000 for (uint32_t x = 0; x <= mbuffer->max; x++) {
1001 InspectionBuffer *buffer = &mbuffer->inspection_buffers[x];
1002 buffer->inspect = NULL;
1003 }
1004 mbuffer->init = 0;
1005 mbuffer->max = 0;
1006 }
1007 det_ctx->multi_inspect.to_clear_idx = 0;
1008 }
1009
1010 InspectionBuffer *InspectionBufferGet(DetectEngineThreadCtx *det_ctx, const int list_id)
1011 {
1012 return &det_ctx->inspect.buffers[list_id];
1013 }
1014
1015 static InspectionBufferMultipleForList *InspectionBufferGetMulti(
1016 DetectEngineThreadCtx *det_ctx, const int list_id)
1017 {
1018 InspectionBufferMultipleForList *buffer = &det_ctx->multi_inspect.buffers[list_id];
1019 if (!buffer->init) {
1020 det_ctx->multi_inspect.to_clear_queue[det_ctx->multi_inspect.to_clear_idx++] = list_id;
1021 buffer->init = 1;
1022 }
1023 return buffer;
1024 }
1025
1026 /** \brief for a InspectionBufferMultipleForList get a InspectionBuffer
1027 * \param fb the multiple buffer array
1028 * \param local_id the index to get a buffer
1029 * \param buffer the inspect buffer or NULL in case of error */
1030 InspectionBuffer *InspectionBufferMultipleForListGet(
1031 DetectEngineThreadCtx *det_ctx, const int list_id, const uint32_t local_id)
1032 {
1033 if (unlikely(local_id >= 1024)) {
1034 DetectEngineSetEvent(det_ctx, DETECT_EVENT_TOO_MANY_BUFFERS);
1035 return NULL;
1036 }
1037
1038 InspectionBufferMultipleForList *fb = InspectionBufferGetMulti(det_ctx, list_id);
1039
1040 if (local_id >= fb->size) {
1041 uint32_t old_size = fb->size;
1042 uint32_t new_size = local_id + 1;
1043 uint32_t grow_by = new_size - old_size;
1044 SCLogDebug("size is %u, need %u, so growing by %u", old_size, new_size, grow_by);
1045
1046 SCLogDebug("fb->inspection_buffers %p", fb->inspection_buffers);
1047 void *ptr = SCRealloc(fb->inspection_buffers, (local_id + 1) * sizeof(InspectionBuffer));
1048 if (ptr == NULL)
1049 return NULL;
1050
1051 InspectionBuffer *to_zero = (InspectionBuffer *)ptr + old_size;
1052 SCLogDebug("ptr %p to_zero %p", ptr, to_zero);
1053 memset((uint8_t *)to_zero, 0, (grow_by * sizeof(InspectionBuffer)));
1054 fb->inspection_buffers = ptr;
1055 fb->size = new_size;
1056 }
1057
1058 fb->max = MAX(fb->max, local_id);
1059 InspectionBuffer *buffer = &fb->inspection_buffers[local_id];
1060 SCLogDebug("using file_data buffer %p", buffer);
1061 #ifdef DEBUG_VALIDATION
1062 buffer->multi = true;
1063 #endif
1064 return buffer;
1065 }
1066
1067 void InspectionBufferInit(InspectionBuffer *buffer, uint32_t initial_size)
1068 {
1069 memset(buffer, 0, sizeof(*buffer));
1070 buffer->buf = SCCalloc(initial_size, sizeof(uint8_t));
1071 if (buffer->buf != NULL) {
1072 buffer->size = initial_size;
1073 }
1074 }
1075
1076 /** \brief setup the buffer with our initial data */
1077 void InspectionBufferSetupMulti(InspectionBuffer *buffer, const DetectEngineTransforms *transforms,
1078 const uint8_t *data, const uint32_t data_len)
1079 {
1080 #ifdef DEBUG_VALIDATION
1081 DEBUG_VALIDATE_BUG_ON(!buffer->multi);
1082 #endif
1083 buffer->inspect = buffer->orig = data;
1084 buffer->inspect_len = buffer->orig_len = data_len;
1085 buffer->len = 0;
1086
1087 InspectionBufferApplyTransforms(buffer, transforms);
1088 }
1089
1090 /** \brief setup the buffer with our initial data */
1091 void InspectionBufferSetup(DetectEngineThreadCtx *det_ctx, const int list_id,
1092 InspectionBuffer *buffer, const uint8_t *data, const uint32_t data_len)
1093 {
1094 #ifdef DEBUG_VALIDATION
1095 DEBUG_VALIDATE_BUG_ON(buffer->multi);
1096 #endif
1097 if (buffer->inspect == NULL) {
1098 #ifdef UNITTESTS
1099 if (det_ctx && list_id != -1)
1100 #endif
1101 det_ctx->inspect.to_clear_queue[det_ctx->inspect.to_clear_idx++] = list_id;
1102 }
1103 buffer->inspect = buffer->orig = data;
1104 buffer->inspect_len = buffer->orig_len = data_len;
1105 buffer->len = 0;
1106 }
1107
1108 void InspectionBufferFree(InspectionBuffer *buffer)
1109 {
1110 if (buffer->buf != NULL) {
1111 SCFree(buffer->buf);
1112 }
1113 memset(buffer, 0, sizeof(*buffer));
1114 }
1115
1116 /**
1117 * \brief make sure that the buffer has at least 'min_size' bytes
1118 * Expand the buffer if necessary
1119 */
1120 void InspectionBufferCheckAndExpand(InspectionBuffer *buffer, uint32_t min_size)
1121 {
1122 if (likely(buffer->size >= min_size))
1123 return;
1124
1125 uint32_t new_size = (buffer->size == 0) ? 4096 : buffer->size;
1126 while (new_size < min_size) {
1127 new_size *= 2;
1128 }
1129
1130 void *ptr = SCRealloc(buffer->buf, new_size);
1131 if (ptr != NULL) {
1132 buffer->buf = ptr;
1133 buffer->size = new_size;
1134 }
1135 }
1136
1137 void InspectionBufferCopy(InspectionBuffer *buffer, uint8_t *buf, uint32_t buf_len)
1138 {
1139 InspectionBufferCheckAndExpand(buffer, buf_len);
1140
1141 if (buffer->size) {
1142 uint32_t copy_size = MIN(buf_len, buffer->size);
1143 memcpy(buffer->buf, buf, copy_size);
1144 buffer->inspect = buffer->buf;
1145 buffer->inspect_len = copy_size;
1146 }
1147 }
1148
1149 /** \brief Check content byte array compatibility with transforms
1150 *
1151 * The "content" array is presented to the transforms so that each
1152 * transform may validate that it's compatible with the transform.
1153 *
1154 * When a transform indicates the byte array is incompatible, none of the
1155 * subsequent transforms, if any, are invoked. This means the first positive
1156 * validation result terminates the loop.
1157 *
1158 * \param de_ctx Detection engine context.
1159 * \param sm_list The SM list id.
1160 * \param content The byte array being validated
1161 * \param namestr returns the name of the transform that is incompatible with
1162 * content.
1163 *
1164 * \retval true (false) If any of the transforms indicate the byte array is
1165 * (is not) compatible.
1166 **/
1167 bool DetectBufferTypeValidateTransform(DetectEngineCtx *de_ctx, int sm_list,
1168 const uint8_t *content, uint16_t content_len, const char **namestr)
1169 {
1170 const DetectBufferType *dbt = DetectBufferTypeGetById(de_ctx, sm_list);
1171 BUG_ON(dbt == NULL);
1172
1173 for (int i = 0; i < dbt->transforms.cnt; i++) {
1174 const TransformData *t = &dbt->transforms.transforms[i];
1175 if (!sigmatch_table[t->transform].TransformValidate)
1176 continue;
1177
1178 if (sigmatch_table[t->transform].TransformValidate(content, content_len, t->options)) {
1179 continue;
1180 }
1181
1182 if (namestr) {
1183 *namestr = sigmatch_table[t->transform].name;
1184 }
1185
1186 return false;
1187 }
1188
1189 return true;
1190 }
1191
1192 void InspectionBufferApplyTransforms(InspectionBuffer *buffer,
1193 const DetectEngineTransforms *transforms)
1194 {
1195 if (transforms) {
1196 for (int i = 0; i < DETECT_TRANSFORMS_MAX; i++) {
1197 const int id = transforms->transforms[i].transform;
1198 if (id == 0)
1199 break;
1200 BUG_ON(sigmatch_table[id].Transform == NULL);
1201 sigmatch_table[id].Transform(buffer, transforms->transforms[i].options);
1202 SCLogDebug("applied transform %s", sigmatch_table[id].name);
1203 }
1204 }
1205 }
1206
1207 static void DetectBufferTypeSetupDetectEngine(DetectEngineCtx *de_ctx)
1208 {
1209 const int size = g_buffer_type_id;
1210 BUG_ON(!(size > 0));
1211
1212 de_ctx->buffer_type_map = SCCalloc(size, sizeof(DetectBufferType *));
1213 BUG_ON(!de_ctx->buffer_type_map);
1214 de_ctx->buffer_type_map_elements = size;
1215 SCLogDebug("de_ctx->buffer_type_map %p with %u members", de_ctx->buffer_type_map, size);
1216
1217 SCLogDebug("DETECT_SM_LIST_DYNAMIC_START %u", DETECT_SM_LIST_DYNAMIC_START);
1218 HashListTableBucket *b = HashListTableGetListHead(g_buffer_type_hash);
1219 while (b) {
1220 DetectBufferType *map = HashListTableGetListData(b);
1221 de_ctx->buffer_type_map[map->id] = map;
1222 SCLogDebug("name %s id %d mpm %s packet %s -- %s. "
1223 "Callbacks: Setup %p Validate %p", map->string, map->id,
1224 map->mpm ? "true" : "false", map->packet ? "true" : "false",
1225 map->description, map->SetupCallback, map->ValidateCallback);
1226 b = HashListTableGetListNext(b);
1227 }
1228
1229 de_ctx->buffer_type_hash = HashListTableInit(256,
1230 DetectBufferTypeHashFunc,
1231 DetectBufferTypeCompareFunc,
1232 DetectBufferTypeFreeFunc);
1233 if (de_ctx->buffer_type_hash == NULL) {
1234 BUG_ON(1);
1235 }
1236 de_ctx->buffer_type_id = g_buffer_type_id;
1237
1238 PrefilterInit(de_ctx);
1239 DetectMpmInitializeAppMpms(de_ctx);
1240 DetectAppLayerInspectEngineCopyListToDetectCtx(de_ctx);
1241 DetectMpmInitializePktMpms(de_ctx);
1242 DetectPktInspectEngineCopyListToDetectCtx(de_ctx);
1243 }
1244
1245 static void DetectBufferTypeFreeDetectEngine(DetectEngineCtx *de_ctx)
1246 {
1247 if (de_ctx) {
1248 if (de_ctx->buffer_type_map)
1249 SCFree(de_ctx->buffer_type_map);
1250 if (de_ctx->buffer_type_hash)
1251 HashListTableFree(de_ctx->buffer_type_hash);
1252
1253 DetectEngineAppInspectionEngine *ilist = de_ctx->app_inspect_engines;
1254 while (ilist) {
1255 DetectEngineAppInspectionEngine *next = ilist->next;
1256 SCFree(ilist);
1257 ilist = next;
1258 }
1259 DetectBufferMpmRegistery *mlist = de_ctx->app_mpms_list;
1260 while (mlist) {
1261 DetectBufferMpmRegistery *next = mlist->next;
1262 SCFree(mlist);
1263 mlist = next;
1264 }
1265 DetectEnginePktInspectionEngine *plist = de_ctx->pkt_inspect_engines;
1266 while (plist) {
1267 DetectEnginePktInspectionEngine *next = plist->next;
1268 SCFree(plist);
1269 plist = next;
1270 }
1271 DetectBufferMpmRegistery *pmlist = de_ctx->pkt_mpms_list;
1272 while (pmlist) {
1273 DetectBufferMpmRegistery *next = pmlist->next;
1274 SCFree(pmlist);
1275 pmlist = next;
1276 }
1277 PrefilterDeinit(de_ctx);
1278 }
1279 }
1280
1281 void DetectBufferTypeCloseRegistration(void)
1282 {
1283 BUG_ON(g_buffer_type_hash == NULL);
1284
1285 g_buffer_type_reg_closed = 1;
1286 }
1287
1288 int DetectBufferTypeGetByIdTransforms(DetectEngineCtx *de_ctx, const int id,
1289 TransformData *transforms, int transform_cnt)
1290 {
1291 const DetectBufferType *base_map = DetectBufferTypeGetById(de_ctx, id);
1292 if (!base_map) {
1293 return -1;
1294 }
1295 if (!base_map->supports_transforms) {
1296 SCLogError(SC_ERR_INVALID_SIGNATURE, "buffer '%s' does not support transformations",
1297 base_map->string);
1298 return -1;
1299 }
1300
1301 SCLogDebug("base_map %s", base_map->string);
1302
1303 DetectEngineTransforms t;
1304 memset(&t, 0, sizeof(t));
1305 for (int i = 0; i < transform_cnt; i++) {
1306 t.transforms[i] = transforms[i];
1307 }
1308 t.cnt = transform_cnt;
1309
1310 DetectBufferType lookup_map = { (char *)base_map->string, NULL, 0, 0, 0, 0, false, NULL, NULL, t };
1311 DetectBufferType *res = HashListTableLookup(de_ctx->buffer_type_hash, &lookup_map, 0);
1312
1313 SCLogDebug("res %p", res);
1314 if (res != NULL) {
1315 return res->id;
1316 }
1317
1318 DetectBufferType *map = SCCalloc(1, sizeof(*map));
1319 if (map == NULL)
1320 return -1;
1321
1322 map->string = base_map->string;
1323 map->id = de_ctx->buffer_type_id++;
1324 map->parent_id = base_map->id;
1325 map->transforms = t;
1326 map->mpm = base_map->mpm;
1327 map->packet = base_map->packet;
1328 map->SetupCallback = base_map->SetupCallback;
1329 map->ValidateCallback = base_map->ValidateCallback;
1330 if (map->packet) {
1331 DetectPktMpmRegisterByParentId(de_ctx,
1332 map->id, map->parent_id, &map->transforms);
1333 } else {
1334 DetectAppLayerMpmRegisterByParentId(de_ctx,
1335 map->id, map->parent_id, &map->transforms);
1336 }
1337
1338 BUG_ON(HashListTableAdd(de_ctx->buffer_type_hash, (void *)map, 0) != 0);
1339 SCLogDebug("buffer %s registered with id %d, parent %d", map->string, map->id, map->parent_id);
1340
1341 if (map->id >= 0 && (uint32_t)map->id >= de_ctx->buffer_type_map_elements) {
1342 void *ptr = SCRealloc(de_ctx->buffer_type_map, (map->id + 1) * sizeof(DetectBufferType *));
1343 BUG_ON(ptr == NULL);
1344 SCLogDebug("de_ctx->buffer_type_map resized to %u (was %u)", (map->id + 1), de_ctx->buffer_type_map_elements);
1345 de_ctx->buffer_type_map = ptr;
1346 de_ctx->buffer_type_map[map->id] = map;
1347 de_ctx->buffer_type_map_elements = map->id + 1;
1348
1349 if (map->packet) {
1350 DetectPktInspectEngineCopy(de_ctx, map->parent_id, map->id,
1351 &map->transforms);
1352 } else {
1353 DetectAppLayerInspectEngineCopy(de_ctx, map->parent_id, map->id,
1354 &map->transforms);
1355 }
1356 }
1357 return map->id;
1358 }
1359
1360 /* returns false if no match, true if match */
1361 static int DetectEngineInspectRulePacketMatches(
1362 DetectEngineThreadCtx *det_ctx,
1363 const DetectEnginePktInspectionEngine *engine,
1364 const Signature *s,
1365 Packet *p, uint8_t *_alert_flags)
1366 {
1367 SCEnter();
1368
1369 /* run the packet match functions */
1370 KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_MATCH);
1371 const SigMatchData *smd = s->sm_arrays[DETECT_SM_LIST_MATCH];
1372
1373 SCLogDebug("running match functions, sm %p", smd);
1374 while (1) {
1375 KEYWORD_PROFILING_START;
1376 if (sigmatch_table[smd->type].Match(det_ctx, p, s, smd->ctx) <= 0) {
1377 KEYWORD_PROFILING_END(det_ctx, smd->type, 0);
1378 SCLogDebug("no match");
1379 return false;
1380 }
1381 KEYWORD_PROFILING_END(det_ctx, smd->type, 1);
1382 if (smd->is_last) {
1383 SCLogDebug("match and is_last");
1384 break;
1385 }
1386 smd++;
1387 }
1388 return true;
1389 }
1390
1391 static int DetectEngineInspectRulePayloadMatches(
1392 DetectEngineThreadCtx *det_ctx,
1393 const DetectEnginePktInspectionEngine *engine,
1394 const Signature *s, Packet *p, uint8_t *alert_flags)
1395 {
1396 SCEnter();
1397
1398 DetectEngineCtx *de_ctx = det_ctx->de_ctx;
1399
1400 KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_PMATCH);
1401 /* if we have stream msgs, inspect against those first,
1402 * but not for a "dsize" signature */
1403 if (s->flags & SIG_FLAG_REQUIRE_STREAM) {
1404 int pmatch = 0;
1405 if (p->flags & PKT_DETECT_HAS_STREAMDATA) {
1406 pmatch = DetectEngineInspectStreamPayload(de_ctx, det_ctx, s, p->flow, p);
1407 if (pmatch) {
1408 det_ctx->flags |= DETECT_ENGINE_THREAD_CTX_STREAM_CONTENT_MATCH;
1409 *alert_flags |= PACKET_ALERT_FLAG_STREAM_MATCH;
1410 }
1411 }
1412 /* no match? then inspect packet payload */
1413 if (pmatch == 0) {
1414 SCLogDebug("no match in stream, fall back to packet payload");
1415
1416 /* skip if we don't have to inspect the packet and segment was
1417 * added to stream */
1418 if (!(s->flags & SIG_FLAG_REQUIRE_PACKET) && (p->flags & PKT_STREAM_ADD)) {
1419 return false;
1420 }
1421 if (DetectEngineInspectPacketPayload(de_ctx, det_ctx, s, p->flow, p) != 1) {
1422 return false;
1423 }
1424 }
1425 } else {
1426 if (DetectEngineInspectPacketPayload(de_ctx, det_ctx, s, p->flow, p) != 1) {
1427 return false;
1428 }
1429 }
1430 return true;
1431 }
1432
1433 bool DetectEnginePktInspectionRun(ThreadVars *tv,
1434 DetectEngineThreadCtx *det_ctx, const Signature *s,
1435 Flow *f, Packet *p,
1436 uint8_t *alert_flags)
1437 {
1438 SCEnter();
1439
1440 for (DetectEnginePktInspectionEngine *e = s->pkt_inspect; e != NULL; e = e->next) {
1441 if (e->v1.Callback(det_ctx, e, s, p, alert_flags) == false) {
1442 SCLogDebug("sid %u: e %p Callback returned false", s->id, e);
1443 return false;
1444 }
1445 SCLogDebug("sid %u: e %p Callback returned true", s->id, e);
1446 }
1447
1448 SCLogDebug("sid %u: returning true", s->id);
1449 return true;
1450 }
1451
1452 /**
1453 * \param data pointer to SigMatchData. Allowed to be NULL.
1454 */
1455 static int DetectEnginePktInspectionAppend(Signature *s, InspectionBufferPktInspectFunc Callback,
1456 SigMatchData *data, const int list_id)
1457 {
1458 DetectEnginePktInspectionEngine *e = SCCalloc(1, sizeof(*e));
1459 if (e == NULL)
1460 return -1;
1461
1462 e->mpm = (SigMatchListSMBelongsTo(s, s->init_data->mpm_sm) == list_id);
1463 e->sm_list = list_id;
1464 e->sm_list_base = list_id;
1465 e->v1.Callback = Callback;
1466 e->smd = data;
1467
1468 if (s->pkt_inspect == NULL) {
1469 s->pkt_inspect = e;
1470 } else {
1471 DetectEnginePktInspectionEngine *a = s->pkt_inspect;
1472 while (a->next != NULL) {
1473 a = a->next;
1474 }
1475 a->next = e;
1476 }
1477 return 0;
1478 }
1479
1480 int DetectEnginePktInspectionSetup(Signature *s)
1481 {
1482 /* only handle PMATCH here if we're not an app inspect rule */
1483 if (s->sm_arrays[DETECT_SM_LIST_PMATCH] && (s->init_data->init_flags & SIG_FLAG_INIT_STATE_MATCH) == 0) {
1484 if (DetectEnginePktInspectionAppend(
1485 s, DetectEngineInspectRulePayloadMatches, NULL, DETECT_SM_LIST_PMATCH) < 0)
1486 return -1;
1487 SCLogDebug("sid %u: DetectEngineInspectRulePayloadMatches appended", s->id);
1488 }
1489
1490 if (s->sm_arrays[DETECT_SM_LIST_MATCH]) {
1491 if (DetectEnginePktInspectionAppend(
1492 s, DetectEngineInspectRulePacketMatches, NULL, DETECT_SM_LIST_MATCH) < 0)
1493 return -1;
1494 SCLogDebug("sid %u: DetectEngineInspectRulePacketMatches appended", s->id);
1495 }
1496
1497 return 0;
1498 }
1499
1500 /* code to control the main thread to do a reload */
1501
1502 enum DetectEngineSyncState {
1503 IDLE, /**< ready to start a reload */
1504 RELOAD, /**< command main thread to do the reload */
1505 };
1506
1507
1508 typedef struct DetectEngineSyncer_ {
1509 SCMutex m;
1510 enum DetectEngineSyncState state;
1511 } DetectEngineSyncer;
1512
1513 static DetectEngineSyncer detect_sync = { SCMUTEX_INITIALIZER, IDLE };
1514
1515 /* tell main to start reloading */
1516 int DetectEngineReloadStart(void)
1517 {
1518 int r = 0;
1519 SCMutexLock(&detect_sync.m);
1520 if (detect_sync.state == IDLE) {
1521 detect_sync.state = RELOAD;
1522 } else {
1523 r = -1;
1524 }
1525 SCMutexUnlock(&detect_sync.m);
1526 return r;
1527 }
1528
1529 /* main thread checks this to see if it should start */
1530 int DetectEngineReloadIsStart(void)
1531 {
1532 int r = 0;
1533 SCMutexLock(&detect_sync.m);
1534 if (detect_sync.state == RELOAD) {
1535 r = 1;
1536 }
1537 SCMutexUnlock(&detect_sync.m);
1538 return r;
1539 }
1540
1541 /* main thread sets done when it's done */
1542 void DetectEngineReloadSetIdle(void)
1543 {
1544 SCMutexLock(&detect_sync.m);
1545 detect_sync.state = IDLE;
1546 SCMutexUnlock(&detect_sync.m);
1547 }
1548
1549 /* caller loops this until it returns 1 */
1550 int DetectEngineReloadIsIdle(void)
1551 {
1552 int r = 0;
1553 SCMutexLock(&detect_sync.m);
1554 if (detect_sync.state == IDLE) {
1555 r = 1;
1556 }
1557 SCMutexUnlock(&detect_sync.m);
1558 return r;
1559 }
1560
1561 /** \brief Do the content inspection & validation for a signature
1562 *
1563 * \param de_ctx Detection engine context
1564 * \param det_ctx Detection engine thread context
1565 * \param s Signature to inspect
1566 * \param sm SigMatch to inspect
1567 * \param f Flow
1568 * \param flags app layer flags
1569 * \param state App layer state
1570 *
1571 * \retval 0 no match
1572 * \retval 1 match
1573 */
1574 int DetectEngineInspectGenericList(const DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
1575 const Signature *s, const SigMatchData *smd, Flow *f, const uint8_t flags, void *alstate,
1576 void *txv, uint64_t tx_id)
1577 {
1578 SCLogDebug("running match functions, sm %p", smd);
1579 if (smd != NULL) {
1580 while (1) {
1581 int match = 0;
1582 KEYWORD_PROFILING_START;
1583 match = sigmatch_table[smd->type].
1584 AppLayerTxMatch(det_ctx, f, flags, alstate, txv, s, smd->ctx);
1585 KEYWORD_PROFILING_END(det_ctx, smd->type, (match == 1));
1586 if (match == 0)
1587 return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
1588 if (match == 2) {
1589 return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
1590 }
1591
1592 if (smd->is_last)
1593 break;
1594 smd++;
1595 }
1596 }
1597
1598 return DETECT_ENGINE_INSPECT_SIG_MATCH;
1599 }
1600
1601
1602 /**
1603 * \brief Do the content inspection & validation for a signature
1604 *
1605 * \param de_ctx Detection engine context
1606 * \param det_ctx Detection engine thread context
1607 * \param s Signature to inspect
1608 * \param f Flow
1609 * \param flags app layer flags
1610 * \param state App layer state
1611 *
1612 * \retval 0 no match.
1613 * \retval 1 match.
1614 * \retval 2 Sig can't match.
1615 */
1616 int DetectEngineInspectBufferGeneric(
1617 DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
1618 const DetectEngineAppInspectionEngine *engine,
1619 const Signature *s,
1620 Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id)
1621 {
1622 const int list_id = engine->sm_list;
1623 SCLogDebug("running inspect on %d", list_id);
1624
1625 const bool eof = (AppLayerParserGetStateProgress(f->proto, f->alproto, txv, flags) > engine->progress);
1626
1627 SCLogDebug("list %d mpm? %s transforms %p",
1628 engine->sm_list, engine->mpm ? "true" : "false", engine->v2.transforms);
1629
1630 /* if prefilter didn't already run, we need to consider transformations */
1631 const DetectEngineTransforms *transforms = NULL;
1632 if (!engine->mpm) {
1633 transforms = engine->v2.transforms;
1634 }
1635
1636 const InspectionBuffer *buffer = engine->v2.GetData(det_ctx, transforms,
1637 f, flags, txv, list_id);
1638 if (unlikely(buffer == NULL)) {
1639 return eof ? DETECT_ENGINE_INSPECT_SIG_CANT_MATCH :
1640 DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
1641 }
1642
1643 const uint32_t data_len = buffer->inspect_len;
1644 const uint8_t *data = buffer->inspect;
1645 const uint64_t offset = buffer->inspect_offset;
1646
1647 uint8_t ci_flags = eof ? DETECT_CI_FLAGS_END : 0;
1648 ci_flags |= (offset == 0 ? DETECT_CI_FLAGS_START : 0);
1649 ci_flags |= buffer->flags;
1650
1651 det_ctx->discontinue_matching = 0;
1652 det_ctx->buffer_offset = 0;
1653 det_ctx->inspection_recursion_counter = 0;
1654
1655 /* Inspect all the uricontents fetched on each
1656 * transaction at the app layer */
1657 int r = DetectEngineContentInspection(de_ctx, det_ctx,
1658 s, engine->smd,
1659 NULL, f,
1660 (uint8_t *)data, data_len, offset, ci_flags,
1661 DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE);
1662 if (r == 1) {
1663 return DETECT_ENGINE_INSPECT_SIG_MATCH;
1664 } else {
1665 return eof ? DETECT_ENGINE_INSPECT_SIG_CANT_MATCH :
1666 DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
1667 }
1668 }
1669
1670 /**
1671 * \brief Do the content inspection & validation for a signature
1672 *
1673 * \param de_ctx Detection engine context
1674 * \param det_ctx Detection engine thread context
1675 * \param s Signature to inspect
1676 * \param p Packet
1677 *
1678 * \retval 0 no match.
1679 * \retval 1 match.
1680 */
1681 int DetectEngineInspectPktBufferGeneric(
1682 DetectEngineThreadCtx *det_ctx,
1683 const DetectEnginePktInspectionEngine *engine,
1684 const Signature *s, Packet *p, uint8_t *_alert_flags)
1685 {
1686 const int list_id = engine->sm_list;
1687 SCLogDebug("running inspect on %d", list_id);
1688
1689 SCLogDebug("list %d transforms %p",
1690 engine->sm_list, engine->v1.transforms);
1691
1692 /* if prefilter didn't already run, we need to consider transformations */
1693 const DetectEngineTransforms *transforms = NULL;
1694 if (!engine->mpm) {
1695 transforms = engine->v1.transforms;
1696 }
1697
1698 const InspectionBuffer *buffer = engine->v1.GetData(det_ctx, transforms, p,
1699 list_id);
1700 if (unlikely(buffer == NULL)) {
1701 return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
1702 }
1703
1704 const uint32_t data_len = buffer->inspect_len;
1705 const uint8_t *data = buffer->inspect;
1706 const uint64_t offset = 0;
1707
1708 uint8_t ci_flags = DETECT_CI_FLAGS_START|DETECT_CI_FLAGS_END;
1709 ci_flags |= buffer->flags;
1710
1711 det_ctx->discontinue_matching = 0;
1712 det_ctx->buffer_offset = 0;
1713 det_ctx->inspection_recursion_counter = 0;
1714
1715 /* Inspect all the uricontents fetched on each
1716 * transaction at the app layer */
1717 int r = DetectEngineContentInspection(det_ctx->de_ctx, det_ctx,
1718 s, engine->smd,
1719 p, p->flow,
1720 (uint8_t *)data, data_len, offset, ci_flags,
1721 DETECT_ENGINE_CONTENT_INSPECTION_MODE_HEADER);
1722 if (r == 1) {
1723 return DETECT_ENGINE_INSPECT_SIG_MATCH;
1724 } else {
1725 return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
1726 }
1727 }
1728
1729
1730 /* nudge capture loops to wake up */
1731 static void BreakCapture(void)
1732 {
1733 SCMutexLock(&tv_root_lock);
1734 for (ThreadVars *tv = tv_root[TVT_PPT]; tv != NULL; tv = tv->next) {
1735 if ((tv->tmm_flags & TM_FLAG_RECEIVE_TM) == 0) {
1736 continue;
1737 }
1738 /* find the correct slot */
1739 for (TmSlot *s = tv->tm_slots; s != NULL; s = s->slot_next) {
1740 if (suricata_ctl_flags != 0) {
1741 SCMutexUnlock(&tv_root_lock);
1742 return;
1743 }
1744
1745 TmModule *tm = TmModuleGetById(s->tm_id);
1746 if (!(tm->flags & TM_FLAG_RECEIVE_TM)) {
1747 continue;
1748 }
1749
1750 /* signal capture method that we need a packet. */
1751 TmThreadsSetFlag(tv, THV_CAPTURE_INJECT_PKT);
1752 /* if the method supports it, BreakLoop. Otherwise we rely on
1753 * the capture method's recv timeout */
1754 if (tm->PktAcqLoop && tm->PktAcqBreakLoop) {
1755 tm->PktAcqBreakLoop(tv, SC_ATOMIC_GET(s->slot_data));
1756 }
1757 break;
1758 }
1759 }
1760 SCMutexUnlock(&tv_root_lock);
1761 }
1762
1763 /** \internal
1764 * \brief inject a pseudo packet into each detect thread that doesn't use the
1765 * new det_ctx yet
1766 */
1767 static void InjectPackets(ThreadVars **detect_tvs,
1768 DetectEngineThreadCtx **new_det_ctx,
1769 int no_of_detect_tvs)
1770 {
1771 /* inject a fake packet if the detect thread isn't using the new ctx yet,
1772 * this speeds up the process */
1773 for (int i = 0; i < no_of_detect_tvs; i++) {
1774 if (SC_ATOMIC_GET(new_det_ctx[i]->so_far_used_by_detect) != 1) {
1775 if (detect_tvs[i]->inq != NULL) {
1776 Packet *p = PacketGetFromAlloc();
1777 if (p != NULL) {
1778 p->flags |= PKT_PSEUDO_STREAM_END;
1779 PKT_SET_SRC(p, PKT_SRC_DETECT_RELOAD_FLUSH);
1780 PacketQueue *q = detect_tvs[i]->inq->pq;
1781 SCMutexLock(&q->mutex_q);
1782 PacketEnqueue(q, p);
1783 SCCondSignal(&q->cond_q);
1784 SCMutexUnlock(&q->mutex_q);
1785 }
1786 }
1787 }
1788 }
1789 }
1790
1791 /** \internal
1792 * \brief Update detect threads with new detect engine
1793 *
1794 * Atomically update each detect thread with a new thread context
1795 * that is associated to the new detection engine(s).
1796 *
1797 * If called in unix socket mode, it's possible that we don't have
1798 * detect threads yet.
1799 *
1800 * \retval -1 error
1801 * \retval 0 no detection threads
1802 * \retval 1 successful reload
1803 */
1804 static int DetectEngineReloadThreads(DetectEngineCtx *new_de_ctx)
1805 {
1806 SCEnter();
1807 uint32_t i = 0;
1808
1809 /* count detect threads in use */
1810 uint32_t no_of_detect_tvs = TmThreadCountThreadsByTmmFlags(TM_FLAG_DETECT_TM);
1811 /* can be zero in unix socket mode */
1812 if (no_of_detect_tvs == 0) {
1813 return 0;
1814 }
1815
1816 /* prepare swap structures */
1817 DetectEngineThreadCtx *old_det_ctx[no_of_detect_tvs];
1818 DetectEngineThreadCtx *new_det_ctx[no_of_detect_tvs];
1819 ThreadVars *detect_tvs[no_of_detect_tvs];
1820 memset(old_det_ctx, 0x00, (no_of_detect_tvs * sizeof(DetectEngineThreadCtx *)));
1821 memset(new_det_ctx, 0x00, (no_of_detect_tvs * sizeof(DetectEngineThreadCtx *)));
1822 memset(detect_tvs, 0x00, (no_of_detect_tvs * sizeof(ThreadVars *)));
1823
1824 /* start the process of swapping detect threads ctxs */
1825
1826 /* get reference to tv's and setup new_det_ctx array */
1827 SCMutexLock(&tv_root_lock);
1828 for (ThreadVars *tv = tv_root[TVT_PPT]; tv != NULL; tv = tv->next) {
1829 if ((tv->tmm_flags & TM_FLAG_DETECT_TM) == 0) {
1830 continue;
1831 }
1832 for (TmSlot *s = tv->tm_slots; s != NULL; s = s->slot_next) {
1833 TmModule *tm = TmModuleGetById(s->tm_id);
1834 if (!(tm->flags & TM_FLAG_DETECT_TM)) {
1835 continue;
1836 }
1837
1838 if (suricata_ctl_flags != 0) {
1839 SCMutexUnlock(&tv_root_lock);
1840 goto error;
1841 }
1842
1843 old_det_ctx[i] = FlowWorkerGetDetectCtxPtr(SC_ATOMIC_GET(s->slot_data));
1844 detect_tvs[i] = tv;
1845
1846 new_det_ctx[i] = DetectEngineThreadCtxInitForReload(tv, new_de_ctx, 1);
1847 if (new_det_ctx[i] == NULL) {
1848 SCLogError(SC_ERR_LIVE_RULE_SWAP, "Detect engine thread init "
1849 "failure in live rule swap. Let's get out of here");
1850 SCMutexUnlock(&tv_root_lock);
1851 goto error;
1852 }
1853 SCLogDebug("live rule swap created new det_ctx - %p and de_ctx "
1854 "- %p\n", new_det_ctx[i], new_de_ctx);
1855 i++;
1856 break;
1857 }
1858 }
1859 BUG_ON(i != no_of_detect_tvs);
1860
1861 /* atomically replace the det_ctx data */
1862 i = 0;
1863 for (ThreadVars *tv = tv_root[TVT_PPT]; tv != NULL; tv = tv->next) {
1864 if ((tv->tmm_flags & TM_FLAG_DETECT_TM) == 0) {
1865 continue;
1866 }
1867 for (TmSlot *s = tv->tm_slots; s != NULL; s = s->slot_next) {
1868 TmModule *tm = TmModuleGetById(s->tm_id);
1869 if (!(tm->flags & TM_FLAG_DETECT_TM)) {
1870 continue;
1871 }
1872 SCLogDebug("swapping new det_ctx - %p with older one - %p",
1873 new_det_ctx[i], SC_ATOMIC_GET(s->slot_data));
1874 FlowWorkerReplaceDetectCtx(SC_ATOMIC_GET(s->slot_data), new_det_ctx[i++]);
1875 break;
1876 }
1877 }
1878 SCMutexUnlock(&tv_root_lock);
1879
1880 /* threads now all have new data, however they may not have started using
1881 * it and may still use the old data */
1882
1883 SCLogDebug("Live rule swap has swapped %d old det_ctx's with new ones, "
1884 "along with the new de_ctx", no_of_detect_tvs);
1885
1886 InjectPackets(detect_tvs, new_det_ctx, no_of_detect_tvs);
1887
1888 for (i = 0; i < no_of_detect_tvs; i++) {
1889 int break_out = 0;
1890 usleep(1000);
1891 while (SC_ATOMIC_GET(new_det_ctx[i]->so_far_used_by_detect) != 1) {
1892 if (suricata_ctl_flags != 0) {
1893 break_out = 1;
1894 break;
1895 }
1896
1897 BreakCapture();
1898 usleep(1000);
1899 }
1900 if (break_out)
1901 break;
1902 SCLogDebug("new_det_ctx - %p used by detect engine", new_det_ctx[i]);
1903 }
1904
1905 /* this is to make sure that if someone initiated shutdown during a live
1906 * rule swap, the live rule swap won't clean up the old det_ctx and
1907 * de_ctx, till all detect threads have stopped working and sitting
1908 * silently after setting RUNNING_DONE flag and while waiting for
1909 * THV_DEINIT flag */
1910 if (i != no_of_detect_tvs) { // not all threads we swapped
1911 for (ThreadVars *tv = tv_root[TVT_PPT]; tv != NULL; tv = tv->next) {
1912 if ((tv->tmm_flags & TM_FLAG_DETECT_TM) == 0) {
1913 continue;
1914 }
1915
1916 while (!TmThreadsCheckFlag(tv, THV_RUNNING_DONE)) {
1917 usleep(100);
1918 }
1919 }
1920 }
1921
1922 /* free all the ctxs */
1923 for (i = 0; i < no_of_detect_tvs; i++) {
1924 SCLogDebug("Freeing old_det_ctx - %p used by detect",
1925 old_det_ctx[i]);
1926 DetectEngineThreadCtxDeinit(NULL, old_det_ctx[i]);
1927 }
1928
1929 SRepReloadComplete();
1930
1931 return 1;
1932
1933 error:
1934 for (i = 0; i < no_of_detect_tvs; i++) {
1935 if (new_det_ctx[i] != NULL)
1936 DetectEngineThreadCtxDeinit(NULL, new_det_ctx[i]);
1937 }
1938 return -1;
1939 }
1940
1941 static DetectEngineCtx *DetectEngineCtxInitReal(enum DetectEngineType type, const char *prefix)
1942 {
1943 DetectEngineCtx *de_ctx = SCMalloc(sizeof(DetectEngineCtx));
1944 if (unlikely(de_ctx == NULL))
1945 goto error;
1946
1947 memset(de_ctx,0,sizeof(DetectEngineCtx));
1948 memset(&de_ctx->sig_stat, 0, sizeof(SigFileLoaderStat));
1949 TAILQ_INIT(&de_ctx->sig_stat.failed_sigs);
1950 de_ctx->sigerror = NULL;
1951 de_ctx->type = type;
1952
1953 if (type == DETECT_ENGINE_TYPE_DD_STUB || type == DETECT_ENGINE_TYPE_MT_STUB) {
1954 de_ctx->version = DetectEngineGetVersion();
1955 SCLogDebug("stub %u with version %u", type, de_ctx->version);
1956 return de_ctx;
1957 }
1958
1959 if (prefix != NULL) {
1960 strlcpy(de_ctx->config_prefix, prefix, sizeof(de_ctx->config_prefix));
1961 }
1962
1963 if (ConfGetBool("engine.init-failure-fatal", (int *)&(de_ctx->failure_fatal)) != 1) {
1964 SCLogDebug("ConfGetBool could not load the value.");
1965 }
1966
1967 de_ctx->mpm_matcher = PatternMatchDefaultMatcher();
1968 de_ctx->spm_matcher = SinglePatternMatchDefaultMatcher();
1969 SCLogConfig("pattern matchers: MPM: %s, SPM: %s",
1970 mpm_table[de_ctx->mpm_matcher].name,
1971 spm_table[de_ctx->spm_matcher].name);
1972
1973 de_ctx->spm_global_thread_ctx = SpmInitGlobalThreadCtx(de_ctx->spm_matcher);
1974 if (de_ctx->spm_global_thread_ctx == NULL) {
1975 SCLogDebug("Unable to alloc SpmGlobalThreadCtx.");
1976 goto error;
1977 }
1978
1979 if (DetectEngineCtxLoadConf(de_ctx) == -1) {
1980 goto error;
1981 }
1982
1983 SigGroupHeadHashInit(de_ctx);
1984 MpmStoreInit(de_ctx);
1985 ThresholdHashInit(de_ctx);
1986 DetectParseDupSigHashInit(de_ctx);
1987 DetectAddressMapInit(de_ctx);
1988 DetectMetadataHashInit(de_ctx);
1989 DetectBufferTypeSetupDetectEngine(de_ctx);
1990
1991 /* init iprep... ignore errors for now */
1992 (void)SRepInit(de_ctx);
1993
1994 SCClassConfLoadClassficationConfigFile(de_ctx, NULL);
1995 SCRConfLoadReferenceConfigFile(de_ctx, NULL);
1996
1997 if (ActionInitConfig() < 0) {
1998 goto error;
1999 }
2000
2001 de_ctx->version = DetectEngineGetVersion();
2002 VarNameStoreSetupStaging(de_ctx->version);
2003 SCLogDebug("dectx with version %u", de_ctx->version);
2004 return de_ctx;
2005 error:
2006 if (de_ctx != NULL) {
2007 DetectEngineCtxFree(de_ctx);
2008 }
2009 return NULL;
2010
2011 }
2012
2013 DetectEngineCtx *DetectEngineCtxInitStubForMT(void)
2014 {
2015 return DetectEngineCtxInitReal(DETECT_ENGINE_TYPE_MT_STUB, NULL);
2016 }
2017
2018 DetectEngineCtx *DetectEngineCtxInitStubForDD(void)
2019 {
2020 return DetectEngineCtxInitReal(DETECT_ENGINE_TYPE_DD_STUB, NULL);
2021 }
2022
2023 DetectEngineCtx *DetectEngineCtxInit(void)
2024 {
2025 return DetectEngineCtxInitReal(DETECT_ENGINE_TYPE_NORMAL, NULL);
2026 }
2027
2028 DetectEngineCtx *DetectEngineCtxInitWithPrefix(const char *prefix)
2029 {
2030 if (prefix == NULL || strlen(prefix) == 0)
2031 return DetectEngineCtxInit();
2032 else
2033 return DetectEngineCtxInitReal(DETECT_ENGINE_TYPE_NORMAL, prefix);
2034 }
2035
2036 static void DetectEngineCtxFreeThreadKeywordData(DetectEngineCtx *de_ctx)
2037 {
2038 DetectEngineThreadKeywordCtxItem *item = de_ctx->keyword_list;
2039 while (item) {
2040 DetectEngineThreadKeywordCtxItem *next = item->next;
2041 SCFree(item);
2042 item = next;
2043 }
2044 de_ctx->keyword_list = NULL;
2045 }
2046
2047 static void DetectEngineCtxFreeFailedSigs(DetectEngineCtx *de_ctx)
2048 {
2049 SigString *item = NULL;
2050 SigString *sitem;
2051
2052 TAILQ_FOREACH_SAFE(item, &de_ctx->sig_stat.failed_sigs, next, sitem) {
2053 SCFree(item->filename);
2054 SCFree(item->sig_str);
2055 if (item->sig_error) {
2056 SCFree(item->sig_error);
2057 }
2058 TAILQ_REMOVE(&de_ctx->sig_stat.failed_sigs, item, next);
2059 SCFree(item);
2060 }
2061 }
2062
2063 /**
2064 * \brief Free a DetectEngineCtx::
2065 *
2066 * \param de_ctx DetectEngineCtx:: to be freed
2067 */
2068 void DetectEngineCtxFree(DetectEngineCtx *de_ctx)
2069 {
2070
2071 if (de_ctx == NULL)
2072 return;
2073
2074 #ifdef PROFILING
2075 if (de_ctx->profile_ctx != NULL) {
2076 SCProfilingRuleDestroyCtx(de_ctx->profile_ctx);
2077 de_ctx->profile_ctx = NULL;
2078 }
2079 if (de_ctx->profile_keyword_ctx != NULL) {
2080 SCProfilingKeywordDestroyCtx(de_ctx);//->profile_keyword_ctx);
2081 // de_ctx->profile_keyword_ctx = NULL;
2082 }
2083 if (de_ctx->profile_sgh_ctx != NULL) {
2084 SCProfilingSghDestroyCtx(de_ctx);
2085 }
2086 SCProfilingPrefilterDestroyCtx(de_ctx);
2087 #endif
2088
2089 /* Normally the hashes are freed elsewhere, but
2090 * to be sure look at them again here.
2091 */
2092 SigGroupHeadHashFree(de_ctx);
2093 MpmStoreFree(de_ctx);
2094 DetectParseDupSigHashFree(de_ctx);
2095 SCSigSignatureOrderingModuleCleanup(de_ctx);
2096 ThresholdContextDestroy(de_ctx);
2097 SigCleanSignatures(de_ctx);
2098 if (de_ctx->sig_array)
2099 SCFree(de_ctx->sig_array);
2100
2101 SCClassConfDeInitContext(de_ctx);
2102 SCRConfDeInitContext(de_ctx);
2103
2104 SigGroupCleanup(de_ctx);
2105
2106 SpmDestroyGlobalThreadCtx(de_ctx->spm_global_thread_ctx);
2107
2108 MpmFactoryDeRegisterAllMpmCtxProfiles(de_ctx);
2109
2110 DetectEngineCtxFreeThreadKeywordData(de_ctx);
2111 SRepDestroy(de_ctx);
2112 DetectEngineCtxFreeFailedSigs(de_ctx);
2113
2114 DetectAddressMapFree(de_ctx);
2115 DetectMetadataHashFree(de_ctx);
2116
2117 /* if we have a config prefix, remove the config from the tree */
2118 if (strlen(de_ctx->config_prefix) > 0) {
2119 /* remove config */
2120 ConfNode *node = ConfGetNode(de_ctx->config_prefix);
2121 if (node != NULL) {
2122 ConfNodeRemove(node); /* frees node */
2123 }
2124 #if 0
2125 ConfDump();
2126 #endif
2127 }
2128
2129 DetectPortCleanupList(de_ctx, de_ctx->tcp_whitelist);
2130 DetectPortCleanupList(de_ctx, de_ctx->udp_whitelist);
2131
2132 DetectBufferTypeFreeDetectEngine(de_ctx);
2133 /* freed our var name hash */
2134 VarNameStoreFree(de_ctx->version);
2135
2136 SCFree(de_ctx);
2137 //DetectAddressGroupPrintMemory();
2138 //DetectSigGroupPrintMemory();
2139 //DetectPortPrintMemory();
2140 }
2141
2142 /** \brief Function that load DetectEngineCtx config for grouping sigs
2143 * used by the engine
2144 * \retval 0 if no config provided, 1 if config was provided
2145 * and loaded successfully
2146 */
2147 static int DetectEngineCtxLoadConf(DetectEngineCtx *de_ctx)
2148 {
2149 uint8_t profile = ENGINE_PROFILE_MEDIUM;
2150 const char *max_uniq_toclient_groups_str = NULL;
2151 const char *max_uniq_toserver_groups_str = NULL;
2152 const char *sgh_mpm_context = NULL;
2153 const char *de_ctx_profile = NULL;
2154
2155 (void)ConfGet("detect.profile", &de_ctx_profile);
2156 (void)ConfGet("detect.sgh-mpm-context", &sgh_mpm_context);
2157
2158 ConfNode *de_ctx_custom = ConfGetNode("detect-engine");
2159 ConfNode *opt = NULL;
2160
2161 if (de_ctx_custom != NULL) {
2162 TAILQ_FOREACH(opt, &de_ctx_custom->head, next) {
2163 if (de_ctx_profile == NULL) {
2164 if (opt->val && strcmp(opt->val, "profile") == 0) {
2165 de_ctx_profile = opt->head.tqh_first->val;
2166 }
2167 }
2168
2169 if (sgh_mpm_context == NULL) {
2170 if (opt->val && strcmp(opt->val, "sgh-mpm-context") == 0) {
2171 sgh_mpm_context = opt->head.tqh_first->val;
2172 }
2173 }
2174 }
2175 }
2176
2177 if (de_ctx_profile != NULL) {
2178 if (strcmp(de_ctx_profile, "low") == 0 ||
2179 strcmp(de_ctx_profile, "lowest") == 0) { // legacy
2180 profile = ENGINE_PROFILE_LOW;
2181 } else if (strcmp(de_ctx_profile, "medium") == 0) {
2182 profile = ENGINE_PROFILE_MEDIUM;
2183 } else if (strcmp(de_ctx_profile, "high") == 0 ||
2184 strcmp(de_ctx_profile, "highest") == 0) { // legacy
2185 profile = ENGINE_PROFILE_HIGH;
2186 } else if (strcmp(de_ctx_profile, "custom") == 0) {
2187 profile = ENGINE_PROFILE_CUSTOM;
2188 } else {
2189 SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY,
2190 "invalid value for detect.profile: '%s'. "
2191 "Valid options: low, medium, high and custom.",
2192 de_ctx_profile);
2193 return -1;
2194 }
2195
2196 SCLogDebug("Profile for detection engine groups is \"%s\"", de_ctx_profile);
2197 } else {
2198 SCLogDebug("Profile for detection engine groups not provided "
2199 "at suricata.yaml. Using default (\"medium\").");
2200 }
2201
2202 /* detect-engine.sgh-mpm-context option parsing */
2203 if (sgh_mpm_context == NULL || strcmp(sgh_mpm_context, "auto") == 0) {
2204 /* for now, since we still haven't implemented any intelligence into
2205 * understanding the patterns and distributing mpm_ctx across sgh */
2206 if (de_ctx->mpm_matcher == MPM_AC || de_ctx->mpm_matcher == MPM_AC_KS ||
2207 #ifdef BUILD_HYPERSCAN
2208 de_ctx->mpm_matcher == MPM_HS ||
2209 #endif
2210 de_ctx->mpm_matcher == MPM_AC_BS) {
2211 de_ctx->sgh_mpm_ctx_cnf = ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE;
2212 } else {
2213 de_ctx->sgh_mpm_ctx_cnf = ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL;
2214 }
2215 } else {
2216 if (strcmp(sgh_mpm_context, "single") == 0) {
2217 de_ctx->sgh_mpm_ctx_cnf = ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE;
2218 } else if (strcmp(sgh_mpm_context, "full") == 0) {
2219 de_ctx->sgh_mpm_ctx_cnf = ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL;
2220 } else {
2221 SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "You have supplied an "
2222 "invalid conf value for detect-engine.sgh-mpm-context-"
2223 "%s", sgh_mpm_context);
2224 exit(EXIT_FAILURE);
2225 }
2226 }
2227
2228 if (run_mode == RUNMODE_UNITTEST) {
2229 de_ctx->sgh_mpm_ctx_cnf = ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL;
2230 }
2231
2232 /* parse profile custom-values */
2233 opt = NULL;
2234 switch (profile) {
2235 case ENGINE_PROFILE_LOW:
2236 de_ctx->max_uniq_toclient_groups = 15;
2237 de_ctx->max_uniq_toserver_groups = 25;
2238 break;
2239
2240 case ENGINE_PROFILE_HIGH:
2241 de_ctx->max_uniq_toclient_groups = 75;
2242 de_ctx->max_uniq_toserver_groups = 75;
2243 break;
2244
2245 case ENGINE_PROFILE_CUSTOM:
2246 (void)ConfGet("detect.custom-values.toclient-groups",
2247 &max_uniq_toclient_groups_str);
2248 (void)ConfGet("detect.custom-values.toserver-groups",
2249 &max_uniq_toserver_groups_str);
2250
2251 if (de_ctx_custom != NULL) {
2252 TAILQ_FOREACH(opt, &de_ctx_custom->head, next) {
2253 if (opt->val && strcmp(opt->val, "custom-values") == 0) {
2254 if (max_uniq_toclient_groups_str == NULL) {
2255 max_uniq_toclient_groups_str = (char *)ConfNodeLookupChildValue
2256 (opt->head.tqh_first, "toclient-sp-groups");
2257 }
2258 if (max_uniq_toclient_groups_str == NULL) {
2259 max_uniq_toclient_groups_str = (char *)ConfNodeLookupChildValue
2260 (opt->head.tqh_first, "toclient-groups");
2261 }
2262 if (max_uniq_toserver_groups_str == NULL) {
2263 max_uniq_toserver_groups_str = (char *)ConfNodeLookupChildValue
2264 (opt->head.tqh_first, "toserver-dp-groups");
2265 }
2266 if (max_uniq_toserver_groups_str == NULL) {
2267 max_uniq_toserver_groups_str = (char *)ConfNodeLookupChildValue
2268 (opt->head.tqh_first, "toserver-groups");
2269 }
2270 }
2271 }
2272 }
2273 if (max_uniq_toclient_groups_str != NULL) {
2274 if (StringParseUint16(&de_ctx->max_uniq_toclient_groups, 10,
2275 strlen(max_uniq_toclient_groups_str),
2276 (const char *)max_uniq_toclient_groups_str) <= 0)
2277 {
2278 de_ctx->max_uniq_toclient_groups = 20;
2279
2280 SCLogWarning(SC_ERR_SIZE_PARSE, "parsing '%s' for "
2281 "toclient-groups failed, using %u",
2282 max_uniq_toclient_groups_str,
2283 de_ctx->max_uniq_toclient_groups);
2284 }
2285 } else {
2286 de_ctx->max_uniq_toclient_groups = 20;
2287 }
2288 SCLogConfig("toclient-groups %u", de_ctx->max_uniq_toclient_groups);
2289
2290 if (max_uniq_toserver_groups_str != NULL) {
2291 if (StringParseUint16(&de_ctx->max_uniq_toserver_groups, 10,
2292 strlen(max_uniq_toserver_groups_str),
2293 (const char *)max_uniq_toserver_groups_str) <= 0)
2294 {
2295 de_ctx->max_uniq_toserver_groups = 40;
2296
2297 SCLogWarning(SC_ERR_SIZE_PARSE, "parsing '%s' for "
2298 "toserver-groups failed, using %u",
2299 max_uniq_toserver_groups_str,
2300 de_ctx->max_uniq_toserver_groups);
2301 }
2302 } else {
2303 de_ctx->max_uniq_toserver_groups = 40;
2304 }
2305 SCLogConfig("toserver-groups %u", de_ctx->max_uniq_toserver_groups);
2306 break;
2307
2308 /* Default (or no config provided) is profile medium */
2309 case ENGINE_PROFILE_MEDIUM:
2310 case ENGINE_PROFILE_UNKNOWN:
2311 default:
2312 de_ctx->max_uniq_toclient_groups = 20;
2313 de_ctx->max_uniq_toserver_groups = 40;
2314 break;
2315 }
2316
2317 intmax_t value = 0;
2318 if (ConfGetInt("detect.inspection-recursion-limit", &value) == 1)
2319 {
2320 if (value >= 0 && value <= INT_MAX) {
2321 de_ctx->inspection_recursion_limit = (int)value;
2322 }
2323
2324 /* fall back to old config parsing */
2325 } else {
2326 ConfNode *insp_recursion_limit_node = NULL;
2327 char *insp_recursion_limit = NULL;
2328
2329 if (de_ctx_custom != NULL) {
2330 opt = NULL;
2331 TAILQ_FOREACH(opt, &de_ctx_custom->head, next) {
2332 if (opt->val && strcmp(opt->val, "inspection-recursion-limit") != 0)
2333 continue;
2334
2335 insp_recursion_limit_node = ConfNodeLookupChild(opt, opt->val);
2336 if (insp_recursion_limit_node == NULL) {
2337 SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "Error retrieving conf "
2338 "entry for detect-engine:inspection-recursion-limit");
2339 break;
2340 }
2341 insp_recursion_limit = insp_recursion_limit_node->val;
2342 SCLogDebug("Found detect-engine.inspection-recursion-limit - %s:%s",
2343 insp_recursion_limit_node->name, insp_recursion_limit_node->val);
2344 break;
2345 }
2346
2347 if (insp_recursion_limit != NULL) {
2348 if (StringParseInt32(&de_ctx->inspection_recursion_limit, 10,
2349 0, (const char *)insp_recursion_limit) < 0) {
2350 SCLogWarning(SC_ERR_INVALID_VALUE, "Invalid value for "
2351 "detect-engine.inspection-recursion-limit: %s "
2352 "resetting to %d", insp_recursion_limit,
2353 DETECT_ENGINE_DEFAULT_INSPECTION_RECURSION_LIMIT);
2354 de_ctx->inspection_recursion_limit =
2355 DETECT_ENGINE_DEFAULT_INSPECTION_RECURSION_LIMIT;
2356 }
2357 } else {
2358 de_ctx->inspection_recursion_limit =
2359 DETECT_ENGINE_DEFAULT_INSPECTION_RECURSION_LIMIT;
2360 }
2361 }
2362 }
2363
2364 if (de_ctx->inspection_recursion_limit == 0)
2365 de_ctx->inspection_recursion_limit = -1;
2366
2367 SCLogDebug("de_ctx->inspection_recursion_limit: %d",
2368 de_ctx->inspection_recursion_limit);
2369
2370 /* parse port grouping whitelisting settings */
2371
2372 const char *ports = NULL;
2373 (void)ConfGet("detect.grouping.tcp-whitelist", &ports);
2374 if (ports) {
2375 SCLogConfig("grouping: tcp-whitelist %s", ports);
2376 } else {
2377 ports = "53, 80, 139, 443, 445, 1433, 3306, 3389, 6666, 6667, 8080";
2378 SCLogConfig("grouping: tcp-whitelist (default) %s", ports);
2379
2380 }
2381 if (DetectPortParse(de_ctx, &de_ctx->tcp_whitelist, ports) != 0) {
2382 SCLogWarning(SC_ERR_INVALID_YAML_CONF_ENTRY, "'%s' is not a valid value "
2383 "for detect.grouping.tcp-whitelist", ports);
2384 }
2385 DetectPort *x = de_ctx->tcp_whitelist;
2386 for ( ; x != NULL; x = x->next) {
2387 if (x->port != x->port2) {
2388 SCLogWarning(SC_ERR_INVALID_YAML_CONF_ENTRY, "'%s' is not a valid value "
2389 "for detect.grouping.tcp-whitelist: only single ports allowed", ports);
2390 DetectPortCleanupList(de_ctx, de_ctx->tcp_whitelist);
2391 de_ctx->tcp_whitelist = NULL;
2392 break;
2393 }
2394 }
2395
2396 ports = NULL;
2397 (void)ConfGet("detect.grouping.udp-whitelist", &ports);
2398 if (ports) {
2399 SCLogConfig("grouping: udp-whitelist %s", ports);
2400 } else {
2401 ports = "53, 135, 5060";
2402 SCLogConfig("grouping: udp-whitelist (default) %s", ports);
2403
2404 }
2405 if (DetectPortParse(de_ctx, &de_ctx->udp_whitelist, ports) != 0) {
2406 SCLogWarning(SC_ERR_INVALID_YAML_CONF_ENTRY, "'%s' is not a valid value "
2407 "forr detect.grouping.udp-whitelist", ports);
2408 }
2409 for (x = de_ctx->udp_whitelist; x != NULL; x = x->next) {
2410 if (x->port != x->port2) {
2411 SCLogWarning(SC_ERR_INVALID_YAML_CONF_ENTRY, "'%s' is not a valid value "
2412 "for detect.grouping.udp-whitelist: only single ports allowed", ports);
2413 DetectPortCleanupList(de_ctx, de_ctx->udp_whitelist);
2414 de_ctx->udp_whitelist = NULL;
2415 break;
2416 }
2417 }
2418
2419 de_ctx->prefilter_setting = DETECT_PREFILTER_MPM;
2420 const char *pf_setting = NULL;
2421 if (ConfGet("detect.prefilter.default", &pf_setting) == 1 && pf_setting) {
2422 if (strcasecmp(pf_setting, "mpm") == 0) {
2423 de_ctx->prefilter_setting = DETECT_PREFILTER_MPM;
2424 } else if (strcasecmp(pf_setting, "auto") == 0) {
2425 de_ctx->prefilter_setting = DETECT_PREFILTER_AUTO;
2426 }
2427 }
2428 switch (de_ctx->prefilter_setting) {
2429 case DETECT_PREFILTER_MPM:
2430 SCLogConfig("prefilter engines: MPM");
2431 break;
2432 case DETECT_PREFILTER_AUTO:
2433 SCLogConfig("prefilter engines: MPM and keywords");
2434 break;
2435 }
2436
2437 return 0;
2438 }
2439
2440 /*
2441 * getting & (re)setting the internal sig i
2442 */
2443
2444 //inline uint32_t DetectEngineGetMaxSigId(DetectEngineCtx *de_ctx)
2445 //{
2446 // return de_ctx->signum;
2447 //}
2448
2449 void DetectEngineResetMaxSigId(DetectEngineCtx *de_ctx)
2450 {
2451 de_ctx->signum = 0;
2452 }
2453
2454 static int DetectEngineThreadCtxInitGlobalKeywords(DetectEngineThreadCtx *det_ctx)
2455 {
2456 const DetectEngineMasterCtx *master = &g_master_de_ctx;
2457
2458 if (master->keyword_id > 0) {
2459 // coverity[suspicious_sizeof : FALSE]
2460 det_ctx->global_keyword_ctxs_array = (void **)SCCalloc(master->keyword_id, sizeof(void *));
2461 if (det_ctx->global_keyword_ctxs_array == NULL) {
2462 SCLogError(SC_ERR_DETECT_PREPARE, "setting up thread local detect ctx");
2463 return TM_ECODE_FAILED;
2464 }
2465 det_ctx->global_keyword_ctxs_size = master->keyword_id;
2466
2467 const DetectEngineThreadKeywordCtxItem *item = master->keyword_list;
2468 while (item) {
2469 det_ctx->global_keyword_ctxs_array[item->id] = item->InitFunc(item->data);
2470 if (det_ctx->global_keyword_ctxs_array[item->id] == NULL) {
2471 SCLogError(SC_ERR_DETECT_PREPARE, "setting up thread local detect ctx "
2472 "for keyword \"%s\" failed", item->name);
2473 return TM_ECODE_FAILED;
2474 }
2475 item = item->next;
2476 }
2477 }
2478 return TM_ECODE_OK;
2479 }
2480
2481 static void DetectEngineThreadCtxDeinitGlobalKeywords(DetectEngineThreadCtx *det_ctx)
2482 {
2483 if (det_ctx->global_keyword_ctxs_array == NULL ||
2484 det_ctx->global_keyword_ctxs_size == 0) {
2485 return;
2486 }
2487
2488 const DetectEngineMasterCtx *master = &g_master_de_ctx;
2489 if (master->keyword_id > 0) {
2490 const DetectEngineThreadKeywordCtxItem *item = master->keyword_list;
2491 while (item) {
2492 if (det_ctx->global_keyword_ctxs_array[item->id] != NULL)
2493 item->FreeFunc(det_ctx->global_keyword_ctxs_array[item->id]);
2494
2495 item = item->next;
2496 }
2497 det_ctx->global_keyword_ctxs_size = 0;
2498 SCFree(det_ctx->global_keyword_ctxs_array);
2499 det_ctx->global_keyword_ctxs_array = NULL;
2500 }
2501 }
2502
2503 static int DetectEngineThreadCtxInitKeywords(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx)
2504 {
2505 if (de_ctx->keyword_id > 0) {
2506 // coverity[suspicious_sizeof : FALSE]
2507 det_ctx->keyword_ctxs_array = SCMalloc(de_ctx->keyword_id * sizeof(void *));
2508 if (det_ctx->keyword_ctxs_array == NULL) {
2509 SCLogError(SC_ERR_DETECT_PREPARE, "setting up thread local detect ctx");
2510 return TM_ECODE_FAILED;
2511 }
2512
2513 memset(det_ctx->keyword_ctxs_array, 0x00, de_ctx->keyword_id * sizeof(void *));
2514
2515 det_ctx->keyword_ctxs_size = de_ctx->keyword_id;
2516
2517 DetectEngineThreadKeywordCtxItem *item = de_ctx->keyword_list;
2518 while (item) {
2519 det_ctx->keyword_ctxs_array[item->id] = item->InitFunc(item->data);
2520 if (det_ctx->keyword_ctxs_array[item->id] == NULL) {
2521 SCLogError(SC_ERR_DETECT_PREPARE, "setting up thread local detect ctx "
2522 "for keyword \"%s\" failed", item->name);
2523 return TM_ECODE_FAILED;
2524 }
2525 item = item->next;
2526 }
2527 }
2528 return TM_ECODE_OK;
2529 }
2530
2531 static void DetectEngineThreadCtxDeinitKeywords(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx)
2532 {
2533 if (de_ctx->keyword_id > 0) {
2534 DetectEngineThreadKeywordCtxItem *item = de_ctx->keyword_list;
2535 while (item) {
2536 if (det_ctx->keyword_ctxs_array[item->id] != NULL)
2537 item->FreeFunc(det_ctx->keyword_ctxs_array[item->id]);
2538
2539 item = item->next;
2540 }
2541 det_ctx->keyword_ctxs_size = 0;
2542 SCFree(det_ctx->keyword_ctxs_array);
2543 det_ctx->keyword_ctxs_array = NULL;
2544 }
2545 }
2546
2547 /** NOTE: master MUST be locked before calling this */
2548 static TmEcode DetectEngineThreadCtxInitForMT(ThreadVars *tv, DetectEngineThreadCtx *det_ctx)
2549 {
2550 DetectEngineMasterCtx *master = &g_master_de_ctx;
2551 DetectEngineTenantMapping *map_array = NULL;
2552 uint32_t map_array_size = 0;
2553 uint32_t map_cnt = 0;
2554 int max_tenant_id = 0;
2555 DetectEngineCtx *list = master->list;
2556 HashTable *mt_det_ctxs_hash = NULL;
2557
2558 if (master->tenant_selector == TENANT_SELECTOR_UNKNOWN) {
2559 SCLogError(SC_ERR_MT_NO_SELECTOR, "no tenant selector set: "
2560 "set using multi-detect.selector");
2561 return TM_ECODE_FAILED;
2562 }
2563
2564 uint32_t tcnt = 0;
2565 while (list) {
2566 if (list->tenant_id > max_tenant_id)
2567 max_tenant_id = list->tenant_id;
2568
2569 list = list->next;
2570 tcnt++;
2571 }
2572
2573 mt_det_ctxs_hash = HashTableInit(tcnt * 2, TenantIdHash, TenantIdCompare, TenantIdFree);
2574 if (mt_det_ctxs_hash == NULL) {
2575 goto error;
2576 }
2577
2578 if (tcnt == 0) {
2579 SCLogInfo("no tenants left, or none registered yet");
2580 } else {
2581 max_tenant_id++;
2582
2583 DetectEngineTenantMapping *map = master->tenant_mapping_list;
2584 while (map) {
2585 map_cnt++;
2586 map = map->next;
2587 }
2588
2589 if (map_cnt > 0) {
2590 map_array_size = map_cnt + 1;
2591
2592 map_array = SCCalloc(map_array_size, sizeof(*map_array));
2593 if (map_array == NULL)
2594 goto error;
2595
2596 /* fill the array */
2597 map_cnt = 0;
2598 map = master->tenant_mapping_list;
2599 while (map) {
2600 if (map_cnt >= map_array_size) {
2601 goto error;
2602 }
2603 map_array[map_cnt].traffic_id = map->traffic_id;
2604 map_array[map_cnt].tenant_id = map->tenant_id;
2605 map_cnt++;
2606 map = map->next;
2607 }
2608
2609 }
2610
2611 /* set up hash for tenant lookup */
2612 list = master->list;
2613 while (list) {
2614 SCLogDebug("tenant-id %u", list->tenant_id);
2615 if (list->tenant_id != 0) {
2616 DetectEngineThreadCtx *mt_det_ctx = DetectEngineThreadCtxInitForReload(tv, list, 0);
2617 if (mt_det_ctx == NULL)
2618 goto error;
2619 if (HashTableAdd(mt_det_ctxs_hash, mt_det_ctx, 0) != 0) {
2620 goto error;
2621 }
2622 }
2623 list = list->next;
2624 }
2625 }
2626
2627 det_ctx->mt_det_ctxs_hash = mt_det_ctxs_hash;
2628 mt_det_ctxs_hash = NULL;
2629
2630 det_ctx->mt_det_ctxs_cnt = max_tenant_id;
2631
2632 det_ctx->tenant_array = map_array;
2633 det_ctx->tenant_array_size = map_array_size;
2634
2635 switch (master->tenant_selector) {
2636 case TENANT_SELECTOR_UNKNOWN:
2637 SCLogDebug("TENANT_SELECTOR_UNKNOWN");
2638 break;
2639 case TENANT_SELECTOR_VLAN:
2640 det_ctx->TenantGetId = DetectEngineTentantGetIdFromVlanId;
2641 SCLogDebug("TENANT_SELECTOR_VLAN");
2642 break;
2643 case TENANT_SELECTOR_LIVEDEV:
2644 det_ctx->TenantGetId = DetectEngineTentantGetIdFromLivedev;
2645 SCLogDebug("TENANT_SELECTOR_LIVEDEV");
2646 break;
2647 case TENANT_SELECTOR_DIRECT:
2648 det_ctx->TenantGetId = DetectEngineTentantGetIdFromPcap;
2649 SCLogDebug("TENANT_SELECTOR_DIRECT");
2650 break;
2651 }
2652
2653 return TM_ECODE_OK;
2654 error:
2655 if (map_array != NULL)
2656 SCFree(map_array);
2657 if (mt_det_ctxs_hash != NULL)
2658 HashTableFree(mt_det_ctxs_hash);
2659
2660 return TM_ECODE_FAILED;
2661 }
2662
2663 /** \internal
2664 * \brief Helper for DetectThread setup functions
2665 */
2666 static TmEcode ThreadCtxDoInit (DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx)
2667 {
2668 PatternMatchThreadPrepare(&det_ctx->mtc, de_ctx->mpm_matcher);
2669 PatternMatchThreadPrepare(&det_ctx->mtcs, de_ctx->mpm_matcher);
2670 PatternMatchThreadPrepare(&det_ctx->mtcu, de_ctx->mpm_matcher);
2671
2672 PmqSetup(&det_ctx->pmq);
2673
2674 det_ctx->spm_thread_ctx = SpmMakeThreadCtx(de_ctx->spm_global_thread_ctx);
2675 if (det_ctx->spm_thread_ctx == NULL) {
2676 return TM_ECODE_FAILED;
2677 }
2678
2679 /* sized to the max of our sgh settings. A max setting of 0 implies that all
2680 * sgh's have: sgh->non_pf_store_cnt == 0 */
2681 if (de_ctx->non_pf_store_cnt_max > 0) {
2682 det_ctx->non_pf_id_array = SCCalloc(de_ctx->non_pf_store_cnt_max, sizeof(SigIntId));
2683 BUG_ON(det_ctx->non_pf_id_array == NULL);
2684 }
2685
2686 /* IP-ONLY */
2687 DetectEngineIPOnlyThreadInit(de_ctx,&det_ctx->io_ctx);
2688
2689 /* DeState */
2690 if (de_ctx->sig_array_len > 0) {
2691 det_ctx->match_array_len = de_ctx->sig_array_len;
2692 det_ctx->match_array = SCMalloc(det_ctx->match_array_len * sizeof(Signature *));
2693 if (det_ctx->match_array == NULL) {
2694 return TM_ECODE_FAILED;
2695 }
2696 memset(det_ctx->match_array, 0,
2697 det_ctx->match_array_len * sizeof(Signature *));
2698
2699 RuleMatchCandidateTxArrayInit(det_ctx, de_ctx->sig_array_len);
2700 }
2701
2702 /* byte_extract storage */
2703 det_ctx->byte_values = SCMalloc(sizeof(*det_ctx->byte_values) *
2704 (de_ctx->byte_extract_max_local_id + 1));
2705 if (det_ctx->byte_values == NULL) {
2706 return TM_ECODE_FAILED;
2707 }
2708
2709 /* Allocate space for base64 decoded data. */
2710 if (de_ctx->base64_decode_max_len) {
2711 det_ctx->base64_decoded = SCMalloc(de_ctx->base64_decode_max_len);
2712 if (det_ctx->base64_decoded == NULL) {
2713 return TM_ECODE_FAILED;
2714 }
2715 det_ctx->base64_decoded_len_max = de_ctx->base64_decode_max_len;
2716 det_ctx->base64_decoded_len = 0;
2717 }
2718
2719 det_ctx->inspect.buffers_size = de_ctx->buffer_type_id;
2720 det_ctx->inspect.buffers = SCCalloc(det_ctx->inspect.buffers_size, sizeof(InspectionBuffer));
2721 if (det_ctx->inspect.buffers == NULL) {
2722 return TM_ECODE_FAILED;
2723 }
2724 det_ctx->inspect.to_clear_queue = SCCalloc(det_ctx->inspect.buffers_size, sizeof(uint32_t));
2725 if (det_ctx->inspect.to_clear_queue == NULL) {
2726 return TM_ECODE_FAILED;
2727 }
2728 det_ctx->inspect.to_clear_idx = 0;
2729
2730 det_ctx->multi_inspect.buffers_size = de_ctx->buffer_type_id;
2731 det_ctx->multi_inspect.buffers = SCCalloc(det_ctx->multi_inspect.buffers_size, sizeof(InspectionBufferMultipleForList));
2732 if (det_ctx->multi_inspect.buffers == NULL) {
2733 return TM_ECODE_FAILED;
2734 }
2735 det_ctx->multi_inspect.to_clear_queue = SCCalloc(det_ctx->multi_inspect.buffers_size, sizeof(uint32_t));
2736 if (det_ctx->multi_inspect.to_clear_queue == NULL) {
2737 return TM_ECODE_FAILED;
2738 }
2739 det_ctx->multi_inspect.to_clear_idx = 0;
2740
2741
2742 DetectEngineThreadCtxInitKeywords(de_ctx, det_ctx);
2743 DetectEngineThreadCtxInitGlobalKeywords(det_ctx);
2744 #ifdef PROFILING
2745 SCProfilingRuleThreadSetup(de_ctx->profile_ctx, det_ctx);
2746 SCProfilingKeywordThreadSetup(de_ctx->profile_keyword_ctx, det_ctx);
2747 SCProfilingPrefilterThreadSetup(de_ctx->profile_prefilter_ctx, det_ctx);
2748 SCProfilingSghThreadSetup(de_ctx->profile_sgh_ctx, det_ctx);
2749 #endif
2750 SC_ATOMIC_INIT(det_ctx->so_far_used_by_detect);
2751
2752 return TM_ECODE_OK;
2753 }
2754
2755 /** \brief initialize thread specific detection engine context
2756 *
2757 * \note there is a special case when using delayed detect. In this case the
2758 * function is called twice per thread. The first time the rules are not
2759 * yet loaded. de_ctx->delayed_detect_initialized will be 0. The 2nd
2760 * time they will be loaded. de_ctx->delayed_detect_initialized will be 1.
2761 * This is needed to do the per thread counter registration before the
2762 * packet runtime starts. In delayed detect mode, the first call will
2763 * return a NULL ptr through the data ptr.
2764 *
2765 * \param tv ThreadVars for this thread
2766 * \param initdata pointer to de_ctx
2767 * \param data[out] pointer to store our thread detection ctx
2768 *
2769 * \retval TM_ECODE_OK if all went well
2770 * \retval TM_ECODE_FAILED on serious errors
2771 */
2772 TmEcode DetectEngineThreadCtxInit(ThreadVars *tv, void *initdata, void **data)
2773 {
2774 DetectEngineThreadCtx *det_ctx = SCMalloc(sizeof(DetectEngineThreadCtx));
2775 if (unlikely(det_ctx == NULL))
2776 return TM_ECODE_FAILED;
2777 memset(det_ctx, 0, sizeof(DetectEngineThreadCtx));
2778
2779 det_ctx->tv = tv;
2780 det_ctx->de_ctx = DetectEngineGetCurrent();
2781 if (det_ctx->de_ctx == NULL) {
2782 #ifdef UNITTESTS
2783 if (RunmodeIsUnittests()) {
2784 det_ctx->de_ctx = (DetectEngineCtx *)initdata;
2785 } else {
2786 DetectEngineThreadCtxDeinit(tv, det_ctx);
2787 return TM_ECODE_FAILED;
2788 }
2789 #else
2790 DetectEngineThreadCtxDeinit(tv, det_ctx);
2791 return TM_ECODE_FAILED;
2792 #endif
2793 }
2794
2795 if (det_ctx->de_ctx->type == DETECT_ENGINE_TYPE_NORMAL ||
2796 det_ctx->de_ctx->type == DETECT_ENGINE_TYPE_TENANT)
2797 {
2798 if (ThreadCtxDoInit(det_ctx->de_ctx, det_ctx) != TM_ECODE_OK) {
2799 DetectEngineThreadCtxDeinit(tv, det_ctx);
2800 return TM_ECODE_FAILED;
2801 }
2802 }
2803
2804 /** alert counter setup */
2805 det_ctx->counter_alerts = StatsRegisterCounter("detect.alert", tv);
2806 #ifdef PROFILING
2807 det_ctx->counter_mpm_list = StatsRegisterAvgCounter("detect.mpm_list", tv);
2808 det_ctx->counter_nonmpm_list = StatsRegisterAvgCounter("detect.nonmpm_list", tv);
2809 det_ctx->counter_fnonmpm_list = StatsRegisterAvgCounter("detect.fnonmpm_list", tv);
2810 det_ctx->counter_match_list = StatsRegisterAvgCounter("detect.match_list", tv);
2811 #endif
2812
2813 if (DetectEngineMultiTenantEnabled()) {
2814 if (DetectEngineThreadCtxInitForMT(tv, det_ctx) != TM_ECODE_OK) {
2815 DetectEngineThreadCtxDeinit(tv, det_ctx);
2816 return TM_ECODE_FAILED;
2817 }
2818 }
2819
2820 /* pass thread data back to caller */
2821 *data = (void *)det_ctx;
2822
2823 return TM_ECODE_OK;
2824 }
2825
2826 /**
2827 * \internal
2828 * \brief initialize a det_ctx for reload cases
2829 * \param new_de_ctx the new detection engine
2830 * \param mt flag to indicate if MT should be set up for this det_ctx
2831 * this should only be done for the 'root' det_ctx
2832 *
2833 * \retval det_ctx detection engine thread ctx or NULL in case of error
2834 */
2835 DetectEngineThreadCtx *DetectEngineThreadCtxInitForReload(
2836 ThreadVars *tv, DetectEngineCtx *new_de_ctx, int mt)
2837 {
2838 DetectEngineThreadCtx *det_ctx = SCMalloc(sizeof(DetectEngineThreadCtx));
2839 if (unlikely(det_ctx == NULL))
2840 return NULL;
2841 memset(det_ctx, 0, sizeof(DetectEngineThreadCtx));
2842
2843 det_ctx->tenant_id = new_de_ctx->tenant_id;
2844 det_ctx->tv = tv;
2845 det_ctx->de_ctx = DetectEngineReference(new_de_ctx);
2846 if (det_ctx->de_ctx == NULL) {
2847 SCFree(det_ctx);
2848 return NULL;
2849 }
2850
2851 /* most of the init happens here */
2852 if (det_ctx->de_ctx->type == DETECT_ENGINE_TYPE_NORMAL ||
2853 det_ctx->de_ctx->type == DETECT_ENGINE_TYPE_TENANT)
2854 {
2855 if (ThreadCtxDoInit(det_ctx->de_ctx, det_ctx) != TM_ECODE_OK) {
2856 DetectEngineDeReference(&det_ctx->de_ctx);
2857 SCFree(det_ctx);
2858 return NULL;
2859 }
2860 }
2861
2862 /** alert counter setup */
2863 det_ctx->counter_alerts = StatsRegisterCounter("detect.alert", tv);
2864 #ifdef PROFILING
2865 uint16_t counter_mpm_list = StatsRegisterAvgCounter("detect.mpm_list", tv);
2866 uint16_t counter_nonmpm_list = StatsRegisterAvgCounter("detect.nonmpm_list", tv);
2867 uint16_t counter_fnonmpm_list = StatsRegisterAvgCounter("detect.fnonmpm_list", tv);
2868 uint16_t counter_match_list = StatsRegisterAvgCounter("detect.match_list", tv);
2869 det_ctx->counter_mpm_list = counter_mpm_list;
2870 det_ctx->counter_nonmpm_list = counter_nonmpm_list;
2871 det_ctx->counter_fnonmpm_list = counter_fnonmpm_list;
2872 det_ctx->counter_match_list = counter_match_list;
2873 #endif
2874
2875 if (mt && DetectEngineMultiTenantEnabled()) {
2876 if (DetectEngineThreadCtxInitForMT(tv, det_ctx) != TM_ECODE_OK) {
2877 DetectEngineDeReference(&det_ctx->de_ctx);
2878 SCFree(det_ctx);
2879 return NULL;
2880 }
2881 }
2882
2883 return det_ctx;
2884 }
2885
2886 static void DetectEngineThreadCtxFree(DetectEngineThreadCtx *det_ctx)
2887 {
2888 #if DEBUG
2889 SCLogDebug("PACKET PKT_STREAM_ADD: %"PRIu64, det_ctx->pkt_stream_add_cnt);
2890
2891 SCLogDebug("PAYLOAD MPM %"PRIu64"/%"PRIu64, det_ctx->payload_mpm_cnt, det_ctx->payload_mpm_size);
2892 SCLogDebug("STREAM MPM %"PRIu64"/%"PRIu64, det_ctx->stream_mpm_cnt, det_ctx->stream_mpm_size);
2893
2894 SCLogDebug("PAYLOAD SIG %"PRIu64"/%"PRIu64, det_ctx->payload_persig_cnt, det_ctx->payload_persig_size);
2895 SCLogDebug("STREAM SIG %"PRIu64"/%"PRIu64, det_ctx->stream_persig_cnt, det_ctx->stream_persig_size);
2896 #endif
2897
2898 if (det_ctx->tenant_array != NULL) {
2899 SCFree(det_ctx->tenant_array);
2900 det_ctx->tenant_array = NULL;
2901 }
2902
2903 #ifdef PROFILING
2904 SCProfilingRuleThreadCleanup(det_ctx);
2905 SCProfilingKeywordThreadCleanup(det_ctx);
2906 SCProfilingPrefilterThreadCleanup(det_ctx);
2907 SCProfilingSghThreadCleanup(det_ctx);
2908 #endif
2909
2910 DetectEngineIPOnlyThreadDeinit(&det_ctx->io_ctx);
2911
2912 /** \todo get rid of this static */
2913 if (det_ctx->de_ctx != NULL) {
2914 PatternMatchThreadDestroy(&det_ctx->mtc, det_ctx->de_ctx->mpm_matcher);
2915 PatternMatchThreadDestroy(&det_ctx->mtcs, det_ctx->de_ctx->mpm_matcher);
2916 PatternMatchThreadDestroy(&det_ctx->mtcu, det_ctx->de_ctx->mpm_matcher);
2917 }
2918
2919 PmqFree(&det_ctx->pmq);
2920
2921 if (det_ctx->spm_thread_ctx != NULL) {
2922 SpmDestroyThreadCtx(det_ctx->spm_thread_ctx);
2923 }
2924
2925 if (det_ctx->non_pf_id_array != NULL)
2926 SCFree(det_ctx->non_pf_id_array);
2927
2928 if (det_ctx->match_array != NULL)
2929 SCFree(det_ctx->match_array);
2930
2931 RuleMatchCandidateTxArrayFree(det_ctx);
2932
2933 if (det_ctx->byte_values != NULL)
2934 SCFree(det_ctx->byte_values);
2935
2936 /* Decoded base64 data. */
2937 if (det_ctx->base64_decoded != NULL) {
2938 SCFree(det_ctx->base64_decoded);
2939 }
2940
2941 if (det_ctx->inspect.buffers) {
2942 for (uint32_t i = 0; i < det_ctx->inspect.buffers_size; i++) {
2943 InspectionBufferFree(&det_ctx->inspect.buffers[i]);
2944 }
2945 SCFree(det_ctx->inspect.buffers);
2946 }
2947 if (det_ctx->inspect.to_clear_queue) {
2948 SCFree(det_ctx->inspect.to_clear_queue);
2949 }
2950 if (det_ctx->multi_inspect.buffers) {
2951 for (uint32_t i = 0; i < det_ctx->multi_inspect.buffers_size; i++) {
2952 InspectionBufferMultipleForList *fb = &det_ctx->multi_inspect.buffers[i];
2953 for (uint32_t x = 0; x < fb->size; x++) {
2954 InspectionBufferFree(&fb->inspection_buffers[x]);
2955 }
2956 SCFree(fb->inspection_buffers);
2957 }
2958 SCFree(det_ctx->multi_inspect.buffers);
2959 }
2960 if (det_ctx->multi_inspect.to_clear_queue) {
2961 SCFree(det_ctx->multi_inspect.to_clear_queue);
2962 }
2963
2964 DetectEngineThreadCtxDeinitGlobalKeywords(det_ctx);
2965 if (det_ctx->de_ctx != NULL) {
2966 DetectEngineThreadCtxDeinitKeywords(det_ctx->de_ctx, det_ctx);
2967 #ifdef UNITTESTS
2968 if (!RunmodeIsUnittests() || det_ctx->de_ctx->ref_cnt > 0)
2969 DetectEngineDeReference(&det_ctx->de_ctx);
2970 #else
2971 DetectEngineDeReference(&det_ctx->de_ctx);
2972 #endif
2973 }
2974
2975 AppLayerDecoderEventsFreeEvents(&det_ctx->decoder_events);
2976
2977 SCFree(det_ctx);
2978 }
2979
2980 TmEcode DetectEngineThreadCtxDeinit(ThreadVars *tv, void *data)
2981 {
2982 DetectEngineThreadCtx *det_ctx = (DetectEngineThreadCtx *)data;
2983
2984 if (det_ctx == NULL) {
2985 SCLogWarning(SC_ERR_INVALID_ARGUMENTS, "argument \"data\" NULL");
2986 return TM_ECODE_OK;
2987 }
2988
2989 if (det_ctx->mt_det_ctxs_hash != NULL) {
2990 HashTableFree(det_ctx->mt_det_ctxs_hash);
2991 det_ctx->mt_det_ctxs_hash = NULL;
2992 }
2993 DetectEngineThreadCtxFree(det_ctx);
2994
2995 return TM_ECODE_OK;
2996 }
2997
2998 void DetectEngineThreadCtxInfo(ThreadVars *t, DetectEngineThreadCtx *det_ctx)
2999 {
3000 /* XXX */
3001 PatternMatchThreadPrint(&det_ctx->mtc, det_ctx->de_ctx->mpm_matcher);
3002 PatternMatchThreadPrint(&det_ctx->mtcu, det_ctx->de_ctx->mpm_matcher);
3003 }
3004
3005 /** \brief Register Thread keyword context Funcs
3006 *
3007 * \param de_ctx detection engine to register in
3008 * \param name keyword name for error printing
3009 * \param InitFunc function ptr
3010 * \param data keyword init data to pass to Func. Can be NULL.
3011 * \param FreeFunc function ptr
3012 * \param mode 0 normal (ctx per keyword instance) 1 shared (one ctx per det_ct)
3013 *
3014 * \retval id for retrieval of ctx at runtime
3015 * \retval -1 on error
3016 *
3017 * \note make sure "data" remains valid and it free'd elsewhere. It's
3018 * recommended to store it in the keywords global ctx so that
3019 * it's freed when the de_ctx is freed.
3020 */
3021 int DetectRegisterThreadCtxFuncs(DetectEngineCtx *de_ctx, const char *name, void *(*InitFunc)(void *), void *data, void (*FreeFunc)(void *), int mode)
3022 {
3023 BUG_ON(de_ctx == NULL || InitFunc == NULL || FreeFunc == NULL);
3024
3025 if (mode) {
3026 DetectEngineThreadKeywordCtxItem *item = de_ctx->keyword_list;
3027 while (item != NULL) {
3028 if (strcmp(name, item->name) == 0) {
3029 return item->id;
3030 }
3031
3032 item = item->next;
3033 }
3034 }
3035
3036 DetectEngineThreadKeywordCtxItem *item = SCMalloc(sizeof(DetectEngineThreadKeywordCtxItem));
3037 if (unlikely(item == NULL))
3038 return -1;
3039 memset(item, 0x00, sizeof(DetectEngineThreadKeywordCtxItem));
3040
3041 item->InitFunc = InitFunc;
3042 item->FreeFunc = FreeFunc;
3043 item->data = data;
3044 item->name = name;
3045
3046 item->next = de_ctx->keyword_list;
3047 de_ctx->keyword_list = item;
3048 item->id = de_ctx->keyword_id++;
3049
3050 return item->id;
3051 }
3052
3053 /** \brief Remove Thread keyword context registration
3054 *
3055 * \param de_ctx detection engine to deregister from
3056 * \param det_ctx detection engine thread context to deregister from
3057 * \param data keyword init data to pass to Func. Can be NULL.
3058 * \param name keyword name for error printing
3059 *
3060 * \retval 1 Item unregistered
3061 * \retval 0 otherwise
3062 *
3063 * \note make sure "data" remains valid and it free'd elsewhere. It's
3064 * recommended to store it in the keywords global ctx so that
3065 * it's freed when the de_ctx is freed.
3066 */
3067 int DetectUnregisterThreadCtxFuncs(DetectEngineCtx *de_ctx,
3068 DetectEngineThreadCtx *det_ctx, void *data, const char *name)
3069 {
3070 BUG_ON(de_ctx == NULL);
3071
3072 DetectEngineThreadKeywordCtxItem *item = de_ctx->keyword_list;
3073 DetectEngineThreadKeywordCtxItem *prev_item = NULL;
3074 while (item != NULL) {
3075 if (strcmp(name, item->name) == 0 && (data == item->data)) {
3076 if (prev_item == NULL)
3077 de_ctx->keyword_list = item->next;
3078 else
3079 prev_item->next = item->next;
3080 if (det_ctx)
3081 item->FreeFunc(det_ctx->keyword_ctxs_array[item->id]);
3082 SCFree(item);
3083 return 1;
3084 }
3085 prev_item = item;
3086 item = item->next;
3087 }
3088 return 0;
3089 }
3090 /** \brief Retrieve thread local keyword ctx by id
3091 *
3092 * \param det_ctx detection engine thread ctx to retrieve the ctx from
3093 * \param id id of the ctx returned by DetectRegisterThreadCtxInitFunc at
3094 * keyword init.
3095 *
3096 * \retval ctx or NULL on error
3097 */
3098 void *DetectThreadCtxGetKeywordThreadCtx(DetectEngineThreadCtx *det_ctx, int id)
3099 {
3100 if (id < 0 || id > det_ctx->keyword_ctxs_size || det_ctx->keyword_ctxs_array == NULL)
3101 return NULL;
3102
3103 return det_ctx->keyword_ctxs_array[id];
3104 }
3105
3106
3107 /** \brief Register Thread keyword context Funcs (Global)
3108 *
3109 * IDs stay static over reloads and between tenants
3110 *
3111 * \param name keyword name for error printing
3112 * \param InitFunc function ptr
3113 * \param FreeFunc function ptr
3114 *
3115 * \retval id for retrieval of ctx at runtime
3116 * \retval -1 on error
3117 */
3118 int DetectRegisterThreadCtxGlobalFuncs(const char *name,
3119 void *(*InitFunc)(void *), void *data, void (*FreeFunc)(void *))
3120 {
3121 int id;
3122 BUG_ON(InitFunc == NULL || FreeFunc == NULL);
3123
3124 DetectEngineMasterCtx *master = &g_master_de_ctx;
3125
3126 /* if already registered, return existing id */
3127 DetectEngineThreadKeywordCtxItem *item = master->keyword_list;
3128 while (item != NULL) {
3129 if (strcmp(name, item->name) == 0) {
3130 id = item->id;
3131 return id;
3132 }
3133
3134 item = item->next;
3135 }
3136
3137 item = SCCalloc(1, sizeof(*item));
3138 if (unlikely(item == NULL)) {
3139 return -1;
3140 }
3141 item->InitFunc = InitFunc;
3142 item->FreeFunc = FreeFunc;
3143 item->name = name;
3144 item->data = data;
3145
3146 item->next = master->keyword_list;
3147 master->keyword_list = item;
3148 item->id = master->keyword_id++;
3149
3150 id = item->id;
3151 return id;
3152 }
3153
3154 /** \brief Retrieve thread local keyword ctx by id
3155 *
3156 * \param det_ctx detection engine thread ctx to retrieve the ctx from
3157 * \param id id of the ctx returned by DetectRegisterThreadCtxInitFunc at
3158 * keyword init.
3159 *
3160 * \retval ctx or NULL on error
3161 */
3162 void *DetectThreadCtxGetGlobalKeywordThreadCtx(DetectEngineThreadCtx *det_ctx, int id)
3163 {
3164 if (id < 0 || id > det_ctx->global_keyword_ctxs_size ||
3165 det_ctx->global_keyword_ctxs_array == NULL) {
3166 return NULL;
3167 }
3168
3169 return det_ctx->global_keyword_ctxs_array[id];
3170 }
3171
3172 /** \brief Check if detection is enabled
3173 * \retval bool true or false */
3174 int DetectEngineEnabled(void)
3175 {
3176 DetectEngineMasterCtx *master = &g_master_de_ctx;
3177 SCMutexLock(&master->lock);
3178
3179 if (master->list == NULL) {
3180 SCMutexUnlock(&master->lock);
3181 return 0;
3182 }
3183
3184 SCMutexUnlock(&master->lock);
3185 return 1;
3186 }
3187
3188 uint32_t DetectEngineGetVersion(void)
3189 {
3190 uint32_t version;
3191 DetectEngineMasterCtx *master = &g_master_de_ctx;
3192 SCMutexLock(&master->lock);
3193 version = master->version;
3194 SCMutexUnlock(&master->lock);
3195 return version;
3196 }
3197
3198 void DetectEngineBumpVersion(void)
3199 {
3200 DetectEngineMasterCtx *master = &g_master_de_ctx;
3201 SCMutexLock(&master->lock);
3202 master->version++;
3203 SCLogDebug("master version now %u", master->version);
3204 SCMutexUnlock(&master->lock);
3205 }
3206
3207 DetectEngineCtx *DetectEngineGetCurrent(void)
3208 {
3209 DetectEngineMasterCtx *master = &g_master_de_ctx;
3210 SCMutexLock(&master->lock);
3211
3212 DetectEngineCtx *de_ctx = master->list;
3213 while (de_ctx) {
3214 if (de_ctx->type == DETECT_ENGINE_TYPE_NORMAL ||
3215 de_ctx->type == DETECT_ENGINE_TYPE_DD_STUB ||
3216 de_ctx->type == DETECT_ENGINE_TYPE_MT_STUB)
3217 {
3218 de_ctx->ref_cnt++;
3219 SCLogDebug("de_ctx %p ref_cnt %u", de_ctx, de_ctx->ref_cnt);
3220 SCMutexUnlock(&master->lock);
3221 return de_ctx;
3222 }
3223 de_ctx = de_ctx->next;
3224 }
3225
3226 SCMutexUnlock(&master->lock);
3227 return NULL;
3228 }
3229
3230 DetectEngineCtx *DetectEngineReference(DetectEngineCtx *de_ctx)
3231 {
3232 if (de_ctx == NULL)
3233 return NULL;
3234 de_ctx->ref_cnt++;
3235 return de_ctx;
3236 }
3237
3238 /** TODO locking? Not needed if this is a one time setting at startup */
3239 int DetectEngineMultiTenantEnabled(void)
3240 {
3241 DetectEngineMasterCtx *master = &g_master_de_ctx;
3242 return (master->multi_tenant_enabled);
3243 }
3244
3245 /** \internal
3246 * \brief load a tenant from a yaml file
3247 *
3248 * \param tenant_id the tenant id by which the config is known
3249 * \param filename full path of a yaml file
3250 * \param loader_id id of loader thread or -1
3251 *
3252 * \retval 0 ok
3253 * \retval -1 failed
3254 */
3255 static int DetectEngineMultiTenantLoadTenant(uint32_t tenant_id, const char *filename, int loader_id)
3256 {
3257 DetectEngineCtx *de_ctx = NULL;
3258 char prefix[64];
3259
3260 snprintf(prefix, sizeof(prefix), "multi-detect.%d", tenant_id);
3261
3262 #ifdef OS_WIN32
3263 struct _stat st;
3264 if(_stat(filename, &st) != 0) {
3265 #else
3266 struct stat st;
3267 if(stat(filename, &st) != 0) {
3268 #endif /* OS_WIN32 */
3269 SCLogError(SC_ERR_FOPEN, "failed to stat file %s", filename);
3270 goto error;
3271 }
3272
3273 de_ctx = DetectEngineGetByTenantId(tenant_id);
3274 if (de_ctx != NULL) {
3275 SCLogError(SC_ERR_MT_DUPLICATE_TENANT, "tenant %u already registered",
3276 tenant_id);
3277 DetectEngineDeReference(&de_ctx);
3278 goto error;
3279 }
3280
3281 ConfNode *node = ConfGetNode(prefix);
3282 if (node == NULL) {
3283 SCLogError(SC_ERR_CONF_YAML_ERROR, "failed to properly setup yaml %s", filename);
3284 goto error;
3285 }
3286
3287 de_ctx = DetectEngineCtxInitWithPrefix(prefix);
3288 if (de_ctx == NULL) {
3289 SCLogError(SC_ERR_INITIALIZATION, "initializing detection engine "
3290 "context failed.");
3291 goto error;
3292 }
3293 SCLogDebug("de_ctx %p with prefix %s", de_ctx, de_ctx->config_prefix);
3294
3295 de_ctx->type = DETECT_ENGINE_TYPE_TENANT;
3296 de_ctx->tenant_id = tenant_id;
3297 de_ctx->loader_id = loader_id;
3298
3299 if (SigLoadSignatures(de_ctx, NULL, 0) < 0) {
3300 SCLogError(SC_ERR_NO_RULES_LOADED, "Loading signatures failed.");
3301 goto error;
3302 }
3303
3304 DetectEngineAddToMaster(de_ctx);
3305
3306 return 0;
3307
3308 error:
3309 if (de_ctx != NULL) {
3310 DetectEngineCtxFree(de_ctx);
3311 }
3312 return -1;
3313 }
3314
3315 static int DetectEngineMultiTenantReloadTenant(uint32_t tenant_id, const char *filename, int reload_cnt)
3316 {
3317 DetectEngineCtx *old_de_ctx = DetectEngineGetByTenantId(tenant_id);
3318 if (old_de_ctx == NULL) {
3319 SCLogError(SC_ERR_INITIALIZATION, "tenant detect engine not found");
3320 return -1;
3321 }
3322
3323 char prefix[64];
3324 snprintf(prefix, sizeof(prefix), "multi-detect.%d.reload.%d", tenant_id, reload_cnt);
3325 reload_cnt++;
3326 SCLogDebug("prefix %s", prefix);
3327
3328 if (ConfYamlLoadFileWithPrefix(filename, prefix) != 0) {
3329 SCLogError(SC_ERR_INITIALIZATION,"failed to load yaml");
3330 goto error;
3331 }
3332
3333 ConfNode *node = ConfGetNode(prefix);
3334 if (node == NULL) {
3335 SCLogError(SC_ERR_CONF_YAML_ERROR, "failed to properly setup yaml %s", filename);
3336 goto error;
3337 }
3338
3339 DetectEngineCtx *new_de_ctx = DetectEngineCtxInitWithPrefix(prefix);
3340 if (new_de_ctx == NULL) {
3341 SCLogError(SC_ERR_INITIALIZATION, "initializing detection engine "
3342 "context failed.");
3343 goto error;
3344 }
3345 SCLogDebug("de_ctx %p with prefix %s", new_de_ctx, new_de_ctx->config_prefix);
3346
3347 new_de_ctx->type = DETECT_ENGINE_TYPE_TENANT;
3348 new_de_ctx->tenant_id = tenant_id;
3349 new_de_ctx->loader_id = old_de_ctx->loader_id;
3350
3351 if (SigLoadSignatures(new_de_ctx, NULL, 0) < 0) {
3352 SCLogError(SC_ERR_NO_RULES_LOADED, "Loading signatures failed.");
3353 goto error;
3354 }
3355
3356 DetectEngineAddToMaster(new_de_ctx);
3357
3358 /* move to free list */
3359 DetectEngineMoveToFreeList(old_de_ctx);
3360 DetectEngineDeReference(&old_de_ctx);
3361 return 0;
3362
3363 error:
3364 DetectEngineDeReference(&old_de_ctx);
3365 return -1;
3366 }
3367
3368
3369 typedef struct TenantLoaderCtx_ {
3370 uint32_t tenant_id;
3371 int reload_cnt; /**< used by reload */
3372 const char *yaml;
3373 } TenantLoaderCtx;
3374
3375 static int DetectLoaderFuncLoadTenant(void *vctx, int loader_id)
3376 {
3377 TenantLoaderCtx *ctx = (TenantLoaderCtx *)vctx;
3378
3379 SCLogDebug("loader %d", loader_id);
3380 if (DetectEngineMultiTenantLoadTenant(ctx->tenant_id, ctx->yaml, loader_id) != 0) {
3381 return -1;
3382 }
3383 return 0;
3384 }
3385
3386 static int DetectLoaderSetupLoadTenant(uint32_t tenant_id, const char *yaml)
3387 {
3388 TenantLoaderCtx *t = SCCalloc(1, sizeof(*t));
3389 if (t == NULL)
3390 return -ENOMEM;
3391
3392 t->tenant_id = tenant_id;
3393 t->yaml = yaml;
3394
3395 return DetectLoaderQueueTask(-1, DetectLoaderFuncLoadTenant, t);
3396 }
3397
3398 static int DetectLoaderFuncReloadTenant(void *vctx, int loader_id)
3399 {
3400 TenantLoaderCtx *ctx = (TenantLoaderCtx *)vctx;
3401
3402 SCLogDebug("loader_id %d", loader_id);
3403
3404 if (DetectEngineMultiTenantReloadTenant(ctx->tenant_id, ctx->yaml, ctx->reload_cnt) != 0) {
3405 return -1;
3406 }
3407 return 0;
3408 }
3409
3410 static int DetectLoaderSetupReloadTenant(uint32_t tenant_id, const char *yaml, int reload_cnt)
3411 {
3412 DetectEngineCtx *old_de_ctx = DetectEngineGetByTenantId(tenant_id);
3413 if (old_de_ctx == NULL)
3414 return -ENOENT;
3415 int loader_id = old_de_ctx->loader_id;
3416 DetectEngineDeReference(&old_de_ctx);
3417
3418 TenantLoaderCtx *t = SCCalloc(1, sizeof(*t));
3419 if (t == NULL)
3420 return -ENOMEM;
3421
3422 t->tenant_id = tenant_id;
3423 t->yaml = yaml;
3424 t->reload_cnt = reload_cnt;
3425
3426 SCLogDebug("loader_id %d", loader_id);
3427
3428 return DetectLoaderQueueTask(loader_id, DetectLoaderFuncReloadTenant, t);
3429 }
3430
3431 /** \brief Load a tenant and wait for loading to complete
3432 */
3433 int DetectEngineLoadTenantBlocking(uint32_t tenant_id, const char *yaml)
3434 {
3435 int r = DetectLoaderSetupLoadTenant(tenant_id, yaml);
3436 if (r < 0)
3437 return r;
3438
3439 if (DetectLoadersSync() != 0)
3440 return -1;
3441
3442 return 0;
3443 }
3444
3445 /** \brief Reload a tenant and wait for loading to complete
3446 */
3447 int DetectEngineReloadTenantBlocking(uint32_t tenant_id, const char *yaml, int reload_cnt)
3448 {
3449 int r = DetectLoaderSetupReloadTenant(tenant_id, yaml, reload_cnt);
3450 if (r < 0)
3451 return r;
3452
3453 if (DetectLoadersSync() != 0)
3454 return -1;
3455
3456 return 0;
3457 }
3458
3459 static int DetectEngineMultiTenantSetupLoadLivedevMappings(const ConfNode *mappings_root_node,
3460 bool failure_fatal)
3461 {
3462 ConfNode *mapping_node = NULL;
3463
3464 int mapping_cnt = 0;
3465 if (mappings_root_node != NULL) {
3466 TAILQ_FOREACH(mapping_node, &mappings_root_node->head, next) {
3467 ConfNode *tenant_id_node = ConfNodeLookupChild(mapping_node, "tenant-id");
3468 if (tenant_id_node == NULL)
3469 goto bad_mapping;
3470 ConfNode *device_node = ConfNodeLookupChild(mapping_node, "device");
3471 if (device_node == NULL)
3472 goto bad_mapping;
3473
3474 uint32_t tenant_id = 0;
3475 if (StringParseUint32(&tenant_id, 10, strlen(tenant_id_node->val),
3476 tenant_id_node->val) < 0)
3477 {
3478 SCLogError(SC_ERR_INVALID_ARGUMENT, "tenant-id "
3479 "of %s is invalid", tenant_id_node->val);
3480 goto bad_mapping;
3481 }
3482
3483 const char *dev = device_node->val;
3484 LiveDevice *ld = LiveGetDevice(dev);
3485 if (ld == NULL) {
3486 SCLogWarning(SC_ERR_MT_NO_MAPPING, "device %s not found", dev);
3487 goto bad_mapping;
3488 }
3489
3490 if (ld->tenant_id_set) {
3491 SCLogWarning(SC_ERR_MT_NO_MAPPING, "device %s already mapped to tenant-id %u",
3492 dev, ld->tenant_id);
3493 goto bad_mapping;
3494 }
3495
3496 ld->tenant_id = tenant_id;
3497 ld->tenant_id_set = true;
3498
3499 if (DetectEngineTentantRegisterLivedev(tenant_id, ld->id) != 0) {
3500 goto error;
3501 }
3502
3503 SCLogConfig("device %s connected to tenant-id %u", dev, tenant_id);
3504 mapping_cnt++;
3505 continue;
3506
3507 bad_mapping:
3508 if (failure_fatal)
3509 goto error;
3510 }
3511 }
3512 SCLogConfig("%d device - tenant-id mappings defined", mapping_cnt);
3513 return mapping_cnt;
3514
3515 error:
3516 return 0;
3517 }
3518
3519 static int DetectEngineMultiTenantSetupLoadVlanMappings(const ConfNode *mappings_root_node,
3520 bool failure_fatal)
3521 {
3522 ConfNode *mapping_node = NULL;
3523
3524 int mapping_cnt = 0;
3525 if (mappings_root_node != NULL) {
3526 TAILQ_FOREACH(mapping_node, &mappings_root_node->head, next) {
3527 ConfNode *tenant_id_node = ConfNodeLookupChild(mapping_node, "tenant-id");
3528 if (tenant_id_node == NULL)
3529 goto bad_mapping;
3530 ConfNode *vlan_id_node = ConfNodeLookupChild(mapping_node, "vlan-id");
3531 if (vlan_id_node == NULL)
3532 goto bad_mapping;
3533
3534 uint32_t tenant_id = 0;
3535 if (StringParseUint32(&tenant_id, 10, strlen(tenant_id_node->val),
3536 tenant_id_node->val) < 0)
3537 {
3538 SCLogError(SC_ERR_INVALID_ARGUMENT, "tenant-id "
3539 "of %s is invalid", tenant_id_node->val);
3540 goto bad_mapping;
3541 }
3542
3543 uint16_t vlan_id = 0;
3544 if (StringParseUint16(&vlan_id, 10, strlen(vlan_id_node->val),
3545 vlan_id_node->val) < 0)
3546 {
3547 SCLogError(SC_ERR_INVALID_ARGUMENT, "vlan-id "
3548 "of %s is invalid", vlan_id_node->val);
3549 goto bad_mapping;
3550 }
3551 if (vlan_id == 0 || vlan_id >= 4095) {
3552 SCLogError(SC_ERR_INVALID_ARGUMENT, "vlan-id "
3553 "of %s is invalid. Valid range 1-4094.", vlan_id_node->val);
3554 goto bad_mapping;
3555 }
3556
3557 if (DetectEngineTentantRegisterVlanId(tenant_id, (uint32_t)vlan_id) != 0) {
3558 goto error;
3559 }
3560 SCLogConfig("vlan %u connected to tenant-id %u", vlan_id, tenant_id);
3561 mapping_cnt++;
3562 continue;
3563
3564 bad_mapping:
3565 if (failure_fatal)
3566 goto error;
3567 }
3568 }
3569 return mapping_cnt;
3570
3571 error:
3572 return 0;
3573 }
3574
3575 /**
3576 * \brief setup multi-detect / multi-tenancy
3577 *
3578 * See if MT is enabled. If so, setup the selector, tenants and mappings.
3579 * Tenants and mappings are optional, and can also dynamically be added
3580 * and removed from the unix socket.
3581 */
3582 int DetectEngineMultiTenantSetup(void)
3583 {
3584 enum DetectEngineTenantSelectors tenant_selector = TENANT_SELECTOR_UNKNOWN;
3585 DetectEngineMasterCtx *master = &g_master_de_ctx;
3586
3587 int unix_socket = ConfUnixSocketIsEnable();
3588
3589 int failure_fatal = 0;
3590 (void)ConfGetBool("engine.init-failure-fatal", &failure_fatal);
3591
3592 int enabled = 0;
3593 (void)ConfGetBool("multi-detect.enabled", &enabled);
3594 if (enabled == 1) {
3595 DetectLoadersInit();
3596 TmModuleDetectLoaderRegister();
3597 DetectLoaderThreadSpawn();
3598 TmThreadContinueDetectLoaderThreads();
3599
3600 SCMutexLock(&master->lock);
3601 master->multi_tenant_enabled = 1;
3602
3603 const char *handler = NULL;
3604 if (ConfGet("multi-detect.selector", &handler) == 1) {
3605 SCLogConfig("multi-tenant selector type %s", handler);
3606
3607 if (strcmp(handler, "vlan") == 0) {
3608 tenant_selector = master->tenant_selector = TENANT_SELECTOR_VLAN;
3609
3610 int vlanbool = 0;
3611 if ((ConfGetBool("vlan.use-for-tracking", &vlanbool)) == 1 && vlanbool == 0) {
3612 SCLogError(SC_ERR_INVALID_VALUE, "vlan tracking is disabled, "
3613 "can't use multi-detect selector 'vlan'");
3614 SCMutexUnlock(&master->lock);
3615 goto error;
3616 }
3617
3618 } else if (strcmp(handler, "direct") == 0) {
3619 tenant_selector = master->tenant_selector = TENANT_SELECTOR_DIRECT;
3620 } else if (strcmp(handler, "device") == 0) {
3621 tenant_selector = master->tenant_selector = TENANT_SELECTOR_LIVEDEV;
3622 if (EngineModeIsIPS()) {
3623 SCLogWarning(SC_ERR_MT_NO_MAPPING,
3624 "multi-tenant 'device' mode not supported for IPS");
3625 SCMutexUnlock(&master->lock);
3626 goto error;
3627 }
3628
3629 } else {
3630 SCLogError(SC_ERR_INVALID_VALUE, "unknown value %s "
3631 "multi-detect.selector", handler);
3632 SCMutexUnlock(&master->lock);
3633 goto error;
3634 }
3635 }
3636 SCMutexUnlock(&master->lock);
3637 SCLogConfig("multi-detect is enabled (multi tenancy). Selector: %s", handler);
3638
3639 /* traffic -- tenant mappings */
3640 ConfNode *mappings_root_node = ConfGetNode("multi-detect.mappings");
3641
3642 if (tenant_selector == TENANT_SELECTOR_VLAN) {
3643 int mapping_cnt = DetectEngineMultiTenantSetupLoadVlanMappings(mappings_root_node,
3644 failure_fatal);
3645 if (mapping_cnt == 0) {
3646 /* no mappings are valid when we're in unix socket mode,
3647 * they can be added on the fly. Otherwise warn/error
3648 * depending on failure_fatal */
3649
3650 if (unix_socket) {
3651 SCLogNotice("no tenant traffic mappings defined, "
3652 "tenants won't be used until mappings are added");
3653 } else {
3654 if (failure_fatal) {
3655 SCLogError(SC_ERR_MT_NO_MAPPING, "no multi-detect mappings defined");
3656 goto error;
3657 } else {
3658 SCLogWarning(SC_ERR_MT_NO_MAPPING, "no multi-detect mappings defined");
3659 }
3660 }
3661 }
3662 } else if (tenant_selector == TENANT_SELECTOR_LIVEDEV) {
3663 int mapping_cnt = DetectEngineMultiTenantSetupLoadLivedevMappings(mappings_root_node,
3664 failure_fatal);
3665 if (mapping_cnt == 0) {
3666 if (failure_fatal) {
3667 SCLogError(SC_ERR_MT_NO_MAPPING, "no multi-detect mappings defined");
3668 goto error;
3669 } else {
3670 SCLogWarning(SC_ERR_MT_NO_MAPPING, "no multi-detect mappings defined");
3671 }
3672 }
3673 }
3674
3675 /* tenants */
3676 ConfNode *tenants_root_node = ConfGetNode("multi-detect.tenants");
3677 ConfNode *tenant_node = NULL;
3678
3679 if (tenants_root_node != NULL) {
3680 TAILQ_FOREACH(tenant_node, &tenants_root_node->head, next) {
3681 ConfNode *id_node = ConfNodeLookupChild(tenant_node, "id");
3682 if (id_node == NULL) {
3683 goto bad_tenant;
3684 }
3685 ConfNode *yaml_node = ConfNodeLookupChild(tenant_node, "yaml");
3686 if (yaml_node == NULL) {
3687 goto bad_tenant;
3688 }
3689
3690 uint32_t tenant_id = 0;
3691 if (StringParseUint32(&tenant_id, 10, strlen(id_node->val),
3692 id_node->val) < 0)
3693 {
3694 SCLogError(SC_ERR_INVALID_ARGUMENT, "tenant_id "
3695 "of %s is invalid", id_node->val);
3696 goto bad_tenant;
3697 }
3698 SCLogDebug("tenant id: %u, %s", tenant_id, yaml_node->val);
3699
3700 /* setup the yaml in this loop so that it's not done by the loader
3701 * threads. ConfYamlLoadFileWithPrefix is not thread safe. */
3702 char prefix[64];
3703 snprintf(prefix, sizeof(prefix), "multi-detect.%d", tenant_id);
3704 if (ConfYamlLoadFileWithPrefix(yaml_node->val, prefix) != 0) {
3705 SCLogError(SC_ERR_CONF_YAML_ERROR, "failed to load yaml %s", yaml_node->val);
3706 goto bad_tenant;
3707 }
3708
3709 int r = DetectLoaderSetupLoadTenant(tenant_id, yaml_node->val);
3710 if (r < 0) {
3711 /* error logged already */
3712 goto bad_tenant;
3713 }
3714 continue;
3715
3716 bad_tenant:
3717 if (failure_fatal)
3718 goto error;
3719 }
3720 }
3721
3722 /* wait for our loaders to complete their tasks */
3723 if (DetectLoadersSync() != 0) {
3724 goto error;
3725 }
3726
3727 VarNameStoreActivateStaging();
3728
3729 } else {
3730 SCLogDebug("multi-detect not enabled (multi tenancy)");
3731 }
3732 return 0;
3733 error:
3734 return -1;
3735 }
3736
3737 static uint32_t DetectEngineTentantGetIdFromVlanId(const void *ctx, const Packet *p)
3738 {
3739 const DetectEngineThreadCtx *det_ctx = ctx;
3740 uint32_t x = 0;
3741 uint32_t vlan_id = 0;
3742
3743 if (p->vlan_idx == 0)
3744 return 0;
3745
3746 vlan_id = p->vlan_id[0];
3747
3748 if (det_ctx == NULL || det_ctx->tenant_array == NULL || det_ctx->tenant_array_size == 0)
3749 return 0;
3750
3751 /* not very efficient, but for now we're targeting only limited amounts.
3752 * Can use hash/tree approach later. */
3753 for (x = 0; x < det_ctx->tenant_array_size; x++) {
3754 if (det_ctx->tenant_array[x].traffic_id == vlan_id)
3755 return det_ctx->tenant_array[x].tenant_id;
3756 }
3757
3758 return 0;
3759 }
3760
3761 static uint32_t DetectEngineTentantGetIdFromLivedev(const void *ctx, const Packet *p)
3762 {
3763 const DetectEngineThreadCtx *det_ctx = ctx;
3764 const LiveDevice *ld = p->livedev;
3765
3766 if (ld == NULL || det_ctx == NULL)
3767 return 0;
3768
3769 SCLogDebug("using tenant-id %u for packet on device %s", ld->tenant_id, ld->dev);
3770 return ld->tenant_id;
3771 }
3772
3773 static int DetectEngineTentantRegisterSelector(enum DetectEngineTenantSelectors selector,
3774 uint32_t tenant_id, uint32_t traffic_id)
3775 {
3776 DetectEngineMasterCtx *master = &g_master_de_ctx;
3777 SCMutexLock(&master->lock);
3778
3779 if (!(master->tenant_selector == TENANT_SELECTOR_UNKNOWN || master->tenant_selector == selector)) {
3780 SCLogInfo("conflicting selector already set");
3781 SCMutexUnlock(&master->lock);
3782 return -1;
3783 }
3784
3785 DetectEngineTenantMapping *m = master->tenant_mapping_list;
3786 while (m) {
3787 if (m->traffic_id == traffic_id) {
3788 SCLogInfo("traffic id already registered");
3789 SCMutexUnlock(&master->lock);
3790 return -1;
3791 }
3792 m = m->next;
3793 }
3794
3795 DetectEngineTenantMapping *map = SCCalloc(1, sizeof(*map));
3796 if (map == NULL) {
3797 SCLogInfo("memory fail");
3798 SCMutexUnlock(&master->lock);
3799 return -1;
3800 }
3801 map->traffic_id = traffic_id;
3802 map->tenant_id = tenant_id;
3803
3804 map->next = master->tenant_mapping_list;
3805 master->tenant_mapping_list = map;
3806
3807 master->tenant_selector = selector;
3808
3809 SCLogDebug("tenant handler %u %u %u registered", selector, tenant_id, traffic_id);
3810 SCMutexUnlock(&master->lock);
3811 return 0;
3812 }
3813
3814 static int DetectEngineTentantUnregisterSelector(enum DetectEngineTenantSelectors selector,
3815 uint32_t tenant_id, uint32_t traffic_id)
3816 {
3817 DetectEngineMasterCtx *master = &g_master_de_ctx;
3818 SCMutexLock(&master->lock);
3819
3820 if (master->tenant_mapping_list == NULL) {
3821 SCMutexUnlock(&master->lock);
3822 return -1;
3823 }
3824
3825 DetectEngineTenantMapping *prev = NULL;
3826 DetectEngineTenantMapping *map = master->tenant_mapping_list;
3827 while (map) {
3828 if (map->traffic_id == traffic_id &&
3829 map->tenant_id == tenant_id)
3830 {
3831 if (prev != NULL)
3832 prev->next = map->next;
3833 else
3834 master->tenant_mapping_list = map->next;
3835
3836 map->next = NULL;
3837 SCFree(map);
3838 SCLogInfo("tenant handler %u %u %u unregistered", selector, tenant_id, traffic_id);
3839 SCMutexUnlock(&master->lock);
3840 return 0;
3841 }
3842 prev = map;
3843 map = map->next;
3844 }
3845
3846 SCMutexUnlock(&master->lock);
3847 return -1;
3848 }
3849
3850 int DetectEngineTentantRegisterLivedev(uint32_t tenant_id, int device_id)
3851 {
3852 return DetectEngineTentantRegisterSelector(TENANT_SELECTOR_LIVEDEV, tenant_id, (uint32_t)device_id);
3853 }
3854
3855 int DetectEngineTentantRegisterVlanId(uint32_t tenant_id, uint16_t vlan_id)
3856 {
3857 return DetectEngineTentantRegisterSelector(TENANT_SELECTOR_VLAN, tenant_id, (uint32_t)vlan_id);
3858 }
3859
3860 int DetectEngineTentantUnregisterVlanId(uint32_t tenant_id, uint16_t vlan_id)
3861 {
3862 return DetectEngineTentantUnregisterSelector(TENANT_SELECTOR_VLAN, tenant_id, (uint32_t)vlan_id);
3863 }
3864
3865 int DetectEngineTentantRegisterPcapFile(uint32_t tenant_id)
3866 {
3867 SCLogInfo("registering %u %d 0", TENANT_SELECTOR_DIRECT, tenant_id);
3868 return DetectEngineTentantRegisterSelector(TENANT_SELECTOR_DIRECT, tenant_id, 0);
3869 }
3870
3871 int DetectEngineTentantUnregisterPcapFile(uint32_t tenant_id)
3872 {
3873 SCLogInfo("unregistering %u %d 0", TENANT_SELECTOR_DIRECT, tenant_id);
3874 return DetectEngineTentantUnregisterSelector(TENANT_SELECTOR_DIRECT, tenant_id, 0);
3875 }
3876
3877 static uint32_t DetectEngineTentantGetIdFromPcap(const void *ctx, const Packet *p)
3878 {
3879 return p->pcap_v.tenant_id;
3880 }
3881
3882 DetectEngineCtx *DetectEngineGetByTenantId(int tenant_id)
3883 {
3884 DetectEngineMasterCtx *master = &g_master_de_ctx;
3885 SCMutexLock(&master->lock);
3886
3887 if (master->list == NULL) {
3888 SCMutexUnlock(&master->lock);
3889 return NULL;
3890 }
3891
3892 DetectEngineCtx *de_ctx = master->list;
3893 while (de_ctx) {
3894 if (de_ctx->type == DETECT_ENGINE_TYPE_TENANT &&
3895 de_ctx->tenant_id == tenant_id)
3896 {
3897 de_ctx->ref_cnt++;
3898 break;
3899 }
3900
3901 de_ctx = de_ctx->next;
3902 }
3903
3904 SCMutexUnlock(&master->lock);
3905 return de_ctx;
3906 }
3907
3908 void DetectEngineDeReference(DetectEngineCtx **de_ctx)
3909 {
3910 BUG_ON((*de_ctx)->ref_cnt == 0);
3911 (*de_ctx)->ref_cnt--;
3912 *de_ctx = NULL;
3913 }
3914
3915 static int DetectEngineAddToList(DetectEngineCtx *instance)
3916 {
3917 DetectEngineMasterCtx *master = &g_master_de_ctx;
3918
3919 if (instance == NULL)
3920 return -1;
3921
3922 if (master->list == NULL) {
3923 master->list = instance;
3924 } else {
3925 instance->next = master->list;
3926 master->list = instance;
3927 }
3928
3929 return 0;
3930 }
3931
3932 int DetectEngineAddToMaster(DetectEngineCtx *de_ctx)
3933 {
3934 int r;
3935
3936 if (de_ctx == NULL)
3937 return -1;
3938
3939 SCLogDebug("adding de_ctx %p to master", de_ctx);
3940
3941 DetectEngineMasterCtx *master = &g_master_de_ctx;
3942 SCMutexLock(&master->lock);
3943 r = DetectEngineAddToList(de_ctx);
3944 SCMutexUnlock(&master->lock);
3945 return r;
3946 }
3947
3948 int DetectEngineMoveToFreeList(DetectEngineCtx *de_ctx)
3949 {
3950 DetectEngineMasterCtx *master = &g_master_de_ctx;
3951
3952 SCMutexLock(&master->lock);
3953 DetectEngineCtx *instance = master->list;
3954 if (instance == NULL) {
3955 SCMutexUnlock(&master->lock);
3956 return -1;
3957 }
3958
3959 /* remove from active list */
3960 if (instance == de_ctx) {
3961 master->list = instance->next;
3962 } else {
3963 DetectEngineCtx *prev = instance;
3964 instance = instance->next; /* already checked first element */
3965
3966 while (instance) {
3967 DetectEngineCtx *next = instance->next;
3968
3969 if (instance == de_ctx) {
3970 prev->next = instance->next;
3971 break;
3972 }
3973
3974 prev = instance;
3975 instance = next;
3976 }
3977 if (instance == NULL) {
3978 SCMutexUnlock(&master->lock);
3979 return -1;
3980 }
3981 }
3982
3983 /* instance is now detached from list */
3984 instance->next = NULL;
3985
3986 /* add to free list */
3987 if (master->free_list == NULL) {
3988 master->free_list = instance;
3989 } else {
3990 instance->next = master->free_list;
3991 master->free_list = instance;
3992 }
3993 SCLogDebug("detect engine %p moved to free list (%u refs)", de_ctx, de_ctx->ref_cnt);
3994
3995 SCMutexUnlock(&master->lock);
3996 return 0;
3997 }
3998
3999 void DetectEnginePruneFreeList(void)
4000 {
4001 DetectEngineMasterCtx *master = &g_master_de_ctx;
4002 SCMutexLock(&master->lock);
4003
4004 DetectEngineCtx *prev = NULL;
4005 DetectEngineCtx *instance = master->free_list;
4006 while (instance) {
4007 DetectEngineCtx *next = instance->next;
4008
4009 SCLogDebug("detect engine %p has %u ref(s)", instance, instance->ref_cnt);
4010
4011 if (instance->ref_cnt == 0) {
4012 if (prev == NULL) {
4013 master->free_list = next;
4014 } else {
4015 prev->next = next;
4016 }
4017
4018 SCLogDebug("freeing detect engine %p", instance);
4019 DetectEngineCtxFree(instance);
4020 instance = NULL;
4021 }
4022
4023 prev = instance;
4024 instance = next;
4025 }
4026 SCMutexUnlock(&master->lock);
4027 }
4028
4029 static int reloads = 0;
4030
4031 /** \brief Reload the detection engine
4032 *
4033 * \param filename YAML file to load for the detect config
4034 *
4035 * \retval -1 error
4036 * \retval 0 ok
4037 */
4038 int DetectEngineReload(const SCInstance *suri)
4039 {
4040 DetectEngineCtx *new_de_ctx = NULL;
4041 DetectEngineCtx *old_de_ctx = NULL;
4042
4043 char prefix[128];
4044 memset(prefix, 0, sizeof(prefix));
4045
4046 SCLogNotice("rule reload starting");
4047
4048 if (suri->conf_filename != NULL) {
4049 snprintf(prefix, sizeof(prefix), "detect-engine-reloads.%d", reloads++);
4050 if (ConfYamlLoadFileWithPrefix(suri->conf_filename, prefix) != 0) {
4051 SCLogError(SC_ERR_CONF_YAML_ERROR, "failed to load yaml %s",
4052 suri->conf_filename);
4053 return -1;
4054 }
4055
4056 ConfNode *node = ConfGetNode(prefix);
4057 if (node == NULL) {
4058 SCLogError(SC_ERR_CONF_YAML_ERROR, "failed to properly setup yaml %s",
4059 suri->conf_filename);
4060 return -1;
4061 }
4062 #if 0
4063 ConfDump();
4064 #endif
4065 }
4066
4067 /* get a reference to the current de_ctx */
4068 old_de_ctx = DetectEngineGetCurrent();
4069 if (old_de_ctx == NULL)
4070 return -1;
4071 SCLogDebug("get ref to old_de_ctx %p", old_de_ctx);
4072 DatasetReload();
4073
4074 /* only reload a regular 'normal' and 'delayed detect stub' detect engines */
4075 if (!(old_de_ctx->type == DETECT_ENGINE_TYPE_NORMAL ||
4076 old_de_ctx->type == DETECT_ENGINE_TYPE_DD_STUB))
4077 {
4078 DetectEngineDeReference(&old_de_ctx);
4079 SCLogNotice("rule reload complete");
4080 return -1;
4081 }
4082
4083 /* get new detection engine */
4084 new_de_ctx = DetectEngineCtxInitWithPrefix(prefix);
4085 if (new_de_ctx == NULL) {
4086 SCLogError(SC_ERR_INITIALIZATION, "initializing detection engine "
4087 "context failed.");
4088 DetectEngineDeReference(&old_de_ctx);
4089 return -1;
4090 }
4091 if (SigLoadSignatures(new_de_ctx,
4092 suri->sig_file, suri->sig_file_exclusive) != 0) {
4093 DetectEngineCtxFree(new_de_ctx);
4094 DetectEngineDeReference(&old_de_ctx);
4095 return -1;
4096 }
4097 SCLogDebug("set up new_de_ctx %p", new_de_ctx);
4098
4099 /* add to master */
4100 DetectEngineAddToMaster(new_de_ctx);
4101
4102 /* move to old free list */
4103 DetectEngineMoveToFreeList(old_de_ctx);
4104 DetectEngineDeReference(&old_de_ctx);
4105
4106 SCLogDebug("going to reload the threads to use new_de_ctx %p", new_de_ctx);
4107 /* update the threads */
4108 DetectEngineReloadThreads(new_de_ctx);
4109 SCLogDebug("threads now run new_de_ctx %p", new_de_ctx);
4110
4111 /* walk free list, freeing the old_de_ctx */
4112 DetectEnginePruneFreeList();
4113
4114 DatasetPostReloadCleanup();
4115
4116 DetectEngineBumpVersion();
4117
4118 SCLogDebug("old_de_ctx should have been freed");
4119
4120 SCLogNotice("rule reload complete");
4121 return 0;
4122 }
4123
4124 static uint32_t TenantIdHash(HashTable *h, void *data, uint16_t data_len)
4125 {
4126 DetectEngineThreadCtx *det_ctx = (DetectEngineThreadCtx *)data;
4127 return det_ctx->tenant_id % h->array_size;
4128 }
4129
4130 static char TenantIdCompare(void *d1, uint16_t d1_len, void *d2, uint16_t d2_len)
4131 {
4132 DetectEngineThreadCtx *det1 = (DetectEngineThreadCtx *)d1;
4133 DetectEngineThreadCtx *det2 = (DetectEngineThreadCtx *)d2;
4134 return (det1->tenant_id == det2->tenant_id);
4135 }
4136
4137 static void TenantIdFree(void *d)
4138 {
4139 DetectEngineThreadCtxFree(d);
4140 }
4141
4142 int DetectEngineMTApply(void)
4143 {
4144 DetectEngineMasterCtx *master = &g_master_de_ctx;
4145 SCMutexLock(&master->lock);
4146
4147 if (master->tenant_selector == TENANT_SELECTOR_UNKNOWN) {
4148 SCLogInfo("error, no tenant selector");
4149 SCMutexUnlock(&master->lock);
4150 return -1;
4151 }
4152
4153 DetectEngineCtx *stub_de_ctx = NULL;
4154 DetectEngineCtx *list = master->list;
4155 for ( ; list != NULL; list = list->next) {
4156 SCLogDebug("list %p tenant %u", list, list->tenant_id);
4157
4158 if (list->type == DETECT_ENGINE_TYPE_NORMAL ||
4159 list->type == DETECT_ENGINE_TYPE_MT_STUB ||
4160 list->type == DETECT_ENGINE_TYPE_DD_STUB)
4161 {
4162 stub_de_ctx = list;
4163 break;
4164 }
4165 }
4166 if (stub_de_ctx == NULL) {
4167 stub_de_ctx = DetectEngineCtxInitStubForMT();
4168 if (stub_de_ctx == NULL) {
4169 SCMutexUnlock(&master->lock);
4170 return -1;
4171 }
4172
4173 if (master->list == NULL) {
4174 master->list = stub_de_ctx;
4175 } else {
4176 stub_de_ctx->next = master->list;
4177 master->list = stub_de_ctx;
4178 }
4179 }
4180
4181 /* update the threads */
4182 SCLogDebug("MT reload starting");
4183 DetectEngineReloadThreads(stub_de_ctx);
4184 SCLogDebug("MT reload done");
4185
4186 SCMutexUnlock(&master->lock);
4187
4188 /* walk free list, freeing the old_de_ctx */
4189 DetectEnginePruneFreeList();
4190
4191 SCLogDebug("old_de_ctx should have been freed");
4192 return 0;
4193 }
4194
4195 static int g_parse_metadata = 0;
4196
4197 void DetectEngineSetParseMetadata(void)
4198 {
4199 g_parse_metadata = 1;
4200 }
4201
4202 void DetectEngineUnsetParseMetadata(void)
4203 {
4204 g_parse_metadata = 0;
4205 }
4206
4207 int DetectEngineMustParseMetadata(void)
4208 {
4209 return g_parse_metadata;
4210 }
4211
4212 const char *DetectSigmatchListEnumToString(enum DetectSigmatchListEnum type)
4213 {
4214 switch (type) {
4215 case DETECT_SM_LIST_MATCH:
4216 return "packet";
4217 case DETECT_SM_LIST_PMATCH:
4218 return "packet/stream payload";
4219
4220 case DETECT_SM_LIST_TMATCH:
4221 return "tag";
4222
4223 case DETECT_SM_LIST_BASE64_DATA:
4224 return "base64_data";
4225
4226 case DETECT_SM_LIST_POSTMATCH:
4227 return "post-match";
4228
4229 case DETECT_SM_LIST_SUPPRESS:
4230 return "suppress";
4231 case DETECT_SM_LIST_THRESHOLD:
4232 return "threshold";
4233
4234 case DETECT_SM_LIST_MAX:
4235 return "max (internal)";
4236 }
4237 return "error";
4238 }
4239
4240 /* events api */
4241 void DetectEngineSetEvent(DetectEngineThreadCtx *det_ctx, uint8_t e)
4242 {
4243 AppLayerDecoderEventsSetEventRaw(&det_ctx->decoder_events, e);
4244 det_ctx->events++;
4245 }
4246
4247 AppLayerDecoderEvents *DetectEngineGetEvents(DetectEngineThreadCtx *det_ctx)
4248 {
4249 return det_ctx->decoder_events;
4250 }
4251
4252 int DetectEngineGetEventInfo(const char *event_name, int *event_id,
4253 AppLayerEventType *event_type)
4254 {
4255 *event_id = SCMapEnumNameToValue(event_name, det_ctx_event_table);
4256 if (*event_id == -1) {
4257 SCLogError(SC_ERR_INVALID_ENUM_MAP, "event \"%s\" not present in "
4258 "det_ctx's enum map table.", event_name);
4259 /* this should be treated as fatal */
4260 return -1;
4261 }
4262 *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;
4263
4264 return 0;
4265 }
4266
4267 /*************************************Unittest*********************************/
4268
4269 #ifdef UNITTESTS
4270
4271 static int DetectEngineInitYamlConf(const char *conf)
4272 {
4273 ConfCreateContextBackup();
4274 ConfInit();
4275 return ConfYamlLoadString(conf, strlen(conf));
4276 }
4277
4278 static void DetectEngineDeInitYamlConf(void)
4279 {
4280 ConfDeInit();
4281 ConfRestoreContextBackup();
4282
4283 return;
4284 }
4285
4286 static int DetectEngineTest01(void)
4287 {
4288 const char *conf =
4289 "%YAML 1.1\n"
4290 "---\n"
4291 "detect-engine:\n"
4292 " - profile: medium\n"
4293 " - custom-values:\n"
4294 " toclient_src_groups: 2\n"
4295 " toclient_dst_groups: 2\n"
4296 " toclient_sp_groups: 2\n"
4297 " toclient_dp_groups: 3\n"
4298 " toserver_src_groups: 2\n"
4299 " toserver_dst_groups: 4\n"
4300 " toserver_sp_groups: 2\n"
4301 " toserver_dp_groups: 25\n"
4302 " - inspection-recursion-limit: 0\n";
4303
4304 FAIL_IF(DetectEngineInitYamlConf(conf) == -1);
4305
4306 DetectEngineCtx *de_ctx = DetectEngineCtxInit();
4307 FAIL_IF_NULL(de_ctx);
4308
4309 FAIL_IF_NOT(de_ctx->inspection_recursion_limit == -1);
4310
4311 DetectEngineCtxFree(de_ctx);
4312
4313 DetectEngineDeInitYamlConf();
4314
4315 PASS;
4316 }
4317
4318 static int DetectEngineTest02(void)
4319 {
4320 const char *conf =
4321 "%YAML 1.1\n"
4322 "---\n"
4323 "detect-engine:\n"
4324 " - profile: medium\n"
4325 " - custom-values:\n"
4326 " toclient_src_groups: 2\n"
4327 " toclient_dst_groups: 2\n"
4328 " toclient_sp_groups: 2\n"
4329 " toclient_dp_groups: 3\n"
4330 " toserver_src_groups: 2\n"
4331 " toserver_dst_groups: 4\n"
4332 " toserver_sp_groups: 2\n"
4333 " toserver_dp_groups: 25\n"
4334 " - inspection-recursion-limit:\n";
4335
4336 FAIL_IF(DetectEngineInitYamlConf(conf) == -1);
4337
4338 DetectEngineCtx *de_ctx = DetectEngineCtxInit();
4339 FAIL_IF_NULL(de_ctx);
4340
4341 FAIL_IF_NOT(
4342 de_ctx->inspection_recursion_limit == DETECT_ENGINE_DEFAULT_INSPECTION_RECURSION_LIMIT);
4343
4344 DetectEngineCtxFree(de_ctx);
4345
4346 DetectEngineDeInitYamlConf();
4347
4348 PASS;
4349 }
4350
4351 static int DetectEngineTest03(void)
4352 {
4353 const char *conf =
4354 "%YAML 1.1\n"
4355 "---\n"
4356 "detect-engine:\n"
4357 " - profile: medium\n"
4358 " - custom-values:\n"
4359 " toclient_src_groups: 2\n"
4360 " toclient_dst_groups: 2\n"
4361 " toclient_sp_groups: 2\n"
4362 " toclient_dp_groups: 3\n"
4363 " toserver_src_groups: 2\n"
4364 " toserver_dst_groups: 4\n"
4365 " toserver_sp_groups: 2\n"
4366 " toserver_dp_groups: 25\n";
4367
4368 FAIL_IF(DetectEngineInitYamlConf(conf) == -1);
4369
4370 DetectEngineCtx *de_ctx = DetectEngineCtxInit();
4371 FAIL_IF_NULL(de_ctx);
4372
4373 FAIL_IF_NOT(
4374 de_ctx->inspection_recursion_limit == DETECT_ENGINE_DEFAULT_INSPECTION_RECURSION_LIMIT);
4375
4376 DetectEngineCtxFree(de_ctx);
4377
4378 DetectEngineDeInitYamlConf();
4379
4380 PASS;
4381 }
4382
4383 static int DetectEngineTest04(void)
4384 {
4385 const char *conf =
4386 "%YAML 1.1\n"
4387 "---\n"
4388 "detect-engine:\n"
4389 " - profile: medium\n"
4390 " - custom-values:\n"
4391 " toclient_src_groups: 2\n"
4392 " toclient_dst_groups: 2\n"
4393 " toclient_sp_groups: 2\n"
4394 " toclient_dp_groups: 3\n"
4395 " toserver_src_groups: 2\n"
4396 " toserver_dst_groups: 4\n"
4397 " toserver_sp_groups: 2\n"
4398 " toserver_dp_groups: 25\n"
4399 " - inspection-recursion-limit: 10\n";
4400
4401 FAIL_IF(DetectEngineInitYamlConf(conf) == -1);
4402
4403 DetectEngineCtx *de_ctx = DetectEngineCtxInit();
4404 FAIL_IF_NULL(de_ctx);
4405
4406 FAIL_IF_NOT(de_ctx->inspection_recursion_limit == 10);
4407
4408 DetectEngineCtxFree(de_ctx);
4409
4410 DetectEngineDeInitYamlConf();
4411
4412 PASS;
4413 }
4414
4415 static int DetectEngineTest08(void)
4416 {
4417 const char *conf =
4418 "%YAML 1.1\n"
4419 "---\n"
4420 "detect-engine:\n"
4421 " - profile: custom\n"
4422 " - custom-values:\n"
4423 " toclient-groups: 23\n"
4424 " toserver-groups: 27\n";
4425
4426 FAIL_IF(DetectEngineInitYamlConf(conf) == -1);
4427
4428 DetectEngineCtx *de_ctx = DetectEngineCtxInit();
4429 FAIL_IF_NULL(de_ctx);
4430
4431 FAIL_IF_NOT(de_ctx->max_uniq_toclient_groups == 23);
4432 FAIL_IF_NOT(de_ctx->max_uniq_toserver_groups == 27);
4433
4434 DetectEngineCtxFree(de_ctx);
4435
4436 DetectEngineDeInitYamlConf();
4437
4438 PASS;
4439 }
4440
4441 /** \test bug 892 bad values */
4442 static int DetectEngineTest09(void)
4443 {
4444 const char *conf =
4445 "%YAML 1.1\n"
4446 "---\n"
4447 "detect-engine:\n"
4448 " - profile: custom\n"
4449 " - custom-values:\n"
4450 " toclient-groups: BA\n"
4451 " toserver-groups: BA\n"
4452 " - inspection-recursion-limit: 10\n";
4453
4454 FAIL_IF(DetectEngineInitYamlConf(conf) == -1);
4455
4456 DetectEngineCtx *de_ctx = DetectEngineCtxInit();
4457 FAIL_IF_NULL(de_ctx);
4458
4459 FAIL_IF_NOT(de_ctx->max_uniq_toclient_groups == 20);
4460 FAIL_IF_NOT(de_ctx->max_uniq_toserver_groups == 40);
4461
4462 DetectEngineCtxFree(de_ctx);
4463
4464 DetectEngineDeInitYamlConf();
4465
4466 PASS;
4467 }
4468
4469 #endif
4470
4471 void DetectEngineRegisterTests()
4472 {
4473 #ifdef UNITTESTS
4474 UtRegisterTest("DetectEngineTest01", DetectEngineTest01);
4475 UtRegisterTest("DetectEngineTest02", DetectEngineTest02);
4476 UtRegisterTest("DetectEngineTest03", DetectEngineTest03);
4477 UtRegisterTest("DetectEngineTest04", DetectEngineTest04);
4478 UtRegisterTest("DetectEngineTest08", DetectEngineTest08);
4479 UtRegisterTest("DetectEngineTest09", DetectEngineTest09);
4480 #endif
4481 return;
4482 }