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()
181 @unittest.skipIf(is_skip_aarch64_set() and is_platform_aarch64(),
182 "test doesn't work on aarch64")
183 def test_random(self):
184 """ random order reassembly """
186 fragments = list(self.fragments_200)
189 self.pg_enable_capture()
190 self.src_if.add_stream(fragments)
193 packets = self.dst_if.get_capture(len(self.packet_infos))
194 self.verify_capture(packets)
195 self.src_if.assert_nothing_captured()
197 # run it all again to verify correctness
198 self.pg_enable_capture()
199 self.src_if.add_stream(fragments)
202 packets = self.dst_if.get_capture(len(self.packet_infos))
203 self.verify_capture(packets)
204 self.src_if.assert_nothing_captured()
206 def test_duplicates(self):
207 """ duplicate fragments """
210 x for (_, frags, _, _) in self.pkt_infos
212 for _ in range(0, min(2, len(frags)))
215 self.pg_enable_capture()
216 self.src_if.add_stream(fragments)
219 packets = self.dst_if.get_capture(len(self.pkt_infos))
220 self.verify_capture(packets)
221 self.src_if.assert_nothing_captured()
223 def test_overlap1(self):
224 """ overlapping fragments case #1 """
227 for _, _, frags_300, frags_200 in self.pkt_infos:
228 if len(frags_300) == 1:
229 fragments.extend(frags_300)
231 for i, j in zip(frags_200, frags_300):
235 self.pg_enable_capture()
236 self.src_if.add_stream(fragments)
239 packets = self.dst_if.get_capture(len(self.pkt_infos))
240 self.verify_capture(packets)
241 self.src_if.assert_nothing_captured()
243 # run it all to verify correctness
244 self.pg_enable_capture()
245 self.src_if.add_stream(fragments)
248 packets = self.dst_if.get_capture(len(self.pkt_infos))
249 self.verify_capture(packets)
250 self.src_if.assert_nothing_captured()
252 def test_overlap2(self):
253 """ overlapping fragments case #2 """
256 for _, _, frags_300, frags_200 in self.pkt_infos:
257 if len(frags_300) == 1:
258 fragments.extend(frags_300)
260 # care must be taken here so that there are no fragments
261 # received by vpp after reassembly is finished, otherwise
262 # new reassemblies will be started and packet generator will
263 # freak out when it detects unfreed buffers
264 zipped = zip(frags_300, frags_200)
265 for i, j in zipped[:-1]:
268 fragments.append(zipped[-1][0])
270 self.pg_enable_capture()
271 self.src_if.add_stream(fragments)
274 packets = self.dst_if.get_capture(len(self.pkt_infos))
275 self.verify_capture(packets)
276 self.src_if.assert_nothing_captured()
278 # run it all to verify correctness
279 self.pg_enable_capture()
280 self.src_if.add_stream(fragments)
283 packets = self.dst_if.get_capture(len(self.pkt_infos))
284 self.verify_capture(packets)
285 self.src_if.assert_nothing_captured()
287 def test_timeout_inline(self):
288 """ timeout (inline) """
290 dropped_packet_indexes = set(
291 index for (index, frags, _, _) in self.pkt_infos if len(frags) > 1
294 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
295 expire_walk_interval_ms=10000)
297 self.pg_enable_capture()
298 self.src_if.add_stream(self.fragments_400)
301 packets = self.dst_if.get_capture(
302 len(self.pkt_infos) - len(dropped_packet_indexes))
303 self.verify_capture(packets, dropped_packet_indexes)
304 self.src_if.assert_nothing_captured()
306 def test_timeout_cleanup(self):
307 """ timeout (cleanup) """
309 # whole packets + fragmented packets sans last fragment
311 x for (_, frags_400, _, _) in self.pkt_infos
312 for x in frags_400[:-1 if len(frags_400) > 1 else None]
315 # last fragments for fragmented packets
316 fragments2 = [frags_400[-1]
317 for (_, frags_400, _, _) in self.pkt_infos
318 if len(frags_400) > 1]
320 dropped_packet_indexes = set(
321 index for (index, frags_400, _, _) in self.pkt_infos
322 if len(frags_400) > 1)
324 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
325 expire_walk_interval_ms=50)
327 self.pg_enable_capture()
328 self.src_if.add_stream(fragments)
331 self.sleep(.25, "wait before sending rest of fragments")
333 self.src_if.add_stream(fragments2)
336 packets = self.dst_if.get_capture(
337 len(self.pkt_infos) - len(dropped_packet_indexes))
338 self.verify_capture(packets, dropped_packet_indexes)
339 self.src_if.assert_nothing_captured()
341 def test_disabled(self):
342 """ reassembly disabled """
344 dropped_packet_indexes = set(
345 index for (index, frags_400, _, _) in self.pkt_infos
346 if len(frags_400) > 1)
348 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
349 expire_walk_interval_ms=10000)
351 self.pg_enable_capture()
352 self.src_if.add_stream(self.fragments_400)
355 packets = self.dst_if.get_capture(
356 len(self.pkt_infos) - len(dropped_packet_indexes))
357 self.verify_capture(packets, dropped_packet_indexes)
358 self.src_if.assert_nothing_captured()
361 class TestIPv6Reassembly(VppTestCase):
362 """ IPv6 Reassembly """
366 super(TestIPv6Reassembly, cls).setUpClass()
368 cls.create_pg_interfaces([0, 1])
372 # setup all interfaces
373 for i in cls.pg_interfaces:
379 cls.packet_sizes = [64, 512, 1518, 9018]
380 cls.padding = " abcdefghijklmn"
381 cls.create_stream(cls.packet_sizes)
382 cls.create_fragments()
385 """ Test setup - force timeout on existing reassemblies """
386 super(TestIPv6Reassembly, self).setUp()
387 self.vapi.ip_reassembly_enable_disable(
388 sw_if_index=self.src_if.sw_if_index, enable_ip6=True)
389 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
390 expire_walk_interval_ms=10, is_ip6=1)
392 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
393 expire_walk_interval_ms=10000, is_ip6=1)
394 self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
397 super(TestIPv6Reassembly, self).tearDown()
398 self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
401 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
402 """Create input packet stream for defined interface.
404 :param list packet_sizes: Required packet sizes.
406 for i in range(0, packet_count):
407 info = cls.create_packet_info(cls.src_if, cls.src_if)
408 payload = cls.info_to_payload(info)
409 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
410 IPv6(src=cls.src_if.remote_ip6,
411 dst=cls.dst_if.remote_ip6) /
412 UDP(sport=1234, dport=5678) /
414 size = packet_sizes[(i // 2) % len(packet_sizes)]
415 cls.extend_packet(p, size, cls.padding)
419 def create_fragments(cls):
420 infos = cls._packet_infos
422 for index, info in six.iteritems(infos):
424 # cls.logger.debug(ppp("Packet:", p.__class__(str(p))))
425 fragments_400 = fragment_rfc8200(p, info.index, 400)
426 fragments_300 = fragment_rfc8200(p, info.index, 300)
427 cls.pkt_infos.append((index, fragments_400, fragments_300))
428 cls.fragments_400 = [
429 x for _, frags, _ in cls.pkt_infos for x in frags]
430 cls.fragments_300 = [
431 x for _, _, frags in cls.pkt_infos for x in frags]
432 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
433 "and %s 300-byte fragments" %
434 (len(infos), len(cls.fragments_400),
435 len(cls.fragments_300)))
437 def verify_capture(self, capture, dropped_packet_indexes=[]):
438 """Verify captured packet strea .
440 :param list capture: Captured packet stream.
444 for packet in capture:
446 self.logger.debug(ppp("Got packet:", packet))
449 payload_info = self.payload_to_info(str(packet[Raw]))
450 packet_index = payload_info.index
452 packet_index not in dropped_packet_indexes,
453 ppp("Packet received, but should be dropped:", packet))
454 if packet_index in seen:
455 raise Exception(ppp("Duplicate packet received", packet))
456 seen.add(packet_index)
457 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
458 info = self._packet_infos[packet_index]
459 self.assertTrue(info is not None)
460 self.assertEqual(packet_index, info.index)
461 saved_packet = info.data
462 self.assertEqual(ip.src, saved_packet[IPv6].src)
463 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
464 self.assertEqual(udp.payload, saved_packet[UDP].payload)
466 self.logger.error(ppp("Unexpected or invalid packet:", packet))
468 for index in self._packet_infos:
469 self.assertTrue(index in seen or index in dropped_packet_indexes,
470 "Packet with packet_index %d not received" % index)
472 def test_reassembly(self):
473 """ basic reassembly """
475 self.pg_enable_capture()
476 self.src_if.add_stream(self.fragments_400)
479 packets = self.dst_if.get_capture(len(self.pkt_infos))
480 self.verify_capture(packets)
481 self.src_if.assert_nothing_captured()
483 # run it all again to verify correctness
484 self.pg_enable_capture()
485 self.src_if.add_stream(self.fragments_400)
488 packets = self.dst_if.get_capture(len(self.pkt_infos))
489 self.verify_capture(packets)
490 self.src_if.assert_nothing_captured()
492 def test_reversed(self):
493 """ reverse order reassembly """
495 fragments = list(self.fragments_400)
498 self.pg_enable_capture()
499 self.src_if.add_stream(fragments)
502 packets = self.dst_if.get_capture(len(self.pkt_infos))
503 self.verify_capture(packets)
504 self.src_if.assert_nothing_captured()
506 # run it all again to verify correctness
507 self.pg_enable_capture()
508 self.src_if.add_stream(fragments)
511 packets = self.dst_if.get_capture(len(self.pkt_infos))
512 self.verify_capture(packets)
513 self.src_if.assert_nothing_captured()
515 def test_random(self):
516 """ random order reassembly """
518 fragments = list(self.fragments_400)
521 self.pg_enable_capture()
522 self.src_if.add_stream(fragments)
525 packets = self.dst_if.get_capture(len(self.pkt_infos))
526 self.verify_capture(packets)
527 self.src_if.assert_nothing_captured()
529 # run it all again to verify correctness
530 self.pg_enable_capture()
531 self.src_if.add_stream(fragments)
534 packets = self.dst_if.get_capture(len(self.pkt_infos))
535 self.verify_capture(packets)
536 self.src_if.assert_nothing_captured()
538 def test_duplicates(self):
539 """ duplicate fragments """
542 x for (_, frags, _) in self.pkt_infos
544 for _ in range(0, min(2, len(frags)))
547 self.pg_enable_capture()
548 self.src_if.add_stream(fragments)
551 packets = self.dst_if.get_capture(len(self.pkt_infos))
552 self.verify_capture(packets)
553 self.src_if.assert_nothing_captured()
555 def test_overlap1(self):
556 """ overlapping fragments case #1 """
559 for _, frags_400, frags_300 in self.pkt_infos:
560 if len(frags_300) == 1:
561 fragments.extend(frags_400)
563 for i, j in zip(frags_300, frags_400):
567 dropped_packet_indexes = set(
568 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
571 self.pg_enable_capture()
572 self.src_if.add_stream(fragments)
575 packets = self.dst_if.get_capture(
576 len(self.pkt_infos) - len(dropped_packet_indexes))
577 self.verify_capture(packets, dropped_packet_indexes)
578 self.src_if.assert_nothing_captured()
580 def test_overlap2(self):
581 """ overlapping fragments case #2 """
584 for _, frags_400, frags_300 in self.pkt_infos:
585 if len(frags_400) == 1:
586 fragments.extend(frags_400)
588 # care must be taken here so that there are no fragments
589 # received by vpp after reassembly is finished, otherwise
590 # new reassemblies will be started and packet generator will
591 # freak out when it detects unfreed buffers
592 zipped = zip(frags_400, frags_300)
593 for i, j in zipped[:-1]:
596 fragments.append(zipped[-1][0])
598 dropped_packet_indexes = set(
599 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
602 self.pg_enable_capture()
603 self.src_if.add_stream(fragments)
606 packets = self.dst_if.get_capture(
607 len(self.pkt_infos) - len(dropped_packet_indexes))
608 self.verify_capture(packets, dropped_packet_indexes)
609 self.src_if.assert_nothing_captured()
611 def test_timeout_inline(self):
612 """ timeout (inline) """
614 dropped_packet_indexes = set(
615 index for (index, frags, _) in self.pkt_infos if len(frags) > 1
618 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
619 expire_walk_interval_ms=10000, is_ip6=1)
621 self.pg_enable_capture()
622 self.src_if.add_stream(self.fragments_400)
625 packets = self.dst_if.get_capture(
626 len(self.pkt_infos) - len(dropped_packet_indexes))
627 self.verify_capture(packets, dropped_packet_indexes)
628 pkts = self.src_if.get_capture(
629 expected_count=len(dropped_packet_indexes))
631 self.assertIn(ICMPv6TimeExceeded, icmp)
632 self.assertIn(IPv6ExtHdrFragment, icmp)
633 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
634 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
636 def test_timeout_cleanup(self):
637 """ timeout (cleanup) """
639 # whole packets + fragmented packets sans last fragment
641 x for (_, frags_400, _) in self.pkt_infos
642 for x in frags_400[:-1 if len(frags_400) > 1 else None]
645 # last fragments for fragmented packets
646 fragments2 = [frags_400[-1]
647 for (_, frags_400, _) in self.pkt_infos
648 if len(frags_400) > 1]
650 dropped_packet_indexes = set(
651 index for (index, frags_400, _) in self.pkt_infos
652 if len(frags_400) > 1)
654 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
655 expire_walk_interval_ms=50)
657 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
658 expire_walk_interval_ms=50, is_ip6=1)
660 self.pg_enable_capture()
661 self.src_if.add_stream(fragments)
664 self.sleep(.25, "wait before sending rest of fragments")
666 self.src_if.add_stream(fragments2)
669 packets = self.dst_if.get_capture(
670 len(self.pkt_infos) - len(dropped_packet_indexes))
671 self.verify_capture(packets, dropped_packet_indexes)
672 pkts = self.src_if.get_capture(
673 expected_count=len(dropped_packet_indexes))
675 self.assertIn(ICMPv6TimeExceeded, icmp)
676 self.assertIn(IPv6ExtHdrFragment, icmp)
677 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
678 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
680 def test_disabled(self):
681 """ reassembly disabled """
683 dropped_packet_indexes = set(
684 index for (index, frags_400, _) in self.pkt_infos
685 if len(frags_400) > 1)
687 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
688 expire_walk_interval_ms=10000, is_ip6=1)
690 self.pg_enable_capture()
691 self.src_if.add_stream(self.fragments_400)
694 packets = self.dst_if.get_capture(
695 len(self.pkt_infos) - len(dropped_packet_indexes))
696 self.verify_capture(packets, dropped_packet_indexes)
697 self.src_if.assert_nothing_captured()
699 def test_missing_upper(self):
700 """ missing upper layer """
701 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
702 IPv6(src=self.src_if.remote_ip6,
703 dst=self.src_if.local_ip6) /
704 UDP(sport=1234, dport=5678) /
706 self.extend_packet(p, 1000, self.padding)
707 fragments = fragment_rfc8200(p, 1, 500)
708 bad_fragment = p.__class__(str(fragments[1]))
709 bad_fragment[IPv6ExtHdrFragment].nh = 59
710 bad_fragment[IPv6ExtHdrFragment].offset = 0
711 self.pg_enable_capture()
712 self.src_if.add_stream([bad_fragment])
714 pkts = self.src_if.get_capture(expected_count=1)
716 self.assertIn(ICMPv6ParamProblem, icmp)
717 self.assert_equal(icmp[ICMPv6ParamProblem].code, 3, "ICMP code")
719 def test_invalid_frag_size(self):
720 """ fragment size not a multiple of 8 """
721 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
722 IPv6(src=self.src_if.remote_ip6,
723 dst=self.src_if.local_ip6) /
724 UDP(sport=1234, dport=5678) /
726 self.extend_packet(p, 1000, self.padding)
727 fragments = fragment_rfc8200(p, 1, 500)
728 bad_fragment = fragments[0]
729 self.extend_packet(bad_fragment, len(bad_fragment) + 5)
730 self.pg_enable_capture()
731 self.src_if.add_stream([bad_fragment])
733 pkts = self.src_if.get_capture(expected_count=1)
735 self.assertIn(ICMPv6ParamProblem, icmp)
736 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
738 def test_invalid_packet_size(self):
739 """ total packet size > 65535 """
740 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
741 IPv6(src=self.src_if.remote_ip6,
742 dst=self.src_if.local_ip6) /
743 UDP(sport=1234, dport=5678) /
745 self.extend_packet(p, 1000, self.padding)
746 fragments = fragment_rfc8200(p, 1, 500)
747 bad_fragment = fragments[1]
748 bad_fragment[IPv6ExtHdrFragment].offset = 65500
749 self.pg_enable_capture()
750 self.src_if.add_stream([bad_fragment])
752 pkts = self.src_if.get_capture(expected_count=1)
754 self.assertIn(ICMPv6ParamProblem, icmp)
755 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
758 class TestIPv4ReassemblyLocalNode(VppTestCase):
759 """ IPv4 Reassembly for packets coming to ip4-local node """
763 super(TestIPv4ReassemblyLocalNode, cls).setUpClass()
765 cls.create_pg_interfaces([0])
766 cls.src_dst_if = cls.pg0
768 # setup all interfaces
769 for i in cls.pg_interfaces:
774 cls.padding = " abcdefghijklmn"
776 cls.create_fragments()
779 """ Test setup - force timeout on existing reassemblies """
780 super(TestIPv4ReassemblyLocalNode, self).setUp()
781 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
782 expire_walk_interval_ms=10)
784 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
785 expire_walk_interval_ms=10000)
788 super(TestIPv4ReassemblyLocalNode, self).tearDown()
789 self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
792 def create_stream(cls, packet_count=test_packet_count):
793 """Create input packet stream for defined interface.
795 :param list packet_sizes: Required packet sizes.
797 for i in range(0, packet_count):
798 info = cls.create_packet_info(cls.src_dst_if, cls.src_dst_if)
799 payload = cls.info_to_payload(info)
800 p = (Ether(dst=cls.src_dst_if.local_mac,
801 src=cls.src_dst_if.remote_mac) /
802 IP(id=info.index, src=cls.src_dst_if.remote_ip4,
803 dst=cls.src_dst_if.local_ip4) /
804 ICMP(type='echo-request', id=1234) /
806 cls.extend_packet(p, 1518, cls.padding)
810 def create_fragments(cls):
811 infos = cls._packet_infos
813 for index, info in six.iteritems(infos):
815 # cls.logger.debug(ppp("Packet:", p.__class__(str(p))))
816 fragments_300 = fragment_rfc791(p, 300)
817 cls.pkt_infos.append((index, fragments_300))
818 cls.fragments_300 = [x for (_, frags) in cls.pkt_infos for x in frags]
819 cls.logger.debug("Fragmented %s packets into %s 300-byte fragments" %
820 (len(infos), len(cls.fragments_300)))
822 def verify_capture(self, capture):
823 """Verify captured packet stream.
825 :param list capture: Captured packet stream.
829 for packet in capture:
831 self.logger.debug(ppp("Got packet:", packet))
834 payload_info = self.payload_to_info(str(packet[Raw]))
835 packet_index = payload_info.index
836 if packet_index in seen:
837 raise Exception(ppp("Duplicate packet received", packet))
838 seen.add(packet_index)
839 self.assertEqual(payload_info.dst, self.src_dst_if.sw_if_index)
840 info = self._packet_infos[packet_index]
841 self.assertTrue(info is not None)
842 self.assertEqual(packet_index, info.index)
843 saved_packet = info.data
844 self.assertEqual(ip.src, saved_packet[IP].dst)
845 self.assertEqual(ip.dst, saved_packet[IP].src)
846 self.assertEqual(icmp.type, 0) # echo reply
847 self.assertEqual(icmp.id, saved_packet[ICMP].id)
848 self.assertEqual(icmp.payload, saved_packet[ICMP].payload)
850 self.logger.error(ppp("Unexpected or invalid packet:", packet))
852 for index in self._packet_infos:
853 self.assertTrue(index in seen or index in dropped_packet_indexes,
854 "Packet with packet_index %d not received" % index)
856 def test_reassembly(self):
857 """ basic reassembly """
859 self.pg_enable_capture()
860 self.src_dst_if.add_stream(self.fragments_300)
863 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
864 self.verify_capture(packets)
866 # run it all again to verify correctness
867 self.pg_enable_capture()
868 self.src_dst_if.add_stream(self.fragments_300)
871 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
872 self.verify_capture(packets)
875 class TestFIFReassembly(VppTestCase):
876 """ Fragments in fragments reassembly """
880 super(TestFIFReassembly, cls).setUpClass()
882 cls.create_pg_interfaces([0, 1])
885 for i in cls.pg_interfaces:
892 cls.packet_sizes = [64, 512, 1518, 9018]
893 cls.padding = " abcdefghijklmn"
896 """ Test setup - force timeout on existing reassemblies """
897 super(TestFIFReassembly, self).setUp()
898 self.vapi.ip_reassembly_enable_disable(
899 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
901 self.vapi.ip_reassembly_enable_disable(
902 sw_if_index=self.dst_if.sw_if_index, enable_ip4=True,
904 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
905 expire_walk_interval_ms=10)
906 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
907 expire_walk_interval_ms=10, is_ip6=1)
909 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
910 expire_walk_interval_ms=10000)
911 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
912 expire_walk_interval_ms=10000, is_ip6=1)
915 self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
916 self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
917 super(TestFIFReassembly, self).tearDown()
919 def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):
920 """Verify captured packet stream.
922 :param list capture: Captured packet stream.
926 for packet in capture:
928 self.logger.debug(ppp("Got packet:", packet))
929 ip = packet[ip_class]
931 payload_info = self.payload_to_info(str(packet[Raw]))
932 packet_index = payload_info.index
934 packet_index not in dropped_packet_indexes,
935 ppp("Packet received, but should be dropped:", packet))
936 if packet_index in seen:
937 raise Exception(ppp("Duplicate packet received", packet))
938 seen.add(packet_index)
939 self.assertEqual(payload_info.dst, self.dst_if.sw_if_index)
940 info = self._packet_infos[packet_index]
941 self.assertTrue(info is not None)
942 self.assertEqual(packet_index, info.index)
943 saved_packet = info.data
944 self.assertEqual(ip.src, saved_packet[ip_class].src)
945 self.assertEqual(ip.dst, saved_packet[ip_class].dst)
946 self.assertEqual(udp.payload, saved_packet[UDP].payload)
948 self.logger.error(ppp("Unexpected or invalid packet:", packet))
950 for index in self._packet_infos:
951 self.assertTrue(index in seen or index in dropped_packet_indexes,
952 "Packet with packet_index %d not received" % index)
955 """ Fragments in fragments (4o4) """
957 # TODO this should be ideally in setUpClass, but then we hit a bug
958 # with VppIpRoute incorrectly reporting it's present when it's not
959 # so we need to manually remove the vpp config, thus we cannot have
960 # it shared for multiple test cases
961 self.tun_ip4 = "1.1.1.2"
963 self.gre4 = VppGreInterface(self, self.src_if.local_ip4, self.tun_ip4)
964 self.gre4.add_vpp_config()
966 self.gre4.config_ip4()
968 self.vapi.ip_reassembly_enable_disable(
969 sw_if_index=self.gre4.sw_if_index, enable_ip4=True)
971 self.route4 = VppIpRoute(self, self.tun_ip4, 32,
972 [VppRoutePath(self.src_if.remote_ip4,
973 self.src_if.sw_if_index)])
974 self.route4.add_vpp_config()
976 self.reset_packet_infos()
977 for i in range(test_packet_count):
978 info = self.create_packet_info(self.src_if, self.dst_if)
979 payload = self.info_to_payload(info)
980 # Ethernet header here is only for size calculation, thus it
981 # doesn't matter how it's initialized. This is to ensure that
982 # reassembled packet is not > 9000 bytes, so that it's not dropped
984 IP(id=i, src=self.src_if.remote_ip4,
985 dst=self.dst_if.remote_ip4) /
986 UDP(sport=1234, dport=5678) /
988 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
989 self.extend_packet(p, size, self.padding)
990 info.data = p[IP] # use only IP part, without ethernet header
992 fragments = [x for _, p in six.iteritems(self._packet_infos)
993 for x in fragment_rfc791(p.data, 400)]
995 encapped_fragments = \
996 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
997 IP(src=self.tun_ip4, dst=self.src_if.local_ip4) /
1002 fragmented_encapped_fragments = \
1003 [x for p in encapped_fragments
1004 for x in fragment_rfc791(p, 200)]
1006 self.src_if.add_stream(fragmented_encapped_fragments)
1008 self.pg_enable_capture(self.pg_interfaces)
1011 self.src_if.assert_nothing_captured()
1012 packets = self.dst_if.get_capture(len(self._packet_infos))
1013 self.verify_capture(packets, IP)
1015 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
1016 # so that it's query_vpp_config() works as it should
1017 self.gre4.remove_vpp_config()
1018 self.logger.debug(self.vapi.ppcli("show interface"))
1020 def test_fif6(self):
1021 """ Fragments in fragments (6o6) """
1022 # TODO this should be ideally in setUpClass, but then we hit a bug
1023 # with VppIpRoute incorrectly reporting it's present when it's not
1024 # so we need to manually remove the vpp config, thus we cannot have
1025 # it shared for multiple test cases
1026 self.tun_ip6 = "1002::1"
1028 self.gre6 = VppGre6Interface(self, self.src_if.local_ip6, self.tun_ip6)
1029 self.gre6.add_vpp_config()
1030 self.gre6.admin_up()
1031 self.gre6.config_ip6()
1033 self.vapi.ip_reassembly_enable_disable(
1034 sw_if_index=self.gre6.sw_if_index, enable_ip6=True)
1036 self.route6 = VppIpRoute(self, self.tun_ip6, 128,
1037 [VppRoutePath(self.src_if.remote_ip6,
1038 self.src_if.sw_if_index,
1039 proto=DpoProto.DPO_PROTO_IP6)],
1041 self.route6.add_vpp_config()
1043 self.reset_packet_infos()
1044 for i in range(test_packet_count):
1045 info = self.create_packet_info(self.src_if, self.dst_if)
1046 payload = self.info_to_payload(info)
1047 # Ethernet header here is only for size calculation, thus it
1048 # doesn't matter how it's initialized. This is to ensure that
1049 # reassembled packet is not > 9000 bytes, so that it's not dropped
1051 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1052 UDP(sport=1234, dport=5678) /
1054 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1055 self.extend_packet(p, size, self.padding)
1056 info.data = p[IPv6] # use only IPv6 part, without ethernet header
1058 fragments = [x for _, i in six.iteritems(self._packet_infos)
1059 for x in fragment_rfc8200(
1060 i.data, i.index, 400)]
1062 encapped_fragments = \
1063 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1064 IPv6(src=self.tun_ip6, dst=self.src_if.local_ip6) /
1069 fragmented_encapped_fragments = \
1070 [x for p in encapped_fragments for x in (
1073 2 * len(self._packet_infos) + p[IPv6ExtHdrFragment].id,
1075 if IPv6ExtHdrFragment in p else [p]
1079 self.src_if.add_stream(fragmented_encapped_fragments)
1081 self.pg_enable_capture(self.pg_interfaces)
1084 self.src_if.assert_nothing_captured()
1085 packets = self.dst_if.get_capture(len(self._packet_infos))
1086 self.verify_capture(packets, IPv6)
1088 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
1089 # so that it's query_vpp_config() works as it should
1090 self.gre6.remove_vpp_config()
1093 if __name__ == '__main__':
1094 unittest.main(testRunner=VppTestRunner)