From: Konstantin Ananyev <konstantin.ananyev@huawei.com>
To: Stephen Hemminger <stephen@networkplumber.org>,
"dev@dpdk.org" <dev@dpdk.org>
Cc: Marat Khalili <marat.khalili@huawei.com>
Subject: RE: [PATCH v6 2/2] bpf: add test for Rx and Tx filtering
Date: Wed, 12 Nov 2025 15:08:09 +0000 [thread overview]
Message-ID: <bb1bce77ccfd46b5850439be9bcb5607@huawei.com> (raw)
In-Reply-To: <20251111225719.540140-3-stephen@networkplumber.org>
>
> New test using null device to test filtering with BPF.
>
> If libelf library is not available, then DPDK bpf
> will return -ENOTSUP to the test and the test will be skipped.
>
> Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
> Acked-by: Marat Khalili <marat.khalili@huawei.com>
> ---
> app/test/bpf/filter.c | 53 +++++++
> app/test/bpf/meson.build | 1 +
> app/test/test_bpf.c | 321 ++++++++++++++++++++++++++++++++++++++-
> 3 files changed, 373 insertions(+), 2 deletions(-)
> 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))
> +uint64_t
> +test_filter(void *pkt)
> +{
> + uint8_t *data = pkt;
> +
> + /* 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 c460002358..6bbcbb5eb3 100644
> --- a/app/test/test_bpf.c
> +++ b/app/test/test_bpf.c
> @@ -3424,10 +3424,326 @@ test_bpf_elf_load(void)
> printf("%s: ELF load test passed\n", __func__);
> return TEST_SUCCESS;
> }
> +
> +#include <rte_ethdev.h>
> +#include <rte_bpf_ethdev.h>
> +#include <rte_bus_vdev.h>
> +
> +#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)
> +{
> + int ret;
> +
> + /* Make a null device */
> + ret = rte_vdev_init(name, NULL);
> + TEST_ASSERT(ret == 0, "rte_vdev_init(%s) failed: %d", name, ret);
> +
> + ret = rte_eth_dev_get_port_by_name(name, port);
> + TEST_ASSERT(ret == 0, "failed to get port id for %s: %d", name, ret);
> +
> + struct rte_eth_conf conf = { };
> + ret = rte_eth_dev_configure(*port, 1, 1, &conf);
> + TEST_ASSERT(ret == 0, "failed to configure port %u: %d", *port, ret);
> +
> + struct rte_eth_txconf txconf = { };
> + ret = rte_eth_tx_queue_setup(*port, 0, BPF_TEST_BURST, SOCKET_ID_ANY,
> &txconf);
> + TEST_ASSERT(ret == 0, "failed to setup tx queue port %u: %d", *port, ret);
> +
> + struct rte_eth_rxconf rxconf = { };
> + ret = rte_eth_rx_queue_setup(*port, 0, BPF_TEST_BURST, SOCKET_ID_ANY,
> + &rxconf, pool);
> + TEST_ASSERT(ret == 0, "failed to setup rx queue port %u: %d", *port, ret);
> +
> + ret = rte_eth_dev_start(*port);
> + TEST_ASSERT(ret == 0, "failed to start port %u: %d", *port, ret);
> +
> + return 0;
> +}
> +
> +static unsigned int
> +setup_mbufs(struct rte_mbuf *burst[], unsigned int n)
> +{
> + struct rte_ether_hdr eh = {
> + .ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4),
> + };
> + const struct rte_ipv4_hdr iph = {
> + .version_ihl = RTE_IPV4_VHL_DEF,
> + .total_length = rte_cpu_to_be_16(BPF_TEST_PKT_LEN - sizeof(eh)),
> + .time_to_live = IPDEFTTL,
> + .src_addr = rte_cpu_to_be_32(ip_src_addr),
> + .dst_addr = rte_cpu_to_be_32(ip_dst_addr),
> + };
> + unsigned int tcp_count = 0;
> +
> + rte_eth_random_addr(eh.dst_addr.addr_bytes);
> +
> + for (unsigned int i = 0; i < n; i++) {
> + struct rte_mbuf *mb = burst[i];
> +
> + /* Setup Ethernet header */
> + *rte_pktmbuf_mtod(mb, struct rte_ether_hdr *) = eh;
> +
> + /* Setup IP header */
> + struct rte_ipv4_hdr *ip
> + = rte_pktmbuf_mtod_offset(mb, struct rte_ipv4_hdr *,
> sizeof(eh));
> + *ip = iph;
> +
> + if (rte_rand() & 1) {
> + struct rte_udp_hdr *udp
> + = rte_pktmbuf_mtod_offset(mb, struct rte_udp_hdr
> *,
> + sizeof(eh) + sizeof(iph));
> +
> + ip->next_proto_id = IPPROTO_UDP;
> + *udp = (struct rte_udp_hdr) {
> + .src_port = rte_cpu_to_be_16(9), /* discard
> */
> + .dst_port = rte_cpu_to_be_16(9), /* discard
> */
> + .dgram_len = BPF_TEST_PKT_LEN - sizeof(eh) -
> sizeof(iph),
> + };
> +
> + } else {
> + struct rte_tcp_hdr *tcp
> + = rte_pktmbuf_mtod_offset(mb, struct rte_tcp_hdr
> *,
> + sizeof(eh) + sizeof(iph));
> +
> + ip->next_proto_id = IPPROTO_TCP;
> + *tcp = (struct rte_tcp_hdr) {
> + .src_port = rte_cpu_to_be_16(9), /* discard
> */
> + .dst_port = rte_cpu_to_be_16(9), /* discard
> */
> + .tcp_flags = RTE_TCP_RST_FLAG,
> + };
> + ++tcp_count;
> + }
> + }
> +
> + return tcp_count;
> +}
> +
> +static int bpf_tx_test(uint16_t port, const char *tmpfile, struct rte_mempool *pool,
> + const char *section, uint32_t flags)
> +{
> + const struct rte_bpf_prm prm = {
> + .prog_arg = {
> + .type = RTE_BPF_ARG_PTR,
> + .size = sizeof(struct rte_mbuf),
> + },
> + };
> + int ret;
> +
> + /* Try to load BPF TX program from temp file */
> + ret = rte_bpf_eth_tx_elf_load(port, 0, &prm, tmpfile, section, flags);
> + if (ret != 0) {
> + printf("%s@%d: failed to load BPF filter from file=%s
> error=%d:(%s)\n",
> + __func__, __LINE__, tmpfile, rte_errno,
> rte_strerror(rte_errno));
> + return 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);
> +
> + 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;
> +}
> +
> +/* Test loading a transmit filter which only allows IPv4 packets */
> +static int
> +test_bpf_elf_tx_load(void)
> +{
> + static const char null_dev[] = "net_null_bpf0";
> + char *tmpfile = NULL;
> + struct rte_mempool *mb_pool = NULL;
> + uint16_t port = UINT16_MAX;
> + int ret;
> +
> + printf("%s start\n", __func__);
> +
> + /* Make a pool for packets */
> + mb_pool = rte_pktmbuf_pool_create("bpf_tx_test_pool",
> BPF_TEST_POOLSIZE,
> + 0, 0, RTE_MBUF_DEFAULT_BUF_SIZE,
> + SOCKET_ID_ANY);
> +
> + ret = null_vdev_setup(null_dev, &port, mb_pool);
> + if (ret != 0)
> + goto fail;
> +
> + /* Create temp file from embedded BPF object */
> + tmpfile = create_temp_bpf_file(app_test_bpf_filter_o,
> app_test_bpf_filter_o_len, "tx");
> + if (tmpfile == NULL)
> + goto fail;
> +
> + /* Do test with VM */
> + ret = bpf_tx_test(port, tmpfile, mb_pool, "filter", 0);
> + if (ret != 0)
> + goto fail;
> +
> + /* Repeat with JIT */
> + ret = bpf_tx_test(port, tmpfile, mb_pool, "filter", RTE_BPF_ETH_F_JIT);
> + if (ret == 0)
> + printf("%s: TX ELF load test passed\n", __func__);
> +
> +fail:
> + if (tmpfile) {
> + unlink(tmpfile);
> + free(tmpfile);
> + }
> +
> + if (port != UINT16_MAX)
> + rte_vdev_uninit(null_dev);
> +
> + rte_mempool_free(mb_pool);
> +
> + if (ret == 0)
> + return TEST_SUCCESS;
> + else if (ret == -ENOTSUP)
> + return TEST_SKIPPED;
> + else
> + return TEST_FAILED;
> +}
> +
> +/* Test loading a receive filter */
> +static int bpf_rx_test(uint16_t port, const char *tmpfile, struct rte_mempool *pool,
> + const char *section, uint32_t flags, uint16_t expected)
> +{
> + struct rte_mbuf *pkts[BPF_TEST_BURST];
> + const struct rte_bpf_prm prm = {
> + .prog_arg = {
> + .type = RTE_BPF_ARG_PTR,
> + .size = sizeof(struct rte_mbuf),
> + },
> + };
> + int ret;
> +
> + /* Load BPF program to drop all packets */
> + ret = rte_bpf_eth_rx_elf_load(port, 0, &prm, tmpfile, section, flags);
> + if (ret != 0) {
> + printf("%s@%d: failed to load BPF filter from file=%s
> error=%d:(%s)\n",
> + __func__, __LINE__, tmpfile, rte_errno,
> rte_strerror(rte_errno));
> + return ret;
> + }
> +
> + uint16_t rcvd = rte_eth_rx_burst(port, 0, pkts, BPF_TEST_BURST);
> + TEST_ASSERT_EQUAL(rcvd, expected,
> + "rte_eth_rx_burst returned: %u expect: %u", rcvd,
> expected);
> +
> + /* Drop the received packets */
> + rte_pktmbuf_free_bulk(pkts, rcvd);
> +
> + rte_bpf_eth_rx_unload(port, 0);
> +
> + /* Pool should now be full */
> + unsigned int avail = rte_mempool_avail_count(pool);
> + TEST_ASSERT_EQUAL(avail, BPF_TEST_POOLSIZE,
> + "Mempool available %u != %u leaks?", avail,
> BPF_TEST_POOLSIZE);
> +
> + return TEST_SUCCESS;
> +}
> +
> +/* Test loading a receive filters, first with drop all and then with allow all packets */
> +static int
> +test_bpf_elf_rx_load(void)
> +{
> + static const char null_dev[] = "net_null_bpf0";
> + struct rte_mempool *pool = NULL;
> + char *tmpfile = NULL;
> + uint16_t port;
> + int ret;
> +
> + printf("%s start\n", __func__);
> +
> + /* Make a pool for packets */
> + pool = rte_pktmbuf_pool_create("bpf_rx_test_pool", 2 * BPF_TEST_BURST,
> + 0, 0, RTE_MBUF_DEFAULT_BUF_SIZE,
> + SOCKET_ID_ANY);
> + TEST_ASSERT(pool != NULL, "failed to create mempool");
> +
> + ret = null_vdev_setup(null_dev, &port, pool);
> + if (ret != 0)
> + goto fail;
> +
> + /* Create temp file from embedded BPF object */
> + tmpfile = create_temp_bpf_file(app_test_bpf_filter_o,
> app_test_bpf_filter_o_len, "rx");
> + if (tmpfile == NULL)
> + goto fail;
> +
> + /* Do test with VM */
> + ret = bpf_rx_test(port, tmpfile, pool, "drop", 0, 0);
> + if (ret != 0)
> + goto fail;
> +
> + /* Repeat with JIT */
> + ret = bpf_rx_test(port, tmpfile, pool, "drop", RTE_BPF_ETH_F_JIT, 0);
> + if (ret != 0)
> + goto fail;
> +
> + /* Repeat with allow all */
> + ret = bpf_rx_test(port, tmpfile, pool, "allow", 0, BPF_TEST_BURST);
> + if (ret != 0)
> + goto fail;
> +
> + /* Repeat with JIT */
> + ret = bpf_rx_test(port, tmpfile, pool, "allow", RTE_BPF_ETH_F_JIT,
> BPF_TEST_BURST);
> + if (ret != 0)
> + goto fail;
> +
> + printf("%s: RX ELF load test passed\n", __func__);
> +
> + /* The filter should free the mbufs */
> + unsigned int avail = rte_mempool_avail_count(pool);
> + TEST_ASSERT_EQUAL(avail, BPF_TEST_POOLSIZE,
> + "Mempool available %u != %u leaks?", avail,
> BPF_TEST_POOLSIZE);
> +
> +fail:
> + if (tmpfile) {
> + unlink(tmpfile);
> + free(tmpfile);
> + }
> +
> + if (port != UINT16_MAX)
> + rte_vdev_uninit(null_dev);
> +
> + rte_mempool_free(pool);
> +
> + return ret == 0 ? TEST_SUCCESS : TEST_FAILED;
> +}
> +
> +
> +static int
> +test_bpf_elf(void)
> +{
> + int ret;
> +
> + ret = test_bpf_elf_load();
> + if (ret == TEST_SUCCESS)
> + ret = test_bpf_elf_tx_load();
> + if (ret == TEST_SUCCESS)
> + ret = test_bpf_elf_rx_load();
> +
> + return ret;
> +}
> +
> #else
>
> static int
> -test_bpf_elf_load(void)
> +test_bpf_elf(void)
> {
> printf("BPF compile not supported, skipping test\n");
> return TEST_SKIPPED;
> @@ -3435,7 +3751,8 @@ test_bpf_elf_load(void)
>
> #endif /* !TEST_BPF_ELF_LOAD */
>
> -REGISTER_FAST_TEST(bpf_elf_load_autotest, true, true, test_bpf_elf_load);
> +
> +REGISTER_FAST_TEST(bpf_elf_autotest, true, true, test_bpf_elf);
>
> #ifndef RTE_HAS_LIBPCAP
>
> --
Acked-by: Konstantin Ananyev <konstantin.ananyev@huawei.com>
> 2.51.0
next prev parent reply other threads:[~2025-11-12 15:08 UTC|newest]
Thread overview: 60+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-10-30 17:34 [PATCH 0/5] bpf enhancements Stephen Hemminger
2025-10-30 17:34 ` [PATCH 1/5] bpf: add allocation annotations to functions Stephen Hemminger
2025-10-30 17:34 ` [PATCH 2/5] bpf: use rte_pktmbuf_free_bulk Stephen Hemminger
2025-10-30 17:34 ` [PATCH 3/5] bpf: add a test for BPF ELF load Stephen Hemminger
2025-10-30 17:34 ` [PATCH 4/5] bpf: add test for rx and tx filtering Stephen Hemminger
2025-10-30 17:34 ` [PATCH 5/5] bpf: remove use of vla Stephen Hemminger
2025-10-31 11:39 ` [PATCH 0/5] bpf enhancements Marat Khalili
2025-10-31 16:37 ` Stephen Hemminger
2025-10-31 16:41 ` [PATCH v2 0/5] BPF enhancements Stephen Hemminger
2025-10-31 16:41 ` [PATCH v2 1/5] bpf: add allocation annotations to functions Stephen Hemminger
2025-10-31 16:41 ` [PATCH v2 2/5] bpf: use bulk free on filtered packets Stephen Hemminger
2025-10-31 16:41 ` [PATCH v2 3/5] bpf: add a test for BPF ELF load Stephen Hemminger
2025-10-31 16:41 ` [PATCH v2 4/5] bpf: add test for Rx and Tx filtering Stephen Hemminger
2025-10-31 16:41 ` [PATCH v2 5/5] bpf: remove use of VLA Stephen Hemminger
2025-11-01 18:04 ` [PATCH v3 0/5] BPF enhancements Stephen Hemminger
2025-11-01 18:04 ` [PATCH v3 1/5] bpf: add allocation annotations to functions Stephen Hemminger
2025-11-02 21:42 ` Konstantin Ananyev
2025-11-01 18:04 ` [PATCH v3 2/5] bpf: use bulk free on filtered packets Stephen Hemminger
2025-11-01 18:04 ` [PATCH v3 3/5] bpf: add a test for BPF ELF load Stephen Hemminger
2025-11-01 18:04 ` [PATCH v3 4/5] bpf: add test for Rx and Tx filtering Stephen Hemminger
2025-11-01 18:04 ` [PATCH v3 5/5] bpf: remove use of VLA Stephen Hemminger
2025-11-03 9:21 ` Konstantin Ananyev
2025-11-04 16:07 ` [PATCH v4 0/5] BPF enhancements Stephen Hemminger
2025-11-04 16:07 ` [PATCH v4 1/5] bpf: add allocation annotations to functions Stephen Hemminger
2025-11-07 17:35 ` Marat Khalili
2025-11-04 16:07 ` [PATCH v4 2/5] bpf: use bulk free on filtered packets Stephen Hemminger
2025-11-06 7:25 ` Konstantin Ananyev
2025-11-07 17:36 ` Marat Khalili
2025-11-04 16:07 ` [PATCH v4 3/5] bpf: add a test for BPF ELF load Stephen Hemminger
2025-11-07 17:33 ` Marat Khalili
2025-11-07 17:45 ` Marat Khalili
2025-11-08 1:09 ` Stephen Hemminger
2025-11-10 15:34 ` Marat Khalili
2025-11-08 1:08 ` Stephen Hemminger
2025-11-04 16:07 ` [PATCH v4 4/5] bpf: add test for Rx and Tx filtering Stephen Hemminger
2025-11-07 17:30 ` Marat Khalili
2025-11-08 1:11 ` Stephen Hemminger
2025-11-10 15:43 ` Marat Khalili
2025-11-04 16:07 ` [PATCH v4 5/5] bpf: replace use of VLA Stephen Hemminger
2025-11-06 7:26 ` Konstantin Ananyev
2025-11-07 17:36 ` Marat Khalili
2025-11-09 20:07 ` [PATCH v5 0/5] BPF cleanup and tests Stephen Hemminger
2025-11-09 20:07 ` [PATCH v5 1/5] bpf: add allocation annotations to functions Stephen Hemminger
2025-11-09 20:07 ` [PATCH v5 2/5] bpf: use bulk free on filtered packets Stephen Hemminger
2025-11-09 20:07 ` [PATCH v5 3/5] bpf: add a test for BPF ELF load Stephen Hemminger
2025-11-10 16:38 ` Marat Khalili
2025-11-10 17:07 ` Stephen Hemminger
2025-11-10 17:17 ` Marat Khalili
2025-11-10 17:08 ` Stephen Hemminger
2025-11-11 9:46 ` Marat Khalili
2025-11-09 20:07 ` [PATCH v5 4/5] bpf: add test for Rx and Tx filtering Stephen Hemminger
2025-11-11 9:46 ` Marat Khalili
2025-11-09 20:07 ` [PATCH v5 5/5] bpf: replace use of VLA Stephen Hemminger
2025-11-11 12:13 ` [PATCH v5 0/5] BPF cleanup and tests Thomas Monjalon
2025-11-11 22:55 ` [PATCH v6 0/2] BPF tests Stephen Hemminger
2025-11-11 22:55 ` [PATCH v6 1/2] bpf: add a test for BPF ELF load Stephen Hemminger
2025-11-12 15:06 ` Konstantin Ananyev
2025-11-11 22:55 ` [PATCH v6 2/2] bpf: add test for Rx and Tx filtering Stephen Hemminger
2025-11-12 15:08 ` Konstantin Ananyev [this message]
2025-11-12 15:03 ` [PATCH v6 0/2] BPF tests Marat Khalili
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=bb1bce77ccfd46b5850439be9bcb5607@huawei.com \
--to=konstantin.ananyev@huawei.com \
--cc=dev@dpdk.org \
--cc=marat.khalili@huawei.com \
--cc=stephen@networkplumber.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).