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 
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,
56  last[2];
57 };
58 
60 
62 {
63  ConfNode *root = ConfGetNode("outputs");
64  ConfNode *node = NULL;
65  /* we only need to register if at least one enabled 'eve-log' output
66  has the ethernet setting enabled */
67  if (root != NULL) {
68  TAILQ_FOREACH(node, &root->head, next) {
69  if (strcmp(node->val, "eve-log") == 0) {
70  const char *enabled = ConfNodeLookupChildValue(node->head.tqh_first, "enabled");
71  if (enabled != NULL && ConfValIsTrue(enabled)) {
72  const char *ethernet = ConfNodeLookupChildValue(node->head.tqh_first, "ethernet");
73  if (ethernet != NULL && ConfValIsTrue(ethernet)) {
74  g_macset_storage_id = FlowStorageRegister("macset", sizeof(void *),
75  NULL, (void(*)(void *)) MacSetFree);
76  return;
77  }
78  }
79  }
80  }
81  }
82 }
83 
85 {
86  return (g_macset_storage_id != -1);
87 }
88 
89 
90 MacSet *MacSetInit(int size)
91 {
92  MacSet *ms = NULL;
93  if (!FLOW_CHECK_MEMCAP(sizeof(ms))) {
94  return NULL;
95  }
96  ms = SCCalloc(1, sizeof(*ms));
97  if (unlikely(ms == NULL)) {
98  SCLogError(SC_ERR_MEM_ALLOC, "Unable to allocate MacSet memory");
99  return NULL;
100  }
101  (void) SC_ATOMIC_ADD(flow_memuse, (sizeof(*ms)));
103  if (size < 3) {
104  /* we want to make sure we have at space for at least 3 items to
105  fit MACs during the initial extension to MULTI_MAC storage */
106  size = 3;
107  }
108  ms->size = size;
109  ms->last[MAC_SET_SRC] = ms->last[MAC_SET_DST] = 0;
110  return ms;
111 }
112 
114 {
115  return g_macset_storage_id;
116 }
117 
118 static inline void MacUpdateEntry(MacSet *ms, uint8_t *addr, int side, ThreadVars *tv, uint16_t 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  StatsSetUI64(tv, 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(SC_ERR_MEM_ALLOC, "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  StatsSetUI64(tv, 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  StatsSetUI64(tv, ctr, ms->last[side]);
175  break;
176  }
177 }
178 
179 void MacSetAddWithCtr(MacSet *ms, uint8_t *src_addr, uint8_t *dst_addr, ThreadVars *tv,
180  uint16_t ctr_src, uint16_t 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, uint8_t *src_addr, uint8_t *dst_addr)
189 {
190  MacSetAddWithCtr(ms, src_addr, dst_addr, NULL, 0, 0);
191 }
192 
193 static inline int MacSetIterateSide(const MacSet *ms, MacSetIteratorFunc IterFunc,
194  MacSetSide side, void *data)
195 {
196  int ret = 0;
197  switch (ms->state[side]) {
198  case EMPTY_SET:
199  return 0;
200  case SINGLE_MAC:
201  ret = IterFunc((uint8_t*) ms->singles[side], side, data);
202  if (unlikely(ret != 0)) {
203  return ret;
204  }
205  break;
206  case MULTI_MAC:
207  for (int i = 0; i < ms->last[side]; i++) {
208  ret = IterFunc((uint8_t*) ms->buf[side][i], side, data);
209  if (unlikely(ret != 0)) {
210  return ret;
211  }
212  }
213  break;
214  }
215  return 0;
216 }
217 
218 int MacSetForEach(const MacSet *ms, MacSetIteratorFunc IterFunc, void *data)
219 {
220  int ret = 0;
221  if (ms == NULL)
222  return 0;
223 
224  ret = MacSetIterateSide(ms, IterFunc, MAC_SET_SRC, data);
225  if (ret != 0) {
226  return ret;
227  }
228  return MacSetIterateSide(ms, IterFunc, MAC_SET_DST, data);
229 }
230 
231 int MacSetSize(const MacSet *ms)
232 {
233  int size = 0;
234  if (ms == NULL)
235  return 0;
236 
237  switch(ms->state[MAC_SET_SRC]) {
238  case EMPTY_SET:
239  /* pass */
240  break;
241  case SINGLE_MAC:
242  size += 1;
243  break;
244  case MULTI_MAC:
245  size += ms->last[MAC_SET_SRC];
246  break;
247  }
248  switch(ms->state[MAC_SET_DST]) {
249  case EMPTY_SET:
250  /* pass */
251  break;
252  case SINGLE_MAC:
253  size += 1;
254  break;
255  case MULTI_MAC:
256  size += ms->last[MAC_SET_DST];
257  break;
258  }
259  return size;
260 }
261 
263 {
264  if (ms == NULL)
265  return;
267  ms->last[MAC_SET_SRC] = ms->last[MAC_SET_DST] = 0;
268 }
269 
271 {
272  size_t total_free = 0;
273  if (ms == NULL)
274  return;
275  if (ms->buf[MAC_SET_SRC] != NULL) {
276  SCFree(ms->buf[MAC_SET_SRC]);
277  total_free += ms->size * sizeof(MacAddr);
278  }
279  if (ms->buf[MAC_SET_DST] != NULL) {
280  SCFree(ms->buf[MAC_SET_DST]);
281  total_free += ms->size * sizeof(MacAddr);
282  }
283  SCFree(ms);
284  total_free += sizeof(*ms);
285  (void) SC_ATOMIC_SUB(flow_memuse, total_free);
286 }
287 
288 #ifdef UNITTESTS
289 
290 static int CheckTest1Membership(uint8_t *addr, MacSetSide side, void *data)
291 {
292  int *i = (int*) data;
293  switch (*i) {
294  case 0:
295  if (addr[5] != 1) return 1;
296  break;
297  case 1:
298  if (addr[5] != 2) return 1;
299  break;
300  case 2:
301  if (addr[5] != 3) return 1;
302  break;
303  }
304  (*i)++;
305  return 0;
306 }
307 
308 static int MacSetTest01(void)
309 {
310  MacSet *ms = NULL;
311  int ret = 0, i = 0;
312  MacAddr addr1 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
313  addr2 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x2},
314  addr3 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x3};
315  SC_ATOMIC_SET(flow_config.memcap, 10000);
316 
317  ms = MacSetInit(10);
318  FAIL_IF_NULL(ms);
319  FAIL_IF_NOT(MacSetSize(ms) == 0);
320 
321  ret = MacSetForEach(ms, CheckTest1Membership, &i);
322  FAIL_IF_NOT(ret == 0);
323 
324  MacSetAdd(ms, addr1, addr2);
325  FAIL_IF_NOT(MacSetSize(ms) == 2);
326 
327  ret = MacSetForEach(ms, CheckTest1Membership, &i);
328  FAIL_IF_NOT(ret == 0);
329 
330  MacSetAdd(ms, addr1, addr3);
331  FAIL_IF_NOT(MacSetSize(ms) == 3);
332 
333  i = 0;
334  ret = MacSetForEach(ms, CheckTest1Membership, &i);
335  FAIL_IF_NOT(ret == 0);
336 
337  MacSetReset(ms);
338  FAIL_IF_NOT(MacSetSize(ms) == 0);
339 
340  MacSetAdd(ms, addr2, addr3);
341  FAIL_IF_NOT(MacSetSize(ms) == 2);
342 
343  i = 1;
344  ret = MacSetForEach(ms, CheckTest1Membership, &i);
345  FAIL_IF_NOT(ret == 0);
346 
347  MacSetFree(ms);
348  PASS;
349 }
350 
351 static int MacSetTest02(void)
352 {
353  MacSet *ms = NULL;
354  int ret = 0, i = 0;
355  SC_ATOMIC_SET(flow_config.memcap, 10000);
356 
357  ms = MacSetInit(10);
358  FAIL_IF_NULL(ms);
359  FAIL_IF_NOT(MacSetSize(ms) == 0);
360 
361  for (i = 1; i < 100; i++) {
362  MacAddr addr1 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
363  addr2 = {0x1, 0x0, 0x0, 0x0, 0x0, 0x2};
364  MacSetAdd(ms, addr1, addr2);
365  }
366  FAIL_IF_NOT(MacSetSize(ms) == 2);
367 
368  ret = MacSetForEach(ms, CheckTest1Membership, &i);
369  FAIL_IF_NOT(ret == 0);
370 
371  MacSetFree(ms);
372  PASS;
373 }
374 
375 static int MacSetTest03(void)
376 {
377  MacSet *ms = NULL;
378  int i = 0;
379  SC_ATOMIC_SET(flow_config.memcap, 10000);
380 
381  ms = MacSetInit(10);
382  FAIL_IF_NULL(ms);
383  FAIL_IF_NOT(MacSetSize(ms) == 0);
384 
385  for (i = 1; i < 100; i++) {
386  MacAddr addr1 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
387  addr2 = {0x1, 0x0, 0x0, 0x0, 0x0, 0x1};
388  addr1[5] = i;
389  addr2[5] = i;
390  MacSetAdd(ms, addr1, addr2);
391  }
392  FAIL_IF_NOT(MacSetSize(ms) == 20);
393 
394  MacSetFree(ms);
395  PASS;
396 }
397 
398 static int MacSetTest04(void)
399 {
400  MacSet *ms = NULL;
401  SC_ATOMIC_SET(flow_config.memcap, 2);
402 
403  ms = MacSetInit(10);
404  FAIL_IF_NOT_NULL(ms);
405 
406  PASS;
407 }
408 
409 static int MacSetTest05(void)
410 {
411  MacSet *ms = NULL;
412  int ret = 0, i = 0;
413  SC_ATOMIC_SET(flow_config.memcap, 10);
414 
415  ms = MacSetInit(10);
416  FAIL_IF_NULL(ms);
417  FAIL_IF_NOT(MacSetSize(ms) == 0);
418 
419  for (i = 1; i < 100; i++) {
420  MacAddr addr1 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
421  addr2 = {0x1, 0x0, 0x0, 0x0, 0x0, 0x1};
422  addr1[5] = i;
423  addr2[5] = i;
424  MacSetAdd(ms, addr1, addr2);
425  }
426  FAIL_IF_NOT(MacSetSize(ms) == 2);
427 
428  ret = MacSetForEach(ms, CheckTest1Membership, &i);
429  FAIL_IF_NOT(ret == 0);
430 
431  MacSetFree(ms);
432  PASS;
433 }
434 
435 #endif /* UNITTESTS */
436 
438 {
439 
440 #ifdef UNITTESTS
441  UtRegisterTest("MacSetTest01", MacSetTest01);
442  UtRegisterTest("MacSetTest02", MacSetTest02);
443  UtRegisterTest("MacSetTest03", MacSetTest03);
444  UtRegisterTest("MacSetTest04", MacSetTest04);
445  UtRegisterTest("MacSetTest04", MacSetTest05);
446 #endif
447 
448  return;
449 }
MacSetSide
MacSetSide
Definition: util-macset.h:28
g_macset_storage_id
int g_macset_storage_id
Definition: util-macset.c:59
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
MacSetAdd
void MacSetAdd(MacSet *ms, uint8_t *src_addr, uint8_t *dst_addr)
Definition: util-macset.c:188
MacSetFree
void MacSetFree(MacSet *ms)
Definition: util-macset.c:270
flow-util.h
ConfNode_::val
char * val
Definition: conf.h:34
MacSetAddWithCtr
void MacSetAddWithCtr(MacSet *ms, uint8_t *src_addr, uint8_t *dst_addr, ThreadVars *tv, uint16_t ctr_src, uint16_t ctr_dst)
Definition: util-macset.c:179
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:387
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:103
MacSetGetFlowStorageID
int MacSetGetFlowStorageID(void)
Definition: util-macset.c:113
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:175
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:333
StatsSetUI64
void StatsSetUI64(ThreadVars *tv, uint16_t id, uint64_t x)
Sets a value of type double to the local counter.
Definition: counters.c:191
TAILQ_FOREACH
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:350
MacSetRegisterFlowStorage
void MacSetRegisterFlowStorage(void)
Definition: util-macset.c:61
util-unittest.h
ConfValIsTrue
int ConfValIsTrue(const char *val)
Check if a value is true.
Definition: conf.c:565
util-unittest-helper.h
FAIL_IF_NOT
#define FAIL_IF_NOT(expr)
Fail a test if expression to true.
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:144
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
MacSet_::singles
MacAddr singles[2]
Definition: util-macset.c:46
FlowStorageRegister
int FlowStorageRegister(const char *name, const unsigned int size, void *(*Alloc)(unsigned int), void(*Free)(void *))
Definition: flow-storage.c:65
ThreadVars_
Per thread variable structure.
Definition: threadvars.h:58
MacSet_::last
int last[2]
Definition: util-macset.c:56
MAC_SET_DST
@ MAC_SET_DST
Definition: util-macset.h:30
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:342
MacSet_::size
int size
Definition: util-macset.c:55
MacSet_
Definition: util-macset.c:44
MacSetRegisterTests
void MacSetRegisterTests(void)
Definition: util-macset.c:437
EMPTY_SET
@ EMPTY_SET
Definition: util-macset.c:39
MAC_SET_SRC
@ MAC_SET_SRC
Definition: util-macset.h:29
MacSetState
MacSetState
Definition: util-macset.c:38
flow-storage.h
MacSetReset
void MacSetReset(MacSet *ms)
Definition: util-macset.c:262
suricata-common.h
MacSetSize
int MacSetSize(const MacSet *ms)
Definition: util-macset.c:231
flow_config
FlowConfig flow_config
Definition: flow.c:98
SCLogError
#define SCLogError(err_code,...)
Macro used to log ERROR messages.
Definition: util-debug.h:257
MacSetInit
MacSet * MacSetInit(int size)
Definition: util-macset.c:90
tv
ThreadVars * tv
Definition: fuzz_decodepcapfile.c:29
SCFree
#define SCFree(p)
Definition: util-mem.h:61
ConfNode_
Definition: conf.h:32
MacSetForEach
int MacSetForEach(const MacSet *ms, MacSetIteratorFunc IterFunc, void *data)
Definition: util-macset.c:218
MacSet_::state
MacSetState state[2]
Definition: util-macset.c:52
SC_ERR_MEM_ALLOC
@ SC_ERR_MEM_ALLOC
Definition: util-error.h:31
suricata.h
MacAddr
uint8_t MacAddr[6]
Definition: util-macset.c:37
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:54
ConfNodeLookupChildValue
const char * ConfNodeLookupChildValue(const ConfNode *node, const char *name)
Lookup the value of a child configuration node by name.
Definition: conf.c:842