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 27820D2FEDF for ; Tue, 27 Jan 2026 21:06:21 +0000 (UTC) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 58397427E3; Tue, 27 Jan 2026 22:05:50 +0100 (CET) Received: from mail-wr1-f52.google.com (mail-wr1-f52.google.com [209.85.221.52]) by mails.dpdk.org (Postfix) with ESMTP id 80FFB427DA for ; Tue, 27 Jan 2026 22:05:48 +0100 (CET) Received: by mail-wr1-f52.google.com with SMTP id ffacd0b85a97d-42fb0fc5aa9so3556203f8f.1 for ; Tue, 27 Jan 2026 13:05:48 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=dyna-nic.com; s=google; t=1769547948; x=1770152748; 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=E0eCwfMqKhvZXwzSehTa0FkN2suLYDMoJqkuYAODBB4=; b=IFawQTr+QBcyzebDeRG+28RrzTspji4FKQ6k9BZxbhq4UrLc+BPKWWz2PmzUTpICx3 0LpddOL4dd9TairAr8esMkGYdQy7GE2MEUvinlAdu5IrpjVF8iqbNywtZH3XgPaFK+ga SWo4u5mIB5ukcMlG+hZUZ1Md6nt+oHSMR9Vm1TjVDeYqI+SJMPEB/gNWFslosShbSJGW ou1HvTLHz82x7zOpox6XxDy/cVyWtRA0Bhr+XIz2J6nDVdbbSj/a4Tyw7DbGSLo5bt/Z fHu9Qf8s/AhDdV+7/XAnp9bbAsG1KbrK8tqJj61AI4XfjhTmSWVqRxqeAcBTCD8CXELw Iwag== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769547948; x=1770152748; 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=E0eCwfMqKhvZXwzSehTa0FkN2suLYDMoJqkuYAODBB4=; b=TCIcEaJFvoDQZZlQa/DXf2l6WYiOEqPiLQgYQxLYL17YgpQqBLCn+xe524hhey5v5P qvMoW6nPILXoRRtnMTUqyKJHmZtjLPuuJ9eDTmEbx+BaesjnzV2u83IQXmdMGHQmuYoZ tsiX2aXmMVGQaN4/J5OqYM9h+wQz2qpye1OJjmg/0IQ1FEQYKsfs6nC5eRGEFBPSSPx8 hmFw2bWfg60iy8TuiZ5MlN19OZy+4vkdOZgCRT+3VmzSsNEdswceGQYPbyxg+tzYH0Rc WYTqgzZzfM/q2OF58PHtEDZehSNKBkSrfQpw/oO9+7PspHuKT7dxkhc/WPBjMB75NZiE giaA== X-Gm-Message-State: AOJu0YzkjCSprgHKlStci4DPsu/YUGW952/wDukBT7KPWiZZtU/3pA+P UNYBxRS41xX1G96VA3BGrQude7Q2VqcClhYX0YBijGhY5Ru8uFq6yROSAi/t9mIdGVTlRj/Rb5B QGns3 X-Gm-Gg: AZuq6aKOdk6BlyUSBDJLtngmcIc9AZlrMGAwraZ+pAJ6GEoC0SmhG32sCIer9DGWQCW cqNypvOww7GVOJbwn+0QRgBDJwzTwoEsdXCnms9Mi184voNfBVIBAbGvjSCBXKyK8RU3vYhwFU6 5LEBqb/95QmNFhYcg7/tVSq3m19h+e7qeywGQllsXEZ6fEv85QYgPWGPOx0gFyqIIeaFWS9Kx2i wHp8GPN9krrM9LX8FLXhyG5mUON5mNjvlo+zTYQr+m6VwIpTX4Feg1C/YWJWjZ60jIy9o5xHkkt 1vFlj9NRABPAXN540uP8kSRoMVqRhH1h/orqx+7Ougr/C9frtdxn5B9dw02ql53KhnKZToArkcJ e1jdGzaAlcQWWT1XcIEOEhYkyizUiIsfNAHFAg6En4JGOrEHTGvW0kc3CYhqvsdr/rgVBLGGERo Gy0zXNs98A/MfU9aTiJo4+1FqgGXa3Oa/xTfJPY+6Uf3CzMQ== X-Received: by 2002:a05:6000:1089:b0:435:91b8:e029 with SMTP id ffacd0b85a97d-435dd1bd8b0mr3370113f8f.37.1769547947765; Tue, 27 Jan 2026 13:05:47 -0800 (PST) Received: from claret.liberouter.org (rt-tmc-kou.liberouter.org. [195.113.172.126]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-435e10ee057sm2021762f8f.15.2026.01.27.13.05.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 27 Jan 2026 13:05:47 -0800 (PST) From: Lukas Sismis To: dev@dpdk.org Cc: stephen@networkplumber.org, Lukas Sismis Subject: [PATCH v6 5/6] examples/flow_parsing: add flow parser demo Date: Tue, 27 Jan 2026 22:05:30 +0100 Message-ID: <20260127210532.301464-6-sismis@dyna-nic.com> X-Mailer: git-send-email 2.43.7 In-Reply-To: <20260127210532.301464-1-sismis@dyna-nic.com> References: <20260127210532.301464-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 example application demonstrating the flow parser library API. The example shows how to use the primary external functions for parsing parts of rte_flow rules. Configure with: meson configure -Dexamples=flow_parsing Signed-off-by: Lukas Sismis --- MAINTAINERS | 1 + examples/flow_parsing/main.c | 291 ++++++++++++++++++++++++++++++ examples/flow_parsing/meson.build | 11 ++ examples/meson.build | 1 + 4 files changed, 304 insertions(+) create mode 100644 examples/flow_parsing/main.c create mode 100644 examples/flow_parsing/meson.build diff --git a/MAINTAINERS b/MAINTAINERS index 5f477ab662..36e9f3af2c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -449,6 +449,7 @@ F: doc/guides/prog_guide/ethdev/flow_offload.rst F: doc/guides/prog_guide/flow_parser_lib.rst F: lib/ethdev/rte_flow* F: lib/flow_parser/ +F: examples/flow_parsing/ Traffic Management API M: Cristian Dumitrescu diff --git a/examples/flow_parsing/main.c b/examples/flow_parsing/main.c new file mode 100644 index 0000000000..a77315aff3 --- /dev/null +++ b/examples/flow_parsing/main.c @@ -0,0 +1,291 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2026 DynaNIC + */ + +/* + * 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) + * + * NOTE: The parse functions return pointers to internal static buffers. + * Each subsequent parse call overwrites the previous result. + */ + +#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; + 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"); + } +} + +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(); + + 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..3cc59fcae0 --- /dev/null +++ b/examples/flow_parsing/meson.build @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2026 DynaNIC + +# 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