DPDK patches and discussions
 help / color / mirror / Atom feed
From: Lukas Sismis <sismis@dyna-nic.com>
To: dev@dpdk.org
Cc: Lukas Sismis <sismis@dyna-nic.com>
Subject: [PATCH v2 3/7] examples/flow_parsing: add flow parser demo
Date: Tue,  6 Jan 2026 16:38:33 +0100	[thread overview]
Message-ID: <20260106153838.398033-4-sismis@dyna-nic.com> (raw)
In-Reply-To: <20260106153838.398033-1-sismis@dyna-nic.com>

Add example application demonstrating the flow parser library API.

The example shows:
- Using parse_attr_str() to parse flow attributes from strings
- Using parse_pattern_str() to parse match patterns
- Using parse_actions_str() to parse flow actions
- Printing parsed results with RTE_FLOW_LOG macros
- Integration without EAL initialization (library is standalone)

Build with: meson configure -Dexamples=flow_parsing

Signed-off-by: Lukas Sismis <sismis@dyna-nic.com>
---
 app/test-pmd/flow_parser.h        |   2 +-
 examples/flow_parsing/main.c      | 345 ++++++++++++++++++++++++++++++
 examples/flow_parsing/meson.build |  11 +
 examples/meson.build              |   1 +
 4 files changed, 358 insertions(+), 1 deletion(-)
 create mode 100644 examples/flow_parsing/main.c
 create mode 100644 examples/flow_parsing/meson.build

diff --git a/app/test-pmd/flow_parser.h b/app/test-pmd/flow_parser.h
index 8b5c99cd8b..2f77bc45e8 100644
--- a/app/test-pmd/flow_parser.h
+++ b/app/test-pmd/flow_parser.h
@@ -5,4 +5,4 @@
 
 #include <rte_flow_parser.h>
 
-#endif /* _TESTPMD_FLOW_PARSER_H_ */
\ No newline at end of file
+#endif /* _TESTPMD_FLOW_PARSER_H_ */
diff --git a/examples/flow_parsing/main.c b/examples/flow_parsing/main.c
new file mode 100644
index 0000000000..9f48e35ed1
--- /dev/null
+++ b/examples/flow_parsing/main.c
@@ -0,0 +1,345 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2026 Dyna-NIC
+ */
+
+/*
+ * Flow Parsing Example
+ * ====================
+ * This example demonstrates how to use the flow_parser library 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.
+ *
+ * Alternative approaches:
+ *   - Construct rte_flow_attr, rte_flow_item[], and rte_flow_action[]
+ *     directly in C code and call rte_flow_create()/rte_flow_validate().
+ *
+ * This string-based approach is useful when:
+ *   - Accepting flow rules from user input or configuration files
+ *   - Reusing the familiar testpmd flow command syntax
+ *   - Rapid prototyping without manually constructing C structures
+ *
+ * Key functions demonstrated:
+ *   - rte_flow_parser_parse_attr_str() - Parse flow attributes (ingress/egress/priority)
+ *   - rte_flow_parser_parse_pattern_str() - Parse match patterns (eth/ipv4/tcp/etc)
+ *   - rte_flow_parser_parse_actions_str() - Parse actions (drop/queue/mark/etc)
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+#include <rte_common.h>
+#include <rte_flow.h>
+#include <rte_flow_parser.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 uint8_t *mac)
+{
+	printf("    %s: %02x:%02x:%02x:%02x:%02x:%02x\n", label,
+	       mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+}
+
+/* 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.addr_bytes);
+				print_mac("src", eth->hdr.src_addr.addr_bytes);
+			} 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;
+				char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN];
+				inet_ntop(AF_INET, &ipv4->hdr.src_addr, src, sizeof(src));
+				inet_ntop(AF_INET, &ipv4->hdr.dst_addr, dst, sizeof(dst));
+				printf(" src=%s dst=%s\n", src, dst);
+			} 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",
+				       ntohs(tcp->hdr.src_port),
+				       ntohs(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",
+				       ntohs(udp->hdr.src_port),
+				       ntohs(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);
+			}
+			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);
+			}
+			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);
+			}
+			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 combining parsed components
+ *
+ * NOTE: The parse functions return pointers to internal static buffers.
+ * Each subsequent parse call overwrites the previous result.
+ * Therefore, you must use the returned data before calling another parse function,
+ * or copy the data to your own storage.
+ */
+static void
+demo_combine(void)
+{
+	struct rte_flow_attr attr;
+	const struct rte_flow_item *pattern;
+	const struct rte_flow_action *actions;
+	uint32_t pattern_n, actions_n;
+	int ret;
+
+	printf("\n=== Combining Parsed Components ===\n");
+	printf("Parse attributes, patterns, and actions separately,\n");
+	printf("then use each result before the next parse call.\n\n");
+	printf("NOTE: Returned pointers are only valid until the next parse call!\n\n");
+
+	/* Parse and display attributes */
+	ret = rte_flow_parser_parse_attr_str("ingress priority 1", &attr);
+	if (ret != 0) {
+		printf("Failed to parse attributes: %d\n", ret);
+		return;
+	}
+	printf("Parsed attributes:\n");
+	print_attr(&attr);
+
+	/* Parse and display pattern */
+	ret = rte_flow_parser_parse_pattern_str(
+		"eth / ipv4 src is 192.168.1.0 / tcp dst is 443 / end",
+		&pattern, &pattern_n);
+	if (ret != 0) {
+		printf("Failed to parse pattern: %d\n", ret);
+		return;
+	}
+	printf("\nParsed pattern:\n");
+	print_pattern(pattern, pattern_n);
+
+	/* Parse and display actions */
+	ret = rte_flow_parser_parse_actions_str(
+		"mark id 1 / queue index 2 / end",
+		&actions, &actions_n);
+	if (ret != 0) {
+		printf("Failed to parse actions: %d\n", ret);
+		return;
+	}
+	printf("\nParsed actions:\n");
+	print_actions(actions, actions_n);
+
+	printf("\nIn a real application, copy pattern/actions to your own storage,\n");
+	printf("then call:\n");
+	printf("  rte_flow_validate(port_id, &attr, pattern, actions, &error)\n");
+	printf("  rte_flow_create(port_id, &attr, pattern, actions, &error)\n");
+}
+
+int
+main(void)
+{
+	int ret;
+
+	printf("Flow Parser Library Example\n");
+	printf("===========================\n");
+
+	/* Initialize the flow parser */
+	ret = rte_flow_parser_init(NULL);
+	if (ret != 0) {
+		fprintf(stderr, "Failed to initialize flow parser: %d\n", ret);
+		return 1;
+	}
+
+	/* Run demonstrations */
+	demo_parse_attr();
+	demo_parse_pattern();
+	demo_parse_actions();
+	demo_combine();
+
+	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..deab338bfb
--- /dev/null
+++ b/examples/flow_parsing/meson.build
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2024 Intel Corporation
+
+# meson file, for building this example as part of a main DPDK build.
+#
+# To build this example as a standalone application with an already-installed
+# DPDK instance, use 'make'
+
+allow_experimental_apis = true
+deps += 'flow_parser'
+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


  parent reply	other threads:[~2026-01-06 15:39 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-01-06 15:38 [RFC PATCH v2 0/7] flow_parser: add shared parser library Lukas Sismis
2026-01-06 15:38 ` [PATCH v2 1/7] " Lukas Sismis
2026-01-06 15:38 ` [PATCH v2 2/7] app/testpmd: integrate shared flow " Lukas Sismis
2026-01-06 15:38 ` Lukas Sismis [this message]
2026-01-06 15:38 ` [PATCH v2 4/7] test: add flow parser unit tests Lukas Sismis
2026-01-06 18:04   ` Stephen Hemminger
2026-01-06 18:07   ` Stephen Hemminger
2026-01-06 15:38 ` [PATCH v2 5/7] doc: add flow parser library maintainers Lukas Sismis
2026-01-06 15:38 ` [PATCH v2 6/7] dts: fix invalid f-string syntax in testpmd API Lukas Sismis
2026-01-06 15:38 ` [PATCH v2 7/7] cmdline: include stddef.h before defining offsetof Lukas Sismis

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=20260106153838.398033-4-sismis@dyna-nic.com \
    --to=sismis@dyna-nic.com \
    --cc=dev@dpdk.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).