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