Coverage Report

Created: 2026-01-16 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/util-macset.c
Line
Count
Source
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
37
typedef uint8_t MacAddr[6];
38
typedef enum {
39
    EMPTY_SET,  /* no address inserted yet */
40
    SINGLE_MAC, /* we have a single pair of addresses (likely) */
41
    MULTI_MAC   /* we have multiple addresses per flow */
42
} MacSetState;
43
44
struct MacSet_ {
45
    /* static store for a single MAC address per side */
46
    MacAddr singles[2];
47
    /* state determines how addresses are stored per side:
48
         - SINGLE_MAC uses static locations allocated with the MacSet
49
           itself to store a single address (most likely case)
50
         - MULTI_MAC is used when more than one distinct address
51
           is detected (causes another allocation and linear-time add) */
52
    MacSetState state[2];
53
    /* buffer for multiple MACs per flow and direction */
54
    MacAddr *buf[2];
55
    int size, last[2];
56
};
57
58
FlowStorageId g_macset_storage_id = { .id = -1 };
59
60
void MacSetRegisterFlowStorage(void)
61
71
{
62
71
    ConfNode *root = ConfGetNode("outputs");
63
71
    ConfNode *node = NULL;
64
    /* we only need to register if at least one enabled 'eve-log' output
65
       has the ethernet setting enabled */
66
71
    if (root != NULL) {
67
345
        TAILQ_FOREACH (node, &root->head, next) {
68
345
            if (node->val && strcmp(node->val, "eve-log") == 0) {
69
69
                const char *enabled = ConfNodeLookupChildValue(node->head.tqh_first, "enabled");
70
69
                if (enabled != NULL && ConfValIsTrue(enabled)) {
71
69
                    const char *ethernet =
72
69
                            ConfNodeLookupChildValue(node->head.tqh_first, "ethernet");
73
69
                    if (ethernet != NULL && ConfValIsTrue(ethernet)) {
74
0
                        g_macset_storage_id = FlowStorageRegister(
75
0
                                "macset", sizeof(void *), NULL, (void (*)(void *))MacSetFree);
76
0
                        return;
77
0
                    }
78
69
                }
79
69
            }
80
345
        }
81
69
    }
82
71
}
83
84
bool MacSetFlowStorageEnabled(void)
85
7.07M
{
86
7.07M
    return (g_macset_storage_id.id != -1);
87
7.07M
}
88
89
MacSet *MacSetInit(int size)
90
0
{
91
0
    MacSet *ms = NULL;
92
0
    if (!FLOW_CHECK_MEMCAP(sizeof(*ms))) {
93
0
        return NULL;
94
0
    }
95
0
    ms = SCCalloc(1, sizeof(*ms));
96
0
    if (unlikely(ms == NULL)) {
97
0
        SCLogError("Unable to allocate MacSet memory");
98
0
        return NULL;
99
0
    }
100
0
    (void)SC_ATOMIC_ADD(flow_memuse, (sizeof(*ms)));
101
0
    ms->state[MAC_SET_SRC] = ms->state[MAC_SET_DST] = EMPTY_SET;
102
0
    if (size < 3) {
103
        /* we want to make sure we have at space for at least 3 items to
104
           fit MACs during the initial extension to MULTI_MAC storage */
105
0
        size = 3;
106
0
    }
107
0
    ms->size = size;
108
0
    ms->last[MAC_SET_SRC] = ms->last[MAC_SET_DST] = 0;
109
0
    return ms;
110
0
}
111
112
FlowStorageId MacSetGetFlowStorageID(void)
113
0
{
114
0
    return g_macset_storage_id;
115
0
}
116
117
static inline void MacUpdateEntry(
118
        MacSet *ms, const uint8_t *addr, int side, ThreadVars *tv, uint16_t ctr)
119
0
{
120
0
    switch (ms->state[side]) {
121
0
        case EMPTY_SET:
122
0
            memcpy(ms->singles[side], addr, sizeof(MacAddr));
123
0
            ms->state[side] = SINGLE_MAC;
124
0
            if (tv != NULL)
125
0
                StatsSetUI64(tv, ctr, 1);
126
0
            break;
127
0
        case SINGLE_MAC:
128
0
            if (unlikely(memcmp(addr, ms->singles[side], sizeof(MacAddr)) != 0)) {
129
0
                if (ms->buf[side] == NULL) {
130
0
                    if (!FLOW_CHECK_MEMCAP(ms->size * sizeof(MacAddr))) {
131
                        /* in this case there is not much we can do */
132
0
                        return;
133
0
                    }
134
0
                    ms->buf[side] = SCCalloc(ms->size, sizeof(MacAddr));
135
0
                    if (unlikely(ms->buf[side] == NULL)) {
136
0
                        SCLogError("Unable to allocate "
137
0
                                   "MacSet memory");
138
0
                        return;
139
0
                    }
140
0
                    (void)SC_ATOMIC_ADD(flow_memuse, (ms->size * sizeof(MacAddr)));
141
0
                }
142
0
                memcpy(ms->buf[side], ms->singles[side], sizeof(MacAddr));
143
0
                memcpy(ms->buf[side] + 1, addr, sizeof(MacAddr));
144
0
                ms->last[side] = 2;
145
0
                if (tv != NULL)
146
0
                    StatsSetUI64(tv, ctr, 2);
147
0
                ms->state[side] = MULTI_MAC;
148
0
            }
149
0
            break;
150
0
        case MULTI_MAC:
151
0
            if (unlikely(ms->last[side] == ms->size)) {
152
                /* MacSet full, ignore item. We intentionally do not output
153
                   any warning in order not to stall packet processing */
154
0
                return;
155
0
            }
156
            /* If the set is non-empty... */
157
0
            if (ms->last[side] > 0) {
158
                /* ...we search for duplicates in the set to decide whether
159
                   we need to insert the current item. We do this backwards,
160
                   since we expect the latest item to match more likely than
161
                   the first */
162
0
                for (int i = ms->last[side] - 1; i >= 0; i--) {
163
0
                    uint8_t *addr2 = (uint8_t *)((ms->buf[side]) + i);
164
                    /* If we find a match, we return early with no action */
165
0
                    if (likely(memcmp(addr2, addr, sizeof(MacAddr)) == 0)) {
166
0
                        return;
167
0
                    }
168
0
                }
169
0
            }
170
            /* Otherwise, we insert the new address at the end */
171
0
            memcpy(ms->buf[side] + ms->last[side], addr, sizeof(MacAddr));
172
0
            ms->last[side]++;
173
0
            if (tv != NULL)
174
0
                StatsSetUI64(tv, ctr, ms->last[side]);
175
0
            break;
176
0
    }
177
0
}
178
179
void MacSetAddWithCtr(MacSet *ms, const uint8_t *src_addr, const uint8_t *dst_addr, ThreadVars *tv,
180
        uint16_t ctr_src, uint16_t ctr_dst)
181
0
{
182
0
    if (ms == NULL)
183
0
        return;
184
0
    MacUpdateEntry(ms, src_addr, MAC_SET_SRC, tv, ctr_src);
185
0
    MacUpdateEntry(ms, dst_addr, MAC_SET_DST, tv, ctr_dst);
186
0
}
187
188
void MacSetAdd(MacSet *ms, const uint8_t *src_addr, const uint8_t *dst_addr)
189
0
{
190
0
    MacSetAddWithCtr(ms, src_addr, dst_addr, NULL, 0, 0);
191
0
}
192
193
static inline int MacSetIterateSide(
194
        const MacSet *ms, MacSetIteratorFunc IterFunc, MacSetSide side, void *data)
195
0
{
196
0
    int ret = 0;
197
0
    switch (ms->state[side]) {
198
0
        case EMPTY_SET:
199
0
            return 0;
200
0
        case SINGLE_MAC:
201
0
            ret = IterFunc((uint8_t *)ms->singles[side], side, data);
202
0
            if (unlikely(ret != 0)) {
203
0
                return ret;
204
0
            }
205
0
            break;
206
0
        case MULTI_MAC:
207
0
            for (int i = 0; i < ms->last[side]; i++) {
208
0
                ret = IterFunc((uint8_t *)ms->buf[side][i], side, data);
209
0
                if (unlikely(ret != 0)) {
210
0
                    return ret;
211
0
                }
212
0
            }
213
0
            break;
214
0
    }
215
0
    return 0;
216
0
}
217
218
int MacSetForEach(const MacSet *ms, MacSetIteratorFunc IterFunc, void *data)
219
0
{
220
0
    int ret = 0;
221
0
    if (ms == NULL)
222
0
        return 0;
223
224
0
    ret = MacSetIterateSide(ms, IterFunc, MAC_SET_SRC, data);
225
0
    if (ret != 0) {
226
0
        return ret;
227
0
    }
228
0
    return MacSetIterateSide(ms, IterFunc, MAC_SET_DST, data);
229
0
}
230
231
int MacSetSize(const MacSet *ms)
232
0
{
233
0
    int size = 0;
234
0
    if (ms == NULL)
235
0
        return 0;
236
237
0
    switch (ms->state[MAC_SET_SRC]) {
238
0
        case EMPTY_SET:
239
            /* pass */
240
0
            break;
241
0
        case SINGLE_MAC:
242
0
            size += 1;
243
0
            break;
244
0
        case MULTI_MAC:
245
0
            size += ms->last[MAC_SET_SRC];
246
0
            break;
247
0
    }
248
0
    switch (ms->state[MAC_SET_DST]) {
249
0
        case EMPTY_SET:
250
            /* pass */
251
0
            break;
252
0
        case SINGLE_MAC:
253
0
            size += 1;
254
0
            break;
255
0
        case MULTI_MAC:
256
0
            size += ms->last[MAC_SET_DST];
257
0
            break;
258
0
    }
259
0
    return size;
260
0
}
261
262
void MacSetFree(MacSet *ms)
263
0
{
264
0
    size_t total_free = 0;
265
0
    if (ms == NULL)
266
0
        return;
267
0
    if (ms->buf[MAC_SET_SRC] != NULL) {
268
0
        SCFree(ms->buf[MAC_SET_SRC]);
269
0
        total_free += ms->size * sizeof(MacAddr);
270
0
    }
271
0
    if (ms->buf[MAC_SET_DST] != NULL) {
272
0
        SCFree(ms->buf[MAC_SET_DST]);
273
0
        total_free += ms->size * sizeof(MacAddr);
274
0
    }
275
0
    SCFree(ms);
276
0
    total_free += sizeof(*ms);
277
0
    (void)SC_ATOMIC_SUB(flow_memuse, total_free);
278
0
}
279
280
void MacSetSwap(MacSet *ms)
281
0
{
282
0
    if (ms == NULL)
283
0
        return;
284
285
0
    MacAddr tmp_single;
286
0
    memcpy(tmp_single, ms->singles[0], sizeof(MacAddr));
287
0
    memcpy(ms->singles[0], ms->singles[1], sizeof(MacAddr));
288
0
    memcpy(ms->singles[1], tmp_single, sizeof(MacAddr));
289
290
0
    MacSetState tmp_state = ms->state[0];
291
0
    ms->state[0] = ms->state[1];
292
0
    ms->state[1] = tmp_state;
293
294
0
    MacAddr *tmp_buf = ms->buf[0];
295
0
    ms->buf[0] = ms->buf[1];
296
0
    ms->buf[1] = tmp_buf;
297
298
0
    int tmp_last = ms->last[0];
299
0
    ms->last[0] = ms->last[1];
300
0
    ms->last[1] = tmp_last;
301
0
}
302
303
#ifdef UNITTESTS
304
305
static int CheckTest1Membership(uint8_t *addr, MacSetSide side, void *data)
306
{
307
    int *i = (int *)data;
308
    switch (*i) {
309
        case 0:
310
            if (addr[5] != 1)
311
                return 1;
312
            break;
313
        case 1:
314
            if (addr[5] != 2)
315
                return 1;
316
            break;
317
        case 2:
318
            if (addr[5] != 3)
319
                return 1;
320
            break;
321
    }
322
    (*i)++;
323
    return 0;
324
}
325
326
static int MacSetTest01(void)
327
{
328
    MacSet *ms = NULL;
329
    int ret = 0, i = 0;
330
    MacAddr addr1 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x1 }, addr2 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x2 },
331
            addr3 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x3 };
332
    SC_ATOMIC_SET(flow_config.memcap, 10000);
333
334
    ms = MacSetInit(10);
335
    FAIL_IF_NULL(ms);
336
    FAIL_IF_NOT(MacSetSize(ms) == 0);
337
338
    ret = MacSetForEach(ms, CheckTest1Membership, &i);
339
    FAIL_IF_NOT(ret == 0);
340
341
    MacSetAdd(ms, addr1, addr2);
342
    FAIL_IF_NOT(MacSetSize(ms) == 2);
343
344
    ret = MacSetForEach(ms, CheckTest1Membership, &i);
345
    FAIL_IF_NOT(ret == 0);
346
347
    MacSetAdd(ms, addr1, addr3);
348
    FAIL_IF_NOT(MacSetSize(ms) == 3);
349
350
    i = 0;
351
    ret = MacSetForEach(ms, CheckTest1Membership, &i);
352
    FAIL_IF_NOT(ret == 0);
353
354
    MacSetFree(ms);
355
    PASS;
356
}
357
358
static int MacSetTest02(void)
359
{
360
    MacSet *ms = NULL;
361
    int ret = 0, i = 0;
362
    SC_ATOMIC_SET(flow_config.memcap, 10000);
363
364
    ms = MacSetInit(10);
365
    FAIL_IF_NULL(ms);
366
    FAIL_IF_NOT(MacSetSize(ms) == 0);
367
368
    for (i = 1; i < 100; i++) {
369
        MacAddr addr1 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x1 }, addr2 = { 0x1, 0x0, 0x0, 0x0, 0x0, 0x2 };
370
        MacSetAdd(ms, addr1, addr2);
371
    }
372
    FAIL_IF_NOT(MacSetSize(ms) == 2);
373
374
    ret = MacSetForEach(ms, CheckTest1Membership, &i);
375
    FAIL_IF_NOT(ret == 0);
376
377
    MacSetFree(ms);
378
    PASS;
379
}
380
381
static int MacSetTest03(void)
382
{
383
    MacSet *ms = NULL;
384
    SC_ATOMIC_SET(flow_config.memcap, 10000);
385
386
    ms = MacSetInit(10);
387
    FAIL_IF_NULL(ms);
388
    FAIL_IF_NOT(MacSetSize(ms) == 0);
389
390
    for (uint8_t i = 1; i < 100; i++) {
391
        MacAddr addr1 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x1 }, addr2 = { 0x1, 0x0, 0x0, 0x0, 0x0, 0x1 };
392
        addr1[5] = i;
393
        addr2[5] = i;
394
        MacSetAdd(ms, addr1, addr2);
395
    }
396
    FAIL_IF_NOT(MacSetSize(ms) == 20);
397
398
    MacSetFree(ms);
399
    PASS;
400
}
401
402
static int MacSetTest04(void)
403
{
404
    MacSet *ms = NULL;
405
    SC_ATOMIC_SET(flow_config.memcap, 2);
406
407
    ms = MacSetInit(10);
408
    FAIL_IF_NOT_NULL(ms);
409
410
    PASS;
411
}
412
413
static int MacSetTest05(void)
414
{
415
    MacSet *ms = NULL;
416
    int ret = 0;
417
    SC_ATOMIC_SET(flow_config.memcap, 64);
418
419
    ms = MacSetInit(10);
420
    FAIL_IF_NULL(ms);
421
    FAIL_IF_NOT(MacSetSize(ms) == 0);
422
423
    for (uint8_t i = 1; i < 100; i++) {
424
        MacAddr addr1 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x1 }, addr2 = { 0x1, 0x0, 0x0, 0x0, 0x0, 0x1 };
425
        addr1[5] = i;
426
        addr2[5] = i;
427
        MacSetAdd(ms, addr1, addr2);
428
    }
429
    FAIL_IF_NOT(MacSetSize(ms) == 2);
430
431
    int i2 = 100;
432
    ret = MacSetForEach(ms, CheckTest1Membership, &i2);
433
    FAIL_IF_NOT(ret == 0);
434
435
    MacSetFree(ms);
436
    PASS;
437
}
438
439
#endif /* UNITTESTS */
440
441
void MacSetRegisterTests(void)
442
0
{
443
444
#ifdef UNITTESTS
445
    UtRegisterTest("MacSetTest01", MacSetTest01);
446
    UtRegisterTest("MacSetTest02", MacSetTest02);
447
    UtRegisterTest("MacSetTest03", MacSetTest03);
448
    UtRegisterTest("MacSetTest04", MacSetTest04);
449
    UtRegisterTest("MacSetTest05", MacSetTest05);
450
#endif
451
452
0
    return;
453
0
}