examples/flow_filtering: introduce use cases snippets

Message ID 20240909135308.241258-1-shperetz@nvidia.com (mailing list archive)
State New
Delegated to: Thomas Monjalon
Headers
Series examples/flow_filtering: introduce use cases snippets |

Checks

Context Check Description
ci/checkpatch warning coding style issues
ci/loongarch-compilation success Compilation OK
ci/loongarch-unit-testing success Unit Testing PASS
ci/Intel-compilation fail Compilation issues
ci/intel-Testing success Testing PASS
ci/github-robot: build fail github build: failed
ci/intel-Functional success Functional PASS
ci/iol-unit-arm64-testing success Testing PASS
ci/iol-unit-amd64-testing success Testing PASS
ci/iol-broadcom-Functional success Functional Testing PASS
ci/iol-compile-amd64-testing success Testing PASS
ci/iol-compile-arm64-testing success Testing PASS
ci/iol-broadcom-Performance success Performance Testing PASS
ci/iol-sample-apps-testing success Testing PASS
ci/iol-marvell-Functional success Functional Testing PASS
ci/iol-intel-Functional success Functional Testing PASS

Commit Message

Shani Peretz Sept. 9, 2024, 1:53 p.m. UTC
These code snippets demonstrate rule creation using
template and non-template APIs.
They include functions that enable developers to create rules.
The purpose of providing these snippets is to allow developers
to reuse them, thereby saving time and effort during the
implementation of flow rules.

The code snippets are categorized based on their usage and can be copied,
paste and modified to suit any requirements.
The snippets provided here are kept up to date and are being compiled
along with the rest of the examples.

There is a skeleton that demonstrates rule creation
using both template and non template APIs.

Developers can change the functions in the skeleton to the corresponding
snippet functions with the appropriate suffix and create rules using the
snippets themselves for easy testing. Each snippet has the same functions
to implement the actions and patterns for the corresponding feature.

Signed-off-by: Shani Peretz <shperetz@nvidia.com>
---
 doc/guides/prog_guide/ethdev/flow_offload.rst |   2 +
 doc/guides/sample_app_ug/flow_filtering.rst   | 295 +++++++-----------
 examples/flow_filtering/common.h              |  29 ++
 examples/flow_filtering/flow_blocks.c         | 112 -------
 examples/flow_filtering/flow_skeleton.c       |  83 +++++
 examples/flow_filtering/main.c                | 134 ++++++--
 examples/flow_filtering/meson.build           |   9 +
 .../snippets/snippet_match_gre.c              |  54 ++++
 .../snippets/snippet_match_gre.h              |  25 ++
 .../snippets/snippet_match_ipv4.c             | 154 +++++++++
 .../snippets/snippet_match_ipv4.h             |  31 ++
 .../snippets/snippet_match_mpls.c             | 178 +++++++++++
 .../snippets/snippet_match_mpls.h             |  29 ++
 13 files changed, 805 insertions(+), 330 deletions(-)
 create mode 100644 examples/flow_filtering/common.h
 delete mode 100644 examples/flow_filtering/flow_blocks.c
 create mode 100644 examples/flow_filtering/flow_skeleton.c
 create mode 100644 examples/flow_filtering/snippets/snippet_match_gre.c
 create mode 100644 examples/flow_filtering/snippets/snippet_match_gre.h
 create mode 100644 examples/flow_filtering/snippets/snippet_match_ipv4.c
 create mode 100644 examples/flow_filtering/snippets/snippet_match_ipv4.h
 create mode 100644 examples/flow_filtering/snippets/snippet_match_mpls.c
 create mode 100644 examples/flow_filtering/snippets/snippet_match_mpls.h
  

Patch

diff --git a/doc/guides/prog_guide/ethdev/flow_offload.rst b/doc/guides/prog_guide/ethdev/flow_offload.rst
index 3f6255c1a4..35ee7016b5 100644
--- a/doc/guides/prog_guide/ethdev/flow_offload.rst
+++ b/doc/guides/prog_guide/ethdev/flow_offload.rst
@@ -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
 ~~~~~~~~~~~~~~
 
diff --git a/doc/guides/sample_app_ug/flow_filtering.rst b/doc/guides/sample_app_ug/flow_filtering.rst
index 8cac2f8a8f..85f0283b00 100644
--- a/doc/guides/sample_app_ug/flow_filtering.rst
+++ b/doc/guides/sample_app_ug/flow_filtering.rst
@@ -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
diff --git a/examples/flow_filtering/common.h b/examples/flow_filtering/common.h
new file mode 100644
index 0000000000..173d641dd4
--- /dev/null
+++ b/examples/flow_filtering/common.h
@@ -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 */
diff --git a/examples/flow_filtering/flow_blocks.c b/examples/flow_filtering/flow_blocks.c
deleted file mode 100644
index f368e6124d..0000000000
--- a/examples/flow_filtering/flow_blocks.c
+++ /dev/null
@@ -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. */
diff --git a/examples/flow_filtering/flow_skeleton.c b/examples/flow_filtering/flow_skeleton.c
new file mode 100644
index 0000000000..49bd156fc9
--- /dev/null
+++ b/examples/flow_filtering/flow_skeleton.c
@@ -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. */
+}
diff --git a/examples/flow_filtering/main.c b/examples/flow_filtering/main.c
index f11f91a67c..7b34635b41 100644
--- a/examples/flow_filtering/main.c
+++ b/examples/flow_filtering/main.c
@@ -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();
diff --git a/examples/flow_filtering/meson.build b/examples/flow_filtering/meson.build
index 441678bbe5..35fb7a2e05 100644
--- a/examples/flow_filtering/meson.build
+++ b/examples/flow_filtering/meson.build
@@ -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'
diff --git a/examples/flow_filtering/snippets/snippet_match_gre.c b/examples/flow_filtering/snippets/snippet_match_gre.c
new file mode 100644
index 0000000000..8d840ab98b
--- /dev/null
+++ b/examples/flow_filtering/snippets/snippet_match_gre.c
@@ -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;
+}
diff --git a/examples/flow_filtering/snippets/snippet_match_gre.h b/examples/flow_filtering/snippets/snippet_match_gre.h
new file mode 100644
index 0000000000..3810e1f071
--- /dev/null
+++ b/examples/flow_filtering/snippets/snippet_match_gre.h
@@ -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 */
diff --git a/examples/flow_filtering/snippets/snippet_match_ipv4.c b/examples/flow_filtering/snippets/snippet_match_ipv4.c
new file mode 100644
index 0000000000..3f9942b9e7
--- /dev/null
+++ b/examples/flow_filtering/snippets/snippet_match_ipv4.c
@@ -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);
+}
diff --git a/examples/flow_filtering/snippets/snippet_match_ipv4.h b/examples/flow_filtering/snippets/snippet_match_ipv4.h
new file mode 100644
index 0000000000..b37614090c
--- /dev/null
+++ b/examples/flow_filtering/snippets/snippet_match_ipv4.h
@@ -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 */
diff --git a/examples/flow_filtering/snippets/snippet_match_mpls.c b/examples/flow_filtering/snippets/snippet_match_mpls.c
new file mode 100644
index 0000000000..67b8504841
--- /dev/null
+++ b/examples/flow_filtering/snippets/snippet_match_mpls.c
@@ -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);
+}
diff --git a/examples/flow_filtering/snippets/snippet_match_mpls.h b/examples/flow_filtering/snippets/snippet_match_mpls.h
new file mode 100644
index 0000000000..bf2b140638
--- /dev/null
+++ b/examples/flow_filtering/snippets/snippet_match_mpls.h
@@ -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 */