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