suricata
util-macset.c
Go to the documentation of this file.
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"
36#include "conf.h"
37
38typedef uint8_t MacAddr[6];
39typedef 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 */
44
45struct MacSet_ {
46 /* static store for a single MAC address per side */
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) */
54 /* buffer for multiple MACs per flow and direction */
56 int size,
57 last[2];
58};
59
61
63{
64 SCConfNode *root = SCConfGetNode("outputs");
65 SCConfNode *node = NULL;
66 /* we only need to register if at least one enabled 'eve-log' output
67 has the ethernet setting enabled */
68 if (root != NULL) {
69 TAILQ_FOREACH(node, &root->head, next) {
70 if (node->val && strcmp(node->val, "eve-log") == 0) {
71 const char *enabled = SCConfNodeLookupChildValue(node->head.tqh_first, "enabled");
72 if (enabled != NULL && SCConfValIsTrue(enabled)) {
73 const char *ethernet =
74 SCConfNodeLookupChildValue(node->head.tqh_first, "ethernet");
75 if (ethernet != NULL && SCConfValIsTrue(ethernet)) {
76 g_macset_storage_id = FlowStorageRegister("macset", sizeof(void *),
77 NULL, (void(*)(void *)) MacSetFree);
78 return;
79 }
80 }
81 }
82 }
83 }
84}
85
87{
88 return (g_macset_storage_id.id != -1);
89}
90
91
93{
94 MacSet *ms = NULL;
95 if (!FLOW_CHECK_MEMCAP(sizeof(*ms))) {
96 return NULL;
97 }
98 ms = SCCalloc(1, sizeof(*ms));
99 if (unlikely(ms == NULL)) {
100 SCLogError("Unable to allocate MacSet memory");
101 return NULL;
102 }
103 (void) SC_ATOMIC_ADD(flow_memuse, (sizeof(*ms)));
105 if (size < 3) {
106 /* we want to make sure we have at space for at least 3 items to
107 fit MACs during the initial extension to MULTI_MAC storage */
108 size = 3;
109 }
110 ms->size = size;
111 ms->last[MAC_SET_SRC] = ms->last[MAC_SET_DST] = 0;
112 return ms;
113}
114
119
120static inline void MacUpdateEntry(
121 MacSet *ms, const uint8_t *addr, int side, ThreadVars *tv, uint16_t ctr)
122{
123 switch (ms->state[side]) {
124 case EMPTY_SET:
125 memcpy(ms->singles[side], addr, sizeof(MacAddr));
126 ms->state[side] = SINGLE_MAC;
127 if (tv != NULL)
128 StatsSetUI64(tv, ctr, 1);
129 break;
130 case SINGLE_MAC:
131 if (unlikely(memcmp(addr, ms->singles[side], sizeof(MacAddr)) != 0)) {
132 if (ms->buf[side] == NULL) {
133 if (!FLOW_CHECK_MEMCAP(ms->size * sizeof(MacAddr))) {
134 /* in this case there is not much we can do */
135 return;
136 }
137 ms->buf[side] = SCCalloc(ms->size, sizeof(MacAddr));
138 if (unlikely(ms->buf[side] == NULL)) {
139 SCLogError("Unable to allocate "
140 "MacSet memory");
141 return;
142 }
143 (void) SC_ATOMIC_ADD(flow_memuse, (ms->size * sizeof(MacAddr)));
144 }
145 memcpy(ms->buf[side], ms->singles[side], sizeof(MacAddr));
146 memcpy(ms->buf[side] + 1, addr, sizeof(MacAddr));
147 ms->last[side] = 2;
148 if (tv != NULL)
149 StatsSetUI64(tv, ctr, 2);
150 ms->state[side] = MULTI_MAC;
151 }
152 break;
153 case MULTI_MAC:
154 if (unlikely(ms->last[side] == ms->size)) {
155 /* MacSet full, ignore item. We intentionally do not output
156 any warning in order not to stall packet processing */
157 return;
158 }
159 /* If the set is non-empty... */
160 if (ms->last[side] > 0) {
161 /* ...we search for duplicates in the set to decide whether
162 we need to insert the current item. We do this backwards,
163 since we expect the latest item to match more likely than
164 the first */
165 for (int i = ms->last[side] - 1; i >= 0; i--) {
166 uint8_t *addr2 = (uint8_t*) ((ms->buf[side]) + i);
167 /* If we find a match, we return early with no action */
168 if (likely(memcmp(addr2, addr, sizeof(MacAddr)) == 0)) {
169 return;
170 }
171 }
172 }
173 /* Otherwise, we insert the new address at the end */
174 memcpy(ms->buf[side] + ms->last[side], addr, sizeof(MacAddr));
175 ms->last[side]++;
176 if (tv != NULL)
177 StatsSetUI64(tv, ctr, ms->last[side]);
178 break;
179 }
180}
181
182void MacSetAddWithCtr(MacSet *ms, const uint8_t *src_addr, const uint8_t *dst_addr, ThreadVars *tv,
183 uint16_t ctr_src, uint16_t ctr_dst)
184{
185 if (ms == NULL)
186 return;
187 MacUpdateEntry(ms, src_addr, MAC_SET_SRC, tv, ctr_src);
188 MacUpdateEntry(ms, dst_addr, MAC_SET_DST, tv, ctr_dst);
189}
190
191void MacSetAdd(MacSet *ms, const uint8_t *src_addr, const uint8_t *dst_addr)
192{
193 MacSetAddWithCtr(ms, src_addr, dst_addr, NULL, 0, 0);
194}
195
196static inline int MacSetIterateSide(const MacSet *ms, MacSetIteratorFunc IterFunc,
197 MacSetSide side, void *data)
198{
199 int ret = 0;
200 switch (ms->state[side]) {
201 case EMPTY_SET:
202 return 0;
203 case SINGLE_MAC:
204 ret = IterFunc((uint8_t*) ms->singles[side], side, data);
205 if (unlikely(ret != 0)) {
206 return ret;
207 }
208 break;
209 case MULTI_MAC:
210 for (int i = 0; i < ms->last[side]; i++) {
211 ret = IterFunc((uint8_t*) ms->buf[side][i], side, data);
212 if (unlikely(ret != 0)) {
213 return ret;
214 }
215 }
216 break;
217 }
218 return 0;
219}
220
221int MacSetForEach(const MacSet *ms, MacSetIteratorFunc IterFunc, void *data)
222{
223 int ret = 0;
224 if (ms == NULL)
225 return 0;
226
227 ret = MacSetIterateSide(ms, IterFunc, MAC_SET_SRC, data);
228 if (ret != 0) {
229 return ret;
230 }
231 return MacSetIterateSide(ms, IterFunc, MAC_SET_DST, data);
232}
233
234uint8_t *MacSetGetFirst(const MacSet *ms, MacSetSide side)
235{
236 switch (ms->state[side]) {
237 case EMPTY_SET:
238 return NULL;
239 case SINGLE_MAC:
240 return (uint8_t *)ms->singles[side];
241 case MULTI_MAC:
242 return (uint8_t *)ms->buf[side][0];
243 }
244 return NULL;
245}
246
247int MacSetSize(const MacSet *ms)
248{
249 int size = 0;
250 if (ms == NULL)
251 return 0;
252
253 switch(ms->state[MAC_SET_SRC]) {
254 case EMPTY_SET:
255 /* pass */
256 break;
257 case SINGLE_MAC:
258 size += 1;
259 break;
260 case MULTI_MAC:
261 size += ms->last[MAC_SET_SRC];
262 break;
263 }
264 switch(ms->state[MAC_SET_DST]) {
265 case EMPTY_SET:
266 /* pass */
267 break;
268 case SINGLE_MAC:
269 size += 1;
270 break;
271 case MULTI_MAC:
272 size += ms->last[MAC_SET_DST];
273 break;
274 }
275 return size;
276}
277
279{
280 size_t total_free = 0;
281 if (ms == NULL)
282 return;
283 if (ms->buf[MAC_SET_SRC] != NULL) {
284 SCFree(ms->buf[MAC_SET_SRC]);
285 total_free += ms->size * sizeof(MacAddr);
286 }
287 if (ms->buf[MAC_SET_DST] != NULL) {
288 SCFree(ms->buf[MAC_SET_DST]);
289 total_free += ms->size * sizeof(MacAddr);
290 }
291 SCFree(ms);
292 total_free += sizeof(*ms);
293 (void) SC_ATOMIC_SUB(flow_memuse, total_free);
294}
295
296#ifdef UNITTESTS
297
298static int CheckTest1Membership(uint8_t *addr, MacSetSide side, void *data)
299{
300 int *i = (int*) data;
301 switch (*i) {
302 case 0:
303 if (addr[5] != 1) return 1;
304 break;
305 case 1:
306 if (addr[5] != 2) return 1;
307 break;
308 case 2:
309 if (addr[5] != 3) return 1;
310 break;
311 }
312 (*i)++;
313 return 0;
314}
315
316static int MacSetTest01(void)
317{
318 MacSet *ms = NULL;
319 int ret = 0, i = 0;
320 MacAddr addr1 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
321 addr2 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x2},
322 addr3 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x3};
323 SC_ATOMIC_SET(flow_config.memcap, 10000);
324
325 ms = MacSetInit(10);
326 FAIL_IF_NULL(ms);
327 FAIL_IF_NOT(MacSetSize(ms) == 0);
328
329 ret = MacSetForEach(ms, CheckTest1Membership, &i);
330 FAIL_IF_NOT(ret == 0);
331
332 MacSetAdd(ms, addr1, addr2);
333 FAIL_IF_NOT(MacSetSize(ms) == 2);
334
335 ret = MacSetForEach(ms, CheckTest1Membership, &i);
336 FAIL_IF_NOT(ret == 0);
337
338 MacSetAdd(ms, addr1, addr3);
339 FAIL_IF_NOT(MacSetSize(ms) == 3);
340
341 i = 0;
342 ret = MacSetForEach(ms, CheckTest1Membership, &i);
343 FAIL_IF_NOT(ret == 0);
344
345 MacSetFree(ms);
346 PASS;
347}
348
349static int MacSetTest02(void)
350{
351 MacSet *ms = NULL;
352 int ret = 0, i = 0;
353 SC_ATOMIC_SET(flow_config.memcap, 10000);
354
355 ms = MacSetInit(10);
356 FAIL_IF_NULL(ms);
357 FAIL_IF_NOT(MacSetSize(ms) == 0);
358
359 for (i = 1; i < 100; i++) {
360 MacAddr addr1 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
361 addr2 = {0x1, 0x0, 0x0, 0x0, 0x0, 0x2};
362 MacSetAdd(ms, addr1, addr2);
363 }
364 FAIL_IF_NOT(MacSetSize(ms) == 2);
365
366 ret = MacSetForEach(ms, CheckTest1Membership, &i);
367 FAIL_IF_NOT(ret == 0);
368
369 MacSetFree(ms);
370 PASS;
371}
372
373static int MacSetTest03(void)
374{
375 MacSet *ms = NULL;
376 SC_ATOMIC_SET(flow_config.memcap, 10000);
377
378 ms = MacSetInit(10);
379 FAIL_IF_NULL(ms);
380 FAIL_IF_NOT(MacSetSize(ms) == 0);
381
382 for (uint8_t i = 1; i < 100; i++) {
383 MacAddr addr1 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
384 addr2 = {0x1, 0x0, 0x0, 0x0, 0x0, 0x1};
385 addr1[5] = i;
386 addr2[5] = i;
387 MacSetAdd(ms, addr1, addr2);
388 }
389 FAIL_IF_NOT(MacSetSize(ms) == 20);
390
391 MacSetFree(ms);
392 PASS;
393}
394
395static int MacSetTest04(void)
396{
397 MacSet *ms = NULL;
398 SC_ATOMIC_SET(flow_config.memcap, 2);
399
400 ms = MacSetInit(10);
402
403 PASS;
404}
405
406static int MacSetTest05(void)
407{
408 MacSet *ms = NULL;
409 int ret = 0;
410 SC_ATOMIC_SET(flow_config.memcap, 64);
411
412 ms = MacSetInit(10);
413 FAIL_IF_NULL(ms);
414 FAIL_IF_NOT(MacSetSize(ms) == 0);
415
416 for (uint8_t i = 1; i < 100; i++) {
417 MacAddr addr1 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
418 addr2 = {0x1, 0x0, 0x0, 0x0, 0x0, 0x1};
419 addr1[5] = i;
420 addr2[5] = i;
421 MacSetAdd(ms, addr1, addr2);
422 }
423 FAIL_IF_NOT(MacSetSize(ms) == 2);
424
425 int i2 = 100;
426 ret = MacSetForEach(ms, CheckTest1Membership, &i2);
427 FAIL_IF_NOT(ret == 0);
428
429 MacSetFree(ms);
430 PASS;
431}
432
433static int MacSetTest06(void)
434{
435 SC_ATOMIC_SET(flow_config.memcap, 128);
436
437 MacSet *ms = MacSetInit(10);
438 FAIL_IF_NULL(ms);
439 FAIL_IF_NOT(MacSetSize(ms) == 0);
440
441 uint8_t *src0 = MacSetGetFirst(ms, MAC_SET_SRC);
442 uint8_t *dst0 = MacSetGetFirst(ms, MAC_SET_DST);
443
444 MacAddr addr1 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x1 }, addr2 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x2 },
445 addr3 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x3 }, addr4 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x4 };
446
447 MacSetAdd(ms, addr1, addr2);
448 uint8_t *src1 = MacSetGetFirst(ms, MAC_SET_SRC);
449 uint8_t *dst1 = MacSetGetFirst(ms, MAC_SET_DST);
450
451 MacSetAdd(ms, addr3, addr4);
452 uint8_t *src2 = MacSetGetFirst(ms, MAC_SET_SRC);
453 uint8_t *dst2 = MacSetGetFirst(ms, MAC_SET_DST);
454
455 FAIL_IF_NOT_NULL(src0);
456 FAIL_IF_NOT_NULL(dst0);
457 FAIL_IF_NOT(src1[5] == addr1[5]);
458 FAIL_IF_NOT(dst1[5] == addr2[5]);
459 FAIL_IF_NOT(src2[5] == addr1[5]);
460 FAIL_IF_NOT(dst2[5] == addr2[5]);
461
462 MacSetFree(ms);
463 PASS;
464}
465
466#endif /* UNITTESTS */
467
469{
470
471#ifdef UNITTESTS
472 UtRegisterTest("MacSetTest01", MacSetTest01);
473 UtRegisterTest("MacSetTest02", MacSetTest02);
474 UtRegisterTest("MacSetTest03", MacSetTest03);
475 UtRegisterTest("MacSetTest04", MacSetTest04);
476 UtRegisterTest("MacSetTest05", MacSetTest05);
477 UtRegisterTest("MacSetTest06", MacSetTest06);
478#endif
479}
struct HtpBodyChunk_ * next
SCConfNode * SCConfGetNode(const char *name)
Get a SCConfNode by name.
Definition conf.c:181
int SCConfValIsTrue(const char *val)
Check if a value is true.
Definition conf.c:551
const char * SCConfNodeLookupChildValue(const SCConfNode *node, const char *name)
Lookup the value of a child configuration node by name.
Definition conf.c:824
void StatsSetUI64(ThreadVars *tv, uint16_t id, uint64_t x)
Sets a value of type double to the local counter.
Definition counters.c:207
FlowConfig flow_config
Definition flow.c:93
FlowStorageId FlowStorageRegister(const char *name, const unsigned int size, void *(*Alloc)(unsigned int), void(*Free)(void *))
#define FLOW_CHECK_MEMCAP(size)
check if a memory alloc would fit in the memcap
Definition flow-util.h:134
ThreadVars * tv
#define FAIL_IF_NULL(expr)
Fail a test if expression evaluates to NULL.
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
#define FAIL_IF_NOT(expr)
Fail a test if expression evaluates to false.
#define PASS
Pass the test.
#define FAIL_IF_NOT_NULL(expr)
Fail a test if expression evaluates to non-NULL.
#define TAILQ_FOREACH(var, head, field)
Definition queue.h:252
MacAddr * buf[2]
Definition util-macset.c:55
int last[2]
Definition util-macset.c:57
MacAddr singles[2]
Definition util-macset.c:47
MacSetState state[2]
Definition util-macset.c:53
char * val
Definition conf.h:39
Per thread variable structure.
Definition threadvars.h:58
#define SC_ATOMIC_ADD(name, val)
add a value to our atomic variable
#define SC_ATOMIC_SUB(name, val)
sub a value from our atomic variable
#define SC_ATOMIC_SET(name, val)
Set the value for the atomic variable.
#define SCLogError(...)
Macro used to log ERROR messages.
Definition util-debug.h:267
MacSetState
Definition util-macset.c:39
@ SINGLE_MAC
Definition util-macset.c:41
@ MULTI_MAC
Definition util-macset.c:42
@ EMPTY_SET
Definition util-macset.c:40
void MacSetFree(MacSet *ms)
uint8_t * MacSetGetFirst(const MacSet *ms, MacSetSide side)
FlowStorageId g_macset_storage_id
Definition util-macset.c:60
FlowStorageId MacSetGetFlowStorageID(void)
void MacSetRegisterTests(void)
MacSet * MacSetInit(int size)
Definition util-macset.c:92
bool MacSetFlowStorageEnabled(void)
Definition util-macset.c:86
void MacSetRegisterFlowStorage(void)
Definition util-macset.c:62
void MacSetAdd(MacSet *ms, const uint8_t *src_addr, const uint8_t *dst_addr)
void MacSetAddWithCtr(MacSet *ms, const uint8_t *src_addr, const uint8_t *dst_addr, ThreadVars *tv, uint16_t ctr_src, uint16_t ctr_dst)
int MacSetForEach(const MacSet *ms, MacSetIteratorFunc IterFunc, void *data)
int MacSetSize(const MacSet *ms)
uint8_t MacAddr[6]
Definition util-macset.c:38
int(* MacSetIteratorFunc)(uint8_t *addr, MacSetSide side, void *)
Definition util-macset.h:33
MacSetSide
Definition util-macset.h:28
@ MAC_SET_DST
Definition util-macset.h:30
@ MAC_SET_SRC
Definition util-macset.h:29
#define SCFree(p)
Definition util-mem.h:61
#define SCCalloc(nm, sz)
Definition util-mem.h:53
#define likely(expr)
#define unlikely(expr)