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 IPv6, IPv6ExtHdrFragment, ICMPv6ParamProblem,\
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_reversed(self):
822 """ reverse order reassembly """
824 fragments = list(self.fragments_400)
827 self.pg_enable_capture()
828 self.src_if.add_stream(fragments)
831 packets = self.dst_if.get_capture(len(self.pkt_infos))
832 self.verify_capture(packets)
833 self.src_if.assert_nothing_captured()
835 # run it all again to verify correctness
836 self.pg_enable_capture()
837 self.src_if.add_stream(fragments)
840 packets = self.dst_if.get_capture(len(self.pkt_infos))
841 self.verify_capture(packets)
842 self.src_if.assert_nothing_captured()
844 def test_random(self):
845 """ random order reassembly """
847 fragments = list(self.fragments_400)
850 self.pg_enable_capture()
851 self.src_if.add_stream(fragments)
854 packets = self.dst_if.get_capture(len(self.pkt_infos))
855 self.verify_capture(packets)
856 self.src_if.assert_nothing_captured()
858 # run it all again to verify correctness
859 self.pg_enable_capture()
860 self.src_if.add_stream(fragments)
863 packets = self.dst_if.get_capture(len(self.pkt_infos))
864 self.verify_capture(packets)
865 self.src_if.assert_nothing_captured()
867 def test_duplicates(self):
868 """ duplicate fragments """
871 x for (_, frags, _) in self.pkt_infos
873 for _ in range(0, min(2, len(frags)))
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_long_fragment_chain(self):
885 """ long fragment chain """
888 "/err/ip6-full-reassembly-feature/fragment chain too long (drop)"
890 error_cnt = self.statistics.get_err_counter(error_cnt_str)
892 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
893 max_reassembly_length=3,
894 expire_walk_interval_ms=50, is_ip6=1)
896 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
897 IPv6(src=self.src_if.remote_ip6,
898 dst=self.dst_if.remote_ip6) /
899 UDP(sport=1234, dport=5678) /
901 frags = fragment_rfc8200(p, 1, 300) + fragment_rfc8200(p, 2, 500)
903 self.pg_enable_capture()
904 self.src_if.add_stream(frags)
907 self.dst_if.get_capture(1)
908 self.assert_error_counter_equal(error_cnt_str, error_cnt + 1)
910 def test_overlap1(self):
911 """ overlapping fragments case #1 """
914 for _, frags_400, frags_300 in self.pkt_infos:
915 if len(frags_300) == 1:
916 fragments.extend(frags_400)
918 for i, j in zip(frags_300, frags_400):
922 dropped_packet_indexes = set(
923 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
926 self.pg_enable_capture()
927 self.src_if.add_stream(fragments)
930 packets = self.dst_if.get_capture(
931 len(self.pkt_infos) - len(dropped_packet_indexes))
932 self.verify_capture(packets, dropped_packet_indexes)
933 self.src_if.assert_nothing_captured()
935 def test_overlap2(self):
936 """ overlapping fragments case #2 """
939 for _, frags_400, frags_300 in self.pkt_infos:
940 if len(frags_400) == 1:
941 fragments.extend(frags_400)
943 # care must be taken here so that there are no fragments
944 # received by vpp after reassembly is finished, otherwise
945 # new reassemblies will be started and packet generator will
946 # freak out when it detects unfreed buffers
947 zipped = zip(frags_400, frags_300)
953 dropped_packet_indexes = set(
954 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
957 self.pg_enable_capture()
958 self.src_if.add_stream(fragments)
961 packets = self.dst_if.get_capture(
962 len(self.pkt_infos) - len(dropped_packet_indexes))
963 self.verify_capture(packets, dropped_packet_indexes)
964 self.src_if.assert_nothing_captured()
966 def test_timeout_inline(self):
967 """ timeout (inline) """
969 dropped_packet_indexes = set(
970 index for (index, frags, _) in self.pkt_infos if len(frags) > 1
973 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
974 max_reassembly_length=3,
975 expire_walk_interval_ms=10000, is_ip6=1)
977 self.pg_enable_capture()
978 self.src_if.add_stream(self.fragments_400)
981 packets = self.dst_if.get_capture(
982 len(self.pkt_infos) - len(dropped_packet_indexes))
983 self.verify_capture(packets, dropped_packet_indexes)
984 pkts = self.src_if.get_capture(
985 expected_count=len(dropped_packet_indexes))
987 self.assertIn(ICMPv6TimeExceeded, icmp)
988 self.assertIn(IPv6ExtHdrFragment, icmp)
989 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
990 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
992 def test_timeout_cleanup(self):
993 """ timeout (cleanup) """
995 # whole packets + fragmented packets sans last fragment
997 x for (_, frags_400, _) in self.pkt_infos
998 for x in frags_400[:-1 if len(frags_400) > 1 else None]
1001 # last fragments for fragmented packets
1002 fragments2 = [frags_400[-1]
1003 for (_, frags_400, _) in self.pkt_infos
1004 if len(frags_400) > 1]
1006 dropped_packet_indexes = set(
1007 index for (index, frags_400, _) in self.pkt_infos
1008 if len(frags_400) > 1)
1010 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
1011 max_reassembly_length=1000,
1012 expire_walk_interval_ms=50)
1014 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
1015 max_reassembly_length=1000,
1016 expire_walk_interval_ms=50, is_ip6=1)
1018 self.pg_enable_capture()
1019 self.src_if.add_stream(fragments)
1022 self.sleep(.25, "wait before sending rest of fragments")
1024 self.src_if.add_stream(fragments2)
1027 packets = self.dst_if.get_capture(
1028 len(self.pkt_infos) - len(dropped_packet_indexes))
1029 self.verify_capture(packets, dropped_packet_indexes)
1030 pkts = self.src_if.get_capture(
1031 expected_count=len(dropped_packet_indexes))
1033 self.assertIn(ICMPv6TimeExceeded, icmp)
1034 self.assertIn(IPv6ExtHdrFragment, icmp)
1035 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
1036 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
1038 def test_disabled(self):
1039 """ reassembly disabled """
1041 dropped_packet_indexes = set(
1042 index for (index, frags_400, _) in self.pkt_infos
1043 if len(frags_400) > 1)
1045 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
1046 max_reassembly_length=3,
1047 expire_walk_interval_ms=10000, is_ip6=1)
1049 self.pg_enable_capture()
1050 self.src_if.add_stream(self.fragments_400)
1053 packets = self.dst_if.get_capture(
1054 len(self.pkt_infos) - len(dropped_packet_indexes))
1055 self.verify_capture(packets, dropped_packet_indexes)
1056 self.src_if.assert_nothing_captured()
1058 def test_missing_upper(self):
1059 """ missing upper layer """
1060 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1061 IPv6(src=self.src_if.remote_ip6,
1062 dst=self.src_if.local_ip6) /
1063 UDP(sport=1234, dport=5678) /
1065 self.extend_packet(p, 1000, self.padding)
1066 fragments = fragment_rfc8200(p, 1, 500)
1067 bad_fragment = p.__class__(scapy.compat.raw(fragments[1]))
1068 bad_fragment[IPv6ExtHdrFragment].nh = 59
1069 bad_fragment[IPv6ExtHdrFragment].offset = 0
1070 self.pg_enable_capture()
1071 self.src_if.add_stream([bad_fragment])
1073 pkts = self.src_if.get_capture(expected_count=1)
1075 self.assertIn(ICMPv6ParamProblem, icmp)
1076 self.assert_equal(icmp[ICMPv6ParamProblem].code, 3, "ICMP code")
1078 def test_invalid_frag_size(self):
1079 """ fragment size not a multiple of 8 """
1080 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1081 IPv6(src=self.src_if.remote_ip6,
1082 dst=self.src_if.local_ip6) /
1083 UDP(sport=1234, dport=5678) /
1085 self.extend_packet(p, 1000, self.padding)
1086 fragments = fragment_rfc8200(p, 1, 500)
1087 bad_fragment = fragments[0]
1088 self.extend_packet(bad_fragment, len(bad_fragment) + 5)
1089 self.pg_enable_capture()
1090 self.src_if.add_stream([bad_fragment])
1092 pkts = self.src_if.get_capture(expected_count=1)
1094 self.assertIn(ICMPv6ParamProblem, icmp)
1095 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1097 def test_invalid_packet_size(self):
1098 """ total packet size > 65535 """
1099 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1100 IPv6(src=self.src_if.remote_ip6,
1101 dst=self.src_if.local_ip6) /
1102 UDP(sport=1234, dport=5678) /
1104 self.extend_packet(p, 1000, self.padding)
1105 fragments = fragment_rfc8200(p, 1, 500)
1106 bad_fragment = fragments[1]
1107 bad_fragment[IPv6ExtHdrFragment].offset = 65500
1108 self.pg_enable_capture()
1109 self.src_if.add_stream([bad_fragment])
1111 pkts = self.src_if.get_capture(expected_count=1)
1113 self.assertIn(ICMPv6ParamProblem, icmp)
1114 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1117 class TestIPv6MWReassembly(VppTestCase):
1118 """ IPv6 Reassembly (multiple workers) """
1119 worker_config = "workers %d" % worker_count
1122 def setUpClass(cls):
1123 super(TestIPv6MWReassembly, cls).setUpClass()
1125 cls.create_pg_interfaces(range(worker_count+1))
1126 cls.src_if = cls.pg0
1127 cls.send_ifs = cls.pg_interfaces[:-1]
1128 cls.dst_if = cls.pg_interfaces[-1]
1130 # setup all interfaces
1131 for i in cls.pg_interfaces:
1136 # packets sizes reduced here because we are generating packets without
1137 # Ethernet headers, which are added later (diff fragments go via
1138 # different interfaces)
1139 cls.packet_sizes = [64-len(Ether()), 512-len(Ether()),
1140 1518-len(Ether()), 9018-len(Ether())]
1141 cls.padding = " abcdefghijklmn"
1142 cls.create_stream(cls.packet_sizes)
1143 cls.create_fragments()
1146 def tearDownClass(cls):
1147 super(TestIPv6MWReassembly, cls).tearDownClass()
1150 """ Test setup - force timeout on existing reassemblies """
1151 super(TestIPv6MWReassembly, self).setUp()
1152 for intf in self.send_ifs:
1153 self.vapi.ip_reassembly_enable_disable(
1154 sw_if_index=intf.sw_if_index, enable_ip6=True)
1155 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1156 max_reassembly_length=1000,
1157 expire_walk_interval_ms=10, is_ip6=1)
1159 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1160 max_reassembly_length=1000,
1161 expire_walk_interval_ms=1000, is_ip6=1)
1164 super(TestIPv6MWReassembly, self).tearDown()
1166 def show_commands_at_teardown(self):
1167 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1168 self.logger.debug(self.vapi.ppcli("show buffers"))
1171 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
1172 """Create input packet stream
1174 :param list packet_sizes: Required packet sizes.
1176 for i in range(0, packet_count):
1177 info = cls.create_packet_info(cls.src_if, cls.src_if)
1178 payload = cls.info_to_payload(info)
1179 p = (IPv6(src=cls.src_if.remote_ip6,
1180 dst=cls.dst_if.remote_ip6) /
1181 UDP(sport=1234, dport=5678) /
1183 size = packet_sizes[(i // 2) % len(packet_sizes)]
1184 cls.extend_packet(p, size, cls.padding)
1188 def create_fragments(cls):
1189 infos = cls._packet_infos
1191 for index, info in six.iteritems(infos):
1193 # cls.logger.debug(ppp("Packet:",
1194 # p.__class__(scapy.compat.raw(p))))
1195 fragments_400 = fragment_rfc8200(p, index, 400)
1196 cls.pkt_infos.append((index, fragments_400))
1197 cls.fragments_400 = [
1198 x for (_, frags) in cls.pkt_infos for x in frags]
1199 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, " %
1200 (len(infos), len(cls.fragments_400)))
1202 def verify_capture(self, capture, dropped_packet_indexes=[]):
1203 """Verify captured packet strea .
1205 :param list capture: Captured packet stream.
1209 for packet in capture:
1211 self.logger.debug(ppp("Got packet:", packet))
1214 payload_info = self.payload_to_info(packet[Raw])
1215 packet_index = payload_info.index
1217 packet_index not in dropped_packet_indexes,
1218 ppp("Packet received, but should be dropped:", packet))
1219 if packet_index in seen:
1220 raise Exception(ppp("Duplicate packet received", packet))
1221 seen.add(packet_index)
1222 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
1223 info = self._packet_infos[packet_index]
1224 self.assertTrue(info is not None)
1225 self.assertEqual(packet_index, info.index)
1226 saved_packet = info.data
1227 self.assertEqual(ip.src, saved_packet[IPv6].src)
1228 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
1229 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1231 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1233 for index in self._packet_infos:
1234 self.assertTrue(index in seen or index in dropped_packet_indexes,
1235 "Packet with packet_index %d not received" % index)
1237 def send_packets(self, packets):
1238 for counter in range(worker_count):
1239 if 0 == len(packets[counter]):
1241 send_if = self.send_ifs[counter]
1243 (Ether(dst=send_if.local_mac, src=send_if.remote_mac) / x
1244 for x in packets[counter]),
1248 def test_worker_conflict(self):
1249 """ 1st and FO=0 fragments on different workers """
1251 # in first wave we send fragments which don't start at offset 0
1252 # then we send fragments with offset 0 on a different thread
1253 # then the rest of packets on a random thread
1254 first_packets = [[] for n in range(worker_count)]
1255 second_packets = [[] for n in range(worker_count)]
1256 rest_of_packets = [[] for n in range(worker_count)]
1257 for (_, p) in self.pkt_infos:
1258 wi = randrange(worker_count)
1259 second_packets[wi].append(p[0])
1264 wi2 = randrange(worker_count)
1265 first_packets[wi2].append(p[1])
1266 wi3 = randrange(worker_count)
1267 rest_of_packets[wi3].extend(p[2:])
1269 self.pg_enable_capture()
1270 self.send_packets(first_packets)
1271 self.send_packets(second_packets)
1272 self.send_packets(rest_of_packets)
1274 packets = self.dst_if.get_capture(len(self.pkt_infos))
1275 self.verify_capture(packets)
1276 for send_if in self.send_ifs:
1277 send_if.assert_nothing_captured()
1279 self.pg_enable_capture()
1280 self.send_packets(first_packets)
1281 self.send_packets(second_packets)
1282 self.send_packets(rest_of_packets)
1284 packets = self.dst_if.get_capture(len(self.pkt_infos))
1285 self.verify_capture(packets)
1286 for send_if in self.send_ifs:
1287 send_if.assert_nothing_captured()
1290 class TestIPv4ReassemblyLocalNode(VppTestCase):
1291 """ IPv4 Reassembly for packets coming to ip4-local node """
1294 def setUpClass(cls):
1295 super(TestIPv4ReassemblyLocalNode, cls).setUpClass()
1297 cls.create_pg_interfaces([0])
1298 cls.src_dst_if = cls.pg0
1300 # setup all interfaces
1301 for i in cls.pg_interfaces:
1306 cls.padding = " abcdefghijklmn"
1308 cls.create_fragments()
1311 def tearDownClass(cls):
1312 super(TestIPv4ReassemblyLocalNode, cls).tearDownClass()
1315 """ Test setup - force timeout on existing reassemblies """
1316 super(TestIPv4ReassemblyLocalNode, self).setUp()
1317 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1318 max_reassembly_length=1000,
1319 expire_walk_interval_ms=10)
1321 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1322 max_reassembly_length=1000,
1323 expire_walk_interval_ms=10000)
1326 super(TestIPv4ReassemblyLocalNode, self).tearDown()
1328 def show_commands_at_teardown(self):
1329 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
1330 self.logger.debug(self.vapi.ppcli("show buffers"))
1333 def create_stream(cls, packet_count=test_packet_count):
1334 """Create input packet stream for defined interface.
1336 :param list packet_sizes: Required packet sizes.
1338 for i in range(0, packet_count):
1339 info = cls.create_packet_info(cls.src_dst_if, cls.src_dst_if)
1340 payload = cls.info_to_payload(info)
1341 p = (Ether(dst=cls.src_dst_if.local_mac,
1342 src=cls.src_dst_if.remote_mac) /
1343 IP(id=info.index, src=cls.src_dst_if.remote_ip4,
1344 dst=cls.src_dst_if.local_ip4) /
1345 ICMP(type='echo-request', id=1234) /
1347 cls.extend_packet(p, 1518, cls.padding)
1351 def create_fragments(cls):
1352 infos = cls._packet_infos
1354 for index, info in six.iteritems(infos):
1356 # cls.logger.debug(ppp("Packet:",
1357 # p.__class__(scapy.compat.raw(p))))
1358 fragments_300 = fragment_rfc791(p, 300)
1359 cls.pkt_infos.append((index, fragments_300))
1360 cls.fragments_300 = [x for (_, frags) in cls.pkt_infos for x in frags]
1361 cls.logger.debug("Fragmented %s packets into %s 300-byte fragments" %
1362 (len(infos), len(cls.fragments_300)))
1364 def verify_capture(self, capture):
1365 """Verify captured packet stream.
1367 :param list capture: Captured packet stream.
1371 for packet in capture:
1373 self.logger.debug(ppp("Got packet:", packet))
1376 payload_info = self.payload_to_info(packet[Raw])
1377 packet_index = payload_info.index
1378 if packet_index in seen:
1379 raise Exception(ppp("Duplicate packet received", packet))
1380 seen.add(packet_index)
1381 self.assertEqual(payload_info.dst, self.src_dst_if.sw_if_index)
1382 info = self._packet_infos[packet_index]
1383 self.assertIsNotNone(info)
1384 self.assertEqual(packet_index, info.index)
1385 saved_packet = info.data
1386 self.assertEqual(ip.src, saved_packet[IP].dst)
1387 self.assertEqual(ip.dst, saved_packet[IP].src)
1388 self.assertEqual(icmp.type, 0) # echo reply
1389 self.assertEqual(icmp.id, saved_packet[ICMP].id)
1390 self.assertEqual(icmp.payload, saved_packet[ICMP].payload)
1392 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1394 for index in self._packet_infos:
1395 self.assertIn(index, seen,
1396 "Packet with packet_index %d not received" % index)
1398 def test_reassembly(self):
1399 """ basic reassembly """
1401 self.pg_enable_capture()
1402 self.src_dst_if.add_stream(self.fragments_300)
1405 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
1406 self.verify_capture(packets)
1408 # run it all again to verify correctness
1409 self.pg_enable_capture()
1410 self.src_dst_if.add_stream(self.fragments_300)
1413 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
1414 self.verify_capture(packets)
1417 class TestFIFReassembly(VppTestCase):
1418 """ Fragments in fragments reassembly """
1421 def setUpClass(cls):
1422 super(TestFIFReassembly, cls).setUpClass()
1424 cls.create_pg_interfaces([0, 1])
1425 cls.src_if = cls.pg0
1426 cls.dst_if = cls.pg1
1427 for i in cls.pg_interfaces:
1434 cls.packet_sizes = [64, 512, 1518, 9018]
1435 cls.padding = " abcdefghijklmn"
1438 def tearDownClass(cls):
1439 super(TestFIFReassembly, cls).tearDownClass()
1442 """ Test setup - force timeout on existing reassemblies """
1443 super(TestFIFReassembly, self).setUp()
1444 self.vapi.ip_reassembly_enable_disable(
1445 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
1447 self.vapi.ip_reassembly_enable_disable(
1448 sw_if_index=self.dst_if.sw_if_index, enable_ip4=True,
1450 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1451 max_reassembly_length=1000,
1452 expire_walk_interval_ms=10)
1453 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1454 max_reassembly_length=1000,
1455 expire_walk_interval_ms=10, is_ip6=1)
1457 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1458 max_reassembly_length=1000,
1459 expire_walk_interval_ms=10000)
1460 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1461 max_reassembly_length=1000,
1462 expire_walk_interval_ms=10000, is_ip6=1)
1465 super(TestFIFReassembly, self).tearDown()
1467 def show_commands_at_teardown(self):
1468 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
1469 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1470 self.logger.debug(self.vapi.ppcli("show buffers"))
1472 def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):
1473 """Verify captured packet stream.
1475 :param list capture: Captured packet stream.
1479 for packet in capture:
1481 self.logger.debug(ppp("Got packet:", packet))
1482 ip = packet[ip_class]
1484 payload_info = self.payload_to_info(packet[Raw])
1485 packet_index = payload_info.index
1487 packet_index not in dropped_packet_indexes,
1488 ppp("Packet received, but should be dropped:", packet))
1489 if packet_index in seen:
1490 raise Exception(ppp("Duplicate packet received", packet))
1491 seen.add(packet_index)
1492 self.assertEqual(payload_info.dst, self.dst_if.sw_if_index)
1493 info = self._packet_infos[packet_index]
1494 self.assertTrue(info is not None)
1495 self.assertEqual(packet_index, info.index)
1496 saved_packet = info.data
1497 self.assertEqual(ip.src, saved_packet[ip_class].src)
1498 self.assertEqual(ip.dst, saved_packet[ip_class].dst)
1499 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1501 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1503 for index in self._packet_infos:
1504 self.assertTrue(index in seen or index in dropped_packet_indexes,
1505 "Packet with packet_index %d not received" % index)
1507 def test_fif4(self):
1508 """ Fragments in fragments (4o4) """
1510 # TODO this should be ideally in setUpClass, but then we hit a bug
1511 # with VppIpRoute incorrectly reporting it's present when it's not
1512 # so we need to manually remove the vpp config, thus we cannot have
1513 # it shared for multiple test cases
1514 self.tun_ip4 = "1.1.1.2"
1516 self.gre4 = VppGreInterface(self, self.src_if.local_ip4, self.tun_ip4)
1517 self.gre4.add_vpp_config()
1518 self.gre4.admin_up()
1519 self.gre4.config_ip4()
1521 self.vapi.ip_reassembly_enable_disable(
1522 sw_if_index=self.gre4.sw_if_index, enable_ip4=True)
1524 self.route4 = VppIpRoute(self, self.tun_ip4, 32,
1525 [VppRoutePath(self.src_if.remote_ip4,
1526 self.src_if.sw_if_index)])
1527 self.route4.add_vpp_config()
1529 self.reset_packet_infos()
1530 for i in range(test_packet_count):
1531 info = self.create_packet_info(self.src_if, self.dst_if)
1532 payload = self.info_to_payload(info)
1533 # Ethernet header here is only for size calculation, thus it
1534 # doesn't matter how it's initialized. This is to ensure that
1535 # reassembled packet is not > 9000 bytes, so that it's not dropped
1537 IP(id=i, src=self.src_if.remote_ip4,
1538 dst=self.dst_if.remote_ip4) /
1539 UDP(sport=1234, dport=5678) /
1541 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1542 self.extend_packet(p, size, self.padding)
1543 info.data = p[IP] # use only IP part, without ethernet header
1545 fragments = [x for _, p in six.iteritems(self._packet_infos)
1546 for x in fragment_rfc791(p.data, 400)]
1548 encapped_fragments = \
1549 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1550 IP(src=self.tun_ip4, dst=self.src_if.local_ip4) /
1555 fragmented_encapped_fragments = \
1556 [x for p in encapped_fragments
1557 for x in fragment_rfc791(p, 200)]
1559 self.src_if.add_stream(fragmented_encapped_fragments)
1561 self.pg_enable_capture(self.pg_interfaces)
1564 self.src_if.assert_nothing_captured()
1565 packets = self.dst_if.get_capture(len(self._packet_infos))
1566 self.verify_capture(packets, IP)
1568 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
1569 # so that it's query_vpp_config() works as it should
1570 self.gre4.remove_vpp_config()
1571 self.logger.debug(self.vapi.ppcli("show interface"))
1573 def test_fif6(self):
1574 """ Fragments in fragments (6o6) """
1575 # TODO this should be ideally in setUpClass, but then we hit a bug
1576 # with VppIpRoute incorrectly reporting it's present when it's not
1577 # so we need to manually remove the vpp config, thus we cannot have
1578 # it shared for multiple test cases
1579 self.tun_ip6 = "1002::1"
1581 self.gre6 = VppGreInterface(self, self.src_if.local_ip6, self.tun_ip6)
1582 self.gre6.add_vpp_config()
1583 self.gre6.admin_up()
1584 self.gre6.config_ip6()
1586 self.vapi.ip_reassembly_enable_disable(
1587 sw_if_index=self.gre6.sw_if_index, enable_ip6=True)
1589 self.route6 = VppIpRoute(self, self.tun_ip6, 128,
1591 self.src_if.remote_ip6,
1592 self.src_if.sw_if_index)])
1593 self.route6.add_vpp_config()
1595 self.reset_packet_infos()
1596 for i in range(test_packet_count):
1597 info = self.create_packet_info(self.src_if, self.dst_if)
1598 payload = self.info_to_payload(info)
1599 # Ethernet header here is only for size calculation, thus it
1600 # doesn't matter how it's initialized. This is to ensure that
1601 # reassembled packet is not > 9000 bytes, so that it's not dropped
1603 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1604 UDP(sport=1234, dport=5678) /
1606 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1607 self.extend_packet(p, size, self.padding)
1608 info.data = p[IPv6] # use only IPv6 part, without ethernet header
1610 fragments = [x for _, i in six.iteritems(self._packet_infos)
1611 for x in fragment_rfc8200(
1612 i.data, i.index, 400)]
1614 encapped_fragments = \
1615 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1616 IPv6(src=self.tun_ip6, dst=self.src_if.local_ip6) /
1621 fragmented_encapped_fragments = \
1622 [x for p in encapped_fragments for x in (
1625 2 * len(self._packet_infos) + p[IPv6ExtHdrFragment].id,
1627 if IPv6ExtHdrFragment in p else [p]
1631 self.src_if.add_stream(fragmented_encapped_fragments)
1633 self.pg_enable_capture(self.pg_interfaces)
1636 self.src_if.assert_nothing_captured()
1637 packets = self.dst_if.get_capture(len(self._packet_infos))
1638 self.verify_capture(packets, IPv6)
1640 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
1641 # so that it's query_vpp_config() works as it should
1642 self.gre6.remove_vpp_config()
1645 if __name__ == '__main__':
1646 unittest.main(testRunner=VppTestRunner)