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.logger.debug(self.vapi.ppcli("show trace"))
840 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
841 self.logger.debug(self.vapi.ppcli("show buffers"))
842 self.vapi.cli("clear trace")
844 self.pg_enable_capture()
845 self.send_packets(first_packets)
846 self.send_packets(second_packets)
847 self.send_packets(rest_of_packets)
849 packets = self.dst_if.get_capture(len(self.pkt_infos))
850 self.verify_capture(packets)
851 for send_if in self.send_ifs:
852 send_if.assert_nothing_captured()
855 class TestIPv6Reassembly(VppTestCase):
856 """ IPv6 Reassembly """
860 super(TestIPv6Reassembly, cls).setUpClass()
862 cls.create_pg_interfaces([0, 1])
866 # setup all interfaces
867 for i in cls.pg_interfaces:
873 cls.packet_sizes = [64, 512, 1518, 9018]
874 cls.padding = " abcdefghijklmn"
875 cls.create_stream(cls.packet_sizes)
876 cls.create_fragments()
879 def tearDownClass(cls):
880 super(TestIPv6Reassembly, cls).tearDownClass()
883 """ Test setup - force timeout on existing reassemblies """
884 super(TestIPv6Reassembly, self).setUp()
885 self.vapi.ip_reassembly_enable_disable(
886 sw_if_index=self.src_if.sw_if_index, enable_ip6=True)
887 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
888 max_reassembly_length=1000,
889 expire_walk_interval_ms=10, is_ip6=1)
891 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
892 max_reassembly_length=1000,
893 expire_walk_interval_ms=10000, is_ip6=1)
894 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
895 self.logger.debug(self.vapi.ppcli("show buffers"))
898 super(TestIPv6Reassembly, self).tearDown()
900 def show_commands_at_teardown(self):
901 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
902 self.logger.debug(self.vapi.ppcli("show buffers"))
905 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
906 """Create input packet stream for defined interface.
908 :param list packet_sizes: Required packet sizes.
910 for i in range(0, packet_count):
911 info = cls.create_packet_info(cls.src_if, cls.src_if)
912 payload = cls.info_to_payload(info)
913 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
914 IPv6(src=cls.src_if.remote_ip6,
915 dst=cls.dst_if.remote_ip6) /
916 UDP(sport=1234, dport=5678) /
918 size = packet_sizes[(i // 2) % len(packet_sizes)]
919 cls.extend_packet(p, size, cls.padding)
923 def create_fragments(cls):
924 infos = cls._packet_infos
926 for index, info in six.iteritems(infos):
928 # cls.logger.debug(ppp("Packet:",
929 # p.__class__(scapy.compat.raw(p))))
930 fragments_400 = fragment_rfc8200(p, info.index, 400)
931 fragments_300 = fragment_rfc8200(p, info.index, 300)
932 cls.pkt_infos.append((index, fragments_400, fragments_300))
933 cls.fragments_400 = [
934 x for _, frags, _ in cls.pkt_infos for x in frags]
935 cls.fragments_300 = [
936 x for _, _, frags in cls.pkt_infos for x in frags]
937 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
938 "and %s 300-byte fragments" %
939 (len(infos), len(cls.fragments_400),
940 len(cls.fragments_300)))
942 def verify_capture(self, capture, dropped_packet_indexes=[]):
943 """Verify captured packet strea .
945 :param list capture: Captured packet stream.
949 for packet in capture:
951 self.logger.debug(ppp("Got packet:", packet))
954 payload_info = self.payload_to_info(packet[Raw])
955 packet_index = payload_info.index
957 packet_index not in dropped_packet_indexes,
958 ppp("Packet received, but should be dropped:", packet))
959 if packet_index in seen:
960 raise Exception(ppp("Duplicate packet received", packet))
961 seen.add(packet_index)
962 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
963 info = self._packet_infos[packet_index]
964 self.assertTrue(info is not None)
965 self.assertEqual(packet_index, info.index)
966 saved_packet = info.data
967 self.assertEqual(ip.src, saved_packet[IPv6].src)
968 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
969 self.assertEqual(udp.payload, saved_packet[UDP].payload)
971 self.logger.error(ppp("Unexpected or invalid packet:", packet))
973 for index in self._packet_infos:
974 self.assertTrue(index in seen or index in dropped_packet_indexes,
975 "Packet with packet_index %d not received" % index)
977 def test_reassembly(self):
978 """ basic reassembly """
980 self.pg_enable_capture()
981 self.src_if.add_stream(self.fragments_400)
984 packets = self.dst_if.get_capture(len(self.pkt_infos))
985 self.verify_capture(packets)
986 self.src_if.assert_nothing_captured()
988 # run it all again to verify correctness
989 self.pg_enable_capture()
990 self.src_if.add_stream(self.fragments_400)
993 packets = self.dst_if.get_capture(len(self.pkt_infos))
994 self.verify_capture(packets)
995 self.src_if.assert_nothing_captured()
997 def test_buffer_boundary(self):
998 """ fragment header crossing buffer boundary """
1000 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1001 IPv6(src=self.src_if.remote_ip6,
1002 dst=self.src_if.local_ip6) /
1004 options=[HBHOptUnknown(otype=0xff, optlen=0)] * 1000) /
1005 IPv6ExtHdrFragment(m=1) /
1006 UDP(sport=1234, dport=5678) /
1008 self.pg_enable_capture()
1009 self.src_if.add_stream([p])
1011 self.src_if.assert_nothing_captured()
1012 self.dst_if.assert_nothing_captured()
1014 def test_reversed(self):
1015 """ reverse order reassembly """
1017 fragments = list(self.fragments_400)
1020 self.pg_enable_capture()
1021 self.src_if.add_stream(fragments)
1024 packets = self.dst_if.get_capture(len(self.pkt_infos))
1025 self.verify_capture(packets)
1026 self.src_if.assert_nothing_captured()
1028 # run it all again to verify correctness
1029 self.pg_enable_capture()
1030 self.src_if.add_stream(fragments)
1033 packets = self.dst_if.get_capture(len(self.pkt_infos))
1034 self.verify_capture(packets)
1035 self.src_if.assert_nothing_captured()
1037 def test_random(self):
1038 """ random order reassembly """
1040 fragments = list(self.fragments_400)
1043 self.pg_enable_capture()
1044 self.src_if.add_stream(fragments)
1047 packets = self.dst_if.get_capture(len(self.pkt_infos))
1048 self.verify_capture(packets)
1049 self.src_if.assert_nothing_captured()
1051 # run it all again to verify correctness
1052 self.pg_enable_capture()
1053 self.src_if.add_stream(fragments)
1056 packets = self.dst_if.get_capture(len(self.pkt_infos))
1057 self.verify_capture(packets)
1058 self.src_if.assert_nothing_captured()
1060 def test_duplicates(self):
1061 """ duplicate fragments """
1064 x for (_, frags, _) in self.pkt_infos
1066 for _ in range(0, min(2, len(frags)))
1069 self.pg_enable_capture()
1070 self.src_if.add_stream(fragments)
1073 packets = self.dst_if.get_capture(len(self.pkt_infos))
1074 self.verify_capture(packets)
1075 self.src_if.assert_nothing_captured()
1077 def test_long_fragment_chain(self):
1078 """ long fragment chain """
1081 "/err/ip6-full-reassembly-feature/fragment chain too long (drop)"
1083 error_cnt = self.statistics.get_err_counter(error_cnt_str)
1085 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
1086 max_reassembly_length=3,
1087 expire_walk_interval_ms=50, is_ip6=1)
1089 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1090 IPv6(src=self.src_if.remote_ip6,
1091 dst=self.dst_if.remote_ip6) /
1092 UDP(sport=1234, dport=5678) /
1094 frags = fragment_rfc8200(p, 1, 300) + fragment_rfc8200(p, 2, 500)
1096 self.pg_enable_capture()
1097 self.src_if.add_stream(frags)
1100 self.dst_if.get_capture(1)
1101 self.assert_error_counter_equal(error_cnt_str, error_cnt + 1)
1103 def test_overlap1(self):
1104 """ overlapping fragments case #1 """
1107 for _, frags_400, frags_300 in self.pkt_infos:
1108 if len(frags_300) == 1:
1109 fragments.extend(frags_400)
1111 for i, j in zip(frags_300, frags_400):
1115 dropped_packet_indexes = set(
1116 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
1119 self.pg_enable_capture()
1120 self.src_if.add_stream(fragments)
1123 packets = self.dst_if.get_capture(
1124 len(self.pkt_infos) - len(dropped_packet_indexes))
1125 self.verify_capture(packets, dropped_packet_indexes)
1126 self.src_if.assert_nothing_captured()
1128 def test_overlap2(self):
1129 """ overlapping fragments case #2 """
1132 for _, frags_400, frags_300 in self.pkt_infos:
1133 if len(frags_400) == 1:
1134 fragments.extend(frags_400)
1136 # care must be taken here so that there are no fragments
1137 # received by vpp after reassembly is finished, otherwise
1138 # new reassemblies will be started and packet generator will
1139 # freak out when it detects unfreed buffers
1140 zipped = zip(frags_400, frags_300)
1146 dropped_packet_indexes = set(
1147 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
1150 self.pg_enable_capture()
1151 self.src_if.add_stream(fragments)
1154 packets = self.dst_if.get_capture(
1155 len(self.pkt_infos) - len(dropped_packet_indexes))
1156 self.verify_capture(packets, dropped_packet_indexes)
1157 self.src_if.assert_nothing_captured()
1159 def test_timeout_inline(self):
1160 """ timeout (inline) """
1162 dropped_packet_indexes = set(
1163 index for (index, frags, _) in self.pkt_infos if len(frags) > 1
1166 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1167 max_reassembly_length=3,
1168 expire_walk_interval_ms=10000, is_ip6=1)
1170 self.pg_enable_capture()
1171 self.src_if.add_stream(self.fragments_400)
1174 packets = self.dst_if.get_capture(
1175 len(self.pkt_infos) - len(dropped_packet_indexes))
1176 self.verify_capture(packets, dropped_packet_indexes)
1177 pkts = self.src_if.get_capture(
1178 expected_count=len(dropped_packet_indexes))
1180 self.assertIn(ICMPv6TimeExceeded, icmp)
1181 self.assertIn(IPv6ExtHdrFragment, icmp)
1182 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
1183 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
1185 def test_timeout_cleanup(self):
1186 """ timeout (cleanup) """
1188 # whole packets + fragmented packets sans last fragment
1190 x for (_, frags_400, _) in self.pkt_infos
1191 for x in frags_400[:-1 if len(frags_400) > 1 else None]
1194 # last fragments for fragmented packets
1195 fragments2 = [frags_400[-1]
1196 for (_, frags_400, _) in self.pkt_infos
1197 if len(frags_400) > 1]
1199 dropped_packet_indexes = set(
1200 index for (index, frags_400, _) in self.pkt_infos
1201 if len(frags_400) > 1)
1203 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
1204 max_reassembly_length=1000,
1205 expire_walk_interval_ms=50)
1207 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
1208 max_reassembly_length=1000,
1209 expire_walk_interval_ms=50, is_ip6=1)
1211 self.pg_enable_capture()
1212 self.src_if.add_stream(fragments)
1215 self.sleep(.25, "wait before sending rest of fragments")
1217 self.src_if.add_stream(fragments2)
1220 packets = self.dst_if.get_capture(
1221 len(self.pkt_infos) - len(dropped_packet_indexes))
1222 self.verify_capture(packets, dropped_packet_indexes)
1223 pkts = self.src_if.get_capture(
1224 expected_count=len(dropped_packet_indexes))
1226 self.assertIn(ICMPv6TimeExceeded, icmp)
1227 self.assertIn(IPv6ExtHdrFragment, icmp)
1228 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
1229 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
1231 def test_disabled(self):
1232 """ reassembly disabled """
1234 dropped_packet_indexes = set(
1235 index for (index, frags_400, _) in self.pkt_infos
1236 if len(frags_400) > 1)
1238 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
1239 max_reassembly_length=3,
1240 expire_walk_interval_ms=10000, is_ip6=1)
1242 self.pg_enable_capture()
1243 self.src_if.add_stream(self.fragments_400)
1246 packets = self.dst_if.get_capture(
1247 len(self.pkt_infos) - len(dropped_packet_indexes))
1248 self.verify_capture(packets, dropped_packet_indexes)
1249 self.src_if.assert_nothing_captured()
1251 def test_missing_upper(self):
1252 """ missing upper layer """
1253 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1254 IPv6(src=self.src_if.remote_ip6,
1255 dst=self.src_if.local_ip6) /
1256 UDP(sport=1234, dport=5678) /
1258 self.extend_packet(p, 1000, self.padding)
1259 fragments = fragment_rfc8200(p, 1, 500)
1260 bad_fragment = p.__class__(scapy.compat.raw(fragments[1]))
1261 bad_fragment[IPv6ExtHdrFragment].nh = 59
1262 bad_fragment[IPv6ExtHdrFragment].offset = 0
1263 self.pg_enable_capture()
1264 self.src_if.add_stream([bad_fragment])
1266 pkts = self.src_if.get_capture(expected_count=1)
1268 self.assertIn(ICMPv6ParamProblem, icmp)
1269 self.assert_equal(icmp[ICMPv6ParamProblem].code, 3, "ICMP code")
1271 def test_invalid_frag_size(self):
1272 """ fragment size not a multiple of 8 """
1273 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1274 IPv6(src=self.src_if.remote_ip6,
1275 dst=self.src_if.local_ip6) /
1276 UDP(sport=1234, dport=5678) /
1278 self.extend_packet(p, 1000, self.padding)
1279 fragments = fragment_rfc8200(p, 1, 500)
1280 bad_fragment = fragments[0]
1281 self.extend_packet(bad_fragment, len(bad_fragment) + 5)
1282 self.pg_enable_capture()
1283 self.src_if.add_stream([bad_fragment])
1285 pkts = self.src_if.get_capture(expected_count=1)
1287 self.assertIn(ICMPv6ParamProblem, icmp)
1288 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1290 def test_invalid_packet_size(self):
1291 """ total packet size > 65535 """
1292 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1293 IPv6(src=self.src_if.remote_ip6,
1294 dst=self.src_if.local_ip6) /
1295 UDP(sport=1234, dport=5678) /
1297 self.extend_packet(p, 1000, self.padding)
1298 fragments = fragment_rfc8200(p, 1, 500)
1299 bad_fragment = fragments[1]
1300 bad_fragment[IPv6ExtHdrFragment].offset = 65500
1301 self.pg_enable_capture()
1302 self.src_if.add_stream([bad_fragment])
1304 pkts = self.src_if.get_capture(expected_count=1)
1306 self.assertIn(ICMPv6ParamProblem, icmp)
1307 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1310 class TestIPv6MWReassembly(VppTestCase):
1311 """ IPv6 Reassembly (multiple workers) """
1312 worker_config = "workers %d" % worker_count
1315 def setUpClass(cls):
1316 super(TestIPv6MWReassembly, cls).setUpClass()
1318 cls.create_pg_interfaces(range(worker_count+1))
1319 cls.src_if = cls.pg0
1320 cls.send_ifs = cls.pg_interfaces[:-1]
1321 cls.dst_if = cls.pg_interfaces[-1]
1323 # setup all interfaces
1324 for i in cls.pg_interfaces:
1329 # packets sizes reduced here because we are generating packets without
1330 # Ethernet headers, which are added later (diff fragments go via
1331 # different interfaces)
1332 cls.packet_sizes = [64-len(Ether()), 512-len(Ether()),
1333 1518-len(Ether()), 9018-len(Ether())]
1334 cls.padding = " abcdefghijklmn"
1335 cls.create_stream(cls.packet_sizes)
1336 cls.create_fragments()
1339 def tearDownClass(cls):
1340 super(TestIPv6MWReassembly, cls).tearDownClass()
1343 """ Test setup - force timeout on existing reassemblies """
1344 super(TestIPv6MWReassembly, self).setUp()
1345 for intf in self.send_ifs:
1346 self.vapi.ip_reassembly_enable_disable(
1347 sw_if_index=intf.sw_if_index, enable_ip6=True)
1348 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1349 max_reassembly_length=1000,
1350 expire_walk_interval_ms=10, is_ip6=1)
1352 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1353 max_reassembly_length=1000,
1354 expire_walk_interval_ms=1000, is_ip6=1)
1357 super(TestIPv6MWReassembly, self).tearDown()
1359 def show_commands_at_teardown(self):
1360 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1361 self.logger.debug(self.vapi.ppcli("show buffers"))
1364 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
1365 """Create input packet stream
1367 :param list packet_sizes: Required packet sizes.
1369 for i in range(0, packet_count):
1370 info = cls.create_packet_info(cls.src_if, cls.src_if)
1371 payload = cls.info_to_payload(info)
1372 p = (IPv6(src=cls.src_if.remote_ip6,
1373 dst=cls.dst_if.remote_ip6) /
1374 UDP(sport=1234, dport=5678) /
1376 size = packet_sizes[(i // 2) % len(packet_sizes)]
1377 cls.extend_packet(p, size, cls.padding)
1381 def create_fragments(cls):
1382 infos = cls._packet_infos
1384 for index, info in six.iteritems(infos):
1386 # cls.logger.debug(ppp("Packet:",
1387 # p.__class__(scapy.compat.raw(p))))
1388 fragments_400 = fragment_rfc8200(p, index, 400)
1389 cls.pkt_infos.append((index, fragments_400))
1390 cls.fragments_400 = [
1391 x for (_, frags) in cls.pkt_infos for x in frags]
1392 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, " %
1393 (len(infos), len(cls.fragments_400)))
1395 def verify_capture(self, capture, dropped_packet_indexes=[]):
1396 """Verify captured packet strea .
1398 :param list capture: Captured packet stream.
1402 for packet in capture:
1404 self.logger.debug(ppp("Got packet:", packet))
1407 payload_info = self.payload_to_info(packet[Raw])
1408 packet_index = payload_info.index
1410 packet_index not in dropped_packet_indexes,
1411 ppp("Packet received, but should be dropped:", packet))
1412 if packet_index in seen:
1413 raise Exception(ppp("Duplicate packet received", packet))
1414 seen.add(packet_index)
1415 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
1416 info = self._packet_infos[packet_index]
1417 self.assertTrue(info is not None)
1418 self.assertEqual(packet_index, info.index)
1419 saved_packet = info.data
1420 self.assertEqual(ip.src, saved_packet[IPv6].src)
1421 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
1422 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1424 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1426 for index in self._packet_infos:
1427 self.assertTrue(index in seen or index in dropped_packet_indexes,
1428 "Packet with packet_index %d not received" % index)
1430 def send_packets(self, packets):
1431 for counter in range(worker_count):
1432 if 0 == len(packets[counter]):
1434 send_if = self.send_ifs[counter]
1436 (Ether(dst=send_if.local_mac, src=send_if.remote_mac) / x
1437 for x in packets[counter]),
1441 def test_worker_conflict(self):
1442 """ 1st and FO=0 fragments on different workers """
1444 # in first wave we send fragments which don't start at offset 0
1445 # then we send fragments with offset 0 on a different thread
1446 # then the rest of packets on a random thread
1447 first_packets = [[] for n in range(worker_count)]
1448 second_packets = [[] for n in range(worker_count)]
1449 rest_of_packets = [[] for n in range(worker_count)]
1450 for (_, p) in self.pkt_infos:
1451 wi = randrange(worker_count)
1452 second_packets[wi].append(p[0])
1457 wi2 = randrange(worker_count)
1458 first_packets[wi2].append(p[1])
1459 wi3 = randrange(worker_count)
1460 rest_of_packets[wi3].extend(p[2:])
1462 self.pg_enable_capture()
1463 self.send_packets(first_packets)
1464 self.send_packets(second_packets)
1465 self.send_packets(rest_of_packets)
1467 packets = self.dst_if.get_capture(len(self.pkt_infos))
1468 self.verify_capture(packets)
1469 for send_if in self.send_ifs:
1470 send_if.assert_nothing_captured()
1472 self.logger.debug(self.vapi.ppcli("show trace"))
1473 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1474 self.logger.debug(self.vapi.ppcli("show buffers"))
1475 self.vapi.cli("clear trace")
1477 self.pg_enable_capture()
1478 self.send_packets(first_packets)
1479 self.send_packets(second_packets)
1480 self.send_packets(rest_of_packets)
1482 packets = self.dst_if.get_capture(len(self.pkt_infos))
1483 self.verify_capture(packets)
1484 for send_if in self.send_ifs:
1485 send_if.assert_nothing_captured()
1488 class TestIPv6SVReassembly(VppTestCase):
1489 """ IPv6 Shallow Virtual Reassembly """
1492 def setUpClass(cls):
1493 super(TestIPv6SVReassembly, cls).setUpClass()
1495 cls.create_pg_interfaces([0, 1])
1496 cls.src_if = cls.pg0
1497 cls.dst_if = cls.pg1
1499 # setup all interfaces
1500 for i in cls.pg_interfaces:
1506 """ Test setup - force timeout on existing reassemblies """
1507 super(TestIPv6SVReassembly, self).setUp()
1508 self.vapi.ip_reassembly_enable_disable(
1509 sw_if_index=self.src_if.sw_if_index, enable_ip6=True,
1510 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
1511 self.vapi.ip_reassembly_set(
1512 timeout_ms=0, max_reassemblies=1000,
1513 max_reassembly_length=1000,
1514 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1515 expire_walk_interval_ms=10, is_ip6=1)
1517 self.vapi.ip_reassembly_set(
1518 timeout_ms=1000000, max_reassemblies=1000,
1519 max_reassembly_length=1000,
1520 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1521 expire_walk_interval_ms=10000, is_ip6=1)
1524 super(TestIPv6SVReassembly, self).tearDown()
1525 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1526 self.logger.debug(self.vapi.ppcli("show buffers"))
1528 def test_basic(self):
1529 """ basic reassembly """
1533 while len(payload) < payload_len:
1534 payload += "%u " % counter
1537 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1538 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1539 UDP(sport=1234, dport=5678) /
1541 fragments = fragment_rfc8200(p, 1, payload_len/4)
1543 # send fragment #2 - should be cached inside reassembly
1544 self.pg_enable_capture()
1545 self.src_if.add_stream(fragments[1])
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 self.dst_if.assert_nothing_captured()
1552 # send fragment #1 - reassembly is finished now and both fragments
1554 self.pg_enable_capture()
1555 self.src_if.add_stream(fragments[0])
1557 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1558 self.logger.debug(self.vapi.ppcli("show buffers"))
1559 self.logger.debug(self.vapi.ppcli("show trace"))
1560 c = self.dst_if.get_capture(2)
1561 for sent, recvd in zip([fragments[1], fragments[0]], 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 # send rest of fragments - should be immediately forwarded
1567 self.pg_enable_capture()
1568 self.src_if.add_stream(fragments[2:])
1570 c = self.dst_if.get_capture(len(fragments[2:]))
1571 for sent, recvd in zip(fragments[2:], c):
1572 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1573 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1574 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1576 def test_timeout(self):
1577 """ reassembly timeout """
1581 while len(payload) < payload_len:
1582 payload += "%u " % counter
1585 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1586 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1587 UDP(sport=1234, dport=5678) /
1589 fragments = fragment_rfc8200(p, 1, payload_len/4)
1591 self.vapi.ip_reassembly_set(
1592 timeout_ms=100, max_reassemblies=1000,
1593 max_reassembly_length=1000,
1594 expire_walk_interval_ms=50,
1596 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
1598 # send fragments #2 and #1 - should be forwarded
1599 self.pg_enable_capture()
1600 self.src_if.add_stream(fragments[0:2])
1602 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
1603 self.logger.debug(self.vapi.ppcli("show buffers"))
1604 self.logger.debug(self.vapi.ppcli("show trace"))
1605 c = self.dst_if.get_capture(2)
1606 for sent, recvd in zip([fragments[1], fragments[0]], c):
1607 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1608 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1609 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1612 self.sleep(.25, "wait before sending rest of fragments")
1614 # send rest of fragments - shouldn't be forwarded
1615 self.pg_enable_capture()
1616 self.src_if.add_stream(fragments[2:])
1618 self.dst_if.assert_nothing_captured()
1621 """ reassembly reuses LRU element """
1623 self.vapi.ip_reassembly_set(
1624 timeout_ms=1000000, max_reassemblies=1,
1625 max_reassembly_length=1000,
1626 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1627 is_ip6=1, expire_walk_interval_ms=10000)
1632 while len(payload) < payload_len:
1633 payload += "%u " % counter
1639 for i in range(packet_count)
1640 for p in (Ether(dst=self.src_if.local_mac,
1641 src=self.src_if.remote_mac) /
1642 IPv6(src=self.src_if.remote_ip6,
1643 dst=self.dst_if.remote_ip6) /
1644 UDP(sport=1234, dport=5678) /
1646 for f in fragment_rfc8200(p, i, payload_len/4)]
1648 self.pg_enable_capture()
1649 self.src_if.add_stream(fragments)
1651 c = self.dst_if.get_capture(len(fragments))
1652 for sent, recvd in zip(fragments, c):
1653 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1654 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1655 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1658 class TestIPv4ReassemblyLocalNode(VppTestCase):
1659 """ IPv4 Reassembly for packets coming to ip4-local node """
1662 def setUpClass(cls):
1663 super(TestIPv4ReassemblyLocalNode, cls).setUpClass()
1665 cls.create_pg_interfaces([0])
1666 cls.src_dst_if = cls.pg0
1668 # setup all interfaces
1669 for i in cls.pg_interfaces:
1674 cls.padding = " abcdefghijklmn"
1676 cls.create_fragments()
1679 def tearDownClass(cls):
1680 super(TestIPv4ReassemblyLocalNode, cls).tearDownClass()
1683 """ Test setup - force timeout on existing reassemblies """
1684 super(TestIPv4ReassemblyLocalNode, self).setUp()
1685 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1686 max_reassembly_length=1000,
1687 expire_walk_interval_ms=10)
1689 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1690 max_reassembly_length=1000,
1691 expire_walk_interval_ms=10000)
1694 super(TestIPv4ReassemblyLocalNode, self).tearDown()
1696 def show_commands_at_teardown(self):
1697 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
1698 self.logger.debug(self.vapi.ppcli("show buffers"))
1701 def create_stream(cls, packet_count=test_packet_count):
1702 """Create input packet stream for defined interface.
1704 :param list packet_sizes: Required packet sizes.
1706 for i in range(0, packet_count):
1707 info = cls.create_packet_info(cls.src_dst_if, cls.src_dst_if)
1708 payload = cls.info_to_payload(info)
1709 p = (Ether(dst=cls.src_dst_if.local_mac,
1710 src=cls.src_dst_if.remote_mac) /
1711 IP(id=info.index, src=cls.src_dst_if.remote_ip4,
1712 dst=cls.src_dst_if.local_ip4) /
1713 ICMP(type='echo-request', id=1234) /
1715 cls.extend_packet(p, 1518, cls.padding)
1719 def create_fragments(cls):
1720 infos = cls._packet_infos
1722 for index, info in six.iteritems(infos):
1724 # cls.logger.debug(ppp("Packet:",
1725 # p.__class__(scapy.compat.raw(p))))
1726 fragments_300 = fragment_rfc791(p, 300)
1727 cls.pkt_infos.append((index, fragments_300))
1728 cls.fragments_300 = [x for (_, frags) in cls.pkt_infos for x in frags]
1729 cls.logger.debug("Fragmented %s packets into %s 300-byte fragments" %
1730 (len(infos), len(cls.fragments_300)))
1732 def verify_capture(self, capture):
1733 """Verify captured packet stream.
1735 :param list capture: Captured packet stream.
1739 for packet in capture:
1741 self.logger.debug(ppp("Got packet:", packet))
1744 payload_info = self.payload_to_info(packet[Raw])
1745 packet_index = payload_info.index
1746 if packet_index in seen:
1747 raise Exception(ppp("Duplicate packet received", packet))
1748 seen.add(packet_index)
1749 self.assertEqual(payload_info.dst, self.src_dst_if.sw_if_index)
1750 info = self._packet_infos[packet_index]
1751 self.assertIsNotNone(info)
1752 self.assertEqual(packet_index, info.index)
1753 saved_packet = info.data
1754 self.assertEqual(ip.src, saved_packet[IP].dst)
1755 self.assertEqual(ip.dst, saved_packet[IP].src)
1756 self.assertEqual(icmp.type, 0) # echo reply
1757 self.assertEqual(icmp.id, saved_packet[ICMP].id)
1758 self.assertEqual(icmp.payload, saved_packet[ICMP].payload)
1760 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1762 for index in self._packet_infos:
1763 self.assertIn(index, seen,
1764 "Packet with packet_index %d not received" % index)
1766 def test_reassembly(self):
1767 """ basic reassembly """
1769 self.pg_enable_capture()
1770 self.src_dst_if.add_stream(self.fragments_300)
1773 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
1774 self.verify_capture(packets)
1776 # run it all again to verify correctness
1777 self.pg_enable_capture()
1778 self.src_dst_if.add_stream(self.fragments_300)
1781 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
1782 self.verify_capture(packets)
1785 class TestFIFReassembly(VppTestCase):
1786 """ Fragments in fragments reassembly """
1789 def setUpClass(cls):
1790 super(TestFIFReassembly, cls).setUpClass()
1792 cls.create_pg_interfaces([0, 1])
1793 cls.src_if = cls.pg0
1794 cls.dst_if = cls.pg1
1795 for i in cls.pg_interfaces:
1802 cls.packet_sizes = [64, 512, 1518, 9018]
1803 cls.padding = " abcdefghijklmn"
1806 def tearDownClass(cls):
1807 super(TestFIFReassembly, cls).tearDownClass()
1810 """ Test setup - force timeout on existing reassemblies """
1811 super(TestFIFReassembly, self).setUp()
1812 self.vapi.ip_reassembly_enable_disable(
1813 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
1815 self.vapi.ip_reassembly_enable_disable(
1816 sw_if_index=self.dst_if.sw_if_index, enable_ip4=True,
1818 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1819 max_reassembly_length=1000,
1820 expire_walk_interval_ms=10)
1821 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1822 max_reassembly_length=1000,
1823 expire_walk_interval_ms=10, is_ip6=1)
1825 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1826 max_reassembly_length=1000,
1827 expire_walk_interval_ms=10000)
1828 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1829 max_reassembly_length=1000,
1830 expire_walk_interval_ms=10000, is_ip6=1)
1833 super(TestFIFReassembly, self).tearDown()
1835 def show_commands_at_teardown(self):
1836 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
1837 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1838 self.logger.debug(self.vapi.ppcli("show buffers"))
1840 def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):
1841 """Verify captured packet stream.
1843 :param list capture: Captured packet stream.
1847 for packet in capture:
1849 self.logger.debug(ppp("Got packet:", packet))
1850 ip = packet[ip_class]
1852 payload_info = self.payload_to_info(packet[Raw])
1853 packet_index = payload_info.index
1855 packet_index not in dropped_packet_indexes,
1856 ppp("Packet received, but should be dropped:", packet))
1857 if packet_index in seen:
1858 raise Exception(ppp("Duplicate packet received", packet))
1859 seen.add(packet_index)
1860 self.assertEqual(payload_info.dst, self.dst_if.sw_if_index)
1861 info = self._packet_infos[packet_index]
1862 self.assertTrue(info is not None)
1863 self.assertEqual(packet_index, info.index)
1864 saved_packet = info.data
1865 self.assertEqual(ip.src, saved_packet[ip_class].src)
1866 self.assertEqual(ip.dst, saved_packet[ip_class].dst)
1867 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1869 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1871 for index in self._packet_infos:
1872 self.assertTrue(index in seen or index in dropped_packet_indexes,
1873 "Packet with packet_index %d not received" % index)
1875 def test_fif4(self):
1876 """ Fragments in fragments (4o4) """
1878 # TODO this should be ideally in setUpClass, but then we hit a bug
1879 # with VppIpRoute incorrectly reporting it's present when it's not
1880 # so we need to manually remove the vpp config, thus we cannot have
1881 # it shared for multiple test cases
1882 self.tun_ip4 = "1.1.1.2"
1884 self.gre4 = VppGreInterface(self, self.src_if.local_ip4, self.tun_ip4)
1885 self.gre4.add_vpp_config()
1886 self.gre4.admin_up()
1887 self.gre4.config_ip4()
1889 self.vapi.ip_reassembly_enable_disable(
1890 sw_if_index=self.gre4.sw_if_index, enable_ip4=True)
1892 self.route4 = VppIpRoute(self, self.tun_ip4, 32,
1893 [VppRoutePath(self.src_if.remote_ip4,
1894 self.src_if.sw_if_index)])
1895 self.route4.add_vpp_config()
1897 self.reset_packet_infos()
1898 for i in range(test_packet_count):
1899 info = self.create_packet_info(self.src_if, self.dst_if)
1900 payload = self.info_to_payload(info)
1901 # Ethernet header here is only for size calculation, thus it
1902 # doesn't matter how it's initialized. This is to ensure that
1903 # reassembled packet is not > 9000 bytes, so that it's not dropped
1905 IP(id=i, src=self.src_if.remote_ip4,
1906 dst=self.dst_if.remote_ip4) /
1907 UDP(sport=1234, dport=5678) /
1909 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1910 self.extend_packet(p, size, self.padding)
1911 info.data = p[IP] # use only IP part, without ethernet header
1913 fragments = [x for _, p in six.iteritems(self._packet_infos)
1914 for x in fragment_rfc791(p.data, 400)]
1916 encapped_fragments = \
1917 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1918 IP(src=self.tun_ip4, dst=self.src_if.local_ip4) /
1923 fragmented_encapped_fragments = \
1924 [x for p in encapped_fragments
1925 for x in fragment_rfc791(p, 200)]
1927 self.src_if.add_stream(fragmented_encapped_fragments)
1929 self.pg_enable_capture(self.pg_interfaces)
1932 self.src_if.assert_nothing_captured()
1933 packets = self.dst_if.get_capture(len(self._packet_infos))
1934 self.verify_capture(packets, IP)
1936 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
1937 # so that it's query_vpp_config() works as it should
1938 self.gre4.remove_vpp_config()
1939 self.logger.debug(self.vapi.ppcli("show interface"))
1941 def test_fif6(self):
1942 """ Fragments in fragments (6o6) """
1943 # TODO this should be ideally in setUpClass, but then we hit a bug
1944 # with VppIpRoute incorrectly reporting it's present when it's not
1945 # so we need to manually remove the vpp config, thus we cannot have
1946 # it shared for multiple test cases
1947 self.tun_ip6 = "1002::1"
1949 self.gre6 = VppGreInterface(self, self.src_if.local_ip6, self.tun_ip6)
1950 self.gre6.add_vpp_config()
1951 self.gre6.admin_up()
1952 self.gre6.config_ip6()
1954 self.vapi.ip_reassembly_enable_disable(
1955 sw_if_index=self.gre6.sw_if_index, enable_ip6=True)
1957 self.route6 = VppIpRoute(self, self.tun_ip6, 128,
1959 self.src_if.remote_ip6,
1960 self.src_if.sw_if_index)])
1961 self.route6.add_vpp_config()
1963 self.reset_packet_infos()
1964 for i in range(test_packet_count):
1965 info = self.create_packet_info(self.src_if, self.dst_if)
1966 payload = self.info_to_payload(info)
1967 # Ethernet header here is only for size calculation, thus it
1968 # doesn't matter how it's initialized. This is to ensure that
1969 # reassembled packet is not > 9000 bytes, so that it's not dropped
1971 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1972 UDP(sport=1234, dport=5678) /
1974 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1975 self.extend_packet(p, size, self.padding)
1976 info.data = p[IPv6] # use only IPv6 part, without ethernet header
1978 fragments = [x for _, i in six.iteritems(self._packet_infos)
1979 for x in fragment_rfc8200(
1980 i.data, i.index, 400)]
1982 encapped_fragments = \
1983 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1984 IPv6(src=self.tun_ip6, dst=self.src_if.local_ip6) /
1989 fragmented_encapped_fragments = \
1990 [x for p in encapped_fragments for x in (
1993 2 * len(self._packet_infos) + p[IPv6ExtHdrFragment].id,
1995 if IPv6ExtHdrFragment in p else [p]
1999 self.src_if.add_stream(fragmented_encapped_fragments)
2001 self.pg_enable_capture(self.pg_interfaces)
2004 self.src_if.assert_nothing_captured()
2005 packets = self.dst_if.get_capture(len(self._packet_infos))
2006 self.verify_capture(packets, IPv6)
2008 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
2009 # so that it's query_vpp_config() works as it should
2010 self.gre6.remove_vpp_config()
2013 if __name__ == '__main__':
2014 unittest.main(testRunner=VppTestRunner)