diff --git a/src/log-pcap.c b/src/log-pcap.c index 3f6f147ca5..7b8cebd778 100644 --- a/src/log-pcap.c +++ b/src/log-pcap.c @@ -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); }