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 <[email protected]>
---
 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 <[email protected]>
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 <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <rte_common.h>
+#include <rte_byteorder.h>
+#include <rte_ether.h>
+#include <rte_flow.h>
+#include <rte_flow_parser.h>
+#include <rte_flow_parser_config.h>
+
+/* 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", &eth->hdr.dst_addr);
+                               print_mac("src", &eth->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

Reply via email to