Coverage Report

Created: 2026-02-14 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/host.c
Line
Count
Source
1
/* Copyright (C) 2007-2012 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
 * Information about hosts.
24
 */
25
26
#include "suricata-common.h"
27
#include "conf.h"
28
29
#include "util-debug.h"
30
#include "host.h"
31
#include "host-storage.h"
32
#include "host-bit.h"
33
34
#include "util-random.h"
35
#include "util-misc.h"
36
#include "util-byte.h"
37
#include "util-validate.h"
38
39
#include "host-queue.h"
40
41
#include "detect-tag.h"
42
#include "detect-engine-tag.h"
43
#include "detect-engine-threshold.h"
44
45
#include "util-hash-lookup3.h"
46
47
static Host *HostGetUsedHost(void);
48
49
/** host hash table */
50
HostHashRow *host_hash;
51
/** queue with spare hosts */
52
static HostQueue host_spare_q;
53
HostConfig host_config;
54
55
SC_ATOMIC_DECLARE(uint64_t,host_memuse);
56
SC_ATOMIC_DECLARE(uint32_t,host_counter);
57
SC_ATOMIC_DECLARE(uint32_t,host_prune_idx);
58
59
/** size of the host object. Maybe updated in HostInitConfig to include
60
 *  the storage APIs additions. */
61
static uint16_t g_host_size = sizeof(Host);
62
63
/**
64
 *  \brief Update memcap value
65
 *
66
 *  \param size new memcap value
67
 */
68
int HostSetMemcap(uint64_t size)
69
0
{
70
0
    if ((uint64_t)SC_ATOMIC_GET(host_memuse) < size) {
71
0
        SC_ATOMIC_SET(host_config.memcap, size);
72
0
        return 1;
73
0
    }
74
75
0
    return 0;
76
0
}
77
78
/**
79
 *  \brief Return memcap value
80
 *
81
 *  \retval memcap value
82
 */
83
uint64_t HostGetMemcap(void)
84
0
{
85
0
    uint64_t memcapcopy = SC_ATOMIC_GET(host_config.memcap);
86
0
    return memcapcopy;
87
0
}
88
89
/**
90
 *  \brief Return memuse value
91
 *
92
 *  \retval memuse value
93
 */
94
uint64_t HostGetMemuse(void)
95
0
{
96
0
    uint64_t memuse = SC_ATOMIC_GET(host_memuse);
97
0
    return memuse;
98
0
}
99
100
uint32_t HostSpareQueueGetSize(void)
101
0
{
102
0
    return HostQueueLen(&host_spare_q);
103
0
}
104
105
void HostMoveToSpare(Host *h)
106
0
{
107
0
    HostEnqueue(&host_spare_q, h);
108
0
    (void) SC_ATOMIC_SUB(host_counter, 1);
109
0
}
110
111
Host *HostAlloc(void)
112
71.0k
{
113
71.0k
    if (!(HOST_CHECK_MEMCAP(g_host_size))) {
114
0
        return NULL;
115
0
    }
116
71.0k
    (void) SC_ATOMIC_ADD(host_memuse, g_host_size);
117
118
71.0k
    Host *h = SCMalloc(g_host_size);
119
71.0k
    if (unlikely(h == NULL))
120
0
        goto error;
121
122
71.0k
    memset(h, 0x00, g_host_size);
123
124
71.0k
    SCMutexInit(&h->m, NULL);
125
71.0k
    SC_ATOMIC_INIT(h->use_cnt);
126
71.0k
    return h;
127
128
0
error:
129
0
    return NULL;
130
71.0k
}
131
132
void HostFree(Host *h)
133
0
{
134
0
    if (h != NULL) {
135
0
        HostClearMemory(h);
136
0
        SCMutexDestroy(&h->m);
137
0
        SCFree(h);
138
0
        (void) SC_ATOMIC_SUB(host_memuse, g_host_size);
139
0
    }
140
0
}
141
142
static Host *HostNew(Address *a)
143
0
{
144
0
    Host *h = HostAlloc();
145
0
    if (h == NULL)
146
0
        goto error;
147
148
    /* copy address */
149
0
    COPY_ADDRESS(a, &h->a);
150
151
0
    return h;
152
153
0
error:
154
0
    return NULL;
155
0
}
156
157
void HostClearMemory(Host *h)
158
0
{
159
0
    if (h->iprep != NULL) {
160
0
        SRepFreeHostData(h);
161
0
    }
162
163
0
    if (HostStorageSize() > 0)
164
0
        HostFreeStorage(h);
165
166
0
    BUG_ON(SC_ATOMIC_GET(h->use_cnt) > 0);
167
0
}
168
169
33
#define HOST_DEFAULT_HASHSIZE 4096
170
#define HOST_DEFAULT_MEMCAP 16777216
171
33
#define HOST_DEFAULT_PREALLOC 1000
172
173
/** \brief initialize the configuration
174
 *  \warning Not thread safe */
175
void HostInitConfig(bool quiet)
176
33
{
177
33
    SCLogDebug("initializing host engine...");
178
33
    if (HostStorageSize() > 0) {
179
33
        DEBUG_VALIDATE_BUG_ON(sizeof(Host) + HostStorageSize() > UINT16_MAX);
180
33
        g_host_size = (uint16_t)(sizeof(Host) + HostStorageSize());
181
33
    }
182
183
33
    memset(&host_config,  0, sizeof(host_config));
184
    //SC_ATOMIC_INIT(flow_flags);
185
33
    SC_ATOMIC_INIT(host_counter);
186
33
    SC_ATOMIC_INIT(host_memuse);
187
33
    SC_ATOMIC_INIT(host_prune_idx);
188
33
    SC_ATOMIC_INIT(host_config.memcap);
189
33
    HostQueueInit(&host_spare_q);
190
191
    /* set defaults */
192
33
    host_config.hash_rand   = (uint32_t)RandomGet();
193
33
    host_config.hash_size   = HOST_DEFAULT_HASHSIZE;
194
33
    host_config.prealloc    = HOST_DEFAULT_PREALLOC;
195
33
    SC_ATOMIC_SET(host_config.memcap, HOST_DEFAULT_MEMCAP);
196
197
    /* Check if we have memcap and hash_size defined at config */
198
33
    const char *conf_val;
199
33
    uint32_t configval = 0;
200
201
    /** set config values for memcap, prealloc and hash_size */
202
33
    if ((ConfGet("host.memcap", &conf_val)) == 1) {
203
0
        uint64_t host_memcap = 0;
204
0
        if (ParseSizeStringU64(conf_val, &host_memcap) < 0) {
205
0
            SCLogError("Error parsing host.memcap "
206
0
                       "from conf file - %s.  Killing engine",
207
0
                    conf_val);
208
0
            exit(EXIT_FAILURE);
209
0
        } else {
210
0
            SC_ATOMIC_SET(host_config.memcap, host_memcap);
211
0
        }
212
0
    }
213
33
    if ((ConfGet("host.hash-size", &conf_val)) == 1) {
214
0
        if (StringParseUint32(&configval, 10, strlen(conf_val),
215
0
                                    conf_val) > 0) {
216
0
            host_config.hash_size = configval;
217
0
        }
218
0
    }
219
220
33
    if ((ConfGet("host.prealloc", &conf_val)) == 1) {
221
0
        if (StringParseUint32(&configval, 10, strlen(conf_val),
222
0
                                    conf_val) > 0) {
223
0
            host_config.prealloc = configval;
224
0
        } else {
225
0
            WarnInvalidConfEntry("host.prealloc", "%"PRIu32, host_config.prealloc);
226
0
        }
227
0
    }
228
33
    SCLogDebug("Host config from suricata.yaml: memcap: %"PRIu64", hash-size: "
229
33
               "%"PRIu32", prealloc: %"PRIu32, SC_ATOMIC_GET(host_config.memcap),
230
33
               host_config.hash_size, host_config.prealloc);
231
232
    /* alloc hash memory */
233
33
    uint64_t hash_size = host_config.hash_size * sizeof(HostHashRow);
234
33
    if (!(HOST_CHECK_MEMCAP(hash_size))) {
235
0
        SCLogError("allocating host hash failed: "
236
0
                   "max host memcap is smaller than projected hash size. "
237
0
                   "Memcap: %" PRIu64 ", Hash table size %" PRIu64 ". Calculate "
238
0
                   "total hash size by multiplying \"host.hash-size\" with %" PRIuMAX ", "
239
0
                   "which is the hash bucket size.",
240
0
                SC_ATOMIC_GET(host_config.memcap), hash_size, (uintmax_t)sizeof(HostHashRow));
241
0
        exit(EXIT_FAILURE);
242
0
    }
243
33
    host_hash = SCMallocAligned(host_config.hash_size * sizeof(HostHashRow), CLS);
244
33
    if (unlikely(host_hash == NULL)) {
245
0
        FatalError("Fatal error encountered in HostInitConfig. Exiting...");
246
0
    }
247
33
    memset(host_hash, 0, host_config.hash_size * sizeof(HostHashRow));
248
249
33
    uint32_t i = 0;
250
135k
    for (i = 0; i < host_config.hash_size; i++) {
251
135k
        HRLOCK_INIT(&host_hash[i]);
252
135k
    }
253
33
    (void) SC_ATOMIC_ADD(host_memuse, (host_config.hash_size * sizeof(HostHashRow)));
254
255
33
    if (!quiet) {
256
33
        SCLogConfig("allocated %"PRIu64" bytes of memory for the host hash... "
257
33
                  "%" PRIu32 " buckets of size %" PRIuMAX "",
258
33
                  SC_ATOMIC_GET(host_memuse), host_config.hash_size,
259
33
                  (uintmax_t)sizeof(HostHashRow));
260
33
    }
261
262
    /* pre allocate hosts */
263
33.0k
    for (i = 0; i < host_config.prealloc; i++) {
264
33.0k
        if (!(HOST_CHECK_MEMCAP(g_host_size))) {
265
0
            SCLogError("preallocating hosts failed: "
266
0
                       "max host memcap reached. Memcap %" PRIu64 ", "
267
0
                       "Memuse %" PRIu64 ".",
268
0
                    SC_ATOMIC_GET(host_config.memcap),
269
0
                    ((uint64_t)SC_ATOMIC_GET(host_memuse) + g_host_size));
270
0
            exit(EXIT_FAILURE);
271
0
        }
272
273
33.0k
        Host *h = HostAlloc();
274
33.0k
        if (h == NULL) {
275
0
            SCLogError("preallocating host failed: %s", strerror(errno));
276
0
            exit(EXIT_FAILURE);
277
0
        }
278
33.0k
        HostEnqueue(&host_spare_q,h);
279
33.0k
    }
280
281
33
    if (!quiet) {
282
33
        SCLogConfig("preallocated %" PRIu32 " hosts of size %" PRIu16 "",
283
33
                host_spare_q.len, g_host_size);
284
33
        SCLogConfig("host memory usage: %"PRIu64" bytes, maximum: %"PRIu64,
285
33
                SC_ATOMIC_GET(host_memuse), SC_ATOMIC_GET(host_config.memcap));
286
33
    }
287
288
33
    return;
289
33
}
290
291
/** \brief print some host stats
292
 *  \warning Not thread safe */
293
void HostPrintStats (void)
294
0
{
295
#ifdef HOSTBITS_STATS
296
    SCLogPerf("hostbits added: %" PRIu32 ", removed: %" PRIu32 ", max memory usage: %" PRIu32 "",
297
        hostbits_added, hostbits_removed, hostbits_memuse_max);
298
#endif /* HOSTBITS_STATS */
299
0
    SCLogPerf("host memory usage: %"PRIu64" bytes, maximum: %"PRIu64,
300
0
            SC_ATOMIC_GET(host_memuse), SC_ATOMIC_GET(host_config.memcap));
301
0
    return;
302
0
}
303
304
/** \brief shutdown the flow engine
305
 *  \warning Not thread safe */
306
void HostShutdown(void)
307
0
{
308
0
    Host *h;
309
0
    uint32_t u;
310
311
0
    HostPrintStats();
312
313
    /* free spare queue */
314
0
    while((h = HostDequeue(&host_spare_q))) {
315
0
        HostFree(h);
316
0
    }
317
318
    /* clear and free the hash */
319
0
    if (host_hash != NULL) {
320
0
        for (u = 0; u < host_config.hash_size; u++) {
321
0
            h = host_hash[u].head;
322
0
            while (h) {
323
0
                Host *n = h->hnext;
324
0
                HostFree(h);
325
0
                h = n;
326
0
            }
327
328
0
            HRLOCK_DESTROY(&host_hash[u]);
329
0
        }
330
0
        SCFreeAligned(host_hash);
331
0
        host_hash = NULL;
332
0
    }
333
0
    (void) SC_ATOMIC_SUB(host_memuse, host_config.hash_size * sizeof(HostHashRow));
334
0
    HostQueueDestroy(&host_spare_q);
335
0
    return;
336
0
}
337
338
/** \brief Cleanup the host engine
339
 *
340
 * Cleanup the host engine from tag and threshold.
341
 *
342
 */
343
void HostCleanup(void)
344
0
{
345
0
    Host *h;
346
0
    uint32_t u;
347
348
0
    if (host_hash != NULL) {
349
0
        for (u = 0; u < host_config.hash_size; u++) {
350
0
            h = host_hash[u].head;
351
0
            HostHashRow *hb = &host_hash[u];
352
0
            HRLOCK_LOCK(hb);
353
0
            while (h) {
354
0
                if ((SC_ATOMIC_GET(h->use_cnt) > 0) && (h->iprep != NULL)) {
355
                    /* iprep is attached to host only clear local storage */
356
0
                    HostFreeStorage(h);
357
0
                    h = h->hnext;
358
0
                } else {
359
0
                    Host *n = h->hnext;
360
                    /* remove from the hash */
361
0
                    if (h->hprev != NULL)
362
0
                        h->hprev->hnext = h->hnext;
363
0
                    if (h->hnext != NULL)
364
0
                        h->hnext->hprev = h->hprev;
365
0
                    if (hb->head == h)
366
0
                        hb->head = h->hnext;
367
0
                    if (hb->tail == h)
368
0
                        hb->tail = h->hprev;
369
0
                    h->hnext = NULL;
370
0
                    h->hprev = NULL;
371
0
                    HostClearMemory(h);
372
0
                    HostMoveToSpare(h);
373
0
                    h = n;
374
0
                }
375
0
            }
376
0
            HRLOCK_UNLOCK(hb);
377
0
        }
378
0
    }
379
380
0
    return;
381
0
}
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
 */
389
static inline uint32_t HostGetKey(Address *a)
390
207k
{
391
207k
    uint32_t key;
392
393
207k
    if (a->family == AF_INET) {
394
132k
        uint32_t hash = hashword(&a->addr_data32[0], 1, host_config.hash_rand);
395
132k
        key = hash % host_config.hash_size;
396
132k
    } else if (a->family == AF_INET6) {
397
21.9k
        uint32_t hash = hashword(a->addr_data32, 4, host_config.hash_rand);
398
21.9k
        key = hash % host_config.hash_size;
399
21.9k
    } else
400
53.2k
        key = 0;
401
402
207k
    return key;
403
207k
}
404
405
static inline int HostCompare(Host *h, Address *a)
406
62.6k
{
407
62.6k
    if (h->a.family == a->family) {
408
62.5k
        switch (a->family) {
409
53.3k
            case AF_INET:
410
53.3k
                return (h->a.addr_data32[0] == a->addr_data32[0]);
411
9.22k
            case AF_INET6:
412
9.22k
                return CMP_ADDR(&h->a, a);
413
62.5k
        }
414
62.5k
    }
415
5
    return 0;
416
62.6k
}
417
418
/**
419
 *  \brief Get a new host
420
 *
421
 *  Get a new host. We're checking memcap first and will try to make room
422
 *  if the memcap is reached.
423
 *
424
 *  \retval h *LOCKED* host on succes, NULL on error.
425
 */
426
static Host *HostGetNew(Address *a)
427
160
{
428
160
    Host *h = NULL;
429
430
    /* get a host from the spare queue */
431
160
    h = HostDequeue(&host_spare_q);
432
160
    if (h == NULL) {
433
        /* If we reached the max memcap, we get a used host */
434
0
        if (!(HOST_CHECK_MEMCAP(g_host_size))) {
435
            /* declare state of emergency */
436
            //if (!(SC_ATOMIC_GET(host_flags) & HOST_EMERGENCY)) {
437
            //    SC_ATOMIC_OR(host_flags, HOST_EMERGENCY);
438
439
                /* under high load, waking up the flow mgr each time leads
440
                 * to high cpu usage. Flows are not timed out much faster if
441
                 * we check a 1000 times a second. */
442
            //    FlowWakeupFlowManagerThread();
443
            //}
444
445
0
            h = HostGetUsedHost();
446
0
            if (h == NULL) {
447
0
                return NULL;
448
0
            }
449
450
            /* freed a host, but it's unlocked */
451
0
        } else {
452
            /* now see if we can alloc a new host */
453
0
            h = HostNew(a);
454
0
            if (h == NULL) {
455
0
                return NULL;
456
0
            }
457
458
            /* host is initialized but *unlocked* */
459
0
        }
460
160
    } else {
461
        /* host has been recycled before it went into the spare queue */
462
463
        /* host is initialized (recycled) but *unlocked* */
464
160
    }
465
466
160
    (void) SC_ATOMIC_ADD(host_counter, 1);
467
160
    SCMutexLock(&h->m);
468
160
    return h;
469
160
}
470
471
static void HostInit(Host *h, Address *a)
472
160
{
473
160
    COPY_ADDRESS(a, &h->a);
474
160
    (void) HostIncrUsecnt(h);
475
160
}
476
477
void HostRelease(Host *h)
478
1.80k
{
479
1.80k
    (void) HostDecrUsecnt(h);
480
1.80k
    SCMutexUnlock(&h->m);
481
1.80k
}
482
483
void HostLock(Host *h)
484
21
{
485
21
    SCMutexLock(&h->m);
486
21
}
487
488
void HostUnlock(Host *h)
489
60.9k
{
490
60.9k
    SCMutexUnlock(&h->m);
491
60.9k
}
492
493
494
/* HostGetHostFromHash
495
 *
496
 * Hash retrieval function for hosts. Looks up the hash bucket containing the
497
 * host pointer. Then compares the packet with the found host to see if it is
498
 * the host we need. If it isn't, walk the list until the right host is found.
499
 *
500
 * returns a *LOCKED* host or NULL
501
 */
502
Host *HostGetHostFromHash (Address *a)
503
1.91k
{
504
1.91k
    Host *h = NULL;
505
506
    /* get the key to our bucket */
507
1.91k
    uint32_t key = HostGetKey(a);
508
    /* get our hash bucket and lock it */
509
1.91k
    HostHashRow *hb = &host_hash[key];
510
1.91k
    HRLOCK_LOCK(hb);
511
512
    /* see if the bucket already has a host */
513
1.91k
    if (hb->head == NULL) {
514
156
        h = HostGetNew(a);
515
156
        if (h == NULL) {
516
0
            HRLOCK_UNLOCK(hb);
517
0
            return NULL;
518
0
        }
519
520
        /* host is locked */
521
156
        hb->head = h;
522
156
        hb->tail = h;
523
524
        /* got one, now lock, initialize and return */
525
156
        HostInit(h,a);
526
527
156
        HRLOCK_UNLOCK(hb);
528
156
        return h;
529
156
    }
530
531
    /* ok, we have a host in the bucket. Let's find out if it is our host */
532
1.75k
    h = hb->head;
533
534
    /* see if this is the host we are looking for */
535
1.75k
    if (HostCompare(h, a) == 0) {
536
6
        Host *ph = NULL; /* previous host */
537
538
8
        while (h) {
539
8
            ph = h;
540
8
            h = h->hnext;
541
542
8
            if (h == NULL) {
543
4
                h = ph->hnext = HostGetNew(a);
544
4
                if (h == NULL) {
545
0
                    HRLOCK_UNLOCK(hb);
546
0
                    return NULL;
547
0
                }
548
4
                hb->tail = h;
549
550
                /* host is locked */
551
552
4
                h->hprev = ph;
553
554
                /* initialize and return */
555
4
                HostInit(h,a);
556
557
4
                HRLOCK_UNLOCK(hb);
558
4
                return h;
559
4
            }
560
561
4
            if (HostCompare(h, a) != 0) {
562
                /* we found our host, lets put it on top of the
563
                 * hash list -- this rewards active hosts */
564
2
                if (h->hnext) {
565
1
                    h->hnext->hprev = h->hprev;
566
1
                }
567
2
                if (h->hprev) {
568
2
                    h->hprev->hnext = h->hnext;
569
2
                }
570
2
                if (h == hb->tail) {
571
1
                    hb->tail = h->hprev;
572
1
                }
573
574
2
                h->hnext = hb->head;
575
2
                h->hprev = NULL;
576
2
                hb->head->hprev = h;
577
2
                hb->head = h;
578
579
                /* found our host, lock & return */
580
2
                SCMutexLock(&h->m);
581
2
                (void) HostIncrUsecnt(h);
582
2
                HRLOCK_UNLOCK(hb);
583
2
                return h;
584
2
            }
585
4
        }
586
6
    }
587
588
    /* lock & return */
589
1.75k
    SCMutexLock(&h->m);
590
1.75k
    (void) HostIncrUsecnt(h);
591
1.75k
    HRLOCK_UNLOCK(hb);
592
1.75k
    return h;
593
1.75k
}
594
595
/** \brief look up a host in the hash
596
 *
597
 *  \param a address to look up
598
 *
599
 *  \retval h *LOCKED* host or NULL
600
 */
601
Host *HostLookupHostFromHash (Address *a)
602
205k
{
603
205k
    Host *h = NULL;
604
605
    /* get the key to our bucket */
606
205k
    uint32_t key = HostGetKey(a);
607
    /* get our hash bucket and lock it */
608
205k
    HostHashRow *hb = &host_hash[key];
609
205k
    HRLOCK_LOCK(hb);
610
611
    /* see if the bucket already has a host */
612
205k
    if (hb->head == NULL) {
613
144k
        HRLOCK_UNLOCK(hb);
614
144k
        return h;
615
144k
    }
616
617
    /* ok, we have a host in the bucket. Let's find out if it is our host */
618
60.8k
    h = hb->head;
619
620
    /* see if this is the host we are looking for */
621
60.8k
    if (HostCompare(h, a) == 0) {
622
27
        while (h) {
623
27
            h = h->hnext;
624
625
27
            if (h == NULL) {
626
27
                HRLOCK_UNLOCK(hb);
627
27
                return h;
628
27
            }
629
630
0
            if (HostCompare(h, a) != 0) {
631
                /* we found our host, lets put it on top of the
632
                 * hash list -- this rewards active hosts */
633
0
                if (h->hnext) {
634
0
                    h->hnext->hprev = h->hprev;
635
0
                }
636
0
                if (h->hprev) {
637
0
                    h->hprev->hnext = h->hnext;
638
0
                }
639
0
                if (h == hb->tail) {
640
0
                    hb->tail = h->hprev;
641
0
                }
642
643
0
                h->hnext = hb->head;
644
0
                h->hprev = NULL;
645
0
                hb->head->hprev = h;
646
0
                hb->head = h;
647
648
                /* found our host, lock & return */
649
0
                SCMutexLock(&h->m);
650
0
                (void) HostIncrUsecnt(h);
651
0
                HRLOCK_UNLOCK(hb);
652
0
                return h;
653
0
            }
654
0
        }
655
27
    }
656
657
    /* lock & return */
658
60.8k
    SCMutexLock(&h->m);
659
60.8k
    (void) HostIncrUsecnt(h);
660
60.8k
    HRLOCK_UNLOCK(hb);
661
60.8k
    return h;
662
60.8k
}
663
664
/** \internal
665
 *  \brief Get a host from the hash directly.
666
 *
667
 *  Called in conditions where the spare queue is empty and memcap is reached.
668
 *
669
 *  Walks the hash until a host can be freed. "host_prune_idx" atomic int makes
670
 *  sure we don't start at the top each time since that would clear the top of
671
 *  the hash leading to longer and longer search times under high pressure (observed).
672
 *
673
 *  \retval h host or NULL
674
 */
675
static Host *HostGetUsedHost(void)
676
0
{
677
0
    uint32_t idx = SC_ATOMIC_GET(host_prune_idx) % host_config.hash_size;
678
0
    uint32_t cnt = host_config.hash_size;
679
680
0
    while (cnt--) {
681
0
        if (++idx >= host_config.hash_size)
682
0
            idx = 0;
683
684
0
        HostHashRow *hb = &host_hash[idx];
685
686
0
        if (HRLOCK_TRYLOCK(hb) != 0)
687
0
            continue;
688
689
0
        Host *h = hb->tail;
690
0
        if (h == NULL) {
691
0
            HRLOCK_UNLOCK(hb);
692
0
            continue;
693
0
        }
694
695
0
        if (SCMutexTrylock(&h->m) != 0) {
696
0
            HRLOCK_UNLOCK(hb);
697
0
            continue;
698
0
        }
699
700
        /** never prune a host that is used by a packets
701
         *  we are currently processing in one of the threads */
702
0
        if (SC_ATOMIC_GET(h->use_cnt) > 0) {
703
0
            HRLOCK_UNLOCK(hb);
704
0
            SCMutexUnlock(&h->m);
705
0
            continue;
706
0
        }
707
708
        /* remove from the hash */
709
0
        if (h->hprev != NULL)
710
0
            h->hprev->hnext = h->hnext;
711
0
        if (h->hnext != NULL)
712
0
            h->hnext->hprev = h->hprev;
713
0
        if (hb->head == h)
714
0
            hb->head = h->hnext;
715
0
        if (hb->tail == h)
716
0
            hb->tail = h->hprev;
717
718
0
        h->hnext = NULL;
719
0
        h->hprev = NULL;
720
0
        HRLOCK_UNLOCK(hb);
721
722
0
        HostClearMemory (h);
723
724
0
        SCMutexUnlock(&h->m);
725
726
0
        (void) SC_ATOMIC_ADD(host_prune_idx, (host_config.hash_size - cnt));
727
0
        return h;
728
0
    }
729
730
0
    return NULL;
731
0
}
732
733
void HostRegisterUnittests(void)
734
0
{
735
0
    RegisterHostStorageTests();
736
0
}
737