5 from random import shuffle
7 from framework import VppTestCase, VppTestRunner, is_skip_aarch64_set,\
10 from scapy.packet import Raw
11 from scapy.layers.l2 import Ether, GRE
12 from scapy.layers.inet import IP, UDP, ICMP
13 from util import ppp, fragment_rfc791, fragment_rfc8200
14 from scapy.layers.inet6 import IPv6, IPv6ExtHdrFragment, ICMPv6ParamProblem,\
16 from vpp_gre_interface import VppGreInterface, VppGre6Interface
17 from vpp_ip import DpoProto
18 from vpp_ip_route import VppIpRoute, VppRoutePath
20 test_packet_count = 257
23 class TestIPv4Reassembly(VppTestCase):
24 """ IPv4 Reassembly """
28 super(TestIPv4Reassembly, cls).setUpClass()
30 cls.create_pg_interfaces([0, 1])
34 # setup all interfaces
35 for i in cls.pg_interfaces:
41 cls.packet_sizes = [64, 512, 1518, 9018]
42 cls.padding = " abcdefghijklmn"
43 cls.create_stream(cls.packet_sizes)
44 cls.create_fragments()
47 """ Test setup - force timeout on existing reassemblies """
48 super(TestIPv4Reassembly, self).setUp()
49 self.vapi.ip_reassembly_enable_disable(
50 sw_if_index=self.src_if.sw_if_index, enable_ip4=True)
51 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
52 expire_walk_interval_ms=10)
54 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
55 expire_walk_interval_ms=10000)
58 super(TestIPv4Reassembly, self).tearDown()
59 self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
62 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
63 """Create input packet stream for defined interface.
65 :param list packet_sizes: Required packet sizes.
67 for i in range(0, packet_count):
68 info = cls.create_packet_info(cls.src_if, cls.src_if)
69 payload = cls.info_to_payload(info)
70 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
71 IP(id=info.index, src=cls.src_if.remote_ip4,
72 dst=cls.dst_if.remote_ip4) /
73 UDP(sport=1234, dport=5678) /
75 size = packet_sizes[(i // 2) % len(packet_sizes)]
76 cls.extend_packet(p, size, cls.padding)
80 def create_fragments(cls):
81 infos = cls._packet_infos
83 for index, info in six.iteritems(infos):
85 # cls.logger.debug(ppp("Packet:", p.__class__(str(p))))
86 fragments_400 = fragment_rfc791(p, 400)
87 fragments_300 = fragment_rfc791(p, 300)
89 x for f in fragments_400 for x in fragment_rfc791(f, 200)]
91 (index, fragments_400, fragments_300, fragments_200))
93 x for (_, frags, _, _) in cls.pkt_infos for x in frags]
95 x for (_, _, frags, _) in cls.pkt_infos for x in frags]
97 x for (_, _, _, frags) in cls.pkt_infos for x in frags]
98 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
99 "%s 300-byte fragments and %s 200-byte fragments" %
100 (len(infos), len(cls.fragments_400),
101 len(cls.fragments_300), len(cls.fragments_200)))
103 def verify_capture(self, capture, dropped_packet_indexes=[]):
104 """Verify captured packet stream.
106 :param list capture: Captured packet stream.
110 for packet in capture:
112 self.logger.debug(ppp("Got packet:", packet))
115 payload_info = self.payload_to_info(str(packet[Raw]))
116 packet_index = payload_info.index
118 packet_index not in dropped_packet_indexes,
119 ppp("Packet received, but should be dropped:", packet))
120 if packet_index in seen:
121 raise Exception(ppp("Duplicate packet received", packet))
122 seen.add(packet_index)
123 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
124 info = self._packet_infos[packet_index]
125 self.assertTrue(info is not None)
126 self.assertEqual(packet_index, info.index)
127 saved_packet = info.data
128 self.assertEqual(ip.src, saved_packet[IP].src)
129 self.assertEqual(ip.dst, saved_packet[IP].dst)
130 self.assertEqual(udp.payload, saved_packet[UDP].payload)
132 self.logger.error(ppp("Unexpected or invalid packet:", packet))
134 for index in self._packet_infos:
135 self.assertTrue(index in seen or index in dropped_packet_indexes,
136 "Packet with packet_index %d not received" % index)
138 def test_reassembly(self):
139 """ basic reassembly """
141 self.pg_enable_capture()
142 self.src_if.add_stream(self.fragments_200)
145 packets = self.dst_if.get_capture(len(self.pkt_infos))
146 self.verify_capture(packets)
147 self.src_if.assert_nothing_captured()
149 # run it all again to verify correctness
150 self.pg_enable_capture()
151 self.src_if.add_stream(self.fragments_200)
154 packets = self.dst_if.get_capture(len(self.pkt_infos))
155 self.verify_capture(packets)
156 self.src_if.assert_nothing_captured()
158 def test_reversed(self):
159 """ reverse order reassembly """
161 fragments = list(self.fragments_200)
164 self.pg_enable_capture()
165 self.src_if.add_stream(fragments)
168 packets = self.dst_if.get_capture(len(self.packet_infos))
169 self.verify_capture(packets)
170 self.src_if.assert_nothing_captured()
172 # run it all again to verify correctness
173 self.pg_enable_capture()
174 self.src_if.add_stream(fragments)
177 packets = self.dst_if.get_capture(len(self.packet_infos))
178 self.verify_capture(packets)
179 self.src_if.assert_nothing_captured()
182 """ fragment length + ip header size > 65535 """
183 raw = ('E\x00\x00\x88,\xf8\x1f\xfe@\x01\x98\x00\xc0\xa8\n-\xc0\xa8\n'
184 '\x01\x08\x00\xf0J\xed\xcb\xf1\xf5Test-group: IPv4.IPv4.ipv4-'
185 'message.Ethernet-Payload.IPv4-Packet.IPv4-Header.Fragment-Of'
186 'fset; Test-case: 5737')
188 malformed_packet = (Ether(dst=self.src_if.local_mac,
189 src=self.src_if.remote_mac) /
191 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
192 IP(id=1000, src=self.src_if.remote_ip4,
193 dst=self.dst_if.remote_ip4) /
194 UDP(sport=1234, dport=5678) /
196 valid_fragments = fragment_rfc791(p, 400)
198 self.pg_enable_capture()
199 self.src_if.add_stream([malformed_packet] + valid_fragments)
202 self.dst_if.get_capture(1)
203 self.assert_packet_counter_equal(
204 "/err/ip4-reassembly-feature/malformed packets", 1)
206 def test_44924(self):
207 """ compress tiny fragments """
208 packets = [(Ether(dst=self.src_if.local_mac,
209 src=self.src_if.remote_mac) /
210 IP(id=24339, flags="MF", frag=0, ttl=64,
211 src=self.src_if.remote_ip4,
212 dst=self.dst_if.remote_ip4) /
213 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
214 Raw(load='Test-group: IPv4')),
215 (Ether(dst=self.src_if.local_mac,
216 src=self.src_if.remote_mac) /
217 IP(id=24339, flags="MF", frag=3, ttl=64,
218 src=self.src_if.remote_ip4,
219 dst=self.dst_if.remote_ip4) /
220 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
221 Raw(load='.IPv4.Fragmentation.vali')),
222 (Ether(dst=self.src_if.local_mac,
223 src=self.src_if.remote_mac) /
224 IP(id=24339, frag=6, ttl=64,
225 src=self.src_if.remote_ip4,
226 dst=self.dst_if.remote_ip4) /
227 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
228 Raw(load='d; Test-case: 44924'))
231 self.pg_enable_capture()
232 self.src_if.add_stream(packets)
235 self.dst_if.get_capture(1)
237 @unittest.skipIf(is_skip_aarch64_set() and is_platform_aarch64(),
238 "test doesn't work on aarch64")
239 def test_random(self):
240 """ random order reassembly """
242 fragments = list(self.fragments_200)
245 self.pg_enable_capture()
246 self.src_if.add_stream(fragments)
249 packets = self.dst_if.get_capture(len(self.packet_infos))
250 self.verify_capture(packets)
251 self.src_if.assert_nothing_captured()
253 # run it all again to verify correctness
254 self.pg_enable_capture()
255 self.src_if.add_stream(fragments)
258 packets = self.dst_if.get_capture(len(self.packet_infos))
259 self.verify_capture(packets)
260 self.src_if.assert_nothing_captured()
262 def test_duplicates(self):
263 """ duplicate fragments """
266 x for (_, frags, _, _) in self.pkt_infos
268 for _ in range(0, min(2, len(frags)))
271 self.pg_enable_capture()
272 self.src_if.add_stream(fragments)
275 packets = self.dst_if.get_capture(len(self.pkt_infos))
276 self.verify_capture(packets)
277 self.src_if.assert_nothing_captured()
279 def test_overlap1(self):
280 """ overlapping fragments case #1 """
283 for _, _, frags_300, frags_200 in self.pkt_infos:
284 if len(frags_300) == 1:
285 fragments.extend(frags_300)
287 for i, j in zip(frags_200, frags_300):
291 self.pg_enable_capture()
292 self.src_if.add_stream(fragments)
295 packets = self.dst_if.get_capture(len(self.pkt_infos))
296 self.verify_capture(packets)
297 self.src_if.assert_nothing_captured()
299 # run it all to verify correctness
300 self.pg_enable_capture()
301 self.src_if.add_stream(fragments)
304 packets = self.dst_if.get_capture(len(self.pkt_infos))
305 self.verify_capture(packets)
306 self.src_if.assert_nothing_captured()
308 def test_overlap2(self):
309 """ overlapping fragments case #2 """
312 for _, _, frags_300, frags_200 in self.pkt_infos:
313 if len(frags_300) == 1:
314 fragments.extend(frags_300)
316 # care must be taken here so that there are no fragments
317 # received by vpp after reassembly is finished, otherwise
318 # new reassemblies will be started and packet generator will
319 # freak out when it detects unfreed buffers
320 zipped = zip(frags_300, frags_200)
321 for i, j in zipped[:-1]:
324 fragments.append(zipped[-1][0])
326 self.pg_enable_capture()
327 self.src_if.add_stream(fragments)
330 packets = self.dst_if.get_capture(len(self.pkt_infos))
331 self.verify_capture(packets)
332 self.src_if.assert_nothing_captured()
334 # run it all to verify correctness
335 self.pg_enable_capture()
336 self.src_if.add_stream(fragments)
339 packets = self.dst_if.get_capture(len(self.pkt_infos))
340 self.verify_capture(packets)
341 self.src_if.assert_nothing_captured()
343 def test_timeout_inline(self):
344 """ timeout (inline) """
346 dropped_packet_indexes = set(
347 index for (index, frags, _, _) in self.pkt_infos if len(frags) > 1
350 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
351 expire_walk_interval_ms=10000)
353 self.pg_enable_capture()
354 self.src_if.add_stream(self.fragments_400)
357 packets = self.dst_if.get_capture(
358 len(self.pkt_infos) - len(dropped_packet_indexes))
359 self.verify_capture(packets, dropped_packet_indexes)
360 self.src_if.assert_nothing_captured()
362 def test_timeout_cleanup(self):
363 """ timeout (cleanup) """
365 # whole packets + fragmented packets sans last fragment
367 x for (_, frags_400, _, _) in self.pkt_infos
368 for x in frags_400[:-1 if len(frags_400) > 1 else None]
371 # last fragments for fragmented packets
372 fragments2 = [frags_400[-1]
373 for (_, frags_400, _, _) in self.pkt_infos
374 if len(frags_400) > 1]
376 dropped_packet_indexes = set(
377 index for (index, frags_400, _, _) in self.pkt_infos
378 if len(frags_400) > 1)
380 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
381 expire_walk_interval_ms=50)
383 self.pg_enable_capture()
384 self.src_if.add_stream(fragments)
387 self.sleep(.25, "wait before sending rest of fragments")
389 self.src_if.add_stream(fragments2)
392 packets = self.dst_if.get_capture(
393 len(self.pkt_infos) - len(dropped_packet_indexes))
394 self.verify_capture(packets, dropped_packet_indexes)
395 self.src_if.assert_nothing_captured()
397 def test_disabled(self):
398 """ reassembly disabled """
400 dropped_packet_indexes = set(
401 index for (index, frags_400, _, _) in self.pkt_infos
402 if len(frags_400) > 1)
404 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
405 expire_walk_interval_ms=10000)
407 self.pg_enable_capture()
408 self.src_if.add_stream(self.fragments_400)
411 packets = self.dst_if.get_capture(
412 len(self.pkt_infos) - len(dropped_packet_indexes))
413 self.verify_capture(packets, dropped_packet_indexes)
414 self.src_if.assert_nothing_captured()
417 class TestIPv6Reassembly(VppTestCase):
418 """ IPv6 Reassembly """
422 super(TestIPv6Reassembly, cls).setUpClass()
424 cls.create_pg_interfaces([0, 1])
428 # setup all interfaces
429 for i in cls.pg_interfaces:
435 cls.packet_sizes = [64, 512, 1518, 9018]
436 cls.padding = " abcdefghijklmn"
437 cls.create_stream(cls.packet_sizes)
438 cls.create_fragments()
441 """ Test setup - force timeout on existing reassemblies """
442 super(TestIPv6Reassembly, self).setUp()
443 self.vapi.ip_reassembly_enable_disable(
444 sw_if_index=self.src_if.sw_if_index, enable_ip6=True)
445 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
446 expire_walk_interval_ms=10, is_ip6=1)
448 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
449 expire_walk_interval_ms=10000, is_ip6=1)
450 self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
453 super(TestIPv6Reassembly, self).tearDown()
454 self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
457 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
458 """Create input packet stream for defined interface.
460 :param list packet_sizes: Required packet sizes.
462 for i in range(0, packet_count):
463 info = cls.create_packet_info(cls.src_if, cls.src_if)
464 payload = cls.info_to_payload(info)
465 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
466 IPv6(src=cls.src_if.remote_ip6,
467 dst=cls.dst_if.remote_ip6) /
468 UDP(sport=1234, dport=5678) /
470 size = packet_sizes[(i // 2) % len(packet_sizes)]
471 cls.extend_packet(p, size, cls.padding)
475 def create_fragments(cls):
476 infos = cls._packet_infos
478 for index, info in six.iteritems(infos):
480 # cls.logger.debug(ppp("Packet:", p.__class__(str(p))))
481 fragments_400 = fragment_rfc8200(p, info.index, 400)
482 fragments_300 = fragment_rfc8200(p, info.index, 300)
483 cls.pkt_infos.append((index, fragments_400, fragments_300))
484 cls.fragments_400 = [
485 x for _, frags, _ in cls.pkt_infos for x in frags]
486 cls.fragments_300 = [
487 x for _, _, frags in cls.pkt_infos for x in frags]
488 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
489 "and %s 300-byte fragments" %
490 (len(infos), len(cls.fragments_400),
491 len(cls.fragments_300)))
493 def verify_capture(self, capture, dropped_packet_indexes=[]):
494 """Verify captured packet strea .
496 :param list capture: Captured packet stream.
500 for packet in capture:
502 self.logger.debug(ppp("Got packet:", packet))
505 payload_info = self.payload_to_info(str(packet[Raw]))
506 packet_index = payload_info.index
508 packet_index not in dropped_packet_indexes,
509 ppp("Packet received, but should be dropped:", packet))
510 if packet_index in seen:
511 raise Exception(ppp("Duplicate packet received", packet))
512 seen.add(packet_index)
513 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
514 info = self._packet_infos[packet_index]
515 self.assertTrue(info is not None)
516 self.assertEqual(packet_index, info.index)
517 saved_packet = info.data
518 self.assertEqual(ip.src, saved_packet[IPv6].src)
519 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
520 self.assertEqual(udp.payload, saved_packet[UDP].payload)
522 self.logger.error(ppp("Unexpected or invalid packet:", packet))
524 for index in self._packet_infos:
525 self.assertTrue(index in seen or index in dropped_packet_indexes,
526 "Packet with packet_index %d not received" % index)
528 def test_reassembly(self):
529 """ basic reassembly """
531 self.pg_enable_capture()
532 self.src_if.add_stream(self.fragments_400)
535 packets = self.dst_if.get_capture(len(self.pkt_infos))
536 self.verify_capture(packets)
537 self.src_if.assert_nothing_captured()
539 # run it all again to verify correctness
540 self.pg_enable_capture()
541 self.src_if.add_stream(self.fragments_400)
544 packets = self.dst_if.get_capture(len(self.pkt_infos))
545 self.verify_capture(packets)
546 self.src_if.assert_nothing_captured()
548 def test_reversed(self):
549 """ reverse order reassembly """
551 fragments = list(self.fragments_400)
554 self.pg_enable_capture()
555 self.src_if.add_stream(fragments)
558 packets = self.dst_if.get_capture(len(self.pkt_infos))
559 self.verify_capture(packets)
560 self.src_if.assert_nothing_captured()
562 # run it all again to verify correctness
563 self.pg_enable_capture()
564 self.src_if.add_stream(fragments)
567 packets = self.dst_if.get_capture(len(self.pkt_infos))
568 self.verify_capture(packets)
569 self.src_if.assert_nothing_captured()
571 def test_random(self):
572 """ random order reassembly """
574 fragments = list(self.fragments_400)
577 self.pg_enable_capture()
578 self.src_if.add_stream(fragments)
581 packets = self.dst_if.get_capture(len(self.pkt_infos))
582 self.verify_capture(packets)
583 self.src_if.assert_nothing_captured()
585 # run it all again to verify correctness
586 self.pg_enable_capture()
587 self.src_if.add_stream(fragments)
590 packets = self.dst_if.get_capture(len(self.pkt_infos))
591 self.verify_capture(packets)
592 self.src_if.assert_nothing_captured()
594 def test_duplicates(self):
595 """ duplicate fragments """
598 x for (_, frags, _) in self.pkt_infos
600 for _ in range(0, min(2, len(frags)))
603 self.pg_enable_capture()
604 self.src_if.add_stream(fragments)
607 packets = self.dst_if.get_capture(len(self.pkt_infos))
608 self.verify_capture(packets)
609 self.src_if.assert_nothing_captured()
611 def test_overlap1(self):
612 """ overlapping fragments case #1 """
615 for _, frags_400, frags_300 in self.pkt_infos:
616 if len(frags_300) == 1:
617 fragments.extend(frags_400)
619 for i, j in zip(frags_300, frags_400):
623 dropped_packet_indexes = set(
624 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
627 self.pg_enable_capture()
628 self.src_if.add_stream(fragments)
631 packets = self.dst_if.get_capture(
632 len(self.pkt_infos) - len(dropped_packet_indexes))
633 self.verify_capture(packets, dropped_packet_indexes)
634 self.src_if.assert_nothing_captured()
636 def test_overlap2(self):
637 """ overlapping fragments case #2 """
640 for _, frags_400, frags_300 in self.pkt_infos:
641 if len(frags_400) == 1:
642 fragments.extend(frags_400)
644 # care must be taken here so that there are no fragments
645 # received by vpp after reassembly is finished, otherwise
646 # new reassemblies will be started and packet generator will
647 # freak out when it detects unfreed buffers
648 zipped = zip(frags_400, frags_300)
649 for i, j in zipped[:-1]:
652 fragments.append(zipped[-1][0])
654 dropped_packet_indexes = set(
655 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
658 self.pg_enable_capture()
659 self.src_if.add_stream(fragments)
662 packets = self.dst_if.get_capture(
663 len(self.pkt_infos) - len(dropped_packet_indexes))
664 self.verify_capture(packets, dropped_packet_indexes)
665 self.src_if.assert_nothing_captured()
667 def test_timeout_inline(self):
668 """ timeout (inline) """
670 dropped_packet_indexes = set(
671 index for (index, frags, _) in self.pkt_infos if len(frags) > 1
674 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
675 expire_walk_interval_ms=10000, is_ip6=1)
677 self.pg_enable_capture()
678 self.src_if.add_stream(self.fragments_400)
681 packets = self.dst_if.get_capture(
682 len(self.pkt_infos) - len(dropped_packet_indexes))
683 self.verify_capture(packets, dropped_packet_indexes)
684 pkts = self.src_if.get_capture(
685 expected_count=len(dropped_packet_indexes))
687 self.assertIn(ICMPv6TimeExceeded, icmp)
688 self.assertIn(IPv6ExtHdrFragment, icmp)
689 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
690 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
692 def test_timeout_cleanup(self):
693 """ timeout (cleanup) """
695 # whole packets + fragmented packets sans last fragment
697 x for (_, frags_400, _) in self.pkt_infos
698 for x in frags_400[:-1 if len(frags_400) > 1 else None]
701 # last fragments for fragmented packets
702 fragments2 = [frags_400[-1]
703 for (_, frags_400, _) in self.pkt_infos
704 if len(frags_400) > 1]
706 dropped_packet_indexes = set(
707 index for (index, frags_400, _) in self.pkt_infos
708 if len(frags_400) > 1)
710 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
711 expire_walk_interval_ms=50)
713 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
714 expire_walk_interval_ms=50, is_ip6=1)
716 self.pg_enable_capture()
717 self.src_if.add_stream(fragments)
720 self.sleep(.25, "wait before sending rest of fragments")
722 self.src_if.add_stream(fragments2)
725 packets = self.dst_if.get_capture(
726 len(self.pkt_infos) - len(dropped_packet_indexes))
727 self.verify_capture(packets, dropped_packet_indexes)
728 pkts = self.src_if.get_capture(
729 expected_count=len(dropped_packet_indexes))
731 self.assertIn(ICMPv6TimeExceeded, icmp)
732 self.assertIn(IPv6ExtHdrFragment, icmp)
733 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
734 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
736 def test_disabled(self):
737 """ reassembly disabled """
739 dropped_packet_indexes = set(
740 index for (index, frags_400, _) in self.pkt_infos
741 if len(frags_400) > 1)
743 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
744 expire_walk_interval_ms=10000, is_ip6=1)
746 self.pg_enable_capture()
747 self.src_if.add_stream(self.fragments_400)
750 packets = self.dst_if.get_capture(
751 len(self.pkt_infos) - len(dropped_packet_indexes))
752 self.verify_capture(packets, dropped_packet_indexes)
753 self.src_if.assert_nothing_captured()
755 def test_missing_upper(self):
756 """ missing upper layer """
757 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
758 IPv6(src=self.src_if.remote_ip6,
759 dst=self.src_if.local_ip6) /
760 UDP(sport=1234, dport=5678) /
762 self.extend_packet(p, 1000, self.padding)
763 fragments = fragment_rfc8200(p, 1, 500)
764 bad_fragment = p.__class__(str(fragments[1]))
765 bad_fragment[IPv6ExtHdrFragment].nh = 59
766 bad_fragment[IPv6ExtHdrFragment].offset = 0
767 self.pg_enable_capture()
768 self.src_if.add_stream([bad_fragment])
770 pkts = self.src_if.get_capture(expected_count=1)
772 self.assertIn(ICMPv6ParamProblem, icmp)
773 self.assert_equal(icmp[ICMPv6ParamProblem].code, 3, "ICMP code")
775 def test_invalid_frag_size(self):
776 """ fragment size not a multiple of 8 """
777 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
778 IPv6(src=self.src_if.remote_ip6,
779 dst=self.src_if.local_ip6) /
780 UDP(sport=1234, dport=5678) /
782 self.extend_packet(p, 1000, self.padding)
783 fragments = fragment_rfc8200(p, 1, 500)
784 bad_fragment = fragments[0]
785 self.extend_packet(bad_fragment, len(bad_fragment) + 5)
786 self.pg_enable_capture()
787 self.src_if.add_stream([bad_fragment])
789 pkts = self.src_if.get_capture(expected_count=1)
791 self.assertIn(ICMPv6ParamProblem, icmp)
792 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
794 def test_invalid_packet_size(self):
795 """ total packet size > 65535 """
796 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
797 IPv6(src=self.src_if.remote_ip6,
798 dst=self.src_if.local_ip6) /
799 UDP(sport=1234, dport=5678) /
801 self.extend_packet(p, 1000, self.padding)
802 fragments = fragment_rfc8200(p, 1, 500)
803 bad_fragment = fragments[1]
804 bad_fragment[IPv6ExtHdrFragment].offset = 65500
805 self.pg_enable_capture()
806 self.src_if.add_stream([bad_fragment])
808 pkts = self.src_if.get_capture(expected_count=1)
810 self.assertIn(ICMPv6ParamProblem, icmp)
811 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
814 class TestIPv4ReassemblyLocalNode(VppTestCase):
815 """ IPv4 Reassembly for packets coming to ip4-local node """
819 super(TestIPv4ReassemblyLocalNode, cls).setUpClass()
821 cls.create_pg_interfaces([0])
822 cls.src_dst_if = cls.pg0
824 # setup all interfaces
825 for i in cls.pg_interfaces:
830 cls.padding = " abcdefghijklmn"
832 cls.create_fragments()
835 """ Test setup - force timeout on existing reassemblies """
836 super(TestIPv4ReassemblyLocalNode, self).setUp()
837 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
838 expire_walk_interval_ms=10)
840 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
841 expire_walk_interval_ms=10000)
844 super(TestIPv4ReassemblyLocalNode, self).tearDown()
845 self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
848 def create_stream(cls, packet_count=test_packet_count):
849 """Create input packet stream for defined interface.
851 :param list packet_sizes: Required packet sizes.
853 for i in range(0, packet_count):
854 info = cls.create_packet_info(cls.src_dst_if, cls.src_dst_if)
855 payload = cls.info_to_payload(info)
856 p = (Ether(dst=cls.src_dst_if.local_mac,
857 src=cls.src_dst_if.remote_mac) /
858 IP(id=info.index, src=cls.src_dst_if.remote_ip4,
859 dst=cls.src_dst_if.local_ip4) /
860 ICMP(type='echo-request', id=1234) /
862 cls.extend_packet(p, 1518, cls.padding)
866 def create_fragments(cls):
867 infos = cls._packet_infos
869 for index, info in six.iteritems(infos):
871 # cls.logger.debug(ppp("Packet:", p.__class__(str(p))))
872 fragments_300 = fragment_rfc791(p, 300)
873 cls.pkt_infos.append((index, fragments_300))
874 cls.fragments_300 = [x for (_, frags) in cls.pkt_infos for x in frags]
875 cls.logger.debug("Fragmented %s packets into %s 300-byte fragments" %
876 (len(infos), len(cls.fragments_300)))
878 def verify_capture(self, capture):
879 """Verify captured packet stream.
881 :param list capture: Captured packet stream.
885 for packet in capture:
887 self.logger.debug(ppp("Got packet:", packet))
890 payload_info = self.payload_to_info(str(packet[Raw]))
891 packet_index = payload_info.index
892 if packet_index in seen:
893 raise Exception(ppp("Duplicate packet received", packet))
894 seen.add(packet_index)
895 self.assertEqual(payload_info.dst, self.src_dst_if.sw_if_index)
896 info = self._packet_infos[packet_index]
897 self.assertIsNotNone(info)
898 self.assertEqual(packet_index, info.index)
899 saved_packet = info.data
900 self.assertEqual(ip.src, saved_packet[IP].dst)
901 self.assertEqual(ip.dst, saved_packet[IP].src)
902 self.assertEqual(icmp.type, 0) # echo reply
903 self.assertEqual(icmp.id, saved_packet[ICMP].id)
904 self.assertEqual(icmp.payload, saved_packet[ICMP].payload)
906 self.logger.error(ppp("Unexpected or invalid packet:", packet))
908 for index in self._packet_infos:
909 self.assertIn(index, seen,
910 "Packet with packet_index %d not received" % index)
912 def test_reassembly(self):
913 """ basic reassembly """
915 self.pg_enable_capture()
916 self.src_dst_if.add_stream(self.fragments_300)
919 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
920 self.verify_capture(packets)
922 # run it all again to verify correctness
923 self.pg_enable_capture()
924 self.src_dst_if.add_stream(self.fragments_300)
927 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
928 self.verify_capture(packets)
931 class TestFIFReassembly(VppTestCase):
932 """ Fragments in fragments reassembly """
936 super(TestFIFReassembly, cls).setUpClass()
938 cls.create_pg_interfaces([0, 1])
941 for i in cls.pg_interfaces:
948 cls.packet_sizes = [64, 512, 1518, 9018]
949 cls.padding = " abcdefghijklmn"
952 """ Test setup - force timeout on existing reassemblies """
953 super(TestFIFReassembly, self).setUp()
954 self.vapi.ip_reassembly_enable_disable(
955 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
957 self.vapi.ip_reassembly_enable_disable(
958 sw_if_index=self.dst_if.sw_if_index, enable_ip4=True,
960 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
961 expire_walk_interval_ms=10)
962 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
963 expire_walk_interval_ms=10, is_ip6=1)
965 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
966 expire_walk_interval_ms=10000)
967 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
968 expire_walk_interval_ms=10000, is_ip6=1)
971 self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
972 self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
973 super(TestFIFReassembly, self).tearDown()
975 def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):
976 """Verify captured packet stream.
978 :param list capture: Captured packet stream.
982 for packet in capture:
984 self.logger.debug(ppp("Got packet:", packet))
985 ip = packet[ip_class]
987 payload_info = self.payload_to_info(str(packet[Raw]))
988 packet_index = payload_info.index
990 packet_index not in dropped_packet_indexes,
991 ppp("Packet received, but should be dropped:", packet))
992 if packet_index in seen:
993 raise Exception(ppp("Duplicate packet received", packet))
994 seen.add(packet_index)
995 self.assertEqual(payload_info.dst, self.dst_if.sw_if_index)
996 info = self._packet_infos[packet_index]
997 self.assertTrue(info is not None)
998 self.assertEqual(packet_index, info.index)
999 saved_packet = info.data
1000 self.assertEqual(ip.src, saved_packet[ip_class].src)
1001 self.assertEqual(ip.dst, saved_packet[ip_class].dst)
1002 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1004 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1006 for index in self._packet_infos:
1007 self.assertTrue(index in seen or index in dropped_packet_indexes,
1008 "Packet with packet_index %d not received" % index)
1010 def test_fif4(self):
1011 """ Fragments in fragments (4o4) """
1013 # TODO this should be ideally in setUpClass, but then we hit a bug
1014 # with VppIpRoute incorrectly reporting it's present when it's not
1015 # so we need to manually remove the vpp config, thus we cannot have
1016 # it shared for multiple test cases
1017 self.tun_ip4 = "1.1.1.2"
1019 self.gre4 = VppGreInterface(self, self.src_if.local_ip4, self.tun_ip4)
1020 self.gre4.add_vpp_config()
1021 self.gre4.admin_up()
1022 self.gre4.config_ip4()
1024 self.vapi.ip_reassembly_enable_disable(
1025 sw_if_index=self.gre4.sw_if_index, enable_ip4=True)
1027 self.route4 = VppIpRoute(self, self.tun_ip4, 32,
1028 [VppRoutePath(self.src_if.remote_ip4,
1029 self.src_if.sw_if_index)])
1030 self.route4.add_vpp_config()
1032 self.reset_packet_infos()
1033 for i in range(test_packet_count):
1034 info = self.create_packet_info(self.src_if, self.dst_if)
1035 payload = self.info_to_payload(info)
1036 # Ethernet header here is only for size calculation, thus it
1037 # doesn't matter how it's initialized. This is to ensure that
1038 # reassembled packet is not > 9000 bytes, so that it's not dropped
1040 IP(id=i, src=self.src_if.remote_ip4,
1041 dst=self.dst_if.remote_ip4) /
1042 UDP(sport=1234, dport=5678) /
1044 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1045 self.extend_packet(p, size, self.padding)
1046 info.data = p[IP] # use only IP part, without ethernet header
1048 fragments = [x for _, p in six.iteritems(self._packet_infos)
1049 for x in fragment_rfc791(p.data, 400)]
1051 encapped_fragments = \
1052 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1053 IP(src=self.tun_ip4, dst=self.src_if.local_ip4) /
1058 fragmented_encapped_fragments = \
1059 [x for p in encapped_fragments
1060 for x in fragment_rfc791(p, 200)]
1062 self.src_if.add_stream(fragmented_encapped_fragments)
1064 self.pg_enable_capture(self.pg_interfaces)
1067 self.src_if.assert_nothing_captured()
1068 packets = self.dst_if.get_capture(len(self._packet_infos))
1069 self.verify_capture(packets, IP)
1071 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
1072 # so that it's query_vpp_config() works as it should
1073 self.gre4.remove_vpp_config()
1074 self.logger.debug(self.vapi.ppcli("show interface"))
1076 def test_fif6(self):
1077 """ Fragments in fragments (6o6) """
1078 # TODO this should be ideally in setUpClass, but then we hit a bug
1079 # with VppIpRoute incorrectly reporting it's present when it's not
1080 # so we need to manually remove the vpp config, thus we cannot have
1081 # it shared for multiple test cases
1082 self.tun_ip6 = "1002::1"
1084 self.gre6 = VppGre6Interface(self, self.src_if.local_ip6, self.tun_ip6)
1085 self.gre6.add_vpp_config()
1086 self.gre6.admin_up()
1087 self.gre6.config_ip6()
1089 self.vapi.ip_reassembly_enable_disable(
1090 sw_if_index=self.gre6.sw_if_index, enable_ip6=True)
1092 self.route6 = VppIpRoute(self, self.tun_ip6, 128,
1093 [VppRoutePath(self.src_if.remote_ip6,
1094 self.src_if.sw_if_index,
1095 proto=DpoProto.DPO_PROTO_IP6)],
1097 self.route6.add_vpp_config()
1099 self.reset_packet_infos()
1100 for i in range(test_packet_count):
1101 info = self.create_packet_info(self.src_if, self.dst_if)
1102 payload = self.info_to_payload(info)
1103 # Ethernet header here is only for size calculation, thus it
1104 # doesn't matter how it's initialized. This is to ensure that
1105 # reassembled packet is not > 9000 bytes, so that it's not dropped
1107 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1108 UDP(sport=1234, dport=5678) /
1110 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1111 self.extend_packet(p, size, self.padding)
1112 info.data = p[IPv6] # use only IPv6 part, without ethernet header
1114 fragments = [x for _, i in six.iteritems(self._packet_infos)
1115 for x in fragment_rfc8200(
1116 i.data, i.index, 400)]
1118 encapped_fragments = \
1119 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1120 IPv6(src=self.tun_ip6, dst=self.src_if.local_ip6) /
1125 fragmented_encapped_fragments = \
1126 [x for p in encapped_fragments for x in (
1129 2 * len(self._packet_infos) + p[IPv6ExtHdrFragment].id,
1131 if IPv6ExtHdrFragment in p else [p]
1135 self.src_if.add_stream(fragmented_encapped_fragments)
1137 self.pg_enable_capture(self.pg_interfaces)
1140 self.src_if.assert_nothing_captured()
1141 packets = self.dst_if.get_capture(len(self._packet_infos))
1142 self.verify_capture(packets, IPv6)
1144 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
1145 # so that it's query_vpp_config() works as it should
1146 self.gre6.remove_vpp_config()
1149 if __name__ == '__main__':
1150 unittest.main(testRunner=VppTestRunner)