.. option:: -r <path>
- Run in pcap offline mode reading files from pcap file.
+ Run in pcap offline mode reading files from pcap file. If <path> specifies
+ a directory, all files in that directory will be processed in order of
+ modified time maintaining flow state between files.
+
+.. option:: --pcap-file-continuous
+
+ Used with the -r option to indicate that the mode should stay alive until
+ interrupted. This is useful with directories to add new files and not reset
+ flow state between files.
.. option:: -i <interface>
Commands in standard running mode
---------------------------------
+You may need to install suricatasc if you have not done so, running the following command from scripts/suricatasc
+::
+
+ sudo python setup.py install
The set of existing commands is the following:
Success: Successfully added file to list
>>> pcap-file /home/benches/file2.pcap /tmp/file2
Success: Successfully added file to list
+ >>> pcap-file-continuous /home/pcaps /tmp/dirout
+ Success: Successfully added file to list
You can add multiple files without waiting the result: they will be
sequentially processed and the generated log/alert files will be put
into the directory specified as second arguments of the pcap-file
command. You need to provide absolute path to the files and directory
-as suricata don’t know from where the script has been run.
+as suricata doesn’t know from where the script has been run. If you pass
+a directory instead of a file, all files in the directory will be processed. If
+using ``pcap-file-continuous`` and passing in a directory, the directory will
+be monitored for new files being added until you use ``pcap-interrupt`` or
+delete/move the directory.
-To know how much files are waiting to get processed, you can do:
+To know how many files are waiting to get processed, you can do:
::
Success:
"/tmp/test.pcap"
+When passing in a directory, you can see last processed time (modified time of last file) in milliseconds since epoch:
+
+::
+
+ >>> pcap-last-processed
+ Success:
+ 1509138964000
+
+To interrupt directory processing which terminates the current state:
+
+::
+
+ >>> pcap-interrupt
+ Success:
+ "Interrupted"
+
Build your own client
---------------------
class SuricataSC:
def __init__(self, sck_path, verbose=False):
- self.cmd_list=['shutdown','quit','pcap-file','pcap-file-number','pcap-file-list','iface-list','iface-stat','register-tenant','unregister-tenant','register-tenant-handler','unregister-tenant-handler', 'add-hostbit', 'remove-hostbit', 'list-hostbit']
+ self.cmd_list=['shutdown','quit','pcap-file','pcap-file-continuous','pcap-file-number','pcap-file-list','pcap-last-processed','pcap-interrupt','iface-list','iface-stat','register-tenant','unregister-tenant','register-tenant-handler','unregister-tenant-handler', 'add-hostbit', 'remove-hostbit', 'list-hostbit']
self.sck_path = sck_path
self.verbose = verbose
arguments["output-dir"] = output
if tenant != None:
arguments["tenant"] = int(tenant)
+ elif "pcap-file-continuous " in command:
+ try:
+ parts = command.split(' ');
+ except:
+ raise SuricataCommandException("Arguments to command '%s' is missing" % (command))
+ cmd, filename, output = parts[0], parts[1], parts[2]
+ tenant = None
+ if len(parts) > 3:
+ tenant = parts[3]
+ if cmd != "pcap-file":
+ raise SuricataCommandException("Invalid command '%s'" % (command))
+ else:
+ arguments = {}
+ arguments["filename"] = filename
+ arguments["output-dir"] = output
+ arguments["continuous"] = True
+ if tenant != None:
+ arguments["tenant"] = int(tenant)
elif "iface-stat" in command:
try:
[cmd, iface] = command.split(' ', 1)
source-nflog.c source-nflog.h \
source-pcap.c source-pcap.h \
source-pcap-file.c source-pcap-file.h \
+source-pcap-file-directory-helper.c source-pcap-file-directory-helper.h \
+source-pcap-file-helper.c source-pcap-file-helper.h \
source-pfring.c source-pfring.h \
stream.c stream.h \
stream-tcp.c stream-tcp.h stream-tcp-private.h \
char *filename;
char *output_dir;
int tenant_id;
+ time_t delay;
+ time_t poll_interval;
+ bool continuous;
TAILQ_ENTRY(PcapFiles_) next;
} PcapFiles;
typedef struct PcapCommand_ {
TAILQ_HEAD(, PcapFiles_) files;
int running;
- char *currentfile;
+ PcapFiles *current_file;
} PcapCommand;
const char *RunModeUnixSocketGetDefaultMode(void)
#ifdef BUILD_UNIX_SOCKET
static int RunModeUnixSocketMaster(void);
-static int unix_manager_file_task_running = 0;
-static int unix_manager_file_task_failed = 0;
+static int unix_manager_pcap_task_running = 0;
+static int unix_manager_pcap_task_failed = 0;
+static int unix_manager_pcap_task_interrupted = 0;
+static struct timespec unix_manager_pcap_last_processed;
/**
* \brief return list of files in the queue
{
PcapCommand *this = (PcapCommand *) data;
- if (this->currentfile) {
- json_object_set_new(answer, "message", json_string(this->currentfile));
+ if (this->current_file && this->current_file->filename) {
+ json_object_set_new(answer, "message",
+ json_string(this->current_file->filename));
} else {
json_object_set_new(answer, "message", json_string("None"));
}
return TM_ECODE_OK;
}
+static TmEcode UnixSocketPcapLastProcessed(json_t *cmd, json_t *answer, void *data)
+{
+ uint64_t epoch_millis = unix_manager_pcap_last_processed.tv_sec * 1000l +
+ unix_manager_pcap_last_processed.tv_nsec / 100000l;
+ json_object_set_new(answer, "message",
+ json_integer(epoch_millis));
+
+ return TM_ECODE_OK;
+}
+
+static TmEcode UnixSocketPcapInterrupt(json_t *cmd, json_t *answer, void *data)
+{
+ unix_manager_pcap_task_interrupted = 1;
+ json_object_set_new(answer, "message", json_string("Interrupted"));
+
+ return TM_ECODE_OK;
+}
static void PcapFilesFree(PcapFiles *cfile)
{
* \param this a UnixCommand:: structure
* \param filename absolute filename
* \param output_dir absolute name of directory where log will be put
+ * \param tenant_id Id of tenant associated with this file
+ * \param continuous If file should be run in continuous mode
+ * \param delay Delay required for file modified time before being processed
+ * \param poll_interval How frequently directory mode polls for new files
*
* \retval 0 in case of error, 1 in case of success
*/
-static TmEcode UnixListAddFile(PcapCommand *this,
- const char *filename, const char *output_dir, int tenant_id)
+static TmEcode UnixListAddFile(
+ PcapCommand *this,
+ const char *filename,
+ const char *output_dir,
+ int tenant_id,
+ bool continuous,
+ time_t delay,
+ time_t poll_interval
+)
{
PcapFiles *cfile = NULL;
if (filename == NULL || this == NULL)
}
cfile->tenant_id = tenant_id;
+ cfile->continuous = continuous;
+ cfile->delay = delay;
+ cfile->poll_interval = poll_interval;
TAILQ_INSERT_TAIL(&this->files, cfile, next);
return TM_ECODE_OK;
static TmEcode UnixSocketAddPcapFile(json_t *cmd, json_t* answer, void *data)
{
PcapCommand *this = (PcapCommand *) data;
- int ret;
const char *filename;
const char *output_dir;
int tenant_id = 0;
+ bool continuous = false;
+ time_t delay = 30;
+ time_t poll_interval = 5;
#ifdef OS_WIN32
struct _stat st;
#else
#endif /* OS_WIN32 */
json_t *jarg = json_object_get(cmd, "filename");
- if(!json_is_string(jarg)) {
- SCLogInfo("error: command is not a string");
- json_object_set_new(answer, "message", json_string("command is not a string"));
+ if (!json_is_string(jarg)) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "filename is not a string");
+ json_object_set_new(answer, "message",
+ json_string("filename is not a string"));
return TM_ECODE_FAILED;
}
filename = json_string_value(jarg);
#ifdef OS_WIN32
- if(_stat(filename, &st) != 0) {
+ if (_stat(filename, &st) != 0) {
#else
- if(stat(filename, &st) != 0) {
+ if (stat(filename, &st) != 0) {
#endif /* OS_WIN32 */
- json_object_set_new(answer, "message", json_string("File does not exist"));
+ json_object_set_new(answer, "message",
+ json_string("filename does not exist"));
return TM_ECODE_FAILED;
}
json_t *oarg = json_object_get(cmd, "output-dir");
if (oarg != NULL) {
- if(!json_is_string(oarg)) {
- SCLogInfo("error: output dir is not a string");
- json_object_set_new(answer, "message", json_string("output dir is not a string"));
+ if (!json_is_string(oarg)) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "output-dir is not a string");
+
+ json_object_set_new(answer, "message",
+ json_string("output-dir is not a string"));
return TM_ECODE_FAILED;
}
output_dir = json_string_value(oarg);
} else {
- SCLogInfo("error: can't get output-dir");
- json_object_set_new(answer, "message", json_string("output dir param is mandatory"));
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "can't get output-dir");
+
+ json_object_set_new(answer, "message",
+ json_string("output-dir param is mandatory"));
return TM_ECODE_FAILED;
}
#ifdef OS_WIN32
- if(_stat(output_dir, &st) != 0) {
+ if (_stat(output_dir, &st) != 0) {
#else
- if(stat(output_dir, &st) != 0) {
+ if (stat(output_dir, &st) != 0) {
#endif /* OS_WIN32 */
- json_object_set_new(answer, "message", json_string("Output directory does not exist"));
+ json_object_set_new(answer, "message",
+ json_string("output-dir does not exist"));
return TM_ECODE_FAILED;
}
json_t *targ = json_object_get(cmd, "tenant");
if (targ != NULL) {
- if(!json_is_number(targ)) {
- json_object_set_new(answer, "message", json_string("tenant is not a number"));
+ if (!json_is_integer(targ)) {
+ json_object_set_new(answer, "message",
+ json_string("tenant is not a number"));
return TM_ECODE_FAILED;
}
tenant_id = json_number_value(targ);
}
- ret = UnixListAddFile(this, filename, output_dir, tenant_id);
- switch(ret) {
+ json_t *cont_arg = json_object_get(cmd, "continuous");
+ if (cont_arg != NULL) {
+ continuous = json_is_true(cont_arg);
+ }
+
+ json_t *delay_arg = json_object_get(cmd, "delay");
+ if (delay_arg != NULL) {
+ if (!json_is_integer(delay_arg)) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "delay is not a integer");
+ json_object_set_new(answer, "message",
+ json_string("delay is not a integer"));
+ return TM_ECODE_FAILED;
+ }
+ delay = json_integer_value(delay_arg);
+ }
+
+ json_t *interval_arg = json_object_get(cmd, "poll-interval");
+ if (interval_arg != NULL) {
+ if (!json_is_integer(interval_arg)) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "poll-interval is not a integer");
+
+ json_object_set_new(answer, "message",
+ json_string("poll-interval is not a integer"));
+ return TM_ECODE_FAILED;
+ }
+ poll_interval = json_integer_value(interval_arg);
+ }
+
+ switch (UnixListAddFile(this, filename, output_dir, tenant_id, continuous,
+ delay, poll_interval)) {
case TM_ECODE_FAILED:
- json_object_set_new(answer, "message", json_string("Unable to add file to list"));
+ case TM_ECODE_DONE:
+ json_object_set_new(answer, "message",
+ json_string("Unable to add file to list"));
return TM_ECODE_FAILED;
case TM_ECODE_OK:
SCLogInfo("Added file '%s' to list", filename);
- json_object_set_new(answer, "message", json_string("Successfully added file to list"));
+ json_object_set_new(answer, "message",
+ json_string("Successfully added file to list"));
return TM_ECODE_OK;
}
return TM_ECODE_OK;
static TmEcode UnixSocketPcapFilesCheck(void *data)
{
PcapCommand *this = (PcapCommand *) data;
- if (unix_manager_file_task_running == 1) {
+ if (unix_manager_pcap_task_running == 1) {
return TM_ECODE_OK;
}
- if ((unix_manager_file_task_failed == 1) || (this->running == 1)) {
- if (unix_manager_file_task_failed) {
+ if ((unix_manager_pcap_task_failed == 1) || (this->running == 1)) {
+ if (unix_manager_pcap_task_failed) {
SCLogInfo("Preceeding task failed, cleaning the running mode");
}
- unix_manager_file_task_failed = 0;
+ unix_manager_pcap_task_failed = 0;
this->running = 0;
- if (this->currentfile) {
- SCFree(this->currentfile);
- }
- this->currentfile = NULL;
+ SCLogInfo("Resetting engine state");
PostRunDeinit(RUNMODE_PCAP_FILE, NULL /* no ts */);
+
+ if (this->current_file) {
+ PcapFilesFree(this->current_file);
+ }
+ this->current_file = NULL;
}
if (TAILQ_EMPTY(&this->files)) {
// nothing to do
PcapFiles *cfile = TAILQ_FIRST(&this->files);
TAILQ_REMOVE(&this->files, cfile, next);
- SCLogInfo("Starting run for '%s'", cfile->filename);
- unix_manager_file_task_running = 1;
+
+ unix_manager_pcap_task_running = 1;
this->running = 1;
- if (ConfSet("pcap-file.file", cfile->filename) != 1) {
- SCLogError(SC_ERR_INVALID_ARGUMENTS,
- "Can not set working file to '%s'", cfile->filename);
+
+ if (ConfSetFinal("pcap-file.file", cfile->filename) != 1) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Can not set working file to '%s'",
+ cfile->filename);
PcapFilesFree(cfile);
return TM_ECODE_FAILED;
}
- if (cfile->output_dir) {
- if (ConfSet("default-log-dir", cfile->output_dir) != 1) {
- SCLogError(SC_ERR_INVALID_ARGUMENTS,
- "Can not set output dir to '%s'", cfile->output_dir);
+
+ int set_res = 0;
+ if (cfile->continuous) {
+ set_res = ConfSetFinal("pcap-file.continuous", "true");
+ } else {
+ set_res = ConfSetFinal("pcap-file.continuous", "false");
+ }
+ if (set_res != 1) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Can not set continuous mode for pcap processing");
+ PcapFilesFree(cfile);
+ return TM_ECODE_FAILED;
+ }
+
+ if (cfile->delay > 0) {
+ char tstr[32];
+ snprintf(tstr, sizeof(tstr), "%" PRIuMAX, (uintmax_t)cfile->delay);
+ if (ConfSetFinal("pcap-file.delay", tstr) != 1) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Can not set delay to '%s'", tstr);
PcapFilesFree(cfile);
return TM_ECODE_FAILED;
}
}
+
+ if (cfile->poll_interval > 0) {
+ char tstr[32];
+ snprintf(tstr, sizeof(tstr), "%" PRIuMAX, (uintmax_t)cfile->poll_interval);
+ if (ConfSetFinal("pcap-file.poll-interval", tstr) != 1) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "Can not set poll-interval to '%s'", tstr);
+ PcapFilesFree(cfile);
+ return TM_ECODE_FAILED;
+ }
+ }
+
if (cfile->tenant_id > 0) {
char tstr[16];
snprintf(tstr, sizeof(tstr), "%d", cfile->tenant_id);
- if (ConfSet("pcap-file.tenant-id", tstr) != 1) {
- SCLogError(SC_ERR_INVALID_ARGUMENTS,
- "Can not set working tenant-id to '%s'", tstr);
+ if (ConfSetFinal("pcap-file.tenant-id", tstr) != 1) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "Can not set working tenant-id to '%s'", tstr);
PcapFilesFree(cfile);
return TM_ECODE_FAILED;
}
} else {
SCLogInfo("pcap-file.tenant-id not set");
}
- this->currentfile = SCStrdup(cfile->filename);
- if (unlikely(this->currentfile == NULL)) {
- SCLogError(SC_ERR_MEM_ALLOC, "Failed file name allocation");
- return TM_ECODE_FAILED;
+
+ if (cfile->output_dir) {
+ if (ConfSetFinal("default-log-dir", cfile->output_dir) != 1) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "Can not set output dir to '%s'", cfile->output_dir);
+ PcapFilesFree(cfile);
+ return TM_ECODE_FAILED;
+ }
}
- PcapFilesFree(cfile);
+
+ this->current_file = cfile;
PreRunInit(RUNMODE_PCAP_FILE);
PreRunPostPrivsDropInit(RUNMODE_PCAP_FILE);
/* Un-pause all the paused threads */
TmThreadWaitOnThreadInit();
TmThreadContinueThreads();
+
+ SCLogInfo("Starting run for '%s'", this->current_file->filename);
+
return TM_ECODE_OK;
}
#endif
RunModeUnixSocketMaster);
default_mode = "autofp";
#endif
- return;
}
-void UnixSocketPcapFile(TmEcode tm)
+TmEcode UnixSocketPcapFile(TmEcode tm, struct timespec *last_processed)
{
#ifdef BUILD_UNIX_SOCKET
+ unix_manager_pcap_last_processed.tv_sec = last_processed->tv_sec;
+ unix_manager_pcap_last_processed.tv_nsec = last_processed->tv_nsec;
switch (tm) {
case TM_ECODE_DONE:
- unix_manager_file_task_running = 0;
- break;
+ SCLogInfo("Marking current task as done");
+ unix_manager_pcap_task_running = 0;
+ return TM_ECODE_DONE;
case TM_ECODE_FAILED:
- unix_manager_file_task_running = 0;
- unix_manager_file_task_failed = 1;
- break;
+ SCLogInfo("Marking current task as failed");
+ unix_manager_pcap_task_running = 0;
+ unix_manager_pcap_task_failed = 1;
+ //if we return failed, we can't stop the thread and suricata will fail to close
+ return TM_ECODE_DONE;
case TM_ECODE_OK:
- break;
+ if (unix_manager_pcap_task_interrupted == 1) {
+ SCLogInfo("Interrupting current run mode");
+ return TM_ECODE_DONE;
+ } else {
+ return TM_ECODE_OK;
+ }
}
#endif
+ return TM_ECODE_FAILED;
}
#ifdef BUILD_UNIX_SOCKET
}
filename = json_string_value(jarg);
#ifdef OS_WIN32
- if(_stat(filename, &st) != 0) {
+ if (_stat(filename, &st) != 0) {
#else
- if(stat(filename, &st) != 0) {
+ if (stat(filename, &st) != 0) {
#endif /* OS_WIN32 */
json_object_set_new(answer, "message", json_string("file does not exist"));
return TM_ECODE_FAILED;
}
filename = json_string_value(jarg);
#ifdef OS_WIN32
- if(_stat(filename, &st) != 0) {
+ if (_stat(filename, &st) != 0) {
#else
- if(stat(filename, &st) != 0) {
+ if (stat(filename, &st) != 0) {
#endif /* OS_WIN32 */
json_object_set_new(answer, "message", json_string("file does not exist"));
return TM_ECODE_FAILED;
}
TAILQ_INIT(&pcapcmd->files);
pcapcmd->running = 0;
- pcapcmd->currentfile = NULL;
+ pcapcmd->current_file = NULL;
UnixManagerRegisterCommand("pcap-file", UnixSocketAddPcapFile, pcapcmd, UNIX_CMD_TAKE_ARGS);
UnixManagerRegisterCommand("pcap-file-number", UnixSocketPcapFilesNumber, pcapcmd, 0);
UnixManagerRegisterCommand("pcap-file-list", UnixSocketPcapFilesList, pcapcmd, 0);
+ UnixManagerRegisterCommand("pcap-last-processed", UnixSocketPcapLastProcessed, pcapcmd, 0);
+ UnixManagerRegisterCommand("pcap-interrupt", UnixSocketPcapInterrupt, pcapcmd, 0);
UnixManagerRegisterCommand("pcap-current", UnixSocketPcapCurrent, pcapcmd, 0);
UnixManagerRegisterBackgroundTask(UnixSocketPcapFilesCheck, pcapcmd);
int RunModeUnixSocketIsActive(void);
-void UnixSocketPcapFile(TmEcode tm);
+TmEcode UnixSocketPcapFile(TmEcode tm, struct timespec *last_processed);
#ifdef BUILD_UNIX_SOCKET
TmEcode UnixSocketRegisterTenantHandler(json_t *cmd, json_t* answer, void *data);
--- /dev/null
+/* Copyright (C) 2007-2016 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 Danny Browning <danny.browning@protectwise.com>
+ *
+ * Helper methods for directory based packet acquisition
+ */
+
+#include "source-pcap-file-directory-helper.h"
+#include "runmode-unix-socket.h"
+#include "util-mem.h"
+
+static void GetTime(struct timespec *tm);
+static void CopyTime(struct timespec *from, struct timespec *to);
+static int CompareTimes(struct timespec *left, struct timespec *right);
+static TmEcode PcapRunStatus(PcapFileDirectoryVars *);
+static TmEcode PcapDirectoryFailure(PcapFileDirectoryVars *ptv);
+static TmEcode PcapDirectoryDone(PcapFileDirectoryVars *ptv);
+static int PcapDirectoryGetModifiedTime(char const * file, struct timespec * out);
+static TmEcode PcapDirectoryInsertFile(PcapFileDirectoryVars *pv,
+ PendingFile *file_to_add,
+ struct timespec *file_to_add_modified_time);
+static TmEcode PcapDirectoryPopulateBuffer(PcapFileDirectoryVars *ptv,
+ struct timespec * newer_than,
+ struct timespec * older_than);
+static TmEcode PcapDirectoryDispatchForTimeRange(PcapFileDirectoryVars *pv,
+ struct timespec *newer_than,
+ struct timespec *older_than);
+
+void GetTime(struct timespec *tm)
+{
+ struct timeval now;
+ if(gettimeofday(&now, NULL) == 0) {
+ tm->tv_sec = now.tv_sec;
+ tm->tv_nsec = now.tv_usec * 1000;
+ }
+}
+
+void CopyTime(struct timespec *from, struct timespec *to)
+{
+ to->tv_sec = from->tv_sec;
+ to->tv_nsec = from->tv_nsec;
+}
+
+int CompareTimes(struct timespec *left, struct timespec *right)
+{
+ if (left->tv_sec < right->tv_sec) {
+ return -1;
+ } else if (left->tv_sec > right->tv_sec) {
+ return 1;
+ } else {
+ if (left->tv_nsec < right->tv_nsec) {
+ return -1;
+ } else if (left->tv_nsec > right->tv_nsec) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+}
+
+/**
+ * Pcap Folder Utilities
+ */
+TmEcode PcapRunStatus(PcapFileDirectoryVars *ptv)
+{
+ if (RunModeUnixSocketIsActive()) {
+ TmEcode done = UnixSocketPcapFile(TM_ECODE_OK, &ptv->shared->last_processed);
+ if ( (suricata_ctl_flags & SURICATA_STOP) || done != TM_ECODE_OK) {
+ SCReturnInt(TM_ECODE_DONE);
+ }
+ } else {
+ if (suricata_ctl_flags & SURICATA_STOP) {
+ SCReturnInt(TM_ECODE_DONE);
+ }
+ }
+ SCReturnInt(TM_ECODE_OK);
+}
+
+void CleanupPendingFile(PendingFile *pending) {
+ if (pending != NULL) {
+ if (pending->filename != NULL) {
+ SCFree(pending->filename);
+ }
+ SCFree(pending);
+ }
+}
+
+void CleanupPcapFileDirectoryVars(PcapFileDirectoryVars *ptv)
+{
+ if (ptv != NULL) {
+ if (ptv->current_file != NULL) {
+ CleanupPcapFileFileVars(ptv->current_file);
+ ptv->current_file = NULL;
+ }
+ if (ptv->directory != NULL) {
+ closedir(ptv->directory);
+ ptv->directory = NULL;
+ }
+ if (ptv->filename != NULL) {
+ SCFree(ptv->filename);
+ }
+ ptv->shared = NULL;
+ PendingFile *current_file = NULL;
+ while (!TAILQ_EMPTY(&ptv->directory_content)) {
+ current_file = TAILQ_FIRST(&ptv->directory_content);
+ TAILQ_REMOVE(&ptv->directory_content, current_file, next);
+ CleanupPendingFile(current_file);
+ }
+ SCFree(ptv);
+ }
+}
+
+TmEcode PcapDirectoryFailure(PcapFileDirectoryVars *ptv)
+{
+ TmEcode status = TM_ECODE_FAILED;
+
+ if (unlikely(ptv == NULL)) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Directory vars was null");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ if (unlikely(ptv->shared == NULL)) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Directory shared vars was null");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ if (RunModeUnixSocketIsActive()) {
+ status = UnixSocketPcapFile(status, &ptv->shared->last_processed);
+ }
+
+ SCReturnInt(status);
+}
+
+TmEcode PcapDirectoryDone(PcapFileDirectoryVars *ptv)
+{
+ TmEcode status = TM_ECODE_DONE;
+
+ if (unlikely(ptv == NULL)) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Directory vars was null");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ if (unlikely(ptv->shared == NULL)) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Directory shared vars was null");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ if (RunModeUnixSocketIsActive()) {
+ status = UnixSocketPcapFile(status, &ptv->shared->last_processed);
+ }
+
+ SCReturnInt(status);
+}
+
+TmEcode PcapDetermineDirectoryOrFile(char *filename, DIR **directory)
+{
+ DIR *temp_dir = NULL;
+ TmEcode return_code = TM_ECODE_FAILED;
+
+ temp_dir = opendir(filename);
+
+ if (temp_dir == NULL) {//if null, our filename may just be a normal file
+ switch (errno) {
+ case EACCES:
+ SCLogError(SC_ERR_FOPEN, "%s: Permission denied", filename);
+ break;
+
+ case EBADF:
+ SCLogError(SC_ERR_FOPEN,
+ "%s: Not a valid file descriptor opened for reading",
+ filename);
+ break;
+
+ case EMFILE:
+ SCLogError(SC_ERR_FOPEN,
+ "%s: Per process open file descriptor limit reached",
+ filename);
+ break;
+
+ case ENFILE:
+ SCLogError(SC_ERR_FOPEN,
+ "%s: System wide open file descriptor limit reached",
+ filename);
+ break;
+
+ case ENOENT:
+ SCLogError(SC_ERR_FOPEN,
+ "%s: Does not exist, or name is an empty string",
+ filename);
+ break;
+ case ENOMEM:
+ SCLogError(SC_ERR_FOPEN,
+ "%s: Insufficient memory to complete the operation",
+ filename);
+ break;
+
+ case ENOTDIR: //no error checking the directory, just is a plain file
+ SCLogInfo("%s: Plain file, not a directory", filename);
+ return_code = TM_ECODE_OK;
+ break;
+
+ default:
+ SCLogError(SC_ERR_FOPEN, "%s: %" PRId32, filename, errno);
+ }
+ } else {
+ //no error, filename references a directory
+ *directory = temp_dir;
+ return_code = TM_ECODE_OK;
+ }
+
+ return return_code;
+}
+
+int PcapDirectoryGetModifiedTime(char const *file, struct timespec *out)
+{
+ struct stat buf;
+ int ret;
+
+ if (file == NULL)
+ return -1;
+
+ if ((ret = stat(file, &buf)) != 0)
+ return ret;
+
+#ifdef OS_DARWIN
+ *out = buf.st_mtimespec;
+#else
+ *out = buf.st_mtim;
+#endif
+
+ return ret;
+}
+
+TmEcode PcapDirectoryInsertFile(PcapFileDirectoryVars *pv,
+ PendingFile *file_to_add,
+ struct timespec *file_to_add_modified_time
+) {
+ PendingFile *file_to_compare = NULL;
+ PendingFile *next_file_to_compare = NULL;
+
+ if (unlikely(pv == NULL)) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "No directory vars passed");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ if (unlikely(file_to_add == NULL)) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "File passed was null");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ if (unlikely(file_to_add->filename == NULL)) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "File was passed with null filename");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ SCLogDebug("Inserting %s into directory buffer", file_to_add->filename);
+
+ if (TAILQ_EMPTY(&pv->directory_content)) {
+ TAILQ_INSERT_TAIL(&pv->directory_content, file_to_add, next);
+ } else {
+ file_to_compare = TAILQ_FIRST(&pv->directory_content);
+ while(file_to_compare != NULL) {
+ struct timespec modified_time;
+ memset(&modified_time, 0, sizeof(struct timespec));
+
+ if (PcapDirectoryGetModifiedTime(file_to_compare->filename,
+ &modified_time) == TM_ECODE_FAILED) {
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ if (CompareTimes(file_to_add_modified_time, &modified_time) < 0) {
+ TAILQ_INSERT_BEFORE(file_to_compare, file_to_add, next);
+ file_to_compare = NULL;
+ } else {
+ next_file_to_compare = TAILQ_NEXT(file_to_compare, next);
+ if (next_file_to_compare == NULL) {
+ TAILQ_INSERT_AFTER(&pv->directory_content, file_to_compare,
+ file_to_add, next);
+ }
+ file_to_compare = next_file_to_compare;
+ }
+ }
+ }
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+TmEcode PcapDirectoryPopulateBuffer(PcapFileDirectoryVars *pv,
+ struct timespec *newer_than,
+ struct timespec *older_than
+) {
+ if (unlikely(pv == NULL)) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "No directory vars passed");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ if (unlikely(pv->filename == NULL)) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "No directory filename was passed");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ struct dirent * dir = NULL;
+ PendingFile *file_to_add = NULL;
+
+ while ((dir = readdir(pv->directory)) != NULL) {
+ if (dir->d_type != DT_REG) {
+ continue;
+ }
+
+ char pathbuff[PATH_MAX] = {0};
+
+ int written = 0;
+
+ written = snprintf(pathbuff, PATH_MAX, "%s/%s", pv->filename, dir->d_name);
+
+ if (written <= 0 || written >= PATH_MAX) {
+ SCLogError(SC_ERR_SPRINTF, "Could not write path");
+
+ SCReturnInt(TM_ECODE_FAILED);
+ } else {
+ struct timespec temp_time;
+ memset(&temp_time, 0, sizeof(struct timespec));
+
+ if (PcapDirectoryGetModifiedTime(pathbuff, &temp_time) == 0) {
+ SCLogDebug("File %s time (%lu > %lu < %lu)", pathbuff,
+ newer_than->tv_sec, temp_time.tv_sec, older_than->tv_sec);
+
+ // Skip files outside of our time range
+ if (CompareTimes(&temp_time, newer_than) < 0) {
+ SCLogDebug("Skipping old file %s", pathbuff);
+ continue;
+ }
+ else if (CompareTimes(&temp_time, older_than) >= 0) {
+ SCLogDebug("Skipping new file %s", pathbuff);
+ continue;
+ }
+ } else {
+ SCLogDebug("Unable to get modified time on %s, skipping", pathbuff);
+ continue;
+ }
+
+ file_to_add = SCCalloc(1, sizeof(PendingFile));
+ if (unlikely(file_to_add == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Failed to allocate pending file");
+
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ file_to_add->filename = SCStrdup(pathbuff);
+ if (unlikely(file_to_add->filename == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Failed to copy filename");
+ CleanupPendingFile(file_to_add);
+
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ SCLogDebug("Found \"%s\"", file_to_add->filename);
+
+ if (PcapDirectoryInsertFile(pv, file_to_add, &temp_time) == TM_ECODE_FAILED) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Failed to add file");
+ CleanupPendingFile(file_to_add);
+
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ }
+ }
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+
+TmEcode PcapDirectoryDispatchForTimeRange(PcapFileDirectoryVars *pv,
+ struct timespec *newer_than,
+ struct timespec *older_than)
+{
+ if (PcapDirectoryPopulateBuffer(pv, newer_than, older_than) == TM_ECODE_FAILED) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Failed to populate directory buffer");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ TmEcode status = TM_ECODE_OK;
+
+ if (TAILQ_EMPTY(&pv->directory_content)) {
+ SCLogInfo("Directory %s has no files to process", pv->filename);
+ GetTime(older_than);
+ older_than->tv_sec = older_than->tv_sec - pv->delay;
+ rewinddir(pv->directory);
+ status = TM_ECODE_OK;
+ } else {
+ PendingFile *current_file = NULL;
+
+ while (status == TM_ECODE_OK && !TAILQ_EMPTY(&pv->directory_content)) {
+ current_file = TAILQ_FIRST(&pv->directory_content);
+ TAILQ_REMOVE(&pv->directory_content, current_file, next);
+
+ if (unlikely(current_file == NULL)) {
+ SCLogWarning(SC_ERR_PCAP_DISPATCH, "Current file was null");
+ } else if (unlikely(current_file->filename == NULL)) {
+ SCLogWarning(SC_ERR_PCAP_DISPATCH, "Current file filename was null");
+ } else {
+ SCLogDebug("Processing file %s", current_file->filename);
+
+ PcapFileFileVars *pftv = SCMalloc(sizeof(PcapFileFileVars));
+ if (unlikely(pftv == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Failed to allocate PcapFileFileVars");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ memset(pftv, 0, sizeof(PcapFileFileVars));
+
+ pftv->filename = SCStrdup(current_file->filename);
+ if (unlikely(pftv->filename == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Failed to allocate filename");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ pftv->shared = pv->shared;
+
+ if (InitPcapFile(pftv) == TM_ECODE_FAILED) {
+ SCLogWarning(SC_ERR_PCAP_DISPATCH,
+ "Failed to init pcap file %s, skipping",
+ current_file->filename);
+ CleanupPendingFile(current_file);
+ CleanupPcapFileFileVars(pftv);
+ status = TM_ECODE_OK;
+ } else {
+ pv->current_file = pftv;
+
+ status = PcapFileDispatch(pftv);
+
+ CleanupPcapFileFileVars(pftv);
+
+ if (status == TM_ECODE_FAILED) {
+ CleanupPendingFile(current_file);
+ SCReturnInt(status);
+ }
+
+ struct timespec temp_time;
+ memset(&temp_time, 0, sizeof(struct timespec));
+
+ if (PcapDirectoryGetModifiedTime(current_file->filename,
+ &temp_time) != 0) {
+ CopyTime(&temp_time, newer_than);
+ }
+ SCLogDebug("Processed file %s, processed up to %ld",
+ current_file->filename, temp_time.tv_sec);
+ CopyTime(&temp_time, &pv->shared->last_processed);
+
+ CleanupPendingFile(current_file);
+ pv->current_file = NULL;
+
+ status = PcapRunStatus(pv);
+ }
+ }
+ }
+
+ *newer_than = *older_than;
+ }
+ GetTime(older_than);
+ older_than->tv_sec = older_than->tv_sec - pv->delay;
+
+ SCReturnInt(status);
+}
+
+TmEcode PcapDirectoryDispatch(PcapFileDirectoryVars *ptv)
+{
+ SCEnter();
+
+ DIR *directory_check = NULL;
+
+ struct timespec newer_than;
+ memset(&newer_than, 0, sizeof(struct timespec));
+ struct timespec older_than;
+ memset(&older_than, 0, sizeof(struct timespec));
+ older_than.tv_sec = LONG_MAX;
+ uint32_t poll_seconds = (uint32_t)localtime(&ptv->poll_interval)->tm_sec;
+
+ if (ptv->should_loop) {
+ GetTime(&older_than);
+ older_than.tv_sec = older_than.tv_sec - ptv->delay;
+ }
+ TmEcode status = TM_ECODE_OK;
+
+ while (status == TM_ECODE_OK) {
+ //loop while directory is ok
+ SCLogInfo("Processing pcaps directory %s, files must be newer than %" PRIuMAX " and older than %" PRIuMAX,
+ ptv->filename, (uintmax_t)newer_than.tv_sec, (uintmax_t)older_than.tv_sec);
+ status = PcapDirectoryDispatchForTimeRange(ptv, &newer_than, &older_than);
+ if (ptv->should_loop && status == TM_ECODE_OK) {
+ sleep(poll_seconds);
+ //update our status based on suricata control flags or unix command socket
+ status = PcapRunStatus(ptv);
+ if (status == TM_ECODE_OK) {
+ SCLogDebug("Checking if directory %s still exists", ptv->filename);
+ //check directory
+ if (PcapDetermineDirectoryOrFile(ptv->filename,
+ &directory_check) == TM_ECODE_FAILED) {
+ SCLogInfo("Directory %s no longer exists, stopping",
+ ptv->filename);
+ status = TM_ECODE_DONE;
+ } else {
+ closedir(directory_check);
+ }
+ }
+ } else if (status == TM_ECODE_OK) { //not looping, mark done
+ SCLogDebug("Not looping, stopping directory mode");
+ status = TM_ECODE_DONE;
+ }
+ }
+
+ StatsSyncCountersIfSignalled(ptv->shared->tv);
+
+ if (status == TM_ECODE_FAILED) {
+ SCLogError(SC_ERR_PCAP_DISPATCH, "Directory %s run mode failed", ptv->filename);
+ status = PcapDirectoryFailure(ptv);
+ } else {
+ SCLogInfo("Directory run mode complete");
+ status = PcapDirectoryDone(ptv);
+ }
+
+ SCReturnInt(status);
+}
+
+/* eof */
--- /dev/null
+/* Copyright (C) 2007-2010 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 Danny Browning <danny.browning@protectwise.com>
+ */
+
+#include "suricata-common.h"
+#include "source-pcap-file-helper.h"
+#include "queue.h"
+
+#ifndef __SOURCE_PCAP_FILE_DIRECTORY_HELPER_H__
+#define __SOURCE_PCAP_FILE_DIRECTORY_HELPER_H__
+
+typedef struct PendingFile_
+{
+ char *filename;
+ TAILQ_ENTRY(PendingFile_) next;
+} PendingFile;
+/**
+ * Data specific to a directory of pcap files
+ */
+typedef struct PcapFileDirectoryVars_
+{
+ char *filename;
+ DIR *directory;
+ PcapFileFileVars *current_file;
+ bool should_loop;
+ time_t delay;
+ time_t poll_interval;
+
+ TAILQ_HEAD(PendingFiles, PendingFile_) directory_content;
+
+ PcapFileSharedVars *shared;
+} PcapFileDirectoryVars;
+
+/**
+ * Cleanup resources associated with a PendingFile object
+ * @param pending Object to be cleaned up
+ */
+void CleanupPendingFile(PendingFile *pending);
+
+/**
+ * Cleanup resources associated with a PcapFileDirectoryVars object
+ * @param ptv Object to be cleaned up
+ */
+void CleanupPcapFileDirectoryVars(PcapFileDirectoryVars *ptv);
+
+/**
+ * Determine if a given string represents a file or directory. If a directory,
+ * populate the directory object.
+ * @param filename String to check
+ * @param directory Directory point to populate if directory
+ * @return TM_ECODE_OK if string or directory
+ */
+TmEcode PcapDetermineDirectoryOrFile(char *filename, DIR **directory);
+
+/**
+ * Dispatch a directory for processing, where information for processing the
+ * directory is contained in a PcapFileDirectoryVars object
+ * @param ptv PcapFileDirectoryVars object containing information for processing
+ * the directory
+ * @return
+ */
+TmEcode PcapDirectoryDispatch(PcapFileDirectoryVars *ptv);
+
+#endif /* __SOURCE_PCAP_FILE_DIRECTORY_HELPER_H__ */
--- /dev/null
+/* Copyright (C) 2007-2016 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 Danny Browning <danny.browning@protectwise.com>
+ *
+ * File based pcap packet acquisition support
+ */
+
+#include "source-pcap-file-helper.h"
+#include "util-checksum.h"
+#include "util-profiling.h"
+
+#ifdef __SC_CUDA_SUPPORT__
+
+#include "util-cuda.h"
+#include "util-cuda-buffer.h"
+#include "util-mpm-ac.h"
+#include "util-cuda-handlers.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "util-cuda-vars.h"
+
+#endif /* __SC_CUDA_SUPPORT__ */
+
+extern int max_pending_packets;
+extern PcapFileGlobalVars pcap_g;
+
+static void PcapFileCallbackLoop(char *user, struct pcap_pkthdr *h, u_char *pkt);
+
+void CleanupPcapFileFileVars(PcapFileFileVars *pfv)
+{
+ if (pfv != NULL) {
+ if (pfv->pcap_handle != NULL) {
+ pcap_close(pfv->pcap_handle);
+ pfv->pcap_handle = NULL;
+ }
+ if (pfv->filename != NULL) {
+ SCFree(pfv->filename);
+ pfv->filename = NULL;
+ }
+ pfv->shared = NULL;
+ SCFree(pfv);
+ }
+}
+
+void PcapFileCallbackLoop(char *user, struct pcap_pkthdr *h, u_char *pkt)
+{
+ SCEnter();
+
+ PcapFileFileVars *ptv = (PcapFileFileVars *)user;
+ Packet *p = PacketGetFromQueueOrAlloc();
+
+ if (unlikely(p == NULL)) {
+ SCReturn;
+ }
+ PACKET_PROFILING_TMM_START(p, TMM_RECEIVEPCAPFILE);
+
+ PKT_SET_SRC(p, PKT_SRC_WIRE);
+ p->ts.tv_sec = h->ts.tv_sec;
+ p->ts.tv_usec = h->ts.tv_usec;
+ SCLogDebug("p->ts.tv_sec %"PRIuMAX"", (uintmax_t)p->ts.tv_sec);
+ p->datalink = ptv->datalink;
+ p->pcap_cnt = ++pcap_g.cnt;
+
+ p->pcap_v.tenant_id = ptv->shared->tenant_id;
+ ptv->shared->pkts++;
+ ptv->shared->bytes += h->caplen;
+
+ if (unlikely(PacketCopyData(p, pkt, h->caplen))) {
+ TmqhOutputPacketpool(ptv->shared->tv, p);
+ PACKET_PROFILING_TMM_END(p, TMM_RECEIVEPCAPFILE);
+ SCReturn;
+ }
+
+ /* We only check for checksum disable */
+ if (pcap_g.checksum_mode == CHECKSUM_VALIDATION_DISABLE) {
+ p->flags |= PKT_IGNORE_CHECKSUM;
+ } else if (pcap_g.checksum_mode == CHECKSUM_VALIDATION_AUTO) {
+ if (ChecksumAutoModeCheck(ptv->shared->pkts, p->pcap_cnt,
+ SC_ATOMIC_GET(pcap_g.invalid_checksums))) {
+ pcap_g.checksum_mode = CHECKSUM_VALIDATION_DISABLE;
+ p->flags |= PKT_IGNORE_CHECKSUM;
+ }
+ }
+
+ PACKET_PROFILING_TMM_END(p, TMM_RECEIVEPCAPFILE);
+
+ if (TmThreadsSlotProcessPkt(ptv->shared->tv, ptv->shared->slot, p) != TM_ECODE_OK) {
+ pcap_breakloop(ptv->pcap_handle);
+ ptv->shared->cb_result = TM_ECODE_FAILED;
+ }
+
+ SCReturn;
+}
+
+/**
+ * \brief Main PCAP file reading Loop function
+ */
+TmEcode PcapFileDispatch(PcapFileFileVars *ptv)
+{
+ SCEnter();
+
+ int packet_q_len = 64;
+ int r;
+ TmEcode loop_result = TM_ECODE_OK;
+
+ while (loop_result == TM_ECODE_OK) {
+ if (suricata_ctl_flags & SURICATA_STOP) {
+ SCReturnInt(TM_ECODE_OK);
+ }
+
+ /* make sure we have at least one packet in the packet pool, to prevent
+ * us from alloc'ing packets at line rate */
+ PacketPoolWait();
+
+ /* Right now we just support reading packets one at a time. */
+ r = pcap_dispatch(ptv->pcap_handle, packet_q_len,
+ (pcap_handler)PcapFileCallbackLoop, (u_char *)ptv);
+ if (unlikely(r == -1)) {
+ SCLogError(SC_ERR_PCAP_DISPATCH, "error code %" PRId32 " %s for %s",
+ r, pcap_geterr(ptv->pcap_handle), ptv->filename);
+ if (ptv->shared->cb_result == TM_ECODE_FAILED) {
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ loop_result = TM_ECODE_DONE;
+ } else if (unlikely(r == 0)) {
+ SCLogInfo("pcap file %s end of file reached (pcap err code %" PRId32 ")",
+ ptv->filename, r);
+ ptv->shared->files++;
+ loop_result = TM_ECODE_DONE;
+ } else if (ptv->shared->cb_result == TM_ECODE_FAILED) {
+ SCLogError(SC_ERR_PCAP_DISPATCH,
+ "Pcap callback PcapFileCallbackLoop failed for %s", ptv->filename);
+ loop_result = TM_ECODE_FAILED;
+ }
+ StatsSyncCountersIfSignalled(ptv->shared->tv);
+ }
+
+ SCReturnInt(loop_result);
+}
+
+TmEcode InitPcapFile(PcapFileFileVars *pfv)
+{
+ char errbuf[PCAP_ERRBUF_SIZE] = "";
+
+ if(unlikely(pfv->filename == NULL)) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Filename was null");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ pfv->pcap_handle = pcap_open_offline(pfv->filename, errbuf);
+ if (pfv->pcap_handle == NULL) {
+ SCLogError(SC_ERR_FOPEN, "%s", errbuf);
+ if (!RunModeUnixSocketIsActive()) {
+ SCReturnInt(TM_ECODE_FAILED);
+ } else {
+ UnixSocketPcapFile(TM_ECODE_FAILED, 0);
+ SCReturnInt(TM_ECODE_DONE);
+ }
+ }
+
+ if (pfv->shared != NULL && pfv->shared->bpf_string != NULL) {
+ SCLogInfo("using bpf-filter \"%s\"", pfv->shared->bpf_string);
+
+ if (pcap_compile(pfv->pcap_handle, &pfv->filter, pfv->shared->bpf_string, 1, 0) < 0) {
+ SCLogError(SC_ERR_BPF, "bpf compilation error %s for %s",
+ pcap_geterr(pfv->pcap_handle), pfv->filename);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ if (pcap_setfilter(pfv->pcap_handle, &pfv->filter) < 0) {
+ SCLogError(SC_ERR_BPF,"could not set bpf filter %s for %s",
+ pcap_geterr(pfv->pcap_handle), pfv->filename);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ }
+
+ pfv->datalink = pcap_datalink(pfv->pcap_handle);
+ SCLogDebug("datalink %" PRId32 "", pfv->datalink);
+
+ Decoder temp;
+ TmEcode validated = ValidateLinkType(pfv->datalink, &temp);
+ SCReturnInt(validated);
+}
+
+TmEcode ValidateLinkType(int datalink, Decoder *decoder)
+{
+ switch (datalink) {
+ case LINKTYPE_LINUX_SLL:
+ *decoder = DecodeSll;
+ break;
+ case LINKTYPE_ETHERNET:
+ *decoder = DecodeEthernet;
+ break;
+ case LINKTYPE_PPP:
+ *decoder = DecodePPP;
+ break;
+ case LINKTYPE_IPV4:
+ case LINKTYPE_RAW:
+ case LINKTYPE_RAW2:
+ *decoder = DecodeRaw;
+ break;
+ case LINKTYPE_NULL:
+ *decoder = DecodeNull;
+ break;
+
+ default:
+ SCLogError(
+ SC_ERR_UNIMPLEMENTED,
+ "datalink type %" PRId32 " not (yet) supported in module PcapFile.",
+ datalink
+ );
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ SCReturnInt(TM_ECODE_OK);
+}
+/* eof */
--- /dev/null
+/* Copyright (C) 2007-2010 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 Danny Browning <danny.browning@protectwise.com>
+ */
+
+#include "suricata-common.h"
+#include "tm-threads.h"
+
+#ifndef __SOURCE_PCAP_FILE_HELPER_H__
+#define __SOURCE_PCAP_FILE_HELPER_H__
+
+typedef struct PcapFileGlobalVars_ {
+ uint64_t cnt; /** packet counter */
+ ChecksumValidationMode conf_checksum_mode;
+ ChecksumValidationMode checksum_mode;
+ SC_ATOMIC_DECLARE(unsigned int, invalid_checksums);
+} PcapFileGlobalVars;
+
+/**
+ * Data that is shared amongst File, Directory, and Thread level vars
+ */
+typedef struct PcapFileSharedVars_
+{
+ char *bpf_string;
+
+ uint32_t tenant_id;
+
+ struct timespec last_processed;
+
+ ThreadVars *tv;
+ TmSlot *slot;
+
+ /* counters */
+ uint64_t pkts;
+ uint64_t bytes;
+ uint64_t files;
+
+ uint8_t done;
+ uint32_t errs;
+
+ /** callback result -- set if one of the thread module failed. */
+ int cb_result;
+} PcapFileSharedVars;
+
+/**
+ * Data specific to a single pcap file
+ */
+typedef struct PcapFileFileVars_
+{
+ char *filename;
+ pcap_t *pcap_handle;
+
+ int datalink;
+ struct bpf_program filter;
+
+ PcapFileSharedVars *shared;
+} PcapFileFileVars;
+
+typedef int (*Decoder)(ThreadVars *, DecodeThreadVars *, Packet *, uint8_t *, uint16_t, PacketQueue *);
+
+/**
+ * Dispatch a file for processing, where the information necessary to process that
+ * file is as PcapFileFileVars object.
+ * @param ptv PcapFileFileVars object to be processed
+ * @return
+ */
+TmEcode PcapFileDispatch(PcapFileFileVars *ptv);
+
+/**
+ * From a PcapFileFileVars, prepare the filename for processing by setting
+ * pcap_handle, datalink, and filter
+ * @param pfv PcapFileFileVars object to populate
+ * @return
+ */
+TmEcode InitPcapFile(PcapFileFileVars *pfv);
+
+/**
+ * Cleanup resources associated with a PcapFileFileVars object.
+ * @param pfv Object to be cleaned up
+ */
+void CleanupPcapFileFileVars(PcapFileFileVars *pfv);
+
+/**
+ * Determine if a datalink type is valid, setting a decoder function if valid.
+ * @param datalink Datalink type to validate
+ * @param decoder Pointer to decoder to set if valid
+ * @return TM_ECODE_OK if valid datalink type and decoder has been set.
+ */
+TmEcode ValidateLinkType(int datalink, Decoder *decoder);
+
+#endif /* __SOURCE_PCAP_FILE_HELPER_H__ */
*/
#include "suricata-common.h"
-#include "suricata.h"
-#include "decode.h"
-#include "packet-queue.h"
-#include "threads.h"
-#include "threadvars.h"
-#include "tm-queuehandlers.h"
#include "source-pcap-file.h"
-#include "util-time.h"
-#include "util-debug.h"
-#include "conf.h"
-#include "util-error.h"
-#include "util-privs.h"
-#include "tmqh-packetpool.h"
-#include "tm-threads.h"
-#include "util-optimize.h"
+#include "source-pcap-file-helper.h"
+#include "source-pcap-file-directory-helper.h"
#include "flow-manager.h"
-#include "util-profiling.h"
-#include "runmode-unix-socket.h"
#include "util-checksum.h"
-#include "util-atomic.h"
#ifdef __SC_CUDA_SUPPORT__
#endif /* __SC_CUDA_SUPPORT__ */
extern int max_pending_packets;
+PcapFileGlobalVars pcap_g;
-typedef struct PcapFileGlobalVars_ {
- pcap_t *pcap_handle;
- int (*Decoder)(ThreadVars *, DecodeThreadVars *, Packet *, uint8_t *, uint16_t, PacketQueue *);
- int datalink;
- struct bpf_program filter;
- uint64_t cnt; /** packet counter */
- ChecksumValidationMode conf_checksum_mode;
- ChecksumValidationMode checksum_mode;
- SC_ATOMIC_DECLARE(unsigned int, invalid_checksums);
-
-} PcapFileGlobalVars;
+/**
+ * Union determining whether the behavior of the thread is file or directory
+ */
+typedef union PcapFileBehaviorVar_
+{
+ PcapFileDirectoryVars *directory;
+ PcapFileFileVars *file;
+} PcapFileBehaviorVar;
+/**
+ * Data specific to the thread
+ */
typedef struct PcapFileThreadVars_
{
- uint32_t tenant_id;
+ PcapFileBehaviorVar behavior;
+ bool is_directory;
- /* counters */
- uint32_t pkts;
- uint64_t bytes;
-
- ThreadVars *tv;
- TmSlot *slot;
+ PcapFileSharedVars shared;
+} PcapFileThreadVars;
- /** callback result -- set if one of the thread module failed. */
- int cb_result;
+static TmEcode ReceivePcapFileLoop(ThreadVars *, void *, void *);
+static TmEcode ReceivePcapFileThreadInit(ThreadVars *, const void *, void **);
+static void ReceivePcapFileThreadExitStats(ThreadVars *, void *);
+static TmEcode ReceivePcapFileThreadDeinit(ThreadVars *, void *);
- uint8_t done;
- uint32_t errs;
-} PcapFileThreadVars;
+static TmEcode DecodePcapFile(ThreadVars *, Packet *, void *, PacketQueue *,
+ PacketQueue *);
+static TmEcode DecodePcapFileThreadInit(ThreadVars *, const void *, void **);
+static TmEcode DecodePcapFileThreadDeinit(ThreadVars *tv, void *data);
-static PcapFileGlobalVars pcap_g;
+static void CleanupPcapDirectoryFromThreadVars(PcapFileThreadVars *tv,
+ PcapFileDirectoryVars *ptv);
+static void CleanupPcapFileFromThreadVars(PcapFileThreadVars *tv, PcapFileFileVars *pfv);
+static void CleanupPcapFileThreadVars(PcapFileThreadVars *tv);
-TmEcode ReceivePcapFileLoop(ThreadVars *, void *, void *);
+void CleanupPcapFileFromThreadVars(PcapFileThreadVars *tv, PcapFileFileVars *pfv)
+{
+ CleanupPcapFileFileVars(pfv);
+ if (tv->is_directory == 0) {
+ tv->behavior.file = NULL;
+ }
+}
-TmEcode ReceivePcapFileThreadInit(ThreadVars *, const void *, void **);
-void ReceivePcapFileThreadExitStats(ThreadVars *, void *);
-TmEcode ReceivePcapFileThreadDeinit(ThreadVars *, void *);
+void CleanupPcapDirectoryFromThreadVars(PcapFileThreadVars *tv, PcapFileDirectoryVars *ptv)
+{
+ CleanupPcapFileDirectoryVars(ptv);
+ if (tv->is_directory == 1) {
+ tv->behavior.directory = NULL;
+ }
+}
-TmEcode DecodePcapFile(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
-TmEcode DecodePcapFileThreadInit(ThreadVars *, const void *, void **);
-TmEcode DecodePcapFileThreadDeinit(ThreadVars *tv, void *data);
+void CleanupPcapFileThreadVars(PcapFileThreadVars *tv)
+{
+ if(tv != NULL) {
+ if (tv->is_directory == 0) {
+ if (tv->behavior.file != NULL) {
+ CleanupPcapFileFromThreadVars(tv, tv->behavior.file);
+ }
+ tv->behavior.file = NULL;
+ } else {
+ if (tv->behavior.directory != NULL) {
+ CleanupPcapDirectoryFromThreadVars(tv, tv->behavior.directory);
+ }
+ tv->behavior.directory = NULL;
+ }
+ if (tv->shared.bpf_string != NULL) {
+ SCFree(tv->shared.bpf_string);
+ tv->shared.bpf_string = NULL;
+ }
+ SCFree(tv);
+ }
+}
+/**
+ * Pcap File Functionality
+ */
void TmModuleReceivePcapFileRegister (void)
{
tmm_modules[TMM_RECEIVEPCAPFILE].name = "ReceivePcapFile";
SC_ATOMIC_INIT(pcap_g.invalid_checksums);
}
-static void PcapFileCallbackLoop(char *user, struct pcap_pkthdr *h, u_char *pkt)
-{
- SCEnter();
-
- PcapFileThreadVars *ptv = (PcapFileThreadVars *)user;
- Packet *p = PacketGetFromQueueOrAlloc();
-
- if (unlikely(p == NULL)) {
- SCReturn;
- }
- PACKET_PROFILING_TMM_START(p, TMM_RECEIVEPCAPFILE);
-
- PKT_SET_SRC(p, PKT_SRC_WIRE);
- p->ts.tv_sec = h->ts.tv_sec;
- p->ts.tv_usec = h->ts.tv_usec;
- SCLogDebug("p->ts.tv_sec %"PRIuMAX"", (uintmax_t)p->ts.tv_sec);
- p->datalink = pcap_g.datalink;
- p->pcap_cnt = ++pcap_g.cnt;
-
- p->pcap_v.tenant_id = ptv->tenant_id;
- ptv->pkts++;
- ptv->bytes += h->caplen;
-
- if (unlikely(PacketCopyData(p, pkt, h->caplen))) {
- TmqhOutputPacketpool(ptv->tv, p);
- PACKET_PROFILING_TMM_END(p, TMM_RECEIVEPCAPFILE);
- SCReturn;
- }
-
- /* We only check for checksum disable */
- if (pcap_g.checksum_mode == CHECKSUM_VALIDATION_DISABLE) {
- p->flags |= PKT_IGNORE_CHECKSUM;
- } else if (pcap_g.checksum_mode == CHECKSUM_VALIDATION_AUTO) {
- if (ChecksumAutoModeCheck(ptv->pkts, p->pcap_cnt,
- SC_ATOMIC_GET(pcap_g.invalid_checksums))) {
- pcap_g.checksum_mode = CHECKSUM_VALIDATION_DISABLE;
- p->flags |= PKT_IGNORE_CHECKSUM;
- }
- }
-
- PACKET_PROFILING_TMM_END(p, TMM_RECEIVEPCAPFILE);
-
- if (TmThreadsSlotProcessPkt(ptv->tv, ptv->slot, p) != TM_ECODE_OK) {
- pcap_breakloop(pcap_g.pcap_handle);
- ptv->cb_result = TM_ECODE_FAILED;
- }
-
- SCReturn;
-}
-
-/**
- * \brief Main PCAP file reading Loop function
- */
TmEcode ReceivePcapFileLoop(ThreadVars *tv, void *data, void *slot)
{
SCEnter();
- int packet_q_len = 64;
- PcapFileThreadVars *ptv = (PcapFileThreadVars *)data;
- int r;
+ TmEcode status = TM_ECODE_OK;
+ PcapFileThreadVars *ptv = (PcapFileThreadVars *) data;
TmSlot *s = (TmSlot *)slot;
- ptv->slot = s->slot_next;
- ptv->cb_result = TM_ECODE_OK;
+ ptv->shared.slot = s->slot_next;
+ ptv->shared.cb_result = TM_ECODE_OK;
- while (1) {
- if (suricata_ctl_flags & SURICATA_STOP) {
- SCReturnInt(TM_ECODE_OK);
- }
-
- /* make sure we have at least one packet in the packet pool, to prevent
- * us from alloc'ing packets at line rate */
- PacketPoolWait();
-
- /* Right now we just support reading packets one at a time. */
- r = pcap_dispatch(pcap_g.pcap_handle, packet_q_len,
- (pcap_handler)PcapFileCallbackLoop, (u_char *)ptv);
- if (unlikely(r == -1)) {
- SCLogError(SC_ERR_PCAP_DISPATCH, "error code %" PRId32 " %s",
- r, pcap_geterr(pcap_g.pcap_handle));
- if (ptv->cb_result == TM_ECODE_FAILED) {
- SCReturnInt(TM_ECODE_FAILED);
- }
- if (! RunModeUnixSocketIsActive()) {
- EngineStop();
- } else {
- pcap_close(pcap_g.pcap_handle);
- pcap_g.pcap_handle = NULL;
- UnixSocketPcapFile(TM_ECODE_DONE);
- SCReturnInt(TM_ECODE_DONE);
- }
- } else if (unlikely(r == 0)) {
- SCLogInfo("pcap file end of file reached (pcap err code %" PRId32 ")", r);
- if (! RunModeUnixSocketIsActive()) {
- EngineStop();
- } else {
- pcap_close(pcap_g.pcap_handle);
- pcap_g.pcap_handle = NULL;
- UnixSocketPcapFile(TM_ECODE_DONE);
- SCReturnInt(TM_ECODE_DONE);
- }
- break;
- } else if (ptv->cb_result == TM_ECODE_FAILED) {
- SCLogError(SC_ERR_PCAP_DISPATCH, "Pcap callback PcapFileCallbackLoop failed");
- if (! RunModeUnixSocketIsActive()) {
- SCReturnInt(TM_ECODE_FAILED);
- } else {
- pcap_close(pcap_g.pcap_handle);
- pcap_g.pcap_handle = NULL;
- UnixSocketPcapFile(TM_ECODE_DONE);
- SCReturnInt(TM_ECODE_DONE);
- }
+ if(ptv->is_directory == 0) {
+ SCLogInfo("Starting file run for %s", ptv->behavior.file->filename);
+ status = PcapFileDispatch(ptv->behavior.file);
+ if (!RunModeUnixSocketIsActive()) {
+ EngineStop();
+ } else {
+ UnixSocketPcapFile(status, &ptv->shared.last_processed);
}
- StatsSyncCountersIfSignalled(tv);
+ CleanupPcapFileFromThreadVars(ptv, ptv->behavior.file);
+ } else {
+ SCLogInfo("Starting directory run for %s", ptv->behavior.directory->filename);
+ PcapDirectoryDispatch(ptv->behavior.directory);
+ CleanupPcapDirectoryFromThreadVars(ptv, ptv->behavior.directory);
}
- SCReturnInt(TM_ECODE_OK);
+ SCLogDebug("Pcap file loop complete with status %u", status);
+
+ if(RunModeUnixSocketIsActive()) {
+ SCReturnInt(TM_ECODE_DONE);
+ } else {
+ EngineStop();
+ SCReturnInt(TM_ECODE_OK);
+ }
}
TmEcode ReceivePcapFileThreadInit(ThreadVars *tv, const void *initdata, void **data)
{
SCEnter();
- const char *tmpbpfstring = NULL;
const char *tmpstring = NULL;
+ const char *tmp_bpf_string = NULL;
if (initdata == NULL) {
SCLogError(SC_ERR_INVALID_ARGUMENT, "error: initdata == NULL");
SCReturnInt(TM_ECODE_FAILED);
}
- SCLogInfo("reading pcap file %s", (char *)initdata);
-
PcapFileThreadVars *ptv = SCMalloc(sizeof(PcapFileThreadVars));
if (unlikely(ptv == NULL))
SCReturnInt(TM_ECODE_FAILED);
intmax_t tenant = 0;
if (ConfGetInt("pcap-file.tenant-id", &tenant) == 1) {
if (tenant > 0 && tenant < UINT_MAX) {
- ptv->tenant_id = (uint32_t)tenant;
- SCLogInfo("tenant %u", ptv->tenant_id);
+ ptv->shared.tenant_id = (uint32_t)tenant;
+ SCLogInfo("tenant %u", ptv->shared.tenant_id);
} else {
SCLogError(SC_ERR_INVALID_ARGUMENT, "tenant out of range");
}
}
- char errbuf[PCAP_ERRBUF_SIZE] = "";
- pcap_g.pcap_handle = pcap_open_offline((char *)initdata, errbuf);
- if (pcap_g.pcap_handle == NULL) {
- SCLogError(SC_ERR_FOPEN, "%s\n", errbuf);
- SCFree(ptv);
- if (! RunModeUnixSocketIsActive()) {
- return TM_ECODE_FAILED;
- } else {
- UnixSocketPcapFile(TM_ECODE_FAILED);
- SCReturnInt(TM_ECODE_DONE);
+ if (ConfGet("bpf-filter", &(tmp_bpf_string)) != 1) {
+ SCLogDebug("could not get bpf or none specified");
+ } else {
+ ptv->shared.bpf_string = SCStrdup(tmp_bpf_string);
+ if (unlikely(ptv->shared.bpf_string == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Failed to allocate bpf_string");
+ CleanupPcapFileThreadVars(ptv);
+ SCReturnInt(TM_ECODE_FAILED);
}
}
- if (ConfGet("bpf-filter", &tmpbpfstring) != 1) {
- SCLogDebug("could not get bpf or none specified");
+ DIR *directory = NULL;
+ SCLogInfo("Checking file or directory %s", (char*)initdata);
+ if(PcapDetermineDirectoryOrFile((char *)initdata, &directory) == TM_ECODE_FAILED) {
+ CleanupPcapFileThreadVars(ptv);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ if(directory == NULL) {
+ SCLogInfo("Argument %s was a file", (char *)initdata);
+ PcapFileFileVars *pv = SCMalloc(sizeof(PcapFileFileVars));
+ if (unlikely(pv == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Failed to allocate file vars");
+ CleanupPcapFileThreadVars(ptv);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ memset(pv, 0, sizeof(PcapFileFileVars));
+
+ pv->filename = SCStrdup((char *)initdata);
+ if (unlikely(pv->filename == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Failed to allocate filename");
+ CleanupPcapFileFileVars(pv);
+ CleanupPcapFileThreadVars(ptv);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ TmEcode init_file_return = InitPcapFile(pv);
+ if(init_file_return == TM_ECODE_OK) {
+ pv->shared = &ptv->shared;
+
+ ptv->is_directory = 0;
+ ptv->behavior.file = pv;
+ } else {
+ SCLogWarning(SC_ERR_PCAP_DISPATCH,
+ "Failed to init pcap file %s, skipping", (char *)initdata);
+ CleanupPcapFileFileVars(pv);
+ CleanupPcapFileThreadVars(ptv);
+ SCReturnInt(init_file_return);
+ }
} else {
- SCLogInfo("using bpf-filter \"%s\"", tmpbpfstring);
+ SCLogInfo("Argument %s was a directory", (char *)initdata);
+ PcapFileDirectoryVars *pv = SCMalloc(sizeof(PcapFileDirectoryVars));
+ if (unlikely(pv == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Failed to allocate directory vars");
+ CleanupPcapFileThreadVars(ptv);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ memset(pv, 0, sizeof(PcapFileDirectoryVars));
+
+ pv->filename = SCStrdup((char*)initdata);
+ if (unlikely(pv->filename == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Failed to allocate filename");
+ CleanupPcapFileDirectoryVars(pv);
+ CleanupPcapFileThreadVars(ptv);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
- if (pcap_compile(pcap_g.pcap_handle, &pcap_g.filter, (char *)tmpbpfstring, 1, 0) < 0) {
- SCLogError(SC_ERR_BPF,"bpf compilation error %s",
- pcap_geterr(pcap_g.pcap_handle));
- SCFree(ptv);
- return TM_ECODE_FAILED;
+ int should_loop = 0;
+ pv->should_loop = false;
+ if (ConfGetBool("pcap-file.continuous", &should_loop) == 1) {
+ pv->should_loop = should_loop == 1;
}
- if (pcap_setfilter(pcap_g.pcap_handle, &pcap_g.filter) < 0) {
- SCLogError(SC_ERR_BPF,"could not set bpf filter %s", pcap_geterr(pcap_g.pcap_handle));
- SCFree(ptv);
- return TM_ECODE_FAILED;
+ pv->delay = 30;
+ intmax_t delay = 0;
+ if (ConfGetInt("pcap-file.delay", &delay) == 1) {
+ if (delay > 0 && delay < UINT_MAX) {
+ pv->delay = (time_t)delay;
+ SCLogDebug("delay %lu", pv->delay);
+ } else {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "delay out of range");
+ }
}
- }
- pcap_g.datalink = pcap_datalink(pcap_g.pcap_handle);
- SCLogDebug("datalink %" PRId32 "", pcap_g.datalink);
-
- switch (pcap_g.datalink) {
- case LINKTYPE_LINUX_SLL:
- pcap_g.Decoder = DecodeSll;
- break;
- case LINKTYPE_ETHERNET:
- pcap_g.Decoder = DecodeEthernet;
- break;
- case LINKTYPE_PPP:
- pcap_g.Decoder = DecodePPP;
- break;
- case LINKTYPE_IPV4:
- case LINKTYPE_RAW:
- case LINKTYPE_RAW2:
- pcap_g.Decoder = DecodeRaw;
- break;
- case LINKTYPE_NULL:
- pcap_g.Decoder = DecodeNull;
- break;
-
- default:
- SCLogError(SC_ERR_UNIMPLEMENTED, "datalink type %" PRId32 " not "
- "(yet) supported in module PcapFile.\n", pcap_g.datalink);
- SCFree(ptv);
- if (! RunModeUnixSocketIsActive()) {
- SCReturnInt(TM_ECODE_FAILED);
+ pv->poll_interval = 5;
+ intmax_t poll_interval = 0;
+ if (ConfGetInt("pcap-file.poll-interval", &poll_interval) == 1) {
+ if (poll_interval > 0 && poll_interval < UINT_MAX) {
+ pv->poll_interval = (time_t)poll_interval;
+ SCLogDebug("poll-interval %lu", pv->delay);
} else {
- pcap_close(pcap_g.pcap_handle);
- pcap_g.pcap_handle = NULL;
- UnixSocketPcapFile(TM_ECODE_DONE);
- SCReturnInt(TM_ECODE_DONE);
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "poll-interval out of range");
}
+ }
+
+ pv->shared = &ptv->shared;
+ pv->directory = directory;
+ TAILQ_INIT(&pv->directory_content);
+
+ ptv->is_directory = 1;
+ ptv->behavior.directory = pv;
}
if (ConfGet("pcap-file.checksum-checks", &tmpstring) != 1) {
}
pcap_g.checksum_mode = pcap_g.conf_checksum_mode;
- ptv->tv = tv;
+ ptv->shared.tv = tv;
*data = (void *)ptv;
SCReturnInt(TM_ECODE_OK);
PcapFileThreadVars *ptv = (PcapFileThreadVars *)data;
if (pcap_g.conf_checksum_mode == CHECKSUM_VALIDATION_AUTO &&
- pcap_g.cnt < CHECKSUM_SAMPLE_COUNT &&
- SC_ATOMIC_GET(pcap_g.invalid_checksums)) {
+ pcap_g.cnt < CHECKSUM_SAMPLE_COUNT &&
+ SC_ATOMIC_GET(pcap_g.invalid_checksums)) {
uint64_t chrate = pcap_g.cnt / SC_ATOMIC_GET(pcap_g.invalid_checksums);
if (chrate < CHECKSUM_INVALID_RATIO)
SCLogWarning(SC_ERR_INVALID_CHECKSUM,
"1/%" PRIu64 "th of packets have an invalid checksum,"
- " consider setting pcap-file.checksum-checks variable to no"
- " or use '-k none' option on command line.",
+ " consider setting pcap-file.checksum-checks variable to no"
+ " or use '-k none' option on command line.",
chrate);
else
SCLogInfo("1/%" PRIu64 "th of packets have an invalid checksum",
chrate);
}
- SCLogNotice("Pcap-file module read %" PRIu32 " packets, %" PRIu64 " bytes", ptv->pkts, ptv->bytes);
- return;
+ SCLogNotice(
+ "Pcap-file module read %" PRIu64 " files, %" PRIu64 " packets, %" PRIu64 " bytes",
+ ptv->shared.files,
+ ptv->shared.pkts,
+ ptv->shared.bytes
+ );
}
TmEcode ReceivePcapFileThreadDeinit(ThreadVars *tv, void *data)
{
SCEnter();
PcapFileThreadVars *ptv = (PcapFileThreadVars *)data;
- if (ptv) {
- SCFree(ptv);
- }
+ CleanupPcapFileThreadVars(ptv);
SCReturnInt(TM_ECODE_OK);
}
FlowWakeupFlowManagerThread();
}
- /* call the decoder */
- pcap_g.Decoder(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
+ Decoder decoder;
+ if(ValidateLinkType(p->datalink, &decoder) == TM_ECODE_OK) {
+
+ /* call the decoder */
+ decoder(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
#ifdef DEBUG
- BUG_ON(p->pkt_src != PKT_SRC_WIRE && p->pkt_src != PKT_SRC_FFR);
+ BUG_ON(p->pkt_src != PKT_SRC_WIRE && p->pkt_src != PKT_SRC_FFR);
#endif
- PacketDecodeFinalize(tv, dtv, p);
+ PacketDecodeFinalize(tv, dtv, p);
- SCReturnInt(TM_ECODE_OK);
+ SCReturnInt(TM_ECODE_OK);
+ } else {
+ SCReturnInt(TM_ECODE_FAILED);
+ }
}
TmEcode DecodePcapFileThreadInit(ThreadVars *tv, const void *initdata, void **data)
}
/* eof */
-
#define CLS 64
#endif
+#if HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+
#if HAVE_STDIO_H
#include <stdio.h>
#endif
printf("\t--dump-config : show the running configuration\n");
printf("\t--build-info : display build information\n");
printf("\t--pcap[=<dev>] : run in pcap mode, no value select interfaces from suricata.yaml\n");
+ printf("\t--pcap-file-continuous : when running in pcap mode with a directory, continue checking directory for pcaps until interrupted");
#ifdef HAVE_PCAP_SET_BUFF
printf("\t--pcap-buffer-size : size of the pcap buffer value from 0 - %i\n",INT_MAX);
#endif /* HAVE_SET_PCAP_BUFF */
{"af-packet", optional_argument, 0, 0},
{"netmap", optional_argument, 0, 0},
{"pcap", optional_argument, 0, 0},
+ {"pcap-file-continuous", 0, 0, 0},
{"simulate-ips", 0, 0 , 0},
{"no-random", 0, &g_disable_randomness, 1},
}
}
}
+ else if (strcmp((long_opts[option_index]).name, "pcap-file-continuous") == 0) {
+ if(ConfSetFinal("pcap-file.continuous", "true") != 1) {
+ SCLogError(SC_ERR_CMD_LINE, "Failed to set pcap-file.continuous");
+ return TM_ECODE_FAILED;
+ }
+ return TM_ECODE_OK;
+ }
break;
case 'c':
suri->conf_filename = optarg;
fprintf(stderr, "ERROR: Failed to set pcap-file.file\n");
return TM_ECODE_FAILED;
}
+
break;
case 's':
if (suri->sig_file != NULL) {