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 = b'''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-message.\
233 Ethernet-Payload.IPv4-Packet.IPv4-Header.Fragment-Offset; Test-case: 5737'''
234 malformed_packet = (Ether(dst=self.src_if.local_mac,
235 src=self.src_if.remote_mac) /
237 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
238 IP(id=1000, src=self.src_if.remote_ip4,
239 dst=self.dst_if.remote_ip4) /
240 UDP(sport=1234, dport=5678) /
242 valid_fragments = fragment_rfc791(p, 400)
244 counter = "/err/ip4-full-reassembly-feature/malformed packets"
245 error_counter = self.statistics.get_err_counter(counter)
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.assertEqual(self.statistics.get_err_counter(counter),
255 def test_44924(self):
256 """ compress tiny fragments """
257 packets = [(Ether(dst=self.src_if.local_mac,
258 src=self.src_if.remote_mac) /
259 IP(id=24339, flags="MF", frag=0, ttl=64,
260 src=self.src_if.remote_ip4,
261 dst=self.dst_if.remote_ip4) /
262 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
263 Raw(load='Test-group: IPv4')),
264 (Ether(dst=self.src_if.local_mac,
265 src=self.src_if.remote_mac) /
266 IP(id=24339, flags="MF", frag=3, ttl=64,
267 src=self.src_if.remote_ip4,
268 dst=self.dst_if.remote_ip4) /
269 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
270 Raw(load='.IPv4.Fragmentation.vali')),
271 (Ether(dst=self.src_if.local_mac,
272 src=self.src_if.remote_mac) /
273 IP(id=24339, frag=6, ttl=64,
274 src=self.src_if.remote_ip4,
275 dst=self.dst_if.remote_ip4) /
276 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
277 Raw(load='d; Test-case: 44924'))
280 self.pg_enable_capture()
281 self.src_if.add_stream(packets)
284 self.dst_if.get_capture(1)
286 def test_frag_1(self):
287 """ fragment of size 1 """
288 self.vapi.cli("clear errors")
289 malformed_packets = [(Ether(dst=self.src_if.local_mac,
290 src=self.src_if.remote_mac) /
291 IP(id=7, len=21, flags="MF", frag=0, ttl=64,
292 src=self.src_if.remote_ip4,
293 dst=self.dst_if.remote_ip4) /
294 ICMP(type="echo-request")),
295 (Ether(dst=self.src_if.local_mac,
296 src=self.src_if.remote_mac) /
297 IP(id=7, len=21, frag=1, ttl=64,
298 src=self.src_if.remote_ip4,
299 dst=self.dst_if.remote_ip4) /
303 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
304 IP(id=1000, src=self.src_if.remote_ip4,
305 dst=self.dst_if.remote_ip4) /
306 UDP(sport=1234, dport=5678) /
308 valid_fragments = fragment_rfc791(p, 400)
310 self.pg_enable_capture()
311 self.src_if.add_stream(malformed_packets + valid_fragments)
314 self.dst_if.get_capture(1)
316 self.assert_packet_counter_equal("ip4-full-reassembly-feature", 1)
317 # TODO remove above, uncomment below once clearing of counters
319 # self.assert_packet_counter_equal(
320 # "/err/ip4-full-reassembly-feature/malformed packets", 1)
322 def test_random(self):
323 """ random order reassembly """
325 fragments = list(self.fragments_200)
328 self.pg_enable_capture()
329 self.src_if.add_stream(fragments)
332 packets = self.dst_if.get_capture(len(self.packet_infos))
333 self.verify_capture(packets)
334 self.src_if.assert_nothing_captured()
336 # run it all again to verify correctness
337 self.pg_enable_capture()
338 self.src_if.add_stream(fragments)
341 packets = self.dst_if.get_capture(len(self.packet_infos))
342 self.verify_capture(packets)
343 self.src_if.assert_nothing_captured()
345 def test_duplicates(self):
346 """ duplicate fragments """
349 x for (_, frags, _, _) in self.pkt_infos
351 for _ in range(0, min(2, len(frags)))
354 self.pg_enable_capture()
355 self.src_if.add_stream(fragments)
358 packets = self.dst_if.get_capture(len(self.pkt_infos))
359 self.verify_capture(packets)
360 self.src_if.assert_nothing_captured()
362 def test_overlap1(self):
363 """ overlapping fragments case #1 """
366 for _, _, frags_300, frags_200 in self.pkt_infos:
367 if len(frags_300) == 1:
368 fragments.extend(frags_300)
370 for i, j in zip(frags_200, frags_300):
374 self.pg_enable_capture()
375 self.src_if.add_stream(fragments)
378 packets = self.dst_if.get_capture(len(self.pkt_infos))
379 self.verify_capture(packets)
380 self.src_if.assert_nothing_captured()
382 # run it all to verify correctness
383 self.pg_enable_capture()
384 self.src_if.add_stream(fragments)
387 packets = self.dst_if.get_capture(len(self.pkt_infos))
388 self.verify_capture(packets)
389 self.src_if.assert_nothing_captured()
391 def test_overlap2(self):
392 """ overlapping fragments case #2 """
395 for _, _, frags_300, frags_200 in self.pkt_infos:
396 if len(frags_300) == 1:
397 fragments.extend(frags_300)
399 # care must be taken here so that there are no fragments
400 # received by vpp after reassembly is finished, otherwise
401 # new reassemblies will be started and packet generator will
402 # freak out when it detects unfreed buffers
403 zipped = zip(frags_300, frags_200)
409 self.pg_enable_capture()
410 self.src_if.add_stream(fragments)
413 packets = self.dst_if.get_capture(len(self.pkt_infos))
414 self.verify_capture(packets)
415 self.src_if.assert_nothing_captured()
417 # run it all to verify correctness
418 self.pg_enable_capture()
419 self.src_if.add_stream(fragments)
422 packets = self.dst_if.get_capture(len(self.pkt_infos))
423 self.verify_capture(packets)
424 self.src_if.assert_nothing_captured()
426 def test_timeout_inline(self):
427 """ timeout (inline) """
429 dropped_packet_indexes = set(
430 index for (index, frags, _, _) in self.pkt_infos if len(frags) > 1
433 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
434 max_reassembly_length=3,
435 expire_walk_interval_ms=10000)
437 self.pg_enable_capture()
438 self.src_if.add_stream(self.fragments_400)
441 packets = self.dst_if.get_capture(
442 len(self.pkt_infos) - len(dropped_packet_indexes))
443 self.verify_capture(packets, dropped_packet_indexes)
444 self.src_if.assert_nothing_captured()
446 def test_timeout_cleanup(self):
447 """ timeout (cleanup) """
449 # whole packets + fragmented packets sans last fragment
451 x for (_, frags_400, _, _) in self.pkt_infos
452 for x in frags_400[:-1 if len(frags_400) > 1 else None]
455 # last fragments for fragmented packets
456 fragments2 = [frags_400[-1]
457 for (_, frags_400, _, _) in self.pkt_infos
458 if len(frags_400) > 1]
460 dropped_packet_indexes = set(
461 index for (index, frags_400, _, _) in self.pkt_infos
462 if len(frags_400) > 1)
464 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
465 max_reassembly_length=1000,
466 expire_walk_interval_ms=50)
468 self.pg_enable_capture()
469 self.src_if.add_stream(fragments)
472 self.sleep(.25, "wait before sending rest of fragments")
474 self.src_if.add_stream(fragments2)
477 packets = self.dst_if.get_capture(
478 len(self.pkt_infos) - len(dropped_packet_indexes))
479 self.verify_capture(packets, dropped_packet_indexes)
480 self.src_if.assert_nothing_captured()
482 def test_disabled(self):
483 """ reassembly disabled """
485 dropped_packet_indexes = set(
486 index for (index, frags_400, _, _) in self.pkt_infos
487 if len(frags_400) > 1)
489 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
490 max_reassembly_length=3,
491 expire_walk_interval_ms=10000)
493 self.pg_enable_capture()
494 self.src_if.add_stream(self.fragments_400)
497 packets = self.dst_if.get_capture(
498 len(self.pkt_infos) - len(dropped_packet_indexes))
499 self.verify_capture(packets, dropped_packet_indexes)
500 self.src_if.assert_nothing_captured()
503 class TestIPv4SVReassembly(VppTestCase):
504 """ IPv4 Shallow Virtual Reassembly """
508 super(TestIPv4SVReassembly, cls).setUpClass()
510 cls.create_pg_interfaces([0, 1])
514 # setup all interfaces
515 for i in cls.pg_interfaces:
521 """ Test setup - force timeout on existing reassemblies """
522 super(TestIPv4SVReassembly, self).setUp()
523 self.vapi.ip_reassembly_enable_disable(
524 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
525 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
526 self.vapi.ip_reassembly_set(
527 timeout_ms=0, max_reassemblies=1000,
528 max_reassembly_length=1000,
529 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
530 expire_walk_interval_ms=10)
532 self.vapi.ip_reassembly_set(
533 timeout_ms=1000000, max_reassemblies=1000,
534 max_reassembly_length=1000,
535 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
536 expire_walk_interval_ms=10000)
539 super(TestIPv4SVReassembly, self).tearDown()
540 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
541 self.logger.debug(self.vapi.ppcli("show buffers"))
543 def test_basic(self):
544 """ basic reassembly """
548 while len(payload) < payload_len:
549 payload += "%u " % counter
552 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
553 IP(id=1, src=self.src_if.remote_ip4,
554 dst=self.dst_if.remote_ip4) /
555 UDP(sport=1234, dport=5678) /
557 fragments = fragment_rfc791(p, payload_len/4)
559 # send fragment #2 - should be cached inside reassembly
560 self.pg_enable_capture()
561 self.src_if.add_stream(fragments[1])
563 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
564 self.logger.debug(self.vapi.ppcli("show buffers"))
565 self.logger.debug(self.vapi.ppcli("show trace"))
566 self.dst_if.assert_nothing_captured()
568 # send fragment #1 - reassembly is finished now and both fragments
570 self.pg_enable_capture()
571 self.src_if.add_stream(fragments[0])
573 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
574 self.logger.debug(self.vapi.ppcli("show buffers"))
575 self.logger.debug(self.vapi.ppcli("show trace"))
576 c = self.dst_if.get_capture(2)
577 for sent, recvd in zip([fragments[1], fragments[0]], c):
578 self.assertEqual(sent[IP].src, recvd[IP].src)
579 self.assertEqual(sent[IP].dst, recvd[IP].dst)
580 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
582 # send rest of fragments - should be immediately forwarded
583 self.pg_enable_capture()
584 self.src_if.add_stream(fragments[2:])
586 c = self.dst_if.get_capture(len(fragments[2:]))
587 for sent, recvd in zip(fragments[2:], c):
588 self.assertEqual(sent[IP].src, recvd[IP].src)
589 self.assertEqual(sent[IP].dst, recvd[IP].dst)
590 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
592 def test_timeout(self):
593 """ reassembly timeout """
597 while len(payload) < payload_len:
598 payload += "%u " % counter
601 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
602 IP(id=1, src=self.src_if.remote_ip4,
603 dst=self.dst_if.remote_ip4) /
604 UDP(sport=1234, dport=5678) /
606 fragments = fragment_rfc791(p, payload_len/4)
608 self.vapi.ip_reassembly_set(
609 timeout_ms=100, max_reassemblies=1000,
610 max_reassembly_length=1000,
611 expire_walk_interval_ms=50,
612 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
614 # send fragments #2 and #1 - should be forwarded
615 self.pg_enable_capture()
616 self.src_if.add_stream(fragments[0:2])
618 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
619 self.logger.debug(self.vapi.ppcli("show buffers"))
620 self.logger.debug(self.vapi.ppcli("show trace"))
621 c = self.dst_if.get_capture(2)
622 for sent, recvd in zip([fragments[1], fragments[0]], c):
623 self.assertEqual(sent[IP].src, recvd[IP].src)
624 self.assertEqual(sent[IP].dst, recvd[IP].dst)
625 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
628 self.sleep(.25, "wait before sending rest of fragments")
630 # send rest of fragments - shouldn't be forwarded
631 self.pg_enable_capture()
632 self.src_if.add_stream(fragments[2:])
634 self.dst_if.assert_nothing_captured()
637 """ reassembly reuses LRU element """
639 self.vapi.ip_reassembly_set(
640 timeout_ms=1000000, max_reassemblies=1,
641 max_reassembly_length=1000,
642 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
643 expire_walk_interval_ms=10000)
648 while len(payload) < payload_len:
649 payload += "%u " % counter
655 for i in range(packet_count)
656 for p in (Ether(dst=self.src_if.local_mac,
657 src=self.src_if.remote_mac) /
658 IP(id=i, src=self.src_if.remote_ip4,
659 dst=self.dst_if.remote_ip4) /
660 UDP(sport=1234, dport=5678) /
662 for f in fragment_rfc791(p, payload_len/4)]
664 self.pg_enable_capture()
665 self.src_if.add_stream(fragments)
667 c = self.dst_if.get_capture(len(fragments))
668 for sent, recvd in zip(fragments, c):
669 self.assertEqual(sent[IP].src, recvd[IP].src)
670 self.assertEqual(sent[IP].dst, recvd[IP].dst)
671 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
674 class TestIPv4MWReassembly(VppTestCase):
675 """ IPv4 Reassembly (multiple workers) """
676 worker_config = "workers %d" % worker_count
680 super(TestIPv4MWReassembly, cls).setUpClass()
682 cls.create_pg_interfaces(range(worker_count+1))
684 cls.send_ifs = cls.pg_interfaces[:-1]
685 cls.dst_if = cls.pg_interfaces[-1]
687 # setup all interfaces
688 for i in cls.pg_interfaces:
693 # packets sizes reduced here because we are generating packets without
694 # Ethernet headers, which are added later (diff fragments go via
695 # different interfaces)
696 cls.packet_sizes = [64-len(Ether()), 512-len(Ether()),
697 1518-len(Ether()), 9018-len(Ether())]
698 cls.padding = " abcdefghijklmn"
699 cls.create_stream(cls.packet_sizes)
700 cls.create_fragments()
703 def tearDownClass(cls):
704 super(TestIPv4MWReassembly, cls).tearDownClass()
707 """ Test setup - force timeout on existing reassemblies """
708 super(TestIPv4MWReassembly, self).setUp()
709 for intf in self.send_ifs:
710 self.vapi.ip_reassembly_enable_disable(
711 sw_if_index=intf.sw_if_index, enable_ip4=True)
712 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
713 max_reassembly_length=1000,
714 expire_walk_interval_ms=10)
716 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
717 max_reassembly_length=1000,
718 expire_walk_interval_ms=10000)
721 super(TestIPv4MWReassembly, self).tearDown()
723 def show_commands_at_teardown(self):
724 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
725 self.logger.debug(self.vapi.ppcli("show buffers"))
728 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
729 """Create input packet stream
731 :param list packet_sizes: Required packet sizes.
733 for i in range(0, packet_count):
734 info = cls.create_packet_info(cls.src_if, cls.src_if)
735 payload = cls.info_to_payload(info)
736 p = (IP(id=info.index, src=cls.src_if.remote_ip4,
737 dst=cls.dst_if.remote_ip4) /
738 UDP(sport=1234, dport=5678) /
740 size = packet_sizes[(i // 2) % len(packet_sizes)]
741 cls.extend_packet(p, size, cls.padding)
745 def create_fragments(cls):
746 infos = cls._packet_infos
748 for index, info in six.iteritems(infos):
750 # cls.logger.debug(ppp("Packet:",
751 # p.__class__(scapy.compat.raw(p))))
752 fragments_400 = fragment_rfc791(p, 400)
753 cls.pkt_infos.append((index, fragments_400))
754 cls.fragments_400 = [
755 x for (_, frags) in cls.pkt_infos for x in frags]
756 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, " %
757 (len(infos), len(cls.fragments_400)))
759 def verify_capture(self, capture, dropped_packet_indexes=[]):
760 """Verify captured packet stream.
762 :param list capture: Captured packet stream.
766 for packet in capture:
768 self.logger.debug(ppp("Got packet:", packet))
771 payload_info = self.payload_to_info(packet[Raw])
772 packet_index = payload_info.index
774 packet_index not in dropped_packet_indexes,
775 ppp("Packet received, but should be dropped:", packet))
776 if packet_index in seen:
777 raise Exception(ppp("Duplicate packet received", packet))
778 seen.add(packet_index)
779 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
780 info = self._packet_infos[packet_index]
781 self.assertTrue(info is not None)
782 self.assertEqual(packet_index, info.index)
783 saved_packet = info.data
784 self.assertEqual(ip.src, saved_packet[IP].src)
785 self.assertEqual(ip.dst, saved_packet[IP].dst)
786 self.assertEqual(udp.payload, saved_packet[UDP].payload)
788 self.logger.error(ppp("Unexpected or invalid packet:", packet))
790 for index in self._packet_infos:
791 self.assertTrue(index in seen or index in dropped_packet_indexes,
792 "Packet with packet_index %d not received" % index)
794 def send_packets(self, packets):
795 for counter in range(worker_count):
796 if 0 == len(packets[counter]):
798 send_if = self.send_ifs[counter]
800 (Ether(dst=send_if.local_mac, src=send_if.remote_mac) / x
801 for x in packets[counter]),
805 def test_worker_conflict(self):
806 """ 1st and FO=0 fragments on different workers """
808 # in first wave we send fragments which don't start at offset 0
809 # then we send fragments with offset 0 on a different thread
810 # then the rest of packets on a random thread
811 first_packets = [[] for n in range(worker_count)]
812 second_packets = [[] for n in range(worker_count)]
813 rest_of_packets = [[] for n in range(worker_count)]
814 for (_, p) in self.pkt_infos:
815 wi = randrange(worker_count)
816 second_packets[wi].append(p[0])
821 wi2 = randrange(worker_count)
822 first_packets[wi2].append(p[1])
823 wi3 = randrange(worker_count)
824 rest_of_packets[wi3].extend(p[2:])
826 self.pg_enable_capture()
827 self.send_packets(first_packets)
828 self.send_packets(second_packets)
829 self.send_packets(rest_of_packets)
831 packets = self.dst_if.get_capture(len(self.pkt_infos))
832 self.verify_capture(packets)
833 for send_if in self.send_ifs:
834 send_if.assert_nothing_captured()
836 self.logger.debug(self.vapi.ppcli("show trace"))
837 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
838 self.logger.debug(self.vapi.ppcli("show buffers"))
839 self.vapi.cli("clear trace")
841 self.pg_enable_capture()
842 self.send_packets(first_packets)
843 self.send_packets(second_packets)
844 self.send_packets(rest_of_packets)
846 packets = self.dst_if.get_capture(len(self.pkt_infos))
847 self.verify_capture(packets)
848 for send_if in self.send_ifs:
849 send_if.assert_nothing_captured()
852 class TestIPv6Reassembly(VppTestCase):
853 """ IPv6 Reassembly """
857 super(TestIPv6Reassembly, cls).setUpClass()
859 cls.create_pg_interfaces([0, 1])
863 # setup all interfaces
864 for i in cls.pg_interfaces:
870 cls.packet_sizes = [64, 512, 1518, 9018]
871 cls.padding = " abcdefghijklmn"
872 cls.create_stream(cls.packet_sizes)
873 cls.create_fragments()
876 def tearDownClass(cls):
877 super(TestIPv6Reassembly, cls).tearDownClass()
880 """ Test setup - force timeout on existing reassemblies """
881 super(TestIPv6Reassembly, self).setUp()
882 self.vapi.ip_reassembly_enable_disable(
883 sw_if_index=self.src_if.sw_if_index, enable_ip6=True)
884 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
885 max_reassembly_length=1000,
886 expire_walk_interval_ms=10, is_ip6=1)
888 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
889 max_reassembly_length=1000,
890 expire_walk_interval_ms=10000, is_ip6=1)
891 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
892 self.logger.debug(self.vapi.ppcli("show buffers"))
895 super(TestIPv6Reassembly, self).tearDown()
897 def show_commands_at_teardown(self):
898 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
899 self.logger.debug(self.vapi.ppcli("show buffers"))
902 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
903 """Create input packet stream for defined interface.
905 :param list packet_sizes: Required packet sizes.
907 for i in range(0, packet_count):
908 info = cls.create_packet_info(cls.src_if, cls.src_if)
909 payload = cls.info_to_payload(info)
910 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
911 IPv6(src=cls.src_if.remote_ip6,
912 dst=cls.dst_if.remote_ip6) /
913 UDP(sport=1234, dport=5678) /
915 size = packet_sizes[(i // 2) % len(packet_sizes)]
916 cls.extend_packet(p, size, cls.padding)
920 def create_fragments(cls):
921 infos = cls._packet_infos
923 for index, info in six.iteritems(infos):
925 # cls.logger.debug(ppp("Packet:",
926 # p.__class__(scapy.compat.raw(p))))
927 fragments_400 = fragment_rfc8200(p, info.index, 400)
928 fragments_300 = fragment_rfc8200(p, info.index, 300)
929 cls.pkt_infos.append((index, fragments_400, fragments_300))
930 cls.fragments_400 = [
931 x for _, frags, _ in cls.pkt_infos for x in frags]
932 cls.fragments_300 = [
933 x for _, _, frags in cls.pkt_infos for x in frags]
934 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
935 "and %s 300-byte fragments" %
936 (len(infos), len(cls.fragments_400),
937 len(cls.fragments_300)))
939 def verify_capture(self, capture, dropped_packet_indexes=[]):
940 """Verify captured packet strea .
942 :param list capture: Captured packet stream.
946 for packet in capture:
948 self.logger.debug(ppp("Got packet:", packet))
951 payload_info = self.payload_to_info(packet[Raw])
952 packet_index = payload_info.index
954 packet_index not in dropped_packet_indexes,
955 ppp("Packet received, but should be dropped:", packet))
956 if packet_index in seen:
957 raise Exception(ppp("Duplicate packet received", packet))
958 seen.add(packet_index)
959 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
960 info = self._packet_infos[packet_index]
961 self.assertTrue(info is not None)
962 self.assertEqual(packet_index, info.index)
963 saved_packet = info.data
964 self.assertEqual(ip.src, saved_packet[IPv6].src)
965 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
966 self.assertEqual(udp.payload, saved_packet[UDP].payload)
968 self.logger.error(ppp("Unexpected or invalid packet:", packet))
970 for index in self._packet_infos:
971 self.assertTrue(index in seen or index in dropped_packet_indexes,
972 "Packet with packet_index %d not received" % index)
974 def test_reassembly(self):
975 """ basic reassembly """
977 self.pg_enable_capture()
978 self.src_if.add_stream(self.fragments_400)
981 packets = self.dst_if.get_capture(len(self.pkt_infos))
982 self.verify_capture(packets)
983 self.src_if.assert_nothing_captured()
985 # run it all again to verify correctness
986 self.pg_enable_capture()
987 self.src_if.add_stream(self.fragments_400)
990 packets = self.dst_if.get_capture(len(self.pkt_infos))
991 self.verify_capture(packets)
992 self.src_if.assert_nothing_captured()
994 def test_buffer_boundary(self):
995 """ fragment header crossing buffer boundary """
997 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
998 IPv6(src=self.src_if.remote_ip6,
999 dst=self.src_if.local_ip6) /
1001 options=[HBHOptUnknown(otype=0xff, optlen=0)] * 1000) /
1002 IPv6ExtHdrFragment(m=1) /
1003 UDP(sport=1234, dport=5678) /
1005 self.pg_enable_capture()
1006 self.src_if.add_stream([p])
1008 self.src_if.assert_nothing_captured()
1009 self.dst_if.assert_nothing_captured()
1011 def test_reversed(self):
1012 """ reverse order reassembly """
1014 fragments = list(self.fragments_400)
1017 self.pg_enable_capture()
1018 self.src_if.add_stream(fragments)
1021 packets = self.dst_if.get_capture(len(self.pkt_infos))
1022 self.verify_capture(packets)
1023 self.src_if.assert_nothing_captured()
1025 # run it all again to verify correctness
1026 self.pg_enable_capture()
1027 self.src_if.add_stream(fragments)
1030 packets = self.dst_if.get_capture(len(self.pkt_infos))
1031 self.verify_capture(packets)
1032 self.src_if.assert_nothing_captured()
1034 def test_random(self):
1035 """ random order reassembly """
1037 fragments = list(self.fragments_400)
1040 self.pg_enable_capture()
1041 self.src_if.add_stream(fragments)
1044 packets = self.dst_if.get_capture(len(self.pkt_infos))
1045 self.verify_capture(packets)
1046 self.src_if.assert_nothing_captured()
1048 # run it all again to verify correctness
1049 self.pg_enable_capture()
1050 self.src_if.add_stream(fragments)
1053 packets = self.dst_if.get_capture(len(self.pkt_infos))
1054 self.verify_capture(packets)
1055 self.src_if.assert_nothing_captured()
1057 def test_duplicates(self):
1058 """ duplicate fragments """
1061 x for (_, frags, _) in self.pkt_infos
1063 for _ in range(0, min(2, len(frags)))
1066 self.pg_enable_capture()
1067 self.src_if.add_stream(fragments)
1070 packets = self.dst_if.get_capture(len(self.pkt_infos))
1071 self.verify_capture(packets)
1072 self.src_if.assert_nothing_captured()
1074 def test_long_fragment_chain(self):
1075 """ long fragment chain """
1078 "/err/ip6-full-reassembly-feature/fragment chain too long (drop)"
1080 error_cnt = self.statistics.get_err_counter(error_cnt_str)
1082 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
1083 max_reassembly_length=3,
1084 expire_walk_interval_ms=50, is_ip6=1)
1086 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1087 IPv6(src=self.src_if.remote_ip6,
1088 dst=self.dst_if.remote_ip6) /
1089 UDP(sport=1234, dport=5678) /
1091 frags = fragment_rfc8200(p, 1, 300) + fragment_rfc8200(p, 2, 500)
1093 self.pg_enable_capture()
1094 self.src_if.add_stream(frags)
1097 self.dst_if.get_capture(1)
1098 self.assert_error_counter_equal(error_cnt_str, error_cnt + 1)
1100 def test_overlap1(self):
1101 """ overlapping fragments case #1 """
1104 for _, frags_400, frags_300 in self.pkt_infos:
1105 if len(frags_300) == 1:
1106 fragments.extend(frags_400)
1108 for i, j in zip(frags_300, frags_400):
1112 dropped_packet_indexes = set(
1113 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
1116 self.pg_enable_capture()
1117 self.src_if.add_stream(fragments)
1120 packets = self.dst_if.get_capture(
1121 len(self.pkt_infos) - len(dropped_packet_indexes))
1122 self.verify_capture(packets, dropped_packet_indexes)
1123 self.src_if.assert_nothing_captured()
1125 def test_overlap2(self):
1126 """ overlapping fragments case #2 """
1129 for _, frags_400, frags_300 in self.pkt_infos:
1130 if len(frags_400) == 1:
1131 fragments.extend(frags_400)
1133 # care must be taken here so that there are no fragments
1134 # received by vpp after reassembly is finished, otherwise
1135 # new reassemblies will be started and packet generator will
1136 # freak out when it detects unfreed buffers
1137 zipped = zip(frags_400, frags_300)
1143 dropped_packet_indexes = set(
1144 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
1147 self.pg_enable_capture()
1148 self.src_if.add_stream(fragments)
1151 packets = self.dst_if.get_capture(
1152 len(self.pkt_infos) - len(dropped_packet_indexes))
1153 self.verify_capture(packets, dropped_packet_indexes)
1154 self.src_if.assert_nothing_captured()
1156 def test_timeout_inline(self):
1157 """ timeout (inline) """
1159 dropped_packet_indexes = set(
1160 index for (index, frags, _) in self.pkt_infos if len(frags) > 1
1163 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1164 max_reassembly_length=3,
1165 expire_walk_interval_ms=10000, is_ip6=1)
1167 self.pg_enable_capture()
1168 self.src_if.add_stream(self.fragments_400)
1171 packets = self.dst_if.get_capture(
1172 len(self.pkt_infos) - len(dropped_packet_indexes))
1173 self.verify_capture(packets, dropped_packet_indexes)
1174 pkts = self.src_if.get_capture(
1175 expected_count=len(dropped_packet_indexes))
1177 self.assertIn(ICMPv6TimeExceeded, icmp)
1178 self.assertIn(IPv6ExtHdrFragment, icmp)
1179 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
1180 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
1182 def test_timeout_cleanup(self):
1183 """ timeout (cleanup) """
1185 # whole packets + fragmented packets sans last fragment
1187 x for (_, frags_400, _) in self.pkt_infos
1188 for x in frags_400[:-1 if len(frags_400) > 1 else None]
1191 # last fragments for fragmented packets
1192 fragments2 = [frags_400[-1]
1193 for (_, frags_400, _) in self.pkt_infos
1194 if len(frags_400) > 1]
1196 dropped_packet_indexes = set(
1197 index for (index, frags_400, _) in self.pkt_infos
1198 if len(frags_400) > 1)
1200 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
1201 max_reassembly_length=1000,
1202 expire_walk_interval_ms=50)
1204 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
1205 max_reassembly_length=1000,
1206 expire_walk_interval_ms=50, is_ip6=1)
1208 self.pg_enable_capture()
1209 self.src_if.add_stream(fragments)
1212 self.sleep(.25, "wait before sending rest of fragments")
1214 self.src_if.add_stream(fragments2)
1217 packets = self.dst_if.get_capture(
1218 len(self.pkt_infos) - len(dropped_packet_indexes))
1219 self.verify_capture(packets, dropped_packet_indexes)
1220 pkts = self.src_if.get_capture(
1221 expected_count=len(dropped_packet_indexes))
1223 self.assertIn(ICMPv6TimeExceeded, icmp)
1224 self.assertIn(IPv6ExtHdrFragment, icmp)
1225 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
1226 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
1228 def test_disabled(self):
1229 """ reassembly disabled """
1231 dropped_packet_indexes = set(
1232 index for (index, frags_400, _) in self.pkt_infos
1233 if len(frags_400) > 1)
1235 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
1236 max_reassembly_length=3,
1237 expire_walk_interval_ms=10000, is_ip6=1)
1239 self.pg_enable_capture()
1240 self.src_if.add_stream(self.fragments_400)
1243 packets = self.dst_if.get_capture(
1244 len(self.pkt_infos) - len(dropped_packet_indexes))
1245 self.verify_capture(packets, dropped_packet_indexes)
1246 self.src_if.assert_nothing_captured()
1248 def test_missing_upper(self):
1249 """ missing upper layer """
1250 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1251 IPv6(src=self.src_if.remote_ip6,
1252 dst=self.src_if.local_ip6) /
1253 UDP(sport=1234, dport=5678) /
1255 self.extend_packet(p, 1000, self.padding)
1256 fragments = fragment_rfc8200(p, 1, 500)
1257 bad_fragment = p.__class__(scapy.compat.raw(fragments[1]))
1258 bad_fragment[IPv6ExtHdrFragment].nh = 59
1259 bad_fragment[IPv6ExtHdrFragment].offset = 0
1260 self.pg_enable_capture()
1261 self.src_if.add_stream([bad_fragment])
1263 pkts = self.src_if.get_capture(expected_count=1)
1265 self.assertIn(ICMPv6ParamProblem, icmp)
1266 self.assert_equal(icmp[ICMPv6ParamProblem].code, 3, "ICMP code")
1268 def test_invalid_frag_size(self):
1269 """ fragment size not a multiple of 8 """
1270 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1271 IPv6(src=self.src_if.remote_ip6,
1272 dst=self.src_if.local_ip6) /
1273 UDP(sport=1234, dport=5678) /
1275 self.extend_packet(p, 1000, self.padding)
1276 fragments = fragment_rfc8200(p, 1, 500)
1277 bad_fragment = fragments[0]
1278 self.extend_packet(bad_fragment, len(bad_fragment) + 5)
1279 self.pg_enable_capture()
1280 self.src_if.add_stream([bad_fragment])
1282 pkts = self.src_if.get_capture(expected_count=1)
1284 self.assertIn(ICMPv6ParamProblem, icmp)
1285 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1287 def test_invalid_packet_size(self):
1288 """ total packet size > 65535 """
1289 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1290 IPv6(src=self.src_if.remote_ip6,
1291 dst=self.src_if.local_ip6) /
1292 UDP(sport=1234, dport=5678) /
1294 self.extend_packet(p, 1000, self.padding)
1295 fragments = fragment_rfc8200(p, 1, 500)
1296 bad_fragment = fragments[1]
1297 bad_fragment[IPv6ExtHdrFragment].offset = 65500
1298 self.pg_enable_capture()
1299 self.src_if.add_stream([bad_fragment])
1301 pkts = self.src_if.get_capture(expected_count=1)
1303 self.assertIn(ICMPv6ParamProblem, icmp)
1304 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1307 class TestIPv6MWReassembly(VppTestCase):
1308 """ IPv6 Reassembly (multiple workers) """
1309 worker_config = "workers %d" % worker_count
1312 def setUpClass(cls):
1313 super(TestIPv6MWReassembly, cls).setUpClass()
1315 cls.create_pg_interfaces(range(worker_count+1))
1316 cls.src_if = cls.pg0
1317 cls.send_ifs = cls.pg_interfaces[:-1]
1318 cls.dst_if = cls.pg_interfaces[-1]
1320 # setup all interfaces
1321 for i in cls.pg_interfaces:
1326 # packets sizes reduced here because we are generating packets without
1327 # Ethernet headers, which are added later (diff fragments go via
1328 # different interfaces)
1329 cls.packet_sizes = [64-len(Ether()), 512-len(Ether()),
1330 1518-len(Ether()), 9018-len(Ether())]
1331 cls.padding = " abcdefghijklmn"
1332 cls.create_stream(cls.packet_sizes)
1333 cls.create_fragments()
1336 def tearDownClass(cls):
1337 super(TestIPv6MWReassembly, cls).tearDownClass()
1340 """ Test setup - force timeout on existing reassemblies """
1341 super(TestIPv6MWReassembly, self).setUp()
1342 for intf in self.send_ifs:
1343 self.vapi.ip_reassembly_enable_disable(
1344 sw_if_index=intf.sw_if_index, enable_ip6=True)
1345 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1346 max_reassembly_length=1000,
1347 expire_walk_interval_ms=10, is_ip6=1)
1349 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1350 max_reassembly_length=1000,
1351 expire_walk_interval_ms=1000, is_ip6=1)
1354 super(TestIPv6MWReassembly, self).tearDown()
1356 def show_commands_at_teardown(self):
1357 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1358 self.logger.debug(self.vapi.ppcli("show buffers"))
1361 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
1362 """Create input packet stream
1364 :param list packet_sizes: Required packet sizes.
1366 for i in range(0, packet_count):
1367 info = cls.create_packet_info(cls.src_if, cls.src_if)
1368 payload = cls.info_to_payload(info)
1369 p = (IPv6(src=cls.src_if.remote_ip6,
1370 dst=cls.dst_if.remote_ip6) /
1371 UDP(sport=1234, dport=5678) /
1373 size = packet_sizes[(i // 2) % len(packet_sizes)]
1374 cls.extend_packet(p, size, cls.padding)
1378 def create_fragments(cls):
1379 infos = cls._packet_infos
1381 for index, info in six.iteritems(infos):
1383 # cls.logger.debug(ppp("Packet:",
1384 # p.__class__(scapy.compat.raw(p))))
1385 fragments_400 = fragment_rfc8200(p, index, 400)
1386 cls.pkt_infos.append((index, fragments_400))
1387 cls.fragments_400 = [
1388 x for (_, frags) in cls.pkt_infos for x in frags]
1389 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, " %
1390 (len(infos), len(cls.fragments_400)))
1392 def verify_capture(self, capture, dropped_packet_indexes=[]):
1393 """Verify captured packet strea .
1395 :param list capture: Captured packet stream.
1399 for packet in capture:
1401 self.logger.debug(ppp("Got packet:", packet))
1404 payload_info = self.payload_to_info(packet[Raw])
1405 packet_index = payload_info.index
1407 packet_index not in dropped_packet_indexes,
1408 ppp("Packet received, but should be dropped:", packet))
1409 if packet_index in seen:
1410 raise Exception(ppp("Duplicate packet received", packet))
1411 seen.add(packet_index)
1412 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
1413 info = self._packet_infos[packet_index]
1414 self.assertTrue(info is not None)
1415 self.assertEqual(packet_index, info.index)
1416 saved_packet = info.data
1417 self.assertEqual(ip.src, saved_packet[IPv6].src)
1418 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
1419 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1421 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1423 for index in self._packet_infos:
1424 self.assertTrue(index in seen or index in dropped_packet_indexes,
1425 "Packet with packet_index %d not received" % index)
1427 def send_packets(self, packets):
1428 for counter in range(worker_count):
1429 if 0 == len(packets[counter]):
1431 send_if = self.send_ifs[counter]
1433 (Ether(dst=send_if.local_mac, src=send_if.remote_mac) / x
1434 for x in packets[counter]),
1438 def test_worker_conflict(self):
1439 """ 1st and FO=0 fragments on different workers """
1441 # in first wave we send fragments which don't start at offset 0
1442 # then we send fragments with offset 0 on a different thread
1443 # then the rest of packets on a random thread
1444 first_packets = [[] for n in range(worker_count)]
1445 second_packets = [[] for n in range(worker_count)]
1446 rest_of_packets = [[] for n in range(worker_count)]
1447 for (_, p) in self.pkt_infos:
1448 wi = randrange(worker_count)
1449 second_packets[wi].append(p[0])
1454 wi2 = randrange(worker_count)
1455 first_packets[wi2].append(p[1])
1456 wi3 = randrange(worker_count)
1457 rest_of_packets[wi3].extend(p[2:])
1459 self.pg_enable_capture()
1460 self.send_packets(first_packets)
1461 self.send_packets(second_packets)
1462 self.send_packets(rest_of_packets)
1464 packets = self.dst_if.get_capture(len(self.pkt_infos))
1465 self.verify_capture(packets)
1466 for send_if in self.send_ifs:
1467 send_if.assert_nothing_captured()
1469 self.logger.debug(self.vapi.ppcli("show trace"))
1470 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1471 self.logger.debug(self.vapi.ppcli("show buffers"))
1472 self.vapi.cli("clear trace")
1474 self.pg_enable_capture()
1475 self.send_packets(first_packets)
1476 self.send_packets(second_packets)
1477 self.send_packets(rest_of_packets)
1479 packets = self.dst_if.get_capture(len(self.pkt_infos))
1480 self.verify_capture(packets)
1481 for send_if in self.send_ifs:
1482 send_if.assert_nothing_captured()
1485 class TestIPv6SVReassembly(VppTestCase):
1486 """ IPv6 Shallow Virtual Reassembly """
1489 def setUpClass(cls):
1490 super(TestIPv6SVReassembly, cls).setUpClass()
1492 cls.create_pg_interfaces([0, 1])
1493 cls.src_if = cls.pg0
1494 cls.dst_if = cls.pg1
1496 # setup all interfaces
1497 for i in cls.pg_interfaces:
1503 """ Test setup - force timeout on existing reassemblies """
1504 super(TestIPv6SVReassembly, self).setUp()
1505 self.vapi.ip_reassembly_enable_disable(
1506 sw_if_index=self.src_if.sw_if_index, enable_ip6=True,
1507 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
1508 self.vapi.ip_reassembly_set(
1509 timeout_ms=0, max_reassemblies=1000,
1510 max_reassembly_length=1000,
1511 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1512 expire_walk_interval_ms=10, is_ip6=1)
1514 self.vapi.ip_reassembly_set(
1515 timeout_ms=1000000, max_reassemblies=1000,
1516 max_reassembly_length=1000,
1517 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1518 expire_walk_interval_ms=10000, is_ip6=1)
1521 super(TestIPv6SVReassembly, self).tearDown()
1522 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1523 self.logger.debug(self.vapi.ppcli("show buffers"))
1525 def test_basic(self):
1526 """ basic reassembly """
1530 while len(payload) < payload_len:
1531 payload += "%u " % counter
1534 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1535 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1536 UDP(sport=1234, dport=5678) /
1538 fragments = fragment_rfc8200(p, 1, payload_len/4)
1540 # send fragment #2 - should be cached inside reassembly
1541 self.pg_enable_capture()
1542 self.src_if.add_stream(fragments[1])
1544 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1545 self.logger.debug(self.vapi.ppcli("show buffers"))
1546 self.logger.debug(self.vapi.ppcli("show trace"))
1547 self.dst_if.assert_nothing_captured()
1549 # send fragment #1 - reassembly is finished now and both fragments
1551 self.pg_enable_capture()
1552 self.src_if.add_stream(fragments[0])
1554 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1555 self.logger.debug(self.vapi.ppcli("show buffers"))
1556 self.logger.debug(self.vapi.ppcli("show trace"))
1557 c = self.dst_if.get_capture(2)
1558 for sent, recvd in zip([fragments[1], fragments[0]], c):
1559 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1560 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1561 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1563 # send rest of fragments - should be immediately forwarded
1564 self.pg_enable_capture()
1565 self.src_if.add_stream(fragments[2:])
1567 c = self.dst_if.get_capture(len(fragments[2:]))
1568 for sent, recvd in zip(fragments[2:], c):
1569 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1570 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1571 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1573 def test_timeout(self):
1574 """ reassembly timeout """
1578 while len(payload) < payload_len:
1579 payload += "%u " % counter
1582 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1583 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1584 UDP(sport=1234, dport=5678) /
1586 fragments = fragment_rfc8200(p, 1, payload_len/4)
1588 self.vapi.ip_reassembly_set(
1589 timeout_ms=100, max_reassemblies=1000,
1590 max_reassembly_length=1000,
1591 expire_walk_interval_ms=50,
1593 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
1595 # send fragments #2 and #1 - should be forwarded
1596 self.pg_enable_capture()
1597 self.src_if.add_stream(fragments[0:2])
1599 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
1600 self.logger.debug(self.vapi.ppcli("show buffers"))
1601 self.logger.debug(self.vapi.ppcli("show trace"))
1602 c = self.dst_if.get_capture(2)
1603 for sent, recvd in zip([fragments[1], fragments[0]], c):
1604 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1605 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1606 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1609 self.sleep(.25, "wait before sending rest of fragments")
1611 # send rest of fragments - shouldn't be forwarded
1612 self.pg_enable_capture()
1613 self.src_if.add_stream(fragments[2:])
1615 self.dst_if.assert_nothing_captured()
1618 """ reassembly reuses LRU element """
1620 self.vapi.ip_reassembly_set(
1621 timeout_ms=1000000, max_reassemblies=1,
1622 max_reassembly_length=1000,
1623 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1624 is_ip6=1, expire_walk_interval_ms=10000)
1629 while len(payload) < payload_len:
1630 payload += "%u " % counter
1636 for i in range(packet_count)
1637 for p in (Ether(dst=self.src_if.local_mac,
1638 src=self.src_if.remote_mac) /
1639 IPv6(src=self.src_if.remote_ip6,
1640 dst=self.dst_if.remote_ip6) /
1641 UDP(sport=1234, dport=5678) /
1643 for f in fragment_rfc8200(p, i, payload_len/4)]
1645 self.pg_enable_capture()
1646 self.src_if.add_stream(fragments)
1648 c = self.dst_if.get_capture(len(fragments))
1649 for sent, recvd in zip(fragments, c):
1650 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1651 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1652 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1655 class TestIPv4ReassemblyLocalNode(VppTestCase):
1656 """ IPv4 Reassembly for packets coming to ip4-local node """
1659 def setUpClass(cls):
1660 super(TestIPv4ReassemblyLocalNode, cls).setUpClass()
1662 cls.create_pg_interfaces([0])
1663 cls.src_dst_if = cls.pg0
1665 # setup all interfaces
1666 for i in cls.pg_interfaces:
1671 cls.padding = " abcdefghijklmn"
1673 cls.create_fragments()
1676 def tearDownClass(cls):
1677 super(TestIPv4ReassemblyLocalNode, cls).tearDownClass()
1680 """ Test setup - force timeout on existing reassemblies """
1681 super(TestIPv4ReassemblyLocalNode, self).setUp()
1682 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1683 max_reassembly_length=1000,
1684 expire_walk_interval_ms=10)
1686 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1687 max_reassembly_length=1000,
1688 expire_walk_interval_ms=10000)
1691 super(TestIPv4ReassemblyLocalNode, self).tearDown()
1693 def show_commands_at_teardown(self):
1694 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
1695 self.logger.debug(self.vapi.ppcli("show buffers"))
1698 def create_stream(cls, packet_count=test_packet_count):
1699 """Create input packet stream for defined interface.
1701 :param list packet_sizes: Required packet sizes.
1703 for i in range(0, packet_count):
1704 info = cls.create_packet_info(cls.src_dst_if, cls.src_dst_if)
1705 payload = cls.info_to_payload(info)
1706 p = (Ether(dst=cls.src_dst_if.local_mac,
1707 src=cls.src_dst_if.remote_mac) /
1708 IP(id=info.index, src=cls.src_dst_if.remote_ip4,
1709 dst=cls.src_dst_if.local_ip4) /
1710 ICMP(type='echo-request', id=1234) /
1712 cls.extend_packet(p, 1518, cls.padding)
1716 def create_fragments(cls):
1717 infos = cls._packet_infos
1719 for index, info in six.iteritems(infos):
1721 # cls.logger.debug(ppp("Packet:",
1722 # p.__class__(scapy.compat.raw(p))))
1723 fragments_300 = fragment_rfc791(p, 300)
1724 cls.pkt_infos.append((index, fragments_300))
1725 cls.fragments_300 = [x for (_, frags) in cls.pkt_infos for x in frags]
1726 cls.logger.debug("Fragmented %s packets into %s 300-byte fragments" %
1727 (len(infos), len(cls.fragments_300)))
1729 def verify_capture(self, capture):
1730 """Verify captured packet stream.
1732 :param list capture: Captured packet stream.
1736 for packet in capture:
1738 self.logger.debug(ppp("Got packet:", packet))
1741 payload_info = self.payload_to_info(packet[Raw])
1742 packet_index = payload_info.index
1743 if packet_index in seen:
1744 raise Exception(ppp("Duplicate packet received", packet))
1745 seen.add(packet_index)
1746 self.assertEqual(payload_info.dst, self.src_dst_if.sw_if_index)
1747 info = self._packet_infos[packet_index]
1748 self.assertIsNotNone(info)
1749 self.assertEqual(packet_index, info.index)
1750 saved_packet = info.data
1751 self.assertEqual(ip.src, saved_packet[IP].dst)
1752 self.assertEqual(ip.dst, saved_packet[IP].src)
1753 self.assertEqual(icmp.type, 0) # echo reply
1754 self.assertEqual(icmp.id, saved_packet[ICMP].id)
1755 self.assertEqual(icmp.payload, saved_packet[ICMP].payload)
1757 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1759 for index in self._packet_infos:
1760 self.assertIn(index, seen,
1761 "Packet with packet_index %d not received" % index)
1763 def test_reassembly(self):
1764 """ basic reassembly """
1766 self.pg_enable_capture()
1767 self.src_dst_if.add_stream(self.fragments_300)
1770 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
1771 self.verify_capture(packets)
1773 # run it all again to verify correctness
1774 self.pg_enable_capture()
1775 self.src_dst_if.add_stream(self.fragments_300)
1778 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
1779 self.verify_capture(packets)
1782 class TestFIFReassembly(VppTestCase):
1783 """ Fragments in fragments reassembly """
1786 def setUpClass(cls):
1787 super(TestFIFReassembly, cls).setUpClass()
1789 cls.create_pg_interfaces([0, 1])
1790 cls.src_if = cls.pg0
1791 cls.dst_if = cls.pg1
1792 for i in cls.pg_interfaces:
1799 cls.packet_sizes = [64, 512, 1518, 9018]
1800 cls.padding = " abcdefghijklmn"
1803 def tearDownClass(cls):
1804 super(TestFIFReassembly, cls).tearDownClass()
1807 """ Test setup - force timeout on existing reassemblies """
1808 super(TestFIFReassembly, self).setUp()
1809 self.vapi.ip_reassembly_enable_disable(
1810 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
1812 self.vapi.ip_reassembly_enable_disable(
1813 sw_if_index=self.dst_if.sw_if_index, enable_ip4=True,
1815 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1816 max_reassembly_length=1000,
1817 expire_walk_interval_ms=10)
1818 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1819 max_reassembly_length=1000,
1820 expire_walk_interval_ms=10, is_ip6=1)
1822 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1823 max_reassembly_length=1000,
1824 expire_walk_interval_ms=10000)
1825 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1826 max_reassembly_length=1000,
1827 expire_walk_interval_ms=10000, is_ip6=1)
1830 super(TestFIFReassembly, self).tearDown()
1832 def show_commands_at_teardown(self):
1833 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
1834 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1835 self.logger.debug(self.vapi.ppcli("show buffers"))
1837 def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):
1838 """Verify captured packet stream.
1840 :param list capture: Captured packet stream.
1844 for packet in capture:
1846 self.logger.debug(ppp("Got packet:", packet))
1847 ip = packet[ip_class]
1849 payload_info = self.payload_to_info(packet[Raw])
1850 packet_index = payload_info.index
1852 packet_index not in dropped_packet_indexes,
1853 ppp("Packet received, but should be dropped:", packet))
1854 if packet_index in seen:
1855 raise Exception(ppp("Duplicate packet received", packet))
1856 seen.add(packet_index)
1857 self.assertEqual(payload_info.dst, self.dst_if.sw_if_index)
1858 info = self._packet_infos[packet_index]
1859 self.assertTrue(info is not None)
1860 self.assertEqual(packet_index, info.index)
1861 saved_packet = info.data
1862 self.assertEqual(ip.src, saved_packet[ip_class].src)
1863 self.assertEqual(ip.dst, saved_packet[ip_class].dst)
1864 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1866 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1868 for index in self._packet_infos:
1869 self.assertTrue(index in seen or index in dropped_packet_indexes,
1870 "Packet with packet_index %d not received" % index)
1872 def test_fif4(self):
1873 """ Fragments in fragments (4o4) """
1875 # TODO this should be ideally in setUpClass, but then we hit a bug
1876 # with VppIpRoute incorrectly reporting it's present when it's not
1877 # so we need to manually remove the vpp config, thus we cannot have
1878 # it shared for multiple test cases
1879 self.tun_ip4 = "1.1.1.2"
1881 self.gre4 = VppGreInterface(self, self.src_if.local_ip4, self.tun_ip4)
1882 self.gre4.add_vpp_config()
1883 self.gre4.admin_up()
1884 self.gre4.config_ip4()
1886 self.vapi.ip_reassembly_enable_disable(
1887 sw_if_index=self.gre4.sw_if_index, enable_ip4=True)
1889 self.route4 = VppIpRoute(self, self.tun_ip4, 32,
1890 [VppRoutePath(self.src_if.remote_ip4,
1891 self.src_if.sw_if_index)])
1892 self.route4.add_vpp_config()
1894 self.reset_packet_infos()
1895 for i in range(test_packet_count):
1896 info = self.create_packet_info(self.src_if, self.dst_if)
1897 payload = self.info_to_payload(info)
1898 # Ethernet header here is only for size calculation, thus it
1899 # doesn't matter how it's initialized. This is to ensure that
1900 # reassembled packet is not > 9000 bytes, so that it's not dropped
1902 IP(id=i, src=self.src_if.remote_ip4,
1903 dst=self.dst_if.remote_ip4) /
1904 UDP(sport=1234, dport=5678) /
1906 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1907 self.extend_packet(p, size, self.padding)
1908 info.data = p[IP] # use only IP part, without ethernet header
1910 fragments = [x for _, p in six.iteritems(self._packet_infos)
1911 for x in fragment_rfc791(p.data, 400)]
1913 encapped_fragments = \
1914 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1915 IP(src=self.tun_ip4, dst=self.src_if.local_ip4) /
1920 fragmented_encapped_fragments = \
1921 [x for p in encapped_fragments
1922 for x in fragment_rfc791(p, 200)]
1924 self.src_if.add_stream(fragmented_encapped_fragments)
1926 self.pg_enable_capture(self.pg_interfaces)
1929 self.src_if.assert_nothing_captured()
1930 packets = self.dst_if.get_capture(len(self._packet_infos))
1931 self.verify_capture(packets, IP)
1933 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
1934 # so that it's query_vpp_config() works as it should
1935 self.gre4.remove_vpp_config()
1936 self.logger.debug(self.vapi.ppcli("show interface"))
1938 def test_fif6(self):
1939 """ Fragments in fragments (6o6) """
1940 # TODO this should be ideally in setUpClass, but then we hit a bug
1941 # with VppIpRoute incorrectly reporting it's present when it's not
1942 # so we need to manually remove the vpp config, thus we cannot have
1943 # it shared for multiple test cases
1944 self.tun_ip6 = "1002::1"
1946 self.gre6 = VppGreInterface(self, self.src_if.local_ip6, self.tun_ip6)
1947 self.gre6.add_vpp_config()
1948 self.gre6.admin_up()
1949 self.gre6.config_ip6()
1951 self.vapi.ip_reassembly_enable_disable(
1952 sw_if_index=self.gre6.sw_if_index, enable_ip6=True)
1954 self.route6 = VppIpRoute(self, self.tun_ip6, 128,
1956 self.src_if.remote_ip6,
1957 self.src_if.sw_if_index)])
1958 self.route6.add_vpp_config()
1960 self.reset_packet_infos()
1961 for i in range(test_packet_count):
1962 info = self.create_packet_info(self.src_if, self.dst_if)
1963 payload = self.info_to_payload(info)
1964 # Ethernet header here is only for size calculation, thus it
1965 # doesn't matter how it's initialized. This is to ensure that
1966 # reassembled packet is not > 9000 bytes, so that it's not dropped
1968 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1969 UDP(sport=1234, dport=5678) /
1971 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1972 self.extend_packet(p, size, self.padding)
1973 info.data = p[IPv6] # use only IPv6 part, without ethernet header
1975 fragments = [x for _, i in six.iteritems(self._packet_infos)
1976 for x in fragment_rfc8200(
1977 i.data, i.index, 400)]
1979 encapped_fragments = \
1980 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1981 IPv6(src=self.tun_ip6, dst=self.src_if.local_ip6) /
1986 fragmented_encapped_fragments = \
1987 [x for p in encapped_fragments for x in (
1990 2 * len(self._packet_infos) + p[IPv6ExtHdrFragment].id,
1992 if IPv6ExtHdrFragment in p else [p]
1996 self.src_if.add_stream(fragmented_encapped_fragments)
1998 self.pg_enable_capture(self.pg_interfaces)
2001 self.src_if.assert_nothing_captured()
2002 packets = self.dst_if.get_capture(len(self._packet_infos))
2003 self.verify_capture(packets, IPv6)
2005 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
2006 # so that it's query_vpp_config() works as it should
2007 self.gre6.remove_vpp_config()
2010 if __name__ == '__main__':
2011 unittest.main(testRunner=VppTestRunner)