FIB Interpose Source
[vpp.git] / test / test_reassembly.py.disabled
1 #!/usr/bin/env python
2 import unittest
3 from random import shuffle
4
5 from framework import VppTestCase, VppTestRunner
6
7 from scapy.packet import Raw
8 from scapy.layers.l2 import Ether, GRE
9 from scapy.layers.inet import IP, UDP
10 from util import ppp, fragment_rfc791, fragment_rfc8200
11 from vpp_punt_socket import VppUDSPuntSocket
12 from scapy.layers.inet6 import IPv6, IPv6ExtHdrFragment, ICMPv6ParamProblem,\
13     ICMPv6TimeExceeded
14 from vpp_gre_interface import VppGreInterface, VppGre6Interface
15 from vpp_ip_route import VppIpRoute, VppRoutePath, DpoProto
16
17 test_packet_count = 257
18
19
20 class TestIPv4Reassembly(VppTestCase):
21     """ IPv4 Reassembly """
22
23     @classmethod
24     def setUpClass(cls):
25         super(TestIPv4Reassembly, cls).setUpClass()
26
27         cls.create_pg_interfaces([0])
28         cls.pg_if = cls.pg0
29
30         # setup all interfaces
31         for i in cls.pg_interfaces:
32             i.admin_up()
33             i.config_ip4()
34             i.resolve_arp()
35
36         cls.punt_port = 9999
37         cls.punt_socket = VppUDSPuntSocket(cls, cls.punt_port)
38
39         # packet sizes
40         cls.packet_sizes = [64, 512, 1518, 9018]
41         cls.padding = " abcdefghijklmn"
42         cls.create_stream(cls.packet_sizes)
43         cls.create_fragments()
44
45     def setUp(self):
46         """ Test setup - force timeout on existing reassemblies """
47         super(TestIPv4Reassembly, self).setUp()
48         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
49                                     expire_walk_interval_ms=10)
50         self.sleep(.25)
51         self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
52                                     expire_walk_interval_ms=10000)
53
54     def tearDown(self):
55         super(TestIPv4Reassembly, self).tearDown()
56         self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
57
58     @classmethod
59     def create_stream(cls, packet_sizes, packet_count=test_packet_count):
60         """Create input packet stream for defined interface.
61
62         :param list packet_sizes: Required packet sizes.
63         """
64         for i in range(0, packet_count):
65             info = cls.create_packet_info(cls.pg_if, cls.pg_if)
66             payload = cls.info_to_payload(info)
67             p = (Ether(dst=cls.pg_if.local_mac, src=cls.pg_if.remote_mac) /
68                  IP(id=info.index, src=cls.pg_if.remote_ip4,
69                     dst=cls.pg_if.local_ip4) /
70                  UDP(sport=1234, dport=cls.punt_port) /
71                  Raw(payload))
72             size = packet_sizes[(i // 2) % len(packet_sizes)]
73             cls.extend_packet(p, size, cls.padding)
74             info.data = p
75
76     @classmethod
77     def create_fragments(cls):
78         infos = cls._packet_infos
79         cls.pkt_infos = []
80         for index, info in infos.iteritems():
81             p = info.data
82             # self.logger.debug(ppp("Packet:", p.__class__(str(p))))
83             fragments_400 = fragment_rfc791(p, 400)
84             fragments_300 = fragment_rfc791(p, 300)
85             fragments_200 = [
86                 x for f in fragments_400 for x in fragment_rfc791(f, 200)]
87             cls.pkt_infos.append(
88                 (index, fragments_400, fragments_300, fragments_200))
89         cls.fragments_400 = [
90             x for (_, frags, _, _) in cls.pkt_infos for x in frags]
91         cls.fragments_300 = [
92             x for (_, _, frags, _) in cls.pkt_infos for x in frags]
93         cls.fragments_200 = [
94             x for (_, _, _, frags) in cls.pkt_infos for x in frags]
95         cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
96                          "%s 300-byte fragments and %s 200-byte fragments" %
97                          (len(infos), len(cls.fragments_400),
98                              len(cls.fragments_300), len(cls.fragments_200)))
99
100     def verify_capture(self, capture, dropped_packet_indexes=[]):
101         """Verify captured packet stream.
102
103         :param list capture: Captured packet stream.
104         """
105         info = None
106         seen = set()
107         for packet in capture:
108             try:
109                 sw_if_index = packet['sw_if_index']
110                 punt_action = packet['punt_action']
111                 packet = Ether(packet['packet'])
112                 self.logger.debug(ppp("Got packet from %s, action %s" %
113                                       (sw_if_index, punt_action), packet))
114                 ip = packet[IP]
115                 udp = packet[UDP]
116                 payload_info = self.payload_to_info(str(packet[Raw]))
117                 packet_index = payload_info.index
118                 self.assertTrue(
119                     packet_index not in dropped_packet_indexes,
120                     ppp("Packet received, but should be dropped:", packet))
121                 if packet_index in seen:
122                     raise Exception(ppp("Duplicate packet received", packet))
123                 seen.add(packet_index)
124                 self.assertEqual(payload_info.dst, self.pg_if.sw_if_index)
125                 info = self._packet_infos[packet_index]
126                 self.assertTrue(info is not None)
127                 self.assertEqual(packet_index, info.index)
128                 saved_packet = info.data
129                 self.assertEqual(ip.src, saved_packet[IP].src)
130                 self.assertEqual(ip.dst, saved_packet[IP].dst)
131                 self.assertEqual(udp.payload, saved_packet[UDP].payload)
132             except:
133                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
134                 raise
135         for index in self._packet_infos:
136             self.assertTrue(index in seen or index in dropped_packet_indexes,
137                             "Packet with packet_index %d not received" % index)
138
139     def test_reassembly(self):
140         """ basic reassembly """
141
142         self.pg_enable_capture()
143         self.pg_if.add_stream(self.fragments_200)
144         self.pg_start()
145
146         packets = self.punt_socket.wait_for_packets(len(self.pkt_infos))
147         self.verify_capture(packets)
148         self.pg_if.assert_nothing_captured()
149
150         # run it all again to verify correctness
151         self.pg_enable_capture()
152         self.pg_if.add_stream(self.fragments_200)
153         self.pg_start()
154
155         packets = self.punt_socket.wait_for_packets(len(self.pkt_infos))
156         self.verify_capture(packets)
157         self.pg_if.assert_nothing_captured()
158
159     def test_reversed(self):
160         """ reverse order reassembly """
161
162         fragments = list(self.fragments_200)
163         fragments.reverse()
164
165         self.pg_enable_capture()
166         self.pg_if.add_stream(fragments)
167         self.pg_start()
168
169         packets = self.punt_socket.wait_for_packets(len(self.packet_infos))
170         self.verify_capture(packets)
171         self.pg_if.assert_nothing_captured()
172
173         # run it all again to verify correctness
174         self.pg_enable_capture()
175         self.pg_if.add_stream(fragments)
176         self.pg_start()
177
178         packets = self.punt_socket.wait_for_packets(len(self.packet_infos))
179         self.verify_capture(packets)
180         self.pg_if.assert_nothing_captured()
181
182     def test_random(self):
183         """ random order reassembly """
184
185         fragments = list(self.fragments_200)
186         shuffle(fragments)
187
188         self.pg_enable_capture()
189         self.pg_if.add_stream(fragments)
190         self.pg_start()
191
192         packets = self.punt_socket.wait_for_packets(len(self.packet_infos))
193         self.verify_capture(packets)
194         self.pg_if.assert_nothing_captured()
195
196         # run it all again to verify correctness
197         self.pg_enable_capture()
198         self.pg_if.add_stream(fragments)
199         self.pg_start()
200
201         packets = self.punt_socket.wait_for_packets(len(self.packet_infos))
202         self.verify_capture(packets)
203         self.pg_if.assert_nothing_captured()
204
205     def test_duplicates(self):
206         """ duplicate fragments """
207
208         fragments = [
209             x for (_, frags, _, _) in self.pkt_infos
210             for x in frags
211             for _ in range(0, min(2, len(frags)))
212         ]
213
214         self.pg_enable_capture()
215         self.pg_if.add_stream(fragments)
216         self.pg_start()
217
218         packets = self.punt_socket.wait_for_packets(len(self.pkt_infos))
219         self.verify_capture(packets)
220         self.pg_if.assert_nothing_captured()
221
222     def test_overlap1(self):
223         """ overlapping fragments case #1 """
224
225         fragments = []
226         for _, _, frags_300, frags_200 in self.pkt_infos:
227             if len(frags_300) == 1:
228                 fragments.extend(frags_300)
229             else:
230                 for i, j in zip(frags_200, frags_300):
231                     fragments.extend(i)
232                     fragments.extend(j)
233
234         self.pg_enable_capture()
235         self.pg_if.add_stream(fragments)
236         self.pg_start()
237
238         packets = self.punt_socket.wait_for_packets(len(self.pkt_infos))
239         self.verify_capture(packets)
240         self.pg_if.assert_nothing_captured()
241
242         # run it all to verify correctness
243         self.pg_enable_capture()
244         self.pg_if.add_stream(fragments)
245         self.pg_start()
246
247         packets = self.punt_socket.wait_for_packets(len(self.pkt_infos))
248         self.verify_capture(packets)
249         self.pg_if.assert_nothing_captured()
250
251     def test_overlap2(self):
252         """ overlapping fragments case #2 """
253
254         fragments = []
255         for _, _, frags_300, frags_200 in self.pkt_infos:
256             if len(frags_300) == 1:
257                 fragments.extend(frags_300)
258             else:
259                 # care must be taken here so that there are no fragments
260                 # received by vpp after reassembly is finished, otherwise
261                 # new reassemblies will be started and packet generator will
262                 # freak out when it detects unfreed buffers
263                 zipped = zip(frags_300, frags_200)
264                 for i, j in zipped[:-1]:
265                     fragments.extend(i)
266                     fragments.extend(j)
267                 fragments.append(zipped[-1][0])
268
269         self.pg_enable_capture()
270         self.pg_if.add_stream(fragments)
271         self.pg_start()
272
273         packets = self.punt_socket.wait_for_packets(len(self.pkt_infos))
274         self.verify_capture(packets)
275         self.pg_if.assert_nothing_captured()
276
277         # run it all to verify correctness
278         self.pg_enable_capture()
279         self.pg_if.add_stream(fragments)
280         self.pg_start()
281
282         packets = self.punt_socket.wait_for_packets(len(self.pkt_infos))
283         self.verify_capture(packets)
284         self.pg_if.assert_nothing_captured()
285
286     def test_timeout_inline(self):
287         """ timeout (inline) """
288
289         dropped_packet_indexes = set(
290             index for (index, frags, _, _) in self.pkt_infos if len(frags) > 1
291         )
292
293         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
294                                     expire_walk_interval_ms=10000)
295
296         self.pg_enable_capture()
297         self.pg_if.add_stream(self.fragments_400)
298         self.pg_start()
299
300         packets = self.punt_socket.wait_for_packets(
301             len(self.pkt_infos) - len(dropped_packet_indexes))
302         self.verify_capture(packets, dropped_packet_indexes)
303         self.pg_if.assert_nothing_captured()
304
305     def test_timeout_cleanup(self):
306         """ timeout (cleanup) """
307
308         # whole packets + fragmented packets sans last fragment
309         fragments = [
310             x for (_, frags_400, _, _) in self.pkt_infos
311             for x in frags_400[:-1 if len(frags_400) > 1 else None]
312         ]
313
314         # last fragments for fragmented packets
315         fragments2 = [frags_400[-1]
316                       for (_, frags_400, _, _) in self.pkt_infos
317                       if len(frags_400) > 1]
318
319         dropped_packet_indexes = set(
320             index for (index, frags_400, _, _) in self.pkt_infos
321             if len(frags_400) > 1)
322
323         self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
324                                     expire_walk_interval_ms=50)
325
326         self.pg_enable_capture()
327         self.pg_if.add_stream(fragments)
328         self.pg_start()
329
330         self.sleep(.25, "wait before sending rest of fragments")
331
332         self.pg_if.add_stream(fragments2)
333         self.pg_start()
334         self.sleep(.25, "wait for vpp to process packets")
335
336         packets = self.punt_socket.wait_for_packets(
337             len(self.pkt_infos) - len(dropped_packet_indexes))
338         self.verify_capture(packets, dropped_packet_indexes)
339         self.pg_if.assert_nothing_captured()
340
341     def test_disabled(self):
342         """ reassembly disabled """
343
344         dropped_packet_indexes = set(
345             index for (index, frags_400, _, _) in self.pkt_infos
346             if len(frags_400) > 1)
347
348         self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
349                                     expire_walk_interval_ms=10000)
350
351         self.pg_enable_capture()
352         self.pg_if.add_stream(self.fragments_400)
353         self.pg_start()
354
355         packets = self.punt_socket.wait_for_packets(
356             len(self.pkt_infos) - len(dropped_packet_indexes))
357         self.verify_capture(packets, dropped_packet_indexes)
358         self.pg_if.assert_nothing_captured()
359
360
361 class TestIPv6Reassembly(VppTestCase):
362     """ IPv6 Reassembly """
363
364     @classmethod
365     def setUpClass(cls):
366         super(TestIPv6Reassembly, cls).setUpClass()
367
368         cls.create_pg_interfaces([0])
369         cls.pg_if = cls.pg0
370
371         # setup all interfaces
372         for i in cls.pg_interfaces:
373             i.admin_up()
374             i.config_ip6()
375             i.resolve_ndp()
376
377         cls.punt_port = 9999
378         cls.punt_socket = VppUDSPuntSocket(cls, cls.punt_port, is_ip4=0)
379
380         # packet sizes
381         cls.packet_sizes = [64, 512, 1518, 9018]
382         cls.padding = " abcdefghijklmn"
383         cls.create_stream(cls.packet_sizes)
384         cls.create_fragments()
385
386     def setUp(self):
387         """ Test setup - force timeout on existing reassemblies """
388         super(TestIPv6Reassembly, self).setUp()
389         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
390                                     expire_walk_interval_ms=10, is_ip6=1)
391         self.sleep(.25)
392         self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
393                                     expire_walk_interval_ms=10000, is_ip6=1)
394
395     def tearDown(self):
396         super(TestIPv6Reassembly, self).tearDown()
397         self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
398
399     @classmethod
400     def create_stream(cls, packet_sizes, packet_count=test_packet_count):
401         """Create input packet stream for defined interface.
402
403         :param list packet_sizes: Required packet sizes.
404         """
405         for i in range(0, packet_count):
406             info = cls.create_packet_info(cls.pg_if, cls.pg_if)
407             payload = cls.info_to_payload(info)
408             p = (Ether(dst=cls.pg_if.local_mac, src=cls.pg_if.remote_mac) /
409                  IPv6(src=cls.pg_if.remote_ip6,
410                       dst=cls.pg_if.local_ip6) /
411                  UDP(sport=1234, dport=cls.punt_port) /
412                  Raw(payload))
413             size = packet_sizes[(i // 2) % len(packet_sizes)]
414             cls.extend_packet(p, size, cls.padding)
415             info.data = p
416
417     @classmethod
418     def create_fragments(cls):
419         infos = cls._packet_infos
420         cls.pkt_infos = []
421         for index, info in infos.iteritems():
422             p = info.data
423             # self.logger.debug(ppp("Packet:", p.__class__(str(p))))
424             fragments_400 = fragment_rfc8200(p, info.index, 400)
425             fragments_300 = fragment_rfc8200(p, info.index, 300)
426             cls.pkt_infos.append((index, fragments_400, fragments_300))
427         cls.fragments_400 = [
428             x for _, frags, _ in cls.pkt_infos for x in frags]
429         cls.fragments_300 = [
430             x for _, _, frags in cls.pkt_infos for x in frags]
431         cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
432                          "and %s 300-byte fragments" %
433                          (len(infos), len(cls.fragments_400),
434                              len(cls.fragments_300)))
435
436     def verify_capture(self, capture, dropped_packet_indexes=[]):
437         """Verify captured packet strea .
438
439         :param list capture: Captured packet stream.
440         """
441         info = None
442         seen = set()
443         for packet in capture:
444             try:
445                 sw_if_index = packet['sw_if_index']
446                 punt_action = packet['punt_action']
447                 packet = Ether(packet['packet'])
448                 self.logger.debug(ppp("Got packet from %s, action %s" %
449                                       (sw_if_index, punt_action), packet))
450                 ip = packet[IPv6]
451                 udp = packet[UDP]
452                 payload_info = self.payload_to_info(str(packet[Raw]))
453                 packet_index = payload_info.index
454                 self.assertTrue(
455                     packet_index not in dropped_packet_indexes,
456                     ppp("Packet received, but should be dropped:", packet))
457                 if packet_index in seen:
458                     raise Exception(ppp("Duplicate packet received", packet))
459                 seen.add(packet_index)
460                 self.assertEqual(payload_info.dst, self.pg_if.sw_if_index)
461                 info = self._packet_infos[packet_index]
462                 self.assertTrue(info is not None)
463                 self.assertEqual(packet_index, info.index)
464                 saved_packet = info.data
465                 self.assertEqual(ip.src, saved_packet[IPv6].src)
466                 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
467                 self.assertEqual(udp.payload, saved_packet[UDP].payload)
468             except:
469                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
470                 raise
471         for index in self._packet_infos:
472             self.assertTrue(index in seen or index in dropped_packet_indexes,
473                             "Packet with packet_index %d not received" % index)
474
475     def test_reassembly(self):
476         """ basic reassembly """
477
478         self.pg_enable_capture()
479         self.pg_if.add_stream(self.fragments_400)
480         self.pg_start()
481
482         packets = self.punt_socket.wait_for_packets(len(self.pkt_infos))
483         self.verify_capture(packets)
484         self.pg_if.assert_nothing_captured()
485
486         # run it all again to verify correctness
487         self.pg_enable_capture()
488         self.pg_if.add_stream(self.fragments_400)
489         self.pg_start()
490
491         packets = self.punt_socket.wait_for_packets(len(self.pkt_infos))
492         self.verify_capture(packets)
493         self.pg_if.assert_nothing_captured()
494
495     def test_reversed(self):
496         """ reverse order reassembly """
497
498         fragments = list(self.fragments_400)
499         fragments.reverse()
500
501         self.pg_enable_capture()
502         self.pg_if.add_stream(fragments)
503         self.pg_start()
504
505         packets = self.punt_socket.wait_for_packets(len(self.pkt_infos))
506         self.verify_capture(packets)
507         self.pg_if.assert_nothing_captured()
508
509         # run it all again to verify correctness
510         self.pg_enable_capture()
511         self.pg_if.add_stream(fragments)
512         self.pg_start()
513
514         packets = self.punt_socket.wait_for_packets(len(self.pkt_infos))
515         self.verify_capture(packets)
516         self.pg_if.assert_nothing_captured()
517
518     def test_random(self):
519         """ random order reassembly """
520
521         fragments = list(self.fragments_400)
522         shuffle(fragments)
523
524         self.pg_enable_capture()
525         self.pg_if.add_stream(fragments)
526         self.pg_start()
527
528         packets = self.punt_socket.wait_for_packets(len(self.pkt_infos))
529         self.verify_capture(packets)
530         self.pg_if.assert_nothing_captured()
531
532         # run it all again to verify correctness
533         self.pg_enable_capture()
534         self.pg_if.add_stream(fragments)
535         self.pg_start()
536
537         packets = self.punt_socket.wait_for_packets(len(self.pkt_infos))
538         self.verify_capture(packets)
539         self.pg_if.assert_nothing_captured()
540
541     def test_duplicates(self):
542         """ duplicate fragments """
543
544         fragments = [
545             x for (_, frags, _) in self.pkt_infos
546             for x in frags
547             for _ in range(0, min(2, len(frags)))
548         ]
549
550         self.pg_enable_capture()
551         self.pg_if.add_stream(fragments)
552         self.pg_start()
553
554         packets = self.punt_socket.wait_for_packets(len(self.pkt_infos))
555         self.verify_capture(packets)
556         self.pg_if.assert_nothing_captured()
557
558     def test_overlap1(self):
559         """ overlapping fragments case #1 """
560
561         fragments = []
562         for _, frags_400, frags_300 in self.pkt_infos:
563             if len(frags_300) == 1:
564                 fragments.extend(frags_400)
565             else:
566                 for i, j in zip(frags_300, frags_400):
567                     fragments.extend(i)
568                     fragments.extend(j)
569
570         dropped_packet_indexes = set(
571             index for (index, _, frags) in self.pkt_infos if len(frags) > 1
572         )
573
574         self.pg_enable_capture()
575         self.pg_if.add_stream(fragments)
576         self.pg_start()
577
578         self.sleep(.1, "wait for vpp to process packets")
579         packets = self.punt_socket.wait_for_packets(
580             len(self.pkt_infos) - len(dropped_packet_indexes))
581         self.verify_capture(packets, dropped_packet_indexes)
582         self.pg_if.assert_nothing_captured()
583
584     def test_overlap2(self):
585         """ overlapping fragments case #2 """
586
587         fragments = []
588         for _, frags_400, frags_30 in self.pkt_infos:
589             if len(frags_400) == 1:
590                 fragments.extend(frags_400)
591             else:
592                 # care must be taken here so that there are no fragments
593                 # received by vpp after reassembly is finished, otherwise
594                 # new reassemblies will be started and packet generator will
595                 # freak out when it detects unfreed buffers
596                 zipped = zip(frags_400, frags_30)
597                 for i, j in zipped[:-1]:
598                     fragments.extend(i)
599                     fragments.extend(j)
600                 fragments.append(zipped[-1][0])
601
602         dropped_packet_indexes = set(
603             index for (index, _, frags) in self.pkt_infos if len(frags) > 1
604         )
605
606         self.pg_enable_capture()
607         self.pg_if.add_stream(fragments)
608         self.pg_start()
609
610         self.sleep(.1, "wait for vpp to process packets")
611         packets = self.punt_socket.wait_for_packets(
612             len(self.pkt_infos) - len(dropped_packet_indexes))
613         self.verify_capture(packets, dropped_packet_indexes)
614         self.pg_if.assert_nothing_captured()
615
616     def test_timeout_inline(self):
617         """ timeout (inline) """
618
619         dropped_packet_indexes = set(
620             index for (index, frags, _) in self.pkt_infos if len(frags) > 1
621         )
622
623         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
624                                     expire_walk_interval_ms=10000, is_ip6=1)
625
626         self.pg_enable_capture()
627         self.pg_if.add_stream(self.fragments_400)
628         self.pg_start()
629
630         packets = self.punt_socket.wait_for_packets(
631             len(self.pkt_infos) - len(dropped_packet_indexes))
632         self.verify_capture(packets, dropped_packet_indexes)
633         pkts = self.pg_if.get_capture(
634             expected_count=len(dropped_packet_indexes))
635         for icmp in pkts:
636             self.assertIn(ICMPv6TimeExceeded, icmp)
637             self.assertIn(IPv6ExtHdrFragment, icmp)
638             self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
639             dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
640
641     def test_timeout_cleanup(self):
642         """ timeout (cleanup) """
643
644         # whole packets + fragmented packets sans last fragment
645         fragments = [
646             x for (_, frags_400, _) in self.pkt_infos
647             for x in frags_400[:-1 if len(frags_400) > 1 else None]
648         ]
649
650         # last fragments for fragmented packets
651         fragments2 = [frags_400[-1]
652                       for (_, frags_400, _) in self.pkt_infos
653                       if len(frags_400) > 1]
654
655         dropped_packet_indexes = set(
656             index for (index, frags_400, _) in self.pkt_infos
657             if len(frags_400) > 1)
658
659         self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
660                                     expire_walk_interval_ms=50)
661
662         self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
663                                     expire_walk_interval_ms=50, is_ip6=1)
664
665         self.pg_enable_capture()
666         self.pg_if.add_stream(fragments)
667         self.pg_start()
668
669         self.sleep(.25, "wait before sending rest of fragments")
670
671         self.pg_if.add_stream(fragments2)
672         self.pg_start()
673         self.sleep(.25, "wait for vpp to process packets")
674
675         packets = self.punt_socket.wait_for_packets(
676             len(self.pkt_infos) - len(dropped_packet_indexes))
677         self.verify_capture(packets, dropped_packet_indexes)
678         pkts = self.pg_if.get_capture(
679             expected_count=len(dropped_packet_indexes))
680         for icmp in pkts:
681             self.assertIn(ICMPv6TimeExceeded, icmp)
682             self.assertIn(IPv6ExtHdrFragment, icmp)
683             self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
684             dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
685
686     def test_disabled(self):
687         """ reassembly disabled """
688
689         dropped_packet_indexes = set(
690             index for (index, frags_400, _) in self.pkt_infos
691             if len(frags_400) > 1)
692
693         self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
694                                     expire_walk_interval_ms=10000, is_ip6=1)
695
696         self.pg_enable_capture()
697         self.pg_if.add_stream(self.fragments_400)
698         self.pg_start()
699
700         packets = self.punt_socket.wait_for_packets(
701             len(self.pkt_infos) - len(dropped_packet_indexes))
702         self.verify_capture(packets, dropped_packet_indexes)
703         self.pg_if.assert_nothing_captured()
704
705     def test_missing_upper(self):
706         """ missing upper layer """
707         p = (Ether(dst=self.pg_if.local_mac, src=self.pg_if.remote_mac) /
708              IPv6(src=self.pg_if.remote_ip6,
709                   dst=self.pg_if.local_ip6) /
710              UDP(sport=1234, dport=self.punt_port) /
711              Raw())
712         self.extend_packet(p, 1000, self.padding)
713         fragments = fragment_rfc8200(p, 1, 500)
714         bad_fragment = p.__class__(str(fragments[1]))
715         bad_fragment[IPv6ExtHdrFragment].nh = 59
716         bad_fragment[IPv6ExtHdrFragment].offset = 0
717         self.pg_enable_capture()
718         self.pg_if.add_stream([bad_fragment])
719         self.pg_start()
720         pkts = self.pg_if.get_capture(expected_count=1)
721         icmp = pkts[0]
722         self.assertIn(ICMPv6ParamProblem, icmp)
723         self.assert_equal(icmp[ICMPv6ParamProblem].code, 3, "ICMP code")
724
725     def test_invalid_frag_size(self):
726         """ fragment size not a multiple of 8 """
727         p = (Ether(dst=self.pg_if.local_mac, src=self.pg_if.remote_mac) /
728              IPv6(src=self.pg_if.remote_ip6,
729                   dst=self.pg_if.local_ip6) /
730              UDP(sport=1234, dport=self.punt_port) /
731              Raw())
732         self.extend_packet(p, 1000, self.padding)
733         fragments = fragment_rfc8200(p, 1, 500)
734         bad_fragment = fragments[0]
735         self.extend_packet(bad_fragment, len(bad_fragment) + 5)
736         self.pg_enable_capture()
737         self.pg_if.add_stream([bad_fragment])
738         self.pg_start()
739         pkts = self.pg_if.get_capture(expected_count=1)
740         icmp = pkts[0]
741         self.assertIn(ICMPv6ParamProblem, icmp)
742         self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
743
744     def test_invalid_packet_size(self):
745         """ total packet size > 65535 """
746         p = (Ether(dst=self.pg_if.local_mac, src=self.pg_if.remote_mac) /
747              IPv6(src=self.pg_if.remote_ip6,
748                   dst=self.pg_if.local_ip6) /
749              UDP(sport=1234, dport=self.punt_port) /
750              Raw())
751         self.extend_packet(p, 1000, self.padding)
752         fragments = fragment_rfc8200(p, 1, 500)
753         bad_fragment = fragments[1]
754         bad_fragment[IPv6ExtHdrFragment].offset = 65500
755         self.pg_enable_capture()
756         self.pg_if.add_stream([bad_fragment])
757         self.pg_start()
758         pkts = self.pg_if.get_capture(expected_count=1)
759         icmp = pkts[0]
760         self.assertIn(ICMPv6ParamProblem, icmp)
761         self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
762
763
764 class TestFIFReassembly(VppTestCase):
765     """ Fragments in fragments reassembly """
766
767     @classmethod
768     def setUpClass(cls):
769         super(TestFIFReassembly, cls).setUpClass()
770
771         cls.create_pg_interfaces([0])
772         cls.pg_if = cls.pg0
773         cls.pg_if.admin_up()
774         cls.pg_if.config_ip4()
775         cls.pg_if.resolve_arp()
776         cls.pg_if.config_ip6()
777         cls.pg_if.resolve_ndp()
778
779         cls.punt_port = 9999
780         cls.punt4_socket = VppUDSPuntSocket(cls, cls.punt_port)
781         cls.punt6_socket = VppUDSPuntSocket(cls, cls.punt_port, is_ip4=0)
782         cls.packet_sizes = [64, 512, 1518, 9018]
783         cls.padding = " abcdefghijklmn"
784
785     def setUp(self):
786         """ Test setup - force timeout on existing reassemblies """
787         super(TestFIFReassembly, self).setUp()
788         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
789                                     expire_walk_interval_ms=10)
790         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
791                                     expire_walk_interval_ms=10, is_ip6=1)
792         self.sleep(.25)
793         self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
794                                     expire_walk_interval_ms=10000)
795         self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
796                                     expire_walk_interval_ms=10000, is_ip6=1)
797
798     def tearDown(self):
799         self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
800         self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
801         super(TestFIFReassembly, self).tearDown()
802
803     def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):
804         """Verify captured packet stream.
805
806         :param list capture: Captured packet stream.
807         """
808         info = None
809         seen = set()
810         for packet in capture:
811             try:
812                 sw_if_index = packet['sw_if_index']
813                 punt_action = packet['punt_action']
814                 packet = Ether(packet['packet'])
815                 self.logger.debug(ppp("Got packet from %s, action %s" %
816                                       (sw_if_index, punt_action), packet))
817                 ip = packet[ip_class]
818                 udp = packet[UDP]
819                 payload_info = self.payload_to_info(str(packet[Raw]))
820                 packet_index = payload_info.index
821                 self.assertTrue(
822                     packet_index not in dropped_packet_indexes,
823                     ppp("Packet received, but should be dropped:", packet))
824                 if packet_index in seen:
825                     raise Exception(ppp("Duplicate packet received", packet))
826                 seen.add(packet_index)
827                 self.assertEqual(payload_info.dst, self.pg_if.sw_if_index)
828                 info = self._packet_infos[packet_index]
829                 self.assertTrue(info is not None)
830                 self.assertEqual(packet_index, info.index)
831                 saved_packet = info.data
832                 self.assertEqual(ip.src, saved_packet[ip_class].src)
833                 self.assertEqual(ip.dst, saved_packet[ip_class].dst)
834                 self.assertEqual(udp.payload, saved_packet[UDP].payload)
835             except:
836                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
837                 raise
838         for index in self._packet_infos:
839             self.assertTrue(index in seen or index in dropped_packet_indexes,
840                             "Packet with packet_index %d not received" % index)
841
842     def test_fif4(self):
843         """ Fragments in fragments (4o4) """
844
845         # TODO this should be ideally in setUpClass, but then we hit a bug
846         # with VppIpRoute incorrectly reporting it's present when it's not
847         # so we need to manually remove the vpp config, thus we cannot have
848         # it shared for multiple test cases
849         self.tun_ip4 = "1.1.1.2"
850
851         self.gre4 = VppGreInterface(self, self.pg0.local_ip4, self.tun_ip4)
852         self.gre4.add_vpp_config()
853         self.gre4.admin_up()
854         self.gre4.config_ip4()
855
856         self.route4 = VppIpRoute(self, self.tun_ip4, 32,
857                                  [VppRoutePath(self.pg0.remote_ip4,
858                                                self.pg0.sw_if_index)])
859         self.route4.add_vpp_config()
860
861         self.reset_packet_infos()
862         for i in range(test_packet_count):
863             info = self.create_packet_info(self.pg0, self.pg0)
864             payload = self.info_to_payload(info)
865             p = (IP(id=i, src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
866                  UDP(sport=1234, dport=self.punt_port) /
867                  Raw(payload))
868             size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
869             self.extend_packet(p, size, self.padding)
870             info.data = p
871
872         fragments = [x for _, p in self._packet_infos.iteritems()
873                      for x in fragment_rfc791(p.data, 400)]
874
875         encapped_fragments = \
876             [Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
877              IP(src=self.tun_ip4, dst=self.pg0.local_ip4) /
878                 GRE() /
879                 p
880                 for p in fragments]
881
882         fragmented_encapped_fragments = \
883             [x for p in encapped_fragments
884              for x in fragment_rfc791(p, 200)]
885
886         self.pg0.add_stream(fragmented_encapped_fragments)
887
888         self.pg_enable_capture(self.pg_interfaces)
889         self.pg_start()
890
891         self.pg0.assert_nothing_captured()
892         packets = self.punt4_socket.wait_for_packets(len(self._packet_infos))
893         self.verify_capture(packets, IP)
894
895         # TODO remove gre vpp config by hand until VppIpRoute gets fixed
896         # so that it's query_vpp_config() works as it should
897         self.gre4.remove_vpp_config()
898
899     def test_fif6(self):
900         """ Fragments in fragments (6o6) """
901         # TODO this should be ideally in setUpClass, but then we hit a bug
902         # with VppIpRoute incorrectly reporting it's present when it's not
903         # so we need to manually remove the vpp config, thus we cannot have
904         # it shared for multiple test cases
905         self.tun_ip6 = "1002::1"
906
907         self.gre6 = VppGre6Interface(self, self.pg0.local_ip6, self.tun_ip6)
908         self.gre6.add_vpp_config()
909         self.gre6.admin_up()
910         self.gre6.config_ip6()
911
912         self.route6 = VppIpRoute(self, self.tun_ip6, 128,
913                                  [VppRoutePath(self.pg0.remote_ip6,
914                                                self.pg0.sw_if_index,
915                                                proto=DpoProto.DPO_PROTO_IP6)],
916                                  is_ip6=1)
917         self.route6.add_vpp_config()
918
919         self.reset_packet_infos()
920         for i in range(test_packet_count):
921             info = self.create_packet_info(self.pg0, self.pg0)
922             payload = self.info_to_payload(info)
923             p = (IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) /
924                  UDP(sport=1234, dport=self.punt_port) /
925                  Raw(payload))
926             size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
927             self.extend_packet(p, size, self.padding)
928             info.data = p
929
930         fragments = [x for _, i in self._packet_infos.iteritems()
931                      for x in fragment_rfc8200(
932                          i.data, i.index, 400)]
933
934         encapped_fragments = \
935             [Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
936              IPv6(src=self.tun_ip6, dst=self.pg0.local_ip6) /
937                 GRE() /
938                 p
939                 for p in fragments]
940
941         fragmented_encapped_fragments = \
942             [x for p in encapped_fragments for x in (
943                 fragment_rfc8200(
944                     p,
945                     2 * len(self._packet_infos) + p[IPv6ExtHdrFragment].id,
946                     200)
947                 if IPv6ExtHdrFragment in p else [p]
948             )
949             ]
950
951         self.pg0.add_stream(fragmented_encapped_fragments)
952
953         self.pg_enable_capture(self.pg_interfaces)
954         self.pg_start()
955
956         self.pg0.assert_nothing_captured()
957         packets = self.punt6_socket.wait_for_packets(len(self._packet_infos))
958         self.verify_capture(packets, IPv6)
959
960         # TODO remove gre vpp config by hand until VppIpRoute gets fixed
961         # so that it's query_vpp_config() works as it should
962         self.gre6.remove_vpp_config()
963
964
965 if __name__ == '__main__':
966     unittest.main(testRunner=VppTestRunner)