• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OISF / suricata / 23374838686

21 Mar 2026 07:29AM UTC coverage: 59.341% (-20.0%) from 79.315%
23374838686

Pull #15075

github

web-flow
Merge 90b4e834f into 6587e363a
Pull Request #15075: Stack 8001 v16.4

38 of 70 new or added lines in 10 files covered. (54.29%)

34165 existing lines in 563 files now uncovered.

119621 of 201584 relevant lines covered (59.34%)

650666.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

9.13
/src/util-macset.c
1
/* Copyright (C) 2020 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 Sascha Steinbiss <sascha.steinbiss@dcso.de>
22
 *
23
 * Set-like data store for MAC addresses. Implemented as array for memory
24
 * locality reasons as the expected number of items is typically low.
25
 *
26
 */
27

28
#include "suricata-common.h"
29
#include "suricata.h"
30
#include "flow-util.h"
31
#include "flow-private.h"
32
#include "flow-storage.h"
33
#include "util-macset.h"
34
#include "util-unittest.h"
35
#include "util-unittest-helper.h"
36
#include "conf.h"
37

38
typedef uint8_t MacAddr[6];
39
typedef enum {
40
    EMPTY_SET,  /* no address inserted yet */
41
    SINGLE_MAC, /* we have a single pair of addresses (likely) */
42
    MULTI_MAC   /* we have multiple addresses per flow */
43
} MacSetState;
44

45
struct MacSet_ {
46
    /* static store for a single MAC address per side */
47
    MacAddr singles[2];
48
    /* state determines how addresses are stored per side:
49
         - SINGLE_MAC uses static locations allocated with the MacSet
50
           itself to store a single address (most likely case)
51
         - MULTI_MAC is used when more than one distinct address
52
           is detected (causes another allocation and linear-time add) */
53
    MacSetState state[2];
54
    /* buffer for multiple MACs per flow and direction */
55
    MacAddr *buf[2];
56
    int size, last[2];
57
};
58

59
FlowStorageId g_macset_storage_id = { .id = -1 };
60

61
void MacSetRegisterFlowStorage(void)
62
{
2✔
63
    SCConfNode *root = SCConfGetNode("outputs");
2✔
64
    SCConfNode *node = NULL;
2✔
65
    /* we only need to register if at least one enabled 'eve-log' output
66
       has the ethernet setting enabled */
67
    if (root != NULL) {
2✔
68
        TAILQ_FOREACH (node, &root->head, next) {
5✔
69
            if (node->val && strcmp(node->val, "eve-log") == 0) {
5✔
70
                const char *enabled = SCConfNodeLookupChildValue(node->head.tqh_first, "enabled");
1✔
71
                if (enabled != NULL && SCConfValIsTrue(enabled)) {
1✔
72
                    const char *ethernet =
1✔
73
                            SCConfNodeLookupChildValue(node->head.tqh_first, "ethernet");
1✔
74
                    if (ethernet != NULL && SCConfValIsTrue(ethernet)) {
1✔
UNCOV
75
                        g_macset_storage_id = FlowStorageRegister(
×
UNCOV
76
                                "macset", sizeof(void *), NULL, (void (*)(void *))MacSetFree);
×
UNCOV
77
                        return;
×
UNCOV
78
                    }
×
79
                }
1✔
80
            }
1✔
81
        }
5✔
82
    }
1✔
83
}
2✔
84

85
bool MacSetFlowStorageEnabled(void)
86
{
1,110,123✔
87
    return (g_macset_storage_id.id != -1);
1,110,123✔
88
}
1,110,123✔
89

90
MacSet *MacSetInit(int size)
UNCOV
91
{
×
UNCOV
92
    MacSet *ms = NULL;
×
UNCOV
93
    if (!FLOW_CHECK_MEMCAP(sizeof(*ms))) {
×
UNCOV
94
        return NULL;
×
UNCOV
95
    }
×
UNCOV
96
    ms = SCCalloc(1, sizeof(*ms));
×
UNCOV
97
    if (unlikely(ms == NULL)) {
×
98
        SCLogError("Unable to allocate MacSet memory");
×
99
        return NULL;
×
100
    }
×
UNCOV
101
    (void)SC_ATOMIC_ADD(flow_memuse, (sizeof(*ms)));
×
UNCOV
102
    ms->state[MAC_SET_SRC] = ms->state[MAC_SET_DST] = EMPTY_SET;
×
UNCOV
103
    if (size < 3) {
×
104
        /* we want to make sure we have at space for at least 3 items to
105
           fit MACs during the initial extension to MULTI_MAC storage */
106
        size = 3;
×
107
    }
×
UNCOV
108
    ms->size = size;
×
UNCOV
109
    ms->last[MAC_SET_SRC] = ms->last[MAC_SET_DST] = 0;
×
UNCOV
110
    return ms;
×
UNCOV
111
}
×
112

113
FlowStorageId MacSetGetFlowStorageID(void)
UNCOV
114
{
×
UNCOV
115
    return g_macset_storage_id;
×
UNCOV
116
}
×
117

118
static inline void MacUpdateEntry(
119
        MacSet *ms, const uint8_t *addr, int side, ThreadVars *tv, StatsCounterMaxId ctr)
UNCOV
120
{
×
UNCOV
121
    switch (ms->state[side]) {
×
UNCOV
122
        case EMPTY_SET:
×
UNCOV
123
            memcpy(ms->singles[side], addr, sizeof(MacAddr));
×
UNCOV
124
            ms->state[side] = SINGLE_MAC;
×
UNCOV
125
            if (tv != NULL)
×
UNCOV
126
                StatsCounterMaxUpdateI64(&tv->stats, ctr, 1);
×
UNCOV
127
            break;
×
UNCOV
128
        case SINGLE_MAC:
×
UNCOV
129
            if (unlikely(memcmp(addr, ms->singles[side], sizeof(MacAddr)) != 0)) {
×
UNCOV
130
                if (ms->buf[side] == NULL) {
×
UNCOV
131
                    if (!FLOW_CHECK_MEMCAP(ms->size * sizeof(MacAddr))) {
×
132
                        /* in this case there is not much we can do */
UNCOV
133
                        return;
×
UNCOV
134
                    }
×
UNCOV
135
                    ms->buf[side] = SCCalloc(ms->size, sizeof(MacAddr));
×
UNCOV
136
                    if (unlikely(ms->buf[side] == NULL)) {
×
137
                        SCLogError("Unable to allocate "
×
138
                                   "MacSet memory");
×
139
                        return;
×
140
                    }
×
UNCOV
141
                    (void)SC_ATOMIC_ADD(flow_memuse, (ms->size * sizeof(MacAddr)));
×
UNCOV
142
                }
×
UNCOV
143
                memcpy(ms->buf[side], ms->singles[side], sizeof(MacAddr));
×
UNCOV
144
                memcpy(ms->buf[side] + 1, addr, sizeof(MacAddr));
×
UNCOV
145
                ms->last[side] = 2;
×
UNCOV
146
                if (tv != NULL)
×
UNCOV
147
                    StatsCounterMaxUpdateI64(&tv->stats, ctr, 2);
×
UNCOV
148
                ms->state[side] = MULTI_MAC;
×
UNCOV
149
            }
×
UNCOV
150
            break;
×
UNCOV
151
        case MULTI_MAC:
×
UNCOV
152
            if (unlikely(ms->last[side] == ms->size)) {
×
153
                /* MacSet full, ignore item. We intentionally do not output
154
                   any warning in order not to stall packet processing */
UNCOV
155
                return;
×
UNCOV
156
            }
×
157
            /* If the set is non-empty... */
UNCOV
158
            if (ms->last[side] > 0) {
×
159
                /* ...we search for duplicates in the set to decide whether
160
                   we need to insert the current item. We do this backwards,
161
                   since we expect the latest item to match more likely than
162
                   the first */
UNCOV
163
                for (int i = ms->last[side] - 1; i >= 0; i--) {
×
UNCOV
164
                    uint8_t *addr2 = (uint8_t *)((ms->buf[side]) + i);
×
165
                    /* If we find a match, we return early with no action */
UNCOV
166
                    if (likely(memcmp(addr2, addr, sizeof(MacAddr)) == 0)) {
×
UNCOV
167
                        return;
×
UNCOV
168
                    }
×
UNCOV
169
                }
×
UNCOV
170
            }
×
171
            /* Otherwise, we insert the new address at the end */
UNCOV
172
            memcpy(ms->buf[side] + ms->last[side], addr, sizeof(MacAddr));
×
UNCOV
173
            ms->last[side]++;
×
UNCOV
174
            if (tv != NULL)
×
175
                StatsCounterMaxUpdateI64(&tv->stats, ctr, (int64_t)ms->last[side]);
×
UNCOV
176
            break;
×
UNCOV
177
    }
×
UNCOV
178
}
×
179

180
void MacSetAddWithCtr(MacSet *ms, const uint8_t *src_addr, const uint8_t *dst_addr, ThreadVars *tv,
181
        StatsCounterMaxId ctr_src, StatsCounterMaxId ctr_dst)
UNCOV
182
{
×
UNCOV
183
    if (ms == NULL)
×
184
        return;
×
UNCOV
185
    MacUpdateEntry(ms, src_addr, MAC_SET_SRC, tv, ctr_src);
×
UNCOV
186
    MacUpdateEntry(ms, dst_addr, MAC_SET_DST, tv, ctr_dst);
×
UNCOV
187
}
×
188

189
void MacSetAdd(MacSet *ms, const uint8_t *src_addr, const uint8_t *dst_addr)
UNCOV
190
{
×
UNCOV
191
    StatsCounterMaxId no_counter = { .id = 0 };
×
UNCOV
192
    MacSetAddWithCtr(ms, src_addr, dst_addr, NULL, no_counter, no_counter);
×
UNCOV
193
}
×
194

195
static inline int MacSetIterateSide(
196
        const MacSet *ms, MacSetIteratorFunc IterFunc, MacSetSide side, void *data)
UNCOV
197
{
×
UNCOV
198
    int ret = 0;
×
UNCOV
199
    switch (ms->state[side]) {
×
UNCOV
200
        case EMPTY_SET:
×
UNCOV
201
            return 0;
×
UNCOV
202
        case SINGLE_MAC:
×
UNCOV
203
            ret = IterFunc((uint8_t *)ms->singles[side], side, data);
×
UNCOV
204
            if (unlikely(ret != 0)) {
×
205
                return ret;
×
206
            }
×
UNCOV
207
            break;
×
UNCOV
208
        case MULTI_MAC:
×
UNCOV
209
            for (int i = 0; i < ms->last[side]; i++) {
×
UNCOV
210
                ret = IterFunc((uint8_t *)ms->buf[side][i], side, data);
×
UNCOV
211
                if (unlikely(ret != 0)) {
×
212
                    return ret;
×
213
                }
×
UNCOV
214
            }
×
UNCOV
215
            break;
×
UNCOV
216
    }
×
UNCOV
217
    return 0;
×
UNCOV
218
}
×
219

220
int MacSetForEach(const MacSet *ms, MacSetIteratorFunc IterFunc, void *data)
UNCOV
221
{
×
UNCOV
222
    int ret = 0;
×
UNCOV
223
    if (ms == NULL)
×
224
        return 0;
×
225

UNCOV
226
    ret = MacSetIterateSide(ms, IterFunc, MAC_SET_SRC, data);
×
UNCOV
227
    if (ret != 0) {
×
228
        return ret;
×
229
    }
×
UNCOV
230
    return MacSetIterateSide(ms, IterFunc, MAC_SET_DST, data);
×
UNCOV
231
}
×
232

233
uint8_t *MacSetGetFirst(const MacSet *ms, MacSetSide side)
UNCOV
234
{
×
UNCOV
235
    switch (ms->state[side]) {
×
UNCOV
236
        case EMPTY_SET:
×
UNCOV
237
            return NULL;
×
UNCOV
238
        case SINGLE_MAC:
×
UNCOV
239
            return (uint8_t *)ms->singles[side];
×
UNCOV
240
        case MULTI_MAC:
×
UNCOV
241
            return (uint8_t *)ms->buf[side][0];
×
UNCOV
242
    }
×
243
    return NULL;
×
UNCOV
244
}
×
245

246
int MacSetSize(const MacSet *ms)
UNCOV
247
{
×
UNCOV
248
    int size = 0;
×
UNCOV
249
    if (ms == NULL)
×
250
        return 0;
×
251

UNCOV
252
    switch (ms->state[MAC_SET_SRC]) {
×
UNCOV
253
        case EMPTY_SET:
×
254
            /* pass */
UNCOV
255
            break;
×
UNCOV
256
        case SINGLE_MAC:
×
UNCOV
257
            size += 1;
×
UNCOV
258
            break;
×
UNCOV
259
        case MULTI_MAC:
×
UNCOV
260
            size += ms->last[MAC_SET_SRC];
×
UNCOV
261
            break;
×
UNCOV
262
    }
×
UNCOV
263
    switch (ms->state[MAC_SET_DST]) {
×
UNCOV
264
        case EMPTY_SET:
×
265
            /* pass */
UNCOV
266
            break;
×
UNCOV
267
        case SINGLE_MAC:
×
UNCOV
268
            size += 1;
×
UNCOV
269
            break;
×
UNCOV
270
        case MULTI_MAC:
×
UNCOV
271
            size += ms->last[MAC_SET_DST];
×
UNCOV
272
            break;
×
UNCOV
273
    }
×
UNCOV
274
    return size;
×
UNCOV
275
}
×
276

277
void MacSetFree(MacSet *ms)
UNCOV
278
{
×
UNCOV
279
    size_t total_free = 0;
×
UNCOV
280
    if (ms == NULL)
×
281
        return;
×
UNCOV
282
    if (ms->buf[MAC_SET_SRC] != NULL) {
×
UNCOV
283
        SCFree(ms->buf[MAC_SET_SRC]);
×
UNCOV
284
        total_free += ms->size * sizeof(MacAddr);
×
UNCOV
285
    }
×
UNCOV
286
    if (ms->buf[MAC_SET_DST] != NULL) {
×
UNCOV
287
        SCFree(ms->buf[MAC_SET_DST]);
×
UNCOV
288
        total_free += ms->size * sizeof(MacAddr);
×
UNCOV
289
    }
×
UNCOV
290
    SCFree(ms);
×
UNCOV
291
    total_free += sizeof(*ms);
×
UNCOV
292
    (void)SC_ATOMIC_SUB(flow_memuse, total_free);
×
UNCOV
293
}
×
294

295
void MacSetSwap(MacSet *ms)
UNCOV
296
{
×
UNCOV
297
    if (ms == NULL)
×
298
        return;
×
299

UNCOV
300
    MacAddr tmp_single;
×
UNCOV
301
    memcpy(tmp_single, ms->singles[0], sizeof(MacAddr));
×
UNCOV
302
    memcpy(ms->singles[0], ms->singles[1], sizeof(MacAddr));
×
UNCOV
303
    memcpy(ms->singles[1], tmp_single, sizeof(MacAddr));
×
304

UNCOV
305
    MacSetState tmp_state = ms->state[0];
×
UNCOV
306
    ms->state[0] = ms->state[1];
×
UNCOV
307
    ms->state[1] = tmp_state;
×
308

UNCOV
309
    MacAddr *tmp_buf = ms->buf[0];
×
UNCOV
310
    ms->buf[0] = ms->buf[1];
×
UNCOV
311
    ms->buf[1] = tmp_buf;
×
312

UNCOV
313
    int tmp_last = ms->last[0];
×
UNCOV
314
    ms->last[0] = ms->last[1];
×
UNCOV
315
    ms->last[1] = tmp_last;
×
UNCOV
316
}
×
317

318
#ifdef UNITTESTS
319

320
static int CheckTest1Membership(uint8_t *addr, MacSetSide side, void *data)
321
{
322
    int *i = (int *)data;
323
    switch (*i) {
324
        case 0:
325
            if (addr[5] != 1)
326
                return 1;
327
            break;
328
        case 1:
329
            if (addr[5] != 2)
330
                return 1;
331
            break;
332
        case 2:
333
            if (addr[5] != 3)
334
                return 1;
335
            break;
336
    }
337
    (*i)++;
338
    return 0;
339
}
340

341
static int MacSetTest01(void)
342
{
343
    MacSet *ms = NULL;
344
    int ret = 0, i = 0;
345
    MacAddr addr1 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x1 }, addr2 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x2 },
346
            addr3 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x3 };
347
    SC_ATOMIC_SET(flow_config.memcap, 10000);
348

349
    ms = MacSetInit(10);
350
    FAIL_IF_NULL(ms);
351
    FAIL_IF_NOT(MacSetSize(ms) == 0);
352

353
    ret = MacSetForEach(ms, CheckTest1Membership, &i);
354
    FAIL_IF_NOT(ret == 0);
355

356
    MacSetAdd(ms, addr1, addr2);
357
    FAIL_IF_NOT(MacSetSize(ms) == 2);
358

359
    ret = MacSetForEach(ms, CheckTest1Membership, &i);
360
    FAIL_IF_NOT(ret == 0);
361

362
    MacSetAdd(ms, addr1, addr3);
363
    FAIL_IF_NOT(MacSetSize(ms) == 3);
364

365
    i = 0;
366
    ret = MacSetForEach(ms, CheckTest1Membership, &i);
367
    FAIL_IF_NOT(ret == 0);
368

369
    MacSetFree(ms);
370
    PASS;
371
}
372

373
static int MacSetTest02(void)
374
{
375
    MacSet *ms = NULL;
376
    int ret = 0, i = 0;
377
    SC_ATOMIC_SET(flow_config.memcap, 10000);
378

379
    ms = MacSetInit(10);
380
    FAIL_IF_NULL(ms);
381
    FAIL_IF_NOT(MacSetSize(ms) == 0);
382

383
    for (i = 1; i < 100; i++) {
384
        MacAddr addr1 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x1 }, addr2 = { 0x1, 0x0, 0x0, 0x0, 0x0, 0x2 };
385
        MacSetAdd(ms, addr1, addr2);
386
    }
387
    FAIL_IF_NOT(MacSetSize(ms) == 2);
388

389
    ret = MacSetForEach(ms, CheckTest1Membership, &i);
390
    FAIL_IF_NOT(ret == 0);
391

392
    MacSetFree(ms);
393
    PASS;
394
}
395

396
static int MacSetTest03(void)
397
{
398
    MacSet *ms = NULL;
399
    SC_ATOMIC_SET(flow_config.memcap, 10000);
400

401
    ms = MacSetInit(10);
402
    FAIL_IF_NULL(ms);
403
    FAIL_IF_NOT(MacSetSize(ms) == 0);
404

405
    for (uint8_t i = 1; i < 100; i++) {
406
        MacAddr addr1 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x1 }, addr2 = { 0x1, 0x0, 0x0, 0x0, 0x0, 0x1 };
407
        addr1[5] = i;
408
        addr2[5] = i;
409
        MacSetAdd(ms, addr1, addr2);
410
    }
411
    FAIL_IF_NOT(MacSetSize(ms) == 20);
412

413
    MacSetFree(ms);
414
    PASS;
415
}
416

417
static int MacSetTest04(void)
418
{
419
    MacSet *ms = NULL;
420
    SC_ATOMIC_SET(flow_config.memcap, 2);
421

422
    ms = MacSetInit(10);
423
    FAIL_IF_NOT_NULL(ms);
424

425
    PASS;
426
}
427

428
static int MacSetTest05(void)
429
{
430
    MacSet *ms = NULL;
431
    int ret = 0;
432
    SC_ATOMIC_SET(flow_config.memcap, 64);
433

434
    ms = MacSetInit(10);
435
    FAIL_IF_NULL(ms);
436
    FAIL_IF_NOT(MacSetSize(ms) == 0);
437

438
    for (uint8_t i = 1; i < 100; i++) {
439
        MacAddr addr1 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x1 }, addr2 = { 0x1, 0x0, 0x0, 0x0, 0x0, 0x1 };
440
        addr1[5] = i;
441
        addr2[5] = i;
442
        MacSetAdd(ms, addr1, addr2);
443
    }
444
    FAIL_IF_NOT(MacSetSize(ms) == 2);
445

446
    int i2 = 100;
447
    ret = MacSetForEach(ms, CheckTest1Membership, &i2);
448
    FAIL_IF_NOT(ret == 0);
449

450
    MacSetFree(ms);
451
    PASS;
452
}
453

454
static int MacSetTest06(void)
455
{
456
    SC_ATOMIC_SET(flow_config.memcap, 128);
457

458
    MacSet *ms = MacSetInit(10);
459
    FAIL_IF_NULL(ms);
460
    FAIL_IF_NOT(MacSetSize(ms) == 0);
461

462
    uint8_t *src0 = MacSetGetFirst(ms, MAC_SET_SRC);
463
    uint8_t *dst0 = MacSetGetFirst(ms, MAC_SET_DST);
464

465
    MacAddr addr1 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x1 }, addr2 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x2 },
466
            addr3 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x3 }, addr4 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x4 };
467

468
    MacSetAdd(ms, addr1, addr2);
469
    uint8_t *src1 = MacSetGetFirst(ms, MAC_SET_SRC);
470
    uint8_t *dst1 = MacSetGetFirst(ms, MAC_SET_DST);
471

472
    MacSetAdd(ms, addr3, addr4);
473
    uint8_t *src2 = MacSetGetFirst(ms, MAC_SET_SRC);
474
    uint8_t *dst2 = MacSetGetFirst(ms, MAC_SET_DST);
475

476
    FAIL_IF_NOT_NULL(src0);
477
    FAIL_IF_NOT_NULL(dst0);
478
    FAIL_IF_NOT(src1[5] == addr1[5]);
479
    FAIL_IF_NOT(dst1[5] == addr2[5]);
480
    FAIL_IF_NOT(src2[5] == addr1[5]);
481
    FAIL_IF_NOT(dst2[5] == addr2[5]);
482

483
    MacSetFree(ms);
484
    PASS;
485
}
486

487
#endif /* UNITTESTS */
488

489
void MacSetRegisterTests(void)
UNCOV
490
{
×
491

492
#ifdef UNITTESTS
493
    UtRegisterTest("MacSetTest01", MacSetTest01);
494
    UtRegisterTest("MacSetTest02", MacSetTest02);
495
    UtRegisterTest("MacSetTest03", MacSetTest03);
496
    UtRegisterTest("MacSetTest04", MacSetTest04);
497
    UtRegisterTest("MacSetTest05", MacSetTest05);
498
    UtRegisterTest("MacSetTest06", MacSetTest06);
499
#endif
UNCOV
500
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc