mirror of https://github.com/OISF/suricata
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
389 lines
11 KiB
C
389 lines
11 KiB
C
/* 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);
|
|
}
|
|
/**
|
|
* @}
|
|
*/
|