]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
output-json: add redis support
authorEric Leblond <eric@regit.org>
Sat, 7 Feb 2015 12:10:23 +0000 (13:10 +0100)
committerVictor Julien <victor@inliniac.net>
Thu, 22 Oct 2015 08:01:05 +0000 (10:01 +0200)
This patch adds redis support to JSON output.

configure.ac
src/output-json.c
src/output-json.h
src/util-logopenfile.c
src/util-logopenfile.h
suricata.yaml.in

index 97ce3ab1742ff9251b1ef369bce489bd27272a8f..9e13938304c7473dc839661b77e69485316d9a91 100644 (file)
         LDFLAGS="${LDFLAGS} -pie"
     fi
 
+# libhiredis
+    AC_ARG_ENABLE(hiredis,
+               AS_HELP_STRING([--enable-hiredis],[Enable Redis support]),
+               [ enable_hiredis="yes"],
+               [ enable_hiredis="no"])
+    AC_ARG_WITH(libhiredis_includes,
+            [  --with-libhiredis-includes=DIR  libhiredis include directory],
+            [with_libhiredis_includes="$withval"],[with_libhiredis_includes="no"])
+    AC_ARG_WITH(libhiredis_libraries,
+            [  --with-libhiredis-libraries=DIR    libhiredis library directory],
+            [with_libhiredis_libraries="$withval"],[with_libhiredis_libraries="no"])
+
+    if test "$enable_hiredis" = "yes"; then
+        if test "$with_libhiredis_includes" != "no"; then
+            CPPFLAGS="${CPPFLAGS} -I${with_libhiredis_includes}"
+        fi
+
+        AC_CHECK_HEADER("hiredis/hiredis.h",HIREDIS="yes",HIREDIS="no")
+        if test "$HIREDIS" = "yes"; then
+            if test "$with_libhiredis_libraries" != "no"; then
+                LDFLAGS="${LDFLAGS}  -L${with_libhiredis_libraries}"
+            fi
+            AC_CHECK_LIB(hiredis, redisConnect,, HIREDIS="no")
+        fi
+        if test "$HIREDIS" = "no"; then
+            echo
+            echo "   ERROR!  libhiredis library not found, go get it"
+            echo "   from https://github.com/redis/hiredis or your distribution:"
+            echo
+            echo "   Ubuntu: apt-get install libhiredis-dev"
+            echo "   Fedora: yum install libhiredis-devel"
+            echo
+            exit 1
+        fi
+        if test "$HIREDIS" = "yes"; then
+            AC_DEFINE([HAVE_LIBHIREDIS],[1],[libhiredis available])
+            enable_hiredis="yes"
+        fi
+    fi
+
 # get cache line size
     AC_PATH_PROG(HAVE_GETCONF_CMD, getconf, "no")
     if test "$HAVE_GETCONF_CMD" != "no"; then
@@ -1810,6 +1850,7 @@ SURICATA_BUILD_CONF="Suricata Configuration:
   libnss support:                          ${enable_nss}
   libnspr support:                         ${enable_nspr}
   libjansson support:                      ${enable_jansson}
+  hiredis support:                         ${enable_hiredis}
   Prelude support:                         ${enable_prelude}
   PCRE jit:                                ${pcre_jit_available}
   LUA support:                             ${enable_lua}
index a8e5456351d118bb896716589c217f23588d4d7b..eb937c4ef561bc9e34dd43758a3f910c46a456ac 100644 (file)
@@ -110,6 +110,9 @@ void OutputJsonRegisterTests (void)
 #else /* implied we do have JSON support */
 
 #include <jansson.h>
+#if HAVE_LIBHIREDIS
+#include <hiredis/hiredis.h>
+#endif
 
 #define DEFAULT_LOG_FILENAME "eve.json"
 #define DEFAULT_ALERT_SYSLOG_FACILITY_STR       "local0"
@@ -379,6 +382,28 @@ int OutputJSONBuffer(json_t *js, LogFileCtx *file_ctx, MemBuffer *buffer)
         file_ctx->Write((const char *)MEMBUFFER_BUFFER(buffer),
             MEMBUFFER_OFFSET(buffer), file_ctx);
     }
+#if HAVE_LIBHIREDIS
+    else if (file_ctx->type == LOGFILE_TYPE_REDIS) {
+        /* FIXME go async here */
+        redisReply *reply = redisCommand(file_ctx->redis, "%s %s %s",
+                                         file_ctx->redis_setup.command,
+                                         file_ctx->redis_setup.key,
+                                         js_s);
+        switch (reply->type) {
+            case REDIS_REPLY_ERROR:
+                SCLogWarning(SC_WARN_NO_UNITTESTS, "Redis error: %s", reply->str);
+                break;
+            case REDIS_REPLY_INTEGER:
+                SCLogDebug("Redis integer %lld", reply->integer);
+                break;
+            default:
+                SCLogError(SC_ERR_INVALID_VALUE,
+                           "Redis default triggered with %d", reply->type);
+                break;
+        }
+        freeReplyObject(reply);
+    }
+#endif
     SCMutexUnlock(&file_ctx->fp_mutex);
     free(js_s);
     return 0;
@@ -477,6 +502,14 @@ OutputCtx *OutputJsonInitCtx(ConfNode *conf)
                 json_ctx->json_out = LOGFILE_TYPE_UNIX_DGRAM;
             } else if (strcmp(output_s, "unix_stream") == 0) {
                 json_ctx->json_out = LOGFILE_TYPE_UNIX_STREAM;
+            } else if (strcmp(output_s, "redis") == 0) {
+#if HAVE_LIBHIREDIS
+                json_ctx->json_out = LOGFILE_TYPE_REDIS;
+#else
+                SCLogError(SC_ERR_INVALID_ARGUMENT,
+                           "redis JSON output option is not compiled");
+                exit(EXIT_FAILURE);
+#endif
             } else {
                 SCLogError(SC_ERR_INVALID_ARGUMENT,
                            "Invalid JSON output option: %s", output_s);
@@ -549,6 +582,58 @@ OutputCtx *OutputJsonInitCtx(ConfNode *conf)
             openlog(ident, LOG_PID|LOG_NDELAY, facility);
 
         }
+#if HAVE_LIBHIREDIS
+        else if (json_ctx->json_out == LOGFILE_TYPE_REDIS) {
+            ConfNode *redis_node = ConfNodeLookupChild(conf, "redis");
+            const char *redis_server = NULL;
+            const char *redis_port = NULL;
+            const char *redis_mode = NULL;
+            const char *redis_key = NULL;
+
+            if (redis_node) {
+                redis_server = ConfNodeLookupChildValue(redis_node, "server");
+                redis_port =  ConfNodeLookupChildValue(redis_node, "port");
+                redis_mode =  ConfNodeLookupChildValue(redis_node, "mode");
+                redis_key =  ConfNodeLookupChildValue(redis_node, "key");
+            }
+            if (!redis_server) {
+                redis_server = "127.0.0.1";
+                SCLogInfo("Using default redis server (127.0.0.1)");
+            }
+            if (!redis_port)
+                redis_port = "6379";
+            if (!redis_mode)
+                redis_mode = "list";
+            if (!redis_key)
+                redis_key = "suricata";
+            json_ctx->file_ctx->redis_setup.key = SCStrdup(redis_key);
+
+            if (!json_ctx->file_ctx->redis_setup.key) {
+                SCLogError(SC_ERR_MEM_ALLOC, "Unable to allocate redis key name");
+                exit(EXIT_FAILURE);
+            }
+
+            if (!strcmp(redis_mode, "list")) {
+                json_ctx->file_ctx->redis_setup.command = SCStrdup("LPUSH");
+                if (!json_ctx->file_ctx->redis_setup.command) {
+                    SCLogError(SC_ERR_MEM_ALLOC, "Unable to allocate redis key command");
+                    exit(EXIT_FAILURE);
+                }
+            } else {
+                json_ctx->file_ctx->redis_setup.command = SCStrdup("PUBLISH");
+                if (!json_ctx->file_ctx->redis_setup.command) {
+                    SCLogError(SC_ERR_MEM_ALLOC, "Unable to allocate redis key command");
+                    exit(EXIT_FAILURE);
+                }
+            }
+            redisContext *c = redisConnect(redis_server, atoi(redis_port));
+            if (c != NULL && c->err) {
+                SCLogError(SC_ERR_SOCKET, "Error connecting to redis server: %s\n", c->errstr);
+                exit(EXIT_FAILURE);
+            }
+            json_ctx->file_ctx->redis = c;
+        }
+#endif
 
         const char *sensor_id_s = ConfNodeLookupChildValue(conf, "sensor-id");
         if (sensor_id_s != NULL) {
index 15c2829555c2ce5813ba7c9c37d5e2191987c138..89e11d86ce0153dbb1e3380564c0e6505859ace5 100644 (file)
@@ -40,7 +40,6 @@ TmEcode OutputJSON(json_t *js, void *data, uint64_t *count);
 int OutputJSONBuffer(json_t *js, LogFileCtx *file_ctx, MemBuffer *buffer);
 OutputCtx *OutputJsonInitCtx(ConfNode *);
 
-
 enum JsonFormat { COMPACT, INDENT };
 
 /*
index b25c4a82705fc3ddc41b31815faef6163e8dcbf8..b22988ae84d102875e02082b074ad67283069a38 100644 (file)
@@ -367,6 +367,12 @@ int LogFileFreeCtx(LogFileCtx *lf_ctx)
         SCMutexUnlock(&lf_ctx->fp_mutex);
     }
 
+#ifdef HAVE_LIBHIREDIS
+    if (lf_ctx->type == LOGFILE_TYPE_REDIS && lf_ctx->redis) {
+        redisFree(lf_ctx->redis);
+    }
+#endif
+
     SCMutexDestroy(&lf_ctx->fp_mutex);
 
     if (lf_ctx->prefix != NULL)
index d345475d220c11f478eca4a7b076793ae3b8fac1..b5ac9823deac8091bb15996357630bc626f3c97d 100644 (file)
@@ -27,6 +27,8 @@
 #include "conf.h"            /* ConfNode   */
 #include "tm-modules.h"      /* LogFileCtx */
 
+#include "hiredis/hiredis.h"
+
 typedef struct {
     uint16_t fileno;
 } PcieFile;
@@ -34,15 +36,31 @@ typedef struct {
 enum LogFileType { LOGFILE_TYPE_FILE,
                    LOGFILE_TYPE_SYSLOG,
                    LOGFILE_TYPE_UNIX_DGRAM,
-                   LOGFILE_TYPE_UNIX_STREAM };
+                   LOGFILE_TYPE_UNIX_STREAM,
+                   LOGFILE_TYPE_REDIS };
+
+enum RedisMode { REDIS_LIST, REDIS_CHANNEL };
+
+typedef struct RedisSetup_ {
+    enum RedisMode mode;
+    char *command;
+    char *key;
+} RedisSetup;
 
 /** Global structure for Output Context */
 typedef struct LogFileCtx_ {
     union {
         FILE *fp;
         PcieFile *pcie_fp;
+#ifdef HAVE_LIBHIREDIS
+        redisContext *redis;
+#endif
     };
 
+#ifdef HAVE_LIBHIREDIS
+    RedisSetup redis_setup;
+#endif
+
     int (*Write)(const char *buffer, int buffer_len, struct LogFileCtx_ *fp);
     void (*Close)(struct LogFileCtx_ *fp);
 
index 9ab40098fdba72b416543ab1baab58e7d20f6a36..30921751a92950ca0cccff3ab26e3f689783d1f4 100644 (file)
@@ -92,7 +92,7 @@ outputs:
   # Extensible Event Format (nicknamed EVE) event log in JSON format
   - eve-log:
       enabled: yes
-      filetype: regular #regular|syslog|unix_dgram|unix_stream
+      filetype: regular #regular|syslog|unix_dgram|unix_stream|redis
       filename: eve.json
       #prefix: "@cee: " # prefix to prepend to each log entry
       # the following are valid when type: syslog above
@@ -100,6 +100,11 @@ outputs:
       #facility: local5
       #level: Info ## possible levels: Emergency, Alert, Critical,
                    ## Error, Warning, Notice, Info, Debug
+      #redis:
+      #  server: 127.0.0.1
+      #  port: 6379
+      #  mode: list ## possible values: list (default), channel
+      #  key: suricata ## key or channel to use (default to suricata)
       types:
         - alert:
             # payload: yes           # enable dumping payload in Base64