mirror of https://github.com/OISF/suricata
source-pcap-file: Pcap Directory Mode (Feature #2222)
https://redmine.openinfosecfoundation.org/issues/2222 Pcap file mode that when passed a directory will process all files in that directory. If --pcap-file-continuous or continuous option is passed in json, the directory will be monitored until the directory is moved/deleted, suricata is interrupted, or the pcap-interrupt command is used with unix command socket. Existing file implementation and new directory implementation has moved from source-pcap-file into pcap-file-helper and pcap-directory-helper. Engine state will not reset between files. Also satisfies: * https://redmine.openinfosecfoundation.org/issues/2299 * https://redmine.openinfosecfoundation.org/issues/724 * https://redmine.openinfosecfoundation.org/issues/1476 Co-Authors: Dana Helwig <dana.helwig@protectwise.com> and Danny Browning <danny.browning@protectwise.com>pull/2871/merge
parent
94e9d13791
commit
3ab9120821
@ -0,0 +1,535 @@
|
||||
/* 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 */
|
||||
@ -0,0 +1,83 @@
|
||||
/* 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__ */
|
||||
@ -0,0 +1,235 @@
|
||||
/* 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 */
|
||||
@ -0,0 +1,109 @@
|
||||
/* 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__ */
|
||||
Loading…
Reference in New Issue