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

OISF / suricata / 22618661228

02 Mar 2026 09:33PM UTC coverage: 42.258% (-34.4%) from 76.611%
22618661228

push

github

victorjulien
github-actions: bump actions/download-artifact from 7.0.0 to 8.0.0

Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 7.0.0 to 8.0.0.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/37930b1c2...70fc10c6e)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: 8.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

91511 of 216553 relevant lines covered (42.26%)

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

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

90
MacSet *MacSetInit(int size)
91
{
×
92
    MacSet *ms = NULL;
×
93
    if (!FLOW_CHECK_MEMCAP(sizeof(*ms))) {
×
94
        return NULL;
×
95
    }
×
96
    ms = SCCalloc(1, sizeof(*ms));
×
97
    if (unlikely(ms == NULL)) {
×
98
        SCLogError("Unable to allocate MacSet memory");
×
99
        return NULL;
×
100
    }
×
101
    (void)SC_ATOMIC_ADD(flow_memuse, (sizeof(*ms)));
×
102
    ms->state[MAC_SET_SRC] = ms->state[MAC_SET_DST] = EMPTY_SET;
×
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
    }
×
108
    ms->size = size;
×
109
    ms->last[MAC_SET_SRC] = ms->last[MAC_SET_DST] = 0;
×
110
    return ms;
×
111
}
×
112

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

118
static inline void MacUpdateEntry(
119
        MacSet *ms, const uint8_t *addr, int side, ThreadVars *tv, StatsCounterMaxId ctr)
120
{
×
121
    switch (ms->state[side]) {
×
122
        case EMPTY_SET:
×
123
            memcpy(ms->singles[side], addr, sizeof(MacAddr));
×
124
            ms->state[side] = SINGLE_MAC;
×
125
            if (tv != NULL)
×
126
                StatsCounterMaxUpdateI64(&tv->stats, ctr, 1);
×
127
            break;
×
128
        case SINGLE_MAC:
×
129
            if (unlikely(memcmp(addr, ms->singles[side], sizeof(MacAddr)) != 0)) {
×
130
                if (ms->buf[side] == NULL) {
×
131
                    if (!FLOW_CHECK_MEMCAP(ms->size * sizeof(MacAddr))) {
×
132
                        /* in this case there is not much we can do */
133
                        return;
×
134
                    }
×
135
                    ms->buf[side] = SCCalloc(ms->size, sizeof(MacAddr));
×
136
                    if (unlikely(ms->buf[side] == NULL)) {
×
137
                        SCLogError("Unable to allocate "
×
138
                                   "MacSet memory");
×
139
                        return;
×
140
                    }
×
141
                    (void)SC_ATOMIC_ADD(flow_memuse, (ms->size * sizeof(MacAddr)));
×
142
                }
×
143
                memcpy(ms->buf[side], ms->singles[side], sizeof(MacAddr));
×
144
                memcpy(ms->buf[side] + 1, addr, sizeof(MacAddr));
×
145
                ms->last[side] = 2;
×
146
                if (tv != NULL)
×
147
                    StatsCounterMaxUpdateI64(&tv->stats, ctr, 2);
×
148
                ms->state[side] = MULTI_MAC;
×
149
            }
×
150
            break;
×
151
        case MULTI_MAC:
×
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 */
155
                return;
×
156
            }
×
157
            /* If the set is non-empty... */
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 */
163
                for (int i = ms->last[side] - 1; i >= 0; i--) {
×
164
                    uint8_t *addr2 = (uint8_t *)((ms->buf[side]) + i);
×
165
                    /* If we find a match, we return early with no action */
166
                    if (likely(memcmp(addr2, addr, sizeof(MacAddr)) == 0)) {
×
167
                        return;
×
168
                    }
×
169
                }
×
170
            }
×
171
            /* Otherwise, we insert the new address at the end */
172
            memcpy(ms->buf[side] + ms->last[side], addr, sizeof(MacAddr));
×
173
            ms->last[side]++;
×
174
            if (tv != NULL)
×
175
                StatsCounterMaxUpdateI64(&tv->stats, ctr, (int64_t)ms->last[side]);
×
176
            break;
×
177
    }
×
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)
182
{
×
183
    if (ms == NULL)
×
184
        return;
×
185
    MacUpdateEntry(ms, src_addr, MAC_SET_SRC, tv, ctr_src);
×
186
    MacUpdateEntry(ms, dst_addr, MAC_SET_DST, tv, ctr_dst);
×
187
}
×
188

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

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

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

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

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

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

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

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

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

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

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

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

313
    int tmp_last = ms->last[0];
×
314
    ms->last[0] = ms->last[1];
×
315
    ms->last[1] = tmp_last;
×
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)
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
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