]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
pcap-log: support dynamic file names in multi
authorVictor Julien <victor@inliniac.net>
Fri, 6 Jun 2014 14:05:11 +0000 (16:05 +0200)
committerVictor Julien <victor@inliniac.net>
Fri, 18 Jul 2014 10:35:31 +0000 (12:35 +0200)
When using multi mode, the filename can use a few variables:

%n -- thread number, where the 1st thread has 1, and it increments
%i -- thread id (system thread id, similar to pid)
%t -- timestamp, where seconds or seconds+usecs depends on
      the ts-format option.

Example:
filename: filename: pcaps/%n/pcap.%t
This will translate to: pcaps/3/pcap.1256792217 for the 3rd thread.

Note that while it's possible to use directories, they won't be
created. So make sure they exist.

src/log-pcap.c

index 3f6f147ca5f1046c7d30722be934f8d3fb490e07..7b8cebd778e5be9ae5555c1314a532f2f1983089 100644 (file)
@@ -45,6 +45,7 @@
 #include "util-byte.h"
 #include "util-misc.h"
 #include "util-cpu.h"
+#include "util-atomic.h"
 
 #include "source-pcap.h"
 
@@ -71,6 +72,8 @@
 #define USE_STREAM_DEPTH_DISABLED       0
 #define USE_STREAM_DEPTH_ENABLED        1
 
+SC_ATOMIC_DECLARE(uint32_t, thread_cnt);
+
 typedef struct PcapFileName_ {
     char *filename;
     char *dirname;
@@ -82,6 +85,8 @@ typedef struct PcapLogProfileData_ {
     uint64_t cnt;
 } PcapLogProfileData;
 
+#define MAX_TOKS 9
+
 /**
  * PcapLog thread vars
  *
@@ -114,12 +119,15 @@ typedef struct PcapLogData_ {
 
     TAILQ_HEAD(, PcapFileName_) pcap_file_list;
 
+    uint32_t thread_number;     /**< thread number, first thread is 1, second 2, etc */
     int use_ringbuffer;         /**< ring buffer mode enabled or disabled */
     int timestamp_format;       /**< timestamp format sec or usec */
     char *prefix;               /**< filename prefix */
     char dir[PATH_MAX];         /**< pcap log directory */
     int reported;
     int threads;                /**< number of threads (only set in the global) */
+    char *filename_parts[MAX_TOKS];
+    int filename_part_cnt;
 } PcapLogData;
 
 typedef struct PcapLogThreadData_ {
@@ -148,6 +156,7 @@ void TmModulePcapLogRegister(void)
 
     OutputRegisterModule(MODULE_NAME, "pcap-log", PcapLogInitCtx);
 
+    SC_ATOMIC_INIT(thread_cnt);
     return;
 }
 
@@ -441,6 +450,14 @@ static PcapLogData *PcapLogDataCopy(const PcapLogData *pl)
 
     strlcpy(copy->dir, pl->dir, sizeof(copy->dir));
 
+    int i;
+    for (i = 0; i < pl->filename_part_cnt && i < MAX_TOKS; i++)
+        copy->filename_parts[i] = pl->filename_parts[i];
+    copy->filename_part_cnt = pl->filename_part_cnt;
+
+    /* set thread number, first thread is 1 */
+    copy->thread_number = SC_ATOMIC_ADD(thread_cnt, 1);
+
     SCLogDebug("copied, returning %p", copy);
     return copy;
 }
@@ -552,6 +569,92 @@ static TmEcode PcapLogDataDeinit(ThreadVars *t, void *thread_data)
     return TM_ECODE_OK;
 }
 
+static int ParseFilename(PcapLogData *pl, const char *filename)
+{
+    char *toks[MAX_TOKS] = { NULL };
+    int tok = 0;
+    char str[512] = "";
+    int s = 0;
+    int i, x;
+    char *p = NULL;
+
+    if (filename) {
+        for (i = 0; i < (int)strlen(filename); i++) {
+            if (tok >= MAX_TOKS) {
+                SCLogError(SC_ERR_INVALID_ARGUMENT,
+                        "invalid filename option. Max 2 %%-sign options");
+                goto error;
+            }
+
+            str[s++] = filename[i];
+
+            if (filename[i] == '%') {
+                str[s-1] = '\0';
+                SCLogDebug("filename with %%-sign: %s", str);
+
+                p = SCStrdup(str);
+                if (p == NULL)
+                    goto error;
+                toks[tok++] = p;
+
+                s = 0;
+
+                if (i+1 < (int)strlen(filename)) {
+                    if (tok >= MAX_TOKS) {
+                        SCLogError(SC_ERR_INVALID_ARGUMENT,
+                                "invalid filename option. Max 2 %%-sign options");
+                        goto error;
+                    }
+
+                    if (filename[i+1] != 'n' && filename[i+1] != 't' && filename[i+1] != 'i') {
+                        SCLogError(SC_ERR_INVALID_ARGUMENT,
+                                "invalid filename option. Valid %%-sign options: %%n, %%i and %%t");
+                        goto error;
+                    }
+                    str[0] = '%';
+                    str[1] = filename[i+1];
+                    str[2] = '\0';
+                    p = SCStrdup(str);
+                    if (p == NULL)
+                        goto error;
+                    toks[tok++] = p;
+                    i++;
+                }
+            }
+        }
+        if (s) {
+            if (tok >= MAX_TOKS) {
+                SCLogError(SC_ERR_INVALID_ARGUMENT,
+                        "invalid filename option. Max 3 %%-sign options");
+                goto error;
+
+            }
+            str[s++] = '\0';
+            p = SCStrdup(str);
+            if (p == NULL)
+                goto error;
+            toks[tok++] = p;
+        }
+
+        /* finally, store tokens in the pl */
+        for (i = 0; i < tok; i++) {
+            if (toks[i] == NULL)
+                goto error;
+
+            SCLogDebug("toks[%d] %s", i, toks[i]);
+            pl->filename_parts[i] = toks[i];
+        }
+        pl->filename_part_cnt = tok;
+    }
+    return 0;
+error:
+    for (x = 0; x < MAX_TOKS; x++) {
+        if (toks[x] != NULL)
+            SCFree(toks[x]);
+    }
+    return -1;
+}
+
 /** \brief Fill in pcap logging struct from the provided ConfNode.
  *  \param conf The configuration node for this output.
  *  \retval output_ctx
@@ -598,6 +701,11 @@ static OutputCtx *PcapLogInitCtx(ConfNode *conf)
         exit(EXIT_FAILURE);
     }
 
+    if (filename) {
+        if (ParseFilename(pl, filename) != 0)
+            exit(EXIT_FAILURE);
+    }
+
     pl->size_limit = DEFAULT_LIMIT;
     if (conf != NULL) {
         const char *s_limit = NULL;
@@ -633,8 +741,8 @@ static OutputCtx *PcapLogInitCtx(ConfNode *conf)
                 pl->mode = LOGMODE_MULTI;
             } else if (strcasecmp(s_mode, "normal") != 0) {
                 SCLogError(SC_ERR_INVALID_ARGUMENT,
-                    "log-pcap you must specify \"sguil\" or \"normal\" mode "
-                    "option to be set.");
+                    "log-pcap: invalid mode \"%s\". Valid options: \"normal\", "
+                    "\"sguil\", or \"multi\" mode ", s_mode);
                 exit(EXIT_FAILURE);
             }
         }
@@ -682,7 +790,7 @@ static OutputCtx *PcapLogInitCtx(ConfNode *conf)
     }
 
     SCLogInfo("using %s logging", pl->mode == LOGMODE_SGUIL ?
-              "Sguil compatible" : "normal");
+              "Sguil compatible" : (pl->mode == LOGMODE_MULTI ? "multi" : "normal"));
 
     uint32_t max_file_limit = DEFAULT_FILE_LIMIT;
     if (conf != NULL) {
@@ -845,16 +953,58 @@ static int PcapLogOpenFileCtx(PcapLogData *pl)
                      pl->prefix, (uint32_t)ts.tv_sec, (uint32_t)ts.tv_usec);
         }
     } else if (pl->mode == LOGMODE_MULTI) {
-        long thread_id = SCGetThreadIdLong();
-        uint64_t tid = (uint64_t)thread_id;
-
-        /* create the filename to use */
-        if (pl->timestamp_format == TS_FORMAT_SEC) {
-            snprintf(filename, PATH_MAX, "%s/%s.%"PRIu64".%" PRIu32, pl->dir,
-                     pl->prefix, tid, (uint32_t)ts.tv_sec);
+        if (pl->filename_part_cnt > 0) {
+            /* assemble filename from stored tokens */
+
+            strlcpy(filename, pl->dir, PATH_MAX);
+            strlcat(filename, "/", PATH_MAX);
+
+            int i;
+            for (i = 0; i < pl->filename_part_cnt; i++) {
+                if (pl->filename_parts[i] == NULL ||strlen(pl->filename_parts[i]) == 0)
+                    continue;
+
+                /* handle variables */
+                if (pl->filename_parts[i][0] == '%') {
+                    char str[64] = "";
+                    if (strlen(pl->filename_parts[i]) < 2)
+                        continue;
+
+                    switch(pl->filename_parts[i][1]) {
+                        case 'n':
+                            snprintf(str, sizeof(str), "%u", pl->thread_number);
+                            break;
+                        case 'i':
+                        {
+                            long thread_id = SCGetThreadIdLong();
+                            snprintf(str, sizeof(str), "%"PRIu64, (uint64_t)thread_id);
+                            break;
+                        }
+                        case 't':
+                        /* create the filename to use */
+                        if (pl->timestamp_format == TS_FORMAT_SEC) {
+                            snprintf(str, sizeof(str), "%"PRIu32, (uint32_t)ts.tv_sec);
+                        } else {
+                            snprintf(str, sizeof(str), "%"PRIu32".%"PRIu32,
+                                    (uint32_t)ts.tv_sec, (uint32_t)ts.tv_usec);
+                        }
+                    }
+                    strlcat(filename, str, PATH_MAX);
+
+                /* copy the rest over */
+                } else {
+                    strlcat(filename, pl->filename_parts[i], PATH_MAX);
+                }
+            }
         } else {
-            snprintf(filename, PATH_MAX, "%s/%s.%"PRIu64".%" PRIu32 ".%" PRIu32, pl->dir,
-                     pl->prefix, tid, (uint32_t)ts.tv_sec, (uint32_t)ts.tv_usec);
+            /* create the filename to use */
+            if (pl->timestamp_format == TS_FORMAT_SEC) {
+                snprintf(filename, PATH_MAX, "%s/%s.%u.%" PRIu32, pl->dir,
+                        pl->prefix, pl->thread_number, (uint32_t)ts.tv_sec);
+            } else {
+                snprintf(filename, PATH_MAX, "%s/%s.%u.%" PRIu32 ".%" PRIu32, pl->dir,
+                        pl->prefix, pl->thread_number, (uint32_t)ts.tv_sec, (uint32_t)ts.tv_usec);
+            }
         }
         SCLogDebug("multi-mode: filename %s", filename);
     }