docs: Add feature build to publish script
[vpp.git] / test / test_reassembly.py
index 4c8712f..e9a3af3 100644 (file)
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import six
 import unittest
 
 import six
 import unittest
@@ -10,13 +10,14 @@ import scapy.compat
 from scapy.packet import Raw
 from scapy.layers.l2 import Ether, GRE
 from scapy.layers.inet import IP, UDP, ICMP
 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 IPv6, IPv6ExtHdrFragment, ICMPv6ParamProblem,\
-    ICMPv6TimeExceeded
+from scapy.layers.inet6 import HBHOptUnknown, ICMPv6ParamProblem,\
+    ICMPv6TimeExceeded, IPv6, IPv6ExtHdrFragment, IPv6ExtHdrHopByHop
 from framework import VppTestCase, VppTestRunner
 from util import ppp, ppc, fragment_rfc791, fragment_rfc8200
 from vpp_gre_interface import VppGreInterface
 from vpp_ip import DpoProto
 from vpp_ip_route import VppIpRoute, VppRoutePath, FibPathProto
 from framework import VppTestCase, VppTestRunner
 from util import ppp, ppc, fragment_rfc791, fragment_rfc8200
 from vpp_gre_interface import VppGreInterface
 from vpp_ip import DpoProto
 from vpp_ip_route import VppIpRoute, VppRoutePath, FibPathProto
+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
@@ -69,7 +70,7 @@ class TestIPv4Reassembly(VppTestCase):
         super(TestIPv4Reassembly, self).tearDown()
 
     def show_commands_at_teardown(self):
         super(TestIPv4Reassembly, self).tearDown()
 
     def show_commands_at_teardown(self):
-        self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
+        self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
         self.logger.debug(self.vapi.ppcli("show buffers"))
 
     @classmethod
         self.logger.debug(self.vapi.ppcli("show buffers"))
 
     @classmethod
@@ -197,7 +198,7 @@ class TestIPv4Reassembly(VppTestCase):
         """ long fragment chain """
 
         error_cnt_str = \
         """ long fragment chain """
 
         error_cnt_str = \
-            "/err/ip4-reassembly-feature/fragment chain too long (drop)"
+            "/err/ip4-full-reassembly-feature/fragment chain too long (drop)"
 
         error_cnt = self.statistics.get_err_counter(error_cnt_str)
 
 
         error_cnt = self.statistics.get_err_counter(error_cnt_str)
 
@@ -209,12 +210,12 @@ class TestIPv4Reassembly(VppTestCase):
               IP(id=1000, src=self.src_if.remote_ip4,
                  dst=self.dst_if.remote_ip4) /
               UDP(sport=1234, dport=5678) /
               IP(id=1000, src=self.src_if.remote_ip4,
                  dst=self.dst_if.remote_ip4) /
               UDP(sport=1234, dport=5678) /
-              Raw("X" * 1000))
+              Raw(b"X" * 1000))
         p2 = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
               IP(id=1001, src=self.src_if.remote_ip4,
                  dst=self.dst_if.remote_ip4) /
               UDP(sport=1234, dport=5678) /
         p2 = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
               IP(id=1001, src=self.src_if.remote_ip4,
                  dst=self.dst_if.remote_ip4) /
               UDP(sport=1234, dport=5678) /
-              Raw("X" * 1000))
+              Raw(b"X" * 1000))
         frags = fragment_rfc791(p1, 200) + fragment_rfc791(p2, 500)
 
         self.pg_enable_capture()
         frags = fragment_rfc791(p1, 200) + fragment_rfc791(p2, 500)
 
         self.pg_enable_capture()
@@ -227,11 +228,9 @@ class TestIPv4Reassembly(VppTestCase):
     def test_5737(self):
         """ fragment length + ip header size > 65535 """
         self.vapi.cli("clear errors")
     def test_5737(self):
         """ fragment length + ip header size > 65535 """
         self.vapi.cli("clear errors")
-        raw = ('E\x00\x00\x88,\xf8\x1f\xfe@\x01\x98\x00\xc0\xa8\n-\xc0\xa8\n'
-               '\x01\x08\x00\xf0J\xed\xcb\xf1\xf5Test-group: IPv4.IPv4.ipv4-'
-               'message.Ethernet-Payload.IPv4-Packet.IPv4-Header.Fragment-Of'
-               'fset; Test-case: 5737')
-
+        raw = b'''E\x00\x00\x88,\xf8\x1f\xfe@\x01\x98\x00\xc0\xa8\n-\xc0\xa8\n\
+\x01\x08\x00\xf0J\xed\xcb\xf1\xf5Test-group: IPv4.IPv4.ipv4-message.\
+Ethernet-Payload.IPv4-Packet.IPv4-Header.Fragment-Offset; Test-case: 5737'''
         malformed_packet = (Ether(dst=self.src_if.local_mac,
                                   src=self.src_if.remote_mac) /
                             IP(raw))
         malformed_packet = (Ether(dst=self.src_if.local_mac,
                                   src=self.src_if.remote_mac) /
                             IP(raw))
@@ -239,19 +238,19 @@ class TestIPv4Reassembly(VppTestCase):
              IP(id=1000, src=self.src_if.remote_ip4,
                 dst=self.dst_if.remote_ip4) /
              UDP(sport=1234, dport=5678) /
              IP(id=1000, src=self.src_if.remote_ip4,
                 dst=self.dst_if.remote_ip4) /
              UDP(sport=1234, dport=5678) /
-             Raw("X" * 1000))
+             Raw(b"X" * 1000))
         valid_fragments = fragment_rfc791(p, 400)
 
         valid_fragments = fragment_rfc791(p, 400)
 
+        counter = "/err/ip4-full-reassembly-feature/malformed packets"
+        error_counter = self.statistics.get_err_counter(counter)
         self.pg_enable_capture()
         self.src_if.add_stream([malformed_packet] + valid_fragments)
         self.pg_start()
 
         self.dst_if.get_capture(1)
         self.pg_enable_capture()
         self.src_if.add_stream([malformed_packet] + valid_fragments)
         self.pg_start()
 
         self.dst_if.get_capture(1)
-        self.assert_packet_counter_equal("ip4-reassembly-feature", 1)
-        # TODO remove above, uncomment below once clearing of counters
-        # is supported
-        # self.assert_packet_counter_equal(
-        #     "/err/ip4-reassembly-feature/malformed packets", 1)
+        self.logger.debug(self.vapi.ppcli("show error"))
+        self.assertEqual(self.statistics.get_err_counter(counter),
+                         error_counter + 1)
 
     def test_44924(self):
         """ compress tiny fragments """
 
     def test_44924(self):
         """ compress tiny fragments """
@@ -298,14 +297,14 @@ class TestIPv4Reassembly(VppTestCase):
                               IP(id=7, len=21, frag=1, ttl=64,
                                  src=self.src_if.remote_ip4,
                                  dst=self.dst_if.remote_ip4) /
                               IP(id=7, len=21, frag=1, ttl=64,
                                  src=self.src_if.remote_ip4,
                                  dst=self.dst_if.remote_ip4) /
-                              Raw(load='\x08')),
+                              Raw(load=b'\x08')),
                              ]
 
         p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
              IP(id=1000, src=self.src_if.remote_ip4,
                 dst=self.dst_if.remote_ip4) /
              UDP(sport=1234, dport=5678) /
                              ]
 
         p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
              IP(id=1000, src=self.src_if.remote_ip4,
                 dst=self.dst_if.remote_ip4) /
              UDP(sport=1234, dport=5678) /
-             Raw("X" * 1000))
+             Raw(b"X" * 1000))
         valid_fragments = fragment_rfc791(p, 400)
 
         self.pg_enable_capture()
         valid_fragments = fragment_rfc791(p, 400)
 
         self.pg_enable_capture()
@@ -314,11 +313,11 @@ class TestIPv4Reassembly(VppTestCase):
 
         self.dst_if.get_capture(1)
 
 
         self.dst_if.get_capture(1)
 
-        self.assert_packet_counter_equal("ip4-reassembly-feature", 1)
+        self.assert_packet_counter_equal("ip4-full-reassembly-feature", 1)
         # TODO remove above, uncomment below once clearing of counters
         # is supported
         # self.assert_packet_counter_equal(
         # TODO remove above, uncomment below once clearing of counters
         # is supported
         # self.assert_packet_counter_equal(
-        #     "/err/ip4-reassembly-feature/malformed packets", 1)
+        #     "/err/ip4-full-reassembly-feature/malformed packets", 1)
 
     def test_random(self):
         """ random order reassembly """
 
     def test_random(self):
         """ random order reassembly """
@@ -501,6 +500,177 @@ class TestIPv4Reassembly(VppTestCase):
         self.src_if.assert_nothing_captured()
 
 
         self.src_if.assert_nothing_captured()
 
 
+class TestIPv4SVReassembly(VppTestCase):
+    """ IPv4 Shallow Virtual Reassembly """
+
+    @classmethod
+    def setUpClass(cls):
+        super(TestIPv4SVReassembly, cls).setUpClass()
+
+        cls.create_pg_interfaces([0, 1])
+        cls.src_if = cls.pg0
+        cls.dst_if = cls.pg1
+
+        # setup all interfaces
+        for i in cls.pg_interfaces:
+            i.admin_up()
+            i.config_ip4()
+            i.resolve_arp()
+
+    def setUp(self):
+        """ Test setup - force timeout on existing reassemblies """
+        super(TestIPv4SVReassembly, self).setUp()
+        self.vapi.ip_reassembly_enable_disable(
+            sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
+            type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
+        self.vapi.ip_reassembly_set(
+            timeout_ms=0, max_reassemblies=1000,
+            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.vapi.ip_reassembly_set(
+            timeout_ms=1000000, max_reassemblies=1000,
+            max_reassembly_length=1000,
+            type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
+            expire_walk_interval_ms=10000)
+
+    def tearDown(self):
+        super(TestIPv4SVReassembly, self).tearDown()
+        self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
+        self.logger.debug(self.vapi.ppcli("show buffers"))
+
+    def test_basic(self):
+        """ basic 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)
+
+        # send fragment #2 - should be cached inside reassembly
+        self.pg_enable_capture()
+        self.src_if.add_stream(fragments[1])
+        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.assert_nothing_captured()
+
+        # send fragment #1 - reassembly is finished now and both fragments
+        # forwarded
+        self.pg_enable_capture()
+        self.src_if.add_stream(fragments[0])
+        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"))
+        c = self.dst_if.get_capture(2)
+        for sent, recvd in zip([fragments[1], fragments[0]], c):
+            self.assertEqual(sent[IP].src, recvd[IP].src)
+            self.assertEqual(sent[IP].dst, recvd[IP].dst)
+            self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
+
+        # send rest of fragments - should be immediately forwarded
+        self.pg_enable_capture()
+        self.src_if.add_stream(fragments[2:])
+        self.pg_start()
+        c = self.dst_if.get_capture(len(fragments[2:]))
+        for sent, recvd in zip(fragments[2:], c):
+            self.assertEqual(sent[IP].src, recvd[IP].src)
+            self.assertEqual(sent[IP].dst, recvd[IP].dst)
+            self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
+
+    def test_timeout(self):
+        """ reassembly timeout """
+        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.vapi.ip_reassembly_set(
+            timeout_ms=100, max_reassemblies=1000,
+            max_reassembly_length=1000,
+            expire_walk_interval_ms=50,
+            type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
+
+        # send fragments #2 and #1 - should be forwarded
+        self.pg_enable_capture()
+        self.src_if.add_stream(fragments[0:2])
+        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"))
+        c = self.dst_if.get_capture(2)
+        for sent, recvd in zip([fragments[1], fragments[0]], c):
+            self.assertEqual(sent[IP].src, recvd[IP].src)
+            self.assertEqual(sent[IP].dst, recvd[IP].dst)
+            self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
+
+        # wait for cleanup
+        self.sleep(.25, "wait before sending rest of fragments")
+
+        # send rest of fragments - shouldn't be forwarded
+        self.pg_enable_capture()
+        self.src_if.add_stream(fragments[2:])
+        self.pg_start()
+        self.dst_if.assert_nothing_captured()
+
+    def test_lru(self):
+        """ reassembly reuses LRU element """
+
+        self.vapi.ip_reassembly_set(
+            timeout_ms=1000000, max_reassemblies=1,
+            max_reassembly_length=1000,
+            type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
+            expire_walk_interval_ms=10000)
+
+        payload_len = 1000
+        payload = ""
+        counter = 0
+        while len(payload) < payload_len:
+            payload += "%u " % counter
+            counter += 1
+
+        packet_count = 10
+
+        fragments = [f
+                     for i in range(packet_count)
+                     for p in (Ether(dst=self.src_if.local_mac,
+                                     src=self.src_if.remote_mac) /
+                               IP(id=i, src=self.src_if.remote_ip4,
+                                   dst=self.dst_if.remote_ip4) /
+                               UDP(sport=1234, dport=5678) /
+                               Raw(payload))
+                     for f in fragment_rfc791(p, payload_len/4)]
+
+        self.pg_enable_capture()
+        self.src_if.add_stream(fragments)
+        self.pg_start()
+        c = self.dst_if.get_capture(len(fragments))
+        for sent, recvd in zip(fragments, c):
+            self.assertEqual(sent[IP].src, recvd[IP].src)
+            self.assertEqual(sent[IP].dst, recvd[IP].dst)
+            self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
+
+
 class TestIPv4MWReassembly(VppTestCase):
     """ IPv4 Reassembly (multiple workers) """
     worker_config = "workers %d" % worker_count
 class TestIPv4MWReassembly(VppTestCase):
     """ IPv4 Reassembly (multiple workers) """
     worker_config = "workers %d" % worker_count
@@ -551,7 +721,7 @@ class TestIPv4MWReassembly(VppTestCase):
         super(TestIPv4MWReassembly, self).tearDown()
 
     def show_commands_at_teardown(self):
         super(TestIPv4MWReassembly, self).tearDown()
 
     def show_commands_at_teardown(self):
-        self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
+        self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
         self.logger.debug(self.vapi.ppcli("show buffers"))
 
     @classmethod
         self.logger.debug(self.vapi.ppcli("show buffers"))
 
     @classmethod
@@ -663,6 +833,11 @@ class TestIPv4MWReassembly(VppTestCase):
         for send_if in self.send_ifs:
             send_if.assert_nothing_captured()
 
         for send_if in self.send_ifs:
             send_if.assert_nothing_captured()
 
+        self.logger.debug(self.vapi.ppcli("show trace"))
+        self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
+        self.logger.debug(self.vapi.ppcli("show buffers"))
+        self.vapi.cli("clear trace")
+
         self.pg_enable_capture()
         self.send_packets(first_packets)
         self.send_packets(second_packets)
         self.pg_enable_capture()
         self.send_packets(first_packets)
         self.send_packets(second_packets)
@@ -713,14 +888,14 @@ class TestIPv6Reassembly(VppTestCase):
         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)
-        self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
+        self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
         self.logger.debug(self.vapi.ppcli("show buffers"))
 
     def tearDown(self):
         super(TestIPv6Reassembly, self).tearDown()
 
     def show_commands_at_teardown(self):
         self.logger.debug(self.vapi.ppcli("show buffers"))
 
     def tearDown(self):
         super(TestIPv6Reassembly, self).tearDown()
 
     def show_commands_at_teardown(self):
-        self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
+        self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
         self.logger.debug(self.vapi.ppcli("show buffers"))
 
     @classmethod
         self.logger.debug(self.vapi.ppcli("show buffers"))
 
     @classmethod
@@ -816,6 +991,23 @@ class TestIPv6Reassembly(VppTestCase):
         self.verify_capture(packets)
         self.src_if.assert_nothing_captured()
 
         self.verify_capture(packets)
         self.src_if.assert_nothing_captured()
 
+    def test_buffer_boundary(self):
+        """ fragment header crossing buffer boundary """
+
+        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) /
+             IPv6ExtHdrHopByHop(
+                 options=[HBHOptUnknown(otype=0xff, optlen=0)] * 1000) /
+             IPv6ExtHdrFragment(m=1) /
+             UDP(sport=1234, dport=5678) /
+             Raw())
+        self.pg_enable_capture()
+        self.src_if.add_stream([p])
+        self.pg_start()
+        self.src_if.assert_nothing_captured()
+        self.dst_if.assert_nothing_captured()
+
     def test_reversed(self):
         """ reverse order reassembly """
 
     def test_reversed(self):
         """ reverse order reassembly """
 
@@ -883,7 +1075,7 @@ class TestIPv6Reassembly(VppTestCase):
         """ long fragment chain """
 
         error_cnt_str = \
         """ long fragment chain """
 
         error_cnt_str = \
-            "/err/ip6-reassembly-feature/fragment chain too long (drop)"
+            "/err/ip6-full-reassembly-feature/fragment chain too long (drop)"
 
         error_cnt = self.statistics.get_err_counter(error_cnt_str)
 
 
         error_cnt = self.statistics.get_err_counter(error_cnt_str)
 
@@ -895,7 +1087,7 @@ class TestIPv6Reassembly(VppTestCase):
              IPv6(src=self.src_if.remote_ip6,
                   dst=self.dst_if.remote_ip6) /
              UDP(sport=1234, dport=5678) /
              IPv6(src=self.src_if.remote_ip6,
                   dst=self.dst_if.remote_ip6) /
              UDP(sport=1234, dport=5678) /
-             Raw("X" * 1000))
+             Raw(b"X" * 1000))
         frags = fragment_rfc8200(p, 1, 300) + fragment_rfc8200(p, 2, 500)
 
         self.pg_enable_capture()
         frags = fragment_rfc8200(p, 1, 300) + fragment_rfc8200(p, 2, 500)
 
         self.pg_enable_capture()
@@ -1162,7 +1354,7 @@ class TestIPv6MWReassembly(VppTestCase):
         super(TestIPv6MWReassembly, self).tearDown()
 
     def show_commands_at_teardown(self):
         super(TestIPv6MWReassembly, self).tearDown()
 
     def show_commands_at_teardown(self):
-        self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
+        self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
         self.logger.debug(self.vapi.ppcli("show buffers"))
 
     @classmethod
         self.logger.debug(self.vapi.ppcli("show buffers"))
 
     @classmethod
@@ -1274,6 +1466,11 @@ class TestIPv6MWReassembly(VppTestCase):
         for send_if in self.send_ifs:
             send_if.assert_nothing_captured()
 
         for send_if in self.send_ifs:
             send_if.assert_nothing_captured()
 
+        self.logger.debug(self.vapi.ppcli("show trace"))
+        self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
+        self.logger.debug(self.vapi.ppcli("show buffers"))
+        self.vapi.cli("clear trace")
+
         self.pg_enable_capture()
         self.send_packets(first_packets)
         self.send_packets(second_packets)
         self.pg_enable_capture()
         self.send_packets(first_packets)
         self.send_packets(second_packets)
@@ -1285,6 +1482,176 @@ class TestIPv6MWReassembly(VppTestCase):
             send_if.assert_nothing_captured()
 
 
             send_if.assert_nothing_captured()
 
 
+class TestIPv6SVReassembly(VppTestCase):
+    """ IPv6 Shallow Virtual Reassembly """
+
+    @classmethod
+    def setUpClass(cls):
+        super(TestIPv6SVReassembly, cls).setUpClass()
+
+        cls.create_pg_interfaces([0, 1])
+        cls.src_if = cls.pg0
+        cls.dst_if = cls.pg1
+
+        # setup all interfaces
+        for i in cls.pg_interfaces:
+            i.admin_up()
+            i.config_ip6()
+            i.resolve_ndp()
+
+    def setUp(self):
+        """ Test setup - force timeout on existing reassemblies """
+        super(TestIPv6SVReassembly, self).setUp()
+        self.vapi.ip_reassembly_enable_disable(
+            sw_if_index=self.src_if.sw_if_index, enable_ip6=True,
+            type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
+        self.vapi.ip_reassembly_set(
+            timeout_ms=0, max_reassemblies=1000,
+            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.vapi.ip_reassembly_set(
+            timeout_ms=1000000, max_reassemblies=1000,
+            max_reassembly_length=1000,
+            type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
+            expire_walk_interval_ms=10000, is_ip6=1)
+
+    def tearDown(self):
+        super(TestIPv6SVReassembly, self).tearDown()
+        self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
+        self.logger.debug(self.vapi.ppcli("show buffers"))
+
+    def test_basic(self):
+        """ basic 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)
+
+        # send fragment #2 - should be cached inside reassembly
+        self.pg_enable_capture()
+        self.src_if.add_stream(fragments[1])
+        self.pg_start()
+        self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
+        self.logger.debug(self.vapi.ppcli("show buffers"))
+        self.logger.debug(self.vapi.ppcli("show trace"))
+        self.dst_if.assert_nothing_captured()
+
+        # send fragment #1 - reassembly is finished now and both fragments
+        # forwarded
+        self.pg_enable_capture()
+        self.src_if.add_stream(fragments[0])
+        self.pg_start()
+        self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
+        self.logger.debug(self.vapi.ppcli("show buffers"))
+        self.logger.debug(self.vapi.ppcli("show trace"))
+        c = self.dst_if.get_capture(2)
+        for sent, recvd in zip([fragments[1], fragments[0]], c):
+            self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
+            self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
+            self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
+
+        # send rest of fragments - should be immediately forwarded
+        self.pg_enable_capture()
+        self.src_if.add_stream(fragments[2:])
+        self.pg_start()
+        c = self.dst_if.get_capture(len(fragments[2:]))
+        for sent, recvd in zip(fragments[2:], c):
+            self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
+            self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
+            self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
+
+    def test_timeout(self):
+        """ reassembly timeout """
+        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.vapi.ip_reassembly_set(
+            timeout_ms=100, max_reassemblies=1000,
+            max_reassembly_length=1000,
+            expire_walk_interval_ms=50,
+            is_ip6=1,
+            type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
+
+        # send fragments #2 and #1 - should be forwarded
+        self.pg_enable_capture()
+        self.src_if.add_stream(fragments[0:2])
+        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"))
+        c = self.dst_if.get_capture(2)
+        for sent, recvd in zip([fragments[1], fragments[0]], c):
+            self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
+            self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
+            self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
+
+        # wait for cleanup
+        self.sleep(.25, "wait before sending rest of fragments")
+
+        # send rest of fragments - shouldn't be forwarded
+        self.pg_enable_capture()
+        self.src_if.add_stream(fragments[2:])
+        self.pg_start()
+        self.dst_if.assert_nothing_captured()
+
+    def test_lru(self):
+        """ reassembly reuses LRU element """
+
+        self.vapi.ip_reassembly_set(
+            timeout_ms=1000000, max_reassemblies=1,
+            max_reassembly_length=1000,
+            type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
+            is_ip6=1, expire_walk_interval_ms=10000)
+
+        payload_len = 1000
+        payload = ""
+        counter = 0
+        while len(payload) < payload_len:
+            payload += "%u " % counter
+            counter += 1
+
+        packet_count = 10
+
+        fragments = [f
+                     for i in range(packet_count)
+                     for p in (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))
+                     for f in fragment_rfc8200(p, i, payload_len/4)]
+
+        self.pg_enable_capture()
+        self.src_if.add_stream(fragments)
+        self.pg_start()
+        c = self.dst_if.get_capture(len(fragments))
+        for sent, recvd in zip(fragments, c):
+            self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
+            self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
+            self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
+
+
 class TestIPv4ReassemblyLocalNode(VppTestCase):
     """ IPv4 Reassembly for packets coming to ip4-local node """
 
 class TestIPv4ReassemblyLocalNode(VppTestCase):
     """ IPv4 Reassembly for packets coming to ip4-local node """
 
@@ -1324,7 +1691,7 @@ class TestIPv4ReassemblyLocalNode(VppTestCase):
         super(TestIPv4ReassemblyLocalNode, self).tearDown()
 
     def show_commands_at_teardown(self):
         super(TestIPv4ReassemblyLocalNode, self).tearDown()
 
     def show_commands_at_teardown(self):
-        self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
+        self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
         self.logger.debug(self.vapi.ppcli("show buffers"))
 
     @classmethod
         self.logger.debug(self.vapi.ppcli("show buffers"))
 
     @classmethod
@@ -1463,8 +1830,8 @@ class TestFIFReassembly(VppTestCase):
         super(TestFIFReassembly, self).tearDown()
 
     def show_commands_at_teardown(self):
         super(TestFIFReassembly, self).tearDown()
 
     def show_commands_at_teardown(self):
-        self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
-        self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
+        self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
+        self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
         self.logger.debug(self.vapi.ppcli("show buffers"))
 
     def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):
         self.logger.debug(self.vapi.ppcli("show buffers"))
 
     def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):