Coverage Report

Created: 2026-11-12 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata8/src/runmode-af-xdp.c
Line
Count
Source
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
66
const char *RunModeAFXDPGetDefaultMode(void)
67
0
{
68
0
    return "workers";
69
0
}
70
71
void RunModeIdsAFXDPRegister(void)
72
20
{
73
20
    RunModeRegisterNewRunMode(RUNMODE_AFXDP_DEV, "single", "Single threaded af-xdp mode",
74
20
            RunModeIdsAFXDPSingle, NULL);
75
20
    RunModeRegisterNewRunMode(RUNMODE_AFXDP_DEV, "workers",
76
20
            "Workers af-xdp mode, each thread does all"
77
20
            " tasks from acquisition to logging",
78
20
            RunModeIdsAFXDPWorkers, NULL);
79
20
}
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
88
static void AFXDPDerefConfig(void *conf)
89
{
90
    AFXDPIfaceConfig *pfp = (AFXDPIfaceConfig *)conf;
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
97
static 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);
109
        SCReturnInt(TM_ECODE_FAILED);
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);
123
        SCReturnInt(TM_ECODE_OK);
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);
129
        SCReturnInt(TM_ECODE_FAILED);
130
    }
131
132
    if (aconf->threads < 0) {
133
        SCLogError("Interface %s has a negative number of threads", aconf->iface);
134
        SCReturnInt(TM_ECODE_FAILED);
135
    }
136
137
    if (aconf->threads > nr_queues) {
138
        SCLogWarning(
139
                "Selected threads greater than configured queues, using: %d thread(s)", nr_queues);
140
        aconf->threads = nr_queues;
141
    }
142
143
    SCReturnInt(TM_ECODE_OK);
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
 */
156
static 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) {
270
        if (SCConfGetChildValueIntWithDefault(
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
277
        if (SCConfGetChildValueIntWithDefault(
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 */
285
        if (SCConfGetChildValueIntWithDefault(
286
                    if_root, if_default, "gro-flush-timeout", &conf_val_int) == 1) {
287
            aconf->gro_flush_timeout = conf_val_int;
288
        }
289
290
        if (SCConfGetChildValueIntWithDefault(
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
297
finalize:
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 {
303
        DisableIfaceOffloading(LiveGetDevice(iface), 0, 1);
304
    }
305
306
    return aconf;
307
}
308
309
static 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
 */
323
int RunModeIdsAFXDPSingle(void)
324
0
{
325
0
    SCEnter();
326
327
#ifdef HAVE_AF_XDP
328
    int ret;
329
    const char *live_dev = NULL;
330
331
    TimeModeSetLive();
332
333
    (void)SCConfGet("af-xdp.live-interface", &live_dev);
334
335
    if (AFXDPQueueProtectionInit() != TM_ECODE_OK) {
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
0
    SCReturnInt(0);
349
0
}
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
 */
357
int RunModeIdsAFXDPWorkers(void)
358
0
{
359
0
    SCEnter();
360
361
#ifdef HAVE_AF_XDP
362
    int ret;
363
    const char *live_dev = NULL;
364
365
    TimeModeSetLive();
366
367
    (void)SCConfGet("af-xdp.live-interface", &live_dev);
368
369
    if (AFXDPQueueProtectionInit() != TM_ECODE_OK) {
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
0
    SCReturnInt(0);
383
0
}
384
/**
385
 * @}
386
 */