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, 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 
182 void 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 
191 void 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 
196 static 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 
221 int 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 
234 uint8_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 
247 int 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 
298 static 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 
316 static 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 
349 static 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 
373 static 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 
395 static int MacSetTest04(void)
396 {
397  MacSet *ms = NULL;
398  SC_ATOMIC_SET(flow_config.memcap, 2);
399 
400  ms = MacSetInit(10);
401  FAIL_IF_NOT_NULL(ms);
402 
403  PASS;
404 }
405 
406 static 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 
433 static 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 }
FlowStorageId
Definition: flow-storage.h:31
MacSetSide
MacSetSide
Definition: util-macset.h:28
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:278
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
StatsSetUI64
void StatsSetUI64(ThreadVars *tv, uint16_t id, uint64_t x)
Sets a value of type double to the local counter.
Definition: counters.c:207
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
MacSet_::singles
MacAddr singles[2]
Definition: util-macset.c:47
MacSetAddWithCtr
void MacSetAddWithCtr(MacSet *ms, const uint8_t *src_addr, const uint8_t *dst_addr, ThreadVars *tv, uint16_t ctr_src, uint16_t ctr_dst)
Definition: util-macset.c:182
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:468
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:247
flow_config
FlowConfig flow_config
Definition: flow.c:92
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:261
SCFree
#define SCFree(p)
Definition: util-mem.h:61
MacSetGetFirst
uint8_t * MacSetGetFirst(const MacSet *ms, MacSetSide side)
Definition: util-macset.c:234
MacSetForEach
int MacSetForEach(const MacSet *ms, MacSetIteratorFunc IterFunc, void *data)
Definition: util-macset.c:221
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
SCConfNode_
Definition: conf.h:32
MacSet_::buf
MacAddr * buf[2]
Definition: util-macset.c:55
SCConfNode_::val
char * val
Definition: conf.h:34
g_macset_storage_id
FlowStorageId g_macset_storage_id
Definition: util-macset.c:60