From: Sascha Steinbiss Date: Mon, 2 Mar 2020 18:10:30 +0000 (+0100) Subject: output-json: add MAC address output X-Git-Tag: suricata-6.0.0-beta1~54 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F5258%2Fhead;p=thirdparty%2Fsuricata.git output-json: add MAC address output This commit adds MAC address output to the EVE-JSON format. We follow the remarks made in Redmine ticket #962: for packets, log MAC src/dst as a scalar field in EVE; for flows, log MAC src/dst as lists in EVE. Field names are different between flow and packet context to avoid type confusion (src_mac vs. src_macs). Configuration approach and JSON representation is taken from previous GitHub PR #2700. --- diff --git a/doc/userguide/output/eve/eve-json-output.rst b/doc/userguide/output/eve/eve-json-output.rst index a8c7ce9320..69f8b071fc 100644 --- a/doc/userguide/output/eve/eve-json-output.rst +++ b/doc/userguide/output/eve/eve-json-output.rst @@ -14,6 +14,9 @@ where all these logs go into a single file. Each alert, http log, etc will go into this one file: 'eve.json'. This file can then be processed by 3rd party tools like Logstash (ELK) or jq. +If ``ethernet`` is set to yes, then ethernet headers will be added to events +if available. + Output types ~~~~~~~~~~~~ @@ -33,6 +36,7 @@ Output types:: #facility: local5 #level: Info ## possible levels: Emergency, Alert, Critical, ## Error, Warning, Notice, Info, Debug + #ethernet: no # log ethernet header in events when available #redis: # server: 127.0.0.1 # port: 6379 diff --git a/src/Makefile.am b/src/Makefile.am index 0b89e932cb..df3f52d68c 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -505,6 +505,7 @@ util-lua-tls.c util-lua-tls.h \ util-lua-ssh.c util-lua-ssh.h \ util-lua-hassh.c util-lua-hassh.h \ util-lua-smtp.c util-lua-smtp.h \ +util-macset.c util-macset.h \ util-magic.c util-magic.h \ util-memcmp.c util-memcmp.h \ util-memcpy.h \ diff --git a/src/decode.c b/src/decode.c index 8e68a24d76..7083e62a18 100644 --- a/src/decode.c +++ b/src/decode.c @@ -506,6 +506,8 @@ void DecodeRegisterPerfCounters(DecodeThreadVars *dtv, ThreadVars *tv) dtv->counter_mpls = StatsRegisterCounter("decoder.mpls", tv); dtv->counter_avg_pkt_size = StatsRegisterAvgCounter("decoder.avg_pkt_size", tv); dtv->counter_max_pkt_size = StatsRegisterMaxCounter("decoder.max_pkt_size", tv); + dtv->counter_max_mac_addrs_src = StatsRegisterMaxCounter("decoder.max_mac_addrs_src", tv); + dtv->counter_max_mac_addrs_dst = StatsRegisterMaxCounter("decoder.max_mac_addrs_dst", tv); dtv->counter_erspan = StatsRegisterMaxCounter("decoder.erspan", tv); dtv->counter_flow_memcap = StatsRegisterCounter("flow.memcap", tv); diff --git a/src/decode.h b/src/decode.h index aa5591518f..ffeb57c845 100644 --- a/src/decode.h +++ b/src/decode.h @@ -631,6 +631,8 @@ typedef struct DecodeThreadVars_ uint16_t counter_bytes; uint16_t counter_avg_pkt_size; uint16_t counter_max_pkt_size; + uint16_t counter_max_mac_addrs_src; + uint16_t counter_max_mac_addrs_dst; uint16_t counter_invalid; diff --git a/src/flow-util.c b/src/flow-util.c index ea18d053ba..f4dad5dd80 100644 --- a/src/flow-util.c +++ b/src/flow-util.c @@ -197,6 +197,16 @@ void FlowInit(Flow *f, const Packet *p) f->protomap = FlowGetProtoMapping(f->proto); + if (MacSetFlowStorageEnabled()) { + MacSet *ms = FlowGetStorageById(f, MacSetGetFlowStorageID()); + if (ms != NULL) { + MacSetReset(ms); + } else { + ms = MacSetInit(10); + FlowSetStorageById(f, MacSetGetFlowStorageID(), ms); + } + } + SCReturn; } diff --git a/src/flow-util.h b/src/flow-util.h index 96ed814c74..ee95294476 100644 --- a/src/flow-util.h +++ b/src/flow-util.h @@ -115,6 +115,12 @@ (f)->sgh_toclient = NULL; \ GenericVarFree((f)->flowvar); \ (f)->flowvar = NULL; \ + if (MacSetFlowStorageEnabled()) { \ + MacSet *ms = FlowGetStorageById((f), MacSetGetFlowStorageID()); \ + if (ms != NULL) { \ + MacSetReset(ms); \ + } \ + } \ RESET_COUNTERS((f)); \ } while(0) diff --git a/src/flow-worker.c b/src/flow-worker.c index 18757308f0..489ffefa4f 100644 --- a/src/flow-worker.c +++ b/src/flow-worker.c @@ -75,7 +75,7 @@ typedef struct FlowWorkerThreadData_ { */ static inline TmEcode FlowUpdate(ThreadVars *tv, FlowWorkerThreadData *fw, Packet *p) { - FlowHandlePacketUpdate(p->flow, p); + FlowHandlePacketUpdate(p->flow, p, tv, fw->dtv); int state = SC_ATOMIC_GET(p->flow->flow_state); switch (state) { diff --git a/src/flow.c b/src/flow.c index 3459b10cfe..a0e637ed94 100644 --- a/src/flow.c +++ b/src/flow.c @@ -402,6 +402,25 @@ static inline void FlowUpdateTTL(Flow *f, Packet *p, uint8_t ttl) } } +static inline void FlowUpdateEthernet(ThreadVars *tv, DecodeThreadVars *dtv, + Flow *f, EthernetHdr *ethh, bool toserver) +{ + if (ethh && MacSetFlowStorageEnabled()) { + MacSet *ms = FlowGetStorageById(f, MacSetGetFlowStorageID()); + if (ms != NULL) { + if (toserver) { + MacSetAddWithCtr(ms, ethh->eth_src, ethh->eth_dst, tv, + dtv->counter_max_mac_addrs_src, + dtv->counter_max_mac_addrs_dst); + } else { + MacSetAddWithCtr(ms, ethh->eth_dst, ethh->eth_src, tv, + dtv->counter_max_mac_addrs_dst, + dtv->counter_max_mac_addrs_src); + } + } + } +} + /** \brief Update Packet and Flow * * Updates packet and flow based on the new packet. @@ -411,7 +430,7 @@ static inline void FlowUpdateTTL(Flow *f, Packet *p, uint8_t ttl) * * \note overwrites p::flowflags */ -void FlowHandlePacketUpdate(Flow *f, Packet *p) +void FlowHandlePacketUpdate(Flow *f, Packet *p, ThreadVars *tv, DecodeThreadVars *dtv) { SCLogDebug("packet %"PRIu64" -- flow %p", p->pcap_cnt, f); @@ -454,6 +473,7 @@ void FlowHandlePacketUpdate(Flow *f, Packet *p) f->flags &= ~FLOW_PROTO_DETECT_TS_DONE; p->flags |= PKT_PROTO_DETECT_TS_DONE; } + FlowUpdateEthernet(tv, dtv, f, p->ethh, true); } else { f->tosrcpktcnt++; f->tosrcbytecnt += GET_PKT_LEN(p); @@ -469,6 +489,7 @@ void FlowHandlePacketUpdate(Flow *f, Packet *p) f->flags &= ~FLOW_PROTO_DETECT_TC_DONE; p->flags |= PKT_PROTO_DETECT_TC_DONE; } + FlowUpdateEthernet(tv, dtv, f, p->ethh, false); } if (SC_ATOMIC_GET(f->flow_state) == FLOW_STATE_ESTABLISHED) { @@ -498,7 +519,6 @@ void FlowHandlePacketUpdate(Flow *f, Packet *p) DecodeSetNoPayloadInspectionFlag(p); } - /* update flow's ttl fields if needed */ if (PKT_IS_IPV4(p)) { FlowUpdateTTL(f, p, IPV4_GET_IPTTL(p)); diff --git a/src/flow.h b/src/flow.h index f625ada106..24113ae3b2 100644 --- a/src/flow.h +++ b/src/flow.h @@ -29,6 +29,7 @@ #include "util-atomic.h" #include "util-device.h" #include "detect-tag.h" +#include "util-macset.h" #include "util-optimize.h" /* Part of the flow structure, so we declare it here. @@ -661,6 +662,6 @@ AppProto FlowGetAppProtocol(const Flow *f); void *FlowGetAppState(const Flow *f); uint8_t FlowGetDisruptionFlags(const Flow *f, uint8_t flags); -void FlowHandlePacketUpdate(Flow *f, Packet *p); +void FlowHandlePacketUpdate(Flow *f, Packet *p, ThreadVars *tv, DecodeThreadVars *dtv); #endif /* __FLOW_H__ */ diff --git a/src/output-json.c b/src/output-json.c index e3c2381ea1..09db2494d4 100644 --- a/src/output-json.c +++ b/src/output-json.c @@ -63,6 +63,7 @@ #include "flow-var.h" #include "flow-bit.h" +#include "flow-storage.h" #include "source-pcap-file.h" @@ -435,12 +436,19 @@ static void EveAddMetadata(const Packet *p, const Flow *f, JsonBuilder *js) } } +int CreateJSONEther(JsonBuilder *parent, const Packet *p, const MacSet *ms); + void EveAddCommonOptions(const OutputJsonCommonSettings *cfg, const Packet *p, const Flow *f, JsonBuilder *js) { if (cfg->include_metadata) { EveAddMetadata(p, f, js); } + if (cfg->include_ethernet) { + MacSet *ms = FlowGetStorageById((Flow*) f, MacSetGetFlowStorageID()); + if (ms != NULL) + CreateJSONEther(js, p, ms); + } if (cfg->include_community_id && f != NULL) { CreateEveCommunityFlowId(js, f, cfg->community_id_seed); } @@ -758,6 +766,80 @@ void CreateEveFlowId(JsonBuilder *js, const Flow *f) } } +static inline void JSONFormatAndAddMACAddr(JsonBuilder *js, const char *key, + uint8_t *val, bool is_array) +{ + char eth_addr[19]; + (void) snprintf(eth_addr, 19, "%02x:%02x:%02x:%02x:%02x:%02x", + val[0], val[1], val[2], val[3], val[4], val[5]); + if (is_array) { + jb_append_string(js, eth_addr); + } else { + jb_set_string(js, key, eth_addr); + } +} + +/* only required to traverse the MAC address set */ +typedef struct JSONMACAddrInfo { + JsonBuilder *src, *dst; +} JSONMACAddrInfo; + +static int MacSetIterateToJSON(uint8_t *val, MacSetSide side, void *data) +{ + JSONMACAddrInfo *info = (JSONMACAddrInfo*) data; + if (side == MAC_SET_DST) { + JSONFormatAndAddMACAddr(info->dst, NULL, val, true); + } else { + JSONFormatAndAddMACAddr(info->src, NULL, val, true); + } + return 0; +} + +int CreateJSONEther(JsonBuilder *js, const Packet *p, const MacSet *ms) +{ + jb_open_object(js, "ether"); + if (unlikely(js == NULL)) + return 0; + if (p == NULL) { + /* we are creating an ether object in a flow context, so we need to + append to arrays */ + if (MacSetSize(ms) > 0) { + JSONMACAddrInfo info; + info.dst = jb_new_array(); + info.src = jb_new_array(); + int ret = MacSetForEach(ms, MacSetIterateToJSON, &info); + if (unlikely(ret != 0)) { + /* should not happen, JSONFlowAppendMACAddrs is sane */ + jb_free(info.dst); + jb_free(info.src); + return ret; + } + jb_close(info.dst); + jb_close(info.src); + jb_set_object(js, "dest_macs", info.dst); + jb_set_object(js, "src_macs", info.src); + jb_free(info.dst); + jb_free(info.src); + } + } else { + /* this is a packet context, so we need to add scalar fields */ + uint8_t *src, *dst; + if (p->ethh != NULL) { + if ((PKT_IS_TOCLIENT(p))) { + src = p->ethh->eth_dst; + dst = p->ethh->eth_src; + } else { + src = p->ethh->eth_src; + dst = p->ethh->eth_dst; + } + JSONFormatAndAddMACAddr(js, "src_mac", src, false); + JSONFormatAndAddMACAddr(js, "dest_mac", dst, false); + } + } + jb_close(js); + return 0; +} + JsonBuilder *CreateEveHeader(const Packet *p, enum OutputJsonLogDirection dir, const char *event_type, JsonAddrInfo *addr) { @@ -1115,6 +1197,15 @@ OutputInitResult OutputJsonInitCtx(ConfNode *conf) json_ctx->cfg.include_metadata = true; } + /* Check if ethernet information should be logged. */ + const ConfNode *ethernet = ConfNodeLookupChild(conf, "ethernet"); + if (ethernet && ethernet->val && ConfValIsTrue(ethernet->val)) { + SCLogConfig("Enabling Ethernet MAC address logging."); + json_ctx->cfg.include_ethernet = true; + } else { + json_ctx->cfg.include_ethernet = false; + } + /* See if we want to enable the community id */ const ConfNode *community_id = ConfNodeLookupChild(conf, "community-id"); if (community_id && community_id->val && ConfValIsTrue(community_id->val)) { @@ -1154,7 +1245,6 @@ OutputInitResult OutputJsonInitCtx(ConfNode *conf) json_ctx->file_ctx->type = json_ctx->json_out; } - SCLogDebug("returning output_ctx %p", output_ctx); result.ctx = output_ctx; diff --git a/src/output-json.h b/src/output-json.h index 57515d0fbc..c34efdf719 100644 --- a/src/output-json.h +++ b/src/output-json.h @@ -90,6 +90,7 @@ TmEcode JsonLogThreadDeinit(ThreadVars *t, void *data); typedef struct OutputJsonCommonSettings_ { bool include_metadata; bool include_community_id; + bool include_ethernet; uint16_t community_id_seed; } OutputJsonCommonSettings; diff --git a/src/runmode-unittests.c b/src/runmode-unittests.c index 4631d31892..67c504cc43 100644 --- a/src/runmode-unittests.c +++ b/src/runmode-unittests.c @@ -92,6 +92,7 @@ #include "util-pool.h" #include "util-byte.h" #include "util-proto-name.h" +#include "util-macset.h" #include "util-memrchr.h" #include "util-mpm-ac.h" @@ -198,6 +199,7 @@ static void RegisterUnittests(void) AppLayerUnittestsRegister(); MimeDecRegisterTests(); StreamingBufferRegisterTests(); + MacSetRegisterTests(); #ifdef OS_WIN32 Win32SyscallRegisterTests(); #endif diff --git a/src/suricata.c b/src/suricata.c index 74cbccef12..3def54f0b0 100644 --- a/src/suricata.c +++ b/src/suricata.c @@ -2470,6 +2470,9 @@ int PostConfLoadedSetup(SCInstance *suri) LiveDevRegisterExtension(); #endif RegisterFlowBypassInfo(); + + MacSetRegisterFlowStorage(); + AppLayerSetup(); /* Suricata will use this umask if provided. By default it will use the diff --git a/src/util-macset.c b/src/util-macset.c new file mode 100644 index 0000000000..30e6c6b343 --- /dev/null +++ b/src/util-macset.c @@ -0,0 +1,449 @@ +/* Copyright (C) 2020 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + * + * \author Sascha Steinbiss + * + * Set-like data store for MAC addresses. Implemented as array for memory + * locality reasons as the expected number of items is typically low. + * + */ + +#include "suricata-common.h" +#include "suricata.h" +#include "flow-util.h" +#include "flow-private.h" +#include "flow-storage.h" +#include "util-macset.h" +#include "util-unittest.h" +#include "util-unittest-helper.h" + +typedef uint8_t MacAddr[6]; +typedef enum { + EMPTY_SET, /* no address inserted yet */ + SINGLE_MAC, /* we have a single pair of addresses (likely) */ + MULTI_MAC /* we have multiple addresses per flow */ +} MacSetState; + +struct MacSet_ { + /* static store for a single MAC address per side */ + MacAddr singles[2]; + /* state determines how addresses are stored per side: + - SINGLE_MAC uses static locations allocated with the MacSet + itself to store a single address (most likely case) + - MULTI_MAC is used when more than one distinct address + is detected (causes another allocation and linear-time add) */ + MacSetState state[2]; + /* buffer for multiple MACs per flow and direction */ + MacAddr *buf[2]; + int size, + last[2]; +}; + +int g_macset_storage_id = -1; + +void MacSetRegisterFlowStorage(void) +{ + ConfNode *root = ConfGetNode("outputs"); + ConfNode *node = NULL; + /* we only need to register if at least one enabled 'eve-log' output + has the ethernet setting enabled */ + if (root != NULL) { + TAILQ_FOREACH(node, &root->head, next) { + if (strcmp(node->val, "eve-log") == 0) { + const char *enabled = ConfNodeLookupChildValue(node->head.tqh_first, "enabled"); + if (enabled != NULL && ConfValIsTrue(enabled)) { + const char *ethernet = ConfNodeLookupChildValue(node->head.tqh_first, "ethernet"); + if (ethernet != NULL && ConfValIsTrue(ethernet)) { + g_macset_storage_id = FlowStorageRegister("macset", sizeof(void *), + NULL, (void(*)(void *)) MacSetFree); + return; + } + } + } + } + } +} + +bool MacSetFlowStorageEnabled(void) +{ + return (g_macset_storage_id != -1); +} + + +MacSet *MacSetInit(int size) +{ + MacSet *ms = NULL; + if (!FLOW_CHECK_MEMCAP(sizeof(ms))) { + return NULL; + } + ms = SCCalloc(1, sizeof(*ms)); + if (unlikely(ms == NULL)) { + SCLogError(SC_ERR_MEM_ALLOC, "Unable to allocate MacSet memory"); + return NULL; + } + (void) SC_ATOMIC_ADD(flow_memuse, (sizeof(*ms))); + ms->state[MAC_SET_SRC] = ms->state[MAC_SET_DST] = EMPTY_SET; + if (size < 3) { + /* we want to make sure we have at space for at least 3 items to + fit MACs during the initial extension to MULTI_MAC storage */ + size = 3; + } + ms->size = size; + ms->last[MAC_SET_SRC] = ms->last[MAC_SET_DST] = 0; + return ms; +} + +int MacSetGetFlowStorageID(void) +{ + return g_macset_storage_id; +} + +static inline void MacUpdateEntry(MacSet *ms, uint8_t *addr, int side, ThreadVars *tv, uint16_t ctr) +{ + switch (ms->state[side]) { + case EMPTY_SET: + memcpy(ms->singles[side], addr, sizeof(MacAddr));; + ms->state[side] = SINGLE_MAC; + if (tv != NULL) + StatsSetUI64(tv, ctr, 1); + break; + case SINGLE_MAC: + if (unlikely(memcmp(addr, ms->singles[side], sizeof(MacAddr)) != 0)) { + if (ms->buf[side] == NULL) { + if (!FLOW_CHECK_MEMCAP(ms->size * sizeof(MacAddr))) { + /* in this case there is not much we can do */ + return; + } + ms->buf[side] = SCCalloc(ms->size, sizeof(MacAddr)); + if (unlikely(ms->buf[side] == NULL)) { + SCLogError(SC_ERR_MEM_ALLOC, "Unable to allocate " + "MacSet memory"); + return; + } + (void) SC_ATOMIC_ADD(flow_memuse, (ms->size * sizeof(MacAddr))); + } + memcpy(ms->buf[side], ms->singles[side], sizeof(MacAddr)); + memcpy(ms->buf[side] + 1, addr, sizeof(MacAddr)); + ms->last[side] = 2; + if (tv != NULL) + StatsSetUI64(tv, ctr, 2); + ms->state[side] = MULTI_MAC; + } + break; + case MULTI_MAC: + if (unlikely(ms->last[side] == ms->size)) { + /* MacSet full, ignore item. We intentionally do not output + any warning in order not to stall packet processing */ + return; + } + /* If the set is non-empty... */ + if (ms->last[side] > 0) { + /* ...we search for duplicates in the set to decide whether + we need to insert the current item. We do this backwards, + since we expect the latest item to match more likely than + the first */ + for (int i = ms->last[side] - 1; i >= 0; i--) { + uint8_t *addr2 = (uint8_t*) ((ms->buf[side]) + i); + /* If we find a match, we return early with no action */ + if (likely(memcmp(addr2, addr, sizeof(MacAddr)) == 0)) { + return; + } + } + } + /* Otherwise, we insert the new address at the end */ + memcpy(ms->buf[side] + ms->last[side], addr, sizeof(MacAddr)); + ms->last[side]++; + if (tv != NULL) + StatsSetUI64(tv, ctr, ms->last[side]); + break; + } +} + +void MacSetAddWithCtr(MacSet *ms, uint8_t *src_addr, uint8_t *dst_addr, ThreadVars *tv, + uint16_t ctr_src, uint16_t ctr_dst) +{ + if (ms == NULL) + return; + MacUpdateEntry(ms, src_addr, MAC_SET_SRC, tv, ctr_src); + MacUpdateEntry(ms, dst_addr, MAC_SET_DST, tv, ctr_dst); +} + +void MacSetAdd(MacSet *ms, uint8_t *src_addr, uint8_t *dst_addr) +{ + MacSetAddWithCtr(ms, src_addr, dst_addr, NULL, 0, 0); +} + +static inline int MacSetIterateSide(const MacSet *ms, MacSetIteratorFunc IterFunc, + MacSetSide side, void *data) +{ + int ret = 0; + switch (ms->state[side]) { + case EMPTY_SET: + return 0; + case SINGLE_MAC: + ret = IterFunc((uint8_t*) ms->singles[side], side, data); + if (unlikely(ret != 0)) { + return ret; + } + break; + case MULTI_MAC: + for (int i = 0; i < ms->last[side]; i++) { + ret = IterFunc((uint8_t*) ms->buf[side][i], side, data); + if (unlikely(ret != 0)) { + return ret; + } + } + break; + } + return 0; +} + +int MacSetForEach(const MacSet *ms, MacSetIteratorFunc IterFunc, void *data) +{ + int ret = 0; + if (ms == NULL) + return 0; + + ret = MacSetIterateSide(ms, IterFunc, MAC_SET_SRC, data); + if (ret != 0) { + return ret; + } + return MacSetIterateSide(ms, IterFunc, MAC_SET_DST, data); +} + +int MacSetSize(const MacSet *ms) +{ + int size = 0; + if (ms == NULL) + return 0; + + switch(ms->state[MAC_SET_SRC]) { + case EMPTY_SET: + /* pass */ + break; + case SINGLE_MAC: + size += 1; + break; + case MULTI_MAC: + size += ms->last[MAC_SET_SRC]; + break; + } + switch(ms->state[MAC_SET_DST]) { + case EMPTY_SET: + /* pass */ + break; + case SINGLE_MAC: + size += 1; + break; + case MULTI_MAC: + size += ms->last[MAC_SET_DST]; + break; + } + return size; +} + +void MacSetReset(MacSet *ms) +{ + if (ms == NULL) + return; + ms->state[MAC_SET_SRC] = ms->state[MAC_SET_DST] = EMPTY_SET; + ms->last[MAC_SET_SRC] = ms->last[MAC_SET_DST] = 0; +} + +void MacSetFree(MacSet *ms) +{ + size_t total_free = 0; + if (ms == NULL) + return; + if (ms->buf[MAC_SET_SRC] != NULL) { + SCFree(ms->buf[MAC_SET_SRC]); + total_free += ms->size * sizeof(MacAddr); + } + if (ms->buf[MAC_SET_DST] != NULL) { + SCFree(ms->buf[MAC_SET_DST]); + total_free += ms->size * sizeof(MacAddr); + } + SCFree(ms); + total_free += sizeof(*ms); + (void) SC_ATOMIC_SUB(flow_memuse, total_free); +} + +#ifdef UNITTESTS + +static int CheckTest1Membership(uint8_t *addr, MacSetSide side, void *data) +{ + int *i = (int*) data; + switch (*i) { + case 0: + if (addr[5] != 1) return 1; + break; + case 1: + if (addr[5] != 2) return 1; + break; + case 2: + if (addr[5] != 3) return 1; + break; + } + (*i)++; + return 0; +} + +static int MacSetTest01(void) +{ + MacSet *ms = NULL; + int ret = 0, i = 0; + MacAddr addr1 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x1}, + addr2 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x2}, + addr3 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x3}; + SC_ATOMIC_SET(flow_config.memcap, 10000); + + ms = MacSetInit(10); + FAIL_IF_NULL(ms); + FAIL_IF_NOT(MacSetSize(ms) == 0); + + ret = MacSetForEach(ms, CheckTest1Membership, &i); + FAIL_IF_NOT(ret == 0); + + MacSetAdd(ms, addr1, addr2); + FAIL_IF_NOT(MacSetSize(ms) == 2); + + ret = MacSetForEach(ms, CheckTest1Membership, &i); + FAIL_IF_NOT(ret == 0); + + MacSetAdd(ms, addr1, addr3); + FAIL_IF_NOT(MacSetSize(ms) == 3); + + i = 0; + ret = MacSetForEach(ms, CheckTest1Membership, &i); + FAIL_IF_NOT(ret == 0); + + MacSetReset(ms); + FAIL_IF_NOT(MacSetSize(ms) == 0); + + MacSetAdd(ms, addr2, addr3); + FAIL_IF_NOT(MacSetSize(ms) == 2); + + i = 1; + ret = MacSetForEach(ms, CheckTest1Membership, &i); + FAIL_IF_NOT(ret == 0); + + MacSetFree(ms); + PASS; +} + +static int MacSetTest02(void) +{ + MacSet *ms = NULL; + int ret = 0, i = 0; + SC_ATOMIC_SET(flow_config.memcap, 10000); + + ms = MacSetInit(10); + FAIL_IF_NULL(ms); + FAIL_IF_NOT(MacSetSize(ms) == 0); + + for (i = 1; i < 100; i++) { + MacAddr addr1 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x1}, + addr2 = {0x1, 0x0, 0x0, 0x0, 0x0, 0x2}; + MacSetAdd(ms, addr1, addr2); + } + FAIL_IF_NOT(MacSetSize(ms) == 2); + + ret = MacSetForEach(ms, CheckTest1Membership, &i); + FAIL_IF_NOT(ret == 0); + + MacSetFree(ms); + PASS; +} + +static int MacSetTest03(void) +{ + MacSet *ms = NULL; + int i = 0; + SC_ATOMIC_SET(flow_config.memcap, 10000); + + ms = MacSetInit(10); + FAIL_IF_NULL(ms); + FAIL_IF_NOT(MacSetSize(ms) == 0); + + for (i = 1; i < 100; i++) { + MacAddr addr1 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x1}, + addr2 = {0x1, 0x0, 0x0, 0x0, 0x0, 0x1}; + addr1[5] = i; + addr2[5] = i; + MacSetAdd(ms, addr1, addr2); + } + FAIL_IF_NOT(MacSetSize(ms) == 20); + + MacSetFree(ms); + PASS; +} + +static int MacSetTest04(void) +{ + MacSet *ms = NULL; + SC_ATOMIC_SET(flow_config.memcap, 2); + + ms = MacSetInit(10); + FAIL_IF_NOT_NULL(ms); + + PASS; +} + +static int MacSetTest05(void) +{ + MacSet *ms = NULL; + int ret = 0, i = 0; + SC_ATOMIC_SET(flow_config.memcap, 10); + + ms = MacSetInit(10); + FAIL_IF_NULL(ms); + FAIL_IF_NOT(MacSetSize(ms) == 0); + + for (i = 1; i < 100; i++) { + MacAddr addr1 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x1}, + addr2 = {0x1, 0x0, 0x0, 0x0, 0x0, 0x1}; + addr1[5] = i; + addr2[5] = i; + MacSetAdd(ms, addr1, addr2); + } + FAIL_IF_NOT(MacSetSize(ms) == 2); + + ret = MacSetForEach(ms, CheckTest1Membership, &i); + FAIL_IF_NOT(ret == 0); + + MacSetFree(ms); + PASS; +} + +#endif /* UNITTESTS */ + +void MacSetRegisterTests(void) +{ + +#ifdef UNITTESTS + UtRegisterTest("MacSetTest01", MacSetTest01); + UtRegisterTest("MacSetTest02", MacSetTest02); + UtRegisterTest("MacSetTest03", MacSetTest03); + UtRegisterTest("MacSetTest04", MacSetTest04); + UtRegisterTest("MacSetTest04", MacSetTest05); +#endif + + return; +} diff --git a/src/util-macset.h b/src/util-macset.h new file mode 100644 index 0000000000..833f9bf8e4 --- /dev/null +++ b/src/util-macset.h @@ -0,0 +1,48 @@ +/* Copyright (C) 2020 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + * + * \author Sascha Steinbiss + */ + +#ifndef __UTIL_MACSET_H__ +#define __UTIL_MACSET_H__ + +typedef struct MacSet_ MacSet; +typedef enum { + MAC_SET_SRC = 0, + MAC_SET_DST +} MacSetSide; + +typedef int (*MacSetIteratorFunc)(uint8_t *addr, MacSetSide side, void*); + +MacSet *MacSetInit(int size); +void MacSetAdd(MacSet*, uint8_t *src_addr, uint8_t *dst_addr); +void MacSetAddWithCtr(MacSet*, uint8_t *src_addr, uint8_t *dst_addr, ThreadVars *tv, + uint16_t ctr_src, uint16_t ctr_dst); +int MacSetForEach(const MacSet*, MacSetIteratorFunc, void*); +int MacSetSize(const MacSet*); +void MacSetReset(MacSet*); +void MacSetFree(MacSet*); +void MacSetRegisterFlowStorage(void); +int MacSetGetFlowStorageID(void); +bool MacSetFlowStorageEnabled(void); +void MacSetRegisterTests(void); + +#endif /* __UTIL_MACSET_H__ */ \ No newline at end of file diff --git a/suricata.yaml.in b/suricata.yaml.in index c15931cd2d..f747c50140 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -93,6 +93,7 @@ outputs: #facility: local5 #level: Info ## possible levels: Emergency, Alert, Critical, ## Error, Warning, Notice, Info, Debug + #ethernet: no # log ethernet header in events when available #redis: # server: 127.0.0.1 # port: 6379