Coverage Report

Created: 2026-03-31 07:45

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