reassembly: prevent long chain attack
[vpp.git] / test / test_reassembly.py
1 #!/usr/bin/env python
2
3 from random import shuffle
4 import six
5 import unittest
6
7 from parameterized import parameterized
8 import scapy.compat
9 from scapy.packet import Raw
10 from scapy.layers.l2 import Ether, GRE
11 from scapy.layers.inet import IP, UDP, ICMP
12
13 from scapy.layers.inet6 import IPv6, IPv6ExtHdrFragment, ICMPv6ParamProblem,\
14     ICMPv6TimeExceeded
15
16 from framework import VppTestCase, VppTestRunner
17 from util import ppp, fragment_rfc791, fragment_rfc8200
18 from vpp_gre_interface import VppGreInterface
19 from vpp_ip import DpoProto
20 from vpp_ip_route import VppIpRoute, VppRoutePath
21
22 # 35 is enough to have >257 400-byte fragments
23 test_packet_count = 35
24
25 # <class 'scapy.layers.inet.IP'>
26 # <class 'scapy.layers.inet6.IPv6'>
27 _scapy_ip_family_types = (IP, IPv6)
28
29
30 def validate_scapy_ip_family(scapy_ip_family):
31
32     if scapy_ip_family not in _scapy_ip_family_types:
33         raise ValueError("'scapy_ip_family' must be of type: %s. Got %s" %
34                          (_scapy_ip_family_types, scapy_ip_family))
35
36
37 class TestIPReassemblyMixin(object):
38
39     def verify_capture(self, scapy_ip_family, capture,
40                        dropped_packet_indexes=None):
41         """Verify captured packet stream.
42
43         :param list capture: Captured packet stream.
44         """
45         validate_scapy_ip_family(scapy_ip_family)
46
47         if dropped_packet_indexes is None:
48             dropped_packet_indexes = []
49         info = None
50         seen = set()
51         for packet in capture:
52             try:
53                 self.logger.debug(ppp("Got packet:", packet))
54                 ip = packet[scapy_ip_family]
55                 udp = packet[UDP]
56                 payload_info = self.payload_to_info(packet[Raw])
57                 packet_index = payload_info.index
58                 self.assertTrue(
59                     packet_index not in dropped_packet_indexes,
60                     ppp("Packet received, but should be dropped:", packet))
61                 if packet_index in seen:
62                     raise Exception(ppp("Duplicate packet received", packet))
63                 seen.add(packet_index)
64                 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
65                 info = self._packet_infos[packet_index]
66                 self.assertTrue(info is not None)
67                 self.assertEqual(packet_index, info.index)
68                 saved_packet = info.data
69                 self.assertEqual(ip.src, saved_packet[scapy_ip_family].src)
70                 self.assertEqual(ip.dst, saved_packet[scapy_ip_family].dst)
71                 self.assertEqual(udp.payload, saved_packet[UDP].payload)
72             except Exception:
73                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
74                 raise
75         for index in self._packet_infos:
76             self.assertTrue(index in seen or index in dropped_packet_indexes,
77                             "Packet with packet_index %d not received" % index)
78
79     def test_disabled(self, scapy_ip_family, stream,
80                       dropped_packet_indexes):
81         """ reassembly disabled """
82         validate_scapy_ip_family(scapy_ip_family)
83         is_ip6 = 1 if scapy_ip_family == IPv6 else 0
84
85         self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
86                                     max_reassembly_length=1000,
87                                     expire_walk_interval_ms=10000,
88                                     is_ip6=is_ip6)
89
90         self.pg_enable_capture()
91         self.src_if.add_stream(stream)
92         self.pg_start()
93
94         packets = self.dst_if.get_capture(
95             len(self.pkt_infos) - len(dropped_packet_indexes))
96         self.verify_capture(scapy_ip_family, packets, dropped_packet_indexes)
97         self.src_if.assert_nothing_captured()
98
99     def test_duplicates(self, scapy_ip_family, stream):
100         """ duplicate fragments """
101         validate_scapy_ip_family(scapy_ip_family)
102
103         self.pg_enable_capture()
104         self.src_if.add_stream(stream)
105         self.pg_start()
106
107         packets = self.dst_if.get_capture(len(self.pkt_infos))
108         self.verify_capture(scapy_ip_family, packets)
109         self.src_if.assert_nothing_captured()
110
111     def test_random(self, scapy_ip_family, stream):
112         """ random order reassembly """
113         validate_scapy_ip_family(scapy_ip_family)
114
115         fragments = list(stream)
116         shuffle(fragments)
117
118         self.pg_enable_capture()
119         self.src_if.add_stream(fragments)
120         self.pg_start()
121
122         packets = self.dst_if.get_capture(len(self.packet_infos))
123         self.verify_capture(scapy_ip_family, packets)
124         self.src_if.assert_nothing_captured()
125
126         # run it all again to verify correctness
127         self.pg_enable_capture()
128         self.src_if.add_stream(fragments)
129         self.pg_start()
130
131         packets = self.dst_if.get_capture(len(self.packet_infos))
132         self.verify_capture(scapy_ip_family, packets)
133         self.src_if.assert_nothing_captured()
134
135     def test_reassembly(self, scapy_ip_family, stream):
136         """ basic reassembly """
137         validate_scapy_ip_family(scapy_ip_family)
138
139         self.pg_enable_capture()
140         self.src_if.add_stream(stream)
141         self.pg_start()
142
143         packets = self.dst_if.get_capture(len(self.pkt_infos))
144         self.verify_capture(scapy_ip_family, packets)
145         self.src_if.assert_nothing_captured()
146
147         # run it all again to verify correctness
148         self.pg_enable_capture()
149         self.src_if.add_stream(stream)
150         self.pg_start()
151
152         packets = self.dst_if.get_capture(len(self.pkt_infos))
153         self.verify_capture(scapy_ip_family, packets)
154         self.src_if.assert_nothing_captured()
155
156     def test_reversed(self, scapy_ip_family, stream):
157         """ reverse order reassembly """
158         validate_scapy_ip_family(scapy_ip_family)
159
160         fragments = list(stream)
161         fragments.reverse()
162
163         self.pg_enable_capture()
164         self.src_if.add_stream(fragments)
165         self.pg_start()
166
167         packets = self.dst_if.get_capture(len(self.packet_infos))
168         self.verify_capture(scapy_ip_family, packets)
169         self.src_if.assert_nothing_captured()
170
171         # run it all again to verify correctness
172         self.pg_enable_capture()
173         self.src_if.add_stream(fragments)
174         self.pg_start()
175
176         packets = self.dst_if.get_capture(len(self.packet_infos))
177         self.verify_capture(scapy_ip_family, packets)
178         self.src_if.assert_nothing_captured()
179
180     def test_timeout_inline(self, scapy_ip_family, stream,
181                             dropped_packet_indexes):
182         """ timeout (inline) """
183         validate_scapy_ip_family(scapy_ip_family)
184         is_ip6 = 1 if scapy_ip_family == IPv6 else 0
185
186         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
187                                     max_reassembly_length=1000,
188                                     expire_walk_interval_ms=10000,
189                                     is_ip6=is_ip6)
190
191         self.pg_enable_capture()
192         self.src_if.add_stream(stream)
193         self.pg_start()
194
195         packets = self.dst_if.get_capture(
196             len(self.pkt_infos) - len(dropped_packet_indexes))
197         self.verify_capture(scapy_ip_family, packets,
198                             dropped_packet_indexes)
199
200
201 class TestIPv4Reassembly(TestIPReassemblyMixin, VppTestCase):
202     """ IPv4 Reassembly """
203
204     @classmethod
205     def setUpClass(cls):
206         super(TestIPv4Reassembly, cls).setUpClass()
207
208         cls.create_pg_interfaces([0, 1])
209         cls.src_if = cls.pg0
210         cls.dst_if = cls.pg1
211
212         # setup all interfaces
213         for i in cls.pg_interfaces:
214             i.admin_up()
215             i.config_ip4()
216             i.resolve_arp()
217
218         # packet sizes
219         cls.packet_sizes = [64, 512, 1518, 9018]
220         cls.padding = " abcdefghijklmn"
221         cls.create_stream(cls.packet_sizes)
222         cls.create_fragments()
223
224     @classmethod
225     def tearDownClass(cls):
226         super(TestIPv4Reassembly, cls).tearDownClass()
227
228     def setUp(self):
229         """ Test setup - force timeout on existing reassemblies """
230         super(TestIPv4Reassembly, self).setUp()
231         self.vapi.ip_reassembly_enable_disable(
232             sw_if_index=self.src_if.sw_if_index, enable_ip4=True)
233         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
234                                     max_reassembly_length=1000,
235                                     expire_walk_interval_ms=10)
236         self.sleep(.25)
237         self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
238                                     max_reassembly_length=1000,
239                                     expire_walk_interval_ms=10000)
240
241     def tearDown(self):
242         super(TestIPv4Reassembly, self).tearDown()
243
244     def show_commands_at_teardown(self):
245         self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
246         self.logger.debug(self.vapi.ppcli("show buffers"))
247
248     @classmethod
249     def create_stream(cls, packet_sizes, packet_count=test_packet_count):
250         """Create input packet stream
251
252         :param list packet_sizes: Required packet sizes.
253         """
254         for i in range(0, packet_count):
255             info = cls.create_packet_info(cls.src_if, cls.src_if)
256             payload = cls.info_to_payload(info)
257             p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
258                  IP(id=info.index, src=cls.src_if.remote_ip4,
259                     dst=cls.dst_if.remote_ip4) /
260                  UDP(sport=1234, dport=5678) /
261                  Raw(payload))
262             size = packet_sizes[(i // 2) % len(packet_sizes)]
263             cls.extend_packet(p, size, cls.padding)
264             info.data = p
265
266     @classmethod
267     def create_fragments(cls):
268         infos = cls._packet_infos
269         cls.pkt_infos = []
270         for index, info in six.iteritems(infos):
271             p = info.data
272             # cls.logger.debug(ppp("Packet:",
273             #                      p.__class__(scapy.compat.raw(p))))
274             fragments_400 = fragment_rfc791(p, 400)
275             fragments_300 = fragment_rfc791(p, 300)
276             fragments_200 = [
277                 x for f in fragments_400 for x in fragment_rfc791(f, 200)]
278             cls.pkt_infos.append(
279                 (index, fragments_400, fragments_300, fragments_200))
280         cls.fragments_400 = [
281             x for (_, frags, _, _) in cls.pkt_infos for x in frags]
282         cls.fragments_300 = [
283             x for (_, _, frags, _) in cls.pkt_infos for x in frags]
284         cls.fragments_200 = [
285             x for (_, _, _, frags) in cls.pkt_infos for x in frags]
286         cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
287                          "%s 300-byte fragments and %s 200-byte fragments" %
288                          (len(infos), len(cls.fragments_400),
289                              len(cls.fragments_300), len(cls.fragments_200)))
290
291     @parameterized.expand([(IP, None)])
292     def test_reassembly(self, family, stream):
293         """ basic reassembly """
294         stream = self.__class__.fragments_200
295         super(TestIPv4Reassembly, self).test_reassembly(family, stream)
296
297     @parameterized.expand([(IP, None)])
298     def test_reversed(self, family, stream):
299         """ reverse order reassembly """
300         stream = self.__class__.fragments_200
301         super(TestIPv4Reassembly, self).test_reversed(family, stream)
302
303     @parameterized.expand([(IP, None)])
304     def test_random(self, family, stream):
305         stream = self.__class__.fragments_200
306         super(TestIPv4Reassembly, self).test_random(family, stream)
307
308     def test_long_fragment_chain(self):
309         """ long fragment chain """
310
311         error_cnt_str = \
312             "/err/ip4-reassembly-feature/fragment chain too long (drop)"
313
314         error_cnt = self.get_packet_counter(error_cnt_str)
315
316         self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
317                                     max_reassembly_length=3,
318                                     expire_walk_interval_ms=50)
319
320         p1 = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
321               IP(id=1000, src=self.src_if.remote_ip4,
322                  dst=self.dst_if.remote_ip4) /
323               UDP(sport=1234, dport=5678) /
324               Raw("X" * 1000))
325         p2 = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
326               IP(id=1001, src=self.src_if.remote_ip4,
327                  dst=self.dst_if.remote_ip4) /
328               UDP(sport=1234, dport=5678) /
329               Raw("X" * 1000))
330         frags = fragment_rfc791(p1, 200) + fragment_rfc791(p2, 500)
331
332         self.pg_enable_capture()
333         self.src_if.add_stream(frags)
334         self.pg_start()
335
336         self.dst_if.get_capture(1)
337         self.assert_packet_counter_equal(error_cnt_str, error_cnt + 1)
338
339     def test_5737(self):
340         """ fragment length + ip header size > 65535 """
341         self.vapi.cli("clear errors")
342         raw = ('E\x00\x00\x88,\xf8\x1f\xfe@\x01\x98\x00\xc0\xa8\n-\xc0\xa8\n'
343                '\x01\x08\x00\xf0J\xed\xcb\xf1\xf5Test-group: IPv4.IPv4.ipv4-'
344                'message.Ethernet-Payload.IPv4-Packet.IPv4-Header.Fragment-Of'
345                'fset; Test-case: 5737')
346
347         malformed_packet = (Ether(dst=self.src_if.local_mac,
348                                   src=self.src_if.remote_mac) /
349                             IP(raw))
350         p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
351              IP(id=1000, src=self.src_if.remote_ip4,
352                 dst=self.dst_if.remote_ip4) /
353              UDP(sport=1234, dport=5678) /
354              Raw("X" * 1000))
355         valid_fragments = fragment_rfc791(p, 400)
356
357         self.pg_enable_capture()
358         self.src_if.add_stream([malformed_packet] + valid_fragments)
359         self.pg_start()
360
361         self.dst_if.get_capture(1)
362         self.assert_packet_counter_equal("ip4-reassembly-feature", 1)
363         # TODO remove above, uncomment below once clearing of counters
364         # is supported
365         # self.assert_packet_counter_equal(
366         #     "/err/ip4-reassembly-feature/malformed packets", 1)
367
368     def test_44924(self):
369         """ compress tiny fragments """
370         packets = [(Ether(dst=self.src_if.local_mac,
371                           src=self.src_if.remote_mac) /
372                     IP(id=24339, flags="MF", frag=0, ttl=64,
373                        src=self.src_if.remote_ip4,
374                        dst=self.dst_if.remote_ip4) /
375                     ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
376                     Raw(load='Test-group: IPv4')),
377                    (Ether(dst=self.src_if.local_mac,
378                           src=self.src_if.remote_mac) /
379                     IP(id=24339, flags="MF", frag=3, ttl=64,
380                        src=self.src_if.remote_ip4,
381                        dst=self.dst_if.remote_ip4) /
382                     ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
383                     Raw(load='.IPv4.Fragmentation.vali')),
384                    (Ether(dst=self.src_if.local_mac,
385                           src=self.src_if.remote_mac) /
386                     IP(id=24339, frag=6, ttl=64,
387                        src=self.src_if.remote_ip4,
388                        dst=self.dst_if.remote_ip4) /
389                     ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
390                     Raw(load='d; Test-case: 44924'))
391                    ]
392
393         self.pg_enable_capture()
394         self.src_if.add_stream(packets)
395         self.pg_start()
396
397         self.dst_if.get_capture(1)
398
399     def test_frag_1(self):
400         """ fragment of size 1 """
401         self.vapi.cli("clear errors")
402         malformed_packets = [(Ether(dst=self.src_if.local_mac,
403                                     src=self.src_if.remote_mac) /
404                               IP(id=7, len=21, flags="MF", frag=0, ttl=64,
405                                  src=self.src_if.remote_ip4,
406                                  dst=self.dst_if.remote_ip4) /
407                               ICMP(type="echo-request")),
408                              (Ether(dst=self.src_if.local_mac,
409                                     src=self.src_if.remote_mac) /
410                               IP(id=7, len=21, frag=1, ttl=64,
411                                  src=self.src_if.remote_ip4,
412                                  dst=self.dst_if.remote_ip4) /
413                               Raw(load='\x08')),
414                              ]
415
416         p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
417              IP(id=1000, src=self.src_if.remote_ip4,
418                 dst=self.dst_if.remote_ip4) /
419              UDP(sport=1234, dport=5678) /
420              Raw("X" * 1000))
421         valid_fragments = fragment_rfc791(p, 400)
422
423         self.pg_enable_capture()
424         self.src_if.add_stream(malformed_packets + valid_fragments)
425         self.pg_start()
426
427         self.dst_if.get_capture(1)
428
429         self.assert_packet_counter_equal("ip4-reassembly-feature", 1)
430         # TODO remove above, uncomment below once clearing of counters
431         # is supported
432         # self.assert_packet_counter_equal(
433         #     "/err/ip4-reassembly-feature/malformed packets", 1)
434
435     @parameterized.expand([(IP, None)])
436     def test_duplicates(self, family, stream):
437         """ duplicate fragments """
438         fragments = [
439             # IPv4 uses 4 fields in pkt_infos, IPv6 uses 3.
440             x for (_, frags, _, _) in self.pkt_infos
441             for x in frags
442             for _ in range(0, min(2, len(frags)))
443         ]
444         super(TestIPv4Reassembly, self).test_duplicates(family, fragments)
445
446     def test_overlap1(self):
447         """ overlapping fragments case #1 """
448
449         fragments = []
450         for _, _, frags_300, frags_200 in self.pkt_infos:
451             if len(frags_300) == 1:
452                 fragments.extend(frags_300)
453             else:
454                 for i, j in zip(frags_200, frags_300):
455                     fragments.extend(i)
456                     fragments.extend(j)
457
458         self.pg_enable_capture()
459         self.src_if.add_stream(fragments)
460         self.pg_start()
461
462         packets = self.dst_if.get_capture(len(self.pkt_infos))
463         self.verify_capture(IP, packets)
464         self.src_if.assert_nothing_captured()
465
466         # run it all to verify correctness
467         self.pg_enable_capture()
468         self.src_if.add_stream(fragments)
469         self.pg_start()
470
471         packets = self.dst_if.get_capture(len(self.pkt_infos))
472         self.verify_capture(IP, packets)
473         self.src_if.assert_nothing_captured()
474
475     def test_overlap2(self):
476         """ overlapping fragments case #2 """
477
478         fragments = []
479         for _, _, frags_300, frags_200 in self.pkt_infos:
480             if len(frags_300) == 1:
481                 fragments.extend(frags_300)
482             else:
483                 # care must be taken here so that there are no fragments
484                 # received by vpp after reassembly is finished, otherwise
485                 # new reassemblies will be started and packet generator will
486                 # freak out when it detects unfreed buffers
487                 zipped = zip(frags_300, frags_200)
488                 for i, j in zipped:
489                     fragments.extend(i)
490                     fragments.extend(j)
491                 fragments.pop()
492
493         self.pg_enable_capture()
494         self.src_if.add_stream(fragments)
495         self.pg_start()
496
497         packets = self.dst_if.get_capture(len(self.pkt_infos))
498         self.verify_capture(IP, packets)
499         self.src_if.assert_nothing_captured()
500
501         # run it all to verify correctness
502         self.pg_enable_capture()
503         self.src_if.add_stream(fragments)
504         self.pg_start()
505
506         packets = self.dst_if.get_capture(len(self.pkt_infos))
507         self.verify_capture(IP, packets)
508         self.src_if.assert_nothing_captured()
509
510     @parameterized.expand([(IP, None, None)])
511     def test_timeout_inline(self, family, stream, dropped_packet_indexes):
512         """ timeout (inline) """
513         stream = self.fragments_400
514
515         dropped_packet_indexes = set(
516             index for (index, frags, _, _) in self.pkt_infos if len(frags) > 1
517         )
518         super(TestIPv4Reassembly, self).test_timeout_inline(
519             family, stream, dropped_packet_indexes)
520
521         self.src_if.assert_nothing_captured()
522
523     def test_timeout_cleanup(self):
524         """ timeout (cleanup) """
525
526         # whole packets + fragmented packets sans last fragment
527         fragments = [
528             x for (_, frags_400, _, _) in self.pkt_infos
529             for x in frags_400[:-1 if len(frags_400) > 1 else None]
530         ]
531
532         # last fragments for fragmented packets
533         fragments2 = [frags_400[-1]
534                       for (_, frags_400, _, _) in self.pkt_infos
535                       if len(frags_400) > 1]
536
537         dropped_packet_indexes = set(
538             index for (index, frags_400, _, _) in self.pkt_infos
539             if len(frags_400) > 1)
540
541         self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
542                                     max_reassembly_length=1000,
543                                     expire_walk_interval_ms=50)
544
545         self.pg_enable_capture()
546         self.src_if.add_stream(fragments)
547         self.pg_start()
548
549         self.sleep(.25, "wait before sending rest of fragments")
550
551         self.src_if.add_stream(fragments2)
552         self.pg_start()
553
554         packets = self.dst_if.get_capture(
555             len(self.pkt_infos) - len(dropped_packet_indexes))
556         self.verify_capture(IP, packets, dropped_packet_indexes)
557         self.src_if.assert_nothing_captured()
558
559     @parameterized.expand([(IP, None, None)])
560     def test_disabled(self, family, stream, dropped_packet_indexes):
561         """ reassembly disabled """
562
563         stream = self.__class__.fragments_400
564         dropped_packet_indexes = set(
565             index for (index, frags_400, _, _) in self.pkt_infos
566             if len(frags_400) > 1)
567         super(TestIPv4Reassembly, self).test_disabled(
568             family, stream, dropped_packet_indexes)
569
570
571 class TestIPv6Reassembly(TestIPReassemblyMixin, VppTestCase):
572     """ IPv6 Reassembly """
573
574     @classmethod
575     def setUpClass(cls):
576         super(TestIPv6Reassembly, cls).setUpClass()
577
578         cls.create_pg_interfaces([0, 1])
579         cls.src_if = cls.pg0
580         cls.dst_if = cls.pg1
581
582         # setup all interfaces
583         for i in cls.pg_interfaces:
584             i.admin_up()
585             i.config_ip6()
586             i.resolve_ndp()
587
588         # packet sizes
589         cls.packet_sizes = [64, 512, 1518, 9018]
590         cls.padding = " abcdefghijklmn"
591         cls.create_stream(cls.packet_sizes)
592         cls.create_fragments()
593
594     @classmethod
595     def tearDownClass(cls):
596         super(TestIPv6Reassembly, cls).tearDownClass()
597
598     def setUp(self):
599         """ Test setup - force timeout on existing reassemblies """
600         super(TestIPv6Reassembly, self).setUp()
601         self.vapi.ip_reassembly_enable_disable(
602             sw_if_index=self.src_if.sw_if_index, enable_ip6=True)
603         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
604                                     max_reassembly_length=1000,
605                                     expire_walk_interval_ms=10, is_ip6=1)
606         self.sleep(.25)
607         self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
608                                     max_reassembly_length=1000,
609                                     expire_walk_interval_ms=10000, is_ip6=1)
610         self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
611         self.logger.debug(self.vapi.ppcli("show buffers"))
612
613     def tearDown(self):
614         super(TestIPv6Reassembly, self).tearDown()
615
616     def show_commands_at_teardown(self):
617         self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
618         self.logger.debug(self.vapi.ppcli("show buffers"))
619
620     @classmethod
621     def create_stream(cls, packet_sizes, packet_count=test_packet_count):
622         """Create input packet stream for defined interface.
623
624         :param list packet_sizes: Required packet sizes.
625         """
626         for i in range(0, packet_count):
627             info = cls.create_packet_info(cls.src_if, cls.src_if)
628             payload = cls.info_to_payload(info)
629             p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
630                  IPv6(src=cls.src_if.remote_ip6,
631                       dst=cls.dst_if.remote_ip6) /
632                  UDP(sport=1234, dport=5678) /
633                  Raw(payload))
634             size = packet_sizes[(i // 2) % len(packet_sizes)]
635             cls.extend_packet(p, size, cls.padding)
636             info.data = p
637
638     @classmethod
639     def create_fragments(cls):
640         infos = cls._packet_infos
641         cls.pkt_infos = []
642         for index, info in six.iteritems(infos):
643             p = info.data
644             # cls.logger.debug(ppp("Packet:",
645             #                      p.__class__(scapy.compat.raw(p))))
646             fragments_400 = fragment_rfc8200(p, info.index, 400)
647             fragments_300 = fragment_rfc8200(p, info.index, 300)
648             cls.pkt_infos.append((index, fragments_400, fragments_300))
649         cls.fragments_400 = [
650             x for _, frags, _ in cls.pkt_infos for x in frags]
651         cls.fragments_300 = [
652             x for _, _, frags in cls.pkt_infos for x in frags]
653         cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
654                          "and %s 300-byte fragments" %
655                          (len(infos), len(cls.fragments_400),
656                              len(cls.fragments_300)))
657
658     @parameterized.expand([(IPv6, None)])
659     def test_reassembly(self, family, stream):
660         """ basic reassembly """
661         stream = self.__class__.fragments_400
662         super(TestIPv6Reassembly, self).test_reassembly(family, stream)
663
664     @parameterized.expand([(IPv6, None)])
665     def test_reversed(self, family, stream):
666         """ reverse order reassembly """
667         stream = self.__class__.fragments_400
668         super(TestIPv6Reassembly, self).test_reversed(family, stream)
669
670     @parameterized.expand([(IPv6, None)])
671     def test_random(self, family, stream):
672         """ random order reassembly """
673         stream = self.__class__.fragments_400
674         super(TestIPv6Reassembly, self).test_random(family, stream)
675
676     @parameterized.expand([(IPv6, None)])
677     def test_duplicates(self, family, stream):
678         """ duplicate fragments """
679
680         fragments = [
681             # IPv4 uses 4 fields in pkt_infos, IPv6 uses 3.
682             x for (_, frags, _) in self.pkt_infos
683             for x in frags
684             for _ in range(0, min(2, len(frags)))
685         ]
686         super(TestIPv6Reassembly, self).test_duplicates(family, fragments)
687
688     def test_long_fragment_chain(self):
689         """ long fragment chain """
690
691         error_cnt_str = \
692             "/err/ip6-reassembly-feature/fragment chain too long (drop)"
693
694         error_cnt = self.get_packet_counter(error_cnt_str)
695
696         self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
697                                     max_reassembly_length=3,
698                                     expire_walk_interval_ms=50, is_ip6=1)
699
700         p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
701              IPv6(src=self.src_if.remote_ip6,
702                   dst=self.dst_if.remote_ip6) /
703              UDP(sport=1234, dport=5678) /
704              Raw("X" * 1000))
705         frags = fragment_rfc8200(p, 1, 300) + fragment_rfc8200(p, 2, 500)
706
707         self.pg_enable_capture()
708         self.src_if.add_stream(frags)
709         self.pg_start()
710
711         self.dst_if.get_capture(1)
712         self.assert_packet_counter_equal(error_cnt_str, error_cnt + 1)
713
714     def test_overlap1(self):
715         """ overlapping fragments case #1 (differs from IP test case)"""
716
717         fragments = []
718         for _, frags_400, frags_300 in self.pkt_infos:
719             if len(frags_300) == 1:
720                 fragments.extend(frags_400)
721             else:
722                 for i, j in zip(frags_300, frags_400):
723                     fragments.extend(i)
724                     fragments.extend(j)
725
726         dropped_packet_indexes = set(
727             index for (index, _, frags) in self.pkt_infos if len(frags) > 1
728         )
729
730         self.pg_enable_capture()
731         self.src_if.add_stream(fragments)
732         self.pg_start()
733
734         packets = self.dst_if.get_capture(
735             len(self.pkt_infos) - len(dropped_packet_indexes))
736         self.verify_capture(IPv6, packets, dropped_packet_indexes)
737         self.src_if.assert_nothing_captured()
738
739     def test_overlap2(self):
740         """ overlapping fragments case #2 (differs from IP test case)"""
741
742         fragments = []
743         for _, frags_400, frags_300 in self.pkt_infos:
744             if len(frags_400) == 1:
745                 fragments.extend(frags_400)
746             else:
747                 # care must be taken here so that there are no fragments
748                 # received by vpp after reassembly is finished, otherwise
749                 # new reassemblies will be started and packet generator will
750                 # freak out when it detects unfreed buffers
751                 zipped = zip(frags_400, frags_300)
752                 for i, j in zipped:
753                     fragments.extend(i)
754                     fragments.extend(j)
755                 fragments.pop()
756
757         dropped_packet_indexes = set(
758             index for (index, _, frags) in self.pkt_infos if len(frags) > 1
759         )
760
761         self.pg_enable_capture()
762         self.src_if.add_stream(fragments)
763         self.pg_start()
764
765         packets = self.dst_if.get_capture(
766             len(self.pkt_infos) - len(dropped_packet_indexes))
767         self.verify_capture(IPv6, packets, dropped_packet_indexes)
768         self.src_if.assert_nothing_captured()
769
770     @parameterized.expand([(IPv6, None, None)])
771     def test_timeout_inline(self, family, stream, dropped_packets_index):
772         """ timeout (inline) """
773         stream = self.__class__.fragments_400
774
775         dropped_packet_indexes = set(
776             index for (index, frags, _) in self.pkt_infos if len(frags) > 1
777         )
778         super(TestIPv6Reassembly, self).test_timeout_inline(
779             family, stream, dropped_packet_indexes)
780
781         pkts = self.src_if.get_capture(
782             expected_count=len(dropped_packet_indexes))
783         for icmp in pkts:
784             self.assertIn(ICMPv6TimeExceeded, icmp)
785             self.assertIn(IPv6ExtHdrFragment, icmp)
786             self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
787             dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
788
789     def test_timeout_cleanup(self):
790         """ timeout (cleanup) """
791
792         # whole packets + fragmented packets sans last fragment
793         fragments = [
794             x for (_, frags_400, _) in self.pkt_infos
795             for x in frags_400[:-1 if len(frags_400) > 1 else None]
796         ]
797
798         # last fragments for fragmented packets
799         fragments2 = [frags_400[-1]
800                       for (_, frags_400, _) in self.pkt_infos
801                       if len(frags_400) > 1]
802
803         dropped_packet_indexes = set(
804             index for (index, frags_400, _) in self.pkt_infos
805             if len(frags_400) > 1)
806
807         self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
808                                     max_reassembly_length=1000,
809                                     expire_walk_interval_ms=50)
810
811         self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
812                                     max_reassembly_length=1000,
813                                     expire_walk_interval_ms=50, is_ip6=1)
814
815         self.pg_enable_capture()
816         self.src_if.add_stream(fragments)
817         self.pg_start()
818
819         self.sleep(.25, "wait before sending rest of fragments")
820
821         self.src_if.add_stream(fragments2)
822         self.pg_start()
823
824         packets = self.dst_if.get_capture(
825             len(self.pkt_infos) - len(dropped_packet_indexes))
826         self.verify_capture(IPv6, packets, dropped_packet_indexes)
827         pkts = self.src_if.get_capture(
828             expected_count=len(dropped_packet_indexes))
829         for icmp in pkts:
830             self.assertIn(ICMPv6TimeExceeded, icmp)
831             self.assertIn(IPv6ExtHdrFragment, icmp)
832             self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
833             dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
834
835     @parameterized.expand([(IPv6, None, None)])
836     def test_disabled(self, family, stream, dropped_packet_indexes):
837         """ reassembly disabled """
838
839         stream = self.__class__.fragments_400
840         dropped_packet_indexes = set(
841             index for (index, frags_400, _) in self.pkt_infos
842             if len(frags_400) > 1)
843         super(TestIPv6Reassembly, self).test_disabled(
844             family, stream, dropped_packet_indexes)
845         self.src_if.assert_nothing_captured()
846
847     def test_missing_upper(self):
848         """ missing upper layer """
849         p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
850              IPv6(src=self.src_if.remote_ip6,
851                   dst=self.src_if.local_ip6) /
852              UDP(sport=1234, dport=5678) /
853              Raw())
854         self.extend_packet(p, 1000, self.padding)
855         fragments = fragment_rfc8200(p, 1, 500)
856         bad_fragment = p.__class__(scapy.compat.raw(fragments[1]))
857         bad_fragment[IPv6ExtHdrFragment].nh = 59
858         bad_fragment[IPv6ExtHdrFragment].offset = 0
859         self.pg_enable_capture()
860         self.src_if.add_stream([bad_fragment])
861         self.pg_start()
862         pkts = self.src_if.get_capture(expected_count=1)
863         icmp = pkts[0]
864         self.assertIn(ICMPv6ParamProblem, icmp)
865         self.assert_equal(icmp[ICMPv6ParamProblem].code, 3, "ICMP code")
866
867     def test_invalid_frag_size(self):
868         """ fragment size not a multiple of 8 """
869         p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
870              IPv6(src=self.src_if.remote_ip6,
871                   dst=self.src_if.local_ip6) /
872              UDP(sport=1234, dport=5678) /
873              Raw())
874         self.extend_packet(p, 1000, self.padding)
875         fragments = fragment_rfc8200(p, 1, 500)
876         bad_fragment = fragments[0]
877         self.extend_packet(bad_fragment, len(bad_fragment) + 5)
878         self.pg_enable_capture()
879         self.src_if.add_stream([bad_fragment])
880         self.pg_start()
881         pkts = self.src_if.get_capture(expected_count=1)
882         icmp = pkts[0]
883         self.assertIn(ICMPv6ParamProblem, icmp)
884         self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
885
886     def test_invalid_packet_size(self):
887         """ total packet size > 65535 """
888         p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
889              IPv6(src=self.src_if.remote_ip6,
890                   dst=self.src_if.local_ip6) /
891              UDP(sport=1234, dport=5678) /
892              Raw())
893         self.extend_packet(p, 1000, self.padding)
894         fragments = fragment_rfc8200(p, 1, 500)
895         bad_fragment = fragments[1]
896         bad_fragment[IPv6ExtHdrFragment].offset = 65500
897         self.pg_enable_capture()
898         self.src_if.add_stream([bad_fragment])
899         self.pg_start()
900         pkts = self.src_if.get_capture(expected_count=1)
901         icmp = pkts[0]
902         self.assertIn(ICMPv6ParamProblem, icmp)
903         self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
904
905
906 class TestIPv4ReassemblyLocalNode(VppTestCase):
907     """ IPv4 Reassembly for packets coming to ip4-local node """
908
909     @classmethod
910     def setUpClass(cls):
911         super(TestIPv4ReassemblyLocalNode, cls).setUpClass()
912
913         cls.create_pg_interfaces([0])
914         cls.src_dst_if = cls.pg0
915
916         # setup all interfaces
917         for i in cls.pg_interfaces:
918             i.admin_up()
919             i.config_ip4()
920             i.resolve_arp()
921
922         cls.padding = " abcdefghijklmn"
923         cls.create_stream()
924         cls.create_fragments()
925
926     @classmethod
927     def tearDownClass(cls):
928         super(TestIPv4ReassemblyLocalNode, cls).tearDownClass()
929
930     def setUp(self):
931         """ Test setup - force timeout on existing reassemblies """
932         super(TestIPv4ReassemblyLocalNode, self).setUp()
933         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
934                                     max_reassembly_length=1000,
935                                     expire_walk_interval_ms=10)
936         self.sleep(.25)
937         self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
938                                     max_reassembly_length=1000,
939                                     expire_walk_interval_ms=10000)
940
941     def tearDown(self):
942         super(TestIPv4ReassemblyLocalNode, self).tearDown()
943
944     def show_commands_at_teardown(self):
945         self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
946         self.logger.debug(self.vapi.ppcli("show buffers"))
947
948     @classmethod
949     def create_stream(cls, packet_count=test_packet_count):
950         """Create input packet stream for defined interface.
951
952         :param list packet_sizes: Required packet sizes.
953         """
954         for i in range(0, packet_count):
955             info = cls.create_packet_info(cls.src_dst_if, cls.src_dst_if)
956             payload = cls.info_to_payload(info)
957             p = (Ether(dst=cls.src_dst_if.local_mac,
958                        src=cls.src_dst_if.remote_mac) /
959                  IP(id=info.index, src=cls.src_dst_if.remote_ip4,
960                     dst=cls.src_dst_if.local_ip4) /
961                  ICMP(type='echo-request', id=1234) /
962                  Raw(payload))
963             cls.extend_packet(p, 1518, cls.padding)
964             info.data = p
965
966     @classmethod
967     def create_fragments(cls):
968         infos = cls._packet_infos
969         cls.pkt_infos = []
970         for index, info in six.iteritems(infos):
971             p = info.data
972             # cls.logger.debug(ppp("Packet:",
973             #                      p.__class__(scapy.compat.raw(p))))
974             fragments_300 = fragment_rfc791(p, 300)
975             cls.pkt_infos.append((index, fragments_300))
976         cls.fragments_300 = [x for (_, frags) in cls.pkt_infos for x in frags]
977         cls.logger.debug("Fragmented %s packets into %s 300-byte fragments" %
978                          (len(infos), len(cls.fragments_300)))
979
980     def verify_capture(self, capture):
981         """Verify captured packet stream.
982
983         :param list capture: Captured packet stream.
984         """
985         info = None
986         seen = set()
987         for packet in capture:
988             try:
989                 self.logger.debug(ppp("Got packet:", packet))
990                 ip = packet[IP]
991                 icmp = packet[ICMP]
992                 payload_info = self.payload_to_info(packet[Raw])
993                 packet_index = payload_info.index
994                 if packet_index in seen:
995                     raise Exception(ppp("Duplicate packet received", packet))
996                 seen.add(packet_index)
997                 self.assertEqual(payload_info.dst, self.src_dst_if.sw_if_index)
998                 info = self._packet_infos[packet_index]
999                 self.assertIsNotNone(info)
1000                 self.assertEqual(packet_index, info.index)
1001                 saved_packet = info.data
1002                 self.assertEqual(ip.src, saved_packet[IP].dst)
1003                 self.assertEqual(ip.dst, saved_packet[IP].src)
1004                 self.assertEqual(icmp.type, 0)  # echo reply
1005                 self.assertEqual(icmp.id, saved_packet[ICMP].id)
1006                 self.assertEqual(icmp.payload, saved_packet[ICMP].payload)
1007             except Exception:
1008                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1009                 raise
1010         for index in self._packet_infos:
1011             self.assertIn(index, seen,
1012                           "Packet with packet_index %d not received" % index)
1013
1014     def test_reassembly(self):
1015         """ basic reassembly """
1016
1017         self.pg_enable_capture()
1018         self.src_dst_if.add_stream(self.fragments_300)
1019         self.pg_start()
1020
1021         packets = self.src_dst_if.get_capture(len(self.pkt_infos))
1022         self.verify_capture(packets)
1023
1024         # run it all again to verify correctness
1025         self.pg_enable_capture()
1026         self.src_dst_if.add_stream(self.fragments_300)
1027         self.pg_start()
1028
1029         packets = self.src_dst_if.get_capture(len(self.pkt_infos))
1030         self.verify_capture(packets)
1031
1032
1033 class TestFIFReassembly(VppTestCase):
1034     """ Fragments in fragments reassembly """
1035
1036     @classmethod
1037     def setUpClass(cls):
1038         super(TestFIFReassembly, cls).setUpClass()
1039
1040         cls.create_pg_interfaces([0, 1])
1041         cls.src_if = cls.pg0
1042         cls.dst_if = cls.pg1
1043         for i in cls.pg_interfaces:
1044             i.admin_up()
1045             i.config_ip4()
1046             i.resolve_arp()
1047             i.config_ip6()
1048             i.resolve_ndp()
1049
1050         cls.packet_sizes = [64, 512, 1518, 9018]
1051         cls.padding = " abcdefghijklmn"
1052
1053     @classmethod
1054     def tearDownClass(cls):
1055         super(TestFIFReassembly, cls).tearDownClass()
1056
1057     def setUp(self):
1058         """ Test setup - force timeout on existing reassemblies """
1059         super(TestFIFReassembly, self).setUp()
1060         self.vapi.ip_reassembly_enable_disable(
1061             sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
1062             enable_ip6=True)
1063         self.vapi.ip_reassembly_enable_disable(
1064             sw_if_index=self.dst_if.sw_if_index, enable_ip4=True,
1065             enable_ip6=True)
1066         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1067                                     max_reassembly_length=1000,
1068                                     expire_walk_interval_ms=10)
1069         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1070                                     max_reassembly_length=1000,
1071                                     expire_walk_interval_ms=10, is_ip6=1)
1072         self.sleep(.25)
1073         self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1074                                     max_reassembly_length=1000,
1075                                     expire_walk_interval_ms=10000)
1076         self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1077                                     max_reassembly_length=1000,
1078                                     expire_walk_interval_ms=10000, is_ip6=1)
1079
1080     def tearDown(self):
1081         super(TestFIFReassembly, self).tearDown()
1082
1083     def show_commands_at_teardown(self):
1084         self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
1085         self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
1086         self.logger.debug(self.vapi.ppcli("show buffers"))
1087
1088     def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):
1089         """Verify captured packet stream.
1090
1091         :param list capture: Captured packet stream.
1092         """
1093         info = None
1094         seen = set()
1095         for packet in capture:
1096             try:
1097                 self.logger.debug(ppp("Got packet:", packet))
1098                 ip = packet[ip_class]
1099                 udp = packet[UDP]
1100                 payload_info = self.payload_to_info(packet[Raw])
1101                 packet_index = payload_info.index
1102                 self.assertTrue(
1103                     packet_index not in dropped_packet_indexes,
1104                     ppp("Packet received, but should be dropped:", packet))
1105                 if packet_index in seen:
1106                     raise Exception(ppp("Duplicate packet received", packet))
1107                 seen.add(packet_index)
1108                 self.assertEqual(payload_info.dst, self.dst_if.sw_if_index)
1109                 info = self._packet_infos[packet_index]
1110                 self.assertTrue(info is not None)
1111                 self.assertEqual(packet_index, info.index)
1112                 saved_packet = info.data
1113                 self.assertEqual(ip.src, saved_packet[ip_class].src)
1114                 self.assertEqual(ip.dst, saved_packet[ip_class].dst)
1115                 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1116             except Exception:
1117                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1118                 raise
1119         for index in self._packet_infos:
1120             self.assertTrue(index in seen or index in dropped_packet_indexes,
1121                             "Packet with packet_index %d not received" % index)
1122
1123     def test_fif4(self):
1124         """ Fragments in fragments (4o4) """
1125
1126         # TODO this should be ideally in setUpClass, but then we hit a bug
1127         # with VppIpRoute incorrectly reporting it's present when it's not
1128         # so we need to manually remove the vpp config, thus we cannot have
1129         # it shared for multiple test cases
1130         self.tun_ip4 = "1.1.1.2"
1131
1132         self.gre4 = VppGreInterface(self, self.src_if.local_ip4, self.tun_ip4)
1133         self.gre4.add_vpp_config()
1134         self.gre4.admin_up()
1135         self.gre4.config_ip4()
1136
1137         self.vapi.ip_reassembly_enable_disable(
1138             sw_if_index=self.gre4.sw_if_index, enable_ip4=True)
1139
1140         self.route4 = VppIpRoute(self, self.tun_ip4, 32,
1141                                  [VppRoutePath(self.src_if.remote_ip4,
1142                                                self.src_if.sw_if_index)])
1143         self.route4.add_vpp_config()
1144
1145         self.reset_packet_infos()
1146         for i in range(test_packet_count):
1147             info = self.create_packet_info(self.src_if, self.dst_if)
1148             payload = self.info_to_payload(info)
1149             # Ethernet header here is only for size calculation, thus it
1150             # doesn't matter how it's initialized. This is to ensure that
1151             # reassembled packet is not > 9000 bytes, so that it's not dropped
1152             p = (Ether() /
1153                  IP(id=i, src=self.src_if.remote_ip4,
1154                     dst=self.dst_if.remote_ip4) /
1155                  UDP(sport=1234, dport=5678) /
1156                  Raw(payload))
1157             size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1158             self.extend_packet(p, size, self.padding)
1159             info.data = p[IP]  # use only IP part, without ethernet header
1160
1161         fragments = [x for _, p in six.iteritems(self._packet_infos)
1162                      for x in fragment_rfc791(p.data, 400)]
1163
1164         encapped_fragments = \
1165             [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1166              IP(src=self.tun_ip4, dst=self.src_if.local_ip4) /
1167                 GRE() /
1168                 p
1169                 for p in fragments]
1170
1171         fragmented_encapped_fragments = \
1172             [x for p in encapped_fragments
1173              for x in fragment_rfc791(p, 200)]
1174
1175         self.src_if.add_stream(fragmented_encapped_fragments)
1176
1177         self.pg_enable_capture(self.pg_interfaces)
1178         self.pg_start()
1179
1180         self.src_if.assert_nothing_captured()
1181         packets = self.dst_if.get_capture(len(self._packet_infos))
1182         self.verify_capture(packets, IP)
1183
1184         # TODO remove gre vpp config by hand until VppIpRoute gets fixed
1185         # so that it's query_vpp_config() works as it should
1186         self.gre4.remove_vpp_config()
1187         self.logger.debug(self.vapi.ppcli("show interface"))
1188
1189     def test_fif6(self):
1190         """ Fragments in fragments (6o6) """
1191         # TODO this should be ideally in setUpClass, but then we hit a bug
1192         # with VppIpRoute incorrectly reporting it's present when it's not
1193         # so we need to manually remove the vpp config, thus we cannot have
1194         # it shared for multiple test cases
1195         self.tun_ip6 = "1002::1"
1196
1197         self.gre6 = VppGreInterface(self, self.src_if.local_ip6, self.tun_ip6)
1198         self.gre6.add_vpp_config()
1199         self.gre6.admin_up()
1200         self.gre6.config_ip6()
1201
1202         self.vapi.ip_reassembly_enable_disable(
1203             sw_if_index=self.gre6.sw_if_index, enable_ip6=True)
1204
1205         self.route6 = VppIpRoute(self, self.tun_ip6, 128,
1206                                  [VppRoutePath(self.src_if.remote_ip6,
1207                                                self.src_if.sw_if_index,
1208                                                proto=DpoProto.DPO_PROTO_IP6)],
1209                                  is_ip6=1)
1210         self.route6.add_vpp_config()
1211
1212         self.reset_packet_infos()
1213         for i in range(test_packet_count):
1214             info = self.create_packet_info(self.src_if, self.dst_if)
1215             payload = self.info_to_payload(info)
1216             # Ethernet header here is only for size calculation, thus it
1217             # doesn't matter how it's initialized. This is to ensure that
1218             # reassembled packet is not > 9000 bytes, so that it's not dropped
1219             p = (Ether() /
1220                  IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1221                  UDP(sport=1234, dport=5678) /
1222                  Raw(payload))
1223             size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1224             self.extend_packet(p, size, self.padding)
1225             info.data = p[IPv6]  # use only IPv6 part, without ethernet header
1226
1227         fragments = [x for _, i in six.iteritems(self._packet_infos)
1228                      for x in fragment_rfc8200(
1229                          i.data, i.index, 400)]
1230
1231         encapped_fragments = \
1232             [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1233              IPv6(src=self.tun_ip6, dst=self.src_if.local_ip6) /
1234                 GRE() /
1235                 p
1236                 for p in fragments]
1237
1238         fragmented_encapped_fragments = \
1239             [x for p in encapped_fragments for x in (
1240                 fragment_rfc8200(
1241                     p,
1242                     2 * len(self._packet_infos) + p[IPv6ExtHdrFragment].id,
1243                     200)
1244                 if IPv6ExtHdrFragment in p else [p]
1245             )
1246             ]
1247
1248         self.src_if.add_stream(fragmented_encapped_fragments)
1249
1250         self.pg_enable_capture(self.pg_interfaces)
1251         self.pg_start()
1252
1253         self.src_if.assert_nothing_captured()
1254         packets = self.dst_if.get_capture(len(self._packet_infos))
1255         self.verify_capture(packets, IPv6)
1256
1257         # TODO remove gre vpp config by hand until VppIpRoute gets fixed
1258         # so that it's query_vpp_config() works as it should
1259         self.gre6.remove_vpp_config()
1260
1261
1262 if __name__ == '__main__':
1263     unittest.main(testRunner=VppTestRunner)