diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index c461224b59..48d8f78b0c 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 d905c596eb..f6a74febec 100644 --- a/src/source-af-packet.c +++ b/src/source-af-packet.c @@ -355,7 +355,13 @@ typedef struct AFPThreadVars_ int ebpf_filter_fd; struct ebpf_timeout_config ebpf_t_config; #endif - +#ifdef AFPACKET_TEST_REPLAY + // Number of packets to read on afpacket interface before stopping Suricata + // This is meant for test purposes, especially to run suricata-verify against tcpreplay + // to test features that are not available when reading from a pcap (such as ebpf) + // Value 0 means no packet (and should thus not be used). + uint32_t max_packets; +#endif } AFPThreadVars; static TmEcode ReceiveAFPThreadInit(ThreadVars *, const void *, void **); @@ -1430,6 +1436,15 @@ 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 + // When we reached the configured maximum number of packets + // we tell Suricata to stop. + // This is useful for test purposes when tcpreplaying a pcap + // so we know we do not expect anymore packets. + 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 +2583,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