suricata
defrag-hash.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 #include "suricata-common.h"
19 #include "conf.h"
20 #include "defrag-hash.h"
21 #include "defrag-queue.h"
22 #include "defrag-config.h"
23 #include "util-random.h"
24 #include "util-byte.h"
25 #include "util-misc.h"
26 #include "util-hash-lookup3.h"
27 #include "util-validate.h"
28 
29 /** defrag tracker hash table */
32 SC_ATOMIC_DECLARE(uint64_t,defrag_memuse);
33 SC_ATOMIC_DECLARE(unsigned int,defragtracker_counter);
34 SC_ATOMIC_DECLARE(unsigned int,defragtracker_prune_idx);
35 
36 static DefragTracker *DefragTrackerGetUsedDefragTracker(void);
37 
38 /** queue with spare tracker */
39 static DefragTrackerQueue defragtracker_spare_q;
40 
41 /**
42  * \brief Update memcap value
43  *
44  * \param size new memcap value
45  */
46 int DefragTrackerSetMemcap(uint64_t size)
47 {
48  if ((uint64_t)SC_ATOMIC_GET(defrag_memuse) < size) {
49  SC_ATOMIC_SET(defrag_config.memcap, size);
50  return 1;
51  }
52 
53  return 0;
54 }
55 
56 /**
57  * \brief Return memcap value
58  *
59  * \retval memcap value
60  */
61 uint64_t DefragTrackerGetMemcap(void)
62 {
63  uint64_t memcapcopy = SC_ATOMIC_GET(defrag_config.memcap);
64  return memcapcopy;
65 }
66 
67 /**
68  * \brief Return memuse value
69  *
70  * \retval memuse value
71  */
72 uint64_t DefragTrackerGetMemuse(void)
73 {
74  uint64_t memusecopy = (uint64_t)SC_ATOMIC_GET(defrag_memuse);
75  return memusecopy;
76 }
77 
79 {
81 }
82 
84 {
85  DefragTrackerEnqueue(&defragtracker_spare_q, h);
86  (void) SC_ATOMIC_SUB(defragtracker_counter, 1);
87 }
88 
89 static DefragTracker *DefragTrackerAlloc(void)
90 {
91  if (!(DEFRAG_CHECK_MEMCAP(sizeof(DefragTracker)))) {
92  return NULL;
93  }
94 
95  (void) SC_ATOMIC_ADD(defrag_memuse, sizeof(DefragTracker));
96 
97  DefragTracker *dt = SCCalloc(1, sizeof(DefragTracker));
98  if (unlikely(dt == NULL))
99  goto error;
100 
101  SCMutexInit(&dt->lock, NULL);
102  SC_ATOMIC_INIT(dt->use_cnt);
103  return dt;
104 
105 error:
106  return NULL;
107 }
108 
109 static void DefragTrackerFree(DefragTracker *dt)
110 {
111  if (dt != NULL) {
113 
114  SCMutexDestroy(&dt->lock);
115  SCFree(dt);
116  (void) SC_ATOMIC_SUB(defrag_memuse, sizeof(DefragTracker));
117  }
118 }
119 
120 #define DefragTrackerIncrUsecnt(dt) \
121  SC_ATOMIC_ADD((dt)->use_cnt, 1)
122 #define DefragTrackerDecrUsecnt(dt) \
123  SC_ATOMIC_SUB((dt)->use_cnt, 1)
124 
125 static void DefragTrackerInit(DefragTracker *dt, Packet *p)
126 {
127  /* copy address */
128  COPY_ADDRESS(&p->src, &dt->src_addr);
129  COPY_ADDRESS(&p->dst, &dt->dst_addr);
130 
131  if (PacketIsIPv4(p)) {
132  const IPV4Hdr *ip4h = PacketGetIPv4(p);
133  dt->id = (int32_t)IPV4_GET_RAW_IPID(ip4h);
134  dt->af = AF_INET;
135  } else {
136  DEBUG_VALIDATE_BUG_ON(!PacketIsIPv6(p));
137  dt->id = (int32_t)IPV6_EXTHDR_GET_FH_ID(p);
138  dt->af = AF_INET6;
139  }
140  dt->proto = PacketGetIPProto(p);
141  memcpy(&dt->vlan_id[0], &p->vlan_id[0], sizeof(dt->vlan_id));
142  dt->policy = DefragGetOsPolicy(p);
144  dt->remove = 0;
145  dt->seen_last = 0;
146 
147  (void) DefragTrackerIncrUsecnt(dt);
148 }
149 
151 {
152  (void) DefragTrackerDecrUsecnt(t);
153  SCMutexUnlock(&t->lock);
154 }
155 
157 {
159 }
160 
161 #define DEFRAG_DEFAULT_HASHSIZE 4096
162 #define DEFRAG_DEFAULT_MEMCAP 16777216
163 #define DEFRAG_DEFAULT_PREALLOC 1000
164 
165 /** \brief initialize the configuration
166  * \warning Not thread safe */
167 void DefragInitConfig(bool quiet)
168 {
169  SCLogDebug("initializing defrag engine...");
170 
171  memset(&defrag_config, 0, sizeof(defrag_config));
172  //SC_ATOMIC_INIT(flow_flags);
173  SC_ATOMIC_INIT(defragtracker_counter);
174  SC_ATOMIC_INIT(defrag_memuse);
175  SC_ATOMIC_INIT(defragtracker_prune_idx);
177  DefragTrackerQueueInit(&defragtracker_spare_q);
178 
179  /* set defaults */
180  defrag_config.hash_rand = (uint32_t)RandomGet();
184  defrag_config.memcap_policy = ExceptionPolicyParse("defrag.memcap-policy", false);
185 
186  /* Check if we have memcap and hash_size defined at config */
187  const char *conf_val;
188  uint32_t configval = 0;
189 
190  uint64_t defrag_memcap;
191  /** set config values for memcap, prealloc and hash_size */
192  if ((ConfGet("defrag.memcap", &conf_val)) == 1)
193  {
194  if (ParseSizeStringU64(conf_val, &defrag_memcap) < 0) {
195  SCLogError("Error parsing defrag.memcap "
196  "from conf file - %s. Killing engine",
197  conf_val);
198  exit(EXIT_FAILURE);
199  } else {
200  SC_ATOMIC_SET(defrag_config.memcap, defrag_memcap);
201  }
202  }
203  if ((ConfGet("defrag.hash-size", &conf_val)) == 1)
204  {
205  if (StringParseUint32(&configval, 10, strlen(conf_val),
206  conf_val) > 0) {
207  defrag_config.hash_size = configval;
208  } else {
209  WarnInvalidConfEntry("defrag.hash-size", "%"PRIu32, defrag_config.hash_size);
210  }
211  }
212 
213 
214  if ((ConfGet("defrag.trackers", &conf_val)) == 1)
215  {
216  if (StringParseUint32(&configval, 10, strlen(conf_val),
217  conf_val) > 0) {
218  defrag_config.prealloc = configval;
219  } else {
220  WarnInvalidConfEntry("defrag.trackers", "%"PRIu32, defrag_config.prealloc);
221  }
222  }
223  SCLogDebug("DefragTracker config from suricata.yaml: memcap: %"PRIu64", hash-size: "
224  "%"PRIu32", prealloc: %"PRIu32, SC_ATOMIC_GET(defrag_config.memcap),
226 
227  /* alloc hash memory */
228  uint64_t hash_size = defrag_config.hash_size * sizeof(DefragTrackerHashRow);
229  if (!(DEFRAG_CHECK_MEMCAP(hash_size))) {
230  SCLogError("allocating defrag hash failed: "
231  "max defrag memcap is smaller than projected hash size. "
232  "Memcap: %" PRIu64 ", Hash table size %" PRIu64 ". Calculate "
233  "total hash size by multiplying \"defrag.hash-size\" with %" PRIuMAX ", "
234  "which is the hash bucket size.",
235  SC_ATOMIC_GET(defrag_config.memcap), hash_size,
236  (uintmax_t)sizeof(DefragTrackerHashRow));
237  exit(EXIT_FAILURE);
238  }
240  if (unlikely(defragtracker_hash == NULL)) {
241  FatalError("Fatal error encountered in DefragTrackerInitConfig. Exiting...");
242  }
244 
245  uint32_t i = 0;
246  for (i = 0; i < defrag_config.hash_size; i++) {
248  }
249  (void) SC_ATOMIC_ADD(defrag_memuse, (defrag_config.hash_size * sizeof(DefragTrackerHashRow)));
250 
251  if (!quiet) {
252  SCLogConfig("allocated %"PRIu64" bytes of memory for the defrag hash... "
253  "%" PRIu32 " buckets of size %" PRIuMAX "",
254  SC_ATOMIC_GET(defrag_memuse), defrag_config.hash_size,
255  (uintmax_t)sizeof(DefragTrackerHashRow));
256  }
257 
258  if ((ConfGet("defrag.prealloc", &conf_val)) == 1)
259  {
260  if (ConfValIsTrue(conf_val)) {
261  /* pre allocate defrag trackers */
262  for (i = 0; i < defrag_config.prealloc; i++) {
263  if (!(DEFRAG_CHECK_MEMCAP(sizeof(DefragTracker)))) {
264  SCLogError("preallocating defrag trackers failed: "
265  "max defrag memcap reached. Memcap %" PRIu64 ", "
266  "Memuse %" PRIu64 ".",
268  ((uint64_t)SC_ATOMIC_GET(defrag_memuse) +
269  (uint64_t)sizeof(DefragTracker)));
270  exit(EXIT_FAILURE);
271  }
272 
273  DefragTracker *h = DefragTrackerAlloc();
274  if (h == NULL) {
275  SCLogError("preallocating defrag failed: %s", strerror(errno));
276  exit(EXIT_FAILURE);
277  }
278  DefragTrackerEnqueue(&defragtracker_spare_q,h);
279  }
280  if (!quiet) {
281  SCLogConfig("preallocated %" PRIu32 " defrag trackers of size %" PRIuMAX "",
282  defragtracker_spare_q.len, (uintmax_t)sizeof(DefragTracker));
283  }
284  }
285  }
286 
287  if (!quiet) {
288  SCLogConfig("defrag memory usage: %"PRIu64" bytes, maximum: %"PRIu64,
289  SC_ATOMIC_GET(defrag_memuse), SC_ATOMIC_GET(defrag_config.memcap));
290  }
291 
292  return;
293 }
294 
295 /** \brief print some defrag stats
296  * \warning Not thread safe */
297 static void DefragTrackerPrintStats (void)
298 {
299 }
300 
301 /** \brief shutdown the flow engine
302  * \warning Not thread safe */
304 {
305  DefragTracker *dt;
306  uint32_t u;
307 
308  DefragTrackerPrintStats();
309 
310  /* free spare queue */
311  while((dt = DefragTrackerDequeue(&defragtracker_spare_q))) {
312  BUG_ON(SC_ATOMIC_GET(dt->use_cnt) > 0);
313  DefragTrackerFree(dt);
314  }
315 
316  /* clear and free the hash */
317  if (defragtracker_hash != NULL) {
318  for (u = 0; u < defrag_config.hash_size; u++) {
319  dt = defragtracker_hash[u].head;
320  while (dt) {
321  DefragTracker *n = dt->hnext;
323  DefragTrackerFree(dt);
324  dt = n;
325  }
326 
328  }
330  defragtracker_hash = NULL;
331  }
332  (void) SC_ATOMIC_SUB(defrag_memuse, defrag_config.hash_size * sizeof(DefragTrackerHashRow));
333  DefragTrackerQueueDestroy(&defragtracker_spare_q);
334  return;
335 }
336 
337 /** \brief compare two raw ipv6 addrs
338  *
339  * \note we don't care about the real ipv6 ip's, this is just
340  * to consistently fill the DefragHashKey6 struct, without all
341  * the SCNtohl calls.
342  *
343  * \warning do not use elsewhere unless you know what you're doing.
344  * detect-engine-address-ipv6.c's AddressIPv6GtU32 is likely
345  * what you are looking for.
346  */
347 static inline int DefragHashRawAddressIPv6GtU32(const uint32_t *a, const uint32_t *b)
348 {
349  for (int i = 0; i < 4; i++) {
350  if (a[i] > b[i])
351  return 1;
352  if (a[i] < b[i])
353  break;
354  }
355 
356  return 0;
357 }
358 
359 typedef struct DefragHashKey4_ {
360  union {
361  struct {
362  uint32_t src, dst;
363  uint32_t id;
365  uint16_t pad[1];
366  };
367  uint32_t u32[5];
368  };
370 
371 typedef struct DefragHashKey6_ {
372  union {
373  struct {
374  uint32_t src[4], dst[4];
375  uint32_t id;
377  uint16_t pad[1];
378  };
379  uint32_t u32[11];
380  };
382 
383 /* calculate the hash key for this packet
384  *
385  * we're using:
386  * hash_rand -- set at init time
387  * source address
388  * destination address
389  * id
390  * vlan_id
391  */
392 static inline uint32_t DefragHashGetKey(Packet *p)
393 {
394  uint32_t key;
395 
396  if (PacketIsIPv4(p)) {
397  const IPV4Hdr *ip4h = PacketGetIPv4(p);
398  DefragHashKey4 dhk = { .pad[0] = 0 };
399  if (p->src.addr_data32[0] > p->dst.addr_data32[0]) {
400  dhk.src = p->src.addr_data32[0];
401  dhk.dst = p->dst.addr_data32[0];
402  } else {
403  dhk.src = p->dst.addr_data32[0];
404  dhk.dst = p->src.addr_data32[0];
405  }
406  dhk.id = (uint32_t)IPV4_GET_RAW_IPID(ip4h);
407  memcpy(&dhk.vlan_id[0], &p->vlan_id[0], sizeof(dhk.vlan_id));
408 
409  uint32_t hash =
410  hashword(dhk.u32, sizeof(dhk.u32) / sizeof(uint32_t), defrag_config.hash_rand);
411  key = hash % defrag_config.hash_size;
412  } else if (PacketIsIPv6(p)) {
413  DefragHashKey6 dhk = { .pad[0] = 0 };
414  if (DefragHashRawAddressIPv6GtU32(p->src.addr_data32, p->dst.addr_data32)) {
415  dhk.src[0] = p->src.addr_data32[0];
416  dhk.src[1] = p->src.addr_data32[1];
417  dhk.src[2] = p->src.addr_data32[2];
418  dhk.src[3] = p->src.addr_data32[3];
419  dhk.dst[0] = p->dst.addr_data32[0];
420  dhk.dst[1] = p->dst.addr_data32[1];
421  dhk.dst[2] = p->dst.addr_data32[2];
422  dhk.dst[3] = p->dst.addr_data32[3];
423  } else {
424  dhk.src[0] = p->dst.addr_data32[0];
425  dhk.src[1] = p->dst.addr_data32[1];
426  dhk.src[2] = p->dst.addr_data32[2];
427  dhk.src[3] = p->dst.addr_data32[3];
428  dhk.dst[0] = p->src.addr_data32[0];
429  dhk.dst[1] = p->src.addr_data32[1];
430  dhk.dst[2] = p->src.addr_data32[2];
431  dhk.dst[3] = p->src.addr_data32[3];
432  }
433  dhk.id = IPV6_EXTHDR_GET_FH_ID(p);
434  memcpy(&dhk.vlan_id[0], &p->vlan_id[0], sizeof(dhk.vlan_id));
435 
436  uint32_t hash =
437  hashword(dhk.u32, sizeof(dhk.u32) / sizeof(uint32_t), defrag_config.hash_rand);
438  key = hash % defrag_config.hash_size;
439  } else
440  key = 0;
441 
442  return key;
443 }
444 
445 /* Since two or more trackers can have the same hash key, we need to compare
446  * the tracker with the current tracker key. */
447 #define CMP_DEFRAGTRACKER(d1, d2, id) \
448  (((CMP_ADDR(&(d1)->src_addr, &(d2)->src) && CMP_ADDR(&(d1)->dst_addr, &(d2)->dst)) || \
449  (CMP_ADDR(&(d1)->src_addr, &(d2)->dst) && CMP_ADDR(&(d1)->dst_addr, &(d2)->src))) && \
450  (d1)->proto == PacketGetIPProto(d2) && (d1)->id == (id) && \
451  (d1)->vlan_id[0] == (d2)->vlan_id[0] && (d1)->vlan_id[1] == (d2)->vlan_id[1] && \
452  (d1)->vlan_id[2] == (d2)->vlan_id[2])
453 
454 static inline int DefragTrackerCompare(DefragTracker *t, Packet *p)
455 {
456  uint32_t id;
457  if (PacketIsIPv4(p)) {
458  const IPV4Hdr *ip4h = PacketGetIPv4(p);
459  id = (uint32_t)IPV4_GET_RAW_IPID(ip4h);
460  } else {
461  id = IPV6_EXTHDR_GET_FH_ID(p);
462  }
463 
464  return CMP_DEFRAGTRACKER(t, p, id);
465 }
466 
467 static void DefragExceptionPolicyStatsIncr(
469 {
470  uint16_t id = dtv->counter_defrag_memcap_eps.eps_id[policy];
471  if (likely(tv && id > 0)) {
472  StatsIncr(tv, id);
473  }
474 }
475 
476 /**
477  * \brief Get a new defrag tracker
478  *
479  * Get a new defrag tracker. We're checking memcap first and will try to make room
480  * if the memcap is reached.
481  *
482  * \retval dt *LOCKED* tracker on success, NULL on error.
483  */
484 static DefragTracker *DefragTrackerGetNew(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p)
485 {
486 #ifdef DEBUG
487  if (g_eps_defrag_memcap != UINT64_MAX && g_eps_defrag_memcap == p->pcap_cnt) {
488  SCLogNotice("simulating memcap hit for packet %" PRIu64, p->pcap_cnt);
490  DefragExceptionPolicyStatsIncr(tv, dtv, defrag_config.memcap_policy);
491  return NULL;
492  }
493 #endif
494 
495  DefragTracker *dt = NULL;
496 
497  /* get a tracker from the spare queue */
498  dt = DefragTrackerDequeue(&defragtracker_spare_q);
499  if (dt == NULL) {
500  /* If we reached the max memcap, we get a used tracker */
501  if (!(DEFRAG_CHECK_MEMCAP(sizeof(DefragTracker)))) {
502  /* declare state of emergency */
503  //if (!(SC_ATOMIC_GET(defragtracker_flags) & DEFRAG_EMERGENCY)) {
504  // SC_ATOMIC_OR(defragtracker_flags, DEFRAG_EMERGENCY);
505 
506  /* under high load, waking up the flow mgr each time leads
507  * to high cpu usage. Flows are not timed out much faster if
508  * we check a 1000 times a second. */
509  // FlowWakeupFlowManagerThread();
510  //}
511 
512  dt = DefragTrackerGetUsedDefragTracker();
513  if (dt == NULL) {
515  DefragExceptionPolicyStatsIncr(tv, dtv, defrag_config.memcap_policy);
516  return NULL;
517  }
518 
519  /* freed a tracker, but it's unlocked */
520  } else {
521  /* now see if we can alloc a new tracker */
522  dt = DefragTrackerAlloc();
523  if (dt == NULL) {
525  DefragExceptionPolicyStatsIncr(tv, dtv, defrag_config.memcap_policy);
526  return NULL;
527  }
528 
529  /* tracker is initialized but *unlocked* */
530  }
531  } else {
532  /* tracker has been recycled before it went into the spare queue */
533 
534  /* tracker is initialized (recycled) but *unlocked* */
535  }
536 
537  (void) SC_ATOMIC_ADD(defragtracker_counter, 1);
538  SCMutexLock(&dt->lock);
539  return dt;
540 }
541 
542 /* DefragGetTrackerFromHash
543  *
544  * Hash retrieval function for trackers. Looks up the hash bucket containing the
545  * tracker pointer. Then compares the packet with the found tracker to see if it is
546  * the tracker we need. If it isn't, walk the list until the right tracker is found.
547  *
548  * returns a *LOCKED* tracker or NULL
549  */
551 {
552  DefragTracker *dt = NULL;
553 
554  /* get the key to our bucket */
555  uint32_t key = DefragHashGetKey(p);
556  /* get our hash bucket and lock it */
558  DRLOCK_LOCK(hb);
559 
560  /* see if the bucket already has a tracker */
561  if (hb->head == NULL) {
562  dt = DefragTrackerGetNew(tv, dtv, p);
563  if (dt == NULL) {
564  DRLOCK_UNLOCK(hb);
565  return NULL;
566  }
567 
568  /* tracker is locked */
569  hb->head = dt;
570  hb->tail = dt;
571 
572  /* got one, now lock, initialize and return */
573  DefragTrackerInit(dt,p);
574 
575  DRLOCK_UNLOCK(hb);
576  return dt;
577  }
578 
579  /* ok, we have a tracker in the bucket. Let's find out if it is our tracker */
580  dt = hb->head;
581 
582  /* see if this is the tracker we are looking for */
583  if (dt->remove || DefragTrackerCompare(dt, p) == 0) {
584  DefragTracker *pdt = NULL; /* previous tracker */
585 
586  while (dt) {
587  pdt = dt;
588  dt = dt->hnext;
589 
590  if (dt == NULL) {
591  dt = pdt->hnext = DefragTrackerGetNew(tv, dtv, p);
592  if (dt == NULL) {
593  DRLOCK_UNLOCK(hb);
594  return NULL;
595  }
596  hb->tail = dt;
597 
598  /* tracker is locked */
599 
600  dt->hprev = pdt;
601 
602  /* initialize and return */
603  DefragTrackerInit(dt,p);
604 
605  DRLOCK_UNLOCK(hb);
606  return dt;
607  }
608 
609  if (DefragTrackerCompare(dt, p) != 0) {
610  /* we found our tracker, lets put it on top of the
611  * hash list -- this rewards active trackers */
612  if (dt->hnext) {
613  dt->hnext->hprev = dt->hprev;
614  }
615  if (dt->hprev) {
616  dt->hprev->hnext = dt->hnext;
617  }
618  if (dt == hb->tail) {
619  hb->tail = dt->hprev;
620  }
621 
622  dt->hnext = hb->head;
623  dt->hprev = NULL;
624  hb->head->hprev = dt;
625  hb->head = dt;
626 
627  /* found our tracker, lock & return */
628  SCMutexLock(&dt->lock);
629  (void) DefragTrackerIncrUsecnt(dt);
630  DRLOCK_UNLOCK(hb);
631  return dt;
632  }
633  }
634  }
635 
636  /* lock & return */
637  SCMutexLock(&dt->lock);
638  (void) DefragTrackerIncrUsecnt(dt);
639  DRLOCK_UNLOCK(hb);
640  return dt;
641 }
642 
643 /** \brief look up a tracker in the hash
644  *
645  * \param a address to look up
646  *
647  * \retval h *LOCKED* tracker or NULL
648  */
650 {
651  DefragTracker *dt = NULL;
652 
653  /* get the key to our bucket */
654  uint32_t key = DefragHashGetKey(p);
655  /* get our hash bucket and lock it */
657  DRLOCK_LOCK(hb);
658 
659  /* see if the bucket already has a tracker */
660  if (hb->head == NULL) {
661  DRLOCK_UNLOCK(hb);
662  return dt;
663  }
664 
665  /* ok, we have a tracker in the bucket. Let's find out if it is our tracker */
666  dt = hb->head;
667 
668  /* see if this is the tracker we are looking for */
669  if (DefragTrackerCompare(dt, p) == 0) {
670  while (dt) {
671  dt = dt->hnext;
672 
673  if (dt == NULL) {
674  DRLOCK_UNLOCK(hb);
675  return dt;
676  }
677 
678  if (DefragTrackerCompare(dt, p) != 0) {
679  /* we found our tracker, lets put it on top of the
680  * hash list -- this rewards active tracker */
681  if (dt->hnext) {
682  dt->hnext->hprev = dt->hprev;
683  }
684  if (dt->hprev) {
685  dt->hprev->hnext = dt->hnext;
686  }
687  if (dt == hb->tail) {
688  hb->tail = dt->hprev;
689  }
690 
691  dt->hnext = hb->head;
692  dt->hprev = NULL;
693  hb->head->hprev = dt;
694  hb->head = dt;
695 
696  /* found our tracker, lock & return */
697  SCMutexLock(&dt->lock);
698  (void) DefragTrackerIncrUsecnt(dt);
699  DRLOCK_UNLOCK(hb);
700  return dt;
701  }
702  }
703  }
704 
705  /* lock & return */
706  SCMutexLock(&dt->lock);
707  (void) DefragTrackerIncrUsecnt(dt);
708  DRLOCK_UNLOCK(hb);
709  return dt;
710 }
711 
712 /** \internal
713  * \brief Get a tracker from the hash directly.
714  *
715  * Called in conditions where the spare queue is empty and memcap is reached.
716  *
717  * Walks the hash until a tracker can be freed. "defragtracker_prune_idx" atomic int makes
718  * sure we don't start at the top each time since that would clear the top of
719  * the hash leading to longer and longer search times under high pressure (observed).
720  *
721  * \retval dt tracker or NULL
722  */
723 static DefragTracker *DefragTrackerGetUsedDefragTracker(void)
724 {
725  uint32_t idx = SC_ATOMIC_GET(defragtracker_prune_idx) % defrag_config.hash_size;
726  uint32_t cnt = defrag_config.hash_size;
727 
728  while (cnt--) {
729  if (++idx >= defrag_config.hash_size)
730  idx = 0;
731 
733 
734  if (DRLOCK_TRYLOCK(hb) != 0)
735  continue;
736 
737  DefragTracker *dt = hb->tail;
738  if (dt == NULL) {
739  DRLOCK_UNLOCK(hb);
740  continue;
741  }
742 
743  if (SCMutexTrylock(&dt->lock) != 0) {
744  DRLOCK_UNLOCK(hb);
745  continue;
746  }
747 
748  /** never prune a tracker that is used by a packets
749  * we are currently processing in one of the threads */
750  if (SC_ATOMIC_GET(dt->use_cnt) > 0) {
751  DRLOCK_UNLOCK(hb);
752  SCMutexUnlock(&dt->lock);
753  continue;
754  }
755 
756  /* remove from the hash */
757  if (dt->hprev != NULL)
758  dt->hprev->hnext = dt->hnext;
759  if (dt->hnext != NULL)
760  dt->hnext->hprev = dt->hprev;
761  if (hb->head == dt)
762  hb->head = dt->hnext;
763  if (hb->tail == dt)
764  hb->tail = dt->hprev;
765 
766  dt->hnext = NULL;
767  dt->hprev = NULL;
768  DRLOCK_UNLOCK(hb);
769 
771 
772  SCMutexUnlock(&dt->lock);
773 
774  (void) SC_ATOMIC_ADD(defragtracker_prune_idx, (defrag_config.hash_size - cnt));
775  return dt;
776  }
777 
778  return NULL;
779 }
780 
781 
PKT_DROP_REASON_DEFRAG_MEMCAP
@ PKT_DROP_REASON_DEFRAG_MEMCAP
Definition: decode.h:370
util-byte.h
IPV4_GET_RAW_IPID
#define IPV4_GET_RAW_IPID(ip4h)
Definition: decode-ipv4.h:99
DefragTrackerFreeFrags
void DefragTrackerFreeFrags(DefragTracker *tracker)
Free all frags associated with a tracker.
Definition: defrag.c:133
DefragLookupTrackerFromHash
DefragTracker * DefragLookupTrackerFromHash(Packet *p)
look up a tracker in the hash
Definition: defrag-hash.c:649
ExceptionPolicyApply
void ExceptionPolicyApply(Packet *p, enum ExceptionPolicy policy, enum PacketDropReason drop_reason)
Definition: util-exception-policy.c:69
DefragHashKey4
struct DefragHashKey4_ DefragHashKey4
IPV6_EXTHDR_GET_FH_ID
#define IPV6_EXTHDR_GET_FH_ID(p)
Definition: decode-ipv6.h:103
StatsIncr
void StatsIncr(ThreadVars *tv, uint16_t id)
Increments the local counter.
Definition: counters.c:167
DefragHashKey4_::src
uint32_t src
Definition: defrag-hash.c:362
hashword
uint32_t hashword(const uint32_t *k, size_t length, uint32_t initval)
Definition: util-hash-lookup3.c:172
DefragTracker_::hnext
struct DefragTracker_ * hnext
Definition: defrag.h:119
defragtracker_hash
DefragTrackerHashRow * defragtracker_hash
Definition: defrag-hash.c:30
SC_ATOMIC_INIT
#define SC_ATOMIC_INIT(name)
wrapper for initializing an atomic variable.
Definition: util-atomic.h:314
DefragTrackerIncrUsecnt
#define DefragTrackerIncrUsecnt(dt)
Definition: defrag-hash.c:120
DefragHashKey6_::u32
uint32_t u32[11]
Definition: defrag-hash.c:379
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
DefragTrackerHashRow_
Definition: defrag-hash.h:60
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:269
Packet_::pcap_cnt
uint64_t pcap_cnt
Definition: decode.h:609
ParseSizeStringU64
int ParseSizeStringU64(const char *size, uint64_t *res)
Definition: util-misc.c:198
DefragHashKey4_::vlan_id
uint16_t vlan_id[VLAN_MAX_LAYERS]
Definition: defrag-hash.c:364
DefragTrackerQueueDestroy
void DefragTrackerQueueDestroy(DefragTrackerQueue *q)
Destroy a tracker queue.
Definition: defrag-queue.c:57
DefragHashKey4_::id
uint32_t id
Definition: defrag-hash.c:363
SC_ATOMIC_ADD
#define SC_ATOMIC_ADD(name, val)
add a value to our atomic variable
Definition: util-atomic.h:332
DefragTrackerHashRow
struct DefragTrackerHashRow_ DefragTrackerHashRow
DefragTrackerClearMemory
void DefragTrackerClearMemory(DefragTracker *dt)
Definition: defrag-hash.c:156
SCMutexLock
#define SCMutexLock(mut)
Definition: threads-debug.h:117
DefragTracker_::host_timeout
uint32_t host_timeout
Definition: defrag.h:111
RandomGet
long int RandomGet(void)
Definition: util-random.c:130
DefragTrackerGetMemuse
uint64_t DefragTrackerGetMemuse(void)
Return memuse value.
Definition: defrag-hash.c:72
defrag-config.h
DefragPolicyGetHostTimeout
int DefragPolicyGetHostTimeout(Packet *p)
Definition: defrag-config.c:92
ExceptionPolicyParse
enum ExceptionPolicy ExceptionPolicyParse(const char *option, bool support_flow)
Definition: util-exception-policy.c:232
DefragTrackerMoveToSpare
void DefragTrackerMoveToSpare(DefragTracker *h)
Definition: defrag-hash.c:83
ConfValIsTrue
int ConfValIsTrue(const char *val)
Check if a value is true.
Definition: conf.c:537
DefragTracker_::hprev
struct DefragTracker_ * hprev
Definition: defrag.h:120
DefragTracker_::lock
SCMutex lock
Definition: defrag.h:85
DefragConfig_::hash_size
uint32_t hash_size
Definition: defrag-hash.h:72
DefragGetOsPolicy
uint8_t DefragGetOsPolicy(Packet *p)
Get the defrag policy based on the destination address of the packet.
Definition: defrag.c:980
DefragConfig_::prealloc
uint32_t prealloc
Definition: defrag-hash.h:73
DefragHashKey6_::src
uint32_t src[4]
Definition: defrag-hash.c:374
ConfGet
int ConfGet(const char *name, const char **vptr)
Retrieve the value of a configuration node.
Definition: conf.c:335
DefragTrackerHashRow_::head
DefragTracker * head
Definition: defrag-hash.h:62
DefragTracker_::remove
uint8_t remove
Definition: defrag.h:104
defrag_config
DefragConfig defrag_config
Definition: defrag-hash.c:31
DefragHashKey6_::vlan_id
uint16_t vlan_id[VLAN_MAX_LAYERS]
Definition: defrag-hash.c:376
DefragTracker_::seen_last
uint8_t seen_last
Definition: defrag.h:102
DefragTracker_::policy
uint8_t policy
Definition: defrag.h:97
SCMutexUnlock
#define SCMutexUnlock(mut)
Definition: threads-debug.h:119
DefragTracker_
Definition: defrag.h:84
DEFRAG_CHECK_MEMCAP
#define DEFRAG_CHECK_MEMCAP(size)
check if a memory alloc would fit in the memcap
Definition: defrag-hash.h:84
DefragTracker_::vlan_id
uint16_t vlan_id[VLAN_MAX_LAYERS]
Definition: defrag.h:88
ExceptionPolicyCounters_::eps_id
uint16_t eps_id[EXCEPTION_POLICY_MAX]
Definition: util-exception-policy-types.h:45
ThreadVars_
Per thread variable structure.
Definition: threadvars.h:57
DefragTrackerHashRow_::tail
DefragTracker * tail
Definition: defrag-hash.h:63
DEFRAG_DEFAULT_HASHSIZE
#define DEFRAG_DEFAULT_HASHSIZE
Definition: defrag-hash.c:161
StringParseUint32
int StringParseUint32(uint32_t *res, int base, size_t len, const char *str)
Definition: util-byte.c:313
DefragHashKey6_::dst
uint32_t dst[4]
Definition: defrag-hash.c:374
defrag-queue.h
DefragTrackerGetMemcap
uint64_t DefragTrackerGetMemcap(void)
Return memcap value.
Definition: defrag-hash.c:61
DefragTracker_::dst_addr
Address dst_addr
Definition: defrag.h:107
DefragTrackerDequeue
DefragTracker * DefragTrackerDequeue(DefragTrackerQueue *q)
remove a tracker from the queue
Definition: defrag-queue.c:101
DRLOCK_LOCK
#define DRLOCK_LOCK(fb)
Definition: defrag-hash.h:53
BUG_ON
#define BUG_ON(x)
Definition: suricata-common.h:300
SC_ATOMIC_SUB
#define SC_ATOMIC_SUB(name, val)
sub a value from our atomic variable
Definition: util-atomic.h:341
DefragHashKey6
struct DefragHashKey6_ DefragHashKey6
Packet_
Definition: decode.h:482
DefragTracker_::id
uint32_t id
Definition: defrag.h:92
DefragTracker_::src_addr
Address src_addr
Definition: defrag.h:106
DRLOCK_UNLOCK
#define DRLOCK_UNLOCK(fb)
Definition: defrag-hash.h:55
conf.h
DefragInitConfig
void DefragInitConfig(bool quiet)
initialize the configuration
Definition: defrag-hash.c:167
DefragTrackerQueue_
Definition: defrag-queue.h:42
DEFRAG_DEFAULT_MEMCAP
#define DEFRAG_DEFAULT_MEMCAP
Definition: defrag-hash.c:162
DefragHashKey4_::u32
uint32_t u32[5]
Definition: defrag-hash.c:367
DefragTrackerRelease
void DefragTrackerRelease(DefragTracker *t)
Definition: defrag-hash.c:150
DefragTracker_::proto
uint8_t proto
Definition: defrag.h:95
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
DefragConfig_::memcap_policy
enum ExceptionPolicy memcap_policy
Definition: defrag-hash.h:74
dtv
DecodeThreadVars * dtv
Definition: fuzz_decodepcapfile.c:33
DecodeThreadVars_::counter_defrag_memcap_eps
ExceptionPolicyCounters counter_defrag_memcap_eps
Definition: decode.h:1002
IPV4Hdr_
Definition: decode-ipv4.h:72
DRLOCK_DESTROY
#define DRLOCK_DESTROY(fb)
Definition: defrag-hash.h:52
DefragTrackerQueue_::len
uint32_t len
Definition: defrag-queue.h:45
cnt
uint32_t cnt
Definition: tmqh-packetpool.h:7
suricata-common.h
DefragHashKey4_::pad
uint16_t pad[1]
Definition: defrag-hash.c:365
DefragTrackerQueueInit
DefragTrackerQueue * DefragTrackerQueueInit(DefragTrackerQueue *q)
Definition: defrag-queue.c:32
DefragHashKey4_
Definition: defrag-hash.c:359
DefragHashKey6_
Definition: defrag-hash.c:371
VLAN_MAX_LAYERS
#define VLAN_MAX_LAYERS
Definition: decode-vlan.h:51
DefragConfig_
Definition: defrag-hash.h:69
CMP_DEFRAGTRACKER
#define CMP_DEFRAGTRACKER(d1, d2, id)
Definition: defrag-hash.c:447
FatalError
#define FatalError(...)
Definition: util-debug.h:502
util-hash-lookup3.h
DRLOCK_TRYLOCK
#define DRLOCK_TRYLOCK(fb)
Definition: defrag-hash.h:54
tv
ThreadVars * tv
Definition: fuzz_decodepcapfile.c:32
DefragTrackerSetMemcap
int DefragTrackerSetMemcap(uint64_t size)
Update memcap value.
Definition: defrag-hash.c:46
util-validate.h
DefragConfig_::hash_rand
uint32_t hash_rand
Definition: defrag-hash.h:71
SCLogConfig
struct SCLogConfig_ SCLogConfig
Holds the config state used by the logging api.
DefragTrackerEnqueue
void DefragTrackerEnqueue(DefragTrackerQueue *q, DefragTracker *dt)
add a tracker to a queue
Definition: defrag-queue.c:68
SCLogError
#define SCLogError(...)
Macro used to log ERROR messages.
Definition: util-debug.h:261
SCFree
#define SCFree(p)
Definition: util-mem.h:61
DecodeThreadVars_
Structure to hold thread specific data for all decode modules.
Definition: decode.h:946
DRLOCK_INIT
#define DRLOCK_INIT(fb)
Definition: defrag-hash.h:51
DefragTracker_::af
uint8_t af
Definition: defrag.h:99
util-random.h
DEFRAG_DEFAULT_PREALLOC
#define DEFRAG_DEFAULT_PREALLOC
Definition: defrag-hash.c:163
DefragTrackerDecrUsecnt
#define DefragTrackerDecrUsecnt(dt)
Definition: defrag-hash.c:122
defrag-hash.h
Packet_::dst
Address dst
Definition: decode.h:487
DefragHashKey6_::id
uint32_t id
Definition: defrag-hash.c:375
Packet_::vlan_id
uint16_t vlan_id[VLAN_MAX_LAYERS]
Definition: decode.h:509
likely
#define likely(expr)
Definition: util-optimize.h:32
DefragGetTrackerFromHash
DefragTracker * DefragGetTrackerFromHash(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p)
Definition: defrag-hash.c:550
DefragHashShutdown
void DefragHashShutdown(void)
shutdown the flow engine
Definition: defrag-hash.c:303
SC_ATOMIC_GET
#define SC_ATOMIC_GET(name)
Get the value from the atomic variable.
Definition: util-atomic.h:375
util-misc.h
COPY_ADDRESS
#define COPY_ADDRESS(a, b)
Definition: decode.h:132
SC_ATOMIC_DECLARE
SC_ATOMIC_DECLARE(uint64_t, defrag_memuse)
DefragHashKey6_::pad
uint16_t pad[1]
Definition: defrag-hash.c:377
SCLogNotice
#define SCLogNotice(...)
Macro used to log NOTICE messages.
Definition: util-debug.h:237
ExceptionPolicy
ExceptionPolicy
Definition: util-exception-policy-types.h:25
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
SCMutexDestroy
#define SCMutexDestroy
Definition: threads-debug.h:120
DefragHashKey4_::dst
uint32_t dst
Definition: defrag-hash.c:362
DefragGetMemcapExceptionPolicy
enum ExceptionPolicy DefragGetMemcapExceptionPolicy(void)
Definition: defrag-hash.c:78
DEBUG_VALIDATE_BUG_ON
#define DEBUG_VALIDATE_BUG_ON(exp)
Definition: util-validate.h:102
Packet_::src
Address src
Definition: decode.h:486
SCMutexTrylock
#define SCMutexTrylock(mut)
Definition: threads-debug.h:118