Coverage Report

Created: 2025-11-16 07:09

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