From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by smtp.lore.kernel.org (Postfix) with ESMTP id B3514CD3439 for ; Tue, 5 May 2026 18:39:50 +0000 (UTC) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id D06B940A6B; Tue, 5 May 2026 20:39:35 +0200 (CEST) Received: from mail-wr1-f46.google.com (mail-wr1-f46.google.com [209.85.221.46]) by mails.dpdk.org (Postfix) with ESMTP id E3FED4067B for ; Tue, 5 May 2026 20:39:29 +0200 (CEST) Received: by mail-wr1-f46.google.com with SMTP id ffacd0b85a97d-43fe608cb92so3321154f8f.2 for ; Tue, 05 May 2026 11:39:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=dyna-nic.com; s=google; t=1778006369; x=1778611169; darn=dpdk.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=g2rBV/YnCNbyK5aysV/de0P+gRwnWBE9AMWd56uB4NI=; b=dr10bPj6hMQz1zB646PCKhtGBHD7ZC+0JDfnVjbr7RMOEYwawQu4wKEeFqn6EjsOd9 SPSwq6qQNq0UCLYDsUOJmkxFyWrhKHGGJCeCZyk8M0ZuoFXtDqHTUT7TBqoRskrQBQfB rCN8DI7Vxw4OOKxsR+WrPv/7tkTA4Q8RUF7hwt8HI3KqCKE6r86VDvui8GPXiGNw29KH n9IvQASghbtD7dq+os5ukA1bbV8EcmcC1YKwHL96hotBnDQFLAPf3LGknGbHjeyO1uX5 p/Zs2W8+RobW9xxpwbdrM8b6f0ZrCkinaDkMYIsviVd/7n+Lk3kqpwDkgazneIWz5Lr0 flNg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778006369; x=1778611169; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=g2rBV/YnCNbyK5aysV/de0P+gRwnWBE9AMWd56uB4NI=; b=qeksXvz28JGh8WKFUrNiNQFTbZw6kZFRZl5Gl/Ynos4yc2YE3KwzWpUKiZcDnCI/V2 YXGsTzT/9R6E2jZ5xPCGApwW+8lVzceDTXcZUZ7u6jt75ZDrzfw4ScpEa9kGMvVcuDIq 1xUHK+9HTiyvOcKfaLqKfcyfxH/zBzZBKJo0TXMJQHr/qfWqAf5nvIMY2sO2H4VRTK03 TOsxSpZmY0vtMvt/t7GBY1AClX2gZuVX2pk/20+/ECrOPEeq6c2JXK/jMhu7huaguwbj xOzoNu9a3ZolfIJcHRKmOwdnS59p7zRwvXgw7SNsTQm7Cyt4OVHYj+4lqGKKjPZOTWVQ GQJw== X-Gm-Message-State: AOJu0Yx3PwmtlzO6LpOVqFQ2EZqy5RdTsx/0wdRNscDe6PyNZ3SJBu9+ frIvMVth9M1JQrSLOyT/T0ttc0CEWdUgCh84Fb4Gxc0RfA5Ogb5PLHrI65Ane0Ub1IP5PIrfKyu fMKXN X-Gm-Gg: AeBDiesZsahuBE8DmwbG998xgut6oAIZHBwaikSHDxkgW5Xs+DdnIKCSAMBlUIE4HEI dU+cQx3j0yK0r3tYdOjVSJZ5M4q9dC5CNb4I3zRa0lxd4rY5bScj2hBWF43QvFYkdldEe4Cm53W kL9Hmq4vo6UAd92vIEAQiOPf3Ow1WZc8HLyr24RdkTt3ZOoKf88srxnJghDTJcU0So0UqnLtEKQ 0xe2Oqf96dgzK5x9mEIXhDdWOvB9iDNRyttf2KoNUlSsuMUXbKcOcyxHau+wMXibZRzRips6V2m wihtu+6YVs8U6w75+ijUqy3RLyw6vIg4lTmeQ9ZTqd/zlYIy6OQWY5y8yInSQz3baiZCElYo+Sc yoCDBpIYtdMhQfM8q7rrW5U23m+CH2x3QKkiEGgvNylMIUdyZbQyAQEkBeU8o53e/jHo7Iq0x4k XHSKrqFB+BO0/vTY8OMvnQYD+SRtRkVAEZ/BeNS+rcJnh42VrThsRSrz1oHGhTDXDlfdFqd1MVy g== X-Received: by 2002:a05:6000:2f83:b0:43c:fe0e:5bbc with SMTP id ffacd0b85a97d-4515bae9122mr493273f8f.19.1778006369211; Tue, 05 May 2026 11:39:29 -0700 (PDT) Received: from dpdk-test3.liberouter.org (rt-tmc-kou.liberouter.org. [195.113.172.126]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-45054b02abbsm6197067f8f.18.2026.05.05.11.39.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 05 May 2026 11:39:28 -0700 (PDT) From: Lukas Sismis To: dev@dpdk.org Cc: orika@nvidia.com, stephen@networkplumber.org, thomas@monjalon.net, Lukas Sismis Subject: [PATCH v12 5/6] examples/flow_parsing: add flow parser demo Date: Tue, 5 May 2026 20:39:12 +0200 Message-ID: <20260505183917.370281-6-sismis@dyna-nic.com> X-Mailer: git-send-email 2.43.7 In-Reply-To: <20260505183917.370281-1-sismis@dyna-nic.com> References: <20260505183917.370281-1-sismis@dyna-nic.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Add a standalone example demonstrating the flow parser API. Parses attribute, pattern, and action strings into rte_flow structures and prints the results. Signed-off-by: Lukas Sismis --- MAINTAINERS | 2 + doc/guides/sample_app_ug/flow_parsing.rst | 60 ++++ doc/guides/sample_app_ug/index.rst | 1 + examples/flow_parsing/main.c | 409 ++++++++++++++++++++++ examples/flow_parsing/meson.build | 8 + examples/meson.build | 1 + 6 files changed, 481 insertions(+) create mode 100644 doc/guides/sample_app_ug/flow_parsing.rst create mode 100644 examples/flow_parsing/main.c create mode 100644 examples/flow_parsing/meson.build diff --git a/MAINTAINERS b/MAINTAINERS index fdd0555bb4..fdd841c835 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -448,6 +448,8 @@ F: doc/guides/prog_guide/ethdev/flow_offload.rst F: doc/guides/prog_guide/flow_parser_lib.rst F: app/test-pmd/flow_parser* F: lib/ethdev/rte_flow* +F: doc/guides/sample_app_ug/flow_parsing.rst +F: examples/flow_parsing/ Traffic Management API M: Cristian Dumitrescu diff --git a/doc/guides/sample_app_ug/flow_parsing.rst b/doc/guides/sample_app_ug/flow_parsing.rst new file mode 100644 index 0000000000..a037951c5e --- /dev/null +++ b/doc/guides/sample_app_ug/flow_parsing.rst @@ -0,0 +1,60 @@ +.. SPDX-License-Identifier: BSD-3-Clause + Copyright(c) 2026 DynaNIC Semiconductors, Ltd. + +Flow Parsing Sample Application +================================ + +Overview +-------- + +The flow parsing sample application demonstrates how to use the ethdev flow +parser library to convert testpmd-style flow rule strings into ``rte_flow`` C +structures without requiring EAL initialization. + + +Compiling the Application +------------------------- + +To compile the sample application, see :doc:`compiling`. + +The application is located in the ``flow_parsing`` sub-directory. + + +Running the Application +----------------------- + +Since this example does not use EAL, it can be run directly: + +.. code-block:: console + + ./build/examples/dpdk-flow_parsing + +The application prints parsed attributes, patterns, and actions for several +example flow rule strings. + + +Example Output +-------------- + +.. code-block:: none + + === Parsing Flow Attributes === + Input: "ingress" + Attributes: + group=0 priority=0 + ingress=1 egress=0 transfer=0 + + === Parsing Flow Patterns === + Input: "eth / ipv4 src is 192.168.1.1 / end" + Pattern (3 items): + [0] ETH (any) + [1] IPV4 src=192.168.1.1 dst=0.0.0.0 + [2] END + + === Parsing Flow Actions === + Input: "mark id 100 / count / queue index 5 / end" + Actions (4 items): + [0] MARK id=100 + [1] COUNT + [2] QUEUE index=5 + [3] END diff --git a/doc/guides/sample_app_ug/index.rst b/doc/guides/sample_app_ug/index.rst index e895f692f9..eadea67569 100644 --- a/doc/guides/sample_app_ug/index.rst +++ b/doc/guides/sample_app_ug/index.rst @@ -16,6 +16,7 @@ Sample Applications User Guides skeleton rxtx_callbacks flow_filtering + flow_parsing ip_frag ipv4_multicast ip_reassembly diff --git a/examples/flow_parsing/main.c b/examples/flow_parsing/main.c new file mode 100644 index 0000000000..243cd56a70 --- /dev/null +++ b/examples/flow_parsing/main.c @@ -0,0 +1,409 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2026 DynaNIC Semiconductors, Ltd. + */ + +/* + * Flow Parsing Example + * ==================== + * This example demonstrates how to use the ethdev flow parser to parse + * flow rule strings into rte_flow C structures. The library provides ONE WAY + * to create rte_flow structures - by parsing testpmd-style command strings. + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* Helper to print flow attributes */ +static void +print_attr(const struct rte_flow_attr *attr) +{ + printf(" Attributes:\n"); + printf(" group=%u priority=%u\n", attr->group, attr->priority); + printf(" ingress=%u egress=%u transfer=%u\n", + attr->ingress, attr->egress, attr->transfer); +} + +/* Helper to print a MAC address */ +static void +print_mac(const char *label, const struct rte_ether_addr *mac) +{ + char buf[RTE_ETHER_ADDR_FMT_SIZE]; + + rte_ether_format_addr(buf, sizeof(buf), mac); + printf(" %s: %s\n", label, buf); +} + +/* Helper to print pattern items */ +static void +print_pattern(const struct rte_flow_item *pattern, uint32_t pattern_n) +{ + uint32_t i; + + printf(" Pattern (%u items):\n", pattern_n); + for (i = 0; i < pattern_n; i++) { + const struct rte_flow_item *item = &pattern[i]; + + switch (item->type) { + case RTE_FLOW_ITEM_TYPE_END: + printf(" [%u] END\n", i); + break; + case RTE_FLOW_ITEM_TYPE_ETH: + printf(" [%u] ETH", i); + if (item->spec) { + const struct rte_flow_item_eth *eth = item->spec; + + printf("\n"); + print_mac("dst", ð->hdr.dst_addr); + print_mac("src", ð->hdr.src_addr); + } else { + printf(" (any)\n"); + } + break; + case RTE_FLOW_ITEM_TYPE_IPV4: + printf(" [%u] IPV4", i); + if (item->spec) { + const struct rte_flow_item_ipv4 *ipv4 = item->spec; + const uint8_t *s = (const uint8_t *)&ipv4->hdr.src_addr; + const uint8_t *d = (const uint8_t *)&ipv4->hdr.dst_addr; + + printf(" src=%u.%u.%u.%u dst=%u.%u.%u.%u\n", + s[0], s[1], s[2], s[3], + d[0], d[1], d[2], d[3]); + } else { + printf(" (any)\n"); + } + break; + case RTE_FLOW_ITEM_TYPE_TCP: + printf(" [%u] TCP", i); + if (item->spec) { + const struct rte_flow_item_tcp *tcp = item->spec; + + printf(" sport=%u dport=%u\n", + rte_be_to_cpu_16(tcp->hdr.src_port), + rte_be_to_cpu_16(tcp->hdr.dst_port)); + } else { + printf(" (any)\n"); + } + break; + case RTE_FLOW_ITEM_TYPE_UDP: + printf(" [%u] UDP", i); + if (item->spec) { + const struct rte_flow_item_udp *udp = item->spec; + + printf(" sport=%u dport=%u\n", + rte_be_to_cpu_16(udp->hdr.src_port), + rte_be_to_cpu_16(udp->hdr.dst_port)); + } else { + printf(" (any)\n"); + } + break; + default: + printf(" [%u] type=%d\n", i, item->type); + break; + } + } +} + +/* Helper to print actions */ +static void +print_actions(const struct rte_flow_action *actions, uint32_t actions_n) +{ + uint32_t i; + + printf(" Actions (%u items):\n", actions_n); + for (i = 0; i < actions_n; i++) { + const struct rte_flow_action *action = &actions[i]; + + switch (action->type) { + case RTE_FLOW_ACTION_TYPE_END: + printf(" [%u] END\n", i); + break; + case RTE_FLOW_ACTION_TYPE_DROP: + printf(" [%u] DROP\n", i); + break; + case RTE_FLOW_ACTION_TYPE_QUEUE: + if (action->conf) { + const struct rte_flow_action_queue *q = action->conf; + + printf(" [%u] QUEUE index=%u\n", i, q->index); + } else { + printf(" [%u] QUEUE\n", i); + } + break; + case RTE_FLOW_ACTION_TYPE_MARK: + if (action->conf) { + const struct rte_flow_action_mark *m = action->conf; + + printf(" [%u] MARK id=%u\n", i, m->id); + } else { + printf(" [%u] MARK\n", i); + } + break; + case RTE_FLOW_ACTION_TYPE_COUNT: + printf(" [%u] COUNT\n", i); + break; + case RTE_FLOW_ACTION_TYPE_PORT_ID: + if (action->conf) { + const struct rte_flow_action_port_id *p = action->conf; + + printf(" [%u] PORT_ID id=%u\n", i, p->id); + } else { + printf(" [%u] PORT_ID\n", i); + } + break; + default: + printf(" [%u] type=%d\n", i, action->type); + break; + } + } +} + +/* + * Demonstrate parsing flow attributes + */ +static void +demo_parse_attr(void) +{ + static const char * const attr_strings[] = { + "ingress", + "egress", + "ingress priority 5", + "ingress group 1 priority 10", + "transfer", + }; + struct rte_flow_attr attr; + unsigned int i; + int ret; + + printf("\n=== Parsing Flow Attributes ===\n"); + printf("Use rte_flow_parser_parse_attr_str() to parse attribute strings.\n\n"); + + for (i = 0; i < RTE_DIM(attr_strings); i++) { + printf("Input: \"%s\"\n", attr_strings[i]); + memset(&attr, 0, sizeof(attr)); + ret = rte_flow_parser_parse_attr_str(attr_strings[i], &attr); + if (ret == 0) + print_attr(&attr); + else + printf(" ERROR: %d (%s)\n", ret, strerror(-ret)); + printf("\n"); + } +} + +/* + * Demonstrate parsing flow patterns + */ +static void +demo_parse_pattern(void) +{ + static const char * const pattern_strings[] = { + "eth / end", + "eth dst is 90:61:ae:fd:41:43 / end", + "eth / ipv4 src is 192.168.1.1 / end", + "eth / ipv4 / tcp dst is 80 / end", + "eth / ipv4 src is 10.0.0.1 dst is 10.0.0.2 / udp src is 1234 dst is 5678 / end", + }; + const struct rte_flow_item *pattern; + uint32_t pattern_n; + unsigned int i; + int ret; + + printf("\n=== Parsing Flow Patterns ===\n"); + printf("Use rte_flow_parser_parse_pattern_str() to parse pattern strings.\n\n"); + + for (i = 0; i < RTE_DIM(pattern_strings); i++) { + printf("Input: \"%s\"\n", pattern_strings[i]); + ret = rte_flow_parser_parse_pattern_str(pattern_strings[i], + &pattern, &pattern_n); + if (ret == 0) + print_pattern(pattern, pattern_n); + else + printf(" ERROR: %d (%s)\n", ret, strerror(-ret)); + printf("\n"); + } +} + +/* + * Demonstrate parsing flow actions + */ +static void +demo_parse_actions(void) +{ + static const char * const action_strings[] = { + "drop / end", + "queue index 3 / end", + "mark id 42 / end", + "count / queue index 1 / end", + "mark id 100 / count / queue index 5 / end", + }; + const struct rte_flow_action *actions; + uint32_t actions_n; + unsigned int i; + int ret; + + printf("\n=== Parsing Flow Actions ===\n"); + printf("Use rte_flow_parser_parse_actions_str() to parse action strings.\n\n"); + + for (i = 0; i < RTE_DIM(action_strings); i++) { + printf("Input: \"%s\"\n", action_strings[i]); + ret = rte_flow_parser_parse_actions_str(action_strings[i], + &actions, &actions_n); + if (ret == 0) + print_actions(actions, actions_n); + else + printf(" ERROR: %d (%s)\n", ret, strerror(-ret)); + printf("\n"); + } +} + +/* + * Demonstrate full command parsing + */ +static void +demo_full_command_parse(void) +{ + uint8_t buf[4096]; + struct rte_flow_parser_output *out = (void *)buf; + int ret; + + static const char * const commands[] = { + "flow create 0 ingress pattern eth / ipv4 / end actions drop / end", + "flow validate 0 ingress pattern eth / ipv4 / tcp dst is 80 / end actions queue index 3 / end", + "flow list 0", + "flow flush 0", + }; + + printf("\n=== Full Command Parsing ===\n"); + printf("Use rte_flow_parser_parse() from rte_flow_parser_config.h\n"); + printf("to parse complete flow CLI commands.\n\n"); + + for (unsigned int i = 0; i < RTE_DIM(commands); i++) { + printf("Input: \"%s\"\n", commands[i]); + memset(buf, 0, sizeof(buf)); + ret = rte_flow_parser_parse(commands[i], out, sizeof(buf)); + if (ret == 0) { + printf(" command=%d port=%u\n", + out->command, out->port); + if (out->command == RTE_FLOW_PARSER_CMD_CREATE || + out->command == RTE_FLOW_PARSER_CMD_VALIDATE) + printf(" pattern_n=%u actions_n=%u\n", + out->args.vc.pattern_n, + out->args.vc.actions_n); + } else { + printf(" ERROR: %d (%s)\n", ret, strerror(-ret)); + } + printf("\n"); + } +} + +/* + * Demonstrate configuration registration + */ +static void +demo_config_registration(void) +{ + static struct rte_flow_parser_vxlan_encap_conf vxlan; + static struct rte_flow_parser_raw_encap_data raw_encap[2]; + const struct rte_flow_item *items; + uint32_t items_n; + int ret; + + printf("\n=== Configuration Registration ===\n"); + printf("Applications own config storage and register it\n"); + printf("with rte_flow_parser_config_register().\n\n"); + + memset(raw_encap, 0, sizeof(raw_encap)); + + struct rte_flow_parser_config cfg = { + .vxlan_encap = &vxlan, + .raw_encap = { raw_encap, 2 }, + }; + ret = rte_flow_parser_config_register(&cfg); + printf("config_register: %s\n\n", ret == 0 ? "OK" : "FAILED"); + + /* Write directly to app-owned config */ + vxlan.select_ipv4 = 1; + vxlan.vni[0] = 0x12; + vxlan.vni[1] = 0x34; + vxlan.vni[2] = 0x56; + /* + * Parse a flow rule that references vxlan_encap. + * The parser reads the config we just wrote above. + */ + uint8_t buf[4096]; + struct rte_flow_parser_output *out = (void *)buf; + + ret = rte_flow_parser_parse( + "flow create 0 transfer pattern eth / end " + "actions vxlan_encap / port_id id 1 / end", + out, sizeof(buf)); + if (ret == 0 && out->args.vc.actions_n > 0) { + const struct rte_flow_action *act = &out->args.vc.actions[0]; + + printf("Parsed vxlan_encap action: type=%d conf=%s\n", + act->type, act->conf ? "present" : "NULL"); + if (act->conf) { + const struct rte_flow_action_vxlan_encap *ve = + act->conf; + const struct rte_flow_item *item = ve->definition; + unsigned int n = 0; + + printf(" Encap tunnel headers:"); + while (item && item->type != RTE_FLOW_ITEM_TYPE_END) { + printf(" 0x%02x", item->type); + item++; + n++; + } + printf(" (%u items)\n", n); + } + } else { + printf("vxlan_encap parse: %s\n", + ret == 0 ? "no actions" : strerror(-ret)); + } + + printf("VXLAN config: ipv4=%u vni=0x%02x%02x%02x\n", + vxlan.select_ipv4, + vxlan.vni[0], vxlan.vni[1], vxlan.vni[2]); + printf("\n"); + + /* Use setter API for raw encap */ + ret = rte_flow_parser_parse_pattern_str( + "eth / ipv4 / udp / vxlan / end", &items, &items_n); + if (ret == 0) { + ret = rte_flow_parser_raw_encap_conf_set(0, items, items_n); + printf("raw_encap_conf_set: %s\n", + ret == 0 ? "OK" : "FAILED"); + } + + const struct rte_flow_action_raw_encap *encap = + rte_flow_parser_raw_encap_conf(0); + if (encap != NULL) + printf("raw_encap[0]: %zu bytes serialized\n", encap->size); + printf("\n"); +} + +int +main(void) +{ + printf("Flow Parser Library Example\n"); + printf("===========================\n"); + + /* Run demonstrations */ + demo_parse_attr(); + demo_parse_pattern(); + demo_parse_actions(); + demo_config_registration(); + demo_full_command_parse(); + + printf("\n=== Example Complete ===\n"); + return 0; +} diff --git a/examples/flow_parsing/meson.build b/examples/flow_parsing/meson.build new file mode 100644 index 0000000000..83554409d1 --- /dev/null +++ b/examples/flow_parsing/meson.build @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2026 DynaNIC Semiconductors, Ltd. + +# meson file, for building this example as part of a main DPDK build. + +allow_experimental_apis = true +deps += 'ethdev' +sources = files('main.c') diff --git a/examples/meson.build b/examples/meson.build index 25d9c88457..22f45e8c81 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -17,6 +17,7 @@ all_examples = [ 'eventdev_pipeline', 'fips_validation', 'flow_filtering', + 'flow_parsing', 'helloworld', 'ip_fragmentation', 'ip_pipeline', -- 2.43.7