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_invalid_frag_size(self):
1396 """ fragment size not a multiple of 8 """
1397 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1398 IPv6(src=self.src_if.remote_ip6,
1399 dst=self.src_if.local_ip6) /
1400 UDP(sport=1234, dport=5678) /
1402 self.extend_packet(p, 1000, self.padding)
1403 fragments = fragment_rfc8200(p, 1, 500)
1404 bad_fragment = fragments[0]
1405 self.extend_packet(bad_fragment, len(bad_fragment) + 5)
1406 self.pg_enable_capture()
1407 self.src_if.add_stream([bad_fragment])
1409 pkts = self.src_if.get_capture(expected_count=1)
1411 self.assertIn(ICMPv6ParamProblem, icmp)
1412 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1414 def test_invalid_packet_size(self):
1415 """ total packet size > 65535 """
1416 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1417 IPv6(src=self.src_if.remote_ip6,
1418 dst=self.src_if.local_ip6) /
1419 UDP(sport=1234, dport=5678) /
1421 self.extend_packet(p, 1000, self.padding)
1422 fragments = fragment_rfc8200(p, 1, 500)
1423 bad_fragment = fragments[1]
1424 bad_fragment[IPv6ExtHdrFragment].offset = 65500
1425 self.pg_enable_capture()
1426 self.src_if.add_stream([bad_fragment])
1428 pkts = self.src_if.get_capture(expected_count=1)
1430 self.assertIn(ICMPv6ParamProblem, icmp)
1431 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1433 def test_atomic_fragment(self):
1434 """ IPv6 atomic fragment """
1435 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1436 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6,
1437 nh=44, plen=65535) /
1438 IPv6ExtHdrFragment(offset=8191, m=1, res1=0xFF, res2=0xFF,
1439 nh=255, id=0xffff)/('X'*1452))
1441 rx = self.send_and_expect(self.pg0, [pkt], self.pg0)
1442 self.assertIn(ICMPv6ParamProblem, rx[0])
1444 def test_truncated_fragment(self):
1445 """ IPv6 truncated fragment header """
1446 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1447 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6,
1449 IPv6ExtHdrFragment(nh=6))
1451 self.send_and_assert_no_replies(self.pg0, [pkt])
1453 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1454 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.remote_ip6) /
1455 ICMPv6EchoRequest())
1456 rx = self.send_and_expect(self.pg0, [pkt], self.pg0)
1458 def test_one_fragment(self):
1459 """ whole packet in one fragment processed independently """
1460 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1461 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) /
1462 ICMPv6EchoRequest()/Raw('X' * 1600))
1463 frags = fragment_rfc8200(pkt, 1, 400)
1465 # send a fragment with known id
1466 self.send_and_assert_no_replies(self.pg0, [frags[0]])
1468 # send an atomic fragment with same id - should be reassembled
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 IPv6ExtHdrFragment(id=1)/ICMPv6EchoRequest())
1472 rx = self.send_and_expect(self.pg0, [pkt], self.pg0)
1473 self.assertNotIn(IPv6ExtHdrFragment, rx)
1475 # now finish the original reassembly, this should still be possible
1476 rx = self.send_and_expect(self.pg0, frags[1:], self.pg0, n_rx=1)
1477 self.assertNotIn(IPv6ExtHdrFragment, rx)
1479 def test_bunch_of_fragments(self):
1480 """ valid fragments followed by rogue fragments and atomic fragment"""
1481 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1482 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) /
1483 ICMPv6EchoRequest()/Raw('X' * 1600))
1484 frags = fragment_rfc8200(pkt, 1, 400)
1485 self.send_and_expect(self.pg0, frags, self.pg0, n_rx=1)
1487 inc_frag = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1488 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) /
1489 IPv6ExtHdrFragment(id=1, nh=58, offset=608)/Raw('X'*308))
1491 self.send_and_assert_no_replies(self.pg0, inc_frag*604)
1493 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1494 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) /
1495 IPv6ExtHdrFragment(id=1)/ICMPv6EchoRequest())
1496 rx = self.send_and_expect(self.pg0, [pkt], self.pg0)
1497 self.assertNotIn(IPv6ExtHdrFragment, rx)
1500 class TestIPv6MWReassembly(VppTestCase):
1501 """ IPv6 Reassembly (multiple workers) """
1502 vpp_worker_count = 3
1505 def setUpClass(cls):
1506 super(TestIPv6MWReassembly, cls).setUpClass()
1508 cls.create_pg_interfaces(range(cls.vpp_worker_count+1))
1509 cls.src_if = cls.pg0
1510 cls.send_ifs = cls.pg_interfaces[:-1]
1511 cls.dst_if = cls.pg_interfaces[-1]
1513 # setup all interfaces
1514 for i in cls.pg_interfaces:
1519 # packets sizes reduced here because we are generating packets without
1520 # Ethernet headers, which are added later (diff fragments go via
1521 # different interfaces)
1522 cls.packet_sizes = [64-len(Ether()), 512-len(Ether()),
1523 1518-len(Ether()), 9018-len(Ether())]
1524 cls.padding = " abcdefghijklmn"
1525 cls.create_stream(cls.packet_sizes)
1526 cls.create_fragments()
1529 def tearDownClass(cls):
1530 super(TestIPv6MWReassembly, cls).tearDownClass()
1533 """ Test setup - force timeout on existing reassemblies """
1534 super(TestIPv6MWReassembly, self).setUp()
1535 for intf in self.send_ifs:
1536 self.vapi.ip_reassembly_enable_disable(
1537 sw_if_index=intf.sw_if_index, enable_ip6=True)
1538 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1539 max_reassembly_length=1000,
1540 expire_walk_interval_ms=10, is_ip6=1)
1541 self.virtual_sleep(.25)
1542 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1543 max_reassembly_length=1000,
1544 expire_walk_interval_ms=1000, is_ip6=1)
1547 super(TestIPv6MWReassembly, self).tearDown()
1549 def show_commands_at_teardown(self):
1550 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1551 self.logger.debug(self.vapi.ppcli("show buffers"))
1554 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
1555 """Create input packet stream
1557 :param list packet_sizes: Required packet sizes.
1559 for i in range(0, packet_count):
1560 info = cls.create_packet_info(cls.src_if, cls.src_if)
1561 payload = cls.info_to_payload(info)
1562 p = (IPv6(src=cls.src_if.remote_ip6,
1563 dst=cls.dst_if.remote_ip6) /
1564 UDP(sport=1234, dport=5678) /
1566 size = packet_sizes[(i // 2) % len(packet_sizes)]
1567 cls.extend_packet(p, size, cls.padding)
1571 def create_fragments(cls):
1572 infos = cls._packet_infos
1574 for index, info in infos.items():
1576 # cls.logger.debug(ppp("Packet:",
1577 # p.__class__(scapy.compat.raw(p))))
1578 fragments_400 = fragment_rfc8200(p, index, 400)
1579 cls.pkt_infos.append((index, fragments_400))
1580 cls.fragments_400 = [
1581 x for (_, frags) in cls.pkt_infos for x in frags]
1582 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, " %
1583 (len(infos), len(cls.fragments_400)))
1585 def verify_capture(self, capture, dropped_packet_indexes=[]):
1586 """Verify captured packet strea .
1588 :param list capture: Captured packet stream.
1592 for packet in capture:
1594 self.logger.debug(ppp("Got packet:", packet))
1597 payload_info = self.payload_to_info(packet[Raw])
1598 packet_index = payload_info.index
1600 packet_index not in dropped_packet_indexes,
1601 ppp("Packet received, but should be dropped:", packet))
1602 if packet_index in seen:
1603 raise Exception(ppp("Duplicate packet received", packet))
1604 seen.add(packet_index)
1605 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
1606 info = self._packet_infos[packet_index]
1607 self.assertTrue(info is not None)
1608 self.assertEqual(packet_index, info.index)
1609 saved_packet = info.data
1610 self.assertEqual(ip.src, saved_packet[IPv6].src)
1611 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
1612 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1614 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1616 for index in self._packet_infos:
1617 self.assertTrue(index in seen or index in dropped_packet_indexes,
1618 "Packet with packet_index %d not received" % index)
1620 def send_packets(self, packets):
1621 for counter in range(self.vpp_worker_count):
1622 if 0 == len(packets[counter]):
1624 send_if = self.send_ifs[counter]
1626 (Ether(dst=send_if.local_mac, src=send_if.remote_mac) / x
1627 for x in packets[counter]),
1631 def test_worker_conflict(self):
1632 """ 1st and FO=0 fragments on different workers """
1634 # in first wave we send fragments which don't start at offset 0
1635 # then we send fragments with offset 0 on a different thread
1636 # then the rest of packets on a random thread
1637 first_packets = [[] for n in range(self.vpp_worker_count)]
1638 second_packets = [[] for n in range(self.vpp_worker_count)]
1639 rest_of_packets = [[] for n in range(self.vpp_worker_count)]
1640 for (_, p) in self.pkt_infos:
1641 wi = randrange(self.vpp_worker_count)
1642 second_packets[wi].append(p[0])
1647 wi2 = randrange(self.vpp_worker_count)
1648 first_packets[wi2].append(p[1])
1649 wi3 = randrange(self.vpp_worker_count)
1650 rest_of_packets[wi3].extend(p[2:])
1652 self.pg_enable_capture()
1653 self.send_packets(first_packets)
1654 self.send_packets(second_packets)
1655 self.send_packets(rest_of_packets)
1657 packets = self.dst_if.get_capture(len(self.pkt_infos))
1658 self.verify_capture(packets)
1659 for send_if in self.send_ifs:
1660 send_if.assert_nothing_captured()
1662 self.logger.debug(self.vapi.ppcli("show trace"))
1663 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1664 self.logger.debug(self.vapi.ppcli("show buffers"))
1665 self.vapi.cli("clear trace")
1667 self.pg_enable_capture()
1668 self.send_packets(first_packets)
1669 self.send_packets(second_packets)
1670 self.send_packets(rest_of_packets)
1672 packets = self.dst_if.get_capture(len(self.pkt_infos))
1673 self.verify_capture(packets)
1674 for send_if in self.send_ifs:
1675 send_if.assert_nothing_captured()
1678 class TestIPv6SVReassembly(VppTestCase):
1679 """ IPv6 Shallow Virtual Reassembly """
1682 def setUpClass(cls):
1683 super(TestIPv6SVReassembly, cls).setUpClass()
1685 cls.create_pg_interfaces([0, 1])
1686 cls.src_if = cls.pg0
1687 cls.dst_if = cls.pg1
1689 # setup all interfaces
1690 for i in cls.pg_interfaces:
1696 """ Test setup - force timeout on existing reassemblies """
1697 super(TestIPv6SVReassembly, self).setUp()
1698 self.vapi.ip_reassembly_enable_disable(
1699 sw_if_index=self.src_if.sw_if_index, enable_ip6=True,
1700 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
1701 self.vapi.ip_reassembly_set(
1702 timeout_ms=0, max_reassemblies=1000,
1703 max_reassembly_length=1000,
1704 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1705 expire_walk_interval_ms=10, is_ip6=1)
1706 self.virtual_sleep(.25)
1707 self.vapi.ip_reassembly_set(
1708 timeout_ms=1000000, max_reassemblies=1000,
1709 max_reassembly_length=1000,
1710 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1711 expire_walk_interval_ms=10000, is_ip6=1)
1714 super(TestIPv6SVReassembly, self).tearDown()
1715 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1716 self.logger.debug(self.vapi.ppcli("show buffers"))
1718 def test_basic(self):
1719 """ basic reassembly """
1723 while len(payload) < payload_len:
1724 payload += "%u " % counter
1727 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1728 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1729 UDP(sport=1234, dport=5678) /
1731 fragments = fragment_rfc8200(p, 1, payload_len/4)
1733 # send fragment #2 - should be cached inside reassembly
1734 self.pg_enable_capture()
1735 self.src_if.add_stream(fragments[1])
1737 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1738 self.logger.debug(self.vapi.ppcli("show buffers"))
1739 self.logger.debug(self.vapi.ppcli("show trace"))
1740 self.dst_if.assert_nothing_captured()
1742 # send fragment #1 - reassembly is finished now and both fragments
1744 self.pg_enable_capture()
1745 self.src_if.add_stream(fragments[0])
1747 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1748 self.logger.debug(self.vapi.ppcli("show buffers"))
1749 self.logger.debug(self.vapi.ppcli("show trace"))
1750 c = self.dst_if.get_capture(2)
1751 for sent, recvd in zip([fragments[1], fragments[0]], c):
1752 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1753 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1754 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1756 # send rest of fragments - should be immediately forwarded
1757 self.pg_enable_capture()
1758 self.src_if.add_stream(fragments[2:])
1760 c = self.dst_if.get_capture(len(fragments[2:]))
1761 for sent, recvd in zip(fragments[2:], c):
1762 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1763 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1764 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1766 def test_verify_clear_trace_mid_reassembly(self):
1767 """ verify clear trace works mid-reassembly """
1771 while len(payload) < payload_len:
1772 payload += "%u " % counter
1775 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1776 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1777 UDP(sport=1234, dport=5678) /
1779 fragments = fragment_rfc8200(p, 1, payload_len/4)
1781 self.pg_enable_capture()
1782 self.src_if.add_stream(fragments[1])
1785 self.logger.debug(self.vapi.cli("show trace"))
1786 self.vapi.cli("clear trace")
1788 self.pg_enable_capture()
1789 self.src_if.add_stream(fragments[0])
1791 self.dst_if.get_capture(2)
1793 self.logger.debug(self.vapi.cli("show trace"))
1794 self.vapi.cli("clear trace")
1796 self.pg_enable_capture()
1797 self.src_if.add_stream(fragments[2:])
1799 self.dst_if.get_capture(len(fragments[2:]))
1801 def test_timeout(self):
1802 """ reassembly timeout """
1806 while len(payload) < payload_len:
1807 payload += "%u " % counter
1810 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1811 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1812 UDP(sport=1234, dport=5678) /
1814 fragments = fragment_rfc8200(p, 1, payload_len/4)
1816 self.vapi.ip_reassembly_set(
1817 timeout_ms=100, max_reassemblies=1000,
1818 max_reassembly_length=1000,
1819 expire_walk_interval_ms=50,
1821 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
1823 # send fragments #2 and #1 - should be forwarded
1824 self.pg_enable_capture()
1825 self.src_if.add_stream(fragments[0:2])
1827 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
1828 self.logger.debug(self.vapi.ppcli("show buffers"))
1829 self.logger.debug(self.vapi.ppcli("show trace"))
1830 c = self.dst_if.get_capture(2)
1831 for sent, recvd in zip([fragments[1], fragments[0]], c):
1832 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1833 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1834 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1837 self.virtual_sleep(.25, "wait before sending rest of fragments")
1839 # send rest of fragments - shouldn't be forwarded
1840 self.pg_enable_capture()
1841 self.src_if.add_stream(fragments[2:])
1843 self.dst_if.assert_nothing_captured()
1846 """ reassembly reuses LRU element """
1848 self.vapi.ip_reassembly_set(
1849 timeout_ms=1000000, max_reassemblies=1,
1850 max_reassembly_length=1000,
1851 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1852 is_ip6=1, expire_walk_interval_ms=10000)
1857 while len(payload) < payload_len:
1858 payload += "%u " % counter
1864 for i in range(packet_count)
1865 for p in (Ether(dst=self.src_if.local_mac,
1866 src=self.src_if.remote_mac) /
1867 IPv6(src=self.src_if.remote_ip6,
1868 dst=self.dst_if.remote_ip6) /
1869 UDP(sport=1234, dport=5678) /
1871 for f in fragment_rfc8200(p, i, payload_len/4)]
1873 self.pg_enable_capture()
1874 self.src_if.add_stream(fragments)
1876 c = self.dst_if.get_capture(len(fragments))
1877 for sent, recvd in zip(fragments, c):
1878 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1879 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1880 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1882 def test_one_fragment(self):
1883 """ whole packet in one fragment processed independently """
1884 pkt = (Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) /
1885 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1886 ICMPv6EchoRequest()/Raw('X' * 1600))
1887 frags = fragment_rfc8200(pkt, 1, 400)
1889 # send a fragment with known id
1890 self.send_and_expect(self.src_if, [frags[0]], self.dst_if)
1892 # send an atomic fragment with same id - should be reassembled
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 IPv6ExtHdrFragment(id=1)/ICMPv6EchoRequest())
1896 rx = self.send_and_expect(self.src_if, [pkt], self.dst_if)
1898 # now forward packets matching original reassembly, should still work
1899 rx = self.send_and_expect(self.src_if, frags[1:], self.dst_if)
1901 def test_bunch_of_fragments(self):
1902 """ valid fragments followed by rogue fragments and atomic fragment"""
1903 pkt = (Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) /
1904 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1905 ICMPv6EchoRequest()/Raw('X' * 1600))
1906 frags = fragment_rfc8200(pkt, 1, 400)
1907 rx = self.send_and_expect(self.src_if, frags, self.dst_if)
1909 rogue = (Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) /
1910 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1911 IPv6ExtHdrFragment(id=1, nh=58, offset=608)/Raw('X'*308))
1913 self.send_and_expect(self.src_if, rogue*604, self.dst_if)
1915 pkt = (Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) /
1916 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1917 IPv6ExtHdrFragment(id=1)/ICMPv6EchoRequest())
1918 rx = self.send_and_expect(self.src_if, [pkt], self.dst_if)
1921 class TestIPv4ReassemblyLocalNode(VppTestCase):
1922 """ IPv4 Reassembly for packets coming to ip4-local node """
1925 def setUpClass(cls):
1926 super(TestIPv4ReassemblyLocalNode, cls).setUpClass()
1928 cls.create_pg_interfaces([0])
1929 cls.src_dst_if = cls.pg0
1931 # setup all interfaces
1932 for i in cls.pg_interfaces:
1937 cls.padding = " abcdefghijklmn"
1939 cls.create_fragments()
1942 def tearDownClass(cls):
1943 super(TestIPv4ReassemblyLocalNode, cls).tearDownClass()
1946 """ Test setup - force timeout on existing reassemblies """
1947 super(TestIPv4ReassemblyLocalNode, self).setUp()
1948 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1949 max_reassembly_length=1000,
1950 expire_walk_interval_ms=10)
1951 self.virtual_sleep(.25)
1952 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1953 max_reassembly_length=1000,
1954 expire_walk_interval_ms=10000)
1957 super(TestIPv4ReassemblyLocalNode, self).tearDown()
1959 def show_commands_at_teardown(self):
1960 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
1961 self.logger.debug(self.vapi.ppcli("show buffers"))
1964 def create_stream(cls, packet_count=test_packet_count):
1965 """Create input packet stream for defined interface.
1967 :param list packet_sizes: Required packet sizes.
1969 for i in range(0, packet_count):
1970 info = cls.create_packet_info(cls.src_dst_if, cls.src_dst_if)
1971 payload = cls.info_to_payload(info)
1972 p = (Ether(dst=cls.src_dst_if.local_mac,
1973 src=cls.src_dst_if.remote_mac) /
1974 IP(id=info.index, src=cls.src_dst_if.remote_ip4,
1975 dst=cls.src_dst_if.local_ip4) /
1976 ICMP(type='echo-request', id=1234) /
1978 cls.extend_packet(p, 1518, cls.padding)
1982 def create_fragments(cls):
1983 infos = cls._packet_infos
1985 for index, info in infos.items():
1987 # cls.logger.debug(ppp("Packet:",
1988 # p.__class__(scapy.compat.raw(p))))
1989 fragments_300 = fragment_rfc791(p, 300)
1990 cls.pkt_infos.append((index, fragments_300))
1991 cls.fragments_300 = [x for (_, frags) in cls.pkt_infos for x in frags]
1992 cls.logger.debug("Fragmented %s packets into %s 300-byte fragments" %
1993 (len(infos), len(cls.fragments_300)))
1995 def verify_capture(self, capture):
1996 """Verify captured packet stream.
1998 :param list capture: Captured packet stream.
2002 for packet in capture:
2004 self.logger.debug(ppp("Got packet:", packet))
2007 payload_info = self.payload_to_info(packet[Raw])
2008 packet_index = payload_info.index
2009 if packet_index in seen:
2010 raise Exception(ppp("Duplicate packet received", packet))
2011 seen.add(packet_index)
2012 self.assertEqual(payload_info.dst, self.src_dst_if.sw_if_index)
2013 info = self._packet_infos[packet_index]
2014 self.assertIsNotNone(info)
2015 self.assertEqual(packet_index, info.index)
2016 saved_packet = info.data
2017 self.assertEqual(ip.src, saved_packet[IP].dst)
2018 self.assertEqual(ip.dst, saved_packet[IP].src)
2019 self.assertEqual(icmp.type, 0) # echo reply
2020 self.assertEqual(icmp.id, saved_packet[ICMP].id)
2021 self.assertEqual(icmp.payload, saved_packet[ICMP].payload)
2023 self.logger.error(ppp("Unexpected or invalid packet:", packet))
2025 for index in self._packet_infos:
2026 self.assertIn(index, seen,
2027 "Packet with packet_index %d not received" % index)
2029 def test_reassembly(self):
2030 """ basic reassembly """
2032 self.pg_enable_capture()
2033 self.src_dst_if.add_stream(self.fragments_300)
2036 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
2037 self.verify_capture(packets)
2039 # run it all again to verify correctness
2040 self.pg_enable_capture()
2041 self.src_dst_if.add_stream(self.fragments_300)
2044 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
2045 self.verify_capture(packets)
2048 class TestFIFReassembly(VppTestCase):
2049 """ Fragments in fragments reassembly """
2052 def setUpClass(cls):
2053 super(TestFIFReassembly, cls).setUpClass()
2055 cls.create_pg_interfaces([0, 1])
2056 cls.src_if = cls.pg0
2057 cls.dst_if = cls.pg1
2058 for i in cls.pg_interfaces:
2065 cls.packet_sizes = [64, 512, 1518, 9018]
2066 cls.padding = " abcdefghijklmn"
2069 def tearDownClass(cls):
2070 super(TestFIFReassembly, cls).tearDownClass()
2073 """ Test setup - force timeout on existing reassemblies """
2074 super(TestFIFReassembly, self).setUp()
2075 self.vapi.ip_reassembly_enable_disable(
2076 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
2078 self.vapi.ip_reassembly_enable_disable(
2079 sw_if_index=self.dst_if.sw_if_index, enable_ip4=True,
2081 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
2082 max_reassembly_length=1000,
2083 expire_walk_interval_ms=10)
2084 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
2085 max_reassembly_length=1000,
2086 expire_walk_interval_ms=10, is_ip6=1)
2087 self.virtual_sleep(.25)
2088 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
2089 max_reassembly_length=1000,
2090 expire_walk_interval_ms=10000)
2091 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
2092 max_reassembly_length=1000,
2093 expire_walk_interval_ms=10000, is_ip6=1)
2096 super(TestFIFReassembly, self).tearDown()
2098 def show_commands_at_teardown(self):
2099 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
2100 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
2101 self.logger.debug(self.vapi.ppcli("show buffers"))
2103 def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):
2104 """Verify captured packet stream.
2106 :param list capture: Captured packet stream.
2110 for packet in capture:
2112 self.logger.debug(ppp("Got packet:", packet))
2113 ip = packet[ip_class]
2115 payload_info = self.payload_to_info(packet[Raw])
2116 packet_index = payload_info.index
2118 packet_index not in dropped_packet_indexes,
2119 ppp("Packet received, but should be dropped:", packet))
2120 if packet_index in seen:
2121 raise Exception(ppp("Duplicate packet received", packet))
2122 seen.add(packet_index)
2123 self.assertEqual(payload_info.dst, self.dst_if.sw_if_index)
2124 info = self._packet_infos[packet_index]
2125 self.assertTrue(info is not None)
2126 self.assertEqual(packet_index, info.index)
2127 saved_packet = info.data
2128 self.assertEqual(ip.src, saved_packet[ip_class].src)
2129 self.assertEqual(ip.dst, saved_packet[ip_class].dst)
2130 self.assertEqual(udp.payload, saved_packet[UDP].payload)
2132 self.logger.error(ppp("Unexpected or invalid packet:", packet))
2134 for index in self._packet_infos:
2135 self.assertTrue(index in seen or index in dropped_packet_indexes,
2136 "Packet with packet_index %d not received" % index)
2138 def test_fif4(self):
2139 """ Fragments in fragments (4o4) """
2141 # TODO this should be ideally in setUpClass, but then we hit a bug
2142 # with VppIpRoute incorrectly reporting it's present when it's not
2143 # so we need to manually remove the vpp config, thus we cannot have
2144 # it shared for multiple test cases
2145 self.tun_ip4 = "1.1.1.2"
2147 self.gre4 = VppGreInterface(self, self.src_if.local_ip4, self.tun_ip4)
2148 self.gre4.add_vpp_config()
2149 self.gre4.admin_up()
2150 self.gre4.config_ip4()
2152 self.vapi.ip_reassembly_enable_disable(
2153 sw_if_index=self.gre4.sw_if_index, enable_ip4=True)
2155 self.route4 = VppIpRoute(self, self.tun_ip4, 32,
2156 [VppRoutePath(self.src_if.remote_ip4,
2157 self.src_if.sw_if_index)])
2158 self.route4.add_vpp_config()
2160 self.reset_packet_infos()
2161 for i in range(test_packet_count):
2162 info = self.create_packet_info(self.src_if, self.dst_if)
2163 payload = self.info_to_payload(info)
2164 # Ethernet header here is only for size calculation, thus it
2165 # doesn't matter how it's initialized. This is to ensure that
2166 # reassembled packet is not > 9000 bytes, so that it's not dropped
2168 IP(id=i, src=self.src_if.remote_ip4,
2169 dst=self.dst_if.remote_ip4) /
2170 UDP(sport=1234, dport=5678) /
2172 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
2173 self.extend_packet(p, size, self.padding)
2174 info.data = p[IP] # use only IP part, without ethernet header
2176 fragments = [x for _, p in self._packet_infos.items()
2177 for x in fragment_rfc791(p.data, 400)]
2179 encapped_fragments = \
2180 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
2181 IP(src=self.tun_ip4, dst=self.src_if.local_ip4) /
2186 fragmented_encapped_fragments = \
2187 [x for p in encapped_fragments
2188 for x in fragment_rfc791(p, 200)]
2190 self.src_if.add_stream(fragmented_encapped_fragments)
2192 self.pg_enable_capture(self.pg_interfaces)
2195 self.src_if.assert_nothing_captured()
2196 packets = self.dst_if.get_capture(len(self._packet_infos))
2197 self.verify_capture(packets, IP)
2199 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
2200 # so that it's query_vpp_config() works as it should
2201 self.gre4.remove_vpp_config()
2202 self.logger.debug(self.vapi.ppcli("show interface"))
2204 def test_fif6(self):
2205 """ Fragments in fragments (6o6) """
2206 # TODO this should be ideally in setUpClass, but then we hit a bug
2207 # with VppIpRoute incorrectly reporting it's present when it's not
2208 # so we need to manually remove the vpp config, thus we cannot have
2209 # it shared for multiple test cases
2210 self.tun_ip6 = "1002::1"
2212 self.gre6 = VppGreInterface(self, self.src_if.local_ip6, self.tun_ip6)
2213 self.gre6.add_vpp_config()
2214 self.gre6.admin_up()
2215 self.gre6.config_ip6()
2217 self.vapi.ip_reassembly_enable_disable(
2218 sw_if_index=self.gre6.sw_if_index, enable_ip6=True)
2220 self.route6 = VppIpRoute(self, self.tun_ip6, 128,
2222 self.src_if.remote_ip6,
2223 self.src_if.sw_if_index)])
2224 self.route6.add_vpp_config()
2226 self.reset_packet_infos()
2227 for i in range(test_packet_count):
2228 info = self.create_packet_info(self.src_if, self.dst_if)
2229 payload = self.info_to_payload(info)
2230 # Ethernet header here is only for size calculation, thus it
2231 # doesn't matter how it's initialized. This is to ensure that
2232 # reassembled packet is not > 9000 bytes, so that it's not dropped
2234 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
2235 UDP(sport=1234, dport=5678) /
2237 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
2238 self.extend_packet(p, size, self.padding)
2239 info.data = p[IPv6] # use only IPv6 part, without ethernet header
2241 fragments = [x for _, i in self._packet_infos.items()
2242 for x in fragment_rfc8200(
2243 i.data, i.index, 400)]
2245 encapped_fragments = \
2246 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
2247 IPv6(src=self.tun_ip6, dst=self.src_if.local_ip6) /
2252 fragmented_encapped_fragments = \
2253 [x for p in encapped_fragments for x in (
2256 2 * len(self._packet_infos) + p[IPv6ExtHdrFragment].id,
2258 if IPv6ExtHdrFragment in p else [p]
2262 self.src_if.add_stream(fragmented_encapped_fragments)
2264 self.pg_enable_capture(self.pg_interfaces)
2267 self.src_if.assert_nothing_captured()
2268 packets = self.dst_if.get_capture(len(self._packet_infos))
2269 self.verify_capture(packets, IPv6)
2271 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
2272 # so that it's query_vpp_config() works as it should
2273 self.gre6.remove_vpp_config()
2276 if __name__ == '__main__':
2277 unittest.main(testRunner=VppTestRunner)