5 from random import shuffle, choice, randrange
7 from framework import VppTestCase, VppTestRunner
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 scapy.layers.inet6 import HBHOptUnknown, ICMPv6ParamProblem,\
14 ICMPv6TimeExceeded, IPv6, IPv6ExtHdrFragment, IPv6ExtHdrHopByHop
15 from framework import VppTestCase, VppTestRunner
16 from util import ppp, ppc, fragment_rfc791, fragment_rfc8200
17 from vpp_gre_interface import VppGreInterface
18 from vpp_ip import DpoProto
19 from vpp_ip_route import VppIpRoute, VppRoutePath, FibPathProto
20 from vpp_papi import VppEnum
22 # 35 is enough to have >257 400-byte fragments
23 test_packet_count = 35
25 # number of workers used for multi-worker test cases
29 class TestIPv4Reassembly(VppTestCase):
30 """ IPv4 Reassembly """
34 super(TestIPv4Reassembly, cls).setUpClass()
36 cls.create_pg_interfaces([0, 1])
40 # setup all interfaces
41 for i in cls.pg_interfaces:
47 cls.packet_sizes = [64, 512, 1518, 9018]
48 cls.padding = " abcdefghijklmn"
49 cls.create_stream(cls.packet_sizes)
50 cls.create_fragments()
53 def tearDownClass(cls):
54 super(TestIPv4Reassembly, cls).tearDownClass()
57 """ Test setup - force timeout on existing reassemblies """
58 super(TestIPv4Reassembly, self).setUp()
59 self.vapi.ip_reassembly_enable_disable(
60 sw_if_index=self.src_if.sw_if_index, enable_ip4=True)
61 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
62 max_reassembly_length=1000,
63 expire_walk_interval_ms=10)
65 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
66 max_reassembly_length=1000,
67 expire_walk_interval_ms=10000)
70 super(TestIPv4Reassembly, self).tearDown()
72 def show_commands_at_teardown(self):
73 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
74 self.logger.debug(self.vapi.ppcli("show buffers"))
77 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
78 """Create input packet stream
80 :param list packet_sizes: Required packet sizes.
82 for i in range(0, packet_count):
83 info = cls.create_packet_info(cls.src_if, cls.src_if)
84 payload = cls.info_to_payload(info)
85 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
86 IP(id=info.index, src=cls.src_if.remote_ip4,
87 dst=cls.dst_if.remote_ip4) /
88 UDP(sport=1234, dport=5678) /
90 size = packet_sizes[(i // 2) % len(packet_sizes)]
91 cls.extend_packet(p, size, cls.padding)
95 def create_fragments(cls):
96 infos = cls._packet_infos
98 for index, info in six.iteritems(infos):
100 # cls.logger.debug(ppp("Packet:",
101 # p.__class__(scapy.compat.raw(p))))
102 fragments_400 = fragment_rfc791(p, 400)
103 fragments_300 = fragment_rfc791(p, 300)
105 x for f in fragments_400 for x in fragment_rfc791(f, 200)]
106 cls.pkt_infos.append(
107 (index, fragments_400, fragments_300, fragments_200))
108 cls.fragments_400 = [
109 x for (_, frags, _, _) in cls.pkt_infos for x in frags]
110 cls.fragments_300 = [
111 x for (_, _, frags, _) in cls.pkt_infos for x in frags]
112 cls.fragments_200 = [
113 x for (_, _, _, frags) in cls.pkt_infos for x in frags]
114 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
115 "%s 300-byte fragments and %s 200-byte fragments" %
116 (len(infos), len(cls.fragments_400),
117 len(cls.fragments_300), len(cls.fragments_200)))
119 def verify_capture(self, capture, dropped_packet_indexes=[]):
120 """Verify captured packet stream.
122 :param list capture: Captured packet stream.
126 for packet in capture:
128 self.logger.debug(ppp("Got packet:", packet))
131 payload_info = self.payload_to_info(packet[Raw])
132 packet_index = payload_info.index
134 packet_index not in dropped_packet_indexes,
135 ppp("Packet received, but should be dropped:", packet))
136 if packet_index in seen:
137 raise Exception(ppp("Duplicate packet received", packet))
138 seen.add(packet_index)
139 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
140 info = self._packet_infos[packet_index]
141 self.assertTrue(info is not None)
142 self.assertEqual(packet_index, info.index)
143 saved_packet = info.data
144 self.assertEqual(ip.src, saved_packet[IP].src)
145 self.assertEqual(ip.dst, saved_packet[IP].dst)
146 self.assertEqual(udp.payload, saved_packet[UDP].payload)
148 self.logger.error(ppp("Unexpected or invalid packet:", packet))
150 for index in self._packet_infos:
151 self.assertTrue(index in seen or index in dropped_packet_indexes,
152 "Packet with packet_index %d not received" % index)
154 def test_reassembly(self):
155 """ basic reassembly """
157 self.pg_enable_capture()
158 self.src_if.add_stream(self.fragments_200)
161 packets = self.dst_if.get_capture(len(self.pkt_infos))
162 self.verify_capture(packets)
163 self.src_if.assert_nothing_captured()
165 # run it all again to verify correctness
166 self.pg_enable_capture()
167 self.src_if.add_stream(self.fragments_200)
170 packets = self.dst_if.get_capture(len(self.pkt_infos))
171 self.verify_capture(packets)
172 self.src_if.assert_nothing_captured()
174 def test_reversed(self):
175 """ reverse order reassembly """
177 fragments = list(self.fragments_200)
180 self.pg_enable_capture()
181 self.src_if.add_stream(fragments)
184 packets = self.dst_if.get_capture(len(self.packet_infos))
185 self.verify_capture(packets)
186 self.src_if.assert_nothing_captured()
188 # run it all again to verify correctness
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 def test_long_fragment_chain(self):
198 """ long fragment chain """
201 "/err/ip4-full-reassembly-feature/fragment chain too long (drop)"
203 error_cnt = self.statistics.get_err_counter(error_cnt_str)
205 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
206 max_reassembly_length=3,
207 expire_walk_interval_ms=50)
209 p1 = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
210 IP(id=1000, src=self.src_if.remote_ip4,
211 dst=self.dst_if.remote_ip4) /
212 UDP(sport=1234, dport=5678) /
214 p2 = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
215 IP(id=1001, src=self.src_if.remote_ip4,
216 dst=self.dst_if.remote_ip4) /
217 UDP(sport=1234, dport=5678) /
219 frags = fragment_rfc791(p1, 200) + fragment_rfc791(p2, 500)
221 self.pg_enable_capture()
222 self.src_if.add_stream(frags)
225 self.dst_if.get_capture(1)
226 self.assert_error_counter_equal(error_cnt_str, error_cnt + 1)
229 """ fragment length + ip header size > 65535 """
230 self.vapi.cli("clear errors")
231 raw = ('E\x00\x00\x88,\xf8\x1f\xfe@\x01\x98\x00\xc0\xa8\n-\xc0\xa8\n'
232 '\x01\x08\x00\xf0J\xed\xcb\xf1\xf5Test-group: IPv4.IPv4.ipv4-'
233 'message.Ethernet-Payload.IPv4-Packet.IPv4-Header.Fragment-Of'
234 'fset; Test-case: 5737')
236 malformed_packet = (Ether(dst=self.src_if.local_mac,
237 src=self.src_if.remote_mac) /
239 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
240 IP(id=1000, src=self.src_if.remote_ip4,
241 dst=self.dst_if.remote_ip4) /
242 UDP(sport=1234, dport=5678) /
244 valid_fragments = fragment_rfc791(p, 400)
246 self.pg_enable_capture()
247 self.src_if.add_stream([malformed_packet] + valid_fragments)
250 self.dst_if.get_capture(1)
251 self.logger.debug(self.vapi.ppcli("show error"))
252 self.assert_packet_counter_equal("ip4-full-reassembly-feature", 1)
253 # TODO remove above, uncomment below once clearing of counters
255 # self.assert_packet_counter_equal(
256 # "/err/ip4-full-reassembly-feature/malformed packets", 1)
258 def test_44924(self):
259 """ compress tiny fragments """
260 packets = [(Ether(dst=self.src_if.local_mac,
261 src=self.src_if.remote_mac) /
262 IP(id=24339, flags="MF", frag=0, ttl=64,
263 src=self.src_if.remote_ip4,
264 dst=self.dst_if.remote_ip4) /
265 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
266 Raw(load='Test-group: IPv4')),
267 (Ether(dst=self.src_if.local_mac,
268 src=self.src_if.remote_mac) /
269 IP(id=24339, flags="MF", frag=3, ttl=64,
270 src=self.src_if.remote_ip4,
271 dst=self.dst_if.remote_ip4) /
272 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
273 Raw(load='.IPv4.Fragmentation.vali')),
274 (Ether(dst=self.src_if.local_mac,
275 src=self.src_if.remote_mac) /
276 IP(id=24339, frag=6, ttl=64,
277 src=self.src_if.remote_ip4,
278 dst=self.dst_if.remote_ip4) /
279 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
280 Raw(load='d; Test-case: 44924'))
283 self.pg_enable_capture()
284 self.src_if.add_stream(packets)
287 self.dst_if.get_capture(1)
289 def test_frag_1(self):
290 """ fragment of size 1 """
291 self.vapi.cli("clear errors")
292 malformed_packets = [(Ether(dst=self.src_if.local_mac,
293 src=self.src_if.remote_mac) /
294 IP(id=7, len=21, flags="MF", frag=0, ttl=64,
295 src=self.src_if.remote_ip4,
296 dst=self.dst_if.remote_ip4) /
297 ICMP(type="echo-request")),
298 (Ether(dst=self.src_if.local_mac,
299 src=self.src_if.remote_mac) /
300 IP(id=7, len=21, frag=1, ttl=64,
301 src=self.src_if.remote_ip4,
302 dst=self.dst_if.remote_ip4) /
306 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
307 IP(id=1000, src=self.src_if.remote_ip4,
308 dst=self.dst_if.remote_ip4) /
309 UDP(sport=1234, dport=5678) /
311 valid_fragments = fragment_rfc791(p, 400)
313 self.pg_enable_capture()
314 self.src_if.add_stream(malformed_packets + valid_fragments)
317 self.dst_if.get_capture(1)
319 self.assert_packet_counter_equal("ip4-full-reassembly-feature", 1)
320 # TODO remove above, uncomment below once clearing of counters
322 # self.assert_packet_counter_equal(
323 # "/err/ip4-full-reassembly-feature/malformed packets", 1)
325 def test_random(self):
326 """ random order reassembly """
328 fragments = list(self.fragments_200)
331 self.pg_enable_capture()
332 self.src_if.add_stream(fragments)
335 packets = self.dst_if.get_capture(len(self.packet_infos))
336 self.verify_capture(packets)
337 self.src_if.assert_nothing_captured()
339 # run it all again to verify correctness
340 self.pg_enable_capture()
341 self.src_if.add_stream(fragments)
344 packets = self.dst_if.get_capture(len(self.packet_infos))
345 self.verify_capture(packets)
346 self.src_if.assert_nothing_captured()
348 def test_duplicates(self):
349 """ duplicate fragments """
352 x for (_, frags, _, _) in self.pkt_infos
354 for _ in range(0, min(2, len(frags)))
357 self.pg_enable_capture()
358 self.src_if.add_stream(fragments)
361 packets = self.dst_if.get_capture(len(self.pkt_infos))
362 self.verify_capture(packets)
363 self.src_if.assert_nothing_captured()
365 def test_overlap1(self):
366 """ overlapping fragments case #1 """
369 for _, _, frags_300, frags_200 in self.pkt_infos:
370 if len(frags_300) == 1:
371 fragments.extend(frags_300)
373 for i, j in zip(frags_200, frags_300):
377 self.pg_enable_capture()
378 self.src_if.add_stream(fragments)
381 packets = self.dst_if.get_capture(len(self.pkt_infos))
382 self.verify_capture(packets)
383 self.src_if.assert_nothing_captured()
385 # run it all to verify correctness
386 self.pg_enable_capture()
387 self.src_if.add_stream(fragments)
390 packets = self.dst_if.get_capture(len(self.pkt_infos))
391 self.verify_capture(packets)
392 self.src_if.assert_nothing_captured()
394 def test_overlap2(self):
395 """ overlapping fragments case #2 """
398 for _, _, frags_300, frags_200 in self.pkt_infos:
399 if len(frags_300) == 1:
400 fragments.extend(frags_300)
402 # care must be taken here so that there are no fragments
403 # received by vpp after reassembly is finished, otherwise
404 # new reassemblies will be started and packet generator will
405 # freak out when it detects unfreed buffers
406 zipped = zip(frags_300, frags_200)
412 self.pg_enable_capture()
413 self.src_if.add_stream(fragments)
416 packets = self.dst_if.get_capture(len(self.pkt_infos))
417 self.verify_capture(packets)
418 self.src_if.assert_nothing_captured()
420 # run it all to verify correctness
421 self.pg_enable_capture()
422 self.src_if.add_stream(fragments)
425 packets = self.dst_if.get_capture(len(self.pkt_infos))
426 self.verify_capture(packets)
427 self.src_if.assert_nothing_captured()
429 def test_timeout_inline(self):
430 """ timeout (inline) """
432 dropped_packet_indexes = set(
433 index for (index, frags, _, _) in self.pkt_infos if len(frags) > 1
436 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
437 max_reassembly_length=3,
438 expire_walk_interval_ms=10000)
440 self.pg_enable_capture()
441 self.src_if.add_stream(self.fragments_400)
444 packets = self.dst_if.get_capture(
445 len(self.pkt_infos) - len(dropped_packet_indexes))
446 self.verify_capture(packets, dropped_packet_indexes)
447 self.src_if.assert_nothing_captured()
449 def test_timeout_cleanup(self):
450 """ timeout (cleanup) """
452 # whole packets + fragmented packets sans last fragment
454 x for (_, frags_400, _, _) in self.pkt_infos
455 for x in frags_400[:-1 if len(frags_400) > 1 else None]
458 # last fragments for fragmented packets
459 fragments2 = [frags_400[-1]
460 for (_, frags_400, _, _) in self.pkt_infos
461 if len(frags_400) > 1]
463 dropped_packet_indexes = set(
464 index for (index, frags_400, _, _) in self.pkt_infos
465 if len(frags_400) > 1)
467 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
468 max_reassembly_length=1000,
469 expire_walk_interval_ms=50)
471 self.pg_enable_capture()
472 self.src_if.add_stream(fragments)
475 self.sleep(.25, "wait before sending rest of fragments")
477 self.src_if.add_stream(fragments2)
480 packets = self.dst_if.get_capture(
481 len(self.pkt_infos) - len(dropped_packet_indexes))
482 self.verify_capture(packets, dropped_packet_indexes)
483 self.src_if.assert_nothing_captured()
485 def test_disabled(self):
486 """ reassembly disabled """
488 dropped_packet_indexes = set(
489 index for (index, frags_400, _, _) in self.pkt_infos
490 if len(frags_400) > 1)
492 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
493 max_reassembly_length=3,
494 expire_walk_interval_ms=10000)
496 self.pg_enable_capture()
497 self.src_if.add_stream(self.fragments_400)
500 packets = self.dst_if.get_capture(
501 len(self.pkt_infos) - len(dropped_packet_indexes))
502 self.verify_capture(packets, dropped_packet_indexes)
503 self.src_if.assert_nothing_captured()
506 class TestIPv4MWReassembly(VppTestCase):
507 """ IPv4 Reassembly (multiple workers) """
508 worker_config = "workers %d" % worker_count
512 super(TestIPv4MWReassembly, cls).setUpClass()
514 cls.create_pg_interfaces(range(worker_count+1))
516 cls.send_ifs = cls.pg_interfaces[:-1]
517 cls.dst_if = cls.pg_interfaces[-1]
519 # setup all interfaces
520 for i in cls.pg_interfaces:
525 # packets sizes reduced here because we are generating packets without
526 # Ethernet headers, which are added later (diff fragments go via
527 # different interfaces)
528 cls.packet_sizes = [64-len(Ether()), 512-len(Ether()),
529 1518-len(Ether()), 9018-len(Ether())]
530 cls.padding = " abcdefghijklmn"
531 cls.create_stream(cls.packet_sizes)
532 cls.create_fragments()
535 def tearDownClass(cls):
536 super(TestIPv4MWReassembly, cls).tearDownClass()
539 """ Test setup - force timeout on existing reassemblies """
540 super(TestIPv4MWReassembly, self).setUp()
541 for intf in self.send_ifs:
542 self.vapi.ip_reassembly_enable_disable(
543 sw_if_index=intf.sw_if_index, enable_ip4=True)
544 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
545 max_reassembly_length=1000,
546 expire_walk_interval_ms=10)
548 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
549 max_reassembly_length=1000,
550 expire_walk_interval_ms=10000)
553 super(TestIPv4MWReassembly, self).tearDown()
555 def show_commands_at_teardown(self):
556 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
557 self.logger.debug(self.vapi.ppcli("show buffers"))
560 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
561 """Create input packet stream
563 :param list packet_sizes: Required packet sizes.
565 for i in range(0, packet_count):
566 info = cls.create_packet_info(cls.src_if, cls.src_if)
567 payload = cls.info_to_payload(info)
568 p = (IP(id=info.index, src=cls.src_if.remote_ip4,
569 dst=cls.dst_if.remote_ip4) /
570 UDP(sport=1234, dport=5678) /
572 size = packet_sizes[(i // 2) % len(packet_sizes)]
573 cls.extend_packet(p, size, cls.padding)
577 def create_fragments(cls):
578 infos = cls._packet_infos
580 for index, info in six.iteritems(infos):
582 # cls.logger.debug(ppp("Packet:",
583 # p.__class__(scapy.compat.raw(p))))
584 fragments_400 = fragment_rfc791(p, 400)
585 cls.pkt_infos.append((index, fragments_400))
586 cls.fragments_400 = [
587 x for (_, frags) in cls.pkt_infos for x in frags]
588 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, " %
589 (len(infos), len(cls.fragments_400)))
591 def verify_capture(self, capture, dropped_packet_indexes=[]):
592 """Verify captured packet stream.
594 :param list capture: Captured packet stream.
598 for packet in capture:
600 self.logger.debug(ppp("Got packet:", packet))
603 payload_info = self.payload_to_info(packet[Raw])
604 packet_index = payload_info.index
606 packet_index not in dropped_packet_indexes,
607 ppp("Packet received, but should be dropped:", packet))
608 if packet_index in seen:
609 raise Exception(ppp("Duplicate packet received", packet))
610 seen.add(packet_index)
611 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
612 info = self._packet_infos[packet_index]
613 self.assertTrue(info is not None)
614 self.assertEqual(packet_index, info.index)
615 saved_packet = info.data
616 self.assertEqual(ip.src, saved_packet[IP].src)
617 self.assertEqual(ip.dst, saved_packet[IP].dst)
618 self.assertEqual(udp.payload, saved_packet[UDP].payload)
620 self.logger.error(ppp("Unexpected or invalid packet:", packet))
622 for index in self._packet_infos:
623 self.assertTrue(index in seen or index in dropped_packet_indexes,
624 "Packet with packet_index %d not received" % index)
626 def send_packets(self, packets):
627 for counter in range(worker_count):
628 if 0 == len(packets[counter]):
630 send_if = self.send_ifs[counter]
632 (Ether(dst=send_if.local_mac, src=send_if.remote_mac) / x
633 for x in packets[counter]),
637 def test_worker_conflict(self):
638 """ 1st and FO=0 fragments on different workers """
640 # in first wave we send fragments which don't start at offset 0
641 # then we send fragments with offset 0 on a different thread
642 # then the rest of packets on a random thread
643 first_packets = [[] for n in range(worker_count)]
644 second_packets = [[] for n in range(worker_count)]
645 rest_of_packets = [[] for n in range(worker_count)]
646 for (_, p) in self.pkt_infos:
647 wi = randrange(worker_count)
648 second_packets[wi].append(p[0])
653 wi2 = randrange(worker_count)
654 first_packets[wi2].append(p[1])
655 wi3 = randrange(worker_count)
656 rest_of_packets[wi3].extend(p[2:])
658 self.pg_enable_capture()
659 self.send_packets(first_packets)
660 self.send_packets(second_packets)
661 self.send_packets(rest_of_packets)
663 packets = self.dst_if.get_capture(len(self.pkt_infos))
664 self.verify_capture(packets)
665 for send_if in self.send_ifs:
666 send_if.assert_nothing_captured()
668 self.pg_enable_capture()
669 self.send_packets(first_packets)
670 self.send_packets(second_packets)
671 self.send_packets(rest_of_packets)
673 packets = self.dst_if.get_capture(len(self.pkt_infos))
674 self.verify_capture(packets)
675 for send_if in self.send_ifs:
676 send_if.assert_nothing_captured()
679 class TestIPv6Reassembly(VppTestCase):
680 """ IPv6 Reassembly """
684 super(TestIPv6Reassembly, cls).setUpClass()
686 cls.create_pg_interfaces([0, 1])
690 # setup all interfaces
691 for i in cls.pg_interfaces:
697 cls.packet_sizes = [64, 512, 1518, 9018]
698 cls.padding = " abcdefghijklmn"
699 cls.create_stream(cls.packet_sizes)
700 cls.create_fragments()
703 def tearDownClass(cls):
704 super(TestIPv6Reassembly, cls).tearDownClass()
707 """ Test setup - force timeout on existing reassemblies """
708 super(TestIPv6Reassembly, self).setUp()
709 self.vapi.ip_reassembly_enable_disable(
710 sw_if_index=self.src_if.sw_if_index, enable_ip6=True)
711 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
712 max_reassembly_length=1000,
713 expire_walk_interval_ms=10, is_ip6=1)
715 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
716 max_reassembly_length=1000,
717 expire_walk_interval_ms=10000, is_ip6=1)
718 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
719 self.logger.debug(self.vapi.ppcli("show buffers"))
722 super(TestIPv6Reassembly, self).tearDown()
724 def show_commands_at_teardown(self):
725 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
726 self.logger.debug(self.vapi.ppcli("show buffers"))
729 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
730 """Create input packet stream for defined interface.
732 :param list packet_sizes: Required packet sizes.
734 for i in range(0, packet_count):
735 info = cls.create_packet_info(cls.src_if, cls.src_if)
736 payload = cls.info_to_payload(info)
737 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
738 IPv6(src=cls.src_if.remote_ip6,
739 dst=cls.dst_if.remote_ip6) /
740 UDP(sport=1234, dport=5678) /
742 size = packet_sizes[(i // 2) % len(packet_sizes)]
743 cls.extend_packet(p, size, cls.padding)
747 def create_fragments(cls):
748 infos = cls._packet_infos
750 for index, info in six.iteritems(infos):
752 # cls.logger.debug(ppp("Packet:",
753 # p.__class__(scapy.compat.raw(p))))
754 fragments_400 = fragment_rfc8200(p, info.index, 400)
755 fragments_300 = fragment_rfc8200(p, info.index, 300)
756 cls.pkt_infos.append((index, fragments_400, fragments_300))
757 cls.fragments_400 = [
758 x for _, frags, _ in cls.pkt_infos for x in frags]
759 cls.fragments_300 = [
760 x for _, _, frags in cls.pkt_infos for x in frags]
761 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
762 "and %s 300-byte fragments" %
763 (len(infos), len(cls.fragments_400),
764 len(cls.fragments_300)))
766 def verify_capture(self, capture, dropped_packet_indexes=[]):
767 """Verify captured packet strea .
769 :param list capture: Captured packet stream.
773 for packet in capture:
775 self.logger.debug(ppp("Got packet:", packet))
778 payload_info = self.payload_to_info(packet[Raw])
779 packet_index = payload_info.index
781 packet_index not in dropped_packet_indexes,
782 ppp("Packet received, but should be dropped:", packet))
783 if packet_index in seen:
784 raise Exception(ppp("Duplicate packet received", packet))
785 seen.add(packet_index)
786 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
787 info = self._packet_infos[packet_index]
788 self.assertTrue(info is not None)
789 self.assertEqual(packet_index, info.index)
790 saved_packet = info.data
791 self.assertEqual(ip.src, saved_packet[IPv6].src)
792 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
793 self.assertEqual(udp.payload, saved_packet[UDP].payload)
795 self.logger.error(ppp("Unexpected or invalid packet:", packet))
797 for index in self._packet_infos:
798 self.assertTrue(index in seen or index in dropped_packet_indexes,
799 "Packet with packet_index %d not received" % index)
801 def test_reassembly(self):
802 """ basic reassembly """
804 self.pg_enable_capture()
805 self.src_if.add_stream(self.fragments_400)
808 packets = self.dst_if.get_capture(len(self.pkt_infos))
809 self.verify_capture(packets)
810 self.src_if.assert_nothing_captured()
812 # run it all again to verify correctness
813 self.pg_enable_capture()
814 self.src_if.add_stream(self.fragments_400)
817 packets = self.dst_if.get_capture(len(self.pkt_infos))
818 self.verify_capture(packets)
819 self.src_if.assert_nothing_captured()
821 def test_buffer_boundary(self):
822 """ fragment header crossing buffer boundary """
824 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
825 IPv6(src=self.src_if.remote_ip6,
826 dst=self.src_if.local_ip6) /
828 options=[HBHOptUnknown(otype=0xff, optlen=0)] * 1000) /
829 IPv6ExtHdrFragment(m=1) /
830 UDP(sport=1234, dport=5678) /
832 self.pg_enable_capture()
833 self.src_if.add_stream([p])
835 self.src_if.assert_nothing_captured()
836 self.dst_if.assert_nothing_captured()
838 def test_reversed(self):
839 """ reverse order reassembly """
841 fragments = list(self.fragments_400)
844 self.pg_enable_capture()
845 self.src_if.add_stream(fragments)
848 packets = self.dst_if.get_capture(len(self.pkt_infos))
849 self.verify_capture(packets)
850 self.src_if.assert_nothing_captured()
852 # run it all again to verify correctness
853 self.pg_enable_capture()
854 self.src_if.add_stream(fragments)
857 packets = self.dst_if.get_capture(len(self.pkt_infos))
858 self.verify_capture(packets)
859 self.src_if.assert_nothing_captured()
861 def test_random(self):
862 """ random order reassembly """
864 fragments = list(self.fragments_400)
867 self.pg_enable_capture()
868 self.src_if.add_stream(fragments)
871 packets = self.dst_if.get_capture(len(self.pkt_infos))
872 self.verify_capture(packets)
873 self.src_if.assert_nothing_captured()
875 # run it all again to verify correctness
876 self.pg_enable_capture()
877 self.src_if.add_stream(fragments)
880 packets = self.dst_if.get_capture(len(self.pkt_infos))
881 self.verify_capture(packets)
882 self.src_if.assert_nothing_captured()
884 def test_duplicates(self):
885 """ duplicate fragments """
888 x for (_, frags, _) in self.pkt_infos
890 for _ in range(0, min(2, len(frags)))
893 self.pg_enable_capture()
894 self.src_if.add_stream(fragments)
897 packets = self.dst_if.get_capture(len(self.pkt_infos))
898 self.verify_capture(packets)
899 self.src_if.assert_nothing_captured()
901 def test_long_fragment_chain(self):
902 """ long fragment chain """
905 "/err/ip6-full-reassembly-feature/fragment chain too long (drop)"
907 error_cnt = self.statistics.get_err_counter(error_cnt_str)
909 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
910 max_reassembly_length=3,
911 expire_walk_interval_ms=50, is_ip6=1)
913 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
914 IPv6(src=self.src_if.remote_ip6,
915 dst=self.dst_if.remote_ip6) /
916 UDP(sport=1234, dport=5678) /
918 frags = fragment_rfc8200(p, 1, 300) + fragment_rfc8200(p, 2, 500)
920 self.pg_enable_capture()
921 self.src_if.add_stream(frags)
924 self.dst_if.get_capture(1)
925 self.assert_error_counter_equal(error_cnt_str, error_cnt + 1)
927 def test_overlap1(self):
928 """ overlapping fragments case #1 """
931 for _, frags_400, frags_300 in self.pkt_infos:
932 if len(frags_300) == 1:
933 fragments.extend(frags_400)
935 for i, j in zip(frags_300, frags_400):
939 dropped_packet_indexes = set(
940 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
943 self.pg_enable_capture()
944 self.src_if.add_stream(fragments)
947 packets = self.dst_if.get_capture(
948 len(self.pkt_infos) - len(dropped_packet_indexes))
949 self.verify_capture(packets, dropped_packet_indexes)
950 self.src_if.assert_nothing_captured()
952 def test_overlap2(self):
953 """ overlapping fragments case #2 """
956 for _, frags_400, frags_300 in self.pkt_infos:
957 if len(frags_400) == 1:
958 fragments.extend(frags_400)
960 # care must be taken here so that there are no fragments
961 # received by vpp after reassembly is finished, otherwise
962 # new reassemblies will be started and packet generator will
963 # freak out when it detects unfreed buffers
964 zipped = zip(frags_400, frags_300)
970 dropped_packet_indexes = set(
971 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
974 self.pg_enable_capture()
975 self.src_if.add_stream(fragments)
978 packets = self.dst_if.get_capture(
979 len(self.pkt_infos) - len(dropped_packet_indexes))
980 self.verify_capture(packets, dropped_packet_indexes)
981 self.src_if.assert_nothing_captured()
983 def test_timeout_inline(self):
984 """ timeout (inline) """
986 dropped_packet_indexes = set(
987 index for (index, frags, _) in self.pkt_infos if len(frags) > 1
990 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
991 max_reassembly_length=3,
992 expire_walk_interval_ms=10000, is_ip6=1)
994 self.pg_enable_capture()
995 self.src_if.add_stream(self.fragments_400)
998 packets = self.dst_if.get_capture(
999 len(self.pkt_infos) - len(dropped_packet_indexes))
1000 self.verify_capture(packets, dropped_packet_indexes)
1001 pkts = self.src_if.get_capture(
1002 expected_count=len(dropped_packet_indexes))
1004 self.assertIn(ICMPv6TimeExceeded, icmp)
1005 self.assertIn(IPv6ExtHdrFragment, icmp)
1006 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
1007 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
1009 def test_timeout_cleanup(self):
1010 """ timeout (cleanup) """
1012 # whole packets + fragmented packets sans last fragment
1014 x for (_, frags_400, _) in self.pkt_infos
1015 for x in frags_400[:-1 if len(frags_400) > 1 else None]
1018 # last fragments for fragmented packets
1019 fragments2 = [frags_400[-1]
1020 for (_, frags_400, _) in self.pkt_infos
1021 if len(frags_400) > 1]
1023 dropped_packet_indexes = set(
1024 index for (index, frags_400, _) in self.pkt_infos
1025 if len(frags_400) > 1)
1027 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
1028 max_reassembly_length=1000,
1029 expire_walk_interval_ms=50)
1031 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
1032 max_reassembly_length=1000,
1033 expire_walk_interval_ms=50, is_ip6=1)
1035 self.pg_enable_capture()
1036 self.src_if.add_stream(fragments)
1039 self.sleep(.25, "wait before sending rest of fragments")
1041 self.src_if.add_stream(fragments2)
1044 packets = self.dst_if.get_capture(
1045 len(self.pkt_infos) - len(dropped_packet_indexes))
1046 self.verify_capture(packets, dropped_packet_indexes)
1047 pkts = self.src_if.get_capture(
1048 expected_count=len(dropped_packet_indexes))
1050 self.assertIn(ICMPv6TimeExceeded, icmp)
1051 self.assertIn(IPv6ExtHdrFragment, icmp)
1052 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
1053 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
1055 def test_disabled(self):
1056 """ reassembly disabled """
1058 dropped_packet_indexes = set(
1059 index for (index, frags_400, _) in self.pkt_infos
1060 if len(frags_400) > 1)
1062 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
1063 max_reassembly_length=3,
1064 expire_walk_interval_ms=10000, is_ip6=1)
1066 self.pg_enable_capture()
1067 self.src_if.add_stream(self.fragments_400)
1070 packets = self.dst_if.get_capture(
1071 len(self.pkt_infos) - len(dropped_packet_indexes))
1072 self.verify_capture(packets, dropped_packet_indexes)
1073 self.src_if.assert_nothing_captured()
1075 def test_missing_upper(self):
1076 """ missing upper layer """
1077 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1078 IPv6(src=self.src_if.remote_ip6,
1079 dst=self.src_if.local_ip6) /
1080 UDP(sport=1234, dport=5678) /
1082 self.extend_packet(p, 1000, self.padding)
1083 fragments = fragment_rfc8200(p, 1, 500)
1084 bad_fragment = p.__class__(scapy.compat.raw(fragments[1]))
1085 bad_fragment[IPv6ExtHdrFragment].nh = 59
1086 bad_fragment[IPv6ExtHdrFragment].offset = 0
1087 self.pg_enable_capture()
1088 self.src_if.add_stream([bad_fragment])
1090 pkts = self.src_if.get_capture(expected_count=1)
1092 self.assertIn(ICMPv6ParamProblem, icmp)
1093 self.assert_equal(icmp[ICMPv6ParamProblem].code, 3, "ICMP code")
1095 def test_invalid_frag_size(self):
1096 """ fragment size not a multiple of 8 """
1097 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1098 IPv6(src=self.src_if.remote_ip6,
1099 dst=self.src_if.local_ip6) /
1100 UDP(sport=1234, dport=5678) /
1102 self.extend_packet(p, 1000, self.padding)
1103 fragments = fragment_rfc8200(p, 1, 500)
1104 bad_fragment = fragments[0]
1105 self.extend_packet(bad_fragment, len(bad_fragment) + 5)
1106 self.pg_enable_capture()
1107 self.src_if.add_stream([bad_fragment])
1109 pkts = self.src_if.get_capture(expected_count=1)
1111 self.assertIn(ICMPv6ParamProblem, icmp)
1112 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1114 def test_invalid_packet_size(self):
1115 """ total packet size > 65535 """
1116 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1117 IPv6(src=self.src_if.remote_ip6,
1118 dst=self.src_if.local_ip6) /
1119 UDP(sport=1234, dport=5678) /
1121 self.extend_packet(p, 1000, self.padding)
1122 fragments = fragment_rfc8200(p, 1, 500)
1123 bad_fragment = fragments[1]
1124 bad_fragment[IPv6ExtHdrFragment].offset = 65500
1125 self.pg_enable_capture()
1126 self.src_if.add_stream([bad_fragment])
1128 pkts = self.src_if.get_capture(expected_count=1)
1130 self.assertIn(ICMPv6ParamProblem, icmp)
1131 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1134 class TestIPv6MWReassembly(VppTestCase):
1135 """ IPv6 Reassembly (multiple workers) """
1136 worker_config = "workers %d" % worker_count
1139 def setUpClass(cls):
1140 super(TestIPv6MWReassembly, cls).setUpClass()
1142 cls.create_pg_interfaces(range(worker_count+1))
1143 cls.src_if = cls.pg0
1144 cls.send_ifs = cls.pg_interfaces[:-1]
1145 cls.dst_if = cls.pg_interfaces[-1]
1147 # setup all interfaces
1148 for i in cls.pg_interfaces:
1153 # packets sizes reduced here because we are generating packets without
1154 # Ethernet headers, which are added later (diff fragments go via
1155 # different interfaces)
1156 cls.packet_sizes = [64-len(Ether()), 512-len(Ether()),
1157 1518-len(Ether()), 9018-len(Ether())]
1158 cls.padding = " abcdefghijklmn"
1159 cls.create_stream(cls.packet_sizes)
1160 cls.create_fragments()
1163 def tearDownClass(cls):
1164 super(TestIPv6MWReassembly, cls).tearDownClass()
1167 """ Test setup - force timeout on existing reassemblies """
1168 super(TestIPv6MWReassembly, self).setUp()
1169 for intf in self.send_ifs:
1170 self.vapi.ip_reassembly_enable_disable(
1171 sw_if_index=intf.sw_if_index, enable_ip6=True)
1172 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1173 max_reassembly_length=1000,
1174 expire_walk_interval_ms=10, is_ip6=1)
1176 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1177 max_reassembly_length=1000,
1178 expire_walk_interval_ms=1000, is_ip6=1)
1181 super(TestIPv6MWReassembly, self).tearDown()
1183 def show_commands_at_teardown(self):
1184 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1185 self.logger.debug(self.vapi.ppcli("show buffers"))
1188 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
1189 """Create input packet stream
1191 :param list packet_sizes: Required packet sizes.
1193 for i in range(0, packet_count):
1194 info = cls.create_packet_info(cls.src_if, cls.src_if)
1195 payload = cls.info_to_payload(info)
1196 p = (IPv6(src=cls.src_if.remote_ip6,
1197 dst=cls.dst_if.remote_ip6) /
1198 UDP(sport=1234, dport=5678) /
1200 size = packet_sizes[(i // 2) % len(packet_sizes)]
1201 cls.extend_packet(p, size, cls.padding)
1205 def create_fragments(cls):
1206 infos = cls._packet_infos
1208 for index, info in six.iteritems(infos):
1210 # cls.logger.debug(ppp("Packet:",
1211 # p.__class__(scapy.compat.raw(p))))
1212 fragments_400 = fragment_rfc8200(p, index, 400)
1213 cls.pkt_infos.append((index, fragments_400))
1214 cls.fragments_400 = [
1215 x for (_, frags) in cls.pkt_infos for x in frags]
1216 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, " %
1217 (len(infos), len(cls.fragments_400)))
1219 def verify_capture(self, capture, dropped_packet_indexes=[]):
1220 """Verify captured packet strea .
1222 :param list capture: Captured packet stream.
1226 for packet in capture:
1228 self.logger.debug(ppp("Got packet:", packet))
1231 payload_info = self.payload_to_info(packet[Raw])
1232 packet_index = payload_info.index
1234 packet_index not in dropped_packet_indexes,
1235 ppp("Packet received, but should be dropped:", packet))
1236 if packet_index in seen:
1237 raise Exception(ppp("Duplicate packet received", packet))
1238 seen.add(packet_index)
1239 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
1240 info = self._packet_infos[packet_index]
1241 self.assertTrue(info is not None)
1242 self.assertEqual(packet_index, info.index)
1243 saved_packet = info.data
1244 self.assertEqual(ip.src, saved_packet[IPv6].src)
1245 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
1246 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1248 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1250 for index in self._packet_infos:
1251 self.assertTrue(index in seen or index in dropped_packet_indexes,
1252 "Packet with packet_index %d not received" % index)
1254 def send_packets(self, packets):
1255 for counter in range(worker_count):
1256 if 0 == len(packets[counter]):
1258 send_if = self.send_ifs[counter]
1260 (Ether(dst=send_if.local_mac, src=send_if.remote_mac) / x
1261 for x in packets[counter]),
1265 def test_worker_conflict(self):
1266 """ 1st and FO=0 fragments on different workers """
1268 # in first wave we send fragments which don't start at offset 0
1269 # then we send fragments with offset 0 on a different thread
1270 # then the rest of packets on a random thread
1271 first_packets = [[] for n in range(worker_count)]
1272 second_packets = [[] for n in range(worker_count)]
1273 rest_of_packets = [[] for n in range(worker_count)]
1274 for (_, p) in self.pkt_infos:
1275 wi = randrange(worker_count)
1276 second_packets[wi].append(p[0])
1281 wi2 = randrange(worker_count)
1282 first_packets[wi2].append(p[1])
1283 wi3 = randrange(worker_count)
1284 rest_of_packets[wi3].extend(p[2:])
1286 self.pg_enable_capture()
1287 self.send_packets(first_packets)
1288 self.send_packets(second_packets)
1289 self.send_packets(rest_of_packets)
1291 packets = self.dst_if.get_capture(len(self.pkt_infos))
1292 self.verify_capture(packets)
1293 for send_if in self.send_ifs:
1294 send_if.assert_nothing_captured()
1296 self.pg_enable_capture()
1297 self.send_packets(first_packets)
1298 self.send_packets(second_packets)
1299 self.send_packets(rest_of_packets)
1301 packets = self.dst_if.get_capture(len(self.pkt_infos))
1302 self.verify_capture(packets)
1303 for send_if in self.send_ifs:
1304 send_if.assert_nothing_captured()
1307 class TestIPv4ReassemblyLocalNode(VppTestCase):
1308 """ IPv4 Reassembly for packets coming to ip4-local node """
1311 def setUpClass(cls):
1312 super(TestIPv4ReassemblyLocalNode, cls).setUpClass()
1314 cls.create_pg_interfaces([0])
1315 cls.src_dst_if = cls.pg0
1317 # setup all interfaces
1318 for i in cls.pg_interfaces:
1323 cls.padding = " abcdefghijklmn"
1325 cls.create_fragments()
1328 def tearDownClass(cls):
1329 super(TestIPv4ReassemblyLocalNode, cls).tearDownClass()
1332 """ Test setup - force timeout on existing reassemblies """
1333 super(TestIPv4ReassemblyLocalNode, self).setUp()
1334 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1335 max_reassembly_length=1000,
1336 expire_walk_interval_ms=10)
1338 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1339 max_reassembly_length=1000,
1340 expire_walk_interval_ms=10000)
1343 super(TestIPv4ReassemblyLocalNode, self).tearDown()
1345 def show_commands_at_teardown(self):
1346 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
1347 self.logger.debug(self.vapi.ppcli("show buffers"))
1350 def create_stream(cls, packet_count=test_packet_count):
1351 """Create input packet stream for defined interface.
1353 :param list packet_sizes: Required packet sizes.
1355 for i in range(0, packet_count):
1356 info = cls.create_packet_info(cls.src_dst_if, cls.src_dst_if)
1357 payload = cls.info_to_payload(info)
1358 p = (Ether(dst=cls.src_dst_if.local_mac,
1359 src=cls.src_dst_if.remote_mac) /
1360 IP(id=info.index, src=cls.src_dst_if.remote_ip4,
1361 dst=cls.src_dst_if.local_ip4) /
1362 ICMP(type='echo-request', id=1234) /
1364 cls.extend_packet(p, 1518, cls.padding)
1368 def create_fragments(cls):
1369 infos = cls._packet_infos
1371 for index, info in six.iteritems(infos):
1373 # cls.logger.debug(ppp("Packet:",
1374 # p.__class__(scapy.compat.raw(p))))
1375 fragments_300 = fragment_rfc791(p, 300)
1376 cls.pkt_infos.append((index, fragments_300))
1377 cls.fragments_300 = [x for (_, frags) in cls.pkt_infos for x in frags]
1378 cls.logger.debug("Fragmented %s packets into %s 300-byte fragments" %
1379 (len(infos), len(cls.fragments_300)))
1381 def verify_capture(self, capture):
1382 """Verify captured packet stream.
1384 :param list capture: Captured packet stream.
1388 for packet in capture:
1390 self.logger.debug(ppp("Got packet:", packet))
1393 payload_info = self.payload_to_info(packet[Raw])
1394 packet_index = payload_info.index
1395 if packet_index in seen:
1396 raise Exception(ppp("Duplicate packet received", packet))
1397 seen.add(packet_index)
1398 self.assertEqual(payload_info.dst, self.src_dst_if.sw_if_index)
1399 info = self._packet_infos[packet_index]
1400 self.assertIsNotNone(info)
1401 self.assertEqual(packet_index, info.index)
1402 saved_packet = info.data
1403 self.assertEqual(ip.src, saved_packet[IP].dst)
1404 self.assertEqual(ip.dst, saved_packet[IP].src)
1405 self.assertEqual(icmp.type, 0) # echo reply
1406 self.assertEqual(icmp.id, saved_packet[ICMP].id)
1407 self.assertEqual(icmp.payload, saved_packet[ICMP].payload)
1409 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1411 for index in self._packet_infos:
1412 self.assertIn(index, seen,
1413 "Packet with packet_index %d not received" % index)
1415 def test_reassembly(self):
1416 """ basic reassembly """
1418 self.pg_enable_capture()
1419 self.src_dst_if.add_stream(self.fragments_300)
1422 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
1423 self.verify_capture(packets)
1425 # run it all again to verify correctness
1426 self.pg_enable_capture()
1427 self.src_dst_if.add_stream(self.fragments_300)
1430 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
1431 self.verify_capture(packets)
1434 class TestFIFReassembly(VppTestCase):
1435 """ Fragments in fragments reassembly """
1438 def setUpClass(cls):
1439 super(TestFIFReassembly, cls).setUpClass()
1441 cls.create_pg_interfaces([0, 1])
1442 cls.src_if = cls.pg0
1443 cls.dst_if = cls.pg1
1444 for i in cls.pg_interfaces:
1451 cls.packet_sizes = [64, 512, 1518, 9018]
1452 cls.padding = " abcdefghijklmn"
1455 def tearDownClass(cls):
1456 super(TestFIFReassembly, cls).tearDownClass()
1459 """ Test setup - force timeout on existing reassemblies """
1460 super(TestFIFReassembly, self).setUp()
1461 self.vapi.ip_reassembly_enable_disable(
1462 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
1464 self.vapi.ip_reassembly_enable_disable(
1465 sw_if_index=self.dst_if.sw_if_index, enable_ip4=True,
1467 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1468 max_reassembly_length=1000,
1469 expire_walk_interval_ms=10)
1470 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1471 max_reassembly_length=1000,
1472 expire_walk_interval_ms=10, is_ip6=1)
1474 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1475 max_reassembly_length=1000,
1476 expire_walk_interval_ms=10000)
1477 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1478 max_reassembly_length=1000,
1479 expire_walk_interval_ms=10000, is_ip6=1)
1482 super(TestFIFReassembly, self).tearDown()
1484 def show_commands_at_teardown(self):
1485 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
1486 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1487 self.logger.debug(self.vapi.ppcli("show buffers"))
1489 def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):
1490 """Verify captured packet stream.
1492 :param list capture: Captured packet stream.
1496 for packet in capture:
1498 self.logger.debug(ppp("Got packet:", packet))
1499 ip = packet[ip_class]
1501 payload_info = self.payload_to_info(packet[Raw])
1502 packet_index = payload_info.index
1504 packet_index not in dropped_packet_indexes,
1505 ppp("Packet received, but should be dropped:", packet))
1506 if packet_index in seen:
1507 raise Exception(ppp("Duplicate packet received", packet))
1508 seen.add(packet_index)
1509 self.assertEqual(payload_info.dst, self.dst_if.sw_if_index)
1510 info = self._packet_infos[packet_index]
1511 self.assertTrue(info is not None)
1512 self.assertEqual(packet_index, info.index)
1513 saved_packet = info.data
1514 self.assertEqual(ip.src, saved_packet[ip_class].src)
1515 self.assertEqual(ip.dst, saved_packet[ip_class].dst)
1516 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1518 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1520 for index in self._packet_infos:
1521 self.assertTrue(index in seen or index in dropped_packet_indexes,
1522 "Packet with packet_index %d not received" % index)
1524 def test_fif4(self):
1525 """ Fragments in fragments (4o4) """
1527 # TODO this should be ideally in setUpClass, but then we hit a bug
1528 # with VppIpRoute incorrectly reporting it's present when it's not
1529 # so we need to manually remove the vpp config, thus we cannot have
1530 # it shared for multiple test cases
1531 self.tun_ip4 = "1.1.1.2"
1533 self.gre4 = VppGreInterface(self, self.src_if.local_ip4, self.tun_ip4)
1534 self.gre4.add_vpp_config()
1535 self.gre4.admin_up()
1536 self.gre4.config_ip4()
1538 self.vapi.ip_reassembly_enable_disable(
1539 sw_if_index=self.gre4.sw_if_index, enable_ip4=True)
1541 self.route4 = VppIpRoute(self, self.tun_ip4, 32,
1542 [VppRoutePath(self.src_if.remote_ip4,
1543 self.src_if.sw_if_index)])
1544 self.route4.add_vpp_config()
1546 self.reset_packet_infos()
1547 for i in range(test_packet_count):
1548 info = self.create_packet_info(self.src_if, self.dst_if)
1549 payload = self.info_to_payload(info)
1550 # Ethernet header here is only for size calculation, thus it
1551 # doesn't matter how it's initialized. This is to ensure that
1552 # reassembled packet is not > 9000 bytes, so that it's not dropped
1554 IP(id=i, src=self.src_if.remote_ip4,
1555 dst=self.dst_if.remote_ip4) /
1556 UDP(sport=1234, dport=5678) /
1558 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1559 self.extend_packet(p, size, self.padding)
1560 info.data = p[IP] # use only IP part, without ethernet header
1562 fragments = [x for _, p in six.iteritems(self._packet_infos)
1563 for x in fragment_rfc791(p.data, 400)]
1565 encapped_fragments = \
1566 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1567 IP(src=self.tun_ip4, dst=self.src_if.local_ip4) /
1572 fragmented_encapped_fragments = \
1573 [x for p in encapped_fragments
1574 for x in fragment_rfc791(p, 200)]
1576 self.src_if.add_stream(fragmented_encapped_fragments)
1578 self.pg_enable_capture(self.pg_interfaces)
1581 self.src_if.assert_nothing_captured()
1582 packets = self.dst_if.get_capture(len(self._packet_infos))
1583 self.verify_capture(packets, IP)
1585 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
1586 # so that it's query_vpp_config() works as it should
1587 self.gre4.remove_vpp_config()
1588 self.logger.debug(self.vapi.ppcli("show interface"))
1590 def test_fif6(self):
1591 """ Fragments in fragments (6o6) """
1592 # TODO this should be ideally in setUpClass, but then we hit a bug
1593 # with VppIpRoute incorrectly reporting it's present when it's not
1594 # so we need to manually remove the vpp config, thus we cannot have
1595 # it shared for multiple test cases
1596 self.tun_ip6 = "1002::1"
1598 self.gre6 = VppGreInterface(self, self.src_if.local_ip6, self.tun_ip6)
1599 self.gre6.add_vpp_config()
1600 self.gre6.admin_up()
1601 self.gre6.config_ip6()
1603 self.vapi.ip_reassembly_enable_disable(
1604 sw_if_index=self.gre6.sw_if_index, enable_ip6=True)
1606 self.route6 = VppIpRoute(self, self.tun_ip6, 128,
1608 self.src_if.remote_ip6,
1609 self.src_if.sw_if_index)])
1610 self.route6.add_vpp_config()
1612 self.reset_packet_infos()
1613 for i in range(test_packet_count):
1614 info = self.create_packet_info(self.src_if, self.dst_if)
1615 payload = self.info_to_payload(info)
1616 # Ethernet header here is only for size calculation, thus it
1617 # doesn't matter how it's initialized. This is to ensure that
1618 # reassembled packet is not > 9000 bytes, so that it's not dropped
1620 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1621 UDP(sport=1234, dport=5678) /
1623 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1624 self.extend_packet(p, size, self.padding)
1625 info.data = p[IPv6] # use only IPv6 part, without ethernet header
1627 fragments = [x for _, i in six.iteritems(self._packet_infos)
1628 for x in fragment_rfc8200(
1629 i.data, i.index, 400)]
1631 encapped_fragments = \
1632 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1633 IPv6(src=self.tun_ip6, dst=self.src_if.local_ip6) /
1638 fragmented_encapped_fragments = \
1639 [x for p in encapped_fragments for x in (
1642 2 * len(self._packet_infos) + p[IPv6ExtHdrFragment].id,
1644 if IPv6ExtHdrFragment in p else [p]
1648 self.src_if.add_stream(fragmented_encapped_fragments)
1650 self.pg_enable_capture(self.pg_interfaces)
1653 self.src_if.assert_nothing_captured()
1654 packets = self.dst_if.get_capture(len(self._packet_infos))
1655 self.verify_capture(packets, IPv6)
1657 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
1658 # so that it's query_vpp_config() works as it should
1659 self.gre6.remove_vpp_config()
1662 if __name__ == '__main__':
1663 unittest.main(testRunner=VppTestRunner)