suricata
output-json-flow.c
Go to the documentation of this file.
1/* Copyright (C) 2007-2025 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 Victor Julien <victor@inliniac.net>
22 *
23 * Implements Flow JSON logging portion of the engine.
24 */
25
26#include "suricata-common.h"
27#include "detect.h"
28#include "pkt-var.h"
29#include "conf.h"
30#include "app-layer-parser.h"
31
32#include "threads.h"
33#include "threadvars.h"
34#include "tm-threads.h"
35
36#include "util-print.h"
37#include "util-unittest.h"
38
39#include "util-debug.h"
40
41#include "output.h"
42#include "util-privs.h"
43#include "util-buffer.h"
44#include "util-device-private.h"
45#include "util-proto-name.h"
46#include "util-logopenfile.h"
47#include "util-time.h"
48#include "output-json.h"
49#include "output-json-flow.h"
50
51#include "stream-tcp.h"
52#include "stream-tcp-private.h"
53#include "flow-storage.h"
55
56static SCJsonBuilder *CreateEveHeaderFromFlow(const Flow *f)
57{
58 char timebuf[64];
59 char srcip[46] = {0}, dstip[46] = {0};
60 Port sp, dp;
61
62 SCJsonBuilder *jb = SCJbNewObject();
63 if (unlikely(jb == NULL)) {
64 return NULL;
65 }
66
68
69 CreateIsoTimeString(ts, timebuf, sizeof(timebuf));
70
71 if ((f->flags & FLOW_DIR_REVERSED) == 0) {
72 if (FLOW_IS_IPV4(f)) {
73 PrintInet(AF_INET, (const void *)&(f->src.addr_data32[0]), srcip, sizeof(srcip));
74 PrintInet(AF_INET, (const void *)&(f->dst.addr_data32[0]), dstip, sizeof(dstip));
75 } else if (FLOW_IS_IPV6(f)) {
76 PrintInet(AF_INET6, (const void *)&(f->src.address), srcip, sizeof(srcip));
77 PrintInet(AF_INET6, (const void *)&(f->dst.address), dstip, sizeof(dstip));
78 }
79 sp = f->sp;
80 dp = f->dp;
81 } else {
82 if (FLOW_IS_IPV4(f)) {
83 PrintInet(AF_INET, (const void *)&(f->dst.addr_data32[0]), srcip, sizeof(srcip));
84 PrintInet(AF_INET, (const void *)&(f->src.addr_data32[0]), dstip, sizeof(dstip));
85 } else if (FLOW_IS_IPV6(f)) {
86 PrintInet(AF_INET6, (const void *)&(f->dst.address), srcip, sizeof(srcip));
87 PrintInet(AF_INET6, (const void *)&(f->src.address), dstip, sizeof(dstip));
88 }
89 sp = f->dp;
90 dp = f->sp;
91 }
92
93 /* time */
94 SCJbSetString(jb, "timestamp", timebuf);
95
96 CreateEveFlowId(jb, (const Flow *)f);
97
98#if 0 // TODO
99 /* sensor id */
100 if (sensor_id >= 0)
101 json_object_set_new(js, "sensor_id", json_integer(sensor_id));
102#endif
103
104 /* input interface */
105 if (f->livedev) {
106 SCJbSetString(jb, "in_iface", f->livedev->dev);
107 }
108
109 JB_SET_STRING(jb, "event_type", "flow");
110
111 /* vlan */
112 if (f->vlan_idx > 0) {
113 SCJbOpenArray(jb, "vlan");
114 SCJbAppendUint(jb, f->vlan_id[0]);
115 if (f->vlan_idx > 1) {
116 SCJbAppendUint(jb, f->vlan_id[1]);
117 }
118 if (f->vlan_idx > 2) {
119 SCJbAppendUint(jb, f->vlan_id[2]);
120 }
121 SCJbClose(jb);
122 }
123
124 /* tuple */
125 SCJbSetString(jb, "src_ip", srcip);
126 switch(f->proto) {
127 case IPPROTO_ICMP:
128 break;
129 case IPPROTO_UDP:
130 case IPPROTO_TCP:
131 case IPPROTO_SCTP:
132 SCJbSetUint(jb, "src_port", sp);
133 break;
134 }
135 SCJbSetString(jb, "dest_ip", dstip);
136 switch(f->proto) {
137 case IPPROTO_ICMP:
138 break;
139 case IPPROTO_UDP:
140 case IPPROTO_TCP:
141 case IPPROTO_SCTP:
142 SCJbSetUint(jb, "dest_port", dp);
143 break;
144 }
145
146 /* ip version */
147 if (FLOW_IS_IPV4(f)) {
148 SCJbSetUint(jb, "ip_v", 4);
149 } else if (FLOW_IS_IPV6(f)) {
150 SCJbSetUint(jb, "ip_v", 6);
151 }
152
153 if (SCProtoNameValid(f->proto)) {
154 SCJbSetString(jb, "proto", known_proto[f->proto]);
155 } else {
156 char proto[4];
157 snprintf(proto, sizeof(proto), "%"PRIu8"", f->proto);
158 SCJbSetString(jb, "proto", proto);
159 }
160
161 switch (f->proto) {
162 case IPPROTO_ICMP:
163 case IPPROTO_ICMPV6:
164 SCJbSetUint(jb, "icmp_type", f->icmp_s.type);
165 SCJbSetUint(jb, "icmp_code", f->icmp_s.code);
166 if (f->tosrcpktcnt) {
167 SCJbSetUint(jb, "response_icmp_type", f->icmp_d.type);
168 SCJbSetUint(jb, "response_icmp_code", f->icmp_d.code);
169 }
170 break;
171 case IPPROTO_ESP:
172 SCJbSetUint(jb, "spi", f->esp.spi);
173 break;
174 }
175 return jb;
176}
177
178void EveAddAppProto(Flow *f, SCJsonBuilder *js)
179{
180 if (f->alproto) {
181 SCJbSetString(js, "app_proto", AppProtoToString(f->alproto));
182 }
183 if (f->alproto_ts && f->alproto_ts != f->alproto) {
184 SCJbSetString(js, "app_proto_ts", AppProtoToString(f->alproto_ts));
185 }
186 if (f->alproto_tc && f->alproto_tc != f->alproto) {
187 SCJbSetString(js, "app_proto_tc", AppProtoToString(f->alproto_tc));
188 }
189 if (f->alproto_orig != f->alproto && f->alproto_orig != ALPROTO_UNKNOWN) {
190 SCJbSetString(js, "app_proto_orig", AppProtoToString(f->alproto_orig));
191 }
193 SCJbSetString(js, "app_proto_expected", AppProtoToString(f->alproto_expect));
194 }
195
196}
197
198void EveAddFlow(Flow *f, SCJsonBuilder *js)
199{
201 if (fc) {
202 SCJbSetUint(js, "pkts_toserver", f->todstpktcnt + fc->todstpktcnt);
203 SCJbSetUint(js, "pkts_toclient", f->tosrcpktcnt + fc->tosrcpktcnt);
204 SCJbSetUint(js, "bytes_toserver", f->todstbytecnt + fc->todstbytecnt);
205 SCJbSetUint(js, "bytes_toclient", f->tosrcbytecnt + fc->tosrcbytecnt);
206
207 SCJbOpenObject(js, "bypassed");
208 SCJbSetUint(js, "pkts_toserver", fc->todstpktcnt);
209 SCJbSetUint(js, "pkts_toclient", fc->tosrcpktcnt);
210 SCJbSetUint(js, "bytes_toserver", fc->todstbytecnt);
211 SCJbSetUint(js, "bytes_toclient", fc->tosrcbytecnt);
212 SCJbClose(js);
213 } else {
214 SCJbSetUint(js, "pkts_toserver", f->todstpktcnt);
215 SCJbSetUint(js, "pkts_toclient", f->tosrcpktcnt);
216 SCJbSetUint(js, "bytes_toserver", f->todstbytecnt);
217 SCJbSetUint(js, "bytes_toclient", f->tosrcbytecnt);
218 }
219
220 char timebuf1[64];
221 CreateIsoTimeString(f->startts, timebuf1, sizeof(timebuf1));
222 SCJbSetString(js, "start", timebuf1);
223}
224
225static void EveExceptionPolicyLog(SCJsonBuilder *js, uint16_t flag)
226{
228 SCJbStartObject(js);
229 SCJbSetString(js, "target",
231 SCJbSetString(js, "policy",
234 SCJbClose(js);
235 }
237 SCJbStartObject(js);
238 SCJbSetString(js, "target",
240 SCJbSetString(js, "policy",
243 SCJbClose(js);
244 }
246 SCJbStartObject(js);
247 SCJbSetString(js, "target",
249 SCJbSetString(js, "policy",
252 true));
253 SCJbClose(js);
254 }
256 SCJbStartObject(js);
257 SCJbSetString(
259 SCJbSetString(js, "policy",
262 SCJbClose(js);
263 }
265 SCJbStartObject(js);
266 SCJbSetString(
268 SCJbSetString(js, "policy",
271 SCJbClose(js);
272 }
274 SCJbStartObject(js);
275 SCJbSetString(js, "target",
277 SCJbSetString(js, "policy",
280 SCJbClose(js);
281 }
282}
283
284/* Eve format logging */
285static void EveFlowLogJSON(OutputJsonThreadCtx *aft, SCJsonBuilder *jb, Flow *f)
286{
287 EveAddAppProto(f, jb);
288 SCJbOpenObject(jb, "flow");
289 EveAddFlow(f, jb);
290
291 char timebuf2[64];
292 CreateIsoTimeString(f->lastts, timebuf2, sizeof(timebuf2));
293 SCJbSetString(jb, "end", timebuf2);
294
295 uint64_t age = (SCTIME_SECS(f->lastts) - SCTIME_SECS(f->startts));
296 SCJbSetUint(jb, "age", age);
297
299 JB_SET_TRUE(jb, "emergency");
300
301 const int flow_state = f->flow_state;
302 switch (flow_state) {
303 case FLOW_STATE_NEW:
304 JB_SET_STRING(jb, "state", "new");
305 break;
307 JB_SET_STRING(jb, "state", "established");
308 break;
310 JB_SET_STRING(jb, "state", "closed");
311 break;
313 JB_SET_STRING(jb, "state", "bypassed");
314 JB_SET_STRING(jb, "bypass", "local");
315 break;
316#ifdef CAPTURE_OFFLOAD
317 case FLOW_STATE_CAPTURE_BYPASSED:
318 JB_SET_STRING(jb, "state", "bypassed");
319 JB_SET_STRING(jb, "bypass", "capture");
320 break;
321#endif
322 case FLOW_STATE_SIZE:
324 SCLogDebug("invalid flow state: %d, contact developers", flow_state);
325 }
326
327 const char *reason = NULL;
329 reason = "tcp_reuse";
331 reason = "forced";
333 reason = "shutdown";
335 reason = "timeout";
336 else
337 reason = "unknown";
338
339 SCJbSetString(jb, "reason", reason);
340
341 SCJbSetBool(jb, "alerted", FlowHasAlerts(f));
342 if (f->flags & FLOW_WRONG_THREAD)
343 JB_SET_TRUE(jb, "wrong_thread");
344
345 if (f->flags & FLOW_IS_ELEPHANT)
346 JB_SET_TRUE(jb, "elephant");
347
348 if (f->flags & FLOW_ACTION_DROP) {
349 JB_SET_STRING(jb, "action", "drop");
350 } else if (f->flags & FLOW_ACTION_ACCEPT) {
351 JB_SET_STRING(jb, "action", "accept");
352 } else if (f->flags & FLOW_ACTION_PASS) {
353 JB_SET_STRING(jb, "action", "pass");
354 }
355 if (f->applied_exception_policy != 0) {
356 SCJbOpenArray(jb, "exception_policy");
357 EveExceptionPolicyLog(jb, f->applied_exception_policy);
358 SCJbClose(jb); /* close array */
359 }
360
361 if (f->alstate) {
362 uint64_t tx_id = AppLayerParserGetTxCnt(f, f->alstate);
363 if (tx_id) {
364 SCJbSetUint(jb, "tx_cnt", tx_id);
365 }
366 }
367
368 /* Close flow. */
369 SCJbClose(jb);
370
371 EveAddCommonOptions(&aft->ctx->cfg, NULL, f, jb, LOG_DIR_FLOW);
372
373 /* TCP */
374 if (f->proto == IPPROTO_TCP) {
375 SCJbOpenObject(jb, "tcp");
376
377 TcpSession *ssn = f->protoctx;
378
379 char hexflags[3];
380 snprintf(hexflags, sizeof(hexflags), "%02x",
381 ssn ? ssn->tcp_packet_flags : 0);
382 SCJbSetString(jb, "tcp_flags", hexflags);
383
384 snprintf(hexflags, sizeof(hexflags), "%02x",
385 ssn ? ssn->client.tcp_flags : 0);
386 SCJbSetString(jb, "tcp_flags_ts", hexflags);
387
388 snprintf(hexflags, sizeof(hexflags), "%02x",
389 ssn ? ssn->server.tcp_flags : 0);
390 SCJbSetString(jb, "tcp_flags_tc", hexflags);
391
392 EveTcpFlags(ssn ? ssn->tcp_packet_flags : 0, jb);
393
394 if (ssn) {
395 const char *tcp_state = StreamTcpStateAsString(ssn->state);
396 if (tcp_state != NULL)
397 SCJbSetString(jb, "state", tcp_state);
399 JB_SET_TRUE(jb, "tc_gap");
400 }
402 JB_SET_TRUE(jb, "ts_gap");
403 }
404
405 SCJbSetUint(jb, "ts_max_regions", ssn->client.sb.max_regions);
406 SCJbSetUint(jb, "tc_max_regions", ssn->server.sb.max_regions);
407
408 if (ssn->urg_offset_ts)
409 SCJbSetUint(jb, "ts_urgent_oob_data", ssn->urg_offset_ts);
410 if (ssn->urg_offset_tc)
411 SCJbSetUint(jb, "tc_urgent_oob_data", ssn->urg_offset_tc);
412 }
413
414 /* Close tcp. */
415 SCJbClose(jb);
416 }
417}
418
419static int JsonFlowLogger(ThreadVars *tv, void *thread_data, Flow *f)
420{
421 SCEnter();
422 OutputJsonThreadCtx *thread = thread_data;
423
424 /* reset */
425 MemBufferReset(thread->buffer);
426
427 SCJsonBuilder *jb = CreateEveHeaderFromFlow(f);
428 if (unlikely(jb == NULL)) {
430 }
431
432 EveFlowLogJSON(thread, jb, f);
433
434 OutputJsonBuilderBuffer(tv, NULL, f, jb, thread);
435 SCJbFree(jb);
436
438}
439
441{
442 /* register as child of eve-log */
443 OutputRegisterFlowSubModule(LOGGER_JSON_FLOW, "eve-log", "JsonFlowLog", "eve-log.flow",
445}
uint64_t AppLayerParserGetTxCnt(const Flow *f, void *alstate)
const char * AppProtoToString(AppProto alproto)
Maps the ALPROTO_*, to its string equivalent.
@ ALPROTO_UNKNOWN
uint8_t proto
uint16_t Port
Definition decode.h:218
#define IPPROTO_SCTP
Definition decode.h:1228
void * FlowGetStorageById(const Flow *f, FlowStorageId id)
FlowStorageId GetFlowBypassInfoID(void)
Definition flow-util.c:222
int FlowHasAlerts(const Flow *f)
Check if flow has alerts.
Definition flow.c:164
#define FLOW_STATE_SIZE
Definition flow.h:515
#define FLOW_IS_IPV6(f)
Definition flow.h:172
@ FLOW_STATE_LOCAL_BYPASSED
Definition flow.h:507
@ FLOW_STATE_NEW
Definition flow.h:504
@ FLOW_STATE_CLOSED
Definition flow.h:506
@ FLOW_STATE_ESTABLISHED
Definition flow.h:505
#define FLOW_DIR_REVERSED
Definition flow.h:112
#define FLOW_WRONG_THREAD
Definition flow.h:110
#define FLOW_END_FLAG_FORCED
Definition flow.h:244
#define FLOW_ACTION_DROP
Definition flow.h:70
#define FLOW_IS_ELEPHANT
Definition flow.h:59
#define FLOW_END_FLAG_TIMEOUT
Definition flow.h:243
#define FLOW_ACTION_ACCEPT
Definition flow.h:62
#define FLOW_END_FLAG_TCPREUSE
Definition flow.h:246
#define FLOW_END_FLAG_SHUTDOWN
Definition flow.h:245
#define FLOW_END_FLAG_EMERGENCY
Definition flow.h:242
#define FLOW_IS_IPV4(f)
Definition flow.h:170
#define FLOW_ACTION_PASS
Definition flow.h:117
ThreadVars * tv
@ LOG_DIR_FLOW
OutputInitResult OutputJsonLogInitSub(SCConfNode *conf, OutputCtx *parent_ctx)
TmEcode JsonLogThreadInit(ThreadVars *t, const void *initdata, void **data)
TmEcode JsonLogThreadDeinit(ThreadVars *t, void *data)
void JsonFlowLogRegister(void)
void EveAddAppProto(Flow *f, SCJsonBuilder *js)
void EveAddFlow(Flow *f, SCJsonBuilder *js)
void EveTcpFlags(const uint8_t flags, SCJsonBuilder *js)
jsonify tcp flags field Only add 'true' fields in an attempt to keep things reasonably compact.
void OutputJsonBuilderBuffer(ThreadVars *tv, const Packet *p, Flow *f, SCJsonBuilder *js, OutputJsonThreadCtx *ctx)
void CreateEveFlowId(SCJsonBuilder *js, const Flow *f)
void EveAddCommonOptions(const OutputJsonCommonSettings *cfg, const Packet *p, const Flow *f, SCJsonBuilder *js, enum SCOutputJsonLogDirection dir)
void OutputRegisterFlowSubModule(LoggerId id, const char *parent_name, const char *name, const char *conf_name, OutputInitSubFunc InitFunc, FlowLogger FlowLogFunc, ThreadInitFunc ThreadInit, ThreadDeinitFunc ThreadDeinit)
Register a flow output sub-module.
Definition output.c:495
#define JB_SET_TRUE(jb, key)
Definition rust.h:27
#define JB_SET_STRING(jb, key, val)
Definition rust.h:26
uint64_t ts
#define STREAMTCP_STREAM_FLAG_HAS_GAP
const char * StreamTcpStateAsString(const enum TcpState state)
union FlowAddress_::@128 address
uint64_t tosrcpktcnt
Definition flow.h:533
uint64_t tosrcbytecnt
Definition flow.h:534
uint64_t todstpktcnt
Definition flow.h:535
uint64_t todstbytecnt
Definition flow.h:536
Flow data structure.
Definition flow.h:356
uint8_t applied_exception_policy
Definition flow.h:473
struct Flow_::@129::@135 icmp_s
AppProto alproto_ts
Definition flow.h:451
Port dp
Definition flow.h:372
AppProto alproto_tc
Definition flow.h:452
uint8_t proto
Definition flow.h:378
uint32_t flags
Definition flow.h:421
AppProto alproto_expect
Definition flow.h:459
uint8_t flow_end_flags
Definition flow.h:447
AppProto alproto
application level protocol
Definition flow.h:450
void * alstate
Definition flow.h:479
FlowStateType flow_state
Definition flow.h:412
uint64_t tosrcbytecnt
Definition flow.h:498
AppProto alproto_orig
Definition flow.h:456
uint8_t vlan_idx
Definition flow.h:382
void * protoctx
Definition flow.h:441
uint16_t vlan_id[VLAN_MAX_LAYERS]
Definition flow.h:380
uint64_t todstbytecnt
Definition flow.h:497
SCTime_t lastts
Definition flow.h:410
struct LiveDevice_ * livedev
Definition flow.h:398
struct Flow_::@131::@137 icmp_d
uint8_t code
Definition flow.h:364
struct Flow_::@129::@136 esp
uint32_t todstpktcnt
Definition flow.h:495
uint32_t spi
Definition flow.h:368
FlowAddress src
Definition flow.h:359
Port sp
Definition flow.h:361
FlowAddress dst
Definition flow.h:359
uint32_t tosrcpktcnt
Definition flow.h:496
uint8_t type
Definition flow.h:363
SCTime_t startts
Definition flow.h:493
OutputJsonCommonSettings cfg
Definition output-json.h:78
OutputJsonCtx * ctx
Definition output-json.h:84
StreamingBuffer sb
Per thread variable structure.
Definition threadvars.h:58
@ LOGGER_JSON_FLOW
@ TM_ECODE_OK
#define SCEnter(...)
Definition util-debug.h:277
#define SCLogDebug(...)
Definition util-debug.h:275
#define SCReturnInt(x)
Definition util-debug.h:281
#define EXCEPTION_TARGET_FLAG_DEFRAG_MEMCAP
#define EXCEPTION_TARGET_FLAG_REASSEMBLY_MEMCAP
#define EXCEPTION_TARGET_FLAG_APPLAYER_ERROR
#define EXCEPTION_TARGET_FLAG_MIDSTREAM
#define EXCEPTION_TARGET_FLAG_FLOW_MEMCAP
#define EXCEPTION_TARGET_FLAG_SESSION_MEMCAP
const char * ExceptionPolicyTargetFlagToString(uint8_t target_flag)
enum ExceptionPolicy ExceptionPolicyTargetPolicy(uint8_t target_flag)
const char * ExceptionPolicyEnumToString(enum ExceptionPolicy policy, bool is_json)
#define unlikely(expr)
const char * PrintInet(int af, const void *src, char *dst, socklen_t size)
Definition util-print.c:231
bool SCProtoNameValid(uint16_t proto)
Function to check if the received protocol number is valid and do we have corresponding name entry fo...
const char * known_proto[256]
void CreateIsoTimeString(const SCTime_t ts, char *str, size_t size)
Definition util-time.c:209
SCTime_t TimeGet(void)
Definition util-time.c:152
#define SCTIME_SECS(t)
Definition util-time.h:57
#define DEBUG_VALIDATE_BUG_ON(exp)