suricata
runmode-af-xdp.c
Go to the documentation of this file.
1/* Copyright (C) 2022 Open Information Security Foundation
2 *
3 * You can copy, redistribute or modify this Program under the terms of
4 * the GNU General Public License version 2 as published by the Free
5 * Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * version 2 along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15 * 02110-1301, USA.
16 */
17
18/**
19 * \ingroup afxdppacket
20 *
21 * @{
22 */
23
24/**
25 * \file
26 *
27 * \author Richard McConnell <richard_mcconnell@rapid7.com>
28 *
29 * AF_XDP socket runmode
30 *
31 */
32#define SC_PCAP_DONT_INCLUDE_PCAP_H 1
33#include "suricata-common.h"
34#include "tm-threads.h"
35#include "conf.h"
36#include "runmodes.h"
37#include "runmode-af-xdp.h"
38#include "output.h"
39#include "log-httplog.h"
40#include "detect-engine-mpm.h"
41
42#include "alert-fastlog.h"
43#include "alert-debuglog.h"
44
45#include "flow-bypass.h"
46
47#include "util-conf.h"
48#include "util-debug.h"
49#include "util-time.h"
50#include "util-cpu.h"
51#include "util-affinity.h"
52#include "util-device-private.h"
53#include "util-runmodes.h"
54#include "util-ioctl.h"
55#include "util-ebpf.h"
56#include "util-byte.h"
57
58#include "source-af-xdp.h"
59
60#ifdef HAVE_AF_XDP
61#include <linux/if_xdp.h>
62#include <linux/if_link.h>
63#include <xdp/xsk.h>
64#endif
65
67{
68 return "workers";
69}
70
72{
73 RunModeRegisterNewRunMode(RUNMODE_AFXDP_DEV, "single", "Single threaded af-xdp mode",
76 "Workers af-xdp mode, each thread does all"
77 " tasks from acquisition to logging",
79}
80
81#ifdef HAVE_AF_XDP
82
83#define DEFAULT_BUSY_POLL_TIME 20
84#define DEFAULT_BUSY_POLL_BUDGET 64
85#define DEFAULT_GRO_FLUSH_TIMEOUT 2000000
86#define DEFAULT_NAPI_HARD_IRQS 2
87
88static void AFXDPDerefConfig(void *conf)
89{
91 /* Pcap config is used only once but cost of this low. */
92 if (SC_ATOMIC_SUB(pfp->ref, 1) <= 1) {
93 SCFree(pfp);
94 }
95}
96
97static TmEcode ConfigSetThreads(AFXDPIfaceConfig *aconf, const char *entry_str)
98{
99 SCEnter();
100 const char *active_runmode = RunmodeGetActive();
101
102 if (active_runmode && !strcmp("single", active_runmode)) {
103 aconf->threads = 1;
104 SCReturnInt(0);
105 }
106
107 if (entry_str == NULL) {
108 SCLogError("Number of threads for interface \"%s\" not specified", aconf->iface);
110 }
111
112 const int nr_queues = GetIfaceRSSQueuesNum(aconf->iface);
113
114 if (strcmp(entry_str, "auto") == 0) {
115
116 const int nr_cores = (int)UtilCpuGetNumProcessorsOnline();
117
118 /* Threads limited to MIN(cores vs queues) */
119 aconf->threads = (nr_cores <= nr_queues) ? nr_cores : nr_queues;
120 const char *sys_type = nr_cores <= nr_queues ? "cores" : "queues";
121
122 SCLogPerf("%u %s, so using %u threads", aconf->threads, sys_type, aconf->threads);
124 }
125
126 if (StringParseInt32(&aconf->threads, 10, 0, entry_str) < 0) {
127 SCLogError("Threads entry for interface %s contain non-numerical characters - \"%s\"",
128 aconf->iface, entry_str);
130 }
131
132 if (aconf->threads < 0) {
133 SCLogError("Interface %s has a negative number of threads", aconf->iface);
135 }
136
137 if (aconf->threads > nr_queues) {
139 "Selected threads greater than configured queues, using: %d thread(s)", nr_queues);
140 aconf->threads = nr_queues;
141 }
142
144}
145
146/**
147 * \brief extract information from config file
148 *
149 * The returned structure will be freed by the thread init function.
150 * This is thus necessary to copy the structure before giving it
151 * to thread or to reparse the file for each thread (and thus have
152 * new structure.
153 *
154 * \return a AFXDPIfaceConfig corresponding to the interface name
155 */
156static void *ParseAFXDPConfig(const char *iface)
157{
158 const char *confstr = NULL;
159 SCConfNode *if_root;
160 SCConfNode *if_default = NULL;
161 SCConfNode *af_xdp_node = NULL;
162 int conf_val = 0;
163 intmax_t conf_val_int = 0;
164 int boolval = 0;
165
166 if (iface == NULL) {
167 return NULL;
168 }
169
170 AFXDPIfaceConfig *aconf = SCCalloc(1, sizeof(*aconf));
171 if (unlikely(aconf == NULL)) {
172 return NULL;
173 }
174
175 /* default/basic config setup */
176 strlcpy(aconf->iface, iface, sizeof(aconf->iface));
177 aconf->DerefFunc = AFXDPDerefConfig;
178 aconf->threads = 1;
179 aconf->promisc = 1;
180 aconf->enable_busy_poll = true;
181 aconf->busy_poll_time = DEFAULT_BUSY_POLL_TIME;
182 aconf->busy_poll_budget = DEFAULT_BUSY_POLL_BUDGET;
183 aconf->mode = XDP_FLAGS_UPDATE_IF_NOEXIST;
184 aconf->gro_flush_timeout = DEFAULT_GRO_FLUSH_TIMEOUT;
185 aconf->napi_defer_hard_irqs = DEFAULT_NAPI_HARD_IRQS;
186 aconf->mem_alignment = XSK_UMEM__DEFAULT_FLAGS;
187
188 /* Find initial node */
189 af_xdp_node = SCConfGetNode("af-xdp");
190 if (af_xdp_node == NULL) {
191 SCLogInfo("unable to find af-xdp config using default values");
192 goto finalize;
193 }
194
195 if_root = ConfFindDeviceConfig(af_xdp_node, iface);
196 if_default = ConfFindDeviceConfig(af_xdp_node, "default");
197
198 if (if_root == NULL && if_default == NULL) {
199 SCLogInfo("unable to find af-xdp config for "
200 "interface \"%s\" or \"default\", using default values",
201 iface);
202 goto finalize;
203 }
204
205 /* If there is no setting for current interface use default one as main iface */
206 if (if_root == NULL) {
207 if_root = if_default;
208 if_default = NULL;
209 }
210
211 /* Threading */
212 confstr = "auto";
213 (void)SCConfGetChildValueWithDefault(if_root, if_default, "threads", &confstr);
214 if (ConfigSetThreads(aconf, confstr) != TM_ECODE_OK) {
215 aconf->DerefFunc(aconf);
216 return NULL;
217 }
218
219 SC_ATOMIC_RESET(aconf->ref);
220 (void)SC_ATOMIC_ADD(aconf->ref, aconf->threads);
221
222 /* Promisc Mode */
223 (void)SCConfGetChildValueBoolWithDefault(if_root, if_default, "disable-promisc", &boolval);
224 if (boolval) {
225 SCLogConfig("Disabling promiscuous mode on iface %s", aconf->iface);
226 aconf->promisc = 0;
227 }
228
229#ifdef HAVE_AF_XDP
230 /* AF_XDP socket mode options */
231 if (SCConfGetChildValueWithDefault(if_root, if_default, "force-xdp-mode", &confstr) == 1) {
232 if (strncasecmp(confstr, "drv", 3) == 0) {
233 aconf->mode |= XDP_FLAGS_DRV_MODE;
234 } else if (strncasecmp(confstr, "skb", 3) == 0) {
235 aconf->mode |= XDP_FLAGS_SKB_MODE;
236 } else if (strncasecmp(confstr, "none", 4) == 0) {
237 } else {
238 SCLogWarning("Incorrect af-xdp xdp-mode setting, default (none) shall be applied");
239 }
240 }
241
242 /* copy and zerocopy binding options */
243 if (SCConfGetChildValueWithDefault(if_root, if_default, "force-bind-mode", &confstr) == 1) {
244 if (strncasecmp(confstr, "zero", 4) == 0) {
245 aconf->bind_flags |= XDP_ZEROCOPY;
246 } else if (strncasecmp(confstr, "copy", 4) == 0) {
247 aconf->bind_flags |= XDP_COPY;
248 } else if (strncasecmp(confstr, "none", 4) == 0) {
249 } else {
250 SCLogWarning("Incorrect af-xdp copy-mode setting, default (none) shall be applied");
251 }
252 }
253
254 /* memory alignment mode selection */
255 if (SCConfGetChildValueWithDefault(if_root, if_default, "mem-unaligned", &confstr) == 1) {
256 if (strncasecmp(confstr, "yes", 3) == 0) {
257 aconf->mem_alignment = XDP_UMEM_UNALIGNED_CHUNK_FLAG;
258 }
259 }
260
261 /* Busy polling options */
262 if (SCConfGetChildValueBoolWithDefault(if_root, if_default, "enable-busy-poll", &conf_val) ==
263 1) {
264 if (conf_val == 0) {
265 aconf->enable_busy_poll = false;
266 }
267 }
268
269 if (aconf->enable_busy_poll) {
271 if_root, if_default, "busy-poll-time", &conf_val_int) == 1) {
272 if (conf_val_int) {
273 aconf->busy_poll_time = conf_val_int;
274 }
275 }
276
278 if_root, if_default, "busy-poll-budget", &conf_val_int) == 1) {
279 if (conf_val_int) {
280 aconf->busy_poll_budget = conf_val_int;
281 }
282 }
283
284 /* 0 value is valid for these Linux tunable's */
286 if_root, if_default, "gro-flush-timeout", &conf_val_int) == 1) {
287 aconf->gro_flush_timeout = conf_val_int;
288 }
289
291 if_root, if_default, "napi-defer-hard-irq", &conf_val_int) == 1) {
292 aconf->napi_defer_hard_irqs = conf_val_int;
293 }
294 }
295#endif
296
297finalize:
298 if (LiveGetOffload() == 0) {
299 if (GetIfaceOffloading(iface, 0, 1) == 1) {
300 SCLogWarning("Using AF_XDP with offloading activated leads to capture problems");
301 }
302 } else {
304 }
305
306 return aconf;
307}
308
309static int AFXDPConfigGetThreadsCount(void *conf)
310{
311 if (conf == NULL)
312 FatalError("Configuration file is NULL");
313
314 AFXDPIfaceConfig *afxdp_conf = (AFXDPIfaceConfig *)conf;
315 return afxdp_conf->threads;
316}
317
318#endif /* HAVE_AF_XDP */
319
320/**
321 * \brief Single thread version of the AF_XDP processing.
322 */
324{
325 SCEnter();
326
327#ifdef HAVE_AF_XDP
328 int ret;
329 const char *live_dev = NULL;
330
332
333 (void)SCConfGet("af-xdp.live-interface", &live_dev);
334
336 FatalError("Unable to init AF_XDP queue protection.");
337 }
338
339 ret = RunModeSetLiveCaptureSingle(ParseAFXDPConfig, AFXDPConfigGetThreadsCount, "ReceiveAFXDP",
340 "DecodeAFXDP", thread_name_single, live_dev);
341 if (ret != 0) {
342 FatalError("Unable to start runmode");
343 }
344
345 SCLogDebug("RunModeIdsAFXDPSingle initialised");
346
347#endif /* HAVE_AF_XDP */
348 SCReturnInt(0);
349}
350
351/**
352 * \brief Workers version of the AF_XDP processing.
353 *
354 * Start N threads with each thread doing all the work.
355 *
356 */
358{
359 SCEnter();
360
361#ifdef HAVE_AF_XDP
362 int ret;
363 const char *live_dev = NULL;
364
366
367 (void)SCConfGet("af-xdp.live-interface", &live_dev);
368
370 FatalError("Unable to init AF_XDP queue protection.");
371 }
372
373 ret = RunModeSetLiveCaptureWorkers(ParseAFXDPConfig, AFXDPConfigGetThreadsCount, "ReceiveAFXDP",
374 "DecodeAFXDP", thread_name_workers, live_dev);
375 if (ret != 0) {
376 FatalError("Unable to start runmode");
377 }
378
379 SCLogDebug("RunModeIdsAFXDPWorkers initialised");
380
381#endif /* HAVE_AF_XDP */
382 SCReturnInt(0);
383}
384/**
385 * @}
386 */
SCConfNode * SCConfGetNode(const char *name)
Get a SCConfNode by name.
Definition conf.c:181
int SCConfGetChildValueWithDefault(const SCConfNode *base, const SCConfNode *dflt, const char *name, const char **vptr)
Definition conf.c:393
int SCConfGet(const char *name, const char **vptr)
Retrieve the value of a configuration node.
Definition conf.c:350
int SCConfGetChildValueBoolWithDefault(const SCConfNode *base, const SCConfNode *dflt, const char *name, int *val)
Definition conf.c:528
int SCConfGetChildValueIntWithDefault(const SCConfNode *base, const SCConfNode *dflt, const char *name, intmax_t *val)
Definition conf.c:476
int RunModeIdsAFXDPWorkers(void)
Workers version of the AF_XDP processing.
void RunModeIdsAFXDPRegister(void)
int RunModeIdsAFXDPSingle(void)
Single thread version of the AF_XDP processing.
const char * RunModeAFXDPGetDefaultMode(void)
char * RunmodeGetActive(void)
Definition runmodes.c:199
const char * thread_name_single
Definition runmodes.c:67
void RunModeRegisterNewRunMode(enum SCRunModes runmode, const char *name, const char *description, int(*RunModeFunc)(void), int(*RunModeIsIPSEnabled)(void))
Registers a new runmode.
Definition runmodes.c:486
const char * thread_name_workers
Definition runmodes.c:68
@ RUNMODE_AFXDP_DEV
Definition runmodes.h:37
TmEcode AFXDPQueueProtectionInit(void)
void(* DerefFunc)(void *)
uint32_t busy_poll_budget
uint32_t busy_poll_time
uint32_t gro_flush_timeout
uint32_t napi_defer_hard_irqs
char iface[AFXDP_IFACE_NAME_LENGTH]
size_t strlcpy(char *dst, const char *src, size_t siz)
@ TM_ECODE_FAILED
@ TM_ECODE_OK
#define SC_ATOMIC_ADD(name, val)
add a value to our atomic variable
#define SC_ATOMIC_RESET(name)
wrapper for reinitializing an atomic variable.
#define SC_ATOMIC_SUB(name, val)
sub a value from our atomic variable
int StringParseInt32(int32_t *res, int base, size_t len, const char *str)
Definition util-byte.c:622
SCConfNode * ConfFindDeviceConfig(SCConfNode *node, const char *iface)
Find the configuration node for a specific device.
Definition util-conf.c:121
uint16_t UtilCpuGetNumProcessorsOnline(void)
Get the number of cpus online in the system.
Definition util-cpu.c:108
#define SCEnter(...)
Definition util-debug.h:277
#define FatalError(...)
Definition util-debug.h:510
#define SCLogPerf(...)
Definition util-debug.h:234
#define SCLogDebug(...)
Definition util-debug.h:275
#define SCReturnInt(x)
Definition util-debug.h:281
#define SCLogWarning(...)
Macro used to log WARNING messages.
Definition util-debug.h:255
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition util-debug.h:225
#define SCLogError(...)
Macro used to log ERROR messages.
Definition util-debug.h:267
#define SCLogConfig(...)
Definition util-debug.h:229
LiveDevice * LiveGetDevice(const char *name)
Get a pointer to the device at idx.
int LiveGetOffload(void)
Definition util-device.c:87
int GetIfaceRSSQueuesNum(const char *dev)
Definition util-ioctl.c:713
int GetIfaceOffloading(const char *dev, int csum, int other)
output offloading status of the link
Definition util-ioctl.c:670
int DisableIfaceOffloading(LiveDevice *dev, int csum, int other)
Definition util-ioctl.c:683
#define SCFree(p)
Definition util-mem.h:61
#define SCCalloc(nm, sz)
Definition util-mem.h:53
#define unlikely(expr)
int RunModeSetLiveCaptureSingle(ConfigIfaceParserFunc ConfigParser, ConfigIfaceThreadsCountFunc ModThreadsCount, const char *recv_mod_name, const char *decode_mod_name, const char *thread_name, const char *live_dev)
int RunModeSetLiveCaptureWorkers(ConfigIfaceParserFunc ConfigParser, ConfigIfaceThreadsCountFunc ModThreadsCount, const char *recv_mod_name, const char *decode_mod_name, const char *thread_name, const char *live_dev)
void TimeModeSetLive(void)
Definition util-time.c:99