From 4b8b5e69ccbc87197826c296fd6cd145485c071b Mon Sep 17 00:00:00 2001 From: Samaresh Kumar Singh Date: Wed, 20 May 2026 11:43:55 -0500 Subject: [PATCH 1/5] af-packet: respect vlan.use-for-tracking in eBPF bypass AFPBypassCallback and AFPXDPBypassCallback populate the flow_table_v4 and flow_table_v6 eBPF maps using p->vlan_id[N] directly. When the matching xdp_filter.c program is built with VLAN_TRACKING=0 (i.e. vlan.use-for-tracking is false in suricata.yaml), the kernel side writes vlan0 and vlan1 as 0 on every map lookup, so userspace inserts that carry the real VLAN id never match and per-flow bypass counters stay at zero. Apply g_vlan_mask to each vlan_id assignment, matching the masking pattern already used by flow-hash.c. When vlan.use-for-tracking is true, g_vlan_mask is 0xffff and behaviour is unchanged. Original patch by John Graat on Redmine ticket 8242. Rebased to current main and dropped the now-removed vlan2 field. Ticket: 8242 --- src/source-af-packet.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/source-af-packet.c b/src/source-af-packet.c index 1ab466d104..de41a53a6a 100644 --- a/src/source-af-packet.c +++ b/src/source-af-packet.c @@ -2248,8 +2248,8 @@ static int AFPSetFlowStorage(Packet *p, int map_fd, void *key0, void* key1, #define FlowKeyIpFill(k, p) \ { \ (k)->ip_proto = ((p)->proto == IPPROTO_TCP) ? 1 : 0; \ - (k)->vlan0 = (p)->vlan_id[0]; \ - (k)->vlan1 = (p)->vlan_id[1]; \ + (k)->vlan0 = (p)->vlan_id[0] & g_vlan_mask; \ + (k)->vlan1 = (p)->vlan_id[1] & g_vlan_mask; \ } /** From 816515e1180ced490acc2aa77f7f28bc71c4de0a Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Mon, 13 Oct 2025 13:50:49 +0200 Subject: [PATCH 2/5] ci: add new xdp build with live tests so as to run ebpf live tests --- .github/workflows/builds.yml | 73 ++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index e4e02c212f..fd70ea8f1e 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -2895,6 +2895,79 @@ jobs: # DPDK configuration checks - run: ./qa/live/dpdk/dpdk-testsuite.sh + ubuntu-xdp: + name: Ubuntu (xdp) + runs-on: ubuntu-latest + needs: [prepare-deps] + steps: + # Cache Rust stuff. + - name: Cache cargo registry + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 + with: + path: ~/.cargo/registry + key: cargo-registry + + - name: Determine number of CPUs + run: echo CPUS=$(nproc --all) >> $GITHUB_ENV + + - run: sudo apt update + - run: | + sudo apt -y install \ + autoconf \ + automake \ + build-essential \ + cmake \ + curl \ + git \ + hwloc \ + libhwloc-dev \ + jq \ + make \ + libpcre3 \ + libpcre3-dbg \ + libpcre3-dev \ + libpcre2-dev \ + libtool \ + libpcap-dev \ + libnet1-dev \ + libyaml-0-2 \ + libyaml-dev \ + libcap-ng-dev \ + libcap-ng0 \ + libjansson-dev \ + libjansson4 \ + libnuma-dev \ + liblz4-dev \ + libssl-dev \ + pkg-config \ + python3 \ + python3-yaml \ + tcpreplay \ + zlib1g \ + zlib1g-dev \ + clang \ + libxdp-dev + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 + - run: git config --global --add safe.directory /__w/suricata/suricata + - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 + with: + name: prep + path: prep + - name: Install Rust + run: curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain $(grep rust-version rust/Cargo.toml.in|sed 's/\"//g'|awk '{print $3}') -y + - run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH + - uses: ./.github/actions/install-cbindgen + - run: tar xf prep/suricata-verify.tar.gz + - run: ./autogen.sh + - run: CFLAGS="${DEFAULT_CFLAGS}" ./configure --enable-warnings --enable-unittests --enable-ebpf --enable-ebpf-build + - run: make -j ${{ env.CPUS }} + - run: make check + - name: Running suricata-verify + run: python3 ./suricata-verify/run.py -q --debug-failed + - run: ulimit -a + - name: Running suricata-verify live tests + run: sudo python3 ./suricata-verify/run.py --live --debug-failed + debian-12: name: Debian 12 (xdp) runs-on: ubuntu-latest From 78c0a300c447666128538b2192e3fa7a45ec7854 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Fri, 13 Mar 2026 14:54:02 +0100 Subject: [PATCH 3/5] test: new afpacket max-packets feature Ticket: 7674 Allows a compile-time option AFPACKET_TEST_REPLAY, that allows to set a configuration max-packets per afpacket interface, after which the PktAcqLoop stops. This allows suricata-verify tests to run with tcpreplay, and know when to stop --- .github/workflows/builds.yml | 2 +- src/runmode-af-packet.c | 8 +++++++- src/source-af-packet.c | 12 +++++++++++- src/source-af-packet.h | 3 +++ src/suricata.c | 3 +++ 5 files changed, 25 insertions(+), 3 deletions(-) diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index fd70ea8f1e..58fc5da010 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -2959,7 +2959,7 @@ jobs: - uses: ./.github/actions/install-cbindgen - run: tar xf prep/suricata-verify.tar.gz - run: ./autogen.sh - - run: CFLAGS="${DEFAULT_CFLAGS}" ./configure --enable-warnings --enable-unittests --enable-ebpf --enable-ebpf-build + - run: CFLAGS="${DEFAULT_CFLAGS} -DAFPACKET_TEST_REPLAY" ./configure --enable-warnings --enable-unittests --enable-ebpf --enable-ebpf-build - run: make -j ${{ env.CPUS }} - run: make check - name: Running suricata-verify diff --git a/src/runmode-af-packet.c b/src/runmode-af-packet.c index 8fbce6d964..fb773faf8f 100644 --- a/src/runmode-af-packet.c +++ b/src/runmode-af-packet.c @@ -594,7 +594,13 @@ static void *ParseAFPConfig(const char *iface) SCLogError("%s: XDP support is not built-in", iface); #endif } - +#ifdef AFPACKET_TEST_REPLAY + if ((SCConfGetChildValueIntWithDefault(if_root, if_default, "max-packets", &value)) == 1) { + aconf->max_packets = (uint32_t)value; + } else { + aconf->max_packets = 0; + } +#endif if ((SCConfGetChildValueIntWithDefault(if_root, if_default, "buffer-size", &value)) == 1) { aconf->buffer_size = (int)value; } else { diff --git a/src/source-af-packet.c b/src/source-af-packet.c index de41a53a6a..228a5b3788 100644 --- a/src/source-af-packet.c +++ b/src/source-af-packet.c @@ -355,7 +355,9 @@ typedef struct AFPThreadVars_ int ebpf_filter_fd; struct ebpf_timeout_config ebpf_t_config; #endif - +#ifdef AFPACKET_TEST_REPLAY + uint32_t max_packets; +#endif } AFPThreadVars; static TmEcode ReceiveAFPThreadInit(ThreadVars *, const void *, void **); @@ -1430,6 +1432,11 @@ TmEcode ReceiveAFPLoop(ThreadVars *tv, void *data, void *slot) } else if (r > 0) { StatsCounterIncr(&ptv->tv->stats, ptv->capture_afp_poll_data); r = AFPReadFunc(ptv); +#ifdef AFPACKET_TEST_REPLAY + if (ptv->max_packets > 0 && ptv->pkts >= ptv->max_packets) { + suricata_ctl_flags |= SURICATA_STOP; + } +#endif switch (r) { case AFP_READ_OK: /* Trigger one dump of stats every second */ @@ -2568,6 +2575,9 @@ TmEcode ReceiveAFPThreadInit(ThreadVars *tv, const void *initdata, void **data) ptv->promisc = afpconfig->promisc; ptv->checksum_mode = afpconfig->checksum_mode; ptv->bpf_filter = NULL; +#ifdef AFPACKET_TEST_REPLAY + ptv->max_packets = afpconfig->max_packets; +#endif ptv->threads = 1; #ifdef HAVE_PACKET_FANOUT diff --git a/src/source-af-packet.h b/src/source-af-packet.h index 8645bdcffc..3fbf3c068f 100644 --- a/src/source-af-packet.h +++ b/src/source-af-packet.h @@ -120,6 +120,9 @@ typedef struct AFPIfaceConfig_ const char *out_iface; #ifdef HAVE_PACKET_EBPF struct ebpf_timeout_config ebpf_t_config; +#endif +#ifdef AFPACKET_TEST_REPLAY + uint32_t max_packets; #endif SC_ATOMIC_DECLARE(unsigned int, ref); void (*DerefFunc)(void *); diff --git a/src/suricata.c b/src/suricata.c index 14b3f4d6b1..e5f33927a1 100644 --- a/src/suricata.c +++ b/src/suricata.c @@ -844,6 +844,9 @@ static void PrintBuildInfo(void) #ifdef HAVE_PACKET_EBPF strlcat(features, "EBPF ", sizeof(features)); #endif +#ifdef AFPACKET_TEST_REPLAY + strlcat(features, "AFPACKET_TEST_REPLAY ", sizeof(features)); +#endif #ifdef PROFILE_LOCKING strlcat(features, "PROFILE_LOCKING ", sizeof(features)); #endif From e4de6def66f013b7f0907afe8355c8c53d63cc36 Mon Sep 17 00:00:00 2001 From: Samaresh Kumar Singh Date: Thu, 28 May 2026 17:57:23 -0500 Subject: [PATCH 4/5] af-packet: widen max_packets comparison to uint64_t ptv->pkts is uint64_t and ptv->max_packets is uint32_t; cast the latter so the comparison is unsigned and same-width, addressing @victorjulien's review comment on #15469. --- src/source-af-packet.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/source-af-packet.c b/src/source-af-packet.c index 228a5b3788..a11c3c85e9 100644 --- a/src/source-af-packet.c +++ b/src/source-af-packet.c @@ -356,6 +356,9 @@ typedef struct AFPThreadVars_ struct ebpf_timeout_config ebpf_t_config; #endif #ifdef AFPACKET_TEST_REPLAY + /* Test-replay stop condition: ReceiveAFPLoop exits after this many + * packets. Set from AFPIfaceConfig::max_packets at thread init. A + * value of 0 disables the cap (loop runs until normal stop). */ uint32_t max_packets; #endif } AFPThreadVars; @@ -1433,7 +1436,12 @@ TmEcode ReceiveAFPLoop(ThreadVars *tv, void *data, void *slot) StatsCounterIncr(&ptv->tv->stats, ptv->capture_afp_poll_data); r = AFPReadFunc(ptv); #ifdef AFPACKET_TEST_REPLAY - if (ptv->max_packets > 0 && ptv->pkts >= ptv->max_packets) { + /* Test-replay stop condition: signal Suricata to stop after the + * configured number of packets so suricata-verify live tests + * driven by tcpreplay terminate deterministically. ptv->pkts is + * uint64_t; widen max_packets so the comparison is unsigned and + * same-width. */ + if (ptv->max_packets > 0 && ptv->pkts >= (uint64_t)ptv->max_packets) { suricata_ctl_flags |= SURICATA_STOP; } #endif From c813e57c916672b7def9f4d830a3355491309d5b Mon Sep 17 00:00:00 2001 From: Samaresh Kumar Singh Date: Thu, 28 May 2026 18:06:23 -0500 Subject: [PATCH 5/5] af-packet: document max_packets in AFPIfaceConfig Mirror the doc comment from the AFPThreadVars copy onto the config struct field, since they hold the same value (config -> per-thread copy at init). --- src/source-af-packet.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/source-af-packet.h b/src/source-af-packet.h index 3fbf3c068f..777c943c88 100644 --- a/src/source-af-packet.h +++ b/src/source-af-packet.h @@ -122,6 +122,10 @@ typedef struct AFPIfaceConfig_ struct ebpf_timeout_config ebpf_t_config; #endif #ifdef AFPACKET_TEST_REPLAY + /* Stop the af-packet receive loop after this many packets. Used by + * suricata-verify live tests driven by tcpreplay so Suricata exits + * deterministically once the replay completes. A value of 0 means no + * cap (loop runs until the normal stop signal). */ uint32_t max_packets; #endif SC_ATOMIC_DECLARE(unsigned int, ref);