Coverage Report

Created: 2026-11-12 06:59

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