suricata
output-json.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2018 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 "debug.h"
29 #include "detect.h"
30 #include "flow.h"
31 #include "conf.h"
32 
33 #include "threads.h"
34 #include "tm-threads.h"
35 #include "threadvars.h"
36 #include "util-debug.h"
37 
38 #include "util-unittest.h"
39 #include "util-unittest-helper.h"
40 
41 #include "detect-parse.h"
42 #include "detect-engine.h"
43 #include "detect-engine-mpm.h"
44 #include "detect-reference.h"
45 #include "app-layer-parser.h"
47 #include "util-syslog.h"
48 
49 #include "output.h"
50 #include "output-json.h"
51 
52 #include "util-byte.h"
53 #include "util-privs.h"
54 #include "util-print.h"
55 #include "util-proto-name.h"
56 #include "util-optimize.h"
57 #include "util-buffer.h"
58 #include "util-logopenfile.h"
59 #include "util-log-redis.h"
60 #include "util-device.h"
61 #include "util-validate.h"
62 #include "util-crypt.h"
63 
64 #include "flow-var.h"
65 #include "flow-bit.h"
66 
67 #include "source-pcap-file.h"
68 
69 #ifndef HAVE_LIBJANSSON
70 
71 /** Handle the case where no JSON support is compiled in.
72  *
73  */
74 
75 int OutputJsonOpenFileCtx(LogFileCtx *, char *);
76 
77 void OutputJsonRegister (void)
78 {
79  SCLogDebug("Can't register JSON output - JSON support was disabled during build.");
80 }
81 
82 #else /* implied we do have JSON support */
83 
84 #define DEFAULT_LOG_FILENAME "eve.json"
85 #define DEFAULT_ALERT_SYSLOG_FACILITY_STR "local0"
86 #define DEFAULT_ALERT_SYSLOG_FACILITY LOG_LOCAL0
87 #define DEFAULT_ALERT_SYSLOG_LEVEL LOG_INFO
88 #define MODULE_NAME "OutputJSON"
89 
90 #define OUTPUT_BUFFER_SIZE 65536
91 #define MAX_JSON_SIZE 2048
92 
93 static void OutputJsonDeInitCtx(OutputCtx *);
94 static void CreateJSONCommunityFlowId(json_t *js, const Flow *f, const uint16_t seed);
95 
96 static const char *TRAFFIC_ID_PREFIX = "traffic/id/";
97 static const char *TRAFFIC_LABEL_PREFIX = "traffic/label/";
98 static size_t traffic_id_prefix_len = 0;
99 static size_t traffic_label_prefix_len = 0;
100 
101 void OutputJsonRegister (void)
102 {
103  OutputRegisterModule(MODULE_NAME, "eve-log", OutputJsonInitCtx);
104 
105  traffic_id_prefix_len = strlen(TRAFFIC_ID_PREFIX);
106  traffic_label_prefix_len = strlen(TRAFFIC_LABEL_PREFIX);
107 }
108 
109 json_t *SCJsonBool(int val)
110 {
111  return (val ? json_true() : json_false());
112 }
113 
114 /**
115  * Wrap json_decref. This is mainly to expose this function to Rust as its
116  * defined in the Jansson header file as an inline function.
117  */
118 void SCJsonDecref(json_t *json)
119 {
120  json_decref(json);
121 }
122 
123 json_t *SCJsonString(const char *val)
124 {
125  if (val == NULL){
126  return NULL;
127  }
128  json_t * retval = json_string(val);
129  char retbuf[MAX_JSON_SIZE] = {0};
130  if (retval == NULL) {
131  uint32_t u = 0;
132  uint32_t offset = 0;
133  for (u = 0; u < strlen(val); u++) {
134  if (isprint(val[u])) {
135  PrintBufferData(retbuf, &offset, MAX_JSON_SIZE-1, "%c",
136  val[u]);
137  } else {
138  PrintBufferData(retbuf, &offset, MAX_JSON_SIZE-1,
139  "\\x%02X", val[u]);
140  }
141  }
142  retbuf[offset] = '\0';
143  retval = json_string(retbuf);
144  }
145  return retval;
146 }
147 
148 /* Default Sensor ID value */
149 static int64_t sensor_id = -1; /* -1 = not defined */
150 
151 static void JsonAddPacketvars(const Packet *p, json_t *js_vars)
152 {
153  if (p == NULL || p->pktvar == NULL) {
154  return;
155  }
156  json_t *js_pktvars = NULL;
157  PktVar *pv = p->pktvar;
158  while (pv != NULL) {
159  if (pv->key || pv->id > 0) {
160  if (js_pktvars == NULL) {
161  js_pktvars = json_array();
162  if (js_pktvars == NULL)
163  break;
164  }
165  json_t *js_pair = json_object();
166  if (js_pair == NULL) {
167  break;
168  }
169 
170  if (pv->key != NULL) {
171  uint32_t offset = 0;
172  uint8_t keybuf[pv->key_len + 1];
173  PrintStringsToBuffer(keybuf, &offset,
174  sizeof(keybuf),
175  pv->key, pv->key_len);
176  uint32_t len = pv->value_len;
177  uint8_t printable_buf[len + 1];
178  offset = 0;
179  PrintStringsToBuffer(printable_buf, &offset,
180  sizeof(printable_buf),
181  pv->value, pv->value_len);
182  json_object_set_new(js_pair, (char *)keybuf,
183  json_string((char *)printable_buf));
184  } else {
185  const char *varname = VarNameStoreLookupById(pv->id, VAR_TYPE_PKT_VAR);
186  uint32_t len = pv->value_len;
187  uint8_t printable_buf[len + 1];
188  uint32_t offset = 0;
189  PrintStringsToBuffer(printable_buf, &offset,
190  sizeof(printable_buf),
191  pv->value, pv->value_len);
192 
193  json_object_set_new(js_pair, varname,
194  json_string((char *)printable_buf));
195  }
196  json_array_append_new(js_pktvars, js_pair);
197  }
198  pv = pv->next;
199  }
200  if (js_pktvars) {
201  json_object_set_new(js_vars, "pktvars", js_pktvars);
202  }
203 }
204 
205 /**
206  * \brief Check if string s has prefix prefix.
207  *
208  * \retval true if string has prefix
209  * \retval false if string does not have prefix
210  *
211  * TODO: Move to file with other string handling functions.
212  */
213 static bool SCStringHasPrefix(const char *s, const char *prefix)
214 {
215  if (strncmp(s, prefix, strlen(prefix)) == 0) {
216  return true;
217  }
218  return false;
219 }
220 
221 /**
222  * \brief Add flow variables to a json object.
223  *
224  * Adds "flowvars" (map), "flowints" (map) and "flowbits" (array) to
225  * the json object provided as js_root.
226  */
227 static void JsonAddFlowVars(const Flow *f, json_t *js_root, json_t **js_traffic)
228 {
229  if (f == NULL || f->flowvar == NULL) {
230  return;
231  }
232  json_t *js_flowvars = NULL;
233  json_t *js_traffic_id = NULL;
234  json_t *js_traffic_label = NULL;
235  json_t *js_flowints = NULL;
236  json_t *js_flowbits = NULL;
237  GenericVar *gv = f->flowvar;
238  while (gv != NULL) {
239  if (gv->type == DETECT_FLOWVAR || gv->type == DETECT_FLOWINT) {
240  FlowVar *fv = (FlowVar *)gv;
241  if (fv->datatype == FLOWVAR_TYPE_STR && fv->key == NULL) {
242  const char *varname = VarNameStoreLookupById(fv->idx,
244  if (varname) {
245  if (js_flowvars == NULL) {
246  js_flowvars = json_array();
247  if (js_flowvars == NULL)
248  break;
249  }
250 
251  uint32_t len = fv->data.fv_str.value_len;
252  uint8_t printable_buf[len + 1];
253  uint32_t offset = 0;
254  PrintStringsToBuffer(printable_buf, &offset,
255  sizeof(printable_buf),
256  fv->data.fv_str.value, fv->data.fv_str.value_len);
257 
258  json_t *js_flowvar = json_object();
259  if (unlikely(js_flowvar == NULL)) {
260  break;
261  }
262  json_object_set_new(js_flowvar, varname,
263  json_string((char *)printable_buf));
264  json_array_append_new(js_flowvars, js_flowvar);
265  }
266  } else if (fv->datatype == FLOWVAR_TYPE_STR && fv->key != NULL) {
267  if (js_flowvars == NULL) {
268  js_flowvars = json_array();
269  if (js_flowvars == NULL)
270  break;
271  }
272 
273  uint8_t keybuf[fv->keylen + 1];
274  uint32_t offset = 0;
275  PrintStringsToBuffer(keybuf, &offset,
276  sizeof(keybuf),
277  fv->key, fv->keylen);
278 
279  uint32_t len = fv->data.fv_str.value_len;
280  uint8_t printable_buf[len + 1];
281  offset = 0;
282  PrintStringsToBuffer(printable_buf, &offset,
283  sizeof(printable_buf),
284  fv->data.fv_str.value, fv->data.fv_str.value_len);
285 
286  json_t *js_flowvar = json_object();
287  if (unlikely(js_flowvar == NULL)) {
288  break;
289  }
290  json_object_set_new(js_flowvar, (const char *)keybuf,
291  json_string((char *)printable_buf));
292  json_array_append_new(js_flowvars, js_flowvar);
293  } else if (fv->datatype == FLOWVAR_TYPE_INT) {
294  const char *varname = VarNameStoreLookupById(fv->idx,
296  if (varname) {
297  if (js_flowints == NULL) {
298  js_flowints = json_object();
299  if (js_flowints == NULL)
300  break;
301  }
302 
303  json_object_set_new(js_flowints, varname,
304  json_integer(fv->data.fv_int.value));
305  }
306 
307  }
308  } else if (gv->type == DETECT_FLOWBITS) {
309  FlowBit *fb = (FlowBit *)gv;
310  const char *varname = VarNameStoreLookupById(fb->idx,
312  if (varname) {
313  if (SCStringHasPrefix(varname, TRAFFIC_ID_PREFIX)) {
314  if (js_traffic_id == NULL) {
315  js_traffic_id = json_array();
316  if (unlikely(js_traffic_id == NULL)) {
317  break;
318  }
319  }
320  json_array_append_new(js_traffic_id,
321  json_string(&varname[traffic_id_prefix_len]));
322  } else if (SCStringHasPrefix(varname, TRAFFIC_LABEL_PREFIX)) {
323  if (js_traffic_label == NULL) {
324  js_traffic_label = json_array();
325  if (unlikely(js_traffic_label == NULL)) {
326  break;
327  }
328  }
329  json_array_append_new(js_traffic_label,
330  json_string(&varname[traffic_label_prefix_len]));
331  } else {
332  if (js_flowbits == NULL) {
333  js_flowbits = json_array();
334  if (unlikely(js_flowbits == NULL))
335  break;
336  }
337  json_array_append_new(js_flowbits, json_string(varname));
338  }
339  }
340  }
341  gv = gv->next;
342  }
343  if (js_flowbits) {
344  json_object_set_new(js_root, "flowbits", js_flowbits);
345  }
346  if (js_flowints) {
347  json_object_set_new(js_root, "flowints", js_flowints);
348  }
349  if (js_flowvars) {
350  json_object_set_new(js_root, "flowvars", js_flowvars);
351  }
352 
353  if (js_traffic_id != NULL || js_traffic_label != NULL) {
354  *js_traffic = json_object();
355  if (likely(*js_traffic != NULL)) {
356  if (js_traffic_id != NULL) {
357  json_object_set_new(*js_traffic, "id", js_traffic_id);
358  }
359  if (js_traffic_label != NULL) {
360  json_object_set_new(*js_traffic, "label", js_traffic_label);
361  }
362  }
363  }
364 }
365 
366 /**
367  * \brief Add top-level metadata to the eve json object.
368  */
369 static void JsonAddMetadata(const Packet *p, const Flow *f, json_t *js)
370 {
371  if ((p && p->pktvar) || (f && f->flowvar)) {
372  json_t *js_vars = json_object();
373  json_t *js_traffic = NULL;
374  if (js_vars) {
375  if (f && f->flowvar) {
376  JsonAddFlowVars(f, js_vars, &js_traffic);
377  if (js_traffic != NULL) {
378  json_object_set_new(js, "traffic", js_traffic);
379  }
380  }
381  if (p && p->pktvar) {
382  JsonAddPacketvars(p, js_vars);
383  }
384 
385  json_object_set_new(js, "metadata", js_vars);
386  }
387  }
388 }
389 
390 void JsonAddCommonOptions(const OutputJsonCommonSettings *cfg,
391  const Packet *p, const Flow *f, json_t *js)
392 {
393  if (cfg->include_metadata) {
394  JsonAddMetadata(p, f, js);
395  }
396  if (cfg->include_community_id && f != NULL) {
397  CreateJSONCommunityFlowId(js, f, cfg->community_id_seed);
398  }
399 }
400 
401 /** \brief jsonify tcp flags field
402  * Only add 'true' fields in an attempt to keep things reasonably compact.
403  */
404 void JsonTcpFlags(uint8_t flags, json_t *js)
405 {
406  if (flags & TH_SYN)
407  json_object_set_new(js, "syn", json_true());
408  if (flags & TH_FIN)
409  json_object_set_new(js, "fin", json_true());
410  if (flags & TH_RST)
411  json_object_set_new(js, "rst", json_true());
412  if (flags & TH_PUSH)
413  json_object_set_new(js, "psh", json_true());
414  if (flags & TH_ACK)
415  json_object_set_new(js, "ack", json_true());
416  if (flags & TH_URG)
417  json_object_set_new(js, "urg", json_true());
418  if (flags & TH_ECN)
419  json_object_set_new(js, "ecn", json_true());
420  if (flags & TH_CWR)
421  json_object_set_new(js, "cwr", json_true());
422 }
423 
424 /**
425  * \brief Add five tuple from packet to JSON object
426  *
427  * \param p Packet
428  * \param dir log direction (packet or flow)
429  * \param js JSON object
430  */
431 void JsonFiveTuple(const Packet *p, enum OutputJsonLogDirection dir, json_t *js)
432 {
433  char srcip[46] = "", dstip[46] = "";
434  Port sp, dp;
435  char proto[16];
436 
437  switch (dir) {
438  case LOG_DIR_PACKET:
439  if (PKT_IS_IPV4(p)) {
440  PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p),
441  srcip, sizeof(srcip));
442  PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p),
443  dstip, sizeof(dstip));
444  } else if (PKT_IS_IPV6(p)) {
445  PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p),
446  srcip, sizeof(srcip));
447  PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p),
448  dstip, sizeof(dstip));
449  }
450  sp = p->sp;
451  dp = p->dp;
452  break;
453  case LOG_DIR_FLOW:
454  case LOG_DIR_FLOW_TOSERVER:
455  if ((PKT_IS_TOSERVER(p))) {
456  if (PKT_IS_IPV4(p)) {
457  PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p),
458  srcip, sizeof(srcip));
459  PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p),
460  dstip, sizeof(dstip));
461  } else if (PKT_IS_IPV6(p)) {
462  PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p),
463  srcip, sizeof(srcip));
464  PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p),
465  dstip, sizeof(dstip));
466  }
467  sp = p->sp;
468  dp = p->dp;
469  } else {
470  if (PKT_IS_IPV4(p)) {
471  PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p),
472  srcip, sizeof(srcip));
473  PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p),
474  dstip, sizeof(dstip));
475  } else if (PKT_IS_IPV6(p)) {
476  PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p),
477  srcip, sizeof(srcip));
478  PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p),
479  dstip, sizeof(dstip));
480  }
481  sp = p->dp;
482  dp = p->sp;
483  }
484  break;
485  case LOG_DIR_FLOW_TOCLIENT:
486  if ((PKT_IS_TOCLIENT(p))) {
487  if (PKT_IS_IPV4(p)) {
488  PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p),
489  srcip, sizeof(srcip));
490  PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p),
491  dstip, sizeof(dstip));
492  } else if (PKT_IS_IPV6(p)) {
493  PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p),
494  srcip, sizeof(srcip));
495  PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p),
496  dstip, sizeof(dstip));
497  }
498  sp = p->sp;
499  dp = p->dp;
500  } else {
501  if (PKT_IS_IPV4(p)) {
502  PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p),
503  srcip, sizeof(srcip));
504  PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p),
505  dstip, sizeof(dstip));
506  } else if (PKT_IS_IPV6(p)) {
507  PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p),
508  srcip, sizeof(srcip));
509  PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p),
510  dstip, sizeof(dstip));
511  }
512  sp = p->dp;
513  dp = p->sp;
514  }
515  break;
516  default:
518  return;
519  }
520 
521  if (SCProtoNameValid(IP_GET_IPPROTO(p)) == TRUE) {
522  strlcpy(proto, known_proto[IP_GET_IPPROTO(p)], sizeof(proto));
523  } else {
524  snprintf(proto, sizeof(proto), "%03" PRIu32, IP_GET_IPPROTO(p));
525  }
526 
527  json_object_set_new(js, "src_ip", json_string(srcip));
528 
529  switch(p->proto) {
530  case IPPROTO_ICMP:
531  break;
532  case IPPROTO_UDP:
533  case IPPROTO_TCP:
534  case IPPROTO_SCTP:
535  json_object_set_new(js, "src_port", json_integer(sp));
536  break;
537  }
538 
539  json_object_set_new(js, "dest_ip", json_string(dstip));
540 
541  switch(p->proto) {
542  case IPPROTO_ICMP:
543  break;
544  case IPPROTO_UDP:
545  case IPPROTO_TCP:
546  case IPPROTO_SCTP:
547  json_object_set_new(js, "dest_port", json_integer(dp));
548  break;
549  }
550 
551  json_object_set_new(js, "proto", json_string(proto));
552 }
553 
554 static void CreateJSONCommunityFlowIdv4(json_t *js, const Flow *f,
555  const uint16_t seed)
556 {
557  struct {
558  uint16_t seed;
559  uint32_t src;
560  uint32_t dst;
561  uint8_t proto;
562  uint8_t pad0;
563  uint16_t sp;
564  uint16_t dp;
565  } __attribute__((__packed__)) ipv4;
566 
567  uint32_t src = f->src.addr_data32[0];
568  uint32_t dst = f->dst.addr_data32[0];
569  uint16_t sp = f->sp;
570  if (f->proto == IPPROTO_ICMP)
571  sp = f->icmp_s.type;
572  sp = htons(sp);
573  uint16_t dp = f->dp;
574  if (f->proto == IPPROTO_ICMP)
575  dp = f->icmp_d.type;
576  dp = htons(dp);
577 
578  ipv4.seed = htons(seed);
579  if (ntohl(src) < ntohl(dst) || (src == dst && sp < dp)) {
580  ipv4.src = src;
581  ipv4.dst = dst;
582  ipv4.sp = sp;
583  ipv4.dp = dp;
584  } else {
585  ipv4.src = dst;
586  ipv4.dst = src;
587  ipv4.sp = dp;
588  ipv4.dp = sp;
589  }
590  ipv4.proto = f->proto;
591  ipv4.pad0 = 0;
592 
593  uint8_t hash[20];
594  if (ComputeSHA1((const uint8_t *)&ipv4, sizeof(ipv4), hash, sizeof(hash)) == 1) {
595  unsigned char base64buf[64] = "1:";
596  unsigned long out_len = sizeof(base64buf) - 2;
597  if (Base64Encode(hash, sizeof(hash), base64buf+2, &out_len) == SC_BASE64_OK) {
598  json_object_set_new(js, "community_id", json_string((const char *)base64buf));
599  }
600  }
601 }
602 
603 static inline bool FlowHashRawAddressIPv6LtU32(const uint32_t *a, const uint32_t *b)
604 {
605  for (int i = 0; i < 4; i++) {
606  if (a[i] < b[i])
607  return true;
608  if (a[i] > b[i])
609  break;
610  }
611 
612  return false;
613 }
614 
615 static void CreateJSONCommunityFlowIdv6(json_t *js, const Flow *f,
616  const uint16_t seed)
617 {
618  struct {
619  uint16_t seed;
620  uint32_t src[4];
621  uint32_t dst[4];
622  uint8_t proto;
623  uint8_t pad0;
624  uint16_t sp;
625  uint16_t dp;
626  } __attribute__((__packed__)) ipv6;
627 
628  uint16_t sp = f->sp;
629  if (f->proto == IPPROTO_ICMPV6)
630  sp = f->icmp_s.type;
631  sp = htons(sp);
632  uint16_t dp = f->dp;
633  if (f->proto == IPPROTO_ICMPV6)
634  dp = f->icmp_d.type;
635  dp = htons(dp);
636 
637  ipv6.seed = htons(seed);
638  if (FlowHashRawAddressIPv6LtU32(f->src.addr_data32, f->dst.addr_data32) ||
639  ((memcmp(&f->src, &f->dst, sizeof(f->src)) == 0) && sp < dp))
640  {
641  memcpy(&ipv6.src, &f->src.addr_data32, 16);
642  memcpy(&ipv6.dst, &f->dst.addr_data32, 16);
643  ipv6.sp = sp;
644  ipv6.dp = dp;
645  } else {
646  memcpy(&ipv6.src, &f->dst.addr_data32, 16);
647  memcpy(&ipv6.dst, &f->src.addr_data32, 16);
648  ipv6.sp = dp;
649  ipv6.dp = sp;
650  }
651  ipv6.proto = f->proto;
652  ipv6.pad0 = 0;
653 
654  uint8_t hash[20];
655  if (ComputeSHA1((const uint8_t *)&ipv6, sizeof(ipv6), hash, sizeof(hash)) == 1) {
656  unsigned char base64buf[64] = "1:";
657  unsigned long out_len = sizeof(base64buf) - 2;
658  if (Base64Encode(hash, sizeof(hash), base64buf+2, &out_len) == SC_BASE64_OK) {
659  json_object_set_new(js, "community_id", json_string((const char *)base64buf));
660  }
661  }
662 }
663 
664 static void CreateJSONCommunityFlowId(json_t *js, const Flow *f, const uint16_t seed)
665 {
666  if (f->flags & FLOW_IPV4)
667  return CreateJSONCommunityFlowIdv4(js, f, seed);
668  else if (f->flags & FLOW_IPV6)
669  return CreateJSONCommunityFlowIdv6(js, f, seed);
670 }
671 
672 void CreateJSONFlowId(json_t *js, const Flow *f)
673 {
674  if (f == NULL)
675  return;
676  int64_t flow_id = FlowGetId(f);
677  json_object_set_new(js, "flow_id", json_integer(flow_id));
678  if (f->parent_id) {
679  json_object_set_new(js, "parent_id", json_integer(f->parent_id));
680  }
681 }
682 
683 json_t *CreateJSONHeader(const Packet *p, enum OutputJsonLogDirection dir,
684  const char *event_type)
685 {
686  char timebuf[64];
687  const Flow *f = (const Flow *)p->flow;
688 
689  json_t *js = json_object();
690  if (unlikely(js == NULL))
691  return NULL;
692 
693  CreateIsoTimeString(&p->ts, timebuf, sizeof(timebuf));
694 
695  /* time & tx */
696  json_object_set_new(js, "timestamp", json_string(timebuf));
697 
698  CreateJSONFlowId(js, f);
699 
700  /* sensor id */
701  if (sensor_id >= 0)
702  json_object_set_new(js, "sensor_id", json_integer(sensor_id));
703 
704  /* input interface */
705  if (p->livedev) {
706  json_object_set_new(js, "in_iface", json_string(p->livedev->dev));
707  }
708 
709  /* pcap_cnt */
710  if (p->pcap_cnt != 0) {
711  json_object_set_new(js, "pcap_cnt", json_integer(p->pcap_cnt));
712  }
713 
714  if (event_type) {
715  json_object_set_new(js, "event_type", json_string(event_type));
716  }
717 
718  /* vlan */
719  if (p->vlan_idx > 0) {
720  json_t *js_vlan;
721  switch (p->vlan_idx) {
722  case 1:
723  json_object_set_new(js, "vlan",
724  json_integer(VLAN_GET_ID1(p)));
725  break;
726  case 2:
727  js_vlan = json_array();
728  if (unlikely(js != NULL)) {
729  json_array_append_new(js_vlan,
730  json_integer(VLAN_GET_ID1(p)));
731  json_array_append_new(js_vlan,
732  json_integer(VLAN_GET_ID2(p)));
733  json_object_set_new(js, "vlan", js_vlan);
734  }
735  break;
736  default:
737  /* shouldn't get here */
738  break;
739  }
740  }
741 
742  /* 5-tuple */
743  JsonFiveTuple(p, dir, js);
744 
745  /* icmp */
746  switch (p->proto) {
747  case IPPROTO_ICMP:
748  if (p->icmpv4h) {
749  json_object_set_new(js, "icmp_type",
750  json_integer(p->icmpv4h->type));
751  json_object_set_new(js, "icmp_code",
752  json_integer(p->icmpv4h->code));
753  }
754  break;
755  case IPPROTO_ICMPV6:
756  if (p->icmpv6h) {
757  json_object_set_new(js, "icmp_type",
758  json_integer(p->icmpv6h->type));
759  json_object_set_new(js, "icmp_code",
760  json_integer(p->icmpv6h->code));
761  }
762  break;
763  }
764 
765  return js;
766 }
767 
768 json_t *CreateJSONHeaderWithTxId(const Packet *p, enum OutputJsonLogDirection dir,
769  const char *event_type, uint64_t tx_id)
770 {
771  json_t *js = CreateJSONHeader(p, dir, event_type);
772  if (unlikely(js == NULL))
773  return NULL;
774 
775  /* tx id for correlation with other events */
776  json_object_set_new(js, "tx_id", json_integer(tx_id));
777 
778  return js;
779 }
780 
781 int OutputJSONMemBufferCallback(const char *str, size_t size, void *data)
782 {
783  OutputJSONMemBufferWrapper *wrapper = data;
784  MemBuffer **memb = wrapper->buffer;
785 
786  if (MEMBUFFER_OFFSET(*memb) + size >= MEMBUFFER_SIZE(*memb)) {
787  MemBufferExpand(memb, wrapper->expand_by);
788  }
789 
790  MemBufferWriteRaw((*memb), str, size);
791  return 0;
792 }
793 
794 int OutputJSONBuffer(json_t *js, LogFileCtx *file_ctx, MemBuffer **buffer)
795 {
796  if (file_ctx->sensor_name) {
797  json_object_set_new(js, "host",
798  json_string(file_ctx->sensor_name));
799  }
800 
801  if (file_ctx->is_pcap_offline) {
802  json_object_set_new(js, "pcap_filename", json_string(PcapFileGetFilename()));
803  }
804 
805  if (file_ctx->prefix) {
806  MemBufferWriteRaw((*buffer), file_ctx->prefix, file_ctx->prefix_len);
807  }
808 
809  OutputJSONMemBufferWrapper wrapper = {
810  .buffer = buffer,
811  .expand_by = OUTPUT_BUFFER_SIZE
812  };
813 
814  int r = json_dump_callback(js, OutputJSONMemBufferCallback, &wrapper,
815  file_ctx->json_flags);
816  if (r != 0)
817  return TM_ECODE_OK;
818 
819  LogFileWrite(file_ctx, *buffer);
820  return 0;
821 }
822 
823 /**
824  * \brief Create a new LogFileCtx for "fast" output style.
825  * \param conf The configuration node for this output.
826  * \return A LogFileCtx pointer on success, NULL on failure.
827  */
828 OutputInitResult OutputJsonInitCtx(ConfNode *conf)
829 {
830  OutputInitResult result = { NULL, false };
831 
832  OutputJsonCtx *json_ctx = SCCalloc(1, sizeof(OutputJsonCtx));
833  if (unlikely(json_ctx == NULL)) {
834  SCLogDebug("could not create new OutputJsonCtx");
835  return result;
836  }
837 
838  /* First lookup a sensor-name value in this outputs configuration
839  * node (deprecated). If that fails, lookup the global one. */
840  const char *sensor_name = ConfNodeLookupChildValue(conf, "sensor-name");
841  if (sensor_name != NULL) {
843  "Found deprecated eve-log setting \"sensor-name\". "
844  "Please set sensor-name globally.");
845  }
846  else {
847  (void)ConfGet("sensor-name", &sensor_name);
848  }
849 
850  json_ctx->file_ctx = LogFileNewCtx();
851  if (unlikely(json_ctx->file_ctx == NULL)) {
852  SCLogDebug("AlertJsonInitCtx: Could not create new LogFileCtx");
853  SCFree(json_ctx);
854  return result;
855  }
856 
857  if (sensor_name) {
858  json_ctx->file_ctx->sensor_name = SCStrdup(sensor_name);
859  if (json_ctx->file_ctx->sensor_name == NULL) {
860  LogFileFreeCtx(json_ctx->file_ctx);
861  SCFree(json_ctx);
862  return result;
863  }
864  } else {
865  json_ctx->file_ctx->sensor_name = NULL;
866  }
867 
868  OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
869  if (unlikely(output_ctx == NULL)) {
870  LogFileFreeCtx(json_ctx->file_ctx);
871  SCFree(json_ctx);
872  return result;
873  }
874 
875  output_ctx->data = json_ctx;
876  output_ctx->DeInit = OutputJsonDeInitCtx;
877 
878  if (conf) {
879  const char *output_s = ConfNodeLookupChildValue(conf, "filetype");
880 
881  // Backwards compatibility
882  if (output_s == NULL) {
883  output_s = ConfNodeLookupChildValue(conf, "type");
884  }
885 
886  if (output_s != NULL) {
887  if (strcmp(output_s, "file") == 0 ||
888  strcmp(output_s, "regular") == 0) {
889  json_ctx->json_out = LOGFILE_TYPE_FILE;
890  } else if (strcmp(output_s, "syslog") == 0) {
891  json_ctx->json_out = LOGFILE_TYPE_SYSLOG;
892  } else if (strcmp(output_s, "unix_dgram") == 0) {
893  json_ctx->json_out = LOGFILE_TYPE_UNIX_DGRAM;
894  } else if (strcmp(output_s, "unix_stream") == 0) {
895  json_ctx->json_out = LOGFILE_TYPE_UNIX_STREAM;
896  } else if (strcmp(output_s, "redis") == 0) {
897 #ifdef HAVE_LIBHIREDIS
898  SCLogRedisInit();
899  json_ctx->json_out = LOGFILE_TYPE_REDIS;
900 #else
902  "redis JSON output option is not compiled");
903  exit(EXIT_FAILURE);
904 #endif
905  } else {
907  "Invalid JSON output option: %s", output_s);
908  exit(EXIT_FAILURE);
909  }
910  }
911 
912  const char *prefix = ConfNodeLookupChildValue(conf, "prefix");
913  if (prefix != NULL)
914  {
915  SCLogInfo("Using prefix '%s' for JSON messages", prefix);
916  json_ctx->file_ctx->prefix = SCStrdup(prefix);
917  if (json_ctx->file_ctx->prefix == NULL)
918  {
920  "Failed to allocate memory for eve-log.prefix setting.");
921  exit(EXIT_FAILURE);
922  }
923  json_ctx->file_ctx->prefix_len = strlen(prefix);
924  }
925 
926  if (json_ctx->json_out == LOGFILE_TYPE_FILE ||
927  json_ctx->json_out == LOGFILE_TYPE_UNIX_DGRAM ||
928  json_ctx->json_out == LOGFILE_TYPE_UNIX_STREAM)
929  {
930  if (SCConfLogOpenGeneric(conf, json_ctx->file_ctx, DEFAULT_LOG_FILENAME, 1) < 0) {
931  LogFileFreeCtx(json_ctx->file_ctx);
932  SCFree(json_ctx);
933  SCFree(output_ctx);
934  return result;
935  }
936  OutputRegisterFileRotationFlag(&json_ctx->file_ctx->rotation_flag);
937  }
938 #ifndef OS_WIN32
939  else if (json_ctx->json_out == LOGFILE_TYPE_SYSLOG) {
940  const char *facility_s = ConfNodeLookupChildValue(conf, "facility");
941  if (facility_s == NULL) {
943  }
944 
945  int facility = SCMapEnumNameToValue(facility_s, SCSyslogGetFacilityMap());
946  if (facility == -1) {
947  SCLogWarning(SC_ERR_INVALID_ARGUMENT, "Invalid syslog facility: \"%s\","
948  " now using \"%s\" as syslog facility", facility_s,
951  }
952 
953  const char *level_s = ConfNodeLookupChildValue(conf, "level");
954  if (level_s != NULL) {
955  int level = SCMapEnumNameToValue(level_s, SCSyslogGetLogLevelMap());
956  if (level != -1) {
957  json_ctx->file_ctx->syslog_setup.alert_syslog_level = level;
958  }
959  }
960 
961  const char *ident = ConfNodeLookupChildValue(conf, "identity");
962  /* if null we just pass that to openlog, which will then
963  * figure it out by itself. */
964 
965  openlog(ident, LOG_PID|LOG_NDELAY, facility);
966  }
967 #endif
968 #ifdef HAVE_LIBHIREDIS
969  else if (json_ctx->json_out == LOGFILE_TYPE_REDIS) {
970  ConfNode *redis_node = ConfNodeLookupChild(conf, "redis");
971  if (!json_ctx->file_ctx->sensor_name) {
972  char hostname[1024];
973  gethostname(hostname, 1023);
974  json_ctx->file_ctx->sensor_name = SCStrdup(hostname);
975  }
976  if (json_ctx->file_ctx->sensor_name == NULL) {
977  LogFileFreeCtx(json_ctx->file_ctx);
978  SCFree(json_ctx);
979  SCFree(output_ctx);
980  return result;
981  }
982 
983  if (SCConfLogOpenRedis(redis_node, json_ctx->file_ctx) < 0) {
984  LogFileFreeCtx(json_ctx->file_ctx);
985  SCFree(json_ctx);
986  SCFree(output_ctx);
987  return result;
988  }
989  }
990 #endif
991 
992  const char *sensor_id_s = ConfNodeLookupChildValue(conf, "sensor-id");
993  if (sensor_id_s != NULL) {
994  if (ByteExtractStringUint64((uint64_t *)&sensor_id, 10, 0, sensor_id_s) == -1) {
996  "Failed to initialize JSON output, "
997  "invalid sensor-id: %s", sensor_id_s);
998  exit(EXIT_FAILURE);
999  }
1000  }
1001 
1002  /* Check if top-level metadata should be logged. */
1003  const ConfNode *metadata = ConfNodeLookupChild(conf, "metadata");
1004  if (metadata && metadata->val && ConfValIsFalse(metadata->val)) {
1005  SCLogConfig("Disabling eve metadata logging.");
1006  json_ctx->cfg.include_metadata = false;
1007  } else {
1008  json_ctx->cfg.include_metadata = true;
1009  }
1010 
1011  /* See if we want to enable the community id */
1012  const ConfNode *community_id = ConfNodeLookupChild(conf, "community-id");
1013  if (community_id && community_id->val && ConfValIsTrue(community_id->val)) {
1014  SCLogConfig("Enabling eve community_id logging.");
1015  json_ctx->cfg.include_community_id = true;
1016  } else {
1017  json_ctx->cfg.include_community_id = false;
1018  }
1019  const char *cid_seed = ConfNodeLookupChildValue(conf, "community-id-seed");
1020  if (cid_seed != NULL) {
1021  if (ByteExtractStringUint16(&json_ctx->cfg.community_id_seed,
1022  10, 0, cid_seed) == -1)
1023  {
1025  "Failed to initialize JSON output, "
1026  "invalid community-id-seed: %s", cid_seed);
1027  exit(EXIT_FAILURE);
1028  }
1029  }
1030 
1031  /* Do we have a global eve xff configuration? */
1032  const ConfNode *xff = ConfNodeLookupChild(conf, "xff");
1033  if (xff != NULL) {
1034  json_ctx->xff_cfg = SCCalloc(1, sizeof(HttpXFFCfg));
1035  if (likely(json_ctx->xff_cfg != NULL)) {
1036  HttpXFFGetCfg(conf, json_ctx->xff_cfg);
1037  }
1038  }
1039 
1040  const char *pcapfile_s = ConfNodeLookupChildValue(conf, "pcap-file");
1041  if (pcapfile_s != NULL && ConfValIsTrue(pcapfile_s)) {
1042  json_ctx->file_ctx->is_pcap_offline =
1044  }
1045 
1046  json_ctx->file_ctx->type = json_ctx->json_out;
1047  }
1048 
1049 
1050  SCLogDebug("returning output_ctx %p", output_ctx);
1051 
1052  result.ctx = output_ctx;
1053  result.ok = true;
1054  return result;
1055 }
1056 
1057 static void OutputJsonDeInitCtx(OutputCtx *output_ctx)
1058 {
1059  OutputJsonCtx *json_ctx = (OutputJsonCtx *)output_ctx->data;
1060  LogFileCtx *logfile_ctx = json_ctx->file_ctx;
1061  if (logfile_ctx->dropped) {
1063  "%"PRIu64" events were dropped due to slow or "
1064  "disconnected socket", logfile_ctx->dropped);
1065  }
1066  if (json_ctx->xff_cfg != NULL) {
1067  SCFree(json_ctx->xff_cfg);
1068  }
1069  LogFileFreeCtx(logfile_ctx);
1070  SCFree(json_ctx);
1071  SCFree(output_ctx);
1072 }
1073 
1074 #endif
void OutputJsonRegister(void)
Definition: output-json.c:77
#define GET_IPV4_SRC_ADDR_PTR(p)
Definition: decode.h:213
char * known_proto[256]
uint16_t flags
uint8_t type
Definition: util-var.h:49
#define FLOWVAR_TYPE_STR
Definition: flow-var.h:33
uint8_t SCProtoNameValid(uint16_t proto)
Function to check if the received protocol number is valid and do we have corresponding name entry fo...
#define SCLogDebug(...)
Definition: util-debug.h:335
#define MemBufferWriteRaw(dst, raw_buffer, raw_buffer_len)
Write a raw buffer to the MemBuffer dst.
Definition: util-buffer.h:133
uint32_t idx
Definition: flow-var.h:52
#define FLOW_IPV6
Definition: flow.h:95
uint16_t value_len
Definition: flow-var.h:39
struct Flow_ * flow
Definition: decode.h:444
struct PktVar_ * next
Definition: decode.h:311
#define MEMBUFFER_SIZE(mem_buffer)
Get the MemBuffers current size.
Definition: util-buffer.h:60
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: util-strlcpyu.c:43
uint32_t event_type
uint8_t proto
Definition: flow.h:346
int ComputeSHA1(const uint8_t *inbuf, size_t inbuf_len, uint8_t *outbuf, size_t outbuf_size)
calculate SHA1 hash
Definition: util-crypt.c:230
#define PKT_IS_TOCLIENT(p)
Definition: decode.h:257
Port sp
Definition: flow.h:333
void HttpXFFGetCfg(ConfNode *conf, HttpXFFCfg *result)
Function to return XFF configuration from a configuration node.
#define unlikely(expr)
Definition: util-optimize.h:35
#define GET_IPV4_DST_ADDR_PTR(p)
Definition: decode.h:214
Port sp
Definition: decode.h:414
uint8_t * key
Definition: flow-var.h:60
Port dp
Definition: decode.h:422
ICMPV4Hdr * icmpv4h
Definition: decode.h:531
#define TH_RST
Definition: decode-tcp.h:37
uint8_t code
#define TH_FIN
Definition: decode-tcp.h:35
uint64_t offset
int LogFileWrite(LogFileCtx *file_ctx, MemBuffer *buffer)
#define PKT_IS_IPV6(p)
Definition: decode.h:251
SCEnumCharMap * SCSyslogGetFacilityMap(void)
returns the syslog facility enum map
Definition: util-syslog.c:57
uint16_t src
void(* DeInit)(struct OutputCtx_ *)
Definition: tm-modules.h:84
uint64_t pcap_cnt
Definition: decode.h:566
int SCMapEnumNameToValue(const char *enum_name, SCEnumCharMap *table)
Maps a string name to an enum value from the supplied table. Please specify the last element of any m...
Definition: util-enum.c:41
uint16_t key_len
Definition: decode.h:314
char * val
Definition: conf.h:34
ConfNode * ConfNodeLookupChild(const ConfNode *node, const char *name)
Lookup a child configuration node by name.
Definition: conf.c:815
#define TRUE
typedef __attribute__
DNP3 application header.
#define PKT_IS_IPV4(p)
Definition: decode.h:250
int ConfGet(const char *name, const char **vptr)
Retrieve the value of a configuration node.
Definition: conf.c:331
FlowVarTypeStr fv_str
Definition: flow-var.h:57
FlowAddress dst
Definition: flow.h:331
struct Flow_::@124::@128 icmp_s
char * dev
Definition: util-device.h:41
int ByteExtractStringUint16(uint16_t *res, int base, uint16_t len, const char *str)
Definition: util-byte.c:265
uint8_t datatype
Definition: flow-var.h:50
int ByteExtractStringUint64(uint64_t *res, int base, uint16_t len, const char *str)
Definition: util-byte.c:239
struct GenericVar_ * next
Definition: util-var.h:52
PktVar * pktvar
Definition: decode.h:493
#define str(s)
uint16_t dst
#define SCCalloc(nm, a)
Definition: util-mem.h:205
#define openlog(__ident, __option, __facility)
Definition: win32-syslog.h:76
const char * ConfNodeLookupChildValue(const ConfNode *node, const char *name)
Lookup the value of a child configuration node by name.
Definition: conf.c:843
void OutputRegisterFileRotationFlag(int *flag)
Register a flag for file rotation notification.
Definition: output.c:868
union FlowVar_::@120 data
void CreateIsoTimeString(const struct timeval *ts, char *str, size_t size)
Definition: util-time.c:179
ICMPV6Hdr * icmpv6h
Definition: decode.h:533
uint16_t value_len
Definition: decode.h:315
uint8_t proto
Definition: decode.h:429
#define GET_IPV6_DST_ADDR(p)
Definition: decode.h:219
#define SCLogError(err_code,...)
Macro used to log ERROR messages.
Definition: util-debug.h:294
#define PrintBufferData(buf, buf_offset_ptr, buf_size,...)
Definition: util-print.h:29
#define VLAN_GET_ID2(p)
Definition: decode-vlan.h:41
uint32_t idx
Definition: flow-bit.h:33
uint32_t value
Definition: flow-var.h:44
#define TH_URG
Definition: decode-tcp.h:40
int OutputJsonOpenFileCtx(LogFileCtx *, char *)
#define TH_ACK
Definition: decode-tcp.h:39
#define MODULE_NAME
int ConfValIsFalse(const char *val)
Check if a value is false.
Definition: conf.c:591
LogFileCtx * LogFileNewCtx(void)
LogFileNewCtx() Get a new LogFileCtx.
uint8_t * buffer
Definition: util-buffer.h:28
#define PKT_IS_TOSERVER(p)
Definition: decode.h:256
#define TH_PUSH
Definition: decode-tcp.h:38
#define DEFAULT_ALERT_SYSLOG_FACILITY
Definition: alert-syslog.c:56
#define TH_ECN
Definition: decode-tcp.h:42
int SCConfLogOpenGeneric(ConfNode *conf, LogFileCtx *log_ctx, const char *default_filename, int rotate)
open a generic output "log file", which may be a regular file or a socket
const char * PrintInet(int af, const void *src, char *dst, socklen_t size)
Definition: util-print.c:267
char * sensor_name
void PrintStringsToBuffer(uint8_t *dst_buf, uint32_t *dst_buf_offset_ptr, uint32_t dst_buf_size, const uint8_t *src_buf, const uint32_t src_buf_len)
Definition: util-print.c:224
struct Flow_::@126::@129 icmp_d
uint8_t proto
uint16_t Port
Definition: decode.h:234
GenericVar * flowvar
Definition: flow.h:446
#define SCLogWarning(err_code,...)
Macro used to log WARNING messages.
Definition: util-debug.h:281
int ConfValIsTrue(const char *val)
Check if a value is true.
Definition: conf.c:566
FlowVarTypeInt fv_int
Definition: flow-var.h:58
uint8_t vlan_idx
Definition: decode.h:435
uint8_t * value
Definition: flow-var.h:38
uint32_t id
Definition: decode.h:310
Definition: conf.h:32
OutputCtx * ctx
Definition: output.h:42
#define DEFAULT_LOG_FILENAME
#define GET_IPV6_SRC_ADDR(p)
Definition: decode.h:218
int LogFileFreeCtx(LogFileCtx *lf_ctx)
LogFileFreeCtx() Destroy a LogFileCtx (Close the file and free memory)
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition: util-debug.h:254
#define TH_SYN
Definition: decode-tcp.h:36
#define SCFree(a)
Definition: util-mem.h:236
#define VLAN_GET_ID1(p)
Definition: decode-vlan.h:40
uint8_t * key
Definition: decode.h:316
Port dp
Definition: flow.h:340
#define OUTPUT_BUFFER_SIZE
Definition: log-dnslog.c:58
uint8_t * value
Definition: decode.h:317
uint16_t tx_id
SCEnumCharMap * SCSyslogGetLogLevelMap(void)
returns the syslog facility enum map
Definition: util-syslog.c:75
uint8_t type
#define MEMBUFFER_OFFSET(mem_buffer)
Get the MemBuffers current offset.
Definition: util-buffer.h:55
const char * VarNameStoreLookupById(const uint32_t id, const enum VarTypes type)
const char * PcapFileGetFilename(void)
void * data
Definition: tm-modules.h:81
int64_t parent_id
Definition: flow.h:387
int RunmodeGetCurrent(void)
Definition: suricata.c:269
#define IP_GET_IPPROTO(p)
Definition: decode.h:262
int Base64Encode(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen)
Definition: util-crypt.c:272
#define DEFAULT_ALERT_SYSLOG_FACILITY_STR
Definition: alert-syslog.c:55
#define SCStrdup(a)
Definition: util-mem.h:220
FlowAddress src
Definition: flow.h:331
uint16_t keylen
Definition: flow-var.h:51
struct LiveDevice_ * livedev
Definition: decode.h:558
uint8_t len
struct timeval ts
Definition: decode.h:450
#define FLOWVAR_TYPE_INT
Definition: flow-var.h:34
struct SCLogConfig_ SCLogConfig
Holds the config state used by the logging api.
#define likely(expr)
Definition: util-optimize.h:32
uint8_t pad0
#define TH_CWR
Definition: decode-tcp.h:44
int MemBufferExpand(MemBuffer **buffer, uint32_t expand_by)
expand membuffer by size of &#39;expand_by&#39;
Definition: util-buffer.c:60
Flow data structure.
Definition: flow.h:327
#define FLOW_IPV4
Definition: flow.h:93
uint32_t flags
Definition: flow.h:377
#define DEBUG_VALIDATE_BUG_ON(exp)
void OutputRegisterModule(const char *, const char *, OutputInitFunc)