mirror of https://github.com/OISF/suricata
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
406 lines
12 KiB
C
406 lines
12 KiB
C
/* Copyright (C) 2012 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.
|
|
*/
|
|
|
|
#include "suricata-common.h"
|
|
#include "tm-threads.h"
|
|
#include "conf.h"
|
|
#include "runmodes.h"
|
|
#include "runmode-pcap-file.h"
|
|
#include "log-httplog.h"
|
|
#include "output.h"
|
|
#include "source-pfring.h"
|
|
#include "detect-engine-mpm.h"
|
|
|
|
#include "alert-fastlog.h"
|
|
#include "alert-prelude.h"
|
|
#include "alert-unified2-alert.h"
|
|
#include "alert-debuglog.h"
|
|
|
|
#include "util-debug.h"
|
|
#include "util-time.h"
|
|
#include "util-cpu.h"
|
|
#include "util-affinity.h"
|
|
#include "unix-manager.h"
|
|
|
|
#include "flow-manager.h"
|
|
#include "flow-timeout.h"
|
|
#include "stream-tcp.h"
|
|
#include "output.h"
|
|
#include "host.h"
|
|
#include "defrag.h"
|
|
|
|
static const char *default_mode = NULL;
|
|
|
|
int unix_socket_mode_is_running = 0;
|
|
|
|
typedef struct PcapFiles_ {
|
|
char *filename;
|
|
char *output_dir;
|
|
TAILQ_ENTRY(PcapFiles_) next;
|
|
} PcapFiles;
|
|
|
|
typedef struct PcapCommand_ {
|
|
DetectEngineCtx *de_ctx;
|
|
TAILQ_HEAD(, PcapFiles_) files;
|
|
int running;
|
|
char *currentfile;
|
|
} PcapCommand;
|
|
|
|
const char *RunModeUnixSocketGetDefaultMode(void)
|
|
{
|
|
return default_mode;
|
|
}
|
|
|
|
#ifdef BUILD_UNIX_SOCKET
|
|
|
|
static int unix_manager_file_task_running = 0;
|
|
static int unix_manager_file_task_failed = 0;
|
|
|
|
/**
|
|
* \brief return list of files in the queue
|
|
*
|
|
* \retval 0 in case of error, 1 in case of success
|
|
*/
|
|
static TmEcode UnixSocketPcapFilesList(json_t *cmd, json_t* answer, void *data)
|
|
{
|
|
PcapCommand *this = (PcapCommand *) data;
|
|
int i = 0;
|
|
PcapFiles *file;
|
|
json_t *jdata;
|
|
json_t *jarray;
|
|
|
|
jdata = json_object();
|
|
if (jdata == NULL) {
|
|
json_object_set_new(answer, "message",
|
|
json_string("internal error at json object creation"));
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
jarray = json_array();
|
|
if (jarray == NULL) {
|
|
json_decref(jdata);
|
|
json_object_set_new(answer, "message",
|
|
json_string("internal error at json object creation"));
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
TAILQ_FOREACH(file, &this->files, next) {
|
|
json_array_append_new(jarray, json_string(file->filename));
|
|
i++;
|
|
}
|
|
json_object_set_new(jdata, "count", json_integer(i));
|
|
json_object_set_new(jdata, "files", jarray);
|
|
json_object_set_new(answer, "message", jdata);
|
|
return TM_ECODE_OK;
|
|
}
|
|
|
|
static TmEcode UnixSocketPcapFilesNumber(json_t *cmd, json_t* answer, void *data)
|
|
{
|
|
PcapCommand *this = (PcapCommand *) data;
|
|
int i = 0;
|
|
PcapFiles *file;
|
|
|
|
TAILQ_FOREACH(file, &this->files, next) {
|
|
i++;
|
|
}
|
|
json_object_set_new(answer, "message", json_integer(i));
|
|
return TM_ECODE_OK;
|
|
}
|
|
|
|
static TmEcode UnixSocketPcapCurrent(json_t *cmd, json_t* answer, void *data)
|
|
{
|
|
PcapCommand *this = (PcapCommand *) data;
|
|
|
|
if (this->currentfile) {
|
|
json_object_set_new(answer, "message", json_string(this->currentfile));
|
|
} else {
|
|
json_object_set_new(answer, "message", json_string("None"));
|
|
}
|
|
return TM_ECODE_OK;
|
|
}
|
|
|
|
|
|
|
|
static void PcapFilesFree(PcapFiles *cfile)
|
|
{
|
|
if (cfile == NULL)
|
|
return;
|
|
if (cfile->filename)
|
|
SCFree(cfile->filename);
|
|
if (cfile->output_dir)
|
|
SCFree(cfile->output_dir);
|
|
SCFree(cfile);
|
|
}
|
|
|
|
/**
|
|
* \brief Add file to file queue
|
|
*
|
|
* \param this a UnixCommand:: structure
|
|
* \param filename absolute filename
|
|
* \param output_dir absolute name of directory where log will be put
|
|
*
|
|
* \retval 0 in case of error, 1 in case of success
|
|
*/
|
|
TmEcode UnixListAddFile(PcapCommand *this, const char *filename, const char *output_dir)
|
|
{
|
|
PcapFiles *cfile = NULL;
|
|
if (filename == NULL || this == NULL)
|
|
return TM_ECODE_FAILED;
|
|
cfile = SCMalloc(sizeof(PcapFiles));
|
|
if (unlikely(cfile == NULL)) {
|
|
SCLogError(SC_ERR_MEM_ALLOC, "Unable to allocate new file");
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
memset(cfile, 0, sizeof(PcapFiles));
|
|
|
|
cfile->filename = SCStrdup(filename);
|
|
if (unlikely(cfile->filename == NULL)) {
|
|
SCFree(cfile);
|
|
SCLogError(SC_ERR_MEM_ALLOC, "Unable to dup filename");
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
|
|
if (output_dir) {
|
|
cfile->output_dir = SCStrdup(output_dir);
|
|
if (unlikely(cfile->output_dir == NULL)) {
|
|
SCFree(cfile->filename);
|
|
SCFree(cfile);
|
|
SCLogError(SC_ERR_MEM_ALLOC, "Unable to dup output_dir");
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
}
|
|
|
|
TAILQ_INSERT_TAIL(&this->files, cfile, next);
|
|
return TM_ECODE_OK;
|
|
}
|
|
|
|
/**
|
|
* \brief Command to add a file to treatment list
|
|
*
|
|
* \param cmd the content of command Arguments as a json_t object
|
|
* \param answer the json_t object that has to be used to answer
|
|
* \param data pointer to data defining the context here a PcapCommand::
|
|
*/
|
|
TmEcode UnixSocketAddPcapFile(json_t *cmd, json_t* answer, void *data)
|
|
{
|
|
PcapCommand *this = (PcapCommand *) data;
|
|
int ret;
|
|
const char *filename;
|
|
const char *output_dir;
|
|
#ifdef OS_WIN32
|
|
struct _stat st;
|
|
#else
|
|
struct stat st;
|
|
#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"));
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
filename = json_string_value(jarg);
|
|
#ifdef OS_WIN32
|
|
if(_stat(filename, &st) != 0) {
|
|
#else
|
|
if(stat(filename, &st) != 0) {
|
|
#endif /* OS_WIN32 */
|
|
json_object_set_new(answer, "message", json_string("File 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"));
|
|
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"));
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
|
|
#ifdef OS_WIN32
|
|
if(_stat(output_dir, &st) != 0) {
|
|
#else
|
|
if(stat(output_dir, &st) != 0) {
|
|
#endif /* OS_WIN32 */
|
|
json_object_set_new(answer, "message", json_string("Output directory does not exist"));
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
|
|
ret = UnixListAddFile(this, filename, output_dir);
|
|
switch(ret) {
|
|
case TM_ECODE_FAILED:
|
|
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"));
|
|
return TM_ECODE_OK;
|
|
}
|
|
return TM_ECODE_OK;
|
|
}
|
|
|
|
/**
|
|
* \brief Handle the file queue
|
|
*
|
|
* This function check if there is currently a file
|
|
* being parse. If it is not the case, it will start to
|
|
* work on a new file. This implies to start a new 'pcap-file'
|
|
* running mode after having set the file and the output dir.
|
|
* This function also handles the cleaning of the previous
|
|
* running mode.
|
|
*
|
|
* \param this a UnixCommand:: structure
|
|
* \retval 0 in case of error, 1 in case of success
|
|
*/
|
|
TmEcode UnixSocketPcapFilesCheck(void *data)
|
|
{
|
|
PcapCommand *this = (PcapCommand *) data;
|
|
if (unix_manager_file_task_running == 1) {
|
|
return TM_ECODE_OK;
|
|
}
|
|
if ((unix_manager_file_task_failed == 1) || (this->running == 1)) {
|
|
if (unix_manager_file_task_failed) {
|
|
SCLogInfo("Preceeding taks failed, cleaning the running mode");
|
|
}
|
|
unix_manager_file_task_failed = 0;
|
|
this->running = 0;
|
|
if (this->currentfile) {
|
|
SCFree(this->currentfile);
|
|
}
|
|
this->currentfile = NULL;
|
|
TmThreadKillThreadsFamily(TVT_MGMT);
|
|
TmThreadClearThreadsFamily(TVT_MGMT);
|
|
TmThreadDisableThreadsWithTMS(TM_FLAG_RECEIVE_TM | TM_FLAG_DECODE_TM);
|
|
FlowForceReassembly();
|
|
TmThreadKillThreadsFamily(TVT_PPT);
|
|
TmThreadClearThreadsFamily(TVT_PPT);
|
|
RunModeShutDown();
|
|
SCPerfReleaseResources();
|
|
/* thread killed, we can run non thread-safe shutdown functions */
|
|
FlowShutdown();
|
|
HostCleanup();
|
|
StreamTcpFreeConfig(STREAM_VERBOSE);
|
|
DefragDestroy();
|
|
TmqResetQueues();
|
|
}
|
|
if (!TAILQ_EMPTY(&this->files)) {
|
|
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;
|
|
this->running = 1;
|
|
if (ConfSet("pcap-file.file", cfile->filename, 1) != 1) {
|
|
SCLogInfo("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) != 1) {
|
|
SCLogInfo("Can not set output dir to '%s'", cfile->output_dir);
|
|
PcapFilesFree(cfile);
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
}
|
|
this->currentfile = SCStrdup(cfile->filename);
|
|
PcapFilesFree(cfile);
|
|
SCPerfInitCounterApi();
|
|
DefragInit();
|
|
FlowInitConfig(FLOW_QUIET);
|
|
StreamTcpInitConfig(STREAM_VERBOSE);
|
|
RunModeInitializeOutputs();
|
|
RunModeDispatch(RUNMODE_PCAP_FILE, NULL, this->de_ctx);
|
|
FlowManagerThreadSpawn();
|
|
SCPerfSpawnThreads();
|
|
/* Un-pause all the paused threads */
|
|
TmThreadContinueThreads();
|
|
}
|
|
return TM_ECODE_OK;
|
|
}
|
|
#endif
|
|
|
|
void RunModeUnixSocketRegister(void)
|
|
{
|
|
#ifdef BUILD_UNIX_SOCKET
|
|
RunModeRegisterNewRunMode(RUNMODE_UNIX_SOCKET, "single",
|
|
"Unix socket mode",
|
|
RunModeUnixSocketSingle);
|
|
default_mode = "single";
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
void UnixSocketPcapFile(TmEcode tm)
|
|
{
|
|
#ifdef BUILD_UNIX_SOCKET
|
|
switch (tm) {
|
|
case TM_ECODE_DONE:
|
|
unix_manager_file_task_running = 0;
|
|
break;
|
|
case TM_ECODE_FAILED:
|
|
unix_manager_file_task_running = 0;
|
|
unix_manager_file_task_failed = 1;
|
|
break;
|
|
case TM_ECODE_OK:
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* \brief Single thread version of the Pcap file processing.
|
|
*/
|
|
int RunModeUnixSocketSingle(DetectEngineCtx *de_ctx)
|
|
{
|
|
#ifdef BUILD_UNIX_SOCKET
|
|
PcapCommand *pcapcmd = SCMalloc(sizeof(PcapCommand));
|
|
|
|
if (unlikely(pcapcmd == NULL)) {
|
|
SCLogError(SC_ERR_MEM_ALLOC, "Can not allocate pcap command");
|
|
return 1;
|
|
}
|
|
pcapcmd->de_ctx = de_ctx;
|
|
TAILQ_INIT(&pcapcmd->files);
|
|
pcapcmd->currentfile = NULL;
|
|
|
|
UnixManagerThreadSpawn(de_ctx, 1);
|
|
|
|
unix_socket_mode_is_running = 1;
|
|
|
|
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-current", UnixSocketPcapCurrent, pcapcmd, 0);
|
|
|
|
UnixManagerRegisterBackgroundTask(UnixSocketPcapFilesCheck, pcapcmd);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
int RunModeUnixSocketIsActive(void)
|
|
{
|
|
return unix_socket_mode_is_running;
|
|
}
|
|
|
|
|
|
|
|
|