diff --git a/etc/schema.json b/etc/schema.json index 9df20702acad..bf923e02194d 100644 --- a/etc/schema.json +++ b/etc/schema.json @@ -8813,6 +8813,10 @@ "tx_id": { "type": "integer" }, + "v": { + "type": "integer", + "description": "Version of EVE in the event" + }, "verdict": { "$ref": "#/$defs/verdict_type" }, diff --git a/src/output-json-alert.c b/src/output-json-alert.c index e5ee355d7cc8..ffc7bca03299 100644 --- a/src/output-json-alert.c +++ b/src/output-json-alert.c @@ -717,7 +717,9 @@ static int AlertJson(ThreadVars *tv, JsonAlertLogThread *aft, const Packet *p) } } - EveAddAppProto(p->flow, jb); + if (json_output_ctx->eve_ctx->cfg.eve_version < EVE_VERSION_GLOBAL_APP_PROTO) { + EveAddAppProto(p->flow, jb); + } if (p->flowflags & FLOW_PKT_TOSERVER) { SCJbSetString(jb, "direction", "to_server"); diff --git a/src/output-json-file.c b/src/output-json-file.c index 9c046b3c7d18..7eb6e742bf55 100644 --- a/src/output-json-file.c +++ b/src/output-json-file.c @@ -189,7 +189,11 @@ SCJsonBuilder *JsonBuildFileInfoRecord(const Packet *p, const File *ff, void *tx break; } - SCJbSetString(js, "app_proto", AppProtoToString(p->flow->alproto)); + /* if eve_ctx is NULL, we are called in filestore context, so for compatibility + * we need to log app_proto */ + if (eve_ctx == NULL || eve_ctx->cfg.eve_version < EVE_VERSION_GLOBAL_APP_PROTO) { + SCJbSetString(js, "app_proto", AppProtoToString(p->flow->alproto)); + } SCJbOpenObject(js, "fileinfo"); if (stored) { diff --git a/src/output-json-flow.c b/src/output-json-flow.c index 2bd5d0129b1f..3459d2e79f2f 100644 --- a/src/output-json-flow.c +++ b/src/output-json-flow.c @@ -175,26 +175,6 @@ static SCJsonBuilder *CreateEveHeaderFromFlow(const Flow *f) return jb; } -void EveAddAppProto(Flow *f, SCJsonBuilder *js) -{ - if (f->alproto) { - SCJbSetString(js, "app_proto", AppProtoToString(f->alproto)); - } - if (f->alproto_ts && f->alproto_ts != f->alproto) { - SCJbSetString(js, "app_proto_ts", AppProtoToString(f->alproto_ts)); - } - if (f->alproto_tc && f->alproto_tc != f->alproto) { - SCJbSetString(js, "app_proto_tc", AppProtoToString(f->alproto_tc)); - } - if (f->alproto_orig != f->alproto && f->alproto_orig != ALPROTO_UNKNOWN) { - SCJbSetString(js, "app_proto_orig", AppProtoToString(f->alproto_orig)); - } - if (f->alproto_expect != f->alproto && f->alproto_expect != ALPROTO_UNKNOWN) { - SCJbSetString(js, "app_proto_expected", AppProtoToString(f->alproto_expect)); - } - -} - void EveAddFlow(Flow *f, SCJsonBuilder *js) { FlowBypassInfo *fc = FlowGetStorageById(f, GetFlowBypassInfoID()); diff --git a/src/output-json-flow.h b/src/output-json-flow.h index 4524370d11a7..362d0610e1df 100644 --- a/src/output-json-flow.h +++ b/src/output-json-flow.h @@ -26,6 +26,5 @@ void JsonFlowLogRegister(void); void EveAddFlow(Flow *f, SCJsonBuilder *js); -void EveAddAppProto(Flow *f, SCJsonBuilder *js); #endif /* SURICATA_OUTPUT_JSON_FLOW_H */ diff --git a/src/output-json-frame.c b/src/output-json-frame.c index a27885cc4ab4..6d566fd9ebb3 100644 --- a/src/output-json-frame.c +++ b/src/output-json-frame.c @@ -299,7 +299,9 @@ static int FrameJsonUdp(ThreadVars *tv, JsonFrameLogThread *aft, const Packet *p if (unlikely(jb == NULL)) return TM_ECODE_OK; - SCJbSetString(jb, "app_proto", AppProtoToString(f->alproto)); + if (json_output_ctx->eve_ctx->cfg.eve_version < EVE_VERSION_GLOBAL_APP_PROTO) { + SCJbSetString(jb, "app_proto", AppProtoToString(f->alproto)); + } FrameJsonLogOneFrame(IPPROTO_UDP, frame, p->flow, NULL, p, jb, aft->payload_buffer); OutputJsonBuilderBuffer(tv, p, p->flow, jb, aft->ctx); SCJbFree(jb); @@ -373,7 +375,9 @@ static int FrameJson(ThreadVars *tv, JsonFrameLogThread *aft, const Packet *p) if (unlikely(jb == NULL)) return TM_ECODE_OK; - SCJbSetString(jb, "app_proto", AppProtoToString(p->flow->alproto)); + if (json_output_ctx->eve_ctx->cfg.eve_version < EVE_VERSION_GLOBAL_APP_PROTO) { + SCJbSetString(jb, "app_proto", AppProtoToString(p->flow->alproto)); + } FrameJsonLogOneFrame(IPPROTO_TCP, frame, p->flow, stream, p, jb, aft->payload_buffer); OutputJsonBuilderBuffer(tv, p, p->flow, jb, aft->ctx); SCJbFree(jb); diff --git a/src/output-json-netflow.c b/src/output-json-netflow.c index 6a0d1d2e60a0..2094f9b80d3e 100644 --- a/src/output-json-netflow.c +++ b/src/output-json-netflow.c @@ -176,9 +176,14 @@ static SCJsonBuilder *CreateEveHeaderFromNetFlow(const Flow *f, int dir) } /* JSON format logging */ -static void NetFlowLogEveToServer(SCJsonBuilder *js, Flow *f) +static void NetFlowLogEveToServer(SCJsonBuilder *js, Flow *f, OutputJsonThreadCtx *json_outout_ctx) { - SCJbSetString(js, "app_proto", AppProtoToString(f->alproto_ts ? f->alproto_ts : f->alproto)); + if (json_outout_ctx->ctx->cfg.eve_version < EVE_VERSION_GLOBAL_APP_PROTO) { + SCJbSetString( + js, "app_proto", AppProtoToString(f->alproto_ts ? f->alproto_ts : f->alproto)); + } else { + EveAddAppProto(f, js); + } SCJbOpenObject(js, "netflow"); @@ -226,9 +231,14 @@ static void NetFlowLogEveToServer(SCJsonBuilder *js, Flow *f) } } -static void NetFlowLogEveToClient(SCJsonBuilder *js, Flow *f) +static void NetFlowLogEveToClient(SCJsonBuilder *js, Flow *f, OutputJsonThreadCtx *json_output_ctx) { - SCJbSetString(js, "app_proto", AppProtoToString(f->alproto_tc ? f->alproto_tc : f->alproto)); + if (json_output_ctx->ctx->cfg.eve_version < EVE_VERSION_GLOBAL_APP_PROTO) { + SCJbSetString( + js, "app_proto", AppProtoToString(f->alproto_tc ? f->alproto_tc : f->alproto)); + } else { + EveAddAppProto(f, js); + } SCJbOpenObject(js, "netflow"); @@ -287,7 +297,7 @@ static int JsonNetFlowLogger(ThreadVars *tv, void *thread_data, Flow *f) SCJsonBuilder *jb = CreateEveHeaderFromNetFlow(f, 0); if (unlikely(jb == NULL)) return TM_ECODE_OK; - NetFlowLogEveToServer(jb, f); + NetFlowLogEveToServer(jb, f, jhl); EveAddCommonOptions(&jhl->ctx->cfg, NULL, f, jb, LOG_DIR_FLOW_TOSERVER); OutputJsonBuilderBuffer(tv, NULL, f, jb, jhl); SCJbFree(jb); @@ -297,7 +307,7 @@ static int JsonNetFlowLogger(ThreadVars *tv, void *thread_data, Flow *f) jb = CreateEveHeaderFromNetFlow(f, 1); if (unlikely(jb == NULL)) return TM_ECODE_OK; - NetFlowLogEveToClient(jb, f); + NetFlowLogEveToClient(jb, f, jhl); EveAddCommonOptions(&jhl->ctx->cfg, NULL, f, jb, LOG_DIR_FLOW_TOCLIENT); OutputJsonBuilderBuffer(tv, NULL, f, jb, jhl); SCJbFree(jb); diff --git a/src/output-json.c b/src/output-json.c index 1b7464e35ee4..2f9d01bd77c5 100644 --- a/src/output-json.c +++ b/src/output-json.c @@ -410,6 +410,9 @@ void EveAddCommonOptions(const OutputJsonCommonSettings *cfg, const Packet *p, c if (cfg->include_community_id && f != NULL) { CreateEveCommunityFlowId(js, f, cfg->community_id_seed); } + if (cfg->eve_version >= EVE_MIN_LOG_VERSION) { + SCJbSetUint(js, "v", cfg->eve_version); + } if (f != NULL && f->tenant_id > 0) { SCJbSetUint(js, "tenant_id", f->tenant_id); } @@ -831,6 +834,28 @@ static int CreateJSONEther( return 0; } +void EveAddAppProto(const Flow *f, SCJsonBuilder *js) +{ + if (f == NULL) { + return; + } + if (f->alproto) { + SCJbSetString(js, "app_proto", AppProtoToString(f->alproto)); + } + if (f->alproto_ts && f->alproto_ts != f->alproto) { + SCJbSetString(js, "app_proto_ts", AppProtoToString(f->alproto_ts)); + } + if (f->alproto_tc && f->alproto_tc != f->alproto) { + SCJbSetString(js, "app_proto_tc", AppProtoToString(f->alproto_tc)); + } + if (f->alproto_orig != f->alproto && f->alproto_orig != ALPROTO_UNKNOWN) { + SCJbSetString(js, "app_proto_orig", AppProtoToString(f->alproto_orig)); + } + if (f->alproto_expect != f->alproto && f->alproto_expect != ALPROTO_UNKNOWN) { + SCJbSetString(js, "app_proto_expected", AppProtoToString(f->alproto_expect)); + } +} + SCJsonBuilder *CreateEveHeader(const Packet *p, enum SCOutputJsonLogDirection dir, const char *event_type, JsonAddrInfo *addr, OutputJsonCtx *eve_ctx) { @@ -848,6 +873,10 @@ SCJsonBuilder *CreateEveHeader(const Packet *p, enum SCOutputJsonLogDirection di CreateEveFlowId(js, f); + if (eve_ctx != NULL && eve_ctx->cfg.eve_version >= EVE_VERSION_GLOBAL_APP_PROTO) { + EveAddAppProto(f, js); + } + /* sensor id */ if (sensor_id >= 0) { SCJbSetUint(js, "sensor_id", sensor_id); @@ -1243,6 +1272,22 @@ OutputInitResult OutputJsonInitCtx(SCConfNode *conf) } else { json_ctx->cfg.include_suricata_version = false; } + const char *eve_version = SCConfNodeLookupChildValue(conf, "version"); + if (eve_version != NULL) { + if (StringParseUint16(&json_ctx->cfg.eve_version, 10, 0, eve_version) < 0) { + FatalError("Failed to initialize JSON output, " + "invalid EVE version: %s", + eve_version); + } + if (json_ctx->cfg.eve_version > EVE_MAX_VERSION) { + SCLogWarning("Configured EVE version %u is higher than " + "maximum supported version %u, using max version.", + json_ctx->cfg.eve_version, EVE_MAX_VERSION); + json_ctx->cfg.eve_version = EVE_MAX_VERSION; + } + } else { + json_ctx->cfg.eve_version = 1; + } /* See if we want to enable the community id */ const SCConfNode *community_id = SCConfNodeLookupChild(conf, "community-id"); diff --git a/src/output-json.h b/src/output-json.h index 1f4fec70d041..41957028808e 100644 --- a/src/output-json.h +++ b/src/output-json.h @@ -37,6 +37,10 @@ void OutputJsonRegister(void); #define JSON_ADDR_LEN 46 #define JSON_PROTO_LEN 16 +#define EVE_MAX_VERSION 2 +#define EVE_MIN_LOG_VERSION 2 +#define EVE_VERSION_GLOBAL_APP_PROTO 2 + /* A struct to contain address info for rendering to JSON. */ typedef struct JsonAddrInfo_ { char src_ip[JSON_ADDR_LEN]; @@ -67,6 +71,7 @@ typedef struct OutputJsonCommonSettings_ { bool include_ethernet; bool include_suricata_version; uint16_t community_id_seed; + uint16_t eve_version; } OutputJsonCommonSettings; /* @@ -110,6 +115,7 @@ void EveAddCommonOptions(const OutputJsonCommonSettings *cfg, const Packet *p, c SCJsonBuilder *js, enum SCOutputJsonLogDirection dir); int OutputJsonLogFlush(ThreadVars *tv, void *thread_data, const Packet *p); void EveAddMetadata(const Packet *p, const Flow *f, SCJsonBuilder *js); +void EveAddAppProto(const Flow *f, SCJsonBuilder *js); int OutputJSONMemBufferCallback(const char *str, size_t size, void *data); diff --git a/suricata.yaml.in b/suricata.yaml.in index 3b29513f9dfb..ceff5a9d6080 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -143,6 +143,7 @@ outputs: #metadata: no # Include suricata version. Default no. #suricata-version: yes + version: 2 # include the name of the input pcap file in pcap file processing mode pcap-file: false