diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index a9ad630fcf..ed58c5af5e 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -3068,6 +3068,79 @@ jobs: !suricata-verify/live/tests/**/output/sur if-no-files-found: ignore + 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} -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 + 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 diff --git a/src/runmode-af-packet.c b/src/runmode-af-packet.c index 440a345971..8aa1399aaf 100644 --- a/src/runmode-af-packet.c +++ b/src/runmode-af-packet.c @@ -593,7 +593,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 eb87c750e8..10992574d9 100644 --- a/src/source-af-packet.c +++ b/src/source-af-packet.c @@ -353,7 +353,12 @@ typedef struct AFPThreadVars_ int ebpf_filter_fd; 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; static TmEcode ReceiveAFPThreadInit(ThreadVars *, const void *, void **); @@ -1428,6 +1433,16 @@ 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 + /* 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 switch (r) { case AFP_READ_OK: /* Trigger one dump of stats every second */ @@ -2246,8 +2261,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; \ } /** @@ -2566,6 +2581,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..777c943c88 100644 --- a/src/source-af-packet.h +++ b/src/source-af-packet.h @@ -120,6 +120,13 @@ typedef struct AFPIfaceConfig_ const char *out_iface; #ifdef HAVE_PACKET_EBPF 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); void (*DerefFunc)(void *); diff --git a/src/suricata.c b/src/suricata.c index e9a377421d..6f66d516ef 100644 --- a/src/suricata.c +++ b/src/suricata.c @@ -855,6 +855,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