summaryrefslogtreecommitdiffstats
path: root/src/runmode-pfring.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/runmode-pfring.c531
1 files changed, 531 insertions, 0 deletions
diff --git a/src/runmode-pfring.c b/src/runmode-pfring.c
new file mode 100644
index 0000000..b0af83b
--- /dev/null
+++ b/src/runmode-pfring.c
@@ -0,0 +1,531 @@
+/* Copyright (C) 2007-2018 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "suricata-common.h"
+#include "runmode-pfring.h"
+#include "tm-threads.h"
+#include "conf.h"
+#include "runmodes.h"
+#include "source-pfring.h"
+#include "suricata.h"
+#include "util-bpf.h"
+#include "util-debug.h"
+#include "util-time.h"
+#include "util-cpu.h"
+#include "util-runmodes.h"
+#include "util-device.h"
+#include "util-ioctl.h"
+#include "util-byte.h"
+#include "util-conf.h"
+
+#ifdef HAVE_PFRING
+#include <pfring.h>
+#endif
+
+#define PFRING_CONF_V1 1
+#define PFRING_CONF_V2 2
+
+const char *RunModeIdsPfringGetDefaultMode(void)
+{
+#ifdef HAVE_PFRING
+ return "workers";
+#else
+ return NULL;
+#endif
+}
+
+void RunModeIdsPfringRegister(void)
+{
+ RunModeRegisterNewRunMode(RUNMODE_PFRING, "autofp",
+ "Multi threaded pfring mode. Packets from "
+ "each flow are assigned to a single detect "
+ "thread, unlike \"pfring_auto\" where packets "
+ "from the same flow can be processed by any "
+ "detect thread",
+ RunModeIdsPfringAutoFp, NULL);
+ RunModeRegisterNewRunMode(
+ RUNMODE_PFRING, "single", "Single threaded pfring mode", RunModeIdsPfringSingle, NULL);
+ RunModeRegisterNewRunMode(RUNMODE_PFRING, "workers",
+ "Workers pfring mode, each thread does all"
+ " tasks from acquisition to logging",
+ RunModeIdsPfringWorkers, NULL);
+ return;
+}
+
+#ifdef HAVE_PFRING
+static void PfringDerefConfig(void *conf)
+{
+ PfringIfaceConfig *pfp = (PfringIfaceConfig *)conf;
+ if (SC_ATOMIC_SUB(pfp->ref, 1) == 1) {
+ SCFree(pfp);
+ }
+}
+
+/**
+ * \brief extract information from config file
+ *
+ * The returned structure will be freed by the thread init function.
+ * This is thus necessary to or copy the structure before giving it
+ * to thread or to reparse the file for each thread (and thus have
+ * new structure.
+ *
+ * If old config system is used, then return the same parameters
+ * value for each interface.
+ *
+ * \return a PfringIfaceConfig corresponding to the interface name
+ */
+static void *OldParsePfringConfig(const char *iface)
+{
+ const char *threadsstr = NULL;
+ PfringIfaceConfig *pfconf = SCMalloc(sizeof(*pfconf));
+ const char *tmpclusterid;
+ const char *tmpctype = NULL;
+ cluster_type default_ctype = CLUSTER_FLOW;
+
+ if (unlikely(pfconf == NULL)) {
+ return NULL;
+ }
+
+ if (iface == NULL) {
+ SCFree(pfconf);
+ return NULL;
+ }
+
+ strlcpy(pfconf->iface, iface, sizeof(pfconf->iface));
+ pfconf->flags = 0;
+ pfconf->threads = 1;
+ pfconf->cluster_id = 1;
+ pfconf->ctype = default_ctype;
+ pfconf->DerefFunc = PfringDerefConfig;
+ pfconf->checksum_mode = CHECKSUM_VALIDATION_AUTO;
+ SC_ATOMIC_INIT(pfconf->ref);
+ (void) SC_ATOMIC_ADD(pfconf->ref, 1);
+
+ /* Find initial node */
+ if (ConfGet("pfring.threads", &threadsstr) != 1) {
+ pfconf->threads = 1;
+ } else {
+ if (threadsstr != NULL) {
+ if (StringParseInt32(&pfconf->threads, 10, 0, threadsstr) < 0) {
+ SCLogWarning("Invalid value for "
+ "pfring.threads: '%s'. Resetting to 1.",
+ threadsstr);
+ pfconf->threads = 1;
+ }
+ }
+ }
+ if (pfconf->threads == 0) {
+ pfconf->threads = 1;
+ }
+
+ SC_ATOMIC_RESET(pfconf->ref);
+ (void) SC_ATOMIC_ADD(pfconf->ref, pfconf->threads);
+
+ if (strncmp(pfconf->iface, "zc", 2) == 0) {
+ SCLogInfo("%s: ZC interface detected, not setting cluster-id", pfconf->iface);
+ }
+ else if ((pfconf->threads == 1) && (strncmp(pfconf->iface, "dna", 3) == 0)) {
+ SCLogInfo("DNA interface detected, not setting cluster-id");
+ } else if (ConfGet("pfring.cluster-id", &tmpclusterid) != 1) {
+ SCLogError("Could not get cluster-id from config");
+ } else {
+ if (StringParseInt32(&pfconf->cluster_id, 10, 0, (const char *)tmpclusterid) < 0) {
+ SCLogWarning("Invalid value for "
+ "pfring.cluster_id: '%s'. Resetting to 1.",
+ tmpclusterid);
+ pfconf->cluster_id = 1;
+ }
+ pfconf->flags |= PFRING_CONF_FLAGS_CLUSTER;
+ SCLogDebug("Going to use cluster-id %" PRId32, pfconf->cluster_id);
+ }
+
+ if (strncmp(pfconf->iface, "zc", 2) == 0) {
+ SCLogInfo("%s: ZC interface detected, not setting cluster type for PF_RING", pfconf->iface);
+ } else if ((pfconf->threads == 1) && (strncmp(pfconf->iface, "dna", 3) == 0)) {
+ SCLogInfo(
+ "%s: DNA interface detected, not setting cluster type for PF_RING", pfconf->iface);
+ } else if (ConfGet("pfring.cluster-type", &tmpctype) != 1) {
+ SCLogError("Could not get cluster-type from config");
+ } else if (strcmp(tmpctype, "cluster_round_robin") == 0) {
+ SCLogInfo("%s: Using round-robin cluster mode for PF_RING", pfconf->iface);
+ pfconf->ctype = (cluster_type)tmpctype;
+ } else if (strcmp(tmpctype, "cluster_flow") == 0) {
+ SCLogInfo("%s: Using flow cluster mode for PF_RING", pfconf->iface);
+ pfconf->ctype = (cluster_type)tmpctype;
+ } else {
+ SCLogError("invalid cluster-type %s", tmpctype);
+ SCFree(pfconf);
+ return NULL;
+ }
+
+ return pfconf;
+}
+
+/**
+ * \brief extract information from config file
+ *
+ * The returned structure will be freed by the thread init function.
+ * This is thus necessary to or copy the structure before giving it
+ * to thread or to reparse the file for each thread (and thus have
+ * new structure.
+ *
+ * If old config system is used, then return the same parameters
+ * value for each interface.
+ *
+ * \return a PfringIfaceConfig corresponding to the interface name
+ */
+static void *ParsePfringConfig(const char *iface)
+{
+ const char *threadsstr = NULL;
+ ConfNode *if_root;
+ ConfNode *if_default = NULL;
+ ConfNode *pf_ring_node;
+ PfringIfaceConfig *pfconf = SCMalloc(sizeof(*pfconf));
+ const char *tmpclusterid;
+ const char *tmpctype = NULL;
+ cluster_type default_ctype = CLUSTER_FLOW;
+ int getctype = 0;
+ int bool_val;
+
+ if (unlikely(pfconf == NULL)) {
+ return NULL;
+ }
+
+ if (iface == NULL) {
+ SCFree(pfconf);
+ return NULL;
+ }
+
+ memset(pfconf, 0, sizeof(PfringIfaceConfig));
+ strlcpy(pfconf->iface, iface, sizeof(pfconf->iface));
+ pfconf->threads = 1;
+ pfconf->cluster_id = 1;
+ pfconf->ctype = (cluster_type)default_ctype;
+ pfconf->DerefFunc = PfringDerefConfig;
+ SC_ATOMIC_INIT(pfconf->ref);
+ (void) SC_ATOMIC_ADD(pfconf->ref, 1);
+
+ /* Find initial node */
+ pf_ring_node = ConfGetNode("pfring");
+ if (pf_ring_node == NULL) {
+ SCLogInfo("Unable to find pfring config using default value");
+ return pfconf;
+ }
+
+ if_root = ConfFindDeviceConfig(pf_ring_node, iface);
+
+ if_default = ConfFindDeviceConfig(pf_ring_node, "default");
+
+ if (if_root == NULL && if_default == NULL) {
+ SCLogInfo("Unable to find pfring config for "
+ "interface %s, using default value or 1.0 "
+ "configuration system. ",
+ iface);
+ return pfconf;
+ }
+
+ /* If there is no setting for current interface use default one as main iface */
+ if (if_root == NULL) {
+ if_root = if_default;
+ if_default = NULL;
+ }
+
+ if (ConfGetChildValueWithDefault(if_root, if_default, "threads", &threadsstr) != 1) {
+ pfconf->threads = 1;
+ } else if (threadsstr != NULL) {
+ if (strcmp(threadsstr, "auto") == 0) {
+ pfconf->threads = (int)UtilCpuGetNumProcessorsOnline();
+ if (pfconf->threads > 0) {
+ SCLogPerf("%u cores, so using %u threads", pfconf->threads, pfconf->threads);
+ } else {
+ pfconf->threads = GetIfaceRSSQueuesNum(iface);
+ if (pfconf->threads > 0) {
+ SCLogPerf("%d RSS queues, so using %u threads", pfconf->threads, pfconf->threads);
+ }
+ }
+ } else {
+ uint16_t threads = 0;
+ if (StringParseUint16(&threads, 10, 0, (const char *)threadsstr) < 0) {
+ SCLogWarning("Invalid value for "
+ "pfring.threads: '%s'. Resetting to 1.",
+ threadsstr);
+ pfconf->threads = 1;
+ } else {
+ pfconf->threads = threads;
+ }
+ }
+ }
+ if (pfconf->threads <= 0) {
+ pfconf->threads = 1;
+ }
+
+ SC_ATOMIC_RESET(pfconf->ref);
+ (void) SC_ATOMIC_ADD(pfconf->ref, pfconf->threads);
+
+ /* command line value has precedence */
+ if (ConfGet("pfring.cluster-id", &tmpclusterid) == 1) {
+ if (StringParseInt32(&pfconf->cluster_id, 10, 0, (const char *)tmpclusterid) < 0) {
+ SCLogWarning("Invalid value for "
+ "pfring.cluster-id: '%s'. Resetting to 1.",
+ tmpclusterid);
+ pfconf->cluster_id = 1;
+ }
+ pfconf->flags |= PFRING_CONF_FLAGS_CLUSTER;
+ SCLogDebug("Going to use command-line provided cluster-id %" PRId32,
+ pfconf->cluster_id);
+ } else {
+
+ if (strncmp(pfconf->iface, "zc", 2) == 0) {
+ SCLogInfo(
+ "%s: ZC interface detected, not setting cluster-id for PF_RING", pfconf->iface);
+ } else if ((pfconf->threads == 1) && (strncmp(pfconf->iface, "dna", 3) == 0)) {
+ SCLogInfo("%s: DNA interface detected, not setting cluster-id for PF_RING",
+ pfconf->iface);
+ } else if (ConfGetChildValueWithDefault(if_root, if_default, "cluster-id", &tmpclusterid) != 1) {
+ SCLogError("Could not get cluster-id from config");
+ } else {
+ if (StringParseInt32(&pfconf->cluster_id, 10, 0, (const char *)tmpclusterid) < 0) {
+ SCLogWarning("Invalid value for "
+ "pfring.cluster-id: '%s'. Resetting to 1.",
+ tmpclusterid);
+ pfconf->cluster_id = 1;
+ }
+ pfconf->flags |= PFRING_CONF_FLAGS_CLUSTER;
+ SCLogDebug("Going to use cluster-id %" PRId32, pfconf->cluster_id);
+ }
+ }
+
+ ConfSetBPFFilter(if_root, if_default, pfconf->iface, &pfconf->bpf_filter);
+
+ if (EngineModeIsIPS()) {
+ FatalError("IPS mode not supported in PF_RING.");
+ }
+
+ if (ConfGet("pfring.cluster-type", &tmpctype) == 1) {
+ SCLogDebug("Going to use command-line provided cluster-type");
+ getctype = 1;
+ } else {
+ if (strncmp(pfconf->iface, "zc", 2) == 0) {
+ SCLogInfo("%s: ZC interface detected, not setting cluster type for PF_RING",
+ pfconf->iface);
+ } else if ((pfconf->threads == 1) && (strncmp(pfconf->iface, "dna", 3) == 0)) {
+ SCLogInfo("%s: DNA interface detected, not setting cluster type for PF_RING",
+ pfconf->iface);
+ } else if (ConfGetChildValueWithDefault(if_root, if_default, "cluster-type", &tmpctype) != 1) {
+ SCLogError("Could not get cluster-type from config");
+ } else {
+ getctype = 1;
+ }
+ }
+
+ if (getctype) {
+ if (strcmp(tmpctype, "cluster_round_robin") == 0) {
+ SCLogInfo("%s: Using round-robin cluster mode for PF_RING."
+ " This mode is not recommended.",
+ pfconf->iface);
+ pfconf->ctype = CLUSTER_ROUND_ROBIN;
+ } else if (strcmp(tmpctype, "cluster_flow") == 0) {
+ SCLogInfo("%s: Using flow cluster mode for PF_RING", pfconf->iface);
+ pfconf->ctype = CLUSTER_FLOW;
+ } else if (strcmp(tmpctype, "cluster_inner_flow") == 0) {
+ SCLogInfo("%s: Using flow cluster mode inner mode for PF_RING", pfconf->iface);
+ pfconf->ctype = CLUSTER_INNER_FLOW;
+ } else if (strcmp(tmpctype, "cluster_inner_flow_2_tuple") == 0) {
+ SCLogInfo("%s: Using flow cluster inner 2 tuple mode for PF_RING", pfconf->iface);
+ pfconf->ctype = CLUSTER_INNER_FLOW_2_TUPLE;
+ } else if (strcmp(tmpctype, "cluster_inner_flow_4_tuple") == 0) {
+ SCLogInfo("%s: Using flow cluster inner 4 tuple mode for PF_RING", pfconf->iface);
+ pfconf->ctype = CLUSTER_INNER_FLOW_4_TUPLE;
+ } else if (strcmp(tmpctype, "cluster_inner_flow_5_tuple") == 0) {
+ SCLogInfo("%s: Using flow cluster inner 5 tuple mode for PF_RING", pfconf->iface);
+ pfconf->ctype = CLUSTER_INNER_FLOW_5_TUPLE;
+ } else {
+ SCLogError("invalid cluster-type %s", tmpctype);
+ SCFree(pfconf);
+ return NULL;
+ }
+ }
+ if (ConfGetChildValueWithDefault(if_root, if_default, "checksum-checks", &tmpctype) == 1) {
+ if (strcmp(tmpctype, "auto") == 0) {
+ pfconf->checksum_mode = CHECKSUM_VALIDATION_AUTO;
+ } else if (ConfValIsTrue(tmpctype)) {
+ pfconf->checksum_mode = CHECKSUM_VALIDATION_ENABLE;
+ } else if (ConfValIsFalse(tmpctype)) {
+ pfconf->checksum_mode = CHECKSUM_VALIDATION_DISABLE;
+ } else if (strcmp(tmpctype, "rx-only") == 0) {
+ pfconf->checksum_mode = CHECKSUM_VALIDATION_RXONLY;
+ } else {
+ SCLogError("%s: Invalid value for checksum-checks", pfconf->iface);
+ }
+ }
+
+ if (ConfGetChildValueBoolWithDefault(if_root, if_default, "bypass", &bool_val) == 1) {
+ if (bool_val) {
+#ifdef HAVE_PF_RING_FLOW_OFFLOAD
+ SCLogConfig("%s: Enabling bypass support in PF_RING (if supported by underlying hw)",
+ pfconf->iface);
+ pfconf->flags |= PFRING_CONF_FLAGS_BYPASS;
+#else
+ SCLogError("Bypass is not supported by this Pfring version, please upgrade");
+ SCFree(pfconf);
+ return NULL;
+#endif
+ }
+ }
+
+ if (LiveGetOffload() == 0) {
+ if (GetIfaceOffloading(iface, 0, 1) == 1) {
+ SCLogWarning("Using PF_RING with offloading activated leads to capture problems");
+ }
+ } else {
+ DisableIfaceOffloading(LiveGetDevice(iface), 0, 1);
+ }
+ return pfconf;
+}
+
+static int PfringConfigGetThreadsCount(void *conf)
+{
+ PfringIfaceConfig *pfp = (PfringIfaceConfig *)conf;
+ return pfp->threads;
+}
+
+static int PfringConfLevel(void)
+{
+ const char *def_dev = NULL;
+ /* 1.0 config should return a string */
+ if (ConfGet("pfring.interface", &def_dev) != 1) {
+ return PFRING_CONF_V2;
+ } else {
+ return PFRING_CONF_V1;
+ }
+}
+
+static int GetDevAndParser(const char **live_dev, ConfigIfaceParserFunc *parser)
+{
+ ConfGet("pfring.live-interface", live_dev);
+
+ /* determine which config type we have */
+ if (PfringConfLevel() > PFRING_CONF_V1) {
+ *parser = ParsePfringConfig;
+ } else {
+ SCLogInfo("Using 1.0 style configuration for pfring");
+ *parser = OldParsePfringConfig;
+ /* In v1: try to get interface name from config */
+ if (*live_dev == NULL) {
+ if (ConfGet("pfring.interface", live_dev) == 1) {
+ SCLogInfo("Using interface %s", *live_dev);
+ LiveRegisterDevice(*live_dev);
+ } else {
+ SCLogInfo("No interface found, problem incoming");
+ *live_dev = NULL;
+ }
+ }
+ }
+
+ return 0;
+}
+#endif
+
+int RunModeIdsPfringAutoFp(void)
+{
+ SCEnter();
+
+/* We include only if pfring is enabled */
+#ifdef HAVE_PFRING
+ int ret;
+ const char *live_dev = NULL;
+ ConfigIfaceParserFunc tparser;
+
+ TimeModeSetLive();
+
+ ret = GetDevAndParser(&live_dev, &tparser);
+ if (ret != 0) {
+ FatalError("Unable to get parser and interface params");
+ }
+
+ ret = RunModeSetLiveCaptureAutoFp(tparser, PfringConfigGetThreadsCount, "ReceivePfring",
+ "DecodePfring", thread_name_autofp, live_dev);
+ if (ret != 0) {
+ FatalError("Runmode start failed");
+ }
+
+ SCLogInfo("RunModeIdsPfringAutoFp initialised");
+#endif /* HAVE_PFRING */
+
+ return 0;
+}
+
+int RunModeIdsPfringSingle(void)
+{
+ SCEnter();
+
+/* We include only if pfring is enabled */
+#ifdef HAVE_PFRING
+ int ret;
+ const char *live_dev = NULL;
+ ConfigIfaceParserFunc tparser;
+
+ TimeModeSetLive();
+
+ ret = GetDevAndParser(&live_dev, &tparser);
+ if (ret != 0) {
+ FatalError("Unable to get parser and interface params");
+ }
+
+ ret = RunModeSetLiveCaptureSingle(tparser,
+ PfringConfigGetThreadsCount,
+ "ReceivePfring",
+ "DecodePfring", thread_name_single,
+ live_dev);
+ if (ret != 0) {
+ FatalError("Runmode start failed");
+ }
+
+ SCLogInfo("RunModeIdsPfringSingle initialised");
+#endif /* HAVE_PFRING */
+
+ return 0;
+}
+
+int RunModeIdsPfringWorkers(void)
+{
+ SCEnter();
+
+/* We include only if pfring is enabled */
+#ifdef HAVE_PFRING
+ int ret;
+ const char *live_dev = NULL;
+ ConfigIfaceParserFunc tparser;
+
+ TimeModeSetLive();
+
+ ret = GetDevAndParser(&live_dev, &tparser);
+ if (ret != 0) {
+ FatalError("Unable to get parser and interface params");
+ }
+
+ ret = RunModeSetLiveCaptureWorkers(tparser, PfringConfigGetThreadsCount, "ReceivePfring",
+ "DecodePfring", thread_name_workers, live_dev);
+ if (ret != 0) {
+ FatalError("Runmode start failed");
+ }
+
+ SCLogInfo("RunModeIdsPfringWorkers initialised");
+#endif /* HAVE_PFRING */
+
+ return 0;
+}