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