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(MacSet *ms, uint8_t *addr, int side, ThreadVars *tv, uint16_t ctr)
120 {
121  switch (ms->state[side]) {
122  case EMPTY_SET:
123  memcpy(ms->singles[side], addr, sizeof(MacAddr));
124  ms->state[side] = SINGLE_MAC;
125  if (tv != NULL)
126  StatsSetUI64(tv, ctr, 1);
127  break;
128  case SINGLE_MAC:
129  if (unlikely(memcmp(addr, ms->singles[side], sizeof(MacAddr)) != 0)) {
130  if (ms->buf[side] == NULL) {
131  if (!FLOW_CHECK_MEMCAP(ms->size * sizeof(MacAddr))) {
132  /* in this case there is not much we can do */
133  return;
134  }
135  ms->buf[side] = SCCalloc(ms->size, sizeof(MacAddr));
136  if (unlikely(ms->buf[side] == NULL)) {
137  SCLogError("Unable to allocate "
138  "MacSet memory");
139  return;
140  }
141  (void) SC_ATOMIC_ADD(flow_memuse, (ms->size * sizeof(MacAddr)));
142  }
143  memcpy(ms->buf[side], ms->singles[side], sizeof(MacAddr));
144  memcpy(ms->buf[side] + 1, addr, sizeof(MacAddr));
145  ms->last[side] = 2;
146  if (tv != NULL)
147  StatsSetUI64(tv, ctr, 2);
148  ms->state[side] = MULTI_MAC;
149  }
150  break;
151  case MULTI_MAC:
152  if (unlikely(ms->last[side] == ms->size)) {
153  /* MacSet full, ignore item. We intentionally do not output
154  any warning in order not to stall packet processing */
155  return;
156  }
157  /* If the set is non-empty... */
158  if (ms->last[side] > 0) {
159  /* ...we search for duplicates in the set to decide whether
160  we need to insert the current item. We do this backwards,
161  since we expect the latest item to match more likely than
162  the first */
163  for (int i = ms->last[side] - 1; i >= 0; i--) {
164  uint8_t *addr2 = (uint8_t*) ((ms->buf[side]) + i);
165  /* If we find a match, we return early with no action */
166  if (likely(memcmp(addr2, addr, sizeof(MacAddr)) == 0)) {
167  return;
168  }
169  }
170  }
171  /* Otherwise, we insert the new address at the end */
172  memcpy(ms->buf[side] + ms->last[side], addr, sizeof(MacAddr));
173  ms->last[side]++;
174  if (tv != NULL)
175  StatsSetUI64(tv, ctr, ms->last[side]);
176  break;
177  }
178 }
179 
180 void MacSetAddWithCtr(MacSet *ms, uint8_t *src_addr, uint8_t *dst_addr, ThreadVars *tv,
181  uint16_t ctr_src, uint16_t ctr_dst)
182 {
183  if (ms == NULL)
184  return;
185  MacUpdateEntry(ms, src_addr, MAC_SET_SRC, tv, ctr_src);
186  MacUpdateEntry(ms, dst_addr, MAC_SET_DST, tv, ctr_dst);
187 }
188 
189 void MacSetAdd(MacSet *ms, uint8_t *src_addr, uint8_t *dst_addr)
190 {
191  MacSetAddWithCtr(ms, src_addr, dst_addr, NULL, 0, 0);
192 }
193 
194 static inline int MacSetIterateSide(const MacSet *ms, MacSetIteratorFunc IterFunc,
195  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 int MacSetSize(const MacSet *ms)
233 {
234  int size = 0;
235  if (ms == NULL)
236  return 0;
237 
238  switch(ms->state[MAC_SET_SRC]) {
239  case EMPTY_SET:
240  /* pass */
241  break;
242  case SINGLE_MAC:
243  size += 1;
244  break;
245  case MULTI_MAC:
246  size += ms->last[MAC_SET_SRC];
247  break;
248  }
249  switch(ms->state[MAC_SET_DST]) {
250  case EMPTY_SET:
251  /* pass */
252  break;
253  case SINGLE_MAC:
254  size += 1;
255  break;
256  case MULTI_MAC:
257  size += ms->last[MAC_SET_DST];
258  break;
259  }
260  return size;
261 }
262 
264 {
265  size_t total_free = 0;
266  if (ms == NULL)
267  return;
268  if (ms->buf[MAC_SET_SRC] != NULL) {
269  SCFree(ms->buf[MAC_SET_SRC]);
270  total_free += ms->size * sizeof(MacAddr);
271  }
272  if (ms->buf[MAC_SET_DST] != NULL) {
273  SCFree(ms->buf[MAC_SET_DST]);
274  total_free += ms->size * sizeof(MacAddr);
275  }
276  SCFree(ms);
277  total_free += sizeof(*ms);
278  (void) SC_ATOMIC_SUB(flow_memuse, total_free);
279 }
280 
281 #ifdef UNITTESTS
282 
283 static int CheckTest1Membership(uint8_t *addr, MacSetSide side, void *data)
284 {
285  int *i = (int*) data;
286  switch (*i) {
287  case 0:
288  if (addr[5] != 1) return 1;
289  break;
290  case 1:
291  if (addr[5] != 2) return 1;
292  break;
293  case 2:
294  if (addr[5] != 3) return 1;
295  break;
296  }
297  (*i)++;
298  return 0;
299 }
300 
301 static int MacSetTest01(void)
302 {
303  MacSet *ms = NULL;
304  int ret = 0, i = 0;
305  MacAddr addr1 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
306  addr2 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x2},
307  addr3 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x3};
308  SC_ATOMIC_SET(flow_config.memcap, 10000);
309 
310  ms = MacSetInit(10);
311  FAIL_IF_NULL(ms);
312  FAIL_IF_NOT(MacSetSize(ms) == 0);
313 
314  ret = MacSetForEach(ms, CheckTest1Membership, &i);
315  FAIL_IF_NOT(ret == 0);
316 
317  MacSetAdd(ms, addr1, addr2);
318  FAIL_IF_NOT(MacSetSize(ms) == 2);
319 
320  ret = MacSetForEach(ms, CheckTest1Membership, &i);
321  FAIL_IF_NOT(ret == 0);
322 
323  MacSetAdd(ms, addr1, addr3);
324  FAIL_IF_NOT(MacSetSize(ms) == 3);
325 
326  i = 0;
327  ret = MacSetForEach(ms, CheckTest1Membership, &i);
328  FAIL_IF_NOT(ret == 0);
329 
330  MacSetFree(ms);
331  PASS;
332 }
333 
334 static int MacSetTest02(void)
335 {
336  MacSet *ms = NULL;
337  int ret = 0, i = 0;
338  SC_ATOMIC_SET(flow_config.memcap, 10000);
339 
340  ms = MacSetInit(10);
341  FAIL_IF_NULL(ms);
342  FAIL_IF_NOT(MacSetSize(ms) == 0);
343 
344  for (i = 1; i < 100; i++) {
345  MacAddr addr1 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
346  addr2 = {0x1, 0x0, 0x0, 0x0, 0x0, 0x2};
347  MacSetAdd(ms, addr1, addr2);
348  }
349  FAIL_IF_NOT(MacSetSize(ms) == 2);
350 
351  ret = MacSetForEach(ms, CheckTest1Membership, &i);
352  FAIL_IF_NOT(ret == 0);
353 
354  MacSetFree(ms);
355  PASS;
356 }
357 
358 static int MacSetTest03(void)
359 {
360  MacSet *ms = NULL;
361  SC_ATOMIC_SET(flow_config.memcap, 10000);
362 
363  ms = MacSetInit(10);
364  FAIL_IF_NULL(ms);
365  FAIL_IF_NOT(MacSetSize(ms) == 0);
366 
367  for (uint8_t i = 1; i < 100; i++) {
368  MacAddr addr1 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
369  addr2 = {0x1, 0x0, 0x0, 0x0, 0x0, 0x1};
370  addr1[5] = i;
371  addr2[5] = i;
372  MacSetAdd(ms, addr1, addr2);
373  }
374  FAIL_IF_NOT(MacSetSize(ms) == 20);
375 
376  MacSetFree(ms);
377  PASS;
378 }
379 
380 static int MacSetTest04(void)
381 {
382  MacSet *ms = NULL;
383  SC_ATOMIC_SET(flow_config.memcap, 2);
384 
385  ms = MacSetInit(10);
386  FAIL_IF_NOT_NULL(ms);
387 
388  PASS;
389 }
390 
391 static int MacSetTest05(void)
392 {
393  MacSet *ms = NULL;
394  int ret = 0;
395  SC_ATOMIC_SET(flow_config.memcap, 64);
396 
397  ms = MacSetInit(10);
398  FAIL_IF_NULL(ms);
399  FAIL_IF_NOT(MacSetSize(ms) == 0);
400 
401  for (uint8_t i = 1; i < 100; i++) {
402  MacAddr addr1 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
403  addr2 = {0x1, 0x0, 0x0, 0x0, 0x0, 0x1};
404  addr1[5] = i;
405  addr2[5] = i;
406  MacSetAdd(ms, addr1, addr2);
407  }
408  FAIL_IF_NOT(MacSetSize(ms) == 2);
409 
410  int i2 = 100;
411  ret = MacSetForEach(ms, CheckTest1Membership, &i2);
412  FAIL_IF_NOT(ret == 0);
413 
414  MacSetFree(ms);
415  PASS;
416 }
417 
418 #endif /* UNITTESTS */
419 
421 {
422 
423 #ifdef UNITTESTS
424  UtRegisterTest("MacSetTest01", MacSetTest01);
425  UtRegisterTest("MacSetTest02", MacSetTest02);
426  UtRegisterTest("MacSetTest03", MacSetTest03);
427  UtRegisterTest("MacSetTest04", MacSetTest04);
428  UtRegisterTest("MacSetTest05", MacSetTest05);
429 #endif
430 
431  return;
432 }
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
MacSetAdd
void MacSetAdd(MacSet *ms, uint8_t *src_addr, uint8_t *dst_addr)
Definition: util-macset.c:189
MacSetFree
void MacSetFree(MacSet *ms)
Definition: util-macset.c:263
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:180
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:210
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:537
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:136
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
ThreadVars_
Per thread variable structure.
Definition: threadvars.h:57
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:420
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:232
flow_config
FlowConfig flow_config
Definition: flow.c:102
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
MacSetForEach
int MacSetForEach(const MacSet *ms, MacSetIteratorFunc IterFunc, void *data)
Definition: util-macset.c:219
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:814