/src/suricata7/src/output-json-flow.c
Line | Count | Source |
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 | | |
31 | | #include "threads.h" |
32 | | #include "threadvars.h" |
33 | | #include "tm-threads.h" |
34 | | |
35 | | #include "util-print.h" |
36 | | #include "util-unittest.h" |
37 | | |
38 | | #include "util-debug.h" |
39 | | |
40 | | #include "output.h" |
41 | | #include "util-privs.h" |
42 | | #include "util-buffer.h" |
43 | | #include "util-device.h" |
44 | | #include "util-proto-name.h" |
45 | | #include "util-logopenfile.h" |
46 | | #include "util-time.h" |
47 | | #include "output-json.h" |
48 | | #include "output-json-flow.h" |
49 | | |
50 | | #include "stream-tcp.h" |
51 | | #include "stream-tcp-private.h" |
52 | | #include "flow-storage.h" |
53 | | #include "util-exception-policy.h" |
54 | | |
55 | | typedef struct LogFlowCtx_ { |
56 | | bool log_exception_policies; |
57 | | OutputJsonCtx *eve_ctx; |
58 | | } LogFlowCtx; |
59 | | |
60 | | typedef struct LogFlowLogThread_ { |
61 | | LogFlowCtx *flowlog_ctx; |
62 | | OutputJsonThreadCtx *output_ctx; |
63 | | } LogFlowLogThread; |
64 | | |
65 | | static void OutputFlowLogDeInitCtxSub(OutputCtx *output_ctx) |
66 | 0 | { |
67 | 0 | LogFlowCtx *flow_ctx = output_ctx->data; |
68 | 0 | SCFree(flow_ctx); |
69 | 0 | SCFree(output_ctx); |
70 | 0 | } |
71 | | |
72 | | static void JsonFlowLogParseConfig(ConfNode *conf, LogFlowCtx *flow_ctx) |
73 | 2 | { |
74 | | /* by default, don't log exception policies */ |
75 | 2 | flow_ctx->log_exception_policies = false; |
76 | | |
77 | 2 | if (conf != NULL) { |
78 | 0 | if (ConfNodeChildValueIsTrue(conf, "exception-policy")) { |
79 | 0 | flow_ctx->log_exception_policies = true; |
80 | 0 | } |
81 | 0 | } |
82 | 2 | SCLogDebug("Exception policy logging for flow %s", |
83 | 2 | flow_ctx->log_exception_policies ? "enabled" : "disabled"); |
84 | 2 | } |
85 | | |
86 | | static OutputInitResult OutputFlowLogInitSub(ConfNode *conf, OutputCtx *parent_ctx) |
87 | 2 | { |
88 | 2 | OutputInitResult result = { NULL, false }; |
89 | 2 | OutputJsonCtx *ojc = parent_ctx->data; |
90 | | |
91 | 2 | LogFlowCtx *flow_ctx = SCCalloc(1, sizeof(LogFlowCtx)); |
92 | 2 | if (unlikely(flow_ctx == NULL)) { |
93 | 0 | return result; |
94 | 0 | } |
95 | | |
96 | 2 | OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx)); |
97 | 2 | if (unlikely(output_ctx == NULL)) { |
98 | 0 | SCFree(flow_ctx); |
99 | 0 | return result; |
100 | 0 | } |
101 | | |
102 | 2 | flow_ctx->eve_ctx = ojc; |
103 | | |
104 | 2 | output_ctx->data = flow_ctx; |
105 | 2 | output_ctx->DeInit = OutputFlowLogDeInitCtxSub; |
106 | 2 | JsonFlowLogParseConfig(conf, flow_ctx); |
107 | | |
108 | 2 | result.ctx = output_ctx; |
109 | 2 | result.ok = true; |
110 | | |
111 | 2 | return result; |
112 | 2 | } |
113 | | |
114 | | static TmEcode JsonFlowLogThreadInit(ThreadVars *tv, const void *initdata, void **data) |
115 | 6 | { |
116 | 6 | LogFlowLogThread *thread = SCCalloc(1, sizeof(LogFlowLogThread)); |
117 | 6 | if (unlikely(thread == NULL)) { |
118 | 0 | return TM_ECODE_FAILED; |
119 | 0 | } |
120 | | |
121 | 6 | if (initdata == NULL) { |
122 | 0 | SCLogDebug("Error getting context for EveLogFlow. \"initdata\" argument NULL"); |
123 | 0 | goto error_exit; |
124 | 0 | } |
125 | | |
126 | 6 | thread->flowlog_ctx = ((OutputCtx *)initdata)->data; |
127 | 6 | thread->output_ctx = CreateEveThreadCtx(tv, thread->flowlog_ctx->eve_ctx); |
128 | 6 | if (!thread->output_ctx) { |
129 | 0 | goto error_exit; |
130 | 0 | } |
131 | | |
132 | 6 | *data = (void *)thread; |
133 | 6 | return TM_ECODE_OK; |
134 | | |
135 | 0 | error_exit: |
136 | 0 | SCFree(thread); |
137 | 0 | return TM_ECODE_FAILED; |
138 | 6 | } |
139 | | |
140 | | static TmEcode JsonFlowLogThreadDeInit(ThreadVars *tv, void *data) |
141 | 0 | { |
142 | 0 | LogFlowLogThread *thread = (LogFlowLogThread *)data; |
143 | 0 | if (thread == NULL) { |
144 | 0 | return TM_ECODE_FAILED; |
145 | 0 | } |
146 | | |
147 | 0 | FreeEveThreadCtx(thread->output_ctx); |
148 | 0 | SCFree(thread); |
149 | 0 | return TM_ECODE_OK; |
150 | 0 | } |
151 | | |
152 | | static JsonBuilder *CreateEveHeaderFromFlow(const Flow *f) |
153 | 102k | { |
154 | 102k | char timebuf[64]; |
155 | 102k | char srcip[46] = {0}, dstip[46] = {0}; |
156 | 102k | Port sp, dp; |
157 | | |
158 | 102k | JsonBuilder *jb = jb_new_object(); |
159 | 102k | if (unlikely(jb == NULL)) { |
160 | 0 | return NULL; |
161 | 0 | } |
162 | | |
163 | 102k | SCTime_t ts = TimeGet(); |
164 | | |
165 | 102k | CreateIsoTimeString(ts, timebuf, sizeof(timebuf)); |
166 | | |
167 | 102k | if ((f->flags & FLOW_DIR_REVERSED) == 0) { |
168 | 51.7k | if (FLOW_IS_IPV4(f)) { |
169 | 47.5k | PrintInet(AF_INET, (const void *)&(f->src.addr_data32[0]), srcip, sizeof(srcip)); |
170 | 47.5k | PrintInet(AF_INET, (const void *)&(f->dst.addr_data32[0]), dstip, sizeof(dstip)); |
171 | 47.5k | } else if (FLOW_IS_IPV6(f)) { |
172 | 4.21k | PrintInet(AF_INET6, (const void *)&(f->src.address), srcip, sizeof(srcip)); |
173 | 4.21k | PrintInet(AF_INET6, (const void *)&(f->dst.address), dstip, sizeof(dstip)); |
174 | 4.21k | } |
175 | 51.7k | sp = f->sp; |
176 | 51.7k | dp = f->dp; |
177 | 51.7k | } else { |
178 | 50.2k | if (FLOW_IS_IPV4(f)) { |
179 | 48.4k | PrintInet(AF_INET, (const void *)&(f->dst.addr_data32[0]), srcip, sizeof(srcip)); |
180 | 48.4k | PrintInet(AF_INET, (const void *)&(f->src.addr_data32[0]), dstip, sizeof(dstip)); |
181 | 48.4k | } else if (FLOW_IS_IPV6(f)) { |
182 | 1.82k | PrintInet(AF_INET6, (const void *)&(f->dst.address), srcip, sizeof(srcip)); |
183 | 1.82k | PrintInet(AF_INET6, (const void *)&(f->src.address), dstip, sizeof(dstip)); |
184 | 1.82k | } |
185 | 50.2k | sp = f->dp; |
186 | 50.2k | dp = f->sp; |
187 | 50.2k | } |
188 | | |
189 | | /* time */ |
190 | 102k | jb_set_string(jb, "timestamp", timebuf); |
191 | | |
192 | 102k | CreateEveFlowId(jb, (const Flow *)f); |
193 | | |
194 | | #if 0 // TODO |
195 | | /* sensor id */ |
196 | | if (sensor_id >= 0) |
197 | | json_object_set_new(js, "sensor_id", json_integer(sensor_id)); |
198 | | #endif |
199 | | |
200 | | /* input interface */ |
201 | 102k | if (f->livedev) { |
202 | 0 | jb_set_string(jb, "in_iface", f->livedev->dev); |
203 | 0 | } |
204 | | |
205 | 102k | JB_SET_STRING(jb, "event_type", "flow"); |
206 | | |
207 | | /* vlan */ |
208 | 102k | if (f->vlan_idx > 0) { |
209 | 1.38k | jb_open_array(jb, "vlan"); |
210 | 1.38k | jb_append_uint(jb, f->vlan_id[0]); |
211 | 1.38k | if (f->vlan_idx > 1) { |
212 | 12 | jb_append_uint(jb, f->vlan_id[1]); |
213 | 12 | } |
214 | 1.38k | if (f->vlan_idx > 2) { |
215 | 0 | jb_append_uint(jb, f->vlan_id[2]); |
216 | 0 | } |
217 | 1.38k | jb_close(jb); |
218 | 1.38k | } |
219 | | |
220 | | /* tuple */ |
221 | 102k | jb_set_string(jb, "src_ip", srcip); |
222 | 102k | switch(f->proto) { |
223 | 483 | case IPPROTO_ICMP: |
224 | 483 | break; |
225 | 1.40k | case IPPROTO_UDP: |
226 | 99.6k | case IPPROTO_TCP: |
227 | 99.6k | case IPPROTO_SCTP: |
228 | 99.6k | jb_set_uint(jb, "src_port", sp); |
229 | 99.6k | break; |
230 | 102k | } |
231 | 102k | jb_set_string(jb, "dest_ip", dstip); |
232 | 102k | switch(f->proto) { |
233 | 483 | case IPPROTO_ICMP: |
234 | 483 | break; |
235 | 1.40k | case IPPROTO_UDP: |
236 | 99.6k | case IPPROTO_TCP: |
237 | 99.6k | case IPPROTO_SCTP: |
238 | 99.6k | jb_set_uint(jb, "dest_port", dp); |
239 | 99.6k | break; |
240 | 102k | } |
241 | | |
242 | 102k | if (SCProtoNameValid(f->proto)) { |
243 | 102k | jb_set_string(jb, "proto", known_proto[f->proto]); |
244 | 102k | } else { |
245 | 0 | char proto[4]; |
246 | 0 | snprintf(proto, sizeof(proto), "%"PRIu8"", f->proto); |
247 | 0 | jb_set_string(jb, "proto", proto); |
248 | 0 | } |
249 | | |
250 | 102k | switch (f->proto) { |
251 | 483 | case IPPROTO_ICMP: |
252 | 836 | case IPPROTO_ICMPV6: |
253 | 836 | jb_set_uint(jb, "icmp_type", f->icmp_s.type); |
254 | 836 | jb_set_uint(jb, "icmp_code", f->icmp_s.code); |
255 | 836 | if (f->tosrcpktcnt) { |
256 | 9 | jb_set_uint(jb, "response_icmp_type", f->icmp_d.type); |
257 | 9 | jb_set_uint(jb, "response_icmp_code", f->icmp_d.code); |
258 | 9 | } |
259 | 836 | break; |
260 | 1.53k | case IPPROTO_ESP: |
261 | 1.53k | jb_set_uint(jb, "spi", f->esp.spi); |
262 | 1.53k | break; |
263 | 102k | } |
264 | 102k | return jb; |
265 | 102k | } |
266 | | |
267 | | void EveAddAppProto(Flow *f, JsonBuilder *js) |
268 | 965k | { |
269 | 965k | if (f->alproto) { |
270 | 660k | jb_set_string(js, "app_proto", AppProtoToString(f->alproto)); |
271 | 660k | } |
272 | 965k | if (f->alproto_ts && f->alproto_ts != f->alproto) { |
273 | 65.9k | jb_set_string(js, "app_proto_ts", AppProtoToString(f->alproto_ts)); |
274 | 65.9k | } |
275 | 965k | if (f->alproto_tc && f->alproto_tc != f->alproto) { |
276 | 119k | jb_set_string(js, "app_proto_tc", AppProtoToString(f->alproto_tc)); |
277 | 119k | } |
278 | 965k | if (f->alproto_orig != f->alproto && f->alproto_orig != ALPROTO_UNKNOWN) { |
279 | 7.16k | jb_set_string(js, "app_proto_orig", AppProtoToString(f->alproto_orig)); |
280 | 7.16k | } |
281 | 965k | if (f->alproto_expect != f->alproto && f->alproto_expect != ALPROTO_UNKNOWN) { |
282 | 16.7k | jb_set_string(js, "app_proto_expected", |
283 | 16.7k | AppProtoToString(f->alproto_expect)); |
284 | 16.7k | } |
285 | | |
286 | 965k | } |
287 | | |
288 | | void EveAddFlow(Flow *f, JsonBuilder *js) |
289 | 965k | { |
290 | 965k | FlowBypassInfo *fc = FlowGetStorageById(f, GetFlowBypassInfoID()); |
291 | 965k | if (fc) { |
292 | 0 | jb_set_uint(js, "pkts_toserver", f->todstpktcnt + fc->todstpktcnt); |
293 | 0 | jb_set_uint(js, "pkts_toclient", f->tosrcpktcnt + fc->tosrcpktcnt); |
294 | 0 | jb_set_uint(js, "bytes_toserver", f->todstbytecnt + fc->todstbytecnt); |
295 | 0 | jb_set_uint(js, "bytes_toclient", f->tosrcbytecnt + fc->tosrcbytecnt); |
296 | |
|
297 | 0 | jb_open_object(js, "bypassed"); |
298 | 0 | jb_set_uint(js, "pkts_toserver", fc->todstpktcnt); |
299 | 0 | jb_set_uint(js, "pkts_toclient", fc->tosrcpktcnt); |
300 | 0 | jb_set_uint(js, "bytes_toserver", fc->todstbytecnt); |
301 | 0 | jb_set_uint(js, "bytes_toclient", fc->tosrcbytecnt); |
302 | 0 | jb_close(js); |
303 | 965k | } else { |
304 | 965k | jb_set_uint(js, "pkts_toserver", f->todstpktcnt); |
305 | 965k | jb_set_uint(js, "pkts_toclient", f->tosrcpktcnt); |
306 | 965k | jb_set_uint(js, "bytes_toserver", f->todstbytecnt); |
307 | 965k | jb_set_uint(js, "bytes_toclient", f->tosrcbytecnt); |
308 | 965k | } |
309 | | |
310 | 965k | char timebuf1[64]; |
311 | 965k | CreateIsoTimeString(f->startts, timebuf1, sizeof(timebuf1)); |
312 | 965k | jb_set_string(js, "start", timebuf1); |
313 | 965k | } |
314 | | |
315 | | static void EveExceptionPolicyLog(JsonBuilder *js, uint16_t flag) |
316 | 87.6k | { |
317 | 87.6k | if (flag & EXCEPTION_TARGET_FLAG_DEFRAG_MEMCAP) { |
318 | 0 | jb_start_object(js); |
319 | 0 | jb_set_string(js, "target", |
320 | 0 | ExceptionPolicyTargetFlagToString(EXCEPTION_TARGET_FLAG_DEFRAG_MEMCAP)); |
321 | 0 | jb_set_string(js, "policy", |
322 | 0 | ExceptionPolicyEnumToString( |
323 | 0 | ExceptionPolicyTargetPolicy(EXCEPTION_TARGET_FLAG_DEFRAG_MEMCAP), true)); |
324 | 0 | jb_close(js); |
325 | 0 | } |
326 | 87.6k | if (flag & EXCEPTION_TARGET_FLAG_SESSION_MEMCAP) { |
327 | 0 | jb_start_object(js); |
328 | 0 | jb_set_string(js, "target", |
329 | 0 | ExceptionPolicyTargetFlagToString(EXCEPTION_TARGET_FLAG_SESSION_MEMCAP)); |
330 | 0 | jb_set_string(js, "policy", |
331 | 0 | ExceptionPolicyEnumToString( |
332 | 0 | ExceptionPolicyTargetPolicy(EXCEPTION_TARGET_FLAG_SESSION_MEMCAP), true)); |
333 | 0 | jb_close(js); |
334 | 0 | } |
335 | 87.6k | if (flag & EXCEPTION_TARGET_FLAG_REASSEMBLY_MEMCAP) { |
336 | 0 | jb_start_object(js); |
337 | 0 | jb_set_string(js, "target", |
338 | 0 | ExceptionPolicyTargetFlagToString(EXCEPTION_TARGET_FLAG_REASSEMBLY_MEMCAP)); |
339 | 0 | jb_set_string(js, "policy", |
340 | 0 | ExceptionPolicyEnumToString( |
341 | 0 | ExceptionPolicyTargetPolicy(EXCEPTION_TARGET_FLAG_REASSEMBLY_MEMCAP), |
342 | 0 | true)); |
343 | 0 | jb_close(js); |
344 | 0 | } |
345 | 87.6k | if (flag & EXCEPTION_TARGET_FLAG_FLOW_MEMCAP) { |
346 | 0 | jb_start_object(js); |
347 | 0 | jb_set_string( |
348 | 0 | js, "target", ExceptionPolicyTargetFlagToString(EXCEPTION_TARGET_FLAG_FLOW_MEMCAP)); |
349 | 0 | jb_set_string(js, "policy", |
350 | 0 | ExceptionPolicyEnumToString( |
351 | 0 | ExceptionPolicyTargetPolicy(EXCEPTION_TARGET_FLAG_FLOW_MEMCAP), true)); |
352 | 0 | jb_close(js); |
353 | 0 | } |
354 | 87.6k | if (flag & EXCEPTION_TARGET_FLAG_MIDSTREAM) { |
355 | 86.9k | jb_start_object(js); |
356 | 86.9k | jb_set_string( |
357 | 86.9k | js, "target", ExceptionPolicyTargetFlagToString(EXCEPTION_TARGET_FLAG_MIDSTREAM)); |
358 | 86.9k | jb_set_string(js, "policy", |
359 | 86.9k | ExceptionPolicyEnumToString( |
360 | 86.9k | ExceptionPolicyTargetPolicy(EXCEPTION_TARGET_FLAG_MIDSTREAM), true)); |
361 | 86.9k | jb_close(js); |
362 | 86.9k | } |
363 | 87.6k | if (flag & EXCEPTION_TARGET_FLAG_APPLAYER_ERROR) { |
364 | 25.6k | jb_start_object(js); |
365 | 25.6k | jb_set_string(js, "target", |
366 | 25.6k | ExceptionPolicyTargetFlagToString(EXCEPTION_TARGET_FLAG_APPLAYER_ERROR)); |
367 | 25.6k | jb_set_string(js, "policy", |
368 | 25.6k | ExceptionPolicyEnumToString( |
369 | 25.6k | ExceptionPolicyTargetPolicy(EXCEPTION_TARGET_FLAG_APPLAYER_ERROR), true)); |
370 | 25.6k | jb_close(js); |
371 | 25.6k | } |
372 | 87.6k | } |
373 | | |
374 | | /* Eve format logging */ |
375 | | static void EveFlowLogJSON(LogFlowLogThread *ft, JsonBuilder *jb, Flow *f) |
376 | 102k | { |
377 | 102k | EveAddAppProto(f, jb); |
378 | 102k | jb_open_object(jb, "flow"); |
379 | 102k | EveAddFlow(f, jb); |
380 | | |
381 | 102k | char timebuf2[64]; |
382 | 102k | CreateIsoTimeString(f->lastts, timebuf2, sizeof(timebuf2)); |
383 | 102k | jb_set_string(jb, "end", timebuf2); |
384 | | |
385 | 102k | int32_t age = SCTIME_SECS(f->lastts) - SCTIME_SECS(f->startts); |
386 | 102k | jb_set_uint(jb, "age", age); |
387 | | |
388 | 102k | if (f->flow_end_flags & FLOW_END_FLAG_EMERGENCY) |
389 | 0 | JB_SET_TRUE(jb, "emergency"); |
390 | 102k | const char *state = NULL; |
391 | 102k | if (f->flow_end_flags & FLOW_END_FLAG_STATE_NEW) |
392 | 29.7k | state = "new"; |
393 | 72.2k | else if (f->flow_end_flags & FLOW_END_FLAG_STATE_ESTABLISHED) |
394 | 36.1k | state = "established"; |
395 | 36.1k | else if (f->flow_end_flags & FLOW_END_FLAG_STATE_CLOSED) |
396 | 36.1k | state = "closed"; |
397 | 0 | else if (f->flow_end_flags & FLOW_END_FLAG_STATE_BYPASSED) { |
398 | 0 | state = "bypassed"; |
399 | 0 | int flow_state = f->flow_state; |
400 | 0 | switch (flow_state) { |
401 | 0 | case FLOW_STATE_LOCAL_BYPASSED: |
402 | 0 | JB_SET_STRING(jb, "bypass", "local"); |
403 | 0 | break; |
404 | | #ifdef CAPTURE_OFFLOAD |
405 | | case FLOW_STATE_CAPTURE_BYPASSED: |
406 | | JB_SET_STRING(jb, "bypass", "capture"); |
407 | | break; |
408 | | #endif |
409 | 0 | default: |
410 | 0 | SCLogError("Invalid flow state: %d, contact developers", flow_state); |
411 | 0 | } |
412 | 0 | } |
413 | | |
414 | 102k | jb_set_string(jb, "state", state); |
415 | | |
416 | 102k | const char *reason = NULL; |
417 | 102k | if (f->flow_end_flags & FLOW_END_FLAG_FORCED) |
418 | 0 | reason = "forced"; |
419 | 102k | else if (f->flow_end_flags & FLOW_END_FLAG_SHUTDOWN) |
420 | 0 | reason = "shutdown"; |
421 | 102k | else if (f->flow_end_flags & FLOW_END_FLAG_TIMEOUT) |
422 | 102k | reason = "timeout"; |
423 | 0 | else |
424 | 0 | reason = "unknown"; |
425 | | |
426 | 102k | jb_set_string(jb, "reason", reason); |
427 | | |
428 | 102k | jb_set_bool(jb, "alerted", FlowHasAlerts(f)); |
429 | 102k | if (f->flags & FLOW_WRONG_THREAD) |
430 | 0 | JB_SET_TRUE(jb, "wrong_thread"); |
431 | | |
432 | 102k | if (f->flags & FLOW_ACTION_DROP) { |
433 | 122 | JB_SET_STRING(jb, "action", "drop"); |
434 | 101k | } else if (f->flags & FLOW_ACTION_PASS) { |
435 | 303 | JB_SET_STRING(jb, "action", "pass"); |
436 | 303 | } |
437 | 102k | if (f->applied_exception_policy != 0 && ft->flowlog_ctx->log_exception_policies) { |
438 | 0 | jb_open_array(jb, "exception_policy"); |
439 | 0 | EveExceptionPolicyLog(jb, f->applied_exception_policy); |
440 | 0 | jb_close(jb); /* close array */ |
441 | 0 | } |
442 | | |
443 | | /* Close flow. */ |
444 | 102k | jb_close(jb); |
445 | | |
446 | 102k | EveAddCommonOptions(&ft->output_ctx->ctx->cfg, NULL, f, jb, LOG_DIR_FLOW); |
447 | | |
448 | | /* TCP */ |
449 | 102k | if (f->proto == IPPROTO_TCP) { |
450 | 98.2k | jb_open_object(jb, "tcp"); |
451 | | |
452 | 98.2k | TcpSession *ssn = f->protoctx; |
453 | | |
454 | 98.2k | char hexflags[3]; |
455 | 98.2k | snprintf(hexflags, sizeof(hexflags), "%02x", |
456 | 98.2k | ssn ? ssn->tcp_packet_flags : 0); |
457 | 98.2k | jb_set_string(jb, "tcp_flags", hexflags); |
458 | | |
459 | 98.2k | snprintf(hexflags, sizeof(hexflags), "%02x", |
460 | 98.2k | ssn ? ssn->client.tcp_flags : 0); |
461 | 98.2k | jb_set_string(jb, "tcp_flags_ts", hexflags); |
462 | | |
463 | 98.2k | snprintf(hexflags, sizeof(hexflags), "%02x", |
464 | 98.2k | ssn ? ssn->server.tcp_flags : 0); |
465 | 98.2k | jb_set_string(jb, "tcp_flags_tc", hexflags); |
466 | | |
467 | 98.2k | EveTcpFlags(ssn ? ssn->tcp_packet_flags : 0, jb); |
468 | | |
469 | 98.2k | if (ssn) { |
470 | 95.0k | const char *tcp_state = StreamTcpStateAsString(ssn->state); |
471 | 95.0k | if (tcp_state != NULL) |
472 | 95.0k | jb_set_string(jb, "state", tcp_state); |
473 | 95.0k | if (ssn->server.flags & STREAMTCP_STREAM_FLAG_HAS_GAP) { |
474 | 15.5k | JB_SET_TRUE(jb, "tc_gap"); |
475 | 15.5k | } |
476 | 95.0k | if (ssn->client.flags & STREAMTCP_STREAM_FLAG_HAS_GAP) { |
477 | 16.6k | JB_SET_TRUE(jb, "ts_gap"); |
478 | 16.6k | } |
479 | | |
480 | 95.0k | jb_set_uint(jb, "ts_max_regions", ssn->client.sb.max_regions); |
481 | 95.0k | jb_set_uint(jb, "tc_max_regions", ssn->server.sb.max_regions); |
482 | | |
483 | 95.0k | if (ssn->urg_offset_ts) |
484 | 0 | jb_set_uint(jb, "ts_urgent_oob_data", ssn->urg_offset_ts); |
485 | 95.0k | if (ssn->urg_offset_tc) |
486 | 0 | jb_set_uint(jb, "tc_urgent_oob_data", ssn->urg_offset_tc); |
487 | 95.0k | } |
488 | | |
489 | | /* Close tcp. */ |
490 | 98.2k | jb_close(jb); |
491 | 98.2k | } |
492 | 102k | } |
493 | | |
494 | | static int JsonFlowLogger(ThreadVars *tv, void *thread_data, Flow *f) |
495 | 206k | { |
496 | 206k | SCEnter(); |
497 | 206k | LogFlowLogThread *thread = thread_data; |
498 | | |
499 | 206k | JsonBuilder *jb = CreateEveHeaderFromFlow(f); |
500 | 206k | if (unlikely(jb == NULL)) { |
501 | 0 | SCReturnInt(TM_ECODE_OK); |
502 | 0 | } |
503 | | |
504 | 206k | EveFlowLogJSON(thread, jb, f); |
505 | | |
506 | 206k | OutputJsonBuilderBuffer(jb, thread->output_ctx); |
507 | 206k | jb_free(jb); |
508 | | |
509 | 206k | SCReturnInt(TM_ECODE_OK); |
510 | 206k | } |
511 | | |
512 | | void JsonFlowLogRegister (void) |
513 | 71 | { |
514 | | /* register as child of eve-log */ |
515 | 71 | OutputRegisterFlowSubModule(LOGGER_JSON_FLOW, "eve-log", "JsonFlowLog", "eve-log.flow", |
516 | 71 | OutputFlowLogInitSub, JsonFlowLogger, JsonFlowLogThreadInit, JsonFlowLogThreadDeInit, |
517 | | NULL); |
518 | 71 | } |