@@ -3833,6 +3833,8 @@ For example, to configure a RTE_FLOW_TYPE_JUMP action as a miss action for ingre
struct rte_flow_error err;
rte_flow_group_set_miss_actions(port, 1, &attr, act, &err);
+.. _flow_templates:
+
Flow templates
~~~~~~~~~~~~~~
@@ -1,13 +1,14 @@
.. SPDX-License-Identifier: BSD-3-Clause
Copyright 2017 Mellanox Technologies, Ltd
-Basic RTE Flow Filtering Sample Application
-===========================================
+Flow Filtering Sample Application
+=================================
-The Basic RTE flow filtering sample application is a simple example of a
-creating a RTE flow rule.
+The flow filtering sample application provides a simple example of creating flow rules.
-It is intended as a demonstration of the basic components RTE flow rules.
+It serves as a demonstration of the fundamental components of flow rules.
+
+It demonstrates how to create rules and configure them, using both template and non template API.
Compiling the Application
@@ -15,6 +16,8 @@ Compiling the Application
To compile the sample application see :doc:`compiling`.
+The application is located in the ``flow_filtering`` sub-directory.
+
Running the Application
-----------------------
@@ -23,231 +26,155 @@ To run the example in a ``linux`` environment:
.. code-block:: console
- ./<build_dir>/examples/dpdk-flow_filtering -l 1 -n 1
-
-Refer to *DPDK Getting Started Guide* for general information on running
-applications and the Environment Abstraction Layer (EAL) options.
-
-
-Explanation
------------
-
-The example is built from 2 files,
-``main.c`` which holds the example logic and ``flow_blocks.c`` that holds the
-implementation for building the flow rule.
-
-The following sections provide an explanation of the main components of the
-code.
+ ./<build_dir>/examples/dpdk-flow_filtering -n <number of channels> -a <pci_dev>,dv_flow_en=<1|2> -- [--[non-]template]
-All DPDK library functions used in the sample code are prefixed with ``rte_``
-and are explained in detail in the *DPDK API Documentation*.
+where,
+``--[non-]template``: Specify whether to use the template API (default is template API).
+For more details on template API please refer to :ref:`flow_templates`.
-The Main Function
-~~~~~~~~~~~~~~~~~
-
-The ``main()`` function located in ``main.c`` file performs the initialization
-and runs the main loop function.
-
-The first task is to initialize the Environment Abstraction Layer (EAL). The
-``argc`` and ``argv`` arguments are provided to the ``rte_eal_init()``
-function. The value returned is the number of parsed arguments:
-
-.. literalinclude:: ../../../examples/flow_filtering/main.c
- :language: c
- :start-after: Initialize EAL. 8<
- :end-before: >8 End of Initialization of EAL.
- :dedent: 1
-
-
-The ``main()`` also allocates a mempool to hold the mbufs (Message Buffers)
-used by the application:
-
-.. literalinclude:: ../../../examples/flow_filtering/main.c
- :language: c
- :start-after: Allocates a mempool to hold the mbufs. 8<
- :end-before: >8 End of allocating a mempool to hold the mbufs.
- :dedent: 1
-
-Mbufs are the packet buffer structure used by DPDK. They are explained in
-detail in the "Mbuf Library" section of the *DPDK Programmer's Guide*.
-
-The ``main()`` function also initializes all the ports using the user defined
-``init_port()`` function which is explained in the next section:
-
-.. literalinclude:: ../../../examples/flow_filtering/main.c
- :language: c
- :start-after: Initializes all the ports using the user defined init_port(). 8<
- :end-before: >8 End of Initializing the ports using user defined init_port().
- :dedent: 1
-
-Once the initialization is complete, we set the flow rule using the
-following code:
-
-.. literalinclude:: ../../../examples/flow_filtering/main.c
- :language: c
- :start-after: Create flow for send packet with. 8<
- :end-before: >8 End of creating flow for send packet with.
- :dedent: 1
+Refer to *DPDK Getting Started Guide* for general information on running
+applications and the Environment Abstraction Layer (EAL) options.
-In the last part the application is ready to launch the
-``main_loop()`` function. Which is explained below.
+Structure
+---------
-.. literalinclude:: ../../../examples/flow_filtering/main.c
- :language: c
- :start-after: Launching main_loop(). 8<
- :end-before: >8 End of launching main_loop().
- :dedent: 1
+The example is built from 2 main files:
-The Port Initialization Function
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+- ``main.c``: Contains the application logic, including initializations and the main loop.
+- ``flow_skeleton.c``: Implements the creation of flow rules.
-The main functional part of the port initialization used in the flow filtering
-application is shown below:
+Additionally, the ``snippets`` directory includes code snippets showcasing various features
+that can override the basic ``flow_skeleton.c`` implementation.
-.. literalinclude:: ../../../examples/flow_filtering/main.c
- :language: c
- :start-after: Port initialization used in flow filtering. 8<
- :end-before: >8 End of Port initialization used in flow filtering.
-The Ethernet port is configured with default settings using the
-``rte_eth_dev_configure()`` function and the ``port_conf_default`` struct:
+Application Flow
+----------------
-.. literalinclude:: ../../../examples/flow_filtering/main.c
- :language: c
- :start-after: Ethernet port configured with default settings. 8<
- :end-before: >8 End of ethernet port configured with default settings.
- :dedent: 1
+Initialization
+~~~~~~~~~~~~~~
-For this example we are configuring number of rx and tx queues that are connected
-to a single port.
+Begin by setting up the Environment Abstraction Layer (EAL) using ``rte_eal_init()``.
+This function initializes EAL with arguments ``argc`` and ``argv``,
+returning the number of parsed arguments:
.. literalinclude:: ../../../examples/flow_filtering/main.c
- :language: c
- :start-after: Configuring number of RX and TX queues connected to single port. 8<
- :end-before: >8 End of Configuring RX and TX queues connected to single port.
- :dedent: 1
+ :language: c
+ :start-after: Initialize EAL. 8<
+ :end-before: >8 End of Initialization of EAL.
+ :dedent: 1
-In the next step we create and apply the flow rule. which is to send packets
-with destination ip equals to 192.168.1.1 to queue number 1. The detail
-explanation of the ``generate_ipv4_flow()`` appears later in this document:
+Allocate a memory pool for managing mbufs (Message Buffers) used within the application:
.. literalinclude:: ../../../examples/flow_filtering/main.c
- :language: c
- :start-after: Create flow for send packet with. 8<
- :end-before: >8 End of create flow and the flow rule.
- :dedent: 1
+ :language: c
+ :start-after: Allocates a mempool to hold the mbufs. 8<
+ :end-before: >8 End of allocating a mempool to hold the mbufs.
+ :dedent: 1
-We are setting the RX port to promiscuous mode:
+Initialize the ports using the user-defined ``init_port()`` function,
+configuring Ethernet ports with default settings, including both RX and TX queues for a single port:
.. literalinclude:: ../../../examples/flow_filtering/main.c
- :language: c
- :start-after: Setting the RX port to promiscuous mode. 8<
- :end-before: >8 End of setting the RX port to promiscuous mode.
- :dedent: 1
+ :language: c
+ :start-after: Initializes all the ports using the user defined init_port(). 8<
+ :end-before: >8 End of Initializing the ports using user defined init_port().
+ :dedent: 1
-The last step is to start the port.
+For template API, the flow API requires preallocating resources.
+The function ``rte_flow_configure()`` should be called after configuring the Ethernet device
+and before creating any flow rules to set up flow queues for asynchronous operations.
.. literalinclude:: ../../../examples/flow_filtering/main.c
- :language: c
- :start-after: Starting the port. 8<
- :end-before: >8 End of starting the port.
- :dedent: 1
+ :language: c
+ :start-after: Adds rules engine configuration. 8<
+ :end-before: >8 End of adding rules engine configuration.
+ :dedent: 1
-
-The main_loop function
+Creating the Flow Rule
~~~~~~~~~~~~~~~~~~~~~~
-As we saw above the ``main()`` function calls an application function to handle
-the main loop. For the flow filtering application the main_loop function
-looks like the following:
-
-.. literalinclude:: ../../../examples/flow_filtering/main.c
- :language: c
- :start-after: Main_loop for flow filtering. 8<
- :end-before: >8 End of reading the packets from all queues.
+This section is the core of the flow filtering functionality involves creating flow rules.
+The flow rules are created using two primary approaches: template API and non-template API.
+Both template and non-template API configure flow rules using attributes (like ingress or egress),
+pattern items (for matching packet data), and actions (for operations on matched packets).
+However, template API extend this by introducing pattern templates and actions templates,
+which define reusable matching criteria and action lists, respectively.
+These templates are then combined in a template table to optimize resource allocation and management.
+In contrast, non-template API handle each rule individually without such shared templates.
-The main work of the application is reading the packets from all
-queues and printing for each packet the destination queue:
+This is handled by the ``generate_flow_skeleton()`` function in ``flow_skeleton.c``.
.. literalinclude:: ../../../examples/flow_filtering/main.c
- :language: c
- :start-after: Reading the packets from all queues. 8<
- :end-before: >8 End of main_loop for flow filtering.
+ :language: c
+ :start-after: Function responsible for creating the flow rule. 8<
+ :end-before: >8 End of function responsible for creating the flow rule.
+ :dedent: 1
+This part of the code defines necessary data structures,
+as well as configures action and pattern structures for the rule.
+Common for both template and non-template API.
-The forwarding loop can be interrupted and the application closed using
-``Ctrl-C``. Which results in closing the port and the device using
-``rte_eth_dev_stop`` and ``rte_eth_dev_close``
+.. literalinclude:: ../../../examples/flow_filtering/flow_skeleton.c
+ :language: c
+ :start-after: Set the common action and pattern structures 8<
+ :end-before: >8 End of setting the common action and pattern structures.
+ :dedent: 1
-The generate_ipv4_flow function
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+For template API, this part of the code creates the necessary template tables and finally create the rule.
-The generate_ipv4_flow function is responsible for creating the flow rule.
-This function is located in the ``flow_blocks.c`` file.
+.. literalinclude:: ../../../examples/flow_filtering/flow_skeleton.c
+ :language: c
+ :start-after: Create a flow rule using template API 8<
+ :end-before: >8 End of creating a flow rule using template API.
+ :dedent: 1
-.. literalinclude:: ../../../examples/flow_filtering/flow_blocks.c
- :language: c
- :start-after: Function responsible for creating the flow rule. 8<
- :end-before: >8 End of function responsible for creating the flow rule.
+For non-template API, validate the rule and create it.
-The first part of the function is declaring the structures that will be used.
+.. literalinclude:: ../../../examples/flow_filtering/flow_skeleton.c
+ :language: c
+ :start-after: Validate and create the rule 8<
+ :end-before: >8 End of validating and creating the rule.
+ :dedent: 1
-.. literalinclude:: ../../../examples/flow_filtering/flow_blocks.c
- :language: c
- :start-after: Declaring structs being used. 8<
- :end-before: >8 End of declaring structs being used.
- :dedent: 1
+Main Loop Execution
+~~~~~~~~~~~~~~~~~~~
-The following part create the flow attributes, in our case ingress.
+Launch the ``main_loop()`` function from ``main.c``,
+which reading the packets from all queues and printing for each packet the destination queue:
-.. literalinclude:: ../../../examples/flow_filtering/flow_blocks.c
- :language: c
- :start-after: Set the rule attribute, only ingress packets will be checked. 8<
- :end-before: >8 End of setting the rule attribute.
- :dedent: 1
+.. literalinclude:: ../../../examples/flow_filtering/main.c
+ :language: c
+ :start-after: Launching main_loop(). 8<
+ :end-before: >8 End of launching main_loop().
+ :dedent: 1
-The third part defines the action to be taken when a packet matches
-the rule. In this case send the packet to queue.
+Exiting the Application
+~~~~~~~~~~~~~~~~~~~~~~~
-.. literalinclude:: ../../../examples/flow_filtering/flow_blocks.c
- :language: c
- :start-after: Function responsible for creating the flow rule. 8<
- :end-before: >8 End of setting the rule attribute.
+To terminate the application, use ``Ctrl-C``.
+This action closes the port and device using ``rte_eth_dev_stop`` and ``rte_eth_dev_close``.
-The fourth part is responsible for creating the pattern and is built from
-number of steps. In each step we build one level of the pattern starting with
-the lowest one.
-Setting the first level of the pattern ETH:
+Flow API Snippets
+------------------
-.. literalinclude:: ../../../examples/flow_filtering/flow_blocks.c
- :language: c
- :start-after: Set this level to allow all. 8<
- :end-before: >8 End of setting the first level of the pattern.
- :dedent: 1
+The ``snippets`` directory offers additional customization options through code snippets.
+These snippets cover various aspects of flow configuration, allowing developers to reuse them.
-Setting the second level of the pattern IP:
+These snippets are categorized by usage and can be copied, pasted, and modified as needed.
+They are maintained and compiled alongside other examples, ensuring up-to-date functionality.
-.. literalinclude:: ../../../examples/flow_filtering/flow_blocks.c
- :language: c
- :start-after: Setting the second level of the pattern. 8<
- :end-before: >8 End of setting the second level of the pattern.
- :dedent: 1
-Closing the pattern part.
+Using Snippets
+--------------
-.. literalinclude:: ../../../examples/flow_filtering/flow_blocks.c
- :language: c
- :start-after: The final level must be always type end. 8<
- :end-before: >8 End of final level must be always type end.
- :dedent: 1
+Developers can customize flow rules by modifying ``flow_skeleton.c`` and utilizing functions from ``snippets`` directory.
+For example, within ``snippet_match_ipv4_flow.c``, developers can find the functions:
-The last part of the function is to validate the rule and create it.
+- ``snippet_ipv4_flow_create_actions()`` for defining actions,
+- ``snippet_ipv4_flow_create_patterns()`` for setting packet matching patterns,
+- ``snippet_ipv4_flow_create_table()`` for creating the patterns and actions template table.
-.. literalinclude:: ../../../examples/flow_filtering/flow_blocks.c
- :language: c
- :start-after: Validate the rule and create it. 8<
- :end-before: >8 End of validation the rule and create it.
- :dedent: 1
+These function can simply be called in the appropriate place in ``flow_skeleton.c`` to change the default created flow.
\ No newline at end of file
new file mode 100644
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 NVIDIA Corporation & Affiliates
+ */
+
+#ifndef COMMON
+#define COMMON
+
+#define QUEUE_ID 1
+
+extern struct rte_flow_attr attr;
+extern struct rte_flow_op_attr ops_attr;
+
+/**
+ * Skeleton for creation of a flow rule using template and non template API
+ *
+ * @param port_id
+ * The selected port.
+ * @param[out] error
+ * Perform verbose error reporting if not NULL.
+ * @param[out] flow_template
+ * The selected API to use.
+ * @return
+ * A flow if the rule could be created else return NULL.
+ */
+ /* Function responsible for creating the flow rule. 8< */
+struct rte_flow *
+generate_flow_skeleton(uint16_t port_id, struct rte_flow_error *error, int use_template_api);
+
+#endif /* COMMON */
deleted file mode 100644
@@ -1,112 +0,0 @@
-/* SPDX-License-Identifier: BSD-3-Clause
- * Copyright 2017 Mellanox Technologies, Ltd
- */
-
-#define MAX_PATTERN_NUM 3
-#define MAX_ACTION_NUM 2
-
-struct rte_flow *
-generate_ipv4_flow(uint16_t port_id, uint16_t rx_q,
- uint32_t src_ip, uint32_t src_mask,
- uint32_t dest_ip, uint32_t dest_mask,
- struct rte_flow_error *error);
-
-
-/**
- * create a flow rule that sends packets with matching src and dest ip
- * to selected queue.
- *
- * @param port_id
- * The selected port.
- * @param rx_q
- * The selected target queue.
- * @param src_ip
- * The src ip value to match the input packet.
- * @param src_mask
- * The mask to apply to the src ip.
- * @param dest_ip
- * The dest ip value to match the input packet.
- * @param dest_mask
- * The mask to apply to the dest ip.
- * @param[out] error
- * Perform verbose error reporting if not NULL.
- *
- * @return
- * A flow if the rule could be created else return NULL.
- */
-
-/* Function responsible for creating the flow rule. 8< */
-struct rte_flow *
-generate_ipv4_flow(uint16_t port_id, uint16_t rx_q,
- uint32_t src_ip, uint32_t src_mask,
- uint32_t dest_ip, uint32_t dest_mask,
- struct rte_flow_error *error)
-{
- /* Declaring structs being used. 8< */
- struct rte_flow_attr attr;
- struct rte_flow_item pattern[MAX_PATTERN_NUM];
- struct rte_flow_action action[MAX_ACTION_NUM];
- struct rte_flow *flow = NULL;
- struct rte_flow_action_queue queue = { .index = rx_q };
- struct rte_flow_item_ipv4 ip_spec;
- struct rte_flow_item_ipv4 ip_mask;
- /* >8 End of declaring structs being used. */
- int res;
-
- memset(pattern, 0, sizeof(pattern));
- memset(action, 0, sizeof(action));
-
- /* Set the rule attribute, only ingress packets will be checked. 8< */
- memset(&attr, 0, sizeof(struct rte_flow_attr));
- attr.ingress = 1;
- /* >8 End of setting the rule attribute. */
-
- /*
- * create the action sequence.
- * one action only, move packet to queue
- */
- action[0].type = RTE_FLOW_ACTION_TYPE_QUEUE;
- action[0].conf = &queue;
- action[1].type = RTE_FLOW_ACTION_TYPE_END;
-
- /*
- * set the first level of the pattern (ETH).
- * since in this example we just want to get the
- * ipv4 we set this level to allow all.
- */
-
- /* Set this level to allow all. 8< */
- pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
- /* >8 End of setting the first level of the pattern. */
-
- /*
- * setting the second level of the pattern (IP).
- * in this example this is the level we care about
- * so we set it according to the parameters.
- */
-
- /* Setting the second level of the pattern. 8< */
- memset(&ip_spec, 0, sizeof(struct rte_flow_item_ipv4));
- memset(&ip_mask, 0, sizeof(struct rte_flow_item_ipv4));
- ip_spec.hdr.dst_addr = htonl(dest_ip);
- ip_mask.hdr.dst_addr = dest_mask;
- ip_spec.hdr.src_addr = htonl(src_ip);
- ip_mask.hdr.src_addr = src_mask;
- pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4;
- pattern[1].spec = &ip_spec;
- pattern[1].mask = &ip_mask;
- /* >8 End of setting the second level of the pattern. */
-
- /* The final level must be always type end. 8< */
- pattern[2].type = RTE_FLOW_ITEM_TYPE_END;
- /* >8 End of final level must be always type end. */
-
- /* Validate the rule and create it. 8< */
- res = rte_flow_validate(port_id, &attr, pattern, action, error);
- if (!res)
- flow = rte_flow_create(port_id, &attr, pattern, action, error);
- /* >8 End of validation the rule and create it. */
-
- return flow;
-}
-/* >8 End of function responsible for creating the flow rule. */
new file mode 100644
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 NVIDIA Corporation & Affiliates
+ */
+
+#include <stdint.h>
+#include <rte_flow.h>
+
+#include "common.h"
+#include "snippets/snippet_match_ipv4.h"
+
+
+struct rte_flow_attr attr = { .ingress = 1 };
+struct rte_flow_op_attr ops_attr = { .postpone = 0 };
+
+static struct rte_flow *
+create_flow_non_template(uint16_t port_id, struct rte_flow_attr *attr,
+ struct rte_flow_item *patterns,
+ struct rte_flow_action *actions,
+ struct rte_flow_error *error)
+{
+ struct rte_flow *flow = NULL;
+
+ /* Validate the rule and create it. */
+ if (rte_flow_validate(port_id, attr, patterns, actions, error) == 0)
+ flow = rte_flow_create(port_id, attr, patterns, actions, error);
+ return flow;
+}
+
+static struct rte_flow *
+create_flow_template(uint16_t port_id, struct rte_flow_op_attr *ops_attr,
+ struct rte_flow_item *patterns,
+ struct rte_flow_action *actions,
+ struct rte_flow_error *error)
+{
+ /* Replace this function call with
+ * snippet_*_create_table() function from the snippets directory.
+ */
+ struct rte_flow_template_table *table = snippet_ipv4_flow_create_table(port_id, error);
+ if (table == NULL) {
+ printf("Failed to create table: %s (%s)\n",
+ error->message, rte_strerror(rte_errno));
+ return NULL;
+ }
+
+ return rte_flow_async_create(port_id,
+ QUEUE_ID, /* Flow queue used to insert the rule. */
+ ops_attr,
+ table,
+ patterns,
+ 0, /* Pattern template index in the table. */
+ actions,
+ 0, /* Actions template index in the table. */
+ 0, /* user data */
+ error);
+}
+
+struct rte_flow *
+generate_flow_skeleton(uint16_t port_id, struct rte_flow_error *error, int use_template_api)
+{
+ /* Set the common action and pattern structures 8< */
+ struct rte_flow_action actions[MAX_ACTION_NUM] = {0};
+ struct rte_flow_item patterns[MAX_PATTERN_NUM] = {0};
+
+ /* Replace this function call with
+ * snippet_*_create_actions() function from the snippets directory
+ */
+ snippet_ipv4_flow_create_actions(actions);
+
+ /* Replace this function call with
+ * snippet_*_create_patterns() function from the snippets directory
+ */
+ snippet_ipv4_flow_create_patterns(patterns);
+ /* >8 End of setting the common action and pattern structures. */
+
+ /* Create a flow rule using template API 8< */
+ if (use_template_api)
+ return create_flow_template(port_id, &ops_attr, patterns, actions, error);
+ /* >8 End of creating a flow rule using template API. */
+
+ /* Validate and create the rule 8< */
+ return create_flow_non_template(port_id, &attr, patterns, actions, error);
+ /* >8 End of validating and creating the rule. */
+}
@@ -27,21 +27,20 @@
#include <rte_net.h>
#include <rte_flow.h>
#include <rte_cycles.h>
+#include <rte_argparse.h>
-static volatile bool force_quit;
+#include "common.h"
+
+/* Template API enabled by default. */
+static int use_template_api = 1;
+static volatile bool force_quit;
static uint16_t port_id;
static uint16_t nr_queues = 5;
-static uint8_t selected_queue = 1;
struct rte_mempool *mbuf_pool;
struct rte_flow *flow;
-#define SRC_IP ((0<<24) + (0<<16) + (0<<8) + 0) /* src ip = 0.0.0.0 */
-#define DEST_IP ((192<<24) + (168<<16) + (1<<8) + 1) /* dest ip = 192.168.1.1 */
-#define FULL_MASK 0xffffffff /* full mask */
-#define EMPTY_MASK 0x0 /* empty mask */
-
-#include "flow_blocks.c"
+#define MAX_QUEUE_SIZE 256
static inline void
print_ether_addr(const char *what, struct rte_ether_addr *eth_addr)
@@ -51,7 +50,6 @@ print_ether_addr(const char *what, struct rte_ether_addr *eth_addr)
printf("%s%s", what, buf);
}
-/* Main_loop for flow filtering. 8< */
static int
main_loop(void)
{
@@ -63,7 +61,7 @@ main_loop(void)
uint16_t j;
int ret;
- /* Reading the packets from all queues. 8< */
+ /* Reading the packets from all queues. */
while (!force_quit) {
for (i = 0; i < nr_queues; i++) {
nb_rx = rte_eth_rx_burst(port_id,
@@ -87,18 +85,16 @@ main_loop(void)
}
}
}
- /* >8 End of reading the packets from all queues. */
/* closing and releasing resources */
rte_flow_flush(port_id, &error);
ret = rte_eth_dev_stop(port_id);
if (ret < 0)
printf("Failed to stop port %u: %s",
- port_id, rte_strerror(-ret));
+ port_id, rte_strerror(-ret));
rte_eth_dev_close(port_id);
return ret;
}
-/* >8 End of main_loop for flow filtering. */
#define CHECK_INTERVAL 1000 /* 100ms */
#define MAX_REPEAT_TIMES 90 /* 9s (90 * 100ms) in total */
@@ -125,13 +121,36 @@ assert_link_status(void)
rte_exit(EXIT_FAILURE, ":: error: link is still down\n");
}
-/* Port initialization used in flow filtering. 8< */
+static void
+configure_hws(uint16_t port_id)
+{
+ int ret;
+ uint16_t std_queue;
+ struct rte_flow_error error;
+ struct rte_flow_queue_attr queue_attr[RTE_MAX_LCORE];
+ const struct rte_flow_queue_attr *attr_list[RTE_MAX_LCORE];
+ struct rte_flow_port_attr port_attr = { .nb_counters = 1 /* rules count */ };
+
+ for (std_queue = 0; std_queue < RTE_MAX_LCORE; std_queue++) {
+ queue_attr[std_queue].size = MAX_QUEUE_SIZE;
+ attr_list[std_queue] = &queue_attr[std_queue];
+ }
+
+ ret = rte_flow_configure(port_id, &port_attr,
+ 1, attr_list, &error);
+ if (ret != 0)
+ rte_exit(EXIT_FAILURE,
+ "rte_flow_configure:err=%d, port=%u\n",
+ ret, port_id);
+ printf(":: Configuring HWS port [%d] Done ..\n", port_id);
+}
+
static void
init_port(void)
{
int ret;
uint16_t i;
- /* Ethernet port configured with default settings. 8< */
+ /* Ethernet port configured with default settings. */
struct rte_eth_conf port_conf = {
.txmode = {
.offloads =
@@ -165,14 +184,13 @@ init_port(void)
rxq_conf = dev_info.default_rxconf;
rxq_conf.offloads = port_conf.rxmode.offloads;
- /* >8 End of ethernet port configured with default settings. */
- /* Configuring number of RX and TX queues connected to single port. 8< */
+ /* Configuring number of RX and TX queues connected to single port. */
for (i = 0; i < nr_queues; i++) {
ret = rte_eth_rx_queue_setup(port_id, i, 512,
- rte_eth_dev_socket_id(port_id),
- &rxq_conf,
- mbuf_pool);
+ rte_eth_dev_socket_id(port_id),
+ &rxq_conf,
+ mbuf_pool);
if (ret < 0) {
rte_exit(EXIT_FAILURE,
":: Rx queue setup failed: err=%d, port=%u\n",
@@ -193,30 +211,43 @@ init_port(void)
ret, port_id);
}
}
- /* >8 End of Configuring RX and TX queues connected to single port. */
- /* Setting the RX port to promiscuous mode. 8< */
+ /* Setting the RX port to promiscuous mode. */
ret = rte_eth_promiscuous_enable(port_id);
if (ret != 0)
rte_exit(EXIT_FAILURE,
":: promiscuous mode enable failed: err=%s, port=%u\n",
rte_strerror(-ret), port_id);
- /* >8 End of setting the RX port to promiscuous mode. */
- /* Starting the port. 8< */
ret = rte_eth_dev_start(port_id);
if (ret < 0) {
rte_exit(EXIT_FAILURE,
"rte_eth_dev_start:err=%d, port=%u\n",
ret, port_id);
}
- /* >8 End of starting the port. */
assert_link_status();
printf(":: initializing port: %d done\n", port_id);
+
+ if (use_template_api == 0)
+ return;
+
+ /* Adds rules engine configuration. 8< */
+ ret = rte_eth_dev_stop(port_id);
+ if (ret < 0)
+ rte_exit(EXIT_FAILURE,
+ "rte_eth_dev_stop:err=%d, port=%u\n",
+ ret, port_id);
+
+ configure_hws(port_id);
+ ret = rte_eth_dev_start(port_id);
+ if (ret < 0)
+ rte_exit(EXIT_FAILURE,
+ "rte_eth_dev_start:err=%d, port=%u\n",
+ ret, port_id);
+ /* >8 End of adding rules engine configuration. */
}
-/* >8 End of Port initialization used in flow filtering. */
static void
signal_handler(int signum)
@@ -228,6 +259,34 @@ signal_handler(int signum)
}
}
+/* Parse the argument given in the command line of the application */
+static int
+flow_filtering_parse_args(int argc, char **argv)
+{
+ static struct rte_argparse obj = {
+ .prog_name = "flow_filtering",
+ .usage = "[EAL options] -- [optional parameters]",
+ .descriptor = NULL,
+ .epilog = NULL,
+ .exit_on_error = false,
+ .callback = NULL,
+ .opaque = NULL,
+ .args = {
+ { "--template", NULL, "Enable template API flow",
+ &use_template_api, (void *)1,
+ RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT,
+ },
+ { "--non-template", NULL, "Enable non template API flow",
+ &use_template_api, (void *)0,
+ RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT,
+ },
+ ARGPARSE_ARG_END(),
+ },
+ };
+
+ return rte_argparse_parse(&obj, argc, argv);
+}
+
int
main(int argc, char **argv)
{
@@ -240,11 +299,18 @@ main(int argc, char **argv)
if (ret < 0)
rte_exit(EXIT_FAILURE, ":: invalid EAL arguments\n");
/* >8 End of Initialization of EAL. */
+ argc -= ret;
+ argv += ret;
force_quit = false;
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
+ /* Parse application arguments (after the EAL ones) */
+ ret = flow_filtering_parse_args(argc, argv);
+ if (ret < 0)
+ rte_exit(EXIT_FAILURE, "Invalid flow filtering arguments\n");
+
nr_ports = rte_eth_dev_count_avail();
if (nr_ports == 0)
rte_exit(EXIT_FAILURE, ":: no Ethernet ports found\n");
@@ -253,10 +319,11 @@ main(int argc, char **argv)
printf(":: warn: %d ports detected, but we use only one: port %u\n",
nr_ports, port_id);
}
+
/* Allocates a mempool to hold the mbufs. 8< */
mbuf_pool = rte_pktmbuf_pool_create("mbuf_pool", 4096, 128, 0,
- RTE_MBUF_DEFAULT_BUF_SIZE,
- rte_socket_id());
+ RTE_MBUF_DEFAULT_BUF_SIZE,
+ rte_socket_id());
/* >8 End of allocating a mempool to hold the mbufs. */
if (mbuf_pool == NULL)
rte_exit(EXIT_FAILURE, "Cannot init mbuf pool\n");
@@ -265,18 +332,17 @@ main(int argc, char **argv)
init_port();
/* >8 End of Initializing the ports using user defined init_port(). */
- /* Create flow for send packet with. 8< */
- flow = generate_ipv4_flow(port_id, selected_queue,
- SRC_IP, EMPTY_MASK,
- DEST_IP, FULL_MASK, &error);
- /* >8 End of create flow and the flow rule. */
+ /* Function responsible for creating the flow rule. 8< */
+ flow = generate_flow_skeleton(port_id, &error, use_template_api);
+ /* >8 End of function responsible for creating the flow rule. */
+
if (!flow) {
printf("Flow can't be created %d message: %s\n",
error.type,
error.message ? error.message : "(no stated reason)");
rte_exit(EXIT_FAILURE, "error in creating flow");
}
- /* >8 End of creating flow for send packet with. */
+ printf("Flow created!!:\n");
/* Launching main_loop(). 8< */
ret = main_loop();
@@ -7,6 +7,15 @@
# DPDK instance, use 'make'
allow_experimental_apis = true
+deps += ['argparse']
+
sources = files(
'main.c',
+ 'flow_skeleton.c',
+ 'snippets/snippet_match_ipv4.c',
+ 'snippets/snippet_match_gre.c',
+ 'snippets/snippet_match_mpls.c',
)
+
+# The code snippets are not utilized.
+cflags += '-Wno-unused-function'
new file mode 100644
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 NVIDIA Corporation & Affiliates
+ */
+
+#include <stdlib.h>
+#include <rte_flow.h>
+
+#include "snippet_match_gre.h"
+
+static void
+snippet_match_gre_create_actions(struct rte_flow_action *action)
+{
+ /* Create one action that moves the packet to the selected queue. */
+ struct rte_flow_action_queue *queue = calloc(1, sizeof(struct rte_flow_action_queue));
+ if (queue == NULL)
+ fprintf(stderr, "Failed to allocate memory for queue\n");
+
+ /* Set the selected queue. */
+ queue->index = 1;
+
+ /* Set the action move packet to the selected queue. */
+ action[0].type = RTE_FLOW_ACTION_TYPE_QUEUE;
+ action[0].conf = queue;
+ action[1].type = RTE_FLOW_ACTION_TYPE_END;
+}
+
+static void
+snippet_match_gre_create_patterns(struct rte_flow_item *pattern)
+{
+ struct rte_flow_item_gre *gre_spec;
+ struct rte_flow_item_gre_opt *gre_opt_spec;
+
+ gre_spec = calloc(1, sizeof(struct rte_flow_item_gre));
+ if (gre_spec == NULL)
+ fprintf(stderr, "Failed to allocate memory for gre_spec\n");
+
+ gre_opt_spec = calloc(1, sizeof(struct rte_flow_item_gre_opt));
+ if (gre_opt_spec == NULL)
+ fprintf(stderr, "Failed to allocate memory for gre_opt_spec\n");
+
+ /* Set the Checksum GRE option. */
+ gre_spec->c_rsvd0_ver = RTE_BE16(0x8000);
+ gre_opt_spec->checksum_rsvd.checksum = RTE_BE16(0x11);
+
+ /* Set the patterns. */
+ pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
+ pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4;
+ pattern[2].type = RTE_FLOW_ITEM_TYPE_GRE;
+ pattern[2].spec = gre_spec;
+ pattern[3].type = RTE_FLOW_ITEM_TYPE_GRE_OPTION;
+ pattern[3].spec = gre_opt_spec;
+ pattern[3].mask = gre_opt_spec;
+ pattern[4].type = RTE_FLOW_ITEM_TYPE_END;
+}
new file mode 100644
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 NVIDIA Corporation & Affiliates
+ */
+
+#ifndef SNIPPET_MATCH_GRE
+#define SNIPPET_MATCH_GRE
+
+/* Matching GRE Checksum/Key/Sequence
+ * GRE optional fields (checksum, key and sequence) can be matched using the gre_option item.
+ * The item requires a GRE item such as gre_key, and its pattern must correspond with the
+ * c_bit/k_bit/s_bit in the GRE pattern.
+ */
+
+#define MAX_PATTERN_NUM 5 /* Maximal number of patterns for this example. */
+#define MAX_ACTION_NUM 2 /* Maximal number of actions for this example. */
+
+/* Replace this function with the snippet_*_create_actions() function in flow_skeleton.c. */
+static void
+snippet_match_gre_create_actions(struct rte_flow_action *action);
+
+/* Replace this function with the snippet_*_create_patterns() function in flow_skeleton.c. */
+static void
+snippet_match_gre_create_patterns(struct rte_flow_item *pattern);
+
+#endif /* SNIPPET_MATCH_GRE */
new file mode 100644
@@ -0,0 +1,154 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 NVIDIA Corporation & Affiliates
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <rte_flow.h>
+
+#include "snippet_match_ipv4.h"
+#include "../common.h"
+
+void
+snippet_ipv4_flow_create_actions(struct rte_flow_action *action)
+{
+ /*
+ * create the action sequence.
+ * one action only, move packet to queue
+ */
+ struct rte_flow_action_queue *queue = calloc(1, sizeof(struct rte_flow_action_queue));
+ if (queue == NULL)
+ fprintf(stderr, "Failed to allocate memory for queue\n");
+ queue->index = QUEUE_ID; /* The selected target queue.*/
+ action[0].type = RTE_FLOW_ACTION_TYPE_QUEUE;
+ action[0].conf = queue;
+ action[1].type = RTE_FLOW_ACTION_TYPE_END;
+}
+
+void
+snippet_ipv4_flow_create_patterns(struct rte_flow_item *patterns)
+{
+ struct rte_flow_item_ipv4 *ip_spec;
+ struct rte_flow_item_ipv4 *ip_mask;
+
+ /*
+ * set the first level of the pattern (ETH).
+ * since in this example we just want to get the
+ * IPV4 we set this level to allow all.
+ */
+ patterns[0].type = RTE_FLOW_ITEM_TYPE_ETH;
+
+ /*
+ * setting the second level of the pattern (IP).
+ * in this example this is the level we care about
+ * so we set it according to the parameters.
+ */
+ patterns[1].type = RTE_FLOW_ITEM_TYPE_IPV4;
+
+ ip_spec = calloc(1, sizeof(struct rte_flow_item_ipv4));
+ if (ip_spec == NULL)
+ fprintf(stderr, "Failed to allocate memory for ip_spec\n");
+
+ ip_mask = calloc(1, sizeof(struct rte_flow_item_ipv4));
+ if (ip_mask == NULL)
+ fprintf(stderr, "Failed to allocate memory for ip_mask\n");
+
+ ip_spec->hdr.dst_addr = htonl(DEST_IP); /* The dest ip value to match the input packet. */
+ ip_mask->hdr.dst_addr = DEST_MASK; /* The mask to apply to the dest ip. */
+ ip_spec->hdr.src_addr = htonl(SRC_IP); /* The src ip value to match the input packet. */
+ ip_mask->hdr.src_addr = EMPTY_MASK; /* The mask to apply to the src ip. */
+ patterns[1].spec = ip_spec;
+ patterns[1].mask = ip_mask;
+
+ /* The final level must be always type end. */
+ patterns[2].type = RTE_FLOW_ITEM_TYPE_END;
+
+}
+
+/* creates a template that holds a list of action types without any specific values set. */
+static struct rte_flow_actions_template *
+snippet_ipv4_flow_create_actions_template(uint16_t port_id, struct rte_flow_error *error)
+{
+ struct rte_flow_action tactions[MAX_ACTION_NUM] = {0};
+ struct rte_flow_action masks[MAX_ACTION_NUM] = {0};
+ struct rte_flow_actions_template_attr action_attr = {
+ .ingress = 1,
+ };
+
+ tactions[0].type = RTE_FLOW_ACTION_TYPE_QUEUE;
+ tactions[0].type = RTE_FLOW_ACTION_TYPE_END;
+
+ /* This sets the masks to match the actions, indicating that all fields of the actions
+ * should be considered as part of the template.
+ */
+ memcpy(masks, tactions, sizeof(masks));
+
+ /* Create the flow actions template using the configured attributes, actions, and masks */
+ return rte_flow_actions_template_create(port_id, &action_attr,
+ tactions, masks, error);
+}
+
+static struct rte_flow_pattern_template *
+snippet_ipv4_flow_create_pattern_template(uint16_t port_id, struct rte_flow_error *error)
+{
+ struct rte_flow_item titems[MAX_PATTERN_NUM] = {0};
+ struct rte_flow_item_tcp ip_mask = {0};
+
+ struct rte_flow_pattern_template_attr attr = {
+ .relaxed_matching = 1,
+ .ingress = 1
+ };
+
+ titems[0].type = RTE_FLOW_ITEM_TYPE_ETH;
+ titems[1].type = RTE_FLOW_ITEM_TYPE_IPV4;
+ ip_mask.hdr.src_port = EMPTY_MASK;
+ ip_mask.hdr.dst_port = DEST_MASK;
+ titems[1].mask = &ip_mask;
+ titems[2].type = RTE_FLOW_ITEM_TYPE_END;
+
+ return rte_flow_pattern_template_create(port_id, &attr, titems, error);
+}
+
+struct rte_flow_template_table *
+snippet_ipv4_flow_create_table(uint16_t port_id, struct rte_flow_error *error)
+{
+ struct rte_flow_pattern_template *pt;
+ struct rte_flow_actions_template *at;
+
+ /* Set the rule attribute, only ingress packets will be checked. */
+ struct rte_flow_template_table_attr table_attr = {
+ .flow_attr = {
+ .group = 0,
+ .priority = 0,
+ .ingress = 1,
+ .egress = 0,
+ .transfer = 0,
+ .reserved = 0,
+ },
+ /* Maximum number of flow rules that this table holds. */
+ .nb_flows = 1,
+ };
+
+ /* The pattern template defines common matching fields without values.
+ * The number and order of items in the template must be the same at the rule creation.
+ */
+ pt = snippet_ipv4_flow_create_pattern_template(port_id, error);
+ if (pt == NULL) {
+ printf("Failed to create pattern template: %s (%s)\n",
+ error->message, rte_strerror(rte_errno));
+ return NULL;
+ }
+
+ /* The actions template holds a list of action types without values.
+ * The number and order of actions in the template must be the same at the rule creation.
+ */
+ at = snippet_ipv4_flow_create_actions_template(port_id, error);
+ if (at == NULL) {
+ printf("Failed to create actions template: %s (%s)\n",
+ error->message, rte_strerror(rte_errno));
+ return NULL;
+ }
+
+ return rte_flow_template_table_create(port_id, &table_attr, &pt, 1, &at, 1, error);
+}
new file mode 100644
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 NVIDIA Corporation & Affiliates
+ */
+
+#ifndef SNIPPET_MATCH_IPV4
+#define SNIPPET_MATCH_IPV4
+
+/* Match IPV4 flow
+ * sends packets with matching src and dest ip to selected queue.
+ */
+
+#define SRC_IP ((0<<24) + (0<<16) + (0<<8) + 0) /* src ip = 0.0.0.0 */
+#define DEST_IP ((192<<24) + (168<<16) + (1<<8) + 1) /* dest ip = 192.168.1.1 */
+#define FULL_MASK 0xffffffff /* full mask */
+#define EMPTY_MASK 0x0 /* empty mask */
+#define DEST_MASK 0xffff /* full mask */
+
+#define MAX_PATTERN_NUM 3 /* Maximal number of patterns for this example. */
+#define MAX_ACTION_NUM 2 /* Maximal number of actions for this example. */
+
+void
+snippet_ipv4_flow_create_actions(struct rte_flow_action *action);
+
+void
+snippet_ipv4_flow_create_patterns(struct rte_flow_item *patterns);
+
+struct rte_flow_template_table *
+snippet_ipv4_flow_create_table(uint16_t port_id, struct rte_flow_error *error);
+
+
+#endif /* SNIPPET_MATCH_IPV4 */
new file mode 100644
@@ -0,0 +1,178 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 NVIDIA Corporation & Affiliates
+ */
+
+#include <stdlib.h>
+
+#include <rte_flow.h>
+#include <rte_common.h>
+
+#include "../common.h"
+#include "snippet_match_mpls.h"
+
+static void
+snippet_mpls_create_actions(struct rte_flow_action *actions)
+{
+ /* Create one action that moves the packet to the selected queue. */
+ struct rte_flow_action_queue *queue;
+
+ queue = calloc(1, sizeof(struct rte_flow_item_ipv4));
+ if (queue == NULL)
+ fprintf(stderr, "Failed to allocate memory for queue\n");
+
+ /* Set the selected queue. */
+ queue->index = UINT16_MAX;
+
+ /* Set the action move packet to the selected queue. */
+ actions[0].type = RTE_FLOW_ACTION_TYPE_QUEUE;
+ actions[0].conf = queue;
+ actions[1].type = RTE_FLOW_ACTION_TYPE_END;
+}
+
+static void
+snippet_mpls_create_patterns(struct rte_flow_item *pattern)
+{
+ struct rte_flow_item_mpls *mpls_item;
+
+ mpls_item = calloc(1, sizeof(struct rte_flow_item_ipv4));
+ if (mpls_item == NULL)
+ fprintf(stderr, "Failed to allocate memory for mpls_item\n");
+
+ memcpy(mpls_item->label_tc_s, "\xab\xcd\xe1", sizeof(mpls_item->label_tc_s));
+
+ pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
+ pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4;
+ pattern[2].type = RTE_FLOW_ITEM_TYPE_UDP;
+ pattern[3].type = RTE_FLOW_ITEM_TYPE_MPLS;
+ pattern[4].type = RTE_FLOW_ITEM_TYPE_MPLS;
+ pattern[5].type = RTE_FLOW_ITEM_TYPE_MPLS;
+ pattern[5].spec = mpls_item;
+}
+
+static struct rte_flow_pattern_template *
+snippet_mpls_create_pattern_template(uint16_t port_id, struct rte_flow_error *error)
+{
+ /* Initialize the MPLS flow item with specific values. */
+ struct rte_flow_item_mpls mpls_item = {
+ /* The MPLS label set to the maximum value (0xfffff),
+ * the Traffic Class set to 0,
+ * and the Bottom of Stack bit set to 1.
+ */
+ .label_tc_s = "\xff\xff\xf1",
+ };
+
+ /* Define the flow pattern template. */
+ struct rte_flow_item pattern[] = {
+ {
+ .type = RTE_FLOW_ITEM_TYPE_ETH,
+ },
+ {
+ .type = RTE_FLOW_ITEM_TYPE_IPV4,
+ },
+ {
+ .type = RTE_FLOW_ITEM_TYPE_UDP,
+ },
+ {
+ .type = RTE_FLOW_ITEM_TYPE_MPLS,
+ },
+ {
+ .type = RTE_FLOW_ITEM_TYPE_MPLS,
+ },
+ {
+ .type = RTE_FLOW_ITEM_TYPE_MPLS,
+ .mask = &mpls_item,
+ },
+ {
+ .type = RTE_FLOW_ITEM_TYPE_END,
+ },
+ };
+
+ /* Set the pattern template attributes */
+ const struct rte_flow_pattern_template_attr pt_attr = {
+ .relaxed_matching = 0,
+ .ingress = 1,
+ };
+
+ return rte_flow_pattern_template_create(port_id, &pt_attr, pattern, error);
+}
+
+static struct rte_flow_actions_template *
+snippet_mpls_create_actions_template(uint16_t port_id, struct rte_flow_error *error)
+{
+ /* Define the queue action value. */
+ struct rte_flow_action_queue queue_v = {
+ .index = 0 /* The queue index is set to 0. */
+ };
+
+ /* Define the queue action mask. */
+ struct rte_flow_action_queue queue_m = {
+ /* The queue index mask is set to the maximum value (0xFFFF, the mask) */
+ .index = UINT16_MAX
+ };
+
+ /* Define the actions template. */
+ struct rte_flow_action actions[] = {
+ {
+ .type = RTE_FLOW_ACTION_TYPE_QUEUE,
+ .conf = &queue_v,
+ },
+ {
+ .type = RTE_FLOW_ACTION_TYPE_END,
+ },
+ };
+
+ /* Define the actions template masks. */
+ struct rte_flow_action masks[] = {
+ {
+ .type = RTE_FLOW_ACTION_TYPE_QUEUE,
+ .conf = &queue_m,
+ },
+ {
+ .type = RTE_FLOW_ACTION_TYPE_END,
+ },
+ };
+
+ const struct rte_flow_actions_template_attr at_attr = {
+ .ingress = 1,
+ };
+
+ return rte_flow_actions_template_create(port_id, &at_attr, actions, masks, error);
+}
+
+static struct rte_flow_template_table *
+snippet_mpls_create_table(uint16_t port_id, struct rte_flow_error *error)
+{
+ struct rte_flow_pattern_template *pt;
+ struct rte_flow_actions_template *at;
+
+ /* Define the template table attributes. */
+ const struct rte_flow_template_table_attr tbl_attr = {
+ .flow_attr = {
+ .group = 1,
+ .priority = 0,
+ .ingress = 1,
+ },
+
+ /* set the maximum number of flow rules that this table holds. */
+ .nb_flows = 1000,
+ };
+
+ /* Create the pattern template. */
+ pt = snippet_mpls_create_pattern_template(port_id, error);
+ if (pt == NULL) {
+ printf("Failed to create pattern template: %s (%s)\n",
+ error->message, rte_strerror(rte_errno));
+ return NULL;
+ }
+
+ /* Create the actions template. */
+ at = snippet_mpls_create_actions_template(port_id, error);
+ if (at == NULL) {
+ printf("Failed to create actions template: %s (%s)\n",
+ error->message, rte_strerror(rte_errno));
+ return NULL;
+ }
+
+ /* Create the template table. */
+ return rte_flow_template_table_create(port_id, &tbl_attr, &pt, 1, &at, 1, error);
+}
new file mode 100644
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 NVIDIA Corporation & Affiliates
+ */
+
+#ifndef SNIPPET_MATCH_MPLS
+#define SNIPPET_MATCH_MPLS
+
+/* Multiprotocol Label Switching snippet
+ * In this example we match MPLS tunnel over UDP when in HW steering mode.
+ * It supports multiple MPLS headers for matching as well as encapsulation/decapsulation.
+ * The maximum supported MPLS headers is 5.
+ */
+
+#define MAX_PATTERN_NUM 6 /* Maximal number of patterns for this example. */
+#define MAX_ACTION_NUM 2 /* Maximal number of actions for this example. */
+
+/* Replace this function with the snippet_*_create_actions() function in flow_skeleton.c. */
+static void
+snippet_mpls_create_actions(struct rte_flow_action *actions);
+
+/* Replace this function with the snippet_*_create_patterns() function in flow_skeleton.c. */
+static void
+snippet_mpls_create_patterns(struct rte_flow_item *pattern);
+
+/* Replace this function with the snippet_*_create_table() function in flow_skeleton.c. */
+static struct rte_flow_template_table *
+snippet_mpls_create_table(uint16_t port_id, struct rte_flow_error *error);
+
+#endif /* SNIPPET_MATCH_MPLS */