1 /* Copyright (C) 2007-2021 Open Information Security Foundation
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
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.
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
21 * \author Victor Julien <victor@inliniac.net>
24 #include "suricata-common.h"
29 #include "flow-private.h"
30 #include "flow-util.h"
31 #include "flow-worker.h"
33 #include "conf-yaml-loader.h"
36 #include "app-layer-parser.h"
37 #include "app-layer-htp.h"
39 #include "detect-parse.h"
40 #include "detect-engine-sigorder.h"
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"
50 #include "detect-engine-file.h"
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"
62 #include "detect-engine-loader.h"
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"
76 #include "util-device.h"
77 #include "util-var-name.h"
78 #include "util-profiling.h"
79 #include "util-validate.h"
81 #include "tm-threads.h"
84 #include "reputation.h"
86 #define DETECT_ENGINE_DEFAULT_INSPECTION_RECURSION_LIMIT 3000
88 static int DetectEngineCtxLoadConf(DetectEngineCtx
*);
90 static DetectEngineMasterCtx g_master_de_ctx
= { SCMUTEX_INITIALIZER
,
91 0, 99, NULL
, NULL
, TENANT_SELECTOR_UNKNOWN
, NULL
, NULL
, 0};
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
);
100 static DetectEngineAppInspectionEngine
*g_app_inspect_engines
= NULL
;
101 static DetectEnginePktInspectionEngine
*g_pkt_inspect_engines
= NULL
;
103 SCEnumCharMap det_ctx_event_table
[] = {
105 { "TEST", DET_CTX_EVENT_TEST
},
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
},
123 DETECT_EVENT_TOO_MANY_BUFFERS
,
128 /** \brief register inspect engine at start up time
130 * \note errors are fatal */
131 void DetectPktInspectEngineRegister(const char *name
,
132 InspectionBufferGetPktDataPtr GetPktData
,
133 InspectionBufferPktInspectFunc Callback
)
135 DetectBufferTypeRegister(name
);
136 const int sm_list
= DetectBufferTypeGetByName(name
);
138 FatalError(SC_ERR_INITIALIZATION
,
139 "failed to register inspect engine %s", name
);
142 if ((sm_list
< DETECT_SM_LIST_MATCH
) || (sm_list
>= SHRT_MAX
) ||
145 SCLogError(SC_ERR_INVALID_ARGUMENTS
, "Invalid arguments");
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
));
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
;
159 if (g_pkt_inspect_engines
== NULL
) {
160 g_pkt_inspect_engines
= new_engine
;
162 DetectEnginePktInspectionEngine
*t
= g_pkt_inspect_engines
;
163 while (t
->next
!= NULL
) {
167 t
->next
= new_engine
;
171 /** \brief register inspect engine at start up time
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
)
179 BUG_ON(progress
>= 48);
181 DetectBufferTypeRegister(name
);
182 const int sm_list
= DetectBufferTypeGetByName(name
);
184 FatalError(SC_ERR_INITIALIZATION
,
185 "failed to register inspect engine %s", name
);
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
) ||
194 SCLogError(SC_ERR_INVALID_ARGUMENTS
, "Invalid arguments");
196 } else if (Callback2
== DetectEngineInspectBufferGeneric
&& GetData
== NULL
) {
197 SCLogError(SC_ERR_INVALID_ARGUMENTS
, "Invalid arguments: must register "
198 "GetData with DetectEngineInspectBufferGeneric");
203 if (dir
== SIG_FLAG_TOSERVER
) {
209 DetectEngineAppInspectionEngine
*new_engine
= SCMalloc(sizeof(DetectEngineAppInspectionEngine
));
210 if (unlikely(new_engine
== NULL
)) {
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
;
222 if (g_app_inspect_engines
== NULL
) {
223 g_app_inspect_engines
= new_engine
;
225 DetectEngineAppInspectionEngine
*t
= g_app_inspect_engines
;
226 while (t
->next
!= NULL
) {
230 t
->next
= new_engine
;
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
)
240 const DetectEngineAppInspectionEngine
*t
= g_app_inspect_engines
;
242 if (t
->sm_list
== sm_list
) {
243 DetectEngineAppInspectionEngine
*new_engine
= SCCalloc(1, sizeof(DetectEngineAppInspectionEngine
));
244 if (unlikely(new_engine
== NULL
)) {
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 */
255 if (de_ctx
->app_inspect_engines
== NULL
) {
256 de_ctx
->app_inspect_engines
= new_engine
;
258 DetectEngineAppInspectionEngine
*list
= de_ctx
->app_inspect_engines
;
259 while (list
->next
!= NULL
) {
263 list
->next
= new_engine
;
270 /* copy inspect engines from global registrations to de_ctx list */
271 static void DetectAppLayerInspectEngineCopyListToDetectCtx(DetectEngineCtx
*de_ctx
)
273 const DetectEngineAppInspectionEngine
*t
= g_app_inspect_engines
;
275 DetectEngineAppInspectionEngine
*new_engine
= SCCalloc(1, sizeof(DetectEngineAppInspectionEngine
));
276 if (unlikely(new_engine
== NULL
)) {
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
;
286 if (de_ctx
->app_inspect_engines
== NULL
) {
287 de_ctx
->app_inspect_engines
= new_engine
;
289 DetectEngineAppInspectionEngine
*list
= de_ctx
->app_inspect_engines
;
290 while (list
->next
!= NULL
) {
294 list
->next
= new_engine
;
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
)
307 const DetectEnginePktInspectionEngine
*t
= g_pkt_inspect_engines
;
309 if (t
->sm_list
== sm_list
) {
310 DetectEnginePktInspectionEngine
*new_engine
= SCCalloc(1, sizeof(DetectEnginePktInspectionEngine
));
311 if (unlikely(new_engine
== NULL
)) {
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 */
319 if (de_ctx
->pkt_inspect_engines
== NULL
) {
320 de_ctx
->pkt_inspect_engines
= new_engine
;
322 DetectEnginePktInspectionEngine
*list
= de_ctx
->pkt_inspect_engines
;
323 while (list
->next
!= NULL
) {
327 list
->next
= new_engine
;
334 /* copy inspect engines from global registrations to de_ctx list */
335 static void DetectPktInspectEngineCopyListToDetectCtx(DetectEngineCtx
*de_ctx
)
337 const DetectEnginePktInspectionEngine
*t
= g_pkt_inspect_engines
;
339 SCLogDebug("engine %p", t
);
340 DetectEnginePktInspectionEngine
*new_engine
= SCCalloc(1, sizeof(DetectEnginePktInspectionEngine
));
341 if (unlikely(new_engine
== NULL
)) {
344 new_engine
->sm_list
= t
->sm_list
;
345 new_engine
->sm_list_base
= t
->sm_list
;
346 new_engine
->v1
= t
->v1
;
348 if (de_ctx
->pkt_inspect_engines
== NULL
) {
349 de_ctx
->pkt_inspect_engines
= new_engine
;
351 DetectEnginePktInspectionEngine
*list
= de_ctx
->pkt_inspect_engines
;
352 while (list
->next
!= NULL
) {
356 list
->next
= new_engine
;
364 * \brief append the stream inspection
366 * If stream inspection is MPM, then prepend it.
368 static void AppendStreamInspectEngine(Signature
*s
, SigMatchData
*stream
, int direction
, uint32_t id
)
370 bool prepend
= false;
372 DetectEngineAppInspectionEngine
*new_engine
= SCCalloc(1, sizeof(DetectEngineAppInspectionEngine
));
373 if (unlikely(new_engine
== NULL
)) {
376 if (SigMatchListSMBelongsTo(s
, s
->init_data
->mpm_sm
) == DETECT_SM_LIST_PMATCH
) {
377 SCLogDebug("stream is mpm");
379 new_engine
->mpm
= true;
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;
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
;
400 DetectEngineAppInspectionEngine
*a
= s
->app_inspect
;
401 while (a
->next
!= NULL
) {
405 a
->next
= new_engine
;
408 SCLogDebug("sid %u: engine %p/%u added", s
->id
, new_engine
, new_engine
->id
);
412 * \note for the file inspect engine, the id DE_STATE_ID_FILE_INSPECT
415 int DetectEngineAppInspectionEngine2Signature(DetectEngineCtx
*de_ctx
, Signature
*s
)
417 const int nlists
= s
->init_data
->smlists_array_size
;
418 SigMatchData
*ptrs
[nlists
];
419 memset(&ptrs
, 0, (nlists
* sizeof(SigMatchData
*)));
421 const int mpm_list
= s
->init_data
->mpm_sm
?
422 SigMatchListSMBelongsTo(s
, s
->init_data
->mpm_sm
) :
425 const int files_id
= DetectBufferTypeGetByName("files");
427 /* convert lists to SigMatchData arrays */
429 for (i
= DETECT_SM_LIST_DYNAMIC_START
; i
< nlists
; i
++) {
430 if (s
->init_data
->smlists
[i
] == NULL
)
433 ptrs
[i
] = SigMatchList2DataArray(s
->init_data
->smlists
[i
]);
434 SCLogDebug("ptrs[%d] is set", i
);
437 /* set up pkt inspect engines */
438 const DetectEnginePktInspectionEngine
*e
= de_ctx
->pkt_inspect_engines
;
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;
444 DetectEnginePktInspectionEngine
*new_engine
= SCCalloc(1, sizeof(DetectEnginePktInspectionEngine
));
445 if (unlikely(new_engine
== NULL
)) {
448 if (mpm_list
== e
->sm_list
) {
449 SCLogDebug("%s is mpm", DetectBufferTypeGetNameById(de_ctx
, e
->sm_list
));
451 new_engine
->mpm
= true;
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
);
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
;
468 DetectEnginePktInspectionEngine
*a
= s
->pkt_inspect
;
469 while (a
->next
!= NULL
) {
472 new_engine
->next
= a
->next
;
473 a
->next
= new_engine
;
479 bool head_is_mpm
= false;
480 uint32_t last_id
= DE_STATE_FLAG_BASE
;
481 const DetectEngineAppInspectionEngine
*t
= de_ctx
->app_inspect_engines
;
483 bool prepend
= false;
485 if (t
->sm_list
>= nlists
)
488 if (ptrs
[t
->sm_list
] == NULL
)
491 SCLogDebug("ptrs[%d] is set", t
->sm_list
);
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
))
498 if (s
->flags
& SIG_FLAG_TOSERVER
&& !(s
->flags
& SIG_FLAG_TOCLIENT
)) {
501 } else if (s
->flags
& SIG_FLAG_TOCLIENT
&& !(s
->flags
& SIG_FLAG_TOSERVER
)) {
505 DetectEngineAppInspectionEngine
*new_engine
= SCCalloc(1, sizeof(DetectEngineAppInspectionEngine
));
506 if (unlikely(new_engine
== NULL
)) {
509 if (mpm_list
== t
->sm_list
) {
510 SCLogDebug("%s is mpm", DetectBufferTypeGetNameById(de_ctx
, t
->sm_list
));
513 new_engine
->mpm
= true;
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
);
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
;
533 new_engine
->id
= DE_STATE_FLAG_BASE
; /* id is used as flag in stateful detect */
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
;
544 new_engine
->id
= ++last_id
;
548 DetectEngineAppInspectionEngine
*a
= s
->app_inspect
;
549 while (a
->next
!= NULL
) {
550 if (a
->next
&& a
->next
->progress
> new_engine
->progress
) {
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
;
563 new_engine
->id
= ++last_id
;
567 SCLogDebug("sid %u: engine %p/%u added", s
->id
, new_engine
, new_engine
->id
);
569 s
->init_data
->init_flags
|= SIG_FLAG_INIT_STATE_MATCH
;
574 if ((s
->init_data
->init_flags
& SIG_FLAG_INIT_STATE_MATCH
) &&
575 s
->init_data
->smlists
[DETECT_SM_LIST_PMATCH
] != NULL
)
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);
585 AppendStreamInspectEngine(s
, stream
, 0, last_id
+ 1);
586 AppendStreamInspectEngine(s
, stream
, 1, last_id
+ 1);
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
;
596 const DetectEngineAppInspectionEngine
*iter
= s
->app_inspect
;
598 SCLogDebug("%u: engine %s id %u progress %d %s", s
->id
,
599 DetectBufferTypeGetNameById(de_ctx
, iter
->sm_list
), iter
->id
,
601 iter
->sm_list
== mpm_list
? "MPM":"");
608 /** \brief free app inspect engines for a signature
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.
617 void DetectEngineAppInspectionEngineSignatureFree(DetectEngineCtx
*de_ctx
, Signature
*s
)
621 DetectEngineAppInspectionEngine
*ie
= s
->app_inspect
;
623 nlists
= MAX(ie
->sm_list
+ 1, nlists
);
626 DetectEnginePktInspectionEngine
*e
= s
->pkt_inspect
;
628 nlists
= MAX(e
->sm_list
+ 1, nlists
);
632 BUG_ON(s
->pkt_inspect
);
636 SigMatchData
*ptrs
[nlists
];
637 memset(&ptrs
, 0, (nlists
* sizeof(SigMatchData
*)));
639 /* free engines and put smd in the array */
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
;
650 DetectEnginePktInspectionEngine
*next
= e
->next
;
651 ptrs
[e
->sm_list
] = e
->smd
;
657 for (int i
= 0; i
< nlists
; i
++)
662 SigMatchData
*smd
= ptrs
[i
];
664 if (sigmatch_table
[smd
->type
].Free
!= NULL
) {
665 sigmatch_table
[smd
->type
].Free(de_ctx
, smd
->ctx
);
675 /* code for registering buffers */
677 #include "util-hash-lookup3.h"
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;
683 static DetectEngineTransforms no_transforms
= {
684 .transforms
[0] = {0, NULL
},
688 int DetectBufferTypeMaxId(void)
690 return g_buffer_type_id
;
693 static uint32_t DetectBufferTypeHashFunc(HashListTable
*ht
, void *data
, uint16_t datalen
)
695 const DetectBufferType
*map
= (DetectBufferType
*)data
;
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
;
705 static char DetectBufferTypeCompareFunc(void *data1
, uint16_t len1
, void *data2
,
708 DetectBufferType
*map1
= (DetectBufferType
*)data1
;
709 DetectBufferType
*map2
= (DetectBufferType
*)data2
;
711 int r
= (strcmp(map1
->string
, map2
->string
) == 0);
712 r
&= (memcmp((uint8_t *)&map1
->transforms
, (uint8_t *)&map2
->transforms
, sizeof(map2
->transforms
)) == 0);
716 static void DetectBufferTypeFreeFunc(void *data
)
718 DetectBufferType
*map
= (DetectBufferType
*)data
;
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
)
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
);
734 sigmatch_table
[map
->transforms
.transforms
[i
].transform
].Free(NULL
, map
->transforms
.transforms
[i
].options
);
740 static int DetectBufferTypeInit(void)
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
)
753 static void DetectBufferTypeFree(void)
755 if (g_buffer_type_hash
== NULL
)
758 HashListTableFree(g_buffer_type_hash
);
759 g_buffer_type_hash
= NULL
;
763 static int DetectBufferTypeAdd(const char *string
)
765 DetectBufferType
*map
= SCCalloc(1, sizeof(*map
));
769 map
->string
= string
;
770 map
->id
= g_buffer_type_id
++;
772 BUG_ON(HashListTableAdd(g_buffer_type_hash
, (void *)map
, 0) != 0);
773 SCLogDebug("buffer %s registered with id %d", map
->string
, map
->id
);
777 static DetectBufferType
*DetectBufferTypeLookupByName(const char *string
)
779 DetectBufferType map
= { (char *)string
, NULL
, 0, 0, 0, 0, false, NULL
, NULL
, no_transforms
};
781 DetectBufferType
*res
= HashListTableLookup(g_buffer_type_hash
, &map
, 0);
785 int DetectBufferTypeRegister(const char *name
)
787 BUG_ON(g_buffer_type_reg_closed
);
788 if (g_buffer_type_hash
== NULL
)
789 DetectBufferTypeInit();
791 DetectBufferType
*exists
= DetectBufferTypeLookupByName(name
);
793 return DetectBufferTypeAdd(name
);
799 void DetectBufferTypeSupportsPacket(const char *name
)
801 BUG_ON(g_buffer_type_reg_closed
);
802 DetectBufferTypeRegister(name
);
803 DetectBufferType
*exists
= DetectBufferTypeLookupByName(name
);
805 exists
->packet
= true;
806 SCLogDebug("%p %s -- %d supports packet inspection", exists
, name
, exists
->id
);
809 void DetectBufferTypeSupportsMpm(const char *name
)
811 BUG_ON(g_buffer_type_reg_closed
);
812 DetectBufferTypeRegister(name
);
813 DetectBufferType
*exists
= DetectBufferTypeLookupByName(name
);
816 SCLogDebug("%p %s -- %d supports mpm", exists
, name
, exists
->id
);
819 void DetectBufferTypeSupportsTransformations(const char *name
)
821 BUG_ON(g_buffer_type_reg_closed
);
822 DetectBufferTypeRegister(name
);
823 DetectBufferType
*exists
= DetectBufferTypeLookupByName(name
);
825 exists
->supports_transforms
= true;
826 SCLogDebug("%p %s -- %d supports transformations", exists
, name
, exists
->id
);
829 int DetectBufferTypeGetByName(const char *name
)
831 DetectBufferType
*exists
= DetectBufferTypeLookupByName(name
);
838 const char *DetectBufferTypeGetNameById(const DetectEngineCtx
*de_ctx
, const int id
)
840 BUG_ON(id
< 0 || (uint32_t)id
>= de_ctx
->buffer_type_map_elements
);
841 BUG_ON(de_ctx
->buffer_type_map
== NULL
);
843 if (de_ctx
->buffer_type_map
[id
] == NULL
)
846 return de_ctx
->buffer_type_map
[id
]->string
;
849 static const DetectBufferType
*DetectBufferTypeGetById(const DetectEngineCtx
*de_ctx
, const int id
)
851 BUG_ON(id
< 0 || (uint32_t)id
>= de_ctx
->buffer_type_map_elements
);
852 BUG_ON(de_ctx
->buffer_type_map
== NULL
);
854 return de_ctx
->buffer_type_map
[id
];
857 void DetectBufferTypeSetDescriptionByName(const char *name
, const char *desc
)
859 DetectBufferType
*exists
= DetectBufferTypeLookupByName(name
);
863 exists
->description
= desc
;
866 const char *DetectBufferTypeGetDescriptionById(const DetectEngineCtx
*de_ctx
, const int id
)
868 const DetectBufferType
*exists
= DetectBufferTypeGetById(de_ctx
, id
);
872 return exists
->description
;
875 const char *DetectBufferTypeGetDescriptionByName(const char *name
)
877 const DetectBufferType
*exists
= DetectBufferTypeLookupByName(name
);
881 return exists
->description
;
884 bool DetectBufferTypeSupportsPacketGetById(const DetectEngineCtx
*de_ctx
, const int id
)
886 const DetectBufferType
*map
= DetectBufferTypeGetById(de_ctx
, id
);
889 SCLogDebug("map %p id %d packet? %d", map
, id
, map
->packet
);
893 bool DetectBufferTypeSupportsMpmGetById(const DetectEngineCtx
*de_ctx
, const int id
)
895 const DetectBufferType
*map
= DetectBufferTypeGetById(de_ctx
, id
);
898 SCLogDebug("map %p id %d mpm? %d", map
, id
, map
->mpm
);
902 void DetectBufferTypeRegisterSetupCallback(const char *name
,
903 void (*SetupCallback
)(const DetectEngineCtx
*, Signature
*))
905 BUG_ON(g_buffer_type_reg_closed
);
906 DetectBufferTypeRegister(name
);
907 DetectBufferType
*exists
= DetectBufferTypeLookupByName(name
);
909 exists
->SetupCallback
= SetupCallback
;
912 void DetectBufferRunSetupCallback(const DetectEngineCtx
*de_ctx
,
913 const int id
, Signature
*s
)
915 const DetectBufferType
*map
= DetectBufferTypeGetById(de_ctx
, id
);
916 if (map
&& map
->SetupCallback
) {
917 map
->SetupCallback(de_ctx
, s
);
921 void DetectBufferTypeRegisterValidateCallback(const char *name
,
922 bool (*ValidateCallback
)(const Signature
*, const char **sigerror
))
924 BUG_ON(g_buffer_type_reg_closed
);
925 DetectBufferTypeRegister(name
);
926 DetectBufferType
*exists
= DetectBufferTypeLookupByName(name
);
928 exists
->ValidateCallback
= ValidateCallback
;
931 bool DetectBufferRunValidateCallback(const DetectEngineCtx
*de_ctx
,
932 const int id
, const Signature
*s
, const char **sigerror
)
934 const DetectBufferType
*map
= DetectBufferTypeGetById(de_ctx
, id
);
935 if (map
&& map
->ValidateCallback
) {
936 return map
->ValidateCallback(s
, sigerror
);
941 int DetectBufferSetActiveList(Signature
*s
, const int list
)
943 BUG_ON(s
->init_data
== NULL
);
945 if (s
->init_data
->list
&& s
->init_data
->transforms
.cnt
) {
948 s
->init_data
->list
= list
;
949 s
->init_data
->list_set
= true;
954 int DetectBufferGetActiveList(DetectEngineCtx
*de_ctx
, Signature
*s
)
956 BUG_ON(s
->init_data
== NULL
);
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
);
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) {
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;
984 void InspectionBufferClean(DetectEngineThreadCtx
*det_ctx
)
987 for (uint32_t i
= 0; i
< det_ctx
->inspect
.to_clear_idx
; i
++)
989 const uint32_t idx
= det_ctx
->inspect
.to_clear_queue
[i
];
990 InspectionBuffer
*buffer
= &det_ctx
->inspect
.buffers
[idx
];
991 buffer
->inspect
= NULL
;
993 det_ctx
->inspect
.to_clear_idx
= 0;
996 for (uint32_t i
= 0; i
< det_ctx
->multi_inspect
.to_clear_idx
; i
++)
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
;
1007 det_ctx
->multi_inspect
.to_clear_idx
= 0;
1010 InspectionBuffer
*InspectionBufferGet(DetectEngineThreadCtx
*det_ctx
, const int list_id
)
1012 return &det_ctx
->inspect
.buffers
[list_id
];
1015 static InspectionBufferMultipleForList
*InspectionBufferGetMulti(
1016 DetectEngineThreadCtx
*det_ctx
, const int list_id
)
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
;
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
)
1033 if (unlikely(local_id
>= 1024)) {
1034 DetectEngineSetEvent(det_ctx
, DETECT_EVENT_TOO_MANY_BUFFERS
);
1038 InspectionBufferMultipleForList
*fb
= InspectionBufferGetMulti(det_ctx
, list_id
);
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
);
1046 SCLogDebug("fb->inspection_buffers %p", fb
->inspection_buffers
);
1047 void *ptr
= SCRealloc(fb
->inspection_buffers
, (local_id
+ 1) * sizeof(InspectionBuffer
));
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
;
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;
1067 void InspectionBufferInit(InspectionBuffer
*buffer
, uint32_t initial_size
)
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
;
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
)
1080 #ifdef DEBUG_VALIDATION
1081 DEBUG_VALIDATE_BUG_ON(!buffer
->multi
);
1083 buffer
->inspect
= buffer
->orig
= data
;
1084 buffer
->inspect_len
= buffer
->orig_len
= data_len
;
1087 InspectionBufferApplyTransforms(buffer
, transforms
);
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
)
1094 #ifdef DEBUG_VALIDATION
1095 DEBUG_VALIDATE_BUG_ON(buffer
->multi
);
1097 if (buffer
->inspect
== NULL
) {
1099 if (det_ctx
&& list_id
!= -1)
1101 det_ctx
->inspect
.to_clear_queue
[det_ctx
->inspect
.to_clear_idx
++] = list_id
;
1103 buffer
->inspect
= buffer
->orig
= data
;
1104 buffer
->inspect_len
= buffer
->orig_len
= data_len
;
1108 void InspectionBufferFree(InspectionBuffer
*buffer
)
1110 if (buffer
->buf
!= NULL
) {
1111 SCFree(buffer
->buf
);
1113 memset(buffer
, 0, sizeof(*buffer
));
1117 * \brief make sure that the buffer has at least 'min_size' bytes
1118 * Expand the buffer if necessary
1120 void InspectionBufferCheckAndExpand(InspectionBuffer
*buffer
, uint32_t min_size
)
1122 if (likely(buffer
->size
>= min_size
))
1125 uint32_t new_size
= (buffer
->size
== 0) ? 4096 : buffer
->size
;
1126 while (new_size
< min_size
) {
1130 void *ptr
= SCRealloc(buffer
->buf
, new_size
);
1133 buffer
->size
= new_size
;
1137 void InspectionBufferCopy(InspectionBuffer
*buffer
, uint8_t *buf
, uint32_t buf_len
)
1139 InspectionBufferCheckAndExpand(buffer
, buf_len
);
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
;
1149 /** \brief Check content byte array compatibility with transforms
1151 * The "content" array is presented to the transforms so that each
1152 * transform may validate that it's compatible with the transform.
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.
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
1164 * \retval true (false) If any of the transforms indicate the byte array is
1165 * (is not) compatible.
1167 bool DetectBufferTypeValidateTransform(DetectEngineCtx
*de_ctx
, int sm_list
,
1168 const uint8_t *content
, uint16_t content_len
, const char **namestr
)
1170 const DetectBufferType
*dbt
= DetectBufferTypeGetById(de_ctx
, sm_list
);
1171 BUG_ON(dbt
== NULL
);
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
)
1178 if (sigmatch_table
[t
->transform
].TransformValidate(content
, content_len
, t
->options
)) {
1183 *namestr
= sigmatch_table
[t
->transform
].name
;
1192 void InspectionBufferApplyTransforms(InspectionBuffer
*buffer
,
1193 const DetectEngineTransforms
*transforms
)
1196 for (int i
= 0; i
< DETECT_TRANSFORMS_MAX
; i
++) {
1197 const int id
= transforms
->transforms
[i
].transform
;
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
);
1207 static void DetectBufferTypeSetupDetectEngine(DetectEngineCtx
*de_ctx
)
1209 const int size
= g_buffer_type_id
;
1210 BUG_ON(!(size
> 0));
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
);
1217 SCLogDebug("DETECT_SM_LIST_DYNAMIC_START %u", DETECT_SM_LIST_DYNAMIC_START
);
1218 HashListTableBucket
*b
= HashListTableGetListHead(g_buffer_type_hash
);
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
);
1229 de_ctx
->buffer_type_hash
= HashListTableInit(256,
1230 DetectBufferTypeHashFunc
,
1231 DetectBufferTypeCompareFunc
,
1232 DetectBufferTypeFreeFunc
);
1233 if (de_ctx
->buffer_type_hash
== NULL
) {
1236 de_ctx
->buffer_type_id
= g_buffer_type_id
;
1238 PrefilterInit(de_ctx
);
1239 DetectMpmInitializeAppMpms(de_ctx
);
1240 DetectAppLayerInspectEngineCopyListToDetectCtx(de_ctx
);
1241 DetectMpmInitializePktMpms(de_ctx
);
1242 DetectPktInspectEngineCopyListToDetectCtx(de_ctx
);
1245 static void DetectBufferTypeFreeDetectEngine(DetectEngineCtx
*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
);
1253 DetectEngineAppInspectionEngine
*ilist
= de_ctx
->app_inspect_engines
;
1255 DetectEngineAppInspectionEngine
*next
= ilist
->next
;
1259 DetectBufferMpmRegistery
*mlist
= de_ctx
->app_mpms_list
;
1261 DetectBufferMpmRegistery
*next
= mlist
->next
;
1265 DetectEnginePktInspectionEngine
*plist
= de_ctx
->pkt_inspect_engines
;
1267 DetectEnginePktInspectionEngine
*next
= plist
->next
;
1271 DetectBufferMpmRegistery
*pmlist
= de_ctx
->pkt_mpms_list
;
1273 DetectBufferMpmRegistery
*next
= pmlist
->next
;
1277 PrefilterDeinit(de_ctx
);
1281 void DetectBufferTypeCloseRegistration(void)
1283 BUG_ON(g_buffer_type_hash
== NULL
);
1285 g_buffer_type_reg_closed
= 1;
1288 int DetectBufferTypeGetByIdTransforms(DetectEngineCtx
*de_ctx
, const int id
,
1289 TransformData
*transforms
, int transform_cnt
)
1291 const DetectBufferType
*base_map
= DetectBufferTypeGetById(de_ctx
, id
);
1295 if (!base_map
->supports_transforms
) {
1296 SCLogError(SC_ERR_INVALID_SIGNATURE
, "buffer '%s' does not support transformations",
1301 SCLogDebug("base_map %s", base_map
->string
);
1303 DetectEngineTransforms t
;
1304 memset(&t
, 0, sizeof(t
));
1305 for (int i
= 0; i
< transform_cnt
; i
++) {
1306 t
.transforms
[i
] = transforms
[i
];
1308 t
.cnt
= transform_cnt
;
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);
1313 SCLogDebug("res %p", res
);
1318 DetectBufferType
*map
= SCCalloc(1, sizeof(*map
));
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
;
1331 DetectPktMpmRegisterByParentId(de_ctx
,
1332 map
->id
, map
->parent_id
, &map
->transforms
);
1334 DetectAppLayerMpmRegisterByParentId(de_ctx
,
1335 map
->id
, map
->parent_id
, &map
->transforms
);
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
);
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;
1350 DetectPktInspectEngineCopy(de_ctx
, map
->parent_id
, map
->id
,
1353 DetectAppLayerInspectEngineCopy(de_ctx
, map
->parent_id
, map
->id
,
1360 /* returns false if no match, true if match */
1361 static int DetectEngineInspectRulePacketMatches(
1362 DetectEngineThreadCtx
*det_ctx
,
1363 const DetectEnginePktInspectionEngine
*engine
,
1365 Packet
*p
, uint8_t *_alert_flags
)
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
];
1373 SCLogDebug("running match functions, sm %p", smd
);
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");
1381 KEYWORD_PROFILING_END(det_ctx
, smd
->type
, 1);
1383 SCLogDebug("match and is_last");
1391 static int DetectEngineInspectRulePayloadMatches(
1392 DetectEngineThreadCtx
*det_ctx
,
1393 const DetectEnginePktInspectionEngine
*engine
,
1394 const Signature
*s
, Packet
*p
, uint8_t *alert_flags
)
1398 DetectEngineCtx
*de_ctx
= det_ctx
->de_ctx
;
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
) {
1405 if (p
->flags
& PKT_DETECT_HAS_STREAMDATA
) {
1406 pmatch
= DetectEngineInspectStreamPayload(de_ctx
, det_ctx
, s
, p
->flow
, p
);
1408 det_ctx
->flags
|= DETECT_ENGINE_THREAD_CTX_STREAM_CONTENT_MATCH
;
1409 *alert_flags
|= PACKET_ALERT_FLAG_STREAM_MATCH
;
1412 /* no match? then inspect packet payload */
1414 SCLogDebug("no match in stream, fall back to packet payload");
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
)) {
1421 if (DetectEngineInspectPacketPayload(de_ctx
, det_ctx
, s
, p
->flow
, p
) != 1) {
1426 if (DetectEngineInspectPacketPayload(de_ctx
, det_ctx
, s
, p
->flow
, p
) != 1) {
1433 bool DetectEnginePktInspectionRun(ThreadVars
*tv
,
1434 DetectEngineThreadCtx
*det_ctx
, const Signature
*s
,
1436 uint8_t *alert_flags
)
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
);
1445 SCLogDebug("sid %u: e %p Callback returned true", s
->id
, e
);
1448 SCLogDebug("sid %u: returning true", s
->id
);
1453 * \param data pointer to SigMatchData. Allowed to be NULL.
1455 static int DetectEnginePktInspectionAppend(Signature
*s
, InspectionBufferPktInspectFunc Callback
,
1456 SigMatchData
*data
, const int list_id
)
1458 DetectEnginePktInspectionEngine
*e
= SCCalloc(1, sizeof(*e
));
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
;
1468 if (s
->pkt_inspect
== NULL
) {
1471 DetectEnginePktInspectionEngine
*a
= s
->pkt_inspect
;
1472 while (a
->next
!= NULL
) {
1480 int DetectEnginePktInspectionSetup(Signature
*s
)
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)
1487 SCLogDebug("sid %u: DetectEngineInspectRulePayloadMatches appended", s
->id
);
1490 if (s
->sm_arrays
[DETECT_SM_LIST_MATCH
]) {
1491 if (DetectEnginePktInspectionAppend(
1492 s
, DetectEngineInspectRulePacketMatches
, NULL
, DETECT_SM_LIST_MATCH
) < 0)
1494 SCLogDebug("sid %u: DetectEngineInspectRulePacketMatches appended", s
->id
);
1500 /* code to control the main thread to do a reload */
1502 enum DetectEngineSyncState
{
1503 IDLE
, /**< ready to start a reload */
1504 RELOAD
, /**< command main thread to do the reload */
1508 typedef struct DetectEngineSyncer_
{
1510 enum DetectEngineSyncState state
;
1511 } DetectEngineSyncer
;
1513 static DetectEngineSyncer detect_sync
= { SCMUTEX_INITIALIZER
, IDLE
};
1515 /* tell main to start reloading */
1516 int DetectEngineReloadStart(void)
1519 SCMutexLock(&detect_sync
.m
);
1520 if (detect_sync
.state
== IDLE
) {
1521 detect_sync
.state
= RELOAD
;
1525 SCMutexUnlock(&detect_sync
.m
);
1529 /* main thread checks this to see if it should start */
1530 int DetectEngineReloadIsStart(void)
1533 SCMutexLock(&detect_sync
.m
);
1534 if (detect_sync
.state
== RELOAD
) {
1537 SCMutexUnlock(&detect_sync
.m
);
1541 /* main thread sets done when it's done */
1542 void DetectEngineReloadSetIdle(void)
1544 SCMutexLock(&detect_sync
.m
);
1545 detect_sync
.state
= IDLE
;
1546 SCMutexUnlock(&detect_sync
.m
);
1549 /* caller loops this until it returns 1 */
1550 int DetectEngineReloadIsIdle(void)
1553 SCMutexLock(&detect_sync
.m
);
1554 if (detect_sync
.state
== IDLE
) {
1557 SCMutexUnlock(&detect_sync
.m
);
1561 /** \brief Do the content inspection & validation for a signature
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
1568 * \param flags app layer flags
1569 * \param state App layer state
1571 * \retval 0 no match
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
)
1578 SCLogDebug("running match functions, sm %p", smd
);
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));
1587 return DETECT_ENGINE_INSPECT_SIG_NO_MATCH
;
1589 return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH
;
1598 return DETECT_ENGINE_INSPECT_SIG_MATCH
;
1603 * \brief Do the content inspection & validation for a signature
1605 * \param de_ctx Detection engine context
1606 * \param det_ctx Detection engine thread context
1607 * \param s Signature to inspect
1609 * \param flags app layer flags
1610 * \param state App layer state
1612 * \retval 0 no match.
1614 * \retval 2 Sig can't match.
1616 int DetectEngineInspectBufferGeneric(
1617 DetectEngineCtx
*de_ctx
, DetectEngineThreadCtx
*det_ctx
,
1618 const DetectEngineAppInspectionEngine
*engine
,
1620 Flow
*f
, uint8_t flags
, void *alstate
, void *txv
, uint64_t tx_id
)
1622 const int list_id
= engine
->sm_list
;
1623 SCLogDebug("running inspect on %d", list_id
);
1625 const bool eof
= (AppLayerParserGetStateProgress(f
->proto
, f
->alproto
, txv
, flags
) > engine
->progress
);
1627 SCLogDebug("list %d mpm? %s transforms %p",
1628 engine
->sm_list
, engine
->mpm
? "true" : "false", engine
->v2
.transforms
);
1630 /* if prefilter didn't already run, we need to consider transformations */
1631 const DetectEngineTransforms
*transforms
= NULL
;
1633 transforms
= engine
->v2
.transforms
;
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
;
1643 const uint32_t data_len
= buffer
->inspect_len
;
1644 const uint8_t *data
= buffer
->inspect
;
1645 const uint64_t offset
= buffer
->inspect_offset
;
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
;
1651 det_ctx
->discontinue_matching
= 0;
1652 det_ctx
->buffer_offset
= 0;
1653 det_ctx
->inspection_recursion_counter
= 0;
1655 /* Inspect all the uricontents fetched on each
1656 * transaction at the app layer */
1657 int r
= DetectEngineContentInspection(de_ctx
, det_ctx
,
1660 (uint8_t *)data
, data_len
, offset
, ci_flags
,
1661 DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE
);
1663 return DETECT_ENGINE_INSPECT_SIG_MATCH
;
1665 return eof
? DETECT_ENGINE_INSPECT_SIG_CANT_MATCH
:
1666 DETECT_ENGINE_INSPECT_SIG_NO_MATCH
;
1671 * \brief Do the content inspection & validation for a signature
1673 * \param de_ctx Detection engine context
1674 * \param det_ctx Detection engine thread context
1675 * \param s Signature to inspect
1678 * \retval 0 no match.
1681 int DetectEngineInspectPktBufferGeneric(
1682 DetectEngineThreadCtx
*det_ctx
,
1683 const DetectEnginePktInspectionEngine
*engine
,
1684 const Signature
*s
, Packet
*p
, uint8_t *_alert_flags
)
1686 const int list_id
= engine
->sm_list
;
1687 SCLogDebug("running inspect on %d", list_id
);
1689 SCLogDebug("list %d transforms %p",
1690 engine
->sm_list
, engine
->v1
.transforms
);
1692 /* if prefilter didn't already run, we need to consider transformations */
1693 const DetectEngineTransforms
*transforms
= NULL
;
1695 transforms
= engine
->v1
.transforms
;
1698 const InspectionBuffer
*buffer
= engine
->v1
.GetData(det_ctx
, transforms
, p
,
1700 if (unlikely(buffer
== NULL
)) {
1701 return DETECT_ENGINE_INSPECT_SIG_NO_MATCH
;
1704 const uint32_t data_len
= buffer
->inspect_len
;
1705 const uint8_t *data
= buffer
->inspect
;
1706 const uint64_t offset
= 0;
1708 uint8_t ci_flags
= DETECT_CI_FLAGS_START
|DETECT_CI_FLAGS_END
;
1709 ci_flags
|= buffer
->flags
;
1711 det_ctx
->discontinue_matching
= 0;
1712 det_ctx
->buffer_offset
= 0;
1713 det_ctx
->inspection_recursion_counter
= 0;
1715 /* Inspect all the uricontents fetched on each
1716 * transaction at the app layer */
1717 int r
= DetectEngineContentInspection(det_ctx
->de_ctx
, det_ctx
,
1720 (uint8_t *)data
, data_len
, offset
, ci_flags
,
1721 DETECT_ENGINE_CONTENT_INSPECTION_MODE_HEADER
);
1723 return DETECT_ENGINE_INSPECT_SIG_MATCH
;
1725 return DETECT_ENGINE_INSPECT_SIG_NO_MATCH
;
1730 /* nudge capture loops to wake up */
1731 static void BreakCapture(void)
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) {
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
);
1745 TmModule
*tm
= TmModuleGetById(s
->tm_id
);
1746 if (!(tm
->flags
& TM_FLAG_RECEIVE_TM
)) {
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
));
1760 SCMutexUnlock(&tv_root_lock
);
1764 * \brief inject a pseudo packet into each detect thread that doesn't use the
1767 static void InjectPackets(ThreadVars
**detect_tvs
,
1768 DetectEngineThreadCtx
**new_det_ctx
,
1769 int no_of_detect_tvs
)
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();
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
);
1792 * \brief Update detect threads with new detect engine
1794 * Atomically update each detect thread with a new thread context
1795 * that is associated to the new detection engine(s).
1797 * If called in unix socket mode, it's possible that we don't have
1798 * detect threads yet.
1801 * \retval 0 no detection threads
1802 * \retval 1 successful reload
1804 static int DetectEngineReloadThreads(DetectEngineCtx
*new_de_ctx
)
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) {
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
*)));
1824 /* start the process of swapping detect threads ctxs */
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) {
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
)) {
1838 if (suricata_ctl_flags
!= 0) {
1839 SCMutexUnlock(&tv_root_lock
);
1843 old_det_ctx
[i
] = FlowWorkerGetDetectCtxPtr(SC_ATOMIC_GET(s
->slot_data
));
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
);
1853 SCLogDebug("live rule swap created new det_ctx - %p and de_ctx "
1854 "- %p\n", new_det_ctx
[i
], new_de_ctx
);
1859 BUG_ON(i
!= no_of_detect_tvs
);
1861 /* atomically replace the det_ctx data */
1863 for (ThreadVars
*tv
= tv_root
[TVT_PPT
]; tv
!= NULL
; tv
= tv
->next
) {
1864 if ((tv
->tmm_flags
& TM_FLAG_DETECT_TM
) == 0) {
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
)) {
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
++]);
1878 SCMutexUnlock(&tv_root_lock
);
1880 /* threads now all have new data, however they may not have started using
1881 * it and may still use the old data */
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
);
1886 InjectPackets(detect_tvs
, new_det_ctx
, no_of_detect_tvs
);
1888 for (i
= 0; i
< no_of_detect_tvs
; i
++) {
1891 while (SC_ATOMIC_GET(new_det_ctx
[i
]->so_far_used_by_detect
) != 1) {
1892 if (suricata_ctl_flags
!= 0) {
1902 SCLogDebug("new_det_ctx - %p used by detect engine", new_det_ctx
[i
]);
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) {
1916 while (!TmThreadsCheckFlag(tv
, THV_RUNNING_DONE
)) {
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",
1926 DetectEngineThreadCtxDeinit(NULL
, old_det_ctx
[i
]);
1929 SRepReloadComplete();
1934 for (i
= 0; i
< no_of_detect_tvs
; i
++) {
1935 if (new_det_ctx
[i
] != NULL
)
1936 DetectEngineThreadCtxDeinit(NULL
, new_det_ctx
[i
]);
1941 static DetectEngineCtx
*DetectEngineCtxInitReal(enum DetectEngineType type
, const char *prefix
)
1943 DetectEngineCtx
*de_ctx
= SCMalloc(sizeof(DetectEngineCtx
));
1944 if (unlikely(de_ctx
== NULL
))
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
;
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
);
1959 if (prefix
!= NULL
) {
1960 strlcpy(de_ctx
->config_prefix
, prefix
, sizeof(de_ctx
->config_prefix
));
1963 if (ConfGetBool("engine.init-failure-fatal", (int *)&(de_ctx
->failure_fatal
)) != 1) {
1964 SCLogDebug("ConfGetBool could not load the value.");
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
);
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.");
1979 if (DetectEngineCtxLoadConf(de_ctx
) == -1) {
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
);
1991 /* init iprep... ignore errors for now */
1992 (void)SRepInit(de_ctx
);
1994 SCClassConfLoadClassficationConfigFile(de_ctx
, NULL
);
1995 SCRConfLoadReferenceConfigFile(de_ctx
, NULL
);
1997 if (ActionInitConfig() < 0) {
2001 de_ctx
->version
= DetectEngineGetVersion();
2002 VarNameStoreSetupStaging(de_ctx
->version
);
2003 SCLogDebug("dectx with version %u", de_ctx
->version
);
2006 if (de_ctx
!= NULL
) {
2007 DetectEngineCtxFree(de_ctx
);
2013 DetectEngineCtx
*DetectEngineCtxInitStubForMT(void)
2015 return DetectEngineCtxInitReal(DETECT_ENGINE_TYPE_MT_STUB
, NULL
);
2018 DetectEngineCtx
*DetectEngineCtxInitStubForDD(void)
2020 return DetectEngineCtxInitReal(DETECT_ENGINE_TYPE_DD_STUB
, NULL
);
2023 DetectEngineCtx
*DetectEngineCtxInit(void)
2025 return DetectEngineCtxInitReal(DETECT_ENGINE_TYPE_NORMAL
, NULL
);
2028 DetectEngineCtx
*DetectEngineCtxInitWithPrefix(const char *prefix
)
2030 if (prefix
== NULL
|| strlen(prefix
) == 0)
2031 return DetectEngineCtxInit();
2033 return DetectEngineCtxInitReal(DETECT_ENGINE_TYPE_NORMAL
, prefix
);
2036 static void DetectEngineCtxFreeThreadKeywordData(DetectEngineCtx
*de_ctx
)
2038 DetectEngineThreadKeywordCtxItem
*item
= de_ctx
->keyword_list
;
2040 DetectEngineThreadKeywordCtxItem
*next
= item
->next
;
2044 de_ctx
->keyword_list
= NULL
;
2047 static void DetectEngineCtxFreeFailedSigs(DetectEngineCtx
*de_ctx
)
2049 SigString
*item
= NULL
;
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
);
2058 TAILQ_REMOVE(&de_ctx
->sig_stat
.failed_sigs
, item
, next
);
2064 * \brief Free a DetectEngineCtx::
2066 * \param de_ctx DetectEngineCtx:: to be freed
2068 void DetectEngineCtxFree(DetectEngineCtx
*de_ctx
)
2075 if (de_ctx
->profile_ctx
!= NULL
) {
2076 SCProfilingRuleDestroyCtx(de_ctx
->profile_ctx
);
2077 de_ctx
->profile_ctx
= NULL
;
2079 if (de_ctx
->profile_keyword_ctx
!= NULL
) {
2080 SCProfilingKeywordDestroyCtx(de_ctx
);//->profile_keyword_ctx);
2081 // de_ctx->profile_keyword_ctx = NULL;
2083 if (de_ctx
->profile_sgh_ctx
!= NULL
) {
2084 SCProfilingSghDestroyCtx(de_ctx
);
2086 SCProfilingPrefilterDestroyCtx(de_ctx
);
2089 /* Normally the hashes are freed elsewhere, but
2090 * to be sure look at them again here.
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
);
2101 SCClassConfDeInitContext(de_ctx
);
2102 SCRConfDeInitContext(de_ctx
);
2104 SigGroupCleanup(de_ctx
);
2106 SpmDestroyGlobalThreadCtx(de_ctx
->spm_global_thread_ctx
);
2108 MpmFactoryDeRegisterAllMpmCtxProfiles(de_ctx
);
2110 DetectEngineCtxFreeThreadKeywordData(de_ctx
);
2111 SRepDestroy(de_ctx
);
2112 DetectEngineCtxFreeFailedSigs(de_ctx
);
2114 DetectAddressMapFree(de_ctx
);
2115 DetectMetadataHashFree(de_ctx
);
2117 /* if we have a config prefix, remove the config from the tree */
2118 if (strlen(de_ctx
->config_prefix
) > 0) {
2120 ConfNode
*node
= ConfGetNode(de_ctx
->config_prefix
);
2122 ConfNodeRemove(node
); /* frees node */
2129 DetectPortCleanupList(de_ctx
, de_ctx
->tcp_whitelist
);
2130 DetectPortCleanupList(de_ctx
, de_ctx
->udp_whitelist
);
2132 DetectBufferTypeFreeDetectEngine(de_ctx
);
2133 /* freed our var name hash */
2134 VarNameStoreFree(de_ctx
->version
);
2137 //DetectAddressGroupPrintMemory();
2138 //DetectSigGroupPrintMemory();
2139 //DetectPortPrintMemory();
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
2147 static int DetectEngineCtxLoadConf(DetectEngineCtx
*de_ctx
)
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
;
2155 (void)ConfGet("detect.profile", &de_ctx_profile
);
2156 (void)ConfGet("detect.sgh-mpm-context", &sgh_mpm_context
);
2158 ConfNode
*de_ctx_custom
= ConfGetNode("detect-engine");
2159 ConfNode
*opt
= NULL
;
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
;
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
;
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
;
2189 SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY
,
2190 "invalid value for detect.profile: '%s'. "
2191 "Valid options: low, medium, high and custom.",
2196 SCLogDebug("Profile for detection engine groups is \"%s\"", de_ctx_profile
);
2198 SCLogDebug("Profile for detection engine groups not provided "
2199 "at suricata.yaml. Using default (\"medium\").");
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
||
2210 de_ctx
->mpm_matcher
== MPM_AC_BS
) {
2211 de_ctx
->sgh_mpm_ctx_cnf
= ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE
;
2213 de_ctx
->sgh_mpm_ctx_cnf
= ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL
;
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
;
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
);
2228 if (run_mode
== RUNMODE_UNITTEST
) {
2229 de_ctx
->sgh_mpm_ctx_cnf
= ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL
;
2232 /* parse profile custom-values */
2235 case ENGINE_PROFILE_LOW
:
2236 de_ctx
->max_uniq_toclient_groups
= 15;
2237 de_ctx
->max_uniq_toserver_groups
= 25;
2240 case ENGINE_PROFILE_HIGH
:
2241 de_ctx
->max_uniq_toclient_groups
= 75;
2242 de_ctx
->max_uniq_toserver_groups
= 75;
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
);
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");
2258 if (max_uniq_toclient_groups_str
== NULL
) {
2259 max_uniq_toclient_groups_str
= (char *)ConfNodeLookupChildValue
2260 (opt
->head
.tqh_first
, "toclient-groups");
2262 if (max_uniq_toserver_groups_str
== NULL
) {
2263 max_uniq_toserver_groups_str
= (char *)ConfNodeLookupChildValue
2264 (opt
->head
.tqh_first
, "toserver-dp-groups");
2266 if (max_uniq_toserver_groups_str
== NULL
) {
2267 max_uniq_toserver_groups_str
= (char *)ConfNodeLookupChildValue
2268 (opt
->head
.tqh_first
, "toserver-groups");
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)
2278 de_ctx
->max_uniq_toclient_groups
= 20;
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
);
2286 de_ctx
->max_uniq_toclient_groups
= 20;
2288 SCLogConfig("toclient-groups %u", de_ctx
->max_uniq_toclient_groups
);
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)
2295 de_ctx
->max_uniq_toserver_groups
= 40;
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
);
2303 de_ctx
->max_uniq_toserver_groups
= 40;
2305 SCLogConfig("toserver-groups %u", de_ctx
->max_uniq_toserver_groups
);
2308 /* Default (or no config provided) is profile medium */
2309 case ENGINE_PROFILE_MEDIUM
:
2310 case ENGINE_PROFILE_UNKNOWN
:
2312 de_ctx
->max_uniq_toclient_groups
= 20;
2313 de_ctx
->max_uniq_toserver_groups
= 40;
2318 if (ConfGetInt("detect.inspection-recursion-limit", &value
) == 1)
2320 if (value
>= 0 && value
<= INT_MAX
) {
2321 de_ctx
->inspection_recursion_limit
= (int)value
;
2324 /* fall back to old config parsing */
2326 ConfNode
*insp_recursion_limit_node
= NULL
;
2327 char *insp_recursion_limit
= NULL
;
2329 if (de_ctx_custom
!= NULL
) {
2331 TAILQ_FOREACH(opt
, &de_ctx_custom
->head
, next
) {
2332 if (opt
->val
&& strcmp(opt
->val
, "inspection-recursion-limit") != 0)
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");
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
);
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
;
2358 de_ctx
->inspection_recursion_limit
=
2359 DETECT_ENGINE_DEFAULT_INSPECTION_RECURSION_LIMIT
;
2364 if (de_ctx
->inspection_recursion_limit
== 0)
2365 de_ctx
->inspection_recursion_limit
= -1;
2367 SCLogDebug("de_ctx->inspection_recursion_limit: %d",
2368 de_ctx
->inspection_recursion_limit
);
2370 /* parse port grouping whitelisting settings */
2372 const char *ports
= NULL
;
2373 (void)ConfGet("detect.grouping.tcp-whitelist", &ports
);
2375 SCLogConfig("grouping: tcp-whitelist %s", ports
);
2377 ports
= "53, 80, 139, 443, 445, 1433, 3306, 3389, 6666, 6667, 8080";
2378 SCLogConfig("grouping: tcp-whitelist (default) %s", ports
);
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
);
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
;
2397 (void)ConfGet("detect.grouping.udp-whitelist", &ports
);
2399 SCLogConfig("grouping: udp-whitelist %s", ports
);
2401 ports
= "53, 135, 5060";
2402 SCLogConfig("grouping: udp-whitelist (default) %s", ports
);
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
);
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
;
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
;
2428 switch (de_ctx
->prefilter_setting
) {
2429 case DETECT_PREFILTER_MPM
:
2430 SCLogConfig("prefilter engines: MPM");
2432 case DETECT_PREFILTER_AUTO
:
2433 SCLogConfig("prefilter engines: MPM and keywords");
2441 * getting & (re)setting the internal sig i
2444 //inline uint32_t DetectEngineGetMaxSigId(DetectEngineCtx *de_ctx)
2446 // return de_ctx->signum;
2449 void DetectEngineResetMaxSigId(DetectEngineCtx
*de_ctx
)
2454 static int DetectEngineThreadCtxInitGlobalKeywords(DetectEngineThreadCtx
*det_ctx
)
2456 const DetectEngineMasterCtx
*master
= &g_master_de_ctx
;
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
;
2465 det_ctx
->global_keyword_ctxs_size
= master
->keyword_id
;
2467 const DetectEngineThreadKeywordCtxItem
*item
= master
->keyword_list
;
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
;
2481 static void DetectEngineThreadCtxDeinitGlobalKeywords(DetectEngineThreadCtx
*det_ctx
)
2483 if (det_ctx
->global_keyword_ctxs_array
== NULL
||
2484 det_ctx
->global_keyword_ctxs_size
== 0) {
2488 const DetectEngineMasterCtx
*master
= &g_master_de_ctx
;
2489 if (master
->keyword_id
> 0) {
2490 const DetectEngineThreadKeywordCtxItem
*item
= master
->keyword_list
;
2492 if (det_ctx
->global_keyword_ctxs_array
[item
->id
] != NULL
)
2493 item
->FreeFunc(det_ctx
->global_keyword_ctxs_array
[item
->id
]);
2497 det_ctx
->global_keyword_ctxs_size
= 0;
2498 SCFree(det_ctx
->global_keyword_ctxs_array
);
2499 det_ctx
->global_keyword_ctxs_array
= NULL
;
2503 static int DetectEngineThreadCtxInitKeywords(DetectEngineCtx
*de_ctx
, DetectEngineThreadCtx
*det_ctx
)
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
;
2513 memset(det_ctx
->keyword_ctxs_array
, 0x00, de_ctx
->keyword_id
* sizeof(void *));
2515 det_ctx
->keyword_ctxs_size
= de_ctx
->keyword_id
;
2517 DetectEngineThreadKeywordCtxItem
*item
= de_ctx
->keyword_list
;
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
;
2531 static void DetectEngineThreadCtxDeinitKeywords(DetectEngineCtx
*de_ctx
, DetectEngineThreadCtx
*det_ctx
)
2533 if (de_ctx
->keyword_id
> 0) {
2534 DetectEngineThreadKeywordCtxItem
*item
= de_ctx
->keyword_list
;
2536 if (det_ctx
->keyword_ctxs_array
[item
->id
] != NULL
)
2537 item
->FreeFunc(det_ctx
->keyword_ctxs_array
[item
->id
]);
2541 det_ctx
->keyword_ctxs_size
= 0;
2542 SCFree(det_ctx
->keyword_ctxs_array
);
2543 det_ctx
->keyword_ctxs_array
= NULL
;
2547 /** NOTE: master MUST be locked before calling this */
2548 static TmEcode
DetectEngineThreadCtxInitForMT(ThreadVars
*tv
, DetectEngineThreadCtx
*det_ctx
)
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
;
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
;
2566 if (list
->tenant_id
> max_tenant_id
)
2567 max_tenant_id
= list
->tenant_id
;
2573 mt_det_ctxs_hash
= HashTableInit(tcnt
* 2, TenantIdHash
, TenantIdCompare
, TenantIdFree
);
2574 if (mt_det_ctxs_hash
== NULL
) {
2579 SCLogInfo("no tenants left, or none registered yet");
2583 DetectEngineTenantMapping
*map
= master
->tenant_mapping_list
;
2590 map_array_size
= map_cnt
+ 1;
2592 map_array
= SCCalloc(map_array_size
, sizeof(*map_array
));
2593 if (map_array
== NULL
)
2596 /* fill the array */
2598 map
= master
->tenant_mapping_list
;
2600 if (map_cnt
>= map_array_size
) {
2603 map_array
[map_cnt
].traffic_id
= map
->traffic_id
;
2604 map_array
[map_cnt
].tenant_id
= map
->tenant_id
;
2611 /* set up hash for tenant lookup */
2612 list
= master
->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
)
2619 if (HashTableAdd(mt_det_ctxs_hash
, mt_det_ctx
, 0) != 0) {
2627 det_ctx
->mt_det_ctxs_hash
= mt_det_ctxs_hash
;
2628 mt_det_ctxs_hash
= NULL
;
2630 det_ctx
->mt_det_ctxs_cnt
= max_tenant_id
;
2632 det_ctx
->tenant_array
= map_array
;
2633 det_ctx
->tenant_array_size
= map_array_size
;
2635 switch (master
->tenant_selector
) {
2636 case TENANT_SELECTOR_UNKNOWN
:
2637 SCLogDebug("TENANT_SELECTOR_UNKNOWN");
2639 case TENANT_SELECTOR_VLAN
:
2640 det_ctx
->TenantGetId
= DetectEngineTentantGetIdFromVlanId
;
2641 SCLogDebug("TENANT_SELECTOR_VLAN");
2643 case TENANT_SELECTOR_LIVEDEV
:
2644 det_ctx
->TenantGetId
= DetectEngineTentantGetIdFromLivedev
;
2645 SCLogDebug("TENANT_SELECTOR_LIVEDEV");
2647 case TENANT_SELECTOR_DIRECT
:
2648 det_ctx
->TenantGetId
= DetectEngineTentantGetIdFromPcap
;
2649 SCLogDebug("TENANT_SELECTOR_DIRECT");
2655 if (map_array
!= NULL
)
2657 if (mt_det_ctxs_hash
!= NULL
)
2658 HashTableFree(mt_det_ctxs_hash
);
2660 return TM_ECODE_FAILED
;
2664 * \brief Helper for DetectThread setup functions
2666 static TmEcode
ThreadCtxDoInit (DetectEngineCtx
*de_ctx
, DetectEngineThreadCtx
*det_ctx
)
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
);
2672 PmqSetup(&det_ctx
->pmq
);
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
;
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
);
2687 DetectEngineIPOnlyThreadInit(de_ctx
,&det_ctx
->io_ctx
);
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
;
2696 memset(det_ctx
->match_array
, 0,
2697 det_ctx
->match_array_len
* sizeof(Signature
*));
2699 RuleMatchCandidateTxArrayInit(det_ctx
, de_ctx
->sig_array_len
);
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
;
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
;
2715 det_ctx
->base64_decoded_len_max
= de_ctx
->base64_decode_max_len
;
2716 det_ctx
->base64_decoded_len
= 0;
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
;
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
;
2728 det_ctx
->inspect
.to_clear_idx
= 0;
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
;
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
;
2739 det_ctx
->multi_inspect
.to_clear_idx
= 0;
2742 DetectEngineThreadCtxInitKeywords(de_ctx
, det_ctx
);
2743 DetectEngineThreadCtxInitGlobalKeywords(det_ctx
);
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
);
2750 SC_ATOMIC_INIT(det_ctx
->so_far_used_by_detect
);
2755 /** \brief initialize thread specific detection engine context
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.
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
2769 * \retval TM_ECODE_OK if all went well
2770 * \retval TM_ECODE_FAILED on serious errors
2772 TmEcode
DetectEngineThreadCtxInit(ThreadVars
*tv
, void *initdata
, void **data
)
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
));
2780 det_ctx
->de_ctx
= DetectEngineGetCurrent();
2781 if (det_ctx
->de_ctx
== NULL
) {
2783 if (RunmodeIsUnittests()) {
2784 det_ctx
->de_ctx
= (DetectEngineCtx
*)initdata
;
2786 DetectEngineThreadCtxDeinit(tv
, det_ctx
);
2787 return TM_ECODE_FAILED
;
2790 DetectEngineThreadCtxDeinit(tv
, det_ctx
);
2791 return TM_ECODE_FAILED
;
2795 if (det_ctx
->de_ctx
->type
== DETECT_ENGINE_TYPE_NORMAL
||
2796 det_ctx
->de_ctx
->type
== DETECT_ENGINE_TYPE_TENANT
)
2798 if (ThreadCtxDoInit(det_ctx
->de_ctx
, det_ctx
) != TM_ECODE_OK
) {
2799 DetectEngineThreadCtxDeinit(tv
, det_ctx
);
2800 return TM_ECODE_FAILED
;
2804 /** alert counter setup */
2805 det_ctx
->counter_alerts
= StatsRegisterCounter("detect.alert", tv
);
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
);
2813 if (DetectEngineMultiTenantEnabled()) {
2814 if (DetectEngineThreadCtxInitForMT(tv
, det_ctx
) != TM_ECODE_OK
) {
2815 DetectEngineThreadCtxDeinit(tv
, det_ctx
);
2816 return TM_ECODE_FAILED
;
2820 /* pass thread data back to caller */
2821 *data
= (void *)det_ctx
;
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
2833 * \retval det_ctx detection engine thread ctx or NULL in case of error
2835 DetectEngineThreadCtx
*DetectEngineThreadCtxInitForReload(
2836 ThreadVars
*tv
, DetectEngineCtx
*new_de_ctx
, int mt
)
2838 DetectEngineThreadCtx
*det_ctx
= SCMalloc(sizeof(DetectEngineThreadCtx
));
2839 if (unlikely(det_ctx
== NULL
))
2841 memset(det_ctx
, 0, sizeof(DetectEngineThreadCtx
));
2843 det_ctx
->tenant_id
= new_de_ctx
->tenant_id
;
2845 det_ctx
->de_ctx
= DetectEngineReference(new_de_ctx
);
2846 if (det_ctx
->de_ctx
== NULL
) {
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
)
2855 if (ThreadCtxDoInit(det_ctx
->de_ctx
, det_ctx
) != TM_ECODE_OK
) {
2856 DetectEngineDeReference(&det_ctx
->de_ctx
);
2862 /** alert counter setup */
2863 det_ctx
->counter_alerts
= StatsRegisterCounter("detect.alert", tv
);
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
;
2875 if (mt
&& DetectEngineMultiTenantEnabled()) {
2876 if (DetectEngineThreadCtxInitForMT(tv
, det_ctx
) != TM_ECODE_OK
) {
2877 DetectEngineDeReference(&det_ctx
->de_ctx
);
2886 static void DetectEngineThreadCtxFree(DetectEngineThreadCtx
*det_ctx
)
2889 SCLogDebug("PACKET PKT_STREAM_ADD: %"PRIu64
, det_ctx
->pkt_stream_add_cnt
);
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
);
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
);
2898 if (det_ctx
->tenant_array
!= NULL
) {
2899 SCFree(det_ctx
->tenant_array
);
2900 det_ctx
->tenant_array
= NULL
;
2904 SCProfilingRuleThreadCleanup(det_ctx
);
2905 SCProfilingKeywordThreadCleanup(det_ctx
);
2906 SCProfilingPrefilterThreadCleanup(det_ctx
);
2907 SCProfilingSghThreadCleanup(det_ctx
);
2910 DetectEngineIPOnlyThreadDeinit(&det_ctx
->io_ctx
);
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
);
2919 PmqFree(&det_ctx
->pmq
);
2921 if (det_ctx
->spm_thread_ctx
!= NULL
) {
2922 SpmDestroyThreadCtx(det_ctx
->spm_thread_ctx
);
2925 if (det_ctx
->non_pf_id_array
!= NULL
)
2926 SCFree(det_ctx
->non_pf_id_array
);
2928 if (det_ctx
->match_array
!= NULL
)
2929 SCFree(det_ctx
->match_array
);
2931 RuleMatchCandidateTxArrayFree(det_ctx
);
2933 if (det_ctx
->byte_values
!= NULL
)
2934 SCFree(det_ctx
->byte_values
);
2936 /* Decoded base64 data. */
2937 if (det_ctx
->base64_decoded
!= NULL
) {
2938 SCFree(det_ctx
->base64_decoded
);
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
]);
2945 SCFree(det_ctx
->inspect
.buffers
);
2947 if (det_ctx
->inspect
.to_clear_queue
) {
2948 SCFree(det_ctx
->inspect
.to_clear_queue
);
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
]);
2956 SCFree(fb
->inspection_buffers
);
2958 SCFree(det_ctx
->multi_inspect
.buffers
);
2960 if (det_ctx
->multi_inspect
.to_clear_queue
) {
2961 SCFree(det_ctx
->multi_inspect
.to_clear_queue
);
2964 DetectEngineThreadCtxDeinitGlobalKeywords(det_ctx
);
2965 if (det_ctx
->de_ctx
!= NULL
) {
2966 DetectEngineThreadCtxDeinitKeywords(det_ctx
->de_ctx
, det_ctx
);
2968 if (!RunmodeIsUnittests() || det_ctx
->de_ctx
->ref_cnt
> 0)
2969 DetectEngineDeReference(&det_ctx
->de_ctx
);
2971 DetectEngineDeReference(&det_ctx
->de_ctx
);
2975 AppLayerDecoderEventsFreeEvents(&det_ctx
->decoder_events
);
2980 TmEcode
DetectEngineThreadCtxDeinit(ThreadVars
*tv
, void *data
)
2982 DetectEngineThreadCtx
*det_ctx
= (DetectEngineThreadCtx
*)data
;
2984 if (det_ctx
== NULL
) {
2985 SCLogWarning(SC_ERR_INVALID_ARGUMENTS
, "argument \"data\" NULL");
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
;
2993 DetectEngineThreadCtxFree(det_ctx
);
2998 void DetectEngineThreadCtxInfo(ThreadVars
*t
, DetectEngineThreadCtx
*det_ctx
)
3001 PatternMatchThreadPrint(&det_ctx
->mtc
, det_ctx
->de_ctx
->mpm_matcher
);
3002 PatternMatchThreadPrint(&det_ctx
->mtcu
, det_ctx
->de_ctx
->mpm_matcher
);
3005 /** \brief Register Thread keyword context Funcs
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)
3014 * \retval id for retrieval of ctx at runtime
3015 * \retval -1 on error
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.
3021 int DetectRegisterThreadCtxFuncs(DetectEngineCtx
*de_ctx
, const char *name
, void *(*InitFunc
)(void *), void *data
, void (*FreeFunc
)(void *), int mode
)
3023 BUG_ON(de_ctx
== NULL
|| InitFunc
== NULL
|| FreeFunc
== NULL
);
3026 DetectEngineThreadKeywordCtxItem
*item
= de_ctx
->keyword_list
;
3027 while (item
!= NULL
) {
3028 if (strcmp(name
, item
->name
) == 0) {
3036 DetectEngineThreadKeywordCtxItem
*item
= SCMalloc(sizeof(DetectEngineThreadKeywordCtxItem
));
3037 if (unlikely(item
== NULL
))
3039 memset(item
, 0x00, sizeof(DetectEngineThreadKeywordCtxItem
));
3041 item
->InitFunc
= InitFunc
;
3042 item
->FreeFunc
= FreeFunc
;
3046 item
->next
= de_ctx
->keyword_list
;
3047 de_ctx
->keyword_list
= item
;
3048 item
->id
= de_ctx
->keyword_id
++;
3053 /** \brief Remove Thread keyword context registration
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
3060 * \retval 1 Item unregistered
3061 * \retval 0 otherwise
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.
3067 int DetectUnregisterThreadCtxFuncs(DetectEngineCtx
*de_ctx
,
3068 DetectEngineThreadCtx
*det_ctx
, void *data
, const char *name
)
3070 BUG_ON(de_ctx
== NULL
);
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
;
3079 prev_item
->next
= item
->next
;
3081 item
->FreeFunc(det_ctx
->keyword_ctxs_array
[item
->id
]);
3090 /** \brief Retrieve thread local keyword ctx by id
3092 * \param det_ctx detection engine thread ctx to retrieve the ctx from
3093 * \param id id of the ctx returned by DetectRegisterThreadCtxInitFunc at
3096 * \retval ctx or NULL on error
3098 void *DetectThreadCtxGetKeywordThreadCtx(DetectEngineThreadCtx
*det_ctx
, int id
)
3100 if (id
< 0 || id
> det_ctx
->keyword_ctxs_size
|| det_ctx
->keyword_ctxs_array
== NULL
)
3103 return det_ctx
->keyword_ctxs_array
[id
];
3107 /** \brief Register Thread keyword context Funcs (Global)
3109 * IDs stay static over reloads and between tenants
3111 * \param name keyword name for error printing
3112 * \param InitFunc function ptr
3113 * \param FreeFunc function ptr
3115 * \retval id for retrieval of ctx at runtime
3116 * \retval -1 on error
3118 int DetectRegisterThreadCtxGlobalFuncs(const char *name
,
3119 void *(*InitFunc
)(void *), void *data
, void (*FreeFunc
)(void *))
3122 BUG_ON(InitFunc
== NULL
|| FreeFunc
== NULL
);
3124 DetectEngineMasterCtx
*master
= &g_master_de_ctx
;
3126 /* if already registered, return existing id */
3127 DetectEngineThreadKeywordCtxItem
*item
= master
->keyword_list
;
3128 while (item
!= NULL
) {
3129 if (strcmp(name
, item
->name
) == 0) {
3137 item
= SCCalloc(1, sizeof(*item
));
3138 if (unlikely(item
== NULL
)) {
3141 item
->InitFunc
= InitFunc
;
3142 item
->FreeFunc
= FreeFunc
;
3146 item
->next
= master
->keyword_list
;
3147 master
->keyword_list
= item
;
3148 item
->id
= master
->keyword_id
++;
3154 /** \brief Retrieve thread local keyword ctx by id
3156 * \param det_ctx detection engine thread ctx to retrieve the ctx from
3157 * \param id id of the ctx returned by DetectRegisterThreadCtxInitFunc at
3160 * \retval ctx or NULL on error
3162 void *DetectThreadCtxGetGlobalKeywordThreadCtx(DetectEngineThreadCtx
*det_ctx
, int id
)
3164 if (id
< 0 || id
> det_ctx
->global_keyword_ctxs_size
||
3165 det_ctx
->global_keyword_ctxs_array
== NULL
) {
3169 return det_ctx
->global_keyword_ctxs_array
[id
];
3172 /** \brief Check if detection is enabled
3173 * \retval bool true or false */
3174 int DetectEngineEnabled(void)
3176 DetectEngineMasterCtx
*master
= &g_master_de_ctx
;
3177 SCMutexLock(&master
->lock
);
3179 if (master
->list
== NULL
) {
3180 SCMutexUnlock(&master
->lock
);
3184 SCMutexUnlock(&master
->lock
);
3188 uint32_t DetectEngineGetVersion(void)
3191 DetectEngineMasterCtx
*master
= &g_master_de_ctx
;
3192 SCMutexLock(&master
->lock
);
3193 version
= master
->version
;
3194 SCMutexUnlock(&master
->lock
);
3198 void DetectEngineBumpVersion(void)
3200 DetectEngineMasterCtx
*master
= &g_master_de_ctx
;
3201 SCMutexLock(&master
->lock
);
3203 SCLogDebug("master version now %u", master
->version
);
3204 SCMutexUnlock(&master
->lock
);
3207 DetectEngineCtx
*DetectEngineGetCurrent(void)
3209 DetectEngineMasterCtx
*master
= &g_master_de_ctx
;
3210 SCMutexLock(&master
->lock
);
3212 DetectEngineCtx
*de_ctx
= master
->list
;
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
)
3219 SCLogDebug("de_ctx %p ref_cnt %u", de_ctx
, de_ctx
->ref_cnt
);
3220 SCMutexUnlock(&master
->lock
);
3223 de_ctx
= de_ctx
->next
;
3226 SCMutexUnlock(&master
->lock
);
3230 DetectEngineCtx
*DetectEngineReference(DetectEngineCtx
*de_ctx
)
3238 /** TODO locking? Not needed if this is a one time setting at startup */
3239 int DetectEngineMultiTenantEnabled(void)
3241 DetectEngineMasterCtx
*master
= &g_master_de_ctx
;
3242 return (master
->multi_tenant_enabled
);
3246 * \brief load a tenant from a yaml file
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
3255 static int DetectEngineMultiTenantLoadTenant(uint32_t tenant_id
, const char *filename
, int loader_id
)
3257 DetectEngineCtx
*de_ctx
= NULL
;
3260 snprintf(prefix
, sizeof(prefix
), "multi-detect.%d", tenant_id
);
3264 if(_stat(filename
, &st
) != 0) {
3267 if(stat(filename
, &st
) != 0) {
3268 #endif /* OS_WIN32 */
3269 SCLogError(SC_ERR_FOPEN
, "failed to stat file %s", filename
);
3273 de_ctx
= DetectEngineGetByTenantId(tenant_id
);
3274 if (de_ctx
!= NULL
) {
3275 SCLogError(SC_ERR_MT_DUPLICATE_TENANT
, "tenant %u already registered",
3277 DetectEngineDeReference(&de_ctx
);
3281 ConfNode
*node
= ConfGetNode(prefix
);
3283 SCLogError(SC_ERR_CONF_YAML_ERROR
, "failed to properly setup yaml %s", filename
);
3287 de_ctx
= DetectEngineCtxInitWithPrefix(prefix
);
3288 if (de_ctx
== NULL
) {
3289 SCLogError(SC_ERR_INITIALIZATION
, "initializing detection engine "
3293 SCLogDebug("de_ctx %p with prefix %s", de_ctx
, de_ctx
->config_prefix
);
3295 de_ctx
->type
= DETECT_ENGINE_TYPE_TENANT
;
3296 de_ctx
->tenant_id
= tenant_id
;
3297 de_ctx
->loader_id
= loader_id
;
3299 if (SigLoadSignatures(de_ctx
, NULL
, 0) < 0) {
3300 SCLogError(SC_ERR_NO_RULES_LOADED
, "Loading signatures failed.");
3304 DetectEngineAddToMaster(de_ctx
);
3309 if (de_ctx
!= NULL
) {
3310 DetectEngineCtxFree(de_ctx
);
3315 static int DetectEngineMultiTenantReloadTenant(uint32_t tenant_id
, const char *filename
, int reload_cnt
)
3317 DetectEngineCtx
*old_de_ctx
= DetectEngineGetByTenantId(tenant_id
);
3318 if (old_de_ctx
== NULL
) {
3319 SCLogError(SC_ERR_INITIALIZATION
, "tenant detect engine not found");
3324 snprintf(prefix
, sizeof(prefix
), "multi-detect.%d.reload.%d", tenant_id
, reload_cnt
);
3326 SCLogDebug("prefix %s", prefix
);
3328 if (ConfYamlLoadFileWithPrefix(filename
, prefix
) != 0) {
3329 SCLogError(SC_ERR_INITIALIZATION
,"failed to load yaml");
3333 ConfNode
*node
= ConfGetNode(prefix
);
3335 SCLogError(SC_ERR_CONF_YAML_ERROR
, "failed to properly setup yaml %s", filename
);
3339 DetectEngineCtx
*new_de_ctx
= DetectEngineCtxInitWithPrefix(prefix
);
3340 if (new_de_ctx
== NULL
) {
3341 SCLogError(SC_ERR_INITIALIZATION
, "initializing detection engine "
3345 SCLogDebug("de_ctx %p with prefix %s", new_de_ctx
, new_de_ctx
->config_prefix
);
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
;
3351 if (SigLoadSignatures(new_de_ctx
, NULL
, 0) < 0) {
3352 SCLogError(SC_ERR_NO_RULES_LOADED
, "Loading signatures failed.");
3356 DetectEngineAddToMaster(new_de_ctx
);
3358 /* move to free list */
3359 DetectEngineMoveToFreeList(old_de_ctx
);
3360 DetectEngineDeReference(&old_de_ctx
);
3364 DetectEngineDeReference(&old_de_ctx
);
3369 typedef struct TenantLoaderCtx_
{
3371 int reload_cnt
; /**< used by reload */
3375 static int DetectLoaderFuncLoadTenant(void *vctx
, int loader_id
)
3377 TenantLoaderCtx
*ctx
= (TenantLoaderCtx
*)vctx
;
3379 SCLogDebug("loader %d", loader_id
);
3380 if (DetectEngineMultiTenantLoadTenant(ctx
->tenant_id
, ctx
->yaml
, loader_id
) != 0) {
3386 static int DetectLoaderSetupLoadTenant(uint32_t tenant_id
, const char *yaml
)
3388 TenantLoaderCtx
*t
= SCCalloc(1, sizeof(*t
));
3392 t
->tenant_id
= tenant_id
;
3395 return DetectLoaderQueueTask(-1, DetectLoaderFuncLoadTenant
, t
);
3398 static int DetectLoaderFuncReloadTenant(void *vctx
, int loader_id
)
3400 TenantLoaderCtx
*ctx
= (TenantLoaderCtx
*)vctx
;
3402 SCLogDebug("loader_id %d", loader_id
);
3404 if (DetectEngineMultiTenantReloadTenant(ctx
->tenant_id
, ctx
->yaml
, ctx
->reload_cnt
) != 0) {
3410 static int DetectLoaderSetupReloadTenant(uint32_t tenant_id
, const char *yaml
, int reload_cnt
)
3412 DetectEngineCtx
*old_de_ctx
= DetectEngineGetByTenantId(tenant_id
);
3413 if (old_de_ctx
== NULL
)
3415 int loader_id
= old_de_ctx
->loader_id
;
3416 DetectEngineDeReference(&old_de_ctx
);
3418 TenantLoaderCtx
*t
= SCCalloc(1, sizeof(*t
));
3422 t
->tenant_id
= tenant_id
;
3424 t
->reload_cnt
= reload_cnt
;
3426 SCLogDebug("loader_id %d", loader_id
);
3428 return DetectLoaderQueueTask(loader_id
, DetectLoaderFuncReloadTenant
, t
);
3431 /** \brief Load a tenant and wait for loading to complete
3433 int DetectEngineLoadTenantBlocking(uint32_t tenant_id
, const char *yaml
)
3435 int r
= DetectLoaderSetupLoadTenant(tenant_id
, yaml
);
3439 if (DetectLoadersSync() != 0)
3445 /** \brief Reload a tenant and wait for loading to complete
3447 int DetectEngineReloadTenantBlocking(uint32_t tenant_id
, const char *yaml
, int reload_cnt
)
3449 int r
= DetectLoaderSetupReloadTenant(tenant_id
, yaml
, reload_cnt
);
3453 if (DetectLoadersSync() != 0)
3459 static int DetectEngineMultiTenantSetupLoadLivedevMappings(const ConfNode
*mappings_root_node
,
3462 ConfNode
*mapping_node
= NULL
;
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
)
3470 ConfNode
*device_node
= ConfNodeLookupChild(mapping_node
, "device");
3471 if (device_node
== NULL
)
3474 uint32_t tenant_id
= 0;
3475 if (StringParseUint32(&tenant_id
, 10, strlen(tenant_id_node
->val
),
3476 tenant_id_node
->val
) < 0)
3478 SCLogError(SC_ERR_INVALID_ARGUMENT
, "tenant-id "
3479 "of %s is invalid", tenant_id_node
->val
);
3483 const char *dev
= device_node
->val
;
3484 LiveDevice
*ld
= LiveGetDevice(dev
);
3486 SCLogWarning(SC_ERR_MT_NO_MAPPING
, "device %s not found", dev
);
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
);
3496 ld
->tenant_id
= tenant_id
;
3497 ld
->tenant_id_set
= true;
3499 if (DetectEngineTentantRegisterLivedev(tenant_id
, ld
->id
) != 0) {
3503 SCLogConfig("device %s connected to tenant-id %u", dev
, tenant_id
);
3512 SCLogConfig("%d device - tenant-id mappings defined", mapping_cnt
);
3519 static int DetectEngineMultiTenantSetupLoadVlanMappings(const ConfNode
*mappings_root_node
,
3522 ConfNode
*mapping_node
= NULL
;
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
)
3530 ConfNode
*vlan_id_node
= ConfNodeLookupChild(mapping_node
, "vlan-id");
3531 if (vlan_id_node
== NULL
)
3534 uint32_t tenant_id
= 0;
3535 if (StringParseUint32(&tenant_id
, 10, strlen(tenant_id_node
->val
),
3536 tenant_id_node
->val
) < 0)
3538 SCLogError(SC_ERR_INVALID_ARGUMENT
, "tenant-id "
3539 "of %s is invalid", tenant_id_node
->val
);
3543 uint16_t vlan_id
= 0;
3544 if (StringParseUint16(&vlan_id
, 10, strlen(vlan_id_node
->val
),
3545 vlan_id_node
->val
) < 0)
3547 SCLogError(SC_ERR_INVALID_ARGUMENT
, "vlan-id "
3548 "of %s is invalid", vlan_id_node
->val
);
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
);
3557 if (DetectEngineTentantRegisterVlanId(tenant_id
, (uint32_t)vlan_id
) != 0) {
3560 SCLogConfig("vlan %u connected to tenant-id %u", vlan_id
, tenant_id
);
3576 * \brief setup multi-detect / multi-tenancy
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.
3582 int DetectEngineMultiTenantSetup(void)
3584 enum DetectEngineTenantSelectors tenant_selector
= TENANT_SELECTOR_UNKNOWN
;
3585 DetectEngineMasterCtx
*master
= &g_master_de_ctx
;
3587 int unix_socket
= ConfUnixSocketIsEnable();
3589 int failure_fatal
= 0;
3590 (void)ConfGetBool("engine.init-failure-fatal", &failure_fatal
);
3593 (void)ConfGetBool("multi-detect.enabled", &enabled
);
3595 DetectLoadersInit();
3596 TmModuleDetectLoaderRegister();
3597 DetectLoaderThreadSpawn();
3598 TmThreadContinueDetectLoaderThreads();
3600 SCMutexLock(&master
->lock
);
3601 master
->multi_tenant_enabled
= 1;
3603 const char *handler
= NULL
;
3604 if (ConfGet("multi-detect.selector", &handler
) == 1) {
3605 SCLogConfig("multi-tenant selector type %s", handler
);
3607 if (strcmp(handler
, "vlan") == 0) {
3608 tenant_selector
= master
->tenant_selector
= TENANT_SELECTOR_VLAN
;
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
);
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
);
3630 SCLogError(SC_ERR_INVALID_VALUE
, "unknown value %s "
3631 "multi-detect.selector", handler
);
3632 SCMutexUnlock(&master
->lock
);
3636 SCMutexUnlock(&master
->lock
);
3637 SCLogConfig("multi-detect is enabled (multi tenancy). Selector: %s", handler
);
3639 /* traffic -- tenant mappings */
3640 ConfNode
*mappings_root_node
= ConfGetNode("multi-detect.mappings");
3642 if (tenant_selector
== TENANT_SELECTOR_VLAN
) {
3643 int mapping_cnt
= DetectEngineMultiTenantSetupLoadVlanMappings(mappings_root_node
,
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 */
3651 SCLogNotice("no tenant traffic mappings defined, "
3652 "tenants won't be used until mappings are added");
3654 if (failure_fatal
) {
3655 SCLogError(SC_ERR_MT_NO_MAPPING
, "no multi-detect mappings defined");
3658 SCLogWarning(SC_ERR_MT_NO_MAPPING
, "no multi-detect mappings defined");
3662 } else if (tenant_selector
== TENANT_SELECTOR_LIVEDEV
) {
3663 int mapping_cnt
= DetectEngineMultiTenantSetupLoadLivedevMappings(mappings_root_node
,
3665 if (mapping_cnt
== 0) {
3666 if (failure_fatal
) {
3667 SCLogError(SC_ERR_MT_NO_MAPPING
, "no multi-detect mappings defined");
3670 SCLogWarning(SC_ERR_MT_NO_MAPPING
, "no multi-detect mappings defined");
3676 ConfNode
*tenants_root_node
= ConfGetNode("multi-detect.tenants");
3677 ConfNode
*tenant_node
= NULL
;
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
) {
3685 ConfNode
*yaml_node
= ConfNodeLookupChild(tenant_node
, "yaml");
3686 if (yaml_node
== NULL
) {
3690 uint32_t tenant_id
= 0;
3691 if (StringParseUint32(&tenant_id
, 10, strlen(id_node
->val
),
3694 SCLogError(SC_ERR_INVALID_ARGUMENT
, "tenant_id "
3695 "of %s is invalid", id_node
->val
);
3698 SCLogDebug("tenant id: %u, %s", tenant_id
, yaml_node
->val
);
3700 /* setup the yaml in this loop so that it's not done by the loader
3701 * threads. ConfYamlLoadFileWithPrefix is not thread safe. */
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
);
3709 int r
= DetectLoaderSetupLoadTenant(tenant_id
, yaml_node
->val
);
3711 /* error logged already */
3722 /* wait for our loaders to complete their tasks */
3723 if (DetectLoadersSync() != 0) {
3727 VarNameStoreActivateStaging();
3730 SCLogDebug("multi-detect not enabled (multi tenancy)");
3737 static uint32_t DetectEngineTentantGetIdFromVlanId(const void *ctx
, const Packet
*p
)
3739 const DetectEngineThreadCtx
*det_ctx
= ctx
;
3741 uint32_t vlan_id
= 0;
3743 if (p
->vlan_idx
== 0)
3746 vlan_id
= p
->vlan_id
[0];
3748 if (det_ctx
== NULL
|| det_ctx
->tenant_array
== NULL
|| det_ctx
->tenant_array_size
== 0)
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
;
3761 static uint32_t DetectEngineTentantGetIdFromLivedev(const void *ctx
, const Packet
*p
)
3763 const DetectEngineThreadCtx
*det_ctx
= ctx
;
3764 const LiveDevice
*ld
= p
->livedev
;
3766 if (ld
== NULL
|| det_ctx
== NULL
)
3769 SCLogDebug("using tenant-id %u for packet on device %s", ld
->tenant_id
, ld
->dev
);
3770 return ld
->tenant_id
;
3773 static int DetectEngineTentantRegisterSelector(enum DetectEngineTenantSelectors selector
,
3774 uint32_t tenant_id
, uint32_t traffic_id
)
3776 DetectEngineMasterCtx
*master
= &g_master_de_ctx
;
3777 SCMutexLock(&master
->lock
);
3779 if (!(master
->tenant_selector
== TENANT_SELECTOR_UNKNOWN
|| master
->tenant_selector
== selector
)) {
3780 SCLogInfo("conflicting selector already set");
3781 SCMutexUnlock(&master
->lock
);
3785 DetectEngineTenantMapping
*m
= master
->tenant_mapping_list
;
3787 if (m
->traffic_id
== traffic_id
) {
3788 SCLogInfo("traffic id already registered");
3789 SCMutexUnlock(&master
->lock
);
3795 DetectEngineTenantMapping
*map
= SCCalloc(1, sizeof(*map
));
3797 SCLogInfo("memory fail");
3798 SCMutexUnlock(&master
->lock
);
3801 map
->traffic_id
= traffic_id
;
3802 map
->tenant_id
= tenant_id
;
3804 map
->next
= master
->tenant_mapping_list
;
3805 master
->tenant_mapping_list
= map
;
3807 master
->tenant_selector
= selector
;
3809 SCLogDebug("tenant handler %u %u %u registered", selector
, tenant_id
, traffic_id
);
3810 SCMutexUnlock(&master
->lock
);
3814 static int DetectEngineTentantUnregisterSelector(enum DetectEngineTenantSelectors selector
,
3815 uint32_t tenant_id
, uint32_t traffic_id
)
3817 DetectEngineMasterCtx
*master
= &g_master_de_ctx
;
3818 SCMutexLock(&master
->lock
);
3820 if (master
->tenant_mapping_list
== NULL
) {
3821 SCMutexUnlock(&master
->lock
);
3825 DetectEngineTenantMapping
*prev
= NULL
;
3826 DetectEngineTenantMapping
*map
= master
->tenant_mapping_list
;
3828 if (map
->traffic_id
== traffic_id
&&
3829 map
->tenant_id
== tenant_id
)
3832 prev
->next
= map
->next
;
3834 master
->tenant_mapping_list
= map
->next
;
3838 SCLogInfo("tenant handler %u %u %u unregistered", selector
, tenant_id
, traffic_id
);
3839 SCMutexUnlock(&master
->lock
);
3846 SCMutexUnlock(&master
->lock
);
3850 int DetectEngineTentantRegisterLivedev(uint32_t tenant_id
, int device_id
)
3852 return DetectEngineTentantRegisterSelector(TENANT_SELECTOR_LIVEDEV
, tenant_id
, (uint32_t)device_id
);
3855 int DetectEngineTentantRegisterVlanId(uint32_t tenant_id
, uint16_t vlan_id
)
3857 return DetectEngineTentantRegisterSelector(TENANT_SELECTOR_VLAN
, tenant_id
, (uint32_t)vlan_id
);
3860 int DetectEngineTentantUnregisterVlanId(uint32_t tenant_id
, uint16_t vlan_id
)
3862 return DetectEngineTentantUnregisterSelector(TENANT_SELECTOR_VLAN
, tenant_id
, (uint32_t)vlan_id
);
3865 int DetectEngineTentantRegisterPcapFile(uint32_t tenant_id
)
3867 SCLogInfo("registering %u %d 0", TENANT_SELECTOR_DIRECT
, tenant_id
);
3868 return DetectEngineTentantRegisterSelector(TENANT_SELECTOR_DIRECT
, tenant_id
, 0);
3871 int DetectEngineTentantUnregisterPcapFile(uint32_t tenant_id
)
3873 SCLogInfo("unregistering %u %d 0", TENANT_SELECTOR_DIRECT
, tenant_id
);
3874 return DetectEngineTentantUnregisterSelector(TENANT_SELECTOR_DIRECT
, tenant_id
, 0);
3877 static uint32_t DetectEngineTentantGetIdFromPcap(const void *ctx
, const Packet
*p
)
3879 return p
->pcap_v
.tenant_id
;
3882 DetectEngineCtx
*DetectEngineGetByTenantId(int tenant_id
)
3884 DetectEngineMasterCtx
*master
= &g_master_de_ctx
;
3885 SCMutexLock(&master
->lock
);
3887 if (master
->list
== NULL
) {
3888 SCMutexUnlock(&master
->lock
);
3892 DetectEngineCtx
*de_ctx
= master
->list
;
3894 if (de_ctx
->type
== DETECT_ENGINE_TYPE_TENANT
&&
3895 de_ctx
->tenant_id
== tenant_id
)
3901 de_ctx
= de_ctx
->next
;
3904 SCMutexUnlock(&master
->lock
);
3908 void DetectEngineDeReference(DetectEngineCtx
**de_ctx
)
3910 BUG_ON((*de_ctx
)->ref_cnt
== 0);
3911 (*de_ctx
)->ref_cnt
--;
3915 static int DetectEngineAddToList(DetectEngineCtx
*instance
)
3917 DetectEngineMasterCtx
*master
= &g_master_de_ctx
;
3919 if (instance
== NULL
)
3922 if (master
->list
== NULL
) {
3923 master
->list
= instance
;
3925 instance
->next
= master
->list
;
3926 master
->list
= instance
;
3932 int DetectEngineAddToMaster(DetectEngineCtx
*de_ctx
)
3939 SCLogDebug("adding de_ctx %p to master", de_ctx
);
3941 DetectEngineMasterCtx
*master
= &g_master_de_ctx
;
3942 SCMutexLock(&master
->lock
);
3943 r
= DetectEngineAddToList(de_ctx
);
3944 SCMutexUnlock(&master
->lock
);
3948 int DetectEngineMoveToFreeList(DetectEngineCtx
*de_ctx
)
3950 DetectEngineMasterCtx
*master
= &g_master_de_ctx
;
3952 SCMutexLock(&master
->lock
);
3953 DetectEngineCtx
*instance
= master
->list
;
3954 if (instance
== NULL
) {
3955 SCMutexUnlock(&master
->lock
);
3959 /* remove from active list */
3960 if (instance
== de_ctx
) {
3961 master
->list
= instance
->next
;
3963 DetectEngineCtx
*prev
= instance
;
3964 instance
= instance
->next
; /* already checked first element */
3967 DetectEngineCtx
*next
= instance
->next
;
3969 if (instance
== de_ctx
) {
3970 prev
->next
= instance
->next
;
3977 if (instance
== NULL
) {
3978 SCMutexUnlock(&master
->lock
);
3983 /* instance is now detached from list */
3984 instance
->next
= NULL
;
3986 /* add to free list */
3987 if (master
->free_list
== NULL
) {
3988 master
->free_list
= instance
;
3990 instance
->next
= master
->free_list
;
3991 master
->free_list
= instance
;
3993 SCLogDebug("detect engine %p moved to free list (%u refs)", de_ctx
, de_ctx
->ref_cnt
);
3995 SCMutexUnlock(&master
->lock
);
3999 void DetectEnginePruneFreeList(void)
4001 DetectEngineMasterCtx
*master
= &g_master_de_ctx
;
4002 SCMutexLock(&master
->lock
);
4004 DetectEngineCtx
*prev
= NULL
;
4005 DetectEngineCtx
*instance
= master
->free_list
;
4007 DetectEngineCtx
*next
= instance
->next
;
4009 SCLogDebug("detect engine %p has %u ref(s)", instance
, instance
->ref_cnt
);
4011 if (instance
->ref_cnt
== 0) {
4013 master
->free_list
= next
;
4018 SCLogDebug("freeing detect engine %p", instance
);
4019 DetectEngineCtxFree(instance
);
4026 SCMutexUnlock(&master
->lock
);
4029 static int reloads
= 0;
4031 /** \brief Reload the detection engine
4033 * \param filename YAML file to load for the detect config
4038 int DetectEngineReload(const SCInstance
*suri
)
4040 DetectEngineCtx
*new_de_ctx
= NULL
;
4041 DetectEngineCtx
*old_de_ctx
= NULL
;
4044 memset(prefix
, 0, sizeof(prefix
));
4046 SCLogNotice("rule reload starting");
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
);
4056 ConfNode
*node
= ConfGetNode(prefix
);
4058 SCLogError(SC_ERR_CONF_YAML_ERROR
, "failed to properly setup yaml %s",
4059 suri
->conf_filename
);
4067 /* get a reference to the current de_ctx */
4068 old_de_ctx
= DetectEngineGetCurrent();
4069 if (old_de_ctx
== NULL
)
4071 SCLogDebug("get ref to old_de_ctx %p", old_de_ctx
);
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
))
4078 DetectEngineDeReference(&old_de_ctx
);
4079 SCLogNotice("rule reload complete");
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 "
4088 DetectEngineDeReference(&old_de_ctx
);
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
);
4097 SCLogDebug("set up new_de_ctx %p", new_de_ctx
);
4100 DetectEngineAddToMaster(new_de_ctx
);
4102 /* move to old free list */
4103 DetectEngineMoveToFreeList(old_de_ctx
);
4104 DetectEngineDeReference(&old_de_ctx
);
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
);
4111 /* walk free list, freeing the old_de_ctx */
4112 DetectEnginePruneFreeList();
4114 DatasetPostReloadCleanup();
4116 DetectEngineBumpVersion();
4118 SCLogDebug("old_de_ctx should have been freed");
4120 SCLogNotice("rule reload complete");
4124 static uint32_t TenantIdHash(HashTable
*h
, void *data
, uint16_t data_len
)
4126 DetectEngineThreadCtx
*det_ctx
= (DetectEngineThreadCtx
*)data
;
4127 return det_ctx
->tenant_id
% h
->array_size
;
4130 static char TenantIdCompare(void *d1
, uint16_t d1_len
, void *d2
, uint16_t d2_len
)
4132 DetectEngineThreadCtx
*det1
= (DetectEngineThreadCtx
*)d1
;
4133 DetectEngineThreadCtx
*det2
= (DetectEngineThreadCtx
*)d2
;
4134 return (det1
->tenant_id
== det2
->tenant_id
);
4137 static void TenantIdFree(void *d
)
4139 DetectEngineThreadCtxFree(d
);
4142 int DetectEngineMTApply(void)
4144 DetectEngineMasterCtx
*master
= &g_master_de_ctx
;
4145 SCMutexLock(&master
->lock
);
4147 if (master
->tenant_selector
== TENANT_SELECTOR_UNKNOWN
) {
4148 SCLogInfo("error, no tenant selector");
4149 SCMutexUnlock(&master
->lock
);
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
);
4158 if (list
->type
== DETECT_ENGINE_TYPE_NORMAL
||
4159 list
->type
== DETECT_ENGINE_TYPE_MT_STUB
||
4160 list
->type
== DETECT_ENGINE_TYPE_DD_STUB
)
4166 if (stub_de_ctx
== NULL
) {
4167 stub_de_ctx
= DetectEngineCtxInitStubForMT();
4168 if (stub_de_ctx
== NULL
) {
4169 SCMutexUnlock(&master
->lock
);
4173 if (master
->list
== NULL
) {
4174 master
->list
= stub_de_ctx
;
4176 stub_de_ctx
->next
= master
->list
;
4177 master
->list
= stub_de_ctx
;
4181 /* update the threads */
4182 SCLogDebug("MT reload starting");
4183 DetectEngineReloadThreads(stub_de_ctx
);
4184 SCLogDebug("MT reload done");
4186 SCMutexUnlock(&master
->lock
);
4188 /* walk free list, freeing the old_de_ctx */
4189 DetectEnginePruneFreeList();
4191 SCLogDebug("old_de_ctx should have been freed");
4195 static int g_parse_metadata
= 0;
4197 void DetectEngineSetParseMetadata(void)
4199 g_parse_metadata
= 1;
4202 void DetectEngineUnsetParseMetadata(void)
4204 g_parse_metadata
= 0;
4207 int DetectEngineMustParseMetadata(void)
4209 return g_parse_metadata
;
4212 const char *DetectSigmatchListEnumToString(enum DetectSigmatchListEnum type
)
4215 case DETECT_SM_LIST_MATCH
:
4217 case DETECT_SM_LIST_PMATCH
:
4218 return "packet/stream payload";
4220 case DETECT_SM_LIST_TMATCH
:
4223 case DETECT_SM_LIST_BASE64_DATA
:
4224 return "base64_data";
4226 case DETECT_SM_LIST_POSTMATCH
:
4227 return "post-match";
4229 case DETECT_SM_LIST_SUPPRESS
:
4231 case DETECT_SM_LIST_THRESHOLD
:
4234 case DETECT_SM_LIST_MAX
:
4235 return "max (internal)";
4241 void DetectEngineSetEvent(DetectEngineThreadCtx
*det_ctx
, uint8_t e
)
4243 AppLayerDecoderEventsSetEventRaw(&det_ctx
->decoder_events
, e
);
4247 AppLayerDecoderEvents
*DetectEngineGetEvents(DetectEngineThreadCtx
*det_ctx
)
4249 return det_ctx
->decoder_events
;
4252 int DetectEngineGetEventInfo(const char *event_name
, int *event_id
,
4253 AppLayerEventType
*event_type
)
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 */
4262 *event_type
= APP_LAYER_EVENT_TYPE_TRANSACTION
;
4267 /*************************************Unittest*********************************/
4271 static int DetectEngineInitYamlConf(const char *conf
)
4273 ConfCreateContextBackup();
4275 return ConfYamlLoadString(conf
, strlen(conf
));
4278 static void DetectEngineDeInitYamlConf(void)
4281 ConfRestoreContextBackup();
4286 static int DetectEngineTest01(void)
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";
4304 FAIL_IF(DetectEngineInitYamlConf(conf
) == -1);
4306 DetectEngineCtx
*de_ctx
= DetectEngineCtxInit();
4307 FAIL_IF_NULL(de_ctx
);
4309 FAIL_IF_NOT(de_ctx
->inspection_recursion_limit
== -1);
4311 DetectEngineCtxFree(de_ctx
);
4313 DetectEngineDeInitYamlConf();
4318 static int DetectEngineTest02(void)
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";
4336 FAIL_IF(DetectEngineInitYamlConf(conf
) == -1);
4338 DetectEngineCtx
*de_ctx
= DetectEngineCtxInit();
4339 FAIL_IF_NULL(de_ctx
);
4342 de_ctx
->inspection_recursion_limit
== DETECT_ENGINE_DEFAULT_INSPECTION_RECURSION_LIMIT
);
4344 DetectEngineCtxFree(de_ctx
);
4346 DetectEngineDeInitYamlConf();
4351 static int DetectEngineTest03(void)
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";
4368 FAIL_IF(DetectEngineInitYamlConf(conf
) == -1);
4370 DetectEngineCtx
*de_ctx
= DetectEngineCtxInit();
4371 FAIL_IF_NULL(de_ctx
);
4374 de_ctx
->inspection_recursion_limit
== DETECT_ENGINE_DEFAULT_INSPECTION_RECURSION_LIMIT
);
4376 DetectEngineCtxFree(de_ctx
);
4378 DetectEngineDeInitYamlConf();
4383 static int DetectEngineTest04(void)
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";
4401 FAIL_IF(DetectEngineInitYamlConf(conf
) == -1);
4403 DetectEngineCtx
*de_ctx
= DetectEngineCtxInit();
4404 FAIL_IF_NULL(de_ctx
);
4406 FAIL_IF_NOT(de_ctx
->inspection_recursion_limit
== 10);
4408 DetectEngineCtxFree(de_ctx
);
4410 DetectEngineDeInitYamlConf();
4415 static int DetectEngineTest08(void)
4421 " - profile: custom\n"
4422 " - custom-values:\n"
4423 " toclient-groups: 23\n"
4424 " toserver-groups: 27\n";
4426 FAIL_IF(DetectEngineInitYamlConf(conf
) == -1);
4428 DetectEngineCtx
*de_ctx
= DetectEngineCtxInit();
4429 FAIL_IF_NULL(de_ctx
);
4431 FAIL_IF_NOT(de_ctx
->max_uniq_toclient_groups
== 23);
4432 FAIL_IF_NOT(de_ctx
->max_uniq_toserver_groups
== 27);
4434 DetectEngineCtxFree(de_ctx
);
4436 DetectEngineDeInitYamlConf();
4441 /** \test bug 892 bad values */
4442 static int DetectEngineTest09(void)
4448 " - profile: custom\n"
4449 " - custom-values:\n"
4450 " toclient-groups: BA\n"
4451 " toserver-groups: BA\n"
4452 " - inspection-recursion-limit: 10\n";
4454 FAIL_IF(DetectEngineInitYamlConf(conf
) == -1);
4456 DetectEngineCtx
*de_ctx
= DetectEngineCtxInit();
4457 FAIL_IF_NULL(de_ctx
);
4459 FAIL_IF_NOT(de_ctx
->max_uniq_toclient_groups
== 20);
4460 FAIL_IF_NOT(de_ctx
->max_uniq_toserver_groups
== 40);
4462 DetectEngineCtxFree(de_ctx
);
4464 DetectEngineDeInitYamlConf();
4471 void DetectEngineRegisterTests()
4474 UtRegisterTest("DetectEngineTest01", DetectEngineTest01
);
4475 UtRegisterTest("DetectEngineTest02", DetectEngineTest02
);
4476 UtRegisterTest("DetectEngineTest03", DetectEngineTest03
);
4477 UtRegisterTest("DetectEngineTest04", DetectEngineTest04
);
4478 UtRegisterTest("DetectEngineTest08", DetectEngineTest08
);
4479 UtRegisterTest("DetectEngineTest09", DetectEngineTest09
);