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"
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 */
44 
45 struct 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 */
55  MacAddr *buf[2];
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 
92 MacSet *MacSetInit(int size)
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 
116 {
117  return g_macset_storage_id;
118 }
119 
120 static inline void MacUpdateEntry(
121  MacSet *ms, const uint8_t *addr, int side, ThreadVars *tv, StatsCounterMaxId 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  StatsCounterMaxUpdateI64(&tv->stats, 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  StatsCounterMaxUpdateI64(&tv->stats, 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  StatsCounterMaxUpdateI64(&tv->stats, ctr, (int64_t)ms->last[side]);
178  break;
179  }
180 }
181 
182 void MacSetAddWithCtr(MacSet *ms, const uint8_t *src_addr, const uint8_t *dst_addr, ThreadVars *tv,
183  StatsCounterMaxId ctr_src, StatsCounterMaxId 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 
191 void MacSetAdd(MacSet *ms, const uint8_t *src_addr, const uint8_t *dst_addr)
192 {
193  StatsCounterMaxId no_counter = { .id = 0 };
194  MacSetAddWithCtr(ms, src_addr, dst_addr, NULL, no_counter, no_counter);
195 }
196 
197 static inline int MacSetIterateSide(const MacSet *ms, MacSetIteratorFunc IterFunc,
198  MacSetSide side, void *data)
199 {
200  int ret = 0;
201  switch (ms->state[side]) {
202  case EMPTY_SET:
203  return 0;
204  case SINGLE_MAC:
205  ret = IterFunc((uint8_t*) ms->singles[side], side, data);
206  if (unlikely(ret != 0)) {
207  return ret;
208  }
209  break;
210  case MULTI_MAC:
211  for (int i = 0; i < ms->last[side]; i++) {
212  ret = IterFunc((uint8_t*) ms->buf[side][i], side, data);
213  if (unlikely(ret != 0)) {
214  return ret;
215  }
216  }
217  break;
218  }
219  return 0;
220 }
221 
222 int MacSetForEach(const MacSet *ms, MacSetIteratorFunc IterFunc, void *data)
223 {
224  int ret = 0;
225  if (ms == NULL)
226  return 0;
227 
228  ret = MacSetIterateSide(ms, IterFunc, MAC_SET_SRC, data);
229  if (ret != 0) {
230  return ret;
231  }
232  return MacSetIterateSide(ms, IterFunc, MAC_SET_DST, data);
233 }
234 
235 uint8_t *MacSetGetFirst(const MacSet *ms, MacSetSide side)
236 {
237  switch (ms->state[side]) {
238  case EMPTY_SET:
239  return NULL;
240  case SINGLE_MAC:
241  return (uint8_t *)ms->singles[side];
242  case MULTI_MAC:
243  return (uint8_t *)ms->buf[side][0];
244  }
245  return NULL;
246 }
247 
248 int MacSetSize(const MacSet *ms)
249 {
250  int size = 0;
251  if (ms == NULL)
252  return 0;
253 
254  switch(ms->state[MAC_SET_SRC]) {
255  case EMPTY_SET:
256  /* pass */
257  break;
258  case SINGLE_MAC:
259  size += 1;
260  break;
261  case MULTI_MAC:
262  size += ms->last[MAC_SET_SRC];
263  break;
264  }
265  switch(ms->state[MAC_SET_DST]) {
266  case EMPTY_SET:
267  /* pass */
268  break;
269  case SINGLE_MAC:
270  size += 1;
271  break;
272  case MULTI_MAC:
273  size += ms->last[MAC_SET_DST];
274  break;
275  }
276  return size;
277 }
278 
280 {
281  size_t total_free = 0;
282  if (ms == NULL)
283  return;
284  if (ms->buf[MAC_SET_SRC] != NULL) {
285  SCFree(ms->buf[MAC_SET_SRC]);
286  total_free += ms->size * sizeof(MacAddr);
287  }
288  if (ms->buf[MAC_SET_DST] != NULL) {
289  SCFree(ms->buf[MAC_SET_DST]);
290  total_free += ms->size * sizeof(MacAddr);
291  }
292  SCFree(ms);
293  total_free += sizeof(*ms);
294  (void) SC_ATOMIC_SUB(flow_memuse, total_free);
295 }
296 
297 #ifdef UNITTESTS
298 
299 static int CheckTest1Membership(uint8_t *addr, MacSetSide side, void *data)
300 {
301  int *i = (int*) data;
302  switch (*i) {
303  case 0:
304  if (addr[5] != 1) return 1;
305  break;
306  case 1:
307  if (addr[5] != 2) return 1;
308  break;
309  case 2:
310  if (addr[5] != 3) return 1;
311  break;
312  }
313  (*i)++;
314  return 0;
315 }
316 
317 static int MacSetTest01(void)
318 {
319  MacSet *ms = NULL;
320  int ret = 0, i = 0;
321  MacAddr addr1 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
322  addr2 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x2},
323  addr3 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x3};
324  SC_ATOMIC_SET(flow_config.memcap, 10000);
325 
326  ms = MacSetInit(10);
327  FAIL_IF_NULL(ms);
328  FAIL_IF_NOT(MacSetSize(ms) == 0);
329 
330  ret = MacSetForEach(ms, CheckTest1Membership, &i);
331  FAIL_IF_NOT(ret == 0);
332 
333  MacSetAdd(ms, addr1, addr2);
334  FAIL_IF_NOT(MacSetSize(ms) == 2);
335 
336  ret = MacSetForEach(ms, CheckTest1Membership, &i);
337  FAIL_IF_NOT(ret == 0);
338 
339  MacSetAdd(ms, addr1, addr3);
340  FAIL_IF_NOT(MacSetSize(ms) == 3);
341 
342  i = 0;
343  ret = MacSetForEach(ms, CheckTest1Membership, &i);
344  FAIL_IF_NOT(ret == 0);
345 
346  MacSetFree(ms);
347  PASS;
348 }
349 
350 static int MacSetTest02(void)
351 {
352  MacSet *ms = NULL;
353  int ret = 0, i = 0;
354  SC_ATOMIC_SET(flow_config.memcap, 10000);
355 
356  ms = MacSetInit(10);
357  FAIL_IF_NULL(ms);
358  FAIL_IF_NOT(MacSetSize(ms) == 0);
359 
360  for (i = 1; i < 100; i++) {
361  MacAddr addr1 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
362  addr2 = {0x1, 0x0, 0x0, 0x0, 0x0, 0x2};
363  MacSetAdd(ms, addr1, addr2);
364  }
365  FAIL_IF_NOT(MacSetSize(ms) == 2);
366 
367  ret = MacSetForEach(ms, CheckTest1Membership, &i);
368  FAIL_IF_NOT(ret == 0);
369 
370  MacSetFree(ms);
371  PASS;
372 }
373 
374 static int MacSetTest03(void)
375 {
376  MacSet *ms = NULL;
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 (uint8_t i = 1; i < 100; i++) {
384  MacAddr addr1 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
385  addr2 = {0x1, 0x0, 0x0, 0x0, 0x0, 0x1};
386  addr1[5] = i;
387  addr2[5] = i;
388  MacSetAdd(ms, addr1, addr2);
389  }
390  FAIL_IF_NOT(MacSetSize(ms) == 20);
391 
392  MacSetFree(ms);
393  PASS;
394 }
395 
396 static int MacSetTest04(void)
397 {
398  MacSet *ms = NULL;
399  SC_ATOMIC_SET(flow_config.memcap, 2);
400 
401  ms = MacSetInit(10);
402  FAIL_IF_NOT_NULL(ms);
403 
404  PASS;
405 }
406 
407 static int MacSetTest05(void)
408 {
409  MacSet *ms = NULL;
410  int ret = 0;
411  SC_ATOMIC_SET(flow_config.memcap, 64);
412 
413  ms = MacSetInit(10);
414  FAIL_IF_NULL(ms);
415  FAIL_IF_NOT(MacSetSize(ms) == 0);
416 
417  for (uint8_t i = 1; i < 100; i++) {
418  MacAddr addr1 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
419  addr2 = {0x1, 0x0, 0x0, 0x0, 0x0, 0x1};
420  addr1[5] = i;
421  addr2[5] = i;
422  MacSetAdd(ms, addr1, addr2);
423  }
424  FAIL_IF_NOT(MacSetSize(ms) == 2);
425 
426  int i2 = 100;
427  ret = MacSetForEach(ms, CheckTest1Membership, &i2);
428  FAIL_IF_NOT(ret == 0);
429 
430  MacSetFree(ms);
431  PASS;
432 }
433 
434 static int MacSetTest06(void)
435 {
436  SC_ATOMIC_SET(flow_config.memcap, 128);
437 
438  MacSet *ms = MacSetInit(10);
439  FAIL_IF_NULL(ms);
440  FAIL_IF_NOT(MacSetSize(ms) == 0);
441 
442  uint8_t *src0 = MacSetGetFirst(ms, MAC_SET_SRC);
443  uint8_t *dst0 = MacSetGetFirst(ms, MAC_SET_DST);
444 
445  MacAddr addr1 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x1 }, addr2 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x2 },
446  addr3 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x3 }, addr4 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x4 };
447 
448  MacSetAdd(ms, addr1, addr2);
449  uint8_t *src1 = MacSetGetFirst(ms, MAC_SET_SRC);
450  uint8_t *dst1 = MacSetGetFirst(ms, MAC_SET_DST);
451 
452  MacSetAdd(ms, addr3, addr4);
453  uint8_t *src2 = MacSetGetFirst(ms, MAC_SET_SRC);
454  uint8_t *dst2 = MacSetGetFirst(ms, MAC_SET_DST);
455 
456  FAIL_IF_NOT_NULL(src0);
457  FAIL_IF_NOT_NULL(dst0);
458  FAIL_IF_NOT(src1[5] == addr1[5]);
459  FAIL_IF_NOT(dst1[5] == addr2[5]);
460  FAIL_IF_NOT(src2[5] == addr1[5]);
461  FAIL_IF_NOT(dst2[5] == addr2[5]);
462 
463  MacSetFree(ms);
464  PASS;
465 }
466 
467 #endif /* UNITTESTS */
468 
470 {
471 
472 #ifdef UNITTESTS
473  UtRegisterTest("MacSetTest01", MacSetTest01);
474  UtRegisterTest("MacSetTest02", MacSetTest02);
475  UtRegisterTest("MacSetTest03", MacSetTest03);
476  UtRegisterTest("MacSetTest04", MacSetTest04);
477  UtRegisterTest("MacSetTest05", MacSetTest05);
478  UtRegisterTest("MacSetTest06", MacSetTest06);
479 #endif
480 }
FlowStorageId
Definition: flow-storage.h:31
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:182
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:224
SCConfValIsTrue
int SCConfValIsTrue(const char *val)
Check if a value is true.
Definition: conf.c:551
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:41
MacSetFree
void MacSetFree(MacSet *ms)
Definition: util-macset.c:279
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:103
next
struct HtpBodyChunk_ * next
Definition: app-layer-htp.h:0
MacSetIteratorFunc
int(* MacSetIteratorFunc)(uint8_t *addr, MacSetSide side, void *)
Definition: util-macset.h:33
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
TAILQ_FOREACH
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:252
MacSetRegisterFlowStorage
void MacSetRegisterFlowStorage(void)
Definition: util-macset.c:62
SCConfNodeLookupChildValue
const char * SCConfNodeLookupChildValue(const SCConfNode *node, const char *name)
Lookup the value of a child configuration node by name.
Definition: conf.c:824
util-unittest.h
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
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:42
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:47
ThreadVars_
Per thread variable structure.
Definition: threadvars.h:58
MacSet_::last
int last[2]
Definition: util-macset.c:57
FlowStorageRegister
FlowStorageId FlowStorageRegister(const char *name, const unsigned int size, void *(*Alloc)(unsigned int), void(*Free)(void *))
Definition: flow-storage.c:66
MAC_SET_DST
@ MAC_SET_DST
Definition: util-macset.h:30
MacSetFlowStorageEnabled
bool MacSetFlowStorageEnabled(void)
Definition: util-macset.c:86
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:56
MacSet_
Definition: util-macset.c:45
conf.h
MacSetGetFlowStorageID
FlowStorageId MacSetGetFlowStorageID(void)
Definition: util-macset.c:115
MacSetRegisterTests
void MacSetRegisterTests(void)
Definition: util-macset.c:469
MacSetAdd
void MacSetAdd(MacSet *ms, const uint8_t *src_addr, const uint8_t *dst_addr)
Definition: util-macset.c:191
EMPTY_SET
@ EMPTY_SET
Definition: util-macset.c:40
MAC_SET_SRC
@ MAC_SET_SRC
Definition: util-macset.h:29
MacSetState
MacSetState
Definition: util-macset.c:39
flow-storage.h
suricata-common.h
MacSetSize
int MacSetSize(const MacSet *ms)
Definition: util-macset.c:248
flow_config
FlowConfig flow_config
Definition: flow.c:94
StatsCounterMaxId::id
uint16_t id
Definition: counters.h:39
MacSetInit
MacSet * MacSetInit(int size)
Definition: util-macset.c:92
tv
ThreadVars * tv
Definition: fuzz_decodepcapfile.c:32
SCConfGetNode
SCConfNode * SCConfGetNode(const char *name)
Get a SCConfNode by name.
Definition: conf.c:181
SCLogError
#define SCLogError(...)
Macro used to log ERROR messages.
Definition: util-debug.h:271
SCFree
#define SCFree(p)
Definition: util-mem.h:61
MacSetGetFirst
uint8_t * MacSetGetFirst(const MacSet *ms, MacSetSide side)
Definition: util-macset.c:235
MacSetForEach
int MacSetForEach(const MacSet *ms, MacSetIteratorFunc IterFunc, void *data)
Definition: util-macset.c:222
FlowStorageId::id
int id
Definition: flow-storage.h:32
MacSet_::state
MacSetState state[2]
Definition: util-macset.c:53
suricata.h
MacAddr
uint8_t MacAddr[6]
Definition: util-macset.c:38
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:121
SCConfNode_
Definition: conf.h:37
MacSet_::buf
MacAddr * buf[2]
Definition: util-macset.c:55
SCConfNode_::val
char * val
Definition: conf.h:39
g_macset_storage_id
FlowStorageId g_macset_storage_id
Definition: util-macset.c:60