Coverage Report

Created: 2026-09-28 07:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata8/src/output-json.c
Line
Count
Source
1
/* Copyright (C) 2007-2023 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 Tom DeCanio <td@npulsetech.com>
22
 *
23
 * Logs detection and monitoring events in JSON format.
24
 *
25
 */
26
27
#include "suricata-common.h"
28
#include "flow.h"
29
#include "conf.h"
30
31
#include "util-debug.h"
32
#include "util-time.h"
33
#include "util-var-name.h"
34
#include "util-macset.h"
35
36
#include "util-unittest.h"
37
#include "util-unittest-helper.h"
38
39
#include "detect-engine.h"
40
#include "util-classification-config.h"
41
#include "util-syslog.h"
42
43
/* Internal output plugins */
44
#include "output-eve-syslog.h"
45
#include "output-eve-null.h"
46
47
#include "output.h"
48
#include "output-json.h"
49
50
#include "util-byte.h"
51
#include "util-print.h"
52
#include "util-proto-name.h"
53
#include "util-optimize.h"
54
#include "util-buffer.h"
55
#include "util-logopenfile.h"
56
#include "util-log-redis.h"
57
#include "util-device-private.h"
58
#include "util-validate.h"
59
60
#include "flow-var.h"
61
#include "flow-bit.h"
62
#include "flow-storage.h"
63
64
#include "source-pcap-file-helper.h"
65
66
4
#define DEFAULT_LOG_FILENAME "eve.json"
67
78
#define MODULE_NAME "OutputJSON"
68
69
#define MAX_JSON_SIZE 2048
70
71
static void OutputJsonDeInitCtx(OutputCtx *);
72
static void CreateEveCommunityFlowId(SCJsonBuilder *js, const Flow *f, const uint16_t seed);
73
static int CreateJSONEther(
74
        SCJsonBuilder *parent, const Packet *p, const Flow *f, enum SCOutputJsonLogDirection dir);
75
76
static const char *TRAFFIC_ID_PREFIX = "traffic/id/";
77
static const char *TRAFFIC_LABEL_PREFIX = "traffic/label/";
78
static size_t traffic_id_prefix_len = 0;
79
static size_t traffic_label_prefix_len = 0;
80
81
const JsonAddrInfo json_addr_info_zero;
82
83
void OutputJsonRegister (void)
84
78
{
85
78
    OutputRegisterModule(MODULE_NAME, "eve-log", OutputJsonInitCtx);
86
87
78
    traffic_id_prefix_len = strlen(TRAFFIC_ID_PREFIX);
88
78
    traffic_label_prefix_len = strlen(TRAFFIC_LABEL_PREFIX);
89
90
    // Register output file types that use the new eve filetype registration
91
    // API.
92
78
    SyslogInitialize();
93
78
    NullLogInitialize();
94
78
}
95
96
json_t *SCJsonString(const char *val)
97
0
{
98
0
    if (val == NULL){
99
0
        return NULL;
100
0
    }
101
0
    json_t * retval = json_string(val);
102
0
    char retbuf[MAX_JSON_SIZE] = {0};
103
0
    if (retval == NULL) {
104
0
        uint32_t u = 0;
105
0
        uint32_t offset = 0;
106
0
        for (u = 0; u < strlen(val); u++) {
107
0
            if (isprint(val[u])) {
108
0
                PrintBufferData(retbuf, &offset, MAX_JSON_SIZE-1, "%c",
109
0
                        val[u]);
110
0
            } else {
111
0
                PrintBufferData(retbuf, &offset, MAX_JSON_SIZE-1,
112
0
                        "\\x%02X", val[u]);
113
0
            }
114
0
        }
115
0
        retbuf[offset] = '\0';
116
0
        retval = json_string(retbuf);
117
0
    }
118
0
    return retval;
119
0
}
120
121
/* Default Sensor ID value */
122
static int64_t sensor_id = -1; /* -1 = not defined */
123
124
void EveFileInfo(SCJsonBuilder *jb, const File *ff, const uint64_t tx_id, const uint16_t flags)
125
172k
{
126
172k
    SCJbSetStringFromBytes(jb, "filename", ff->name, ff->name_len);
127
128
172k
    if (ff->sid_cnt > 0) {
129
32.5k
        SCJbOpenArray(jb, "sid");
130
96.6k
        for (uint32_t i = 0; ff->sid != NULL && i < ff->sid_cnt; i++) {
131
64.1k
            SCJbAppendUint(jb, ff->sid[i]);
132
64.1k
        }
133
32.5k
        SCJbClose(jb);
134
32.5k
    }
135
136
#ifdef HAVE_MAGIC
137
    if (ff->magic)
138
        SCJbSetString(jb, "magic", (char *)ff->magic);
139
#endif
140
172k
    SCJbSetBool(jb, "gaps", ff->flags & FILE_HAS_GAPS);
141
172k
    switch (ff->state) {
142
157k
        case FILE_STATE_CLOSED:
143
157k
            JB_SET_STRING(jb, "state", "CLOSED");
144
157k
            if (ff->flags & FILE_MD5) {
145
3.04k
                SCJbSetHex(jb, "md5", (uint8_t *)ff->md5, (uint32_t)sizeof(ff->md5));
146
3.04k
            }
147
157k
            if (ff->flags & FILE_SHA1) {
148
3.04k
                SCJbSetHex(jb, "sha1", (uint8_t *)ff->sha1, (uint32_t)sizeof(ff->sha1));
149
3.04k
            }
150
157k
            break;
151
3.12k
        case FILE_STATE_TRUNCATED:
152
3.12k
            JB_SET_STRING(jb, "state", "TRUNCATED");
153
3.12k
            break;
154
0
        case FILE_STATE_ERROR:
155
0
            JB_SET_STRING(jb, "state", "ERROR");
156
0
            break;
157
11.8k
        default:
158
11.8k
            JB_SET_STRING(jb, "state", "UNKNOWN");
159
11.8k
            break;
160
172k
    }
161
162
172k
    if (ff->flags & FILE_SHA256) {
163
160k
        SCJbSetHex(jb, "sha256", (uint8_t *)ff->sha256, (uint32_t)sizeof(ff->sha256));
164
160k
    }
165
166
172k
    if (flags & FILE_STORED) {
167
118k
        JB_SET_TRUE(jb, "stored");
168
118k
        SCJbSetUint(jb, "file_id", ff->file_store_id);
169
118k
    } else {
170
53.6k
        JB_SET_FALSE(jb, "stored");
171
53.6k
        if (flags & FILE_STORE) {
172
53.6k
            JB_SET_TRUE(jb, "storing");
173
53.6k
        }
174
53.6k
    }
175
176
172k
    SCJbSetUint(jb, "size", FileTrackedSize(ff));
177
172k
    if (ff->end > 0) {
178
18.8k
        SCJbSetUint(jb, "start", ff->start);
179
18.8k
        SCJbSetUint(jb, "end", ff->end);
180
18.8k
    }
181
172k
    SCJbSetUint(jb, "tx_id", tx_id);
182
172k
}
183
184
static void EveAddPacketVars(const Packet *p, SCJsonBuilder *js_vars)
185
0
{
186
0
    if (p == NULL || p->pktvar == NULL) {
187
0
        return;
188
0
    }
189
0
    PktVar *pv = p->pktvar;
190
0
    bool open = false;
191
0
    while (pv != NULL) {
192
0
        if (pv->key || pv->id > 0) {
193
0
            if (!open) {
194
0
                SCJbOpenArray(js_vars, "pktvars");
195
0
                open = true;
196
0
            }
197
0
            SCJbStartObject(js_vars);
198
199
0
            if (pv->key != NULL) {
200
0
                uint32_t offset = 0;
201
0
                uint8_t keybuf[pv->key_len + 1];
202
0
                PrintStringsToBuffer(keybuf, &offset, pv->key_len + 1, pv->key, pv->key_len);
203
0
                SCJbSetPrintAsciiString(js_vars, (char *)keybuf, pv->value, pv->value_len);
204
0
            } else {
205
0
                const char *varname = VarNameStoreLookupById(pv->id, VAR_TYPE_PKT_VAR);
206
0
                SCJbSetPrintAsciiString(js_vars, varname, pv->value, pv->value_len);
207
0
            }
208
0
            SCJbClose(js_vars);
209
0
        }
210
0
        pv = pv->next;
211
0
    }
212
0
    if (open) {
213
0
        SCJbClose(js_vars);
214
0
    }
215
0
}
216
217
/**
218
 * \brief Check if string s has prefix prefix.
219
 *
220
 * \retval true if string has prefix
221
 * \retval false if string does not have prefix
222
 *
223
 * TODO: Move to file with other string handling functions.
224
 */
225
static bool SCStringHasPrefix(const char *s, const char *prefix)
226
14.4k
{
227
14.4k
    if (strncmp(s, prefix, strlen(prefix)) == 0) {
228
36
        return true;
229
36
    }
230
14.4k
    return false;
231
14.4k
}
232
233
static void EveAddFlowVars(const Flow *f, SCJsonBuilder *js_root, SCJsonBuilder **js_traffic)
234
86.9k
{
235
86.9k
    if (f == NULL || f->flowvar == NULL) {
236
0
        return;
237
0
    }
238
86.9k
    SCJsonBuilder *js_flowvars = NULL;
239
86.9k
    SCJsonBuilder *js_traffic_id = NULL;
240
86.9k
    SCJsonBuilder *js_traffic_label = NULL;
241
86.9k
    SCJsonBuilder *js_flowints = NULL;
242
86.9k
    SCJsonBuilder *js_entropyvals = NULL;
243
86.9k
    SCJsonBuilder *js_flowbits = NULL;
244
86.9k
    GenericVar *gv = f->flowvar;
245
190k
    while (gv != NULL) {
246
103k
        if (gv->type == DETECT_FLOWVAR || gv->type == DETECT_FLOWINT) {
247
21.6k
            FlowVar *fv = (FlowVar *)gv;
248
21.6k
            if (fv->datatype == FLOWVAR_TYPE_STR && fv->key == NULL) {
249
73
                const char *varname = VarNameStoreLookupById(fv->idx,
250
73
                        VAR_TYPE_FLOW_VAR);
251
73
                if (varname) {
252
73
                    if (js_flowvars == NULL) {
253
65
                        js_flowvars = SCJbNewArray();
254
65
                        if (js_flowvars == NULL)
255
0
                            break;
256
65
                    }
257
258
73
                    SCJbStartObject(js_flowvars);
259
73
                    SCJbSetPrintAsciiString(
260
73
                            js_flowvars, varname, fv->data.fv_str.value, fv->data.fv_str.value_len);
261
73
                    SCJbClose(js_flowvars);
262
73
                }
263
21.5k
            } else if (fv->datatype == FLOWVAR_TYPE_STR && fv->key != NULL) {
264
0
                if (js_flowvars == NULL) {
265
0
                    js_flowvars = SCJbNewArray();
266
0
                    if (js_flowvars == NULL)
267
0
                        break;
268
0
                }
269
270
0
                uint8_t keybuf[fv->keylen + 1];
271
0
                uint32_t offset = 0;
272
0
                PrintStringsToBuffer(keybuf, &offset, fv->keylen + 1, fv->key, fv->keylen);
273
274
0
                SCJbStartObject(js_flowvars);
275
0
                SCJbSetPrintAsciiString(js_flowvars, (const char *)keybuf, fv->data.fv_str.value,
276
0
                        fv->data.fv_str.value_len);
277
0
                SCJbClose(js_flowvars);
278
21.5k
            } else if (fv->datatype == FLOWVAR_TYPE_FLOAT) {
279
9.10k
                const char *varname = VarNameStoreLookupById(fv->idx, VAR_TYPE_FLOW_FLOAT);
280
9.10k
                if (varname) {
281
9.10k
                    if (js_entropyvals == NULL) {
282
9.10k
                        js_entropyvals = SCJbNewObject();
283
9.10k
                        if (js_entropyvals == NULL)
284
0
                            break;
285
9.10k
                    }
286
9.10k
                    SCJbSetFloat(js_entropyvals, varname, fv->data.fv_float.value);
287
9.10k
                }
288
289
12.4k
            } else if (fv->datatype == FLOWVAR_TYPE_INT) {
290
12.4k
                const char *varname = VarNameStoreLookupById(fv->idx,
291
12.4k
                        VAR_TYPE_FLOW_INT);
292
12.4k
                if (varname) {
293
12.4k
                    if (js_flowints == NULL) {
294
11.9k
                        js_flowints = SCJbNewObject();
295
11.9k
                        if (js_flowints == NULL)
296
0
                            break;
297
11.9k
                    }
298
12.4k
                    SCJbSetUint(js_flowints, varname, fv->data.fv_int.value);
299
12.4k
                }
300
12.4k
            }
301
81.6k
        } else if (gv->type == DETECT_FLOWBITS) {
302
81.5k
            FlowBit *fb = (FlowBit *)gv;
303
81.5k
            const char *varname = VarNameStoreLookupById(fb->idx,
304
81.5k
                    VAR_TYPE_FLOW_BIT);
305
81.5k
            if (varname) {
306
80.4k
                if (SCStringHasPrefix(varname, TRAFFIC_ID_PREFIX)) {
307
779
                    if (js_traffic_id == NULL) {
308
500
                        js_traffic_id = SCJbNewArray();
309
500
                        if (unlikely(js_traffic_id == NULL)) {
310
0
                            break;
311
0
                        }
312
500
                    }
313
779
                    SCJbAppendString(js_traffic_id, &varname[traffic_id_prefix_len]);
314
79.6k
                } else if (SCStringHasPrefix(varname, TRAFFIC_LABEL_PREFIX)) {
315
1.35k
                    if (js_traffic_label == NULL) {
316
1.19k
                        js_traffic_label = SCJbNewArray();
317
1.19k
                        if (unlikely(js_traffic_label == NULL)) {
318
0
                            break;
319
0
                        }
320
1.19k
                    }
321
1.35k
                    SCJbAppendString(js_traffic_label, &varname[traffic_label_prefix_len]);
322
78.3k
                } else {
323
78.3k
                    if (js_flowbits == NULL) {
324
66.2k
                        js_flowbits = SCJbNewArray();
325
66.2k
                        if (unlikely(js_flowbits == NULL))
326
0
                            break;
327
66.2k
                    }
328
78.3k
                    SCJbAppendString(js_flowbits, varname);
329
78.3k
                }
330
80.4k
            }
331
81.5k
        }
332
103k
        gv = gv->next;
333
103k
    }
334
86.9k
    if (js_flowbits) {
335
66.2k
        SCJbClose(js_flowbits);
336
66.2k
        SCJbSetObject(js_root, "flowbits", js_flowbits);
337
66.2k
        SCJbFree(js_flowbits);
338
66.2k
    }
339
86.9k
    if (js_flowints) {
340
11.9k
        SCJbClose(js_flowints);
341
11.9k
        SCJbSetObject(js_root, "flowints", js_flowints);
342
11.9k
        SCJbFree(js_flowints);
343
11.9k
    }
344
86.9k
    if (js_entropyvals) {
345
9.10k
        SCJbClose(js_entropyvals);
346
9.10k
        SCJbSetObject(js_root, "entropy", js_entropyvals);
347
9.10k
        SCJbFree(js_entropyvals);
348
9.10k
    }
349
86.9k
    if (js_flowvars) {
350
65
        SCJbClose(js_flowvars);
351
65
        SCJbSetObject(js_root, "flowvars", js_flowvars);
352
65
        SCJbFree(js_flowvars);
353
65
    }
354
355
86.9k
    if (js_traffic_id != NULL || js_traffic_label != NULL) {
356
1.64k
        *js_traffic = SCJbNewObject();
357
1.64k
        if (likely(*js_traffic != NULL)) {
358
1.64k
            if (js_traffic_id != NULL) {
359
500
                SCJbClose(js_traffic_id);
360
500
                SCJbSetObject(*js_traffic, "id", js_traffic_id);
361
500
                SCJbFree(js_traffic_id);
362
500
            }
363
1.64k
            if (js_traffic_label != NULL) {
364
1.19k
                SCJbClose(js_traffic_label);
365
1.19k
                SCJbSetObject(*js_traffic, "label", js_traffic_label);
366
1.19k
                SCJbFree(js_traffic_label);
367
1.19k
            }
368
1.64k
            SCJbClose(*js_traffic);
369
1.64k
        }
370
1.64k
    }
371
86.9k
}
372
373
void EveAddMetadata(const Packet *p, const Flow *f, SCJsonBuilder *js)
374
16.8M
{
375
16.8M
    if ((p && p->pktvar) || (f && f->flowvar)) {
376
86.9k
        SCJsonBuilder *js_vars = SCJbNewObject();
377
86.9k
        if (js_vars) {
378
86.9k
            if (f && f->flowvar) {
379
86.9k
                SCJsonBuilder *js_traffic = NULL;
380
86.9k
                EveAddFlowVars(f, js_vars, &js_traffic);
381
86.9k
                if (js_traffic != NULL) {
382
1.64k
                    SCJbSetObject(js, "traffic", js_traffic);
383
1.64k
                    SCJbFree(js_traffic);
384
1.64k
                }
385
86.9k
            }
386
86.9k
            if (p && p->pktvar) {
387
17
                EveAddPacketVars(p, js_vars);
388
17
            }
389
86.9k
            SCJbClose(js_vars);
390
86.9k
            SCJbSetObject(js, "metadata", js_vars);
391
86.9k
            SCJbFree(js_vars);
392
86.9k
        }
393
86.9k
    }
394
16.8M
}
395
396
void EveAddCommonOptions(const OutputJsonCommonSettings *cfg, const Packet *p, const Flow *f,
397
        SCJsonBuilder *js, enum SCOutputJsonLogDirection dir)
398
16.8M
{
399
16.8M
    if (cfg->include_suricata_version) {
400
0
        SCJbSetString(js, "suricata_version", PROG_VER);
401
0
    }
402
16.8M
    if (cfg->include_metadata) {
403
16.8M
        EveAddMetadata(p, f, js);
404
16.8M
    }
405
16.8M
    if (cfg->include_ethernet) {
406
0
        CreateJSONEther(js, p, f, dir);
407
0
    }
408
16.8M
    if (cfg->include_community_id && f != NULL) {
409
0
        CreateEveCommunityFlowId(js, f, cfg->community_id_seed);
410
0
    }
411
16.8M
    if (f != NULL && f->tenant_id > 0) {
412
0
        SCJbSetUint(js, "tenant_id", f->tenant_id);
413
0
    }
414
16.8M
}
415
416
/**
417
 * \brief Jsonify a packet
418
 *
419
 * \param p Packet
420
 * \param js JSON object
421
 * \param max_length If non-zero, restricts the number of packet data bytes handled.
422
 */
423
void EvePacket(const Packet *p, SCJsonBuilder *js, uint32_t max_length)
424
13.8M
{
425
13.8M
    uint32_t max_len = max_length == 0 ? GET_PKT_LEN(p) : max_length;
426
13.8M
    SCJbSetBase64(js, "packet", GET_PKT_DATA(p), max_len);
427
428
13.8M
    if (!SCJbOpenObject(js, "packet_info")) {
429
0
        return;
430
0
    }
431
13.8M
    if (!SCJbSetUint(js, "linktype", p->datalink)) {
432
0
        SCJbClose(js);
433
0
        return;
434
0
    }
435
436
13.8M
    const char *dl_name = DatalinkValueToName(p->datalink);
437
438
    // Intentionally ignore the return value from SCJbSetString and proceed
439
    // so the jb object is closed
440
13.8M
    (void)SCJbSetString(js, "linktype_name", dl_name == NULL ? "n/a" : dl_name);
441
442
13.8M
    SCJbClose(js);
443
13.8M
}
444
445
/** \brief jsonify tcp flags field
446
 *  Only add 'true' fields in an attempt to keep things reasonably compact.
447
 */
448
void EveTcpFlags(const uint8_t flags, SCJsonBuilder *js)
449
403k
{
450
403k
    if (flags & TH_SYN)
451
231k
        JB_SET_TRUE(js, "syn");
452
403k
    if (flags & TH_FIN)
453
212k
        JB_SET_TRUE(js, "fin");
454
403k
    if (flags & TH_RST)
455
125k
        JB_SET_TRUE(js, "rst");
456
403k
    if (flags & TH_PUSH)
457
291k
        JB_SET_TRUE(js, "psh");
458
403k
    if (flags & TH_ACK)
459
360k
        JB_SET_TRUE(js, "ack");
460
403k
    if (flags & TH_URG)
461
129k
        JB_SET_TRUE(js, "urg");
462
403k
    if (flags & TH_ECN)
463
143k
        JB_SET_TRUE(js, "ecn");
464
403k
    if (flags & TH_CWR)
465
58.6k
        JB_SET_TRUE(js, "cwr");
466
403k
}
467
468
void JsonAddrInfoInit(const Packet *p, enum SCOutputJsonLogDirection dir, JsonAddrInfo *addr)
469
7.45M
{
470
7.45M
    char srcip[46] = {0}, dstip[46] = {0};
471
7.45M
    Port sp, dp;
472
473
7.45M
    switch (dir) {
474
7.41M
        case LOG_DIR_PACKET:
475
7.41M
            if (PacketIsIPv4(p)) {
476
432k
                PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p),
477
432k
                        srcip, sizeof(srcip));
478
432k
                PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p),
479
432k
                        dstip, sizeof(dstip));
480
6.97M
            } else if (PacketIsIPv6(p)) {
481
6.27M
                PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p),
482
6.27M
                        srcip, sizeof(srcip));
483
6.27M
                PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p),
484
6.27M
                        dstip, sizeof(dstip));
485
6.27M
            } else {
486
                /* Not an IP packet so don't do anything */
487
707k
                return;
488
707k
            }
489
6.70M
            sp = p->sp;
490
6.70M
            dp = p->dp;
491
6.70M
            break;
492
38.0k
        case LOG_DIR_FLOW:
493
38.0k
        case LOG_DIR_FLOW_TOSERVER:
494
38.0k
            if ((PKT_IS_TOSERVER(p))) {
495
27.4k
                if (PacketIsIPv4(p)) {
496
27.4k
                    PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p),
497
27.4k
                            srcip, sizeof(srcip));
498
27.4k
                    PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p),
499
27.4k
                            dstip, sizeof(dstip));
500
27.4k
                } else if (PacketIsIPv6(p)) {
501
0
                    PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p),
502
0
                            srcip, sizeof(srcip));
503
0
                    PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p),
504
0
                            dstip, sizeof(dstip));
505
0
                }
506
27.4k
                sp = p->sp;
507
27.4k
                dp = p->dp;
508
27.4k
            } else {
509
10.6k
                if (PacketIsIPv4(p)) {
510
10.6k
                    PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p),
511
10.6k
                            srcip, sizeof(srcip));
512
10.6k
                    PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p),
513
10.6k
                            dstip, sizeof(dstip));
514
10.6k
                } else if (PacketIsIPv6(p)) {
515
52
                    PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p),
516
52
                            srcip, sizeof(srcip));
517
52
                    PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p),
518
52
                            dstip, sizeof(dstip));
519
52
                }
520
10.6k
                sp = p->dp;
521
10.6k
                dp = p->sp;
522
10.6k
            }
523
38.0k
            break;
524
2.83k
        case LOG_DIR_FLOW_TOCLIENT:
525
2.83k
            if ((PKT_IS_TOCLIENT(p))) {
526
2.53k
                if (PacketIsIPv4(p)) {
527
2.53k
                    PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p),
528
2.53k
                            srcip, sizeof(srcip));
529
2.53k
                    PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p),
530
2.53k
                            dstip, sizeof(dstip));
531
2.53k
                } else if (PacketIsIPv6(p)) {
532
0
                    PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p),
533
0
                            srcip, sizeof(srcip));
534
0
                    PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p),
535
0
                            dstip, sizeof(dstip));
536
0
                }
537
2.53k
                sp = p->sp;
538
2.53k
                dp = p->dp;
539
2.53k
            } else {
540
292
                if (PacketIsIPv4(p)) {
541
292
                    PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p),
542
292
                            srcip, sizeof(srcip));
543
292
                    PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p),
544
292
                            dstip, sizeof(dstip));
545
292
                } else if (PacketIsIPv6(p)) {
546
0
                    PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p),
547
0
                            srcip, sizeof(srcip));
548
0
                    PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p),
549
0
                            dstip, sizeof(dstip));
550
0
                }
551
292
                sp = p->dp;
552
292
                dp = p->sp;
553
292
            }
554
2.83k
            break;
555
0
        default:
556
0
            DEBUG_VALIDATE_BUG_ON(1);
557
0
            return;
558
7.45M
    }
559
560
6.74M
    strlcpy(addr->src_ip, srcip, JSON_ADDR_LEN);
561
6.74M
    strlcpy(addr->dst_ip, dstip, JSON_ADDR_LEN);
562
563
6.74M
    switch (p->proto) {
564
3.59k
        case IPPROTO_UDP:
565
6.40M
        case IPPROTO_TCP:
566
6.41M
        case IPPROTO_SCTP:
567
6.41M
            addr->sp = sp;
568
6.41M
            addr->dp = dp;
569
6.41M
            addr->log_port = true;
570
6.41M
            break;
571
335k
        default:
572
335k
            addr->log_port = false;
573
335k
            break;
574
6.74M
    }
575
576
6.74M
    if (SCProtoNameValid(PacketGetIPProto(p))) {
577
6.69M
        strlcpy(addr->proto, known_proto[PacketGetIPProto(p)], sizeof(addr->proto));
578
6.69M
    } else {
579
48.3k
        snprintf(addr->proto, sizeof(addr->proto), "%" PRIu32, PacketGetIPProto(p));
580
48.3k
    }
581
6.74M
}
582
583
0
#define COMMUNITY_ID_BUF_SIZE 64
584
585
static bool CalculateCommunityFlowIdv4(const Flow *f,
586
        const uint16_t seed, unsigned char *base64buf)
587
0
{
588
0
    struct {
589
0
        uint16_t seed;
590
0
        uint32_t src;
591
0
        uint32_t dst;
592
0
        uint8_t proto;
593
0
        uint8_t pad0;
594
0
        uint16_t sp;
595
0
        uint16_t dp;
596
0
    } __attribute__((__packed__)) ipv4;
597
598
0
    uint32_t src = f->src.addr_data32[0];
599
0
    uint32_t dst = f->dst.addr_data32[0];
600
0
    uint16_t sp = f->sp;
601
0
    if (f->proto == IPPROTO_ICMP)
602
0
        sp = f->icmp_s.type;
603
0
    sp = htons(sp);
604
0
    uint16_t dp = f->dp;
605
0
    if (f->proto == IPPROTO_ICMP)
606
0
        dp = f->icmp_d.type;
607
0
    dp = htons(dp);
608
609
0
    ipv4.seed = htons(seed);
610
0
    if (ntohl(src) < ntohl(dst) || (src == dst && ntohs(sp) < ntohs(dp))) {
611
0
        ipv4.src = src;
612
0
        ipv4.dst = dst;
613
0
        ipv4.sp = sp;
614
0
        ipv4.dp = dp;
615
0
    } else {
616
0
        ipv4.src = dst;
617
0
        ipv4.dst = src;
618
0
        ipv4.sp = dp;
619
0
        ipv4.dp = sp;
620
0
    }
621
0
    ipv4.proto = f->proto;
622
0
    ipv4.pad0 = 0;
623
624
0
    uint8_t hash[20];
625
0
    if (SCSha1HashBuffer((const uint8_t *)&ipv4, sizeof(ipv4), hash, sizeof(hash)) == 1) {
626
0
        strlcpy((char *)base64buf, "1:", COMMUNITY_ID_BUF_SIZE);
627
0
        unsigned long out_len = COMMUNITY_ID_BUF_SIZE - 2;
628
0
        if (SCBase64Encode(hash, sizeof(hash), base64buf + 2, &out_len) == SC_BASE64_OK) {
629
0
            return true;
630
0
        }
631
0
    }
632
0
    return false;
633
0
}
634
635
static bool CalculateCommunityFlowIdv6(const Flow *f,
636
        const uint16_t seed, unsigned char *base64buf)
637
0
{
638
0
    struct {
639
0
        uint16_t seed;
640
0
        uint32_t src[4];
641
0
        uint32_t dst[4];
642
0
        uint8_t proto;
643
0
        uint8_t pad0;
644
0
        uint16_t sp;
645
0
        uint16_t dp;
646
0
    } __attribute__((__packed__)) ipv6;
647
648
0
    uint16_t sp = f->sp;
649
0
    if (f->proto == IPPROTO_ICMPV6)
650
0
        sp = f->icmp_s.type;
651
0
    sp = htons(sp);
652
0
    uint16_t dp = f->dp;
653
0
    if (f->proto == IPPROTO_ICMPV6)
654
0
        dp = f->icmp_d.type;
655
0
    dp = htons(dp);
656
657
0
    ipv6.seed = htons(seed);
658
0
    int cmp_r = memcmp(&f->src, &f->dst, sizeof(f->src));
659
0
    if ((cmp_r < 0) || (cmp_r == 0 && ntohs(sp) < ntohs(dp))) {
660
0
        memcpy(&ipv6.src, &f->src.addr_data32, 16);
661
0
        memcpy(&ipv6.dst, &f->dst.addr_data32, 16);
662
0
        ipv6.sp = sp;
663
0
        ipv6.dp = dp;
664
0
    } else {
665
0
        memcpy(&ipv6.src, &f->dst.addr_data32, 16);
666
0
        memcpy(&ipv6.dst, &f->src.addr_data32, 16);
667
0
        ipv6.sp = dp;
668
0
        ipv6.dp = sp;
669
0
    }
670
0
    ipv6.proto = f->proto;
671
0
    ipv6.pad0 = 0;
672
673
0
    uint8_t hash[20];
674
0
    if (SCSha1HashBuffer((const uint8_t *)&ipv6, sizeof(ipv6), hash, sizeof(hash)) == 1) {
675
0
        strlcpy((char *)base64buf, "1:", COMMUNITY_ID_BUF_SIZE);
676
0
        unsigned long out_len = COMMUNITY_ID_BUF_SIZE - 2;
677
0
        if (SCBase64Encode(hash, sizeof(hash), base64buf + 2, &out_len) == SC_BASE64_OK) {
678
0
            return true;
679
0
        }
680
0
    }
681
0
    return false;
682
0
}
683
684
static void CreateEveCommunityFlowId(SCJsonBuilder *js, const Flow *f, const uint16_t seed)
685
0
{
686
0
    unsigned char buf[COMMUNITY_ID_BUF_SIZE];
687
0
    if (f->flags & FLOW_IPV4) {
688
0
        if (CalculateCommunityFlowIdv4(f, seed, buf)) {
689
0
            SCJbSetString(js, "community_id", (const char *)buf);
690
0
        }
691
0
    } else if (f->flags & FLOW_IPV6) {
692
0
        if (CalculateCommunityFlowIdv6(f, seed, buf)) {
693
0
            SCJbSetString(js, "community_id", (const char *)buf);
694
0
        }
695
0
    }
696
0
}
697
698
void CreateEveFlowId(SCJsonBuilder *js, const Flow *f)
699
16.8M
{
700
16.8M
    if (f == NULL) {
701
4.12M
        return;
702
4.12M
    }
703
12.7M
    uint64_t flow_id = FlowGetId(f);
704
12.7M
    SCJbSetUint(js, "flow_id", flow_id);
705
12.7M
    if (f->parent_id) {
706
2.14k
        SCJbSetUint(js, "parent_id", f->parent_id);
707
2.14k
    }
708
12.7M
}
709
710
void JSONFormatAndAddMACAddr(SCJsonBuilder *js, const char *key, const uint8_t *val, bool is_array)
711
0
{
712
0
    char eth_addr[19];
713
0
    (void) snprintf(eth_addr, 19, "%02x:%02x:%02x:%02x:%02x:%02x",
714
0
                    val[0], val[1], val[2], val[3], val[4], val[5]);
715
0
    if (is_array) {
716
0
        SCJbAppendString(js, eth_addr);
717
0
    } else {
718
0
        SCJbSetString(js, key, eth_addr);
719
0
    }
720
0
}
721
722
/* only required to traverse the MAC address set */
723
typedef struct JSONMACAddrInfo {
724
    SCJsonBuilder *src, *dst;
725
} JSONMACAddrInfo;
726
727
static int MacSetIterateToJSON(uint8_t *val, MacSetSide side, void *data)
728
0
{
729
0
    JSONMACAddrInfo *info = (JSONMACAddrInfo*) data;
730
0
    if (side == MAC_SET_DST) {
731
0
        JSONFormatAndAddMACAddr(info->dst, NULL, val, true);
732
0
    } else {
733
0
        JSONFormatAndAddMACAddr(info->src, NULL, val, true);
734
0
    }
735
0
    return 0;
736
0
}
737
738
static int CreateJSONEther(
739
        SCJsonBuilder *js, const Packet *p, const Flow *f, enum SCOutputJsonLogDirection dir)
740
0
{
741
0
    if (p != NULL) {
742
        /* this is a packet context, so we need to add scalar fields */
743
0
        if (PacketIsEthernet(p)) {
744
0
            const EthernetHdr *ethh = PacketGetEthernet(p);
745
0
            SCJbOpenObject(js, "ether");
746
0
            SCJbSetUint(js, "ether_type", SCNtohs(ethh->eth_type));
747
0
            const uint8_t *src;
748
0
            const uint8_t *dst;
749
0
            switch (dir) {
750
0
                case LOG_DIR_FLOW_TOSERVER:
751
                    // fallthrough
752
0
                case LOG_DIR_FLOW:
753
0
                    if (PKT_IS_TOCLIENT(p)) {
754
0
                        src = ethh->eth_dst;
755
0
                        dst = ethh->eth_src;
756
0
                    } else {
757
0
                        src = ethh->eth_src;
758
0
                        dst = ethh->eth_dst;
759
0
                    }
760
0
                    break;
761
0
                case LOG_DIR_FLOW_TOCLIENT:
762
0
                    if (PKT_IS_TOSERVER(p)) {
763
0
                        src = ethh->eth_dst;
764
0
                        dst = ethh->eth_src;
765
0
                    } else {
766
0
                        src = ethh->eth_src;
767
0
                        dst = ethh->eth_dst;
768
0
                    }
769
0
                    break;
770
0
                case LOG_DIR_PACKET:
771
0
                default:
772
0
                    src = ethh->eth_src;
773
0
                    dst = ethh->eth_dst;
774
0
                    break;
775
0
            }
776
0
            JSONFormatAndAddMACAddr(js, "src_mac", src, false);
777
0
            JSONFormatAndAddMACAddr(js, "dest_mac", dst, false);
778
0
            SCJbClose(js);
779
0
        } else if (f != NULL) {
780
            /* When pseudopackets do not have associated ethernet metadata,
781
               use the first set of mac addresses stored with their flow.
782
               The first set of macs should come from the flow's first packet,
783
               providing the most fitting representation of the event's ethernet. */
784
0
            MacSet *ms = FlowGetStorageById(f, MacSetGetFlowStorageID());
785
0
            if (ms != NULL && MacSetSize(ms) > 0) {
786
0
                uint8_t *src = MacSetGetFirst(ms, MAC_SET_SRC);
787
0
                uint8_t *dst = MacSetGetFirst(ms, MAC_SET_DST);
788
0
                if (dst != NULL && src != NULL) {
789
0
                    SCJbOpenObject(js, "ether");
790
0
                    JSONFormatAndAddMACAddr(js, "src_mac", src, false);
791
0
                    JSONFormatAndAddMACAddr(js, "dest_mac", dst, false);
792
0
                    SCJbClose(js);
793
0
                }
794
0
            }
795
0
        }
796
0
    } else if (f != NULL) {
797
        /* we are creating an ether object in a flow context, so we need to
798
           append to arrays */
799
0
        MacSet *ms = FlowGetStorageById(f, MacSetGetFlowStorageID());
800
0
        if (ms != NULL && MacSetSize(ms) > 0) {
801
0
            SCJbOpenObject(js, "ether");
802
0
            JSONMACAddrInfo info;
803
0
            info.dst = SCJbNewArray();
804
0
            info.src = SCJbNewArray();
805
0
            int ret = MacSetForEach(ms, MacSetIterateToJSON, &info);
806
0
            if (unlikely(ret != 0)) {
807
                /* should not happen, JSONFlowAppendMACAddrs is sane */
808
0
                SCJbFree(info.dst);
809
0
                SCJbFree(info.src);
810
0
                SCJbClose(js);
811
0
                return ret;
812
0
            }
813
0
            SCJbClose(info.dst);
814
0
            SCJbClose(info.src);
815
            /* case is handling netflow too so may need to revert */
816
0
            if (dir == LOG_DIR_FLOW_TOCLIENT) {
817
0
                SCJbSetObject(js, "dest_macs", info.src);
818
0
                SCJbSetObject(js, "src_macs", info.dst);
819
0
            } else {
820
0
                DEBUG_VALIDATE_BUG_ON(dir != LOG_DIR_FLOW_TOSERVER && dir != LOG_DIR_FLOW);
821
0
                SCJbSetObject(js, "dest_macs", info.dst);
822
0
                SCJbSetObject(js, "src_macs", info.src);
823
0
            }
824
0
            SCJbFree(info.dst);
825
0
            SCJbFree(info.src);
826
0
            SCJbClose(js);
827
0
        }
828
0
    }
829
0
    return 0;
830
0
}
831
832
SCJsonBuilder *CreateEveHeader(const Packet *p, enum SCOutputJsonLogDirection dir,
833
        const char *event_type, JsonAddrInfo *addr, OutputJsonCtx *eve_ctx)
834
7.45M
{
835
7.45M
    char timebuf[64];
836
7.45M
    const Flow *f = (const Flow *)p->flow;
837
838
7.45M
    SCJsonBuilder *js = SCJbNewObject();
839
7.45M
    if (unlikely(js == NULL)) {
840
0
        return NULL;
841
0
    }
842
843
7.45M
    CreateIsoTimeString(p->ts, timebuf, sizeof(timebuf));
844
845
7.45M
    SCJbSetString(js, "timestamp", timebuf);
846
847
7.45M
    CreateEveFlowId(js, f);
848
849
    /* sensor id */
850
7.45M
    if (sensor_id >= 0) {
851
0
        SCJbSetUint(js, "sensor_id", sensor_id);
852
0
    }
853
854
    /* input interface */
855
7.45M
    if (p->livedev) {
856
0
        SCJbSetString(js, "in_iface", p->livedev->dev);
857
0
    }
858
859
    /* pcap_cnt */
860
7.45M
    if (p->pcap_cnt != 0) {
861
7.43M
        SCJbSetUint(js, "pcap_cnt", p->pcap_cnt);
862
7.43M
    }
863
864
7.45M
    if (event_type) {
865
7.45M
        SCJbSetString(js, "event_type", event_type);
866
7.45M
    }
867
868
    /* vlan */
869
7.45M
    if (p->vlan_idx > 0) {
870
649
        SCJbOpenArray(js, "vlan");
871
649
        SCJbAppendUint(js, p->vlan_id[0]);
872
649
        if (p->vlan_idx > 1) {
873
2
            SCJbAppendUint(js, p->vlan_id[1]);
874
2
        }
875
649
        if (p->vlan_idx > 2) {
876
0
            SCJbAppendUint(js, p->vlan_id[2]);
877
0
        }
878
649
        SCJbClose(js);
879
649
    }
880
881
    /* 5-tuple */
882
7.45M
    JsonAddrInfo addr_info = json_addr_info_zero;
883
7.45M
    if (addr == NULL) {
884
7.41M
        JsonAddrInfoInit(p, dir, &addr_info);
885
7.41M
        addr = &addr_info;
886
7.41M
    }
887
7.45M
    if (addr->src_ip[0] != '\0') {
888
6.74M
        SCJbSetString(js, "src_ip", addr->src_ip);
889
6.74M
    }
890
7.45M
    if (addr->log_port) {
891
6.41M
        SCJbSetUint(js, "src_port", addr->sp);
892
6.41M
    }
893
7.45M
    if (addr->dst_ip[0] != '\0') {
894
6.74M
        SCJbSetString(js, "dest_ip", addr->dst_ip);
895
6.74M
    }
896
7.45M
    if (addr->log_port) {
897
6.41M
        SCJbSetUint(js, "dest_port", addr->dp);
898
6.41M
    }
899
7.45M
    if (addr->proto[0] != '\0') {
900
6.74M
        SCJbSetString(js, "proto", addr->proto);
901
6.74M
    }
902
903
    /* ip version */
904
7.45M
    if (PacketIsIPv4(p)) {
905
473k
        SCJbSetUint(js, "ip_v", 4);
906
6.97M
    } else if (PacketIsIPv6(p)) {
907
6.27M
        SCJbSetUint(js, "ip_v", 6);
908
6.27M
    }
909
910
    /* icmp */
911
7.45M
    switch (p->proto) {
912
3.06k
        case IPPROTO_ICMP:
913
3.06k
            if (PacketIsICMPv4(p)) {
914
2.90k
                SCJbSetUint(js, "icmp_type", p->icmp_s.type);
915
2.90k
                SCJbSetUint(js, "icmp_code", p->icmp_s.code);
916
2.90k
            }
917
3.06k
            break;
918
89.9k
        case IPPROTO_ICMPV6:
919
89.9k
            if (PacketIsICMPv6(p)) {
920
89.6k
                SCJbSetUint(js, "icmp_type", PacketGetICMPv6(p)->type);
921
89.6k
                SCJbSetUint(js, "icmp_code", PacketGetICMPv6(p)->code);
922
89.6k
            }
923
89.9k
            break;
924
7.45M
    }
925
926
7.45M
    SCJbSetString(js, "pkt_src", PktSrcToString(p->pkt_src));
927
928
7.45M
    if (eve_ctx != NULL) {
929
7.45M
        EveAddCommonOptions(&eve_ctx->cfg, p, f, js, dir);
930
7.45M
    }
931
932
7.45M
    return js;
933
7.45M
}
934
935
SCJsonBuilder *CreateEveHeaderWithTxId(const Packet *p, enum SCOutputJsonLogDirection dir,
936
        const char *event_type, JsonAddrInfo *addr, uint64_t tx_id, OutputJsonCtx *eve_ctx)
937
1.85M
{
938
1.85M
    SCJsonBuilder *js = CreateEveHeader(p, dir, event_type, addr, eve_ctx);
939
1.85M
    if (unlikely(js == NULL))
940
0
        return NULL;
941
942
    /* tx id for correlation with other events */
943
1.85M
    SCJbSetUint(js, "tx_id", tx_id);
944
945
1.85M
    return js;
946
1.85M
}
947
948
int OutputJSONMemBufferCallback(const char *str, size_t size, void *data)
949
0
{
950
0
    OutputJSONMemBufferWrapper *wrapper = data;
951
0
    MemBuffer **memb = wrapper->buffer;
952
953
0
    if (MEMBUFFER_OFFSET(*memb) + size >= MEMBUFFER_SIZE(*memb)) {
954
0
        MemBufferExpand(memb, wrapper->expand_by);
955
0
    }
956
957
0
    DEBUG_VALIDATE_BUG_ON(size > UINT32_MAX);
958
0
    MemBufferWriteRaw((*memb), (const uint8_t *)str, (uint32_t)size);
959
0
    return 0;
960
0
}
961
962
int OutputJSONBuffer(json_t *js, LogFileCtx *file_ctx, MemBuffer **buffer)
963
0
{
964
0
    if (file_ctx->sensor_name) {
965
0
        json_object_set_new(js, "host",
966
0
                            json_string(file_ctx->sensor_name));
967
0
    }
968
969
0
    if (file_ctx->is_pcap_offline) {
970
0
        json_object_set_new(js, "pcap_filename", json_string(PcapFileGetFilename()));
971
0
    }
972
973
0
    if (file_ctx->prefix) {
974
0
        MemBufferWriteRaw((*buffer), (const uint8_t *)file_ctx->prefix, file_ctx->prefix_len);
975
0
    }
976
977
0
    OutputJSONMemBufferWrapper wrapper = {
978
0
        .buffer = buffer,
979
0
        .expand_by = JSON_OUTPUT_BUFFER_SIZE
980
0
    };
981
982
0
    int r = json_dump_callback(js, OutputJSONMemBufferCallback, &wrapper,
983
0
            file_ctx->json_flags);
984
0
    if (r != 0)
985
0
        return TM_ECODE_OK;
986
987
0
    LogFileWrite(file_ctx, *buffer);
988
0
    return 0;
989
0
}
990
991
void OutputJsonBuilderBuffer(
992
        ThreadVars *tv, const Packet *p, Flow *f, SCJsonBuilder *js, OutputJsonThreadCtx *ctx)
993
16.8M
{
994
16.8M
    LogFileCtx *file_ctx = ctx->file_ctx;
995
16.8M
    MemBuffer **buffer = &ctx->buffer;
996
16.8M
    if (file_ctx->sensor_name) {
997
0
        SCJbSetString(js, "host", file_ctx->sensor_name);
998
0
    }
999
1000
16.8M
    if (file_ctx->is_pcap_offline) {
1001
0
        SCJbSetString(js, "pcap_filename", PcapFileGetFilename());
1002
0
    }
1003
1004
16.8M
    SCEveRunCallbacks(tv, p, f, js);
1005
1006
16.8M
    SCJbClose(js);
1007
1008
16.8M
    MemBufferReset(*buffer);
1009
1010
16.8M
    if (file_ctx->prefix) {
1011
0
        MemBufferWriteRaw((*buffer), (const uint8_t *)file_ctx->prefix, file_ctx->prefix_len);
1012
0
    }
1013
1014
16.8M
    size_t jslen = SCJbLen(js);
1015
16.8M
    DEBUG_VALIDATE_BUG_ON(SCJbLen(js) > UINT32_MAX);
1016
16.8M
    size_t remaining = MEMBUFFER_SIZE(*buffer) - MEMBUFFER_OFFSET(*buffer);
1017
16.8M
    if (jslen >= remaining) {
1018
77
        size_t expand_by = jslen + 1 - remaining;
1019
77
        if (MemBufferExpand(buffer, (uint32_t)expand_by) < 0) {
1020
0
            if (!ctx->too_large_warning) {
1021
                /* Log a warning once, and include enough of the log
1022
                 * message to hopefully identify the event_type. */
1023
0
                char partial[120];
1024
0
                size_t partial_len = MIN(sizeof(partial), jslen);
1025
0
                memcpy(partial, SCJbPtr(js), partial_len - 1);
1026
0
                partial[partial_len - 1] = '\0';
1027
0
                SCLogWarning("Formatted JSON EVE record too large, will be dropped: %s", partial);
1028
0
                ctx->too_large_warning = true;
1029
0
            }
1030
0
            return;
1031
0
        }
1032
77
    }
1033
1034
16.8M
    MemBufferWriteRaw((*buffer), SCJbPtr(js), (uint32_t)jslen);
1035
16.8M
    LogFileWrite(file_ctx, *buffer);
1036
16.8M
}
1037
1038
static inline enum LogFileType FileTypeFromConf(const char *typestr)
1039
4
{
1040
4
    enum LogFileType log_filetype = LOGFILE_TYPE_NOTSET;
1041
1042
4
    if (typestr == NULL) {
1043
0
        log_filetype = LOGFILE_TYPE_FILE;
1044
4
    } else if (strcmp(typestr, "file") == 0 || strcmp(typestr, "regular") == 0) {
1045
4
        log_filetype = LOGFILE_TYPE_FILE;
1046
4
    } else if (strcmp(typestr, "unix_dgram") == 0) {
1047
0
        log_filetype = LOGFILE_TYPE_UNIX_DGRAM;
1048
0
    } else if (strcmp(typestr, "unix_stream") == 0) {
1049
0
        log_filetype = LOGFILE_TYPE_UNIX_STREAM;
1050
0
    } else if (strcmp(typestr, "redis") == 0) {
1051
#ifdef HAVE_LIBHIREDIS
1052
        log_filetype = LOGFILE_TYPE_REDIS;
1053
#else
1054
0
        FatalError("redis JSON output option is not compiled");
1055
0
#endif
1056
0
    }
1057
4
    SCLogDebug("type %s, file type value %d", typestr, log_filetype);
1058
4
    return log_filetype;
1059
4
}
1060
1061
static int LogFileTypePrepare(
1062
        OutputJsonCtx *json_ctx, enum LogFileType log_filetype, SCConfNode *conf)
1063
4
{
1064
1065
4
    if (log_filetype == LOGFILE_TYPE_FILE || log_filetype == LOGFILE_TYPE_UNIX_DGRAM ||
1066
4
            log_filetype == LOGFILE_TYPE_UNIX_STREAM) {
1067
4
        if (SCConfLogOpenGeneric(conf, json_ctx->file_ctx, DEFAULT_LOG_FILENAME, 1) < 0) {
1068
0
            return -1;
1069
0
        }
1070
4
    }
1071
#ifdef HAVE_LIBHIREDIS
1072
    else if (log_filetype == LOGFILE_TYPE_REDIS) {
1073
        SCLogRedisInit();
1074
        SCConfNode *redis_node = SCConfNodeLookupChild(conf, "redis");
1075
        if (!json_ctx->file_ctx->sensor_name) {
1076
            char hostname[1024];
1077
            gethostname(hostname, 1023);
1078
            json_ctx->file_ctx->sensor_name = SCStrdup(hostname);
1079
        }
1080
        if (json_ctx->file_ctx->sensor_name == NULL) {
1081
            return -1;
1082
        }
1083
1084
        if (SCConfLogOpenRedis(redis_node, json_ctx->file_ctx) < 0) {
1085
            return -1;
1086
        }
1087
    }
1088
#endif
1089
0
    else if (log_filetype == LOGFILE_TYPE_FILETYPE) {
1090
0
        if (json_ctx->file_ctx->threaded) {
1091
            /* Prepare for threaded log output. */
1092
0
            if (!SCLogOpenThreadedFile(NULL, NULL, json_ctx->file_ctx)) {
1093
0
                return -1;
1094
0
            }
1095
0
        }
1096
0
        if (json_ctx->filetype->Init(conf, json_ctx->file_ctx->threaded,
1097
0
                    &json_ctx->file_ctx->filetype.init_data) < 0) {
1098
0
            return -1;
1099
0
        }
1100
0
        if (json_ctx->filetype->ThreadInit) {
1101
0
            if (json_ctx->filetype->ThreadInit(json_ctx->file_ctx->filetype.init_data, 0,
1102
0
                        &json_ctx->file_ctx->filetype.thread_data) < 0) {
1103
0
                return -1;
1104
0
            }
1105
0
        }
1106
0
        json_ctx->file_ctx->filetype.filetype = json_ctx->filetype;
1107
0
    }
1108
1109
4
    return 0;
1110
4
}
1111
1112
/**
1113
 * \brief Create a new LogFileCtx for "fast" output style.
1114
 * \param conf The configuration node for this output.
1115
 * \return A LogFileCtx pointer on success, NULL on failure.
1116
 */
1117
OutputInitResult OutputJsonInitCtx(SCConfNode *conf)
1118
2
{
1119
2
    OutputInitResult result = { NULL, false };
1120
2
    OutputCtx *output_ctx = NULL;
1121
1122
2
    OutputJsonCtx *json_ctx = SCCalloc(1, sizeof(OutputJsonCtx));
1123
2
    if (unlikely(json_ctx == NULL)) {
1124
0
        SCLogDebug("could not create new OutputJsonCtx");
1125
0
        return result;
1126
0
    }
1127
1128
    /* First lookup a sensor-name value in this outputs configuration
1129
     * node (deprecated). If that fails, lookup the global one. */
1130
2
    const char *sensor_name = SCConfNodeLookupChildValue(conf, "sensor-name");
1131
2
    if (sensor_name != NULL) {
1132
0
        SCLogWarning("Found deprecated eve-log setting \"sensor-name\". "
1133
0
                     "Please set sensor-name globally.");
1134
0
    }
1135
2
    else {
1136
2
        (void)SCConfGet("sensor-name", &sensor_name);
1137
2
    }
1138
1139
2
    json_ctx->file_ctx = LogFileNewCtx();
1140
2
    if (unlikely(json_ctx->file_ctx == NULL)) {
1141
0
        SCLogDebug("AlertJsonInitCtx: Could not create new LogFileCtx");
1142
0
        goto error_exit;
1143
0
    }
1144
1145
2
    if (sensor_name) {
1146
0
        json_ctx->file_ctx->sensor_name = SCStrdup(sensor_name);
1147
0
        if (json_ctx->file_ctx->sensor_name == NULL) {
1148
0
            goto error_exit;
1149
0
        }
1150
2
    } else {
1151
2
        json_ctx->file_ctx->sensor_name = NULL;
1152
2
    }
1153
1154
2
    output_ctx = SCCalloc(1, sizeof(OutputCtx));
1155
2
    if (unlikely(output_ctx == NULL)) {
1156
0
        goto error_exit;
1157
0
    }
1158
1159
2
    output_ctx->data = json_ctx;
1160
2
    output_ctx->DeInit = OutputJsonDeInitCtx;
1161
1162
2
    if (conf) {
1163
2
        const char *output_s = SCConfNodeLookupChildValue(conf, "filetype");
1164
        // Backwards compatibility
1165
2
        if (output_s == NULL) {
1166
0
            output_s = SCConfNodeLookupChildValue(conf, "type");
1167
0
        }
1168
1169
2
        enum LogFileType log_filetype = FileTypeFromConf(output_s);
1170
2
        if (log_filetype == LOGFILE_TYPE_NOTSET) {
1171
0
            SCEveFileType *filetype = SCEveFindFileType(output_s);
1172
0
            if (filetype != NULL) {
1173
0
                log_filetype = LOGFILE_TYPE_FILETYPE;
1174
0
                json_ctx->filetype = filetype;
1175
0
            } else
1176
0
                FatalError("Invalid JSON output option: %s", output_s);
1177
0
        }
1178
1179
2
        const char *prefix = SCConfNodeLookupChildValue(conf, "prefix");
1180
2
        if (prefix != NULL)
1181
0
        {
1182
0
            SCLogInfo("Using prefix '%s' for JSON messages", prefix);
1183
0
            json_ctx->file_ctx->prefix = SCStrdup(prefix);
1184
0
            if (json_ctx->file_ctx->prefix == NULL)
1185
0
            {
1186
0
                FatalError("Failed to allocate memory for eve-log.prefix setting.");
1187
0
            }
1188
0
            json_ctx->file_ctx->prefix_len = (uint32_t)strlen(prefix);
1189
0
        }
1190
1191
        /* Threaded file output */
1192
2
        const SCConfNode *threaded = SCConfNodeLookupChild(conf, "threaded");
1193
2
        if (threaded && threaded->val && SCConfValIsTrue(threaded->val)) {
1194
0
            SCLogConfig("Threaded EVE logging configured");
1195
0
            json_ctx->file_ctx->threaded = true;
1196
2
        } else {
1197
2
            json_ctx->file_ctx->threaded = false;
1198
2
        }
1199
2
        if (LogFileTypePrepare(json_ctx, log_filetype, conf) < 0) {
1200
0
            goto error_exit;
1201
0
        }
1202
1203
2
        const char *sensor_id_s = SCConfNodeLookupChildValue(conf, "sensor-id");
1204
2
        if (sensor_id_s != NULL) {
1205
0
            if (StringParseUint64((uint64_t *)&sensor_id, 10, 0, sensor_id_s) < 0) {
1206
0
                FatalError("Failed to initialize JSON output, "
1207
0
                           "invalid sensor-id: %s",
1208
0
                        sensor_id_s);
1209
0
            }
1210
0
        }
1211
1212
        /* Check if top-level metadata should be logged. */
1213
2
        const SCConfNode *metadata = SCConfNodeLookupChild(conf, "metadata");
1214
2
        if (metadata && metadata->val && SCConfValIsFalse(metadata->val)) {
1215
0
            SCLogConfig("Disabling eve metadata logging.");
1216
0
            json_ctx->cfg.include_metadata = false;
1217
2
        } else {
1218
2
            json_ctx->cfg.include_metadata = true;
1219
2
        }
1220
1221
        /* Check if ethernet information should be logged. */
1222
2
        const SCConfNode *ethernet = SCConfNodeLookupChild(conf, "ethernet");
1223
2
        if (ethernet && ethernet->val && SCConfValIsTrue(ethernet->val)) {
1224
0
            SCLogConfig("Enabling Ethernet MAC address logging.");
1225
0
            json_ctx->cfg.include_ethernet = true;
1226
2
        } else {
1227
2
            json_ctx->cfg.include_ethernet = false;
1228
2
        }
1229
1230
2
        const SCConfNode *suriver = SCConfNodeLookupChild(conf, "suricata-version");
1231
2
        if (suriver && suriver->val && SCConfValIsTrue(suriver->val)) {
1232
0
            SCLogConfig("Enabling Suricata version logging.");
1233
0
            json_ctx->cfg.include_suricata_version = true;
1234
2
        } else {
1235
2
            json_ctx->cfg.include_suricata_version = false;
1236
2
        }
1237
1238
        /* See if we want to enable the community id */
1239
2
        const SCConfNode *community_id = SCConfNodeLookupChild(conf, "community-id");
1240
2
        if (community_id && community_id->val && SCConfValIsTrue(community_id->val)) {
1241
0
            SCLogConfig("Enabling eve community_id logging.");
1242
0
            json_ctx->cfg.include_community_id = true;
1243
2
        } else {
1244
2
            json_ctx->cfg.include_community_id = false;
1245
2
        }
1246
2
        const char *cid_seed = SCConfNodeLookupChildValue(conf, "community-id-seed");
1247
2
        if (cid_seed != NULL) {
1248
0
            if (StringParseUint16(&json_ctx->cfg.community_id_seed,
1249
0
                        10, 0, cid_seed) < 0)
1250
0
            {
1251
0
                FatalError("Failed to initialize JSON output, "
1252
0
                           "invalid community-id-seed: %s",
1253
0
                        cid_seed);
1254
0
            }
1255
0
        }
1256
1257
        /* Do we have a global eve xff configuration? */
1258
2
        const SCConfNode *xff = SCConfNodeLookupChild(conf, "xff");
1259
2
        if (xff != NULL) {
1260
2
            json_ctx->xff_cfg = SCCalloc(1, sizeof(HttpXFFCfg));
1261
2
            if (likely(json_ctx->xff_cfg != NULL)) {
1262
2
                HttpXFFGetCfg(conf, json_ctx->xff_cfg);
1263
2
            }
1264
2
        }
1265
1266
2
        const char *pcapfile_s = SCConfNodeLookupChildValue(conf, "pcap-file");
1267
2
        if (pcapfile_s != NULL && SCConfValIsTrue(pcapfile_s)) {
1268
0
            json_ctx->file_ctx->is_pcap_offline =
1269
0
                    (SCRunmodeGet() == RUNMODE_PCAP_FILE || SCRunmodeGet() == RUNMODE_UNIX_SOCKET);
1270
0
        }
1271
2
        json_ctx->file_ctx->type = log_filetype;
1272
2
    }
1273
1274
2
    SCLogDebug("returning output_ctx %p", output_ctx);
1275
1276
2
    result.ctx = output_ctx;
1277
2
    result.ok = true;
1278
2
    return result;
1279
1280
0
error_exit:
1281
0
    if (json_ctx->file_ctx) {
1282
0
        if (json_ctx->file_ctx->prefix) {
1283
0
            SCFree(json_ctx->file_ctx->prefix);
1284
0
        }
1285
0
        LogFileFreeCtx(json_ctx->file_ctx);
1286
0
    }
1287
0
    SCFree(json_ctx);
1288
1289
0
    if (output_ctx) {
1290
0
        SCFree(output_ctx);
1291
0
    }
1292
0
    return result;
1293
2
}
1294
1295
static void OutputJsonDeInitCtx(OutputCtx *output_ctx)
1296
0
{
1297
0
    OutputJsonCtx *json_ctx = (OutputJsonCtx *)output_ctx->data;
1298
0
    LogFileCtx *logfile_ctx = json_ctx->file_ctx;
1299
0
    if (logfile_ctx->dropped) {
1300
0
        SCLogWarning("%" PRIu64 " events were dropped due to slow or "
1301
0
                     "disconnected socket",
1302
0
                logfile_ctx->dropped);
1303
0
    }
1304
0
    if (json_ctx->xff_cfg != NULL) {
1305
0
        SCFree(json_ctx->xff_cfg);
1306
0
    }
1307
0
    LogFileFreeCtx(logfile_ctx);
1308
0
    SCFree(json_ctx);
1309
0
    SCFree(output_ctx);
1310
0
}