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], self.pg0)
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)
1459 class TestIPv6MWReassembly(VppTestCase):
1460 """ IPv6 Reassembly (multiple workers) """
1461 vpp_worker_count = 3
1464 def setUpClass(cls):
1465 super(TestIPv6MWReassembly, cls).setUpClass()
1467 cls.create_pg_interfaces(range(cls.vpp_worker_count+1))
1468 cls.src_if = cls.pg0
1469 cls.send_ifs = cls.pg_interfaces[:-1]
1470 cls.dst_if = cls.pg_interfaces[-1]
1472 # setup all interfaces
1473 for i in cls.pg_interfaces:
1478 # packets sizes reduced here because we are generating packets without
1479 # Ethernet headers, which are added later (diff fragments go via
1480 # different interfaces)
1481 cls.packet_sizes = [64-len(Ether()), 512-len(Ether()),
1482 1518-len(Ether()), 9018-len(Ether())]
1483 cls.padding = " abcdefghijklmn"
1484 cls.create_stream(cls.packet_sizes)
1485 cls.create_fragments()
1488 def tearDownClass(cls):
1489 super(TestIPv6MWReassembly, cls).tearDownClass()
1492 """ Test setup - force timeout on existing reassemblies """
1493 super(TestIPv6MWReassembly, self).setUp()
1494 for intf in self.send_ifs:
1495 self.vapi.ip_reassembly_enable_disable(
1496 sw_if_index=intf.sw_if_index, enable_ip6=True)
1497 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1498 max_reassembly_length=1000,
1499 expire_walk_interval_ms=10, is_ip6=1)
1500 self.virtual_sleep(.25)
1501 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1502 max_reassembly_length=1000,
1503 expire_walk_interval_ms=1000, is_ip6=1)
1506 super(TestIPv6MWReassembly, self).tearDown()
1508 def show_commands_at_teardown(self):
1509 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1510 self.logger.debug(self.vapi.ppcli("show buffers"))
1513 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
1514 """Create input packet stream
1516 :param list packet_sizes: Required packet sizes.
1518 for i in range(0, packet_count):
1519 info = cls.create_packet_info(cls.src_if, cls.src_if)
1520 payload = cls.info_to_payload(info)
1521 p = (IPv6(src=cls.src_if.remote_ip6,
1522 dst=cls.dst_if.remote_ip6) /
1523 UDP(sport=1234, dport=5678) /
1525 size = packet_sizes[(i // 2) % len(packet_sizes)]
1526 cls.extend_packet(p, size, cls.padding)
1530 def create_fragments(cls):
1531 infos = cls._packet_infos
1533 for index, info in infos.items():
1535 # cls.logger.debug(ppp("Packet:",
1536 # p.__class__(scapy.compat.raw(p))))
1537 fragments_400 = fragment_rfc8200(p, index, 400)
1538 cls.pkt_infos.append((index, fragments_400))
1539 cls.fragments_400 = [
1540 x for (_, frags) in cls.pkt_infos for x in frags]
1541 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, " %
1542 (len(infos), len(cls.fragments_400)))
1544 def verify_capture(self, capture, dropped_packet_indexes=[]):
1545 """Verify captured packet strea .
1547 :param list capture: Captured packet stream.
1551 for packet in capture:
1553 self.logger.debug(ppp("Got packet:", packet))
1556 payload_info = self.payload_to_info(packet[Raw])
1557 packet_index = payload_info.index
1559 packet_index not in dropped_packet_indexes,
1560 ppp("Packet received, but should be dropped:", packet))
1561 if packet_index in seen:
1562 raise Exception(ppp("Duplicate packet received", packet))
1563 seen.add(packet_index)
1564 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
1565 info = self._packet_infos[packet_index]
1566 self.assertTrue(info is not None)
1567 self.assertEqual(packet_index, info.index)
1568 saved_packet = info.data
1569 self.assertEqual(ip.src, saved_packet[IPv6].src)
1570 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
1571 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1573 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1575 for index in self._packet_infos:
1576 self.assertTrue(index in seen or index in dropped_packet_indexes,
1577 "Packet with packet_index %d not received" % index)
1579 def send_packets(self, packets):
1580 for counter in range(self.vpp_worker_count):
1581 if 0 == len(packets[counter]):
1583 send_if = self.send_ifs[counter]
1585 (Ether(dst=send_if.local_mac, src=send_if.remote_mac) / x
1586 for x in packets[counter]),
1590 def test_worker_conflict(self):
1591 """ 1st and FO=0 fragments on different workers """
1593 # in first wave we send fragments which don't start at offset 0
1594 # then we send fragments with offset 0 on a different thread
1595 # then the rest of packets on a random thread
1596 first_packets = [[] for n in range(self.vpp_worker_count)]
1597 second_packets = [[] for n in range(self.vpp_worker_count)]
1598 rest_of_packets = [[] for n in range(self.vpp_worker_count)]
1599 for (_, p) in self.pkt_infos:
1600 wi = randrange(self.vpp_worker_count)
1601 second_packets[wi].append(p[0])
1606 wi2 = randrange(self.vpp_worker_count)
1607 first_packets[wi2].append(p[1])
1608 wi3 = randrange(self.vpp_worker_count)
1609 rest_of_packets[wi3].extend(p[2:])
1611 self.pg_enable_capture()
1612 self.send_packets(first_packets)
1613 self.send_packets(second_packets)
1614 self.send_packets(rest_of_packets)
1616 packets = self.dst_if.get_capture(len(self.pkt_infos))
1617 self.verify_capture(packets)
1618 for send_if in self.send_ifs:
1619 send_if.assert_nothing_captured()
1621 self.logger.debug(self.vapi.ppcli("show trace"))
1622 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1623 self.logger.debug(self.vapi.ppcli("show buffers"))
1624 self.vapi.cli("clear trace")
1626 self.pg_enable_capture()
1627 self.send_packets(first_packets)
1628 self.send_packets(second_packets)
1629 self.send_packets(rest_of_packets)
1631 packets = self.dst_if.get_capture(len(self.pkt_infos))
1632 self.verify_capture(packets)
1633 for send_if in self.send_ifs:
1634 send_if.assert_nothing_captured()
1637 class TestIPv6SVReassembly(VppTestCase):
1638 """ IPv6 Shallow Virtual Reassembly """
1641 def setUpClass(cls):
1642 super(TestIPv6SVReassembly, cls).setUpClass()
1644 cls.create_pg_interfaces([0, 1])
1645 cls.src_if = cls.pg0
1646 cls.dst_if = cls.pg1
1648 # setup all interfaces
1649 for i in cls.pg_interfaces:
1655 """ Test setup - force timeout on existing reassemblies """
1656 super(TestIPv6SVReassembly, self).setUp()
1657 self.vapi.ip_reassembly_enable_disable(
1658 sw_if_index=self.src_if.sw_if_index, enable_ip6=True,
1659 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
1660 self.vapi.ip_reassembly_set(
1661 timeout_ms=0, max_reassemblies=1000,
1662 max_reassembly_length=1000,
1663 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1664 expire_walk_interval_ms=10, is_ip6=1)
1665 self.virtual_sleep(.25)
1666 self.vapi.ip_reassembly_set(
1667 timeout_ms=1000000, max_reassemblies=1000,
1668 max_reassembly_length=1000,
1669 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1670 expire_walk_interval_ms=10000, is_ip6=1)
1673 super(TestIPv6SVReassembly, self).tearDown()
1674 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1675 self.logger.debug(self.vapi.ppcli("show buffers"))
1677 def test_basic(self):
1678 """ basic reassembly """
1682 while len(payload) < payload_len:
1683 payload += "%u " % counter
1686 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1687 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1688 UDP(sport=1234, dport=5678) /
1690 fragments = fragment_rfc8200(p, 1, payload_len/4)
1692 # send fragment #2 - should be cached inside reassembly
1693 self.pg_enable_capture()
1694 self.src_if.add_stream(fragments[1])
1696 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1697 self.logger.debug(self.vapi.ppcli("show buffers"))
1698 self.logger.debug(self.vapi.ppcli("show trace"))
1699 self.dst_if.assert_nothing_captured()
1701 # send fragment #1 - reassembly is finished now and both fragments
1703 self.pg_enable_capture()
1704 self.src_if.add_stream(fragments[0])
1706 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1707 self.logger.debug(self.vapi.ppcli("show buffers"))
1708 self.logger.debug(self.vapi.ppcli("show trace"))
1709 c = self.dst_if.get_capture(2)
1710 for sent, recvd in zip([fragments[1], fragments[0]], c):
1711 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1712 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1713 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1715 # send rest of fragments - should be immediately forwarded
1716 self.pg_enable_capture()
1717 self.src_if.add_stream(fragments[2:])
1719 c = self.dst_if.get_capture(len(fragments[2:]))
1720 for sent, recvd in zip(fragments[2:], c):
1721 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1722 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1723 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1725 def test_verify_clear_trace_mid_reassembly(self):
1726 """ verify clear trace works mid-reassembly """
1730 while len(payload) < payload_len:
1731 payload += "%u " % counter
1734 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1735 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1736 UDP(sport=1234, dport=5678) /
1738 fragments = fragment_rfc8200(p, 1, payload_len/4)
1740 self.pg_enable_capture()
1741 self.src_if.add_stream(fragments[1])
1744 self.logger.debug(self.vapi.cli("show trace"))
1745 self.vapi.cli("clear trace")
1747 self.pg_enable_capture()
1748 self.src_if.add_stream(fragments[0])
1750 self.dst_if.get_capture(2)
1752 self.logger.debug(self.vapi.cli("show trace"))
1753 self.vapi.cli("clear trace")
1755 self.pg_enable_capture()
1756 self.src_if.add_stream(fragments[2:])
1758 self.dst_if.get_capture(len(fragments[2:]))
1760 def test_timeout(self):
1761 """ reassembly timeout """
1765 while len(payload) < payload_len:
1766 payload += "%u " % counter
1769 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1770 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1771 UDP(sport=1234, dport=5678) /
1773 fragments = fragment_rfc8200(p, 1, payload_len/4)
1775 self.vapi.ip_reassembly_set(
1776 timeout_ms=100, max_reassemblies=1000,
1777 max_reassembly_length=1000,
1778 expire_walk_interval_ms=50,
1780 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
1782 # send fragments #2 and #1 - should be forwarded
1783 self.pg_enable_capture()
1784 self.src_if.add_stream(fragments[0:2])
1786 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
1787 self.logger.debug(self.vapi.ppcli("show buffers"))
1788 self.logger.debug(self.vapi.ppcli("show trace"))
1789 c = self.dst_if.get_capture(2)
1790 for sent, recvd in zip([fragments[1], fragments[0]], c):
1791 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1792 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1793 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1796 self.virtual_sleep(.25, "wait before sending rest of fragments")
1798 # send rest of fragments - shouldn't be forwarded
1799 self.pg_enable_capture()
1800 self.src_if.add_stream(fragments[2:])
1802 self.dst_if.assert_nothing_captured()
1805 """ reassembly reuses LRU element """
1807 self.vapi.ip_reassembly_set(
1808 timeout_ms=1000000, max_reassemblies=1,
1809 max_reassembly_length=1000,
1810 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1811 is_ip6=1, expire_walk_interval_ms=10000)
1816 while len(payload) < payload_len:
1817 payload += "%u " % counter
1823 for i in range(packet_count)
1824 for p in (Ether(dst=self.src_if.local_mac,
1825 src=self.src_if.remote_mac) /
1826 IPv6(src=self.src_if.remote_ip6,
1827 dst=self.dst_if.remote_ip6) /
1828 UDP(sport=1234, dport=5678) /
1830 for f in fragment_rfc8200(p, i, payload_len/4)]
1832 self.pg_enable_capture()
1833 self.src_if.add_stream(fragments)
1835 c = self.dst_if.get_capture(len(fragments))
1836 for sent, recvd in zip(fragments, c):
1837 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1838 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1839 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1842 class TestIPv4ReassemblyLocalNode(VppTestCase):
1843 """ IPv4 Reassembly for packets coming to ip4-local node """
1846 def setUpClass(cls):
1847 super(TestIPv4ReassemblyLocalNode, cls).setUpClass()
1849 cls.create_pg_interfaces([0])
1850 cls.src_dst_if = cls.pg0
1852 # setup all interfaces
1853 for i in cls.pg_interfaces:
1858 cls.padding = " abcdefghijklmn"
1860 cls.create_fragments()
1863 def tearDownClass(cls):
1864 super(TestIPv4ReassemblyLocalNode, cls).tearDownClass()
1867 """ Test setup - force timeout on existing reassemblies """
1868 super(TestIPv4ReassemblyLocalNode, self).setUp()
1869 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1870 max_reassembly_length=1000,
1871 expire_walk_interval_ms=10)
1872 self.virtual_sleep(.25)
1873 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1874 max_reassembly_length=1000,
1875 expire_walk_interval_ms=10000)
1878 super(TestIPv4ReassemblyLocalNode, self).tearDown()
1880 def show_commands_at_teardown(self):
1881 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
1882 self.logger.debug(self.vapi.ppcli("show buffers"))
1885 def create_stream(cls, packet_count=test_packet_count):
1886 """Create input packet stream for defined interface.
1888 :param list packet_sizes: Required packet sizes.
1890 for i in range(0, packet_count):
1891 info = cls.create_packet_info(cls.src_dst_if, cls.src_dst_if)
1892 payload = cls.info_to_payload(info)
1893 p = (Ether(dst=cls.src_dst_if.local_mac,
1894 src=cls.src_dst_if.remote_mac) /
1895 IP(id=info.index, src=cls.src_dst_if.remote_ip4,
1896 dst=cls.src_dst_if.local_ip4) /
1897 ICMP(type='echo-request', id=1234) /
1899 cls.extend_packet(p, 1518, cls.padding)
1903 def create_fragments(cls):
1904 infos = cls._packet_infos
1906 for index, info in infos.items():
1908 # cls.logger.debug(ppp("Packet:",
1909 # p.__class__(scapy.compat.raw(p))))
1910 fragments_300 = fragment_rfc791(p, 300)
1911 cls.pkt_infos.append((index, fragments_300))
1912 cls.fragments_300 = [x for (_, frags) in cls.pkt_infos for x in frags]
1913 cls.logger.debug("Fragmented %s packets into %s 300-byte fragments" %
1914 (len(infos), len(cls.fragments_300)))
1916 def verify_capture(self, capture):
1917 """Verify captured packet stream.
1919 :param list capture: Captured packet stream.
1923 for packet in capture:
1925 self.logger.debug(ppp("Got packet:", packet))
1928 payload_info = self.payload_to_info(packet[Raw])
1929 packet_index = payload_info.index
1930 if packet_index in seen:
1931 raise Exception(ppp("Duplicate packet received", packet))
1932 seen.add(packet_index)
1933 self.assertEqual(payload_info.dst, self.src_dst_if.sw_if_index)
1934 info = self._packet_infos[packet_index]
1935 self.assertIsNotNone(info)
1936 self.assertEqual(packet_index, info.index)
1937 saved_packet = info.data
1938 self.assertEqual(ip.src, saved_packet[IP].dst)
1939 self.assertEqual(ip.dst, saved_packet[IP].src)
1940 self.assertEqual(icmp.type, 0) # echo reply
1941 self.assertEqual(icmp.id, saved_packet[ICMP].id)
1942 self.assertEqual(icmp.payload, saved_packet[ICMP].payload)
1944 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1946 for index in self._packet_infos:
1947 self.assertIn(index, seen,
1948 "Packet with packet_index %d not received" % index)
1950 def test_reassembly(self):
1951 """ basic reassembly """
1953 self.pg_enable_capture()
1954 self.src_dst_if.add_stream(self.fragments_300)
1957 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
1958 self.verify_capture(packets)
1960 # run it all again to verify correctness
1961 self.pg_enable_capture()
1962 self.src_dst_if.add_stream(self.fragments_300)
1965 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
1966 self.verify_capture(packets)
1969 class TestFIFReassembly(VppTestCase):
1970 """ Fragments in fragments reassembly """
1973 def setUpClass(cls):
1974 super(TestFIFReassembly, cls).setUpClass()
1976 cls.create_pg_interfaces([0, 1])
1977 cls.src_if = cls.pg0
1978 cls.dst_if = cls.pg1
1979 for i in cls.pg_interfaces:
1986 cls.packet_sizes = [64, 512, 1518, 9018]
1987 cls.padding = " abcdefghijklmn"
1990 def tearDownClass(cls):
1991 super(TestFIFReassembly, cls).tearDownClass()
1994 """ Test setup - force timeout on existing reassemblies """
1995 super(TestFIFReassembly, self).setUp()
1996 self.vapi.ip_reassembly_enable_disable(
1997 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
1999 self.vapi.ip_reassembly_enable_disable(
2000 sw_if_index=self.dst_if.sw_if_index, enable_ip4=True,
2002 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
2003 max_reassembly_length=1000,
2004 expire_walk_interval_ms=10)
2005 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
2006 max_reassembly_length=1000,
2007 expire_walk_interval_ms=10, is_ip6=1)
2008 self.virtual_sleep(.25)
2009 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
2010 max_reassembly_length=1000,
2011 expire_walk_interval_ms=10000)
2012 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
2013 max_reassembly_length=1000,
2014 expire_walk_interval_ms=10000, is_ip6=1)
2017 super(TestFIFReassembly, self).tearDown()
2019 def show_commands_at_teardown(self):
2020 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
2021 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
2022 self.logger.debug(self.vapi.ppcli("show buffers"))
2024 def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):
2025 """Verify captured packet stream.
2027 :param list capture: Captured packet stream.
2031 for packet in capture:
2033 self.logger.debug(ppp("Got packet:", packet))
2034 ip = packet[ip_class]
2036 payload_info = self.payload_to_info(packet[Raw])
2037 packet_index = payload_info.index
2039 packet_index not in dropped_packet_indexes,
2040 ppp("Packet received, but should be dropped:", packet))
2041 if packet_index in seen:
2042 raise Exception(ppp("Duplicate packet received", packet))
2043 seen.add(packet_index)
2044 self.assertEqual(payload_info.dst, self.dst_if.sw_if_index)
2045 info = self._packet_infos[packet_index]
2046 self.assertTrue(info is not None)
2047 self.assertEqual(packet_index, info.index)
2048 saved_packet = info.data
2049 self.assertEqual(ip.src, saved_packet[ip_class].src)
2050 self.assertEqual(ip.dst, saved_packet[ip_class].dst)
2051 self.assertEqual(udp.payload, saved_packet[UDP].payload)
2053 self.logger.error(ppp("Unexpected or invalid packet:", packet))
2055 for index in self._packet_infos:
2056 self.assertTrue(index in seen or index in dropped_packet_indexes,
2057 "Packet with packet_index %d not received" % index)
2059 def test_fif4(self):
2060 """ Fragments in fragments (4o4) """
2062 # TODO this should be ideally in setUpClass, but then we hit a bug
2063 # with VppIpRoute incorrectly reporting it's present when it's not
2064 # so we need to manually remove the vpp config, thus we cannot have
2065 # it shared for multiple test cases
2066 self.tun_ip4 = "1.1.1.2"
2068 self.gre4 = VppGreInterface(self, self.src_if.local_ip4, self.tun_ip4)
2069 self.gre4.add_vpp_config()
2070 self.gre4.admin_up()
2071 self.gre4.config_ip4()
2073 self.vapi.ip_reassembly_enable_disable(
2074 sw_if_index=self.gre4.sw_if_index, enable_ip4=True)
2076 self.route4 = VppIpRoute(self, self.tun_ip4, 32,
2077 [VppRoutePath(self.src_if.remote_ip4,
2078 self.src_if.sw_if_index)])
2079 self.route4.add_vpp_config()
2081 self.reset_packet_infos()
2082 for i in range(test_packet_count):
2083 info = self.create_packet_info(self.src_if, self.dst_if)
2084 payload = self.info_to_payload(info)
2085 # Ethernet header here is only for size calculation, thus it
2086 # doesn't matter how it's initialized. This is to ensure that
2087 # reassembled packet is not > 9000 bytes, so that it's not dropped
2089 IP(id=i, src=self.src_if.remote_ip4,
2090 dst=self.dst_if.remote_ip4) /
2091 UDP(sport=1234, dport=5678) /
2093 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
2094 self.extend_packet(p, size, self.padding)
2095 info.data = p[IP] # use only IP part, without ethernet header
2097 fragments = [x for _, p in self._packet_infos.items()
2098 for x in fragment_rfc791(p.data, 400)]
2100 encapped_fragments = \
2101 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
2102 IP(src=self.tun_ip4, dst=self.src_if.local_ip4) /
2107 fragmented_encapped_fragments = \
2108 [x for p in encapped_fragments
2109 for x in fragment_rfc791(p, 200)]
2111 self.src_if.add_stream(fragmented_encapped_fragments)
2113 self.pg_enable_capture(self.pg_interfaces)
2116 self.src_if.assert_nothing_captured()
2117 packets = self.dst_if.get_capture(len(self._packet_infos))
2118 self.verify_capture(packets, IP)
2120 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
2121 # so that it's query_vpp_config() works as it should
2122 self.gre4.remove_vpp_config()
2123 self.logger.debug(self.vapi.ppcli("show interface"))
2125 def test_fif6(self):
2126 """ Fragments in fragments (6o6) """
2127 # TODO this should be ideally in setUpClass, but then we hit a bug
2128 # with VppIpRoute incorrectly reporting it's present when it's not
2129 # so we need to manually remove the vpp config, thus we cannot have
2130 # it shared for multiple test cases
2131 self.tun_ip6 = "1002::1"
2133 self.gre6 = VppGreInterface(self, self.src_if.local_ip6, self.tun_ip6)
2134 self.gre6.add_vpp_config()
2135 self.gre6.admin_up()
2136 self.gre6.config_ip6()
2138 self.vapi.ip_reassembly_enable_disable(
2139 sw_if_index=self.gre6.sw_if_index, enable_ip6=True)
2141 self.route6 = VppIpRoute(self, self.tun_ip6, 128,
2143 self.src_if.remote_ip6,
2144 self.src_if.sw_if_index)])
2145 self.route6.add_vpp_config()
2147 self.reset_packet_infos()
2148 for i in range(test_packet_count):
2149 info = self.create_packet_info(self.src_if, self.dst_if)
2150 payload = self.info_to_payload(info)
2151 # Ethernet header here is only for size calculation, thus it
2152 # doesn't matter how it's initialized. This is to ensure that
2153 # reassembled packet is not > 9000 bytes, so that it's not dropped
2155 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
2156 UDP(sport=1234, dport=5678) /
2158 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
2159 self.extend_packet(p, size, self.padding)
2160 info.data = p[IPv6] # use only IPv6 part, without ethernet header
2162 fragments = [x for _, i in self._packet_infos.items()
2163 for x in fragment_rfc8200(
2164 i.data, i.index, 400)]
2166 encapped_fragments = \
2167 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
2168 IPv6(src=self.tun_ip6, dst=self.src_if.local_ip6) /
2173 fragmented_encapped_fragments = \
2174 [x for p in encapped_fragments for x in (
2177 2 * len(self._packet_infos) + p[IPv6ExtHdrFragment].id,
2179 if IPv6ExtHdrFragment in p else [p]
2183 self.src_if.add_stream(fragmented_encapped_fragments)
2185 self.pg_enable_capture(self.pg_interfaces)
2188 self.src_if.assert_nothing_captured()
2189 packets = self.dst_if.get_capture(len(self._packet_infos))
2190 self.verify_capture(packets, IPv6)
2192 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
2193 # so that it's query_vpp_config() works as it should
2194 self.gre6.remove_vpp_config()
2197 if __name__ == '__main__':
2198 unittest.main(testRunner=VppTestRunner)