[PATCH v4 4/5] bpf: add test for Rx and Tx filtering
Marat Khalili
marat.khalili at huawei.com
Fri Nov 7 18:30:51 CET 2025
Thank you for doing this! See comments below.
> -----Original Message-----
> From: Stephen Hemminger <stephen at networkplumber.org>
> Sent: Tuesday 4 November 2025 16:07
> To: dev at dpdk.org
> Cc: Stephen Hemminger <stephen at networkplumber.org>; Konstantin Ananyev <konstantin.ananyev at huawei.com>
> Subject: [PATCH v4 4/5] bpf: add test for Rx and Tx filtering
>
> New test using null device to test filtering with BPF.
>
> Signed-off-by: Stephen Hemminger <stephen at networkplumber.org>
> ---
> app/test/bpf/filter.c | 53 +++++++
> app/test/bpf/meson.build | 1 +
> app/test/test_bpf.c | 304 +++++++++++++++++++++++++++++++++++++++
> 3 files changed, 358 insertions(+)
> create mode 100644 app/test/bpf/filter.c
>
> diff --git a/app/test/bpf/filter.c b/app/test/bpf/filter.c
> new file mode 100644
> index 0000000000..d47233a47a
> --- /dev/null
> +++ b/app/test/bpf/filter.c
> @@ -0,0 +1,53 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * BPF TX filter program for testing rte_bpf_eth_tx_elf_load
> + */
> +
> +typedef unsigned char uint8_t;
> +typedef unsigned short uint16_t;
> +typedef unsigned int uint32_t;
> +typedef unsigned long uint64_t;
> +
> +/*
> + * Simple TX filter that accepts TCP packets
> + *
> + * BPF TX programs receive pointer to data and should return:
> + * 0 = drop packet
> + * non-zero = rx/tx packet
> + *
> + * This filter checks:
> + * 1. Packet is IPv4
> + * 2. Protocol is TCP (IPPROTO_TCP = 6)
> + */
> +__attribute__((section("filter"), used))
Could this section (and function) be called better, e.g. filter_ip_tcp?
> +uint64_t
> +test_filter(void *pkt)
> +{
> + uint8_t *data = pkt;
Does pkt point to packet data or an mbuf? I would think we need mbuf functions
here to access data. Not sure why the test is not failing though, maybe I
misunderstand it.
> +
> + /* Read version and IHL (first byte of IP header) */
> + uint8_t version_ihl = data[14];
> +
> + /* Check IPv4 version (upper 4 bits should be 4) */
> + if ((version_ihl >> 4) != 4)
> + return 0;
> +
> + /* Protocol field (byte 9 of IP header) must be TCP (6) */
> + uint8_t proto = data[14 + 9];
> + return (proto == 6);
> +}
> +
> +__attribute__((section("drop"), used))
> +uint64_t
> +test_drop(void *pkt)
> +{
> + (void)pkt;
> + return 0;
> +}
> +
> +__attribute__((section("allow"), used))
> +uint64_t
> +test_allow(void *pkt)
> +{
> + (void)pkt;
> + return 1;
> +}
> diff --git a/app/test/bpf/meson.build b/app/test/bpf/meson.build
> index b4f54aa976..19fec05521 100644
> --- a/app/test/bpf/meson.build
> +++ b/app/test/bpf/meson.build
> @@ -32,6 +32,7 @@ cflags += '-DTEST_BPF_ELF_LOAD'
> # BPF sources to compile
> bpf_progs = {
> 'load' : 'test_bpf_load',
> + 'filter' : 'test_bpf_filter',
> }
>
> foreach bpf_src, bpf_hdr : bpf_progs
> diff --git a/app/test/test_bpf.c b/app/test/test_bpf.c
> index ec84b85f1c..54b6330678 100644
> --- a/app/test/test_bpf.c
> +++ b/app/test/test_bpf.c
> @@ -16,6 +16,12 @@
> #include <rte_byteorder.h>
> #include <rte_errno.h>
> #include <rte_bpf.h>
> +#include <rte_ethdev.h>
> +#include <rte_bpf_ethdev.h>
> +#include <rte_bus_vdev.h>
> +#include <rte_ether.h>
> +#include <rte_ip.h>
> +#include <rte_tcp.h>
>
> #include "test.h"
>
> @@ -3412,6 +3418,288 @@ test_bpf_elf_load(void)
> printf("%s: ELF load test passed\n", __func__);
> return TEST_SUCCESS;
> }
> +
> +#include "test_bpf_filter.h"
> +
> +#define BPF_TEST_BURST 128
> +#define BPF_TEST_POOLSIZE 256 /* at least 2x burst */
> +#define BPF_TEST_PKT_LEN 64 /* Ether + IP + TCP */
> +
> +static int null_vdev_setup(const char *name, uint16_t *port, struct rte_mempool *pool)
Nit: The function type should be on a line by itself preceding the function (also in other places).
// snip packets setup functions
> +static int bpf_tx_test(uint16_t port, const char *tmpfile, struct rte_mempool *pool,
> + const char *fname, uint32_t flags)
Nit: not sure `fname` is a good name for ELF section.
> +{
> + const struct rte_bpf_prm prm = {
> + .prog_arg = {
> + .type = RTE_BPF_ARG_PTR,
Should this be RTE_BPF_ARG_PTR_MBUF and include buf_size?
> + .size = sizeof(struct rte_mbuf),
> + },
> + };
> + int ret;
> +
> + struct rte_mbuf *pkts[BPF_TEST_BURST] = { };
> + ret = rte_pktmbuf_alloc_bulk(pool, pkts, BPF_TEST_BURST);
> + TEST_ASSERT(ret == 0, "failed to allocate mbufs");
> +
> + uint16_t expect = setup_mbufs(pkts, BPF_TEST_BURST);
> +
> + /* Try to load BPF TX program from temp file */
> + ret = rte_bpf_eth_tx_elf_load(port, 0, &prm, tmpfile, fname, flags);
> + TEST_ASSERT(ret == 0, "failed to load BPF filter from temp file %s: %d",
> + tmpfile, ret);
> +
> + uint16_t sent = rte_eth_tx_burst(port, 0, pkts, BPF_TEST_BURST);
> + TEST_ASSERT_EQUAL(sent, expect, "rte_eth_tx_burst returned: %u expected %u",
> + sent, expect);
> +
> + /* The unsent packets should be dropped */
> + rte_pktmbuf_free_bulk(pkts + sent, BPF_TEST_BURST - sent);
> +
> + /* Pool should have same number of packets avail */
> + unsigned int avail = rte_mempool_avail_count(pool);
> + TEST_ASSERT_EQUAL(avail, BPF_TEST_POOLSIZE,
> + "Mempool available %u != %u leaks?", avail, BPF_TEST_POOLSIZE);
> +
> + rte_bpf_eth_tx_unload(port, 0);
> + return TEST_SUCCESS;
> +}
// snip test_bpf_elf_tx_load
> +static int bpf_rx_test(uint16_t port, const char *tmpfile, struct rte_mempool *pool,
> + const char *fname, uint32_t flags, uint16_t expected)
Is rx test significantly different from tx one? Maybe add a comment what each test does?
// snip the rest, lgtm
More information about the dev
mailing list