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 int MacSetSize(const MacSet *ms)
234 {
235  int size = 0;
236  if (ms == NULL)
237  return 0;
238 
239  switch(ms->state[MAC_SET_SRC]) {
240  case EMPTY_SET:
241  /* pass */
242  break;
243  case SINGLE_MAC:
244  size += 1;
245  break;
246  case MULTI_MAC:
247  size += ms->last[MAC_SET_SRC];
248  break;
249  }
250  switch(ms->state[MAC_SET_DST]) {
251  case EMPTY_SET:
252  /* pass */
253  break;
254  case SINGLE_MAC:
255  size += 1;
256  break;
257  case MULTI_MAC:
258  size += ms->last[MAC_SET_DST];
259  break;
260  }
261  return size;
262 }
263 
265 {
266  size_t total_free = 0;
267  if (ms == NULL)
268  return;
269  if (ms->buf[MAC_SET_SRC] != NULL) {
270  SCFree(ms->buf[MAC_SET_SRC]);
271  total_free += ms->size * sizeof(MacAddr);
272  }
273  if (ms->buf[MAC_SET_DST] != NULL) {
274  SCFree(ms->buf[MAC_SET_DST]);
275  total_free += ms->size * sizeof(MacAddr);
276  }
277  SCFree(ms);
278  total_free += sizeof(*ms);
279  (void) SC_ATOMIC_SUB(flow_memuse, total_free);
280 }
281 
282 #ifdef UNITTESTS
283 
284 static int CheckTest1Membership(uint8_t *addr, MacSetSide side, void *data)
285 {
286  int *i = (int*) data;
287  switch (*i) {
288  case 0:
289  if (addr[5] != 1) return 1;
290  break;
291  case 1:
292  if (addr[5] != 2) return 1;
293  break;
294  case 2:
295  if (addr[5] != 3) return 1;
296  break;
297  }
298  (*i)++;
299  return 0;
300 }
301 
302 static int MacSetTest01(void)
303 {
304  MacSet *ms = NULL;
305  int ret = 0, i = 0;
306  MacAddr addr1 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
307  addr2 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x2},
308  addr3 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x3};
309  SC_ATOMIC_SET(flow_config.memcap, 10000);
310 
311  ms = MacSetInit(10);
312  FAIL_IF_NULL(ms);
313  FAIL_IF_NOT(MacSetSize(ms) == 0);
314 
315  ret = MacSetForEach(ms, CheckTest1Membership, &i);
316  FAIL_IF_NOT(ret == 0);
317 
318  MacSetAdd(ms, addr1, addr2);
319  FAIL_IF_NOT(MacSetSize(ms) == 2);
320 
321  ret = MacSetForEach(ms, CheckTest1Membership, &i);
322  FAIL_IF_NOT(ret == 0);
323 
324  MacSetAdd(ms, addr1, addr3);
325  FAIL_IF_NOT(MacSetSize(ms) == 3);
326 
327  i = 0;
328  ret = MacSetForEach(ms, CheckTest1Membership, &i);
329  FAIL_IF_NOT(ret == 0);
330 
331  MacSetFree(ms);
332  PASS;
333 }
334 
335 static int MacSetTest02(void)
336 {
337  MacSet *ms = NULL;
338  int ret = 0, i = 0;
339  SC_ATOMIC_SET(flow_config.memcap, 10000);
340 
341  ms = MacSetInit(10);
342  FAIL_IF_NULL(ms);
343  FAIL_IF_NOT(MacSetSize(ms) == 0);
344 
345  for (i = 1; i < 100; i++) {
346  MacAddr addr1 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
347  addr2 = {0x1, 0x0, 0x0, 0x0, 0x0, 0x2};
348  MacSetAdd(ms, addr1, addr2);
349  }
350  FAIL_IF_NOT(MacSetSize(ms) == 2);
351 
352  ret = MacSetForEach(ms, CheckTest1Membership, &i);
353  FAIL_IF_NOT(ret == 0);
354 
355  MacSetFree(ms);
356  PASS;
357 }
358 
359 static int MacSetTest03(void)
360 {
361  MacSet *ms = NULL;
362  SC_ATOMIC_SET(flow_config.memcap, 10000);
363 
364  ms = MacSetInit(10);
365  FAIL_IF_NULL(ms);
366  FAIL_IF_NOT(MacSetSize(ms) == 0);
367 
368  for (uint8_t i = 1; i < 100; i++) {
369  MacAddr addr1 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
370  addr2 = {0x1, 0x0, 0x0, 0x0, 0x0, 0x1};
371  addr1[5] = i;
372  addr2[5] = i;
373  MacSetAdd(ms, addr1, addr2);
374  }
375  FAIL_IF_NOT(MacSetSize(ms) == 20);
376 
377  MacSetFree(ms);
378  PASS;
379 }
380 
381 static int MacSetTest04(void)
382 {
383  MacSet *ms = NULL;
384  SC_ATOMIC_SET(flow_config.memcap, 2);
385 
386  ms = MacSetInit(10);
387  FAIL_IF_NOT_NULL(ms);
388 
389  PASS;
390 }
391 
392 static int MacSetTest05(void)
393 {
394  MacSet *ms = NULL;
395  int ret = 0;
396  SC_ATOMIC_SET(flow_config.memcap, 64);
397 
398  ms = MacSetInit(10);
399  FAIL_IF_NULL(ms);
400  FAIL_IF_NOT(MacSetSize(ms) == 0);
401 
402  for (uint8_t i = 1; i < 100; i++) {
403  MacAddr addr1 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
404  addr2 = {0x1, 0x0, 0x0, 0x0, 0x0, 0x1};
405  addr1[5] = i;
406  addr2[5] = i;
407  MacSetAdd(ms, addr1, addr2);
408  }
409  FAIL_IF_NOT(MacSetSize(ms) == 2);
410 
411  int i2 = 100;
412  ret = MacSetForEach(ms, CheckTest1Membership, &i2);
413  FAIL_IF_NOT(ret == 0);
414 
415  MacSetFree(ms);
416  PASS;
417 }
418 
419 #endif /* UNITTESTS */
420 
422 {
423 
424 #ifdef UNITTESTS
425  UtRegisterTest("MacSetTest01", MacSetTest01);
426  UtRegisterTest("MacSetTest02", MacSetTest02);
427  UtRegisterTest("MacSetTest03", MacSetTest03);
428  UtRegisterTest("MacSetTest04", MacSetTest04);
429  UtRegisterTest("MacSetTest05", MacSetTest05);
430 #endif
431 }
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:264
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: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
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:421
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:233
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
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