Coverage Report

Created: 2026-02-14 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/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 PCAP_DONT_INCLUDE_PCAP_BPF_H 1
33
#define SC_PCAP_DONT_INCLUDE_PCAP_H  1
34
#include "suricata-common.h"
35
#include "tm-threads.h"
36
#include "conf.h"
37
#include "runmodes.h"
38
#include "runmode-af-xdp.h"
39
#include "output.h"
40
#include "log-httplog.h"
41
#include "detect-engine-mpm.h"
42
43
#include "alert-fastlog.h"
44
#include "alert-debuglog.h"
45
46
#include "flow-bypass.h"
47
48
#include "util-conf.h"
49
#include "util-debug.h"
50
#include "util-time.h"
51
#include "util-cpu.h"
52
#include "util-affinity.h"
53
#include "util-device.h"
54
#include "util-runmodes.h"
55
#include "util-ioctl.h"
56
#include "util-ebpf.h"
57
#include "util-byte.h"
58
59
#include "source-af-xdp.h"
60
61
#ifdef HAVE_AF_XDP
62
#include <linux/if_xdp.h>
63
#include <linux/if_link.h>
64
#include <xdp/xsk.h>
65
#endif
66
67
const char *RunModeAFXDPGetDefaultMode(void)
68
0
{
69
0
    return "workers";
70
0
}
71
72
void RunModeIdsAFXDPRegister(void)
73
37
{
74
37
    RunModeRegisterNewRunMode(RUNMODE_AFXDP_DEV, "single", "Single threaded af-xdp mode",
75
37
            RunModeIdsAFXDPSingle, NULL);
76
37
    RunModeRegisterNewRunMode(RUNMODE_AFXDP_DEV, "workers",
77
37
            "Workers af-xdp mode, each thread does all"
78
37
            " tasks from acquisition to logging",
79
37
            RunModeIdsAFXDPWorkers, NULL);
80
81
37
    return;
82
37
}
83
84
#ifdef HAVE_AF_XDP
85
86
#define DEFAULT_BUSY_POLL_TIME    20
87
#define DEFAULT_BUSY_POLL_BUDGET  64
88
#define DEFAULT_GRO_FLUSH_TIMEOUT 2000000
89
#define DEFAULT_NAPI_HARD_IRQS    2
90
91
static void AFXDPDerefConfig(void *conf)
92
{
93
    AFXDPIfaceConfig *pfp = (AFXDPIfaceConfig *)conf;
94
    /* Pcap config is used only once but cost of this low. */
95
    if (SC_ATOMIC_SUB(pfp->ref, 1) <= 1) {
96
        SCFree(pfp);
97
    }
98
}
99
100
static TmEcode ConfigSetThreads(AFXDPIfaceConfig *aconf, const char *entry_str)
101
{
102
    SCEnter();
103
    const char *active_runmode = RunmodeGetActive();
104
105
    if (active_runmode && !strcmp("single", active_runmode)) {
106
        aconf->threads = 1;
107
        SCReturnInt(0);
108
    }
109
110
    if (entry_str == NULL) {
111
        SCLogError("Number of threads for interface \"%s\" not specified", aconf->iface);
112
        SCReturnInt(TM_ECODE_FAILED);
113
    }
114
115
    const int nr_queues = GetIfaceRSSQueuesNum(aconf->iface);
116
117
    if (strcmp(entry_str, "auto") == 0) {
118
119
        const int nr_cores = (int)UtilCpuGetNumProcessorsOnline();
120
121
        /* Threads limited to MIN(cores vs queues) */
122
        aconf->threads = (nr_cores <= nr_queues) ? nr_cores : nr_queues;
123
        const char *sys_type = nr_cores <= nr_queues ? "cores" : "queues";
124
125
        SCLogPerf("%u %s, so using %u threads", aconf->threads, sys_type, aconf->threads);
126
        SCReturnInt(TM_ECODE_OK);
127
    }
128
129
    if (StringParseInt32(&aconf->threads, 10, 0, entry_str) < 0) {
130
        SCLogError("Threads entry for interface %s contain non-numerical characters - \"%s\"",
131
                aconf->iface, entry_str);
132
        SCReturnInt(TM_ECODE_FAILED);
133
    }
134
135
    if (aconf->threads < 0) {
136
        SCLogError("Interface %s has a negative number of threads", aconf->iface);
137
        SCReturnInt(TM_ECODE_FAILED);
138
    }
139
140
    if (aconf->threads > nr_queues) {
141
        SCLogWarning(
142
                "Selected threads greater than configured queues, using: %d thread(s)", nr_queues);
143
        aconf->threads = nr_queues;
144
    }
145
146
    SCReturnInt(TM_ECODE_OK);
147
}
148
149
/**
150
 * \brief extract information from config file
151
 *
152
 * The returned structure will be freed by the thread init function.
153
 * This is thus necessary to copy the structure before giving it
154
 * to thread or to reparse the file for each thread (and thus have
155
 * new structure.
156
 *
157
 * \return a AFXDPIfaceConfig corresponding to the interface name
158
 */
159
static void *ParseAFXDPConfig(const char *iface)
160
{
161
    const char *confstr = NULL;
162
    ConfNode *if_root;
163
    ConfNode *if_default = NULL;
164
    ConfNode *af_xdp_node = NULL;
165
    int conf_val = 0;
166
    intmax_t conf_val_int = 0;
167
    bool boolval = false;
168
169
    if (iface == NULL) {
170
        return NULL;
171
    }
172
173
    AFXDPIfaceConfig *aconf = SCCalloc(1, sizeof(*aconf));
174
    if (unlikely(aconf == NULL)) {
175
        return NULL;
176
    }
177
178
    /* default/basic config setup */
179
    strlcpy(aconf->iface, iface, sizeof(aconf->iface));
180
    aconf->DerefFunc = AFXDPDerefConfig;
181
    aconf->threads = 1;
182
    aconf->promisc = 1;
183
    aconf->enable_busy_poll = true;
184
    aconf->busy_poll_time = DEFAULT_BUSY_POLL_TIME;
185
    aconf->busy_poll_budget = DEFAULT_BUSY_POLL_BUDGET;
186
    aconf->mode = XDP_FLAGS_UPDATE_IF_NOEXIST;
187
    aconf->gro_flush_timeout = DEFAULT_GRO_FLUSH_TIMEOUT;
188
    aconf->napi_defer_hard_irqs = DEFAULT_NAPI_HARD_IRQS;
189
    aconf->mem_alignment = XSK_UMEM__DEFAULT_FLAGS;
190
191
    /* Find initial node */
192
    af_xdp_node = ConfGetNode("af-xdp");
193
    if (af_xdp_node == NULL) {
194
        SCLogInfo("unable to find af-xdp config using default values");
195
        goto finalize;
196
    }
197
198
    if_root = ConfFindDeviceConfig(af_xdp_node, iface);
199
    if_default = ConfFindDeviceConfig(af_xdp_node, "default");
200
201
    if (if_root == NULL && if_default == NULL) {
202
        SCLogInfo("unable to find af-xdp config for "
203
                  "interface \"%s\" or \"default\", using default values",
204
                iface);
205
        goto finalize;
206
    }
207
208
    /* If there is no setting for current interface use default one as main iface */
209
    if (if_root == NULL) {
210
        if_root = if_default;
211
        if_default = NULL;
212
    }
213
214
    /* Threading */
215
    confstr = "auto";
216
    (void)ConfGetChildValueWithDefault(if_root, if_default, "threads", &confstr);
217
    if (ConfigSetThreads(aconf, confstr) != TM_ECODE_OK) {
218
        aconf->DerefFunc(aconf);
219
        return NULL;
220
    }
221
222
    SC_ATOMIC_RESET(aconf->ref);
223
    (void)SC_ATOMIC_ADD(aconf->ref, aconf->threads);
224
225
    /* Promisc Mode */
226
    (void)ConfGetChildValueBoolWithDefault(if_root, if_default, "disable-promisc", (int *)&boolval);
227
    if (boolval) {
228
        SCLogConfig("Disabling promiscuous mode on iface %s", aconf->iface);
229
        aconf->promisc = 0;
230
    }
231
232
#ifdef HAVE_AF_XDP
233
    /* AF_XDP socket mode options */
234
    if (ConfGetChildValueWithDefault(if_root, if_default, "force-xdp-mode", &confstr) == 1) {
235
        if (strncasecmp(confstr, "drv", 3) == 0) {
236
            aconf->mode |= XDP_FLAGS_DRV_MODE;
237
        } else if (strncasecmp(confstr, "skb", 3) == 0) {
238
            aconf->mode |= XDP_FLAGS_SKB_MODE;
239
        } else if (strncasecmp(confstr, "none", 4) == 0) {
240
        } else {
241
            SCLogWarning("Incorrect af-xdp xdp-mode setting, default (none) shall be applied");
242
        }
243
    }
244
245
    /* copy and zerocopy binding options */
246
    if (ConfGetChildValueWithDefault(if_root, if_default, "force-bind-mode", &confstr) == 1) {
247
        if (strncasecmp(confstr, "zero", 4) == 0) {
248
            aconf->bind_flags |= XDP_ZEROCOPY;
249
        } else if (strncasecmp(confstr, "copy", 4) == 0) {
250
            aconf->bind_flags |= XDP_COPY;
251
        } else if (strncasecmp(confstr, "none", 4) == 0) {
252
        } else {
253
            SCLogWarning("Incorrect af-xdp copy-mode setting, default (none) shall be applied");
254
        }
255
    }
256
257
    /* memory alignment mode selection */
258
    if (ConfGetChildValueWithDefault(if_root, if_default, "mem-unaligned", &confstr) == 1) {
259
        if (strncasecmp(confstr, "yes", 3) == 0) {
260
            aconf->mem_alignment = XDP_UMEM_UNALIGNED_CHUNK_FLAG;
261
        }
262
    }
263
264
    /* Busy polling options */
265
    if (ConfGetChildValueBoolWithDefault(if_root, if_default, "enable-busy-poll", &conf_val) == 1) {
266
        if (conf_val == 0) {
267
            aconf->enable_busy_poll = false;
268
        }
269
    }
270
271
    if (aconf->enable_busy_poll) {
272
        if (ConfGetChildValueIntWithDefault(if_root, if_default, "busy-poll-time", &conf_val_int) ==
273
                1) {
274
            if (conf_val_int) {
275
                aconf->busy_poll_time = conf_val_int;
276
            }
277
        }
278
279
        if (ConfGetChildValueIntWithDefault(
280
                    if_root, if_default, "busy-poll-budget", &conf_val_int) == 1) {
281
            if (conf_val_int) {
282
                aconf->busy_poll_budget = conf_val_int;
283
            }
284
        }
285
286
        /* 0 value is valid for these Linux tunable's */
287
        if (ConfGetChildValueIntWithDefault(
288
                    if_root, if_default, "gro-flush-timeout", &conf_val_int) == 1) {
289
            aconf->gro_flush_timeout = conf_val_int;
290
        }
291
292
        if (ConfGetChildValueIntWithDefault(
293
                    if_root, if_default, "napi-defer-hard-irq", &conf_val_int) == 1) {
294
            aconf->napi_defer_hard_irqs = conf_val_int;
295
        }
296
    }
297
#endif
298
299
finalize:
300
    if (LiveGetOffload() == 0) {
301
        if (GetIfaceOffloading(iface, 0, 1) == 1) {
302
            SCLogWarning("Using AF_XDP with offloading activated leads to capture problems");
303
        }
304
    } else {
305
        DisableIfaceOffloading(LiveGetDevice(iface), 0, 1);
306
    }
307
308
    return aconf;
309
}
310
311
static int AFXDPConfigGetThreadsCount(void *conf)
312
{
313
    if (conf == NULL)
314
        FatalError("Configuration file is NULL");
315
316
    AFXDPIfaceConfig *afxdp_conf = (AFXDPIfaceConfig *)conf;
317
    return afxdp_conf->threads;
318
}
319
320
#endif /* HAVE_AF_XDP */
321
322
/**
323
 * \brief Single thread version of the AF_XDP processing.
324
 */
325
int RunModeIdsAFXDPSingle(void)
326
0
{
327
0
    SCEnter();
328
329
#ifdef HAVE_AF_XDP
330
    int ret;
331
    const char *live_dev = NULL;
332
333
    TimeModeSetLive();
334
335
    (void)ConfGet("af-xdp.live-interface", &live_dev);
336
337
    if (AFXDPQueueProtectionInit() != TM_ECODE_OK) {
338
        FatalError("Unable to init AF_XDP queue protection.");
339
    }
340
341
    ret = RunModeSetLiveCaptureSingle(ParseAFXDPConfig, AFXDPConfigGetThreadsCount, "ReceiveAFXDP",
342
            "DecodeAFXDP", thread_name_single, live_dev);
343
    if (ret != 0) {
344
        FatalError("Unable to start runmode");
345
    }
346
347
    SCLogDebug("RunModeIdsAFXDPSingle initialised");
348
349
#endif /* HAVE_AF_XDP */
350
0
    SCReturnInt(0);
351
0
}
352
353
/**
354
 * \brief Workers version of the AF_XDP processing.
355
 *
356
 * Start N threads with each thread doing all the work.
357
 *
358
 */
359
int RunModeIdsAFXDPWorkers(void)
360
0
{
361
0
    SCEnter();
362
363
#ifdef HAVE_AF_XDP
364
    int ret;
365
    const char *live_dev = NULL;
366
367
    TimeModeSetLive();
368
369
    (void)ConfGet("af-xdp.live-interface", &live_dev);
370
371
    if (AFXDPQueueProtectionInit() != TM_ECODE_OK) {
372
        FatalError("Unable to init AF_XDP queue protection.");
373
    }
374
375
    ret = RunModeSetLiveCaptureWorkers(ParseAFXDPConfig, AFXDPConfigGetThreadsCount, "ReceiveAFXDP",
376
            "DecodeAFXDP", thread_name_workers, live_dev);
377
    if (ret != 0) {
378
        FatalError("Unable to start runmode");
379
    }
380
381
    SCLogDebug("RunModeIdsAFXDPWorkers initialised");
382
383
#endif /* HAVE_AF_XDP */
384
0
    SCReturnInt(0);
385
0
}
386
/**
387
 * @}
388
 */