suricata
util-thash.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2024 Open Information Security Foundation
2  *
3  * You can copy, redistribute or modify this Program under the terms of
4  * the GNU General Public License version 2 as published by the Free
5  * Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * version 2 along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15  * 02110-1301, USA.
16  */
17 
18 /**
19  * \file
20  *
21  * \author Victor Julien <victor@inliniac.net>
22  *
23  */
24 
25 #include "suricata-common.h"
26 #include "conf.h"
27 
28 #include "util-debug.h"
29 #include "util-thash.h"
30 
31 #include "util-random.h"
32 #include "util-misc.h"
33 #include "util-byte.h"
34 
35 #include "util-hash-lookup3.h"
36 #include "util-validate.h"
37 
38 static THashData *THashGetUsed(THashTableContext *ctx, uint32_t data_size);
39 static void THashDataEnqueue (THashDataQueue *q, THashData *h);
40 
42 {
43  THashDataEnqueue(&ctx->spare_q, h);
44  (void) SC_ATOMIC_SUB(ctx->counter, 1);
45 }
46 
47 static THashDataQueue *THashDataQueueInit (THashDataQueue *q)
48 {
49  if (q != NULL) {
50  memset(q, 0, sizeof(THashDataQueue));
51  HQLOCK_INIT(q);
52  }
53  return q;
54 }
55 
57 {
59  if (q == NULL) {
60  SCLogError("Fatal error encountered in THashDataQueueNew. Exiting...");
61  exit(EXIT_SUCCESS);
62  }
63  q = THashDataQueueInit(q);
64  return q;
65 }
66 
67 /**
68  * \brief Destroy a queue
69  *
70  * \param q the queue to destroy
71  */
72 static void THashDataQueueDestroy (THashDataQueue *q)
73 {
74  HQLOCK_DESTROY(q);
75 }
76 
77 /**
78  * \brief add to queue
79  *
80  * \param q queue
81  * \param h data
82  */
83 static void THashDataEnqueue (THashDataQueue *q, THashData *h)
84 {
85 #ifdef DEBUG
86  BUG_ON(q == NULL || h == NULL);
87 #endif
88 
89  HQLOCK_LOCK(q);
90 
91  /* more data in queue */
92  if (q->top != NULL) {
93  h->next = q->top;
94  q->top->prev = h;
95  q->top = h;
96  /* only data */
97  } else {
98  q->top = h;
99  q->bot = h;
100  }
101  q->len++;
102 #ifdef DBG_PERF
103  if (q->len > q->dbg_maxlen)
104  q->dbg_maxlen = q->len;
105 #endif /* DBG_PERF */
106  HQLOCK_UNLOCK(q);
107 }
108 
109 /**
110  * \brief remove data from the queue
111  *
112  * \param q queue
113  *
114  * \retval h data or NULL if empty list.
115  */
116 static THashData *THashDataDequeue (THashDataQueue *q)
117 {
118  HQLOCK_LOCK(q);
119 
120  THashData *h = q->bot;
121  if (h == NULL) {
122  HQLOCK_UNLOCK(q);
123  return NULL;
124  }
125 
126  /* more packets in queue */
127  if (q->bot->prev != NULL) {
128  q->bot = q->bot->prev;
129  q->bot->next = NULL;
130  /* just the one we remove, so now empty */
131  } else {
132  q->top = NULL;
133  q->bot = NULL;
134  }
135 
136 #ifdef DEBUG
137  BUG_ON(q->len == 0);
138 #endif
139  if (q->len > 0)
140  q->len--;
141 
142  h->next = NULL;
143  h->prev = NULL;
144 
145  HQLOCK_UNLOCK(q);
146  return h;
147 }
148 
149 #if 0
150 static uint32_t THashDataQueueLen(THashDataQueue *q)
151 {
152  uint32_t len;
153  HQLOCK_LOCK(q);
154  len = q->len;
155  HQLOCK_UNLOCK(q);
156  return len;
157 }
158 #endif
159 
160 static THashData *THashDataAlloc(THashTableContext *ctx, uint32_t data_size)
161 {
162  const size_t thash_data_size = THASH_DATA_SIZE(ctx);
163 
164  if (!(THASH_CHECK_MEMCAP(ctx, thash_data_size + data_size))) {
165  return NULL;
166  }
167 
168  size_t total_data_size = thash_data_size + data_size;
169 
170  (void)SC_ATOMIC_ADD(ctx->memuse, total_data_size);
171 
172  THashData *h = SCCalloc(1, thash_data_size);
173  if (unlikely(h == NULL))
174  goto error;
175 
176  /* points to data right after THashData block */
177  h->data = (uint8_t *)h + sizeof(THashData);
178 
179 // memset(h, 0x00, data_size);
180 
181  SCMutexInit(&h->m, NULL);
182  SC_ATOMIC_INIT(h->use_cnt);
183  return h;
184 
185 error:
186  (void)SC_ATOMIC_SUB(ctx->memuse, total_data_size);
187  return NULL;
188 }
189 
190 static void THashDataFree(THashTableContext *ctx, THashData *h)
191 {
192  if (h != NULL) {
193  DEBUG_VALIDATE_BUG_ON(SC_ATOMIC_GET(h->use_cnt) != 0);
194 
195  uint32_t data_size = 0;
196  if (h->data != NULL) {
197  if (ctx->config.DataSize) {
198  data_size = ctx->config.DataSize(h->data);
199  }
200  ctx->config.DataFree(h->data);
201  }
202  SCMutexDestroy(&h->m);
203  SCFree(h);
204  (void)SC_ATOMIC_SUB(ctx->memuse, THASH_DATA_SIZE(ctx) + (uint64_t)data_size);
205  }
206 }
207 
208 #define THASH_DEFAULT_HASHSIZE 4096
209 #define THASH_DEFAULT_MEMCAP 16777216
210 #define THASH_DEFAULT_PREALLOC 1000
211 
212 #define GET_VAR(prefix,name) \
213  snprintf(varname, sizeof(varname), "%s.%s", (prefix), (name))
214 
215 /** \brief initialize the configuration
216  * \warning Not thread safe */
217 static int THashInitConfig(THashTableContext *ctx, const char *cnf_prefix)
218 {
219  char varname[256];
220 
221  SCLogDebug("initializing thash engine...");
222 
223  /* Check if we have memcap and hash_size defined at config */
224  const char *conf_val;
225  uint32_t configval = 0;
226 
227  /** set config values for memcap, prealloc and hash_size */
228  GET_VAR(cnf_prefix, "memcap");
229  if ((SCConfGet(varname, &conf_val)) == 1) {
230  uint64_t memcap;
231  if (ParseSizeStringU64(conf_val, &memcap) < 0) {
232  SCLogError("Error parsing %s "
233  "from conf file - %s. Killing engine",
234  varname, conf_val);
235  return -1;
236  }
237  SC_ATOMIC_INIT(ctx->config.memcap);
238  SC_ATOMIC_SET(ctx->config.memcap, memcap);
239  }
240  GET_VAR(cnf_prefix, "hash-size");
241  if ((SCConfGet(varname, &conf_val)) == 1) {
242  if (StringParseUint32(&configval, 10, (uint16_t)strlen(conf_val), conf_val) > 0) {
243  ctx->config.hash_size = configval;
244  }
245  }
246 
247  GET_VAR(cnf_prefix, "prealloc");
248  if ((SCConfGet(varname, &conf_val)) == 1) {
249  if (StringParseUint32(&configval, 10, (uint16_t)strlen(conf_val), conf_val) > 0) {
250  ctx->config.prealloc = configval;
251  } else {
252  WarnInvalidConfEntry(varname, "%"PRIu32, ctx->config.prealloc);
253  }
254  }
255 
256  /* alloc hash memory */
257  uint64_t hash_size = ctx->config.hash_size * sizeof(THashHashRow);
258  if (!(THASH_CHECK_MEMCAP(ctx, hash_size))) {
259  SCLogError("allocating hash failed: "
260  "max hash memcap is smaller than projected hash size. "
261  "Memcap: %" PRIu64 ", Hash table size %" PRIu64 ". Calculate "
262  "total hash size by multiplying \"hash-size\" with %" PRIuMAX ", "
263  "which is the hash bucket size.",
264  SC_ATOMIC_GET(ctx->config.memcap), hash_size, (uintmax_t)sizeof(THashHashRow));
265  return -1;
266  }
267  ctx->array = SCMallocAligned(ctx->config.hash_size * sizeof(THashHashRow), CLS);
268  if (unlikely(ctx->array == NULL)) {
269  SCLogError("Fatal error encountered in THashInitConfig. Exiting...");
270  return -1;
271  }
272  memset(ctx->array, 0, ctx->config.hash_size * sizeof(THashHashRow));
273 
274  uint32_t i = 0;
275  for (i = 0; i < ctx->config.hash_size; i++) {
276  HRLOCK_INIT(&ctx->array[i]);
277  }
278  (void)SC_ATOMIC_ADD(ctx->memuse, (ctx->config.hash_size * sizeof(THashHashRow)));
279 
280  /* pre allocate prealloc */
281  for (i = 0; i < ctx->config.prealloc; i++) {
283  SCLogError("preallocating data failed: "
284  "max thash memcap reached. Memcap %" PRIu64 ", "
285  "Memuse %" PRIu64 ".",
286  SC_ATOMIC_GET(ctx->config.memcap),
287  ((uint64_t)SC_ATOMIC_GET(ctx->memuse) + THASH_DATA_SIZE(ctx)));
288  return -1;
289  }
290 
291  THashData *h = THashDataAlloc(ctx, 0 /* as we don't have string data here */);
292  if (h == NULL) {
293  SCLogError("preallocating data failed: %s", strerror(errno));
294  return -1;
295  }
296  THashDataEnqueue(&ctx->spare_q,h);
297  }
298 
299  return 0;
300 }
301 
302 THashTableContext *THashInit(const char *cnf_prefix, uint32_t data_size,
303  int (*DataSet)(void *, void *), void (*DataFree)(void *),
304  uint32_t (*DataHash)(uint32_t, void *), bool (*DataCompare)(void *, void *),
305  bool (*DataExpired)(void *, SCTime_t), uint32_t (*DataSize)(void *), bool reset_memcap,
306  uint64_t memcap, uint32_t hashsize)
307 {
308  THashTableContext *ctx = SCCalloc(1, sizeof(*ctx));
309  BUG_ON(!ctx);
310 
311  ctx->config.data_size = data_size;
312  ctx->config.DataSet = DataSet;
313  ctx->config.DataFree = DataFree;
314  ctx->config.DataHash = DataHash;
315  ctx->config.DataCompare = DataCompare;
316  ctx->config.DataExpired = DataExpired;
317  ctx->config.DataSize = DataSize;
318 
319  /* set defaults */
320  ctx->config.hash_rand = (uint32_t)RandomGet();
321  ctx->config.hash_size = hashsize > 0 ? hashsize : THASH_DEFAULT_HASHSIZE;
322  /* Reset memcap in case of loading from file to the highest possible value
323  unless defined by the rule keyword */
324  if (memcap > 0) {
325  SC_ATOMIC_SET(ctx->config.memcap, memcap);
326  } else {
327  SC_ATOMIC_SET(ctx->config.memcap, reset_memcap ? UINT64_MAX : THASH_DEFAULT_MEMCAP);
328  }
329  ctx->config.prealloc = THASH_DEFAULT_PREALLOC;
330 
331  SC_ATOMIC_INIT(ctx->counter);
332  SC_ATOMIC_INIT(ctx->memuse);
333  SC_ATOMIC_INIT(ctx->prune_idx);
334  THashDataQueueInit(&ctx->spare_q);
335 
336  if (THashInitConfig(ctx, cnf_prefix) < 0) {
338  ctx = NULL;
339  }
340  return ctx;
341 }
342 
343 /* \brief Set memcap to current memuse
344  * */
346 {
348  ctx->config.memcap, MAX(SC_ATOMIC_GET(ctx->memuse), SC_ATOMIC_GET(ctx->config.memcap)));
349  SCLogDebug("memcap after load set to: %" PRIu64, SC_ATOMIC_GET(ctx->config.memcap));
350 }
351 
352 /** \brief shutdown the flow engine
353  * \warning Not thread safe */
355 {
356  THashData *h;
357 
358  /* free spare queue */
359  while ((h = THashDataDequeue(&ctx->spare_q))) {
360  BUG_ON(SC_ATOMIC_GET(h->use_cnt) > 0);
361  THashDataFree(ctx, h);
362  }
363 
364  /* clear and free the hash */
365  if (ctx->array != NULL) {
366  for (uint32_t u = 0; u < ctx->config.hash_size; u++) {
367  h = ctx->array[u].head;
368  while (h) {
369  THashData *n = h->next;
370  THashDataFree(ctx, h);
371  h = n;
372  }
373 
374  HRLOCK_DESTROY(&ctx->array[u]);
375  }
376  SCFreeAligned(ctx->array);
377  ctx->array = NULL;
378  (void)SC_ATOMIC_SUB(ctx->memuse, ctx->config.hash_size * sizeof(THashHashRow));
379  }
380  THashDataQueueDestroy(&ctx->spare_q);
381  DEBUG_VALIDATE_BUG_ON(SC_ATOMIC_GET(ctx->memuse) != 0);
382  SCFree(ctx);
383 }
384 
385 /** \brief Walk the hash
386  *
387  */
388 int THashWalk(THashTableContext *ctx, THashFormatFunc FormatterFunc, THashOutputFunc OutputterFunc, void *output_ctx)
389 {
390  uint32_t u;
391 
392  if (ctx->array == NULL)
393  return -1;
394 
395  bool err = false;
396  for (u = 0; u < ctx->config.hash_size; u++) {
397  THashHashRow *hb = &ctx->array[u];
398  HRLOCK_LOCK(hb);
399  THashData *h = hb->head;
400  while (h) {
401  char output_string[1024] = "";
402  int size = FormatterFunc(h->data, output_string, sizeof(output_string));
403  if (size > 0) {
404  if (size > 1024) {
405  // we did not provide enough space on the stack, let's allocate on the heap
406  char *out_alloc = SCCalloc(1, size);
407  if (out_alloc == NULL) {
408  err = true;
409  break;
410  }
411  size = FormatterFunc(h->data, out_alloc, size);
412  if (size == 0) {
413  err = true;
414  SCFree(out_alloc);
415  break;
416  }
417  if (OutputterFunc(output_ctx, (const uint8_t *)out_alloc, size) < 0) {
418  err = true;
419  SCFree(out_alloc);
420  break;
421  }
422  SCFree(out_alloc);
423  } else if (OutputterFunc(output_ctx, (const uint8_t *)output_string, size) < 0) {
424  err = true;
425  break;
426  }
427  }
428  h = h->next;
429  }
430  HRLOCK_UNLOCK(hb);
431  if (err)
432  return -1;
433  }
434  return 0;
435 }
436 
437 /** \brief expire data from the hash
438  * Walk the hash table and remove data that is exprired according to the
439  * DataExpired callback.
440  * \retval cnt number of items successfully expired/removed
441  */
443 {
444  if (ctx->config.DataExpired == NULL)
445  return 0;
446 
447  SCLogDebug("timeout: starting");
448  uint32_t cnt = 0;
449 
450  for (uint32_t i = 0; i < ctx->config.hash_size; i++) {
451  THashHashRow *hb = &ctx->array[i];
452  if (HRLOCK_TRYLOCK(hb) != 0)
453  continue;
454  /* hash bucket is now locked */
455  THashData *h = hb->head;
456  while (h) {
457  THashData *next = h->next;
458  THashDataLock(h);
459  DEBUG_VALIDATE_BUG_ON(SC_ATOMIC_GET(h->use_cnt) > (uint32_t)INT_MAX);
460  /* only consider items with no references to it */
461  if (SC_ATOMIC_GET(h->use_cnt) == 0 && ctx->config.DataExpired(h->data, ts)) {
462  /* remove from the hash */
463  if (h->prev != NULL)
464  h->prev->next = h->next;
465  if (h->next != NULL)
466  h->next->prev = h->prev;
467  if (hb->head == h)
468  hb->head = h->next;
469  if (hb->tail == h)
470  hb->tail = h->prev;
471  h->next = NULL;
472  h->prev = NULL;
473  SCLogDebug("timeout: removing data %p", h);
474  if (ctx->config.DataSize) {
475  uint32_t data_size = ctx->config.DataSize(h->data);
476  if (data_size > 0)
477  (void)SC_ATOMIC_SUB(ctx->memuse, (uint64_t)data_size);
478  }
479  ctx->config.DataFree(h->data);
480  THashDataUnlock(h);
482  cnt++;
483  } else {
484  THashDataUnlock(h);
485  }
486  h = next;
487  }
488  HRLOCK_UNLOCK(hb);
489  }
490 
491  SCLogDebug("timeout: ending: %u entries expired", cnt);
492  return cnt;
493 }
494 
495 /** \brief Cleanup the thash engine
496  *
497  * Cleanup the thash engine from tag and threshold.
498  *
499  */
501 {
502  uint32_t u;
503 
504  if (ctx->array == NULL)
505  return;
506 
507  for (u = 0; u < ctx->config.hash_size; u++) {
508  THashHashRow *hb = &ctx->array[u];
509  HRLOCK_LOCK(hb);
510  THashData *h = hb->head;
511  while (h) {
512  if ((SC_ATOMIC_GET(h->use_cnt) > 0)) {
513  h = h->next;
514  } else {
515  THashData *n = h->next;
516  /* remove from the hash */
517  if (h->prev != NULL)
518  h->prev->next = h->next;
519  if (h->next != NULL)
520  h->next->prev = h->prev;
521  if (hb->head == h)
522  hb->head = h->next;
523  if (hb->tail == h)
524  hb->tail = h->prev;
525  h->next = NULL;
526  h->prev = NULL;
527  if (ctx->config.DataSize) {
528  uint32_t data_size = ctx->config.DataSize(h->data);
529  if (data_size > 0)
530  (void)SC_ATOMIC_SUB(ctx->memuse, (uint64_t)data_size);
531  }
533  h = n;
534  }
535  }
536  HRLOCK_UNLOCK(hb);
537  }
538 }
539 
540 /* calculate the hash key for this packet
541  *
542  * we're using:
543  * hash_rand -- set at init time
544  * source address
545  */
546 static uint32_t THashGetKey(const THashConfig *cnf, void *data)
547 {
548  uint32_t key;
549 
550  key = cnf->DataHash(cnf->hash_rand, data);
551  key %= cnf->hash_size;
552 
553  return key;
554 }
555 
556 static inline int THashCompare(const THashConfig *cnf, void *a, void *b)
557 {
558  if (cnf->DataCompare(a, b))
559  return 1;
560  return 0;
561 }
562 
563 /**
564  * \brief Get new data
565  *
566  * Get new data. We're checking memcap first and will try to make room
567  * if the memcap is reached.
568  *
569  * \retval h *LOCKED* data on succes, NULL on error.
570  */
571 static THashData *THashDataGetNew(THashTableContext *ctx, void *data)
572 {
573  THashData *h = NULL;
574  uint32_t data_size = 0;
575  if (ctx->config.DataSize) {
576  data_size = ctx->config.DataSize(data);
577  }
578 
579  /* get data from the spare queue */
580  h = THashDataDequeue(&ctx->spare_q);
581  if (h == NULL) {
582  /* If we reached the max memcap, we get used data */
583  if (!(THASH_CHECK_MEMCAP(ctx, THASH_DATA_SIZE(ctx) + data_size))) {
584  h = THashGetUsed(ctx, data_size);
585  if (h == NULL) {
586  return NULL;
587  }
588 
589  if (!SC_ATOMIC_GET(ctx->memcap_reached)) {
590  SC_ATOMIC_SET(ctx->memcap_reached, true);
591  }
592 
593  /* freed data, but it's unlocked */
594  } else {
595  /* now see if we can alloc a new data */
596  h = THashDataAlloc(ctx, data_size);
597  if (h == NULL) {
598  return NULL;
599  }
600 
601  /* data is initialized but *unlocked* */
602  }
603  } else {
604  /* data has been recycled before it went into the spare queue */
605  /* data is initialized (recycled) but *unlocked* */
606  /* the recycled data was THashData and again does not include
607  * the size of current data to be added */
608  if (data_size > 0) {
609  /* Since it is prealloc'd data, it already has THashData in its memuse */
610  (void)SC_ATOMIC_ADD(ctx->memuse, data_size);
611  if (!(THASH_CHECK_MEMCAP(ctx, data_size))) {
612  if (!SC_ATOMIC_GET(ctx->memcap_reached)) {
613  SC_ATOMIC_SET(ctx->memcap_reached, true);
614  }
615  SCLogError("Adding data will exceed memcap: %" PRIu64 ", current memuse: %" PRIu64,
616  SC_ATOMIC_GET((ctx)->config.memcap), SC_ATOMIC_GET(ctx->memuse));
617  }
618  }
619  }
620 
621  // setup the data
622 #ifdef DEBUG_VALIDATION
623  DEBUG_VALIDATE_BUG_ON(ctx->config.DataSet(h->data, data) != 0);
624 #else
625  ctx->config.DataSet(h->data, data);
626 #endif
627  (void) SC_ATOMIC_ADD(ctx->counter, 1);
628  SCMutexLock(&h->m);
629  return h;
630 }
631 
632 /*
633  * returns a *LOCKED* data or NULL
634  */
635 
636 struct THashDataGetResult
638 {
639  struct THashDataGetResult res = { .data = NULL, .is_new = false, };
640  THashData *h = NULL;
641 
642  /* get the key to our bucket */
643  uint32_t key = THashGetKey(&ctx->config, data);
644  /* get our hash bucket and lock it */
645  THashHashRow *hb = &ctx->array[key];
646  HRLOCK_LOCK(hb);
647 
648  /* see if the bucket already has data */
649  if (hb->head == NULL) {
650  h = THashDataGetNew(ctx, data);
651  if (h == NULL) {
652  HRLOCK_UNLOCK(hb);
653  return res;
654  }
655 
656  /* data is locked */
657  hb->head = h;
658  hb->tail = h;
659 
660  /* initialize and return */
661  (void) THashIncrUsecnt(h);
662 
663  HRLOCK_UNLOCK(hb);
664  res.data = h;
665  res.is_new = true;
666  return res;
667  }
668 
669  /* ok, we have data in the bucket. Let's find out if it is our data */
670  h = hb->head;
671 
672  /* see if this is the data we are looking for */
673  if (THashCompare(&ctx->config, h->data, data) == 0) {
674  THashData *ph = NULL; /* previous data */
675 
676  while (h) {
677  ph = h;
678  h = h->next;
679 
680  if (h == NULL) {
681  h = ph->next = THashDataGetNew(ctx, data);
682  if (h == NULL) {
683  HRLOCK_UNLOCK(hb);
684  return res;
685  }
686  hb->tail = h;
687 
688  /* data is locked */
689 
690  h->prev = ph;
691 
692  /* initialize and return */
693  (void) THashIncrUsecnt(h);
694 
695  HRLOCK_UNLOCK(hb);
696  res.data = h;
697  res.is_new = true;
698  return res;
699  }
700 
701  if (THashCompare(&ctx->config, h->data, data) != 0) {
702  /* we found our data, lets put it on top of the
703  * hash list -- this rewards active data */
704  if (h->next) {
705  h->next->prev = h->prev;
706  }
707  if (h->prev) {
708  h->prev->next = h->next;
709  }
710  if (h == hb->tail) {
711  hb->tail = h->prev;
712  }
713 
714  h->next = hb->head;
715  h->prev = NULL;
716  hb->head->prev = h;
717  hb->head = h;
718 
719  /* found our data, lock & return */
720  SCMutexLock(&h->m);
721  (void) THashIncrUsecnt(h);
722  HRLOCK_UNLOCK(hb);
723  res.data = h;
724  res.is_new = false;
725  /* coverity[missing_unlock : FALSE] */
726  return res;
727  }
728  }
729  }
730 
731  /* lock & return */
732  SCMutexLock(&h->m);
733  (void) THashIncrUsecnt(h);
734  HRLOCK_UNLOCK(hb);
735  res.data = h;
736  res.is_new = false;
737  /* coverity[missing_unlock : FALSE] */
738  return res;
739 }
740 
741 /** \brief look up data in the hash
742  *
743  * \param data data to look up
744  *
745  * \retval h *LOCKED* data or NULL
746  */
748 {
749  THashData *h = NULL;
750 
751  /* get the key to our bucket */
752  uint32_t key = THashGetKey(&ctx->config, data);
753  /* get our hash bucket and lock it */
754  THashHashRow *hb = &ctx->array[key];
755  HRLOCK_LOCK(hb);
756 
757  if (hb->head == NULL) {
758  HRLOCK_UNLOCK(hb);
759  return h;
760  }
761 
762  /* ok, we have data in the bucket. Let's find out if it is our data */
763  h = hb->head;
764 
765  /* see if this is the data we are looking for */
766  if (THashCompare(&ctx->config, h->data, data) == 0) {
767  while (h) {
768  h = h->next;
769  if (h == NULL) {
770  HRLOCK_UNLOCK(hb);
771  return h;
772  }
773 
774  if (THashCompare(&ctx->config, h->data, data) != 0) {
775  /* we found our data, lets put it on top of the
776  * hash list -- this rewards active data */
777  if (h->next) {
778  h->next->prev = h->prev;
779  }
780  if (h->prev) {
781  h->prev->next = h->next;
782  }
783  if (h == hb->tail) {
784  hb->tail = h->prev;
785  }
786 
787  h->next = hb->head;
788  h->prev = NULL;
789  hb->head->prev = h;
790  hb->head = h;
791 
792  /* found our data, lock & return */
793  SCMutexLock(&h->m);
794  (void) THashIncrUsecnt(h);
795  HRLOCK_UNLOCK(hb);
796  return h;
797  }
798  }
799  }
800 
801  /* lock & return */
802  SCMutexLock(&h->m);
803  (void) THashIncrUsecnt(h);
804  HRLOCK_UNLOCK(hb);
805  return h;
806 }
807 
808 /** \internal
809  * \brief Get data from the hash directly.
810  *
811  * Called in conditions where the spare queue is empty and memcap is
812  * reached.
813  *
814  * Walks the hash until data can be freed. "prune_idx" atomic int makes
815  * sure we don't start at the top each time since that would clear the top
816  * of the hash leading to longer and longer search times under high
817  * pressure (observed).
818  *
819  * \retval h data or NULL
820  */
821 static THashData *THashGetUsed(THashTableContext *ctx, uint32_t data_size)
822 {
823  uint32_t idx = SC_ATOMIC_GET(ctx->prune_idx) % ctx->config.hash_size;
824  uint32_t cnt = ctx->config.hash_size;
825 
826  while (cnt--) {
827  if (++idx >= ctx->config.hash_size)
828  idx = 0;
829 
830  THashHashRow *hb = &ctx->array[idx];
831 
832  if (HRLOCK_TRYLOCK(hb) != 0)
833  continue;
834 
835  THashData *h = hb->tail;
836  if (h == NULL) {
837  HRLOCK_UNLOCK(hb);
838  continue;
839  }
840 
841  if (SCMutexTrylock(&h->m) != 0) {
842  HRLOCK_UNLOCK(hb);
843  continue;
844  }
845 
846  if (SC_ATOMIC_GET(h->use_cnt) > 0) {
847  HRLOCK_UNLOCK(hb);
848  SCMutexUnlock(&h->m);
849  continue;
850  }
851 
852  /* remove from the hash */
853  if (h->prev != NULL)
854  h->prev->next = h->next;
855  if (h->next != NULL)
856  h->next->prev = h->prev;
857  if (hb->head == h)
858  hb->head = h->next;
859  if (hb->tail == h)
860  hb->tail = h->prev;
861 
862  h->next = NULL;
863  h->prev = NULL;
864  HRLOCK_UNLOCK(hb);
865 
866  if (h->data != NULL) {
867  if (ctx->config.DataSize) {
868  uint32_t h_data_size = ctx->config.DataSize(h->data);
869  if (h_data_size > 0) {
870  (void)SC_ATOMIC_SUB(ctx->memuse, (uint64_t)h_data_size);
871  }
872  }
873  ctx->config.DataFree(h->data);
874  }
875  SCMutexUnlock(&h->m);
876 
877  (void) SC_ATOMIC_ADD(ctx->prune_idx, (ctx->config.hash_size - cnt));
878  if (data_size > 0)
879  (void)SC_ATOMIC_ADD(ctx->memuse, data_size);
880  return h;
881  }
882 
883  return NULL;
884 }
885 
886 /**
887  * \retval int -1 not found
888  * \retval int 0 found, but it was busy (ref cnt)
889  * \retval int 1 found and removed */
891 {
892  /* get the key to our bucket */
893  uint32_t key = THashGetKey(&ctx->config, data);
894  /* get our hash bucket and lock it */
895  THashHashRow *hb = &ctx->array[key];
896 
897  HRLOCK_LOCK(hb);
898  THashData *h = hb->head;
899  while (h != NULL) {
900  /* see if this is the data we are looking for */
901  if (THashCompare(&ctx->config, h->data, data) == 0) {
902  h = h->next;
903  continue;
904  }
905 
906  SCMutexLock(&h->m);
907  if (SC_ATOMIC_GET(h->use_cnt) > 0) {
908  SCMutexUnlock(&h->m);
909  HRLOCK_UNLOCK(hb);
910  return 0;
911  }
912 
913  /* remove from the hash */
914  if (h->prev != NULL)
915  h->prev->next = h->next;
916  if (h->next != NULL)
917  h->next->prev = h->prev;
918  if (hb->head == h)
919  hb->head = h->next;
920  if (hb->tail == h)
921  hb->tail = h->prev;
922 
923  h->next = NULL;
924  h->prev = NULL;
925  SCMutexUnlock(&h->m);
926  HRLOCK_UNLOCK(hb);
927  THashDataFree(ctx, h);
928  SCLogDebug("found and removed");
929  return 1;
930  }
931 
932  HRLOCK_UNLOCK(hb);
933  SCLogDebug("data not found");
934  return -1;
935 }
HRLOCK_DESTROY
#define HRLOCK_DESTROY(fb)
Definition: host.h:50
util-byte.h
HQLOCK_LOCK
#define HQLOCK_LOCK(q)
Definition: host-queue.h:67
len
uint8_t len
Definition: app-layer-dnp3.h:2
ts
uint64_t ts
Definition: source-erf-file.c:55
THashCleanup
void THashCleanup(THashTableContext *ctx)
Cleanup the thash engine.
Definition: util-thash.c:500
THashDataGetResult::data
THashData * data
Definition: util-thash.h:192
SC_ATOMIC_INIT
#define SC_ATOMIC_INIT(name)
wrapper for initializing an atomic variable.
Definition: util-atomic.h:314
CLS
#define CLS
Definition: suricata-common.h:69
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
SC_ATOMIC_SET
#define SC_ATOMIC_SET(name, val)
Set the value for the atomic variable.
Definition: util-atomic.h:386
HQLOCK_DESTROY
#define HQLOCK_DESTROY(q)
Definition: host-queue.h:66
THashOutputFunc
int(* THashOutputFunc)(void *output_ctx, const uint8_t *data, const uint32_t data_len)
Definition: util-thash.h:121
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:282
ParseSizeStringU64
int ParseSizeStringU64(const char *size, uint64_t *res)
Definition: util-misc.c:191
next
struct HtpBodyChunk_ * next
Definition: app-layer-htp.h:0
THashData_::prev
struct THashData_ * prev
Definition: util-thash.h:95
THashRemoveFromHash
int THashRemoveFromHash(THashTableContext *ctx, void *data)
Definition: util-thash.c:890
ctx
struct Thresholds ctx
SC_ATOMIC_ADD
#define SC_ATOMIC_ADD(name, val)
add a value to our atomic variable
Definition: util-atomic.h:332
SCConfGet
int SCConfGet(const char *name, const char **vptr)
Retrieve the value of a configuration node.
Definition: conf.c:351
THashData_::next
struct THashData_ * next
Definition: util-thash.h:94
THashConsolidateMemcap
void THashConsolidateMemcap(THashTableContext *ctx)
Definition: util-thash.c:345
SCMutexLock
#define SCMutexLock(mut)
Definition: threads-debug.h:117
THashDataQueue_
Definition: util-thash.h:105
RandomGet
long int RandomGet(void)
Definition: util-random.c:130
THashDataConfig_::DataCompare
bool(* DataCompare)(void *, void *)
Definition: util-thash.h:134
MAX
#define MAX(x, y)
Definition: suricata-common.h:412
HRLOCK_LOCK
#define HRLOCK_LOCK(fb)
Definition: host.h:51
HRLOCK_UNLOCK
#define HRLOCK_UNLOCK(fb)
Definition: host.h:53
THashDataConfig_::hash_size
uint32_t hash_size
Definition: util-thash.h:127
util-debug.h
THASH_DEFAULT_HASHSIZE
#define THASH_DEFAULT_HASHSIZE
Definition: util-thash.c:208
SCMutexUnlock
#define SCMutexUnlock(mut)
Definition: threads-debug.h:120
THashTableContext_
Definition: util-thash.h:141
THASH_DATA_SIZE
#define THASH_DATA_SIZE(ctx)
Definition: util-thash.h:139
THashDataQueue_::bot
THashData * bot
Definition: util-thash.h:107
StringParseUint32
int StringParseUint32(uint32_t *res, int base, size_t len, const char *str)
Definition: util-byte.c:269
THashData_::m
SCMutex m
Definition: util-thash.h:87
BUG_ON
#define BUG_ON(x)
Definition: suricata-common.h:317
SC_ATOMIC_SUB
#define SC_ATOMIC_SUB(name, val)
sub a value from our atomic variable
Definition: util-atomic.h:341
THashDataGetResult
Definition: util-thash.h:191
SCFreeAligned
#define SCFreeAligned(p)
Definition: util-mem.h:77
GET_VAR
#define GET_VAR(prefix, name)
Definition: util-thash.c:212
conf.h
SCTime_t
Definition: util-time.h:40
HRLOCK_INIT
#define HRLOCK_INIT(fb)
Definition: host.h:49
THashDataMoveToSpare
void THashDataMoveToSpare(THashTableContext *ctx, THashData *h)
Definition: util-thash.c:41
SCMutexInit
#define SCMutexInit(mut, mutattrs)
Definition: threads-debug.h:116
WarnInvalidConfEntry
#define WarnInvalidConfEntry(param_name, format, value)
Generic API that can be used by all to log an invalid conf entry.
Definition: util-misc.h:35
THashShutdown
void THashShutdown(THashTableContext *ctx)
shutdown the flow engine
Definition: util-thash.c:354
HQLOCK_UNLOCK
#define HQLOCK_UNLOCK(q)
Definition: host-queue.h:69
THashData_::data
void * data
Definition: util-thash.h:92
cnt
uint32_t cnt
Definition: tmqh-packetpool.h:7
THashData_
Definition: util-thash.h:85
THashDataQueue_::len
uint32_t len
Definition: util-thash.h:108
suricata-common.h
THASH_DEFAULT_MEMCAP
#define THASH_DEFAULT_MEMCAP
Definition: util-thash.c:209
SCMallocAligned
#define SCMallocAligned(size, align)
Definition: util-mem.h:68
HQLOCK_INIT
#define HQLOCK_INIT(q)
Definition: host-queue.h:65
THashGetFromHash
struct THashDataGetResult THashGetFromHash(THashTableContext *ctx, void *data)
Definition: util-thash.c:637
hashsize
#define hashsize(n)
Definition: util-hash-lookup3.h:40
THashLookupFromHash
THashData * THashLookupFromHash(THashTableContext *ctx, void *data)
look up data in the hash
Definition: util-thash.c:747
util-hash-lookup3.h
THASH_DEFAULT_PREALLOC
#define THASH_DEFAULT_PREALLOC
Definition: util-thash.c:210
util-validate.h
SCMalloc
#define SCMalloc(sz)
Definition: util-mem.h:47
THashDataConfig_
Definition: util-thash.h:124
SCLogError
#define SCLogError(...)
Macro used to log ERROR messages.
Definition: util-debug.h:274
THashWalk
int THashWalk(THashTableContext *ctx, THashFormatFunc FormatterFunc, THashOutputFunc OutputterFunc, void *output_ctx)
Walk the hash.
Definition: util-thash.c:388
SCFree
#define SCFree(p)
Definition: util-mem.h:61
THashFormatFunc
int(* THashFormatFunc)(const void *in_data, char *output, size_t output_size)
Definition: util-thash.h:122
THashDataConfig_::DataHash
uint32_t(* DataHash)(uint32_t, void *)
Definition: util-thash.h:133
util-random.h
HRLOCK_TRYLOCK
#define HRLOCK_TRYLOCK(fb)
Definition: host.h:52
THashDataGetResult::is_new
bool is_new
Definition: util-thash.h:193
THashInit
THashTableContext * THashInit(const char *cnf_prefix, uint32_t data_size, int(*DataSet)(void *, void *), void(*DataFree)(void *), uint32_t(*DataHash)(uint32_t, void *), bool(*DataCompare)(void *, void *), bool(*DataExpired)(void *, SCTime_t), uint32_t(*DataSize)(void *), bool reset_memcap, uint64_t memcap, uint32_t hashsize)
Definition: util-thash.c:302
THashDataConfig_::hash_rand
uint32_t hash_rand
Definition: util-thash.h:126
THashExpire
uint32_t THashExpire(THashTableContext *ctx, const SCTime_t ts)
expire data from the hash Walk the hash table and remove data that is exprired according to the DataE...
Definition: util-thash.c:442
THashIncrUsecnt
#define THashIncrUsecnt(h)
Definition: util-thash.h:168
SC_ATOMIC_GET
#define SC_ATOMIC_GET(name)
Get the value from the atomic variable.
Definition: util-atomic.h:375
util-misc.h
util-thash.h
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
SCMutexDestroy
#define SCMutexDestroy
Definition: threads-debug.h:121
THASH_CHECK_MEMCAP
#define THASH_CHECK_MEMCAP(ctx, size)
check if a memory alloc would fit in the memcap
Definition: util-thash.h:164
DEBUG_VALIDATE_BUG_ON
#define DEBUG_VALIDATE_BUG_ON(exp)
Definition: util-validate.h:102
THashDataQueueNew
THashDataQueue * THashDataQueueNew(void)
Definition: util-thash.c:56
SCMutexTrylock
#define SCMutexTrylock(mut)
Definition: threads-debug.h:118
THashDataQueue_::top
THashData * top
Definition: util-thash.h:106