4 from random import shuffle, choice, randrange
6 from framework import VppTestCase, VppTestRunner
9 from scapy.packet import Raw
10 from scapy.layers.l2 import Ether, GRE
11 from scapy.layers.inet import IP, UDP, ICMP
12 from scapy.layers.inet6 import HBHOptUnknown, ICMPv6ParamProblem,\
13 ICMPv6TimeExceeded, IPv6, IPv6ExtHdrFragment,\
14 IPv6ExtHdrHopByHop, IPv6ExtHdrDestOpt, PadN, ICMPv6EchoRequest
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
26 class TestIPv4Reassembly(VppTestCase):
27 """ IPv4 Reassembly """
31 super(TestIPv4Reassembly, cls).setUpClass()
33 cls.create_pg_interfaces([0, 1])
37 # setup all interfaces
38 for i in cls.pg_interfaces:
44 cls.packet_sizes = [64, 512, 1518, 9018]
45 cls.padding = " abcdefghijklmn"
46 cls.create_stream(cls.packet_sizes)
47 cls.create_fragments()
50 def tearDownClass(cls):
51 super(TestIPv4Reassembly, cls).tearDownClass()
54 """ Test setup - force timeout on existing reassemblies """
55 super(TestIPv4Reassembly, self).setUp()
56 self.vapi.ip_reassembly_enable_disable(
57 sw_if_index=self.src_if.sw_if_index, enable_ip4=True)
58 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
59 max_reassembly_length=1000,
60 expire_walk_interval_ms=10)
61 self.virtual_sleep(.25)
62 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
63 max_reassembly_length=1000,
64 expire_walk_interval_ms=10000)
67 super(TestIPv4Reassembly, self).tearDown()
69 def show_commands_at_teardown(self):
70 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
71 self.logger.debug(self.vapi.ppcli("show buffers"))
74 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
75 """Create input packet stream
77 :param list packet_sizes: Required packet sizes.
79 for i in range(0, packet_count):
80 info = cls.create_packet_info(cls.src_if, cls.src_if)
81 payload = cls.info_to_payload(info)
82 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
83 IP(id=info.index, src=cls.src_if.remote_ip4,
84 dst=cls.dst_if.remote_ip4) /
85 UDP(sport=1234, dport=5678) /
87 size = packet_sizes[(i // 2) % len(packet_sizes)]
88 cls.extend_packet(p, size, cls.padding)
92 def create_fragments(cls):
93 infos = cls._packet_infos
95 for index, info in infos.items():
97 # cls.logger.debug(ppp("Packet:",
98 # p.__class__(scapy.compat.raw(p))))
99 fragments_400 = fragment_rfc791(p, 400)
100 fragments_300 = fragment_rfc791(p, 300)
102 x for f in fragments_400 for x in fragment_rfc791(f, 200)]
103 cls.pkt_infos.append(
104 (index, fragments_400, fragments_300, fragments_200))
105 cls.fragments_400 = [
106 x for (_, frags, _, _) in cls.pkt_infos for x in frags]
107 cls.fragments_300 = [
108 x for (_, _, frags, _) in cls.pkt_infos for x in frags]
109 cls.fragments_200 = [
110 x for (_, _, _, frags) in cls.pkt_infos for x in frags]
111 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
112 "%s 300-byte fragments and %s 200-byte fragments" %
113 (len(infos), len(cls.fragments_400),
114 len(cls.fragments_300), len(cls.fragments_200)))
116 def verify_capture(self, capture, dropped_packet_indexes=[]):
117 """Verify captured packet stream.
119 :param list capture: Captured packet stream.
123 for packet in capture:
125 self.logger.debug(ppp("Got packet:", packet))
128 payload_info = self.payload_to_info(packet[Raw])
129 packet_index = payload_info.index
131 packet_index not in dropped_packet_indexes,
132 ppp("Packet received, but should be dropped:", packet))
133 if packet_index in seen:
134 raise Exception(ppp("Duplicate packet received", packet))
135 seen.add(packet_index)
136 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
137 info = self._packet_infos[packet_index]
138 self.assertTrue(info is not None)
139 self.assertEqual(packet_index, info.index)
140 saved_packet = info.data
141 self.assertEqual(ip.src, saved_packet[IP].src)
142 self.assertEqual(ip.dst, saved_packet[IP].dst)
143 self.assertEqual(udp.payload, saved_packet[UDP].payload)
145 self.logger.error(ppp("Unexpected or invalid packet:", packet))
147 for index in self._packet_infos:
148 self.assertTrue(index in seen or index in dropped_packet_indexes,
149 "Packet with packet_index %d not received" % index)
151 def test_reassembly(self):
152 """ basic reassembly """
154 self.pg_enable_capture()
155 self.src_if.add_stream(self.fragments_200)
158 packets = self.dst_if.get_capture(len(self.pkt_infos))
159 self.verify_capture(packets)
160 self.src_if.assert_nothing_captured()
162 # run it all again to verify correctness
163 self.pg_enable_capture()
164 self.src_if.add_stream(self.fragments_200)
167 packets = self.dst_if.get_capture(len(self.pkt_infos))
168 self.verify_capture(packets)
169 self.src_if.assert_nothing_captured()
171 def test_verify_clear_trace_mid_reassembly(self):
172 """ verify clear trace works mid-reassembly """
174 self.pg_enable_capture()
175 self.src_if.add_stream(self.fragments_200[0:-1])
178 self.logger.debug(self.vapi.cli("show trace"))
179 self.vapi.cli("clear trace")
181 self.src_if.add_stream(self.fragments_200[-1])
183 packets = self.dst_if.get_capture(len(self.pkt_infos))
184 self.verify_capture(packets)
186 def test_reversed(self):
187 """ reverse order reassembly """
189 fragments = list(self.fragments_200)
192 self.pg_enable_capture()
193 self.src_if.add_stream(fragments)
196 packets = self.dst_if.get_capture(len(self.packet_infos))
197 self.verify_capture(packets)
198 self.src_if.assert_nothing_captured()
200 # run it all again to verify correctness
201 self.pg_enable_capture()
202 self.src_if.add_stream(fragments)
205 packets = self.dst_if.get_capture(len(self.packet_infos))
206 self.verify_capture(packets)
207 self.src_if.assert_nothing_captured()
209 def test_long_fragment_chain(self):
210 """ long fragment chain """
213 "/err/ip4-full-reassembly-feature/fragment chain too long (drop)"
215 error_cnt = self.statistics.get_err_counter(error_cnt_str)
217 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
218 max_reassembly_length=3,
219 expire_walk_interval_ms=50)
221 p1 = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
222 IP(id=1000, src=self.src_if.remote_ip4,
223 dst=self.dst_if.remote_ip4) /
224 UDP(sport=1234, dport=5678) /
226 p2 = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
227 IP(id=1001, src=self.src_if.remote_ip4,
228 dst=self.dst_if.remote_ip4) /
229 UDP(sport=1234, dport=5678) /
231 frags = fragment_rfc791(p1, 200) + fragment_rfc791(p2, 500)
233 self.pg_enable_capture()
234 self.src_if.add_stream(frags)
237 self.dst_if.get_capture(1)
238 self.assert_error_counter_equal(error_cnt_str, error_cnt + 1)
241 """ fragment length + ip header size > 65535 """
242 self.vapi.cli("clear errors")
243 raw = b'''E\x00\x00\x88,\xf8\x1f\xfe@\x01\x98\x00\xc0\xa8\n-\xc0\xa8\n\
244 \x01\x08\x00\xf0J\xed\xcb\xf1\xf5Test-group: IPv4.IPv4.ipv4-message.\
245 Ethernet-Payload.IPv4-Packet.IPv4-Header.Fragment-Offset; Test-case: 5737'''
246 malformed_packet = (Ether(dst=self.src_if.local_mac,
247 src=self.src_if.remote_mac) /
249 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
250 IP(id=1000, src=self.src_if.remote_ip4,
251 dst=self.dst_if.remote_ip4) /
252 UDP(sport=1234, dport=5678) /
254 valid_fragments = fragment_rfc791(p, 400)
256 counter = "/err/ip4-full-reassembly-feature/malformed packets"
257 error_counter = self.statistics.get_err_counter(counter)
258 self.pg_enable_capture()
259 self.src_if.add_stream([malformed_packet] + valid_fragments)
262 self.dst_if.get_capture(1)
263 self.logger.debug(self.vapi.ppcli("show error"))
264 self.assertEqual(self.statistics.get_err_counter(counter),
267 def test_44924(self):
268 """ compress tiny fragments """
269 packets = [(Ether(dst=self.src_if.local_mac,
270 src=self.src_if.remote_mac) /
271 IP(id=24339, flags="MF", frag=0, ttl=64,
272 src=self.src_if.remote_ip4,
273 dst=self.dst_if.remote_ip4) /
274 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
275 Raw(load='Test-group: IPv4')),
276 (Ether(dst=self.src_if.local_mac,
277 src=self.src_if.remote_mac) /
278 IP(id=24339, flags="MF", frag=3, ttl=64,
279 src=self.src_if.remote_ip4,
280 dst=self.dst_if.remote_ip4) /
281 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
282 Raw(load='.IPv4.Fragmentation.vali')),
283 (Ether(dst=self.src_if.local_mac,
284 src=self.src_if.remote_mac) /
285 IP(id=24339, frag=6, ttl=64,
286 src=self.src_if.remote_ip4,
287 dst=self.dst_if.remote_ip4) /
288 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
289 Raw(load='d; Test-case: 44924'))
292 self.pg_enable_capture()
293 self.src_if.add_stream(packets)
296 self.dst_if.get_capture(1)
298 def test_frag_1(self):
299 """ fragment of size 1 """
300 self.vapi.cli("clear errors")
301 malformed_packets = [(Ether(dst=self.src_if.local_mac,
302 src=self.src_if.remote_mac) /
303 IP(id=7, len=21, flags="MF", frag=0, ttl=64,
304 src=self.src_if.remote_ip4,
305 dst=self.dst_if.remote_ip4) /
306 ICMP(type="echo-request")),
307 (Ether(dst=self.src_if.local_mac,
308 src=self.src_if.remote_mac) /
309 IP(id=7, len=21, frag=1, ttl=64,
310 src=self.src_if.remote_ip4,
311 dst=self.dst_if.remote_ip4) /
315 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
316 IP(id=1000, src=self.src_if.remote_ip4,
317 dst=self.dst_if.remote_ip4) /
318 UDP(sport=1234, dport=5678) /
320 valid_fragments = fragment_rfc791(p, 400)
322 self.pg_enable_capture()
323 self.src_if.add_stream(malformed_packets + valid_fragments)
326 self.dst_if.get_capture(1)
328 self.assert_packet_counter_equal("ip4-full-reassembly-feature", 1)
329 # TODO remove above, uncomment below once clearing of counters
331 # self.assert_packet_counter_equal(
332 # "/err/ip4-full-reassembly-feature/malformed packets", 1)
334 def test_random(self):
335 """ random order reassembly """
337 fragments = list(self.fragments_200)
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 # run it all again to verify correctness
349 self.pg_enable_capture()
350 self.src_if.add_stream(fragments)
353 packets = self.dst_if.get_capture(len(self.packet_infos))
354 self.verify_capture(packets)
355 self.src_if.assert_nothing_captured()
357 def test_duplicates(self):
358 """ duplicate fragments """
361 x for (_, frags, _, _) in self.pkt_infos
363 for _ in range(0, min(2, len(frags)))
366 self.pg_enable_capture()
367 self.src_if.add_stream(fragments)
370 packets = self.dst_if.get_capture(len(self.pkt_infos))
371 self.verify_capture(packets)
372 self.src_if.assert_nothing_captured()
374 def test_overlap1(self):
375 """ overlapping fragments case #1 """
378 for _, _, frags_300, frags_200 in self.pkt_infos:
379 if len(frags_300) == 1:
380 fragments.extend(frags_300)
382 for i, j in zip(frags_200, frags_300):
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 # run it all to verify correctness
395 self.pg_enable_capture()
396 self.src_if.add_stream(fragments)
399 packets = self.dst_if.get_capture(len(self.pkt_infos))
400 self.verify_capture(packets)
401 self.src_if.assert_nothing_captured()
403 def test_overlap2(self):
404 """ overlapping fragments case #2 """
407 for _, _, frags_300, frags_200 in self.pkt_infos:
408 if len(frags_300) == 1:
409 fragments.extend(frags_300)
411 # care must be taken here so that there are no fragments
412 # received by vpp after reassembly is finished, otherwise
413 # new reassemblies will be started and packet generator will
414 # freak out when it detects unfreed buffers
415 zipped = zip(frags_300, frags_200)
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 # run it all to verify correctness
430 self.pg_enable_capture()
431 self.src_if.add_stream(fragments)
434 packets = self.dst_if.get_capture(len(self.pkt_infos))
435 self.verify_capture(packets)
436 self.src_if.assert_nothing_captured()
438 def test_timeout_inline(self):
439 """ timeout (inline) """
441 dropped_packet_indexes = set(
442 index for (index, frags, _, _) in self.pkt_infos if len(frags) > 1
445 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
446 max_reassembly_length=3,
447 expire_walk_interval_ms=10000)
449 self.pg_enable_capture()
450 self.src_if.add_stream(self.fragments_400)
453 packets = self.dst_if.get_capture(
454 len(self.pkt_infos) - len(dropped_packet_indexes))
455 self.verify_capture(packets, dropped_packet_indexes)
456 self.src_if.assert_nothing_captured()
458 def test_timeout_cleanup(self):
459 """ timeout (cleanup) """
461 # whole packets + fragmented packets sans last fragment
463 x for (_, frags_400, _, _) in self.pkt_infos
464 for x in frags_400[:-1 if len(frags_400) > 1 else None]
467 # last fragments for fragmented packets
468 fragments2 = [frags_400[-1]
469 for (_, frags_400, _, _) in self.pkt_infos
470 if len(frags_400) > 1]
472 dropped_packet_indexes = set(
473 index for (index, frags_400, _, _) in self.pkt_infos
474 if len(frags_400) > 1)
476 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
477 max_reassembly_length=1000,
478 expire_walk_interval_ms=50)
480 self.pg_enable_capture()
481 self.src_if.add_stream(fragments)
484 self.virtual_sleep(.25, "wait before sending rest of fragments")
486 self.src_if.add_stream(fragments2)
489 packets = self.dst_if.get_capture(
490 len(self.pkt_infos) - len(dropped_packet_indexes))
491 self.verify_capture(packets, dropped_packet_indexes)
492 self.src_if.assert_nothing_captured()
494 def test_disabled(self):
495 """ reassembly disabled """
497 dropped_packet_indexes = set(
498 index for (index, frags_400, _, _) in self.pkt_infos
499 if len(frags_400) > 1)
501 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
502 max_reassembly_length=3,
503 expire_walk_interval_ms=10000)
505 self.pg_enable_capture()
506 self.src_if.add_stream(self.fragments_400)
509 packets = self.dst_if.get_capture(
510 len(self.pkt_infos) - len(dropped_packet_indexes))
511 self.verify_capture(packets, dropped_packet_indexes)
512 self.src_if.assert_nothing_captured()
515 class TestIPv4SVReassembly(VppTestCase):
516 """ IPv4 Shallow Virtual Reassembly """
520 super(TestIPv4SVReassembly, cls).setUpClass()
522 cls.create_pg_interfaces([0, 1])
526 # setup all interfaces
527 for i in cls.pg_interfaces:
533 """ Test setup - force timeout on existing reassemblies """
534 super(TestIPv4SVReassembly, self).setUp()
535 self.vapi.ip_reassembly_enable_disable(
536 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
537 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
538 self.vapi.ip_reassembly_set(
539 timeout_ms=0, max_reassemblies=1000,
540 max_reassembly_length=1000,
541 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
542 expire_walk_interval_ms=10)
543 self.virtual_sleep(.25)
544 self.vapi.ip_reassembly_set(
545 timeout_ms=1000000, max_reassemblies=1000,
546 max_reassembly_length=1000,
547 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
548 expire_walk_interval_ms=10000)
551 super(TestIPv4SVReassembly, self).tearDown()
552 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
553 self.logger.debug(self.vapi.ppcli("show buffers"))
555 def test_basic(self):
556 """ basic reassembly """
560 while len(payload) < payload_len:
561 payload += "%u " % counter
564 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
565 IP(id=1, src=self.src_if.remote_ip4,
566 dst=self.dst_if.remote_ip4) /
567 UDP(sport=1234, dport=5678) /
569 fragments = fragment_rfc791(p, payload_len/4)
571 # send fragment #2 - should be cached inside reassembly
572 self.pg_enable_capture()
573 self.src_if.add_stream(fragments[1])
575 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
576 self.logger.debug(self.vapi.ppcli("show buffers"))
577 self.logger.debug(self.vapi.ppcli("show trace"))
578 self.dst_if.assert_nothing_captured()
580 # send fragment #1 - reassembly is finished now and both fragments
582 self.pg_enable_capture()
583 self.src_if.add_stream(fragments[0])
585 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
586 self.logger.debug(self.vapi.ppcli("show buffers"))
587 self.logger.debug(self.vapi.ppcli("show trace"))
588 c = self.dst_if.get_capture(2)
589 for sent, recvd in zip([fragments[1], fragments[0]], c):
590 self.assertEqual(sent[IP].src, recvd[IP].src)
591 self.assertEqual(sent[IP].dst, recvd[IP].dst)
592 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
594 # send rest of fragments - should be immediately forwarded
595 self.pg_enable_capture()
596 self.src_if.add_stream(fragments[2:])
598 c = self.dst_if.get_capture(len(fragments[2:]))
599 for sent, recvd in zip(fragments[2:], c):
600 self.assertEqual(sent[IP].src, recvd[IP].src)
601 self.assertEqual(sent[IP].dst, recvd[IP].dst)
602 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
604 def test_verify_clear_trace_mid_reassembly(self):
605 """ verify clear trace works mid-reassembly """
609 while len(payload) < payload_len:
610 payload += "%u " % counter
613 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
614 IP(id=1, src=self.src_if.remote_ip4,
615 dst=self.dst_if.remote_ip4) /
616 UDP(sport=1234, dport=5678) /
618 fragments = fragment_rfc791(p, payload_len/4)
620 self.pg_enable_capture()
621 self.src_if.add_stream(fragments[1])
624 self.logger.debug(self.vapi.cli("show trace"))
625 self.vapi.cli("clear trace")
627 self.pg_enable_capture()
628 self.src_if.add_stream(fragments[0])
630 self.dst_if.get_capture(2)
632 self.logger.debug(self.vapi.cli("show trace"))
633 self.vapi.cli("clear trace")
635 self.pg_enable_capture()
636 self.src_if.add_stream(fragments[2:])
638 self.dst_if.get_capture(len(fragments[2:]))
640 def test_timeout(self):
641 """ reassembly timeout """
645 while len(payload) < payload_len:
646 payload += "%u " % counter
649 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
650 IP(id=1, src=self.src_if.remote_ip4,
651 dst=self.dst_if.remote_ip4) /
652 UDP(sport=1234, dport=5678) /
654 fragments = fragment_rfc791(p, payload_len/4)
656 self.vapi.ip_reassembly_set(
657 timeout_ms=100, max_reassemblies=1000,
658 max_reassembly_length=1000,
659 expire_walk_interval_ms=50,
660 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
662 # send fragments #2 and #1 - should be forwarded
663 self.pg_enable_capture()
664 self.src_if.add_stream(fragments[0:2])
666 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
667 self.logger.debug(self.vapi.ppcli("show buffers"))
668 self.logger.debug(self.vapi.ppcli("show trace"))
669 c = self.dst_if.get_capture(2)
670 for sent, recvd in zip([fragments[1], fragments[0]], c):
671 self.assertEqual(sent[IP].src, recvd[IP].src)
672 self.assertEqual(sent[IP].dst, recvd[IP].dst)
673 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
676 self.virtual_sleep(.25, "wait before sending rest of fragments")
678 # send rest of fragments - shouldn't be forwarded
679 self.pg_enable_capture()
680 self.src_if.add_stream(fragments[2:])
682 self.dst_if.assert_nothing_captured()
685 """ reassembly reuses LRU element """
687 self.vapi.ip_reassembly_set(
688 timeout_ms=1000000, max_reassemblies=1,
689 max_reassembly_length=1000,
690 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
691 expire_walk_interval_ms=10000)
696 while len(payload) < payload_len:
697 payload += "%u " % counter
703 for i in range(packet_count)
704 for p in (Ether(dst=self.src_if.local_mac,
705 src=self.src_if.remote_mac) /
706 IP(id=i, src=self.src_if.remote_ip4,
707 dst=self.dst_if.remote_ip4) /
708 UDP(sport=1234, dport=5678) /
710 for f in fragment_rfc791(p, payload_len/4)]
712 self.pg_enable_capture()
713 self.src_if.add_stream(fragments)
715 c = self.dst_if.get_capture(len(fragments))
716 for sent, recvd in zip(fragments, c):
717 self.assertEqual(sent[IP].src, recvd[IP].src)
718 self.assertEqual(sent[IP].dst, recvd[IP].dst)
719 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
721 def send_mixed_and_verify_capture(self, traffic):
724 for c in range(t['count']):
726 (Ether(dst=self.src_if.local_mac,
727 src=self.src_if.remote_mac) /
730 src=self.src_if.remote_ip4,
731 dst=self.dst_if.remote_ip4) /
732 UDP(sport=1234, dport=5678) /
734 self.counter = self.counter + 1
736 self.pg_enable_capture()
737 self.src_if.add_stream(stream)
739 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
740 self.logger.debug(self.vapi.ppcli("show buffers"))
741 self.logger.debug(self.vapi.ppcli("show trace"))
742 self.dst_if.get_capture(len(stream))
744 def test_mixed(self):
745 """ mixed traffic correctly passes through SVR """
748 self.send_mixed_and_verify_capture([{'count': 1, 'flags': ''}])
749 self.send_mixed_and_verify_capture([{'count': 2, 'flags': ''}])
750 self.send_mixed_and_verify_capture([{'count': 3, 'flags': ''}])
751 self.send_mixed_and_verify_capture([{'count': 8, 'flags': ''}])
752 self.send_mixed_and_verify_capture([{'count': 257, 'flags': ''}])
754 self.send_mixed_and_verify_capture([{'count': 1, 'flags': 'MF'}])
755 self.send_mixed_and_verify_capture([{'count': 2, 'flags': 'MF'}])
756 self.send_mixed_and_verify_capture([{'count': 3, 'flags': 'MF'}])
757 self.send_mixed_and_verify_capture([{'count': 8, 'flags': 'MF'}])
758 self.send_mixed_and_verify_capture([{'count': 257, 'flags': 'MF'}])
760 self.send_mixed_and_verify_capture(
761 [{'count': 1, 'flags': ''}, {'count': 1, 'flags': 'MF'}])
762 self.send_mixed_and_verify_capture(
763 [{'count': 2, 'flags': ''}, {'count': 2, 'flags': 'MF'}])
764 self.send_mixed_and_verify_capture(
765 [{'count': 3, 'flags': ''}, {'count': 3, 'flags': 'MF'}])
766 self.send_mixed_and_verify_capture(
767 [{'count': 8, 'flags': ''}, {'count': 8, 'flags': 'MF'}])
768 self.send_mixed_and_verify_capture(
769 [{'count': 129, 'flags': ''}, {'count': 129, 'flags': 'MF'}])
771 self.send_mixed_and_verify_capture(
772 [{'count': 1, 'flags': ''}, {'count': 1, 'flags': 'MF'},
773 {'count': 1, 'flags': ''}, {'count': 1, 'flags': 'MF'}])
774 self.send_mixed_and_verify_capture(
775 [{'count': 2, 'flags': ''}, {'count': 2, 'flags': 'MF'},
776 {'count': 2, 'flags': ''}, {'count': 2, 'flags': 'MF'}])
777 self.send_mixed_and_verify_capture(
778 [{'count': 3, 'flags': ''}, {'count': 3, 'flags': 'MF'},
779 {'count': 3, 'flags': ''}, {'count': 3, 'flags': 'MF'}])
780 self.send_mixed_and_verify_capture(
781 [{'count': 8, 'flags': ''}, {'count': 8, 'flags': 'MF'},
782 {'count': 8, 'flags': ''}, {'count': 8, 'flags': 'MF'}])
783 self.send_mixed_and_verify_capture(
784 [{'count': 65, 'flags': ''}, {'count': 65, 'flags': 'MF'},
785 {'count': 65, 'flags': ''}, {'count': 65, 'flags': 'MF'}])
788 class TestIPv4MWReassembly(VppTestCase):
789 """ IPv4 Reassembly (multiple workers) """
794 super(TestIPv4MWReassembly, cls).setUpClass()
796 cls.create_pg_interfaces(range(cls.vpp_worker_count+1))
798 cls.send_ifs = cls.pg_interfaces[:-1]
799 cls.dst_if = cls.pg_interfaces[-1]
801 # setup all interfaces
802 for i in cls.pg_interfaces:
807 # packets sizes reduced here because we are generating packets without
808 # Ethernet headers, which are added later (diff fragments go via
809 # different interfaces)
810 cls.packet_sizes = [64-len(Ether()), 512-len(Ether()),
811 1518-len(Ether()), 9018-len(Ether())]
812 cls.padding = " abcdefghijklmn"
813 cls.create_stream(cls.packet_sizes)
814 cls.create_fragments()
817 def tearDownClass(cls):
818 super(TestIPv4MWReassembly, cls).tearDownClass()
821 """ Test setup - force timeout on existing reassemblies """
822 super(TestIPv4MWReassembly, self).setUp()
823 for intf in self.send_ifs:
824 self.vapi.ip_reassembly_enable_disable(
825 sw_if_index=intf.sw_if_index, enable_ip4=True)
826 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
827 max_reassembly_length=1000,
828 expire_walk_interval_ms=10)
829 self.virtual_sleep(.25)
830 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
831 max_reassembly_length=1000,
832 expire_walk_interval_ms=10000)
835 super(TestIPv4MWReassembly, self).tearDown()
837 def show_commands_at_teardown(self):
838 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
839 self.logger.debug(self.vapi.ppcli("show buffers"))
842 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
843 """Create input packet stream
845 :param list packet_sizes: Required packet sizes.
847 for i in range(0, packet_count):
848 info = cls.create_packet_info(cls.src_if, cls.src_if)
849 payload = cls.info_to_payload(info)
850 p = (IP(id=info.index, src=cls.src_if.remote_ip4,
851 dst=cls.dst_if.remote_ip4) /
852 UDP(sport=1234, dport=5678) /
854 size = packet_sizes[(i // 2) % len(packet_sizes)]
855 cls.extend_packet(p, size, cls.padding)
859 def create_fragments(cls):
860 infos = cls._packet_infos
862 for index, info in infos.items():
864 # cls.logger.debug(ppp("Packet:",
865 # p.__class__(scapy.compat.raw(p))))
866 fragments_400 = fragment_rfc791(p, 400)
867 cls.pkt_infos.append((index, fragments_400))
868 cls.fragments_400 = [
869 x for (_, frags) in cls.pkt_infos for x in frags]
870 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, " %
871 (len(infos), len(cls.fragments_400)))
873 def verify_capture(self, capture, dropped_packet_indexes=[]):
874 """Verify captured packet stream.
876 :param list capture: Captured packet stream.
880 for packet in capture:
882 self.logger.debug(ppp("Got packet:", packet))
885 payload_info = self.payload_to_info(packet[Raw])
886 packet_index = payload_info.index
888 packet_index not in dropped_packet_indexes,
889 ppp("Packet received, but should be dropped:", packet))
890 if packet_index in seen:
891 raise Exception(ppp("Duplicate packet received", packet))
892 seen.add(packet_index)
893 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
894 info = self._packet_infos[packet_index]
895 self.assertTrue(info is not None)
896 self.assertEqual(packet_index, info.index)
897 saved_packet = info.data
898 self.assertEqual(ip.src, saved_packet[IP].src)
899 self.assertEqual(ip.dst, saved_packet[IP].dst)
900 self.assertEqual(udp.payload, saved_packet[UDP].payload)
902 self.logger.error(ppp("Unexpected or invalid packet:", packet))
904 for index in self._packet_infos:
905 self.assertTrue(index in seen or index in dropped_packet_indexes,
906 "Packet with packet_index %d not received" % index)
908 def send_packets(self, packets):
909 for counter in range(self.vpp_worker_count):
910 if 0 == len(packets[counter]):
912 send_if = self.send_ifs[counter]
914 (Ether(dst=send_if.local_mac, src=send_if.remote_mac) / x
915 for x in packets[counter]),
919 def test_worker_conflict(self):
920 """ 1st and FO=0 fragments on different workers """
922 # in first wave we send fragments which don't start at offset 0
923 # then we send fragments with offset 0 on a different thread
924 # then the rest of packets on a random thread
925 first_packets = [[] for n in range(self.vpp_worker_count)]
926 second_packets = [[] for n in range(self.vpp_worker_count)]
927 rest_of_packets = [[] for n in range(self.vpp_worker_count)]
928 for (_, p) in self.pkt_infos:
929 wi = randrange(self.vpp_worker_count)
930 second_packets[wi].append(p[0])
935 wi2 = randrange(self.vpp_worker_count)
936 first_packets[wi2].append(p[1])
937 wi3 = randrange(self.vpp_worker_count)
938 rest_of_packets[wi3].extend(p[2:])
940 self.pg_enable_capture()
941 self.send_packets(first_packets)
942 self.send_packets(second_packets)
943 self.send_packets(rest_of_packets)
945 packets = self.dst_if.get_capture(len(self.pkt_infos))
946 self.verify_capture(packets)
947 for send_if in self.send_ifs:
948 send_if.assert_nothing_captured()
950 self.logger.debug(self.vapi.ppcli("show trace"))
951 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
952 self.logger.debug(self.vapi.ppcli("show buffers"))
953 self.vapi.cli("clear trace")
955 self.pg_enable_capture()
956 self.send_packets(first_packets)
957 self.send_packets(second_packets)
958 self.send_packets(rest_of_packets)
960 packets = self.dst_if.get_capture(len(self.pkt_infos))
961 self.verify_capture(packets)
962 for send_if in self.send_ifs:
963 send_if.assert_nothing_captured()
966 class TestIPv6Reassembly(VppTestCase):
967 """ IPv6 Reassembly """
971 super(TestIPv6Reassembly, cls).setUpClass()
973 cls.create_pg_interfaces([0, 1])
977 # setup all interfaces
978 for i in cls.pg_interfaces:
984 cls.packet_sizes = [64, 512, 1518, 9018]
985 cls.padding = " abcdefghijklmn"
986 cls.create_stream(cls.packet_sizes)
987 cls.create_fragments()
990 def tearDownClass(cls):
991 super(TestIPv6Reassembly, cls).tearDownClass()
994 """ Test setup - force timeout on existing reassemblies """
995 super(TestIPv6Reassembly, self).setUp()
996 self.vapi.ip_reassembly_enable_disable(
997 sw_if_index=self.src_if.sw_if_index, enable_ip6=True)
998 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
999 max_reassembly_length=1000,
1000 expire_walk_interval_ms=10, is_ip6=1)
1001 self.virtual_sleep(.25)
1002 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1003 max_reassembly_length=1000,
1004 expire_walk_interval_ms=10000, is_ip6=1)
1005 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1006 self.logger.debug(self.vapi.ppcli("show buffers"))
1009 super(TestIPv6Reassembly, self).tearDown()
1011 def show_commands_at_teardown(self):
1012 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1013 self.logger.debug(self.vapi.ppcli("show buffers"))
1016 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
1017 """Create input packet stream for defined interface.
1019 :param list packet_sizes: Required packet sizes.
1021 for i in range(0, packet_count):
1022 info = cls.create_packet_info(cls.src_if, cls.src_if)
1023 payload = cls.info_to_payload(info)
1024 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
1025 IPv6(src=cls.src_if.remote_ip6,
1026 dst=cls.dst_if.remote_ip6) /
1027 UDP(sport=1234, dport=5678) /
1029 size = packet_sizes[(i // 2) % len(packet_sizes)]
1030 cls.extend_packet(p, size, cls.padding)
1034 def create_fragments(cls):
1035 infos = cls._packet_infos
1037 for index, info in infos.items():
1039 # cls.logger.debug(ppp("Packet:",
1040 # p.__class__(scapy.compat.raw(p))))
1041 fragments_400 = fragment_rfc8200(p, info.index, 400)
1042 fragments_300 = fragment_rfc8200(p, info.index, 300)
1043 cls.pkt_infos.append((index, fragments_400, fragments_300))
1044 cls.fragments_400 = [
1045 x for _, frags, _ in cls.pkt_infos for x in frags]
1046 cls.fragments_300 = [
1047 x for _, _, frags in cls.pkt_infos for x in frags]
1048 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
1049 "and %s 300-byte fragments" %
1050 (len(infos), len(cls.fragments_400),
1051 len(cls.fragments_300)))
1053 def verify_capture(self, capture, dropped_packet_indexes=[]):
1054 """Verify captured packet strea .
1056 :param list capture: Captured packet stream.
1060 for packet in capture:
1062 self.logger.debug(ppp("Got packet:", packet))
1065 payload_info = self.payload_to_info(packet[Raw])
1066 packet_index = payload_info.index
1068 packet_index not in dropped_packet_indexes,
1069 ppp("Packet received, but should be dropped:", packet))
1070 if packet_index in seen:
1071 raise Exception(ppp("Duplicate packet received", packet))
1072 seen.add(packet_index)
1073 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
1074 info = self._packet_infos[packet_index]
1075 self.assertTrue(info is not None)
1076 self.assertEqual(packet_index, info.index)
1077 saved_packet = info.data
1078 self.assertEqual(ip.src, saved_packet[IPv6].src)
1079 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
1080 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1082 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1084 for index in self._packet_infos:
1085 self.assertTrue(index in seen or index in dropped_packet_indexes,
1086 "Packet with packet_index %d not received" % index)
1088 def test_reassembly(self):
1089 """ basic reassembly """
1091 self.pg_enable_capture()
1092 self.src_if.add_stream(self.fragments_400)
1095 packets = self.dst_if.get_capture(len(self.pkt_infos))
1096 self.verify_capture(packets)
1097 self.src_if.assert_nothing_captured()
1099 # run it all again to verify correctness
1100 self.pg_enable_capture()
1101 self.src_if.add_stream(self.fragments_400)
1104 packets = self.dst_if.get_capture(len(self.pkt_infos))
1105 self.verify_capture(packets)
1106 self.src_if.assert_nothing_captured()
1108 def test_buffer_boundary(self):
1109 """ fragment header crossing buffer boundary """
1111 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1112 IPv6(src=self.src_if.remote_ip6,
1113 dst=self.src_if.local_ip6) /
1115 options=[HBHOptUnknown(otype=0xff, optlen=0)] * 1000) /
1116 IPv6ExtHdrFragment(m=1) /
1117 UDP(sport=1234, dport=5678) /
1119 self.pg_enable_capture()
1120 self.src_if.add_stream([p])
1122 self.src_if.assert_nothing_captured()
1123 self.dst_if.assert_nothing_captured()
1125 def test_verify_clear_trace_mid_reassembly(self):
1126 """ verify clear trace works mid-reassembly """
1128 self.pg_enable_capture()
1129 self.src_if.add_stream(self.fragments_400[0:-1])
1132 self.logger.debug(self.vapi.cli("show trace"))
1133 self.vapi.cli("clear trace")
1135 self.src_if.add_stream(self.fragments_400[-1])
1137 packets = self.dst_if.get_capture(len(self.pkt_infos))
1138 self.verify_capture(packets)
1140 def test_reversed(self):
1141 """ reverse order reassembly """
1143 fragments = list(self.fragments_400)
1146 self.pg_enable_capture()
1147 self.src_if.add_stream(fragments)
1150 packets = self.dst_if.get_capture(len(self.pkt_infos))
1151 self.verify_capture(packets)
1152 self.src_if.assert_nothing_captured()
1154 # run it all again to verify correctness
1155 self.pg_enable_capture()
1156 self.src_if.add_stream(fragments)
1159 packets = self.dst_if.get_capture(len(self.pkt_infos))
1160 self.verify_capture(packets)
1161 self.src_if.assert_nothing_captured()
1163 def test_random(self):
1164 """ random order reassembly """
1166 fragments = list(self.fragments_400)
1169 self.pg_enable_capture()
1170 self.src_if.add_stream(fragments)
1173 packets = self.dst_if.get_capture(len(self.pkt_infos))
1174 self.verify_capture(packets)
1175 self.src_if.assert_nothing_captured()
1177 # run it all again to verify correctness
1178 self.pg_enable_capture()
1179 self.src_if.add_stream(fragments)
1182 packets = self.dst_if.get_capture(len(self.pkt_infos))
1183 self.verify_capture(packets)
1184 self.src_if.assert_nothing_captured()
1186 def test_duplicates(self):
1187 """ duplicate fragments """
1190 x for (_, frags, _) in self.pkt_infos
1192 for _ in range(0, min(2, len(frags)))
1195 self.pg_enable_capture()
1196 self.src_if.add_stream(fragments)
1199 packets = self.dst_if.get_capture(len(self.pkt_infos))
1200 self.verify_capture(packets)
1201 self.src_if.assert_nothing_captured()
1203 def test_long_fragment_chain(self):
1204 """ long fragment chain """
1207 "/err/ip6-full-reassembly-feature/fragment chain too long (drop)"
1209 error_cnt = self.statistics.get_err_counter(error_cnt_str)
1211 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
1212 max_reassembly_length=3,
1213 expire_walk_interval_ms=50, is_ip6=1)
1215 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1216 IPv6(src=self.src_if.remote_ip6,
1217 dst=self.dst_if.remote_ip6) /
1218 UDP(sport=1234, dport=5678) /
1220 frags = fragment_rfc8200(p, 1, 300) + fragment_rfc8200(p, 2, 500)
1222 self.pg_enable_capture()
1223 self.src_if.add_stream(frags)
1226 self.dst_if.get_capture(1)
1227 self.assert_error_counter_equal(error_cnt_str, error_cnt + 1)
1229 def test_overlap1(self):
1230 """ overlapping fragments case #1 """
1233 for _, frags_400, frags_300 in self.pkt_infos:
1234 if len(frags_300) == 1:
1235 fragments.extend(frags_400)
1237 for i, j in zip(frags_300, frags_400):
1241 dropped_packet_indexes = set(
1242 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
1245 self.pg_enable_capture()
1246 self.src_if.add_stream(fragments)
1249 packets = self.dst_if.get_capture(
1250 len(self.pkt_infos) - len(dropped_packet_indexes))
1251 self.verify_capture(packets, dropped_packet_indexes)
1252 self.src_if.assert_nothing_captured()
1254 def test_overlap2(self):
1255 """ overlapping fragments case #2 """
1258 for _, frags_400, frags_300 in self.pkt_infos:
1259 if len(frags_400) == 1:
1260 fragments.extend(frags_400)
1262 # care must be taken here so that there are no fragments
1263 # received by vpp after reassembly is finished, otherwise
1264 # new reassemblies will be started and packet generator will
1265 # freak out when it detects unfreed buffers
1266 zipped = zip(frags_400, frags_300)
1272 dropped_packet_indexes = set(
1273 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
1276 self.pg_enable_capture()
1277 self.src_if.add_stream(fragments)
1280 packets = self.dst_if.get_capture(
1281 len(self.pkt_infos) - len(dropped_packet_indexes))
1282 self.verify_capture(packets, dropped_packet_indexes)
1283 self.src_if.assert_nothing_captured()
1285 def test_timeout_inline(self):
1286 """ timeout (inline) """
1288 dropped_packet_indexes = set(
1289 index for (index, frags, _) in self.pkt_infos if len(frags) > 1
1292 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1293 max_reassembly_length=3,
1294 expire_walk_interval_ms=10000, is_ip6=1)
1296 self.pg_enable_capture()
1297 self.src_if.add_stream(self.fragments_400)
1300 packets = self.dst_if.get_capture(
1301 len(self.pkt_infos) - len(dropped_packet_indexes))
1302 self.verify_capture(packets, dropped_packet_indexes)
1303 pkts = self.src_if.get_capture(
1304 expected_count=len(dropped_packet_indexes))
1306 self.assertIn(ICMPv6TimeExceeded, icmp)
1307 self.assertIn(IPv6ExtHdrFragment, icmp)
1308 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
1309 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
1311 def test_timeout_cleanup(self):
1312 """ timeout (cleanup) """
1314 # whole packets + fragmented packets sans last fragment
1316 x for (_, frags_400, _) in self.pkt_infos
1317 for x in frags_400[:-1 if len(frags_400) > 1 else None]
1320 # last fragments for fragmented packets
1321 fragments2 = [frags_400[-1]
1322 for (_, frags_400, _) in self.pkt_infos
1323 if len(frags_400) > 1]
1325 dropped_packet_indexes = set(
1326 index for (index, frags_400, _) in self.pkt_infos
1327 if len(frags_400) > 1)
1329 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
1330 max_reassembly_length=1000,
1331 expire_walk_interval_ms=50)
1333 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
1334 max_reassembly_length=1000,
1335 expire_walk_interval_ms=50, is_ip6=1)
1337 self.pg_enable_capture()
1338 self.src_if.add_stream(fragments)
1341 self.virtual_sleep(.25, "wait before sending rest of fragments")
1343 self.src_if.add_stream(fragments2)
1346 packets = self.dst_if.get_capture(
1347 len(self.pkt_infos) - len(dropped_packet_indexes))
1348 self.verify_capture(packets, dropped_packet_indexes)
1349 pkts = self.src_if.get_capture(
1350 expected_count=len(dropped_packet_indexes))
1352 self.assertIn(ICMPv6TimeExceeded, icmp)
1353 self.assertIn(IPv6ExtHdrFragment, icmp)
1354 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
1355 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
1357 def test_disabled(self):
1358 """ reassembly disabled """
1360 dropped_packet_indexes = set(
1361 index for (index, frags_400, _) in self.pkt_infos
1362 if len(frags_400) > 1)
1364 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
1365 max_reassembly_length=3,
1366 expire_walk_interval_ms=10000, is_ip6=1)
1368 self.pg_enable_capture()
1369 self.src_if.add_stream(self.fragments_400)
1372 packets = self.dst_if.get_capture(
1373 len(self.pkt_infos) - len(dropped_packet_indexes))
1374 self.verify_capture(packets, dropped_packet_indexes)
1375 self.src_if.assert_nothing_captured()
1377 def test_missing_upper(self):
1378 """ missing upper layer """
1379 optdata = '\x00' * 100
1380 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1381 IPv6(src=self.src_if.remote_ip6,
1382 dst=self.src_if.local_ip6) /
1383 IPv6ExtHdrFragment(m=1) /
1384 IPv6ExtHdrDestOpt(nh=17, options=PadN(optdata='\101' * 255) /
1385 PadN(optdata='\102'*255)))
1387 self.pg_enable_capture()
1388 self.src_if.add_stream([p])
1390 pkts = self.src_if.get_capture(expected_count=1)
1392 self.assertIn(ICMPv6ParamProblem, icmp)
1393 self.assert_equal(icmp[ICMPv6ParamProblem].code, 3, "ICMP code")
1395 def test_truncated_fragment(self):
1396 """ truncated fragment """
1397 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1398 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6,
1400 IPv6ExtHdrFragment(nh=6))
1402 self.send_and_assert_no_replies(self.pg0, [pkt], self.pg0)
1404 def test_invalid_frag_size(self):
1405 """ fragment size not a multiple of 8 """
1406 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1407 IPv6(src=self.src_if.remote_ip6,
1408 dst=self.src_if.local_ip6) /
1409 UDP(sport=1234, dport=5678) /
1411 self.extend_packet(p, 1000, self.padding)
1412 fragments = fragment_rfc8200(p, 1, 500)
1413 bad_fragment = fragments[0]
1414 self.extend_packet(bad_fragment, len(bad_fragment) + 5)
1415 self.pg_enable_capture()
1416 self.src_if.add_stream([bad_fragment])
1418 pkts = self.src_if.get_capture(expected_count=1)
1420 self.assertIn(ICMPv6ParamProblem, icmp)
1421 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1423 def test_invalid_packet_size(self):
1424 """ total packet size > 65535 """
1425 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1426 IPv6(src=self.src_if.remote_ip6,
1427 dst=self.src_if.local_ip6) /
1428 UDP(sport=1234, dport=5678) /
1430 self.extend_packet(p, 1000, self.padding)
1431 fragments = fragment_rfc8200(p, 1, 500)
1432 bad_fragment = fragments[1]
1433 bad_fragment[IPv6ExtHdrFragment].offset = 65500
1434 self.pg_enable_capture()
1435 self.src_if.add_stream([bad_fragment])
1437 pkts = self.src_if.get_capture(expected_count=1)
1439 self.assertIn(ICMPv6ParamProblem, icmp)
1440 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1442 def test_atomic_fragment(self):
1443 """ IPv6 atomic fragment """
1444 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1445 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6,
1446 nh=44, plen=65535) /
1447 IPv6ExtHdrFragment(offset=8191, m=1, res1=0xFF, res2=0xFF,
1448 nh=255, id=0xffff)/('X'*1452))
1450 rx = self.send_and_expect(self.pg0, [pkt], self.pg0)
1451 self.assertIn(ICMPv6ParamProblem, rx[0])
1453 def test_truncated_fragment(self):
1454 """ IPv6 truncated fragment header """
1455 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1456 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6,
1458 IPv6ExtHdrFragment(nh=6))
1460 self.send_and_assert_no_replies(self.pg0, [pkt])
1462 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1463 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.remote_ip6) /
1464 ICMPv6EchoRequest())
1465 rx = self.send_and_expect(self.pg0, [pkt], self.pg0)
1467 def test_one_fragment(self):
1468 """ whole packet in one fragment processed independently """
1469 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1470 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) /
1471 ICMPv6EchoRequest()/Raw('X' * 1600))
1472 frags = fragment_rfc8200(pkt, 1, 400)
1474 # send a fragment with known id
1475 self.send_and_assert_no_replies(self.pg0, [frags[0]])
1477 # send an atomic fragment with same id - should be reassembled
1478 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1479 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) /
1480 IPv6ExtHdrFragment(id=1)/ICMPv6EchoRequest())
1481 rx = self.send_and_expect(self.pg0, [pkt], self.pg0)
1482 self.assertNotIn(IPv6ExtHdrFragment, rx)
1484 # now finish the original reassembly, this should still be possible
1485 rx = self.send_and_expect(self.pg0, frags[1:], self.pg0, n_rx=1)
1486 self.assertNotIn(IPv6ExtHdrFragment, rx)
1488 def test_bunch_of_fragments(self):
1489 """ valid fragments followed by rogue fragments and atomic fragment"""
1490 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1491 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) /
1492 ICMPv6EchoRequest()/Raw('X' * 1600))
1493 frags = fragment_rfc8200(pkt, 1, 400)
1494 self.send_and_expect(self.pg0, frags, self.pg0, n_rx=1)
1496 inc_frag = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1497 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) /
1498 IPv6ExtHdrFragment(id=1, nh=58, offset=608)/Raw('X'*308))
1500 self.send_and_assert_no_replies(self.pg0, inc_frag*604)
1502 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1503 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) /
1504 IPv6ExtHdrFragment(id=1)/ICMPv6EchoRequest())
1505 rx = self.send_and_expect(self.pg0, [pkt], self.pg0)
1506 self.assertNotIn(IPv6ExtHdrFragment, rx)
1509 class TestIPv6MWReassembly(VppTestCase):
1510 """ IPv6 Reassembly (multiple workers) """
1511 vpp_worker_count = 3
1514 def setUpClass(cls):
1515 super(TestIPv6MWReassembly, cls).setUpClass()
1517 cls.create_pg_interfaces(range(cls.vpp_worker_count+1))
1518 cls.src_if = cls.pg0
1519 cls.send_ifs = cls.pg_interfaces[:-1]
1520 cls.dst_if = cls.pg_interfaces[-1]
1522 # setup all interfaces
1523 for i in cls.pg_interfaces:
1528 # packets sizes reduced here because we are generating packets without
1529 # Ethernet headers, which are added later (diff fragments go via
1530 # different interfaces)
1531 cls.packet_sizes = [64-len(Ether()), 512-len(Ether()),
1532 1518-len(Ether()), 9018-len(Ether())]
1533 cls.padding = " abcdefghijklmn"
1534 cls.create_stream(cls.packet_sizes)
1535 cls.create_fragments()
1538 def tearDownClass(cls):
1539 super(TestIPv6MWReassembly, cls).tearDownClass()
1542 """ Test setup - force timeout on existing reassemblies """
1543 super(TestIPv6MWReassembly, self).setUp()
1544 for intf in self.send_ifs:
1545 self.vapi.ip_reassembly_enable_disable(
1546 sw_if_index=intf.sw_if_index, enable_ip6=True)
1547 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1548 max_reassembly_length=1000,
1549 expire_walk_interval_ms=10, is_ip6=1)
1550 self.virtual_sleep(.25)
1551 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1552 max_reassembly_length=1000,
1553 expire_walk_interval_ms=1000, is_ip6=1)
1556 super(TestIPv6MWReassembly, self).tearDown()
1558 def show_commands_at_teardown(self):
1559 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1560 self.logger.debug(self.vapi.ppcli("show buffers"))
1563 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
1564 """Create input packet stream
1566 :param list packet_sizes: Required packet sizes.
1568 for i in range(0, packet_count):
1569 info = cls.create_packet_info(cls.src_if, cls.src_if)
1570 payload = cls.info_to_payload(info)
1571 p = (IPv6(src=cls.src_if.remote_ip6,
1572 dst=cls.dst_if.remote_ip6) /
1573 UDP(sport=1234, dport=5678) /
1575 size = packet_sizes[(i // 2) % len(packet_sizes)]
1576 cls.extend_packet(p, size, cls.padding)
1580 def create_fragments(cls):
1581 infos = cls._packet_infos
1583 for index, info in infos.items():
1585 # cls.logger.debug(ppp("Packet:",
1586 # p.__class__(scapy.compat.raw(p))))
1587 fragments_400 = fragment_rfc8200(p, index, 400)
1588 cls.pkt_infos.append((index, fragments_400))
1589 cls.fragments_400 = [
1590 x for (_, frags) in cls.pkt_infos for x in frags]
1591 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, " %
1592 (len(infos), len(cls.fragments_400)))
1594 def verify_capture(self, capture, dropped_packet_indexes=[]):
1595 """Verify captured packet strea .
1597 :param list capture: Captured packet stream.
1601 for packet in capture:
1603 self.logger.debug(ppp("Got packet:", packet))
1606 payload_info = self.payload_to_info(packet[Raw])
1607 packet_index = payload_info.index
1609 packet_index not in dropped_packet_indexes,
1610 ppp("Packet received, but should be dropped:", packet))
1611 if packet_index in seen:
1612 raise Exception(ppp("Duplicate packet received", packet))
1613 seen.add(packet_index)
1614 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
1615 info = self._packet_infos[packet_index]
1616 self.assertTrue(info is not None)
1617 self.assertEqual(packet_index, info.index)
1618 saved_packet = info.data
1619 self.assertEqual(ip.src, saved_packet[IPv6].src)
1620 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
1621 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1623 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1625 for index in self._packet_infos:
1626 self.assertTrue(index in seen or index in dropped_packet_indexes,
1627 "Packet with packet_index %d not received" % index)
1629 def send_packets(self, packets):
1630 for counter in range(self.vpp_worker_count):
1631 if 0 == len(packets[counter]):
1633 send_if = self.send_ifs[counter]
1635 (Ether(dst=send_if.local_mac, src=send_if.remote_mac) / x
1636 for x in packets[counter]),
1640 def test_worker_conflict(self):
1641 """ 1st and FO=0 fragments on different workers """
1643 # in first wave we send fragments which don't start at offset 0
1644 # then we send fragments with offset 0 on a different thread
1645 # then the rest of packets on a random thread
1646 first_packets = [[] for n in range(self.vpp_worker_count)]
1647 second_packets = [[] for n in range(self.vpp_worker_count)]
1648 rest_of_packets = [[] for n in range(self.vpp_worker_count)]
1649 for (_, p) in self.pkt_infos:
1650 wi = randrange(self.vpp_worker_count)
1651 second_packets[wi].append(p[0])
1656 wi2 = randrange(self.vpp_worker_count)
1657 first_packets[wi2].append(p[1])
1658 wi3 = randrange(self.vpp_worker_count)
1659 rest_of_packets[wi3].extend(p[2:])
1661 self.pg_enable_capture()
1662 self.send_packets(first_packets)
1663 self.send_packets(second_packets)
1664 self.send_packets(rest_of_packets)
1666 packets = self.dst_if.get_capture(len(self.pkt_infos))
1667 self.verify_capture(packets)
1668 for send_if in self.send_ifs:
1669 send_if.assert_nothing_captured()
1671 self.logger.debug(self.vapi.ppcli("show trace"))
1672 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1673 self.logger.debug(self.vapi.ppcli("show buffers"))
1674 self.vapi.cli("clear trace")
1676 self.pg_enable_capture()
1677 self.send_packets(first_packets)
1678 self.send_packets(second_packets)
1679 self.send_packets(rest_of_packets)
1681 packets = self.dst_if.get_capture(len(self.pkt_infos))
1682 self.verify_capture(packets)
1683 for send_if in self.send_ifs:
1684 send_if.assert_nothing_captured()
1687 class TestIPv6SVReassembly(VppTestCase):
1688 """ IPv6 Shallow Virtual Reassembly """
1691 def setUpClass(cls):
1692 super(TestIPv6SVReassembly, cls).setUpClass()
1694 cls.create_pg_interfaces([0, 1])
1695 cls.src_if = cls.pg0
1696 cls.dst_if = cls.pg1
1698 # setup all interfaces
1699 for i in cls.pg_interfaces:
1705 """ Test setup - force timeout on existing reassemblies """
1706 super(TestIPv6SVReassembly, self).setUp()
1707 self.vapi.ip_reassembly_enable_disable(
1708 sw_if_index=self.src_if.sw_if_index, enable_ip6=True,
1709 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
1710 self.vapi.ip_reassembly_set(
1711 timeout_ms=0, max_reassemblies=1000,
1712 max_reassembly_length=1000,
1713 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1714 expire_walk_interval_ms=10, is_ip6=1)
1715 self.virtual_sleep(.25)
1716 self.vapi.ip_reassembly_set(
1717 timeout_ms=1000000, max_reassemblies=1000,
1718 max_reassembly_length=1000,
1719 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1720 expire_walk_interval_ms=10000, is_ip6=1)
1723 super(TestIPv6SVReassembly, self).tearDown()
1724 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1725 self.logger.debug(self.vapi.ppcli("show buffers"))
1727 def test_basic(self):
1728 """ basic reassembly """
1732 while len(payload) < payload_len:
1733 payload += "%u " % counter
1736 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1737 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1738 UDP(sport=1234, dport=5678) /
1740 fragments = fragment_rfc8200(p, 1, payload_len/4)
1742 # send fragment #2 - should be cached inside reassembly
1743 self.pg_enable_capture()
1744 self.src_if.add_stream(fragments[1])
1746 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1747 self.logger.debug(self.vapi.ppcli("show buffers"))
1748 self.logger.debug(self.vapi.ppcli("show trace"))
1749 self.dst_if.assert_nothing_captured()
1751 # send fragment #1 - reassembly is finished now and both fragments
1753 self.pg_enable_capture()
1754 self.src_if.add_stream(fragments[0])
1756 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1757 self.logger.debug(self.vapi.ppcli("show buffers"))
1758 self.logger.debug(self.vapi.ppcli("show trace"))
1759 c = self.dst_if.get_capture(2)
1760 for sent, recvd in zip([fragments[1], fragments[0]], c):
1761 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1762 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1763 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1765 # send rest of fragments - should be immediately forwarded
1766 self.pg_enable_capture()
1767 self.src_if.add_stream(fragments[2:])
1769 c = self.dst_if.get_capture(len(fragments[2:]))
1770 for sent, recvd in zip(fragments[2:], c):
1771 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1772 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1773 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1775 def test_verify_clear_trace_mid_reassembly(self):
1776 """ verify clear trace works mid-reassembly """
1780 while len(payload) < payload_len:
1781 payload += "%u " % counter
1784 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1785 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1786 UDP(sport=1234, dport=5678) /
1788 fragments = fragment_rfc8200(p, 1, payload_len/4)
1790 self.pg_enable_capture()
1791 self.src_if.add_stream(fragments[1])
1794 self.logger.debug(self.vapi.cli("show trace"))
1795 self.vapi.cli("clear trace")
1797 self.pg_enable_capture()
1798 self.src_if.add_stream(fragments[0])
1800 self.dst_if.get_capture(2)
1802 self.logger.debug(self.vapi.cli("show trace"))
1803 self.vapi.cli("clear trace")
1805 self.pg_enable_capture()
1806 self.src_if.add_stream(fragments[2:])
1808 self.dst_if.get_capture(len(fragments[2:]))
1810 def test_timeout(self):
1811 """ reassembly timeout """
1815 while len(payload) < payload_len:
1816 payload += "%u " % counter
1819 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1820 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1821 UDP(sport=1234, dport=5678) /
1823 fragments = fragment_rfc8200(p, 1, payload_len/4)
1825 self.vapi.ip_reassembly_set(
1826 timeout_ms=100, max_reassemblies=1000,
1827 max_reassembly_length=1000,
1828 expire_walk_interval_ms=50,
1830 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
1832 # send fragments #2 and #1 - should be forwarded
1833 self.pg_enable_capture()
1834 self.src_if.add_stream(fragments[0:2])
1836 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
1837 self.logger.debug(self.vapi.ppcli("show buffers"))
1838 self.logger.debug(self.vapi.ppcli("show trace"))
1839 c = self.dst_if.get_capture(2)
1840 for sent, recvd in zip([fragments[1], fragments[0]], c):
1841 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1842 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1843 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1846 self.virtual_sleep(.25, "wait before sending rest of fragments")
1848 # send rest of fragments - shouldn't be forwarded
1849 self.pg_enable_capture()
1850 self.src_if.add_stream(fragments[2:])
1852 self.dst_if.assert_nothing_captured()
1855 """ reassembly reuses LRU element """
1857 self.vapi.ip_reassembly_set(
1858 timeout_ms=1000000, max_reassemblies=1,
1859 max_reassembly_length=1000,
1860 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1861 is_ip6=1, expire_walk_interval_ms=10000)
1866 while len(payload) < payload_len:
1867 payload += "%u " % counter
1873 for i in range(packet_count)
1874 for p in (Ether(dst=self.src_if.local_mac,
1875 src=self.src_if.remote_mac) /
1876 IPv6(src=self.src_if.remote_ip6,
1877 dst=self.dst_if.remote_ip6) /
1878 UDP(sport=1234, dport=5678) /
1880 for f in fragment_rfc8200(p, i, payload_len/4)]
1882 self.pg_enable_capture()
1883 self.src_if.add_stream(fragments)
1885 c = self.dst_if.get_capture(len(fragments))
1886 for sent, recvd in zip(fragments, c):
1887 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1888 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1889 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1891 def test_one_fragment(self):
1892 """ whole packet in one fragment processed independently """
1893 pkt = (Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) /
1894 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1895 ICMPv6EchoRequest()/Raw('X' * 1600))
1896 frags = fragment_rfc8200(pkt, 1, 400)
1898 # send a fragment with known id
1899 self.send_and_expect(self.src_if, [frags[0]], self.dst_if)
1901 # send an atomic fragment with same id - should be reassembled
1902 pkt = (Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) /
1903 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1904 IPv6ExtHdrFragment(id=1)/ICMPv6EchoRequest())
1905 rx = self.send_and_expect(self.src_if, [pkt], self.dst_if)
1907 # now forward packets matching original reassembly, should still work
1908 rx = self.send_and_expect(self.src_if, frags[1:], self.dst_if)
1910 def test_bunch_of_fragments(self):
1911 """ valid fragments followed by rogue fragments and atomic fragment"""
1912 pkt = (Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) /
1913 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1914 ICMPv6EchoRequest()/Raw('X' * 1600))
1915 frags = fragment_rfc8200(pkt, 1, 400)
1916 rx = self.send_and_expect(self.src_if, frags, self.dst_if)
1918 rogue = (Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) /
1919 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1920 IPv6ExtHdrFragment(id=1, nh=58, offset=608)/Raw('X'*308))
1922 self.send_and_expect(self.src_if, rogue*604, self.dst_if)
1924 pkt = (Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) /
1925 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1926 IPv6ExtHdrFragment(id=1)/ICMPv6EchoRequest())
1927 rx = self.send_and_expect(self.src_if, [pkt], self.dst_if)
1929 def test_truncated_fragment(self):
1930 """ truncated fragment """
1931 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1932 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6,
1934 IPv6ExtHdrFragment(nh=6))
1936 self.send_and_assert_no_replies(self.pg0, [pkt], self.pg0)
1939 class TestIPv4ReassemblyLocalNode(VppTestCase):
1940 """ IPv4 Reassembly for packets coming to ip4-local node """
1943 def setUpClass(cls):
1944 super(TestIPv4ReassemblyLocalNode, cls).setUpClass()
1946 cls.create_pg_interfaces([0])
1947 cls.src_dst_if = cls.pg0
1949 # setup all interfaces
1950 for i in cls.pg_interfaces:
1955 cls.padding = " abcdefghijklmn"
1957 cls.create_fragments()
1960 def tearDownClass(cls):
1961 super(TestIPv4ReassemblyLocalNode, cls).tearDownClass()
1964 """ Test setup - force timeout on existing reassemblies """
1965 super(TestIPv4ReassemblyLocalNode, self).setUp()
1966 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1967 max_reassembly_length=1000,
1968 expire_walk_interval_ms=10)
1969 self.virtual_sleep(.25)
1970 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1971 max_reassembly_length=1000,
1972 expire_walk_interval_ms=10000)
1975 super(TestIPv4ReassemblyLocalNode, self).tearDown()
1977 def show_commands_at_teardown(self):
1978 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
1979 self.logger.debug(self.vapi.ppcli("show buffers"))
1982 def create_stream(cls, packet_count=test_packet_count):
1983 """Create input packet stream for defined interface.
1985 :param list packet_sizes: Required packet sizes.
1987 for i in range(0, packet_count):
1988 info = cls.create_packet_info(cls.src_dst_if, cls.src_dst_if)
1989 payload = cls.info_to_payload(info)
1990 p = (Ether(dst=cls.src_dst_if.local_mac,
1991 src=cls.src_dst_if.remote_mac) /
1992 IP(id=info.index, src=cls.src_dst_if.remote_ip4,
1993 dst=cls.src_dst_if.local_ip4) /
1994 ICMP(type='echo-request', id=1234) /
1996 cls.extend_packet(p, 1518, cls.padding)
2000 def create_fragments(cls):
2001 infos = cls._packet_infos
2003 for index, info in infos.items():
2005 # cls.logger.debug(ppp("Packet:",
2006 # p.__class__(scapy.compat.raw(p))))
2007 fragments_300 = fragment_rfc791(p, 300)
2008 cls.pkt_infos.append((index, fragments_300))
2009 cls.fragments_300 = [x for (_, frags) in cls.pkt_infos for x in frags]
2010 cls.logger.debug("Fragmented %s packets into %s 300-byte fragments" %
2011 (len(infos), len(cls.fragments_300)))
2013 def verify_capture(self, capture):
2014 """Verify captured packet stream.
2016 :param list capture: Captured packet stream.
2020 for packet in capture:
2022 self.logger.debug(ppp("Got packet:", packet))
2025 payload_info = self.payload_to_info(packet[Raw])
2026 packet_index = payload_info.index
2027 if packet_index in seen:
2028 raise Exception(ppp("Duplicate packet received", packet))
2029 seen.add(packet_index)
2030 self.assertEqual(payload_info.dst, self.src_dst_if.sw_if_index)
2031 info = self._packet_infos[packet_index]
2032 self.assertIsNotNone(info)
2033 self.assertEqual(packet_index, info.index)
2034 saved_packet = info.data
2035 self.assertEqual(ip.src, saved_packet[IP].dst)
2036 self.assertEqual(ip.dst, saved_packet[IP].src)
2037 self.assertEqual(icmp.type, 0) # echo reply
2038 self.assertEqual(icmp.id, saved_packet[ICMP].id)
2039 self.assertEqual(icmp.payload, saved_packet[ICMP].payload)
2041 self.logger.error(ppp("Unexpected or invalid packet:", packet))
2043 for index in self._packet_infos:
2044 self.assertIn(index, seen,
2045 "Packet with packet_index %d not received" % index)
2047 def test_reassembly(self):
2048 """ basic reassembly """
2050 self.pg_enable_capture()
2051 self.src_dst_if.add_stream(self.fragments_300)
2054 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
2055 self.verify_capture(packets)
2057 # run it all again to verify correctness
2058 self.pg_enable_capture()
2059 self.src_dst_if.add_stream(self.fragments_300)
2062 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
2063 self.verify_capture(packets)
2066 class TestFIFReassembly(VppTestCase):
2067 """ Fragments in fragments reassembly """
2070 def setUpClass(cls):
2071 super(TestFIFReassembly, cls).setUpClass()
2073 cls.create_pg_interfaces([0, 1])
2074 cls.src_if = cls.pg0
2075 cls.dst_if = cls.pg1
2076 for i in cls.pg_interfaces:
2083 cls.packet_sizes = [64, 512, 1518, 9018]
2084 cls.padding = " abcdefghijklmn"
2087 def tearDownClass(cls):
2088 super(TestFIFReassembly, cls).tearDownClass()
2091 """ Test setup - force timeout on existing reassemblies """
2092 super(TestFIFReassembly, self).setUp()
2093 self.vapi.ip_reassembly_enable_disable(
2094 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
2096 self.vapi.ip_reassembly_enable_disable(
2097 sw_if_index=self.dst_if.sw_if_index, enable_ip4=True,
2099 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
2100 max_reassembly_length=1000,
2101 expire_walk_interval_ms=10)
2102 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
2103 max_reassembly_length=1000,
2104 expire_walk_interval_ms=10, is_ip6=1)
2105 self.virtual_sleep(.25)
2106 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
2107 max_reassembly_length=1000,
2108 expire_walk_interval_ms=10000)
2109 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
2110 max_reassembly_length=1000,
2111 expire_walk_interval_ms=10000, is_ip6=1)
2114 super(TestFIFReassembly, self).tearDown()
2116 def show_commands_at_teardown(self):
2117 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
2118 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
2119 self.logger.debug(self.vapi.ppcli("show buffers"))
2121 def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):
2122 """Verify captured packet stream.
2124 :param list capture: Captured packet stream.
2128 for packet in capture:
2130 self.logger.debug(ppp("Got packet:", packet))
2131 ip = packet[ip_class]
2133 payload_info = self.payload_to_info(packet[Raw])
2134 packet_index = payload_info.index
2136 packet_index not in dropped_packet_indexes,
2137 ppp("Packet received, but should be dropped:", packet))
2138 if packet_index in seen:
2139 raise Exception(ppp("Duplicate packet received", packet))
2140 seen.add(packet_index)
2141 self.assertEqual(payload_info.dst, self.dst_if.sw_if_index)
2142 info = self._packet_infos[packet_index]
2143 self.assertTrue(info is not None)
2144 self.assertEqual(packet_index, info.index)
2145 saved_packet = info.data
2146 self.assertEqual(ip.src, saved_packet[ip_class].src)
2147 self.assertEqual(ip.dst, saved_packet[ip_class].dst)
2148 self.assertEqual(udp.payload, saved_packet[UDP].payload)
2150 self.logger.error(ppp("Unexpected or invalid packet:", packet))
2152 for index in self._packet_infos:
2153 self.assertTrue(index in seen or index in dropped_packet_indexes,
2154 "Packet with packet_index %d not received" % index)
2156 def test_fif4(self):
2157 """ Fragments in fragments (4o4) """
2159 # TODO this should be ideally in setUpClass, but then we hit a bug
2160 # with VppIpRoute incorrectly reporting it's present when it's not
2161 # so we need to manually remove the vpp config, thus we cannot have
2162 # it shared for multiple test cases
2163 self.tun_ip4 = "1.1.1.2"
2165 self.gre4 = VppGreInterface(self, self.src_if.local_ip4, self.tun_ip4)
2166 self.gre4.add_vpp_config()
2167 self.gre4.admin_up()
2168 self.gre4.config_ip4()
2170 self.vapi.ip_reassembly_enable_disable(
2171 sw_if_index=self.gre4.sw_if_index, enable_ip4=True)
2173 self.route4 = VppIpRoute(self, self.tun_ip4, 32,
2174 [VppRoutePath(self.src_if.remote_ip4,
2175 self.src_if.sw_if_index)])
2176 self.route4.add_vpp_config()
2178 self.reset_packet_infos()
2179 for i in range(test_packet_count):
2180 info = self.create_packet_info(self.src_if, self.dst_if)
2181 payload = self.info_to_payload(info)
2182 # Ethernet header here is only for size calculation, thus it
2183 # doesn't matter how it's initialized. This is to ensure that
2184 # reassembled packet is not > 9000 bytes, so that it's not dropped
2186 IP(id=i, src=self.src_if.remote_ip4,
2187 dst=self.dst_if.remote_ip4) /
2188 UDP(sport=1234, dport=5678) /
2190 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
2191 self.extend_packet(p, size, self.padding)
2192 info.data = p[IP] # use only IP part, without ethernet header
2194 fragments = [x for _, p in self._packet_infos.items()
2195 for x in fragment_rfc791(p.data, 400)]
2197 encapped_fragments = \
2198 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
2199 IP(src=self.tun_ip4, dst=self.src_if.local_ip4) /
2204 fragmented_encapped_fragments = \
2205 [x for p in encapped_fragments
2206 for x in fragment_rfc791(p, 200)]
2208 self.src_if.add_stream(fragmented_encapped_fragments)
2210 self.pg_enable_capture(self.pg_interfaces)
2213 self.src_if.assert_nothing_captured()
2214 packets = self.dst_if.get_capture(len(self._packet_infos))
2215 self.verify_capture(packets, IP)
2217 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
2218 # so that it's query_vpp_config() works as it should
2219 self.gre4.remove_vpp_config()
2220 self.logger.debug(self.vapi.ppcli("show interface"))
2222 def test_fif6(self):
2223 """ Fragments in fragments (6o6) """
2224 # TODO this should be ideally in setUpClass, but then we hit a bug
2225 # with VppIpRoute incorrectly reporting it's present when it's not
2226 # so we need to manually remove the vpp config, thus we cannot have
2227 # it shared for multiple test cases
2228 self.tun_ip6 = "1002::1"
2230 self.gre6 = VppGreInterface(self, self.src_if.local_ip6, self.tun_ip6)
2231 self.gre6.add_vpp_config()
2232 self.gre6.admin_up()
2233 self.gre6.config_ip6()
2235 self.vapi.ip_reassembly_enable_disable(
2236 sw_if_index=self.gre6.sw_if_index, enable_ip6=True)
2238 self.route6 = VppIpRoute(self, self.tun_ip6, 128,
2240 self.src_if.remote_ip6,
2241 self.src_if.sw_if_index)])
2242 self.route6.add_vpp_config()
2244 self.reset_packet_infos()
2245 for i in range(test_packet_count):
2246 info = self.create_packet_info(self.src_if, self.dst_if)
2247 payload = self.info_to_payload(info)
2248 # Ethernet header here is only for size calculation, thus it
2249 # doesn't matter how it's initialized. This is to ensure that
2250 # reassembled packet is not > 9000 bytes, so that it's not dropped
2252 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
2253 UDP(sport=1234, dport=5678) /
2255 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
2256 self.extend_packet(p, size, self.padding)
2257 info.data = p[IPv6] # use only IPv6 part, without ethernet header
2259 fragments = [x for _, i in self._packet_infos.items()
2260 for x in fragment_rfc8200(
2261 i.data, i.index, 400)]
2263 encapped_fragments = \
2264 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
2265 IPv6(src=self.tun_ip6, dst=self.src_if.local_ip6) /
2270 fragmented_encapped_fragments = \
2271 [x for p in encapped_fragments for x in (
2274 2 * len(self._packet_infos) + p[IPv6ExtHdrFragment].id,
2276 if IPv6ExtHdrFragment in p else [p]
2280 self.src_if.add_stream(fragmented_encapped_fragments)
2282 self.pg_enable_capture(self.pg_interfaces)
2285 self.src_if.assert_nothing_captured()
2286 packets = self.dst_if.get_capture(len(self._packet_infos))
2287 self.verify_capture(packets, IPv6)
2289 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
2290 # so that it's query_vpp_config() works as it should
2291 self.gre6.remove_vpp_config()
2294 if __name__ == '__main__':
2295 unittest.main(testRunner=VppTestRunner)