summaryrefslogtreecommitdiffstats
path: root/src/suricata.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:39:49 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:39:49 +0000
commita0aa2307322cd47bbf416810ac0292925e03be87 (patch)
tree37076262a026c4b48c8a0e84f44ff9187556ca35 /src/suricata.c
parentInitial commit. (diff)
downloadsuricata-a0aa2307322cd47bbf416810ac0292925e03be87.tar.xz
suricata-a0aa2307322cd47bbf416810ac0292925e03be87.zip
Adding upstream version 1:7.0.3.upstream/1%7.0.3
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--src/suricata.c3054
1 files changed, 3054 insertions, 0 deletions
diff --git a/src/suricata.c b/src/suricata.c
new file mode 100644
index 0000000..d0e1049
--- /dev/null
+++ b/src/suricata.c
@@ -0,0 +1,3054 @@
+/* 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
+
+#if HAVE_LIBSYSTEMD
+#include <systemd/sd-daemon.h>
+#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-pcap.h"
+#include "source-pcap-file.h"
+#include "source-pcap-file-helper.h"
+#include "source-pfring.h"
+#include "source-erf-file.h"
+#include "source-erf-dag.h"
+#include "source-napatech.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-byte.h"
+#include "util-conf.h"
+#include "util-coredump-config.h"
+#include "util-cpu.h"
+#include "util-daemon.h"
+#include "util-device.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-luajit.h"
+#include "util-macset.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 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;
+
+/** Run mode selected */
+int run_mode = RUNMODE_UNKNOWN;
+
+/** 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. */
+uint16_t max_pending_packets;
+
+/** global indicating if detection is enabled */
+int g_detect_disabled = 0;
+
+/** set caps or not */
+int 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;
+
+/* flag to disable hashing almost globally, to be similar to disabling nss
+ * support */
+bool g_disable_hashing = 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 (run_mode == RUNMODE_UNITTEST)
+ return 1;
+
+ return 0;
+}
+#endif
+
+int RunmodeGetCurrent(void)
+{
+ return 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();
+ FrameConfigInit();
+}
+
+static void GlobalsDestroy(SCInstance *suri)
+{
+ 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();
+ TagDestroyCtx();
+
+ LiveDeviceListClean();
+ OutputDeregisterAll();
+ FeatureTrackingRelease();
+ SCProtoNameRelease();
+ TimeDeinit();
+ TmqhCleanup();
+ TmModuleRunDeInit();
+ ParseSizeDeinit();
+
+#ifdef HAVE_DPDK
+ DPDKCleanupEAL();
+#endif
+
+#ifdef HAVE_AF_PACKET
+ AFPPeersListClean();
+#endif
+
+#ifdef NFQ
+ NFQContextsClean();
+#endif
+
+#ifdef BUILD_HYPERSCAN
+ MpmHSGlobalCleanup();
+#endif
+
+ ConfDeInit();
+#ifdef HAVE_LUAJIT
+ LuajitFreeStatesPool();
+#endif
+ 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)
+{
+#if HAVE_LIBSYSTEMD
+ if (sd_notify(0, "READY=1") < 0) {
+ SCLogWarning("failed to notify systemd");
+ /* Please refer to:
+ * https://www.freedesktop.org/software/systemd/man/sd_notify.html#Return%20Value
+ * for discussion on why failure should not be considered an error */
+ }
+#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 = SCMalloc(bpf_len);
+ if (unlikely(bpf_filter == NULL))
+ return TM_ECODE_FAILED;
+ memset(bpf_filter, 0x00, bpf_len);
+
+ 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 (ConfSetFinal("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;
+ uint32_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);
+ }
+ bpf_len = st.st_size + 1;
+
+ bpf_filter = SCMalloc(bpf_len);
+ if (unlikely(bpf_filter == NULL)) {
+ SCLogError("Failed to allocate buffer for bpf filter in file %s", filename);
+ exit(EXIT_FAILURE);
+ }
+ memset(bpf_filter, 0x00, bpf_len);
+
+ 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 (ConfSetFinal("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--list-app-layer-protos : list supported app layer protocols\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");
+#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 HAVE_NAPATECH
+ printf("\t--napatech : run Napatech Streams using the API\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_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
+#ifdef HAVE_HTP_URI_NORMALIZE_HOOK
+ strlcat(features, "HAVE_HTP_URI_NORMALIZE_HOOK ", sizeof(features));
+#endif
+#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));
+#ifdef HAVE_LUA
+ strlcat(features, "HAVE_LUA ", sizeof(features));
+#endif
+#ifdef HAVE_LUAJIT
+ strlcat(features, "HAVE_LUAJIT ", 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 (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 (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, linked against %s\n",
+ HTP_VERSION_STRING_FULL, htp_get_version());
+ printf("\n");
+#include "build-info.h"
+}
+
+int coverage_unittests;
+int g_ut_modules;
+int g_ut_covered;
+
+void RegisterAllModules(void)
+{
+ // zero all module storage
+ memset(tmm_modules, 0, TMM_SIZE * sizeof(TmModule));
+
+ /* 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();
+ /* pfring */
+ TmModuleReceivePfringRegister();
+ TmModuleDecodePfringRegister();
+ /* dag file */
+ TmModuleReceiveErfFileRegister();
+ TmModuleDecodeErfFileRegister();
+ /* dag live */
+ TmModuleReceiveErfDagRegister();
+ TmModuleDecodeErfDagRegister();
+ /* napatech */
+ TmModuleNapatechStreamRegister();
+ TmModuleNapatechDecodeRegister();
+
+ /* flow worker */
+ TmModuleFlowWorkerRegister();
+ /* respond-reject */
+ TmModuleRespondRejectRegister();
+
+ /* log api */
+ TmModuleLoggerRegister();
+ TmModuleStatsLoggerRegister();
+
+ TmModuleDebugList();
+ /* nflog */
+ TmModuleReceiveNFLOGRegister();
+ TmModuleDecodeNFLOGRegister();
+
+ /* windivert */
+ TmModuleReceiveWinDivertRegister();
+ TmModuleVerdictWinDivertRegister();
+ TmModuleDecodeWinDivertRegister();
+
+ /* Dpdk */
+ TmModuleReceiveDPDKRegister();
+ TmModuleDecodeDPDKRegister();
+}
+
+static TmEcode LoadYamlConfig(SCInstance *suri)
+{
+ SCEnter();
+
+ if (suri->conf_filename == NULL)
+ suri->conf_filename = DEFAULT_CONF_FILE;
+
+ if (ConfYamlLoadFile(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]);
+ ConfYamlHandleInclude(ConfGetRootNode(), 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_PFRING) {
+ /* FIXME add backward compat support */
+ /* iface has been set on command line */
+ if (strlen(pcap_dev)) {
+ if (ConfSetFinal("pfring.live-interface", pcap_dev) != 1) {
+ SCLogError("Failed to set pfring.live-interface");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+ } else {
+ /* not an error condition if we have a 1.0 config */
+ LiveBuildDeviceList("pfring");
+ }
+#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 (ConfSetFinal("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 (ConfSetFinal("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 (ConfSetFinal("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;
+}
+
+static TmEcode ParseCommandLine(int argc, char** argv, SCInstance *suri)
+{
+ int opt;
+
+ int dump_config = 0;
+ int dump_features = 0;
+ int list_app_layer_protocols = 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},
+ {"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-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},
+ {"napatech", 0, 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},
+ {"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
+ suri->run_mode = RUNMODE_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 (ConfSetFinal("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 (ConfSetFinal("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 (ConfSetFinal("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 (ConfSetFinal("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-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)) {
+ 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;
+ } 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 (ConfSetFinal("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_NAPATECH;
+#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 (ConfSetFinal("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 (!ConfSetFromString(optarg, 1)) {
+ FatalError("failed to set configuration value %s", optarg);
+ }
+ }
+ }
+ else if (strcmp((long_opts[option_index]).name, "pcap-file-continuous") == 0) {
+ if (ConfSetFinal("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 (ConfSetFinal("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 (ConfSetFinal("pcap-file.recursive", "true") != 1) {
+ SCLogError("failed to set pcap-file.recursive");
+ 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 {
+ 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 (ConfSetFinal("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 or pf-ring are available */
+#if defined HAVE_PFRING || HAVE_NETMAP
+ int i = 0;
+#ifdef HAVE_PFRING
+ i++;
+#endif
+#ifdef HAVE_NETMAP
+ i++;
+#endif
+ SCLogWarning("faster capture "
+ "option%s %s available:"
+#ifdef HAVE_PFRING
+ " PF_RING (--pfring-int=%s)"
+#endif
+#ifdef HAVE_NETMAP
+ " NETMAP (--netmap=%s)"
+#endif
+ ". Use --pcap=%s to suppress this warning",
+ i == 1 ? "" : "s", i == 1 ? "is" : "are"
+#ifdef HAVE_PFRING
+ ,
+ optarg
+#endif
+#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 (ConfSetFinal("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) {
+ SCLogError("can't use -s/-S 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_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
+static 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 (ConfGet("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 == FALSE && suri->do_setgid == FALSE) {
+ const char *id;
+ if (ConfGet("run-as.user", &id) == 1) {
+ suri->do_setuid = TRUE;
+ suri->user_name = id;
+ }
+ if (ConfGet("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 == TRUE) {
+ 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 == TRUE) {
+ 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 (ConfGetBool("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)
+{
+ HttpRangeContainersInit();
+ 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();
+}
+
+/* tasks we need to run before packets start flowing,
+ * but after we dropped privs */
+void PreRunPostPrivsDropInit(const int runmode)
+{
+ StatsSetupPostConfigPreOutput();
+ RunModeInitializeOutputs();
+ DatasetsInit();
+
+ if (runmode == RUNMODE_UNIX_SOCKET) {
+ /* As the above did some necessary startup initialization, it
+ * also setup some outputs where only one is allowed, so
+ * deinitialize to the state that unix-mode does after every
+ * pcap. */
+ PostRunDeinit(RUNMODE_PCAP_FILE, NULL);
+ return;
+ }
+
+ StatsSetupPostConfigPostOutput();
+}
+
+/* clean up / shutdown code for both the main modes and for
+ * unix socket mode.
+ *
+ * Will be run once per pcap in unix-socket mode */
+void PostRunDeinit(const int runmode, struct timeval *start_time)
+{
+ if (runmode == RUNMODE_UNIX_SOCKET)
+ return;
+
+ /* needed by FlowForceReassembly */
+ PacketPoolInit();
+
+ /* handle graceful shutdown of the flow engine, it's helper
+ * threads and the packet threads */
+ FlowDisableFlowManagerThread();
+ TmThreadDisableReceiveThreads();
+ FlowForceReassembly();
+ TmThreadDisablePacketThreads();
+ 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();
+
+ TmqResetQueues();
+#ifdef PROFILING
+ if (profiling_rules_enabled)
+ SCProfilingDump();
+ SCProfilingDestroy();
+#endif
+}
+
+
+static int StartInternalRunMode(SCInstance *suri, int argc, char **argv)
+{
+ /* 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_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(argc, argv)) {
+ 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;
+}
+
+static int FinalizeRunMode(SCInstance *suri, char **argv)
+{
+ switch (suri->run_mode) {
+ case RUNMODE_UNKNOWN:
+ PrintUsage(argv[0]);
+ return TM_ECODE_FAILED;
+ default:
+ break;
+ }
+ /* Set the global run mode and offline flag. */
+ run_mode = suri->run_mode;
+
+ 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 (ConfGetBool("detect.delayed-detect", &suri->delayed_detect) != 1) {
+ ConfNode *denode = NULL;
+ ConfNode *decnf = ConfGetNode("detect-engine");
+ if (decnf != NULL) {
+ TAILQ_FOREACH(denode, &decnf->head, next) {
+ if (strcmp(denode->val, "delayed-detect") == 0) {
+ (void)ConfGetChildValueBool(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)
+{
+ 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 (ConfGetInt("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 >= UINT16_MAX) {
+ SCLogError("Maximum max-pending-packets setting is 65534 and must be greater than 0. "
+ "Please check %s for errors",
+ suri->conf_filename);
+ return TM_ECODE_FAILED;
+ } else {
+ max_pending_packets = (uint16_t)tmp_max_pending_packets;
+ }
+
+ SCLogDebug("Max pending packets set to %" PRIu16, 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 ((ConfGet("default-packet-size", &temp_default_packet_size)) != 1) {
+ int lthread;
+ int nlive;
+ int strip_trailing_plus = 0;
+ switch (suri->run_mode) {
+#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_PCAP_DEV:
+ case RUNMODE_AFP_DEV:
+ case RUNMODE_AFXDP_DEV:
+ case RUNMODE_PFRING:
+ 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)ConfGetBool("multi-detect.enabled", &mt_enabled);
+ int default_tenant = 0;
+ if (mt_enabled)
+ (void)ConfGetBool("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 (ConfGet("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 == false) {
+ if (suri->set_logdir == false) {
+ /* override log dir to current work dir" */
+ if (ConfigSetLogDirectory((char *)".") != TM_ECODE_OK) {
+ FatalError("could not set USER mode logdir");
+ }
+ }
+ if (suri->set_datadir == false) {
+ /* 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)
+{
+ /* do this as early as possible #1577 #1955 */
+#ifdef HAVE_LUAJIT
+ if (LuajitSetupStatesPool() != 0) {
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+#endif
+
+ /* load the pattern matchers */
+ MpmTableSetup();
+ SpmTableSetup();
+
+ int disable_offloading;
+ if (ConfGetBool("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 (ConfGet("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:
+ ConfSet("stream.checksum-validation", "0");
+ break;
+ case 1:
+ ConfSet("stream.checksum-validation", "1");
+ break;
+ }
+
+ if (suri->runmode_custom_mode) {
+ ConfSet("runmode", suri->runmode_custom_mode);
+ }
+
+ StorageInit();
+#ifdef HAVE_PACKET_EBPF
+ if (suri->run_mode == RUNMODE_AFP_DEV) {
+ EBPFRegisterExtension();
+ LiveDevRegisterExtension();
+ }
+#endif
+ RegisterFlowBypassInfo();
+
+ MacSetRegisterFlowStorage();
+
+ LiveDeviceFinalize(); // must be after EBPF extension registration
+
+ RunModeEngineIsIPS(
+ suricata.run_mode, suricata.runmode_custom_mode, suricata.capture_plugin_name);
+
+ if (EngineModeIsUnknown()) { // if still uninitialized, set the default
+ SCLogInfo("Setting engine mode to IDS mode by default");
+ EngineModeSetIDS();
+ }
+
+ SetMasterExceptionPolicy();
+
+ 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 (ConfGet("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 (ConfGet("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);
+ }
+
+ FeatureTrackingRegister(); /* must occur prior to output mod registration */
+ RegisterAllModules();
+#ifdef HAVE_PLUGINS
+ SCPluginsLoad(suri->capture_plugin_name, suri->capture_plugin_args);
+#endif
+ 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 = ConfigGetLogDirectory();
+
+ 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)ConfSetFinal("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);
+}
+
+static void SuricataMainLoop(SCInstance *suri)
+{
+ 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)
+{
+ rs_init(&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. */
+ ConfInit();
+
+ VarNameStoreInit();
+ return 0;
+}
+
+int SuricataMain(int argc, char **argv)
+{
+ SCInstanceInit(&suricata, argv[0]);
+
+ if (InitGlobal() != 0) {
+ exit(EXIT_FAILURE);
+ }
+
+#ifdef OS_WIN32
+ /* service initialization */
+ if (WindowsInitService(argc, argv) != 0) {
+ exit(EXIT_FAILURE);
+ }
+#endif /* OS_WIN32 */
+
+ if (ParseCommandLine(argc, argv, &suricata) != TM_ECODE_OK) {
+ exit(EXIT_FAILURE);
+ }
+
+ if (FinalizeRunMode(&suricata, argv) != TM_ECODE_OK) {
+ exit(EXIT_FAILURE);
+ }
+
+ switch (StartInternalRunMode(&suricata, argc, argv)) {
+ case TM_ECODE_DONE:
+ exit(EXIT_SUCCESS);
+ case TM_ECODE_FAILED:
+ exit(EXIT_FAILURE);
+ }
+
+ /* Initializations for global vars, queues, etc (memsets, mutex init..) */
+ GlobalsInitPreConfig();
+
+ /* Load yaml configuration file if provided. */
+ if (LoadYamlConfig(&suricata) != TM_ECODE_OK) {
+ exit(EXIT_FAILURE);
+ }
+
+ if (suricata.run_mode == RUNMODE_DUMP_CONFIG) {
+ ConfDump();
+ exit(EXIT_SUCCESS);
+ }
+
+ int tracking = 1;
+ if (ConfGetBool("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 (ConfGetBool("livedev.use-for-tracking", &tracking) == 1 && !tracking) {
+ /* Ignore livedev id when comparing flows. */
+ g_livedev_mask = 0x0000;
+ }
+ 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);
+
+ 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;
+ }
+
+ SystemHugepageSnapshot *prerun_snap = SystemHugepageSnapshotCreate();
+ SCSetStartTime(&suricata);
+ RunModeDispatch(suricata.run_mode, suricata.runmode_custom_mode,
+ suricata.capture_plugin_name, suricata.capture_plugin_args);
+ if (suricata.run_mode != RUNMODE_UNIX_SOCKET) {
+ UnixManagerThreadSpawnNonRunmode(suricata.unix_socket_enabled);
+ }
+
+ /* Wait till all the threads have been initialized */
+ if (TmThreadWaitOnThreadInit() == TM_ECODE_FAILED) {
+ FatalError("Engine initialization failed, "
+ "aborting...");
+ }
+
+ int limit_nproc = 0;
+ if (ConfGetBool("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 effet 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) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Print notice and send OS specific notification of threads in running state */
+ OnNotifyRunning();
+
+ PostRunStartedDetectSetup(&suricata);
+
+ SystemHugepageSnapshot *postrun_snap = SystemHugepageSnapshotCreate();
+ if (run_mode == RUNMODE_DPDK) // only DPDK uses hpages at the moment
+ SystemHugepageEvaluateHugepages(prerun_snap, postrun_snap);
+ SystemHugepageSnapshotDestroy(prerun_snap);
+ SystemHugepageSnapshotDestroy(postrun_snap);
+
+ SCPledge();
+ SuricataMainLoop(&suricata);
+
+ /* 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();
+
+out:
+ GlobalsDestroy(&suricata);
+
+ exit(EXIT_SUCCESS);
+}