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