Coverage Report

Created: 2026-09-06 07:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata8/src/source-af-xdp.c
Line
Count
Source
1
/* Copyright (C) 2011-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
 *  \defgroup afxdppacket AF_XDP running mode
20
 *
21
 *  @{
22
 */
23
24
/**
25
 * \file
26
 *
27
 * \author Richard McConnell <richard_mcconnell@rapid7.com>
28
 *
29
 * AF_XDP socket acquisition support
30
 *
31
 */
32
#define SC_PCAP_DONT_INCLUDE_PCAP_H  1
33
#include "suricata-common.h"
34
#include "suricata.h"
35
#include "decode.h"
36
#include "packet-queue.h"
37
#include "threads.h"
38
#include "threadvars.h"
39
#include "tm-queuehandlers.h"
40
#include "tm-modules.h"
41
#include "tm-threads.h"
42
#include "tm-threads-common.h"
43
#include "conf.h"
44
#include "util-cpu.h"
45
#include "util-datalink.h"
46
#include "util-debug.h"
47
#include "util-device-private.h"
48
#include "util-ebpf.h"
49
#include "util-error.h"
50
#include "util-privs.h"
51
#include "util-optimize.h"
52
#include "util-checksum.h"
53
#include "util-ioctl.h"
54
#include "util-host-info.h"
55
#include "util-sysfs.h"
56
#include "tmqh-packetpool.h"
57
#include "source-af-xdp.h"
58
#include "runmodes.h"
59
#include "flow-storage.h"
60
#include "util-validate.h"
61
62
#ifdef HAVE_AF_XDP
63
#include <net/if.h>
64
#include <bpf/libbpf.h>
65
#include <xdp/xsk.h>
66
#include <xdp/libxdp.h>
67
#endif
68
69
#if HAVE_LINUX_IF_ETHER_H
70
#include <linux/if_ether.h>
71
#endif
72
73
#ifndef HAVE_AF_XDP
74
75
TmEcode NoAFXDPSupportExit(ThreadVars *, const void *, void **);
76
77
void TmModuleReceiveAFXDPRegister(void)
78
78
{
79
78
    tmm_modules[TMM_RECEIVEAFXDP].name = "ReceiveAFXDP";
80
78
    tmm_modules[TMM_RECEIVEAFXDP].ThreadInit = NoAFXDPSupportExit;
81
78
    tmm_modules[TMM_RECEIVEAFXDP].Func = NULL;
82
78
    tmm_modules[TMM_RECEIVEAFXDP].ThreadExitPrintStats = NULL;
83
78
    tmm_modules[TMM_RECEIVEAFXDP].ThreadDeinit = NULL;
84
78
    tmm_modules[TMM_RECEIVEAFXDP].cap_flags = 0;
85
78
    tmm_modules[TMM_RECEIVEAFXDP].flags = TM_FLAG_RECEIVE_TM;
86
78
}
87
88
/**
89
 * \brief Registration Function for DecodeAFXDP.
90
 */
91
void TmModuleDecodeAFXDPRegister(void)
92
78
{
93
78
    tmm_modules[TMM_DECODEAFXDP].name = "DecodeAFXDP";
94
78
    tmm_modules[TMM_DECODEAFXDP].ThreadInit = NoAFXDPSupportExit;
95
78
    tmm_modules[TMM_DECODEAFXDP].Func = NULL;
96
78
    tmm_modules[TMM_DECODEAFXDP].ThreadExitPrintStats = NULL;
97
78
    tmm_modules[TMM_DECODEAFXDP].ThreadDeinit = NULL;
98
78
    tmm_modules[TMM_DECODEAFXDP].cap_flags = 0;
99
78
    tmm_modules[TMM_DECODEAFXDP].flags = TM_FLAG_DECODE_TM;
100
78
}
101
102
/**
103
 * \brief this function prints an error message and exits.
104
 */
105
TmEcode NoAFXDPSupportExit(ThreadVars *tv, const void *initdata, void **data)
106
0
{
107
0
    SCLogError("Error creating thread %s: you do not have "
108
0
               "support for AF_XDP enabled, on Linux host please recompile "
109
0
               "with --enable-af-xdp",
110
0
            tv->name);
111
    exit(EXIT_FAILURE);
112
0
}
113
114
#else /* We have AF_XDP support */
115
116
#define POLL_TIMEOUT      100
117
#define NUM_FRAMES_PROD   XSK_RING_PROD__DEFAULT_NUM_DESCS
118
#define NUM_FRAMES_CONS   XSK_RING_CONS__DEFAULT_NUM_DESCS
119
#define NUM_FRAMES        NUM_FRAMES_PROD
120
#define FRAME_SIZE        XSK_UMEM__DEFAULT_FRAME_SIZE
121
#define MEM_BYTES         (NUM_FRAMES * FRAME_SIZE * 2)
122
#define RECONNECT_TIMEOUT 500000
123
124
/* Interface state */
125
enum state { AFXDP_STATE_DOWN, AFXDP_STATE_UP };
126
127
struct XskInitProtect {
128
    SCMutex queue_protect;
129
    SC_ATOMIC_DECLARE(uint8_t, queue_num);
130
} xsk_protect;
131
132
struct UmemInfo {
133
    void *buf;
134
    struct xsk_umem *umem;
135
    struct xsk_ring_prod fq;
136
    struct xsk_ring_cons cq;
137
    struct xsk_umem_config cfg;
138
    int mmap_alignment_flag;
139
};
140
141
struct QueueAssignment {
142
    uint32_t queue_num;
143
    bool assigned;
144
};
145
146
struct XskSockInfo {
147
    struct xsk_ring_cons rx;
148
    struct xsk_ring_prod tx;
149
    struct xsk_socket *xsk;
150
151
    /* Queue assignment structure */
152
    struct QueueAssignment queue;
153
154
    /* Configuration items */
155
    struct xsk_socket_config cfg;
156
    bool enable_busy_poll;
157
    uint32_t busy_poll_time;
158
    uint32_t busy_poll_budget;
159
160
    struct pollfd fd;
161
};
162
163
/**
164
 * \brief Structure to hold thread specific variables.
165
 */
166
typedef struct AFXDPThreadVars_ {
167
    ThreadVars *tv;
168
    TmSlot *slot;
169
    LiveDevice *livedev;
170
171
    /* thread specific socket */
172
    int promisc;
173
    int threads;
174
175
    char iface[AFXDP_IFACE_NAME_LENGTH];
176
    uint32_t ifindex;
177
178
    /* AF_XDP structure */
179
    struct UmemInfo umem;
180
    struct XskSockInfo xsk;
181
    uint32_t gro_flush_timeout;
182
    uint32_t napi_defer_hard_irqs;
183
    uint32_t prog_id;
184
185
    /* Handle state */
186
    uint8_t afxdp_state;
187
188
    /* Stats parameters */
189
    uint64_t pkts;
190
    uint64_t bytes;
191
    uint16_t capture_afxdp_packets;
192
    uint16_t capture_kernel_drops;
193
    uint16_t capture_afxdp_poll;
194
    uint16_t capture_afxdp_poll_timeout;
195
    uint16_t capture_afxdp_poll_failed;
196
    uint16_t capture_afxdp_empty_reads;
197
    uint16_t capture_afxdp_failed_reads;
198
    uint16_t capture_afxdp_acquire_pkt_failed;
199
} AFXDPThreadVars;
200
201
static TmEcode ReceiveAFXDPThreadInit(ThreadVars *, const void *, void **);
202
static void ReceiveAFXDPThreadExitStats(ThreadVars *, void *);
203
static TmEcode ReceiveAFXDPThreadDeinit(ThreadVars *, void *);
204
static TmEcode ReceiveAFXDPLoop(ThreadVars *tv, void *data, void *slot);
205
206
static TmEcode DecodeAFXDPThreadInit(ThreadVars *, const void *, void **);
207
static TmEcode DecodeAFXDPThreadDeinit(ThreadVars *tv, void *data);
208
static TmEcode DecodeAFXDP(ThreadVars *, Packet *, void *);
209
210
/**
211
 * \brief Registration Function for RecieveAFXDP.
212
 * \todo Unit tests are needed for this module.
213
 */
214
void TmModuleReceiveAFXDPRegister(void)
215
{
216
    tmm_modules[TMM_RECEIVEAFXDP].name = "ReceiveAFXDP";
217
    tmm_modules[TMM_RECEIVEAFXDP].ThreadInit = ReceiveAFXDPThreadInit;
218
    tmm_modules[TMM_RECEIVEAFXDP].Func = NULL;
219
    tmm_modules[TMM_RECEIVEAFXDP].PktAcqLoop = ReceiveAFXDPLoop;
220
    tmm_modules[TMM_RECEIVEAFXDP].PktAcqBreakLoop = NULL;
221
    tmm_modules[TMM_RECEIVEAFXDP].ThreadExitPrintStats = ReceiveAFXDPThreadExitStats;
222
    tmm_modules[TMM_RECEIVEAFXDP].ThreadDeinit = ReceiveAFXDPThreadDeinit;
223
    tmm_modules[TMM_RECEIVEAFXDP].cap_flags = SC_CAP_NET_RAW;
224
    tmm_modules[TMM_RECEIVEAFXDP].flags = TM_FLAG_RECEIVE_TM;
225
}
226
227
/**
228
 * \brief Registration Function for DecodeAFXDP.
229
 * \todo Unit tests are needed for this module.
230
 */
231
void TmModuleDecodeAFXDPRegister(void)
232
{
233
    tmm_modules[TMM_DECODEAFXDP].name = "DecodeAFXDP";
234
    tmm_modules[TMM_DECODEAFXDP].ThreadInit = DecodeAFXDPThreadInit;
235
    tmm_modules[TMM_DECODEAFXDP].Func = DecodeAFXDP;
236
    tmm_modules[TMM_DECODEAFXDP].ThreadExitPrintStats = NULL;
237
    tmm_modules[TMM_DECODEAFXDP].ThreadDeinit = DecodeAFXDPThreadDeinit;
238
    tmm_modules[TMM_DECODEAFXDP].cap_flags = 0;
239
    tmm_modules[TMM_DECODEAFXDP].flags = TM_FLAG_DECODE_TM;
240
}
241
242
static inline void AFXDPDumpCounters(AFXDPThreadVars *ptv)
243
{
244
    struct xdp_statistics stats;
245
    socklen_t len = sizeof(struct xdp_statistics);
246
    int fd = xsk_socket__fd(ptv->xsk.xsk);
247
248
    if (getsockopt(fd, SOL_XDP, XDP_STATISTICS, &stats, &len) >= 0) {
249
        uint64_t rx_dropped = stats.rx_dropped + stats.rx_invalid_descs + stats.rx_ring_full;
250
251
        StatsAddUI64(ptv->tv, ptv->capture_kernel_drops,
252
                rx_dropped - StatsGetLocalCounterValue(ptv->tv, ptv->capture_kernel_drops));
253
        StatsAddUI64(ptv->tv, ptv->capture_afxdp_packets, ptv->pkts);
254
255
        (void)SC_ATOMIC_SET(ptv->livedev->drop, rx_dropped);
256
        (void)SC_ATOMIC_ADD(ptv->livedev->pkts, ptv->pkts);
257
258
        SCLogDebug("(%s) Kernel: Packets %" PRIu64 ", bytes %" PRIu64 ", dropped %" PRIu64 "",
259
                ptv->tv->name, StatsGetLocalCounterValue(ptv->tv, ptv->capture_afxdp_packets),
260
                ptv->bytes, StatsGetLocalCounterValue(ptv->tv, ptv->capture_kernel_drops));
261
262
        ptv->pkts = 0;
263
    }
264
}
265
266
/**
267
 * \brief Init function for socket creation.
268
 *
269
 * Mutex used to synchronise initialisation - each socket opens a
270
 * different queue. The specific order in which each queue is
271
 * opened is not important, but it is vital the queue_num's
272
 * are different.
273
 *
274
 * \param tv pointer to ThreadVars
275
 */
276
TmEcode AFXDPQueueProtectionInit(void)
277
{
278
    SCEnter();
279
280
    SCMutexInit(&xsk_protect.queue_protect, NULL);
281
    SC_ATOMIC_SET(xsk_protect.queue_num, 0);
282
    SCReturnInt(TM_ECODE_OK);
283
}
284
285
static TmEcode AFXDPAssignQueueID(AFXDPThreadVars *ptv)
286
{
287
    if (!ptv->xsk.queue.assigned) {
288
        ptv->xsk.queue.queue_num = SC_ATOMIC_GET(xsk_protect.queue_num);
289
        SC_ATOMIC_ADD(xsk_protect.queue_num, 1);
290
291
        /* Queue only needs assigned once, on startup */
292
        ptv->xsk.queue.assigned = true;
293
    }
294
    SCReturnInt(TM_ECODE_OK);
295
}
296
297
static void AFXDPAllThreadsRunning(AFXDPThreadVars *ptv)
298
{
299
    SCMutexLock(&xsk_protect.queue_protect);
300
    if ((ptv->threads - 1) == (int)ptv->xsk.queue.queue_num) {
301
        SCLogDebug("All AF_XDP capture threads are running.");
302
    }
303
    SCMutexUnlock(&xsk_protect.queue_protect);
304
}
305
306
static TmEcode AcquireBuffer(AFXDPThreadVars *ptv)
307
{
308
    int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | ptv->umem.mmap_alignment_flag;
309
    ptv->umem.buf = mmap(NULL, MEM_BYTES, PROT_READ | PROT_WRITE, mmap_flags, -1, 0);
310
311
    if (ptv->umem.buf == MAP_FAILED) {
312
        SCLogError("mmap: failed to acquire memory");
313
        SCReturnInt(TM_ECODE_FAILED);
314
    }
315
316
    SCReturnInt(TM_ECODE_OK);
317
}
318
319
static TmEcode ConfigureXSKUmem(AFXDPThreadVars *ptv)
320
{
321
    if (xsk_umem__create(&ptv->umem.umem, ptv->umem.buf, MEM_BYTES, &ptv->umem.fq, &ptv->umem.cq,
322
                &ptv->umem.cfg)) {
323
        SCLogError("failed to create umem: %s", strerror(errno));
324
        SCReturnInt(TM_ECODE_FAILED);
325
    }
326
327
    SCReturnInt(TM_ECODE_OK);
328
}
329
330
static TmEcode InitFillRing(AFXDPThreadVars *ptv, const uint32_t cnt)
331
{
332
    uint32_t idx_fq = 0;
333
334
    uint32_t ret = xsk_ring_prod__reserve(&ptv->umem.fq, cnt, &idx_fq);
335
    if (ret != cnt) {
336
        SCLogError("Failed to initialise the fill ring.");
337
        SCReturnInt(TM_ECODE_FAILED);
338
    }
339
340
    for (uint32_t i = 0; i < cnt; i++) {
341
        *xsk_ring_prod__fill_addr(&ptv->umem.fq, idx_fq++) = i * FRAME_SIZE;
342
    }
343
344
    xsk_ring_prod__submit(&ptv->umem.fq, cnt);
345
    SCReturnInt(TM_ECODE_OK);
346
}
347
348
/**
349
 * \brief Linux knobs are tuned to enable a NAPI polling context
350
 *
351
 * \param tv pointer to AFXDPThreadVars
352
 */
353
static TmEcode WriteLinuxTunables(AFXDPThreadVars *ptv)
354
{
355
    char fname[SYSFS_MAX_FILENAME_SIZE];
356
357
    if (snprintf(fname, SYSFS_MAX_FILENAME_SIZE, "class/net/%s/gro_flush_timeout", ptv->iface) <
358
            0) {
359
        SCReturnInt(TM_ECODE_FAILED);
360
    }
361
362
    if (SysFsWriteValue(fname, ptv->gro_flush_timeout) != TM_ECODE_OK) {
363
        SCReturnInt(TM_ECODE_FAILED);
364
    }
365
366
    if (snprintf(fname, SYSFS_MAX_FILENAME_SIZE, "class/net/%s/napi_defer_hard_irqs", ptv->iface) <
367
            0) {
368
        SCReturnInt(TM_ECODE_FAILED);
369
    }
370
371
    if (SysFsWriteValue(fname, ptv->napi_defer_hard_irqs) != TM_ECODE_OK) {
372
        SCReturnInt(TM_ECODE_FAILED);
373
    }
374
375
    SCReturnInt(TM_ECODE_OK);
376
}
377
378
static TmEcode ConfigureBusyPolling(AFXDPThreadVars *ptv)
379
{
380
    if (!ptv->xsk.enable_busy_poll) {
381
        SCReturnInt(TM_ECODE_OK);
382
    }
383
384
    /* Kernel version must be >= 5.11 to avail of SO_PREFER_BUSY_POLL
385
     * see linux commit: 7fd3253a7de6a317a0683f83739479fb880bffc8
386
     */
387
    if (!SCKernelVersionIsAtLeast(5, 11)) {
388
        SCLogWarning("Kernel version older than required: v5.11,"
389
                     " upgrade kernel version to use 'enable-busy-poll' option.");
390
        SCReturnInt(TM_ECODE_FAILED);
391
    }
392
393
#if defined SO_PREFER_BUSY_POLL && defined SO_BUSY_POLL && defined SO_BUSY_POLL_BUDGET
394
    const int fd = xsk_socket__fd(ptv->xsk.xsk);
395
    int sock_opt = 1;
396
397
    if (WriteLinuxTunables(ptv) != TM_ECODE_OK) {
398
        SCReturnInt(TM_ECODE_FAILED);
399
    }
400
401
    if (setsockopt(fd, SOL_SOCKET, SO_PREFER_BUSY_POLL, (void *)&sock_opt, sizeof(sock_opt)) < 0) {
402
        SCReturnInt(TM_ECODE_FAILED);
403
    }
404
405
    sock_opt = ptv->xsk.busy_poll_time;
406
    if (setsockopt(fd, SOL_SOCKET, SO_BUSY_POLL, (void *)&sock_opt, sizeof(sock_opt)) < 0) {
407
        SCReturnInt(TM_ECODE_FAILED);
408
    }
409
410
    sock_opt = ptv->xsk.busy_poll_budget;
411
    if (setsockopt(fd, SOL_SOCKET, SO_BUSY_POLL_BUDGET, (void *)&sock_opt, sizeof(sock_opt)) < 0) {
412
        SCReturnInt(TM_ECODE_FAILED);
413
    }
414
415
    SCReturnInt(TM_ECODE_OK);
416
#else
417
    SCLogWarning(
418
            "Kernel does not support busy poll, upgrade kernel or disable \"enable-busy-poll\".");
419
    SCReturnInt(TM_ECODE_FAILED);
420
#endif
421
}
422
423
static void AFXDPSwitchState(AFXDPThreadVars *ptv, int state)
424
{
425
    ptv->afxdp_state = state;
426
}
427
428
static TmEcode OpenXSKSocket(AFXDPThreadVars *ptv)
429
{
430
    int ret;
431
432
    SCMutexLock(&xsk_protect.queue_protect);
433
434
    if (AFXDPAssignQueueID(ptv) != TM_ECODE_OK) {
435
        SCLogError("Failed to assign queue ID");
436
        SCReturnInt(TM_ECODE_FAILED);
437
    }
438
439
    if ((ret = xsk_socket__create(&ptv->xsk.xsk, ptv->livedev->dev, ptv->xsk.queue.queue_num,
440
                 ptv->umem.umem, &ptv->xsk.rx, &ptv->xsk.tx, &ptv->xsk.cfg))) {
441
        SCLogError("Failed to create socket: %s", strerror(-ret));
442
        SCMutexUnlock(&xsk_protect.queue_protect);
443
        SCReturnInt(TM_ECODE_FAILED);
444
    }
445
    SCLogDebug("bind to %s on queue %u", ptv->iface, ptv->xsk.queue.queue_num);
446
447
    /* For polling and socket options */
448
    ptv->xsk.fd.fd = xsk_socket__fd(ptv->xsk.xsk);
449
    ptv->xsk.fd.events = POLLIN;
450
451
    /* Set state */
452
    AFXDPSwitchState(ptv, AFXDP_STATE_UP);
453
454
    SCMutexUnlock(&xsk_protect.queue_protect);
455
    SCReturnInt(TM_ECODE_OK);
456
}
457
458
static void AFXDPCloseSocket(AFXDPThreadVars *ptv)
459
{
460
    if (ptv->xsk.xsk) {
461
        xsk_socket__delete(ptv->xsk.xsk);
462
        ptv->xsk.xsk = NULL;
463
    }
464
465
    if (ptv->umem.umem) {
466
        xsk_umem__delete(ptv->umem.umem);
467
        ptv->umem.umem = NULL;
468
    }
469
470
    memset(&ptv->umem.fq, 0, sizeof(struct xsk_ring_prod));
471
    memset(&ptv->umem.cq, 0, sizeof(struct xsk_ring_cons));
472
}
473
474
static TmEcode AFXDPSocketCreation(AFXDPThreadVars *ptv)
475
{
476
    if (ConfigureXSKUmem(ptv) != TM_ECODE_OK) {
477
        SCReturnInt(TM_ECODE_FAILED);
478
    }
479
480
    if (InitFillRing(ptv, NUM_FRAMES * 2) != TM_ECODE_OK) {
481
        SCReturnInt(TM_ECODE_FAILED);
482
    }
483
484
    /* Open AF_XDP socket */
485
    if (OpenXSKSocket(ptv) != TM_ECODE_OK) {
486
        SCReturnInt(TM_ECODE_FAILED);
487
    }
488
489
    if (ConfigureBusyPolling(ptv) != TM_ECODE_OK) {
490
        SCLogWarning("Failed to configure busy polling"
491
                     " performance may be reduced.");
492
    }
493
494
    /* Has the eBPF program successfully bound? */
495
#ifdef HAVE_BPF_XDP_QUERY_ID
496
    if (bpf_xdp_query_id(ptv->ifindex, ptv->xsk.cfg.xdp_flags, &ptv->prog_id)) {
497
        SCLogError("Failed to attach eBPF program to interface: %s", ptv->livedev->dev);
498
        SCReturnInt(TM_ECODE_FAILED);
499
    }
500
#else
501
    if (bpf_get_link_xdp_id(ptv->ifindex, &ptv->prog_id, ptv->xsk.cfg.xdp_flags)) {
502
        SCLogError("Failed to attach eBPF program to interface: %s", ptv->livedev->dev);
503
        SCReturnInt(TM_ECODE_FAILED);
504
    }
505
#endif
506
507
    SCReturnInt(TM_ECODE_OK);
508
}
509
510
/**
511
 * \brief Try to reopen AF_XDP socket
512
 *
513
 * \retval: TM_ECODE_OK in case of success
514
 * TM_ECODE_FAILED if error occurs or a condition is not met.
515
 */
516
static TmEcode AFXDPTryReopen(AFXDPThreadVars *ptv)
517
{
518
    AFXDPCloseSocket(ptv);
519
    usleep(RECONNECT_TIMEOUT);
520
521
    int if_flags = GetIfaceFlags(ptv->iface);
522
    if (if_flags == -1) {
523
        SCLogDebug("Couldn't get flags for interface '%s'", ptv->iface);
524
        goto sock_err;
525
    } else if ((if_flags & (IFF_UP | IFF_RUNNING)) == 0) {
526
        SCLogDebug("Interface '%s' is down", ptv->iface);
527
        goto sock_err;
528
    }
529
530
    if (AFXDPSocketCreation(ptv) != TM_ECODE_OK) {
531
        SCReturnInt(TM_ECODE_FAILED);
532
    }
533
534
    SCLogInfo("Interface '%s' is back", ptv->iface);
535
    SCReturnInt(TM_ECODE_OK);
536
537
sock_err:
538
    SCReturnInt(TM_ECODE_FAILED);
539
}
540
541
/**
542
 * \brief Write packet entry to the fill ring, freeing
543
 * this slot for re/fill with inbound packet descriptor
544
 * \param pointer to Packet
545
 * \retval: None
546
 */
547
static void AFXDPReleasePacket(Packet *p)
548
{
549
    *xsk_ring_prod__fill_addr((struct xsk_ring_prod *)p->afxdp_v.fq, p->afxdp_v.fq_idx) =
550
            p->afxdp_v.orig;
551
552
    PacketFreeOrRelease(p);
553
}
554
555
static inline int DumpStatsEverySecond(AFXDPThreadVars *ptv, time_t *last_dump)
556
{
557
    int stats_dumped = 0;
558
    time_t current_time = time(NULL);
559
560
    if (current_time != *last_dump) {
561
        AFXDPDumpCounters(ptv);
562
        *last_dump = current_time;
563
        stats_dumped = 1;
564
    }
565
566
    StatsSyncCountersIfSignalled(ptv->tv);
567
568
    return stats_dumped;
569
}
570
571
static inline ssize_t WakeupSocket(void *data)
572
{
573
    ssize_t res = 0;
574
    AFXDPThreadVars *ptv = (AFXDPThreadVars *)data;
575
576
    /* Assuming kernel >= 5.11 in use if xdp_busy_poll is enabled */
577
    if (ptv->xsk.enable_busy_poll || xsk_ring_prod__needs_wakeup(&ptv->umem.fq)) {
578
        // cppcheck-suppress nullPointer
579
        res = recvfrom(xsk_socket__fd(ptv->xsk.xsk), NULL, 0, MSG_DONTWAIT, NULL, NULL);
580
    }
581
582
    return res;
583
}
584
585
/**
586
 * \brief Init function for ReceiveAFXDP.
587
 *
588
 * \param tv pointer to ThreadVars
589
 * \param initdata pointer to the interface passed from the user
590
 * \param data pointer gets populated with AFPThreadVars
591
 *
592
 * \todo Create a general AFP setup function.
593
 */
594
static TmEcode ReceiveAFXDPThreadInit(ThreadVars *tv, const void *initdata, void **data)
595
{
596
    SCEnter();
597
598
    AFXDPIfaceConfig *afxdpconfig = (AFXDPIfaceConfig *)initdata;
599
600
    if (initdata == NULL) {
601
        SCLogError("initdata == NULL");
602
        SCReturnInt(TM_ECODE_FAILED);
603
    }
604
605
    AFXDPThreadVars *ptv = SCCalloc(1, sizeof(AFXDPThreadVars));
606
    if (unlikely(ptv == NULL)) {
607
        afxdpconfig->DerefFunc(afxdpconfig);
608
        SCReturnInt(TM_ECODE_FAILED);
609
    }
610
611
    ptv->tv = tv;
612
613
    strlcpy(ptv->iface, afxdpconfig->iface, AFXDP_IFACE_NAME_LENGTH);
614
    ptv->iface[AFXDP_IFACE_NAME_LENGTH - 1] = '\0';
615
    ptv->ifindex = if_nametoindex(ptv->iface);
616
617
    ptv->livedev = LiveGetDevice(ptv->iface);
618
    if (ptv->livedev == NULL) {
619
        SCLogError("Unable to find Live device");
620
        SCFree(ptv);
621
        SCReturnInt(TM_ECODE_FAILED);
622
    }
623
624
    ptv->promisc = afxdpconfig->promisc;
625
    if (ptv->promisc != 0) {
626
        /* Force promiscuous mode */
627
        if (SetIfaceFlags(ptv->iface, IFF_PROMISC | IFF_UP) != 0) {
628
            SCLogError("Failed to switch interface (%s) to promiscuous, error %s", ptv->iface,
629
                    strerror(errno));
630
            SCFree(ptv);
631
            SCReturnInt(TM_ECODE_FAILED);
632
        }
633
    }
634
635
    ptv->threads = afxdpconfig->threads;
636
637
    /* Socket configuration */
638
    ptv->xsk.cfg.rx_size = NUM_FRAMES_CONS;
639
    ptv->xsk.cfg.tx_size = NUM_FRAMES_PROD;
640
    ptv->xsk.cfg.xdp_flags = afxdpconfig->mode;
641
    ptv->xsk.cfg.bind_flags = afxdpconfig->bind_flags;
642
643
    /* UMEM configuration */
644
    ptv->umem.cfg.fill_size = NUM_FRAMES_PROD * 2;
645
    ptv->umem.cfg.comp_size = NUM_FRAMES_CONS;
646
    ptv->umem.cfg.frame_size = XSK_UMEM__DEFAULT_FRAME_SIZE;
647
    ptv->umem.cfg.frame_headroom = XSK_UMEM__DEFAULT_FRAME_HEADROOM;
648
    ptv->umem.cfg.flags = afxdpconfig->mem_alignment;
649
650
    /* Use hugepages if unaligned chunk mode */
651
    if (ptv->umem.cfg.flags == XDP_UMEM_UNALIGNED_CHUNK_FLAG) {
652
        ptv->umem.mmap_alignment_flag = MAP_HUGETLB;
653
    }
654
655
    /* Busy polling configuration */
656
    ptv->xsk.enable_busy_poll = afxdpconfig->enable_busy_poll;
657
    ptv->xsk.busy_poll_budget = afxdpconfig->busy_poll_budget;
658
    ptv->xsk.busy_poll_time = afxdpconfig->busy_poll_time;
659
    ptv->gro_flush_timeout = afxdpconfig->gro_flush_timeout;
660
    ptv->napi_defer_hard_irqs = afxdpconfig->napi_defer_hard_irqs;
661
662
    /* Stats registration */
663
    ptv->capture_afxdp_packets = StatsRegisterCounter("capture.afxdp_packets", ptv->tv);
664
    ptv->capture_kernel_drops = StatsRegisterCounter("capture.kernel_drops", ptv->tv);
665
    ptv->capture_afxdp_poll = StatsRegisterCounter("capture.afxdp.poll", ptv->tv);
666
    ptv->capture_afxdp_poll_timeout = StatsRegisterCounter("capture.afxdp.poll_timeout", ptv->tv);
667
    ptv->capture_afxdp_poll_failed = StatsRegisterCounter("capture.afxdp.poll_failed", ptv->tv);
668
    ptv->capture_afxdp_empty_reads = StatsRegisterCounter("capture.afxdp.empty_reads", ptv->tv);
669
    ptv->capture_afxdp_failed_reads = StatsRegisterCounter("capture.afxdp.failed_reads", ptv->tv);
670
    ptv->capture_afxdp_acquire_pkt_failed =
671
            StatsRegisterCounter("capture.afxdp.acquire_pkt_failed", ptv->tv);
672
673
    /* Reserve memory for umem  */
674
    if (AcquireBuffer(ptv) != TM_ECODE_OK) {
675
        SCFree(ptv);
676
        SCReturnInt(TM_ECODE_FAILED);
677
    }
678
679
    if (AFXDPSocketCreation(ptv) != TM_ECODE_OK) {
680
        ReceiveAFXDPThreadDeinit(tv, ptv);
681
        SCReturnInt(TM_ECODE_FAILED);
682
    }
683
684
    *data = (void *)ptv;
685
    afxdpconfig->DerefFunc(afxdpconfig);
686
    SCReturnInt(TM_ECODE_OK);
687
}
688
689
/**
690
 *  \brief Main AF_XDP reading Loop function
691
 */
692
static TmEcode ReceiveAFXDPLoop(ThreadVars *tv, void *data, void *slot)
693
{
694
    SCEnter();
695
696
    Packet *p;
697
    time_t last_dump = 0;
698
    struct timeval ts;
699
    uint32_t idx_rx = 0, idx_fq = 0, rcvd;
700
    int r;
701
    AFXDPThreadVars *ptv = (AFXDPThreadVars *)data;
702
    TmSlot *s = (TmSlot *)slot;
703
704
    ptv->slot = s->slot_next;
705
706
    AFXDPAllThreadsRunning(ptv);
707
708
    // Indicate that the thread is actually running its application level code (i.e., it can poll
709
    // packets)
710
    TmThreadsSetFlag(tv, THV_RUNNING);
711
712
    PacketPoolWait();
713
    while (1) {
714
        /* Start by checking the state of our interface */
715
        if (unlikely(ptv->afxdp_state == AFXDP_STATE_DOWN)) {
716
            do {
717
                usleep(RECONNECT_TIMEOUT);
718
                if (unlikely(suricata_ctl_flags != 0)) {
719
                    break;
720
                }
721
                r = AFXDPTryReopen(ptv);
722
            } while (r != TM_ECODE_OK);
723
        }
724
725
        if (unlikely(suricata_ctl_flags != 0)) {
726
            SCLogDebug("Stopping Suricata!");
727
            AFXDPDumpCounters(ptv);
728
            break;
729
        }
730
731
        /* Busy polling is not set, using poll() to maintain (relatively) decent
732
         * performance. xdp_busy_poll must be disabled for kernels < 5.11
733
         */
734
        if (!ptv->xsk.enable_busy_poll) {
735
            StatsIncr(ptv->tv, ptv->capture_afxdp_poll);
736
737
            r = poll(&ptv->xsk.fd, 1, POLL_TIMEOUT);
738
739
            /* Report poll results */
740
            if (r <= 0) {
741
                if (r == 0) {
742
                    StatsIncr(ptv->tv, ptv->capture_afxdp_poll_timeout);
743
                } else if (r < 0) {
744
                    StatsIncr(ptv->tv, ptv->capture_afxdp_poll_failed);
745
                    SCLogWarning("poll failed with retval %d", r);
746
                    AFXDPSwitchState(ptv, AFXDP_STATE_DOWN);
747
                }
748
749
                DumpStatsEverySecond(ptv, &last_dump);
750
                continue;
751
            }
752
        }
753
754
        rcvd = xsk_ring_cons__peek(&ptv->xsk.rx, ptv->xsk.busy_poll_budget, &idx_rx);
755
        if (!rcvd) {
756
            StatsIncr(ptv->tv, ptv->capture_afxdp_empty_reads);
757
            ssize_t ret = WakeupSocket(ptv);
758
            if (ret < 0) {
759
                SCLogWarning("recv failed with retval %ld", ret);
760
                AFXDPSwitchState(ptv, AFXDP_STATE_DOWN);
761
            }
762
            DumpStatsEverySecond(ptv, &last_dump);
763
            continue;
764
        }
765
766
        uint32_t res = xsk_ring_prod__reserve(&ptv->umem.fq, rcvd, &idx_fq);
767
        while (res != rcvd) {
768
            StatsIncr(ptv->tv, ptv->capture_afxdp_failed_reads);
769
            ssize_t ret = WakeupSocket(ptv);
770
            if (ret < 0) {
771
                SCLogWarning("recv failed with retval %ld", ret);
772
                AFXDPSwitchState(ptv, AFXDP_STATE_DOWN);
773
                continue;
774
            }
775
            res = xsk_ring_prod__reserve(&ptv->umem.fq, rcvd, &idx_fq);
776
        }
777
778
        gettimeofday(&ts, NULL);
779
        ptv->pkts += rcvd;
780
        for (uint32_t i = 0; i < rcvd; i++) {
781
            p = PacketGetFromQueueOrAlloc();
782
            if (unlikely(p == NULL)) {
783
                StatsIncr(ptv->tv, ptv->capture_afxdp_acquire_pkt_failed);
784
                continue;
785
            }
786
787
            PKT_SET_SRC(p, PKT_SRC_WIRE);
788
            p->datalink = LINKTYPE_ETHERNET;
789
            p->livedev = ptv->livedev;
790
            p->ReleasePacket = AFXDPReleasePacket;
791
            p->flags |= PKT_IGNORE_CHECKSUM;
792
793
            p->ts = SCTIME_FROM_TIMEVAL(&ts);
794
795
            uint64_t addr = xsk_ring_cons__rx_desc(&ptv->xsk.rx, idx_rx)->addr;
796
            uint32_t len = xsk_ring_cons__rx_desc(&ptv->xsk.rx, idx_rx++)->len;
797
            uint64_t orig = xsk_umem__extract_addr(addr);
798
            addr = xsk_umem__add_offset_to_addr(addr);
799
800
            uint8_t *pkt_data = xsk_umem__get_data(ptv->umem.buf, addr);
801
802
            ptv->bytes += len;
803
804
            p->afxdp_v.fq_idx = idx_fq++;
805
            p->afxdp_v.orig = orig;
806
            p->afxdp_v.fq = &ptv->umem.fq;
807
808
            PacketSetData(p, pkt_data, len);
809
810
            if (TmThreadsSlotProcessPkt(ptv->tv, ptv->slot, p) != TM_ECODE_OK) {
811
                TmqhOutputPacketpool(ptv->tv, p);
812
                SCReturnInt(EXIT_FAILURE);
813
            }
814
        }
815
816
        xsk_ring_prod__submit(&ptv->umem.fq, rcvd);
817
        xsk_ring_cons__release(&ptv->xsk.rx, rcvd);
818
819
        /* Trigger one dump of stats every second */
820
        DumpStatsEverySecond(ptv, &last_dump);
821
    }
822
823
    SCReturnInt(TM_ECODE_OK);
824
}
825
826
/**
827
 * \brief function to unload an AF_XDP program
828
 *
829
 */
830
static void RunModeAFXDPRemoveProg(char *iface_name)
831
{
832
    unsigned int ifindex = if_nametoindex(iface_name);
833
834
    struct xdp_multiprog *progs = xdp_multiprog__get_from_ifindex(ifindex);
835
    if (progs == NULL) {
836
        return;
837
    }
838
    enum xdp_attach_mode mode = xdp_multiprog__attach_mode(progs);
839
840
    struct xdp_program *prog = NULL;
841
842
    // loop through the multiprogram struct, removing all the programs
843
    for (prog = xdp_multiprog__next_prog(NULL, progs); prog;
844
            prog = xdp_multiprog__next_prog(prog, progs)) {
845
        int ret = xdp_program__detach(prog, ifindex, mode, 0);
846
        if (ret) {
847
            SCLogDebug("Error: cannot detatch XDP program: %s\n", strerror(errno));
848
        }
849
    }
850
851
    prog = xdp_multiprog__main_prog(progs);
852
    if (xdp_program__is_attached(prog, ifindex) != XDP_MODE_UNSPEC) {
853
        int ret = xdp_program__detach(prog, ifindex, mode, 0);
854
        if (ret) {
855
            SCLogDebug("Error: cannot detatch XDP program: %s\n", strerror(errno));
856
        }
857
    }
858
}
859
860
/**
861
 * \brief DeInit function closes af-xdp socket at exit.
862
 * \param tv pointer to ThreadVars
863
 * \param data pointer that gets cast into AFXDPPThreadVars for ptv
864
 */
865
static SCMutex sync_deinit = SCMUTEX_INITIALIZER;
866
867
static TmEcode ReceiveAFXDPThreadDeinit(ThreadVars *tv, void *data)
868
{
869
    AFXDPThreadVars *ptv = (AFXDPThreadVars *)data;
870
871
    /*
872
     * If AF_XDP is enabled, the program must be detached before the AF_XDP sockets
873
     * are closed to mitigate a bug that causes an IO_PAGEFAULT in linux kernel
874
     * version 5.19, unknown as of now what other versions this affects.
875
     */
876
    SCMutexLock(&sync_deinit);
877
    RunModeAFXDPRemoveProg(ptv->iface);
878
    SCMutexUnlock(&sync_deinit);
879
880
    if (ptv->xsk.xsk) {
881
        xsk_socket__delete(ptv->xsk.xsk);
882
        ptv->xsk.xsk = NULL;
883
    }
884
885
    if (ptv->umem.umem) {
886
        xsk_umem__delete(ptv->umem.umem);
887
        ptv->umem.umem = NULL;
888
    }
889
    munmap(ptv->umem.buf, MEM_BYTES);
890
891
    SCFree(ptv);
892
    SCReturnInt(TM_ECODE_OK);
893
}
894
895
/**
896
 * \brief This function prints stats to the screen at exit.
897
 * \param tv pointer to ThreadVars
898
 * \param data pointer that gets cast into AFXDPThreadVars for ptv
899
 */
900
static void ReceiveAFXDPThreadExitStats(ThreadVars *tv, void *data)
901
{
902
    SCEnter();
903
    AFXDPThreadVars *ptv = (AFXDPThreadVars *)data;
904
905
    AFXDPDumpCounters(ptv);
906
907
    SCLogPerf("(%s) Kernel: Packets %" PRIu64 ", bytes %" PRIu64 ", dropped %" PRIu64 "", tv->name,
908
            StatsGetLocalCounterValue(tv, ptv->capture_afxdp_packets), ptv->bytes,
909
            StatsGetLocalCounterValue(tv, ptv->capture_kernel_drops));
910
}
911
912
/**
913
 * \brief This function passes off to link type decoders.
914
 *
915
 * DecodeAFXDP decodes packets from AF_XDP and passes
916
 * them off to the proper link type decoder.
917
 *
918
 * \param t pointer to ThreadVars
919
 * \param p pointer to the current packet
920
 * \param data pointer that gets cast into AFXDPThreadVars for ptv
921
 */
922
static TmEcode DecodeAFXDP(ThreadVars *tv, Packet *p, void *data)
923
{
924
    SCEnter();
925
926
    DecodeThreadVars *dtv = (DecodeThreadVars *)data;
927
928
    DEBUG_VALIDATE_BUG_ON(PKT_IS_PSEUDOPKT(p));
929
930
    /* update counters */
931
    DecodeUpdatePacketCounters(tv, dtv, p);
932
933
    /* If suri has set vlan during reading, we increase vlan counter */
934
    if (p->vlan_idx) {
935
        StatsIncr(tv, dtv->counter_vlan);
936
    }
937
938
    /* call the decoder */
939
    DecodeLinkLayer(tv, dtv, p->datalink, p, GET_PKT_DATA(p), GET_PKT_LEN(p));
940
941
    PacketDecodeFinalize(tv, dtv, p);
942
943
    SCReturnInt(TM_ECODE_OK);
944
}
945
946
static TmEcode DecodeAFXDPThreadInit(ThreadVars *tv, const void *initdata, void **data)
947
{
948
    SCEnter();
949
    DecodeThreadVars *dtv = DecodeThreadVarsAlloc(tv);
950
    if (dtv == NULL)
951
        SCReturnInt(TM_ECODE_FAILED);
952
953
    DecodeRegisterPerfCounters(dtv, tv);
954
955
    *data = (void *)dtv;
956
957
    SCReturnInt(TM_ECODE_OK);
958
}
959
960
static TmEcode DecodeAFXDPThreadDeinit(ThreadVars *tv, void *data)
961
{
962
    if (data != NULL)
963
        DecodeThreadVarsFree(tv, data);
964
    SCReturnInt(TM_ECODE_OK);
965
}
966
967
#endif /* HAVE_AF_XDP */
968
/* eof */
969
/**
970
 * @}
971
 */