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()
