summaryrefslogtreecommitdiffstats
path: root/src/runmode-af-xdp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/runmode-af-xdp.c')
-rw-r--r--src/runmode-af-xdp.c388
1 files changed, 388 insertions, 0 deletions
diff --git a/src/runmode-af-xdp.c b/src/runmode-af-xdp.c
new file mode 100644
index 0000000..33a47d1
--- /dev/null
+++ b/src/runmode-af-xdp.c
@@ -0,0 +1,388 @@
+/* Copyright (C) 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.
+ */
+
+/**
+ * \ingroup afxdppacket
+ *
+ * @{
+ */
+
+/**
+ * \file
+ *
+ * \author Richard McConnell <richard_mcconnell@rapid7.com>
+ *
+ * AF_XDP socket runmode
+ *
+ */
+#define PCAP_DONT_INCLUDE_PCAP_BPF_H 1
+#define SC_PCAP_DONT_INCLUDE_PCAP_H 1
+#include "suricata-common.h"
+#include "tm-threads.h"
+#include "conf.h"
+#include "runmodes.h"
+#include "runmode-af-xdp.h"
+#include "output.h"
+#include "log-httplog.h"
+#include "detect-engine-mpm.h"
+
+#include "alert-fastlog.h"
+#include "alert-debuglog.h"
+
+#include "flow-bypass.h"
+
+#include "util-conf.h"
+#include "util-debug.h"
+#include "util-time.h"
+#include "util-cpu.h"
+#include "util-affinity.h"
+#include "util-device.h"
+#include "util-runmodes.h"
+#include "util-ioctl.h"
+#include "util-ebpf.h"
+#include "util-byte.h"
+
+#include "source-af-xdp.h"
+
+#ifdef HAVE_AF_XDP
+#include <linux/if_xdp.h>
+#include <linux/if_link.h>
+#include <xdp/xsk.h>
+#endif
+
+const char *RunModeAFXDPGetDefaultMode(void)
+{
+ return "workers";
+}
+
+void RunModeIdsAFXDPRegister(void)
+{
+ RunModeRegisterNewRunMode(RUNMODE_AFXDP_DEV, "single", "Single threaded af-xdp mode",
+ RunModeIdsAFXDPSingle, NULL);
+ RunModeRegisterNewRunMode(RUNMODE_AFXDP_DEV, "workers",
+ "Workers af-xdp mode, each thread does all"
+ " tasks from acquisition to logging",
+ RunModeIdsAFXDPWorkers, NULL);
+
+ return;
+}
+
+#ifdef HAVE_AF_XDP
+
+#define DEFAULT_BUSY_POLL_TIME 20
+#define DEFAULT_BUSY_POLL_BUDGET 64
+#define DEFAULT_GRO_FLUSH_TIMEOUT 2000000
+#define DEFAULT_NAPI_HARD_IRQS 2
+
+static void AFXDPDerefConfig(void *conf)
+{
+ AFXDPIfaceConfig *pfp = (AFXDPIfaceConfig *)conf;
+ /* Pcap config is used only once but cost of this low. */
+ if (SC_ATOMIC_SUB(pfp->ref, 1) <= 1) {
+ SCFree(pfp);
+ }
+}
+
+static TmEcode ConfigSetThreads(AFXDPIfaceConfig *aconf, const char *entry_str)
+{
+ SCEnter();
+ const char *active_runmode = RunmodeGetActive();
+
+ if (active_runmode && !strcmp("single", active_runmode)) {
+ aconf->threads = 1;
+ SCReturnInt(0);
+ }
+
+ if (entry_str == NULL) {
+ SCLogError("Number of threads for interface \"%s\" not specified", aconf->iface);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ const int nr_queues = GetIfaceRSSQueuesNum(aconf->iface);
+
+ if (strcmp(entry_str, "auto") == 0) {
+
+ const int nr_cores = (int)UtilCpuGetNumProcessorsOnline();
+
+ /* Threads limited to MIN(cores vs queues) */
+ aconf->threads = (nr_cores <= nr_queues) ? nr_cores : nr_queues;
+ const char *sys_type = nr_cores <= nr_queues ? "cores" : "queues";
+
+ SCLogPerf("%u %s, so using %u threads", aconf->threads, sys_type, aconf->threads);
+ SCReturnInt(TM_ECODE_OK);
+ }
+
+ if (StringParseInt32(&aconf->threads, 10, 0, entry_str) < 0) {
+ SCLogError("Threads entry for interface %s contain non-numerical characters - \"%s\"",
+ aconf->iface, entry_str);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ if (aconf->threads < 0) {
+ SCLogError("Interface %s has a negative number of threads", aconf->iface);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ if (aconf->threads > nr_queues) {
+ SCLogWarning(
+ "Selected threads greater than configured queues, using: %d thread(s)", nr_queues);
+ aconf->threads = nr_queues;
+ }
+
+ SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief extract information from config file
+ *
+ * The returned structure will be freed by the thread init function.
+ * This is thus necessary to copy the structure before giving it
+ * to thread or to reparse the file for each thread (and thus have
+ * new structure.
+ *
+ * \return a AFXDPIfaceConfig corresponding to the interface name
+ */
+static void *ParseAFXDPConfig(const char *iface)
+{
+ const char *confstr = NULL;
+ ConfNode *if_root;
+ ConfNode *if_default = NULL;
+ ConfNode *af_xdp_node = NULL;
+ int conf_val = 0;
+ intmax_t conf_val_int = 0;
+ bool boolval = false;
+
+ if (iface == NULL) {
+ return NULL;
+ }
+
+ AFXDPIfaceConfig *aconf = SCCalloc(1, sizeof(*aconf));
+ if (unlikely(aconf == NULL)) {
+ return NULL;
+ }
+
+ /* default/basic config setup */
+ strlcpy(aconf->iface, iface, sizeof(aconf->iface));
+ aconf->DerefFunc = AFXDPDerefConfig;
+ aconf->threads = 1;
+ aconf->promisc = 1;
+ aconf->enable_busy_poll = true;
+ aconf->busy_poll_time = DEFAULT_BUSY_POLL_TIME;
+ aconf->busy_poll_budget = DEFAULT_BUSY_POLL_BUDGET;
+ aconf->mode = XDP_FLAGS_UPDATE_IF_NOEXIST;
+ aconf->gro_flush_timeout = DEFAULT_GRO_FLUSH_TIMEOUT;
+ aconf->napi_defer_hard_irqs = DEFAULT_NAPI_HARD_IRQS;
+ aconf->mem_alignment = XSK_UMEM__DEFAULT_FLAGS;
+
+ /* Find initial node */
+ af_xdp_node = ConfGetNode("af-xdp");
+ if (af_xdp_node == NULL) {
+ SCLogInfo("unable to find af-xdp config using default values");
+ goto finalize;
+ }
+
+ if_root = ConfFindDeviceConfig(af_xdp_node, iface);
+ if_default = ConfFindDeviceConfig(af_xdp_node, "default");
+
+ if (if_root == NULL && if_default == NULL) {
+ SCLogInfo("unable to find af-xdp config for "
+ "interface \"%s\" or \"default\", using default values",
+ iface);
+ goto finalize;
+ }
+
+ /* 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;
+ }
+
+ /* Threading */
+ confstr = "auto";
+ (void)ConfGetChildValueWithDefault(if_root, if_default, "threads", &confstr);
+ if (ConfigSetThreads(aconf, confstr) != TM_ECODE_OK) {
+ aconf->DerefFunc(aconf);
+ return NULL;
+ }
+
+ SC_ATOMIC_RESET(aconf->ref);
+ (void)SC_ATOMIC_ADD(aconf->ref, aconf->threads);
+
+ /* Promisc Mode */
+ (void)ConfGetChildValueBoolWithDefault(if_root, if_default, "disable-promisc", (int *)&boolval);
+ if (boolval) {
+ SCLogConfig("Disabling promiscuous mode on iface %s", aconf->iface);
+ aconf->promisc = 0;
+ }
+
+#ifdef HAVE_AF_XDP
+ /* AF_XDP socket mode options */
+ if (ConfGetChildValueWithDefault(if_root, if_default, "force-xdp-mode", &confstr) == 1) {
+ if (strncasecmp(confstr, "drv", 3) == 0) {
+ aconf->mode |= XDP_FLAGS_DRV_MODE;
+ } else if (strncasecmp(confstr, "skb", 3) == 0) {
+ aconf->mode |= XDP_FLAGS_SKB_MODE;
+ } else if (strncasecmp(confstr, "none", 4) == 0) {
+ } else {
+ SCLogWarning("Incorrect af-xdp xdp-mode setting, default (none) shall be applied");
+ }
+ }
+
+ /* copy and zerocopy binding options */
+ if (ConfGetChildValueWithDefault(if_root, if_default, "force-bind-mode", &confstr) == 1) {
+ if (strncasecmp(confstr, "zero", 4) == 0) {
+ aconf->bind_flags |= XDP_ZEROCOPY;
+ } else if (strncasecmp(confstr, "copy", 4) == 0) {
+ aconf->bind_flags |= XDP_COPY;
+ } else if (strncasecmp(confstr, "none", 4) == 0) {
+ } else {
+ SCLogWarning("Incorrect af-xdp copy-mode setting, default (none) shall be applied");
+ }
+ }
+
+ /* memory alignment mode selection */
+ if (ConfGetChildValueWithDefault(if_root, if_default, "mem-unaligned", &confstr) == 1) {
+ if (strncasecmp(confstr, "yes", 3) == 0) {
+ aconf->mem_alignment = XDP_UMEM_UNALIGNED_CHUNK_FLAG;
+ }
+ }
+
+ /* Busy polling options */
+ if (ConfGetChildValueBoolWithDefault(if_root, if_default, "enable-busy-poll", &conf_val) == 1) {
+ if (conf_val == 0) {
+ aconf->enable_busy_poll = false;
+ }
+ }
+
+ if (aconf->enable_busy_poll) {
+ if (ConfGetChildValueIntWithDefault(if_root, if_default, "busy-poll-time", &conf_val_int) ==
+ 1) {
+ if (conf_val_int) {
+ aconf->busy_poll_time = conf_val_int;
+ }
+ }
+
+ if (ConfGetChildValueIntWithDefault(
+ if_root, if_default, "busy-poll-budget", &conf_val_int) == 1) {
+ if (conf_val_int) {
+ aconf->busy_poll_budget = conf_val_int;
+ }
+ }
+
+ /* 0 value is valid for these Linux tunable's */
+ if (ConfGetChildValueIntWithDefault(
+ if_root, if_default, "gro-flush-timeout", &conf_val_int) == 1) {
+ aconf->gro_flush_timeout = conf_val_int;
+ }
+
+ if (ConfGetChildValueIntWithDefault(
+ if_root, if_default, "napi-defer-hard-irq", &conf_val_int) == 1) {
+ aconf->napi_defer_hard_irqs = conf_val_int;
+ }
+ }
+#endif
+
+finalize:
+ if (LiveGetOffload() == 0) {
+ if (GetIfaceOffloading(iface, 0, 1) == 1) {
+ SCLogWarning("Using AF_XDP with offloading activated leads to capture problems");
+ }
+ } else {
+ DisableIfaceOffloading(LiveGetDevice(iface), 0, 1);
+ }
+
+ return aconf;
+}
+
+static int AFXDPConfigGetThreadsCount(void *conf)
+{
+ if (conf == NULL)
+ FatalError("Configuration file is NULL");
+
+ AFXDPIfaceConfig *afxdp_conf = (AFXDPIfaceConfig *)conf;
+ return afxdp_conf->threads;
+}
+
+#endif /* HAVE_AF_XDP */
+
+/**
+ * \brief Single thread version of the AF_XDP processing.
+ */
+int RunModeIdsAFXDPSingle(void)
+{
+ SCEnter();
+
+#ifdef HAVE_AF_XDP
+ int ret;
+ const char *live_dev = NULL;
+
+ TimeModeSetLive();
+
+ (void)ConfGet("af-xdp.live-interface", &live_dev);
+
+ if (AFXDPQueueProtectionInit() != TM_ECODE_OK) {
+ FatalError("Unable to init AF_XDP queue protection.");
+ }
+
+ ret = RunModeSetLiveCaptureSingle(ParseAFXDPConfig, AFXDPConfigGetThreadsCount, "ReceiveAFXDP",
+ "DecodeAFXDP", thread_name_single, live_dev);
+ if (ret != 0) {
+ FatalError("Unable to start runmode");
+ }
+
+ SCLogDebug("RunModeIdsAFXDPSingle initialised");
+
+#endif /* HAVE_AF_XDP */
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Workers version of the AF_XDP processing.
+ *
+ * Start N threads with each thread doing all the work.
+ *
+ */
+int RunModeIdsAFXDPWorkers(void)
+{
+ SCEnter();
+
+#ifdef HAVE_AF_XDP
+ int ret;
+ const char *live_dev = NULL;
+
+ TimeModeSetLive();
+
+ (void)ConfGet("af-xdp.live-interface", &live_dev);
+
+ if (AFXDPQueueProtectionInit() != TM_ECODE_OK) {
+ FatalError("Unable to init AF_XDP queue protection.");
+ }
+
+ ret = RunModeSetLiveCaptureWorkers(ParseAFXDPConfig, AFXDPConfigGetThreadsCount, "ReceiveAFXDP",
+ "DecodeAFXDP", thread_name_workers, live_dev);
+ if (ret != 0) {
+ FatalError("Unable to start runmode");
+ }
+
+ SCLogDebug("RunModeIdsAFXDPWorkers initialised");
+
+#endif /* HAVE_AF_XDP */
+ SCReturnInt(0);
+}
+/**
+ * @}
+ */