Coverage Report

Created: 2026-05-16 07:38

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