test suite reviews and discussions
 help / color / mirror / Atom feed
* [dts] [PATCH] rte flow: added test suite and framework
@ 2020-10-14 20:50 Owen Hilyard
  2020-10-14 21:02 ` [dts] [PATCH] rte flow: fixing checkpatch issues Owen Hilyard
  2020-10-22  8:53 ` [dts] [PATCH] rte flow: added test suite and framework Tu, Lijuan
  0 siblings, 2 replies; 14+ messages in thread
From: Owen Hilyard @ 2020-10-14 20:50 UTC (permalink / raw)
  To: dts, lihongx.ma
  Cc: ohilyard, lylavoie, zhaoyan.chen, yuan.peng, lijuan.tu, shall

The framework for the rte flow can be found in framework/flow. The plan
for this test suite was to design a type system and then use the
constraints created by the type system to generate all possible flow
rules within reason (addresses and ports would only use a few options).
The type system was designed and then it was found that this produces
2,549,026 patterns without dealing with any of the spec attributes of
the pattern items. If all possible actions were to be tested, this
brought the number of required test cases up to
11,479,792,543,757,705,936,896. As such, this brute-force approach was
abandoned. Attempts were made to restrict the type system enough to
create a reasonable number of rules, but eventually it was found to be
a much better use of developer time to write the rules by hand with the
aid of some runtime code generation to create most of the pattern test,
having them all use the queue action due to it's easy to detect presence
when verbose logging is turned on.

The type system is part of this commit because it still provides a
partial framework for creating flow rules, modeled after the way packets
may be created with scapy.

Protocols which are supported by testpmd but are not supported in scapy
were not tested due to the time requirements of creating protocol
implimentations.

Signed-off-by: Owen Hilyard <ohilyard@iol.unh.edu>
Signed-off-by: Sarah Hall <shall@iol.unh.edu>
---
 framework/flow/__init__.py           |    0
 framework/flow/enums.py              |   91 +
 framework/flow/exceptions.py         |   12 +
 framework/flow/flow.py               |  155 +
 framework/flow/flow_action_items.py  | 1018 +++++++
 framework/flow/flow_items.py         |   84 +
 framework/flow/flow_pattern_items.py | 1076 +++++++
 framework/flow/flow_rule.py          |   31 +
 framework/flow/generator.py          |  155 +
 test_plans/index.rst                 |    1 +
 test_plans/rte_flow_test_plan.rst    | 4114 ++++++++++++++++++++++++++
 test_plans/rte_flow_unsupported.rst  |  130 +
 tests/TestSuite_rte_flow.py          |  232 ++
 13 files changed, 7099 insertions(+)
 create mode 100644 framework/flow/__init__.py
 create mode 100644 framework/flow/enums.py
 create mode 100644 framework/flow/exceptions.py
 create mode 100644 framework/flow/flow.py
 create mode 100644 framework/flow/flow_action_items.py
 create mode 100644 framework/flow/flow_items.py
 create mode 100644 framework/flow/flow_pattern_items.py
 create mode 100644 framework/flow/flow_rule.py
 create mode 100644 framework/flow/generator.py
 create mode 100644 test_plans/rte_flow_test_plan.rst
 create mode 100644 test_plans/rte_flow_unsupported.rst
 create mode 100644 tests/TestSuite_rte_flow.py

diff --git a/framework/flow/__init__.py b/framework/flow/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/framework/flow/enums.py b/framework/flow/enums.py
new file mode 100644
index 00000000..b4981f2b
--- /dev/null
+++ b/framework/flow/enums.py
@@ -0,0 +1,91 @@
+from enum import Enum
+
+
+class FlowRuleType(Enum):
+    INGRESS = "ingress"
+    EGRESS = "egress"
+    BOTH = ""
+
+
+class FlowItemType(Enum):
+    UDP = "udp"
+    TCP = "tcp"
+    SCTP = "sctp"
+    IPV4 = "ipv4"
+    IPV6 = "ipv6"
+    END = "end"
+    VOID = "void"
+    INVERT = "invert"
+    ANY = "any"
+    RAW = "raw"
+    ETH = "eth"
+    VLAN = "vlan"
+    VXLAN = "vxlan"
+    GRE = "gre"
+    VXLAN_GPE = "vxlan_gpe"
+    ARP_ETH_IPV4 = "arp_eth_ipv4"
+    ICMP = "icmp"
+    ICMP6 = "icmp6"
+    MARK = "mark"
+    META = "meta"
+    TAG = "tag"
+    FUZZY = "fuzzy"
+
+
+class FlowActionType(Enum):
+    # "Simple" actions that don't need parameters
+    VOID = "void"
+    PASSTHRU = "passthru"
+    FLAG = "flag"
+    DROP = "drop"
+    COUNT = "count"
+    MAC_SWAP = "mac_swap"
+    DEC_TTL = "dec_ttl"
+
+    # Actions that do need parameters
+    JUMP = "jump"
+    MARK = "mark"
+    QUEUE = "queue"
+    RSS = "rss"
+    PF = "pf"
+    VF = "vf"
+    PHY_PORT = "phy_port"
+    PORT_ID = "port_id"
+    METER = "meter"
+    SECURITY = "security"
+    OF_SET_MPLS_TTL = "of_set_mpls_ttl"
+    OF_DEC_MPLS_TTL = "of_dec_mpls_ttl"
+    OF_SET_NW_TTL = "of_set_nw_ttl"
+    OF_DEC_NW_TTL = "of_dec_nw_ttl"
+    OF_COPY_TTL_OUT = "of_copy_ttl_out"
+    OF_COPY_TTL_IN = "of_copy_ttl_in"
+    OF_POP_VLAN = "of_pop_vlan"
+    OF_PUSH_VLAN = "of_push_vlan"
+    OF_SET_VLAN_VID = "of_set_vlan_vid"
+    OF_SET_VLAN_PCP = "of_set_vlan_pcp"
+    OF_POP_MPLS = "of_pop_mpls"
+    OF_PUSH_MPLS = "of_push_mpls"
+    VXLAN_ENCAP = "vxlan_encap"
+    VXLAN_DECAP = "vxlan_decap"
+    NVGRE_ENCAP = "nvgre_encap"
+    NVGRE_DECAP = "nvgre_decap"
+    RAW_ENCAP = "raw_encap"
+    RAW_DECAP = "raw_decap"
+    SET_IPV4_SRC = "set_ipv4_src"
+    SET_IPV4_DST = "set_ipv4_dst"
+    SET_IPV6_SRC = "set_ipv6_src"
+    SET_IPV6_DST = "set_ipv6_dst"
+    SET_TP_SRC = "set_tp_src"
+    SET_TP_DST = "set_tp_dst"
+    SET_TTL = "set_ttl"
+    SET_MAC_SRC = "set_mac_src"
+    SET_MAC_DST = "set_mac_dst"
+    INC_TCP_SEQ = "inc_tcp_seq"
+    DEC_TCP_SEQ = "dec_tcp_seq"
+    INC_TCP_ACK = "inc_tcp_ack"
+    DEC_TCP_ACK = "dec_tcp_ack"
+    SET_TAG = "set_tag"
+    SET_META = "set_meta"
+    SET_IPV4_DSCP = "set_ipv4_dscp"
+    SET_IPV6_DSCP = "set_ipv6_dscp"
+    AGE = "age"
diff --git a/framework/flow/exceptions.py b/framework/flow/exceptions.py
new file mode 100644
index 00000000..746b204f
--- /dev/null
+++ b/framework/flow/exceptions.py
@@ -0,0 +1,12 @@
+class CompositionException(Exception):
+
+    def __init__(self):
+        self.message = "There was an unexpected error in composition"
+
+
+class InvalidFlowItemException(CompositionException):
+    def __init__(self, first_item, second_item, flow=None):
+        if flow is not None:
+            self.message = f'"{first_item}" was not able to accept "{second_item}" as the next item in flow {flow}.'
+        else:
+            self.message = f'"{first_item}" was not able to accept "{second_item}".'
diff --git a/framework/flow/flow.py b/framework/flow/flow.py
new file mode 100644
index 00000000..3fa02e18
--- /dev/null
+++ b/framework/flow/flow.py
@@ -0,0 +1,155 @@
+# Items which may be used to start the protocol chain once actions have been used
+from __future__ import annotations
+
+import copy
+import itertools
+import operator
+from functools import reduce
+from typing import List, FrozenSet, Union, Iterable, Tuple
+
+from scapy.layers.l2 import Ether
+
+from framework.flow.enums import FlowItemType, FlowActionType
+from framework.flow.exceptions import InvalidFlowItemException
+from framework.flow.flow_action_items import ActionFlowItem
+from framework.flow.flow_items import FlowItem
+from framework.flow.flow_pattern_items import PatternFlowItem, TUNNELING_PROTOCOLS
+
+# Get reserved mac addresses
+NEVER_MATCH_PACKET = Ether(src="", dst="") / ('\x00' * 64)
+
+
+def _iterable_deep_compare(i1, i2):
+    return reduce(
+        lambda x, y: x and y,
+        map(lambda x, y: x == y, i1, i2),
+        True
+    )
+
+
+def expand_pattern_list_with_iterable_replacing_item(patterns: List[Iterable[FlowItem]],
+                                                     it: Iterable[Tuple[FlowItem, FrozenSet[str], FrozenSet[str], str]],
+                                                     item):
+    """
+    This function takes a list of patterns and splits each of them into 2
+    parts, excluding the item at index. It then uses the provided
+    iterator to fill in that value for all patterns.
+
+    Ex:
+    if patterns is [['a', 'b', 'c'], ['c','b','a']], it is [1,2], and item is 'b',
+    then this function will produce
+
+    [['a', 1], ['a', 2], ['a', 1], ['a', 2], ['a', 1], ['a', 2]]
+
+    if everything is converted into a list. It is not converted
+    because that requires using the memory to store all of this at
+    the same time, which could be fairly large.
+    """
+
+    split_patterns = list(map(lambda pattern: (pattern[:pattern.index(item)], pattern[pattern.index(item) + 1:],),
+                              filter(lambda pattern: item in pattern, patterns)))
+    # Tee the iterators so I can consume all of them
+
+    iterators = itertools.tee(it, len(patterns))
+    for pattern_before, pattern_after in split_patterns:
+        for iterator in iterators:
+            for dataset in iterator:
+                backup_dataset = copy.deepcopy(dataset)
+                yield (
+                    [*pattern_before, backup_dataset[0], *pattern_after],
+                    *backup_dataset[1:],
+                )
+            # yield from map(
+            #     lambda flow_item_test_properties: (
+            #         [*pattern_before, flow_item_test_properties[0], *pattern_after],
+            #         *flow_item_test_properties[1:],
+            #     ), iterator
+            # )
+
+    yield from filter(lambda pattern: item not in pattern, patterns)
+
+
+class Flow(object):
+    action_items: List[ActionFlowItem]
+    pattern_items: List[PatternFlowItem]
+    entry_points: FrozenSet[FlowItemType]
+
+    def __init__(self, action_items=None, pattern_items=None, ):
+        if action_items is None:
+            action_items = []
+
+        if pattern_items is None:
+            pattern_items = []
+
+        self.action_items = action_items
+        self.pattern_items = pattern_items
+
+    def __truediv__(self, item: Union[FlowItem, Flow]):
+        """
+        Used in a similar way to scapy's packet composition. Returns a new flow with the mutated state.
+        @param item: The other flow item.
+        @return: A Flow containing both items
+        """
+        if isinstance(item, Flow):
+            return Flow(pattern_items=[*self.pattern_items, *item.pattern_items],
+                        action_items=[*self.action_items, *item.action_items])
+        elif isinstance(item, PatternFlowItem):
+            if len(self.pattern_items) == 0:
+                return Flow(pattern_items=[*self.pattern_items, item], action_items=[*self.action_items])
+            elif item.type in self.pattern_items[-1].valid_next_items:
+                return Flow(pattern_items=[*self.pattern_items, item], action_items=[*self.action_items])
+            else:
+                raise InvalidFlowItemException(self.pattern_items[-1], item, flow=self)
+        elif isinstance(item, ActionFlowItem):
+            if len(self.action_items) == 0:
+                return Flow(pattern_items=[*self.pattern_items], action_items=[*self.action_items, item])
+
+            for action in self.action_items:
+                if item.type not in action.allowed_with:
+                    raise InvalidFlowItemException(action, item, flow=self)
+            return Flow(pattern_items=[*self.pattern_items], action_items=[*self.action_items, item])
+
+    def __str__(self):
+        return f"ingress pattern %s actions queue index 1 / end" % (
+                    " / ".join(str(item) for item in self.pattern_items) + " / end")
+
+    def __repr__(self):
+        return str(self)
+
+    def __eq__(self, other):
+        return isinstance(other, Flow) and \
+               len(self.action_items) == len(other.action_items) and \
+               len(self.pattern_items) == len(other.pattern_items) and \
+               _iterable_deep_compare(self.pattern_items, other.pattern_items) and \
+               _iterable_deep_compare(self.action_items, other.action_items)
+
+    def to_scapy_packet(self):
+        return reduce(operator.truediv, map(lambda x: x.to_scapy_packet(), self.pattern_items))
+
+    def get_test_property_flows(self, pattern_item_types_to_update=None, action_item_types_to_update=None) -> \
+            Iterable[Flow]:
+        if pattern_item_types_to_update is None and action_item_types_to_update is None:
+            pattern_item_types_to_update = [self.pattern_items[-1]]
+        elif pattern_item_types_to_update is None:
+            pattern_item_types_to_update = []
+        elif action_item_types_to_update is None:
+            action_item_types_to_update = []
+
+        # So that if this object is mutated before the generator is finished, it won't change anything
+        base_pattern_items = copy.deepcopy(self.pattern_items)
+        base_action_items = copy.deepcopy(self.action_items)
+
+        test_flows: Iterable[Iterable[FlowItem]] = [base_pattern_items]
+
+        tunnelling_protocols = list(filter(lambda i: type(i) in TUNNELING_PROTOCOLS, base_pattern_items))
+        if len(tunnelling_protocols) > 0:
+            test_flows = expand_pattern_list_with_iterable_replacing_item([*test_flows],
+                                                                          tunnelling_protocols[0].get_property_stream(),
+                                                                          tunnelling_protocols[0])
+        else:
+            test_flows = expand_pattern_list_with_iterable_replacing_item([*test_flows],
+                                                                          self.pattern_items[
+                                                                              -1].get_property_stream(),
+                                                                          self.pattern_items[-1])
+        for pattern in test_flows:
+            yield Flow(pattern_items=pattern[0], action_items=base_action_items), *pattern[1:]
diff --git a/framework/flow/flow_action_items.py b/framework/flow/flow_action_items.py
new file mode 100644
index 00000000..50201ee2
--- /dev/null
+++ b/framework/flow/flow_action_items.py
@@ -0,0 +1,1018 @@
+from typing import FrozenSet, Dict, Tuple
+
+from framework.flow.enums import FlowActionType
+from framework.flow.flow_pattern_items import FlowItem
+
+ALWAYS_ALLOWED_ACTIONS = {FlowActionType.VOID}
+
+ENTRY_POINTS = {
+    FlowActionType.VOID,
+    FlowActionType.PASSTHRU,
+    FlowActionType.FLAG,
+    FlowActionType.DROP,
+    FlowActionType.COUNT,
+    FlowActionType.MAC_SWAP,
+    FlowActionType.DEC_TTL,
+    FlowActionType.JUMP,
+    FlowActionType.MARK,
+    FlowActionType.QUEUE,
+    FlowActionType.RSS,
+    FlowActionType.PF,
+    FlowActionType.VF,
+    FlowActionType.PHY_PORT,
+    FlowActionType.PORT_ID,
+    FlowActionType.SECURITY,
+    FlowActionType.OF_SET_MPLS_TTL,
+    FlowActionType.OF_DEC_MPLS_TTL,
+    FlowActionType.OF_SET_NW_TTL,
+    FlowActionType.OF_DEC_NW_TTL,
+    FlowActionType.OF_COPY_TTL_OUT,
+    FlowActionType.OF_COPY_TTL_IN,
+    FlowActionType.OF_POP_VLAN,
+    FlowActionType.OF_PUSH_VLAN,
+    FlowActionType.OF_SET_VLAN_VID,
+    FlowActionType.OF_SET_VLAN_PCP,
+    FlowActionType.OF_POP_MPLS,
+    FlowActionType.OF_PUSH_MPLS,
+    FlowActionType.VXLAN_ENCAP,
+    FlowActionType.VXLAN_DECAP,
+    FlowActionType.NVGRE_ENCAP,
+    FlowActionType.NVGRE_DECAP,
+    FlowActionType.RAW_ENCAP,
+    FlowActionType.RAW_DECAP,
+    FlowActionType.SET_IPV4_SRC,
+    FlowActionType.SET_IPV4_DST,
+    FlowActionType.SET_IPV6_SRC,
+    FlowActionType.SET_IPV6_DST,
+    FlowActionType.SET_TP_SRC,
+    FlowActionType.SET_TP_DST,
+    FlowActionType.SET_TTL,
+    FlowActionType.SET_MAC_SRC,
+    FlowActionType.SET_MAC_DST,
+    FlowActionType.INC_TCP_SEQ,
+    FlowActionType.DEC_TCP_SEQ,
+    FlowActionType.INC_TCP_ACK,
+    FlowActionType.DEC_TCP_ACK,
+    FlowActionType.SET_TAG,
+    FlowActionType.SET_META,
+    FlowActionType.SET_IPV4_DSCP,
+    FlowActionType.SET_IPV6_DSCP,
+    FlowActionType.AGE,
+}
+
+
+class ActionFlowItem(FlowItem):
+    allowed_with: FrozenSet[FlowActionType] = \
+        frozenset({item for item in FlowActionType})
+
+    valid_next_items: FrozenSet[FlowActionType] = \
+        frozenset({item for item in FlowActionType})
+
+    test_case: Dict[str, Tuple[str, frozenset, frozenset]] = dict()
+
+class FlowActionVoid(ActionFlowItem):
+    type = FlowActionType.VOID
+    
+    test_case = {
+         'test': ('ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions void / end',
+                  frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                  frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionPassthru(ActionFlowItem):
+    type = FlowActionType.PASSTHRU
+    test_case = {
+        'test':  ('ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions passthru / end',
+                  frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                  frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionFlag(ActionFlowItem):
+    type = FlowActionType.FLAG
+    test_case = {
+        'test': ('ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions flag / end',
+                 frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                 frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                            "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)",
+                            "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)",
+                            "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionDrop(ActionFlowItem):
+    type = FlowActionType.DROP
+    test_case = {
+        'test': ('ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions drop / end',
+                 frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                 frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                            "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)",
+                            "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)",
+                            "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionCount(ActionFlowItem):
+    type = FlowActionType.COUNT
+    test_case = {
+        'test_shared': ('ingress pattern eth / ipv4 src is 192.168.0.1'
+                        ' / udp / end actions count shared 0 id 1 / end',
+                        frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                        frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                                   "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\x00' * 64)",
+                                   "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\x00' * 64)",
+                                   "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+        'test_id': ('ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions count id 1 / end',
+                    frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                    frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                              "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)",
+                               "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)",
+                               "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionMac_swap(ActionFlowItem):
+    type = FlowActionType.MAC_SWAP
+
+    test_case = {
+         'test': ('ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions mac_swap / end',
+                  frozenset({"Ether(src=\"90:61:ae:fd:41:43\", dst = \"ab:cd:ef:12:34:56\") "
+                            "/ IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                  frozenset({"Ether(src=\"90:61:ae:fd:41:43\", dst = \"ab:cd:ef:12:34:56\") "
+                            "/ IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                             "Ether(src=\"90:61:ae:fd:41:43\", dst = \"ab:cd:ef:12:34:56\") "
+                             "/ IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)",
+                             "Ether(src=\"90:61:ae:fd:41:43\", dst = \"ab:cd:ef:12:34:56\") "
+                             "/ IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)",
+                             "Ether(src=\"90:61:ae:fd:41:43\", dst = \"ab:cd:ef:12:34:56\") "
+                             "/ IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+     }
+
+
+class FlowActionDec_ttl(ActionFlowItem):
+    type = FlowActionType.DEC_TTL
+
+    test_case = {
+        'test': ('ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions dec_ttl / end',
+                 frozenset({"Ether() / IP(src=\"192.168.0.1\", ttl = 128) / UDP() / ('\\x00' * 64)"}),
+                 frozenset({"Ether() / IP(src=\"192.168.0.2\", ttl = 128) / UDP() / ('\\x00' * 64)",
+                            "Ether() / IP(src=\"10.0.30.99\", ttl = 128) / UDP() / ('\\x00' * 64)",
+                            "Ether() / IP(src=\"8.8.8.8\", ttl = 128 ) / UDP() / ('\\x00' * 64)",
+                            "Ether() / IP(src=\"132.177.0.99\", ttl = 128) / UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionJump(ActionFlowItem):
+    type = FlowActionType.JUMP
+
+    test_case = {
+         'test': ('ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions jump group 1 / end',
+                  frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                  frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionMark(ActionFlowItem):
+    type = FlowActionType.MARK
+    test_case = {
+        'test': ('ingress pattern eth / ipv4 src is 192.168.0.1 '
+                 '/ udp / end actions mark id 0xABCDEF / end',
+                 frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                 frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                            "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)",
+                            "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)",
+                            "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionQueue(ActionFlowItem):
+    type = FlowActionType.QUEUE
+    test_case = {
+        'test': ('ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions queue index 1 / end',
+                 frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                 frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                            "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)",
+                            "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)",
+                            "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionRss(ActionFlowItem):
+    type = FlowActionType.RSS
+
+    # RSS already has a test suite.
+    '''
+     test_case = {
+         'case1': ('ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions / end',
+                   frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                   frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                              "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)",
+                              "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)",
+                              "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+     }
+     '''
+
+
+class FlowActionPf(ActionFlowItem):
+    type = FlowActionType.PF
+    test_case = {
+        'test': ('ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions pf / end',
+                 frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                 frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                            "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)",
+                            "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)",
+                            "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionVf(ActionFlowItem):
+    type = FlowActionType.VF
+    test_case = {
+        'test_original': ('ingress pattern eth / ipv4 src is 192.168.0.1 /'
+                          ' udp / end actions vf original 1/ end',
+                          frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                          frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                                     "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\x00' * 64)",
+                                     "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\x00' * 64)",
+                                     "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+        'test_id': ('ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions vf id 1 / end',
+                    frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                    frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                               "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)",
+                               "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)",
+                               "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionPhy_port(ActionFlowItem):
+    type = FlowActionType.PHY_PORT
+
+    test_case = {
+         # original port index
+         'test_original': ('ingress pattern eth / ipv4 src is 192.168.0.1'
+                           ' / udp / end actions phy_port original / end',
+                           frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                           frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                                      "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)",
+                                      "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)",
+                                      "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+         # physical port index
+         'test_index': ('ingress pattern eth / ipv4 src is 192.168.0.1 '
+                        '/ udp / end actions phy_port index 1 / end',
+                        frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                        frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                                   "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)",
+                                   "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)",
+                                   "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionPort_id(ActionFlowItem):
+    type = FlowActionType.PORT_ID
+
+    test_case = {
+        # original DPDK port ID
+        'test_original': ('ingress pattern eth / ipv4 src is 192.168.0.1 '
+                          '/ udp / end actions port_id original / end',
+                          frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                          frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                                     "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)",
+                                     "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)",
+                                     "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+        # DPDK port ID
+        'test_id': ('ingress pattern eth / ipv4 src is 192.168.0.1 '
+                    '/ udp / end actions port_id id 1 / end',
+                    frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                    frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                               "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)",
+                               "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)",
+                               "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+     }
+
+
+class FlowActionMeter(ActionFlowItem):
+    type = FlowActionType.METER
+    test_case = {
+        'test': ('ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions meter mtr_id 1 / end',
+                 frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                 frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                            "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)",
+                            "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)",
+                            "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionSecurity(ActionFlowItem):
+    type = FlowActionType.SECURITY
+    test_case = {
+        'test': ('ingress pattern eth / ipv4 src is 192.168.0.1'
+                 ' / udp / end actions security security_session 1 / end',
+                 frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                 frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                            "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)",
+                            "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)",
+                            "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionOf_set_mpls_ttl(ActionFlowItem):
+    type = FlowActionType.OF_SET_MPLS_TTL
+
+    test_case = {
+         'test': ('ingress pattern eth / ipv4 src is 192.168.0.1 '
+                  '/ udp / end actions of_set_mpls_ttl mpls_ttl 64 / end',
+                  frozenset({"Ether() / IP(src=\"192.168.0.1\") / MPLS(label = 0xab, ttl=128)"
+                             " / UDP() / ('\\x00' * 64)"}),
+                  frozenset({"Ether() / IP(src=\"192.168.0.2\") / MPLS(label = 0xab, ttl=128) / UDP() / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"10.0.30.99\") / MPLS(label = 0xab, ttl=128) / UDP() / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"8.8.8.8\") / MPLS(label = 0xab, ttl=128) / UDP() / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"132.177.0.99\") / MPLS(label = 0xab, ttl=128)"
+                             " / UDP() / ('\\x00' * 64)"})),
+     }
+
+
+class FlowActionOf_dec_mpls_ttl(ActionFlowItem):
+    type = FlowActionType.OF_DEC_MPLS_TTL
+
+    test_case = {
+        'test': (
+            'ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions of_dec_mpls_ttl / end',
+            frozenset({"Ether() / IP(src=\"192.168.0.1\") / MPLS(label = 0xab, ttl=128) / UDP() / ('\\x00' * 64)"}),
+            frozenset({"Ether() / IP(src=\"192.168.0.2\") / MPLS(label = 0xab, ttl=128) / UDP() / ('\\x00' * 64)",
+                       "Ether() / IP(src=\"10.0.30.99\") / MPLS(label = 0xab, ttl=128) / UDP() / ('\\x00' * 64)",
+                       "Ether() / IP(src=\"8.8.8.8\") / MPLS(label = 0xab, ttl=128) / UDP() / ('\\x00' * 64)",
+                       "Ether() / IP(src=\"132.177.0.99\") / MPLS(label = 0xab, ttl=128) / UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionOf_set_nw_ttl(ActionFlowItem):
+    type = FlowActionType.OF_SET_NW_TTL
+
+    test_case = {
+         'test': ('ingress pattern eth / ipv4 src is 192.168.0.1 '
+                  '/ udp / end actions of_set_nw_ttl nw_ttl 64 / end',
+                  frozenset({"Ether() / IP(src=\"192.168.0.1\", ttl=128) / UDP() / ('\\x00' * 64)"}),
+                  frozenset({"Ether() / IP(src=\"192.168.0.2\", ttl=128) / UDP() / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"10.0.30.99\", ttl=128) / UDP() / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"8.8.8.8\", ttl=128) / UDP() / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"132.177.0.99\", ttl=128) / UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionOf_dec_nw_ttl(ActionFlowItem):
+    type = FlowActionType.OF_DEC_NW_TTL
+    test_case = {
+        'test': (
+            'ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions of_dec_nw_ttl / end',
+            frozenset({"Ether() / IP(src=\"192.168.0.1\", ttl=128) / UDP() / ('\\x00' * 64)"}),
+            frozenset({"Ether() / IP(src=\"192.168.0.2\", ttl=128) / UDP() / ('\\x00' * 64)",
+                       "Ether() / IP(src=\"10.0.30.99\", ttl=128) / UDP() / ('\\x00' * 64)",
+                       "Ether() / IP(src=\"8.8.8.8\", ttl=128) / UDP() / ('\\x00' * 64)",
+                       "Ether() / IP(src=\"132.177.0.99\", ttl=128) / UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionOf_copy_ttl_out(ActionFlowItem):
+    type = FlowActionType.OF_COPY_TTL_OUT
+
+    test_case = {
+         'test': ('ingress pattern eth / ipv4 src is 192.168.0.1 '
+                  '/ udp / end actions of_copy_ttl_out / end',
+                  frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                  frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionOf_copy_ttl_in(ActionFlowItem):
+    type = FlowActionType.OF_COPY_TTL_IN
+
+    test_case = {
+         'test': ('ingress pattern eth / ipv4 src is 192.168.0.1 '
+                  '/ udp / end actions of_copy_ttl_out / end',
+                  frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                  frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionOf_pop_vlan(ActionFlowItem):
+    type = FlowActionType.OF_POP_VLAN
+
+    test_case = {
+         'test': ('ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions of_pop_vlan / end',
+                  frozenset({"Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"192.168.0.1\") "
+                             "/ UDP() / ('\\x00' * 64)"}),
+                  frozenset({"Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"192.168.0.2\") "
+                             "/ UDP() / ('\\x00' * 64)",
+                             "Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"10.0.30.99\") "
+                             "/ UDP() / ('\\x00' * 64)",
+                             "Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"8.8.8.8\") "
+                             "/ UDP() / ('\\x00' * 64)",
+                             "Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"132.177.0.99\")"
+                             " / UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionOf_push_vlan(ActionFlowItem):
+    type = FlowActionType.OF_PUSH_VLAN
+    test_case = {
+         'test': ('ingress pattern eth / ipv4 src is 192.168.0.1'
+                  ' / udp / end actions of_push_vlan ethertype 0x8100 / end',
+                  frozenset({"Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"192.168.0.1\") "
+                             "/ UDP() / ('\\x00' * 64)"}),
+                  frozenset({"Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"192.168.0.2\") "
+                             "/ UDP() / ('\\x00' * 64)",
+                             "Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"10.0.30.99\") "
+                             "/ UDP() / ('\\x00' * 64)",
+                             "Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"8.8.8.8\") "
+                             "/ UDP() / ('\\x00' * 64)",
+                             "Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"132.177.0.99\")"
+                             " / UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionOf_set_vlan_vid(ActionFlowItem):
+    type = FlowActionType.OF_SET_VLAN_VID
+
+    test_case = {
+        'test': ('ingress pattern eth / ipv4 src is 192.168.0.1 '
+                 '/ udp / end actions of_set_vlan_vid vlan_vid 0xbbb / end',
+                 frozenset({
+                               "Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"192.168.0.1\")"
+                               " / UDP() / ('\\x00' * 64)"}),
+                 frozenset({
+                               "Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"192.168.0.2\") "
+                               "/ UDP() / ('\\x00' * 64)",
+                               "Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"10.0.30.99\") "
+                               "/ UDP() / ('\x00' * 64)",
+                               "Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"8.8.8.8\") "
+                               "/ UDP() / ('\x00' * 64)",
+                               "Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"132.177.0.99\") "
+                               "/ UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionOf_set_vlan_pcp(ActionFlowItem):
+    type = FlowActionType.OF_SET_VLAN_PCP
+    test_case = {
+        'test': ('ingress pattern eth / ipv4 src is 192.168.0.1'
+                 ' / udp / end actions of_set_vlan_vid vlan_pcp 0x7 / end',
+                 frozenset({
+                               "Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"192.168.0.1\") "
+                               "/ UDP() / ('\\x00' * 64)"}),
+                 frozenset({
+                               "Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"192.168.0.2\") "
+                               "/ UDP() / ('\\x00' * 64)",
+                               "Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"10.0.30.99\") "
+                               "/ UDP() / ('\x00' * 64)",
+                               "Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"8.8.8.8\") "
+                               "/ UDP() / ('\x00' * 64)",
+                               "Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"132.177.0.99\") "
+                               "/ UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionOf_pop_mpls(ActionFlowItem):
+    type = FlowActionType.OF_POP_MPLS
+    test_case = {
+        'test': (
+            'ingress pattern eth / ipv4 src is 192.168.0.1 '
+            '/ udp / end actions of_pop_mpls ethertype 0x0806 / end',
+            frozenset({"Ether() / IP(src=\"192.168.0.1\") / MPLS(label = 0xab, ttl=128) / UDP() / ('\\x00' * 64)"}),
+            frozenset({"Ether() / IP(src=\"192.168.0.2\") / MPLS(label = 0xab, ttl=128) / UDP() / ('\\x00' * 64)",
+                       "Ether() / IP(src=\"10.0.30.99\") / MPLS(label = 0xab, ttl=128) / UDP() / ('\x00' * 64)",
+                       "Ether() / IP(src=\"8.8.8.8\") / MPLS(label = 0xab, ttl=128) / UDP() / ('\x00' * 64)",
+                       "Ether() / IP(src=\"132.177.0.99\") / MPLS(label = 0xab, ttl=128) / UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionOf_push_mpls(ActionFlowItem):
+    type = FlowActionType.OF_PUSH_MPLS
+
+    test_case = {
+        'test': (
+            'ingress pattern eth / ipv4 src is 192.168.0.1'
+            ' / udp / end actions of_push_mpls ethertype 0x0806 / end',
+            frozenset({"Ether() / IP(src=\"192.168.0.1\") / MPLS(label = 0xab, ttl=128) / UDP() / ('\\x00' * 64)"}),
+            frozenset({"Ether() / IP(src=\"192.168.0.2\") / MPLS(label = 0xab, ttl=128) / UDP() / ('\\x00' * 64)",
+                       "Ether() / IP(src=\"10.0.30.99\") / MPLS(label = 0xab, ttl=128) / UDP() / ('\x00' * 64)",
+                       "Ether() / IP(src=\"8.8.8.8\") / MPLS(label = 0xab, ttl=128) / UDP() / ('\x00' * 64)",
+                       "Ether() / IP(src=\"132.177.0.99\") / MPLS(label = 0xab, ttl=128) / UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionVxlan_encap(ActionFlowItem):
+    type = FlowActionType.VXLAN_ENCAP
+
+    test_case = {
+         # VXLAN encap definition is the VNI?
+         'test': ('ingress pattern eth / ipv4 src is 192.168.0.1'
+                  ' / udp / end actions vxlan_encap definition 0x112233 / end',
+                  frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                  frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\x00' * 64)",
+                             "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\x00' * 64)",
+                             "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+     }
+
+
+class FlowActionVxlan_decap(ActionFlowItem):
+    type = FlowActionType.VXLAN_DECAP
+
+    test_case = {
+         'test': ('ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions vxlan_decap / end',
+                  frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / VXLAN() / ('\\x00' * 64)"}),
+                  frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / VXLAN() / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"10.0.30.99\") / UDP() / VXLAN() / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"8.8.8.8\") / UDP() / VXLAN() / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"132.177.0.99\") / UDP() / VXLAN() / ('\\x00' * 64)"})),
+     }
+
+
+class FlowActionNvgre_encap(ActionFlowItem):
+    type = FlowActionType.NVGRE_ENCAP
+    # NVGRE PACKETS NOT SUPPORTED BY SCAPY.
+    '''
+     test_case = {
+         'test': ('ingress pattern eth / ipv4 src is 192.168.0.1 
+         / udp / end actions nvgre_encap definition 0x112233 / end',
+                   frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() /  NVGRE() / ('\\x00' * 64)"}),
+                   frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() /  NVGRE() / ('\\x00' * 64)",
+                              "Ether() / IP(src=\"10.0.30.99\") / UDP() /  NVGRE() / ('\\x00' * 64)",
+                              "Ether() / IP(src=\"8.8.8.8\") / UDP() /  NVGRE() / ('\\x00' * 64)",
+                              "Ether() / IP(src=\"132.177.0.99\") / UDP() /  NVGRE() / ('\\x00' * 64)"})),
+     }
+     '''
+
+
+class FlowActionNvgre_decap(ActionFlowItem):
+    type = FlowActionType.NVGRE_DECAP
+    # NVGRE PACKETS NOT SUPPORTED BY SCAPY.
+    '''
+     test_case = {
+         'test': ('ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions nvgre_decap / end',
+                   frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / NVGRE() / ('\\x00' * 64)"}),
+                   frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / NVGRE() / ('\\x00' * 64)",
+                              "Ether() / IP(src=\"10.0.30.99\") / UDP() / NVGRE() / ('\\x00' * 64)",
+                              "Ether() / IP(src=\"8.8.8.8\") / UDP() / NVGRE() / ('\\x00' * 64)",
+                              "Ether() / IP(src=\"132.177.0.99\") / UDP() / NVGRE() / ('\\x00' * 64)"})),
+     }
+     '''
+
+
+class FlowActionRaw_encap(ActionFlowItem):
+    type = FlowActionType.RAW_ENCAP
+    # Assume we are encapsulating with a VLAN header with the following values:
+    # TPID: 0x8100
+    # Prio: 0x5
+    # PCP: 0
+    # VID: 0xaaa
+    # This makes the full header: 0x8100aaaa
+    test_case = {
+        'test_data': ('ingress pattern eth / ipv4 src is 192.168.0.1 '
+                      '/ udp / end actions raw_encap data 0x8100aaaa / end',
+                      frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                      frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                                 "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\x00' * 64)",
+                                 "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\x00' * 64)",
+                                 "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+
+        'test_preserve': ('ingress pattern eth / ipv4 src is 192.168.0.1 '
+                          '/ udp / end actions raw_encap data 0x8100aaaa preserve 0xffffffff / end',
+                          frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                          frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                                     "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)",
+                                     "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)",
+                                     "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+        # Is "size" in bits or bytes? Unclear in documentation, defaulting to bits.
+        'test_size': ('ingress pattern eth / ipv4 src is 192.168.0.1 '
+                      '/ udp / end actions raw_encap data 0x8100aaaa size 32 / end',
+                      frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                      frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                                 "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\x00' * 64)",
+                                 "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\x00' * 64)",
+                                 "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionRaw_decap(ActionFlowItem):
+    type = FlowActionType.RAW_DECAP
+    test_case = {
+        'test_data': (
+            'ingress pattern eth / ipv4 src is 192.168.0.1 '
+            '/ udp / end actions raw_decap data 0x8100aaaa / end',
+            frozenset({"Ether()  / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+            frozenset({"Ether() /  Dot1Q(prio = 0x5, id = 0x0, vlan = 0xbbb) / IP(src=\"192.168.0.2\")"
+                       " / UDP() / ('\\x00' * 64)",
+                       "Ether() /  Dot1Q(prio = 0x5, id = 0x0, vlan = 0xbbb) / IP(src=\"10.0.30.99\") "
+                       "/ UDP() / ('\x00' * 64)",
+                       "Ether() /  Dot1Q(prio = 0x5, id = 0x0, vlan = 0xbbb) / IP(src=\"8.8.8.8\")"
+                       " / UDP() / ('\x00' * 64)",
+                       "Ether() /  Dot1Q(prio = 0x5, id = 0x0, vlan = 0xbbb) / IP(src=\"132.177.0.99\") "
+                       "/ UDP() / ('\\x00' * 64)"})),
+
+        # Is "size" in bits or bytes? Unclear in documentation, defaulting to bits.
+        'test_size': (
+            'ingress pattern eth / ipv4 src is 192.168.0.1 '
+            '/ udp / end actions raw_decap data 0x8100aaaa size 32 / end',
+            frozenset({"Ether() /  Dot1Q(prio = 0x5, id = 0x0, vlan = 0xbbb) / IP(src=\"192.168.0.1\") "
+                       "/ UDP() / ('\\x00' * 64)"}),
+            frozenset({"Ether() /  Dot1Q(prio = 0x5, id = 0x0, vlan = 0xbbb) / IP(src=\"192.168.0.2\") "
+                       "/ UDP() / ('\\x00' * 64)",
+                       "Ether() /  Dot1Q(prio = 0x5, id = 0x0, vlan = 0xbbb) / IP(src=\"10.0.30.99\")"
+                       " / UDP() / ('\x00' * 64)",
+                       "Ether() /  Dot1Q(prio = 0x5, id = 0x0, vlan = 0xbbb) / IP(src=\"8.8.8.8\") "
+                       "/ UDP() / ('\x00' * 64)",
+                       "Ether() /  Dot1Q(prio = 0x5, id = 0x0, vlan = 0xbbb) / IP(src=\"132.177.0.99\") "
+                       "/ UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionSet_ipv4_src(ActionFlowItem):
+    type = FlowActionType.SET_IPV4_SRC
+
+    test_case = {
+         'test': ('ingress pattern eth / ipv4 src is 192.168.0.1 '
+                  '/ udp / end actions set_ipv4_src ipv4_addr 172.16.0.10  / end',
+                  frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                  frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+     }
+
+
+class FlowActionSet_ipv4_dst(ActionFlowItem):
+    type = FlowActionType.SET_IPV4_DST
+
+    test_case = {
+         'test': ('ingress pattern eth / ipv4 dst is 192.168.0.1'
+                  ' / udp / end actions set_ipv4_dst ipv4_addr 172.16.0.10 / end',
+                  frozenset({"Ether() / IP(dst=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                  frozenset({"Ether() / IP(dst=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                             "Ether() / IP(dst=\"10.0.30.99\") / UDP() / ('\\x00' * 64)",
+                             "Ether() / IP(dst=\"8.8.8.8\") / UDP() / ('\\x00' * 64)",
+                             "Ether() / IP(dst=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionSet_ipv6_src(ActionFlowItem):
+    type = FlowActionType.SET_IPV6_SRC
+
+    test_case = {
+         'test': ('ingress pattern eth / ipv6 src is 2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c2 '
+                  '/ udp / end actions set_ipv6_src ipv6_addr 2001:0000:9d38:6ab8:1c48:9999:aaaa:bbbb',
+                  frozenset({"Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c2\") "
+                             "/ UDP() / ('\\x00' * 64)"}),
+                  frozenset({"Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c3\") / UDP() / ('\\x00' * 64)",
+                            "Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c4\") / UDP() / ('\x00' * 64)",
+                             "Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c5\") / UDP() / ('\x00' * 64)",
+                             "Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c6\") "
+                             "/ UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionSet_ipv6_dst(ActionFlowItem):
+    type = FlowActionType.SET_IPV6_DST
+
+    test_case = {
+         'test': ('ingress pattern eth / ipv6 dst is 2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c2 '
+                  '/ udp / end actions set_ipv6_dst ipv6_addr 2001:0000:9d38:6ab8:1c48:9999:aaaa:bbbb',
+                  frozenset({"Ether() / IPv6(dst=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c2\")"
+                             " / UDP() / ('\\x00' * 64)"}),
+                  frozenset({"Ether() / IPv6(dst=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c3\") / UDP() / ('\\x00' * 64)",
+                            "Ether() / IPv6(dst=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c4\") / UDP() / ('\x00' * 64)",
+                             "Ether() / IPv6(dst=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c5\") / UDP() / ('\x00' * 64)",
+                             "Ether() / IPv6(dst=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c6\") "
+                             "/ UDP() / ('\\x00' * 64)"})),
+      }
+
+
+class FlowActionSet_tp_src(ActionFlowItem):
+    type = FlowActionType.SET_TP_SRC
+
+    test_case = {
+        # UDP
+        'test_udp': ('ingress pattern eth / ipv4 src is 192.168.0.1'
+                     ' / udp / end actions set_tp_src port 1998 / end',
+                     frozenset({"Ether() / IP(src=\"192.168.0.1\") UDP(sport=3838) / ('\\x00' * 64)"}),
+                     frozenset({"Ether() / IP(src=\"192.168.0.2\") UDP(sport=3838) / ('\\x00' * 64)",
+                                "Ether() / IP(src=\"10.0.30.99\") UDP(sport=3838) / ('\x00' * 64)",
+                                "Ether() / IP(src=\"8.8.8.8\") UDP(sport=3838) / ('\x00' * 64)",
+                                "Ether() / IP(src=\"132.177.0.99\") UDP(sport=3838) / ('\\x00' * 64)"})),
+
+        # TCP
+        'test_tcp': (
+          'ingress pattern eth / ipv4 src is 192.168.0.1 / tcp / end actions set_tp_src port 1998 / end',
+          frozenset({"Ether() / IP(src=\"192.168.0.1\") TCP(sport=3838) / ('\\x00' * 64)"}),
+          frozenset({"Ether() / IP(src=\"192.168.0.2\") TCP(sport=3838) / ('\\x00' * 64)",
+                     "Ether() / IP(src=\"10.0.30.99\") TCP(sport=3838) / ('\x00' * 64)",
+                     "Ether() / IP(src=\"8.8.8.8\") TCP(sport=3838) / ('\x00' * 64)",
+                     "Ether() / IP(src=\"132.177.0.99\") TCP(sport=3838) / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionSet_tp_dst(ActionFlowItem):
+    type = FlowActionType.SET_TP_DST
+
+    test_case = {
+        # UDP
+        'test_udp': ('ingress pattern eth / ipv4 src is 192.168.0.1 '
+                     '/ udp / end actions set_tp_dst port 1998 / end',
+                     frozenset({"Ether() / IP(src=\"192.168.0.1\") UDP(dport=3838) / ('\\x00' * 64)"}),
+                     frozenset({"Ether() / IP(src=\"192.168.0.2\") UDP(dport=3838) / ('\\x00' * 64)",
+                                "Ether() / IP(src=\"10.0.30.99\") UDP(dport=3838) / ('\x00' * 64)",
+                                "Ether() / IP(src=\"8.8.8.8\") UDP(dport=3838) / ('\x00' * 64)",
+                                "Ether() / IP(src=\"132.177.0.99\") UDP(dport=3838) / ('\\x00' * 64)"})),
+
+        # TCP
+        'test_tcp': (
+          'ingress pattern eth / ipv4 src is 192.168.0.1 / tcp / end actions set_tp_dst port 1998 / end',
+          frozenset({"Ether() / IP(src=\"192.168.0.1\") TCP(dport=3838) / ('\\x00' * 64)"}),
+          frozenset({"Ether() / IP(src=\"192.168.0.2\") TCP(dport=3838) / ('\\x00' * 64)",
+                     "Ether() / IP(src=\"10.0.30.99\") TCP(dport=3838) / ('\x00' * 64)",
+                     "Ether() / IP(src=\"8.8.8.8\") TCP(dport=3838) / ('\x00' * 64)",
+                     "Ether() / IP(src=\"132.177.0.99\") TCP(dport=3838) / ('\\x00' * 64)"})),
+
+    }
+
+
+class FlowActionSet_ttl(ActionFlowItem):
+    type = FlowActionType.SET_TTL
+
+    test_case = {
+         'test': ('ingress pattern eth / ipv4 src is 192.168.0.1'
+                  ' / udp / end actions set_ttl ttl_value 64 / end',
+                  frozenset({"Ether() / IP(src=\"192.168.0.1\" , ttl=128 ) / UDP() / ('\\x00' * 64)"}),
+                  frozenset({"Ether() / IP(src=\"192.168.0.2\" , ttl=128 ) / UDP() / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"10.0.30.99\" , ttl=128 ) / UDP() / ('\x00' * 64)",
+                             "Ether() / IP(src=\"8.8.8.8\", ttl=128 ) / UDP() / ('\x00' * 64)",
+                             "Ether() / IP(src=\"132.177.0.99\", ttl=128 ) / UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionSet_mac_src(ActionFlowItem):
+    type = FlowActionType.SET_MAC_SRC
+
+    test_case = {
+         'test': ('ingress pattern eth / ipv4 src is 192.168.0.1'
+                  ' / udp / end actions set_mac_src mac_addr 10:20:30:40:50:60 / end',
+                  frozenset({"Ether(src=\"90:61:ae:fd:41:43\") / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                  frozenset({"Ether(src=\"90:61:ae:fd:41:43\") / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                            "Ether(src=\"90:61:ae:fd:41:43\") / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)",
+                             "Ether(src=\"90:61:ae:fd:41:43\") / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)",
+                             "Ether(src=\"90:61:ae:fd:41:43\") / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+     }
+
+
+class FlowActionSet_mac_dst(ActionFlowItem):
+    type = FlowActionType.SET_MAC_DST
+    test_case = {
+             'test': ('ingress pattern eth / ipv4 src is 192.168.0.1'
+                      ' / udp / end actions set_mac_dst mac_addr 10:20:30:40:50:60 / end',
+                      frozenset({"Ether(dst=\"90:61:ae:fd:41:43\") / IP(src=\"192.168.0.1\") "
+                                 "/ UDP() / ('\\x00' * 64)"}),
+                      frozenset({"Ether(dst=\"90:61:ae:fd:41:43\") / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                                "Ether(dst=\"90:61:ae:fd:41:43\") / IP(src=\"10.0.30.99\") / UDP() / ('\x00' * 64)",
+                                 "Ether(dst=\"90:61:ae:fd:41:43\") / IP(src=\"8.8.8.8\") / UDP() / ('\x00' * 64)",
+                                 "Ether(dst=\"90:61:ae:fd:41:43\") / IP(src=\"132.177.0.99\") "
+                                 "/ UDP() / ('\\x00' * 64)"})),
+         }
+
+
+class FlowActionInc_tcp_seq(ActionFlowItem):
+    type = FlowActionType.INC_TCP_SEQ
+
+    test_case = {
+         'test': ('ingress pattern eth / ipv4 src is 192.168.0.1 / tcp / end actions inc_tcp_seq / end',
+                  frozenset({"Ether() / IP(src=\"192.168.0.1\") / TCP(seq=2) / ('\\x00' * 64)"}),
+                  frozenset({"Ether() / IP(src=\"192.168.0.2\") / TCP(seq=2) / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"10.0.30.99\") / TCP(seq=2) / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"8.8.8.8\") / TCP(seq=2) / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"132.177.0.99\") / TCP(seq=2) / ('\\x00' * 64)"})),
+     }
+
+
+class FlowActionDec_tcp_seq(ActionFlowItem):
+    type = FlowActionType.DEC_TCP_SEQ
+
+    test_case = {
+         'test': ('ingress pattern eth / ipv4 src is 192.168.0.1 / tcp / end actions dec_tcp_seq / end',
+                  frozenset({"Ether() / IP(src=\"192.168.0.1\") / TCP(seq=2) / ('\\x00' * 64)"}),
+                  frozenset({"Ether() / IP(src=\"192.168.0.2\") / TCP(seq=2) / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"10.0.30.99\") / TCP(seq=2) / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"8.8.8.8\") / TCP(seq=2) / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"132.177.0.99\") / TCP(seq=2) / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionInc_tcp_ack(ActionFlowItem):
+    type = FlowActionType.INC_TCP_ACK
+
+    test_case = {
+         'test': ('ingress pattern eth / ipv4 src is 192.168.0.1 / tcp / end actions inc_tcp_ack / end',
+                  frozenset({"Ether() / IP(src=\"192.168.0.1\") / TCP(ack=2) / ('\\x00' * 64)"}),
+                  frozenset({"Ether() / IP(src=\"192.168.0.2\") / TCP(ack=2) / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"10.0.30.99\") / TCP(ack=2) / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"8.8.8.8\") / TCP(ack=2) / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"132.177.0.99\") / TCP(ack=2) / ('\\x00' * 64)"})),
+     }
+
+
+class FlowActionDec_tcp_ack(ActionFlowItem):
+    type = FlowActionType.DEC_TCP_ACK
+
+    test_case = {
+         'test': ('ingress pattern eth / ipv4 src is 192.168.0.1 / tcp / end actions dec_tcp_ack / end',
+                  frozenset({"Ether() / IP(src=\"192.168.0.1\") / TCP(ack=2) / ('\\x00' * 64)"}),
+                  frozenset({"Ether() / IP(src=\"192.168.0.2\") / TCP(ack=2) / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"10.0.30.99\") / TCP(ack=2) / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"8.8.8.8\") / TCP(ack=2) / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"132.177.0.99\") / TCP(ack=2) / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionSet_tag(ActionFlowItem):
+    type = FlowActionType.SET_TAG
+
+    test_case = {
+         'test_data': ('ingress pattern eth / ipv4 src is 192.168.0.1 '
+                       '/ udp / end actions set_tag data 0xabc / end',
+                       frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                       frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                                  "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\x00' * 64)",
+                                  "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\x00' * 64)",
+                                  "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+         # bit-mask applies to "data"
+         'test_mask': ('ingress pattern eth / ipv4 src is 192.168.0.1 '
+                       '/ udp / end actions set_tag data 0xabc mask 0xcba / end',
+                       frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                       frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                                  "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)",
+                                  "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)",
+                                  "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+         'test_index': ('ingress pattern eth / ipv4 src is 192.168.0.1 '
+                        '/ udp / end actions set_tag data 0xabc index 1 / end',
+                        frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                        frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                                   "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\x00' * 64)",
+                                   "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\x00' * 64)",
+                                   "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionSet_meta(ActionFlowItem):
+    type = FlowActionType.SET_META
+
+    test_case = {
+         'test_data': ('ingress pattern eth / ipv4 src is 192.168.0.1 '
+                       '/ udp / end actions set_meta data 0xabc / end',
+                       frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                       frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                                  "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)",
+                                  "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)",
+                                  "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+         # bit-mask applies to "data"
+         'test_mask': ('ingress pattern eth / ipv4 src is 192.168.0.1 '
+                       '/ udp / end actions set_meta data 0xabc mask 0xcb / end',
+                       frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                       frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                                  "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\x00' * 64)",
+                                  "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\x00' * 64)",
+                                  "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionSet_ipv4_dscp(ActionFlowItem):
+    type = FlowActionType.SET_IPV4_DSCP
+    
+    test_case = {
+         'test': ('ingress pattern eth / ipv4 src is 192.168.0.1 '
+                  '/ udp / end actions set_ipv4_dscp dscp 2 / end',
+                  frozenset({"Ether() / IP(src=\"192.168.0.1\", tos = 0) / UDP() / ('\\x00' * 64)"}),
+                  frozenset({"Ether() / IP(src=\"192.168.0.2\", tos = 0) / UDP() / ('\\x00' * 64)",
+                             "Ether() / IP(src=\"10.0.30.99\", tos = 0) / UDP() / ('\x00' * 64)",
+                             "Ether() / IP(src=\"8.8.8.8\", tos = 0) / UDP() / ('\x00' * 64)",
+                             "Ether() / IP(src=\"132.177.0.99\", tos = 0) / UDP() / ('\\x00' * 64)"})),
+    }
+
+
+class FlowActionSet_ipv6_dscp(ActionFlowItem):
+    type = FlowActionType.SET_IPV6_DSCP
+
+    test_case = {
+         'test': ('ingress pattern eth / ipv6 src is 2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c2 '
+                  '/ udp / end actions set_ipv6_dscp dscp 0x30',
+                  frozenset({"Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c2\", tc = 0) "
+                             "/ UDP() / ('\\x00' * 64)"}),
+                  frozenset({"Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c3\", tc = 0) "
+                             "/ UDP() / ('\\x00' * 64)",
+                            "Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c4\", tc = 0) "
+                             "/ UDP() / ('\x00' * 64)",
+                             "Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c5\", tc = 0) "
+                             "/ UDP() / ('\x00' * 64)",
+                             "Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c6\", tc = 0) "
+                             "/ UDP() / ('\\x00' * 64)"})),
+     }
+
+
+class FlowActionAge(ActionFlowItem):
+    type = FlowActionType.AGE
+
+    test_case = {
+         'test_timeout': ('ingress pattern eth / ipv4 src is 192.168.0.1 '
+                          '/ udp / end actions age timeout 128 / end',
+                          frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                          frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                                     "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\x00' * 64)",
+                                     "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\x00' * 64)",
+                                     "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+         # 8 bits reserved, must be zero
+         'test_reserved': ('ingress pattern eth / ipv4 src is 192.168.0.1 '
+                           '/ udp / end actions age timeout 128 reserved 0 / end',
+                           frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                           frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                                      "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\x00' * 64)",
+                                      "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\x00' * 64)",
+                                      "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+         # The user flow context, NULL means the rte_flow pointer.
+         'test_context': ('ingress pattern eth / ipv4 src is 192.168.0.1 '
+                          '/ udp / end actions age timeout 128 context NULL / end',
+                          frozenset({"Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)"}),
+                          frozenset({"Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)",
+                                    "Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)",
+                                     "Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)",
+                                     "Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)"})),
+
+    }
+
+
+ACTION_ITEMS_TYPE_CLASS_MAPPING: Dict[FlowActionType, ActionFlowItem] = {
+    FlowActionType.PASSTHRU: FlowActionPassthru,
+    FlowActionType.FLAG: FlowActionFlag,
+    FlowActionType.DROP: FlowActionDrop,
+    FlowActionType.COUNT: FlowActionCount,
+    FlowActionType.MAC_SWAP: FlowActionMac_swap,
+    FlowActionType.DEC_TTL: FlowActionDec_ttl,
+    FlowActionType.JUMP: FlowActionJump,
+    FlowActionType.MARK: FlowActionMark,
+    FlowActionType.QUEUE: FlowActionQueue,
+    FlowActionType.RSS: FlowActionRss,
+    FlowActionType.PF: FlowActionPf,
+    FlowActionType.VF: FlowActionVf,
+    FlowActionType.PHY_PORT: FlowActionPhy_port,
+    FlowActionType.PORT_ID: FlowActionPort_id,
+    FlowActionType.METER: FlowActionMeter,
+    FlowActionType.SECURITY: FlowActionSecurity,
+    FlowActionType.OF_SET_MPLS_TTL: FlowActionOf_set_mpls_ttl,
+    FlowActionType.OF_DEC_MPLS_TTL: FlowActionOf_dec_mpls_ttl,
+    FlowActionType.OF_SET_NW_TTL: FlowActionOf_set_nw_ttl,
+    FlowActionType.OF_DEC_NW_TTL: FlowActionOf_dec_nw_ttl,
+    FlowActionType.OF_COPY_TTL_OUT: FlowActionOf_copy_ttl_out,
+    FlowActionType.OF_COPY_TTL_IN: FlowActionOf_copy_ttl_in,
+    FlowActionType.OF_POP_VLAN: FlowActionOf_pop_vlan,
+    FlowActionType.OF_PUSH_VLAN: FlowActionOf_push_vlan,
+    FlowActionType.OF_SET_VLAN_VID: FlowActionOf_set_vlan_vid,
+    FlowActionType.OF_SET_VLAN_PCP: FlowActionOf_set_vlan_pcp,
+    FlowActionType.OF_POP_MPLS: FlowActionOf_pop_mpls,
+    FlowActionType.OF_PUSH_MPLS: FlowActionOf_push_mpls,
+    FlowActionType.VXLAN_ENCAP: FlowActionVxlan_encap,
+    FlowActionType.VXLAN_DECAP: FlowActionVxlan_decap,
+    FlowActionType.NVGRE_ENCAP: FlowActionNvgre_encap,
+    FlowActionType.NVGRE_DECAP: FlowActionNvgre_decap,
+    FlowActionType.RAW_ENCAP: FlowActionRaw_encap,
+    FlowActionType.RAW_DECAP: FlowActionRaw_decap,
+    FlowActionType.SET_IPV4_SRC: FlowActionSet_ipv4_src,
+    FlowActionType.SET_IPV4_DST: FlowActionSet_ipv4_dst,
+    FlowActionType.SET_IPV6_SRC: FlowActionSet_ipv6_src,
+    FlowActionType.SET_IPV6_DST: FlowActionSet_ipv6_dst,
+    FlowActionType.SET_TP_SRC: FlowActionSet_tp_src,
+    FlowActionType.SET_TP_DST: FlowActionSet_tp_dst,
+    FlowActionType.SET_TTL: FlowActionSet_ttl,
+    FlowActionType.SET_MAC_SRC: FlowActionSet_mac_src,
+    FlowActionType.SET_MAC_DST: FlowActionSet_mac_dst,
+    FlowActionType.INC_TCP_SEQ: FlowActionInc_tcp_seq,
+    FlowActionType.DEC_TCP_SEQ: FlowActionDec_tcp_seq,
+    FlowActionType.INC_TCP_ACK: FlowActionInc_tcp_ack,
+    FlowActionType.DEC_TCP_ACK: FlowActionDec_tcp_ack,
+    FlowActionType.SET_TAG: FlowActionSet_tag,
+    FlowActionType.SET_META: FlowActionSet_meta,
+    FlowActionType.SET_IPV4_DSCP: FlowActionSet_ipv4_dscp,
+    FlowActionType.SET_IPV6_DSCP: FlowActionSet_ipv6_dscp,
+    FlowActionType.AGE: FlowActionAge
+}
diff --git a/framework/flow/flow_items.py b/framework/flow/flow_items.py
new file mode 100644
index 00000000..ed5e8d9c
--- /dev/null
+++ b/framework/flow/flow_items.py
@@ -0,0 +1,84 @@
+from __future__ import annotations
+
+import copy
+import itertools
+from functools import reduce
+from typing import FrozenSet, Union, Any, Set, Dict, Tuple, Iterable, Hashable
+
+from framework.flow.enums import FlowItemType, FlowActionType
+from framework.flow.exceptions import InvalidFlowItemException
+from framework.flow import flow_action_items
+from framework.flow import flow_pattern_items
+
+PATTERN_ACTION_ITEMS = {FlowItemType.INVERT, FlowItemType.VOID, FlowItemType.MARK, FlowItemType.META}
+
+
+class FlowItem(object):
+    type: Union[FlowItemType, FlowActionType]
+    # Defines what items this may not appear with
+    allowed_with: FrozenSet[Union[FlowItemType, FlowActionType]]
+    # OSI Model layer of the protocol
+    # This should be the lowest layer a protocol is used in, for example
+    # QUIC would be considered L5 since it needs to go after UDP (L4),
+    # even though it has capabilities in L6.
+    layer: int
+    valid_next_items: FrozenSet[Union[FlowItemType, FlowActionType]]
+
+    # Types subject to change, should only be accessed through
+    possible_properties: Dict[str, Tuple[str, FrozenSet[str], FrozenSet[str]]]
+    properties: str
+
+    def get_property_stream(self) -> Iterable[Tuple[FlowItem, FrozenSet[str], FrozenSet[str], str]]:
+        """
+        This function will return a generator that will provide all
+        configured property combinations.
+
+        This function will not mutate the instance it is called on.
+
+        @return: a generator that will provide all
+        permutations of possible properties this object has as a flow
+        item with properties
+        """
+        base_copy = copy.deepcopy(self)
+        for key, value in self.possible_properties.items():
+            new_copy = copy.deepcopy(base_copy)
+            new_copy.properties = value[0]  # The properties string
+            yield new_copy, *value[1:], f"{self.type.value}_{key}"
+
+    def __init__(self):
+        self.properties = ""
+
+    def __truediv__(self, other: FlowItem):
+        """
+        Used in a similar way to scapy's packet composition.
+        @param other: The other flow item.
+        @return: A Flow containing both items
+        """
+        if type(self) != type(other):
+            raise InvalidFlowItemException(self, other)
+        elif other.type in self.valid_next_items:
+            # This import is in here so there is no circular import
+            from framework.flow.flow import Flow
+            if isinstance(self, flow_pattern_items.PatternFlowItem):
+                return Flow(pattern_items=[self, other])
+            elif isinstance(self, flow_action_items.ActionFlowItem):
+                return Flow(action_items=[self, other])
+            else:
+                raise TypeError(
+                    f"{type(self):s} is not one of {flow_pattern_items.PatternFlowItem:s}, {flow_action_items.ActionFlowItem:s}.")
+        else:
+            raise InvalidFlowItemException(self, other)
+
+    def __eq__(self, other) -> bool:
+        return type(self) == type(other) and \
+               self.type == other.type and \
+               self.properties == other.properties
+
+    def __str__(self):
+        if self.properties != "":
+            return self.properties
+        else:
+            return self.type.value
+
+    def __repr__(self):
+        return str(self)
diff --git a/framework/flow/flow_pattern_items.py b/framework/flow/flow_pattern_items.py
new file mode 100644
index 00000000..77e2de0d
--- /dev/null
+++ b/framework/flow/flow_pattern_items.py
@@ -0,0 +1,1076 @@
+# Allows the type system to handle referencing a class inside it's definition
+from typing import FrozenSet, Dict, List, Tuple, Iterable
+
+from scapy.layers.inet import UDP, TCP, IP, ICMP
+from scapy.layers.inet6 import IPv6
+from scapy.layers.l2 import Ether, Dot1Q, GRE, ARP
+from scapy.layers.sctp import SCTP
+from scapy.layers.vxlan import VXLAN
+from scapy.packet import Packet
+
+from framework.flow.enums import FlowItemType
+from framework.flow.exceptions import InvalidFlowItemException
+from framework.flow.flow_items import FlowItem
+
+ALWAYS_ALLOWED_ITEMS = {
+    FlowItemType.RAW,
+    FlowItemType.VOID
+}
+L3_FLOW_TYPES = {FlowItemType.IPV4, FlowItemType.IPV6}
+L4_FLOW_ITEMS = {FlowItemType.UDP, FlowItemType.TCP, FlowItemType.SCTP, FlowItemType.GRE, }
+
+PATTERN_OPERATION_TYPES = {
+    FlowItemType.MARK,
+    FlowItemType.META,
+    FlowItemType.TAG,
+    FlowItemType.FUZZY,
+    FlowItemType.INVERT,
+}
+
+TUNNELING_PROTOCOL_TYPES = {
+    FlowItemType.VLAN,
+    FlowItemType.VXLAN,
+    FlowItemType.GRE,
+    FlowItemType.VXLAN_GPE
+}
+
+
+class PatternFlowItem(FlowItem):
+    allowed_with: FrozenSet[FlowItemType] = \
+        frozenset({item for item in FlowItemType})
+
+    valid_next_items: List[FlowItemType] = \
+        [item for item in FlowItemType]
+
+    # Only used for building a tree upward
+    valid_parent_items: List[FlowItemType] = \
+        [item for item in FlowItemType]
+
+    possible_properties: List[Tuple[str, Iterable, Iterable]] = {}
+
+    def __truediv__(self, other: FlowItem):
+        """
+        Used in a similar way to scapy's packet composition.
+        @param other: The other flow item.
+        @return: A Flow containing both items
+        """
+        if other.type in self.valid_next_items or \
+                other.type == FlowItemType.END:
+            # This import is in here so there is no circular import
+            from framework.flow.flow import Flow
+            return Flow(pattern_items=[self, other])
+        else:
+            raise InvalidFlowItemException(self, other)
+
+    # def to_scapy_packet(self):
+    #    scapy_class: type = ITEM_TYPE_SCAPY_CLASS_MAPPING[self.type]
+
+
+class FlowItemEnd(PatternFlowItem):
+    type = FlowItemType.END
+    valid_next_items = list({})
+
+
+class FlowItemVoid(PatternFlowItem):
+    type = FlowItemType.VOID
+
+
+class FlowItemInvert(PatternFlowItem):
+    type = FlowItemType.INVERT
+
+
+class FlowItemAny(PatternFlowItem):
+    type = FlowItemType.ANY
+
+
+class FlowItemRaw(PatternFlowItem):
+    type = FlowItemType.RAW
+
+
+class FlowItemArp_eth_ipv4(PatternFlowItem):
+    type = FlowItemType.ARP_ETH_IPV4
+    valid_next_items = list({FlowItemType.RAW, FlowItemType.VOID})
+    valid_parent_items: List[FlowItemType] = [FlowItemType.IPV4]
+    """
+    - ``hdr``: hardware type, normally 1. => hwtype
+    - ``pro``: protocol type, normally 0x0800. => ptype = 2048 
+    - ``hln``: hardware address length, normally 6. => hwlen
+    - ``pln``: protocol address length, normally 4. => plen
+    - ``op``: opcode (1 for request, 2 for reply). => op
+    - ``sha``: sender hardware address. => hwsrc
+    - ``spa``: sender IPv4 address => psrc
+    - ``tha``: target hardware address. => hwdst
+    - ``tpa``: target IPv4 address. => pdst
+    - Default ``mask`` matches SHA, SPA, THA and TPA.
+    """
+    possible_properties = {
+        'hdr':
+            ('arp_eth_ipv4 hdr is 1',
+             frozenset({"Ether() / ARP(hwtype=1) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / ARP(hwtype=2) / ('\\x00' * 64)",
+                        "Ether() / ARP(hwtype=3) / ('\\x00' * 64)",
+                        "Ether() / ARP(hwtype=6) / ('\\x00' * 64)",
+                        "Ether() / ARP(hwtype-15) / ('\\x00' * 64)"
+                        })),
+        'pro':
+            ('arp_eth_ipv4 pro is 0x0800',
+             frozenset({"Ether() / ARP(ptype=0x0800) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / ARP(ptype=0x0800) / ('\\x00' * 64)",
+                        "Ether() / ARP(ptype=0x0842) / ('\\x00' * 64)",
+                        "Ether() / ARP(ptype=0x6004) / ('\\x00' * 64)",
+                        "Ether() / ARP(ptype=0x809b) / ('\\x00' * 64)"
+                        })),
+
+        'hln':
+            ('arp_eth_ipv4 hln is 6',
+             frozenset({"Ether() / ARP(hwlen=6) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / ARP(hwlen=12) / ('\\x00' * 64)",
+                        "Ether() / ARP(hwlen=2) / ('\\x00' * 64)",
+                        "Ether() / ARP(hwlen=8) / ('\\x00' * 64)",
+                        "Ether() / ARP(hwlen=4) / ('\\x00' * 64)"
+                        })),
+
+        'pln':
+            ('arp_eth_ipv4 pln is 4',
+             frozenset({"Ether() / ARP(plen=4) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / ARP(plen=6) / ('\\x00' * 64)",
+                        "Ether() / ARP(plen=2) / ('\\x00' * 64)",
+                        "Ether() / ARP(plen=8) / ('\\x00' * 64)",
+                        "Ether() / ARP(plen=12) / ('\\x00' * 64)"
+                        })),
+
+        'op':
+            ('arp_eth_ipv4 op is 1',
+             frozenset({"Ether() / ARP(op=1) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / ARP(op=2) / ('\\x00' * 64)",
+                        "Ether() / ARP(op=3) / ('\\x00' * 64)",
+                        "Ether() / ARP(op=4) / ('\\x00' * 64)",
+                        "Ether() / ARP(op=5) / ('\\x00' * 64)"
+                        })),
+
+        'sha':
+            ('arp_eth_ipv4 sha is 90:61:ae:fd:41:43',
+             frozenset({"Ether() / ARP(hwsrc=\"90:61:ae:fd:41:43\") / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / ARP(hwsrc=\"90:61:ae:fd:41:44\") / ('\\x00' * 64)",
+                        "Ether() / ARP(hwsrc=\"90:61:ae:fd:41:45\") / ('\\x00' * 64)",
+                        "Ether() / ARP(hwsrc=\"90:61:ae:fd:41:46\") / ('\\x00' * 64)",
+                        "Ether() / ARP(hwsrc=\"90:61:ae:fd:41:47\") / ('\\x00' * 64)"
+                        })),
+
+        'spa':
+            ('arp_eth_ipv4 spa is 192.168.0.80',
+             frozenset({"Ether() / ARP(psrc=\"192.168.0.80\") / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / ARP(psrc=\"10.0.30.10\") / ('\\x00' * 64)",
+                        "Ether() / ARP(psrc=\"8.8.8.8\") / ('\\x00' * 64)",
+                        "Ether() / ARP(psrc=\"132.177.0.5\") / ('\\x00' * 64)",
+                        "Ether() / ARP(psrc=\"123.4.5.6\") / ('\\x00' * 64)"
+                        })),
+        'tha':
+            ('arp_eth_ipv4 tha is 00:00:00:00:00:00',
+             frozenset({"Ether() / ARP(hwdst=00:00:00:00:00:00) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / ARP(hwdst=90:61:ae:fd:41:45) / ('\\x00' * 64)",
+                        "Ether() / ARP(hwdst=90:61:ae:fd:41:46) / ('\\x00' * 64)",
+                        "Ether() / ARP(hwdst=90:61:ae:fd:41:47) / ('\\x00' * 64)",
+                        "Ether() / ARP(hwdst=90:61:ae:fd:41:48) / ('\\x00' * 64)"
+                        })),
+
+        'tpa':
+            ('arp_eth_ipv4 tpa is 192.168.0.1',
+             frozenset({"Ether() / ARP(pdst=192.168.0.1) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / ARP(pdst=10.0.30.10) / ('\\x00' * 64)",
+                        "Ether() / ARP(pdst=8.8.8.8) / ('\\x00' * 64)",
+                        "Ether() / ARP(pdst=132.177.0.5) / ('\\x00' * 64)",
+                        "Ether() / ARP(pdst=123.4.5.6) / ('\\x00' * 64)"
+                        })),
+
+    }
+
+
+class FlowItemEth(PatternFlowItem):
+    type = FlowItemType.ETH
+    valid_next_items = list(ALWAYS_ALLOWED_ITEMS | L3_FLOW_TYPES | {FlowItemType.VLAN, FlowItemType.ARP_ETH_IPV4})
+    valid_parent_items: List[FlowItemType] = list({})
+    # Matches an Ethernet header (not Ethernet frame).
+
+    """
+    - ``dst``: destination MAC.
+    - ``src``: source MAC.
+    - ``type``: EtherType or TPID. (TPID value is 0x8100, any others are normal EtherType)
+    - Default ``mask`` matches destination and source addresses only.
+    """
+    possible_properties = {
+        'dst':
+            ('eth dst is 90:61:ae:fd:41:43',
+             frozenset({"Ether(dst=\"90:61:ae:fd:41:43\") / ('\\x00' * 64)"}),
+
+             frozenset({"Ether(dst=\"90:61:ae:fd:41:44\") / ('\\x00' * 64)",
+                        "Ether(dst=\"90:61:ae:fd:41:45\") / ('\\x00' * 64)",
+                        "Ether(dst=\"90:61:ae:fd:41:46\") / ('\\x00' * 64)",
+                        "Ether(dst=\"91:61:ae:fd:41:43\") / ('\\x00' * 64)"
+                        })),
+        'src':
+            ('eth src is 90:61:ae:fd:41:43',
+             frozenset({"Ether(src=\"90:61:ae:fd:41:43\") / ('\\x00' * 64)"}),
+
+             frozenset({"Ether(src=\"90:61:ae:fd:41:44\") / ('\\x00' * 64)",
+                        "Ether(src=\"90:61:ae:fd:41:45\") / ('\\x00' * 64)",
+                        "Ether(src=\"90:61:ae:fd:41:46\") / ('\\x00' * 64)",
+                        "Ether(src=\"91:61:ae:fd:41:43\") / ('\\x00' * 64)"
+                        })),
+        'type':
+            ('eth type is 0x0800',  # IPv4 EtherType
+             frozenset({"Ether(type=0x0800) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether(type=0x0842) / ('\\x00' * 64)",
+                        "Ether(type=0x8100) / ('\\x00' * 64)",  # Possibly a special case? TPID/VLAN
+                        "Ether(type=0x9100) / ('\\x00' * 64)",  # Possibly special, VLAN double tagging
+                        "Ether(type=0x8863) / ('\\x00' * 64)",
+                        "Ether(type=0x9000) / ('\\x00' * 64)"
+                        })),
+    }
+
+
+class FlowItemGre(PatternFlowItem):
+    type = FlowItemType.GRE
+    valid_next_items = list(L3_FLOW_TYPES | ALWAYS_ALLOWED_ITEMS)
+    valid_parent_items: List[FlowItemType] = [FlowItemType.IPV4, FlowItemType.IPV6]
+    """
+    - ``c_rsvd0_ver``: checksum, reserved 0 and version.
+    - ``protocol``: protocol type.
+    - Default ``mask`` matches protocol only.
+    """
+    possible_properties = {
+        'c_rsvd0_ver':
+           ('gre c_rsvd0_ver is 0',
+            frozenset({"Ether() / GRE(chksum_present=0, version=0) / ('\\x00' * 64)"}),
+
+           frozenset({"Ether() / GRE(chksum_present=1, version=0)) / ('\\x00' * 64)", #this is the only other option
+                      })),
+        'protocol':
+            ('gre protocol is 0x0800',
+             frozenset({"Ether() / GRE(proto=0x0800) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / GRE(proto=0x0842) / ('\\x00' * 64)",
+                        "Ether() / GRE(proto=0x8100) / ('\\x00' * 64)",
+                        "Ether() / GRE(proto=0x0806) / ('\\x00' * 64)",
+                        "Ether() / GRE(proto=0x809B) / ('\\x00' * 64)"
+                        }))
+    }
+
+
+class FlowItemIcmp(PatternFlowItem):
+    type = FlowItemType.ICMP
+    valid_next_items = list({FlowItemType.RAW, FlowItemType.VOID})
+    valid_parent_items: List[FlowItemType] = [FlowItemType.IPV4]
+    """
+    - ``hdr``: ICMP header definition (``rte_icmp.h``).
+    This definition includes:
+    icmp_type (8 bits; for IPv4 echo request it's "8")
+    icmp_code (8 bits)
+    
+    THE FOLLOWING ARE NOT SUPPORTED IN TESTPMD:
+    icmp_cksum (16 bits)
+    icmp_ident (16 bits)
+    icmp_seq_nb (16 bits)
+    - Default ``mask`` matches ICMP type and code only.
+    """
+    possible_properties = {
+
+        'icmp_type':
+            ('icmp type is 3',
+             frozenset({"Ether() / ICMP(type=3) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / ICMP(type=3) / ('\\x00' * 64)",
+                        "Ether() / ICMP(type=11) / ('\\x00' * 64)",
+                        "Ether() / ICMP(type=13) / ('\\x00' * 64)",
+                        "Ether() / ICMP(type=0) / ('\\x00' * 64)"
+                        })),
+        'icmp_code':
+            ('icmp type is 3 code is 3',  # Assume type 3 code 3; code meanings/options are dependent on type.
+
+             frozenset({"Ether() / ICMP(type=3, code=3) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / ICMP(type=3, code=0) / ('\\x00' * 64)",
+                        "Ether() / ICMP(type=3, code=2) / ('\\x00' * 64)",
+                        "Ether() / ICMP(type=11, code=1) / ('\\x00' * 64)",
+                        "Ether() / ICMP(type=12, code=2) / ('\\x00' * 64)"
+                        })),
+        'icmp_cksum':
+           ('icmp cksum is 0x0800',
+            frozenset({"Ether() / ICMP() / UDP() / ('\x00' * 64)"}),
+
+            frozenset({"Ether() / ICMP() / UDP() / ('\x00' * 64)",
+                       "Ether() / ICMP() / UDP() / ('\x00' * 64)",
+                       "Ether() / ICMP() / UDP() / ('\x00' * 64)",
+                       "Ether() / ICMP() / UDP() / ('\x00' * 64)"
+                       })),
+        'icmp_ident':
+           ('icmp ident is 0x0800',
+            frozenset({"Ether() / ICMP() / UDP() / ('\x00' * 64)"}),
+
+            frozenset({"Ether() / ICMP() / UDP() / ('\x00' * 64)",
+                       "Ether() / ICMP() / UDP() / ('\x00' * 64)",
+                       "Ether() / ICMP() / UDP() / ('\x00' * 64)",
+                       "Ether() / ICMP() / UDP() / ('\x00' * 64)"
+                       })),
+        'icmp_seq_nb':
+           ('icmp seq_nb is 0x0800',
+            frozenset({"Ether() / ICMP(proto=0x0800) / UDP() / ('\x00' * 64)"}),
+
+            frozenset({"Ether() / ICMP() / UDP() / ('\x00' * 64)",
+                       "Ether() / ICMP() / UDP() / ('\x00' * 64)",
+                       "Ether() / ICMP() / UDP() / ('\x00' * 64)",
+                       "Ether() / ICMP() / UDP() / ('\x00' * 64)"
+                        })),
+    }
+
+
+class FlowItemIcmp6(PatternFlowItem):
+    type = FlowItemType.ICMP6
+    valid_next_items = list({FlowItemType.RAW, FlowItemType.VOID})
+    valid_parent_items: List[FlowItemType] = [FlowItemType.IPV6]
+    """
+    - ``type``: ICMPv6 type.
+    - ``code``: ICMPv6 code.
+    - ``checksum``: ICMPv6 checksum.
+    - Default ``mask`` matches ``type`` and ``code``.
+    """
+    # ICMP6 NOT SUPPORTED BY TESTPMD.
+    # DO NOT UNCOMMENT THE FOLLOWING TEST CASES UNTIL IT IS SUPPORTED.
+    possible_properties = {
+        'type':
+            ('icmp6 type is 1',  # Destination Unreachable
+             frozenset({"Ether() / ICMPv6DestUnreach(type=1) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / ICMPv6DestUnreach(type=128) / ('\\x00' * 64)",
+                        "Ether() / ICMPv6DestUnreach(type=129) / ('\\x00' * 64)",
+                        "Ether() / ICMPv6DestUnreach(type=3) / ('\\x00' * 64)",
+                        "Ether() / ICMPv6DestUnreach(type=135) / ('\\x00' * 64)"
+                        })),
+        'code':  # ICMP code is dependent on type; these are possible Destination Unreachable codes
+            ('icmp6 code is 0',
+             frozenset({"Ether() / ICMPv6DestUnreach(code=0) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / ICMPv6DestUnreach(code=1) / ('\\x00' * 64)",
+                        "Ether() / ICMPv6DestUnreach(code=2) / ('\\x00' * 64)",
+                        "Ether() / ICMPv6DestUnreach(code=3) / ('\\x00' * 64)",
+                        "Ether() / ICMPv6DestUnreach(code=4) / ('\\x00' * 64)"
+                        })),
+
+        'checksum':
+            ('icmp6 cksum is 0x1234',
+             frozenset({"Ether() / ICMPv6DestUnreach(cksum=0x1234) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / ICMPv6DestUnreach(cksum=0x4321) / ('\\x00' * 64)",
+                        "Ether() / ICMPv6DestUnreach(cksum=0xffff) / ('\\x00' * 64)",
+                        "Ether() / ICMPv6DestUnreach(cksum=0x1233) / ('\\x00' * 64)",
+                        "Ether() / ICMPv6DestUnreach(cksum=0x1010) / ('\\x00' * 64)"
+                        })),
+    }
+
+
+class FlowItemIpv4(PatternFlowItem):
+    type = FlowItemType.IPV4
+    valid_next_items = list(L4_FLOW_ITEMS | {FlowItemType.ICMP} | ALWAYS_ALLOWED_ITEMS)
+    valid_parent_items: List[FlowItemType] = [FlowItemType.ETH, FlowItemType.GRE]
+    """
+    Note: IPv4 options are handled by dedicated pattern items.
+    
+    - ``hdr``: IPv4 header definition (``rte_ip.h``).
+    - Default ``mask`` matches source and destination addresses only.
+    """
+
+    possible_properties = {
+
+        'tos':
+            ('ipv4 tos is 0',
+             frozenset({"Ether() / IP(tos=0) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / IP(tos=2) / ('\\x00' * 64)",
+                        "Ether() / IP(tos=4) / ('\\x00' * 64)",
+                        "Ether() / IP(tos=8) / ('\\x00' * 64)",
+                        "Ether() / IP(tos=16) / ('\\x00' * 64)"
+                        })),
+        'ttl':
+            ('ipv4 ttl is 64',
+             frozenset({"Ether() / IP(ttl=64) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / IP(ttl=128) / ('\\x00' * 64)",
+                        "Ether() / IP(ttl=255) / ('\\x00' * 64)",
+                        "Ether() / IP(ttl=32)  / ('\\x00' * 64)",
+                        "Ether() / IP(ttl=100) / ('\\x00' * 64)"
+                        })),
+        'proto':
+            ('ipv4 proto is 0x06',  # TCP
+             frozenset({"Ether() / IP(proto=0x06) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / IP(proto=0x01) / ('\\x00' * 64)",
+                        "Ether() / IP(proto=0x11) / ('\\x00' * 64)",
+                        "Ether() / IP(proto=0x12) / ('\\x00' * 64)",
+                        "Ether() / IP(proto=0x58) / ('\\x00' * 64)"
+                        })),
+        'src':
+            ('ipv4 src is 192.168.0.5',
+             frozenset({"Ether() / IP(src=\"192.168.0.5\") / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / IP(src=\"10.10.10.10\") / ('\\x00' * 64)",
+                        "Ether() / IP(src=\"132.177.127.6\") / ('\\x00' * 64)",
+                        "Ether() / IP(src=\"192.168.0.4\") / ('\\x00' * 64)",
+                        "Ether() / IP(src=\"192.168.0.250\") / ('\\x00' * 64)"
+                        })),
+        'dst':
+            ('ipv4 dst is 192.168.0.5',
+             frozenset({"Ether() / IP(dst=\"192.168.0.5\") / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / IP(dst=\"10.10.10.10\") / ('\\x00' * 64)",
+                        "Ether() / IP(dst=\"132.177.127.6\") / ('\\x00' * 64)",
+                        "Ether() / IP(dst=\"192.168.0.4\") / ('\\x00' * 64)",
+                        "Ether() / IP(dst=\"192.168.0.250\") / ('\\x00' * 64)"
+                        })),
+        # CHECKSUM PROPERTY NOT SUPPORTED BY TESTPMD; DO NOT UNCOMMENT UNTIL SUPPORTED
+        # 'checksum':
+        #     ('ipv4 chksum is 0x1234',
+        #     frozenset({"Ether() / ICMPv6DestUnreach(cksum=0x1234) / ('\\x00' * 64)"}),
+
+        #     frozenset({"Ether() / ICMPv6DestUnreach(cksum=0x4321) / ('\\x00' * 64)",
+        #                "Ether() / ICMPv6DestUnreach(cksum=0xffff) / ('\\x00' * 64)",
+        #                "Ether() / ICMPv6DestUnreach(cksum=0x1233) / ('\\x00' * 64)",
+        #                "Ether() / ICMPv6DestUnreach(cksum=0x1010) / ('\\x00' * 64)"
+        #                })),
+
+        ##########################################################################
+    }
+
+
+class FlowItemIpv6(PatternFlowItem):
+    type = FlowItemType.IPV6
+    valid_next_items = list(L4_FLOW_ITEMS | {FlowItemType.ICMP6} | ALWAYS_ALLOWED_ITEMS)
+    valid_parent_items: List[FlowItemType] = [FlowItemType.ETH, FlowItemType.GRE]
+    """
+    Note: IPv6 options are handled by dedicated pattern items, see `Item:
+    IPV6_EXT`_.
+    
+    - ``hdr``: IPv6 header definition (``rte_ip.h``).
+    - Default ``mask`` matches source and destination addresses only.
+    """
+
+    possible_properties = {
+        # THE FOLLOWING PROPERTIES ARE UNSUPPORTED BY TESTPMD AT THE TIME OF WRITING.
+        # They are still tested to future proof this test suite.
+        'vtc_flow':
+            ('ipv6 vtc_flow is 0x0',
+             frozenset({"Ether() / IPv6(tc=0, fl=0, version=0) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / IPv6(tc=1, fl=0, version=0) / ('\\x00' * 64)",
+                        "Ether() / IPv6(tc=0, fl=0xABCD, version=0) / ('\\x00' * 64)",
+                        "Ether() / IPv6(tc=0, fl=0, version=1) / ('\\x00' * 64)",
+                        "Ether() / IPv6(tc=6, fl=0x9999, version=1) / ('\\x00' * 64)"
+                        })),
+        'payload_len':
+            ('ipv6 payload_len is 64',
+             frozenset({"Ether() / IPv6(plen=64) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / IPv6(plen=32) / ('\\x00' * 64)",
+                        "Ether() / IPv6(plen=128) / ('\\x00' * 64)",
+                        "Ether() / IPv6(plen=5000) / ('\\x00' * 64)",
+                        "Ether() / IPv6(plen=4) / ('\\x00' * 64)"
+                        })),
+        # END UNSUPPORTED PROPERTIES
+        'tc':
+            ('ipv6 tc is 0',
+             frozenset({"Ether() / IPv6(tc=0) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / IPv6(tc=1) / ('\\x00' * 64)",
+                        "Ether() / IPv6(tc=2) / ('\\x00' * 64)",
+                        "Ether() / IPv6(tc=4) / ('\\x00' * 64)",
+                        "Ether() / IPv6(tc=6) / ('\\x00' * 64)"
+                        })),
+        'flow':
+            ('ipv6 flow is 0xABCD',
+             frozenset({"Ether() / IPv6(fl=0xABCD) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / IPv6(fl=0xABCE) / ('\\x00' * 64)",
+                        "Ether() / IPv6(fl=0x0001) / ('\\x00' * 64)",
+                        "Ether() / IPv6(fl=0xFFFF) / ('\\x00' * 64)",
+                        "Ether() / IPv6(fl=0x1234) / ('\\x00' * 64)"
+                        })),
+        'proto':  # next header (nh)
+            ('ipv6 proto is 6',  # TCP
+             frozenset({"Ether() / IPv6(nh=6) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / IPv6(nh=17) / ('\\x00' * 64)",
+                        "Ether() / IPv6(nh=41) / ('\\x00' * 64)",
+                        "Ether() / IPv6(nh=0) / ('\\x00' * 64)",
+                        "Ether() / IPv6(nh=60) / ('\\x00' * 64)"
+                        })),
+        'hop':  # hop limit
+            ('ipv6 hop is 64',
+             frozenset({"Ether() / IPv6(hlim=64) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / IPv6(hlim=128) / ('\\x00' * 64)",
+                        "Ether() / IPv6(hlim=32) / ('\\x00' * 64)",
+                        "Ether() / IPv6(hlim=255) / ('\\x00' * 64)",
+                        "Ether() / IPv6(hlim=100) / ('\\x00' * 64)"
+                        })),
+        'dst':
+            ('ipv6 dst is 2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c2',
+             frozenset({"Ether() / IPv6(dst=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c2\") / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / IPv6(dst=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c3\") / ('\\x00' * 64)",
+                        "Ether() / IPv6(dst=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c4\") / ('\\x00' * 64)",
+                        "Ether() / IPv6(dst=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c5\") / ('\\x00' * 64)",
+                        "Ether() / IPv6(dst=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c6\") / ('\\x00' * 64)"
+                        })),
+        'src':
+            ('ipv6 src is 2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c2',
+             frozenset({"Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c2\") / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c3\") / ('\\x00' * 64)",
+                        "Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c4\") / ('\\x00' * 64)",
+                        "Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c5\") / ('\\x00' * 64)",
+                        "Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c6\") / ('\\x00' * 64)"
+                        })),
+    }
+
+
+class FlowItemSctp(PatternFlowItem):
+    type = FlowItemType.SCTP
+    valid_next_items = list(ALWAYS_ALLOWED_ITEMS)
+    valid_parent_items: List[FlowItemType] = [FlowItemType.IPV4, FlowItemType.IPV6]
+    """
+    
+    **chunks?
+    
+    - ``hdr``: SCTP header definition (``rte_sctp.h``).
+    - Default ``mask`` matches source and destination ports only.
+    """
+    possible_properties = {
+
+        'src':
+            ('sctp src is 3838',
+             frozenset({"Ether() / IP() / SCTP(sport=3838) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / IP() / SCTP(sport=3939) / ('\\x00' * 64)",
+                        "Ether() / IP() / SCTP(sport=5000) / ('\\x00' * 64)",
+                        "Ether() / IP() / SCTP(sport=1998) / ('\\x00' * 64)",
+                        "Ether() / IP() / SCTP(sport=1028) / ('\\x00' * 64)"
+                        })),
+
+        'dst':
+            ('sctp dst is 3838',
+             frozenset({"Ether() / IP() / SCTP(dport=3838) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / IP() / SCTP(dport=3939) / ('\\x00' * 64)",
+                        "Ether() / IP() / SCTP(dport=5000) / ('\\x00' * 64)",
+                        "Ether() / IP() / SCTP(dport=1998) / ('\\x00' * 64)",
+                        "Ether() / IP() / SCTP(dport=1028) / ('\\x00' * 64)"
+                        })),
+        'tag':
+            ('sctp tag is 12345',
+             frozenset({"Ether() / IP() / SCTP(tag=12345) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / IP() / SCTP(tag=12346) / ('\\x00' * 64)",
+                        "Ether() / IP() / SCTP(tag=12) / ('\\x00' * 64)",
+                        "Ether() / IP() / SCTP(tag=9999) / ('\\x00' * 64)",
+                        "Ether() / IP() / SCTP(tag=42) / ('\\x00' * 64)"
+                        })),
+
+        'cksum':
+            ('sctp cksum is 0x01535b67',
+             frozenset({"Ether() / IP() / SCTP(chksum=0x01535b67) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / IP() / SCTP(chksum=0x01535b68) / ('\\x00' * 64)",
+                        "Ether() / IP() / SCTP(chksum=0xdeadbeef) / ('\\x00' * 64)",
+                        "Ether() / IP() / SCTP(chksum=0x12345678) / ('\\x00' * 64)",
+                        "Ether() / IP() / SCTP(chksum=0x385030fe) / ('\\x00' * 64)"
+                        }))
+    }
+
+
+class FlowItemTcp(PatternFlowItem):
+    type = FlowItemType.TCP
+    valid_next_items = list(ALWAYS_ALLOWED_ITEMS)
+    valid_parent_items: List[FlowItemType] = [FlowItemType.IPV4, FlowItemType.IPV6]
+    """
+    - ``hdr``: TCP header definition (``rte_tcp.h``).
+    - Default ``mask`` matches source and destination ports only.
+    
+    #define 	RTE_TCP_CWR_FLAG   0x80
+ 
+    #define 	RTE_TCP_ECE_FLAG   0x40
+ 
+    #define 	RTE_TCP_URG_FLAG   0x20
+ 
+    #define 	RTE_TCP_ACK_FLAG   0x10
+ 
+    #define 	RTE_TCP_PSH_FLAG   0x08
+ 
+    #define 	RTE_TCP_RST_FLAG   0x04
+ 
+    #define 	RTE_TCP_SYN_FLAG   0x02
+ 
+    #define 	RTE_TCP_FIN_FLAG   0x01
+    
+    Can we set multiple flags at once in testing (ex. SYN, ACK)? 
+    Probably, and we can definitely test them if necessary.
+    
+    """
+    possible_properties = {
+        # THE FOLLOWING PROPERTIES ARE UNSUPPORTED BY TESTPMD AT THE TIME OF WRITING.
+        # They are still tested to future proof this test suite.
+        'data_off':
+            ('tcp data_off is 0',
+             frozenset({"Ether() / IP() / TCP(dataofs=0) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() /  IP() / TCP(dataofs=1) / ('\\x00' * 64)",
+                        "Ether() /  IP() / TCP(dataofs=2) / ('\\x00' * 64)",
+                        "Ether() /  IP() / TCP(dataofs=3) / ('\\x00' * 64)",
+                        "Ether() /  IP() / TCP(dataofs=4) / ('\\x00' * 64)"
+                        })),
+        'rx_win':
+            ('tcp rx_win is 64',
+             frozenset({"Ether() /  IP() / TCP(window=64)/ ('\\x00' * 64)"}),
+
+             frozenset({"Ether() /  IP() / TCP(window=16)/ ('\\x00' * 64)",
+                        "Ether() /  IP() / TCP(window=128) / ('\\x00' * 64)",
+                        "Ether() /  IP() / TCP(window=32) / ('\\x00' * 64)",
+                        "Ether() /  IP() / TCP(window=255) / ('\\x00' * 64)"
+                        })),
+        'cksum':
+            ('tcp cksum is 0x1234',
+             frozenset({"Ether() /  IP() / TCP(chksum=0x1234) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / IP() / TCP(chksum=0x4321) / ('\\x00' * 64)",
+                        "Ether() /  IP() / TCP(chksum=0xffff) / ('\\x00' * 64)",
+                        "Ether() /  IP() / TCP(chksum=0x9999) / ('\\x00' * 64)",
+                        "Ether() /  IP() / TCP(chksum=0x1233)  / ('\\x00' * 64)"
+                        })),
+        # END UNSUPPORTED PROPERTIES
+        'src':
+            ('tcp src is 3838',
+             frozenset({"Ether() / IP() / TCP(sport=3838) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / IP() / TCP(sport=3939) / ('\\x00' * 64)",
+                        "Ether() / IP() / TCP(sport=5000) / ('\\x00' * 64)",
+                        "Ether() / IP() / TCP(sport=1998) / ('\\x00' * 64)",
+                        "Ether() / IP() / TCP(sport=1028) / ('\\x00' * 64)"
+                        })),
+
+        'dst':
+            ('tcp dst is 3838',
+             frozenset({"Ether() / IP() / TCP(dport=3838) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / IP() / TCP(dport=3939) / ('\\x00' * 64)",
+                        "Ether() / IP() / TCP(dport=5000) / ('\\x00' * 64)",
+                        "Ether() / IP() / TCP(dport=1998) / ('\\x00' * 64)",
+                        "Ether() / IP() / TCP(dport=1028) / ('\\x00' * 64)"
+                        })),
+        'flags':
+            ('tcp flags is 0x02',
+             frozenset({"Ether() / IP() / TCP(flags=0x02) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / IP() / TCP(flags=0x01) / ('\\x00' * 64)",
+                        "Ether() / IP() / TCP(flags=0x04) / ('\\x00' * 64)",
+                        "Ether() / IP() / TCP(flags=0x08) / ('\\x00' * 64)",
+                        "Ether() / IP() / TCP(flags=0x10) / ('\\x00' * 64)"
+                        }))
+
+    }
+
+
+class FlowItemUdp(PatternFlowItem):
+    type = FlowItemType.UDP
+    valid_next_items = list({FlowItemType.VXLAN, FlowItemType.VXLAN_GPE} | ALWAYS_ALLOWED_ITEMS)
+    valid_parent_items: List[FlowItemType] = [FlowItemType.IPV4, FlowItemType.IPV6]
+    """
+    - ``hdr``: UDP header definition (``rte_udp.h``).
+    - Default ``mask`` matches source and destination ports only.
+    """
+
+    possible_properties = {
+        # THE FOLLOWING PROPERTIES ARE UNSUPPORTED BY TESTPMD AT THE TIME OF WRITING.
+        # They are still tested to future proof this test suite.
+        'dgram_len':
+            ('udp dgram_len is 64',
+             frozenset({"Ether() / IP() / UDP(len=64) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() /  IP() / UDP(len=128) / ('\\x00' * 64)",
+                        "Ether() /  IP() / UDP(len=32) / ('\\x00' * 64)",
+                        "Ether() /  IP() / UDP(len=16) / ('\\x00' * 64)",
+                        "Ether() /  IP() / UDP(len=255) / ('\\x00' * 64)"
+                        })),
+        'dgram_cksum':
+            ('udp dgram_cksum is 0x1234',
+             frozenset({"Ether() /  IP() / UDP(chksum=0x1234) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / IP() / UDP(chksum=0x4321) / ('\\x00' * 64)",
+                        "Ether() /  IP() / UDP(chksum=0xffff) / ('\\x00' * 64)",
+                        "Ether() /  IP() / UDP(chksum=0x9999) / ('\\x00' * 64)",
+                        "Ether() /  IP() / UDP(chksum=0x1233)  / ('\\x00' * 64)"
+                        })),
+        # END UNSUPPORTED PROPERTIES
+
+        'src':
+            ('udp src is 3838',
+             frozenset({"Ether() / IP() / UDP(sport=3838) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / IP() / UDP(sport=3939) / ('\\x00' * 64)",
+                        "Ether() / IP() / UDP(sport=5000) / ('\\x00' * 64)",
+                        "Ether() / IP() / UDP(sport=1998) / ('\\x00' * 64)",
+                        "Ether() / IP() / UDP(sport=1028) / ('\\x00' * 64)"
+                        })),
+
+        'dst':
+            ('udp dst is 3838',
+             frozenset({"Ether() / IP() / UDP(dport=3838) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / IP() / UDP(dport=3939) / ('\\x00' * 64)",
+                        "Ether() / IP() / UDP(dport=5000) / ('\\x00' * 64)",
+                        "Ether() / IP() / UDP(dport=1998) / ('\\x00' * 64)",
+                        "Ether() / IP() / UDP(dport=1028) / ('\\x00' * 64)"
+                        })),
+
+    }
+
+
+class FlowItemVlan(PatternFlowItem):
+    type = FlowItemType.VLAN
+    valid_next_items = list(ALWAYS_ALLOWED_ITEMS)
+    valid_parent_items: List[FlowItemType] = [FlowItemType.ETH]
+    """
+    The corresponding standard outer EtherType (TPID) values are
+    ``RTE_ETHER_TYPE_VLAN`` or ``RTE_ETHER_TYPE_QINQ``. It can be overridden by the
+    preceding pattern item.
+    If a ``VLAN`` item is present in the pattern, then only tagged packets will
+    match the pattern.
+    
+    - ``tci``: tag control information.
+    - ``inner_type``: inner EtherType or TPID.
+    - Default ``mask`` matches the VID part of TCI only (lower 12 bits).
+    
+    tci in testpmd = pcp, dei, and vid, altogether.
+    
+    pcp in testpmd = prio in scapy
+    dei in testpmd = id in scapy? 
+    vid in testpmd = vlan in scapy
+    
+    tpid in testpmd = type in scapy
+    
+    
+    """
+    possible_properties = {
+
+        'tci':
+            ('vlan tci is 0xaaaa',
+             frozenset({"Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() /  Dot1Q(prio = 0x0, id = 0x1, vlan = 0xbbb) / ('\\x00' * 64)",
+                        "Ether() /  Dot1Q(prio = 0x5, id = 0x0, vlan = 0xccc) / ('\\x00' * 64)",
+                        "Ether() /  Dot1Q(prio = 0x5, id = 0x1, vlan = 0xaaa) / ('\\x00' * 64)",
+                        "Ether() /  Dot1Q(prio = 0x4, id = 0x0, vlan = 0xaaa) / ('\\x00' * 64)"
+                        })),
+
+        'pcp':
+            ('vlan pcp is 0x0',
+             frozenset({"Ether() / Dot1Q(prio=0x0) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() /  Dot1Q(prio=0x1) /  ('\\x00' * 64)",
+                        "Ether() /  Dot1Q(prio=0x2) /  ('\\x00' * 64)",
+                        "Ether() /  Dot1Q(prio=0x3) / ('\\x00' * 64)",
+                        "Ether() /  Dot1Q(prio=0x7) / ('\\x00' * 64)"
+                        })),
+        'dei':
+            ('vlan dei is 0',
+             frozenset({"Ether() / Dot1Q(id=0) /  ('\\x00' * 64)"}),
+
+             frozenset({"Ether() /  Dot1Q(id=1) / ('\\x00' * 64)"
+                        })),
+
+        'vid':
+            ('vlan vid is 0xabc',
+             frozenset({"Ether() / Dot1Q(vlan=0xabc) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() /  Dot1Q(vlan=0xaaa) / ('\\x00' * 64)",
+                        "Ether() /  Dot1Q(vlan=0x123) / ('\\x00' * 64)",
+                        "Ether() /  Dot1Q(vlan=0x1f5) / ('\\x00' * 64)",
+                        "Ether() /  Dot1Q(vlan=0x999) / ('\\x00' * 64)"
+                        })),
+
+        'tpid':
+            ('vlan tpid is 0x8100',  # standard value
+             frozenset({"Ether() / Dot1Q(type=0x8100) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() /  Dot1Q(type=0x0800) / ('\\x00' * 64)",
+                        "Ether() /  Dot1Q(type=0x0842) / ('\\x00' * 64)",
+                        "Ether() /  Dot1Q(type=0x809b) / ('\\x00' * 64)",
+                        "Ether() /  Dot1Q(type=0x86dd) / ('\\x00' * 64)"
+                        })),
+    }
+
+
+class FlowItemVxlan(PatternFlowItem):
+    type = FlowItemType.VXLAN
+    valid_next_items = frozenset({FlowItemType.ETH} | ALWAYS_ALLOWED_ITEMS)
+    valid_parent_items: FrozenSet[FlowItemType] = frozenset({FlowItemType.UDP})
+    """
+    - ``flags``: normally 0x08 (I flag).
+    - ``rsvd0``: reserved, normally 0x000000.
+    - ``vni``: VXLAN network identifier.
+    - ``rsvd1``: reserved, normally 0x00.
+    - Default ``mask`` matches VNI only.
+    
+    TESTPMD ONLY SUPPORTS VNI. 
+    """
+
+
+possible_properties = {
+    # THE FOLLOWING PROPERTIES ARE UNSUPPORTED BY TESTPMD AT THE TIME OF WRITING.
+    # They are still tested to future proof this test suite.
+    'rsvd0':
+        ('vxlan rsvd0 is 0x000000',
+         frozenset({"Ether() / IP() / VXLAN(reserved0=0) / ('\\x00' * 64)"}),
+
+         frozenset({"Ether() /  IP() / VXLAN(reserved0=1) /  ('\\x00' * 64)",
+                    "Ether() /  IP() /  VXLAN(reserved0=2) /  ('\\x00' * 64)",
+                    "Ether() /  IP() /  VXLAN(reserved0=3) /  ('\\x00' * 64)",
+                    "Ether() /  IP()  / VXLAN(reserved0=4) /  ('\\x00' * 64)"
+                    })),
+    'rsvd1':
+        ('vxlan rsvd1 is 0x00',
+         frozenset({"Ether() /  IP() /  VXLAN(reserved0=0) /  ('\\x00' * 64)"}),
+
+         frozenset({"Ether() / IP() /  VXLAN(reserved0=1) /  ('\\x00' * 64)",
+                    "Ether() /  IP() /  VXLAN(reserved0=2) /  ('\\x00' * 64)",
+                    "Ether() /  IP() / VXLAN(reserved0=3) /  ('\\x00' * 64)",
+                    "Ether() /  IP() /  VXLAN(reserved0=4) /  ('\\x00' * 64)"
+                    })),
+    'flags':
+        ('vxlan flags is 0x08',
+         frozenset({"Ether() /  IP() /  VXLAN(flags=0x08) /  ('\\x00' * 64)"}),
+
+         frozenset({"Ether() / IP() /  VXLAN(flags=0x80) /  ('\\x00' * 64)",
+                    "Ether() /  IP() /  VXLAN(flags=0x00) /  ('\\x00' * 64)",
+                    "Ether() /  IP() / VXLAN(flags=0x99) /  ('\\x00' * 64)",
+                    "Ether() /  IP() /  VXLAN(flags=0x01) /  ('\\x00' * 64)"
+                    })),
+    # END UNSUPPORTED PROPERTIES
+    'vni':  # a 3-byte value
+        ('vxlan vni is 0x112233',
+         frozenset({"Ether() / IP() / VXLAN(vni=0x112233) / ('\\x00' * 64)"}),
+
+         frozenset({"Ether() / IP() / VXLAN(vni=0x112234) / ('\\x00' * 64)",
+                    "Ether() / IP() / VXLAN(vni=0x123456) / ('\\x00' * 64)",
+                    "Ether() / IP() / VXLAN(vni=0xaabbcc) / ('\\x00' * 64)",
+                    "Ether() / IP() / VXLAN(vni=0x999999) / ('\\x00' * 64)"
+                    })),
+}
+
+
+class FlowItemVxlan_gpe(PatternFlowItem):
+    type = FlowItemType.VXLAN_GPE
+    valid_next_items = list({FlowItemType.ETH} | ALWAYS_ALLOWED_ITEMS)
+    valid_parent_items: List[FlowItemType] = [FlowItemType.UDP]
+    """
+    - ``flags``: normally 0x0C (I and P flags).
+    - ``rsvd0``: reserved, normally 0x0000.
+    - ``protocol``: protocol type. => NextProtocol?
+    - ``vni``: VXLAN network identifier.
+    - ``rsvd1``: reserved, normally 0x00.
+    - Default ``mask`` matches VNI only.
+    
+    NOT CURRENTLY SUPPORTED BY TESTPMD.
+    """
+
+    # THE FOLLOWING PROPERTIES ARE UNSUPPORTED BY TESTPMD AT THE TIME OF WRITING.
+    # They are still tested to future proof this test suite.
+    possible_properties = {
+
+        'rsvd0':
+            ('vxlan rsvd0 is 0x000000',
+             frozenset({"Ether() / IP() / VXLAN(reserved0=0) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() /  IP() / VXLAN(reserved0=1) /  ('\\x00' * 64)",
+                        "Ether() /  IP() /  VXLAN(reserved0=2) /  ('\\x00' * 64)",
+                        "Ether() /  IP() /  VXLAN(reserved0=3) /  ('\\x00' * 64)",
+                        "Ether() /  IP()  / VXLAN(reserved0=4) /  ('\\x00' * 64)"
+                        })),
+        'rsvd1':
+            ('vxlan rsvd1 is 0x00',
+             frozenset({"Ether() /  IP() /  VXLAN(reserved0=0) /  ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / IP() /  VXLAN(reserved0=1) /  ('\\x00' * 64)",
+                        "Ether() /  IP() /  VXLAN(reserved0=2) /  ('\\x00' * 64)",
+                        "Ether() /  IP() / VXLAN(reserved0=3) /  ('\\x00' * 64)",
+                        "Ether() /  IP() /  VXLAN(reserved0=4) /  ('\\x00' * 64)"
+                        })),
+        'flags':
+            ('vxlan flags is 0x08',
+             frozenset({"Ether() /  IP() /  VXLAN(flags=0x08) /  ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / IP() /  VXLAN(flags=0x80) /  ('\\x00' * 64)",
+                        "Ether() /  IP() /  VXLAN(flags=0x00) /  ('\\x00' * 64)",
+                        "Ether() /  IP() / VXLAN(flags=0x99) /  ('\\x00' * 64)",
+                        "Ether() /  IP() /  VXLAN(flags=0x01) /  ('\\x00' * 64)"
+                        })),
+
+        'vni':  # a 3-byte value
+            ('vxlan vni is 0x112233',
+             frozenset({"Ether() / IP() / VXLAN(vni=0x112233) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / IP() / VXLAN(vni=0x112234) / ('\\x00' * 64)",
+                        "Ether() / IP() / VXLAN(vni=0x123456) / ('\\x00' * 64)",
+                        "Ether() / IP() / VXLAN(vni=0xaabbcc) / ('\\x00' * 64)",
+                        "Ether() / IP() / VXLAN(vni=0x999999) / ('\\x00' * 64)"
+                        })),
+        'protocol':
+            ('vxlan protocol is 0x01',
+             frozenset({"Ether() / IP() / VXLAN(NextProtocol=0x01) / ('\\x00' * 64)"}),
+
+             frozenset({"Ether() / IP() / VXLAN(NextProtocol=0x01) / ('\\x00' * 64)",
+                        "Ether() / IP() / VXLAN(NextProtocol=0x11) / ('\\x00' * 64)",
+                        "Ether() / IP() / VXLAN(NextProtocol=0x22) / ('\\x00' * 64)",
+                        "Ether() / IP() / VXLAN(NextProtocol=0x33) / ('\\x00' * 64)"
+                        })),
+    }
+
+
+class FlowItemFuzzy(PatternFlowItem):
+    type = FlowItemType.FUZZY
+    layer = 1  # This field needs to go before ethernet, and we ignore layer 1 in these filters
+    valid_next_items = list({FlowItemType.ETH, FlowItemType.RAW, FlowItemType.VOID})
+    """
+   +----------+---------------+--------------------------------------------------+
+   | Field    |   Subfield    | Value                                            |
+   +==========+===============+==================================================+
+   | ``spec`` | ``threshold`` | 0 as perfect match, 0xffffffff as fuzziest match |
+   +----------+---------------+--------------------------------------------------+
+   | ``last`` | ``threshold`` | upper range value                                |
+   +----------+---------------+--------------------------------------------------+
+   | ``mask`` | ``threshold`` | bit-mask apply to "spec" and "last"              |
+   +----------+---------------+--------------------------------------------------+
+    """
+
+
+class FlowItemMark(PatternFlowItem):
+    type = FlowItemType.MARK
+    """
+    +----------+----------+---------------------------+
+    | Field    | Subfield | Value                     |
+    +==========+==========+===========================+
+    | ``spec`` | ``id``   | integer value             |
+    +----------+--------------------------------------+
+    | ``last`` | ``id``   | upper range value         |
+    +----------+----------+---------------------------+
+    | ``mask`` | ``id``   | zeroed to match any value |
+    +----------+----------+---------------------------+
+    """
+
+
+class FlowItemMeta(PatternFlowItem):
+    type = FlowItemType.META
+    """
+    Matches an application specific 32 bit metadata item.
+  
+    - Default ``mask`` matches the specified metadata value.
+    """
+
+
+class FlowItemTag(PatternFlowItem):
+    type = FlowItemType.TAG
+    """
+    Matches tag item set by other flows. Multiple tags are supported by specifying
+    ``index``.
+    
+    - Default ``mask`` matches the specified tag value and index.
+    
+   +----------+----------+----------------------------------------+
+   | Field    | Subfield  | Value                                 |
+   +==========+===========+=======================================+
+   | ``spec`` | ``data``  | 32 bit flow tag value                 |
+   |          +-----------+---------------------------------------+
+   |          | ``index`` | index of flow tag                     |
+   +----------+-----------+---------------------------------------+
+   | ``last`` | ``data``  | upper range value                     |
+   |          +-----------+---------------------------------------+
+   |          | ``index`` | field is ignored                      |
+   +----------+-----------+---------------------------------------+
+   | ``mask`` | ``data``  | bit-mask applies to "spec" and "last" |
+   |          +-----------+---------------------------------------+
+   |          | ``index`` | field is ignored                      |
+   +----------+-----------+---------------------------------------+
+    """
+
+
+PATTERN_ITEMS_TYPE_CLASS_MAPPING: Dict[FlowItemType, PatternFlowItem] = {
+    FlowItemType.UDP: FlowItemUdp,
+    FlowItemType.TCP: FlowItemTcp,
+    FlowItemType.SCTP: FlowItemSctp,
+    FlowItemType.IPV4: FlowItemIpv4,
+    FlowItemType.IPV6: FlowItemIpv6,
+    FlowItemType.ETH: FlowItemEth,
+    FlowItemType.VLAN: FlowItemVlan,
+    FlowItemType.VXLAN: FlowItemVxlan,
+    FlowItemType.GRE: FlowItemGre,
+    FlowItemType.VXLAN_GPE: FlowItemVxlan_gpe,
+    FlowItemType.ARP_ETH_IPV4: FlowItemArp_eth_ipv4,
+    FlowItemType.ICMP: FlowItemIcmp,
+    FlowItemType.ICMP6: FlowItemIcmp6,
+    FlowItemType.MARK: FlowItemMark,
+    FlowItemType.META: FlowItemMeta,
+    FlowItemType.TAG: FlowItemTag,
+    FlowItemType.FUZZY: FlowItemFuzzy,
+    FlowItemType.END: FlowItemEnd,
+    FlowItemType.VOID: FlowItemVoid,
+    FlowItemType.INVERT: FlowItemInvert,
+    FlowItemType.ANY: FlowItemAny,
+    FlowItemType.RAW: FlowItemRaw,
+}
+
+ITEM_TYPE_SCAPY_CLASS_MAPPING: Dict[FlowItemType, Packet] = {
+    FlowItemType.UDP: UDP,
+    FlowItemType.TCP: TCP,
+    FlowItemType.SCTP: SCTP,
+    FlowItemType.IPV4: IP,
+    FlowItemType.IPV6: IPv6,
+    FlowItemType.ETH: Ether,
+    FlowItemType.VLAN: Dot1Q,
+    FlowItemType.VXLAN: VXLAN,
+    FlowItemType.GRE: GRE,
+    FlowItemType.VXLAN_GPE: VXLAN,
+    FlowItemType.ARP_ETH_IPV4: ARP,  # The type rules prevent this from being under anything except Ether / IPv4
+    FlowItemType.ICMP: ICMP,
+    FlowItemType.ICMP6: ICMP,
+    FlowItemType.MARK: None,
+    FlowItemType.META: None,
+    FlowItemType.TAG: None,
+    FlowItemType.FUZZY: None,
+    FlowItemType.END: None,
+    FlowItemType.VOID: None,
+    FlowItemType.INVERT: None,
+    FlowItemType.ANY: None,
+    FlowItemType.RAW: None,
+}
+
+TUNNELING_PROTOCOLS = {
+    FlowItemVlan,
+    FlowItemVxlan,
+    FlowItemGre,
+    FlowItemVxlan_gpe
+}
+
+PATTERN_OPERATIONS = {
+    FlowItemMark,
+    FlowItemMeta,
+    FlowItemTag,
+    FlowItemFuzzy,
+    FlowItemInvert,
+}
diff --git a/framework/flow/flow_rule.py b/framework/flow/flow_rule.py
new file mode 100644
index 00000000..70b308da
--- /dev/null
+++ b/framework/flow/flow_rule.py
@@ -0,0 +1,31 @@
+from typing import Union
+
+from framework.flow.enums import *
+from framework.flow.flow import Flow
+import framework.flow.flow_action_items as flow_action_items
+
+class FlowPattern(Flow):
+    entry_points = {FlowItemType.ETH, FlowItemType.FUZZY}
+
+    def __str__(self):
+        return f"pattern {super(FlowPattern, self).__str__()} / end"
+
+
+class FlowActions(Flow):
+    entry_points = flow_action_items.ENTRY_POINTS
+
+    def __str__(self):
+        return f"action {super(FlowActions, self).__str__()} / end"
+
+
+class FlowRule(object):
+    port: int
+    group: Union[int, None]
+    priority: Union[int, None]
+
+    ingress: bool
+    egress: bool
+    transfer: bool
+
+    pattern: FlowPattern
+    actions: FlowActions
diff --git a/framework/flow/generator.py b/framework/flow/generator.py
new file mode 100644
index 00000000..8267f7af
--- /dev/null
+++ b/framework/flow/generator.py
@@ -0,0 +1,155 @@
+from __future__ import annotations
+
+import copy
+import itertools
+import os
+import sys
+import time
+from typing import List, Set, Generator, Iterable, FrozenSet, Tuple
+
+import numpy as np
+
+from framework.flow.flow import Flow
+from framework.flow.flow_action_items import ACTION_ITEMS_TYPE_CLASS_MAPPING
+from framework.flow.flow_pattern_items import PATTERN_ITEMS_TYPE_CLASS_MAPPING, PatternFlowItem, \
+    PATTERN_OPERATION_TYPES, TUNNELING_PROTOCOL_TYPES, ALWAYS_ALLOWED_ITEMS, FlowItemEnd, FlowItemVxlan, FlowItemIpv4, \
+    FlowItemEth, FlowItemGre, L3_FLOW_TYPES, FlowItemVlan, FlowItemUdp
+from framework.flow.flow_rule import FlowItemType
+
+
+def get_valid_next_protocols(current_protocol, protocol_stack, type_denylist):
+    return list(filter(
+        lambda patent_item: patent_item not in type_denylist and patent_item not in {p.type for p in protocol_stack},
+        current_protocol.valid_parent_items))
+
+
+def _generate(type_denylist=None) -> List[List[PatternFlowItem]]:
+    if type_denylist is None:
+        type_denylist = set()
+    UNUSED_PATTERN_ITEMS = {PATTERN_ITEMS_TYPE_CLASS_MAPPING[i] for i in type_denylist}
+
+    patterns: List[List[PatternFlowItem]] = []
+    for pattern_item in [clazz for clazz in PATTERN_ITEMS_TYPE_CLASS_MAPPING.values() if
+                         clazz not in UNUSED_PATTERN_ITEMS]:
+        protocol_stack = []
+        if protocol_stack.count(pattern_item) >= 2:
+            continue
+
+        current_protocol = pattern_item()
+        valid_next_protocols = get_valid_next_protocols(current_protocol, protocol_stack, type_denylist)
+        while len(valid_next_protocols) > 0:
+            protocol_stack.append(current_protocol)
+            current_protocol = PATTERN_ITEMS_TYPE_CLASS_MAPPING[list(valid_next_protocols)[0]]()
+            valid_next_protocols = get_valid_next_protocols(current_protocol, protocol_stack, type_denylist)
+
+        protocol_stack.append(current_protocol)
+
+        patterns.append(list(reversed(protocol_stack)))  # This will place the lowest level protocols first
+    return patterns
+
+
+def convert_protocol_stack_to_flow_pattern(protocol_stack):
+    return Flow(pattern_items=protocol_stack)
+
+
+def _get_patterns_with_type_denylist(type_denylist: Set):
+    return [convert_protocol_stack_to_flow_pattern(protocol_stack) for protocol_stack in (_generate(
+        type_denylist=type_denylist
+    ))]
+
+
+def _get_normal_protocol_patterns() -> List[Flow]:
+    return _get_patterns_with_type_denylist(
+        PATTERN_OPERATION_TYPES | ALWAYS_ALLOWED_ITEMS | {FlowItemType.ANY,
+                                                          FlowItemType.END})
+
+
+def _get_tunnelled_protocol_patterns(patterns: List[Flow]) -> Generator[Flow]:
+    VXLAN_FLOW = Flow(pattern_items=[FlowItemEth(), FlowItemIpv4(), FlowItemUdp(), FlowItemVxlan()])
+    for pattern in patterns:
+        yield VXLAN_FLOW / pattern
+
+    GRE_FLOW = Flow(pattern_items=[FlowItemEth(), FlowItemIpv4(), FlowItemGre()])
+    for pattern in patterns:
+        if len(pattern.pattern_items) >= 2:
+            if pattern.pattern_items[1].type in L3_FLOW_TYPES:
+                yield GRE_FLOW / pattern
+
+
+def add_vlans_to_patterns(flows: Iterable[Iterable[Flow]]) -> Generator[Flow]:
+    for flow in flows:
+        pattern_items_vlan = copy.deepcopy(flow.pattern_items)
+        pattern_items_qinq = copy.deepcopy(flow.pattern_items)
+        eth_indexes = np.where(np.array(flow.pattern_items) == FlowItemEth())[0]
+        for index in eth_indexes:
+            pattern_items_vlan.insert(index + 1, FlowItemVlan())
+            pattern_items_qinq.insert(index + 1, FlowItemVlan())
+            pattern_items_qinq.insert(index + 1, FlowItemVlan())
+        yield flow
+        yield Flow(pattern_items=pattern_items_vlan, action_items=flow.action_items)
+        yield Flow(pattern_items=pattern_items_qinq, action_items=flow.action_items)
+
+
+def get_patterns() -> Iterable[Iterable[Flow]]:
+    patterns: List[Flow] = _get_normal_protocol_patterns()
+
+    # The flow with only an ethernet header was a consequence of the
+    # generation algorithm, but isn't that useful to test since we can't
+    # create a failing case without getting each NIC to write arbitrary
+    # bytes over the link.
+    eth_only_flow = Flow(pattern_items=[FlowItemEth()])
+    patterns.remove(eth_only_flow)
+
+    # tunnelled_patterns = _get_tunnelled_protocol_patterns(patterns)
+
+    return patterns
+
+
+def add_properties_to_patterns(patterns: Iterable[Flow]) -> Iterable[Tuple[Flow, FrozenSet[str], FrozenSet[str], str]]:
+    test_property_flow_iters = map(lambda f: f.get_test_property_flows(), patterns)
+    for iterator in test_property_flow_iters:
+        yield from iterator
+
+
+def get_patterns_with_properties() -> Iterable[Tuple[Flow, FrozenSet[str], FrozenSet[str], str]]:
+    base_patterns = get_patterns()
+    return add_properties_to_patterns(base_patterns)
+
+
+def create_test_function_strings(test_configurations: Iterable[Tuple[Flow, FrozenSet[str], FrozenSet[str], str]]) -> \
+        Iterable[str]:
+    """
+    This will break if the __str__ methods of frozenset ever changes or if % formatting syntax is removed.
+
+    @param test_configurations: An iterable with test configurations to convert into test case strings.
+    @return: An iterable containing strings that are function parameters.
+    """
+    function_template = \
+        """
+def test_%s(self):
+    self.do_test_with_queue_action("%s", %s, %s)
+        """
+    return map(lambda test_configuration: function_template % (
+        test_configuration[-1], test_configuration[0], test_configuration[1], test_configuration[2],),
+               test_configurations)
+
+def get_test_configs_for_actions() -> Iterable[Tuple[str, FrozenSet[str], FrozenSet[str], str]]:
+    return itertools.chain.from_iterable(
+            [
+                [(*test_case, f"{action_type.value}_{test_case_name}",) \
+                 for test_case_name, test_case in case.test_case.items()] \
+                for action_type, case in ACTION_ITEMS_TYPE_CLASS_MAPPING.items()
+            ]
+        )
+# Used for testing pattern generation, it will output all patterns
+# and their pass/fail packets to stdout
+def main():
+    pattern_tests = list(get_patterns_with_properties())
+    pattern_functions = create_test_function_strings(pattern_tests)
+    # print("\n".join(pattern_functions))
+
+    action_functions = create_test_function_strings(get_test_configs_for_actions())
+    print("\n".join(action_functions))
+
+if __name__ == "__main__":
+    main()
\ No newline at end of file
diff --git a/test_plans/index.rst b/test_plans/index.rst
index 6a0750d1..77ac8248 100644
--- a/test_plans/index.rst
+++ b/test_plans/index.rst
@@ -136,6 +136,7 @@ The following are the test plans for the DPDK DTS automated test system.
     rss_key_update_test_plan
     rxtx_offload_test_plan
     rteflow_priority_test_plan
+    rte_flow_test_plan
     runtime_vf_queue_number_kernel_test_plan
     runtime_vf_queue_number_maxinum_test_plan
     runtime_vf_queue_number_test_plan
diff --git a/test_plans/rte_flow_test_plan.rst b/test_plans/rte_flow_test_plan.rst
new file mode 100644
index 00000000..b53f64b8
--- /dev/null
+++ b/test_plans/rte_flow_test_plan.rst
@@ -0,0 +1,4114 @@
+.. # BSD LICENSE
+    #
+    # Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+    # Copyright © 2018[, 2020] The University of New Hampshire. All rights reserved.
+    # All rights reserved.
+    #
+    # Redistribution and use in source and binary forms, with or without
+    # modification, are permitted provided that the following conditions
+    # are met:
+    #
+    #   * Redistributions of source code must retain the above copyright
+    #     notice, this list of conditions and the following disclaimer.
+    #   * Redistributions in binary form must reproduce the above copyright
+    #     notice, this list of conditions and the following disclaimer in
+    #     the documentation and/or other materials provided with the
+    #     distribution.
+    #   * Neither the name of Intel Corporation nor the names of its
+    #     contributors may be used to endorse or promote products derived
+    #     from this software without specific prior written permission.
+    #
+    # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+    # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+    # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+    # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+    # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+    # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+    # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+    # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+    # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+rte_flow Tests
+========================================
+This document contains the test plan for the rte_flow API.
+
+Prerequisites
+=============
+The DUT must have one 10G Ethernet ports connected to one port on
+Tester that are controlled by packet generator::
+
+    dut_port_0 <---> tester_port_0
+
+Assume the DUT 10G Ethernet ports' pci device id is as the following::
+
+    dut_port_0 : "0000:05:00.0"
+    mac_address: "00:00:00:00:01:00"
+
+Bind the port to dpdk igb_uio driver::
+
+    ./usertools/dpdk-devbind.py -b igb_uio 05:00.0
+
+You will also need to have Python 3.6 installed along with scapy to create test packets.
+
+Pattern Item and Property Tests
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+We create a flow rule for each property in each pattern item. We then send one packet that is expected to pass,
+and about four packets that are expected to fail.
+
+We only test one pattern item and one property at a time.
+
+Flow rules are created using **testpmd** and packets are created/sent using **scapy**.
+
+NOTE: Some pattern items and properties could not be tested
+due to the fact that testpmd does not support them. See **dpdk-dts/test_plans/unsupported.rst**
+for a listing of these items and properties.
+
+Item: ETH
+~~~~~~~~~
+
+
+Test Case: dst (destination MAC) rule
+-------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+.. 
+
+2. Set the test flow rule (If the Ethernet destination MAC is equal to 90:61:ae:fd:41:43, send the packet to queue 1):
+
+::
+
+    flow create 0 ingress pattern eth dst is 90:61:ae:fd:41:43 / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether(dst="90:61:ae:fd:41:43") / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+           Pkt1 = Ether(dst=\"90:61:ae:fd:41:44") / ('\\x00' * 64)
+           Pkt2 = Ether(dst=\"90:61:ae:fd:41:45") / ('\\x00' * 64)
+           Pkt3 = Ether(dst=\"90:61:ae:fd:41:46") / ('\\x00' * 64)
+           Pkt4 = Ether(dst=\"91:61:ae:fd:41:43") / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that only the pass packet was received by queue 1.
+
+Test Case: src (source MAC) rule
+---------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the Ethernet source MAC is equal to 90:61:ae:fd:41:43, send the packet to queue 1)
+
+::
+
+    flow create 0 ingress pattern eth src is 90:61:ae:fd:41:43 / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether(dst="90:61:ae:fd:41:43") / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+            Pkt1 = Ether(dst=\"90:61:ae:fd:41:44") / ('\\x00' * 64)
+            Pkt2 = Ether(dst=\"90:61:ae:fd:41:45") / ('\\x00' * 64)
+            Pkt3 = Ether(dst=\"90:61:ae:fd:41:46") / ('\\x00' * 64)
+            Pkt4 = Ether(dst=\"91:61:ae:fd:41:43") / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that only the pass packet was received by queue 1.
+
+
+Test Case: type (EtherType or TPID) rule
+-----------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the Ethernet type is 0x0800, send the packet to queue 1):
+
+::
+
+    flow create 0 ingress pattern eth type is 0x0800 / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule: 
+
+::
+
+            Pkt0 = Ether(type=0x0800) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+            Pkt1 = Ether(type=0x0842) / ('\\x00' * 64)
+            Pkt2 = Ether(type=0x8100) / ('\\x00' * 64)
+            Pkt3 = Ether(type=0x9100) / ('\\x00' * 64)
+            Pkt4 = Ether(type=0x8863) / ('\\x00' * 64)
+            Pkt5 = Ether(type=0x9000) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that only the pass packet was received by queue 1.
+
+Item: GRE
+~~~~~~~~~
+
+Test Case: protocol (protocol type) rule
+-----------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the GRE protocol is equal to 0x0800,send the packet to queue 1) :
+
+::
+
+
+    flow create 0 ingress pattern gre protocol is 0x0800 / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule: 
+
+::
+
+            Pkt0 = Ether() / GRE(proto=0x0800) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+            Pkt1 = Ether() / GRE(proto=0x0842) / ('\\x00' * 64)
+            Pkt2 = Ether() / GRE(proto=0x8100) / ('\\x00' * 64)
+            Pkt3 = Ether() / GRE(proto=0x0806) / ('\\x00' * 64)
+            Pkt4 = Ether() / GRE(proto=0x809B) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that only the pass packet was received by queue 1.
+
+
+Item: ICMP
+~~~~~~~~~~
+
+Test Case: icmp_type (ICMP message type) rule
+----------------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the ICMP type is 3, send the packet to queue 1) :
+
+::
+
+
+    flow create 0 ingress pattern icmp type is 3 / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule: 
+
+::
+
+            Pkt0 = Ether() / ICMP(type=3) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+            Pkt1 = Ether() / ICMP(type=3) / ('\\x00' * 64)
+            Pkt2 = Ether() / ICMP(type=13) / ('\\x00' * 64)
+            Pkt3 = Ether() / ICMP(type=11) / ('\\x00' * 64)
+            Pkt4 = Ether() / ICMP(type=12) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that only the pass packet was received by queue 1.
+
+
+Test Case: icmp_code (ICMP message code) rule
+-----------------------------------------------
+
+NOTE: ICMP code meaning is dependent on type.
+We tested type 3, code 3.
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the ICMP type is 3 and the ICMP code is 3, send the packet to queue 1) :
+
+::
+
+    flow create 0 ingress pattern icmp type is 3 code is 3 / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule: 
+
+::
+
+ Pkt0 = Ether() / ICMP(type=3, code=3) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / ICMP(type=3, code=3) / ('\\x00' * 64)
+    Pkt2 = Ether() / ICMP(type=3, code=0) / ('\\x00' * 64)
+    Pkt3 = Ether() / ICMP(type=11, code=1) / ('\\x00' * 64)
+    Pkt4 = Ether() / ICMP(type=12, code=2) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that only the pass packet was received by queue 1.
+
+
+Item: IPv4
+~~~~~~~~~~~
+
+Test Case: tos (Type of Service) rule
+----------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 type of service is 0, send the packet to queue 1) :
+
+::
+
+
+    flow create 0 ingress pattern ipv4 tos is 0 / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(tos=0) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(tos=2) / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(tos=4) / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(tos=8) / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(tos=16) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that only the pass packet was received by queue 1.
+
+
+Test Case: ttl (time to live) rule
+-------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 packet's time to live is 64, send the packet to queue 1) :
+
+::
+
+
+   flow create 0 ingress pattern ipv4 ttl is 64 / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(ttl=64) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+   Pkt1 = Ether() / IP(ttl=128) / ('\\x00' * 64)
+   Pkt2 = Ether() / IP(ttl=255) / ('\\x00' * 64)
+   Pkt3 = Ether() / IP(ttl=32) / ('\\x00' * 64)
+   Pkt4 = Ether() / IP(ttl=100) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that only the pass packet was received by queue 1.
+
+Test Case: proto (IPv4 protocol) rule
+----------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 protocol is 0x06, send the packet to queue 1) :
+
+::
+
+ flow create 0 ingress pattern ipv4 proto is 0x06 / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(proto=0x06) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+   Pkt1 = Ether() / IP(proto=0x01) / ('\\x00' * 64)
+   Pkt2 = Ether() / IP(proto=0x11) / ('\\x00' * 64)
+   Pkt3 = Ether() / IP(proto=0x12) / ('\\x00' * 64)
+   Pkt4 = Ether() / IP(proto=0x58) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that only the pass packet was received by queue 1.
+
+
+Test Case: src (IPv4 source) rule
+------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.5, send the packet to queue 1) :
+
+::
+
+   flow create 0 ingress pattern ipv4 src is 192.168.0.5 / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=192.168.0.5) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(src=10.10.10.10) / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=132.177.127.6) / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=192.168.0.4) / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=192.168.0.250) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that only the pass packet was received by queue 1.
+
+Test Case: dst (IPv4 destination) rule
+------------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 destination is 192.168.0.5, send the packet to queue 1) :
+
+::
+
+    flow create 0 ingress pattern ipv4 dst is 192.168.0.5 / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=192.168.0.5) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(dst=10.10.10.10) / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(dst=132.177.127.6) / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(dst=192.168.0.4) / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(dst=192.168.0.250) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that only the pass packet was received by queue 1.
+
+
+Item: IPv6
+~~~~~~~~~~~
+
+Test Case: tc (Traffic Class) rule
+------------------------------------
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv6 traffic class is 0, send the packet to queue 1) :
+
+::
+
+    flow create 0 ingress pattern ipv6 tc is 0 / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IPv6(tc=0) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IPv6(tc=1) / ('\\x00' * 64)
+    Pkt2 = Ether() / IPv6(tc=2) / ('\\x00' * 64)
+    Pkt3 = Ether() / IPv6(tc=4) / ('\\x00' * 64)
+    Pkt4 = Ether() / IPv6(tc=6) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that only the pass packet was received by queue 1.
+
+
+Test Case: flow (Flow Code) rule
+--------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv6 flow code is 0xABCD, send the packet to queue 1) :
+
+::
+
+    flow create 0 ingress pattern ipv6 flow is 0xABCD / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IPv6(fl=0xABCD) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+   Pkt1 = Ether() / IPv6(fl=0xABCE) / ('\\x00' * 64)
+   Pkt2 = Ether() / IPv6(fl=0x0001) / ('\\x00' * 64)
+   Pkt3 = Ether() / IPv6(fl=0xFFFF) / ('\\x00' * 64)
+   Pkt4 = Ether() / IPv6(fl=0x1234) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that only the pass packet was received by queue 1.
+
+
+Test Case: proto (IPv6 protocol/next header protocol) rule
+--------------------------------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv6 protocol is 0x06, send the packet to queue 1) :
+
+::
+
+    flow create 0 ingress pattern ipv6 proto is 0x06 / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IPv6(nh=6) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+   Pkt1 = Ether() / IPv6(nh=17) / ('\\x00' * 64)
+   Pkt2 = Ether() / IPv6(nh=41) / ('\\x00' * 64)
+   Pkt3 = Ether() / IPv6(nh=0) / ('\\x00' * 64)
+   Pkt4 = Ether() / IPv6(nh=60) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that only the pass packet was received by queue 1.
+
+
+Test Case: hop (Hop Limit) rule
+---------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv6 hop limit is 64, send the packet to queue 1) :
+::
+
+    flow create 0 ingress pattern ipv6 hop is 64 / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IPv6(hlim=64) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+   Pkt1 = Ether() / IPv6(hlim=128) / ('\\x00' * 64)
+   Pkt2 = Ether() / IPv6(hlim=32) / ('\\x00' * 64)
+   Pkt3 = Ether() / IPv6(hlim=255) / ('\\x00' * 64)
+   Pkt4 = Ether() / IPv6(hlim=100) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that only the pass packet was received by queue 1.
+
+Test Case: dst (IPv6 destination) rule
+---------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv6 destination is 2001:...:b1c2, send the packet to queue 1) :
+
+::
+
+   flow create 0 ingress pattern ipv6 dst is 2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c2 / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IPv6(dst=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c2\") / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IPv6(dst=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c3\") / ('\\x00' * 64)
+    Pkt2 = Ether() / IPv6(dst=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c4\") / ('\\x00' * 64)
+    Pkt3 = Ether() / IPv6(dst=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c5\") / ('\\x00' * 64)
+    Pkt4 = Ether() / IPv6(dst=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c6\") / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that only the pass packet was received by queue 1.
+
+Test Case: src (IPv6 source) rule
+-----------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv6 destination is 2001:...b1c2, send the packet to queue 1) :
+
+::
+
+    flow create 0 ingress pattern ipv6 src is 2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c2 / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c2\") / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c3\") / ('\\x00' * 64)
+    Pkt2 = Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c4\") / ('\\x00' * 64)
+    Pkt3 = Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c5\") / ('\\x00' * 64)
+    Pkt4 = Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c6\") / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that only the pass packet was received by queue 1.
+
+
+Item: SCTP
+~~~~~~~~~~~
+
+Test Case: src (source port) rule
+-------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the SCTP source port is 3838, send the packet to queue 1) :
+
+::
+
+    flow create 0 ingress pattern sctp src is 3838 / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP() / SCTP(sport=3838) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP() / SCTP(sport=3939) / ('\\x00' * 64)
+    Pkt2 = Ether() / IP() / SCTP(sport=5000) / ('\\x00' * 64)
+    Pkt3 = Ether() / IP() / SCTP(sport=1998) / ('\\x00' * 64)
+    Pkt4 = Ether() / IP() / SCTP(sport=1028) / ('\\x00' * 64)
+
+..
+
+Test Case: dst (destination port) rule
+-----------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the SCTP destination port is 3838, send the packet to queue 1) :
+
+::
+
+    flow create 0 ingress pattern sctp dst is 3838 / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP() / SCTP(dport=3838) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP() / SCTP(dport=3939) / ('\\x00' * 64)
+    Pkt2 = Ether() / IP() / SCTP(dport=5000) / ('\\x00' * 64)
+    Pkt3 = Ether() / IP() / SCTP(dport=1998) / ('\\x00' * 64)
+    Pkt4 = Ether() / IP() / SCTP(dport=1028) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that only the pass packet was received by queue 1.
+
+Test Case: tag (SCTP header tag) rule
+--------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the SCTP tag is equal to 12345, send the packet to queue 1) :
+
+::
+
+    flow create 0 ingress pattern sctp tag is 12345 / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP() / SCTP(tag=12345) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+   Pkt1 = Ether() / IP() / SCTP(tag=12346) / ('\\x00' * 64)
+   Pkt2 = Ether() / IP() / SCTP(tag=12) / ('\\x00' * 64)
+   Pkt3 = Ether() / IP() / SCTP(tag=9999) / ('\\x00' * 64)
+   Pkt4 = Ether() / IP() / SCTP(tag=42) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that only the pass packet was received by queue 1.
+
+
+Test Case: cksum (SCTP header checksum) rule
+-----------------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the SCTP checksum is equal to 0x1535b67, send the packet to queue 1) :
+
+::
+
+    flow create 0 ingress pattern sctp cksum is 0x01535b67 / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP() / SCTP(chksum=0x01535b67)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+   Pkt1 = Ether() / IP() / SCTP(chksum=0x01535b68)
+   Pkt2 = Ether() / IP() / SCTP(chksum=0xdeadbeef)
+   Pkt3 = Ether() / IP() / SCTP(chksum=0x12345678)
+   Pkt4 = Ether() / IP() / SCTP(chksum=0x385030fe)
+
+..
+
+5. Check to make sure that only the pass packet was received by queue 1.
+
+Item: TCP
+~~~~~~~~~~~
+
+Test Case: src (source port) rule
+--------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the TCP source port is equal to 3838, send the packet to queue 1) :
+
+::
+
+    flow create 0 ingress pattern tcp src is 3838 / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP() / TCP(sport=3838) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP() / TCP(sport=3939) / ('\\x00' * 64)
+    Pkt2 = Ether() / IP() / TCP(sport=5000) / ('\\x00' * 64)
+    Pkt3 = Ether() / IP() / TCP(sport=1998) / ('\\x00' * 64)
+    Pkt4 = Ether() / IP() / TCP(sport=1028) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that only the pass packet was received by queue 1.
+
+
+Test Case: dst (destination port) rule
+-----------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the TCP destination port is equal to 3838, send the packet to queue 1) :
+
+::
+
+    flow create 0 ingress pattern tcp dst is 3838 / end actions queue index 1 / end
+
+..
+
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP() / TCP(dport=3838) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP() / TCP(dport=3939) / ('\\x00' * 64)
+    Pkt2 = Ether() / IP() / TCP(dport=5000) / ('\\x00' * 64)
+    Pkt3 = Ether() / IP() / TCP(dport=1998) / ('\\x00' * 64)
+    Pkt4 = Ether() / IP() / TCP(dport=1028) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that only the pass packet was received by queue 1.
+
+Test Case: flags (TCP flags) rule
+-----------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the TCP flags are equal to 0x02, send the packet to queue 1) :
+
+::
+
+    flow create 0 ingress pattern tcp flags is 0x02 / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP() / TCP(flags=0x02) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP() / TCP(flags=0x01) / ('\\x00' * 64)
+    Pkt2 = Ether() / IP() / TCP(flags=0x04) / ('\\x00' * 64)
+    Pkt3 = Ether() / IP() / TCP(flags=0x08) / ('\\x00' * 64)
+    Pkt4 = Ether() / IP() / TCP(flags=0x10) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that only the pass packet was received by queue 1.
+
+Item: UDP
+~~~~~~~~~~~
+
+Test Case: src (source port) rule
+-------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the UDP source port is equal to 3838, send the packet to queue 1) :
+
+::
+
+    flow create 0 ingress pattern udp src is 3838 / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+    Pkt0 = Ether() / IP() / UDP(sport=3838) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP() / UDP(sport=3939) / ('\\x00' * 64)
+    Pkt2 = Ether() / IP() / UDP(sport=5000) / ('\\x00' * 64)
+    Pkt3 = Ether() / IP() / UDP(sport=1998) / ('\\x00' * 64)
+    Pkt4 = Ether() / IP() / UDP(sport=1028) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that only the pass packet was received by queue 1.
+
+
+Test Case: dst (destination port) rule
+-----------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the UDP destination port is equal to 3838, send the packet to queue 1) :
+
+::
+
+    flow create 0 ingress pattern udp dst is 3838 / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP() / UDP(dport=3838) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP() / UDP(dport=3939) / ('\\x00' * 64)
+    Pkt2 = Ether() / IP() / UDP(dport=5000) / ('\\x00' * 64)
+    Pkt3 = Ether() / IP() / UDP(dport=1998) / ('\\x00' * 64)
+    Pkt4 = Ether() / IP() / UDP(dport=1028) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that only the pass packet was received by queue 1.
+
+Item: VLAN
+~~~~~~~~~~~
+
+Test Case: tci (Tag Control Information) rule
+-----------------------------------------------
+
+NOTE: The VLAN tci is the combination of the fields pcp, dei, and vid.
+We test them altogether as the tci and we test each field individually.
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the vlan tag control information value is 0xaaaa, send the packet to queue 1) :
+
+::
+
+
+    flow create 0 ingress pattern vlan tci is 0xaaaa / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / Dot1Q(prio = 0x0, id = 0x0, vlan = 0xbbb) / ('\\x00' * 64)
+    Pkt2 = Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xccc) / ('\\x00' * 64)
+    Pkt3 = Ether() / Dot1Q(prio = 0x5, id = 0x1, vlan = 0xaaa) / ('\\x00' * 64)
+    Pkt4 = Ether() / Dot1Q(prio = 0x4, id = 0x0, vlan = 0xaaa) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that only the pass packet was received by queue 1.
+
+
+Test Case: pcp (Priority Code Point) rule
+--------------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the VLAN priority code point is equal to 0x0, send the packet to queue 1) :
+
+::
+
+    flow create 0 ingress pattern vlan pcp is 0x0 / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / Dot1Q(prio=0x0) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / Dot1Q(prio=0x1) / ('\\x00' * 64)
+    Pkt2 = Ether() / Dot1Q(prio=0x2) / ('\\x00' * 64)
+    Pkt3 = Ether() / Dot1Q(prio=0x3) / ('\\x00' * 64)
+    Pkt4 = Ether() / Dot1Q(prio=0x7) / ('\\x00' * 64)
+
+..
+
+Test Case: dei (Drop Eligible Indicator) rule
+-----------------------------------------------
+NOTE: The only two possible values for dei are 0 and 1.
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the VLAN drop eligible indicator is equal to 0, send the packet to queue 1) :
+
+::
+
+    flow create 0 ingress pattern vlan dei is 0 / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / Dot1Q(id=0)) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / Dot1Q(id=1) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that only the pass packet was received by queue 1.
+
+Test Case: vid (VLAN identifier) rule
+-----------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the VLAN identifier is equal to 0xabc, send the packet to queue 1) :
+
+::
+
+    flow create 0 ingress pattern vlan vid is 0xabc / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / Dot1Q(vlan=0xabc) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / Dot1Q(vlan=0xaaa) / ('\\x00' * 64)
+    Pkt2 = Ether() / Dot1Q(vlan=0x123) / ('\\x00' * 64)
+    Pkt3 = Ether() / Dot1Q(vlan=0x1f5) / ('\\x00' * 64)
+    Pkt4 = Ether() / Dot1Q(vlan=0x999) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that only the pass packet was received by queue 1.
+
+
+Test Case: tpid (Tag Protocol Identifier) rule
+--------------------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the VLAN tag protocol identifier is equal to 0x8100, send the packet to queue 1) :
+
+::
+
+    flow create 0 ingress pattern vlan tpid is 0x8100 / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / Dot1Q(type=0x8100) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / Dot1Q(type=0x0800) / ('\\x00' * 64)
+    Pkt2 = Ether() / Dot1Q(type=0x0842) / ('\\x00' * 64)
+    Pkt3 = Ether() / Dot1Q(type=0x809b) / ('\\x00' * 64)
+    Pkt4 = Ether() / Dot1Q(type=0x86dd) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that only the pass packet was received by queue 1.
+
+
+Item: VXLAN
+~~~~~~~~~~~
+
+Test Case: vni (VXLAN network identifier) rule
+------------------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the VXLAN network identifier is equal to 0x112233, send the packet to queue 1) :
+
+::
+
+    flow create 0 ingress pattern vxlan vni is 0x112233 / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP() / VXLAN(vni=0x112233) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP() / VXLAN(vni=0x112234) / ('\\x00' * 64)
+    Pkt2 = Ether() / IP() / VXLAN(vni=0x123456) / ('\\x00' * 64)
+    Pkt3 = Ether() / IP() / VXLAN(vni=0xaabbcc) / ('\\x00' * 64)
+    Pkt4 = Ether() / IP() / VXLAN(vni=0x999999) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that only the pass packet was received by queue 1.
+
+Action Item Tests
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+We create a simple flow rule that filters packets by matching IPv4 address (more rules are sometimes applied
+depending on the action being tested). We then send one packet that is expected to pass,
+and about four packets that are expected to fail. We check if the packet that is expected to pass
+has the action we are testing applied to it.
+
+We only test one action and one of the action's properties at a time, unless one property requires the context
+of another.
+
+Flow rules are created using **testpmd** and packets are created/sent using **scapy**.
+
+NOTE: NVGRE_ENCAP and NVGRE_DECAP could not be tested at this time because Scapy does not support NVGRE.
+
+We did not create an RSS test suite because one has already been created.
+
+
+Action: PASSTHRU
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: passthru test
+------------------------------------------------
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, let the packet pass through) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions passthru / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet was allowed to pass through.
+
+Action: FLAG
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: flag test
+------------------------------------------------
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, flag the packet) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions flag / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet was flagged.
+
+Action: DROP
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: drop test
+------------------------------------------------
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, drop the packet) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions drop / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet was dropped.
+
+Action: COUNT
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: test_shared
+------------------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, add unshared counter action with id of 1) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions count shared 0 id 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has a counter action added to it.
+
+Test Case: test_id
+------------------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, add counter action with id of 1) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions count id 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has a counter action added to it.
+
+Action: MAC_SWAP
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: mac_swap test
+------------------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, swap dst and src MAC addresses) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions mac_swap / end
+
+..
+
+3. Send a packet that matches the rule, with defined src and dst MAC addresses:
+
+::
+
+    Pkt0 = Ether(src=\"90:61:ae:fd:41:43\", dst = \"ab:cd:ef:12:34:56\") / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule, with defined src and dst MAC addresses:
+
+::
+
+    Pkt1 = Ether(src=\"90:61:ae:fd:41:43\", dst = \"ab:cd:ef:12:34:56\") / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether(src=\"90:61:ae:fd:41:43\", dst = \"ab:cd:ef:12:34:56\") / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether(src=\"90:61:ae:fd:41:43\", dst = \"ab:cd:ef:12:34:56\") / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether(src=\"90:61:ae:fd:41:43\", dst = \"ab:cd:ef:12:34:56\") / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its source and destination MAC addresses swapped.
+
+Action: DEC_TTL
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: dec_ttl test
+------------------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, decrease its TTL) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions dec_ttl / end
+
+..
+
+3. Send a packet that matches the rule, with a defined ttl:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\", ttl = 128) / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule, with a defined ttl:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\", ttl = 128) / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\", ttl = 128) / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\", ttl = 128) / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\", ttl = 128) / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its ttl reduced.
+
+Action: JUMP
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: jump test
+------------------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, redirect the packet to group 1) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions jump group 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has been added to group 1 on the destination device.
+
+
+Action: MARK
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: mark test
+------------------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, mark the packet with an id of 0xABCDEF) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions mark id 0xABCDEF / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has been marked with the correct id.
+
+Action: QUEUE
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: queue test
+------------------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, send the packet to queue 1) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions queue index 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has been added to queue 1.
+
+Action: PF
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: pf test
+------------------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, direct the packet to the physical function of the device) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions pf / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has been directed to the physical function of the device.
+
+Action: VF
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: test_original
+------------------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, direct the packet to the original virtual function of the device) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions vf original / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has been directed to the original virtual function of the device.
+
+Test Case: test_id
+------------------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, direct the packet to the virtual function of id 1) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions vf id 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has been directed to the virtual function with the id of 1.
+
+
+Action: PHY_PORT
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: test_original
+------------------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, direct the packet to the original physical port of the device) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions phy_port original / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has been directed to the original physical port of the device.
+
+Test Case: test_index
+------------------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, direct the packet to the physical port of index 1) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions phy_port index 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has been directed to the physical port of index 1.
+
+
+Action: PORT_ID
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: test_original
+------------------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, direct the packet to the original DPDK port ID) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions port_id original / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has been directed to the original DPDK port ID of the device.
+
+Test Case: test_id
+------------------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, direct the packet to the DPDK port of id 1) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions port_id id 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has been directed to the DPDK port of id 1.
+
+
+Action: METER
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: meter test
+------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, apply a MTR object with id 1) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions meter mtr_id 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had MTR object with id 1 applied to it.
+
+Action: SECURITY
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: security test
+----------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, apply security session of id 1) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions security security_session 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had security session 1 applied to it.
+
+
+Action: OF_SET_MPLS_TTL
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: of_set_mpls_ttl test
+---------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, implement MPLS TTL with a value of 64) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions of_set_mpls_ttl mpls_ttl 64 / end
+
+..
+
+3. Send a packet that matches the rule, with an MPLS layer with assigned ttl:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / MPLS(label = 0xab, ttl=128) / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule, with MPLS layers with assigned ttl:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / MPLS(label = 0xab, ttl=128) / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / MPLS(label = 0xab, ttl=128) / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / MPLS(label = 0xab, ttl=128) / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / MPLS(label = 0xab, ttl=128) / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its MPLS ttl defined as 64.
+
+Action: OF_DEC_MPLS_TTL
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: of_dec_mpls_ttl test
+---------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, decrement the MPLS ttl value) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions of_dec_mpls_ttl / end
+
+..
+
+3. Send a packet that matches the rule, with an MPLS layer with assigned ttl:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / MPLS(label = 0xab, ttl=128) / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule, with MPLS layers with assigned ttl:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / MPLS(label = 0xab, ttl=128) / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / MPLS(label = 0xab, ttl=128) / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / MPLS(label = 0xab, ttl=128) / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / MPLS(label = 0xab, ttl=128) / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its MPLS ttl decremented.
+
+
+Action: OF_SET_NW_TTL
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: of_set_nw_ttl test
+---------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, implement IP TTL with a value of 64) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions of_set_nw_ttl nw_ttl 64 / end
+
+..
+
+3. Send a packet that matches the rule, with a defined TTL in the IP layer:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\", ttl=128)  / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule, with a defined TTL in the IP layer:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\", ttl = 128) / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\", ttl = 128) / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\", ttl = 128) /  UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\", ttl = 128) / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its IP TTL defined as 64.
+
+
+Action: OF_DEC_NW_TTL
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: of_dec_nw_ttl test
+---------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, decrease the IP TTL) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions of_dec_nw_ttl / end
+
+..
+
+3. Send a packet that matches the rule, with a defined TTL in the IP layer:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\", ttl=128 )  / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule, with a defined TTL in the IP layer:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\", ttl = 128) / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\", ttl = 128) / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\", ttl = 128) /  UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\", ttl = 128) / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its IP TTL decremented.
+
+Action: OF_COPY_TTL_OUT
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: of_copy_ttl_out test
+---------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, copy the TTL outwards) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions of_copy_ttl_out / end
+
+..
+
+3. Send a packet that matches the rule, with a defined TTL in the IP layer:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\")  / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule, with a defined TTL in the IP layer:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\", ttl = 128) / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\", ttl = 128) / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\", ttl = 128) /  UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\", ttl = 128) / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its TTL copied outwards.
+
+Action: OF_COPY_TTL_IN
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: of_copy_ttl_in test
+---------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, copy the TTL inwards) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions of_copy_ttl_in / end
+
+..
+
+3. Send a packet that matches the rule, with a defined TTL in the IP layer:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\")  / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule, with a defined TTL in the IP layer:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\", ttl = 128) / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\", ttl = 128) / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\", ttl = 128) /  UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\", ttl = 128) / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its TTL copied inwards.
+
+Action: OF_POP_VLAN
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: of_pop_vlan test
+---------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, pop the outer VLAN tag) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions of_pop_vlan / end
+
+..
+
+3. Send a packet that matches the rule, with a defined VLAN layer/tag:
+
+::
+
+    Pkt0 = Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"192.168.0.1\")  / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule, with defined VLAN layers/tags:
+
+::
+
+    Pkt1 = Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"8.8.8.8\") /  UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its outer (only) VLAN tag popped.
+
+Action: OF_PUSH_VLAN
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: of_push_vlan test
+---------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, push a new VLAN tag with EtherType 0x8100) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions of_push_vlan ethertype 0x8100 / end
+
+..
+
+3. Send a packet that matches the rule, with a defined VLAN layer/tag:
+
+::
+
+    Pkt0 = Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"192.168.0.1\")  / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule, with defined VLAN layers/tags:
+
+::
+
+    Pkt1 = Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"8.8.8.8\") /  UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had a VLAN tag with EtherType 0x8100 pushed onto it.
+
+Action: OF_SET_VLAN_VID
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+Test Case: of_set_vlan_vid test
+---------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, set the VLAN vid to 0xbbb):
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions of_set_vlan_vid 0xbbb / end
+
+..
+
+3. Send a packet that matches the rule, with a defined VLAN layer/tag:
+
+::
+
+    Pkt0 = Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"192.168.0.1\")  / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule, with defined VLAN layers/tags:
+
+::
+
+    Pkt1 = Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"8.8.8.8\") /  UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its VLAN vid set to 0xbbb.
+
+Action: OF_SET_VLAN_PCP
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: of_set_vlan_pcp test
+---------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, set the VLAN pcp to 0x7):
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions of_set_vlan_pcp 0x7 / end
+
+..
+
+3. Send a packet that matches the rule, with a defined VLAN layer/tag:
+
+::
+
+    Pkt0 = Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"192.168.0.1\")  / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule, with defined VLAN layers/tags:
+
+::
+
+    Pkt1 = Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"8.8.8.8\") /  UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its VLAN pcp set to 0x7.
+
+Action: OF_POP_MPLS
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: of_pop_mpls test
+---------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, pop the outer MPLS tag) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions of_pop_mpls / end
+
+..
+
+3. Send a packet that matches the rule, with a defined MPLS layer/tag:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\")  / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule, with defined MPLS layers/tags:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") /  MPLS(label = 0xab, ttl=128) / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") /  MPLS(label = 0xab, ttl=128) / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / MPLS(label = 0xab, ttl=128) / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") /  MPLS(label = 0xab, ttl=128) /  UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its outer (only) MPLS tag popped.
+
+Action: OF_PUSH_MPLS
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: of_push_mpls test
+---------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, push a new MPLS tag with EtherType 0x0806) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions of_push_mpls ethertype 0x0806 / end
+
+..
+
+3. Send a packet that matches the rule, with a defined MPLS layer/tag:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\")  / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule, with defined MPLS layers/tags:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") /  MPLS(label = 0xab, ttl=128) / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") /  MPLS(label = 0xab, ttl=128) / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / MPLS(label = 0xab, ttl=128) / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") /  MPLS(label = 0xab, ttl=128) /  UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had an MPLS tag with EtherType 0x0806 pushed onto it.
+
+
+Action: VXLAN_ENCAP
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Test Case: vxlan_encap
+---------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, encapsulate with a VXLAN tag with overlay definition (vni) 0x112233) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions vxlan_encap definition 0x112233 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\")  / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") /  UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") /  UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") /  UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has been encapsulated with a VXLAN tag with vni 0x112233.
+
+Action: VXLAN_DECAP
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: vxlan_decap
+---------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, strip all VXLAN headers :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions vxlan_decap / end
+
+..
+
+3. Send a packet that matches the rule, with a VXLAN header:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\")  / UDP() / VXLAN() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule, with VXLAN headers:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") /  UDP() / VXLAN() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") /  UDP() / VXLAN() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / VXLAN() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP() / VXLAN()/  ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its VXLAN header stripped.
+
+Action: RAW_ENCAP
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: test_data
+---------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, encapsulate with a VLAN tag with the header value 0x8100aaaa:
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions raw_encap data 0x8100aaaa / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\")  / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has been encapsulated with a VLAN tag with the header value 0x8100aaaa.
+
+Test Case: test_preserve
+---------------------------------
+
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1,
+encapsulate with a VLAN tag with the header value of 0x8100aaaa and a preserve bitmask of 0xffffffff:
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions raw_encap data 0x8100aaaa preserve 0xffffffff / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\")  / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") /  UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has been encapsulated with a VLAN tag with the header value 0x8100aaaa
+and has a preserve bitmask of 0xffffffff.
+
+Test Case: test_size
+---------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1,
+encapsulate with a VLAN tag with the header value of 0x8100aaaa and a data (header) size of 32.
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions raw_encap data 0x8100aaaa size 32/ end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\")  / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") /  UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has been encapsulated with a VLAN tag with the header value 0x8100aaaa
+and has a size of 32.
+
+Action: RAW_DECAP
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: test_data
+---------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, decapsulate a VLAN tag with the header value 0x8100aaaa:
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions raw_decap data 0x8100aaaa / end
+
+..
+
+3. Send a packet that matches the rule, with a matching VLAN header:
+
+::
+
+    Pkt0 = Ether() /  Dot1Q(prio = 0x5, id = 0x0, vlan = 0xbbb) / IP(src=\"192.168.0.1\")  / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule, with matching VLAN headers:
+
+::
+
+    Pkt1 = Ether() /  Dot1Q(prio = 0x5, id = 0x0, vlan = 0xbbb) / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() /  Dot1Q(prio = 0x5, id = 0x0, vlan = 0xbbb) / IP(src=\"10.0.30.99\") /  UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() /  Dot1Q(prio = 0x5, id = 0x0, vlan = 0xbbb) / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() /  Dot1Q(prio = 0x5, id = 0x0, vlan = 0xbbb) / IP(src=\"132.177.0.99\") /  UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its VLAN tag decapsulated.
+
+
+Test Case: test_size
+---------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, decapsulate a VLAN tag with the header value 0x8100aaaa
+and header size of 32:
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions raw_decap data 0x8100aaaa size 32 / end
+
+..
+
+3. Send a packet that matches the rule, with a matching VLAN header:
+
+::
+
+    Pkt0 = Ether() /  Dot1Q(prio = 0x5, id = 0x0, vlan = 0xbbb) / IP(src=\"192.168.0.1\")  / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule, with matching VLAN headers:
+
+::
+
+    Pkt1 = Ether() /  Dot1Q(prio = 0x5, id = 0x0, vlan = 0xbbb) / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() /  Dot1Q(prio = 0x5, id = 0x0, vlan = 0xbbb) / IP(src=\"10.0.30.99\") /  UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() /  Dot1Q(prio = 0x5, id = 0x0, vlan = 0xbbb) / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() /  Dot1Q(prio = 0x5, id = 0x0, vlan = 0xbbb) / IP(src=\"132.177.0.99\") /  UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its VLAN tag of size 32 decapsulated.
+
+Action: SET_IPV4_SRC
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: set_ipv4_src test
+----------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, set the ipv4 src to 172.16.0.10) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions set_ipv4_src ipv4_addr 172.16.0.10 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its IPv4 source address set to 172.16.0.10.
+
+
+Action: SET_IPV4_DST
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: set_ipv4_dst test
+----------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 destination is 192.168.0.1, set the ipv4 dst to 172.16.0.10) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 dst is 192.168.0.1 / udp / end actions set_ipv4_dst ipv4_addr 172.16.0.10 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(dst=\"192.168.0.1\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(dst=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(dst=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(dst=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(dst=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its IPv4 destination address set to 172.16.0.10.
+
+Action: SET_IPV6_SRC
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: set_ipv6_src test
+----------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv6 source is 2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c2,
+set the ipv6 source to 2001:0000:9d38:6ab8:1c48:9999:aaaa:bbbb) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv6 src is 2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c2 / udp /
+    end actions set_ipv6_src ipv6_addr 2001:0000:9d38:6ab8:1c48:9999:aaaa:bbbb / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c2\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c3\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c4\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c5\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c6\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its IPv6 source address set to 2001:0000:9d38:6ab8:1c48:9999:aaaa:bbbb.
+
+Action: SET_IPV6_DST
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: set_ipv6_dst test
+----------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv6 destination is 2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c2,
+set the ipv6 dst to 2001:0000:9d38:6ab8:1c48:9999:aaaa:bbbb) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv6 src is 2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c2 / udp /
+    end actions set_ipv6_dst ipv6_addr 2001:0000:9d38:6ab8:1c48:9999:aaaa:bbbb / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IPv6(dst=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c2\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IPv6(dst=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c3\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IPv6(dst=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c4\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IPv6(dst=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c5\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IPv6(dst=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c6\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its IPv6 destination address set to  2001:0000:9d38:6ab8:1c48:9999:aaaa:bbbb.
+
+Action: SET_TP_SRC
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: test_udp
+----------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, set the tcp/udp source port to 1998:
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions set_tp_src 1998/ end
+
+..
+
+3. Send a packet that matches the rule with a defined UDP source port:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / UDP(sport=3838) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule with defined UDP source ports:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP(sport=3838) / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / UDP(sport=3838) / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP(sport=3838) / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP(sport=3838) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its UDP source port set to 1998.
+
+Test Case: test_tcp
+----------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, set the tcp/udp source port to 1998:
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / tcp / end actions set_tp_src 1998 / end
+
+..
+
+3. Send a packet that matches the rule with a defined TCP source port:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / TCP(sport=3838) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule with defined TCP source ports:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / TCP(sport=3838) / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / TCP(sport=3838) / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / TCP(sport=3838) / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / TCP(sport=3838) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its TCP source port set to 1998.
+
+Action: SET_TP_DST
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+Test Case: test_udp
+----------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 destination is 192.168.0.1, set the tcp/udp destination port to 1998:
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions set_tp_dst 1998/ end
+
+..
+
+3. Send a packet that matches the rule with a defined UDP destination port:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / UDP(dport=3838) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule with defined UDP destination ports:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP(dport=3838) / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / UDP(dport=3838) / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP(dport=3838) / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP(dport=3838) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its UDP destination port set to 1998.
+
+Test Case: test_tcp
+----------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, set the tcp/udp destination port to 1998:
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / tcp / end actions set_tp_dst 1998 / end
+
+..
+
+3. Send a packet that matches the rule with a defined TCP destination port:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / TCP(dport=3838) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule with defined TCP destination ports:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / TCP(dport=3838) / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / TCP(dport=3838) / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / TCP(dport=3838) / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / TCP(dport=3838) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its TCP destination port set to 1998.
+Action: SET_TTL
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: set_ttl test
+---------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, set TTL to a value of 64) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions set_ttl ttl_value 64 / end
+
+..
+
+3. Send a packet that matches the rule, with a defined TTL in the IP layer:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\", ttl=128)  / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule, with a defined TTL in the IP layer:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\", ttl = 128) / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\", ttl = 128) / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\", ttl = 128) /  UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\", ttl = 128) / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its TTL defined as 64.
+
+Action: SET_MAC_SRC
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: set_mac_src test
+------------------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, set MAC src to 10:20:30:40:50:60) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions set_mac_src mac_addr 10:20:30:40:50:60 / end
+
+..
+
+3. Send a packet that matches the rule, with a defined src MAC address:
+
+::
+
+    Pkt0 = Ether(src=\"90:61:ae:fd:41:43\") / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule, with defined src MAC addresses:
+
+::
+
+    Pkt1 = Ether(src=\"90:61:ae:fd:41:43\") / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether(src=\"90:61:ae:fd:41:43\" ) / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether(src=\"90:61:ae:fd:41:43\" ) / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether(src=\"90:61:ae:fd:41:43\" ) / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its source MAC address set to 10:20:30:40:50:60.
+
+Action: SET_MAC_DST
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: set_mac_dst test
+------------------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, set MAC dst to 10:20:30:40:50:60) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions set_mac_dst mac_addr 10:20:30:40:50:60 / end
+
+..
+
+3. Send a packet that matches the rule, with a defined dst MAC address:
+
+::
+
+    Pkt0 = Ether(src=\"90:61:ae:fd:41:43\") / IP(dst =\"192.168.0.1\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule, with defined dst MAC addresses:
+
+::
+
+    Pkt1 = Ether(dst=\"90:61:ae:fd:41:43\") / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether(dst=\"90:61:ae:fd:41:43\" ) / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether(dst=\"90:61:ae:fd:41:43\" ) / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether(dst=\"90:61:ae:fd:41:43\" ) / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its destination MAC address set to 10:20:30:40:50:60.
+
+Action: INC_TCP_SEQ
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: inc_tcp_seq test
+----------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, increase the TCP seq value:
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / tcp / end actions inc_tcp_seq / end
+
+..
+
+3. Send a packet that matches the rule with a defined TCP seq value:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / TCP(seq=2) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule with defined TCP seq values:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / TCP(seq=2) / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / TCP(seq=2) / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / TCP(seq=2) / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / TCP(seq=2) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its TCP seq value increased.
+
+Action: DEC_TCP_SEQ
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: dec_tcp_seq test
+----------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, decrease the TCP seq value:
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / tcp / end actions dec_tcp_seq / end
+
+..
+
+3. Send a packet that matches the rule with a defined TCP seq value:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / TCP(seq=2) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule with defined TCP seq values:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / TCP(seq=2) / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / TCP(seq=2) / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / TCP(seq=2) / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / TCP(seq=2) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its TCP seq value decreased.
+
+Action: INC_TCP_ACK
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: inc_tcp_ack test
+----------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, increase the TCP ack value:
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / tcp / end actions inc_tcp_ack / end
+
+..
+
+3. Send a packet that matches the rule with a defined TCP ack value:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / TCP(ack=2) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule with defined TCP ack values:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / TCP(ack=2) / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / TCP(ack=2) / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / TCP(ack=2) / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / TCP(ack=2) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its TCP ack value increased.
+
+Action: DEC_TCP_ACK
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: dec_tcp_ack test
+----------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, decrease the TCP ack value:
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / tcp / end actions dec_tcp_ack / end
+
+..
+
+3. Send a packet that matches the rule with a defined TCP ack value:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / TCP(ack=2) / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule with defined TCP ack values:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / TCP(ack=2) / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / TCP(ack=2) / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / TCP(ack=2) / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / TCP(ack=2) / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its TCP ack value decreased.
+
+Action: SET_TAG
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: test_data
+------------------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, tag the packet with the value 0xabc) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions set_tag data 0xabc / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has been tagged with the correct data.
+
+Test Case: test_mask
+------------------------------------------------
+
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, tag the packet with the value 0xabc and mask 0xcba) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions set_tag data 0xabc mask 0xcba / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has been tagged with the correct data and mask.
+
+
+Test Case: test_index
+------------------------------------------------
+
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, set the packet tag index 1 to the value 0xabc) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions set_tag data 0xabc index 1 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has been tagged with the correct data on the correct tag index.
+
+
+Action: SET_META
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: test_data
+------------------------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, set the packet's meta to the value 0xabc) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions set_meta data 0xabc / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had the correct metadata assigned to it.
+
+Test Case: test_mask
+------------------------------------------------
+
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, set the packet's meta to the value 0xabc and mask 0xcba):
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions set_meta data 0xabc mask 0xcba / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had the correct metadata and mask assigned to it.
+
+Action: SET_IPV4_DSCP
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: set_ipv4_dscp test
+-------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, set the IPv4 dscp (tos) to 2) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions set_ipv4_dscp 2 / end
+
+..
+
+3. Send a packet that matches the rule with a defined dscp (tos) value:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule with defined dscp (tos) values:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its IPv4 dscp (tos) value set to 2.
+
+
+Action: SET_IPV6_DSCP
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: set_ipv6_dscp test
+-------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv6 src is 2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c2, set its dscp (tc) value to 0x30):
+
+::
+
+    flow create 0 ingress pattern eth / ipv6 src is 2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c2 / udp /
+    end actions set_ipv6_dscp dscp 0x30 / end
+
+..
+
+3. Send a packet that matches the rule, with a defined dscp (tc) value:
+
+::
+
+    Pkt0 = Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c3\", tc=0) / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule, with defined dscp (tc) values:
+
+::
+
+    Pkt1 = Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c3\", tc=0) / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c4\", tc=0) / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c5\", tc=0) / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c6\", tc=0) / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its dscp (tc) value set to 0x30.
+
+Action: AGE
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Test Case: test_timeout
+-------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, set the aging timeout value to 128) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions age timeout 128 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its aging timeout value set to 128.
+
+Test Case: test_reserved
+-------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, set the aging timeout value to 128 and reserved to 0) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions age timeout 128 reserved 0 / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its aging timeout value set to 128 and reserved bits set to 0.
+
+Test Case: test_context
+-------------------------------
+
+1. Run testpmd in interactive mode with one port bound and available:
+
+::
+
+    build/testpmd -c 3 -- -i
+
+..
+
+2. Set the test flow rule (If the IPv4 source is 192.168.0.1, set the aging timeout value to 128
+   and flow context to the rte_flow pointer) :
+
+::
+
+    flow create 0 ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions age timeout 128 context NULL / end
+
+..
+
+3. Send a packet that matches the rule:
+
+::
+
+    Pkt0 = Ether() / IP(src=\"192.168.0.1\") / UDP() / ('\\x00' * 64)
+
+..
+
+4. Send packets that do not match the rule:
+
+::
+
+    Pkt1 = Ether() / IP(src=\"192.168.0.2\") / UDP() / ('\\x00' * 64)
+    Pkt2 = Ether() / IP(src=\"10.0.30.99\") / UDP() / ('\\x00' * 64)
+    Pkt3 = Ether() / IP(src=\"8.8.8.8\") / UDP() / ('\\x00' * 64)
+    Pkt4 = Ether() / IP(src=\"132.177.0.99\") / UDP() / ('\\x00' * 64)
+
+..
+
+5. Check to make sure that the pass packet has had its aging timeout value set to 128 and its user flow context
+set to the rte_flow pointer.
+
+
+
+
+
+
diff --git a/test_plans/rte_flow_unsupported.rst b/test_plans/rte_flow_unsupported.rst
new file mode 100644
index 00000000..9df894f3
--- /dev/null
+++ b/test_plans/rte_flow_unsupported.rst
@@ -0,0 +1,130 @@
+.. # BSD LICENSE
+    #
+    # Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+    # Copyright © 2018[, 2020] The University of New Hampshire. All rights reserved.
+    # All rights reserved.
+    #
+    # Redistribution and use in source and binary forms, with or without
+    # modification, are permitted provided that the following conditions
+    # are met:
+    #
+    #   * Redistributions of source code must retain the above copyright
+    #     notice, this list of conditions and the following disclaimer.
+    #   * Redistributions in binary form must reproduce the above copyright
+    #     notice, this list of conditions and the following disclaimer in
+    #     the documentation and/or other materials provided with the
+    #     distribution.
+    #   * Neither the name of Intel Corporation nor the names of its
+    #     contributors may be used to endorse or promote products derived
+    #     from this software without specific prior written permission.
+    #
+    # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+    # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+    # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+    # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+    # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+    # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+    # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+    # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+    # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+rte_flow Items and Properties Not Supported by testpmd
+=======================================================
+The following rte_flow pattern items and properties are currently unsupported by testpmd. As a result, they cannot be tested at this time.
+
+These items and properties are intended to be a part of **test_plans/flow_pattern_item_test_plan.rst**.
+
+Sample test cases for these properties have been written with the other rte_flow pattern item/property tests, but are left as comments as they cannot be used at this time.
+
+
+Item: ARP
+~~~~~~~~~
+ARP is not supported, along with all of its properties:
+
+- hdr (hardware type)
+- pro (protocol type)
+- hln (hardware address length)
+- pln (protocol address length)
+- op (opcode)
+- sha (sender hardware address)
+- spa (sender ipv4 address)
+- tha (target hardware address)
+- tpa (target ipv4 address)
+
+Item: GRE
+~~~~~~~~~
+
+The following property is not supported by testpmd:
+
+- c_rsvd0_ver (checksum + reserved0 + version)
+
+Item: ICMP
+~~~~~~~~~~
+
+The following properties are not supported by testpmd:
+
+- icmp_cksum (checksum)
+- icmp_ident (identifier)
+- icmp_seq_nb (sequence number)
+
+Item: ICMP6
+~~~~~~~~~~~
+ICMP6 is not supported, along with all of its properties:
+
+- type
+- code
+- checksum
+
+Item: IPv4
+~~~~~~~~~~~
+The following property is not supported by testpmd:
+
+- checksum
+
+Item: IPv6
+~~~~~~~~~~~
+The following properties are not supported by testpmd:
+
+- vtc_flow (version + traffic class + flow)
+- payload_len (payload length)
+
+Item: TCP
+~~~~~~~~~~~
+The following properties are not supported by testpmd:
+
+- data_off (data offset)
+- rx_win (window size)
+- cksum (checksum)
+
+
+Item: UDP
+~~~~~~~~~~~
+The following properties are not supported by testpmd:
+
+- dgram_len (datagram length)
+- dgram_cksum (datagram checksum)
+
+Item: VXLAN
+~~~~~~~~~~~
+The following properties are not supported by testpmd:
+
+- rsvd0
+- rsvd1
+- flags
+
+Item: VXLAN_GPE
+~~~~~~~~~~~~~~~
+VXLAN_GPE is not supported, along with all of its properties:
+
+
+- rsvd0
+- rsvd1
+- flags
+- vni
+- protocol
+
+
+
+
diff --git a/tests/TestSuite_rte_flow.py b/tests/TestSuite_rte_flow.py
new file mode 100644
index 00000000..56277a18
--- /dev/null
+++ b/tests/TestSuite_rte_flow.py
@@ -0,0 +1,232 @@
+# BSD LICENSE
+#
+# Copyright(c) 2020 Intel Corporation. All rights reserved.
+# Copyright © 2018[, 2019] The University of New Hampshire. All rights reserved.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+#   * Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#   * Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in
+#     the documentation and/or other materials provided with the
+#     distribution.
+#   * Neither the name of Intel Corporation nor the names of its
+#     contributors may be used to endorse or promote products derived
+#     from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""
+DPDK Test suite.
+MTU Checks example.
+"""
+import time
+import ipaddress
+from typing import Callable
+
+import utils
+from pmd_output import PmdOutput
+from test_case import TestCase
+
+from test_case import TestCase
+
+from framework.flow import generator
+
+
+class RteFlow(TestCase):
+    #
+    #
+    # Helper methods and setup methods.
+    #
+    # Some of these methods may not be used because they were inlined from a child
+    # of TestCase. This was done because the current test system doesn't support
+    # inheritance.
+    #
+    def exec(self, command: str) -> str:
+        """
+        An abstraction to remove repeated code throughout the subclasses of this class
+        """
+        return self.dut.send_expect(command, "testpmd>")
+
+    def get_mac_address_for_port(self, port_id: int) -> str:
+        return self.dut.get_mac_address(port_id)
+
+    def send_scapy_packet(self, port_id: int, packet: str):
+        itf = self.tester.get_interface(port_id)
+        return self.tester.send_expect(f'sendp({packet}, iface="{itf}")', ">>>", timeout=30)
+
+    def set_up_all(self):
+        """
+        Prerequisite steps for each test suit.
+        """
+        self.dut_ports = self.dut.get_ports()
+        self.verify(len(self.dut_ports) >= 2, "Insufficient ports")
+        self.rx_port = self.dut_ports[0]
+        self.tx_port = self.dut_ports[1]
+
+        self.pmdout = PmdOutput(self.dut)
+        self.pmdout.start_testpmd("default", "--rxq 2 --txq 2")
+        self.exec("set verbose 3")
+        self.exec("set fwd rxonly")
+        self.tester.send_expect("scapy", ">>>")
+
+    def initialize_flow_rule(self, rule: str):
+        output: str = self.exec(f"flow validate {self.dut_ports[0]} {rule}")
+        if "Unsupported pattern" in output:
+            return False
+
+        output = self.exec(f"flow create {self.dut_ports[0]} {rule}")
+        self.verify("created" in output or "Flow rule validated" in output, "Flow rule was not created: " + output)
+        return True
+
+    def send_packets(self, packets, pass_fail_function: Callable[[str], bool], error_message: str):
+        for packet in packets:
+            output = self.send_scapy_packet(0, packet)
+            time.sleep(5)  # Allow the packet to be processed
+            self.verify("Sent" in output, "Broken scapy packet definiton: " + packet)
+            output = self.pmdout.get_output()
+            self.verify(pass_fail_function(output),
+                        error_message + "\r\n" + output)
+
+    def do_test_with_callable_tests_for_pass_fail(self, rule: str, pass_packets: frozenset, fail_packets: frozenset,
+                                                  pass_check_function: Callable[[str], bool],
+                                                  fail_check_function: Callable[[str], bool],
+                                                  error_message: str):
+        was_valid: bool = self.initialize_flow_rule(rule)
+        if not was_valid:  # If a PMD rejects a test case, we let it pass
+            return None
+
+        self.exec("start")
+        self.send_packets(pass_packets, pass_check_function, error_message)
+        self.send_packets(fail_packets, fail_check_function, error_message)
+
+    def do_test_with_expected_strings_for_pass_fail(self, rule: str, pass_packets: frozenset,
+                                                    fail_packets: frozenset,
+                                                    pass_expect: str, fail_expect: str, error_message: str):
+        self.do_test_with_callable_tests_for_pass_fail(rule, pass_packets, fail_packets,
+                                                       lambda output: pass_expect in output,
+                                                       lambda output: fail_expect in output,
+                                                       error_message)
+
+    def do_test_with_queue_action(self, rule: str, pass_packets: frozenset, fail_packets: frozenset):
+        self.do_test_with_expected_strings_for_pass_fail(rule, pass_packets, fail_packets,
+                                                         f"port {self.dut_ports[0]}/queue 1",
+                                                         f"port {self.dut_ports[0]}/queue 0",
+                                                         "Error: packet went to the wrong queue")
+
+    def set_up(self):
+        """
+        This is to clear up environment before the case run.
+        """
+
+    def tear_down(self):
+        """
+        Run after each test case.
+        """
+        self.exec(f"flow flush {self.dut_ports[0]}")
+        self.exec("stop")
+
+    def tear_down_all(self):
+        """
+        When the case of this test suite finished, the environment should
+        clear up.
+        """
+        self.tester.send_expect("exit()", "#")
+        self.dut.kill_all()
+        self.tester.kill_all()
+
+    """
+    Edge Cases
+    
+    These are tests which are designed to deal with edge cases. 
+    """
+
+    def test_excessive_voids(self):
+        """
+        This test puts far, far too many void tokens in an otherwise valid
+        pattern. This may cause a crash or other issue, due to buffer size
+        constraints or other limits inside of either the parser or the nic.
+        """
+        self.do_test_with_queue_action(
+            "ingress pattern eth / ipv4 / " + (
+                    "void / " * 200) + "udp / end actions queue index 1 / end",
+            frozenset({'Ether() / IP() / UDP() / (\'\\x00\' * 64)'}),
+            frozenset({
+                'Ether() / IP() / TCP() / (\'\\x00\' * 64)',
+                'Ether() / IP() / SCTP() / (\'\\x00\' * 64)',
+                'Ether() / IPv6() / UDP() / (\'\\x00\' * 64)',
+            })
+        )
+
+    def test_excessive_tunneling(self):
+        self.do_test_with_queue_action(
+            "ingress pattern " + ("eth / gre / " * 20) + "eth / ipv4 / udp / end actions queue index 1 / end",
+            frozenset({'Ether() / IP() / UDP() / (\'\\x00\' * 64)'}),
+            frozenset({
+                'Ether() / IP() / TCP() / (\'\\x00\' * 64)',
+                'Ether() / IP() / SCTP() / (\'\\x00\' * 64)',
+                'Ether() / IPv6() / UDP() / (\'\\x00\' * 64)',
+            })
+        )
+
+    """
+    Action Test Cases
+    
+    These are test cases built for testing various actions
+    """
+
+    def test_drop_case1(self):
+        self.do_test_with_callable_tests_for_pass_fail(
+            "ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions drop / end",
+            frozenset({'Ether() / IP(src="192.168.0.1") / UDP() / (\'\\x00\' * 64)'}),
+            frozenset({'Ether() / IP(src="10.0.30.99") / UDP() / (\'\\x00\' * 64)',
+                       'Ether() / IP(src="132.177.0.99") / UDP() / (\'\\x00\' * 64)',
+                       'Ether() / IP(src="192.168.0.2") / UDP() / (\'\\x00\' * 64)',
+                       'Ether() / IP(src="8.8.8.8") / UDP() / (\'\\x00\' * 64)'}),
+            lambda output: "port" not in output,
+            lambda output: "port" in output,
+            "Drop function was not correctly applied")
+
+    def test_queue_case1(self):
+        self.do_test_with_queue_action(
+            "ingress pattern eth / ipv4 src is 192.168.0.1 / udp / end actions queue index 1 / end",
+            frozenset({'Ether() / IP(src="192.168.0.1") / UDP() / (\'\\x00\' * 64)'}), frozenset(
+                {'Ether() / IP(src="10.0.30.99") / UDP() / (\'\\x00\' * 64)',
+                 'Ether() / IP(src="132.177.0.99") / UDP() / (\'\\x00\' * 64)',
+                 'Ether() / IP(src="192.168.0.2") / UDP() / (\'\\x00\' * 64)',
+                 'Ether() / IP(src="8.8.8.8") / UDP() / (\'\\x00\' * 64)'}))
+
+
+def do_runtime_test_generation():
+    """
+    There are enough tests for this suite that it presents a maintainability
+    issue to keep using the generator manually, so this approach is designed
+    """
+    print("Generating test cases for RTE Flow test suite.")
+    pattern_functions = generator.create_test_function_strings(generator.get_patterns_with_properties())
+
+    pattern_function_string = "\n".join(pattern_functions)
+    exec(pattern_function_string)
+
+    # Copy it because a for loop creates local variables, which changes the dict at runtime.
+    locals_copy = filter(lambda name_function_tuple: name_function_tuple[0].startswith("test_"), locals().items())
+
+    for name, function in locals_copy:
+        setattr(RteFlow, name, function)
+
+
+do_runtime_test_generation()
-- 
2.25.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2020-10-27  5:08 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-14 20:50 [dts] [PATCH] rte flow: added test suite and framework Owen Hilyard
2020-10-14 21:02 ` [dts] [PATCH] rte flow: fixing checkpatch issues Owen Hilyard
2020-10-22  8:53 ` [dts] [PATCH] rte flow: added test suite and framework Tu, Lijuan
2020-10-23 18:18   ` Owen Hilyard
2020-10-23 23:28     ` [dts] [PATCH v2 1/7] rte flow: add test plan and test suite Owen Hilyard
2020-10-23 23:28       ` [dts] [PATCH v2 2/7] rte flow: add supporting data structures Owen Hilyard
2020-10-23 23:28         ` [dts] [PATCH v2 3/7] rte flow: Add base flow type Owen Hilyard
2020-10-23 23:28           ` [dts] [PATCH v2 4/7] rte flow: add more specifc data structures Owen Hilyard
2020-10-23 23:29             ` [dts] [PATCH v2 5/7] rte flow: add action items Owen Hilyard
2020-10-23 23:29               ` [dts] [PATCH v2 6/7] rte flow: add pattern items Owen Hilyard
2020-10-23 23:29                 ` [dts] [PATCH v2 7/7] rte flow: add flow test generator Owen Hilyard
2020-10-23 23:29                   ` [dts] [PATCH v2] " Owen Hilyard
2020-10-23 23:30                     ` Owen Hilyard
2020-10-27  5:07       ` [dts] [PATCH v2 1/7] rte flow: add test plan and test suite Tu, Lijuan

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).