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