[RFC,v1,21/23] dts: merge DTS framework/flow/flow_pattern_items.py to DPDK

Message ID 20220406151903.2916254-22-juraj.linkes@pantheon.tech (mailing list archive)
State RFC, archived
Delegated to: Thomas Monjalon
Headers
Series merge DTS test resource files to DPDK |

Checks

Context Check Description
ci/checkpatch warning coding style issues

Commit Message

Juraj Linkeš April 6, 2022, 3:19 p.m. UTC
  ---
 dts/framework/flow/flow_pattern_items.py | 1219 ++++++++++++++++++++++
 1 file changed, 1219 insertions(+)
 create mode 100644 dts/framework/flow/flow_pattern_items.py
  

Patch

diff --git a/dts/framework/flow/flow_pattern_items.py b/dts/framework/flow/flow_pattern_items.py
new file mode 100644
index 0000000000..ccb019e765
--- /dev/null
+++ b/dts/framework/flow/flow_pattern_items.py
@@ -0,0 +1,1219 @@ 
+# 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.
+
+# Allows the type system to handle referencing a class inside it's definition
+from typing import Dict, FrozenSet, Iterable, List, Tuple
+
+from scapy.layers.inet import ICMP, IP, TCP, UDP
+from scapy.layers.inet6 import IPv6
+from scapy.layers.l2 import ARP, GRE, Dot1Q, Ether
+from scapy.layers.sctp import SCTP
+from scapy.layers.vxlan import VXLAN
+from scapy.packet import Packet
+
+from .enums import FlowItemType
+from .exceptions import InvalidFlowItemException
+from .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 .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 = {
+        # THE FOLLOWING PROPERTIES ARE UNSUPPORTED BY TESTPMD AT THE TIME OF WRITING.
+        # THEY CAN BE ENABLED ONCE TESTPMD SUPPORTS THEM
+        # 'hdr':
+        #     ('arp_eth_ipv4 hdr is 1',
+        #      frozenset({"Ether() / ARP(hwtype=1) / Raw('\\x00' * 64)"}),
+        #
+        #      frozenset({"Ether() / ARP(hwtype=2) / Raw('\\x00' * 64)",
+        #                 "Ether() / ARP(hwtype=3) / Raw('\\x00' * 64)",
+        #                 "Ether() / ARP(hwtype=6) / Raw('\\x00' * 64)",
+        #                 "Ether() / ARP(hwtype-15) / Raw('\\x00' * 64)"
+        #                 })),
+        # 'pro':
+        #     ('arp_eth_ipv4 pro is 0x0800',
+        #      frozenset({"Ether() / ARP(ptype=0x0800) / Raw('\\x00' * 64)"}),
+        #
+        #      frozenset({"Ether() / ARP(ptype=0x0800) / Raw('\\x00' * 64)",
+        #                 "Ether() / ARP(ptype=0x0842) / Raw('\\x00' * 64)",
+        #                 "Ether() / ARP(ptype=0x6004) / Raw('\\x00' * 64)",
+        #                 "Ether() / ARP(ptype=0x809b) / Raw('\\x00' * 64)"
+        #                 })),
+        #
+        # 'hln':
+        #     ('arp_eth_ipv4 hln is 6',
+        #      frozenset({"Ether() / ARP(hwlen=6) / Raw('\\x00' * 64)"}),
+        #
+        #      frozenset({"Ether() / ARP(hwlen=12) / Raw('\\x00' * 64)",
+        #                 "Ether() / ARP(hwlen=2) / Raw('\\x00' * 64)",
+        #                 "Ether() / ARP(hwlen=8) / Raw('\\x00' * 64)",
+        #                 "Ether() / ARP(hwlen=4) / Raw('\\x00' * 64)"
+        #                 })),
+        #
+        # 'pln':
+        #     ('arp_eth_ipv4 pln is 4',
+        #      frozenset({"Ether() / ARP(plen=4) / Raw('\\x00' * 64)"}),
+        #
+        #      frozenset({"Ether() / ARP(plen=6) / Raw('\\x00' * 64)",
+        #                 "Ether() / ARP(plen=2) / Raw('\\x00' * 64)",
+        #                 "Ether() / ARP(plen=8) / Raw('\\x00' * 64)",
+        #                 "Ether() / ARP(plen=12) / Raw('\\x00' * 64)"
+        #                 })),
+        #
+        # 'op':
+        #     ('arp_eth_ipv4 op is 1',
+        #      frozenset({"Ether() / ARP(op=1) / Raw('\\x00' * 64)"}),
+        #
+        #      frozenset({"Ether() / ARP(op=2) / Raw('\\x00' * 64)",
+        #                 "Ether() / ARP(op=3) / Raw('\\x00' * 64)",
+        #                 "Ether() / ARP(op=4) / Raw('\\x00' * 64)",
+        #                 "Ether() / ARP(op=5) / Raw('\\x00' * 64)"
+        #                 })),
+        # END UNSUPPORTED PROPERTIES
+        "sha": (
+            "arp_eth_ipv4 sha is 90:61:ae:fd:41:43",
+            frozenset(
+                {"Ether() / ARP(hwsrc=\"90:61:ae:fd:41:43\") / Raw('\\x00' * 64)"}
+            ),
+            frozenset(
+                {
+                    "Ether() / ARP(hwsrc=\"90:61:ae:fd:41:44\") / Raw('\\x00' * 64)",
+                    "Ether() / ARP(hwsrc=\"90:61:ae:fd:41:45\") / Raw('\\x00' * 64)",
+                    "Ether() / ARP(hwsrc=\"90:61:ae:fd:41:46\") / Raw('\\x00' * 64)",
+                    "Ether() / ARP(hwsrc=\"90:61:ae:fd:41:47\") / Raw('\\x00' * 64)",
+                }
+            ),
+        ),
+        "spa": (
+            "arp_eth_ipv4 spa is 192.168.0.80",
+            frozenset({"Ether() / ARP(psrc=\"192.168.0.80\") / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() / ARP(psrc=\"10.0.30.10\") / Raw('\\x00' * 64)",
+                    "Ether() / ARP(psrc=\"8.8.8.8\") / Raw('\\x00' * 64)",
+                    "Ether() / ARP(psrc=\"132.177.0.5\") / Raw('\\x00' * 64)",
+                    "Ether() / ARP(psrc=\"123.4.5.6\") / Raw('\\x00' * 64)",
+                }
+            ),
+        ),
+        "tha": (
+            "arp_eth_ipv4 tha is 00:00:00:00:00:00",
+            frozenset({"Ether() / ARP(hwdst=00:00:00:00:00:00) / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() / ARP(hwdst=90:61:ae:fd:41:45) / Raw('\\x00' * 64)",
+                    "Ether() / ARP(hwdst=90:61:ae:fd:41:46) / Raw('\\x00' * 64)",
+                    "Ether() / ARP(hwdst=90:61:ae:fd:41:47) / Raw('\\x00' * 64)",
+                    "Ether() / ARP(hwdst=90:61:ae:fd:41:48) / Raw('\\x00' * 64)",
+                }
+            ),
+        ),
+        "tpa": (
+            "arp_eth_ipv4 tpa is 192.168.0.1",
+            frozenset({"Ether() / ARP(pdst=192.168.0.1) / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() / ARP(pdst=10.0.30.10) / Raw('\\x00' * 64)",
+                    "Ether() / ARP(pdst=8.8.8.8) / Raw('\\x00' * 64)",
+                    "Ether() / ARP(pdst=132.177.0.5) / Raw('\\x00' * 64)",
+                    "Ether() / ARP(pdst=123.4.5.6) / Raw('\\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\") / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether(dst=\"90:61:ae:fd:41:44\") / Raw('\\x00' * 64)",
+                    "Ether(dst=\"90:61:ae:fd:41:45\") / Raw('\\x00' * 64)",
+                    "Ether(dst=\"90:61:ae:fd:41:46\") / Raw('\\x00' * 64)",
+                    "Ether(dst=\"91:61:ae:fd:41:43\") / Raw('\\x00' * 64)",
+                }
+            ),
+        ),
+        "src": (
+            "eth src is 90:61:ae:fd:41:43",
+            frozenset({"Ether(src=\"90:61:ae:fd:41:43\") / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether(src=\"90:61:ae:fd:41:44\") / Raw('\\x00' * 64)",
+                    "Ether(src=\"90:61:ae:fd:41:45\") / Raw('\\x00' * 64)",
+                    "Ether(src=\"90:61:ae:fd:41:46\") / Raw('\\x00' * 64)",
+                    "Ether(src=\"91:61:ae:fd:41:43\") / Raw('\\x00' * 64)",
+                }
+            ),
+        ),
+        "type": (
+            "eth type is 0x0800",  # IPv4 EtherType
+            frozenset({"Ether(type=0x0800) / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether(type=0x0842) / Raw('\\x00' * 64)",
+                    "Ether(type=0x8100) / Raw('\\x00' * 64)",  # Possibly a special case? TPID/VLAN
+                    "Ether(type=0x9100) / Raw('\\x00' * 64)",  # Possibly special, VLAN double tagging
+                    "Ether(type=0x8863) / Raw('\\x00' * 64)",
+                    "Ether(type=0x9000) / Raw('\\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) / Raw('\\x00' * 64)"}
+            ),
+            frozenset(
+                {
+                    "Ether() / GRE(chksum_present=1, version=0)) / Raw('\\x00' * 64)",
+                    # this is the only other option
+                }
+            ),
+        ),
+        "protocol": (
+            "gre protocol is 0x0800",
+            frozenset({"Ether() / GRE(proto=0x0800) / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() / GRE(proto=0x0842) / Raw('\\x00' * 64)",
+                    "Ether() / GRE(proto=0x8100) / Raw('\\x00' * 64)",
+                    "Ether() / GRE(proto=0x0806) / Raw('\\x00' * 64)",
+                    "Ether() / GRE(proto=0x809B) / Raw('\\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 = {
+        # THE FOLLOWING PROPERTIES ARE UNSUPPORTED BY TESTPMD AT THE TIME OF WRITING.
+        # THEY CAN BE ENABLED ONCE TESTPMD SUPPORTS THEM
+        # 'icmp_cksum':
+        #     ('icmp cksum is 0x0800',
+        #      frozenset({"Ether() / ICMP() / UDP() / Raw('\x00' * 64)"}),
+        #
+        #      frozenset({"Ether() / ICMP() / UDP() / Raw('\x00' * 64)",
+        #                 "Ether() / ICMP() / UDP() / Raw('\x00' * 64)",
+        #                 "Ether() / ICMP() / UDP() / Raw('\x00' * 64)",
+        #                 "Ether() / ICMP() / UDP() / Raw('\x00' * 64)"
+        #                 })),
+        # END UNSUPPORTED PROPERTIES
+        "icmp_type": (
+            "icmp type is 3",
+            frozenset({"Ether() / ICMP(type=3) / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() / ICMP(type=3) / Raw('\\x00' * 64)",
+                    "Ether() / ICMP(type=11) / Raw('\\x00' * 64)",
+                    "Ether() / ICMP(type=13) / Raw('\\x00' * 64)",
+                    "Ether() / ICMP(type=0) / Raw('\\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) / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() / ICMP(type=3, code=0) / Raw('\\x00' * 64)",
+                    "Ether() / ICMP(type=3, code=2) / Raw('\\x00' * 64)",
+                    "Ether() / ICMP(type=11, code=1) / Raw('\\x00' * 64)",
+                    "Ether() / ICMP(type=12, code=2) / Raw('\\x00' * 64)",
+                }
+            ),
+        ),
+        "icmp_ident": (
+            "icmp ident is 0x0800",
+            frozenset({"Ether() / ICMP() / UDP() / Raw('\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() / ICMP() / UDP() / Raw('\x00' * 64)",
+                    "Ether() / ICMP() / UDP() / Raw('\x00' * 64)",
+                    "Ether() / ICMP() / UDP() / Raw('\x00' * 64)",
+                    "Ether() / ICMP() / UDP() / Raw('\x00' * 64)",
+                }
+            ),
+        ),
+        "icmp_seq": (
+            "icmp seq is 0x0800",
+            frozenset({"Ether() / ICMP(proto=0x0800) / UDP() / Raw('\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() / ICMP() / UDP() / Raw('\x00' * 64)",
+                    "Ether() / ICMP() / UDP() / Raw('\x00' * 64)",
+                    "Ether() / ICMP() / UDP() / Raw('\x00' * 64)",
+                    "Ether() / ICMP() / UDP() / Raw('\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``.
+    """
+    possible_properties = {
+        # THE FOLLOWING PROPERTIES ARE UNSUPPORTED BY TESTPMD AT THE TIME OF WRITING.
+        # THEY CAN BE ENABLED ONCE TESTPMD SUPPORTS THEM
+        # 'checksum':
+        #     ('icmp6 cksum is 0x1234',
+        #      frozenset({"Ether() / ICMPv6DestUnreach(cksum=0x1234) / Raw('\\x00' * 64)"}),
+        #
+        #      frozenset({"Ether() / ICMPv6DestUnreach(cksum=0x4321) / Raw('\\x00' * 64)",
+        #                 "Ether() / ICMPv6DestUnreach(cksum=0xffff) / Raw('\\x00' * 64)",
+        #                 "Ether() / ICMPv6DestUnreach(cksum=0x1233) / Raw('\\x00' * 64)",
+        #                 "Ether() / ICMPv6DestUnreach(cksum=0x1010) / Raw('\\x00' * 64)"
+        #                 })),
+        # END UNSUPPORTED PROPERTIES
+        "type": (
+            "icmp6 type is 1",  # Destination Unreachable
+            frozenset({"Ether() / ICMPv6DestUnreach(type=1) / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() / ICMPv6DestUnreach(type=128) / Raw('\\x00' * 64)",
+                    "Ether() / ICMPv6DestUnreach(type=129) / Raw('\\x00' * 64)",
+                    "Ether() / ICMPv6DestUnreach(type=3) / Raw('\\x00' * 64)",
+                    "Ether() / ICMPv6DestUnreach(type=135) / Raw('\\x00' * 64)",
+                }
+            ),
+        ),
+        "code": (  # ICMP code is dependent on type; these are possible Destination Unreachable codes
+            "icmp6 code is 0",
+            frozenset({"Ether() / ICMPv6DestUnreach(code=0) / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() / ICMPv6DestUnreach(code=1) / Raw('\\x00' * 64)",
+                    "Ether() / ICMPv6DestUnreach(code=2) / Raw('\\x00' * 64)",
+                    "Ether() / ICMPv6DestUnreach(code=3) / Raw('\\x00' * 64)",
+                    "Ether() / ICMPv6DestUnreach(code=4) / Raw('\\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) / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() / IP(tos=2) / Raw('\\x00' * 64)",
+                    "Ether() / IP(tos=4) / Raw('\\x00' * 64)",
+                    "Ether() / IP(tos=8) / Raw('\\x00' * 64)",
+                    "Ether() / IP(tos=16) / Raw('\\x00' * 64)",
+                }
+            ),
+        ),
+        "ttl": (
+            "ipv4 ttl is 64",
+            frozenset({"Ether() / IP(ttl=64) / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() / IP(ttl=128) / Raw('\\x00' * 64)",
+                    "Ether() / IP(ttl=255) / Raw('\\x00' * 64)",
+                    "Ether() / IP(ttl=32)  / Raw('\\x00' * 64)",
+                    "Ether() / IP(ttl=100) / Raw('\\x00' * 64)",
+                }
+            ),
+        ),
+        "proto": (
+            "ipv4 proto is 0x06",  # TCP
+            frozenset({"Ether() / IP(proto=0x06) / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() / IP(proto=0x01) / Raw('\\x00' * 64)",
+                    "Ether() / IP(proto=0x11) / Raw('\\x00' * 64)",
+                    "Ether() / IP(proto=0x12) / Raw('\\x00' * 64)",
+                    "Ether() / IP(proto=0x58) / Raw('\\x00' * 64)",
+                }
+            ),
+        ),
+        "src": (
+            "ipv4 src is 192.168.0.5",
+            frozenset({"Ether() / IP(src=\"192.168.0.5\") / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() / IP(src=\"10.10.10.10\") / Raw('\\x00' * 64)",
+                    "Ether() / IP(src=\"132.177.127.6\") / Raw('\\x00' * 64)",
+                    "Ether() / IP(src=\"192.168.0.4\") / Raw('\\x00' * 64)",
+                    "Ether() / IP(src=\"192.168.0.250\") / Raw('\\x00' * 64)",
+                }
+            ),
+        ),
+        "dst": (
+            "ipv4 dst is 192.168.0.5",
+            frozenset({"Ether() / IP(dst=\"192.168.0.5\") / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() / IP(dst=\"10.10.10.10\") / Raw('\\x00' * 64)",
+                    "Ether() / IP(dst=\"132.177.127.6\") / Raw('\\x00' * 64)",
+                    "Ether() / IP(dst=\"192.168.0.4\") / Raw('\\x00' * 64)",
+                    "Ether() / IP(dst=\"192.168.0.250\") / Raw('\\x00' * 64)",
+                }
+            ),
+        ),
+        # CHECKSUM PROPERTY NOT SUPPORTED BY TESTPMD; DO NOT UNCOMMENT UNTIL SUPPORTED
+        # 'checksum':
+        #     ('ipv4 chksum is 0x1234',
+        #     frozenset({"Ether() / ICMPv6DestUnreach(cksum=0x1234) / Raw('\\x00' * 64)"}),
+        #     frozenset({"Ether() / ICMPv6DestUnreach(cksum=0x4321) / Raw('\\x00' * 64)",
+        #                "Ether() / ICMPv6DestUnreach(cksum=0xffff) / Raw('\\x00' * 64)",
+        #                "Ether() / ICMPv6DestUnreach(cksum=0x1233) / Raw('\\x00' * 64)",
+        #                "Ether() / ICMPv6DestUnreach(cksum=0x1010) / Raw('\\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 CAN BE ENABLED ONCE TESTPMD SUPPORTS THEM
+        # 'vtc_flow':
+        #     ('ipv6 vtc_flow is 0x0',
+        #      frozenset({"Ether() / IPv6(tc=0, fl=0, version=0) / Raw('\\x00' * 64)"}),
+        #      frozenset({"Ether() / IPv6(tc=1, fl=0, version=0) / Raw('\\x00' * 64)",
+        #                 "Ether() / IPv6(tc=0, fl=0xABCD, version=0) / Raw('\\x00' * 64)",
+        #                 "Ether() / IPv6(tc=0, fl=0, version=1) / Raw('\\x00' * 64)",
+        #                 "Ether() / IPv6(tc=6, fl=0x9999, version=1) / Raw('\\x00' * 64)"
+        #                 })),
+        # 'payload_len':
+        #     ('ipv6 payload_len is 64',
+        #      frozenset({"Ether() / IPv6(plen=64) / Raw('\\x00' * 64)"}),
+        #      frozenset({"Ether() / IPv6(plen=32) / Raw('\\x00' * 64)",
+        #                 "Ether() / IPv6(plen=128) / Raw('\\x00' * 64)",
+        #                 "Ether() / IPv6(plen=5000) / Raw('\\x00' * 64)",
+        #                 "Ether() / IPv6(plen=4) / Raw('\\x00' * 64)"
+        #                 })),
+        # END UNSUPPORTED PROPERTIES
+        "tc": (
+            "ipv6 tc is 0",
+            frozenset({"Ether() / IPv6(tc=0) / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() / IPv6(tc=1) / Raw('\\x00' * 64)",
+                    "Ether() / IPv6(tc=2) / Raw('\\x00' * 64)",
+                    "Ether() / IPv6(tc=4) / Raw('\\x00' * 64)",
+                    "Ether() / IPv6(tc=6) / Raw('\\x00' * 64)",
+                }
+            ),
+        ),
+        "flow": (
+            "ipv6 flow is 0xABCD",
+            frozenset({"Ether() / IPv6(fl=0xABCD) / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() / IPv6(fl=0xABCE) / Raw('\\x00' * 64)",
+                    "Ether() / IPv6(fl=0x0001) / Raw('\\x00' * 64)",
+                    "Ether() / IPv6(fl=0xFFFF) / Raw('\\x00' * 64)",
+                    "Ether() / IPv6(fl=0x1234) / Raw('\\x00' * 64)",
+                }
+            ),
+        ),
+        "proto": (  # next header (nh)
+            "ipv6 proto is 6",  # TCP
+            frozenset({"Ether() / IPv6(nh=6) / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() / IPv6(nh=17) / Raw('\\x00' * 64)",
+                    "Ether() / IPv6(nh=41) / Raw('\\x00' * 64)",
+                    "Ether() / IPv6(nh=0) / Raw('\\x00' * 64)",
+                    "Ether() / IPv6(nh=60) / Raw('\\x00' * 64)",
+                }
+            ),
+        ),
+        "hop": (  # hop limit
+            "ipv6 hop is 64",
+            frozenset({"Ether() / IPv6(hlim=64) / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() / IPv6(hlim=128) / Raw('\\x00' * 64)",
+                    "Ether() / IPv6(hlim=32) / Raw('\\x00' * 64)",
+                    "Ether() / IPv6(hlim=255) / Raw('\\x00' * 64)",
+                    "Ether() / IPv6(hlim=100) / Raw('\\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\") / Raw('\\x00' * 64)"
+                }
+            ),
+            frozenset(
+                {
+                    "Ether() / IPv6(dst=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c3\") / Raw('\\x00' * 64)",
+                    "Ether() / IPv6(dst=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c4\") / Raw('\\x00' * 64)",
+                    "Ether() / IPv6(dst=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c5\") / Raw('\\x00' * 64)",
+                    "Ether() / IPv6(dst=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c6\") / Raw('\\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\") / Raw('\\x00' * 64)"
+                }
+            ),
+            frozenset(
+                {
+                    "Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c3\") / Raw('\\x00' * 64)",
+                    "Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c4\") / Raw('\\x00' * 64)",
+                    "Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c5\") / Raw('\\x00' * 64)",
+                    "Ether() / IPv6(src=\"2001:0000:9d38:6ab8:1c48:3a1c:a95a:b1c6\") / Raw('\\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) / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() / IP() / SCTP(sport=3939) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / SCTP(sport=5000) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / SCTP(sport=1998) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / SCTP(sport=1028) / Raw('\\x00' * 64)",
+                }
+            ),
+        ),
+        "dst": (
+            "sctp dst is 3838",
+            frozenset({"Ether() / IP() / SCTP(dport=3838) / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() / IP() / SCTP(dport=3939) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / SCTP(dport=5000) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / SCTP(dport=1998) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / SCTP(dport=1028) / Raw('\\x00' * 64)",
+                }
+            ),
+        ),
+        "tag": (
+            "sctp tag is 12345",
+            frozenset({"Ether() / IP() / SCTP(tag=12345) / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() / IP() / SCTP(tag=12346) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / SCTP(tag=12) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / SCTP(tag=9999) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / SCTP(tag=42) / Raw('\\x00' * 64)",
+                }
+            ),
+        ),
+        "cksum": (
+            "sctp cksum is 0x01535b67",
+            frozenset({"Ether() / IP() / SCTP(chksum=0x01535b67) / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() / IP() / SCTP(chksum=0x01535b68) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / SCTP(chksum=0xdeadbeef) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / SCTP(chksum=0x12345678) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / SCTP(chksum=0x385030fe) / Raw('\\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 CAN BE ENABLED ONCE TESTPMD SUPPORTS THEM
+        # 'data_off':
+        #     ('tcp data_off is 0',
+        #      frozenset({"Ether() / IP() / TCP(dataofs=0) / Raw('\\x00' * 64)"}),
+        #      frozenset({"Ether() /  IP() / TCP(dataofs=1) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP() / TCP(dataofs=2) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP() / TCP(dataofs=3) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP() / TCP(dataofs=4) / Raw('\\x00' * 64)"
+        #                 })),
+        # 'rx_win':
+        #     ('tcp rx_win is 64',
+        #      frozenset({"Ether() /  IP() / TCP(window=64)/ Raw('\\x00' * 64)"}),
+        #      frozenset({"Ether() /  IP() / TCP(window=16)/ Raw('\\x00' * 64)",
+        #                 "Ether() /  IP() / TCP(window=128) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP() / TCP(window=32) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP() / TCP(window=255) / Raw('\\x00' * 64)"
+        #                 })),
+        # 'cksum':
+        #     ('tcp cksum is 0x1234',
+        #      frozenset({"Ether() /  IP() / TCP(chksum=0x1234) / Raw('\\x00' * 64)"}),
+        #      frozenset({"Ether() / IP() / TCP(chksum=0x4321) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP() / TCP(chksum=0xffff) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP() / TCP(chksum=0x9999) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP() / TCP(chksum=0x1233)  / Raw('\\x00' * 64)"
+        #                 })),
+        # END UNSUPPORTED PROPERTIES
+        "src": (
+            "tcp src is 3838",
+            frozenset({"Ether() / IP() / TCP(sport=3838) / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() / IP() / TCP(sport=3939) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / TCP(sport=5000) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / TCP(sport=1998) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / TCP(sport=1028) / Raw('\\x00' * 64)",
+                }
+            ),
+        ),
+        "dst": (
+            "tcp dst is 3838",
+            frozenset({"Ether() / IP() / TCP(dport=3838) / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() / IP() / TCP(dport=3939) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / TCP(dport=5000) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / TCP(dport=1998) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / TCP(dport=1028) / Raw('\\x00' * 64)",
+                }
+            ),
+        ),
+        "flags": (
+            "tcp flags is 0x02",
+            frozenset({"Ether() / IP() / TCP(flags=0x02) / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() / IP() / TCP(flags=0x01) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / TCP(flags=0x04) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / TCP(flags=0x08) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / TCP(flags=0x10) / Raw('\\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 MAY BE RE-ENABLED ONCE TESTPMD SUPPORTS THEIR USE
+        # 'dgram_len':
+        #     ('udp dgram_len is 64',
+        #      frozenset({"Ether() / IP() / UDP(len=64) / Raw('\\x00' * 64)"}),
+        #
+        #      frozenset({"Ether() /  IP() / UDP(len=128) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP() / UDP(len=32) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP() / UDP(len=16) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP() / UDP(len=255) / Raw('\\x00' * 64)"
+        #                 })),
+        # 'dgram_cksum':
+        #     ('udp dgram_cksum is 0x1234',
+        #      frozenset({"Ether() /  IP() / UDP(chksum=0x1234) / Raw('\\x00' * 64)"}),
+        #
+        #      frozenset({"Ether() / IP() / UDP(chksum=0x4321) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP() / UDP(chksum=0xffff) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP() / UDP(chksum=0x9999) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP() / UDP(chksum=0x1233)  / Raw('\\x00' * 64)"
+        #                 })),
+        # END UNSUPPORTED PROPERTIES
+        "src": (
+            "udp src is 3838",
+            frozenset({"Ether() / IP() / UDP(sport=3838) / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() / IP() / UDP(sport=3939) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / UDP(sport=5000) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / UDP(sport=1998) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / UDP(sport=1028) / Raw('\\x00' * 64)",
+                }
+            ),
+        ),
+        "dst": (
+            "udp dst is 3838",
+            frozenset({"Ether() / IP() / UDP(dport=3838) / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() / IP() / UDP(dport=3939) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / UDP(dport=5000) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / UDP(dport=1998) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / UDP(dport=1028) / Raw('\\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 = {
+        # THE FOLLOWING PROPERTIES ARE UNSUPPORTED BY TESTPMD AT THE TIME OF WRITING.
+        # THEY MAY BE RE-ENABLED ONCE TESTPMD SUPPORTS THEIR USE
+        # 'tpid':
+        #     ('vlan tpid is 0x8100',  # standard value
+        #      frozenset({"Ether() / Dot1Q(type=0x8100) / Raw('\\x00' * 64)"}),
+        #
+        #      frozenset({"Ether() /  Dot1Q(type=0x0800) / Raw('\\x00' * 64)",
+        #                 "Ether() /  Dot1Q(type=0x0842) / Raw('\\x00' * 64)",
+        #                 "Ether() /  Dot1Q(type=0x809b) / Raw('\\x00' * 64)",
+        #                 "Ether() /  Dot1Q(type=0x86dd) / Raw('\\x00' * 64)"
+        #                 })),
+        # END UNSUPPORTED PROPERTIES
+        "tci": (
+            "vlan tci is 0xaaaa",
+            frozenset(
+                {
+                    "Ether() / Dot1Q(prio = 0x5, id = 0x0, vlan = 0xaaa) / Raw('\\x00' * 64)"
+                }
+            ),
+            frozenset(
+                {
+                    "Ether() /  Dot1Q(prio = 0x0, id = 0x1, vlan = 0xbbb) / Raw('\\x00' * 64)",
+                    "Ether() /  Dot1Q(prio = 0x5, id = 0x0, vlan = 0xccc) / Raw('\\x00' * 64)",
+                    "Ether() /  Dot1Q(prio = 0x5, id = 0x1, vlan = 0xaaa) / Raw('\\x00' * 64)",
+                    "Ether() /  Dot1Q(prio = 0x4, id = 0x0, vlan = 0xaaa) / Raw('\\x00' * 64)",
+                }
+            ),
+        ),
+        "pcp": (
+            "vlan pcp is 0x0",
+            frozenset({"Ether() / Dot1Q(prio=0x0) / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() /  Dot1Q(prio=0x1) / Raw('\\x00' * 64)",
+                    "Ether() /  Dot1Q(prio=0x2) / Raw('\\x00' * 64)",
+                    "Ether() /  Dot1Q(prio=0x3) / Raw('\\x00' * 64)",
+                    "Ether() /  Dot1Q(prio=0x7) / Raw('\\x00' * 64)",
+                }
+            ),
+        ),
+        "dei": (
+            "vlan dei is 0",
+            frozenset({"Ether() / Dot1Q(id=0) / Raw('\\x00' * 64)"}),
+            frozenset({"Ether() /  Dot1Q(id=1) / Raw('\\x00' * 64)"}),
+        ),
+        "vid": (
+            "vlan vid is 0xabc",
+            frozenset({"Ether() / Dot1Q(vlan=0xabc) / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() /  Dot1Q(vlan=0xaaa) / Raw('\\x00' * 64)",
+                    "Ether() /  Dot1Q(vlan=0x123) / Raw('\\x00' * 64)",
+                    "Ether() /  Dot1Q(vlan=0x1f5) / Raw('\\x00' * 64)",
+                    "Ether() /  Dot1Q(vlan=0x999) / Raw('\\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 CAN BE ENABLED ONCE TESTPMD SUPPORTS THEM
+        # 'rsvd0':
+        #     ('vxlan rsvd0 is 0x000000',
+        #      frozenset({"Ether() / IP() / VXLAN(reserved0=0) / Raw('\\x00' * 64)"}),
+        #
+        #      frozenset({"Ether() /  IP() / VXLAN(reserved0=1) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP() /  VXLAN(reserved0=2) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP() /  VXLAN(reserved0=3) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP()  / VXLAN(reserved0=4) / Raw('\\x00' * 64)"
+        #                 })),
+        # 'rsvd1':
+        #     ('vxlan rsvd1 is 0x00',
+        #      frozenset({"Ether() /  IP() /  VXLAN(reserved0=0) / Raw('\\x00' * 64)"}),
+        #
+        #      frozenset({"Ether() / IP() /  VXLAN(reserved0=1) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP() /  VXLAN(reserved0=2) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP() / VXLAN(reserved0=3) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP() /  VXLAN(reserved0=4) / Raw('\\x00' * 64)"
+        #                 })),
+        # 'flags':
+        #     ('vxlan flags is 0x08',
+        #      frozenset({"Ether() /  IP() /  VXLAN(flags=0x08) / Raw('\\x00' * 64)"}),
+        #
+        #      frozenset({"Ether() / IP() /  VXLAN(flags=0x80) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP() /  VXLAN(flags=0x00) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP() / VXLAN(flags=0x99) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP() /  VXLAN(flags=0x01) / Raw('\\x00' * 64)"
+        #                 })),
+        # END UNSUPPORTED PROPERTIES
+        "vni": (  # a 3-byte value
+            "vxlan vni is 0x112233",
+            frozenset({"Ether() / IP() / VXLAN(vni=0x112233) / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() / IP() / VXLAN(vni=0x112234) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / VXLAN(vni=0x123456) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / VXLAN(vni=0xaabbcc) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / VXLAN(vni=0x999999) / Raw('\\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.
+    """
+
+    possible_properties = {
+        # THE FOLLOWING PROPERTIES ARE UNSUPPORTED BY TESTPMD AT THE TIME OF WRITING.
+        # THEY CAN BE ENABLED ONCE TESTPMD SUPPORTS THEM
+        # 'rsvd0':
+        #     ('vxlan rsvd0 is 0x000000',
+        #      frozenset({"Ether() / IP() / VXLAN(reserved0=0) / Raw('\\x00' * 64)"}),
+        #
+        #      frozenset({"Ether() /  IP() / VXLAN(reserved0=1) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP() /  VXLAN(reserved0=2) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP() /  VXLAN(reserved0=3) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP()  / VXLAN(reserved0=4) / Raw('\\x00' * 64)"
+        #                 })),
+        # 'rsvd1':
+        #     ('vxlan rsvd1 is 0x00',
+        #      frozenset({"Ether() /  IP() /  VXLAN(reserved0=0) / Raw('\\x00' * 64)"}),
+        #
+        #      frozenset({"Ether() / IP() /  VXLAN(reserved0=1) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP() /  VXLAN(reserved0=2) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP() / VXLAN(reserved0=3) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP() /  VXLAN(reserved0=4) / Raw('\\x00' * 64)"
+        #                 })),
+        # 'flags':
+        #     ('vxlan flags is 0x08',
+        #      frozenset({"Ether() /  IP() /  VXLAN(flags=0x08) / Raw('\\x00' * 64)"}),
+        #
+        #      frozenset({"Ether() / IP() /  VXLAN(flags=0x80) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP() /  VXLAN(flags=0x00) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP() / VXLAN(flags=0x99) / Raw('\\x00' * 64)",
+        #                 "Ether() /  IP() /  VXLAN(flags=0x01) / Raw('\\x00' * 64)"
+        #                 })),
+        # 'protocol':
+        #     ('vxlan protocol is 0x01',
+        #      frozenset({"Ether() / IP() / VXLAN(NextProtocol=0x01) / Raw('\\x00' * 64)"}),
+        #
+        #      frozenset({"Ether() / IP() / VXLAN(NextProtocol=0x01) / Raw('\\x00' * 64)",
+        #                 "Ether() / IP() / VXLAN(NextProtocol=0x11) / Raw('\\x00' * 64)",
+        #                 "Ether() / IP() / VXLAN(NextProtocol=0x22) / Raw('\\x00' * 64)",
+        #                 "Ether() / IP() / VXLAN(NextProtocol=0x33) / Raw('\\x00' * 64)"
+        #                 })),
+        # END UNSUPPORTED PROPERTIES
+        "vni": (  # a 3-byte value
+            "vxlan vni is 0x112233",
+            frozenset({"Ether() / IP() / VXLAN(vni=0x112233) / Raw('\\x00' * 64)"}),
+            frozenset(
+                {
+                    "Ether() / IP() / VXLAN(vni=0x112234) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / VXLAN(vni=0x123456) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / VXLAN(vni=0xaabbcc) / Raw('\\x00' * 64)",
+                    "Ether() / IP() / VXLAN(vni=0x999999) / Raw('\\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,
+}