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 <[email protected]> --- examples/flow_parsing/main.c | 348 ++++++++++++++++++++++++++++++ examples/flow_parsing/meson.build | 11 + examples/meson.build | 1 + 3 files changed, 360 insertions(+) create mode 100644 examples/flow_parsing/main.c create mode 100644 examples/flow_parsing/meson.build diff --git a/examples/flow_parsing/main.c b/examples/flow_parsing/main.c new file mode 100644 index 0000000000..b816cd4100 --- /dev/null +++ b/examples/flow_parsing/main.c @@ -0,0 +1,348 @@ +/* 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 *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 *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 *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

