diff --git a/framework/flow/flow_action_items.py b/framework/flow/flow_action_items.py
new file mode 100644
index 0000000..b08272f
--- /dev/null
+++ b/framework/flow/flow_action_items.py
@@ -0,0 +1,1050 @@
+# 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.
+
+from typing import FrozenSet, Dict, Tuple
+
+from flow.enums import FlowActionType
+from 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
+}
