wireguard: add async mode for encryption packets
[vpp.git] / test / test_reassembly.py
index ace11f7..faec5f4 100644 (file)
@@ -1,6 +1,5 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 
-import six
 import unittest
 from random import shuffle, choice, randrange
 
 import unittest
 from random import shuffle, choice, randrange
 
@@ -11,7 +10,8 @@ from scapy.packet import Raw
 from scapy.layers.l2 import Ether, GRE
 from scapy.layers.inet import IP, UDP, ICMP
 from scapy.layers.inet6 import HBHOptUnknown, ICMPv6ParamProblem,\
 from scapy.layers.l2 import Ether, GRE
 from scapy.layers.inet import IP, UDP, ICMP
 from scapy.layers.inet6 import HBHOptUnknown, ICMPv6ParamProblem,\
-    ICMPv6TimeExceeded, IPv6, IPv6ExtHdrFragment, IPv6ExtHdrHopByHop
+    ICMPv6TimeExceeded, IPv6, IPv6ExtHdrFragment,\
+    IPv6ExtHdrHopByHop, IPv6ExtHdrDestOpt, PadN, ICMPv6EchoRequest
 from framework import VppTestCase, VppTestRunner
 from util import ppp, ppc, fragment_rfc791, fragment_rfc8200
 from vpp_gre_interface import VppGreInterface
 from framework import VppTestCase, VppTestRunner
 from util import ppp, ppc, fragment_rfc791, fragment_rfc8200
 from vpp_gre_interface import VppGreInterface
@@ -22,9 +22,6 @@ from vpp_papi import VppEnum
 # 35 is enough to have >257 400-byte fragments
 test_packet_count = 35
 
 # 35 is enough to have >257 400-byte fragments
 test_packet_count = 35
 
-# number of workers used for multi-worker test cases
-worker_count = 3
-
 
 class TestIPv4Reassembly(VppTestCase):
     """ IPv4 Reassembly """
 
 class TestIPv4Reassembly(VppTestCase):
     """ IPv4 Reassembly """
@@ -61,7 +58,7 @@ class TestIPv4Reassembly(VppTestCase):
         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
                                     max_reassembly_length=1000,
                                     expire_walk_interval_ms=10)
         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
                                     max_reassembly_length=1000,
                                     expire_walk_interval_ms=10)
-        self.sleep(.25)
+        self.virtual_sleep(.25)
         self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
                                     max_reassembly_length=1000,
                                     expire_walk_interval_ms=10000)
         self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
                                     max_reassembly_length=1000,
                                     expire_walk_interval_ms=10000)
@@ -95,7 +92,7 @@ class TestIPv4Reassembly(VppTestCase):
     def create_fragments(cls):
         infos = cls._packet_infos
         cls.pkt_infos = []
     def create_fragments(cls):
         infos = cls._packet_infos
         cls.pkt_infos = []
-        for index, info in six.iteritems(infos):
+        for index, info in infos.items():
             p = info.data
             # cls.logger.debug(ppp("Packet:",
             #                      p.__class__(scapy.compat.raw(p))))
             p = info.data
             # cls.logger.debug(ppp("Packet:",
             #                      p.__class__(scapy.compat.raw(p))))
@@ -171,6 +168,21 @@ class TestIPv4Reassembly(VppTestCase):
         self.verify_capture(packets)
         self.src_if.assert_nothing_captured()
 
         self.verify_capture(packets)
         self.src_if.assert_nothing_captured()
 
+    def test_verify_clear_trace_mid_reassembly(self):
+        """ verify clear trace works mid-reassembly """
+
+        self.pg_enable_capture()
+        self.src_if.add_stream(self.fragments_200[0:-1])
+        self.pg_start()
+
+        self.logger.debug(self.vapi.cli("show trace"))
+        self.vapi.cli("clear trace")
+
+        self.src_if.add_stream(self.fragments_200[-1])
+        self.pg_start()
+        packets = self.dst_if.get_capture(len(self.pkt_infos))
+        self.verify_capture(packets)
+
     def test_reversed(self):
         """ reverse order reassembly """
 
     def test_reversed(self):
         """ reverse order reassembly """
 
@@ -469,7 +481,7 @@ Ethernet-Payload.IPv4-Packet.IPv4-Header.Fragment-Offset; Test-case: 5737'''
         self.src_if.add_stream(fragments)
         self.pg_start()
 
         self.src_if.add_stream(fragments)
         self.pg_start()
 
-        self.sleep(.25, "wait before sending rest of fragments")
+        self.virtual_sleep(.25, "wait before sending rest of fragments")
 
         self.src_if.add_stream(fragments2)
         self.pg_start()
 
         self.src_if.add_stream(fragments2)
         self.pg_start()
@@ -528,7 +540,7 @@ class TestIPv4SVReassembly(VppTestCase):
             max_reassembly_length=1000,
             type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
             expire_walk_interval_ms=10)
             max_reassembly_length=1000,
             type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
             expire_walk_interval_ms=10)
-        self.sleep(.25)
+        self.virtual_sleep(.25)
         self.vapi.ip_reassembly_set(
             timeout_ms=1000000, max_reassemblies=1000,
             max_reassembly_length=1000,
         self.vapi.ip_reassembly_set(
             timeout_ms=1000000, max_reassemblies=1000,
             max_reassembly_length=1000,
@@ -589,6 +601,42 @@ class TestIPv4SVReassembly(VppTestCase):
             self.assertEqual(sent[IP].dst, recvd[IP].dst)
             self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
 
             self.assertEqual(sent[IP].dst, recvd[IP].dst)
             self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
 
+    def test_verify_clear_trace_mid_reassembly(self):
+        """ verify clear trace works mid-reassembly """
+        payload_len = 1000
+        payload = ""
+        counter = 0
+        while len(payload) < payload_len:
+            payload += "%u " % counter
+            counter += 1
+
+        p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
+             IP(id=1, src=self.src_if.remote_ip4,
+                dst=self.dst_if.remote_ip4) /
+             UDP(sport=1234, dport=5678) /
+             Raw(payload))
+        fragments = fragment_rfc791(p, payload_len/4)
+
+        self.pg_enable_capture()
+        self.src_if.add_stream(fragments[1])
+        self.pg_start()
+
+        self.logger.debug(self.vapi.cli("show trace"))
+        self.vapi.cli("clear trace")
+
+        self.pg_enable_capture()
+        self.src_if.add_stream(fragments[0])
+        self.pg_start()
+        self.dst_if.get_capture(2)
+
+        self.logger.debug(self.vapi.cli("show trace"))
+        self.vapi.cli("clear trace")
+
+        self.pg_enable_capture()
+        self.src_if.add_stream(fragments[2:])
+        self.pg_start()
+        self.dst_if.get_capture(len(fragments[2:]))
+
     def test_timeout(self):
         """ reassembly timeout """
         payload_len = 1000
     def test_timeout(self):
         """ reassembly timeout """
         payload_len = 1000
@@ -625,7 +673,7 @@ class TestIPv4SVReassembly(VppTestCase):
             self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
 
         # wait for cleanup
             self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
 
         # wait for cleanup
-        self.sleep(.25, "wait before sending rest of fragments")
+        self.virtual_sleep(.25, "wait before sending rest of fragments")
 
         # send rest of fragments - shouldn't be forwarded
         self.pg_enable_capture()
 
         # send rest of fragments - shouldn't be forwarded
         self.pg_enable_capture()
@@ -670,16 +718,82 @@ class TestIPv4SVReassembly(VppTestCase):
             self.assertEqual(sent[IP].dst, recvd[IP].dst)
             self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
 
             self.assertEqual(sent[IP].dst, recvd[IP].dst)
             self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
 
+    def send_mixed_and_verify_capture(self, traffic):
+        stream = []
+        for t in traffic:
+            for c in range(t['count']):
+                stream.append(
+                    (Ether(dst=self.src_if.local_mac,
+                           src=self.src_if.remote_mac) /
+                     IP(id=self.counter,
+                        flags=t['flags'],
+                        src=self.src_if.remote_ip4,
+                        dst=self.dst_if.remote_ip4) /
+                     UDP(sport=1234, dport=5678) /
+                     Raw("abcdef")))
+                self.counter = self.counter + 1
+
+        self.pg_enable_capture()
+        self.src_if.add_stream(stream)
+        self.pg_start()
+        self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
+        self.logger.debug(self.vapi.ppcli("show buffers"))
+        self.logger.debug(self.vapi.ppcli("show trace"))
+        self.dst_if.get_capture(len(stream))
+
+    def test_mixed(self):
+        """ mixed traffic correctly passes through SVR """
+        self.counter = 1
+
+        self.send_mixed_and_verify_capture([{'count': 1, 'flags': ''}])
+        self.send_mixed_and_verify_capture([{'count': 2, 'flags': ''}])
+        self.send_mixed_and_verify_capture([{'count': 3, 'flags': ''}])
+        self.send_mixed_and_verify_capture([{'count': 8, 'flags': ''}])
+        self.send_mixed_and_verify_capture([{'count': 257, 'flags': ''}])
+
+        self.send_mixed_and_verify_capture([{'count': 1, 'flags': 'MF'}])
+        self.send_mixed_and_verify_capture([{'count': 2, 'flags': 'MF'}])
+        self.send_mixed_and_verify_capture([{'count': 3, 'flags': 'MF'}])
+        self.send_mixed_and_verify_capture([{'count': 8, 'flags': 'MF'}])
+        self.send_mixed_and_verify_capture([{'count': 257, 'flags': 'MF'}])
+
+        self.send_mixed_and_verify_capture(
+            [{'count': 1, 'flags': ''}, {'count': 1, 'flags': 'MF'}])
+        self.send_mixed_and_verify_capture(
+            [{'count': 2, 'flags': ''}, {'count': 2, 'flags': 'MF'}])
+        self.send_mixed_and_verify_capture(
+            [{'count': 3, 'flags': ''}, {'count': 3, 'flags': 'MF'}])
+        self.send_mixed_and_verify_capture(
+            [{'count': 8, 'flags': ''}, {'count': 8, 'flags': 'MF'}])
+        self.send_mixed_and_verify_capture(
+            [{'count': 129, 'flags': ''}, {'count': 129, 'flags': 'MF'}])
+
+        self.send_mixed_and_verify_capture(
+            [{'count': 1, 'flags': ''}, {'count': 1, 'flags': 'MF'},
+             {'count': 1, 'flags': ''}, {'count': 1, 'flags': 'MF'}])
+        self.send_mixed_and_verify_capture(
+            [{'count': 2, 'flags': ''}, {'count': 2, 'flags': 'MF'},
+             {'count': 2, 'flags': ''}, {'count': 2, 'flags': 'MF'}])
+        self.send_mixed_and_verify_capture(
+            [{'count': 3, 'flags': ''}, {'count': 3, 'flags': 'MF'},
+             {'count': 3, 'flags': ''}, {'count': 3, 'flags': 'MF'}])
+        self.send_mixed_and_verify_capture(
+            [{'count': 8, 'flags': ''}, {'count': 8, 'flags': 'MF'},
+             {'count': 8, 'flags': ''}, {'count': 8, 'flags': 'MF'}])
+        self.send_mixed_and_verify_capture(
+            [{'count': 65, 'flags': ''}, {'count': 65, 'flags': 'MF'},
+             {'count': 65, 'flags': ''}, {'count': 65, 'flags': 'MF'}])
+
 
 class TestIPv4MWReassembly(VppTestCase):
     """ IPv4 Reassembly (multiple workers) """
 
 class TestIPv4MWReassembly(VppTestCase):
     """ IPv4 Reassembly (multiple workers) """
-    worker_config = "workers %d" % worker_count
+    vpp_worker_count = 3
 
     @classmethod
     def setUpClass(cls):
         super(TestIPv4MWReassembly, cls).setUpClass()
 
 
     @classmethod
     def setUpClass(cls):
         super(TestIPv4MWReassembly, cls).setUpClass()
 
-        cls.create_pg_interfaces(range(worker_count+1))
+        cls.create_pg_interfaces(range(cls.vpp_worker_count+1))
         cls.src_if = cls.pg0
         cls.send_ifs = cls.pg_interfaces[:-1]
         cls.dst_if = cls.pg_interfaces[-1]
         cls.src_if = cls.pg0
         cls.send_ifs = cls.pg_interfaces[:-1]
         cls.dst_if = cls.pg_interfaces[-1]
@@ -712,7 +826,7 @@ class TestIPv4MWReassembly(VppTestCase):
         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
                                     max_reassembly_length=1000,
                                     expire_walk_interval_ms=10)
         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
                                     max_reassembly_length=1000,
                                     expire_walk_interval_ms=10)
-        self.sleep(.25)
+        self.virtual_sleep(.25)
         self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
                                     max_reassembly_length=1000,
                                     expire_walk_interval_ms=10000)
         self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
                                     max_reassembly_length=1000,
                                     expire_walk_interval_ms=10000)
@@ -745,7 +859,7 @@ class TestIPv4MWReassembly(VppTestCase):
     def create_fragments(cls):
         infos = cls._packet_infos
         cls.pkt_infos = []
     def create_fragments(cls):
         infos = cls._packet_infos
         cls.pkt_infos = []
-        for index, info in six.iteritems(infos):
+        for index, info in infos.items():
             p = info.data
             # cls.logger.debug(ppp("Packet:",
             #                      p.__class__(scapy.compat.raw(p))))
             p = info.data
             # cls.logger.debug(ppp("Packet:",
             #                      p.__class__(scapy.compat.raw(p))))
@@ -792,7 +906,7 @@ class TestIPv4MWReassembly(VppTestCase):
                             "Packet with packet_index %d not received" % index)
 
     def send_packets(self, packets):
                             "Packet with packet_index %d not received" % index)
 
     def send_packets(self, packets):
-        for counter in range(worker_count):
+        for counter in range(self.vpp_worker_count):
             if 0 == len(packets[counter]):
                 continue
             send_if = self.send_ifs[counter]
             if 0 == len(packets[counter]):
                 continue
             send_if = self.send_ifs[counter]
@@ -808,19 +922,19 @@ class TestIPv4MWReassembly(VppTestCase):
         # in first wave we send fragments which don't start at offset 0
         # then we send fragments with offset 0 on a different thread
         # then the rest of packets on a random thread
         # in first wave we send fragments which don't start at offset 0
         # then we send fragments with offset 0 on a different thread
         # then the rest of packets on a random thread
-        first_packets = [[] for n in range(worker_count)]
-        second_packets = [[] for n in range(worker_count)]
-        rest_of_packets = [[] for n in range(worker_count)]
+        first_packets = [[] for n in range(self.vpp_worker_count)]
+        second_packets = [[] for n in range(self.vpp_worker_count)]
+        rest_of_packets = [[] for n in range(self.vpp_worker_count)]
         for (_, p) in self.pkt_infos:
         for (_, p) in self.pkt_infos:
-            wi = randrange(worker_count)
+            wi = randrange(self.vpp_worker_count)
             second_packets[wi].append(p[0])
             if len(p) <= 1:
                 continue
             wi2 = wi
             while wi2 == wi:
             second_packets[wi].append(p[0])
             if len(p) <= 1:
                 continue
             wi2 = wi
             while wi2 == wi:
-                wi2 = randrange(worker_count)
+                wi2 = randrange(self.vpp_worker_count)
             first_packets[wi2].append(p[1])
             first_packets[wi2].append(p[1])
-            wi3 = randrange(worker_count)
+            wi3 = randrange(self.vpp_worker_count)
             rest_of_packets[wi3].extend(p[2:])
 
         self.pg_enable_capture()
             rest_of_packets[wi3].extend(p[2:])
 
         self.pg_enable_capture()
@@ -884,7 +998,7 @@ class TestIPv6Reassembly(VppTestCase):
         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
                                     max_reassembly_length=1000,
                                     expire_walk_interval_ms=10, is_ip6=1)
         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
                                     max_reassembly_length=1000,
                                     expire_walk_interval_ms=10, is_ip6=1)
-        self.sleep(.25)
+        self.virtual_sleep(.25)
         self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
                                     max_reassembly_length=1000,
                                     expire_walk_interval_ms=10000, is_ip6=1)
         self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
                                     max_reassembly_length=1000,
                                     expire_walk_interval_ms=10000, is_ip6=1)
@@ -920,7 +1034,7 @@ class TestIPv6Reassembly(VppTestCase):
     def create_fragments(cls):
         infos = cls._packet_infos
         cls.pkt_infos = []
     def create_fragments(cls):
         infos = cls._packet_infos
         cls.pkt_infos = []
-        for index, info in six.iteritems(infos):
+        for index, info in infos.items():
             p = info.data
             # cls.logger.debug(ppp("Packet:",
             #                      p.__class__(scapy.compat.raw(p))))
             p = info.data
             # cls.logger.debug(ppp("Packet:",
             #                      p.__class__(scapy.compat.raw(p))))
@@ -1008,6 +1122,21 @@ class TestIPv6Reassembly(VppTestCase):
         self.src_if.assert_nothing_captured()
         self.dst_if.assert_nothing_captured()
 
         self.src_if.assert_nothing_captured()
         self.dst_if.assert_nothing_captured()
 
+    def test_verify_clear_trace_mid_reassembly(self):
+        """ verify clear trace works mid-reassembly """
+
+        self.pg_enable_capture()
+        self.src_if.add_stream(self.fragments_400[0:-1])
+        self.pg_start()
+
+        self.logger.debug(self.vapi.cli("show trace"))
+        self.vapi.cli("clear trace")
+
+        self.src_if.add_stream(self.fragments_400[-1])
+        self.pg_start()
+        packets = self.dst_if.get_capture(len(self.pkt_infos))
+        self.verify_capture(packets)
+
     def test_reversed(self):
         """ reverse order reassembly """
 
     def test_reversed(self):
         """ reverse order reassembly """
 
@@ -1209,7 +1338,7 @@ class TestIPv6Reassembly(VppTestCase):
         self.src_if.add_stream(fragments)
         self.pg_start()
 
         self.src_if.add_stream(fragments)
         self.pg_start()
 
-        self.sleep(.25, "wait before sending rest of fragments")
+        self.virtual_sleep(.25, "wait before sending rest of fragments")
 
         self.src_if.add_stream(fragments2)
         self.pg_start()
 
         self.src_if.add_stream(fragments2)
         self.pg_start()
@@ -1247,24 +1376,31 @@ class TestIPv6Reassembly(VppTestCase):
 
     def test_missing_upper(self):
         """ missing upper layer """
 
     def test_missing_upper(self):
         """ missing upper layer """
+        optdata = '\x00' * 100
         p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
              IPv6(src=self.src_if.remote_ip6,
                   dst=self.src_if.local_ip6) /
         p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
              IPv6(src=self.src_if.remote_ip6,
                   dst=self.src_if.local_ip6) /
-             UDP(sport=1234, dport=5678) /
-             Raw())
-        self.extend_packet(p, 1000, self.padding)
-        fragments = fragment_rfc8200(p, 1, 500)
-        bad_fragment = p.__class__(scapy.compat.raw(fragments[1]))
-        bad_fragment[IPv6ExtHdrFragment].nh = 59
-        bad_fragment[IPv6ExtHdrFragment].offset = 0
+             IPv6ExtHdrFragment(m=1) /
+             IPv6ExtHdrDestOpt(nh=17, options=PadN(optdata='\101' * 255) /
+             PadN(optdata='\102'*255)))
+
         self.pg_enable_capture()
         self.pg_enable_capture()
-        self.src_if.add_stream([bad_fragment])
+        self.src_if.add_stream([p])
         self.pg_start()
         pkts = self.src_if.get_capture(expected_count=1)
         icmp = pkts[0]
         self.assertIn(ICMPv6ParamProblem, icmp)
         self.assert_equal(icmp[ICMPv6ParamProblem].code, 3, "ICMP code")
 
         self.pg_start()
         pkts = self.src_if.get_capture(expected_count=1)
         icmp = pkts[0]
         self.assertIn(ICMPv6ParamProblem, icmp)
         self.assert_equal(icmp[ICMPv6ParamProblem].code, 3, "ICMP code")
 
+    def test_truncated_fragment(self):
+        """ truncated fragment """
+        pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
+               IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6,
+                    nh=44, plen=2) /
+               IPv6ExtHdrFragment(nh=6))
+
+        self.send_and_assert_no_replies(self.pg0, [pkt], self.pg0)
+
     def test_invalid_frag_size(self):
         """ fragment size not a multiple of 8 """
         p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
     def test_invalid_frag_size(self):
         """ fragment size not a multiple of 8 """
         p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
@@ -1303,16 +1439,82 @@ class TestIPv6Reassembly(VppTestCase):
         self.assertIn(ICMPv6ParamProblem, icmp)
         self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
 
         self.assertIn(ICMPv6ParamProblem, icmp)
         self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
 
+    def test_atomic_fragment(self):
+        """ IPv6 atomic fragment """
+        pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
+               IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6,
+                    nh=44, plen=65535) /
+               IPv6ExtHdrFragment(offset=8191, m=1, res1=0xFF, res2=0xFF,
+                                  nh=255, id=0xffff)/('X'*1452))
+
+        rx = self.send_and_expect(self.pg0, [pkt], self.pg0)
+        self.assertIn(ICMPv6ParamProblem, rx[0])
+
+    def test_truncated_fragment(self):
+        """ IPv6 truncated fragment header """
+        pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
+               IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6,
+                    nh=44, plen=2) /
+               IPv6ExtHdrFragment(nh=6))
+
+        self.send_and_assert_no_replies(self.pg0, [pkt])
+
+        pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
+               IPv6(src=self.pg0.remote_ip6, dst=self.pg0.remote_ip6) /
+               ICMPv6EchoRequest())
+        rx = self.send_and_expect(self.pg0, [pkt], self.pg0)
+
+    def test_one_fragment(self):
+        """ whole packet in one fragment processed independently """
+        pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
+               IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) /
+               ICMPv6EchoRequest()/Raw('X' * 1600))
+        frags = fragment_rfc8200(pkt, 1, 400)
+
+        # send a fragment with known id
+        self.send_and_assert_no_replies(self.pg0, [frags[0]])
+
+        # send an atomic fragment with same id - should be reassembled
+        pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
+               IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) /
+               IPv6ExtHdrFragment(id=1)/ICMPv6EchoRequest())
+        rx = self.send_and_expect(self.pg0, [pkt], self.pg0)
+        self.assertNotIn(IPv6ExtHdrFragment, rx)
+
+        # now finish the original reassembly, this should still be possible
+        rx = self.send_and_expect(self.pg0, frags[1:], self.pg0, n_rx=1)
+        self.assertNotIn(IPv6ExtHdrFragment, rx)
+
+    def test_bunch_of_fragments(self):
+        """ valid fragments followed by rogue fragments and atomic fragment"""
+        pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
+               IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) /
+               ICMPv6EchoRequest()/Raw('X' * 1600))
+        frags = fragment_rfc8200(pkt, 1, 400)
+        self.send_and_expect(self.pg0, frags, self.pg0, n_rx=1)
+
+        inc_frag = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
+                    IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) /
+                    IPv6ExtHdrFragment(id=1, nh=58, offset=608)/Raw('X'*308))
+
+        self.send_and_assert_no_replies(self.pg0, inc_frag*604)
+
+        pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
+               IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) /
+               IPv6ExtHdrFragment(id=1)/ICMPv6EchoRequest())
+        rx = self.send_and_expect(self.pg0, [pkt], self.pg0)
+        self.assertNotIn(IPv6ExtHdrFragment, rx)
+
 
 class TestIPv6MWReassembly(VppTestCase):
     """ IPv6 Reassembly (multiple workers) """
 
 class TestIPv6MWReassembly(VppTestCase):
     """ IPv6 Reassembly (multiple workers) """
-    worker_config = "workers %d" % worker_count
+    vpp_worker_count = 3
 
     @classmethod
     def setUpClass(cls):
         super(TestIPv6MWReassembly, cls).setUpClass()
 
 
     @classmethod
     def setUpClass(cls):
         super(TestIPv6MWReassembly, cls).setUpClass()
 
-        cls.create_pg_interfaces(range(worker_count+1))
+        cls.create_pg_interfaces(range(cls.vpp_worker_count+1))
         cls.src_if = cls.pg0
         cls.send_ifs = cls.pg_interfaces[:-1]
         cls.dst_if = cls.pg_interfaces[-1]
         cls.src_if = cls.pg0
         cls.send_ifs = cls.pg_interfaces[:-1]
         cls.dst_if = cls.pg_interfaces[-1]
@@ -1345,7 +1547,7 @@ class TestIPv6MWReassembly(VppTestCase):
         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
                                     max_reassembly_length=1000,
                                     expire_walk_interval_ms=10, is_ip6=1)
         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
                                     max_reassembly_length=1000,
                                     expire_walk_interval_ms=10, is_ip6=1)
-        self.sleep(.25)
+        self.virtual_sleep(.25)
         self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
                                     max_reassembly_length=1000,
                                     expire_walk_interval_ms=1000, is_ip6=1)
         self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
                                     max_reassembly_length=1000,
                                     expire_walk_interval_ms=1000, is_ip6=1)
@@ -1378,7 +1580,7 @@ class TestIPv6MWReassembly(VppTestCase):
     def create_fragments(cls):
         infos = cls._packet_infos
         cls.pkt_infos = []
     def create_fragments(cls):
         infos = cls._packet_infos
         cls.pkt_infos = []
-        for index, info in six.iteritems(infos):
+        for index, info in infos.items():
             p = info.data
             # cls.logger.debug(ppp("Packet:",
             #                      p.__class__(scapy.compat.raw(p))))
             p = info.data
             # cls.logger.debug(ppp("Packet:",
             #                      p.__class__(scapy.compat.raw(p))))
@@ -1425,7 +1627,7 @@ class TestIPv6MWReassembly(VppTestCase):
                             "Packet with packet_index %d not received" % index)
 
     def send_packets(self, packets):
                             "Packet with packet_index %d not received" % index)
 
     def send_packets(self, packets):
-        for counter in range(worker_count):
+        for counter in range(self.vpp_worker_count):
             if 0 == len(packets[counter]):
                 continue
             send_if = self.send_ifs[counter]
             if 0 == len(packets[counter]):
                 continue
             send_if = self.send_ifs[counter]
@@ -1441,19 +1643,19 @@ class TestIPv6MWReassembly(VppTestCase):
         # in first wave we send fragments which don't start at offset 0
         # then we send fragments with offset 0 on a different thread
         # then the rest of packets on a random thread
         # in first wave we send fragments which don't start at offset 0
         # then we send fragments with offset 0 on a different thread
         # then the rest of packets on a random thread
-        first_packets = [[] for n in range(worker_count)]
-        second_packets = [[] for n in range(worker_count)]
-        rest_of_packets = [[] for n in range(worker_count)]
+        first_packets = [[] for n in range(self.vpp_worker_count)]
+        second_packets = [[] for n in range(self.vpp_worker_count)]
+        rest_of_packets = [[] for n in range(self.vpp_worker_count)]
         for (_, p) in self.pkt_infos:
         for (_, p) in self.pkt_infos:
-            wi = randrange(worker_count)
+            wi = randrange(self.vpp_worker_count)
             second_packets[wi].append(p[0])
             if len(p) <= 1:
                 continue
             wi2 = wi
             while wi2 == wi:
             second_packets[wi].append(p[0])
             if len(p) <= 1:
                 continue
             wi2 = wi
             while wi2 == wi:
-                wi2 = randrange(worker_count)
+                wi2 = randrange(self.vpp_worker_count)
             first_packets[wi2].append(p[1])
             first_packets[wi2].append(p[1])
-            wi3 = randrange(worker_count)
+            wi3 = randrange(self.vpp_worker_count)
             rest_of_packets[wi3].extend(p[2:])
 
         self.pg_enable_capture()
             rest_of_packets[wi3].extend(p[2:])
 
         self.pg_enable_capture()
@@ -1510,7 +1712,7 @@ class TestIPv6SVReassembly(VppTestCase):
             max_reassembly_length=1000,
             type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
             expire_walk_interval_ms=10, is_ip6=1)
             max_reassembly_length=1000,
             type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
             expire_walk_interval_ms=10, is_ip6=1)
-        self.sleep(.25)
+        self.virtual_sleep(.25)
         self.vapi.ip_reassembly_set(
             timeout_ms=1000000, max_reassemblies=1000,
             max_reassembly_length=1000,
         self.vapi.ip_reassembly_set(
             timeout_ms=1000000, max_reassemblies=1000,
             max_reassembly_length=1000,
@@ -1570,6 +1772,41 @@ class TestIPv6SVReassembly(VppTestCase):
             self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
             self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
 
             self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
             self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
 
+    def test_verify_clear_trace_mid_reassembly(self):
+        """ verify clear trace works mid-reassembly """
+        payload_len = 1000
+        payload = ""
+        counter = 0
+        while len(payload) < payload_len:
+            payload += "%u " % counter
+            counter += 1
+
+        p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
+             IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
+             UDP(sport=1234, dport=5678) /
+             Raw(payload))
+        fragments = fragment_rfc8200(p, 1, payload_len/4)
+
+        self.pg_enable_capture()
+        self.src_if.add_stream(fragments[1])
+        self.pg_start()
+
+        self.logger.debug(self.vapi.cli("show trace"))
+        self.vapi.cli("clear trace")
+
+        self.pg_enable_capture()
+        self.src_if.add_stream(fragments[0])
+        self.pg_start()
+        self.dst_if.get_capture(2)
+
+        self.logger.debug(self.vapi.cli("show trace"))
+        self.vapi.cli("clear trace")
+
+        self.pg_enable_capture()
+        self.src_if.add_stream(fragments[2:])
+        self.pg_start()
+        self.dst_if.get_capture(len(fragments[2:]))
+
     def test_timeout(self):
         """ reassembly timeout """
         payload_len = 1000
     def test_timeout(self):
         """ reassembly timeout """
         payload_len = 1000
@@ -1606,7 +1843,7 @@ class TestIPv6SVReassembly(VppTestCase):
             self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
 
         # wait for cleanup
             self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
 
         # wait for cleanup
-        self.sleep(.25, "wait before sending rest of fragments")
+        self.virtual_sleep(.25, "wait before sending rest of fragments")
 
         # send rest of fragments - shouldn't be forwarded
         self.pg_enable_capture()
 
         # send rest of fragments - shouldn't be forwarded
         self.pg_enable_capture()
@@ -1651,6 +1888,53 @@ class TestIPv6SVReassembly(VppTestCase):
             self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
             self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
 
             self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
             self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
 
+    def test_one_fragment(self):
+        """ whole packet in one fragment processed independently """
+        pkt = (Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) /
+               IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
+               ICMPv6EchoRequest()/Raw('X' * 1600))
+        frags = fragment_rfc8200(pkt, 1, 400)
+
+        # send a fragment with known id
+        self.send_and_expect(self.src_if, [frags[0]], self.dst_if)
+
+        # send an atomic fragment with same id - should be reassembled
+        pkt = (Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) /
+               IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
+               IPv6ExtHdrFragment(id=1)/ICMPv6EchoRequest())
+        rx = self.send_and_expect(self.src_if, [pkt], self.dst_if)
+
+        # now forward packets matching original reassembly, should still work
+        rx = self.send_and_expect(self.src_if, frags[1:], self.dst_if)
+
+    def test_bunch_of_fragments(self):
+        """ valid fragments followed by rogue fragments and atomic fragment"""
+        pkt = (Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) /
+               IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
+               ICMPv6EchoRequest()/Raw('X' * 1600))
+        frags = fragment_rfc8200(pkt, 1, 400)
+        rx = self.send_and_expect(self.src_if, frags, self.dst_if)
+
+        rogue = (Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) /
+                 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
+                 IPv6ExtHdrFragment(id=1, nh=58, offset=608)/Raw('X'*308))
+
+        self.send_and_expect(self.src_if, rogue*604, self.dst_if)
+
+        pkt = (Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) /
+               IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
+               IPv6ExtHdrFragment(id=1)/ICMPv6EchoRequest())
+        rx = self.send_and_expect(self.src_if, [pkt], self.dst_if)
+
+    def test_truncated_fragment(self):
+        """ truncated fragment """
+        pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
+               IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6,
+                    nh=44, plen=2) /
+               IPv6ExtHdrFragment(nh=6))
+
+        self.send_and_assert_no_replies(self.pg0, [pkt], self.pg0)
+
 
 class TestIPv4ReassemblyLocalNode(VppTestCase):
     """ IPv4 Reassembly for packets coming to ip4-local node """
 
 class TestIPv4ReassemblyLocalNode(VppTestCase):
     """ IPv4 Reassembly for packets coming to ip4-local node """
@@ -1682,7 +1966,7 @@ class TestIPv4ReassemblyLocalNode(VppTestCase):
         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
                                     max_reassembly_length=1000,
                                     expire_walk_interval_ms=10)
         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
                                     max_reassembly_length=1000,
                                     expire_walk_interval_ms=10)
-        self.sleep(.25)
+        self.virtual_sleep(.25)
         self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
                                     max_reassembly_length=1000,
                                     expire_walk_interval_ms=10000)
         self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
                                     max_reassembly_length=1000,
                                     expire_walk_interval_ms=10000)
@@ -1716,7 +2000,7 @@ class TestIPv4ReassemblyLocalNode(VppTestCase):
     def create_fragments(cls):
         infos = cls._packet_infos
         cls.pkt_infos = []
     def create_fragments(cls):
         infos = cls._packet_infos
         cls.pkt_infos = []
-        for index, info in six.iteritems(infos):
+        for index, info in infos.items():
             p = info.data
             # cls.logger.debug(ppp("Packet:",
             #                      p.__class__(scapy.compat.raw(p))))
             p = info.data
             # cls.logger.debug(ppp("Packet:",
             #                      p.__class__(scapy.compat.raw(p))))
@@ -1818,7 +2102,7 @@ class TestFIFReassembly(VppTestCase):
         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
                                     max_reassembly_length=1000,
                                     expire_walk_interval_ms=10, is_ip6=1)
         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
                                     max_reassembly_length=1000,
                                     expire_walk_interval_ms=10, is_ip6=1)
-        self.sleep(.25)
+        self.virtual_sleep(.25)
         self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
                                     max_reassembly_length=1000,
                                     expire_walk_interval_ms=10000)
         self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
                                     max_reassembly_length=1000,
                                     expire_walk_interval_ms=10000)
@@ -1907,7 +2191,7 @@ class TestFIFReassembly(VppTestCase):
             self.extend_packet(p, size, self.padding)
             info.data = p[IP]  # use only IP part, without ethernet header
 
             self.extend_packet(p, size, self.padding)
             info.data = p[IP]  # use only IP part, without ethernet header
 
-        fragments = [x for _, p in six.iteritems(self._packet_infos)
+        fragments = [x for _, p in self._packet_infos.items()
                      for x in fragment_rfc791(p.data, 400)]
 
         encapped_fragments = \
                      for x in fragment_rfc791(p.data, 400)]
 
         encapped_fragments = \
@@ -1972,7 +2256,7 @@ class TestFIFReassembly(VppTestCase):
             self.extend_packet(p, size, self.padding)
             info.data = p[IPv6]  # use only IPv6 part, without ethernet header
 
             self.extend_packet(p, size, self.padding)
             info.data = p[IPv6]  # use only IPv6 part, without ethernet header
 
-        fragments = [x for _, i in six.iteritems(self._packet_infos)
+        fragments = [x for _, i in self._packet_infos.items()
                      for x in fragment_rfc8200(
                          i.data, i.index, 400)]
 
                      for x in fragment_rfc8200(
                          i.data, i.index, 400)]