> This example demonstrates usage of the DPDK ACL library
> to classify packets based on:
> 
>   - protocol
>   - source IPv4
>   - destination IPv4
>   - source port
>   - destination port
> 
> Packets matching ACL rules are forwarded,
> others are dropped.
> 
> The example provides a minimal reference
> for developers wanting to integrate ACL
> classification into packet forwarding paths.

Can you probably explain why you think that
l3fwd in acl mode is not enough and extra one is needed?

> 
> Signed-off-by: Harsh Raj Singh <[email protected]>
> 
> From 27423213cde0594243b96114d67ade6f51cdf764 Mon Sep 17 00:00:00 2001
> From: Harsh Raj Singh <[email protected]>
> Date: Thu, 19 Feb 2026 14:16:16 +0530
> Subject: [PATCH] examples/l2fwd_acl: add ACL-based L2 forwarding example
> 
> This example demonstrates usage of the DPDK ACL library
> to classify packets based on:
> 
>   - protocol
>   - source IPv4
>   - destination IPv4
>   - source port
>   - destination port
> 
> Packets matching ACL rules are forwarded,
> others are dropped.
> 
> The example provides a minimal reference
> for developers wanting to integrate ACL
> classification into packet forwarding paths.
> 
> Signed-off-by: Harsh Raj Singh <[email protected]>
> ---
>  examples/l2fwd_acl/README.md   |  18 +++
>  examples/l2fwd_acl/main.c      | 247 +++++++++++++++++++++++++++++++++
>  examples/l2fwd_acl/meson.build |   3 +
>  examples/meson.build           |   1 +
>  4 files changed, 269 insertions(+)
>  create mode 100644 examples/l2fwd_acl/README.md
>  create mode 100644 examples/l2fwd_acl/main.c
>  create mode 100644 examples/l2fwd_acl/meson.build
> 
> diff --git a/examples/l2fwd_acl/README.md b/examples/l2fwd_acl/README.md
> new file mode 100644
> index 0000000000..5b5494e297
> --- /dev/null
> +++ b/examples/l2fwd_acl/README.md
> @@ -0,0 +1,18 @@
> +L2 Forwarding with ACL Example
> +==============================
> +
> +This example demonstrates use of the DPDK ACL library
> +to classify packets based on:
> +
> +- protocol
> +- source IPv4
> +- destination IPv4
> +- source port
> +- destination port
> +
> +Packets matching ACL rules are forwarded,
> +others are dropped.
> +
> +Run:
> +
> +./dpdk-l2fwd-acl -l 0-2 -n 4 -- -p 0x1
> diff --git a/examples/l2fwd_acl/main.c b/examples/l2fwd_acl/main.c
> new file mode 100644
> index 0000000000..6e05145b33
> --- /dev/null
> +++ b/examples/l2fwd_acl/main.c
> @@ -0,0 +1,247 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * l2fwd_acl example: demonstrates use of DPDK ACL library
> + */
> +
> +#include <stdbool.h>
> +#include <signal.h>
> +#include <getopt.h>
> +#include <stdlib.h>
> +
> +#include <rte_acl.h>
> +#include <rte_eal.h>
> +#include <rte_ethdev.h>
> +#include <rte_ether.h>
> +#include <rte_ip.h>
> +#include <rte_mbuf.h>
> +#include <rte_tcp.h>
> +#include <rte_udp.h>
> +#include <rte_log.h>
> +
> +#define NUM_FIELDS 5
> +#define MAX_RULES  32
> +#define NB_MBUF    8192
> +#define BURST      32
> +
> +/* ---- Correct DPDK logging style ---- */
> +#define RTE_LOGTYPE_L2FWD_ACL RTE_LOGTYPE_USER1
> +#define LOGTYPE_L2FWD_ACL RTE_LOGTYPE_L2FWD_ACL
> +
> +RTE_ACL_RULE_DEF(acl_rule, NUM_FIELDS);
> +
> +struct pkt_key {
> +    uint8_t  proto;
> +    uint32_t src_ip;
> +    uint32_t dst_ip;
> +    uint16_t src_port;
> +    uint16_t dst_port;
> +};
> +
> +enum {
> +    PROTO_FIELD,
> +    SRC_FIELD,
> +    DST_FIELD,
> +    SPORT_FIELD,
> +    DPORT_FIELD
> +};
> +
> +static volatile bool force_quit;
> +static uint32_t portmask = 0x1;
> +
> +static struct rte_acl_field_def field_defs[NUM_FIELDS] = {
> +    { RTE_ACL_FIELD_TYPE_BITMASK, sizeof(uint8_t),  PROTO_FIELD, 0,
> offsetof(struct pkt_key, proto) },
> +    { RTE_ACL_FIELD_TYPE_MASK,    sizeof(uint32_t), SRC_FIELD,   1,
> offsetof(struct pkt_key, src_ip) },
> +    { RTE_ACL_FIELD_TYPE_MASK,    sizeof(uint32_t), DST_FIELD,   2,
> offsetof(struct pkt_key, dst_ip) },
> +    { RTE_ACL_FIELD_TYPE_RANGE,   sizeof(uint16_t), SPORT_FIELD, 3,
> offsetof(struct pkt_key, src_port) },
> +    { RTE_ACL_FIELD_TYPE_RANGE,   sizeof(uint16_t), DPORT_FIELD, 3,
> offsetof(struct pkt_key, dst_port) },
> +};
> +
> +static void
> +signal_handler(int sig)
> +{
> +    if (sig == SIGINT || sig == SIGTERM)
> +        force_quit = true;
> +}
> +
> +static int
> +parse_args(int argc, char **argv)
> +{
> +    int opt;
> +    while ((opt = getopt(argc, argv, "p:")) != -1) {
> +        switch (opt) {
> +        case 'p':
> +            portmask = strtoul(optarg, NULL, 16);
> +            break;
> +        default:
> +            return -1;
> +        }
> +    }
> +    return 0;
> +}
> +
> +/* ---------------- ACL INITIALIZATION ---------------- */
> +
> +static struct rte_acl_ctx *
> +init_acl(void)
> +{
> +    struct rte_acl_param prm = {
> +        .name = "acl_ctx",
> +        .socket_id = rte_socket_id(),
> +        .rule_size = RTE_ACL_RULE_SZ(NUM_FIELDS),
> +        .max_rule_num = MAX_RULES
> +    };
> +
> +    struct rte_acl_ctx *ctx = rte_acl_create(&prm);
> +    if (!ctx)
> +        rte_exit(EXIT_FAILURE, "ACL create failed\n");
> +
> +    struct acl_rule rule;
> +    memset(&rule, 0, sizeof(rule));
> +
> +    rule.data.priority = 10;
> +    rule.data.category_mask = 1;
> +    rule.data.userdata = 1;
> +
> +    rule.field[PROTO_FIELD].value.u8 = IPPROTO_TCP;
> +    rule.field[PROTO_FIELD].mask_range.u8 = 0xFF;
> +
> +    rule.field[SRC_FIELD].value.u32 =
> +        rte_be_to_cpu_32(RTE_IPV4(172,17,166,200));
> +    rule.field[SRC_FIELD].mask_range.u32 = 32;
> +
> +    rule.field[DST_FIELD].value.u32 = 0;
> +    rule.field[DST_FIELD].mask_range.u32 = 0;
> +
> +    rule.field[SPORT_FIELD].value.u16 = 0;
> +    rule.field[SPORT_FIELD].mask_range.u16 = 65535;
> +
> +    rule.field[DPORT_FIELD].value.u16 = 0;
> +    rule.field[DPORT_FIELD].mask_range.u16 = 65535;
> +
> +    if (rte_acl_add_rules(ctx, (struct rte_acl_rule *)&rule, 1) < 0)
> +        rte_exit(EXIT_FAILURE, "ACL rule add failed\n");
> +
> +    struct rte_acl_config cfg;
> +    memset(&cfg, 0, sizeof(cfg));
> +    cfg.num_categories = 1;
> +    cfg.num_fields = NUM_FIELDS;
> +    memcpy(cfg.defs, field_defs, sizeof(field_defs));
> +
> +    if (rte_acl_build(ctx, &cfg) != 0)
> +        rte_exit(EXIT_FAILURE, "ACL build failed\n");
> +
> +    RTE_LOG(INFO, L2FWD_ACL, "ACL ready\n");
> +    return ctx;
> +}
> +
> +/* ---------------- MAIN ---------------- */
> +
> +int main(int argc, char **argv)
> +{
> +    force_quit = false;
> +    signal(SIGINT, signal_handler);
> +    signal(SIGTERM, signal_handler);
> +
> +    int ret = rte_eal_init(argc, argv);
> +    if (ret < 0)
> +        rte_exit(EXIT_FAILURE, "EAL init failed\n");
> +
> +    argc -= ret;
> +    argv += ret;
> +
> +    if (parse_args(argc, argv) < 0)
> +        rte_exit(EXIT_FAILURE, "Invalid arguments\n");
> +
> +    struct rte_acl_ctx *acl_ctx = init_acl();
> +
> +    struct rte_mempool *mp = rte_pktmbuf_pool_create(
> +        "mbuf_pool", NB_MBUF, 256, 0,
> +        RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
> +
> +    if (!mp)
> +        rte_exit(EXIT_FAILURE, "mempool failed\n");
> +
> +    uint16_t port_id;
> +    RTE_ETH_FOREACH_DEV(port_id) {
> +
> +        if (!(portmask & (1 << port_id)))
> +            continue;
> +
> +        struct rte_eth_conf conf = {0};
> +
> +        if (rte_eth_dev_configure(port_id, 1, 1, &conf) < 0)
> +            rte_exit(EXIT_FAILURE, "port configure failed\n");
> +
> +        if (rte_eth_rx_queue_setup(port_id, 0, 1024,
> +            rte_eth_dev_socket_id(port_id), NULL, mp) < 0)
> +            rte_exit(EXIT_FAILURE, "rx setup failed\n");
> +
> +        if (rte_eth_tx_queue_setup(port_id, 0, 1024,
> +            rte_eth_dev_socket_id(port_id), NULL) < 0)
> +            rte_exit(EXIT_FAILURE, "tx setup failed\n");
> +
> +        if (rte_eth_dev_start(port_id) < 0)
> +            rte_exit(EXIT_FAILURE, "port start failed\n");
> +
> +        rte_eth_promiscuous_enable(port_id);
> +
> +        RTE_LOG(INFO, L2FWD_ACL, "Started port %u\n", port_id);
> +    }
> +
> +    struct rte_mbuf *bufs[BURST];
> +
> +    while (!force_quit) {
> +
> +        RTE_ETH_FOREACH_DEV(port_id) {
> +
> +            if (!(portmask & (1 << port_id)))
> +                continue;
> +
> +            uint16_t nb = rte_eth_rx_burst(port_id, 0, bufs, BURST);
> +            if (!nb) continue;
> +
> +            for (int i = 0; i < nb; i++) {
> +
> +                struct rte_mbuf *m = bufs[i];
> +                struct rte_ether_hdr *eth =
> +                    rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
> +
> +                if (eth->ether_type !=
> +                    rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4)) {
> +                    rte_pktmbuf_free(m);
> +                    continue;
> +                }
> +
> +                struct rte_ipv4_hdr *ip =
> +                    (struct rte_ipv4_hdr *)(eth + 1);
> +
> +                struct pkt_key key = {0};
> +                key.proto  = ip->next_proto_id;
> +                key.src_ip = rte_be_to_cpu_32(ip->src_addr);
> +                key.dst_ip = rte_be_to_cpu_32(ip->dst_addr);
> +
> +                uint8_t *l4 = (uint8_t *)ip + (ip->ihl * 4);
> +
> +                if (key.proto == IPPROTO_TCP) {
> +                    struct rte_tcp_hdr *tcp = (struct rte_tcp_hdr *)l4;
> +                    key.src_port = rte_be_to_cpu_16(tcp->src_port);
> +                    key.dst_port = rte_be_to_cpu_16(tcp->dst_port);
> +                } else if (key.proto == IPPROTO_UDP) {
> +                    struct rte_udp_hdr *udp = (struct rte_udp_hdr *)l4;
> +                    key.src_port = rte_be_to_cpu_16(udp->src_port);
> +                    key.dst_port = rte_be_to_cpu_16(udp->dst_port);
> +                }
> +
> +                const uint8_t *data[1] = {(const uint8_t *)&key};
> +                uint32_t res[1];
> +
> +                rte_acl_classify(acl_ctx, data, res, 1, 1);
> +
> +                if (res[0])
> +                    rte_eth_tx_burst(port_id, 0, &m, 1);
> +                else
> +                    rte_pktmbuf_free(m);
> +            }
> +        }
> +    }
> +
> +    return 0;
> diff --git a/examples/l2fwd_acl/meson.build b/examples/l2fwd_acl/meson.build
> new file mode 100644
> index 0000000000..331dd545e9
> --- /dev/null
> +++ b/examples/l2fwd_acl/meson.build
> @@ -0,0 +1,3 @@
> +sources = files('main.c')
> +
> +deps += ['acl', 'ethdev', 'mbuf', 'net']
> diff --git a/examples/meson.build b/examples/meson.build
> index 25d9c88457..bce2982229 100644
> --- a/examples/meson.build
> +++ b/examples/meson.build
> @@ -24,6 +24,7 @@ all_examples = [
>          'ipsec-secgw',
>          'ipv4_multicast',
>          'l2fwd',
> +        'l2fwd_acl',
>          'l2fwd-cat',
>          'l2fwd-crypto',
>          'l2fwd-event',
> --
> 2.43.0

Reply via email to