diff --git a/test_plans/ipfrag_test_plan.rst b/test_plans/ipfrag_test_plan.rst
index e30a5961..17ea5c3c 100644
--- a/test_plans/ipfrag_test_plan.rst
+++ b/test_plans/ipfrag_test_plan.rst
@@ -129,35 +129,4 @@ For each of them check that:
 
 #. Check number of output packets.
 #. Check header of each output packet: length, ID, fragment offset, flags.
-#. Check payload: size and contents as expected, not corrupted.
-
-
-
-Test Case 4: Throughput test
-============================
-
-The test report should provide the throughput rate measurements (in mpps and % of the line rate for 2x NIC ports)
-for the following input frame sizes: 64 bytes, 1518 bytes, 1519 bytes, 2K, 9k.
-
-The following configurations should be tested:
-
-|
-
-+----------+-------------------------+----------------------+
-|# of ports|  Socket/Core/HyperThread|Total # of sw threads |
-+----------+-------------------------+----------------------+
-|   2      |    1S/1C/1T             |          1           |
-+----------+-------------------------+----------------------+
-|   2      |    1S/1C/2T             |          2           |
-+----------+-------------------------+----------------------+
-|   2      |    1S/2C/1T             |          2           |
-+----------+-------------------------+----------------------+
-|   2      |    2S/1C/1T             |          2           |
-+----------+-------------------------+----------------------+
-
-|
-
-Command line::
-
-   ./x86_64-native-linuxapp-gcc/examples/dpdk-ip_fragmentation -c <LCOREMASK> -n 4 -- [-P] -p PORTMASK
-      -q <NUM_OF_PORTS_PER_THREAD>
+#. Check payload: size and contents as expected, not corrupted.
\ No newline at end of file
diff --git a/test_plans/perf_ipfrag_test_plan.rst b/test_plans/perf_ipfrag_test_plan.rst
new file mode 100644
index 00000000..96dca62d
--- /dev/null
+++ b/test_plans/perf_ipfrag_test_plan.rst
@@ -0,0 +1,141 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+   Copyright(c) 2011-2017 Intel Corporation
+
+======================
+IP fragmentation Tests
+======================
+
+The IP fragmentation results are produced using ''ip_fragmentation'' application.
+The test application should run with both IPv4 and IPv6 fragmentation.
+
+The suit support NIC: Intel® Ethernet 700 Series, Intel® Ethernet 800 Series, 82599 and igc driver NIC.
+
+Prerequisites
+=============
+
+1. Hardware requirements:
+
+   - For each CPU socket, each memory channel should be populated with at least 1x DIMM
+   - Board is populated with at least 2x 1GbE or 10GbE ports. Special PCIe restrictions may
+     be required for performance. For example, the following requirements should be
+     met for Intel 82599 NICs:
+
+       - NICs are plugged into PCIe Gen2 or Gen3 slots
+       - For PCIe Gen2 slots, the number of lanes should be 8x or higher
+       - A single port from each NIC should be used, so for 2x ports, 2x NICs should
+         be used
+
+   - NIC ports connected to traffic generator. It is assumed that the NIC ports
+     P0, P1, P2, P3 (as identified by the DPDK application) are connected to the
+     traffic generator ports TG0, TG1, TG2, TG3. The application-side port mask of
+     NIC ports P0, P1, P2, P3 is noted as PORTMASK in this section.
+     Traffic generator should support sending jumbo frames with size up to 9K.
+
+2. BIOS requirements:
+
+   - Intel Hyper-Threading Technology is ENABLED
+   - Hardware Prefetcher is DISABLED
+   - Adjacent Cache Line Prefetch is DISABLED
+   - Direct Cache Access is DISABLED
+
+3. Linux kernel requirements:
+
+   - Linux kernel has the following features enabled: huge page support, UIO, HPET
+   - Appropriate number of huge pages are reserved at kernel boot time
+   - The IDs of the hardware threads (logical cores) per each CPU socket can be
+     determined by parsing the file /proc/cpuinfo. The naming convention for the
+     logical cores is: C{x.y.z} = hyper-thread z of physical core y of CPU socket x,
+     with typical values of x = 0 .. 3, y = 0 .. 7, z = 0 .. 1. Logical cores
+     C{0.0.1} and C{0.0.1} should be avoided while executing the test, as they are
+     used by the Linux kernel for running regular processes.
+
+4. Software application requirements
+
+5. If using vfio the kernel must be >= 3.6+ and VT-d must be enabled in bios.When
+   using vfio, use the following commands to load the vfio driver and bind it
+   to the device under test::
+
+      modprobe vfio
+      modprobe vfio-pci
+      usertools/dpdk-devbind.py --bind=vfio-pci device_bus_id
+
+   - The test can be run with IPv4 package. The LPM table used for IPv4 packet routing is:
+
+   +-------+-------------------------------------+-----------+
+   |Entry #|LPM prefix (IP/length)               |Output port|
+   +-------+-------------------------------------+-----------+
+   |   0   |   100.10.0.0/16                     |     P2    |
+   +-------+-------------------------------------+-----------+
+   |   1   |   100.20.0.0/16                     |     P2    |
+   +-------+-------------------------------------+-----------+
+   |   2   |   100.30.0.0/16                     |     P0    |
+   +-------+-------------------------------------+-----------+
+   |   3   |   100.40.0.0/16                     |     P0    |
+   +-------+-------------------------------------+-----------+
+
+
+   - The test can be run with IPv6 package, which follows rules below.
+
+    - There is no support for Hop-by-Hop or Routing extension headers in the packet
+      to be fragmented. All other optional headers, which are not part of the
+      unfragmentable part of the IPv6 packet are supported.
+
+    - When a fragment is generated, its identification field in the IPv6
+      fragmentation extension header is set to 0. This is not RFC compliant, but
+      proper identification number generation is out of the scope of the application
+      and routers in an IPv6 path are not allowed to fragment in the first place...
+      Generating that identification number is the job of a proper IP stack.
+
+   - The LPM table used for IPv6 packet routing is:
+
+   +-------+-------------------------------------+-----------+
+   |Entry #|LPM prefix (IP/length)               |Output port|
+   +-------+-------------------------------------+-----------+
+   |   0   |   101:101:101:101:101:101:101:101/48|     P2    |
+   +-------+-------------------------------------+-----------+
+   |   1   |   201:101:101:101:101:101:101:101/48|     P2    |
+   +-------+-------------------------------------+-----------+
+   |   2   |   301:101:101:101:101:101:101:101/48|     P0    |
+   +-------+-------------------------------------+-----------+
+   |   3   |   401:101:101:101:101:101:101:101/48|     P0    |
+   +-------+-------------------------------------+-----------+
+
+   The following items are configured through the command line interface of the application:
+
+     - The set of one or several RX queues to be enabled for each NIC port
+     - The set of logical cores to execute the packet forwarding task
+     - Mapping of the NIC RX queues to logical cores handling them.
+
+6. Compile examples/ip_fragmentation::
+
+    meson configure -Dexamples=ip_fragmentation x86_64-native-linuxapp-gcc
+    ninja -C x86_64-native-linuxapp-gcc
+
+Test Case 1: Throughput test
+============================
+
+The test report should provide the throughput rate measurements (in mpps and % of the line rate for 2x NIC ports)
+for the following input frame sizes: 64 bytes, 1518 bytes, 1519 bytes, 2K, 9k.
+
+The following configurations should be tested:
+
+|
+
++----------+-------------------------+----------------------+
+|# of ports|  Socket/Core/HyperThread|Total # of sw threads |
++----------+-------------------------+----------------------+
+|   2      |    1S/1C/1T             |          1           |
++----------+-------------------------+----------------------+
+|   2      |    1S/1C/2T             |          2           |
++----------+-------------------------+----------------------+
+|   2      |    1S/2C/1T             |          2           |
++----------+-------------------------+----------------------+
+|   2      |    2S/1C/1T             |          2           |
++----------+-------------------------+----------------------+
+
+|
+
+Command line::
+
+   ./x86_64-native-linuxapp-gcc/examples/dpdk-ip_fragmentation -c <LCOREMASK> -n 4 -- [-P] -p PORTMASK
+      -q <NUM_OF_PORTS_PER_THREAD>
diff --git a/tests/TestSuite_ipfrag.py b/tests/TestSuite_ipfrag.py
index 95f99281..169df06b 100644
--- a/tests/TestSuite_ipfrag.py
+++ b/tests/TestSuite_ipfrag.py
@@ -372,27 +372,6 @@ class TestIpfrag(TestCase):
 
         self.dut.send_expect("^C", "#")
 
-    def test_perf_ipfrag_throughtput(self):
-        """
-        Performance test for 64, 1518, 1519, 2k and 9k.
-        """
-        sizes = [64, 1518, 1519, 2000, 9000]
-
-        tblheader = ["Ports", "S/C/T", "SW threads"]
-        for size in sizes:
-            tblheader.append("%dB Mpps" % size)
-            tblheader.append("%d" % size)
-
-        self.result_table_create(tblheader)
-
-        lcores = [("1S/1C/1T", 2), ("1S/1C/2T", 2), ("1S/2C/1T", 2), ("2S/1C/1T", 2)]
-        index = 1
-        for (lcore, numThr) in lcores:
-            self.benchmark(index, lcore, numThr, sizes)
-            index += 1
-
-        self.result_table_print()
-
     def tear_down(self):
         """
         Run after each test case.
diff --git a/tests/TestSuite_perf_ipfrag.py b/tests/TestSuite_perf_ipfrag.py
new file mode 100644
index 00000000..030aa378
--- /dev/null
+++ b/tests/TestSuite_perf_ipfrag.py
@@ -0,0 +1,386 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2010-2014 Intel Corporation
+#
+
+"""
+DPDK Test suite.
+Test IPv4 fragmentation features in DPDK.
+"""
+
+import os
+import re
+import string
+import time
+
+import framework.utils as utils
+from framework.packet import Packet
+from framework.pktgen import PacketGeneratorHelper
+from framework.settings import HEADER_SIZE
+from framework.test_case import TestCase
+
+lpm_table_ipv6 = [
+    "{{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, 48, P1}",
+    "{{2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, 48, P1}",
+    "{{3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, 48, P0}",
+    "{{4,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, 48, P0}",
+    "{{5,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, 48, P1}",
+    "{{6,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, 48, P1}",
+    "{{7,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, 48, P0}",
+    "{{8,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, 48, P0}",
+]
+
+
+class TestIpfrag(TestCase):
+    def portRepl(self, match):
+        """
+        Function to replace P([0123]) pattern in tables
+        """
+
+        portid = match.group(1)
+        self.verify(int(portid) in range(4), "invalid port id")
+        return "%s" % eval("P" + str(portid))
+
+    def set_up_all(self):
+        """
+        ip_fragmentation Prerequisites
+        """
+
+        # Based on h/w type, choose how many ports to use
+        self.ports = self.dut.get_ports()
+
+        # Verify that enough ports are available
+        self.verify(len(self.ports) >= 2, "Insufficient ports for testing")
+
+        self.ports_socket = self.dut.get_numa_id(self.ports[0])
+
+        # Verify that enough threads are available
+        cores = self.dut.get_core_list("1S/1C/1T")
+        self.verify(cores is not None, "Insufficient cores for speed testing")
+
+        global P0, P1
+        P0 = self.ports[0]
+        P1 = self.ports[1]
+
+        # make application
+        out = self.dut.build_dpdk_apps("examples/ip_fragmentation")
+        self.verify("Error" not in out, "compilation error 1")
+        self.verify("No such file" not in out, "compilation error 2")
+
+        self.eal_para = self.dut.create_eal_parameters(
+            socket=self.ports_socket, ports=self.ports
+        )
+        portmask = utils.create_mask([P0, P1])
+        numPortThread = len([P0, P1]) / len(cores)
+
+        # run ipv4_frag
+        self.app_ip_fragmentation_path = self.dut.apps_name["ip_fragmentation"]
+        self.dut.send_expect(
+            "%s %s -- -p %s -q %s"
+            % (
+                self.app_ip_fragmentation_path,
+                self.eal_para,
+                portmask,
+                int(numPortThread),
+            ),
+            "Link [Uu]p",
+            120,
+        )
+
+        time.sleep(2)
+        self.txItf = self.tester.get_interface(self.tester.get_local_port(P0))
+        self.rxItf = self.tester.get_interface(self.tester.get_local_port(P1))
+        self.dmac = self.dut.get_mac_address(P0)
+
+        # get dts output path
+        if self.logger.log_path.startswith(os.sep):
+            self.output_path = self.logger.log_path
+        else:
+            cur_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
+            self.output_path = os.sep.join([cur_path, self.logger.log_path])
+        # create an instance to set stream field setting
+        self.pktgen_helper = PacketGeneratorHelper()
+
+    def functional_check_ipv4(self, pkt_sizes, burst=1, flag=None):
+        """
+        Perform functional fragmentation checks.
+        """
+        for size in pkt_sizes[::burst]:
+            # simulate to set TG properties
+            if flag == "frag":
+                # do fragment, each packet max length 1518 - 18 - 20 = 1480
+                expPkts = int((size - HEADER_SIZE["eth"] - HEADER_SIZE["ip"]) / 1480)
+                if (size - HEADER_SIZE["eth"] - HEADER_SIZE["ip"]) % 1480:
+                    expPkts += 1
+                val = 0
+            elif flag == "nofrag":
+                expPkts = 0
+                val = 2
+            else:
+                expPkts = 1
+                val = 2
+
+            inst = self.tester.tcpdump_sniff_packets(intf=self.rxItf)
+            # send packet
+            for times in range(burst):
+                pkt_size = pkt_sizes[pkt_sizes.index(size) + times]
+                pkt = Packet(pkt_type="UDP", pkt_len=pkt_size)
+                pkt.config_layer("ether", {"dst": "%s" % self.dmac})
+                pkt.config_layer(
+                    "ipv4", {"dst": "100.20.0.1", "src": "1.2.3.4", "flags": val}
+                )
+                pkt.send_pkt(self.tester, tx_port=self.txItf)
+
+            # verify normal packet just by number, verify fragment packet by all elements
+            pkts = self.tester.load_tcpdump_sniff_packets(inst)
+            self.verify(
+                len(pkts) == expPkts,
+                "in functional_check_ipv4(): failed on forward packet size "
+                + str(size),
+            )
+            if flag == "frag":
+                idx = 1
+                for i in range(len(pkts)):
+                    pkt_id = pkts.strip_element_layer3("id", p_index=i)
+                    if idx == 1:
+                        prev_idx = pkt_id
+                    self.verify(
+                        prev_idx == pkt_id, "Fragmented packets index not match"
+                    )
+                    prev_idx = pkt_id
+
+                    # last flags should be 0
+                    flags = pkts.strip_element_layer3("flags", p_index=i)
+                    if idx == expPkts:
+                        self.verify(
+                            flags == 0, "Fragmented last packet flags not match"
+                        )
+                    else:
+                        self.verify(flags == 1, "Fragmented packets flags not match")
+
+                    # fragment offset should be correct
+                    frag = pkts.strip_element_layer3("frag", p_index=i)
+                    self.verify(
+                        (frag == ((idx - 1) * 185)), "Fragment packet frag not match"
+                    )
+                    idx += 1
+
+    def functional_check_ipv6(self, pkt_sizes, burst=1, flag=None, funtion=None):
+        """
+        Perform functional fragmentation checks.
+        """
+        for size in pkt_sizes[::burst]:
+            # simulate to set TG properties
+            if flag == "frag":
+                # each packet max len: 1518 - 18 (eth) - 40 (ipv6) - 8 (ipv6 ext hdr) = 1452
+                expPkts = int((size - HEADER_SIZE["eth"] - HEADER_SIZE["ipv6"]) / 1452)
+                if (size - HEADER_SIZE["eth"] - HEADER_SIZE["ipv6"]) % 1452:
+                    expPkts += 1
+                val = 0
+            else:
+                expPkts = 1
+                val = 2
+
+            inst = self.tester.tcpdump_sniff_packets(intf=self.rxItf)
+            # send packet
+            for times in range(burst):
+                pkt_size = pkt_sizes[pkt_sizes.index(size) + times]
+                pkt = Packet(pkt_type="IPv6_UDP", pkt_len=pkt_size)
+                pkt.config_layer("ether", {"dst": "%s" % self.dmac})
+                pkt.config_layer(
+                    "ipv6",
+                    {
+                        "dst": "201:101:101:101:101:101:101:101",
+                        "src": "ee80:ee80:ee80:ee80:ee80:ee80:ee80:ee80",
+                    },
+                )
+                pkt.send_pkt(self.tester, tx_port=self.txItf)
+
+            # verify normal packet just by number, verify fragment packet by all elements
+            pkts = self.tester.load_tcpdump_sniff_packets(inst)
+            self.verify(
+                len(pkts) == expPkts,
+                "In functional_check_ipv6(): failed on forward packet size "
+                + str(size),
+            )
+            if flag == "frag":
+                idx = 1
+                for i in range(len(pkts)):
+                    pkt_id = pkts.strip_element_layer4("id", p_index=i)
+                    if idx == 1:
+                        prev_idx = pkt_id
+                    self.verify(
+                        prev_idx == pkt_id, "Fragmented packets index not match"
+                    )
+                    prev_idx = pkt_id
+
+                    # last flags should be 0
+                    flags = pkts.strip_element_layer4("m", p_index=i)
+                    if idx == expPkts:
+                        self.verify(
+                            flags == 0, "Fragmented last packet flags not match"
+                        )
+                    else:
+                        self.verify(flags == 1, "Fragmented packets flags not match")
+
+                    # fragment offset should be correct
+                    frag = pkts.strip_element_layer4("offset", p_index=i)
+                    self.verify(
+                        (frag == int((idx - 1) * 181)), "Fragment packet frag not match"
+                    )
+                    idx += 1
+
+    def set_up(self):
+        """
+        Run before each test case.
+        """
+        self.tester.send_expect(
+            "ifconfig %s mtu 9200"
+            % self.tester.get_interface(self.tester.get_local_port(P0)),
+            "#",
+        )
+        self.tester.send_expect(
+            "ifconfig %s mtu 9200"
+            % self.tester.get_interface(self.tester.get_local_port(P1)),
+            "#",
+        )
+
+    def benchmark(self, index, lcore, num_pthreads, size_list):
+        """
+        Just Test IPv4 Throughput for selected parameters.
+        """
+
+        Bps = dict()
+        Pps = dict()
+        Pct = dict()
+
+        if int(lcore[0]) == 1:
+            eal_param = self.dut.create_eal_parameters(
+                cores=lcore, socket=self.ports_socket, ports=self.ports
+            )
+        else:
+            eal_param = self.dut.create_eal_parameters(cores=lcore, ports=self.ports)
+        portmask = utils.create_mask([P0, P1])
+        self.dut.send_expect("^c", "# ", 120)
+        self.dut.send_expect(
+            "%s %s -- -p %s -q %s"
+            % (self.app_ip_fragmentation_path, eal_param, portmask, num_pthreads),
+            "IP_FRAG:",
+            120,
+        )
+        result = [2, lcore, num_pthreads]
+        for size in size_list:
+            dmac = self.dut.get_mac_address(P0)
+            flows_p0 = [
+                'Ether(dst="%s")/IP(src="1.2.3.4", dst="100.10.0.1", flags=0)/("X"*%d)'
+                % (dmac, size - 38),
+                'Ether(dst="%s")/IP(src="1.2.3.4", dst="100.20.0.1", flags=0)/("X"*%d)'
+                % (dmac, size - 38),
+                'Ether(dst="%s")/IPv6(dst="101:101:101:101:101:101:101:101",src="ee80:ee80:ee80:ee80:ee80:ee80:ee80:ee80")/Raw(load="X"*%d)'
+                % (dmac, size - 58),
+                'Ether(dst="%s")/IPv6(dst="201:101:101:101:101:101:101:101",src="ee80:ee80:ee80:ee80:ee80:ee80:ee80:ee80")/Raw(load="X"*%d)'
+                % (dmac, size - 58),
+            ]
+
+            # reserved for rx/tx bidirection test
+            dmac = self.dut.get_mac_address(P1)
+            flows_p1 = [
+                'Ether(dst="%s")/IP(src="1.2.3.4", dst="100.30.0.1", flags=0)/("X"*%d)'
+                % (dmac, size - 38),
+                'Ether(dst="%s")/IP(src="1.2.3.4", dst="100.40.0.1", flags=0)/("X"*%d)'
+                % (dmac, size - 38),
+                'Ether(dst="%s")/IPv6(dst="301:101:101:101:101:101:101:101",src="ee80:ee80:ee80:ee80:ee80:ee80:ee80:ee80")/Raw(load="X"*%d)'
+                % (dmac, size - 58),
+                'Ether(dst="%s")/IPv6(dst="401:101:101:101:101:101:101:101",src="ee80:ee80:ee80:ee80:ee80:ee80:ee80:ee80")/Raw(load="X"*%d)'
+                % (dmac, size - 58),
+            ]
+            flow_len = len(flows_p0)
+            tgenInput = []
+            for i in range(flow_len):
+
+                pcap0 = os.sep.join([self.output_path, "p0_{}.pcap".format(i)])
+                self.tester.scapy_append('wrpcap("%s", [%s])' % (pcap0, flows_p0[i]))
+                pcap1 = os.sep.join([self.output_path, "p1_{}.pcap".format(i)])
+                self.tester.scapy_append('wrpcap("%s", [%s])' % (pcap1, flows_p1[i]))
+                self.tester.scapy_execute()
+
+                tgenInput.append(
+                    (
+                        self.tester.get_local_port(P0),
+                        self.tester.get_local_port(P1),
+                        pcap0,
+                    )
+                )
+                tgenInput.append(
+                    (
+                        self.tester.get_local_port(P1),
+                        self.tester.get_local_port(P0),
+                        pcap1,
+                    )
+                )
+
+            factor = (size + 1517) / 1518
+            # wireSpd = 2 * 10000.0 / ((20 + size) * 8)
+
+            # clear streams before add new streams
+            self.tester.pktgen.clear_streams()
+            # run packet generator
+            streams = self.pktgen_helper.prepare_stream_from_tginput(
+                tgenInput, 100, None, self.tester.pktgen
+            )
+            Bps[str(size)], Pps[str(size)] = self.tester.pktgen.measure_throughput(
+                stream_ids=streams
+            )
+
+            self.verify(Pps[str(size)] > 0, "No traffic detected")
+            Pps[str(size)] *= 1.0 / factor / 1000000
+            Pct[str(size)] = (1.0 * Bps[str(size)] * 100) / (2 * 10000000000)
+
+            result.append(Pps[str(size)])
+            result.append(Pct[str(size)])
+
+        self.result_table_add(result)
+
+        self.dut.send_expect("^C", "#")
+
+    def test_perf_ipfrag_throughtput(self):
+        """
+        Performance test for 64, 1518, 1519, 2k and 9k.
+        """
+        sizes = [64, 1518, 1519, 2000, 9000]
+
+        tblheader = ["Ports", "S/C/T", "SW threads"]
+        for size in sizes:
+            tblheader.append("%dB Mpps" % size)
+            tblheader.append("%d" % size)
+
+        self.result_table_create(tblheader)
+
+        lcores = [("1S/1C/1T", 2), ("1S/1C/2T", 2), ("1S/2C/1T", 2), ("2S/1C/1T", 2)]
+        index = 1
+        for (lcore, numThr) in lcores:
+            self.benchmark(index, lcore, numThr, sizes)
+            index += 1
+
+        self.result_table_print()
+
+    def tear_down(self):
+        """
+        Run after each test case.
+        """
+        self.tester.send_expect(
+            "ifconfig %s mtu 1500"
+            % self.tester.get_interface(self.tester.get_local_port(P0)),
+            "#",
+        )
+        self.tester.send_expect(
+            "ifconfig %s mtu 1500"
+            % self.tester.get_interface(self.tester.get_local_port(P1)),
+            "#",
+        )
+
+    def tear_down_all(self):
+        """
+        Run after each test suite.
+        """
+        self.dut.send_expect("^C", "#")
+        pass
