diff options
Diffstat (limited to 'src/runmode-af-xdp.c')
-rw-r--r-- | src/runmode-af-xdp.c | 388 |
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); +} +/** + * @} + */ |