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 TestIPv4SVReassembly(VppTestCase):
507 """ IPv4 Shallow Virtual Reassembly """
511 super(TestIPv4SVReassembly, cls).setUpClass()
513 cls.create_pg_interfaces([0, 1])
517 # setup all interfaces
518 for i in cls.pg_interfaces:
524 """ Test setup - force timeout on existing reassemblies """
525 super(TestIPv4SVReassembly, self).setUp()
526 self.vapi.ip_reassembly_enable_disable(
527 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
528 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
529 self.vapi.ip_reassembly_set(
530 timeout_ms=0, max_reassemblies=1000,
531 max_reassembly_length=1000,
532 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
533 expire_walk_interval_ms=10)
535 self.vapi.ip_reassembly_set(
536 timeout_ms=1000000, max_reassemblies=1000,
537 max_reassembly_length=1000,
538 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
539 expire_walk_interval_ms=10000)
542 super(TestIPv4SVReassembly, self).tearDown()
543 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
544 self.logger.debug(self.vapi.ppcli("show buffers"))
546 def test_basic(self):
547 """ basic reassembly """
551 while len(payload) < payload_len:
552 payload += "%u " % counter
555 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
556 IP(id=1, src=self.src_if.remote_ip4,
557 dst=self.dst_if.remote_ip4) /
558 UDP(sport=1234, dport=5678) /
560 fragments = fragment_rfc791(p, payload_len/4)
562 # send fragment #2 - should be cached inside reassembly
563 self.pg_enable_capture()
564 self.src_if.add_stream(fragments[1])
566 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
567 self.logger.debug(self.vapi.ppcli("show buffers"))
568 self.logger.debug(self.vapi.ppcli("show trace"))
569 self.dst_if.assert_nothing_captured()
571 # send fragment #1 - reassembly is finished now and both fragments
573 self.pg_enable_capture()
574 self.src_if.add_stream(fragments[0])
576 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
577 self.logger.debug(self.vapi.ppcli("show buffers"))
578 self.logger.debug(self.vapi.ppcli("show trace"))
579 c = self.dst_if.get_capture(2)
580 for sent, recvd in zip([fragments[1], fragments[0]], c):
581 self.assertEqual(sent[IP].src, recvd[IP].src)
582 self.assertEqual(sent[IP].dst, recvd[IP].dst)
583 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
585 # send rest of fragments - should be immediately forwarded
586 self.pg_enable_capture()
587 self.src_if.add_stream(fragments[2:])
589 c = self.dst_if.get_capture(len(fragments[2:]))
590 for sent, recvd in zip(fragments[2:], c):
591 self.assertEqual(sent[IP].src, recvd[IP].src)
592 self.assertEqual(sent[IP].dst, recvd[IP].dst)
593 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
595 def test_timeout(self):
596 """ reassembly timeout """
600 while len(payload) < payload_len:
601 payload += "%u " % counter
604 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
605 IP(id=1, src=self.src_if.remote_ip4,
606 dst=self.dst_if.remote_ip4) /
607 UDP(sport=1234, dport=5678) /
609 fragments = fragment_rfc791(p, payload_len/4)
611 self.vapi.ip_reassembly_set(
612 timeout_ms=100, max_reassemblies=1000,
613 max_reassembly_length=1000,
614 expire_walk_interval_ms=50,
615 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
617 # send fragments #2 and #1 - should be forwarded
618 self.pg_enable_capture()
619 self.src_if.add_stream(fragments[0:2])
621 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
622 self.logger.debug(self.vapi.ppcli("show buffers"))
623 self.logger.debug(self.vapi.ppcli("show trace"))
624 c = self.dst_if.get_capture(2)
625 for sent, recvd in zip([fragments[1], fragments[0]], c):
626 self.assertEqual(sent[IP].src, recvd[IP].src)
627 self.assertEqual(sent[IP].dst, recvd[IP].dst)
628 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
631 self.sleep(.25, "wait before sending rest of fragments")
633 # send rest of fragments - shouldn't be forwarded
634 self.pg_enable_capture()
635 self.src_if.add_stream(fragments[2:])
637 self.dst_if.assert_nothing_captured()
640 """ reassembly reuses LRU element """
642 self.vapi.ip_reassembly_set(
643 timeout_ms=1000000, max_reassemblies=1,
644 max_reassembly_length=1000,
645 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
646 expire_walk_interval_ms=10000)
651 while len(payload) < payload_len:
652 payload += "%u " % counter
658 for i in range(packet_count)
659 for p in (Ether(dst=self.src_if.local_mac,
660 src=self.src_if.remote_mac) /
661 IP(id=i, src=self.src_if.remote_ip4,
662 dst=self.dst_if.remote_ip4) /
663 UDP(sport=1234, dport=5678) /
665 for f in fragment_rfc791(p, payload_len/4)]
667 self.pg_enable_capture()
668 self.src_if.add_stream(fragments)
670 c = self.dst_if.get_capture(len(fragments))
671 for sent, recvd in zip(fragments, c):
672 self.assertEqual(sent[IP].src, recvd[IP].src)
673 self.assertEqual(sent[IP].dst, recvd[IP].dst)
674 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
677 class TestIPv4MWReassembly(VppTestCase):
678 """ IPv4 Reassembly (multiple workers) """
679 worker_config = "workers %d" % worker_count
683 super(TestIPv4MWReassembly, cls).setUpClass()
685 cls.create_pg_interfaces(range(worker_count+1))
687 cls.send_ifs = cls.pg_interfaces[:-1]
688 cls.dst_if = cls.pg_interfaces[-1]
690 # setup all interfaces
691 for i in cls.pg_interfaces:
696 # packets sizes reduced here because we are generating packets without
697 # Ethernet headers, which are added later (diff fragments go via
698 # different interfaces)
699 cls.packet_sizes = [64-len(Ether()), 512-len(Ether()),
700 1518-len(Ether()), 9018-len(Ether())]
701 cls.padding = " abcdefghijklmn"
702 cls.create_stream(cls.packet_sizes)
703 cls.create_fragments()
706 def tearDownClass(cls):
707 super(TestIPv4MWReassembly, cls).tearDownClass()
710 """ Test setup - force timeout on existing reassemblies """
711 super(TestIPv4MWReassembly, self).setUp()
712 for intf in self.send_ifs:
713 self.vapi.ip_reassembly_enable_disable(
714 sw_if_index=intf.sw_if_index, enable_ip4=True)
715 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
716 max_reassembly_length=1000,
717 expire_walk_interval_ms=10)
719 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
720 max_reassembly_length=1000,
721 expire_walk_interval_ms=10000)
724 super(TestIPv4MWReassembly, self).tearDown()
726 def show_commands_at_teardown(self):
727 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
728 self.logger.debug(self.vapi.ppcli("show buffers"))
731 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
732 """Create input packet stream
734 :param list packet_sizes: Required packet sizes.
736 for i in range(0, packet_count):
737 info = cls.create_packet_info(cls.src_if, cls.src_if)
738 payload = cls.info_to_payload(info)
739 p = (IP(id=info.index, src=cls.src_if.remote_ip4,
740 dst=cls.dst_if.remote_ip4) /
741 UDP(sport=1234, dport=5678) /
743 size = packet_sizes[(i // 2) % len(packet_sizes)]
744 cls.extend_packet(p, size, cls.padding)
748 def create_fragments(cls):
749 infos = cls._packet_infos
751 for index, info in six.iteritems(infos):
753 # cls.logger.debug(ppp("Packet:",
754 # p.__class__(scapy.compat.raw(p))))
755 fragments_400 = fragment_rfc791(p, 400)
756 cls.pkt_infos.append((index, fragments_400))
757 cls.fragments_400 = [
758 x for (_, frags) in cls.pkt_infos for x in frags]
759 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, " %
760 (len(infos), len(cls.fragments_400)))
762 def verify_capture(self, capture, dropped_packet_indexes=[]):
763 """Verify captured packet stream.
765 :param list capture: Captured packet stream.
769 for packet in capture:
771 self.logger.debug(ppp("Got packet:", packet))
774 payload_info = self.payload_to_info(packet[Raw])
775 packet_index = payload_info.index
777 packet_index not in dropped_packet_indexes,
778 ppp("Packet received, but should be dropped:", packet))
779 if packet_index in seen:
780 raise Exception(ppp("Duplicate packet received", packet))
781 seen.add(packet_index)
782 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
783 info = self._packet_infos[packet_index]
784 self.assertTrue(info is not None)
785 self.assertEqual(packet_index, info.index)
786 saved_packet = info.data
787 self.assertEqual(ip.src, saved_packet[IP].src)
788 self.assertEqual(ip.dst, saved_packet[IP].dst)
789 self.assertEqual(udp.payload, saved_packet[UDP].payload)
791 self.logger.error(ppp("Unexpected or invalid packet:", packet))
793 for index in self._packet_infos:
794 self.assertTrue(index in seen or index in dropped_packet_indexes,
795 "Packet with packet_index %d not received" % index)
797 def send_packets(self, packets):
798 for counter in range(worker_count):
799 if 0 == len(packets[counter]):
801 send_if = self.send_ifs[counter]
803 (Ether(dst=send_if.local_mac, src=send_if.remote_mac) / x
804 for x in packets[counter]),
808 def test_worker_conflict(self):
809 """ 1st and FO=0 fragments on different workers """
811 # in first wave we send fragments which don't start at offset 0
812 # then we send fragments with offset 0 on a different thread
813 # then the rest of packets on a random thread
814 first_packets = [[] for n in range(worker_count)]
815 second_packets = [[] for n in range(worker_count)]
816 rest_of_packets = [[] for n in range(worker_count)]
817 for (_, p) in self.pkt_infos:
818 wi = randrange(worker_count)
819 second_packets[wi].append(p[0])
824 wi2 = randrange(worker_count)
825 first_packets[wi2].append(p[1])
826 wi3 = randrange(worker_count)
827 rest_of_packets[wi3].extend(p[2:])
829 self.pg_enable_capture()
830 self.send_packets(first_packets)
831 self.send_packets(second_packets)
832 self.send_packets(rest_of_packets)
834 packets = self.dst_if.get_capture(len(self.pkt_infos))
835 self.verify_capture(packets)
836 for send_if in self.send_ifs:
837 send_if.assert_nothing_captured()
839 self.pg_enable_capture()
840 self.send_packets(first_packets)
841 self.send_packets(second_packets)
842 self.send_packets(rest_of_packets)
844 packets = self.dst_if.get_capture(len(self.pkt_infos))
845 self.verify_capture(packets)
846 for send_if in self.send_ifs:
847 send_if.assert_nothing_captured()
850 class TestIPv6Reassembly(VppTestCase):
851 """ IPv6 Reassembly """
855 super(TestIPv6Reassembly, cls).setUpClass()
857 cls.create_pg_interfaces([0, 1])
861 # setup all interfaces
862 for i in cls.pg_interfaces:
868 cls.packet_sizes = [64, 512, 1518, 9018]
869 cls.padding = " abcdefghijklmn"
870 cls.create_stream(cls.packet_sizes)
871 cls.create_fragments()
874 def tearDownClass(cls):
875 super(TestIPv6Reassembly, cls).tearDownClass()
878 """ Test setup - force timeout on existing reassemblies """
879 super(TestIPv6Reassembly, self).setUp()
880 self.vapi.ip_reassembly_enable_disable(
881 sw_if_index=self.src_if.sw_if_index, enable_ip6=True)
882 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
883 max_reassembly_length=1000,
884 expire_walk_interval_ms=10, is_ip6=1)
886 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
887 max_reassembly_length=1000,
888 expire_walk_interval_ms=10000, is_ip6=1)
889 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
890 self.logger.debug(self.vapi.ppcli("show buffers"))
893 super(TestIPv6Reassembly, self).tearDown()
895 def show_commands_at_teardown(self):
896 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
897 self.logger.debug(self.vapi.ppcli("show buffers"))
900 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
901 """Create input packet stream for defined interface.
903 :param list packet_sizes: Required packet sizes.
905 for i in range(0, packet_count):
906 info = cls.create_packet_info(cls.src_if, cls.src_if)
907 payload = cls.info_to_payload(info)
908 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
909 IPv6(src=cls.src_if.remote_ip6,
910 dst=cls.dst_if.remote_ip6) /
911 UDP(sport=1234, dport=5678) /
913 size = packet_sizes[(i // 2) % len(packet_sizes)]
914 cls.extend_packet(p, size, cls.padding)
918 def create_fragments(cls):
919 infos = cls._packet_infos
921 for index, info in six.iteritems(infos):
923 # cls.logger.debug(ppp("Packet:",
924 # p.__class__(scapy.compat.raw(p))))
925 fragments_400 = fragment_rfc8200(p, info.index, 400)
926 fragments_300 = fragment_rfc8200(p, info.index, 300)
927 cls.pkt_infos.append((index, fragments_400, fragments_300))
928 cls.fragments_400 = [
929 x for _, frags, _ in cls.pkt_infos for x in frags]
930 cls.fragments_300 = [
931 x for _, _, frags in cls.pkt_infos for x in frags]
932 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
933 "and %s 300-byte fragments" %
934 (len(infos), len(cls.fragments_400),
935 len(cls.fragments_300)))
937 def verify_capture(self, capture, dropped_packet_indexes=[]):
938 """Verify captured packet strea .
940 :param list capture: Captured packet stream.
944 for packet in capture:
946 self.logger.debug(ppp("Got packet:", packet))
949 payload_info = self.payload_to_info(packet[Raw])
950 packet_index = payload_info.index
952 packet_index not in dropped_packet_indexes,
953 ppp("Packet received, but should be dropped:", packet))
954 if packet_index in seen:
955 raise Exception(ppp("Duplicate packet received", packet))
956 seen.add(packet_index)
957 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
958 info = self._packet_infos[packet_index]
959 self.assertTrue(info is not None)
960 self.assertEqual(packet_index, info.index)
961 saved_packet = info.data
962 self.assertEqual(ip.src, saved_packet[IPv6].src)
963 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
964 self.assertEqual(udp.payload, saved_packet[UDP].payload)
966 self.logger.error(ppp("Unexpected or invalid packet:", packet))
968 for index in self._packet_infos:
969 self.assertTrue(index in seen or index in dropped_packet_indexes,
970 "Packet with packet_index %d not received" % index)
972 def test_reassembly(self):
973 """ basic reassembly """
975 self.pg_enable_capture()
976 self.src_if.add_stream(self.fragments_400)
979 packets = self.dst_if.get_capture(len(self.pkt_infos))
980 self.verify_capture(packets)
981 self.src_if.assert_nothing_captured()
983 # run it all again to verify correctness
984 self.pg_enable_capture()
985 self.src_if.add_stream(self.fragments_400)
988 packets = self.dst_if.get_capture(len(self.pkt_infos))
989 self.verify_capture(packets)
990 self.src_if.assert_nothing_captured()
992 def test_buffer_boundary(self):
993 """ fragment header crossing buffer boundary """
995 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
996 IPv6(src=self.src_if.remote_ip6,
997 dst=self.src_if.local_ip6) /
999 options=[HBHOptUnknown(otype=0xff, optlen=0)] * 1000) /
1000 IPv6ExtHdrFragment(m=1) /
1001 UDP(sport=1234, dport=5678) /
1003 self.pg_enable_capture()
1004 self.src_if.add_stream([p])
1006 self.src_if.assert_nothing_captured()
1007 self.dst_if.assert_nothing_captured()
1009 def test_reversed(self):
1010 """ reverse order reassembly """
1012 fragments = list(self.fragments_400)
1015 self.pg_enable_capture()
1016 self.src_if.add_stream(fragments)
1019 packets = self.dst_if.get_capture(len(self.pkt_infos))
1020 self.verify_capture(packets)
1021 self.src_if.assert_nothing_captured()
1023 # run it all again to verify correctness
1024 self.pg_enable_capture()
1025 self.src_if.add_stream(fragments)
1028 packets = self.dst_if.get_capture(len(self.pkt_infos))
1029 self.verify_capture(packets)
1030 self.src_if.assert_nothing_captured()
1032 def test_random(self):
1033 """ random order reassembly """
1035 fragments = list(self.fragments_400)
1038 self.pg_enable_capture()
1039 self.src_if.add_stream(fragments)
1042 packets = self.dst_if.get_capture(len(self.pkt_infos))
1043 self.verify_capture(packets)
1044 self.src_if.assert_nothing_captured()
1046 # run it all again to verify correctness
1047 self.pg_enable_capture()
1048 self.src_if.add_stream(fragments)
1051 packets = self.dst_if.get_capture(len(self.pkt_infos))
1052 self.verify_capture(packets)
1053 self.src_if.assert_nothing_captured()
1055 def test_duplicates(self):
1056 """ duplicate fragments """
1059 x for (_, frags, _) in self.pkt_infos
1061 for _ in range(0, min(2, len(frags)))
1064 self.pg_enable_capture()
1065 self.src_if.add_stream(fragments)
1068 packets = self.dst_if.get_capture(len(self.pkt_infos))
1069 self.verify_capture(packets)
1070 self.src_if.assert_nothing_captured()
1072 def test_long_fragment_chain(self):
1073 """ long fragment chain """
1076 "/err/ip6-full-reassembly-feature/fragment chain too long (drop)"
1078 error_cnt = self.statistics.get_err_counter(error_cnt_str)
1080 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
1081 max_reassembly_length=3,
1082 expire_walk_interval_ms=50, is_ip6=1)
1084 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1085 IPv6(src=self.src_if.remote_ip6,
1086 dst=self.dst_if.remote_ip6) /
1087 UDP(sport=1234, dport=5678) /
1089 frags = fragment_rfc8200(p, 1, 300) + fragment_rfc8200(p, 2, 500)
1091 self.pg_enable_capture()
1092 self.src_if.add_stream(frags)
1095 self.dst_if.get_capture(1)
1096 self.assert_error_counter_equal(error_cnt_str, error_cnt + 1)
1098 def test_overlap1(self):
1099 """ overlapping fragments case #1 """
1102 for _, frags_400, frags_300 in self.pkt_infos:
1103 if len(frags_300) == 1:
1104 fragments.extend(frags_400)
1106 for i, j in zip(frags_300, frags_400):
1110 dropped_packet_indexes = set(
1111 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
1114 self.pg_enable_capture()
1115 self.src_if.add_stream(fragments)
1118 packets = self.dst_if.get_capture(
1119 len(self.pkt_infos) - len(dropped_packet_indexes))
1120 self.verify_capture(packets, dropped_packet_indexes)
1121 self.src_if.assert_nothing_captured()
1123 def test_overlap2(self):
1124 """ overlapping fragments case #2 """
1127 for _, frags_400, frags_300 in self.pkt_infos:
1128 if len(frags_400) == 1:
1129 fragments.extend(frags_400)
1131 # care must be taken here so that there are no fragments
1132 # received by vpp after reassembly is finished, otherwise
1133 # new reassemblies will be started and packet generator will
1134 # freak out when it detects unfreed buffers
1135 zipped = zip(frags_400, frags_300)
1141 dropped_packet_indexes = set(
1142 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
1145 self.pg_enable_capture()
1146 self.src_if.add_stream(fragments)
1149 packets = self.dst_if.get_capture(
1150 len(self.pkt_infos) - len(dropped_packet_indexes))
1151 self.verify_capture(packets, dropped_packet_indexes)
1152 self.src_if.assert_nothing_captured()
1154 def test_timeout_inline(self):
1155 """ timeout (inline) """
1157 dropped_packet_indexes = set(
1158 index for (index, frags, _) in self.pkt_infos if len(frags) > 1
1161 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1162 max_reassembly_length=3,
1163 expire_walk_interval_ms=10000, is_ip6=1)
1165 self.pg_enable_capture()
1166 self.src_if.add_stream(self.fragments_400)
1169 packets = self.dst_if.get_capture(
1170 len(self.pkt_infos) - len(dropped_packet_indexes))
1171 self.verify_capture(packets, dropped_packet_indexes)
1172 pkts = self.src_if.get_capture(
1173 expected_count=len(dropped_packet_indexes))
1175 self.assertIn(ICMPv6TimeExceeded, icmp)
1176 self.assertIn(IPv6ExtHdrFragment, icmp)
1177 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
1178 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
1180 def test_timeout_cleanup(self):
1181 """ timeout (cleanup) """
1183 # whole packets + fragmented packets sans last fragment
1185 x for (_, frags_400, _) in self.pkt_infos
1186 for x in frags_400[:-1 if len(frags_400) > 1 else None]
1189 # last fragments for fragmented packets
1190 fragments2 = [frags_400[-1]
1191 for (_, frags_400, _) in self.pkt_infos
1192 if len(frags_400) > 1]
1194 dropped_packet_indexes = set(
1195 index for (index, frags_400, _) in self.pkt_infos
1196 if len(frags_400) > 1)
1198 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
1199 max_reassembly_length=1000,
1200 expire_walk_interval_ms=50)
1202 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
1203 max_reassembly_length=1000,
1204 expire_walk_interval_ms=50, is_ip6=1)
1206 self.pg_enable_capture()
1207 self.src_if.add_stream(fragments)
1210 self.sleep(.25, "wait before sending rest of fragments")
1212 self.src_if.add_stream(fragments2)
1215 packets = self.dst_if.get_capture(
1216 len(self.pkt_infos) - len(dropped_packet_indexes))
1217 self.verify_capture(packets, dropped_packet_indexes)
1218 pkts = self.src_if.get_capture(
1219 expected_count=len(dropped_packet_indexes))
1221 self.assertIn(ICMPv6TimeExceeded, icmp)
1222 self.assertIn(IPv6ExtHdrFragment, icmp)
1223 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
1224 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
1226 def test_disabled(self):
1227 """ reassembly disabled """
1229 dropped_packet_indexes = set(
1230 index for (index, frags_400, _) in self.pkt_infos
1231 if len(frags_400) > 1)
1233 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
1234 max_reassembly_length=3,
1235 expire_walk_interval_ms=10000, is_ip6=1)
1237 self.pg_enable_capture()
1238 self.src_if.add_stream(self.fragments_400)
1241 packets = self.dst_if.get_capture(
1242 len(self.pkt_infos) - len(dropped_packet_indexes))
1243 self.verify_capture(packets, dropped_packet_indexes)
1244 self.src_if.assert_nothing_captured()
1246 def test_missing_upper(self):
1247 """ missing upper layer """
1248 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1249 IPv6(src=self.src_if.remote_ip6,
1250 dst=self.src_if.local_ip6) /
1251 UDP(sport=1234, dport=5678) /
1253 self.extend_packet(p, 1000, self.padding)
1254 fragments = fragment_rfc8200(p, 1, 500)
1255 bad_fragment = p.__class__(scapy.compat.raw(fragments[1]))
1256 bad_fragment[IPv6ExtHdrFragment].nh = 59
1257 bad_fragment[IPv6ExtHdrFragment].offset = 0
1258 self.pg_enable_capture()
1259 self.src_if.add_stream([bad_fragment])
1261 pkts = self.src_if.get_capture(expected_count=1)
1263 self.assertIn(ICMPv6ParamProblem, icmp)
1264 self.assert_equal(icmp[ICMPv6ParamProblem].code, 3, "ICMP code")
1266 def test_invalid_frag_size(self):
1267 """ fragment size not a multiple of 8 """
1268 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1269 IPv6(src=self.src_if.remote_ip6,
1270 dst=self.src_if.local_ip6) /
1271 UDP(sport=1234, dport=5678) /
1273 self.extend_packet(p, 1000, self.padding)
1274 fragments = fragment_rfc8200(p, 1, 500)
1275 bad_fragment = fragments[0]
1276 self.extend_packet(bad_fragment, len(bad_fragment) + 5)
1277 self.pg_enable_capture()
1278 self.src_if.add_stream([bad_fragment])
1280 pkts = self.src_if.get_capture(expected_count=1)
1282 self.assertIn(ICMPv6ParamProblem, icmp)
1283 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1285 def test_invalid_packet_size(self):
1286 """ total packet size > 65535 """
1287 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1288 IPv6(src=self.src_if.remote_ip6,
1289 dst=self.src_if.local_ip6) /
1290 UDP(sport=1234, dport=5678) /
1292 self.extend_packet(p, 1000, self.padding)
1293 fragments = fragment_rfc8200(p, 1, 500)
1294 bad_fragment = fragments[1]
1295 bad_fragment[IPv6ExtHdrFragment].offset = 65500
1296 self.pg_enable_capture()
1297 self.src_if.add_stream([bad_fragment])
1299 pkts = self.src_if.get_capture(expected_count=1)
1301 self.assertIn(ICMPv6ParamProblem, icmp)
1302 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1305 class TestIPv6MWReassembly(VppTestCase):
1306 """ IPv6 Reassembly (multiple workers) """
1307 worker_config = "workers %d" % worker_count
1310 def setUpClass(cls):
1311 super(TestIPv6MWReassembly, cls).setUpClass()
1313 cls.create_pg_interfaces(range(worker_count+1))
1314 cls.src_if = cls.pg0
1315 cls.send_ifs = cls.pg_interfaces[:-1]
1316 cls.dst_if = cls.pg_interfaces[-1]
1318 # setup all interfaces
1319 for i in cls.pg_interfaces:
1324 # packets sizes reduced here because we are generating packets without
1325 # Ethernet headers, which are added later (diff fragments go via
1326 # different interfaces)
1327 cls.packet_sizes = [64-len(Ether()), 512-len(Ether()),
1328 1518-len(Ether()), 9018-len(Ether())]
1329 cls.padding = " abcdefghijklmn"
1330 cls.create_stream(cls.packet_sizes)
1331 cls.create_fragments()
1334 def tearDownClass(cls):
1335 super(TestIPv6MWReassembly, cls).tearDownClass()
1338 """ Test setup - force timeout on existing reassemblies """
1339 super(TestIPv6MWReassembly, self).setUp()
1340 for intf in self.send_ifs:
1341 self.vapi.ip_reassembly_enable_disable(
1342 sw_if_index=intf.sw_if_index, enable_ip6=True)
1343 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1344 max_reassembly_length=1000,
1345 expire_walk_interval_ms=10, is_ip6=1)
1347 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1348 max_reassembly_length=1000,
1349 expire_walk_interval_ms=1000, is_ip6=1)
1352 super(TestIPv6MWReassembly, self).tearDown()
1354 def show_commands_at_teardown(self):
1355 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1356 self.logger.debug(self.vapi.ppcli("show buffers"))
1359 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
1360 """Create input packet stream
1362 :param list packet_sizes: Required packet sizes.
1364 for i in range(0, packet_count):
1365 info = cls.create_packet_info(cls.src_if, cls.src_if)
1366 payload = cls.info_to_payload(info)
1367 p = (IPv6(src=cls.src_if.remote_ip6,
1368 dst=cls.dst_if.remote_ip6) /
1369 UDP(sport=1234, dport=5678) /
1371 size = packet_sizes[(i // 2) % len(packet_sizes)]
1372 cls.extend_packet(p, size, cls.padding)
1376 def create_fragments(cls):
1377 infos = cls._packet_infos
1379 for index, info in six.iteritems(infos):
1381 # cls.logger.debug(ppp("Packet:",
1382 # p.__class__(scapy.compat.raw(p))))
1383 fragments_400 = fragment_rfc8200(p, index, 400)
1384 cls.pkt_infos.append((index, fragments_400))
1385 cls.fragments_400 = [
1386 x for (_, frags) in cls.pkt_infos for x in frags]
1387 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, " %
1388 (len(infos), len(cls.fragments_400)))
1390 def verify_capture(self, capture, dropped_packet_indexes=[]):
1391 """Verify captured packet strea .
1393 :param list capture: Captured packet stream.
1397 for packet in capture:
1399 self.logger.debug(ppp("Got packet:", packet))
1402 payload_info = self.payload_to_info(packet[Raw])
1403 packet_index = payload_info.index
1405 packet_index not in dropped_packet_indexes,
1406 ppp("Packet received, but should be dropped:", packet))
1407 if packet_index in seen:
1408 raise Exception(ppp("Duplicate packet received", packet))
1409 seen.add(packet_index)
1410 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
1411 info = self._packet_infos[packet_index]
1412 self.assertTrue(info is not None)
1413 self.assertEqual(packet_index, info.index)
1414 saved_packet = info.data
1415 self.assertEqual(ip.src, saved_packet[IPv6].src)
1416 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
1417 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1419 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1421 for index in self._packet_infos:
1422 self.assertTrue(index in seen or index in dropped_packet_indexes,
1423 "Packet with packet_index %d not received" % index)
1425 def send_packets(self, packets):
1426 for counter in range(worker_count):
1427 if 0 == len(packets[counter]):
1429 send_if = self.send_ifs[counter]
1431 (Ether(dst=send_if.local_mac, src=send_if.remote_mac) / x
1432 for x in packets[counter]),
1436 def test_worker_conflict(self):
1437 """ 1st and FO=0 fragments on different workers """
1439 # in first wave we send fragments which don't start at offset 0
1440 # then we send fragments with offset 0 on a different thread
1441 # then the rest of packets on a random thread
1442 first_packets = [[] for n in range(worker_count)]
1443 second_packets = [[] for n in range(worker_count)]
1444 rest_of_packets = [[] for n in range(worker_count)]
1445 for (_, p) in self.pkt_infos:
1446 wi = randrange(worker_count)
1447 second_packets[wi].append(p[0])
1452 wi2 = randrange(worker_count)
1453 first_packets[wi2].append(p[1])
1454 wi3 = randrange(worker_count)
1455 rest_of_packets[wi3].extend(p[2:])
1457 self.pg_enable_capture()
1458 self.send_packets(first_packets)
1459 self.send_packets(second_packets)
1460 self.send_packets(rest_of_packets)
1462 packets = self.dst_if.get_capture(len(self.pkt_infos))
1463 self.verify_capture(packets)
1464 for send_if in self.send_ifs:
1465 send_if.assert_nothing_captured()
1467 self.pg_enable_capture()
1468 self.send_packets(first_packets)
1469 self.send_packets(second_packets)
1470 self.send_packets(rest_of_packets)
1472 packets = self.dst_if.get_capture(len(self.pkt_infos))
1473 self.verify_capture(packets)
1474 for send_if in self.send_ifs:
1475 send_if.assert_nothing_captured()
1478 class TestIPv6SVReassembly(VppTestCase):
1479 """ IPv6 Shallow Virtual Reassembly """
1482 def setUpClass(cls):
1483 super(TestIPv6SVReassembly, cls).setUpClass()
1485 cls.create_pg_interfaces([0, 1])
1486 cls.src_if = cls.pg0
1487 cls.dst_if = cls.pg1
1489 # setup all interfaces
1490 for i in cls.pg_interfaces:
1496 """ Test setup - force timeout on existing reassemblies """
1497 super(TestIPv6SVReassembly, self).setUp()
1498 self.vapi.ip_reassembly_enable_disable(
1499 sw_if_index=self.src_if.sw_if_index, enable_ip6=True,
1500 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
1501 self.vapi.ip_reassembly_set(
1502 timeout_ms=0, max_reassemblies=1000,
1503 max_reassembly_length=1000,
1504 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1505 expire_walk_interval_ms=10, is_ip6=1)
1507 self.vapi.ip_reassembly_set(
1508 timeout_ms=1000000, max_reassemblies=1000,
1509 max_reassembly_length=1000,
1510 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1511 expire_walk_interval_ms=10000, is_ip6=1)
1514 super(TestIPv6SVReassembly, self).tearDown()
1515 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1516 self.logger.debug(self.vapi.ppcli("show buffers"))
1518 def test_basic(self):
1519 """ basic reassembly """
1523 while len(payload) < payload_len:
1524 payload += "%u " % counter
1527 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1528 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1529 UDP(sport=1234, dport=5678) /
1531 fragments = fragment_rfc8200(p, 1, payload_len/4)
1533 # send fragment #2 - should be cached inside reassembly
1534 self.pg_enable_capture()
1535 self.src_if.add_stream(fragments[1])
1537 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1538 self.logger.debug(self.vapi.ppcli("show buffers"))
1539 self.logger.debug(self.vapi.ppcli("show trace"))
1540 self.dst_if.assert_nothing_captured()
1542 # send fragment #1 - reassembly is finished now and both fragments
1544 self.pg_enable_capture()
1545 self.src_if.add_stream(fragments[0])
1547 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1548 self.logger.debug(self.vapi.ppcli("show buffers"))
1549 self.logger.debug(self.vapi.ppcli("show trace"))
1550 c = self.dst_if.get_capture(2)
1551 for sent, recvd in zip([fragments[1], fragments[0]], c):
1552 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1553 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1554 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1556 # send rest of fragments - should be immediately forwarded
1557 self.pg_enable_capture()
1558 self.src_if.add_stream(fragments[2:])
1560 c = self.dst_if.get_capture(len(fragments[2:]))
1561 for sent, recvd in zip(fragments[2:], c):
1562 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1563 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1564 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1566 def test_timeout(self):
1567 """ reassembly timeout """
1571 while len(payload) < payload_len:
1572 payload += "%u " % counter
1575 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1576 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1577 UDP(sport=1234, dport=5678) /
1579 fragments = fragment_rfc8200(p, 1, payload_len/4)
1581 self.vapi.ip_reassembly_set(
1582 timeout_ms=100, max_reassemblies=1000,
1583 max_reassembly_length=1000,
1584 expire_walk_interval_ms=50,
1586 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
1588 # send fragments #2 and #1 - should be forwarded
1589 self.pg_enable_capture()
1590 self.src_if.add_stream(fragments[0:2])
1592 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
1593 self.logger.debug(self.vapi.ppcli("show buffers"))
1594 self.logger.debug(self.vapi.ppcli("show trace"))
1595 c = self.dst_if.get_capture(2)
1596 for sent, recvd in zip([fragments[1], fragments[0]], c):
1597 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1598 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1599 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1602 self.sleep(.25, "wait before sending rest of fragments")
1604 # send rest of fragments - shouldn't be forwarded
1605 self.pg_enable_capture()
1606 self.src_if.add_stream(fragments[2:])
1608 self.dst_if.assert_nothing_captured()
1611 """ reassembly reuses LRU element """
1613 self.vapi.ip_reassembly_set(
1614 timeout_ms=1000000, max_reassemblies=1,
1615 max_reassembly_length=1000,
1616 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1617 is_ip6=1, expire_walk_interval_ms=10000)
1622 while len(payload) < payload_len:
1623 payload += "%u " % counter
1629 for i in range(packet_count)
1630 for p in (Ether(dst=self.src_if.local_mac,
1631 src=self.src_if.remote_mac) /
1632 IPv6(src=self.src_if.remote_ip6,
1633 dst=self.dst_if.remote_ip6) /
1634 UDP(sport=1234, dport=5678) /
1636 for f in fragment_rfc8200(p, i, payload_len/4)]
1638 self.pg_enable_capture()
1639 self.src_if.add_stream(fragments)
1641 c = self.dst_if.get_capture(len(fragments))
1642 for sent, recvd in zip(fragments, c):
1643 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1644 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1645 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1648 class TestIPv4ReassemblyLocalNode(VppTestCase):
1649 """ IPv4 Reassembly for packets coming to ip4-local node """
1652 def setUpClass(cls):
1653 super(TestIPv4ReassemblyLocalNode, cls).setUpClass()
1655 cls.create_pg_interfaces([0])
1656 cls.src_dst_if = cls.pg0
1658 # setup all interfaces
1659 for i in cls.pg_interfaces:
1664 cls.padding = " abcdefghijklmn"
1666 cls.create_fragments()
1669 def tearDownClass(cls):
1670 super(TestIPv4ReassemblyLocalNode, cls).tearDownClass()
1673 """ Test setup - force timeout on existing reassemblies """
1674 super(TestIPv4ReassemblyLocalNode, self).setUp()
1675 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1676 max_reassembly_length=1000,
1677 expire_walk_interval_ms=10)
1679 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1680 max_reassembly_length=1000,
1681 expire_walk_interval_ms=10000)
1684 super(TestIPv4ReassemblyLocalNode, self).tearDown()
1686 def show_commands_at_teardown(self):
1687 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
1688 self.logger.debug(self.vapi.ppcli("show buffers"))
1691 def create_stream(cls, packet_count=test_packet_count):
1692 """Create input packet stream for defined interface.
1694 :param list packet_sizes: Required packet sizes.
1696 for i in range(0, packet_count):
1697 info = cls.create_packet_info(cls.src_dst_if, cls.src_dst_if)
1698 payload = cls.info_to_payload(info)
1699 p = (Ether(dst=cls.src_dst_if.local_mac,
1700 src=cls.src_dst_if.remote_mac) /
1701 IP(id=info.index, src=cls.src_dst_if.remote_ip4,
1702 dst=cls.src_dst_if.local_ip4) /
1703 ICMP(type='echo-request', id=1234) /
1705 cls.extend_packet(p, 1518, cls.padding)
1709 def create_fragments(cls):
1710 infos = cls._packet_infos
1712 for index, info in six.iteritems(infos):
1714 # cls.logger.debug(ppp("Packet:",
1715 # p.__class__(scapy.compat.raw(p))))
1716 fragments_300 = fragment_rfc791(p, 300)
1717 cls.pkt_infos.append((index, fragments_300))
1718 cls.fragments_300 = [x for (_, frags) in cls.pkt_infos for x in frags]
1719 cls.logger.debug("Fragmented %s packets into %s 300-byte fragments" %
1720 (len(infos), len(cls.fragments_300)))
1722 def verify_capture(self, capture):
1723 """Verify captured packet stream.
1725 :param list capture: Captured packet stream.
1729 for packet in capture:
1731 self.logger.debug(ppp("Got packet:", packet))
1734 payload_info = self.payload_to_info(packet[Raw])
1735 packet_index = payload_info.index
1736 if packet_index in seen:
1737 raise Exception(ppp("Duplicate packet received", packet))
1738 seen.add(packet_index)
1739 self.assertEqual(payload_info.dst, self.src_dst_if.sw_if_index)
1740 info = self._packet_infos[packet_index]
1741 self.assertIsNotNone(info)
1742 self.assertEqual(packet_index, info.index)
1743 saved_packet = info.data
1744 self.assertEqual(ip.src, saved_packet[IP].dst)
1745 self.assertEqual(ip.dst, saved_packet[IP].src)
1746 self.assertEqual(icmp.type, 0) # echo reply
1747 self.assertEqual(icmp.id, saved_packet[ICMP].id)
1748 self.assertEqual(icmp.payload, saved_packet[ICMP].payload)
1750 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1752 for index in self._packet_infos:
1753 self.assertIn(index, seen,
1754 "Packet with packet_index %d not received" % index)
1756 def test_reassembly(self):
1757 """ basic reassembly """
1759 self.pg_enable_capture()
1760 self.src_dst_if.add_stream(self.fragments_300)
1763 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
1764 self.verify_capture(packets)
1766 # run it all again to verify correctness
1767 self.pg_enable_capture()
1768 self.src_dst_if.add_stream(self.fragments_300)
1771 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
1772 self.verify_capture(packets)
1775 class TestFIFReassembly(VppTestCase):
1776 """ Fragments in fragments reassembly """
1779 def setUpClass(cls):
1780 super(TestFIFReassembly, cls).setUpClass()
1782 cls.create_pg_interfaces([0, 1])
1783 cls.src_if = cls.pg0
1784 cls.dst_if = cls.pg1
1785 for i in cls.pg_interfaces:
1792 cls.packet_sizes = [64, 512, 1518, 9018]
1793 cls.padding = " abcdefghijklmn"
1796 def tearDownClass(cls):
1797 super(TestFIFReassembly, cls).tearDownClass()
1800 """ Test setup - force timeout on existing reassemblies """
1801 super(TestFIFReassembly, self).setUp()
1802 self.vapi.ip_reassembly_enable_disable(
1803 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
1805 self.vapi.ip_reassembly_enable_disable(
1806 sw_if_index=self.dst_if.sw_if_index, enable_ip4=True,
1808 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1809 max_reassembly_length=1000,
1810 expire_walk_interval_ms=10)
1811 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1812 max_reassembly_length=1000,
1813 expire_walk_interval_ms=10, is_ip6=1)
1815 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1816 max_reassembly_length=1000,
1817 expire_walk_interval_ms=10000)
1818 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1819 max_reassembly_length=1000,
1820 expire_walk_interval_ms=10000, is_ip6=1)
1823 super(TestFIFReassembly, self).tearDown()
1825 def show_commands_at_teardown(self):
1826 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
1827 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1828 self.logger.debug(self.vapi.ppcli("show buffers"))
1830 def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):
1831 """Verify captured packet stream.
1833 :param list capture: Captured packet stream.
1837 for packet in capture:
1839 self.logger.debug(ppp("Got packet:", packet))
1840 ip = packet[ip_class]
1842 payload_info = self.payload_to_info(packet[Raw])
1843 packet_index = payload_info.index
1845 packet_index not in dropped_packet_indexes,
1846 ppp("Packet received, but should be dropped:", packet))
1847 if packet_index in seen:
1848 raise Exception(ppp("Duplicate packet received", packet))
1849 seen.add(packet_index)
1850 self.assertEqual(payload_info.dst, self.dst_if.sw_if_index)
1851 info = self._packet_infos[packet_index]
1852 self.assertTrue(info is not None)
1853 self.assertEqual(packet_index, info.index)
1854 saved_packet = info.data
1855 self.assertEqual(ip.src, saved_packet[ip_class].src)
1856 self.assertEqual(ip.dst, saved_packet[ip_class].dst)
1857 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1859 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1861 for index in self._packet_infos:
1862 self.assertTrue(index in seen or index in dropped_packet_indexes,
1863 "Packet with packet_index %d not received" % index)
1865 def test_fif4(self):
1866 """ Fragments in fragments (4o4) """
1868 # TODO this should be ideally in setUpClass, but then we hit a bug
1869 # with VppIpRoute incorrectly reporting it's present when it's not
1870 # so we need to manually remove the vpp config, thus we cannot have
1871 # it shared for multiple test cases
1872 self.tun_ip4 = "1.1.1.2"
1874 self.gre4 = VppGreInterface(self, self.src_if.local_ip4, self.tun_ip4)
1875 self.gre4.add_vpp_config()
1876 self.gre4.admin_up()
1877 self.gre4.config_ip4()
1879 self.vapi.ip_reassembly_enable_disable(
1880 sw_if_index=self.gre4.sw_if_index, enable_ip4=True)
1882 self.route4 = VppIpRoute(self, self.tun_ip4, 32,
1883 [VppRoutePath(self.src_if.remote_ip4,
1884 self.src_if.sw_if_index)])
1885 self.route4.add_vpp_config()
1887 self.reset_packet_infos()
1888 for i in range(test_packet_count):
1889 info = self.create_packet_info(self.src_if, self.dst_if)
1890 payload = self.info_to_payload(info)
1891 # Ethernet header here is only for size calculation, thus it
1892 # doesn't matter how it's initialized. This is to ensure that
1893 # reassembled packet is not > 9000 bytes, so that it's not dropped
1895 IP(id=i, src=self.src_if.remote_ip4,
1896 dst=self.dst_if.remote_ip4) /
1897 UDP(sport=1234, dport=5678) /
1899 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1900 self.extend_packet(p, size, self.padding)
1901 info.data = p[IP] # use only IP part, without ethernet header
1903 fragments = [x for _, p in six.iteritems(self._packet_infos)
1904 for x in fragment_rfc791(p.data, 400)]
1906 encapped_fragments = \
1907 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1908 IP(src=self.tun_ip4, dst=self.src_if.local_ip4) /
1913 fragmented_encapped_fragments = \
1914 [x for p in encapped_fragments
1915 for x in fragment_rfc791(p, 200)]
1917 self.src_if.add_stream(fragmented_encapped_fragments)
1919 self.pg_enable_capture(self.pg_interfaces)
1922 self.src_if.assert_nothing_captured()
1923 packets = self.dst_if.get_capture(len(self._packet_infos))
1924 self.verify_capture(packets, IP)
1926 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
1927 # so that it's query_vpp_config() works as it should
1928 self.gre4.remove_vpp_config()
1929 self.logger.debug(self.vapi.ppcli("show interface"))
1931 def test_fif6(self):
1932 """ Fragments in fragments (6o6) """
1933 # TODO this should be ideally in setUpClass, but then we hit a bug
1934 # with VppIpRoute incorrectly reporting it's present when it's not
1935 # so we need to manually remove the vpp config, thus we cannot have
1936 # it shared for multiple test cases
1937 self.tun_ip6 = "1002::1"
1939 self.gre6 = VppGreInterface(self, self.src_if.local_ip6, self.tun_ip6)
1940 self.gre6.add_vpp_config()
1941 self.gre6.admin_up()
1942 self.gre6.config_ip6()
1944 self.vapi.ip_reassembly_enable_disable(
1945 sw_if_index=self.gre6.sw_if_index, enable_ip6=True)
1947 self.route6 = VppIpRoute(self, self.tun_ip6, 128,
1949 self.src_if.remote_ip6,
1950 self.src_if.sw_if_index)])
1951 self.route6.add_vpp_config()
1953 self.reset_packet_infos()
1954 for i in range(test_packet_count):
1955 info = self.create_packet_info(self.src_if, self.dst_if)
1956 payload = self.info_to_payload(info)
1957 # Ethernet header here is only for size calculation, thus it
1958 # doesn't matter how it's initialized. This is to ensure that
1959 # reassembled packet is not > 9000 bytes, so that it's not dropped
1961 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1962 UDP(sport=1234, dport=5678) /
1964 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1965 self.extend_packet(p, size, self.padding)
1966 info.data = p[IPv6] # use only IPv6 part, without ethernet header
1968 fragments = [x for _, i in six.iteritems(self._packet_infos)
1969 for x in fragment_rfc8200(
1970 i.data, i.index, 400)]
1972 encapped_fragments = \
1973 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1974 IPv6(src=self.tun_ip6, dst=self.src_if.local_ip6) /
1979 fragmented_encapped_fragments = \
1980 [x for p in encapped_fragments for x in (
1983 2 * len(self._packet_infos) + p[IPv6ExtHdrFragment].id,
1985 if IPv6ExtHdrFragment in p else [p]
1989 self.src_if.add_stream(fragmented_encapped_fragments)
1991 self.pg_enable_capture(self.pg_interfaces)
1994 self.src_if.assert_nothing_captured()
1995 packets = self.dst_if.get_capture(len(self._packet_infos))
1996 self.verify_capture(packets, IPv6)
1998 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
1999 # so that it's query_vpp_config() works as it should
2000 self.gre6.remove_vpp_config()
2003 if __name__ == '__main__':
2004 unittest.main(testRunner=VppTestRunner)