diff --git a/src/Makefile.am b/src/Makefile.am
index ff778d813d..da500754c7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -9,6 +9,7 @@ bin_PROGRAMS = suricata
suricata_SOURCES = \
alert-debuglog.c alert-debuglog.h \
alert-fastlog.c alert-fastlog.h \
+alert-json.c alert-json.h \
alert-pcapinfo.c alert-pcapinfo.h \
alert-prelude.c alert-prelude.h \
alert-syslog.c alert-syslog.h \
diff --git a/src/alert-json.c b/src/alert-json.c
new file mode 100644
index 0000000000..6123c8c6a9
--- /dev/null
+++ b/src/alert-json.c
@@ -0,0 +1,733 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Tom DeCanio
+ *
+ * Logs alerts in JSON format.
+ *
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "flow.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "tm-threads.h"
+#include "threadvars.h"
+#include "util-debug.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-reference.h"
+#include "util-classification-config.h"
+#include "util-syslog.h"
+
+#include "output.h"
+#include "alert-json.h"
+
+#include "util-byte.h"
+#include "util-mpm-b2g-cuda.h"
+#include "util-cuda-handlers.h"
+#include "util-privs.h"
+#include "util-print.h"
+#include "util-proto-name.h"
+#include "util-optimize.h"
+#include "util-logopenfile.h"
+
+/*#undef HAVE_LIBJANSSON for testing without messing with config */
+#ifndef HAVE_LIBJANSSON
+/** Handle the case where no JSON support is compiled in.
+ *
+ */
+
+TmEcode AlertJson (ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
+TmEcode AlertJsonThreadInit(ThreadVars *, void *, void **);
+TmEcode AlertJsonThreadDeinit(ThreadVars *, void *);
+int AlertJsonOpenFileCtx(LogFileCtx *, char *);
+void AlertJsonRegisterTests(void);
+
+void TmModuleAlertJsonRegister (void) {
+ tmm_modules[TMM_ALERTJSON].name = "AlertJSON";
+ tmm_modules[TMM_ALERTJSON].ThreadInit = AlertJsonThreadInit;
+ tmm_modules[TMM_ALERTJSON].Func = AlertJson;
+ tmm_modules[TMM_ALERTJSON].ThreadDeinit = AlertJsonThreadDeinit;
+ tmm_modules[TMM_ALERTJSON].RegisterTests = AlertJsonRegisterTests;
+}
+
+OutputCtx *AlertJsonInitCtx(ConfNode *conf)
+{
+ SCLogDebug("Can't init JSON output - JSON support was disabled during build.");
+ return NULL;
+}
+
+TmEcode AlertJsonThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ SCLogDebug("Can't init JSON output thread - JSON support was disabled during build.");
+ return TM_ECODE_FAILED;
+}
+
+TmEcode AlertJson (ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq)
+{
+ return TM_ECODE_OK;
+}
+
+TmEcode AlertJsonThreadDeinit(ThreadVars *t, void *data)
+{
+ return TM_ECODE_FAILED;
+}
+
+void AlertJsonRegisterTests (void) {
+}
+
+#else /* implied we do have JSON support */
+
+#include
+
+#define DEFAULT_LOG_FILENAME "json.log"
+#define DEFAULT_ALERT_SYSLOG_FACILITY_STR "local0"
+#define DEFAULT_ALERT_SYSLOG_FACILITY LOG_LOCAL0
+#define DEFAULT_ALERT_SYSLOG_LEVEL LOG_ERR
+#define MODULE_NAME "AlertJSON"
+
+extern uint8_t engine_mode;
+#ifndef OS_WIN32
+static int alert_syslog_level = DEFAULT_ALERT_SYSLOG_LEVEL;
+#endif /* OS_WIN32 */
+
+TmEcode AlertJson (ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
+TmEcode AlertJsonIPv4(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
+TmEcode AlertJsonIPv6(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
+TmEcode AlertJsonThreadInit(ThreadVars *, void *, void **);
+TmEcode AlertJsonThreadDeinit(ThreadVars *, void *);
+void AlertJsonExitPrintStats(ThreadVars *, void *);
+void AlertJsonRegisterTests(void);
+static void AlertJsonDeInitCtx(OutputCtx *);
+
+void TmModuleAlertJsonRegister (void) {
+ tmm_modules[TMM_ALERTJSON].name = MODULE_NAME;
+ tmm_modules[TMM_ALERTJSON].ThreadInit = AlertJsonThreadInit;
+ tmm_modules[TMM_ALERTJSON].Func = AlertJson;
+ tmm_modules[TMM_ALERTJSON].ThreadExitPrintStats = AlertJsonExitPrintStats;
+ tmm_modules[TMM_ALERTJSON].ThreadDeinit = AlertJsonThreadDeinit;
+ tmm_modules[TMM_ALERTJSON].RegisterTests = AlertJsonRegisterTests;
+ tmm_modules[TMM_ALERTJSON].cap_flags = 0;
+
+ OutputRegisterModule(MODULE_NAME, "json", AlertJsonInitCtx);
+}
+
+/* Default Sensor ID value */
+static uint64_t sensor_id = 0;
+
+enum json_output { ALERT_FILE, ALERT_SYSLOG };
+static enum json_output json_out = ALERT_FILE;
+
+enum json_format { COMPACT, INDENT };
+static enum json_format format = COMPACT;
+
+typedef struct AlertJsonThread_ {
+ /** LogFileCtx has the pointer to the file and a mutex to allow multithreading */
+ LogFileCtx* file_ctx;
+} AlertJsonThread;
+
+static void CreateTimeString (const struct timeval *ts, char *str, size_t size) {
+ time_t time = ts->tv_sec;
+ struct tm local_tm;
+ struct tm *t = (struct tm *)SCLocalTime(time, &local_tm);
+
+ snprintf(str, size, "%02d/%02d/%02d-%02d:%02d:%02d.%06u",
+ t->tm_mon + 1, t->tm_mday, t->tm_year + 1900, t->tm_hour,
+ t->tm_min, t->tm_sec, (uint32_t) ts->tv_usec);
+}
+
+TmEcode AlertJsonIPv4(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq)
+{
+ AlertJsonThread *aft = (AlertJsonThread *)data;
+ int i;
+ char timebuf[64];
+ char *action = "Pass";
+
+ if (p->alerts.cnt == 0)
+ return TM_ECODE_OK;
+
+ CreateTimeString(&p->ts, timebuf, sizeof(timebuf));
+
+ char srcip[16], dstip[16];
+ PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), srcip, sizeof(srcip));
+ PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), dstip, sizeof(dstip));
+ for (i = 0; i < p->alerts.cnt; i++) {
+ PacketAlert *pa = &p->alerts.alerts[i];
+ json_t *js;
+ if (unlikely(pa->s == NULL)) {
+ continue;
+ }
+
+ if ((pa->action & ACTION_DROP) && IS_ENGINE_MODE_IPS(engine_mode)) {
+ action = "Drop";
+ } else if (pa->action & ACTION_DROP) {
+ action = "wDrop";
+ }
+
+ char proto[16] = "";
+ if (SCProtoNameValid(IPV4_GET_IPPROTO(p)) == TRUE) {
+ strlcpy(proto, known_proto[IPV4_GET_IPPROTO(p)], sizeof(proto));
+ } else {
+ snprintf(proto, sizeof(proto), "PROTO:%03" PRIu32, IPV4_GET_IPPROTO(p));
+ }
+ js = json_pack("{"
+ "ss"
+ "ss"
+ "si"
+ "si"
+ "si"
+ "ss"
+ "ss"
+ "si"
+ "ss"
+ "ss"
+ "si"
+ "ss"
+ "si}",
+ "time", timebuf,
+ "action", action,
+ "gid", pa->s->gid,
+ "id", pa->s->id,
+ "rev", pa->s->rev,
+ "msg", pa->s->msg,
+ "class", pa->s->class_msg,
+ "pri", pa->s->prio,
+ "proto", proto,
+ "srcip", srcip,
+ "srcport", p->sp,
+ "dstip", dstip,
+ "dstport", p->dp
+ );
+
+ if (js == NULL)
+ return TM_ECODE_FAILED;
+
+ SCMutexLock(&aft->file_ctx->fp_mutex);
+ if (json_out == ALERT_FILE) {
+ json_dumpf(js, aft->file_ctx->fp,
+ ((format == INDENT) ? JSON_INDENT(2) : 0) |
+ JSON_PRESERVE_ORDER|JSON_COMPACT|JSON_ENSURE_ASCII);
+ if (format == INDENT) {
+ fputs("\n", aft->file_ctx->fp);
+ }
+ } else {
+ char *js_s;
+ js_s = json_dumps(js, JSON_PRESERVE_ORDER|JSON_COMPACT|JSON_ENSURE_ASCII);
+ if (js_s) {
+ syslog(alert_syslog_level, "%s", js_s);
+ free(js_s);
+ }
+ }
+ aft->file_ctx->alerts++;
+ SCMutexUnlock(&aft->file_ctx->fp_mutex);
+ free(js);
+ }
+
+ return TM_ECODE_OK;
+}
+
+TmEcode AlertJsonIPv6(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq)
+{
+ AlertJsonThread *aft = (AlertJsonThread *)data;
+ int i;
+ char timebuf[64];
+ char *action = "Pass";
+
+ if (p->alerts.cnt == 0)
+ return TM_ECODE_OK;
+
+ CreateTimeString(&p->ts, timebuf, sizeof(timebuf));
+
+ char srcip[46], dstip[46];
+ PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), srcip, sizeof(srcip));
+ PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), dstip, sizeof(dstip));
+ for (i = 0; i < p->alerts.cnt; i++) {
+ PacketAlert *pa = &p->alerts.alerts[i];
+ json_t *js;
+ if (unlikely(pa->s == NULL)) {
+ continue;
+ }
+
+ if ((pa->action & ACTION_DROP) && IS_ENGINE_MODE_IPS(engine_mode)) {
+ action = "Drop";
+ } else if (pa->action & ACTION_DROP) {
+ action = "wDrop";
+ }
+
+ char proto[16] = "";
+ if (SCProtoNameValid(IP_GET_IPPROTO(p)) == TRUE) {
+ strlcpy(proto, known_proto[IP_GET_IPPROTO(p)], sizeof(proto));
+ } else {
+ snprintf(proto, sizeof(proto), "PROTO:%03" PRIu32, IP_GET_IPPROTO(p));
+ }
+ js = json_pack("{"
+ "ss"
+ "ss"
+ "si"
+ "si"
+ "si"
+ "ss"
+ "ss"
+ "si"
+ "ss"
+ "ss"
+ "si"
+ "ss"
+ "si}",
+ "time", timebuf,
+ "action", action,
+ "gid", pa->s->gid,
+ "id", pa->s->id,
+ "rev", pa->s->rev,
+ "msg", pa->s->msg,
+ "class", pa->s->class_msg,
+ "pri", pa->s->prio,
+ "proto", proto,
+ "srcip", srcip,
+ "srcport", p->sp,
+ "dstip", dstip,
+ "dstport", p->dp
+ );
+
+ if (js == NULL)
+ return TM_ECODE_FAILED;
+
+ SCMutexLock(&aft->file_ctx->fp_mutex);
+ if (json_out == ALERT_FILE) {
+ json_dumpf(js, aft->file_ctx->fp,
+ ((format == INDENT) ? JSON_INDENT(2) : 0) |
+ JSON_PRESERVE_ORDER|JSON_COMPACT|JSON_ENSURE_ASCII);
+ if (format == INDENT) {
+ fputs("\n", aft->file_ctx->fp);
+ }
+ } else {
+ char *js_s;
+ js_s = json_dumps(js, JSON_PRESERVE_ORDER|JSON_COMPACT|JSON_ENSURE_ASCII);
+ if (js_s) {
+ syslog(alert_syslog_level, "%s", js_s);
+ free(js_s);
+ }
+ }
+ aft->file_ctx->alerts++;
+ SCMutexUnlock(&aft->file_ctx->fp_mutex);
+ free(js);
+ }
+
+ return TM_ECODE_OK;
+}
+
+TmEcode AlertJsonDecoderEvent(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq)
+{
+ AlertJsonThread *aft = (AlertJsonThread *)data;
+ int i;
+ char timebuf[64];
+ char *action = "Pass";
+
+ if (p->alerts.cnt == 0)
+ return TM_ECODE_OK;
+
+ CreateTimeString(&p->ts, timebuf, sizeof(timebuf));
+
+ for (i = 0; i < p->alerts.cnt; i++) {
+ PacketAlert *pa = &p->alerts.alerts[i];
+ json_t *js;
+ if (unlikely(pa->s == NULL)) {
+ continue;
+ }
+
+ if ((pa->action & ACTION_DROP) && IS_ENGINE_MODE_IPS(engine_mode)) {
+ action = "Drop";
+ } else if (pa->action & ACTION_DROP) {
+ action = "wDrop";
+ }
+
+ char buf[(32 * 3) + 1];
+ PrintRawLineHexBuf(buf, sizeof(buf), GET_PKT_DATA(p), GET_PKT_LEN(p) < 32 ? GET_PKT_LEN(p) : 32);
+
+ js = json_pack("{"
+ "ss"
+ "ss"
+ "si"
+ "si"
+ "si"
+ "ss"
+ "ss"
+ "si"
+ "ss}",
+ "time", timebuf,
+ "action", action,
+ "gid", pa->s->gid,
+ "id", pa->s->id,
+ "rev", pa->s->rev,
+ "msg", pa->s->msg,
+ "class", pa->s->class_msg,
+ "pri", pa->s->prio,
+ "pkt", buf
+ );
+
+ if (js == NULL)
+ return TM_ECODE_FAILED;
+
+ SCMutexLock(&aft->file_ctx->fp_mutex);
+ if (json_out == ALERT_FILE) {
+ json_dumpf(js, aft->file_ctx->fp,
+ ((format == INDENT) ? JSON_INDENT(2) : 0) |
+ JSON_PRESERVE_ORDER|JSON_COMPACT|JSON_ENSURE_ASCII);
+ if (format == INDENT) {
+ fputs("\n", aft->file_ctx->fp);
+ }
+ } else {
+ char *js_s;
+ js_s = json_dumps(js, JSON_PRESERVE_ORDER|JSON_COMPACT|JSON_ENSURE_ASCII);
+ if (js_s) {
+ syslog(alert_syslog_level, "%s", js_s);
+ free(js_s);
+ }
+ }
+ aft->file_ctx->alerts++;
+ SCMutexUnlock(&aft->file_ctx->fp_mutex);
+ free(js);
+ }
+
+ return TM_ECODE_OK;
+}
+
+TmEcode AlertJson (ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq)
+{
+ if (PKT_IS_IPV4(p)) {
+ return AlertJsonIPv4(tv, p, data, pq, postpq);
+ } else if (PKT_IS_IPV6(p)) {
+ return AlertJsonIPv6(tv, p, data, pq, postpq);
+ } else if (p->events.cnt > 0) {
+ return AlertJsonDecoderEvent(tv, p, data, pq, postpq);
+ }
+
+ return TM_ECODE_OK;
+}
+
+TmEcode AlertJsonThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+ AlertJsonThread *aft = SCMalloc(sizeof(AlertJsonThread));
+ if (unlikely(aft == NULL))
+ return TM_ECODE_FAILED;
+ memset(aft, 0, sizeof(AlertJsonThread));
+ if(initdata == NULL)
+ {
+ SCLogDebug("Error getting context for AlertJson. \"initdata\" argument NULL");
+ SCFree(aft);
+ return TM_ECODE_FAILED;
+ }
+ /** Use the Ouptut Context (file pointer and mutex) */
+ //aft->ctx = ((OutputCtx *)initdata)->data;
+ aft->file_ctx = ((OutputCtx *)initdata)->data;
+
+ *data = (void *)aft;
+ return TM_ECODE_OK;
+}
+
+TmEcode AlertJsonThreadDeinit(ThreadVars *t, void *data)
+{
+ AlertJsonThread *aft = (AlertJsonThread *)data;
+ if (aft == NULL) {
+ return TM_ECODE_OK;
+ }
+
+ SCFree(aft);
+ return TM_ECODE_OK;
+}
+
+void AlertJsonExitPrintStats(ThreadVars *tv, void *data) {
+ AlertJsonThread *aft = (AlertJsonThread *)data;
+ if (aft == NULL) {
+ return;
+ }
+
+ SCLogInfo("JSON output wrote %" PRIu64 " alerts", aft->file_ctx->alerts);
+
+}
+
+/**
+ * \brief Create a new LogFileCtx for "fast" output style.
+ * \param conf The configuration node for this output.
+ * \return A LogFileCtx pointer on success, NULL on failure.
+ */
+OutputCtx *AlertJsonInitCtx(ConfNode *conf)
+{
+ LogFileCtx *logfile_ctx = LogFileNewCtx();
+ if (logfile_ctx == NULL) {
+ SCLogDebug("AlertJsonInitCtx: Could not create nnew LogFileCtx");
+ return NULL;
+ }
+
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+ if (unlikely(output_ctx == NULL))
+ return NULL;
+ output_ctx->data = logfile_ctx;
+ output_ctx->DeInit = AlertJsonDeInitCtx;
+
+ if (conf) {
+ const char *output_s = ConfNodeLookupChildValue(conf, "output");
+ if (output_s != NULL) {
+ if (strcmp(output_s, "file") == 0) {
+ json_out = ALERT_FILE;
+ } else if (strcmp(output_s, "syslog") == 0) {
+ json_out = ALERT_SYSLOG;
+ } else {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "Invalid JSON output option: %s", output_s);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (json_out == ALERT_FILE) {
+
+ if (SCConfLogOpenGeneric(conf, logfile_ctx, DEFAULT_LOG_FILENAME) < 0) {
+ LogFileFreeCtx(logfile_ctx);
+ return NULL;
+ }
+
+ const char *format_s = ConfNodeLookupChildValue(conf, "format");
+ if (format_s != NULL) {
+ if (strcmp(format_s, "indent") == 0) {
+ format = INDENT;
+ } else if (strcmp(format_s, "compact") == 0) {
+ format = COMPACT;
+ } else {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "Invalid JSON format option: %s", format_s);
+ exit(EXIT_FAILURE);
+ }
+ }
+ } else {
+ const char *facility_s = ConfNodeLookupChildValue(conf, "facility");
+ if (facility_s == NULL) {
+ facility_s = DEFAULT_ALERT_SYSLOG_FACILITY_STR;
+ }
+
+ int facility = SCMapEnumNameToValue(facility_s, SCSyslogGetFacilityMap());
+ if (facility == -1) {
+ SCLogWarning(SC_ERR_INVALID_ARGUMENT, "Invalid syslog facility: \"%s\","
+ " now using \"%s\" as syslog facility", facility_s,
+ DEFAULT_ALERT_SYSLOG_FACILITY_STR);
+ facility = DEFAULT_ALERT_SYSLOG_FACILITY;
+ }
+
+ const char *level_s = ConfNodeLookupChildValue(conf, "level");
+ if (level_s != NULL) {
+ int level = SCMapEnumNameToValue(level_s, SCSyslogGetLogLevelMap());
+ if (level != -1) {
+ alert_syslog_level = level;
+ }
+ }
+
+ const char *ident = ConfNodeLookupChildValue(conf, "identity");
+ /* if null we just pass that to openlog, which will then
+ * figure it out by itself. */
+
+ openlog(ident, LOG_PID|LOG_NDELAY, facility);
+
+ }
+
+ const char *sensor_id_s = ConfNodeLookupChildValue(conf, "sensor-id");
+ if (sensor_id_s != NULL) {
+ if (ByteExtractStringUint64(&sensor_id, 10, 0, sensor_id_s) == -1) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "Failed to initialize broccoli output, "
+ "invalid sensor-is: %s", sensor_id_s);
+ exit(EXIT_FAILURE);
+ }
+ sensor_id = htonl(sensor_id);
+ }
+ }
+
+ return output_ctx;
+}
+
+static void AlertJsonDeInitCtx(OutputCtx *output_ctx)
+{
+ LogFileCtx *logfile_ctx = (LogFileCtx *)output_ctx->data;
+ LogFileFreeCtx(logfile_ctx);
+ SCFree(output_ctx);
+}
+
+/*------------------------------Unittests-------------------------------------*/
+
+#ifdef UNITTESTS
+
+int AlertBroccoliTest01()
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *) "GET /one/ HTTP/1.1\r\n"
+ "Host: one.example.org\r\n";
+
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+
+ memset(&th_v, 0, sizeof(th_v));
+ p = UTHBuildPacket(buf, buflen, IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ return result;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ SCClassConfGenerateValidDummyClassConfigFD01();
+ SCClassConfLoadClassficationConfigFile(de_ctx);
+ SCClassConfDeleteDummyClassificationConfigFD();
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"FastLog test\"; content:\"GET\"; "
+ "Classtype:unknown; sid:1;)");
+ result = (de_ctx->sig_list != NULL);
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (p->alerts.cnt == 1)
+ result = (strcmp(p->alerts.alerts[0].s->class_msg, "Unknown are we") == 0);
+ else
+ result = 0;
+
+#ifdef __SC_CUDA_SUPPORT__
+ B2gCudaKillDispatcherThreadRC();
+ if (SCCudaHlPushCudaContextFromModule("SC_RULES_CONTENT_B2G_CUDA") == -1) {
+ printf("Call to SCCudaHlPushCudaContextForModule() failed\n");
+ return 0;
+ }
+#endif
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+int AlertBroccoliTest02()
+{
+ int result = 0;
+ uint8_t *buf = (uint8_t *) "GET /one/ HTTP/1.1\r\n"
+ "Host: one.example.org\r\n";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx;
+
+ memset(&th_v, 0, sizeof(th_v));
+
+ p = UTHBuildPacket(buf, buflen, IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ return result;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ SCClassConfGenerateValidDummyClassConfigFD01();
+ SCClassConfLoadClassficationConfigFile(de_ctx);
+ SCClassConfDeleteDummyClassificationConfigFD();
+
+ de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"FastLog test\"; content:\"GET\"; "
+ "Classtype:unknown; sid:1;)");
+ result = (de_ctx->sig_list != NULL);
+ if (result == 0)
+ printf("sig parse failed: ");
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (p->alerts.cnt == 1) {
+ result = (strcmp(p->alerts.alerts[0].s->class_msg, "Unknown Traffic") != 0);
+ if (result == 0)
+ printf("p->alerts.alerts[0].class_msg %s: ", p->alerts.alerts[0].s->class_msg);
+
+ result = (strcmp(p->alerts.alerts[0].s->class_msg,
+ "Unknown are we") == 0);
+ if (result == 0)
+ printf("p->alerts.alerts[0].class_msg %s: ", p->alerts.alerts[0].s->class_msg);
+ } else {
+ result = 0;
+ }
+
+#ifdef __SC_CUDA_SUPPORT__
+ B2gCudaKillDispatcherThreadRC();
+ if (SCCudaHlPushCudaContextFromModule("SC_RULES_CONTENT_B2G_CUDA") == -1) {
+ printf("Call to SCCudaHlPushCudaContextForModule() failed\n");
+ return 0;
+ }
+#endif
+
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+#endif /* UNITTESTS */
+
+/**
+ * \brief This function registers unit tests for AlertFastLog API.
+ */
+void AlertJsonRegisterTests(void)
+{
+
+#ifdef UNITTESTS
+
+#ifdef __SC_CUDA_SUPPORT__
+ UtRegisterTest("AlertFastLogCudaContextInit",
+ SCCudaHlTestEnvCudaContextInit, 1);
+#endif
+
+ UtRegisterTest("AlertBroccoliLogTest01", AlertBroccoliLogTest01, 1);
+ UtRegisterTest("AlertBroccoliLogTest02", AlertBroccoliLogTest02, 1);
+
+#ifdef __SC_CUDA_SUPPORT__
+ UtRegisterTest("AlertFastLogCudaContextDeInit",
+ SCCudaHlTestEnvCudaContextDeInit, 1);
+#endif
+
+#endif /* UNITTESTS */
+
+}
+#endif
diff --git a/src/alert-json.h b/src/alert-json.h
new file mode 100644
index 0000000000..1317f5b22d
--- /dev/null
+++ b/src/alert-json.h
@@ -0,0 +1,32 @@
+/* Copyright (C) 2007-2013 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Tom DeCanio
+ */
+
+#ifndef __ALERT_JSON_H__
+#define __ALERT_JSON_H__
+
+void TmModuleAlertJsonRegister (void);
+void TmModuleAlertJsonIPv4Register (void);
+void TmModuleAlertJsonPv6Register (void);
+OutputCtx *AlertJsonInitCtx(ConfNode *);
+
+#endif /* __ALERT_JSON_H__ */
diff --git a/src/suricata.c b/src/suricata.c
index bca1e201ba..389dcac312 100644
--- a/src/suricata.c
+++ b/src/suricata.c
@@ -78,6 +78,7 @@
#include "alert-prelude.h"
#include "alert-syslog.h"
#include "alert-pcapinfo.h"
+#include "alert-json.h"
#include "log-droplog.h"
#include "log-httplog.h"
@@ -791,6 +792,8 @@ void RegisterAllModules()
TmModuleAlertPcapInfoRegister();
/* drop log */
TmModuleLogDropLogRegister();
+ /* json log */
+ TmModuleAlertJsonRegister();
/* http log */
TmModuleLogHttpLogRegister();
TmModuleLogTlsLogRegister();
diff --git a/src/tm-threads-common.h b/src/tm-threads-common.h
index ed595609a3..6d5ec1469b 100644
--- a/src/tm-threads-common.h
+++ b/src/tm-threads-common.h
@@ -48,6 +48,7 @@ typedef enum {
TMM_ALERTPRELUDE,
TMM_ALERTDEBUGLOG,
TMM_ALERTSYSLOG,
+ TMM_ALERTJSON,
TMM_LOGDROPLOG,
TMM_ALERTSYSLOG4,
TMM_ALERTSYSLOG6,
diff --git a/suricata.yaml.in b/suricata.yaml.in
index 7222e229df..199bc7c29e 100644
--- a/suricata.yaml.in
+++ b/suricata.yaml.in
@@ -206,6 +206,13 @@ outputs:
#level: Info ## possible levels: Emergency, Alert, Critical,
## Error, Warning, Notice, Info, Debug
+ # alerts output to JSON
+ - json:
+ enabled: yes
+ format: compact # alternatives 'compact', 'indent'
+ #filename: json.log
+ #output: syslog # alternatives 'file', 'syslog'
+
# a line based information for dropped packets in IPS mode
- drop:
enabled: no
| |