suricata
output-eve-stream.c
Go to the documentation of this file.
1/* Copyright (C) 2023-2024 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
22#include "suricata-common.h"
23#include "packet.h"
24#include "detect.h"
25#include "flow.h"
26#include "conf.h"
27
28#include "threads.h"
29#include "tm-threads.h"
30#include "threadvars.h"
31#include "util-debug.h"
32
33#include "decode-ipv4.h"
34#include "detect-parse.h"
35#include "detect-engine.h"
36#include "detect-engine-mpm.h"
37#include "detect-reference.h"
38
39#include "output.h"
40#include "output-json.h"
41#include "output-json-flow.h"
42#include "output-eve-stream.h"
43
44#include "stream-tcp.h"
45
46#include "util-unittest.h"
49#include "util-privs.h"
50#include "util-print.h"
51#include "util-proto-name.h"
52#include "util-logopenfile.h"
53#include "util-time.h"
54#include "util-buffer.h"
55
56#include "action-globals.h"
57
58#define MODULE_NAME "EveStreamLog"
59
60typedef struct EveStreamOutputCtx_ {
61 uint16_t trigger_flags; /**< presence of flags in packet trigger logging. 0xffff for all. */
64
69
70static TmEcode EveStreamLogThreadInit(ThreadVars *t, const void *initdata, void **data)
71{
73 if (unlikely(aft == NULL))
74 return TM_ECODE_FAILED;
75
76 if (initdata == NULL) {
77 SCLogDebug("Error getting context for EveLogDrop. \"initdata\" argument NULL");
78 goto error_exit;
79 }
80
81 /** Use the Output Context (file pointer and mutex) */
82 aft->stream_ctx = ((OutputCtx *)initdata)->data;
84 if (!aft->ctx) {
85 goto error_exit;
86 }
87
88 *data = (void *)aft;
89 return TM_ECODE_OK;
90
91error_exit:
92 SCFree(aft);
93 return TM_ECODE_FAILED;
94}
95
96static TmEcode EveStreamLogThreadDeinit(ThreadVars *t, void *data)
97{
99 if (aft == NULL) {
100 return TM_ECODE_OK;
101 }
102
103 FreeEveThreadCtx(aft->ctx);
104
105 /* clear memory */
106 memset(aft, 0, sizeof(*aft));
107
108 SCFree(aft);
109 return TM_ECODE_OK;
110}
111
112static void EveStreamOutputCtxFree(EveStreamOutputCtx *ctx)
113{
114 if (ctx != NULL) {
115 SCFree(ctx);
116 }
117}
118
119static void EveStreamLogDeInitCtxSub(OutputCtx *output_ctx)
120{
122
123 EveStreamOutputCtx *ctx = output_ctx->data;
124 SCFree(ctx);
125 SCLogDebug("cleaning up sub output_ctx %p", output_ctx);
126 SCFree(output_ctx);
127}
128
129static uint16_t SetFlag(SCConfNode *conf, const char *opt, const uint16_t inflag)
130{
131 const char *v = SCConfNodeLookupChildValue(conf, opt);
132 if (v != NULL && SCConfValIsTrue(v)) {
133 return inflag;
134 }
135 return 0;
136}
137
138static OutputInitResult EveStreamLogInitCtxSub(SCConfNode *conf, OutputCtx *parent_ctx)
139{
140 OutputInitResult result = { NULL, false };
141 OutputJsonCtx *ajt = parent_ctx->data;
142
143 EveStreamOutputCtx *ctx = SCCalloc(1, sizeof(*ctx));
144 if (ctx == NULL)
145 return result;
146
147 OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
148 if (unlikely(output_ctx == NULL)) {
149 EveStreamOutputCtxFree(ctx);
150 return result;
151 }
152
153 if (conf) {
154 // TODO add all flags
155
156 ctx->trigger_flags |= SetFlag(conf, "event-set", STREAM_PKT_FLAG_EVENTSET);
157 ctx->trigger_flags |= SetFlag(conf, "state-update", STREAM_PKT_FLAG_STATE_UPDATE);
158 ctx->trigger_flags |=
159 SetFlag(conf, "spurious-retransmission", STREAM_PKT_FLAG_SPURIOUS_RETRANSMISSION);
160 ctx->trigger_flags |= SetFlag(conf, "tcp-session-reuse", STREAM_PKT_FLAG_TCP_SESSION_REUSE);
161
162 ctx->trigger_flags |= SetFlag(conf, "all", 0xFFFF);
163 SCLogDebug("trigger_flags %04x", ctx->trigger_flags);
164 }
165 ctx->eve_ctx = ajt;
166
167 output_ctx->data = ctx;
168 output_ctx->DeInit = EveStreamLogDeInitCtxSub;
169
170 result.ctx = output_ctx;
171 result.ok = true;
172
173 SCLogWarning("eve.stream facility is EXPERIMENTAL and can change w/o notice");
174 return result;
175}
176
177void EveAddFlowTcpStreamFlags(const TcpStream *stream, const char *name, SCJsonBuilder *jb)
178{
179 SCJbOpenArray(jb, name);
181 SCJbAppendString(jb, "has_gap");
183 SCJbAppendString(jb, "noreassembly");
185 SCJbAppendString(jb, "keepalive");
187 SCJbAppendString(jb, "depth_reached");
189 SCJbAppendString(jb, "trigger_raw");
191 SCJbAppendString(jb, "timestamp");
193 SCJbAppendString(jb, "zero_timestamp");
195 SCJbAppendString(jb, "appproto_detection_completed");
197 SCJbAppendString(jb, "appproto_detection_skipped");
199 SCJbAppendString(jb, "new_raw_disabled");
201 SCJbAppendString(jb, "disable_raw");
203 SCJbAppendString(jb, "rst_recv");
204 SCJbClose(jb);
205}
206
207void EveAddFlowTcpFlags(const TcpSession *ssn, const char *name, SCJsonBuilder *jb)
208{
209 SCJbOpenObject(jb, "flags");
210
211 SCJbOpenArray(jb, name);
212 if (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) {
213 SCJbAppendString(jb, "midstream");
214 }
216 SCJbAppendString(jb, "midstream_established");
217 }
219 SCJbAppendString(jb, "midstream_synack");
220 }
221 if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
222 SCJbAppendString(jb, "timestamp");
223 }
225 SCJbAppendString(jb, "server_wscale");
226 }
228 SCJbAppendString(jb, "closed_by_rst");
229 }
230 if (ssn->flags & STREAMTCP_FLAG_4WHS) {
231 SCJbAppendString(jb, "4whs");
232 }
234 SCJbAppendString(jb, "detect_evasion_attempt");
235 }
237 SCJbAppendString(jb, "client_sackok");
238 }
240 SCJbAppendString(jb, "sackok");
241 }
243 SCJbAppendString(jb, "3whs_confirmed");
244 }
246 SCJbAppendString(jb, "app_layer_disabled");
247 }
248 if (ssn->flags & STREAMTCP_FLAG_BYPASS) {
249 SCJbAppendString(jb, "bypass");
250 }
252 SCJbAppendString(jb, "tcp_fast_open");
253 }
255 SCJbAppendString(jb, "tfo_data_ignored");
256 }
257 SCJbClose(jb);
258 SCJbClose(jb);
259}
260
261static void LogStreamSB(const StreamingBuffer *sb, SCJsonBuilder *js)
262{
263 SCJbSetUint(js, "sb_region_size", sb->region.buf_size);
264}
265
266static void LogStream(const TcpStream *stream, SCJsonBuilder *js)
267{
268 SCJbSetUint(js, "isn", stream->isn);
269 SCJbSetUint(js, "next_seq", stream->next_seq);
270 SCJbSetUint(js, "last_ack", stream->last_ack);
271 SCJbSetUint(js, "next_win", stream->next_win);
272 if (!(stream->flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY)) {
273 SCJbSetUint(js, "base_seq", stream->base_seq);
274 SCJbSetUint(js, "segs_right_edge", stream->segs_right_edge);
275 }
276 SCJbSetUint(js, "window", stream->window);
277 SCJbSetUint(js, "wscale", stream->wscale);
278
279 EveAddFlowTcpStreamFlags(stream, "flags", js);
280
281 TcpSegment *s;
282 uint32_t segs = 0;
283 RB_FOREACH(s, TCPSEG, (struct TCPSEG *)&stream->seg_tree)
284 {
285 segs++;
286 }
287 SCJbSetUint(js, "seg_cnt", segs);
288 LogStreamSB(&stream->sb, js);
289}
290
291/**
292 * \brief Log the stream packets
293 *
294 * \param tv Pointer the current thread variables
295 * \param data Pointer to the EveStreamLogThread struct
296 * \param p Pointer the packet which is being logged
297 *
298 * \retval 0 on success
299 */
300static int EveStreamLogger(ThreadVars *tv, void *thread_data, const Packet *p)
301{
302 EveStreamLogThread *td = thread_data;
304
307
308 SCJsonBuilder *js = CreateEveHeader(p, LOG_DIR_PACKET, "stream_tcp", &addr, ctx->eve_ctx);
309 if (unlikely(js == NULL))
310 return TM_ECODE_OK;
311
312 if (p->flow != NULL) {
313 if (p->flowflags & FLOW_PKT_TOSERVER) {
314 SCJbSetString(js, "direction", "to_server");
315 } else {
316 SCJbSetString(js, "direction", "to_client");
317 }
318 }
319
320 SCJbOpenObject(js, "stream_tcp");
321 SCJbOpenObject(js, "packet");
322
323 if (PacketIsIPv4(p)) {
324 const IPV4Hdr *ip4h = PacketGetIPv4(p);
325 SCJbSetUint(js, "len", IPV4_GET_RAW_IPLEN(ip4h));
326 SCJbSetUint(js, "tos", IPV4_GET_RAW_IPTOS(ip4h));
327 SCJbSetUint(js, "ttl", IPV4_GET_RAW_IPTTL(ip4h));
328 SCJbSetUint(js, "ipid", IPV4_GET_RAW_IPID(ip4h));
329 } else if (PacketIsIPv6(p)) {
330 const IPV6Hdr *ip6h = PacketGetIPv6(p);
331 SCJbSetUint(js, "len", IPV6_GET_RAW_PLEN(ip6h));
332 SCJbSetUint(js, "tc", IPV6_GET_RAW_CLASS(ip6h));
333 SCJbSetUint(js, "hoplimit", IPV6_GET_RAW_HLIM(ip6h));
334 SCJbSetUint(js, "flowlbl", IPV6_GET_RAW_FLOW(ip6h));
335 }
336 if (PacketIsTCP(p)) {
337 const TCPHdr *tcph = PacketGetTCP(p);
338 SCJbSetUint(js, "tcpseq", TCP_GET_RAW_SEQ(tcph));
339 SCJbSetUint(js, "tcpack", TCP_GET_RAW_ACK(tcph));
340 SCJbSetUint(js, "tcpwin", TCP_GET_RAW_WINDOW(tcph));
341 SCJbSetBool(js, "syn", TCP_ISSET_FLAG_RAW_SYN(tcph) ? true : false);
342 SCJbSetBool(js, "ack", TCP_ISSET_FLAG_RAW_ACK(tcph) ? true : false);
343 SCJbSetBool(js, "psh", TCP_ISSET_FLAG_RAW_PUSH(tcph) ? true : false);
344 SCJbSetBool(js, "rst", TCP_ISSET_FLAG_RAW_RST(tcph) ? true : false);
345 SCJbSetBool(js, "urg", TCP_ISSET_FLAG_RAW_URG(tcph) ? true : false);
346 SCJbSetBool(js, "fin", TCP_ISSET_FLAG_RAW_FIN(tcph) ? true : false);
347 SCJbSetUint(js, "tcpres", TCP_GET_RAW_X2(tcph));
348 SCJbSetUint(js, "tcpurgp", TCP_GET_RAW_URG_POINTER(tcph));
349
350 SCJbOpenArray(js, "flags");
352 SCJbAppendString(js, "retransmission");
354 SCJbAppendString(js, "spurious_retransmission");
356 SCJbAppendString(js, "keepalive");
358 SCJbAppendString(js, "keepalive_ack");
360 SCJbAppendString(js, "window_update");
361
363 SCJbAppendString(js, "event_set");
365 SCJbAppendString(js, "state_update");
367 SCJbAppendString(js, "dup_ack");
369 SCJbAppendString(js, "dsack");
371 SCJbAppendString(js, "ack_unseen_data");
373 SCJbAppendString(js, "tcp_session_reuse");
375 SCJbAppendString(js, "zero_window_probe");
377 SCJbAppendString(js, "zero_window_probe_ack");
378 SCJbClose(js);
379 }
380 SCJbClose(js);
381
382 SCJbOpenObject(js, "session");
383 if (p->flow != NULL && p->flow->protoctx != NULL) {
384 const TcpSession *ssn = p->flow->protoctx;
385 const char *tcp_state = StreamTcpStateAsString(ssn->state);
386 if (tcp_state != NULL)
387 SCJbSetString(js, "state", tcp_state);
389 const char *tcp_pstate = StreamTcpStateAsString(ssn->pstate);
390 if (tcp_pstate != NULL)
391 SCJbSetString(js, "pstate", tcp_pstate);
392 }
393 EveAddFlowTcpFlags(ssn, "flags", js);
394
395 SCJbOpenObject(js, "client");
396 LogStream(&ssn->client, js);
397 SCJbClose(js);
398 SCJbOpenObject(js, "server");
399 LogStream(&ssn->server, js);
400 SCJbClose(js);
401 }
402 SCJbClose(js);
403
405 SCJbOpenArray(js, "events");
406 for (int i = 0; i < p->events.cnt; i++) {
407 uint8_t event_code = p->events.events[i];
408 bool is_decode = EVENT_IS_DECODER_PACKET_ERROR(event_code);
409 if (is_decode)
410 continue;
411 if (event_code >= DECODE_EVENT_MAX)
412 continue;
413 const char *event = DEvents[event_code].event_name;
414 if (event == NULL)
415 continue;
416 SCJbAppendString(js, event);
417 }
418 SCJbClose(js);
419 }
420
421 if (p->drop_reason != 0) {
422 const char *str = PacketDropReasonToString(p->drop_reason);
423 SCJbSetString(js, "reason", str);
424 }
425
426 /* Close stream. */
427 SCJbClose(js);
428
429 OutputJsonBuilderBuffer(tv, p, p->flow, js, td->ctx);
430 SCJbFree(js);
431
432 return TM_ECODE_OK;
433}
434
435/**
436 * \brief Check if we need to log this packet
437 *
438 * \param tv Pointer the current thread variables
439 * \param p Pointer the packet which is tested
440 *
441 * \retval bool true or false
442 */
443static bool EveStreamLogCondition(ThreadVars *tv, void *data, const Packet *p)
444{
445 EveStreamLogThread *td = data;
447
448 return (p->proto == IPPROTO_TCP &&
449 (ctx->trigger_flags == 0xffff ||
450 (p->l4.vars.tcp.stream_pkt_flags & ctx->trigger_flags) != 0));
451}
452
454{
455 OutputPacketLoggerFunctions output_logger_functions = {
456 .LogFunc = EveStreamLogger,
457 .FlushFunc = OutputJsonLogFlush,
458 .ConditionFunc = EveStreamLogCondition,
459 .ThreadInitFunc = EveStreamLogThreadInit,
460 .ThreadDeinitFunc = EveStreamLogThreadDeinit,
461 .ThreadExitPrintStatsFunc = NULL,
462 };
463
465 EveStreamLogInitCtxSub, &output_logger_functions);
466}
int SCConfValIsTrue(const char *val)
Check if a value is true.
Definition conf.c:551
const char * SCConfNodeLookupChildValue(const SCConfNode *node, const char *name)
Lookup the value of a child configuration node by name.
Definition conf.c:824
const struct DecodeEvents_ DEvents[]
#define EVENT_IS_DECODER_PACKET_ERROR(e)
@ DECODE_EVENT_MAX
#define IPV4_GET_RAW_IPTOS(ip4h)
Definition decode-ipv4.h:97
#define IPV4_GET_RAW_IPLEN(ip4h)
Definition decode-ipv4.h:98
#define IPV4_GET_RAW_IPTTL(ip4h)
#define IPV4_GET_RAW_IPID(ip4h)
Definition decode-ipv4.h:99
#define IPV6_GET_RAW_PLEN(ip6h)
Definition decode-ipv6.h:66
#define IPV6_GET_RAW_HLIM(ip6h)
Definition decode-ipv6.h:67
#define IPV6_GET_RAW_FLOW(ip6h)
Definition decode-ipv6.h:64
#define IPV6_GET_RAW_CLASS(ip6h)
Definition decode-ipv6.h:63
#define TCP_GET_RAW_SEQ(tcph)
Definition decode-tcp.h:80
#define TCP_ISSET_FLAG_RAW_PUSH(tcph)
Definition decode-tcp.h:122
#define TCP_ISSET_FLAG_RAW_ACK(tcph)
Definition decode-tcp.h:123
#define TCP_GET_RAW_ACK(tcph)
Definition decode-tcp.h:81
#define TCP_ISSET_FLAG_RAW_FIN(tcph)
Definition decode-tcp.h:119
#define TCP_GET_RAW_X2(tcph)
Definition decode-tcp.h:73
#define TCP_GET_RAW_URG_POINTER(tcph)
Definition decode-tcp.h:84
#define TCP_ISSET_FLAG_RAW_RST(tcph)
Definition decode-tcp.h:121
#define TCP_ISSET_FLAG_RAW_SYN(tcph)
Definition decode-tcp.h:120
#define TCP_GET_RAW_WINDOW(tcph)
Definition decode-tcp.h:83
#define TCP_ISSET_FLAG_RAW_URG(tcph)
Definition decode-tcp.h:124
#define FLOW_PKT_TOSERVER
Definition flow.h:233
ThreadVars * tv
const char * PacketDropReasonToString(enum PacketDropReason r)
Definition decode.c:903
struct Thresholds ctx
@ LOG_DIR_PACKET
void EveAddFlowTcpStreamFlags(const TcpStream *stream, const char *name, SCJsonBuilder *jb)
#define MODULE_NAME
void EveAddFlowTcpFlags(const TcpSession *ssn, const char *name, SCJsonBuilder *jb)
struct EveStreamOutputCtx_ EveStreamOutputCtx
void EveStreamLogRegister(void)
struct EveStreamLogThread_ EveStreamLogThread
int OutputJsonLogFlush(ThreadVars *tv, void *thread_data, const Packet *p)
OutputJsonThreadCtx * CreateEveThreadCtx(ThreadVars *t, OutputJsonCtx *ctx)
void FreeEveThreadCtx(OutputJsonThreadCtx *ctx)
SCJsonBuilder * CreateEveHeader(const Packet *p, enum SCOutputJsonLogDirection dir, const char *event_type, JsonAddrInfo *addr, OutputJsonCtx *eve_ctx)
void JsonAddrInfoInit(const Packet *p, enum SCOutputJsonLogDirection dir, JsonAddrInfo *addr)
const JsonAddrInfo json_addr_info_zero
Definition output-json.c:81
void OutputJsonBuilderBuffer(ThreadVars *tv, const Packet *p, Flow *f, SCJsonBuilder *js, OutputJsonThreadCtx *ctx)
void OutputRegisterPacketSubModule(LoggerId id, const char *parent_name, const char *name, const char *conf_name, OutputInitSubFunc InitFunc, OutputPacketLoggerFunctions *output_logger_functions)
Register a packet output sub-module.
Definition output.c:234
void OutputDropLoggerDisable(void)
Definition output.c:680
#define STREAM_PKT_FLAG_DSACK
#define STREAM_PKT_FLAG_RETRANSMISSION
#define STREAMTCP_STREAM_FLAG_NOREASSEMBLY
#define STREAMTCP_STREAM_FLAG_TIMESTAMP
#define STREAMTCP_STREAM_FLAG_DISABLE_RAW
#define STREAMTCP_FLAG_CLOSED_BY_RST
#define STREAM_PKT_FLAG_TCP_ZERO_WIN_PROBE
#define STREAMTCP_FLAG_BYPASS
#define STREAMTCP_FLAG_SERVER_WSCALE
#define STREAM_PKT_FLAG_DUP_ACK
#define STREAM_PKT_FLAG_SPURIOUS_RETRANSMISSION
#define STREAM_PKT_FLAG_WINDOWUPDATE
#define STREAMTCP_FLAG_TFO_DATA_IGNORED
#define STREAMTCP_STREAM_FLAG_TRIGGER_RAW
#define STREAM_PKT_FLAG_STATE_UPDATE
#define STREAM_PKT_FLAG_EVENTSET
#define STREAM_PKT_FLAG_TCP_ZERO_WIN_PROBE_ACK
#define STREAM_PKT_FLAG_ACK_UNSEEN_DATA
#define STREAMTCP_FLAG_MIDSTREAM
#define STREAMTCP_FLAG_TCP_FAST_OPEN
#define STREAMTCP_FLAG_DETECTION_EVASION_ATTEMPT
#define STREAMTCP_FLAG_3WHS_CONFIRMED
#define STREAM_PKT_FLAG_KEEPALIVEACK
#define STREAMTCP_FLAG_4WHS
#define STREAMTCP_STREAM_FLAG_APPPROTO_DETECTION_SKIPPED
#define STREAMTCP_FLAG_TIMESTAMP
#define STREAMTCP_STREAM_FLAG_KEEPALIVE
#define STREAMTCP_STREAM_FLAG_NEW_RAW_DISABLED
#define STREAMTCP_FLAG_MIDSTREAM_SYNACK
#define STREAMTCP_STREAM_FLAG_RST_RECV
#define STREAMTCP_STREAM_FLAG_ZERO_TIMESTAMP
#define STREAMTCP_FLAG_MIDSTREAM_ESTABLISHED
#define STREAMTCP_STREAM_FLAG_APPPROTO_DETECTION_COMPLETED
#define STREAMTCP_FLAG_APP_LAYER_DISABLED
#define STREAMTCP_STREAM_FLAG_HAS_GAP
#define STREAM_PKT_FLAG_KEEPALIVE
#define STREAMTCP_FLAG_CLIENT_SACKOK
#define STREAM_PKT_FLAG_TCP_SESSION_REUSE
#define STREAMTCP_STREAM_FLAG_DEPTH_REACHED
const char * StreamTcpStateAsString(const enum TcpState state)
const char * event_name
EveStreamOutputCtx * stream_ctx
OutputJsonThreadCtx * ctx
OutputJsonCtx * eve_ctx
void * protoctx
Definition flow.h:441
void * data
Definition tm-modules.h:91
void(* DeInit)(struct OutputCtx_ *)
Definition tm-modules.h:94
OutputCtx * ctx
Definition output.h:47
uint8_t events[PACKET_ENGINE_EVENT_MAX]
Definition decode.h:308
union PacketL4::L4Vars vars
struct PacketL4 l4
Definition decode.h:601
uint8_t flowflags
Definition decode.h:532
uint8_t drop_reason
Definition decode.h:647
struct Flow_ * flow
Definition decode.h:546
PacketEngineEvents events
Definition decode.h:630
uint8_t proto
Definition decode.h:523
StreamingBufferRegion region
uint16_t stream_pkt_flags
Definition decode-tcp.h:175
StreamingBuffer sb
struct TCPSEG seg_tree
uint32_t segs_right_edge
Per thread variable structure.
Definition threadvars.h:58
@ LOGGER_JSON_STREAM
#define str(s)
@ TM_ECODE_FAILED
@ TM_ECODE_OK
const char * name
#define RB_FOREACH(x, name, head)
Definition tree.h:781
TCPVars tcp
Definition decode.h:478
#define SCLogDebug(...)
Definition util-debug.h:275
#define SCLogWarning(...)
Macro used to log WARNING messages.
Definition util-debug.h:255
#define SCFree(p)
Definition util-mem.h:61
#define SCCalloc(nm, sz)
Definition util-mem.h:53
#define unlikely(expr)