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.
3103 lines
102 KiB
C
3103 lines
102 KiB
C
/* Copyright (C) 2007-2022 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 Victor Julien <victor@inliniac.net>
|
|
*/
|
|
|
|
#include "suricata-common.h"
|
|
|
|
#if HAVE_GETOPT_H
|
|
#include <getopt.h>
|
|
#endif
|
|
|
|
#if HAVE_SIGNAL_H
|
|
#include <signal.h>
|
|
#endif
|
|
#ifndef OS_WIN32
|
|
#ifdef HAVE_SYS_RESOURCE_H
|
|
// setrlimit
|
|
#include <sys/resource.h>
|
|
#endif
|
|
#endif
|
|
|
|
#include "suricata.h"
|
|
|
|
#include "conf.h"
|
|
#include "conf-yaml-loader.h"
|
|
|
|
#include "decode.h"
|
|
#include "defrag.h"
|
|
#include "flow.h"
|
|
#include "stream-tcp.h"
|
|
#include "ippair.h"
|
|
|
|
#include "detect.h"
|
|
#include "detect-parse.h"
|
|
#include "detect-engine.h"
|
|
#include "detect-engine-address.h"
|
|
#include "detect-engine-alert.h"
|
|
#include "detect-engine-port.h"
|
|
#include "detect-engine-tag.h"
|
|
#include "detect-engine-threshold.h"
|
|
#include "detect-fast-pattern.h"
|
|
|
|
#include "datasets.h"
|
|
|
|
#include "feature.h"
|
|
|
|
#include "flow-bypass.h"
|
|
#include "flow-manager.h"
|
|
#include "flow-timeout.h"
|
|
#include "flow-worker.h"
|
|
|
|
#include "flow-bit.h"
|
|
#include "host-bit.h"
|
|
#include "ippair-bit.h"
|
|
|
|
#include "app-layer.h"
|
|
#include "app-layer-parser.h"
|
|
#include "app-layer-htp.h"
|
|
#include "app-layer-htp-range.h"
|
|
|
|
#include "output.h"
|
|
#include "output-filestore.h"
|
|
|
|
#include "respond-reject.h"
|
|
|
|
#include "runmode-af-packet.h"
|
|
#include "runmode-af-xdp.h"
|
|
#include "runmode-netmap.h"
|
|
#include "runmode-unittests.h"
|
|
|
|
#include "source-nfq.h"
|
|
#include "source-nfq-prototypes.h"
|
|
#include "source-nflog.h"
|
|
#include "source-ipfw.h"
|
|
#include "source-lib.h"
|
|
#include "source-pcap.h"
|
|
#include "source-pcap-file.h"
|
|
#include "source-pcap-file-helper.h"
|
|
#include "source-erf-file.h"
|
|
#include "source-erf-dag.h"
|
|
#include "source-af-packet.h"
|
|
#include "source-af-xdp.h"
|
|
#include "source-netmap.h"
|
|
#include "source-dpdk.h"
|
|
#include "source-windivert.h"
|
|
#include "source-windivert-prototypes.h"
|
|
|
|
#include "unix-manager.h"
|
|
|
|
#include "util-classification-config.h"
|
|
#include "util-threshold-config.h"
|
|
#include "util-reference-config.h"
|
|
|
|
#include "tmqh-packetpool.h"
|
|
#include "tm-queuehandlers.h"
|
|
|
|
#include "util-affinity.h"
|
|
#include "util-byte.h"
|
|
#include "util-conf.h"
|
|
#include "util-coredump-config.h"
|
|
#include "util-cpu.h"
|
|
#include "util-daemon.h"
|
|
#include "util-device-private.h"
|
|
#include "util-dpdk.h"
|
|
#include "util-ebpf.h"
|
|
#include "util-exception-policy.h"
|
|
#include "util-host-os-info.h"
|
|
#include "util-hugepages.h"
|
|
#include "util-ioctl.h"
|
|
#include "util-landlock.h"
|
|
#include "util-macset.h"
|
|
#include "util-flow-rate.h"
|
|
#include "util-misc.h"
|
|
#include "util-mpm-hs.h"
|
|
#include "util-path.h"
|
|
#include "util-pidfile.h"
|
|
#include "util-plugin.h"
|
|
#include "util-privs.h"
|
|
#include "util-profiling.h"
|
|
#include "util-proto-name.h"
|
|
#include "util-running-modes.h"
|
|
#include "util-signal.h"
|
|
#include "util-time.h"
|
|
#include "util-validate.h"
|
|
#include "util-var-name.h"
|
|
#ifdef SYSTEMD_NOTIFY
|
|
#include "util-systemd.h"
|
|
#endif
|
|
|
|
#ifdef WINDIVERT
|
|
#include "decode-sll.h"
|
|
#include "win32-syscall.h"
|
|
#endif
|
|
|
|
/*
|
|
* we put this here, because we only use it here in main.
|
|
*/
|
|
volatile sig_atomic_t sigint_count = 0;
|
|
volatile sig_atomic_t sighup_count = 0;
|
|
volatile sig_atomic_t sigterm_count = 0;
|
|
volatile sig_atomic_t sigusr2_count = 0;
|
|
|
|
/*
|
|
* Flag to indicate if the engine is at the initialization
|
|
* or already processing packets. 3 stages: SURICATA_INIT,
|
|
* SURICATA_RUNTIME and SURICATA_FINALIZE
|
|
*/
|
|
SC_ATOMIC_DECLARE(unsigned int, engine_stage);
|
|
|
|
/* Max packets processed simultaneously per thread. */
|
|
#define DEFAULT_MAX_PENDING_PACKETS 1024
|
|
|
|
/** suricata engine control flags */
|
|
volatile uint8_t suricata_ctl_flags = 0;
|
|
|
|
/** Engine mode: inline (ENGINE_MODE_IPS) or just
|
|
* detection mode (ENGINE_MODE_IDS by default) */
|
|
static enum EngineMode g_engine_mode = ENGINE_MODE_UNKNOWN;
|
|
|
|
/** Host mode: set if box is sniffing only
|
|
* or is a router */
|
|
uint8_t host_mode = SURI_HOST_IS_SNIFFER_ONLY;
|
|
|
|
/** Maximum packets to simultaneously process. */
|
|
uint32_t max_pending_packets;
|
|
|
|
/** global indicating if detection is enabled */
|
|
int g_detect_disabled = 0;
|
|
|
|
/** set caps or not */
|
|
bool sc_set_caps = false;
|
|
|
|
bool g_system = false;
|
|
|
|
/** disable randomness to get reproducible results across runs */
|
|
#ifndef AFLFUZZ_NO_RANDOM
|
|
int g_disable_randomness = 0;
|
|
#else
|
|
int g_disable_randomness = 1;
|
|
#endif
|
|
|
|
/** determine (without branching) if we include the vlan_ids when hashing or
|
|
* comparing flows */
|
|
uint16_t g_vlan_mask = 0xffff;
|
|
|
|
/** determine (without branching) if we include the livedev ids when hashing or
|
|
* comparing flows */
|
|
uint16_t g_livedev_mask = 0xffff;
|
|
|
|
/** determine (without branching) if we include the recursion levels when hashing or
|
|
* comparing flows */
|
|
uint8_t g_recurlvl_mask = 0xff;
|
|
|
|
/* flag to disable hashing almost globally, to be similar to disabling nss
|
|
* support */
|
|
bool g_disable_hashing = false;
|
|
|
|
/* snapshot of the system's hugepages before system intitialization. */
|
|
SystemHugepageSnapshot *prerun_snap = NULL;
|
|
|
|
/** add per-proto app-layer error counters for exception policies stats? disabled by default */
|
|
bool g_stats_eps_per_app_proto_errors = false;
|
|
|
|
/** Suricata instance */
|
|
SCInstance suricata;
|
|
|
|
int SuriHasSigFile(void)
|
|
{
|
|
return (suricata.sig_file != NULL);
|
|
}
|
|
|
|
int EngineModeIsUnknown(void)
|
|
{
|
|
return (g_engine_mode == ENGINE_MODE_UNKNOWN);
|
|
}
|
|
|
|
int EngineModeIsIPS(void)
|
|
{
|
|
DEBUG_VALIDATE_BUG_ON(g_engine_mode == ENGINE_MODE_UNKNOWN);
|
|
return (g_engine_mode == ENGINE_MODE_IPS);
|
|
}
|
|
|
|
int EngineModeIsIDS(void)
|
|
{
|
|
DEBUG_VALIDATE_BUG_ON(g_engine_mode == ENGINE_MODE_UNKNOWN);
|
|
return (g_engine_mode == ENGINE_MODE_IDS);
|
|
}
|
|
|
|
void EngineModeSetIPS(void)
|
|
{
|
|
g_engine_mode = ENGINE_MODE_IPS;
|
|
}
|
|
|
|
void EngineModeSetIDS(void)
|
|
{
|
|
g_engine_mode = ENGINE_MODE_IDS;
|
|
}
|
|
|
|
#ifdef UNITTESTS
|
|
int RunmodeIsUnittests(void)
|
|
{
|
|
if (suricata.run_mode == RUNMODE_UNITTEST)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
SCRunMode SCRunmodeGet(void)
|
|
{
|
|
return suricata.run_mode;
|
|
}
|
|
|
|
void SCRunmodeSet(SCRunMode run_mode)
|
|
{
|
|
suricata.run_mode = run_mode;
|
|
}
|
|
|
|
/** signal handlers
|
|
*
|
|
* WARNING: don't use the SCLog* API in the handlers. The API is complex
|
|
* with memory allocation possibly happening, calls to syslog, json message
|
|
* construction, etc.
|
|
*/
|
|
|
|
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
|
static void SignalHandlerSigint(/*@unused@*/ int sig)
|
|
{
|
|
sigint_count = 1;
|
|
}
|
|
static void SignalHandlerSigterm(/*@unused@*/ int sig)
|
|
{
|
|
sigterm_count = 1;
|
|
}
|
|
#ifndef OS_WIN32
|
|
#if HAVE_LIBUNWIND
|
|
#define UNW_LOCAL_ONLY
|
|
#include <libunwind.h>
|
|
static void SignalHandlerUnexpected(int sig_num, siginfo_t *info, void *context)
|
|
{
|
|
char msg[SC_LOG_MAX_LOG_MSG_LEN];
|
|
unw_cursor_t cursor;
|
|
/* Restore defaults for signals to avoid loops */
|
|
signal(SIGABRT, SIG_DFL);
|
|
signal(SIGSEGV, SIG_DFL);
|
|
int r;
|
|
if ((r = unw_init_local(&cursor, (unw_context_t *)(context)) != 0)) {
|
|
SCLogError("unable to obtain stack trace: unw_init_local: %s", unw_strerror(r));
|
|
goto terminate;
|
|
}
|
|
|
|
char *temp = msg;
|
|
int cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - msg), "stacktrace:sig %d:", sig_num);
|
|
temp += cw;
|
|
r = 1;
|
|
while (r > 0) {
|
|
if (unw_is_signal_frame(&cursor) == 0) {
|
|
unw_word_t off;
|
|
char name[256];
|
|
if (unw_get_proc_name(&cursor, name, sizeof(name), &off) == UNW_ENOMEM) {
|
|
cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - msg), "[unknown]:");
|
|
} else {
|
|
cw = snprintf(
|
|
temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - msg), "%s+0x%08" PRIx64, name, off);
|
|
}
|
|
temp += cw;
|
|
}
|
|
|
|
r = unw_step(&cursor);
|
|
if (r > 0) {
|
|
cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - msg), ";");
|
|
temp += cw;
|
|
}
|
|
}
|
|
SCLogError("%s", msg);
|
|
|
|
terminate:
|
|
// Propagate signal to watchers, if any
|
|
kill(getpid(), sig_num);
|
|
}
|
|
#undef UNW_LOCAL_ONLY
|
|
#endif /* HAVE_LIBUNWIND */
|
|
#endif /* !OS_WIN32 */
|
|
#endif
|
|
|
|
#ifndef OS_WIN32
|
|
/**
|
|
* SIGUSR2 handler. Just set sigusr2_count. The main loop will act on
|
|
* it.
|
|
*/
|
|
static void SignalHandlerSigusr2(int sig)
|
|
{
|
|
if (sigusr2_count < 2)
|
|
sigusr2_count++;
|
|
}
|
|
|
|
/**
|
|
* SIGHUP handler. Just set sighup_count. The main loop will act on
|
|
* it.
|
|
*/
|
|
static void SignalHandlerSigHup(/*@unused@*/ int sig)
|
|
{
|
|
sighup_count = 1;
|
|
}
|
|
#endif
|
|
|
|
void GlobalsInitPreConfig(void)
|
|
{
|
|
TimeInit();
|
|
SupportFastPatternForSigMatchTypes();
|
|
SCThresholdConfGlobalInit();
|
|
SCProtoNameInit();
|
|
}
|
|
|
|
void GlobalsDestroy(void)
|
|
{
|
|
SCInstance *suri = &suricata;
|
|
ThresholdDestroy();
|
|
HostShutdown();
|
|
HTPFreeConfig();
|
|
HTPAtExitPrintStats();
|
|
|
|
AppLayerHtpPrintStats();
|
|
|
|
/* TODO this can do into it's own func */
|
|
DetectEngineCtx *de_ctx = DetectEngineGetCurrent();
|
|
if (de_ctx) {
|
|
DetectEngineMoveToFreeList(de_ctx);
|
|
DetectEngineDeReference(&de_ctx);
|
|
}
|
|
DetectEngineClearMaster();
|
|
|
|
AppLayerDeSetup();
|
|
DatasetsSave();
|
|
DatasetsDestroy();
|
|
OutputTxShutdown();
|
|
TagDestroyCtx();
|
|
|
|
LiveDeviceListClean();
|
|
OutputDeregisterAll();
|
|
FeatureTrackingRelease();
|
|
SCProtoNameRelease();
|
|
TimeDeinit();
|
|
SigTableCleanup();
|
|
TmqhCleanup();
|
|
TmModuleRunDeInit();
|
|
ParseSizeDeinit();
|
|
DatalinkTableDeinit();
|
|
|
|
#ifdef HAVE_DPDK
|
|
DPDKCleanupEAL();
|
|
#endif
|
|
|
|
#ifdef HAVE_AF_PACKET
|
|
AFPPeersListClean();
|
|
#endif
|
|
|
|
#ifdef NFQ
|
|
NFQContextsClean();
|
|
#endif
|
|
|
|
#ifdef BUILD_HYPERSCAN
|
|
MpmHSGlobalCleanup();
|
|
#endif
|
|
|
|
SCConfDeInit();
|
|
|
|
DetectParseFreeRegexes();
|
|
|
|
SCPidfileRemove(suri->pid_filename);
|
|
SCFree(suri->pid_filename);
|
|
suri->pid_filename = NULL;
|
|
|
|
VarNameStoreDestroy();
|
|
SCLogDeInitLogModule();
|
|
}
|
|
|
|
/**
|
|
* \brief Used to send OS specific notification of running threads
|
|
*
|
|
* \retval TmEcode TM_ECODE_OK on success; TM_ECODE_FAILED on failure.
|
|
*/
|
|
static void OnNotifyRunning(void)
|
|
{
|
|
#ifdef SYSTEMD_NOTIFY
|
|
if (SystemDNotifyReady() < 0) {
|
|
SCLogWarning("failed to notify systemd");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/** \brief make sure threads can stop the engine by calling this
|
|
* function. Purpose: pcap file mode needs to be able to tell the
|
|
* engine the file eof is reached. */
|
|
void EngineStop(void)
|
|
{
|
|
suricata_ctl_flags |= SURICATA_STOP;
|
|
}
|
|
|
|
/**
|
|
* \brief Used to indicate that the current task is done.
|
|
*
|
|
* This is mainly used by pcap-file to tell it has finished
|
|
* to treat a pcap files when running in unix-socket mode.
|
|
*/
|
|
void EngineDone(void)
|
|
{
|
|
suricata_ctl_flags |= SURICATA_DONE;
|
|
}
|
|
|
|
static int SetBpfString(int argc, char *argv[])
|
|
{
|
|
char *bpf_filter = NULL;
|
|
uint32_t bpf_len = 0;
|
|
int tmpindex = 0;
|
|
|
|
/* attempt to parse remaining args as bpf filter */
|
|
tmpindex = argc;
|
|
while(argv[tmpindex] != NULL) {
|
|
bpf_len+=strlen(argv[tmpindex]) + 1;
|
|
tmpindex++;
|
|
}
|
|
|
|
if (bpf_len == 0)
|
|
return TM_ECODE_OK;
|
|
|
|
bpf_filter = SCCalloc(1, bpf_len);
|
|
if (unlikely(bpf_filter == NULL))
|
|
return TM_ECODE_FAILED;
|
|
|
|
tmpindex = optind;
|
|
while(argv[tmpindex] != NULL) {
|
|
strlcat(bpf_filter, argv[tmpindex],bpf_len);
|
|
if(argv[tmpindex + 1] != NULL) {
|
|
strlcat(bpf_filter," ", bpf_len);
|
|
}
|
|
tmpindex++;
|
|
}
|
|
|
|
if(strlen(bpf_filter) > 0) {
|
|
if (SCConfSetFinal("bpf-filter", bpf_filter) != 1) {
|
|
SCLogError("Failed to set bpf filter.");
|
|
SCFree(bpf_filter);
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
}
|
|
SCFree(bpf_filter);
|
|
|
|
return TM_ECODE_OK;
|
|
}
|
|
|
|
static void SetBpfStringFromFile(char *filename)
|
|
{
|
|
char *bpf_filter = NULL;
|
|
char *bpf_comment_tmp = NULL;
|
|
char *bpf_comment_start = NULL;
|
|
size_t bpf_len = 0;
|
|
SCStat st;
|
|
FILE *fp = NULL;
|
|
size_t nm = 0;
|
|
|
|
fp = fopen(filename, "r");
|
|
if (fp == NULL) {
|
|
SCLogError("Failed to open file %s", filename);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (SCFstatFn(fileno(fp), &st) != 0) {
|
|
SCLogError("Failed to stat file %s", filename);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
// st.st_size is signed on Windows
|
|
bpf_len = ((size_t)(st.st_size)) + 1;
|
|
|
|
bpf_filter = SCCalloc(1, bpf_len);
|
|
if (unlikely(bpf_filter == NULL)) {
|
|
SCLogError("Failed to allocate buffer for bpf filter in file %s", filename);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
nm = fread(bpf_filter, 1, bpf_len - 1, fp);
|
|
if ((ferror(fp) != 0) || (nm != (bpf_len - 1))) {
|
|
SCLogError("Failed to read complete BPF file %s", filename);
|
|
SCFree(bpf_filter);
|
|
fclose(fp);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
fclose(fp);
|
|
bpf_filter[nm] = '\0';
|
|
|
|
if(strlen(bpf_filter) > 0) {
|
|
/*replace comments with space*/
|
|
bpf_comment_start = bpf_filter;
|
|
while((bpf_comment_tmp = strchr(bpf_comment_start, '#')) != NULL) {
|
|
while((*bpf_comment_tmp !='\0') &&
|
|
(*bpf_comment_tmp != '\r') && (*bpf_comment_tmp != '\n'))
|
|
{
|
|
*bpf_comment_tmp++ = ' ';
|
|
}
|
|
bpf_comment_start = bpf_comment_tmp;
|
|
}
|
|
/*remove remaining '\r' and '\n' */
|
|
while((bpf_comment_tmp = strchr(bpf_filter, '\r')) != NULL) {
|
|
*bpf_comment_tmp = ' ';
|
|
}
|
|
while((bpf_comment_tmp = strchr(bpf_filter, '\n')) != NULL) {
|
|
*bpf_comment_tmp = ' ';
|
|
}
|
|
/* cut trailing spaces */
|
|
while (strlen(bpf_filter) > 0 &&
|
|
bpf_filter[strlen(bpf_filter)-1] == ' ')
|
|
{
|
|
bpf_filter[strlen(bpf_filter)-1] = '\0';
|
|
}
|
|
if (strlen(bpf_filter) > 0) {
|
|
if (SCConfSetFinal("bpf-filter", bpf_filter) != 1) {
|
|
SCFree(bpf_filter);
|
|
FatalError("failed to set bpf filter");
|
|
}
|
|
}
|
|
}
|
|
SCFree(bpf_filter);
|
|
}
|
|
|
|
static void PrintUsage(const char *progname)
|
|
{
|
|
#ifdef REVISION
|
|
printf("%s %s (%s)\n", PROG_NAME, PROG_VER, xstr(REVISION));
|
|
#else
|
|
printf("%s %s\n", PROG_NAME, PROG_VER);
|
|
#endif
|
|
printf("USAGE: %s [OPTIONS] [BPF FILTER]\n\n", progname);
|
|
printf("\t-c <path> : path to configuration file\n");
|
|
printf("\t-T : test configuration file (use with -c)\n");
|
|
printf("\t-i <dev or ip> : run in pcap live mode\n");
|
|
printf("\t-F <bpf filter file> : bpf filter file\n");
|
|
printf("\t-r <path> : run in pcap file/offline mode\n");
|
|
#ifdef NFQ
|
|
printf("\t-q <qid[:qid]> : run in inline nfqueue mode (use colon to specify a range of queues)\n");
|
|
#endif /* NFQ */
|
|
#ifdef IPFW
|
|
printf("\t-d <divert port> : run in inline ipfw divert mode\n");
|
|
#endif /* IPFW */
|
|
printf("\t-s <path> : path to signature file loaded in addition to suricata.yaml settings (optional)\n");
|
|
printf("\t-S <path> : path to signature file loaded exclusively (optional)\n");
|
|
printf("\t-l <dir> : default log directory\n");
|
|
#ifndef OS_WIN32
|
|
printf("\t-D : run as daemon\n");
|
|
#else
|
|
printf("\t--service-install : install as service\n");
|
|
printf("\t--service-remove : remove service\n");
|
|
printf("\t--service-change-params : change service startup parameters\n");
|
|
#endif /* OS_WIN32 */
|
|
printf("\t-k [all|none] : force checksum check (all) or disabled it (none)\n");
|
|
printf("\t-V : display Suricata version\n");
|
|
printf("\t-v : be more verbose (use multiple times to increase verbosity)\n");
|
|
#ifdef UNITTESTS
|
|
printf("\t-u : run the unittests and exit\n");
|
|
printf("\t-U, --unittest-filter=REGEX : filter unittests with a regex\n");
|
|
printf("\t--list-unittests : list unit tests\n");
|
|
printf("\t--fatal-unittests : enable fatal failure on unittest error\n");
|
|
printf("\t--unittests-coverage : display unittest coverage report\n");
|
|
#endif /* UNITTESTS */
|
|
printf("\t--firewall-rules-exclusive=<path> : path to firewall rule file loaded "
|
|
"exclusively\n");
|
|
printf("\t--list-app-layer-protos : list supported app layer protocols\n");
|
|
printf("\t--list-app-layer-hooks : list supported app layer hooks for use in "
|
|
"rules\n");
|
|
printf("\t--list-keywords[=all|csv|<kword>] : list keywords implemented by the engine\n");
|
|
printf("\t--list-runmodes : list supported runmodes\n");
|
|
printf("\t--runmode <runmode_id> : specific runmode modification the engine should run. The argument\n"
|
|
"\t supplied should be the id for the runmode obtained by running\n"
|
|
"\t --list-runmodes\n");
|
|
printf("\t--engine-analysis : print reports on analysis of different sections in the engine and exit.\n"
|
|
"\t Please have a look at the conf parameter engine-analysis on what reports\n"
|
|
"\t can be printed\n");
|
|
printf("\t--pidfile <file> : write pid to this file\n");
|
|
printf("\t--init-errors-fatal : enable fatal failure on signature init error\n");
|
|
printf("\t--disable-detection : disable detection engine\n");
|
|
printf("\t--dump-config : show the running configuration\n");
|
|
printf("\t--dump-features : display provided features\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\n");
|
|
printf("\t--pcap-file-delete : when running in replay mode (-r with directory or file), will delete pcap files that have been processed when done\n");
|
|
printf("\t--pcap-file-recursive : will descend into subdirectories when running in replay mode (-r)\n");
|
|
printf("\t--pcap-file-buffer-size : set read buffer size (setvbuf)\n");
|
|
#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 */
|
|
#ifdef HAVE_DPDK
|
|
printf("\t--dpdk : run in dpdk mode, uses interfaces from "
|
|
"suricata.yaml\n");
|
|
#endif
|
|
#ifdef HAVE_AF_PACKET
|
|
printf("\t--af-packet[=<dev>] : run in af-packet mode, no value select interfaces from suricata.yaml\n");
|
|
#endif
|
|
#ifdef HAVE_AF_XDP
|
|
printf("\t--af-xdp[=<dev>] : run in af-xdp mode, no value select "
|
|
"interfaces from suricata.yaml\n");
|
|
#endif
|
|
#ifdef HAVE_NETMAP
|
|
printf("\t--netmap[=<dev>] : run in netmap mode, no value select interfaces from suricata.yaml\n");
|
|
#endif
|
|
#ifdef HAVE_PFRING
|
|
printf("\t--pfring[=<dev>] : run in pfring mode, use interfaces from suricata.yaml\n");
|
|
printf("\t--pfring-int <dev> : run in pfring mode, use interface <dev>\n");
|
|
printf("\t--pfring-cluster-id <id> : pfring cluster id \n");
|
|
printf("\t--pfring-cluster-type <type> : pfring cluster type for PF_RING 4.1.2 and later cluster_round_robin|cluster_flow\n");
|
|
#endif /* HAVE_PFRING */
|
|
printf("\t--simulate-ips : force engine into IPS mode. Useful for QA\n");
|
|
#ifdef HAVE_LIBCAP_NG
|
|
printf("\t--user <user> : run suricata as this user after init\n");
|
|
printf("\t--group <group> : run suricata as this group after init\n");
|
|
#endif /* HAVE_LIBCAP_NG */
|
|
printf("\t--erf-in <path> : process an ERF file\n");
|
|
#ifdef HAVE_DAG
|
|
printf("\t--dag <dagX:Y> : process ERF records from DAG interface X, stream Y\n");
|
|
#endif
|
|
#ifdef BUILD_UNIX_SOCKET
|
|
printf("\t--unix-socket[=<file>] : use unix socket to control suricata work\n");
|
|
#endif
|
|
#ifdef WINDIVERT
|
|
printf("\t--windivert <filter> : run in inline WinDivert mode\n");
|
|
printf("\t--windivert-forward <filter> : run in inline WinDivert mode, as a gateway\n");
|
|
#endif
|
|
#ifdef HAVE_LIBNET11
|
|
printf("\t--reject-dev <dev> : send reject packets from this interface\n");
|
|
#endif
|
|
printf("\t--include <path> : additional configuration file\n");
|
|
printf("\t--set name=value : set a configuration value\n");
|
|
printf("\n");
|
|
printf("\nTo run the engine with default configuration on "
|
|
"interface eth0 with signature file \"signatures.rules\", run the "
|
|
"command as:\n\n%s -c suricata.yaml -s signatures.rules -i eth0 \n\n",
|
|
progname);
|
|
}
|
|
|
|
static void PrintBuildInfo(void)
|
|
{
|
|
const char *bits;
|
|
const char *endian;
|
|
char features[2048] = "";
|
|
const char *tls;
|
|
|
|
printf("This is %s version %s\n", PROG_NAME, GetProgramVersion());
|
|
#ifdef DEBUG
|
|
strlcat(features, "DEBUG ", sizeof(features));
|
|
#endif
|
|
#ifdef DEBUG_VALIDATION
|
|
strlcat(features, "DEBUG_VALIDATION ", sizeof(features));
|
|
#endif
|
|
#ifdef UNITTESTS
|
|
strlcat(features, "UNITTESTS ", sizeof(features));
|
|
#endif
|
|
#ifdef NFQ
|
|
strlcat(features, "NFQ ", sizeof(features));
|
|
#endif
|
|
#ifdef IPFW
|
|
strlcat(features, "IPFW ", sizeof(features));
|
|
#endif
|
|
#ifdef HAVE_PCAP_SET_BUFF
|
|
strlcat(features, "PCAP_SET_BUFF ", sizeof(features));
|
|
#endif
|
|
#ifdef HAVE_PFRING
|
|
strlcat(features, "PF_RING ", sizeof(features));
|
|
#endif
|
|
#ifdef HAVE_NAPATECH
|
|
strlcat(features, "NAPATECH ", sizeof(features));
|
|
#endif
|
|
#ifdef HAVE_AF_PACKET
|
|
strlcat(features, "AF_PACKET ", sizeof(features));
|
|
#endif
|
|
#ifdef HAVE_NETMAP
|
|
strlcat(features, "NETMAP ", sizeof(features));
|
|
#endif
|
|
#ifdef HAVE_PACKET_FANOUT
|
|
strlcat(features, "HAVE_PACKET_FANOUT ", sizeof(features));
|
|
#endif
|
|
#ifdef HAVE_DAG
|
|
strlcat(features, "DAG ", sizeof(features));
|
|
#endif
|
|
#ifdef HAVE_LIBCAP_NG
|
|
strlcat(features, "LIBCAP_NG ", sizeof(features));
|
|
#endif
|
|
#ifdef HAVE_LIBNET11
|
|
strlcat(features, "LIBNET1.1 ", sizeof(features));
|
|
#endif
|
|
strlcat(features, "HAVE_HTP_URI_NORMALIZE_HOOK ", sizeof(features));
|
|
#ifdef PCRE2_HAVE_JIT
|
|
strlcat(features, "PCRE_JIT ", sizeof(features));
|
|
#endif
|
|
/* For compatibility, just say we have HAVE_NSS. */
|
|
strlcat(features, "HAVE_NSS ", sizeof(features));
|
|
/* HTTP2_DECOMPRESSION is not an optional feature in this major version */
|
|
strlcat(features, "HTTP2_DECOMPRESSION ", sizeof(features));
|
|
/* Lua is now vendored in and always available. */
|
|
strlcat(features, "HAVE_LUA ", sizeof(features));
|
|
#ifdef HAVE_JA3
|
|
strlcat(features, "HAVE_JA3 ", sizeof(features));
|
|
#endif
|
|
#ifdef HAVE_JA4
|
|
strlcat(features, "HAVE_JA4 ", sizeof(features));
|
|
#endif
|
|
strlcat(features, "HAVE_LIBJANSSON ", sizeof(features));
|
|
#ifdef PROFILING
|
|
strlcat(features, "PROFILING ", sizeof(features));
|
|
#endif
|
|
#ifdef PROFILE_LOCKING
|
|
strlcat(features, "PROFILE_LOCKING ", sizeof(features));
|
|
#endif
|
|
#if defined(TLS_C11) || defined(TLS_GNU)
|
|
strlcat(features, "TLS ", sizeof(features));
|
|
#endif
|
|
#if defined(TLS_C11)
|
|
strlcat(features, "TLS_C11 ", sizeof(features));
|
|
#elif defined(TLS_GNU)
|
|
strlcat(features, "TLS_GNU ", sizeof(features));
|
|
#endif
|
|
#ifdef HAVE_MAGIC
|
|
strlcat(features, "MAGIC ", sizeof(features));
|
|
#endif
|
|
strlcat(features, "RUST ", sizeof(features));
|
|
#if defined(SC_ADDRESS_SANITIZER)
|
|
strlcat(features, "ASAN ", sizeof(features));
|
|
#endif
|
|
#if defined(HAVE_POPCNT64)
|
|
strlcat(features, "POPCNT64 ", sizeof(features));
|
|
#endif
|
|
if (strlen(features) == 0) {
|
|
strlcat(features, "none", sizeof(features));
|
|
}
|
|
|
|
printf("Features: %s\n", features);
|
|
|
|
/* SIMD stuff */
|
|
memset(features, 0x00, sizeof(features));
|
|
#if defined(__SSE4_2__)
|
|
strlcat(features, "SSE_4_2 ", sizeof(features));
|
|
#endif
|
|
#if defined(__SSE4_1__)
|
|
strlcat(features, "SSE_4_1 ", sizeof(features));
|
|
#endif
|
|
#if defined(__SSE3__)
|
|
strlcat(features, "SSE_3 ", sizeof(features));
|
|
#endif
|
|
#if defined(__SSE2__)
|
|
strlcat(features, "SSE_2 ", sizeof(features));
|
|
#endif
|
|
if (strlen(features) == 0) {
|
|
strlcat(features, "none", sizeof(features));
|
|
}
|
|
printf("SIMD support: %s\n", features);
|
|
|
|
/* atomics stuff */
|
|
memset(features, 0x00, sizeof(features));
|
|
#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1)
|
|
strlcat(features, "1 ", sizeof(features));
|
|
#endif
|
|
#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2)
|
|
strlcat(features, "2 ", sizeof(features));
|
|
#endif
|
|
#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)
|
|
strlcat(features, "4 ", sizeof(features));
|
|
#endif
|
|
#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8)
|
|
strlcat(features, "8 ", sizeof(features));
|
|
#endif
|
|
#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16)
|
|
strlcat(features, "16 ", sizeof(features));
|
|
#endif
|
|
if (strlen(features) == 0) {
|
|
strlcat(features, "none", sizeof(features));
|
|
} else {
|
|
strlcat(features, "byte(s)", sizeof(features));
|
|
}
|
|
printf("Atomic intrinsics: %s\n", features);
|
|
|
|
#if __WORDSIZE == 64
|
|
bits = "64-bits";
|
|
#elif __WORDSIZE == 32
|
|
bits = "32-bits";
|
|
#else
|
|
bits = "<unknown>-bits";
|
|
#endif
|
|
|
|
#if __BYTE_ORDER == __BIG_ENDIAN
|
|
endian = "Big-endian";
|
|
#elif __BYTE_ORDER == __LITTLE_ENDIAN
|
|
endian = "Little-endian";
|
|
#else
|
|
endian = "<unknown>-endian";
|
|
#endif
|
|
|
|
printf("%s, %s architecture\n", bits, endian);
|
|
#ifdef __GNUC__
|
|
printf("GCC version %s, C version %"PRIiMAX"\n", __VERSION__, (intmax_t)__STDC_VERSION__);
|
|
#else
|
|
printf("C version %"PRIiMAX"\n", (intmax_t)__STDC_VERSION__);
|
|
#endif
|
|
|
|
#if __SSP__ == 1
|
|
printf("compiled with -fstack-protector\n");
|
|
#endif
|
|
#if __SSP_ALL__ == 2
|
|
printf("compiled with -fstack-protector-all\n");
|
|
#endif
|
|
/*
|
|
* Workaround for special defines of _FORTIFY_SOURCE like
|
|
* FORTIFY_SOURCE=((defined __OPTIMIZE && OPTIMIZE > 0) ? 2 : 0)
|
|
* which is used by Gentoo for example and would result in the error
|
|
* 'defined' undeclared when _FORTIFY_SOURCE used via %d in printf func
|
|
*
|
|
*/
|
|
#if _FORTIFY_SOURCE == 2
|
|
printf("compiled with _FORTIFY_SOURCE=2\n");
|
|
#elif _FORTIFY_SOURCE == 1
|
|
printf("compiled with _FORTIFY_SOURCE=1\n");
|
|
#elif _FORTIFY_SOURCE == 0
|
|
printf("compiled with _FORTIFY_SOURCE=0\n");
|
|
#endif
|
|
#ifdef CLS
|
|
printf("L1 cache line size (CLS)=%d\n", CLS);
|
|
#endif
|
|
#if defined(TLS_C11)
|
|
tls = "_Thread_local";
|
|
#elif defined(TLS_GNU)
|
|
tls = "__thread";
|
|
#else
|
|
#error "Unsupported thread local"
|
|
#endif
|
|
printf("thread local storage method: %s\n", tls);
|
|
|
|
printf("compiled with %s\n", htp_get_version());
|
|
printf("\n");
|
|
#include "build-info.h"
|
|
}
|
|
|
|
int coverage_unittests;
|
|
int g_ut_modules;
|
|
int g_ut_covered;
|
|
|
|
void RegisterAllModules(void)
|
|
{
|
|
/* commanders */
|
|
TmModuleUnixManagerRegister();
|
|
/* managers */
|
|
TmModuleFlowManagerRegister();
|
|
TmModuleFlowRecyclerRegister();
|
|
TmModuleBypassedFlowManagerRegister();
|
|
/* nfq */
|
|
TmModuleReceiveNFQRegister();
|
|
TmModuleVerdictNFQRegister();
|
|
TmModuleDecodeNFQRegister();
|
|
/* ipfw */
|
|
TmModuleReceiveIPFWRegister();
|
|
TmModuleVerdictIPFWRegister();
|
|
TmModuleDecodeIPFWRegister();
|
|
/* pcap live */
|
|
TmModuleReceivePcapRegister();
|
|
TmModuleDecodePcapRegister();
|
|
/* pcap file */
|
|
TmModuleReceivePcapFileRegister();
|
|
TmModuleDecodePcapFileRegister();
|
|
/* af-packet */
|
|
TmModuleReceiveAFPRegister();
|
|
TmModuleDecodeAFPRegister();
|
|
/* af-xdp */
|
|
TmModuleReceiveAFXDPRegister();
|
|
TmModuleDecodeAFXDPRegister();
|
|
/* netmap */
|
|
TmModuleReceiveNetmapRegister();
|
|
TmModuleDecodeNetmapRegister();
|
|
/* dag file */
|
|
TmModuleReceiveErfFileRegister();
|
|
TmModuleDecodeErfFileRegister();
|
|
/* dag live */
|
|
TmModuleReceiveErfDagRegister();
|
|
TmModuleDecodeErfDagRegister();
|
|
|
|
/* flow worker */
|
|
TmModuleFlowWorkerRegister();
|
|
/* respond-reject */
|
|
TmModuleRespondRejectRegister();
|
|
|
|
/* log api */
|
|
TmModuleLoggerRegister();
|
|
TmModuleStatsLoggerRegister();
|
|
|
|
TmModuleDebugList();
|
|
/* nflog */
|
|
TmModuleReceiveNFLOGRegister();
|
|
TmModuleDecodeNFLOGRegister();
|
|
|
|
/* windivert */
|
|
TmModuleReceiveWinDivertRegister();
|
|
TmModuleVerdictWinDivertRegister();
|
|
TmModuleDecodeWinDivertRegister();
|
|
|
|
/* Dpdk */
|
|
TmModuleReceiveDPDKRegister();
|
|
TmModuleDecodeDPDKRegister();
|
|
|
|
/* Library */
|
|
TmModuleDecodeLibRegister();
|
|
}
|
|
|
|
TmEcode SCLoadYamlConfig(void)
|
|
{
|
|
SCEnter();
|
|
|
|
SCInstance *suri = &suricata;
|
|
|
|
if (suri->conf_filename == NULL)
|
|
suri->conf_filename = DEFAULT_CONF_FILE;
|
|
|
|
if (SCConfYamlLoadFile(suri->conf_filename) != 0) {
|
|
/* Error already displayed. */
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
}
|
|
|
|
if (suri->additional_configs) {
|
|
for (int i = 0; suri->additional_configs[i] != NULL; i++) {
|
|
SCLogConfig("Loading additional configuration file %s", suri->additional_configs[i]);
|
|
SCConfYamlHandleInclude(SCConfGetRootNode(), suri->additional_configs[i]);
|
|
}
|
|
}
|
|
|
|
SCReturnInt(TM_ECODE_OK);
|
|
}
|
|
|
|
static TmEcode ParseInterfacesList(const int runmode, char *pcap_dev)
|
|
{
|
|
SCEnter();
|
|
|
|
/* run the selected runmode */
|
|
if (runmode == RUNMODE_PCAP_DEV) {
|
|
if (strlen(pcap_dev) == 0) {
|
|
int ret = LiveBuildDeviceList("pcap");
|
|
if (ret == 0) {
|
|
SCLogError("No interface found in config for pcap");
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
}
|
|
}
|
|
} else if (runmode == RUNMODE_PLUGIN) {
|
|
if (strcmp(suricata.capture_plugin_name, "pfring") == 0) {
|
|
/* Special handling for pfring. */
|
|
if (strlen(pcap_dev)) {
|
|
if (SCConfSetFinal("pfring.live-interface", pcap_dev) != 1) {
|
|
SCLogError("Failed to set pfring.live-interface");
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
}
|
|
}
|
|
}
|
|
#ifdef HAVE_DPDK
|
|
} else if (runmode == RUNMODE_DPDK) {
|
|
char iface_selector[] = "dpdk.interfaces";
|
|
int ret = LiveBuildDeviceList(iface_selector);
|
|
if (ret == 0) {
|
|
SCLogError("No interface found in config for %s", iface_selector);
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
}
|
|
#endif
|
|
#ifdef HAVE_AF_PACKET
|
|
} else if (runmode == RUNMODE_AFP_DEV) {
|
|
/* iface has been set on command line */
|
|
if (strlen(pcap_dev)) {
|
|
if (SCConfSetFinal("af-packet.live-interface", pcap_dev) != 1) {
|
|
SCLogError("Failed to set af-packet.live-interface");
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
}
|
|
} else {
|
|
int ret = LiveBuildDeviceList("af-packet");
|
|
if (ret == 0) {
|
|
SCLogError("No interface found in config for af-packet");
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef HAVE_AF_XDP
|
|
} else if (runmode == RUNMODE_AFXDP_DEV) {
|
|
/* iface has been set on command line */
|
|
if (strlen(pcap_dev)) {
|
|
if (SCConfSetFinal("af-xdp.live-interface", pcap_dev) != 1) {
|
|
SCLogError("Failed to set af-xdp.live-interface");
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
}
|
|
} else {
|
|
int ret = LiveBuildDeviceList("af-xdp");
|
|
if (ret == 0) {
|
|
SCLogError("No interface found in config for af-xdp");
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef HAVE_NETMAP
|
|
} else if (runmode == RUNMODE_NETMAP) {
|
|
/* iface has been set on command line */
|
|
if (strlen(pcap_dev)) {
|
|
if (SCConfSetFinal("netmap.live-interface", pcap_dev) != 1) {
|
|
SCLogError("Failed to set netmap.live-interface");
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
}
|
|
} else {
|
|
int ret = LiveBuildDeviceList("netmap");
|
|
if (ret == 0) {
|
|
SCLogError("No interface found in config for netmap");
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef HAVE_NFLOG
|
|
} else if (runmode == RUNMODE_NFLOG) {
|
|
int ret = LiveBuildDeviceListCustom("nflog", "group");
|
|
if (ret == 0) {
|
|
SCLogError("No group found in config for nflog");
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
SCReturnInt(TM_ECODE_OK);
|
|
}
|
|
|
|
static void SCInstanceInit(SCInstance *suri, const char *progname)
|
|
{
|
|
memset(suri, 0x00, sizeof(*suri));
|
|
|
|
suri->progname = progname;
|
|
suri->run_mode = RUNMODE_UNKNOWN;
|
|
|
|
memset(suri->pcap_dev, 0, sizeof(suri->pcap_dev));
|
|
suri->sig_file = NULL;
|
|
suri->sig_file_exclusive = false;
|
|
suri->pid_filename = NULL;
|
|
suri->regex_arg = NULL;
|
|
|
|
suri->keyword_info = NULL;
|
|
suri->runmode_custom_mode = NULL;
|
|
#ifndef OS_WIN32
|
|
suri->user_name = NULL;
|
|
suri->group_name = NULL;
|
|
suri->do_setuid = false;
|
|
suri->do_setgid = false;
|
|
#endif /* OS_WIN32 */
|
|
suri->userid = 0;
|
|
suri->groupid = 0;
|
|
suri->delayed_detect = 0;
|
|
suri->daemon = 0;
|
|
suri->offline = 0;
|
|
suri->verbose = 0;
|
|
/* use -1 as unknown */
|
|
suri->checksum_validation = -1;
|
|
#if HAVE_DETECT_DISABLED==1
|
|
g_detect_disabled = suri->disabled_detect = 1;
|
|
#else
|
|
g_detect_disabled = suri->disabled_detect = 0;
|
|
#endif
|
|
}
|
|
|
|
const char *GetDocURL(void)
|
|
{
|
|
const char *prog_ver = GetProgramVersion();
|
|
if (strstr(prog_ver, "RELEASE") != NULL) {
|
|
return DOC_URL "suricata-" PROG_VER;
|
|
}
|
|
return DOC_URL "latest";
|
|
}
|
|
|
|
/** \brief get string with program version
|
|
*
|
|
* Get the program version as passed to us from AC_INIT
|
|
*
|
|
* Add 'RELEASE' is no '-dev' in the version. Add the REVISION if passed
|
|
* to us.
|
|
*
|
|
* Possible outputs:
|
|
* release: '5.0.1 RELEASE'
|
|
* dev with rev: '5.0.1-dev (64a789bbf 2019-10-18)'
|
|
* dev w/o rev: '5.0.1-dev'
|
|
*/
|
|
const char *GetProgramVersion(void)
|
|
{
|
|
if (strstr(PROG_VER, "-dev") == NULL) {
|
|
return PROG_VER " RELEASE";
|
|
} else {
|
|
#ifdef REVISION
|
|
return PROG_VER " (" xstr(REVISION) ")";
|
|
#else
|
|
return PROG_VER;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static TmEcode PrintVersion(void)
|
|
{
|
|
printf("This is %s version %s\n", PROG_NAME, GetProgramVersion());
|
|
return TM_ECODE_OK;
|
|
}
|
|
|
|
static TmEcode LogVersion(SCInstance *suri)
|
|
{
|
|
const char *mode = suri->system ? "SYSTEM" : "USER";
|
|
SCLogNotice("This is %s version %s running in %s mode",
|
|
PROG_NAME, GetProgramVersion(), mode);
|
|
return TM_ECODE_OK;
|
|
}
|
|
|
|
static void SCSetStartTime(SCInstance *suri)
|
|
{
|
|
memset(&suri->start_time, 0, sizeof(suri->start_time));
|
|
gettimeofday(&suri->start_time, NULL);
|
|
}
|
|
|
|
static void SCPrintElapsedTime(struct timeval *start_time)
|
|
{
|
|
if (start_time == NULL)
|
|
return;
|
|
struct timeval end_time;
|
|
memset(&end_time, 0, sizeof(end_time));
|
|
gettimeofday(&end_time, NULL);
|
|
uint64_t milliseconds = ((end_time.tv_sec - start_time->tv_sec) * 1000) +
|
|
(((1000000 + end_time.tv_usec - start_time->tv_usec) / 1000) - 1000);
|
|
SCLogInfo("time elapsed %.3fs", (float)milliseconds/(float)1000);
|
|
}
|
|
|
|
static int ParseCommandLineAfpacket(SCInstance *suri, const char *in_arg)
|
|
{
|
|
#ifdef HAVE_AF_PACKET
|
|
if (suri->run_mode == RUNMODE_UNKNOWN) {
|
|
suri->run_mode = RUNMODE_AFP_DEV;
|
|
if (in_arg) {
|
|
LiveRegisterDeviceName(in_arg);
|
|
memset(suri->pcap_dev, 0, sizeof(suri->pcap_dev));
|
|
strlcpy(suri->pcap_dev, in_arg, sizeof(suri->pcap_dev));
|
|
}
|
|
} else if (suri->run_mode == RUNMODE_AFP_DEV) {
|
|
if (in_arg) {
|
|
LiveRegisterDeviceName(in_arg);
|
|
} else {
|
|
SCLogInfo("Multiple af-packet option without interface on each is useless");
|
|
}
|
|
} else {
|
|
SCLogError("more than one run mode "
|
|
"has been specified");
|
|
PrintUsage(suri->progname);
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
return TM_ECODE_OK;
|
|
#else
|
|
SCLogError("AF_PACKET not enabled. On Linux "
|
|
"host, make sure to pass --enable-af-packet to "
|
|
"configure when building.");
|
|
return TM_ECODE_FAILED;
|
|
#endif
|
|
}
|
|
|
|
static int ParseCommandLineAfxdp(SCInstance *suri, const char *in_arg)
|
|
{
|
|
#ifdef HAVE_AF_XDP
|
|
if (suri->run_mode == RUNMODE_UNKNOWN) {
|
|
suri->run_mode = RUNMODE_AFXDP_DEV;
|
|
if (in_arg) {
|
|
LiveRegisterDeviceName(in_arg);
|
|
memset(suri->pcap_dev, 0, sizeof(suri->pcap_dev));
|
|
strlcpy(suri->pcap_dev, in_arg, sizeof(suri->pcap_dev));
|
|
}
|
|
} else if (suri->run_mode == RUNMODE_AFXDP_DEV) {
|
|
if (in_arg) {
|
|
LiveRegisterDeviceName(in_arg);
|
|
} else {
|
|
SCLogInfo("Multiple af-xdp options without interface on each is useless");
|
|
}
|
|
} else {
|
|
SCLogError("more than one run mode "
|
|
"has been specified");
|
|
PrintUsage(suri->progname);
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
return TM_ECODE_OK;
|
|
#else
|
|
SCLogError("AF_XDP not enabled. On Linux "
|
|
"host, make sure correct libraries are installed,"
|
|
" see documentation for information.");
|
|
return TM_ECODE_FAILED;
|
|
#endif
|
|
}
|
|
|
|
static int ParseCommandLineDpdk(SCInstance *suri, const char *in_arg)
|
|
{
|
|
#ifdef HAVE_DPDK
|
|
if (suri->run_mode == RUNMODE_UNKNOWN) {
|
|
suri->run_mode = RUNMODE_DPDK;
|
|
} else if (suri->run_mode == RUNMODE_DPDK) {
|
|
SCLogInfo("Multiple dpdk options have no effect on Suricata");
|
|
} else {
|
|
SCLogError("more than one run mode "
|
|
"has been specified");
|
|
PrintUsage(suri->progname);
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
return TM_ECODE_OK;
|
|
#else
|
|
SCLogError("DPDK not enabled. On Linux "
|
|
"host, make sure to pass --enable-dpdk to "
|
|
"configure when building.");
|
|
return TM_ECODE_FAILED;
|
|
#endif
|
|
}
|
|
|
|
static int ParseCommandLinePcapLive(SCInstance *suri, const char *in_arg)
|
|
{
|
|
#if defined(OS_WIN32) && !defined(HAVE_LIBWPCAP)
|
|
/* If running on Windows without Npcap, bail early as live capture is not supported. */
|
|
FatalError("Live capture not available. To support live capture compile against Npcap.");
|
|
#endif
|
|
memset(suri->pcap_dev, 0, sizeof(suri->pcap_dev));
|
|
|
|
if (in_arg != NULL) {
|
|
/* some windows shells require escaping of the \ in \Device. Otherwise
|
|
* the backslashes are stripped. We put them back here. */
|
|
if (strlen(in_arg) > 9 && strncmp(in_arg, "DeviceNPF", 9) == 0) {
|
|
snprintf(suri->pcap_dev, sizeof(suri->pcap_dev), "\\Device\\NPF%s", in_arg+9);
|
|
} else {
|
|
strlcpy(suri->pcap_dev, in_arg, sizeof(suri->pcap_dev));
|
|
PcapTranslateIPToDevice(suri->pcap_dev, sizeof(suri->pcap_dev));
|
|
}
|
|
|
|
if (strcmp(suri->pcap_dev, in_arg) != 0) {
|
|
SCLogInfo("translated %s to pcap device %s", in_arg, suri->pcap_dev);
|
|
} else if (strlen(suri->pcap_dev) > 0 && isdigit((unsigned char)suri->pcap_dev[0])) {
|
|
SCLogError("failed to find a pcap device for IP %s", in_arg);
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
}
|
|
|
|
if (suri->run_mode == RUNMODE_UNKNOWN) {
|
|
suri->run_mode = RUNMODE_PCAP_DEV;
|
|
if (in_arg) {
|
|
LiveRegisterDeviceName(suri->pcap_dev);
|
|
}
|
|
} else if (suri->run_mode == RUNMODE_PCAP_DEV) {
|
|
LiveRegisterDeviceName(suri->pcap_dev);
|
|
} else {
|
|
SCLogError("more than one run mode "
|
|
"has been specified");
|
|
PrintUsage(suri->progname);
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
return TM_ECODE_OK;
|
|
}
|
|
|
|
/**
|
|
* Helper function to check if log directory is writable
|
|
*/
|
|
static bool IsLogDirectoryWritable(const char* str)
|
|
{
|
|
if (access(str, W_OK) == 0)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
extern int g_skip_prefilter;
|
|
|
|
TmEcode SCParseCommandLine(int argc, char **argv)
|
|
{
|
|
SCInstance *suri = &suricata;
|
|
int opt;
|
|
|
|
int dump_config = 0;
|
|
int dump_features = 0;
|
|
int list_app_layer_protocols = 0;
|
|
int list_app_layer_hooks = 0;
|
|
int list_unittests = 0;
|
|
int list_runmodes = 0;
|
|
int list_keywords = 0;
|
|
int build_info = 0;
|
|
int conf_test = 0;
|
|
int engine_analysis = 0;
|
|
int ret = TM_ECODE_OK;
|
|
|
|
#ifdef UNITTESTS
|
|
coverage_unittests = 0;
|
|
g_ut_modules = 0;
|
|
g_ut_covered = 0;
|
|
#endif
|
|
|
|
// clang-format off
|
|
struct option long_opts[] = {
|
|
{"dump-config", 0, &dump_config, 1},
|
|
{"dump-features", 0, &dump_features, 1},
|
|
{"pfring", optional_argument, 0, 0},
|
|
{"pfring-int", required_argument, 0, 0},
|
|
{"pfring-cluster-id", required_argument, 0, 0},
|
|
{"pfring-cluster-type", required_argument, 0, 0},
|
|
#ifdef HAVE_DPDK
|
|
{"dpdk", 0, 0, 0},
|
|
#endif
|
|
{"af-packet", optional_argument, 0, 0},
|
|
{"af-xdp", optional_argument, 0, 0},
|
|
{"netmap", optional_argument, 0, 0},
|
|
{"pcap", optional_argument, 0, 0},
|
|
{"pcap-file-continuous", 0, 0, 0},
|
|
{"pcap-file-delete", 0, 0, 0},
|
|
{"pcap-file-recursive", 0, 0, 0},
|
|
{"pcap-file-buffer-size", required_argument, 0, 0},
|
|
{"simulate-ips", 0, 0 , 0},
|
|
{"no-random", 0, &g_disable_randomness, 1},
|
|
{"strict-rule-keywords", optional_argument, 0, 0},
|
|
|
|
{"capture-plugin", required_argument, 0, 0},
|
|
{"capture-plugin-args", required_argument, 0, 0},
|
|
|
|
#ifdef BUILD_UNIX_SOCKET
|
|
{"unix-socket", optional_argument, 0, 0},
|
|
#endif
|
|
{"pcap-buffer-size", required_argument, 0, 0},
|
|
{"unittest-filter", required_argument, 0, 'U'},
|
|
{"list-app-layer-protos", 0, &list_app_layer_protocols, 1},
|
|
{"list-app-layer-hooks", 0, &list_app_layer_hooks, 1},
|
|
{"list-unittests", 0, &list_unittests, 1},
|
|
{"list-runmodes", 0, &list_runmodes, 1},
|
|
{"list-keywords", optional_argument, &list_keywords, 1},
|
|
{"runmode", required_argument, NULL, 0},
|
|
{"engine-analysis", 0, &engine_analysis, 1},
|
|
#ifdef OS_WIN32
|
|
{"service-install", 0, 0, 0},
|
|
{"service-remove", 0, 0, 0},
|
|
{"service-change-params", 0, 0, 0},
|
|
#endif /* OS_WIN32 */
|
|
{"pidfile", required_argument, 0, 0},
|
|
{"init-errors-fatal", 0, 0, 0},
|
|
{"disable-detection", 0, 0, 0},
|
|
{"disable-hashing", 0, 0, 0},
|
|
{"fatal-unittests", 0, 0, 0},
|
|
{"unittests-coverage", 0, &coverage_unittests, 1},
|
|
{"user", required_argument, 0, 0},
|
|
{"group", required_argument, 0, 0},
|
|
{"erf-in", required_argument, 0, 0},
|
|
{"dag", required_argument, 0, 0},
|
|
{"build-info", 0, &build_info, 1},
|
|
{"data-dir", required_argument, 0, 0},
|
|
#ifdef WINDIVERT
|
|
{"windivert", required_argument, 0, 0},
|
|
{"windivert-forward", required_argument, 0, 0},
|
|
#endif
|
|
#ifdef HAVE_LIBNET11
|
|
{"reject-dev", required_argument, 0, 0},
|
|
#endif
|
|
{"set", required_argument, 0, 0},
|
|
#ifdef HAVE_NFLOG
|
|
{"nflog", optional_argument, 0, 0},
|
|
#endif
|
|
{"simulate-packet-flow-memcap", required_argument, 0, 0},
|
|
{"simulate-applayer-error-at-offset-ts", required_argument, 0, 0},
|
|
{"simulate-applayer-error-at-offset-tc", required_argument, 0, 0},
|
|
{"simulate-packet-loss", required_argument, 0, 0},
|
|
{"simulate-packet-tcp-reassembly-memcap", required_argument, 0, 0},
|
|
{"simulate-packet-tcp-ssn-memcap", required_argument, 0, 0},
|
|
{"simulate-packet-defrag-memcap", required_argument, 0, 0},
|
|
{"simulate-alert-queue-realloc-failure", 0, 0, 0},
|
|
|
|
{"qa-skip-prefilter", 0, &g_skip_prefilter, 1 },
|
|
|
|
{"firewall-rules-exclusive", required_argument, 0, 0},
|
|
|
|
{"include", required_argument, 0, 0},
|
|
|
|
{NULL, 0, NULL, 0}
|
|
};
|
|
// clang-format on
|
|
|
|
/* getopt_long stores the option index here. */
|
|
int option_index = 0;
|
|
|
|
char short_opts[] = "c:TDhi:l:q:d:r:us:S:U:VF:vk:";
|
|
|
|
while ((opt = getopt_long(argc, argv, short_opts, long_opts, &option_index)) != -1) {
|
|
switch (opt) {
|
|
case 0:
|
|
if (strcmp((long_opts[option_index]).name , "pfring") == 0 ||
|
|
strcmp((long_opts[option_index]).name , "pfring-int") == 0) {
|
|
#ifdef HAVE_PFRING
|
|
/* TODO: Which plugin? */
|
|
suri->run_mode = RUNMODE_PLUGIN;
|
|
suri->capture_plugin_name = "pfring";
|
|
if (optarg != NULL) {
|
|
memset(suri->pcap_dev, 0, sizeof(suri->pcap_dev));
|
|
strlcpy(suri->pcap_dev, optarg,
|
|
((strlen(optarg) < sizeof(suri->pcap_dev)) ?
|
|
(strlen(optarg) + 1) : sizeof(suri->pcap_dev)));
|
|
LiveRegisterDeviceName(optarg);
|
|
}
|
|
#else
|
|
SCLogError("PF_RING not enabled. Make sure "
|
|
"to pass --enable-pfring to configure when building.");
|
|
return TM_ECODE_FAILED;
|
|
#endif /* HAVE_PFRING */
|
|
}
|
|
else if(strcmp((long_opts[option_index]).name , "pfring-cluster-id") == 0){
|
|
#ifdef HAVE_PFRING
|
|
if (SCConfSetFinal("pfring.cluster-id", optarg) != 1) {
|
|
SCLogError("failed to set pfring.cluster-id");
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
#else
|
|
SCLogError("PF_RING not enabled. Make sure "
|
|
"to pass --enable-pfring to configure when building.");
|
|
return TM_ECODE_FAILED;
|
|
#endif /* HAVE_PFRING */
|
|
}
|
|
else if(strcmp((long_opts[option_index]).name , "pfring-cluster-type") == 0){
|
|
#ifdef HAVE_PFRING
|
|
if (SCConfSetFinal("pfring.cluster-type", optarg) != 1) {
|
|
SCLogError("failed to set pfring.cluster-type");
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
#else
|
|
SCLogError("PF_RING not enabled. Make sure "
|
|
"to pass --enable-pfring to configure when building.");
|
|
return TM_ECODE_FAILED;
|
|
#endif /* HAVE_PFRING */
|
|
}
|
|
else if (strcmp((long_opts[option_index]).name , "capture-plugin") == 0){
|
|
suri->run_mode = RUNMODE_PLUGIN;
|
|
suri->capture_plugin_name = optarg;
|
|
}
|
|
else if (strcmp((long_opts[option_index]).name , "capture-plugin-args") == 0){
|
|
suri->capture_plugin_args = optarg;
|
|
} else if (strcmp((long_opts[option_index]).name, "dpdk") == 0) {
|
|
if (ParseCommandLineDpdk(suri, optarg) != TM_ECODE_OK) {
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
} else if (strcmp((long_opts[option_index]).name, "af-packet") == 0) {
|
|
if (ParseCommandLineAfpacket(suri, optarg) != TM_ECODE_OK) {
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
} else if (strcmp((long_opts[option_index]).name, "af-xdp") == 0) {
|
|
if (ParseCommandLineAfxdp(suri, optarg) != TM_ECODE_OK) {
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
} else if (strcmp((long_opts[option_index]).name, "netmap") == 0) {
|
|
#ifdef HAVE_NETMAP
|
|
if (suri->run_mode == RUNMODE_UNKNOWN) {
|
|
suri->run_mode = RUNMODE_NETMAP;
|
|
if (optarg) {
|
|
LiveRegisterDeviceName(optarg);
|
|
memset(suri->pcap_dev, 0, sizeof(suri->pcap_dev));
|
|
strlcpy(suri->pcap_dev, optarg,
|
|
((strlen(optarg) < sizeof(suri->pcap_dev)) ?
|
|
(strlen(optarg) + 1) : sizeof(suri->pcap_dev)));
|
|
}
|
|
} else if (suri->run_mode == RUNMODE_NETMAP) {
|
|
if (optarg) {
|
|
LiveRegisterDeviceName(optarg);
|
|
} else {
|
|
SCLogInfo("Multiple netmap option without interface on each is useless");
|
|
break;
|
|
}
|
|
} else {
|
|
SCLogError("more than one run mode "
|
|
"has been specified");
|
|
PrintUsage(argv[0]);
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
#else
|
|
SCLogError("NETMAP not enabled.");
|
|
return TM_ECODE_FAILED;
|
|
#endif
|
|
} else if (strcmp((long_opts[option_index]).name, "nflog") == 0) {
|
|
#ifdef HAVE_NFLOG
|
|
if (suri->run_mode == RUNMODE_UNKNOWN) {
|
|
suri->run_mode = RUNMODE_NFLOG;
|
|
LiveBuildDeviceListCustom("nflog", "group");
|
|
}
|
|
#else
|
|
SCLogError("NFLOG not enabled.");
|
|
return TM_ECODE_FAILED;
|
|
#endif /* HAVE_NFLOG */
|
|
} else if (strcmp((long_opts[option_index]).name, "pcap") == 0) {
|
|
if (ParseCommandLinePcapLive(suri, optarg) != TM_ECODE_OK) {
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
} else if (strcmp((long_opts[option_index]).name, "simulate-ips") == 0) {
|
|
SCLogInfo("Setting IPS mode");
|
|
EngineModeSetIPS();
|
|
} else if (strcmp((long_opts[option_index]).name, "init-errors-fatal") == 0) {
|
|
if (SCConfSetFinal("engine.init-failure-fatal", "1") != 1) {
|
|
SCLogError("failed to set engine init-failure-fatal");
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
#ifdef BUILD_UNIX_SOCKET
|
|
} else if (strcmp((long_opts[option_index]).name , "unix-socket") == 0) {
|
|
if (suri->run_mode == RUNMODE_UNKNOWN) {
|
|
suri->run_mode = RUNMODE_UNIX_SOCKET;
|
|
if (optarg) {
|
|
if (SCConfSetFinal("unix-command.filename", optarg) != 1) {
|
|
SCLogError("failed to set unix-command.filename");
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
}
|
|
} else {
|
|
SCLogError("more than one run mode "
|
|
"has been specified");
|
|
PrintUsage(argv[0]);
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
#endif
|
|
}
|
|
else if(strcmp((long_opts[option_index]).name, "list-app-layer-protocols") == 0) {
|
|
/* listing all supported app layer protocols */
|
|
} else if (strcmp((long_opts[option_index]).name, "list-app-layer-hooks") == 0) {
|
|
/* listing all supported app layer hooks */
|
|
} else if (strcmp((long_opts[option_index]).name, "list-unittests") == 0) {
|
|
#ifdef UNITTESTS
|
|
suri->run_mode = RUNMODE_LIST_UNITTEST;
|
|
#else
|
|
SCLogError("unit tests not enabled. Make sure to pass --enable-unittests to "
|
|
"configure when building");
|
|
return TM_ECODE_FAILED;
|
|
#endif /* UNITTESTS */
|
|
} else if (strcmp((long_opts[option_index]).name, "list-runmodes") == 0) {
|
|
suri->run_mode = RUNMODE_LIST_RUNMODES;
|
|
return TM_ECODE_OK;
|
|
} else if (strcmp((long_opts[option_index]).name, "list-keywords") == 0) {
|
|
if (optarg) {
|
|
if (strcmp("short", optarg) != 0) {
|
|
suri->keyword_info = optarg;
|
|
}
|
|
}
|
|
} else if (strcmp((long_opts[option_index]).name, "runmode") == 0) {
|
|
suri->runmode_custom_mode = optarg;
|
|
} else if (strcmp((long_opts[option_index]).name, "engine-analysis") == 0) {
|
|
// do nothing for now
|
|
}
|
|
#ifdef OS_WIN32
|
|
else if (strcmp((long_opts[option_index]).name, "service-install") == 0) {
|
|
suri->run_mode = RUNMODE_INSTALL_SERVICE;
|
|
return TM_ECODE_OK;
|
|
} else if (strcmp((long_opts[option_index]).name, "service-remove") == 0) {
|
|
suri->run_mode = RUNMODE_REMOVE_SERVICE;
|
|
return TM_ECODE_OK;
|
|
} else if (strcmp((long_opts[option_index]).name, "service-change-params") == 0) {
|
|
suri->run_mode = RUNMODE_CHANGE_SERVICE_PARAMS;
|
|
return TM_ECODE_OK;
|
|
}
|
|
#endif /* OS_WIN32 */
|
|
else if (strcmp((long_opts[option_index]).name, "pidfile") == 0) {
|
|
suri->pid_filename = SCStrdup(optarg);
|
|
if (suri->pid_filename == NULL) {
|
|
SCLogError("strdup failed: %s", strerror(errno));
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
} else if (strcmp((long_opts[option_index]).name, "disable-detection") == 0) {
|
|
g_detect_disabled = suri->disabled_detect = 1;
|
|
} else if (strcmp((long_opts[option_index]).name, "disable-hashing") == 0) {
|
|
g_disable_hashing = true;
|
|
// for rust
|
|
SCDisableHashing();
|
|
} else if (strcmp((long_opts[option_index]).name, "fatal-unittests") == 0) {
|
|
#ifdef UNITTESTS
|
|
unittests_fatal = 1;
|
|
#else
|
|
SCLogError("unit tests not enabled. Make sure to pass --enable-unittests to "
|
|
"configure when building");
|
|
return TM_ECODE_FAILED;
|
|
#endif /* UNITTESTS */
|
|
} else if (strcmp((long_opts[option_index]).name, "user") == 0) {
|
|
#ifndef HAVE_LIBCAP_NG
|
|
SCLogError("libcap-ng is required to"
|
|
" drop privileges, but it was not compiled into Suricata.");
|
|
return TM_ECODE_FAILED;
|
|
#else
|
|
suri->user_name = optarg;
|
|
suri->do_setuid = true;
|
|
#endif /* HAVE_LIBCAP_NG */
|
|
} else if (strcmp((long_opts[option_index]).name, "group") == 0) {
|
|
#ifndef HAVE_LIBCAP_NG
|
|
SCLogError("libcap-ng is required to"
|
|
" drop privileges, but it was not compiled into Suricata.");
|
|
return TM_ECODE_FAILED;
|
|
#else
|
|
suri->group_name = optarg;
|
|
suri->do_setgid = true;
|
|
#endif /* HAVE_LIBCAP_NG */
|
|
} else if (strcmp((long_opts[option_index]).name, "erf-in") == 0) {
|
|
suri->run_mode = RUNMODE_ERF_FILE;
|
|
if (SCConfSetFinal("erf-file.file", optarg) != 1) {
|
|
SCLogError("failed to set erf-file.file");
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
} else if (strcmp((long_opts[option_index]).name, "dag") == 0) {
|
|
#ifdef HAVE_DAG
|
|
if (suri->run_mode == RUNMODE_UNKNOWN) {
|
|
suri->run_mode = RUNMODE_DAG;
|
|
}
|
|
else if (suri->run_mode != RUNMODE_DAG) {
|
|
SCLogError("more than one run mode has been specified");
|
|
PrintUsage(argv[0]);
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
LiveRegisterDeviceName(optarg);
|
|
#else
|
|
SCLogError("libdag and a DAG card are required"
|
|
" to receive packets using --dag.");
|
|
return TM_ECODE_FAILED;
|
|
#endif /* HAVE_DAG */
|
|
} else if (strcmp((long_opts[option_index]).name, "napatech") == 0) {
|
|
#ifdef HAVE_NAPATECH
|
|
suri->run_mode = RUNMODE_PLUGIN;
|
|
#else
|
|
SCLogError("libntapi and a Napatech adapter are required"
|
|
" to capture packets using --napatech.");
|
|
return TM_ECODE_FAILED;
|
|
#endif /* HAVE_NAPATECH */
|
|
} else if (strcmp((long_opts[option_index]).name, "pcap-buffer-size") == 0) {
|
|
#ifdef HAVE_PCAP_SET_BUFF
|
|
if (SCConfSetFinal("pcap.buffer-size", optarg) != 1) {
|
|
SCLogError("failed to set pcap-buffer-size");
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
#else
|
|
SCLogError("The version of libpcap you have"
|
|
" doesn't support setting buffer size.");
|
|
#endif /* HAVE_PCAP_SET_BUFF */
|
|
} else if (strcmp((long_opts[option_index]).name, "build-info") == 0) {
|
|
suri->run_mode = RUNMODE_PRINT_BUILDINFO;
|
|
return TM_ECODE_OK;
|
|
} else if (strcmp((long_opts[option_index]).name, "windivert-forward") == 0) {
|
|
#ifdef WINDIVERT
|
|
if (suri->run_mode == RUNMODE_UNKNOWN) {
|
|
suri->run_mode = RUNMODE_WINDIVERT;
|
|
if (WinDivertRegisterQueue(true, optarg) == -1) {
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
} else if (suri->run_mode == RUNMODE_WINDIVERT) {
|
|
if (WinDivertRegisterQueue(true, optarg) == -1) {
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
} else {
|
|
SCLogError("more than one run mode "
|
|
"has been specified");
|
|
PrintUsage(argv[0]);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
else if(strcmp((long_opts[option_index]).name, "windivert") == 0) {
|
|
if (suri->run_mode == RUNMODE_UNKNOWN) {
|
|
suri->run_mode = RUNMODE_WINDIVERT;
|
|
if (WinDivertRegisterQueue(false, optarg) == -1) {
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
} else if (suri->run_mode == RUNMODE_WINDIVERT) {
|
|
if (WinDivertRegisterQueue(false, optarg) == -1) {
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
} else {
|
|
SCLogError("more than one run mode "
|
|
"has been specified");
|
|
PrintUsage(argv[0]);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
#else
|
|
SCLogError("WinDivert not enabled. Make sure to pass --enable-windivert to "
|
|
"configure when building.");
|
|
return TM_ECODE_FAILED;
|
|
#endif /* WINDIVERT */
|
|
} else if(strcmp((long_opts[option_index]).name, "reject-dev") == 0) {
|
|
#ifdef HAVE_LIBNET11
|
|
BUG_ON(optarg == NULL); /* for static analysis */
|
|
extern char *g_reject_dev;
|
|
extern uint16_t g_reject_dev_mtu;
|
|
g_reject_dev = optarg;
|
|
int mtu = GetIfaceMTU(g_reject_dev);
|
|
if (mtu > 0) {
|
|
g_reject_dev_mtu = (uint16_t)mtu;
|
|
}
|
|
#else
|
|
SCLogError("Libnet 1.1 support not enabled. Compile Suricata with libnet support.");
|
|
return TM_ECODE_FAILED;
|
|
#endif
|
|
}
|
|
else if (strcmp((long_opts[option_index]).name, "set") == 0) {
|
|
if (optarg != NULL) {
|
|
/* Quick validation. */
|
|
char *val = strchr(optarg, '=');
|
|
if (val == NULL) {
|
|
FatalError("Invalid argument for --set, must be key=val.");
|
|
}
|
|
if (!SCConfSetFromString(optarg, 1)) {
|
|
FatalError("failed to set configuration value %s", optarg);
|
|
}
|
|
}
|
|
}
|
|
else if (strcmp((long_opts[option_index]).name, "pcap-file-continuous") == 0) {
|
|
if (SCConfSetFinal("pcap-file.continuous", "true") != 1) {
|
|
SCLogError("Failed to set pcap-file.continuous");
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
}
|
|
else if (strcmp((long_opts[option_index]).name, "pcap-file-delete") == 0) {
|
|
if (SCConfSetFinal("pcap-file.delete-when-done", "true") != 1) {
|
|
SCLogError("Failed to set pcap-file.delete-when-done");
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
}
|
|
else if (strcmp((long_opts[option_index]).name, "pcap-file-recursive") == 0) {
|
|
if (SCConfSetFinal("pcap-file.recursive", "true") != 1) {
|
|
SCLogError("failed to set pcap-file.recursive");
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
} else if (strcmp((long_opts[option_index]).name, "pcap-file-buffer-size") == 0) {
|
|
if (SCConfSetFinal("pcap-file.buffer-size", optarg) != 1) {
|
|
SCLogError("failed to set pcap-file.buffer-size");
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
} else if (strcmp((long_opts[option_index]).name, "data-dir") == 0) {
|
|
if (optarg == NULL) {
|
|
SCLogError("no option argument (optarg) for -d");
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
|
|
if (ConfigSetDataDirectory(optarg) != TM_ECODE_OK) {
|
|
SCLogError("Failed to set data directory.");
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
if (ConfigCheckDataDirectory(optarg) != TM_ECODE_OK) {
|
|
SCLogError("The data directory \"%s\""
|
|
" supplied at the command-line (-d %s) doesn't "
|
|
"exist. Shutting down the engine.",
|
|
optarg, optarg);
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
suri->set_datadir = true;
|
|
} else if (strcmp((long_opts[option_index]).name, "strict-rule-keywords") == 0) {
|
|
if (optarg == NULL) {
|
|
suri->strict_rule_parsing_string = SCStrdup("all");
|
|
} else {
|
|
suri->strict_rule_parsing_string = SCStrdup(optarg);
|
|
}
|
|
if (suri->strict_rule_parsing_string == NULL) {
|
|
FatalError("failed to duplicate 'strict' string");
|
|
}
|
|
} else if (strcmp((long_opts[option_index]).name, "include") == 0) {
|
|
if (suri->additional_configs == NULL) {
|
|
suri->additional_configs = SCCalloc(2, sizeof(char *));
|
|
if (suri->additional_configs == NULL) {
|
|
FatalError(
|
|
"Failed to allocate memory for additional configuration files: %s",
|
|
strerror(errno));
|
|
}
|
|
suri->additional_configs[0] = optarg;
|
|
} else {
|
|
for (int i = 0;; i++) {
|
|
if (suri->additional_configs[i] == NULL) {
|
|
const char **additional_configs =
|
|
SCRealloc(suri->additional_configs, (i + 2) * sizeof(char *));
|
|
if (additional_configs == NULL) {
|
|
FatalError("Failed to allocate memory for additional configuration "
|
|
"files: %s",
|
|
strerror(errno));
|
|
} else {
|
|
suri->additional_configs = additional_configs;
|
|
}
|
|
suri->additional_configs[i] = optarg;
|
|
suri->additional_configs[i + 1] = NULL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else if (strcmp((long_opts[option_index]).name, "firewall-rules-exclusive") == 0) {
|
|
if (suri->firewall_rule_file != NULL) {
|
|
SCLogError("can't have multiple --firewall-rules-exclusive options");
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
suri->firewall_rule_file = optarg;
|
|
suri->firewall_rule_file_exclusive = true;
|
|
} else {
|
|
int r = ExceptionSimulationCommandLineParser(
|
|
(long_opts[option_index]).name, optarg);
|
|
if (r < 0)
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
break;
|
|
case 'c':
|
|
suri->conf_filename = optarg;
|
|
break;
|
|
case 'T':
|
|
conf_test = 1;
|
|
if (SCConfSetFinal("engine.init-failure-fatal", "1") != 1) {
|
|
SCLogError("failed to set engine init-failure-fatal");
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
break;
|
|
#ifndef OS_WIN32
|
|
case 'D':
|
|
suri->daemon = 1;
|
|
break;
|
|
#endif /* OS_WIN32 */
|
|
case 'h':
|
|
suri->run_mode = RUNMODE_PRINT_USAGE;
|
|
return TM_ECODE_OK;
|
|
case 'i':
|
|
if (optarg == NULL) {
|
|
SCLogError("no option argument (optarg) for -i");
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
#ifdef HAVE_AF_PACKET
|
|
if (ParseCommandLineAfpacket(suri, optarg) != TM_ECODE_OK) {
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
#else /* not afpacket */
|
|
/* warn user if netmap is available */
|
|
#if defined HAVE_NETMAP
|
|
int i = 0;
|
|
#ifdef HAVE_NETMAP
|
|
i++;
|
|
#endif
|
|
SCLogWarning("faster capture "
|
|
"option%s %s available:"
|
|
#ifdef HAVE_NETMAP
|
|
" NETMAP (--netmap=%s)"
|
|
#endif
|
|
". Use --pcap=%s to suppress this warning",
|
|
i == 1 ? "" : "s", i == 1 ? "is" : "are"
|
|
#ifdef HAVE_NETMAP
|
|
,
|
|
optarg
|
|
#endif
|
|
,
|
|
optarg);
|
|
#endif /* have faster methods */
|
|
if (ParseCommandLinePcapLive(suri, optarg) != TM_ECODE_OK) {
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
#endif
|
|
break;
|
|
case 'l':
|
|
if (optarg == NULL) {
|
|
SCLogError("no option argument (optarg) for -l");
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
|
|
if (ConfigSetLogDirectory(optarg) != TM_ECODE_OK) {
|
|
SCLogError("Failed to set log directory.");
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
if (ConfigCheckLogDirectoryExists(optarg) != TM_ECODE_OK) {
|
|
SCLogError("The logging directory \"%s\""
|
|
" supplied at the command-line (-l %s) doesn't "
|
|
"exist. Shutting down the engine.",
|
|
optarg, optarg);
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
if (!IsLogDirectoryWritable(optarg)) {
|
|
SCLogError("The logging directory \"%s\""
|
|
" supplied at the command-line (-l %s) is not "
|
|
"writable. Shutting down the engine.",
|
|
optarg, optarg);
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
suri->set_logdir = true;
|
|
|
|
break;
|
|
case 'q':
|
|
#ifdef NFQ
|
|
if (suri->run_mode == RUNMODE_UNKNOWN) {
|
|
suri->run_mode = RUNMODE_NFQ;
|
|
EngineModeSetIPS();
|
|
if (NFQParseAndRegisterQueues(optarg) == -1)
|
|
return TM_ECODE_FAILED;
|
|
} else if (suri->run_mode == RUNMODE_NFQ) {
|
|
if (NFQParseAndRegisterQueues(optarg) == -1)
|
|
return TM_ECODE_FAILED;
|
|
} else {
|
|
SCLogError("more than one run mode "
|
|
"has been specified");
|
|
PrintUsage(argv[0]);
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
#else
|
|
SCLogError("NFQUEUE not enabled. Make sure to pass --enable-nfqueue to configure when "
|
|
"building.");
|
|
return TM_ECODE_FAILED;
|
|
#endif /* NFQ */
|
|
break;
|
|
case 'd':
|
|
#ifdef IPFW
|
|
if (suri->run_mode == RUNMODE_UNKNOWN) {
|
|
suri->run_mode = RUNMODE_IPFW;
|
|
EngineModeSetIPS();
|
|
if (IPFWRegisterQueue(optarg) == -1)
|
|
return TM_ECODE_FAILED;
|
|
} else if (suri->run_mode == RUNMODE_IPFW) {
|
|
if (IPFWRegisterQueue(optarg) == -1)
|
|
return TM_ECODE_FAILED;
|
|
} else {
|
|
SCLogError("more than one run mode "
|
|
"has been specified");
|
|
PrintUsage(argv[0]);
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
#else
|
|
SCLogError("IPFW not enabled. Make sure to pass --enable-ipfw to configure when "
|
|
"building.");
|
|
return TM_ECODE_FAILED;
|
|
#endif /* IPFW */
|
|
break;
|
|
case 'r':
|
|
BUG_ON(optarg == NULL); /* for static analysis */
|
|
if (suri->run_mode == RUNMODE_UNKNOWN) {
|
|
suri->run_mode = RUNMODE_PCAP_FILE;
|
|
} else {
|
|
SCLogError("more than one run mode "
|
|
"has been specified");
|
|
PrintUsage(argv[0]);
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
SCStat buf;
|
|
if (SCStatFn(optarg, &buf) != 0) {
|
|
SCLogError("pcap file '%s': %s", optarg, strerror(errno));
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
if (SCConfSetFinal("pcap-file.file", optarg) != 1) {
|
|
SCLogError("ERROR: Failed to set pcap-file.file\n");
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
|
|
break;
|
|
case 's':
|
|
if (suri->sig_file != NULL) {
|
|
SCLogError("can't have multiple -s options or mix -s and -S.");
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
suri->sig_file = optarg;
|
|
break;
|
|
case 'S':
|
|
if (suri->sig_file != NULL) {
|
|
SCLogError("can't have multiple -S options or mix -s and -S.");
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
suri->sig_file = optarg;
|
|
suri->sig_file_exclusive = true;
|
|
break;
|
|
case 'u':
|
|
#ifdef UNITTESTS
|
|
if (suri->run_mode == RUNMODE_UNKNOWN) {
|
|
suri->run_mode = RUNMODE_UNITTEST;
|
|
} else {
|
|
SCLogError("more than one run mode has"
|
|
" been specified");
|
|
PrintUsage(argv[0]);
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
#else
|
|
SCLogError("unit tests not enabled. Make sure to pass --enable-unittests to configure "
|
|
"when building.");
|
|
return TM_ECODE_FAILED;
|
|
#endif /* UNITTESTS */
|
|
break;
|
|
case 'U':
|
|
#ifdef UNITTESTS
|
|
suri->regex_arg = optarg;
|
|
|
|
if(strlen(suri->regex_arg) == 0)
|
|
suri->regex_arg = NULL;
|
|
#endif
|
|
break;
|
|
case 'V':
|
|
suri->run_mode = RUNMODE_PRINT_VERSION;
|
|
return TM_ECODE_OK;
|
|
case 'F':
|
|
if (optarg == NULL) {
|
|
SCLogError("no option argument (optarg) for -F");
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
|
|
SetBpfStringFromFile(optarg);
|
|
break;
|
|
case 'v':
|
|
suri->verbose++;
|
|
break;
|
|
case 'k':
|
|
if (optarg == NULL) {
|
|
SCLogError("no option argument (optarg) for -k");
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
if (!strcmp("all", optarg))
|
|
suri->checksum_validation = 1;
|
|
else if (!strcmp("none", optarg))
|
|
suri->checksum_validation = 0;
|
|
else {
|
|
SCLogError("option '%s' invalid for -k", optarg);
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
break;
|
|
default:
|
|
PrintUsage(argv[0]);
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
}
|
|
|
|
if (suri->disabled_detect && (suri->sig_file != NULL || suri->firewall_rule_file != NULL)) {
|
|
SCLogError("can't use -s/-S or --firewall-rules-exclusive when detection is disabled");
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
|
|
/* save the runmode from the command-line (if any) */
|
|
suri->aux_run_mode = suri->run_mode;
|
|
|
|
if (list_app_layer_protocols)
|
|
suri->run_mode = RUNMODE_LIST_APP_LAYERS;
|
|
if (list_app_layer_hooks)
|
|
suri->run_mode = RUNMODE_LIST_APP_LAYER_HOOKS;
|
|
if (list_keywords)
|
|
suri->run_mode = RUNMODE_LIST_KEYWORDS;
|
|
if (list_unittests)
|
|
suri->run_mode = RUNMODE_LIST_UNITTEST;
|
|
if (dump_config)
|
|
suri->run_mode = RUNMODE_DUMP_CONFIG;
|
|
if (dump_features)
|
|
suri->run_mode = RUNMODE_DUMP_FEATURES;
|
|
if (conf_test)
|
|
suri->run_mode = RUNMODE_CONF_TEST;
|
|
if (engine_analysis)
|
|
suri->run_mode = RUNMODE_ENGINE_ANALYSIS;
|
|
|
|
suri->offline = IsRunModeOffline(suri->run_mode);
|
|
g_system = suri->system = IsRunModeSystem(suri->run_mode);
|
|
|
|
ret = SetBpfString(optind, argv);
|
|
if (ret != TM_ECODE_OK)
|
|
return ret;
|
|
|
|
return TM_ECODE_OK;
|
|
}
|
|
|
|
#ifdef OS_WIN32
|
|
int WindowsInitService(int argc, char **argv)
|
|
{
|
|
if (SCRunningAsService()) {
|
|
char path[MAX_PATH];
|
|
char *p = NULL;
|
|
strlcpy(path, argv[0], MAX_PATH);
|
|
if ((p = strrchr(path, '\\'))) {
|
|
*p = '\0';
|
|
}
|
|
if (!SetCurrentDirectory(path)) {
|
|
SCLogError("Can't set current directory to: %s", path);
|
|
return -1;
|
|
}
|
|
SCLogInfo("Current directory is set to: %s", path);
|
|
SCServiceInit(argc, argv);
|
|
}
|
|
|
|
/* Windows socket subsystem initialization */
|
|
WSADATA wsaData;
|
|
if (0 != WSAStartup(MAKEWORD(2, 2), &wsaData)) {
|
|
SCLogError("Can't initialize Windows sockets: %d", WSAGetLastError());
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif /* OS_WIN32 */
|
|
|
|
static int MayDaemonize(SCInstance *suri)
|
|
{
|
|
if (suri->daemon == 1 && suri->pid_filename == NULL) {
|
|
const char *pid_filename;
|
|
|
|
if (SCConfGet("pid-file", &pid_filename) == 1) {
|
|
SCLogInfo("Use pid file %s from config file.", pid_filename);
|
|
} else {
|
|
pid_filename = DEFAULT_PID_FILENAME;
|
|
}
|
|
/* The pid file name may be in config memory, but is needed later. */
|
|
suri->pid_filename = SCStrdup(pid_filename);
|
|
if (suri->pid_filename == NULL) {
|
|
SCLogError("strdup failed: %s", strerror(errno));
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
}
|
|
|
|
if (suri->pid_filename != NULL && SCPidfileTestRunning(suri->pid_filename) != 0) {
|
|
SCFree(suri->pid_filename);
|
|
suri->pid_filename = NULL;
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
|
|
if (suri->daemon == 1) {
|
|
Daemonize();
|
|
}
|
|
|
|
if (suri->pid_filename != NULL) {
|
|
if (SCPidfileCreate(suri->pid_filename) != 0) {
|
|
SCFree(suri->pid_filename);
|
|
suri->pid_filename = NULL;
|
|
SCLogError("Unable to create PID file, concurrent run of"
|
|
" Suricata can occur.");
|
|
SCLogError("PID file creation WILL be mandatory for daemon mode"
|
|
" in future version");
|
|
}
|
|
}
|
|
|
|
return TM_ECODE_OK;
|
|
}
|
|
|
|
/* Initialize the user and group Suricata is to run as. */
|
|
static int InitRunAs(SCInstance *suri)
|
|
{
|
|
#ifndef OS_WIN32
|
|
/* Try to get user/group to run suricata as if
|
|
command line as not decide of that */
|
|
if (!suri->do_setuid && !suri->do_setgid) {
|
|
const char *id;
|
|
if (SCConfGet("run-as.user", &id) == 1) {
|
|
suri->do_setuid = true;
|
|
suri->user_name = id;
|
|
}
|
|
if (SCConfGet("run-as.group", &id) == 1) {
|
|
suri->do_setgid = true;
|
|
suri->group_name = id;
|
|
}
|
|
}
|
|
/* Get the suricata user ID to given user ID */
|
|
if (suri->do_setuid) {
|
|
SCGetUserID(suri->user_name, suri->group_name, &suri->userid, &suri->groupid);
|
|
sc_set_caps = true;
|
|
/* Get the suricata group ID to given group ID */
|
|
} else if (suri->do_setgid) {
|
|
SCGetGroupID(suri->group_name, &suri->groupid);
|
|
sc_set_caps = true;
|
|
}
|
|
#endif
|
|
return TM_ECODE_OK;
|
|
}
|
|
|
|
static int InitSignalHandler(SCInstance *suri)
|
|
{
|
|
/* registering signals we use */
|
|
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
|
UtilSignalHandlerSetup(SIGINT, SignalHandlerSigint);
|
|
UtilSignalHandlerSetup(SIGTERM, SignalHandlerSigterm);
|
|
#if HAVE_LIBUNWIND
|
|
int enabled;
|
|
if (SCConfGetBool("logging.stacktrace-on-signal", &enabled) == 0) {
|
|
enabled = 1;
|
|
}
|
|
|
|
if (enabled) {
|
|
SCLogInfo("Preparing unexpected signal handling");
|
|
struct sigaction stacktrace_action;
|
|
memset(&stacktrace_action, 0, sizeof(stacktrace_action));
|
|
stacktrace_action.sa_sigaction = SignalHandlerUnexpected;
|
|
stacktrace_action.sa_flags = SA_SIGINFO;
|
|
sigaction(SIGSEGV, &stacktrace_action, NULL);
|
|
sigaction(SIGABRT, &stacktrace_action, NULL);
|
|
}
|
|
#endif /* HAVE_LIBUNWIND */
|
|
#endif
|
|
#ifndef OS_WIN32
|
|
UtilSignalHandlerSetup(SIGHUP, SignalHandlerSigHup);
|
|
UtilSignalHandlerSetup(SIGPIPE, SIG_IGN);
|
|
UtilSignalHandlerSetup(SIGSYS, SIG_IGN);
|
|
#endif /* OS_WIN32 */
|
|
|
|
return TM_ECODE_OK;
|
|
}
|
|
|
|
/* initialization code for both the main modes and for
|
|
* unix socket mode.
|
|
*
|
|
* Will be run once per pcap in unix-socket mode */
|
|
void PreRunInit(const int runmode)
|
|
{
|
|
if (runmode == RUNMODE_UNIX_SOCKET)
|
|
return;
|
|
|
|
StatsInit();
|
|
#ifdef PROFILE_RULES
|
|
SCProfilingRulesGlobalInit();
|
|
#endif
|
|
#ifdef PROFILING
|
|
SCProfilingKeywordsGlobalInit();
|
|
SCProfilingPrefilterGlobalInit();
|
|
SCProfilingSghsGlobalInit();
|
|
#endif /* PROFILING */
|
|
#ifdef PROFILE_RULES
|
|
SCProfilingInit();
|
|
#endif
|
|
DefragInit();
|
|
FlowInitConfig(FLOW_QUIET);
|
|
IPPairInitConfig(FLOW_QUIET);
|
|
StreamTcpInitConfig(STREAM_VERBOSE);
|
|
AppLayerParserPostStreamSetup();
|
|
AppLayerRegisterGlobalCounters();
|
|
OutputFilestoreRegisterGlobalCounters();
|
|
HttpRangeContainersInit();
|
|
}
|
|
|
|
/* tasks we need to run before packets start flowing,
|
|
* but after we dropped privs */
|
|
void PreRunPostPrivsDropInit(const int runmode)
|
|
{
|
|
if (runmode == RUNMODE_UNIX_SOCKET) {
|
|
return;
|
|
}
|
|
|
|
StatsSetupPostConfigPreOutput();
|
|
RunModeInitializeOutputs();
|
|
DatasetsInit();
|
|
StatsSetupPostConfigPostOutput();
|
|
}
|
|
|
|
/** \brief clean up / shutdown code for packet modes
|
|
*
|
|
* Shuts down packet modes, so regular packet runmodes and the
|
|
* per pcap mode in the unix socket. */
|
|
void PostRunDeinit(const int runmode, struct timeval *start_time)
|
|
{
|
|
if (runmode == RUNMODE_UNIX_SOCKET)
|
|
return;
|
|
|
|
TmThreadsUnsealThreads();
|
|
|
|
/* needed by FlowWorkToDoCleanup */
|
|
PacketPoolInit();
|
|
|
|
/* handle graceful shutdown of the flow engine, it's helper
|
|
* threads and the packet threads */
|
|
FlowDisableFlowManagerThread();
|
|
/* disable capture */
|
|
TmThreadDisableReceiveThreads();
|
|
/* tell relevant packet threads to enter flow timeout loop */
|
|
TmThreadDisablePacketThreads(
|
|
THV_REQ_FLOW_LOOP, THV_FLOW_LOOP, (TM_FLAG_RECEIVE_TM | TM_FLAG_DETECT_TM));
|
|
/* run cleanup on the flow hash */
|
|
FlowWorkToDoCleanup();
|
|
/* gracefully shut down all packet threads */
|
|
TmThreadDisablePacketThreads(THV_KILL, THV_RUNNING_DONE, TM_FLAG_PACKET_ALL);
|
|
SCPrintElapsedTime(start_time);
|
|
FlowDisableFlowRecyclerThread();
|
|
|
|
/* kill the stats threads */
|
|
TmThreadKillThreadsFamily(TVT_MGMT);
|
|
TmThreadClearThreadsFamily(TVT_MGMT);
|
|
|
|
/* kill packet threads -- already in 'disabled' state */
|
|
TmThreadKillThreadsFamily(TVT_PPT);
|
|
TmThreadClearThreadsFamily(TVT_PPT);
|
|
|
|
PacketPoolDestroy();
|
|
|
|
/* mgt and ppt threads killed, we can run non thread-safe
|
|
* shutdown functions */
|
|
StatsReleaseResources();
|
|
DecodeUnregisterCounters();
|
|
RunModeShutDown();
|
|
FlowShutdown();
|
|
IPPairShutdown();
|
|
HostCleanup();
|
|
StreamTcpFreeConfig(STREAM_VERBOSE);
|
|
DefragDestroy();
|
|
HttpRangeContainersDestroy();
|
|
#ifdef HAVE_HWLOC
|
|
TopologyDestroy();
|
|
#endif /* HAVE_HWLOC */
|
|
|
|
TmqResetQueues();
|
|
#ifdef PROFILING
|
|
if (profiling_packets_enabled)
|
|
SCProfilingDump();
|
|
SCProfilingDestroy();
|
|
#endif
|
|
}
|
|
|
|
int SCStartInternalRunMode(int argc, char **argv)
|
|
{
|
|
SCInstance *suri = &suricata;
|
|
/* Treat internal running mode */
|
|
switch (suri->run_mode) {
|
|
case RUNMODE_LIST_KEYWORDS:
|
|
return ListKeywords(suri->keyword_info);
|
|
case RUNMODE_LIST_APP_LAYERS:
|
|
if (suri->conf_filename != NULL) {
|
|
return ListAppLayerProtocols(suri->conf_filename);
|
|
} else {
|
|
return ListAppLayerProtocols(DEFAULT_CONF_FILE);
|
|
}
|
|
case RUNMODE_LIST_APP_LAYER_HOOKS:
|
|
if (suri->conf_filename != NULL) {
|
|
return ListAppLayerHooks(suri->conf_filename);
|
|
} else {
|
|
return ListAppLayerHooks(DEFAULT_CONF_FILE);
|
|
}
|
|
case RUNMODE_PRINT_VERSION:
|
|
PrintVersion();
|
|
return TM_ECODE_DONE;
|
|
case RUNMODE_PRINT_BUILDINFO:
|
|
PrintBuildInfo();
|
|
return TM_ECODE_DONE;
|
|
case RUNMODE_PRINT_USAGE:
|
|
PrintUsage(argv[0]);
|
|
return TM_ECODE_DONE;
|
|
case RUNMODE_LIST_RUNMODES:
|
|
RunModeListRunmodes();
|
|
return TM_ECODE_DONE;
|
|
case RUNMODE_LIST_UNITTEST:
|
|
RunUnittests(1, suri->regex_arg);
|
|
case RUNMODE_UNITTEST:
|
|
RunUnittests(0, suri->regex_arg);
|
|
#ifdef OS_WIN32
|
|
case RUNMODE_INSTALL_SERVICE:
|
|
if (SCServiceInstall(argc, argv)) {
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
SCLogInfo("Suricata service has been successfully installed.");
|
|
return TM_ECODE_DONE;
|
|
case RUNMODE_REMOVE_SERVICE:
|
|
if (SCServiceRemove()) {
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
SCLogInfo("Suricata service has been successfully removed.");
|
|
return TM_ECODE_DONE;
|
|
case RUNMODE_CHANGE_SERVICE_PARAMS:
|
|
if (SCServiceChangeParams(argc, argv)) {
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
SCLogInfo("Suricata service startup parameters has been successfully changed.");
|
|
return TM_ECODE_DONE;
|
|
#endif /* OS_WIN32 */
|
|
default:
|
|
/* simply continue for other running mode */
|
|
break;
|
|
}
|
|
return TM_ECODE_OK;
|
|
}
|
|
|
|
int SCFinalizeRunMode(void)
|
|
{
|
|
SCInstance *suri = &suricata;
|
|
switch (suri->run_mode) {
|
|
case RUNMODE_UNKNOWN:
|
|
PrintUsage(suri->progname);
|
|
return TM_ECODE_FAILED;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!CheckValidDaemonModes(suri->daemon, suri->run_mode)) {
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
|
|
return TM_ECODE_OK;
|
|
}
|
|
|
|
static void SetupDelayedDetect(SCInstance *suri)
|
|
{
|
|
/* In offline mode delayed init of detect is a bad idea */
|
|
if (suri->offline) {
|
|
suri->delayed_detect = 0;
|
|
} else {
|
|
if (SCConfGetBool("detect.delayed-detect", &suri->delayed_detect) != 1) {
|
|
SCConfNode *denode = NULL;
|
|
SCConfNode *decnf = SCConfGetNode("detect-engine");
|
|
if (decnf != NULL) {
|
|
TAILQ_FOREACH(denode, &decnf->head, next) {
|
|
if (strcmp(denode->val, "delayed-detect") == 0) {
|
|
(void)SCConfGetChildValueBool(
|
|
denode, "delayed-detect", &suri->delayed_detect);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SCLogConfig("Delayed detect %s", suri->delayed_detect ? "enabled" : "disabled");
|
|
if (suri->delayed_detect) {
|
|
SCLogInfo("Packets will start being processed before signatures are active.");
|
|
}
|
|
|
|
}
|
|
|
|
static int LoadSignatures(DetectEngineCtx *de_ctx, SCInstance *suri)
|
|
{
|
|
de_ctx->firewall_rule_file_exclusive = suri->firewall_rule_file;
|
|
|
|
if (SigLoadSignatures(de_ctx, suri->sig_file, suri->sig_file_exclusive) < 0) {
|
|
SCLogError("Loading signatures failed.");
|
|
if (de_ctx->failure_fatal)
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
|
|
return TM_ECODE_OK;
|
|
}
|
|
|
|
static int ConfigGetCaptureValue(SCInstance *suri)
|
|
{
|
|
/* Pull the max pending packets from the config, if not found fall
|
|
* back on a sane default. */
|
|
intmax_t tmp_max_pending_packets;
|
|
if (SCConfGetInt("max-pending-packets", &tmp_max_pending_packets) != 1)
|
|
tmp_max_pending_packets = DEFAULT_MAX_PENDING_PACKETS;
|
|
if (tmp_max_pending_packets < 1 || tmp_max_pending_packets > 2147483648) {
|
|
SCLogError("Maximum max-pending-packets setting is 2147483648 and must be greater than 0. "
|
|
"Please check %s for errors",
|
|
suri->conf_filename);
|
|
return TM_ECODE_FAILED;
|
|
} else {
|
|
max_pending_packets = (uint32_t)tmp_max_pending_packets;
|
|
}
|
|
|
|
SCLogDebug("Max pending packets set to %" PRIu32, max_pending_packets);
|
|
|
|
/* Pull the default packet size from the config, if not found fall
|
|
* back on a sane default. */
|
|
const char *temp_default_packet_size;
|
|
if ((SCConfGet("default-packet-size", &temp_default_packet_size)) != 1) {
|
|
int lthread;
|
|
int nlive;
|
|
int strip_trailing_plus = 0;
|
|
switch (suri->run_mode) {
|
|
case RUNMODE_AFP_DEV:
|
|
/* For AF_PACKET we delay setting the
|
|
* default-packet-size until we know more about the
|
|
* configuration. */
|
|
break;
|
|
#ifdef WINDIVERT
|
|
case RUNMODE_WINDIVERT: {
|
|
/* by default, WinDivert collects from all devices */
|
|
const int mtu = GetGlobalMTUWin32();
|
|
|
|
if (mtu > 0) {
|
|
/* SLL_HEADER_LEN is the longest header + 8 for VLAN */
|
|
default_packet_size = mtu + SLL_HEADER_LEN + 8;
|
|
break;
|
|
}
|
|
default_packet_size = DEFAULT_PACKET_SIZE;
|
|
break;
|
|
}
|
|
#endif /* WINDIVERT */
|
|
case RUNMODE_NETMAP:
|
|
/* in netmap igb0+ has a special meaning, however the
|
|
* interface really is igb0 */
|
|
strip_trailing_plus = 1;
|
|
/* fall through */
|
|
case RUNMODE_PLUGIN:
|
|
case RUNMODE_PCAP_DEV:
|
|
case RUNMODE_AFXDP_DEV:
|
|
nlive = LiveGetDeviceCount();
|
|
for (lthread = 0; lthread < nlive; lthread++) {
|
|
const char *live_dev = LiveGetDeviceName(lthread);
|
|
char dev[128]; /* need to be able to support GUID names on Windows */
|
|
(void)strlcpy(dev, live_dev, sizeof(dev));
|
|
|
|
if (strip_trailing_plus) {
|
|
size_t len = strlen(dev);
|
|
if (len &&
|
|
(dev[len-1] == '+' ||
|
|
dev[len-1] == '^' ||
|
|
dev[len-1] == '*'))
|
|
{
|
|
dev[len-1] = '\0';
|
|
}
|
|
}
|
|
LiveDevice *ld = LiveGetDevice(dev);
|
|
unsigned int iface_max_packet_size = GetIfaceMaxPacketSize(ld);
|
|
if (iface_max_packet_size > default_packet_size)
|
|
default_packet_size = iface_max_packet_size;
|
|
}
|
|
if (default_packet_size)
|
|
break;
|
|
/* fall through */
|
|
default:
|
|
default_packet_size = DEFAULT_PACKET_SIZE;
|
|
}
|
|
} else {
|
|
if (ParseSizeStringU32(temp_default_packet_size, &default_packet_size) < 0) {
|
|
SCLogError("Error parsing max-pending-packets "
|
|
"from conf file - %s. Killing engine",
|
|
temp_default_packet_size);
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
}
|
|
|
|
SCLogDebug("Default packet size set to %"PRIu32, default_packet_size);
|
|
|
|
return TM_ECODE_OK;
|
|
}
|
|
|
|
static void PostRunStartedDetectSetup(const SCInstance *suri)
|
|
{
|
|
#ifndef OS_WIN32
|
|
/* registering signal handlers we use. We setup usr2 here, so that one
|
|
* can't call it during the first sig load phase or while threads are still
|
|
* starting up. */
|
|
if (DetectEngineEnabled() && suri->delayed_detect == 0) {
|
|
UtilSignalHandlerSetup(SIGUSR2, SignalHandlerSigusr2);
|
|
UtilSignalUnblock(SIGUSR2);
|
|
}
|
|
#endif
|
|
if (suri->delayed_detect) {
|
|
/* force 'reload', this will load the rules and swap engines */
|
|
DetectEngineReload(suri);
|
|
SCLogNotice("Signature(s) loaded, Detect thread(s) activated.");
|
|
#ifndef OS_WIN32
|
|
UtilSignalHandlerSetup(SIGUSR2, SignalHandlerSigusr2);
|
|
UtilSignalUnblock(SIGUSR2);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void PostConfLoadedDetectSetup(SCInstance *suri)
|
|
{
|
|
DetectEngineCtx *de_ctx = NULL;
|
|
if (!suri->disabled_detect) {
|
|
SetupDelayedDetect(suri);
|
|
int mt_enabled = 0;
|
|
(void)SCConfGetBool("multi-detect.enabled", &mt_enabled);
|
|
int default_tenant = 0;
|
|
if (mt_enabled)
|
|
(void)SCConfGetBool("multi-detect.default", &default_tenant);
|
|
if (DetectEngineMultiTenantSetup(suri->unix_socket_enabled) == -1) {
|
|
FatalError("initializing multi-detect "
|
|
"detection engine contexts failed.");
|
|
}
|
|
if (suri->delayed_detect && suri->run_mode != RUNMODE_CONF_TEST) {
|
|
de_ctx = DetectEngineCtxInitStubForDD();
|
|
} else if (mt_enabled && !default_tenant && suri->run_mode != RUNMODE_CONF_TEST) {
|
|
de_ctx = DetectEngineCtxInitStubForMT();
|
|
} else {
|
|
de_ctx = DetectEngineCtxInit();
|
|
}
|
|
if (de_ctx == NULL) {
|
|
FatalError("initializing detection engine failed.");
|
|
}
|
|
|
|
if (de_ctx->type == DETECT_ENGINE_TYPE_NORMAL) {
|
|
if (LoadSignatures(de_ctx, suri) != TM_ECODE_OK)
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
gettimeofday(&de_ctx->last_reload, NULL);
|
|
DetectEngineAddToMaster(de_ctx);
|
|
DetectEngineBumpVersion();
|
|
}
|
|
}
|
|
|
|
static void PostConfLoadedSetupHostMode(void)
|
|
{
|
|
const char *hostmode = NULL;
|
|
|
|
if (SCConfGet("host-mode", &hostmode) == 1) {
|
|
if (!strcmp(hostmode, "router")) {
|
|
host_mode = SURI_HOST_IS_ROUTER;
|
|
} else if (!strcmp(hostmode, "sniffer-only")) {
|
|
host_mode = SURI_HOST_IS_SNIFFER_ONLY;
|
|
} else {
|
|
if (strcmp(hostmode, "auto") != 0) {
|
|
WarnInvalidConfEntry("host-mode", "%s", "auto");
|
|
}
|
|
if (EngineModeIsIPS()) {
|
|
host_mode = SURI_HOST_IS_ROUTER;
|
|
} else {
|
|
host_mode = SURI_HOST_IS_SNIFFER_ONLY;
|
|
}
|
|
}
|
|
} else {
|
|
if (EngineModeIsIPS()) {
|
|
host_mode = SURI_HOST_IS_ROUTER;
|
|
SCLogInfo("No 'host-mode': suricata is in IPS mode, using "
|
|
"default setting 'router'");
|
|
} else {
|
|
host_mode = SURI_HOST_IS_SNIFFER_ONLY;
|
|
SCLogInfo("No 'host-mode': suricata is in IDS mode, using "
|
|
"default setting 'sniffer-only'");
|
|
}
|
|
}
|
|
}
|
|
|
|
static void SetupUserMode(SCInstance *suri)
|
|
{
|
|
/* apply 'user mode' config updates here */
|
|
if (!suri->system) {
|
|
if (!suri->set_logdir) {
|
|
/* override log dir to current work dir" */
|
|
if (ConfigSetLogDirectory((char *)".") != TM_ECODE_OK) {
|
|
FatalError("could not set USER mode logdir");
|
|
}
|
|
}
|
|
if (!suri->set_datadir) {
|
|
/* override data dir to current work dir" */
|
|
if (ConfigSetDataDirectory((char *)".") != TM_ECODE_OK) {
|
|
FatalError("could not set USER mode datadir");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This function is meant to contain code that needs
|
|
* to be run once the configuration has been loaded.
|
|
*/
|
|
int PostConfLoadedSetup(SCInstance *suri)
|
|
{
|
|
/* load the pattern matchers */
|
|
MpmTableSetup();
|
|
SpmTableSetup();
|
|
|
|
int disable_offloading;
|
|
if (SCConfGetBool("capture.disable-offloading", &disable_offloading) == 0)
|
|
disable_offloading = 1;
|
|
if (disable_offloading) {
|
|
LiveSetOffloadDisable();
|
|
} else {
|
|
LiveSetOffloadWarn();
|
|
}
|
|
|
|
if (suri->checksum_validation == -1) {
|
|
const char *cv = NULL;
|
|
if (SCConfGet("capture.checksum-validation", &cv) == 1) {
|
|
if (strcmp(cv, "none") == 0) {
|
|
suri->checksum_validation = 0;
|
|
} else if (strcmp(cv, "all") == 0) {
|
|
suri->checksum_validation = 1;
|
|
}
|
|
}
|
|
}
|
|
switch (suri->checksum_validation) {
|
|
case 0:
|
|
SCConfSet("stream.checksum-validation", "0");
|
|
break;
|
|
case 1:
|
|
SCConfSet("stream.checksum-validation", "1");
|
|
break;
|
|
}
|
|
|
|
if (suri->runmode_custom_mode) {
|
|
SCConfSet("runmode", suri->runmode_custom_mode);
|
|
}
|
|
|
|
StorageInit();
|
|
#ifdef HAVE_PACKET_EBPF
|
|
if (suri->run_mode == RUNMODE_AFP_DEV) {
|
|
EBPFRegisterExtension();
|
|
LiveDevRegisterExtension();
|
|
}
|
|
#endif
|
|
RegisterFlowBypassInfo();
|
|
|
|
MacSetRegisterFlowStorage();
|
|
FlowRateRegisterFlowStorage();
|
|
|
|
SigTableInit();
|
|
|
|
#ifdef HAVE_PLUGINS
|
|
SCPluginsLoad(suri->capture_plugin_name, suri->capture_plugin_args);
|
|
#endif
|
|
|
|
LiveDeviceFinalize(); // must be after EBPF extension registration
|
|
|
|
if (RunModeEngineIsIPS(suricata.run_mode, suricata.runmode_custom_mode,
|
|
suricata.capture_plugin_name) < 0) {
|
|
FatalError("IPS mode setup failed");
|
|
}
|
|
|
|
if (EngineModeIsUnknown()) { // if still uninitialized, set the default
|
|
SCLogInfo("Setting engine mode to IDS mode by default");
|
|
EngineModeSetIDS();
|
|
}
|
|
|
|
SetMasterExceptionPolicy();
|
|
|
|
SCConfNode *eps = SCConfGetNode("stats.exception-policy");
|
|
if (eps != NULL) {
|
|
if (SCConfNodeChildValueIsTrue(eps, "per-app-proto-errors")) {
|
|
g_stats_eps_per_app_proto_errors = true;
|
|
}
|
|
}
|
|
|
|
/* Must occur prior to output mod registration
|
|
and app layer setup. */
|
|
FeatureTrackingRegister();
|
|
|
|
AppLayerSetup();
|
|
|
|
/* Suricata will use this umask if provided. By default it will use the
|
|
umask passed on from the shell. */
|
|
const char *custom_umask;
|
|
if (SCConfGet("umask", &custom_umask) == 1) {
|
|
uint16_t mask;
|
|
if (StringParseUint16(&mask, 8, (uint16_t)strlen(custom_umask), custom_umask) > 0) {
|
|
umask((mode_t)mask);
|
|
}
|
|
}
|
|
|
|
if (ConfigGetCaptureValue(suri) != TM_ECODE_OK) {
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
}
|
|
|
|
#ifdef NFQ
|
|
if (suri->run_mode == RUNMODE_NFQ)
|
|
NFQInitConfig(false);
|
|
#endif
|
|
|
|
/* Load the Host-OS lookup. */
|
|
SCHInfoLoadFromConfig();
|
|
|
|
if (suri->run_mode == RUNMODE_ENGINE_ANALYSIS) {
|
|
SCLogInfo("== Carrying out Engine Analysis ==");
|
|
const char *temp = NULL;
|
|
if (SCConfGet("engine-analysis", &temp) == 0) {
|
|
SCLogInfo("no engine-analysis parameter(s) defined in conf file. "
|
|
"Please define/enable them in the conf to use this "
|
|
"feature.");
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
}
|
|
}
|
|
|
|
/* hardcoded initialization code */
|
|
SigTableSetup(); /* load the rule keywords */
|
|
SigTableApplyStrictCommandLineOption(suri->strict_rule_parsing_string);
|
|
TmqhSetup();
|
|
|
|
TagInitCtx();
|
|
PacketAlertTagInit();
|
|
ThresholdInit();
|
|
HostBitInitCtx();
|
|
IPPairBitInitCtx();
|
|
|
|
if (DetectAddressTestConfVars() < 0) {
|
|
SCLogError(
|
|
"basic address vars test failed. Please check %s for errors", suri->conf_filename);
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
}
|
|
if (DetectPortTestConfVars() < 0) {
|
|
SCLogError("basic port vars test failed. Please check %s for errors", suri->conf_filename);
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
}
|
|
|
|
RegisterAllModules();
|
|
AppLayerHtpNeedFileInspection();
|
|
|
|
StorageFinalize();
|
|
|
|
TmModuleRunInit();
|
|
|
|
if (MayDaemonize(suri) != TM_ECODE_OK)
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
|
|
if (InitSignalHandler(suri) != TM_ECODE_OK)
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
|
|
/* Check for the existence of the default logging directory which we pick
|
|
* from suricata.yaml. If not found, shut the engine down */
|
|
suri->log_dir = SCConfigGetLogDirectory();
|
|
|
|
if (ConfigCheckLogDirectoryExists(suri->log_dir) != TM_ECODE_OK) {
|
|
SCLogError("The logging directory \"%s\" "
|
|
"supplied by %s (default-log-dir) doesn't exist. "
|
|
"Shutting down the engine",
|
|
suri->log_dir, suri->conf_filename);
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
}
|
|
if (!IsLogDirectoryWritable(suri->log_dir)) {
|
|
SCLogError("The logging directory \"%s\" "
|
|
"supplied by %s (default-log-dir) is not writable. "
|
|
"Shutting down the engine",
|
|
suri->log_dir, suri->conf_filename);
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
}
|
|
|
|
if (suri->disabled_detect) {
|
|
SCLogConfig("detection engine disabled");
|
|
/* disable raw reassembly */
|
|
(void)SCConfSetFinal("stream.reassembly.raw", "false");
|
|
}
|
|
|
|
HostInitConfig(HOST_VERBOSE);
|
|
|
|
CoredumpLoadConfig();
|
|
|
|
DecodeGlobalConfig();
|
|
|
|
/* hostmode depends on engine mode being set */
|
|
PostConfLoadedSetupHostMode();
|
|
|
|
PreRunInit(suri->run_mode);
|
|
|
|
SCReturnInt(TM_ECODE_OK);
|
|
}
|
|
|
|
void SuricataMainLoop(void)
|
|
{
|
|
SCInstance *suri = &suricata;
|
|
while(1) {
|
|
if (sigterm_count || sigint_count) {
|
|
suricata_ctl_flags |= SURICATA_STOP;
|
|
}
|
|
|
|
if (suricata_ctl_flags & SURICATA_STOP) {
|
|
SCLogNotice("Signal Received. Stopping engine.");
|
|
break;
|
|
}
|
|
|
|
TmThreadCheckThreadState();
|
|
|
|
if (sighup_count > 0) {
|
|
OutputNotifyFileRotation();
|
|
sighup_count--;
|
|
}
|
|
|
|
if (sigusr2_count > 0) {
|
|
if (!(DetectEngineReloadIsStart())) {
|
|
DetectEngineReloadStart();
|
|
DetectEngineReload(suri);
|
|
DetectEngineReloadSetIdle();
|
|
sigusr2_count--;
|
|
}
|
|
|
|
} else if (DetectEngineReloadIsStart()) {
|
|
DetectEngineReload(suri);
|
|
DetectEngineReloadSetIdle();
|
|
}
|
|
|
|
usleep(10* 1000);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Global initialization common to all runmodes.
|
|
*
|
|
* This can be used by fuzz targets.
|
|
*/
|
|
|
|
int InitGlobal(void)
|
|
{
|
|
SCRustInit(&suricata_context);
|
|
|
|
SC_ATOMIC_INIT(engine_stage);
|
|
|
|
/* initialize the logging subsys */
|
|
SCLogInitLogModule(NULL);
|
|
|
|
SCSetThreadName("Suricata-Main");
|
|
|
|
/* Ignore SIGUSR2 as early as possible. We redeclare interest
|
|
* once we're done launching threads. The goal is to either die
|
|
* completely or handle any and all SIGUSR2s correctly.
|
|
*/
|
|
#ifndef OS_WIN32
|
|
UtilSignalHandlerSetup(SIGUSR2, SIG_IGN);
|
|
if (UtilSignalBlock(SIGUSR2)) {
|
|
SCLogError("SIGUSR2 initialization error");
|
|
return EXIT_FAILURE;
|
|
}
|
|
#endif
|
|
|
|
ParseSizeInit();
|
|
RunModeRegisterRunModes();
|
|
|
|
/* Initialize the configuration module. */
|
|
SCConfInit();
|
|
DatalinkTableInit();
|
|
|
|
VarNameStoreInit();
|
|
|
|
// zero all module storage
|
|
memset(tmm_modules, 0, TMM_SIZE * sizeof(TmModule));
|
|
|
|
return 0;
|
|
}
|
|
|
|
void SuricataPreInit(const char *progname)
|
|
{
|
|
SCInstanceInit(&suricata, progname);
|
|
|
|
if (InitGlobal() != 0) {
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
void SuricataInit(void)
|
|
{
|
|
/* Initializations for global vars, queues, etc (memsets, mutex init..) */
|
|
GlobalsInitPreConfig();
|
|
|
|
if (suricata.run_mode == RUNMODE_DUMP_CONFIG) {
|
|
SCConfDump();
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
int tracking = 1;
|
|
if (SCConfGetBool("vlan.use-for-tracking", &tracking) == 1 && !tracking) {
|
|
/* Ignore vlan_ids when comparing flows. */
|
|
g_vlan_mask = 0x0000;
|
|
}
|
|
SCLogDebug("vlan tracking is %s", tracking == 1 ? "enabled" : "disabled");
|
|
if (SCConfGetBool("livedev.use-for-tracking", &tracking) == 1 && !tracking) {
|
|
/* Ignore livedev id when comparing flows. */
|
|
g_livedev_mask = 0x0000;
|
|
}
|
|
if (SCConfGetBool("decoder.recursion-level.use-for-tracking", &tracking) == 1 && !tracking) {
|
|
/* Ignore recursion level when comparing flows. */
|
|
g_recurlvl_mask = 0x00;
|
|
}
|
|
SetupUserMode(&suricata);
|
|
InitRunAs(&suricata);
|
|
|
|
/* Since our config is now loaded we can finish configurating the
|
|
* logging module. */
|
|
SCLogLoadConfig(suricata.daemon, suricata.verbose, suricata.userid, suricata.groupid);
|
|
|
|
LogVersion(&suricata);
|
|
UtilCpuPrintSummary();
|
|
RunModeInitializeThreadSettings();
|
|
|
|
if (suricata.run_mode == RUNMODE_CONF_TEST)
|
|
SCLogInfo("Running suricata under test mode");
|
|
|
|
if (ParseInterfacesList(suricata.aux_run_mode, suricata.pcap_dev) != TM_ECODE_OK) {
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (PostConfLoadedSetup(&suricata) != TM_ECODE_OK) {
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
SCDropMainThreadCaps(suricata.userid, suricata.groupid);
|
|
|
|
/* Re-enable coredumps after privileges are dropped. */
|
|
CoredumpEnable();
|
|
|
|
if (suricata.run_mode != RUNMODE_UNIX_SOCKET && !suricata.disabled_detect) {
|
|
suricata.unix_socket_enabled = ConfUnixSocketIsEnable();
|
|
}
|
|
|
|
PreRunPostPrivsDropInit(suricata.run_mode);
|
|
|
|
SCOnLoggingReady();
|
|
|
|
LandlockSandboxing(&suricata);
|
|
|
|
PostConfLoadedDetectSetup(&suricata);
|
|
if (suricata.run_mode == RUNMODE_ENGINE_ANALYSIS) {
|
|
goto out;
|
|
} else if (suricata.run_mode == RUNMODE_CONF_TEST){
|
|
SCLogNotice("Configuration provided was successfully loaded. Exiting.");
|
|
goto out;
|
|
} else if (suricata.run_mode == RUNMODE_DUMP_FEATURES) {
|
|
FeatureDump();
|
|
goto out;
|
|
}
|
|
|
|
if (suricata.run_mode == RUNMODE_DPDK)
|
|
prerun_snap = SystemHugepageSnapshotCreate();
|
|
|
|
SCSetStartTime(&suricata);
|
|
if (suricata.run_mode != RUNMODE_UNIX_SOCKET) {
|
|
UnixManagerThreadSpawnNonRunmode(suricata.unix_socket_enabled);
|
|
}
|
|
RunModeDispatch(suricata.run_mode, suricata.runmode_custom_mode, suricata.capture_plugin_name,
|
|
suricata.capture_plugin_args);
|
|
return;
|
|
|
|
out:
|
|
GlobalsDestroy();
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
void SuricataShutdown(void)
|
|
{
|
|
/* Update the engine stage/status flag */
|
|
SC_ATOMIC_SET(engine_stage, SURICATA_DEINIT);
|
|
|
|
UnixSocketKillSocketThread();
|
|
PostRunDeinit(suricata.run_mode, &suricata.start_time);
|
|
/* kill remaining threads */
|
|
TmThreadKillThreads();
|
|
}
|
|
|
|
void SuricataPostInit(void)
|
|
{
|
|
/* Wait till all the threads have been initialized */
|
|
if (TmThreadWaitOnThreadInit() == TM_ECODE_FAILED) {
|
|
SystemHugepageSnapshotDestroy(prerun_snap);
|
|
FatalError("Engine initialization failed, "
|
|
"aborting...");
|
|
}
|
|
|
|
int limit_nproc = 0;
|
|
if (SCConfGetBool("security.limit-noproc", &limit_nproc) == 0) {
|
|
limit_nproc = 0;
|
|
}
|
|
|
|
#if defined(SC_ADDRESS_SANITIZER)
|
|
if (limit_nproc) {
|
|
SCLogWarning(
|
|
"\"security.limit-noproc\" (setrlimit()) not set when using address sanitizer");
|
|
limit_nproc = 0;
|
|
}
|
|
#endif
|
|
|
|
if (limit_nproc) {
|
|
#if defined(HAVE_SYS_RESOURCE_H)
|
|
#ifdef linux
|
|
if (geteuid() == 0) {
|
|
SCLogWarning("setrlimit has no effect when running as root.");
|
|
}
|
|
#endif
|
|
struct rlimit r = { 0, 0 };
|
|
if (setrlimit(RLIMIT_NPROC, &r) != 0) {
|
|
SCLogWarning("setrlimit failed to prevent process creation.");
|
|
}
|
|
#else
|
|
SCLogWarning("setrlimit unavailable.");
|
|
#endif
|
|
}
|
|
|
|
SC_ATOMIC_SET(engine_stage, SURICATA_RUNTIME);
|
|
PacketPoolPostRunmodes();
|
|
|
|
/* Un-pause all the paused threads */
|
|
TmThreadContinueThreads();
|
|
|
|
/* Must ensure all threads are fully operational before continuing with init process */
|
|
if (TmThreadWaitOnThreadRunning() != TM_ECODE_OK) {
|
|
SystemHugepageSnapshotDestroy(prerun_snap);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* Print notice and send OS specific notification of threads in running state */
|
|
OnNotifyRunning();
|
|
|
|
PostRunStartedDetectSetup(&suricata);
|
|
if (suricata.run_mode == RUNMODE_DPDK) { // only DPDK uses hpages at the moment
|
|
SystemHugepageSnapshot *postrun_snap = SystemHugepageSnapshotCreate();
|
|
SystemHugepageEvaluateHugepages(prerun_snap, postrun_snap);
|
|
SystemHugepageSnapshotDestroy(prerun_snap);
|
|
SystemHugepageSnapshotDestroy(postrun_snap);
|
|
}
|
|
SCPledge();
|
|
}
|