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, icmptypes
12 from scapy.layers.inet6 import HBHOptUnknown, ICMPv6ParamProblem,\
13 ICMPv6TimeExceeded, IPv6, IPv6ExtHdrFragment,\
14 IPv6ExtHdrHopByHop, IPv6ExtHdrDestOpt, PadN, ICMPv6EchoRequest,\
16 from framework import VppTestCase, VppTestRunner
17 from util import ppp, ppc, fragment_rfc791, fragment_rfc8200
18 from vpp_gre_interface import VppGreInterface
19 from vpp_ip import DpoProto
20 from vpp_ip_route import VppIpRoute, VppRoutePath, FibPathProto
21 from vpp_papi import VppEnum
23 # 35 is enough to have >257 400-byte fragments
24 test_packet_count = 35
27 class TestIPv4Reassembly(VppTestCase):
28 """ IPv4 Reassembly """
34 cls.create_pg_interfaces([0, 1])
38 # setup all interfaces
39 for i in cls.pg_interfaces:
45 cls.packet_sizes = [64, 512, 1518, 9018]
46 cls.padding = " abcdefghijklmn"
47 cls.create_stream(cls.packet_sizes)
48 cls.create_fragments()
51 def tearDownClass(cls):
52 super().tearDownClass()
55 """ Test setup - force timeout on existing reassemblies """
57 self.vapi.ip_reassembly_enable_disable(
58 sw_if_index=self.src_if.sw_if_index, enable_ip4=True)
59 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
60 max_reassembly_length=1000,
61 expire_walk_interval_ms=10)
62 self.virtual_sleep(.25)
63 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
64 max_reassembly_length=1000,
65 expire_walk_interval_ms=10000)
68 self.vapi.ip_reassembly_enable_disable(
69 sw_if_index=self.src_if.sw_if_index, enable_ip4=False)
72 def show_commands_at_teardown(self):
73 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
74 self.logger.debug(self.vapi.ppcli("show buffers"))
77 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
78 """Create input packet stream
80 :param list packet_sizes: Required packet sizes.
82 for i in range(0, packet_count):
83 info = cls.create_packet_info(cls.src_if, cls.src_if)
84 payload = cls.info_to_payload(info)
85 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
86 IP(id=info.index, src=cls.src_if.remote_ip4,
87 dst=cls.dst_if.remote_ip4) /
88 UDP(sport=1234, dport=5678) /
90 size = packet_sizes[(i // 2) % len(packet_sizes)]
91 cls.extend_packet(p, size, cls.padding)
95 def create_fragments(cls):
96 infos = cls._packet_infos
98 for index, info in infos.items():
100 # cls.logger.debug(ppp("Packet:",
101 # p.__class__(scapy.compat.raw(p))))
102 fragments_400 = fragment_rfc791(p, 400)
103 fragments_300 = fragment_rfc791(p, 300)
105 x for f in fragments_400 for x in fragment_rfc791(f, 200)]
106 cls.pkt_infos.append(
107 (index, fragments_400, fragments_300, fragments_200))
108 cls.fragments_400 = [
109 x for (_, frags, _, _) in cls.pkt_infos for x in frags]
110 cls.fragments_300 = [
111 x for (_, _, frags, _) in cls.pkt_infos for x in frags]
112 cls.fragments_200 = [
113 x for (_, _, _, frags) in cls.pkt_infos for x in frags]
114 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
115 "%s 300-byte fragments and %s 200-byte fragments" %
116 (len(infos), len(cls.fragments_400),
117 len(cls.fragments_300), len(cls.fragments_200)))
119 def verify_capture(self, capture, dropped_packet_indexes=[]):
120 """Verify captured packet stream.
122 :param list capture: Captured packet stream.
126 for packet in capture:
128 self.logger.debug(ppp("Got packet:", packet))
131 payload_info = self.payload_to_info(packet[Raw])
132 packet_index = payload_info.index
134 packet_index not in dropped_packet_indexes,
135 ppp("Packet received, but should be dropped:", packet))
136 if packet_index in seen:
137 raise Exception(ppp("Duplicate packet received", packet))
138 seen.add(packet_index)
139 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
140 info = self._packet_infos[packet_index]
141 self.assertTrue(info is not None)
142 self.assertEqual(packet_index, info.index)
143 saved_packet = info.data
144 self.assertEqual(ip.src, saved_packet[IP].src)
145 self.assertEqual(ip.dst, saved_packet[IP].dst)
146 self.assertEqual(udp.payload, saved_packet[UDP].payload)
148 self.logger.error(ppp("Unexpected or invalid packet:", packet))
150 for index in self._packet_infos:
151 self.assertTrue(index in seen or index in dropped_packet_indexes,
152 "Packet with packet_index %d not received" % index)
154 def test_reassembly(self):
155 """ basic reassembly """
157 self.pg_enable_capture()
158 self.src_if.add_stream(self.fragments_200)
161 packets = self.dst_if.get_capture(len(self.pkt_infos))
162 self.verify_capture(packets)
163 self.src_if.assert_nothing_captured()
165 # run it all again to verify correctness
166 self.pg_enable_capture()
167 self.src_if.add_stream(self.fragments_200)
170 packets = self.dst_if.get_capture(len(self.pkt_infos))
171 self.verify_capture(packets)
172 self.src_if.assert_nothing_captured()
174 def test_verify_clear_trace_mid_reassembly(self):
175 """ verify clear trace works mid-reassembly """
177 self.pg_enable_capture()
178 self.src_if.add_stream(self.fragments_200[0:-1])
181 self.logger.debug(self.vapi.cli("show trace"))
182 self.vapi.cli("clear trace")
184 self.src_if.add_stream(self.fragments_200[-1])
186 packets = self.dst_if.get_capture(len(self.pkt_infos))
187 self.verify_capture(packets)
189 def test_reversed(self):
190 """ reverse order reassembly """
192 fragments = list(self.fragments_200)
195 self.pg_enable_capture()
196 self.src_if.add_stream(fragments)
199 packets = self.dst_if.get_capture(len(self.packet_infos))
200 self.verify_capture(packets)
201 self.src_if.assert_nothing_captured()
203 # run it all again to verify correctness
204 self.pg_enable_capture()
205 self.src_if.add_stream(fragments)
208 packets = self.dst_if.get_capture(len(self.packet_infos))
209 self.verify_capture(packets)
210 self.src_if.assert_nothing_captured()
212 def test_long_fragment_chain(self):
213 """ long fragment chain """
216 "/err/ip4-full-reassembly-feature/fragment chain too long (drop)"
218 error_cnt = self.statistics.get_err_counter(error_cnt_str)
220 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
221 max_reassembly_length=3,
222 expire_walk_interval_ms=50)
224 p1 = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
225 IP(id=1000, src=self.src_if.remote_ip4,
226 dst=self.dst_if.remote_ip4) /
227 UDP(sport=1234, dport=5678) /
229 p2 = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
230 IP(id=1001, src=self.src_if.remote_ip4,
231 dst=self.dst_if.remote_ip4) /
232 UDP(sport=1234, dport=5678) /
234 frags = fragment_rfc791(p1, 200) + fragment_rfc791(p2, 500)
236 self.pg_enable_capture()
237 self.src_if.add_stream(frags)
240 self.dst_if.get_capture(1)
241 self.assert_error_counter_equal(error_cnt_str, error_cnt + 1)
244 """ fragment length + ip header size > 65535 """
245 self.vapi.cli("clear errors")
246 raw = b'''E\x00\x00\x88,\xf8\x1f\xfe@\x01\x98\x00\xc0\xa8\n-\xc0\xa8\n\
247 \x01\x08\x00\xf0J\xed\xcb\xf1\xf5Test-group: IPv4.IPv4.ipv4-message.\
248 Ethernet-Payload.IPv4-Packet.IPv4-Header.Fragment-Offset; Test-case: 5737'''
249 malformed_packet = (Ether(dst=self.src_if.local_mac,
250 src=self.src_if.remote_mac) /
252 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
253 IP(id=1000, src=self.src_if.remote_ip4,
254 dst=self.dst_if.remote_ip4) /
255 UDP(sport=1234, dport=5678) /
257 valid_fragments = fragment_rfc791(p, 400)
259 counter = "/err/ip4-full-reassembly-feature/malformed packets"
260 error_counter = self.statistics.get_err_counter(counter)
261 self.pg_enable_capture()
262 self.src_if.add_stream([malformed_packet] + valid_fragments)
265 self.dst_if.get_capture(1)
266 self.logger.debug(self.vapi.ppcli("show error"))
267 self.assertEqual(self.statistics.get_err_counter(counter),
270 def test_44924(self):
271 """ compress tiny fragments """
272 packets = [(Ether(dst=self.src_if.local_mac,
273 src=self.src_if.remote_mac) /
274 IP(id=24339, flags="MF", frag=0, ttl=64,
275 src=self.src_if.remote_ip4,
276 dst=self.dst_if.remote_ip4) /
277 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
278 Raw(load='Test-group: IPv4')),
279 (Ether(dst=self.src_if.local_mac,
280 src=self.src_if.remote_mac) /
281 IP(id=24339, flags="MF", frag=3, ttl=64,
282 src=self.src_if.remote_ip4,
283 dst=self.dst_if.remote_ip4) /
284 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
285 Raw(load='.IPv4.Fragmentation.vali')),
286 (Ether(dst=self.src_if.local_mac,
287 src=self.src_if.remote_mac) /
288 IP(id=24339, frag=6, ttl=64,
289 src=self.src_if.remote_ip4,
290 dst=self.dst_if.remote_ip4) /
291 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
292 Raw(load='d; Test-case: 44924'))
295 self.pg_enable_capture()
296 self.src_if.add_stream(packets)
299 self.dst_if.get_capture(1)
301 def test_frag_1(self):
302 """ fragment of size 1 """
303 self.vapi.cli("clear errors")
304 malformed_packets = [(Ether(dst=self.src_if.local_mac,
305 src=self.src_if.remote_mac) /
306 IP(id=7, len=21, flags="MF", frag=0, ttl=64,
307 src=self.src_if.remote_ip4,
308 dst=self.dst_if.remote_ip4) /
309 ICMP(type="echo-request")),
310 (Ether(dst=self.src_if.local_mac,
311 src=self.src_if.remote_mac) /
312 IP(id=7, len=21, frag=1, ttl=64,
313 src=self.src_if.remote_ip4,
314 dst=self.dst_if.remote_ip4) /
318 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
319 IP(id=1000, src=self.src_if.remote_ip4,
320 dst=self.dst_if.remote_ip4) /
321 UDP(sport=1234, dport=5678) /
323 valid_fragments = fragment_rfc791(p, 400)
325 self.pg_enable_capture()
326 self.src_if.add_stream(malformed_packets + valid_fragments)
329 self.dst_if.get_capture(1)
331 self.assert_packet_counter_equal("ip4-full-reassembly-feature", 1)
332 # TODO remove above, uncomment below once clearing of counters
334 # self.assert_packet_counter_equal(
335 # "/err/ip4-full-reassembly-feature/malformed packets", 1)
337 def test_random(self):
338 """ random order reassembly """
340 fragments = list(self.fragments_200)
343 self.pg_enable_capture()
344 self.src_if.add_stream(fragments)
347 packets = self.dst_if.get_capture(len(self.packet_infos))
348 self.verify_capture(packets)
349 self.src_if.assert_nothing_captured()
351 # run it all again to verify correctness
352 self.pg_enable_capture()
353 self.src_if.add_stream(fragments)
356 packets = self.dst_if.get_capture(len(self.packet_infos))
357 self.verify_capture(packets)
358 self.src_if.assert_nothing_captured()
360 def test_duplicates(self):
361 """ duplicate fragments """
364 x for (_, frags, _, _) in self.pkt_infos
366 for _ in range(0, min(2, len(frags)))
369 self.pg_enable_capture()
370 self.src_if.add_stream(fragments)
373 packets = self.dst_if.get_capture(len(self.pkt_infos))
374 self.verify_capture(packets)
375 self.src_if.assert_nothing_captured()
377 def test_overlap1(self):
378 """ overlapping fragments case #1 """
381 for _, _, frags_300, frags_200 in self.pkt_infos:
382 if len(frags_300) == 1:
383 fragments.extend(frags_300)
385 for i, j in zip(frags_200, frags_300):
389 self.pg_enable_capture()
390 self.src_if.add_stream(fragments)
393 packets = self.dst_if.get_capture(len(self.pkt_infos))
394 self.verify_capture(packets)
395 self.src_if.assert_nothing_captured()
397 # run it all to verify correctness
398 self.pg_enable_capture()
399 self.src_if.add_stream(fragments)
402 packets = self.dst_if.get_capture(len(self.pkt_infos))
403 self.verify_capture(packets)
404 self.src_if.assert_nothing_captured()
406 def test_overlap2(self):
407 """ overlapping fragments case #2 """
410 for _, _, frags_300, frags_200 in self.pkt_infos:
411 if len(frags_300) == 1:
412 fragments.extend(frags_300)
414 # care must be taken here so that there are no fragments
415 # received by vpp after reassembly is finished, otherwise
416 # new reassemblies will be started and packet generator will
417 # freak out when it detects unfreed buffers
418 zipped = zip(frags_300, frags_200)
424 self.pg_enable_capture()
425 self.src_if.add_stream(fragments)
428 packets = self.dst_if.get_capture(len(self.pkt_infos))
429 self.verify_capture(packets)
430 self.src_if.assert_nothing_captured()
432 # run it all to verify correctness
433 self.pg_enable_capture()
434 self.src_if.add_stream(fragments)
437 packets = self.dst_if.get_capture(len(self.pkt_infos))
438 self.verify_capture(packets)
439 self.src_if.assert_nothing_captured()
441 def test_timeout_inline(self):
442 """ timeout (inline) """
444 dropped_packet_indexes = set(
445 index for (index, frags, _, _) in self.pkt_infos if len(frags) > 1
448 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
449 max_reassembly_length=3,
450 expire_walk_interval_ms=10000)
452 self.pg_enable_capture()
453 self.src_if.add_stream(self.fragments_400)
456 packets = self.dst_if.get_capture(
457 len(self.pkt_infos) - len(dropped_packet_indexes))
458 self.verify_capture(packets, dropped_packet_indexes)
459 self.src_if.assert_nothing_captured()
461 def test_timeout_cleanup(self):
462 """ timeout (cleanup) """
464 # whole packets + fragmented packets sans last fragment
466 x for (_, frags_400, _, _) in self.pkt_infos
467 for x in frags_400[:-1 if len(frags_400) > 1 else None]
470 # last fragments for fragmented packets
471 fragments2 = [frags_400[-1]
472 for (_, frags_400, _, _) in self.pkt_infos
473 if len(frags_400) > 1]
475 dropped_packet_indexes = set(
476 index for (index, frags_400, _, _) in self.pkt_infos
477 if len(frags_400) > 1)
479 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
480 max_reassembly_length=1000,
481 expire_walk_interval_ms=50)
483 self.pg_enable_capture()
484 self.src_if.add_stream(fragments)
487 self.virtual_sleep(.25, "wait before sending rest of fragments")
489 self.src_if.add_stream(fragments2)
492 packets = self.dst_if.get_capture(
493 len(self.pkt_infos) - len(dropped_packet_indexes))
494 self.verify_capture(packets, dropped_packet_indexes)
495 self.src_if.assert_nothing_captured()
497 def test_disabled(self):
498 """ reassembly disabled """
500 dropped_packet_indexes = set(
501 index for (index, frags_400, _, _) in self.pkt_infos
502 if len(frags_400) > 1)
504 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
505 max_reassembly_length=3,
506 expire_walk_interval_ms=10000)
508 self.pg_enable_capture()
509 self.src_if.add_stream(self.fragments_400)
512 packets = self.dst_if.get_capture(
513 len(self.pkt_infos) - len(dropped_packet_indexes))
514 self.verify_capture(packets, dropped_packet_indexes)
515 self.src_if.assert_nothing_captured()
517 def test_local_enable_disable(self):
518 """ local reassembly enabled/disable """
519 self.vapi.ip_reassembly_enable_disable(
520 sw_if_index=self.src_if.sw_if_index, enable_ip4=False)
521 self.vapi.ip_local_reass_enable_disable(enable_ip4=True)
522 p = (Ether(src=self.src_if.remote_mac, dst=self.src_if.local_mac) /
523 IP(src=self.src_if.remote_ip4, dst=self.src_if.local_ip4) /
524 ICMP(id=1234, type='echo-request') /
526 frags = fragment_rfc791(p, 400)
527 r = self.send_and_expect(self.src_if, frags, self.src_if, n_rx=1)[0]
528 self.assertEqual(1234, r[ICMP].id)
529 self.assertEqual(icmptypes[r[ICMP].type], 'echo-reply')
530 self.vapi.ip_local_reass_enable_disable()
532 self.send_and_assert_no_replies(self.src_if, frags)
533 self.vapi.ip_local_reass_enable_disable(enable_ip4=True)
536 class TestIPv4SVReassembly(VppTestCase):
537 """ IPv4 Shallow Virtual Reassembly """
543 cls.create_pg_interfaces([0, 1])
547 # setup all interfaces
548 for i in cls.pg_interfaces:
554 """ Test setup - force timeout on existing reassemblies """
556 self.vapi.ip_reassembly_enable_disable(
557 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
558 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
559 self.vapi.ip_reassembly_set(
560 timeout_ms=0, max_reassemblies=1000,
561 max_reassembly_length=1000,
562 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
563 expire_walk_interval_ms=10)
564 self.virtual_sleep(.25)
565 self.vapi.ip_reassembly_set(
566 timeout_ms=1000000, max_reassemblies=1000,
567 max_reassembly_length=1000,
568 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
569 expire_walk_interval_ms=10000)
573 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
574 self.logger.debug(self.vapi.ppcli("show buffers"))
576 def test_basic(self):
577 """ basic reassembly """
581 while len(payload) < payload_len:
582 payload += "%u " % counter
585 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
586 IP(id=1, src=self.src_if.remote_ip4,
587 dst=self.dst_if.remote_ip4) /
588 UDP(sport=1234, dport=5678) /
590 fragments = fragment_rfc791(p, payload_len/4)
592 # send fragment #2 - should be cached inside reassembly
593 self.pg_enable_capture()
594 self.src_if.add_stream(fragments[1])
596 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
597 self.logger.debug(self.vapi.ppcli("show buffers"))
598 self.logger.debug(self.vapi.ppcli("show trace"))
599 self.dst_if.assert_nothing_captured()
601 # send fragment #1 - reassembly is finished now and both fragments
603 self.pg_enable_capture()
604 self.src_if.add_stream(fragments[0])
606 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
607 self.logger.debug(self.vapi.ppcli("show buffers"))
608 self.logger.debug(self.vapi.ppcli("show trace"))
609 c = self.dst_if.get_capture(2)
610 for sent, recvd in zip([fragments[1], fragments[0]], c):
611 self.assertEqual(sent[IP].src, recvd[IP].src)
612 self.assertEqual(sent[IP].dst, recvd[IP].dst)
613 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
615 # send rest of fragments - should be immediately forwarded
616 self.pg_enable_capture()
617 self.src_if.add_stream(fragments[2:])
619 c = self.dst_if.get_capture(len(fragments[2:]))
620 for sent, recvd in zip(fragments[2:], c):
621 self.assertEqual(sent[IP].src, recvd[IP].src)
622 self.assertEqual(sent[IP].dst, recvd[IP].dst)
623 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
625 def test_verify_clear_trace_mid_reassembly(self):
626 """ verify clear trace works mid-reassembly """
630 while len(payload) < payload_len:
631 payload += "%u " % counter
634 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
635 IP(id=1, src=self.src_if.remote_ip4,
636 dst=self.dst_if.remote_ip4) /
637 UDP(sport=1234, dport=5678) /
639 fragments = fragment_rfc791(p, payload_len/4)
641 self.pg_enable_capture()
642 self.src_if.add_stream(fragments[1])
645 self.logger.debug(self.vapi.cli("show trace"))
646 self.vapi.cli("clear trace")
648 self.pg_enable_capture()
649 self.src_if.add_stream(fragments[0])
651 self.dst_if.get_capture(2)
653 self.logger.debug(self.vapi.cli("show trace"))
654 self.vapi.cli("clear trace")
656 self.pg_enable_capture()
657 self.src_if.add_stream(fragments[2:])
659 self.dst_if.get_capture(len(fragments[2:]))
661 def test_timeout(self):
662 """ reassembly timeout """
666 while len(payload) < payload_len:
667 payload += "%u " % counter
670 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
671 IP(id=1, src=self.src_if.remote_ip4,
672 dst=self.dst_if.remote_ip4) /
673 UDP(sport=1234, dport=5678) /
675 fragments = fragment_rfc791(p, payload_len/4)
677 self.vapi.ip_reassembly_set(
678 timeout_ms=100, max_reassemblies=1000,
679 max_reassembly_length=1000,
680 expire_walk_interval_ms=50,
681 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
683 # send fragments #2 and #1 - should be forwarded
684 self.pg_enable_capture()
685 self.src_if.add_stream(fragments[0:2])
687 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
688 self.logger.debug(self.vapi.ppcli("show buffers"))
689 self.logger.debug(self.vapi.ppcli("show trace"))
690 c = self.dst_if.get_capture(2)
691 for sent, recvd in zip([fragments[1], fragments[0]], c):
692 self.assertEqual(sent[IP].src, recvd[IP].src)
693 self.assertEqual(sent[IP].dst, recvd[IP].dst)
694 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
697 self.virtual_sleep(.25, "wait before sending rest of fragments")
699 # send rest of fragments - shouldn't be forwarded
700 self.pg_enable_capture()
701 self.src_if.add_stream(fragments[2:])
703 self.dst_if.assert_nothing_captured()
706 """ reassembly reuses LRU element """
708 self.vapi.ip_reassembly_set(
709 timeout_ms=1000000, max_reassemblies=1,
710 max_reassembly_length=1000,
711 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
712 expire_walk_interval_ms=10000)
717 while len(payload) < payload_len:
718 payload += "%u " % counter
724 for i in range(packet_count)
725 for p in (Ether(dst=self.src_if.local_mac,
726 src=self.src_if.remote_mac) /
727 IP(id=i, src=self.src_if.remote_ip4,
728 dst=self.dst_if.remote_ip4) /
729 UDP(sport=1234, dport=5678) /
731 for f in fragment_rfc791(p, payload_len/4)]
733 self.pg_enable_capture()
734 self.src_if.add_stream(fragments)
736 c = self.dst_if.get_capture(len(fragments))
737 for sent, recvd in zip(fragments, c):
738 self.assertEqual(sent[IP].src, recvd[IP].src)
739 self.assertEqual(sent[IP].dst, recvd[IP].dst)
740 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
742 def send_mixed_and_verify_capture(self, traffic):
745 for c in range(t['count']):
747 (Ether(dst=self.src_if.local_mac,
748 src=self.src_if.remote_mac) /
751 src=self.src_if.remote_ip4,
752 dst=self.dst_if.remote_ip4) /
753 UDP(sport=1234, dport=5678) /
755 self.counter = self.counter + 1
757 self.pg_enable_capture()
758 self.src_if.add_stream(stream)
760 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
761 self.logger.debug(self.vapi.ppcli("show buffers"))
762 self.logger.debug(self.vapi.ppcli("show trace"))
763 self.dst_if.get_capture(len(stream))
765 def test_mixed(self):
766 """ mixed traffic correctly passes through SVR """
769 self.send_mixed_and_verify_capture([{'count': 1, 'flags': ''}])
770 self.send_mixed_and_verify_capture([{'count': 2, 'flags': ''}])
771 self.send_mixed_and_verify_capture([{'count': 3, 'flags': ''}])
772 self.send_mixed_and_verify_capture([{'count': 8, 'flags': ''}])
773 self.send_mixed_and_verify_capture([{'count': 257, 'flags': ''}])
775 self.send_mixed_and_verify_capture([{'count': 1, 'flags': 'MF'}])
776 self.send_mixed_and_verify_capture([{'count': 2, 'flags': 'MF'}])
777 self.send_mixed_and_verify_capture([{'count': 3, 'flags': 'MF'}])
778 self.send_mixed_and_verify_capture([{'count': 8, 'flags': 'MF'}])
779 self.send_mixed_and_verify_capture([{'count': 257, 'flags': 'MF'}])
781 self.send_mixed_and_verify_capture(
782 [{'count': 1, 'flags': ''}, {'count': 1, 'flags': 'MF'}])
783 self.send_mixed_and_verify_capture(
784 [{'count': 2, 'flags': ''}, {'count': 2, 'flags': 'MF'}])
785 self.send_mixed_and_verify_capture(
786 [{'count': 3, 'flags': ''}, {'count': 3, 'flags': 'MF'}])
787 self.send_mixed_and_verify_capture(
788 [{'count': 8, 'flags': ''}, {'count': 8, 'flags': 'MF'}])
789 self.send_mixed_and_verify_capture(
790 [{'count': 129, 'flags': ''}, {'count': 129, 'flags': 'MF'}])
792 self.send_mixed_and_verify_capture(
793 [{'count': 1, 'flags': ''}, {'count': 1, 'flags': 'MF'},
794 {'count': 1, 'flags': ''}, {'count': 1, 'flags': 'MF'}])
795 self.send_mixed_and_verify_capture(
796 [{'count': 2, 'flags': ''}, {'count': 2, 'flags': 'MF'},
797 {'count': 2, 'flags': ''}, {'count': 2, 'flags': 'MF'}])
798 self.send_mixed_and_verify_capture(
799 [{'count': 3, 'flags': ''}, {'count': 3, 'flags': 'MF'},
800 {'count': 3, 'flags': ''}, {'count': 3, 'flags': 'MF'}])
801 self.send_mixed_and_verify_capture(
802 [{'count': 8, 'flags': ''}, {'count': 8, 'flags': 'MF'},
803 {'count': 8, 'flags': ''}, {'count': 8, 'flags': 'MF'}])
804 self.send_mixed_and_verify_capture(
805 [{'count': 65, 'flags': ''}, {'count': 65, 'flags': 'MF'},
806 {'count': 65, 'flags': ''}, {'count': 65, 'flags': 'MF'}])
809 class TestIPv4MWReassembly(VppTestCase):
810 """ IPv4 Reassembly (multiple workers) """
817 cls.create_pg_interfaces(range(cls.vpp_worker_count+1))
819 cls.send_ifs = cls.pg_interfaces[:-1]
820 cls.dst_if = cls.pg_interfaces[-1]
822 # setup all interfaces
823 for i in cls.pg_interfaces:
828 # packets sizes reduced here because we are generating packets without
829 # Ethernet headers, which are added later (diff fragments go via
830 # different interfaces)
831 cls.packet_sizes = [64-len(Ether()), 512-len(Ether()),
832 1518-len(Ether()), 9018-len(Ether())]
833 cls.padding = " abcdefghijklmn"
834 cls.create_stream(cls.packet_sizes)
835 cls.create_fragments()
838 def tearDownClass(cls):
839 super().tearDownClass()
842 """ Test setup - force timeout on existing reassemblies """
844 for intf in self.send_ifs:
845 self.vapi.ip_reassembly_enable_disable(
846 sw_if_index=intf.sw_if_index, enable_ip4=True)
847 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
848 max_reassembly_length=1000,
849 expire_walk_interval_ms=10)
850 self.virtual_sleep(.25)
851 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
852 max_reassembly_length=1000,
853 expire_walk_interval_ms=10000)
856 for intf in self.send_ifs:
857 self.vapi.ip_reassembly_enable_disable(
858 sw_if_index=intf.sw_if_index, enable_ip4=False)
861 def show_commands_at_teardown(self):
862 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
863 self.logger.debug(self.vapi.ppcli("show buffers"))
866 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
867 """Create input packet stream
869 :param list packet_sizes: Required packet sizes.
871 for i in range(0, packet_count):
872 info = cls.create_packet_info(cls.src_if, cls.src_if)
873 payload = cls.info_to_payload(info)
874 p = (IP(id=info.index, src=cls.src_if.remote_ip4,
875 dst=cls.dst_if.remote_ip4) /
876 UDP(sport=1234, dport=5678) /
878 size = packet_sizes[(i // 2) % len(packet_sizes)]
879 cls.extend_packet(p, size, cls.padding)
883 def create_fragments(cls):
884 infos = cls._packet_infos
886 for index, info in infos.items():
888 # cls.logger.debug(ppp("Packet:",
889 # p.__class__(scapy.compat.raw(p))))
890 fragments_400 = fragment_rfc791(p, 400)
891 cls.pkt_infos.append((index, fragments_400))
892 cls.fragments_400 = [
893 x for (_, frags) in cls.pkt_infos for x in frags]
894 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, " %
895 (len(infos), len(cls.fragments_400)))
897 def verify_capture(self, capture, dropped_packet_indexes=[]):
898 """Verify captured packet stream.
900 :param list capture: Captured packet stream.
904 for packet in capture:
906 self.logger.debug(ppp("Got packet:", packet))
909 payload_info = self.payload_to_info(packet[Raw])
910 packet_index = payload_info.index
912 packet_index not in dropped_packet_indexes,
913 ppp("Packet received, but should be dropped:", packet))
914 if packet_index in seen:
915 raise Exception(ppp("Duplicate packet received", packet))
916 seen.add(packet_index)
917 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
918 info = self._packet_infos[packet_index]
919 self.assertTrue(info is not None)
920 self.assertEqual(packet_index, info.index)
921 saved_packet = info.data
922 self.assertEqual(ip.src, saved_packet[IP].src)
923 self.assertEqual(ip.dst, saved_packet[IP].dst)
924 self.assertEqual(udp.payload, saved_packet[UDP].payload)
926 self.logger.error(ppp("Unexpected or invalid packet:", packet))
928 for index in self._packet_infos:
929 self.assertTrue(index in seen or index in dropped_packet_indexes,
930 "Packet with packet_index %d not received" % index)
932 def send_packets(self, packets):
933 for counter in range(self.vpp_worker_count):
934 if 0 == len(packets[counter]):
936 send_if = self.send_ifs[counter]
938 (Ether(dst=send_if.local_mac, src=send_if.remote_mac) / x
939 for x in packets[counter]),
943 def test_worker_conflict(self):
944 """ 1st and FO=0 fragments on different workers """
946 # in first wave we send fragments which don't start at offset 0
947 # then we send fragments with offset 0 on a different thread
948 # then the rest of packets on a random thread
949 first_packets = [[] for n in range(self.vpp_worker_count)]
950 second_packets = [[] for n in range(self.vpp_worker_count)]
951 rest_of_packets = [[] for n in range(self.vpp_worker_count)]
952 for (_, p) in self.pkt_infos:
953 wi = randrange(self.vpp_worker_count)
954 second_packets[wi].append(p[0])
959 wi2 = randrange(self.vpp_worker_count)
960 first_packets[wi2].append(p[1])
961 wi3 = randrange(self.vpp_worker_count)
962 rest_of_packets[wi3].extend(p[2:])
964 self.pg_enable_capture()
965 self.send_packets(first_packets)
966 self.send_packets(second_packets)
967 self.send_packets(rest_of_packets)
969 packets = self.dst_if.get_capture(len(self.pkt_infos))
970 self.verify_capture(packets)
971 for send_if in self.send_ifs:
972 send_if.assert_nothing_captured()
974 self.logger.debug(self.vapi.ppcli("show trace"))
975 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
976 self.logger.debug(self.vapi.ppcli("show buffers"))
977 self.vapi.cli("clear trace")
979 self.pg_enable_capture()
980 self.send_packets(first_packets)
981 self.send_packets(second_packets)
982 self.send_packets(rest_of_packets)
984 packets = self.dst_if.get_capture(len(self.pkt_infos))
985 self.verify_capture(packets)
986 for send_if in self.send_ifs:
987 send_if.assert_nothing_captured()
990 class TestIPv6Reassembly(VppTestCase):
991 """ IPv6 Reassembly """
997 cls.create_pg_interfaces([0, 1])
1001 # setup all interfaces
1002 for i in cls.pg_interfaces:
1008 cls.packet_sizes = [64, 512, 1518, 9018]
1009 cls.padding = " abcdefghijklmn"
1010 cls.create_stream(cls.packet_sizes)
1011 cls.create_fragments()
1014 def tearDownClass(cls):
1015 super().tearDownClass()
1018 """ Test setup - force timeout on existing reassemblies """
1020 self.vapi.ip_reassembly_enable_disable(
1021 sw_if_index=self.src_if.sw_if_index, enable_ip6=True)
1022 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1023 max_reassembly_length=1000,
1024 expire_walk_interval_ms=10, is_ip6=1)
1025 self.virtual_sleep(.25)
1026 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1027 max_reassembly_length=1000,
1028 expire_walk_interval_ms=10000, is_ip6=1)
1029 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1030 self.logger.debug(self.vapi.ppcli("show buffers"))
1033 self.vapi.ip_reassembly_enable_disable(
1034 sw_if_index=self.src_if.sw_if_index, enable_ip6=False)
1037 def show_commands_at_teardown(self):
1038 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1039 self.logger.debug(self.vapi.ppcli("show buffers"))
1042 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
1043 """Create input packet stream for defined interface.
1045 :param list packet_sizes: Required packet sizes.
1047 for i in range(0, packet_count):
1048 info = cls.create_packet_info(cls.src_if, cls.src_if)
1049 payload = cls.info_to_payload(info)
1050 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
1051 IPv6(src=cls.src_if.remote_ip6,
1052 dst=cls.dst_if.remote_ip6) /
1053 UDP(sport=1234, dport=5678) /
1055 size = packet_sizes[(i // 2) % len(packet_sizes)]
1056 cls.extend_packet(p, size, cls.padding)
1060 def create_fragments(cls):
1061 infos = cls._packet_infos
1063 for index, info in infos.items():
1065 # cls.logger.debug(ppp("Packet:",
1066 # p.__class__(scapy.compat.raw(p))))
1067 fragments_400 = fragment_rfc8200(p, info.index, 400)
1068 fragments_300 = fragment_rfc8200(p, info.index, 300)
1069 cls.pkt_infos.append((index, fragments_400, fragments_300))
1070 cls.fragments_400 = [
1071 x for _, frags, _ in cls.pkt_infos for x in frags]
1072 cls.fragments_300 = [
1073 x for _, _, frags in cls.pkt_infos for x in frags]
1074 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
1075 "and %s 300-byte fragments" %
1076 (len(infos), len(cls.fragments_400),
1077 len(cls.fragments_300)))
1079 def verify_capture(self, capture, dropped_packet_indexes=[]):
1080 """Verify captured packet strea .
1082 :param list capture: Captured packet stream.
1086 for packet in capture:
1088 self.logger.debug(ppp("Got packet:", packet))
1091 payload_info = self.payload_to_info(packet[Raw])
1092 packet_index = payload_info.index
1094 packet_index not in dropped_packet_indexes,
1095 ppp("Packet received, but should be dropped:", packet))
1096 if packet_index in seen:
1097 raise Exception(ppp("Duplicate packet received", packet))
1098 seen.add(packet_index)
1099 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
1100 info = self._packet_infos[packet_index]
1101 self.assertTrue(info is not None)
1102 self.assertEqual(packet_index, info.index)
1103 saved_packet = info.data
1104 self.assertEqual(ip.src, saved_packet[IPv6].src)
1105 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
1106 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1108 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1110 for index in self._packet_infos:
1111 self.assertTrue(index in seen or index in dropped_packet_indexes,
1112 "Packet with packet_index %d not received" % index)
1114 def test_reassembly(self):
1115 """ basic reassembly """
1117 self.pg_enable_capture()
1118 self.src_if.add_stream(self.fragments_400)
1121 packets = self.dst_if.get_capture(len(self.pkt_infos))
1122 self.verify_capture(packets)
1123 self.src_if.assert_nothing_captured()
1125 # run it all again to verify correctness
1126 self.pg_enable_capture()
1127 self.src_if.add_stream(self.fragments_400)
1130 packets = self.dst_if.get_capture(len(self.pkt_infos))
1131 self.verify_capture(packets)
1132 self.src_if.assert_nothing_captured()
1134 def test_buffer_boundary(self):
1135 """ fragment header crossing buffer boundary """
1137 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1138 IPv6(src=self.src_if.remote_ip6,
1139 dst=self.src_if.local_ip6) /
1141 options=[HBHOptUnknown(otype=0xff, optlen=0)] * 1000) /
1142 IPv6ExtHdrFragment(m=1) /
1143 UDP(sport=1234, dport=5678) /
1145 self.pg_enable_capture()
1146 self.src_if.add_stream([p])
1148 self.src_if.assert_nothing_captured()
1149 self.dst_if.assert_nothing_captured()
1151 def test_verify_clear_trace_mid_reassembly(self):
1152 """ verify clear trace works mid-reassembly """
1154 self.pg_enable_capture()
1155 self.src_if.add_stream(self.fragments_400[0:-1])
1158 self.logger.debug(self.vapi.cli("show trace"))
1159 self.vapi.cli("clear trace")
1161 self.src_if.add_stream(self.fragments_400[-1])
1163 packets = self.dst_if.get_capture(len(self.pkt_infos))
1164 self.verify_capture(packets)
1166 def test_reversed(self):
1167 """ reverse order reassembly """
1169 fragments = list(self.fragments_400)
1172 self.pg_enable_capture()
1173 self.src_if.add_stream(fragments)
1176 packets = self.dst_if.get_capture(len(self.pkt_infos))
1177 self.verify_capture(packets)
1178 self.src_if.assert_nothing_captured()
1180 # run it all again to verify correctness
1181 self.pg_enable_capture()
1182 self.src_if.add_stream(fragments)
1185 packets = self.dst_if.get_capture(len(self.pkt_infos))
1186 self.verify_capture(packets)
1187 self.src_if.assert_nothing_captured()
1189 def test_random(self):
1190 """ random order reassembly """
1192 fragments = list(self.fragments_400)
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 # run it all again to verify correctness
1204 self.pg_enable_capture()
1205 self.src_if.add_stream(fragments)
1208 packets = self.dst_if.get_capture(len(self.pkt_infos))
1209 self.verify_capture(packets)
1210 self.src_if.assert_nothing_captured()
1212 def test_duplicates(self):
1213 """ duplicate fragments """
1216 x for (_, frags, _) in self.pkt_infos
1218 for _ in range(0, min(2, len(frags)))
1221 self.pg_enable_capture()
1222 self.src_if.add_stream(fragments)
1225 packets = self.dst_if.get_capture(len(self.pkt_infos))
1226 self.verify_capture(packets)
1227 self.src_if.assert_nothing_captured()
1229 def test_long_fragment_chain(self):
1230 """ long fragment chain """
1233 "/err/ip6-full-reassembly-feature/fragment chain too long (drop)"
1235 error_cnt = self.statistics.get_err_counter(error_cnt_str)
1237 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
1238 max_reassembly_length=3,
1239 expire_walk_interval_ms=50, is_ip6=1)
1241 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1242 IPv6(src=self.src_if.remote_ip6,
1243 dst=self.dst_if.remote_ip6) /
1244 UDP(sport=1234, dport=5678) /
1246 frags = fragment_rfc8200(p, 1, 300) + fragment_rfc8200(p, 2, 500)
1248 self.pg_enable_capture()
1249 self.src_if.add_stream(frags)
1252 self.dst_if.get_capture(1)
1253 self.assert_error_counter_equal(error_cnt_str, error_cnt + 1)
1255 def test_overlap1(self):
1256 """ overlapping fragments case #1 """
1259 for _, frags_400, frags_300 in self.pkt_infos:
1260 if len(frags_300) == 1:
1261 fragments.extend(frags_400)
1263 for i, j in zip(frags_300, frags_400):
1267 dropped_packet_indexes = set(
1268 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
1271 self.pg_enable_capture()
1272 self.src_if.add_stream(fragments)
1275 packets = self.dst_if.get_capture(
1276 len(self.pkt_infos) - len(dropped_packet_indexes))
1277 self.verify_capture(packets, dropped_packet_indexes)
1278 self.src_if.assert_nothing_captured()
1280 def test_overlap2(self):
1281 """ overlapping fragments case #2 """
1284 for _, frags_400, frags_300 in self.pkt_infos:
1285 if len(frags_400) == 1:
1286 fragments.extend(frags_400)
1288 # care must be taken here so that there are no fragments
1289 # received by vpp after reassembly is finished, otherwise
1290 # new reassemblies will be started and packet generator will
1291 # freak out when it detects unfreed buffers
1292 zipped = zip(frags_400, frags_300)
1298 dropped_packet_indexes = set(
1299 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
1302 self.pg_enable_capture()
1303 self.src_if.add_stream(fragments)
1306 packets = self.dst_if.get_capture(
1307 len(self.pkt_infos) - len(dropped_packet_indexes))
1308 self.verify_capture(packets, dropped_packet_indexes)
1309 self.src_if.assert_nothing_captured()
1311 def test_timeout_inline(self):
1312 """ timeout (inline) """
1314 dropped_packet_indexes = set(
1315 index for (index, frags, _) in self.pkt_infos if len(frags) > 1
1318 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1319 max_reassembly_length=3,
1320 expire_walk_interval_ms=10000, is_ip6=1)
1322 self.pg_enable_capture()
1323 self.src_if.add_stream(self.fragments_400)
1326 packets = self.dst_if.get_capture(
1327 len(self.pkt_infos) - len(dropped_packet_indexes))
1328 self.verify_capture(packets, dropped_packet_indexes)
1329 pkts = self.src_if._get_capture(1)
1331 self.assertIn(ICMPv6TimeExceeded, icmp)
1332 self.assertIn(IPv6ExtHdrFragment, icmp)
1333 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
1334 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
1336 def test_timeout_cleanup(self):
1337 """ timeout (cleanup) """
1339 # whole packets + fragmented packets sans last fragment
1341 x for (_, frags_400, _) in self.pkt_infos
1342 for x in frags_400[:-1 if len(frags_400) > 1 else None]
1345 # last fragments for fragmented packets
1346 fragments2 = [frags_400[-1]
1347 for (_, frags_400, _) in self.pkt_infos
1348 if len(frags_400) > 1]
1350 dropped_packet_indexes = set(
1351 index for (index, frags_400, _) in self.pkt_infos
1352 if len(frags_400) > 1)
1354 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
1355 max_reassembly_length=1000,
1356 expire_walk_interval_ms=50)
1358 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
1359 max_reassembly_length=1000,
1360 expire_walk_interval_ms=50, is_ip6=1)
1362 self.pg_enable_capture()
1363 self.src_if.add_stream(fragments)
1366 self.virtual_sleep(.25, "wait before sending rest of fragments")
1368 self.src_if.add_stream(fragments2)
1371 packets = self.dst_if.get_capture(
1372 len(self.pkt_infos) - len(dropped_packet_indexes))
1373 self.verify_capture(packets, dropped_packet_indexes)
1374 pkts = self.src_if._get_capture(1)
1376 self.assertIn(ICMPv6TimeExceeded, icmp)
1377 self.assertIn(IPv6ExtHdrFragment, icmp)
1378 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
1379 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
1381 def test_disabled(self):
1382 """ reassembly disabled """
1384 dropped_packet_indexes = set(
1385 index for (index, frags_400, _) in self.pkt_infos
1386 if len(frags_400) > 1)
1388 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
1389 max_reassembly_length=3,
1390 expire_walk_interval_ms=10000, is_ip6=1)
1392 self.pg_enable_capture()
1393 self.src_if.add_stream(self.fragments_400)
1396 packets = self.dst_if.get_capture(
1397 len(self.pkt_infos) - len(dropped_packet_indexes))
1398 self.verify_capture(packets, dropped_packet_indexes)
1399 self.src_if.assert_nothing_captured()
1401 def test_missing_upper(self):
1402 """ missing upper layer """
1403 optdata = '\x00' * 100
1404 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1405 IPv6(src=self.src_if.remote_ip6,
1406 dst=self.src_if.local_ip6) /
1407 IPv6ExtHdrFragment(m=1) /
1408 IPv6ExtHdrDestOpt(nh=17, options=PadN(optdata='\101' * 255) /
1409 PadN(optdata='\102'*255)))
1411 self.pg_enable_capture()
1412 self.src_if.add_stream([p])
1414 pkts = self.src_if.get_capture(expected_count=1)
1416 self.assertIn(ICMPv6ParamProblem, icmp)
1417 self.assert_equal(icmp[ICMPv6ParamProblem].code, 3, "ICMP code")
1419 def test_truncated_fragment(self):
1420 """ truncated fragment """
1421 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1422 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6,
1424 IPv6ExtHdrFragment(nh=6))
1426 self.send_and_assert_no_replies(self.pg0, [pkt], self.pg0)
1428 def test_invalid_frag_size(self):
1429 """ fragment size not a multiple of 8 """
1430 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1431 IPv6(src=self.src_if.remote_ip6,
1432 dst=self.src_if.local_ip6) /
1433 UDP(sport=1234, dport=5678) /
1435 self.extend_packet(p, 1000, self.padding)
1436 fragments = fragment_rfc8200(p, 1, 500)
1437 bad_fragment = fragments[0]
1438 self.extend_packet(bad_fragment, len(bad_fragment) + 5)
1439 self.pg_enable_capture()
1440 self.src_if.add_stream([bad_fragment])
1442 pkts = self.src_if.get_capture(expected_count=1)
1444 self.assertIn(ICMPv6ParamProblem, icmp)
1445 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1447 def test_invalid_packet_size(self):
1448 """ total packet size > 65535 """
1449 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1450 IPv6(src=self.src_if.remote_ip6,
1451 dst=self.src_if.local_ip6) /
1452 UDP(sport=1234, dport=5678) /
1454 self.extend_packet(p, 1000, self.padding)
1455 fragments = fragment_rfc8200(p, 1, 500)
1456 bad_fragment = fragments[1]
1457 bad_fragment[IPv6ExtHdrFragment].offset = 65500
1458 self.pg_enable_capture()
1459 self.src_if.add_stream([bad_fragment])
1461 pkts = self.src_if.get_capture(expected_count=1)
1463 self.assertIn(ICMPv6ParamProblem, icmp)
1464 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1466 def test_atomic_fragment(self):
1467 """ IPv6 atomic fragment """
1468 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1469 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6,
1470 nh=44, plen=65535) /
1471 IPv6ExtHdrFragment(offset=8191, m=1, res1=0xFF, res2=0xFF,
1472 nh=255, id=0xffff)/('X'*1452))
1474 rx = self.send_and_expect(self.pg0, [pkt], self.pg0)
1475 self.assertIn(ICMPv6ParamProblem, rx[0])
1477 def test_truncated_fragment(self):
1478 """ IPv6 truncated fragment header """
1479 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1480 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6,
1482 IPv6ExtHdrFragment(nh=6))
1484 self.send_and_assert_no_replies(self.pg0, [pkt])
1486 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1487 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.remote_ip6) /
1488 ICMPv6EchoRequest())
1489 rx = self.send_and_expect(self.pg0, [pkt], self.pg0)
1491 def test_one_fragment(self):
1492 """ whole packet in one fragment processed independently """
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 ICMPv6EchoRequest()/Raw('X' * 1600))
1496 frags = fragment_rfc8200(pkt, 1, 400)
1498 # send a fragment with known id
1499 self.send_and_assert_no_replies(self.pg0, [frags[0]])
1501 # send an atomic fragment with same id - should be reassembled
1502 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1503 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) /
1504 IPv6ExtHdrFragment(id=1)/ICMPv6EchoRequest())
1505 rx = self.send_and_expect(self.pg0, [pkt], self.pg0)
1506 self.assertNotIn(IPv6ExtHdrFragment, rx)
1508 # now finish the original reassembly, this should still be possible
1509 rx = self.send_and_expect(self.pg0, frags[1:], self.pg0, n_rx=1)
1510 self.assertNotIn(IPv6ExtHdrFragment, rx)
1512 def test_bunch_of_fragments(self):
1513 """ valid fragments followed by rogue fragments and atomic fragment"""
1514 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1515 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) /
1516 ICMPv6EchoRequest()/Raw('X' * 1600))
1517 frags = fragment_rfc8200(pkt, 1, 400)
1518 self.send_and_expect(self.pg0, frags, self.pg0, n_rx=1)
1520 inc_frag = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1521 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) /
1522 IPv6ExtHdrFragment(id=1, nh=58, offset=608)/Raw('X'*308))
1524 self.send_and_assert_no_replies(self.pg0, inc_frag*604)
1526 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1527 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) /
1528 IPv6ExtHdrFragment(id=1)/ICMPv6EchoRequest())
1529 rx = self.send_and_expect(self.pg0, [pkt], self.pg0)
1530 self.assertNotIn(IPv6ExtHdrFragment, rx)
1532 def test_local_enable_disable(self):
1533 """ local reassembly enabled/disable """
1534 self.vapi.ip_reassembly_enable_disable(
1535 sw_if_index=self.src_if.sw_if_index, enable_ip6=False)
1536 self.vapi.ip_local_reass_enable_disable(enable_ip6=True)
1537 pkt = (Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) /
1538 IPv6(src=self.src_if.remote_ip6, dst=self.src_if.local_ip6) /
1539 ICMPv6EchoRequest(id=1234)/Raw('X' * 1600))
1540 frags = fragment_rfc8200(pkt, 1, 400)
1541 r = self.send_and_expect(self.src_if, frags, self.src_if, n_rx=1)[0]
1542 self.assertEqual(1234, r[ICMPv6EchoReply].id)
1543 self.vapi.ip_local_reass_enable_disable()
1545 self.send_and_assert_no_replies(self.src_if, frags)
1546 self.vapi.ip_local_reass_enable_disable(enable_ip6=True)
1549 class TestIPv6MWReassembly(VppTestCase):
1550 """ IPv6 Reassembly (multiple workers) """
1551 vpp_worker_count = 3
1554 def setUpClass(cls):
1555 super().setUpClass()
1557 cls.create_pg_interfaces(range(cls.vpp_worker_count+1))
1558 cls.src_if = cls.pg0
1559 cls.send_ifs = cls.pg_interfaces[:-1]
1560 cls.dst_if = cls.pg_interfaces[-1]
1562 # setup all interfaces
1563 for i in cls.pg_interfaces:
1568 # packets sizes reduced here because we are generating packets without
1569 # Ethernet headers, which are added later (diff fragments go via
1570 # different interfaces)
1571 cls.packet_sizes = [64-len(Ether()), 512-len(Ether()),
1572 1518-len(Ether()), 9018-len(Ether())]
1573 cls.padding = " abcdefghijklmn"
1574 cls.create_stream(cls.packet_sizes)
1575 cls.create_fragments()
1578 def tearDownClass(cls):
1579 super().tearDownClass()
1582 """ Test setup - force timeout on existing reassemblies """
1584 for intf in self.send_ifs:
1585 self.vapi.ip_reassembly_enable_disable(
1586 sw_if_index=intf.sw_if_index, enable_ip6=True)
1587 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1588 max_reassembly_length=1000,
1589 expire_walk_interval_ms=10, is_ip6=1)
1590 self.virtual_sleep(.25)
1591 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1592 max_reassembly_length=1000,
1593 expire_walk_interval_ms=1000, is_ip6=1)
1596 for intf in self.send_ifs:
1597 self.vapi.ip_reassembly_enable_disable(
1598 sw_if_index=intf.sw_if_index, enable_ip6=False)
1601 def show_commands_at_teardown(self):
1602 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1603 self.logger.debug(self.vapi.ppcli("show buffers"))
1606 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
1607 """Create input packet stream
1609 :param list packet_sizes: Required packet sizes.
1611 for i in range(0, packet_count):
1612 info = cls.create_packet_info(cls.src_if, cls.src_if)
1613 payload = cls.info_to_payload(info)
1614 p = (IPv6(src=cls.src_if.remote_ip6,
1615 dst=cls.dst_if.remote_ip6) /
1616 UDP(sport=1234, dport=5678) /
1618 size = packet_sizes[(i // 2) % len(packet_sizes)]
1619 cls.extend_packet(p, size, cls.padding)
1623 def create_fragments(cls):
1624 infos = cls._packet_infos
1626 for index, info in infos.items():
1628 # cls.logger.debug(ppp("Packet:",
1629 # p.__class__(scapy.compat.raw(p))))
1630 fragments_400 = fragment_rfc8200(p, index, 400)
1631 cls.pkt_infos.append((index, fragments_400))
1632 cls.fragments_400 = [
1633 x for (_, frags) in cls.pkt_infos for x in frags]
1634 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, " %
1635 (len(infos), len(cls.fragments_400)))
1637 def verify_capture(self, capture, dropped_packet_indexes=[]):
1638 """Verify captured packet strea .
1640 :param list capture: Captured packet stream.
1644 for packet in capture:
1646 self.logger.debug(ppp("Got packet:", packet))
1649 payload_info = self.payload_to_info(packet[Raw])
1650 packet_index = payload_info.index
1652 packet_index not in dropped_packet_indexes,
1653 ppp("Packet received, but should be dropped:", packet))
1654 if packet_index in seen:
1655 raise Exception(ppp("Duplicate packet received", packet))
1656 seen.add(packet_index)
1657 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
1658 info = self._packet_infos[packet_index]
1659 self.assertTrue(info is not None)
1660 self.assertEqual(packet_index, info.index)
1661 saved_packet = info.data
1662 self.assertEqual(ip.src, saved_packet[IPv6].src)
1663 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
1664 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1666 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1668 for index in self._packet_infos:
1669 self.assertTrue(index in seen or index in dropped_packet_indexes,
1670 "Packet with packet_index %d not received" % index)
1672 def send_packets(self, packets):
1673 for counter in range(self.vpp_worker_count):
1674 if 0 == len(packets[counter]):
1676 send_if = self.send_ifs[counter]
1678 (Ether(dst=send_if.local_mac, src=send_if.remote_mac) / x
1679 for x in packets[counter]),
1683 def test_worker_conflict(self):
1684 """ 1st and FO=0 fragments on different workers """
1686 # in first wave we send fragments which don't start at offset 0
1687 # then we send fragments with offset 0 on a different thread
1688 # then the rest of packets on a random thread
1689 first_packets = [[] for n in range(self.vpp_worker_count)]
1690 second_packets = [[] for n in range(self.vpp_worker_count)]
1691 rest_of_packets = [[] for n in range(self.vpp_worker_count)]
1692 for (_, p) in self.pkt_infos:
1693 wi = randrange(self.vpp_worker_count)
1694 second_packets[wi].append(p[0])
1699 wi2 = randrange(self.vpp_worker_count)
1700 first_packets[wi2].append(p[1])
1701 wi3 = randrange(self.vpp_worker_count)
1702 rest_of_packets[wi3].extend(p[2:])
1704 self.pg_enable_capture()
1705 self.send_packets(first_packets)
1706 self.send_packets(second_packets)
1707 self.send_packets(rest_of_packets)
1709 packets = self.dst_if.get_capture(len(self.pkt_infos))
1710 self.verify_capture(packets)
1711 for send_if in self.send_ifs:
1712 send_if.assert_nothing_captured()
1714 self.logger.debug(self.vapi.ppcli("show trace"))
1715 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1716 self.logger.debug(self.vapi.ppcli("show buffers"))
1717 self.vapi.cli("clear trace")
1719 self.pg_enable_capture()
1720 self.send_packets(first_packets)
1721 self.send_packets(second_packets)
1722 self.send_packets(rest_of_packets)
1724 packets = self.dst_if.get_capture(len(self.pkt_infos))
1725 self.verify_capture(packets)
1726 for send_if in self.send_ifs:
1727 send_if.assert_nothing_captured()
1730 class TestIPv6SVReassembly(VppTestCase):
1731 """ IPv6 Shallow Virtual Reassembly """
1734 def setUpClass(cls):
1735 super().setUpClass()
1737 cls.create_pg_interfaces([0, 1])
1738 cls.src_if = cls.pg0
1739 cls.dst_if = cls.pg1
1741 # setup all interfaces
1742 for i in cls.pg_interfaces:
1748 """ Test setup - force timeout on existing reassemblies """
1750 self.vapi.ip_reassembly_enable_disable(
1751 sw_if_index=self.src_if.sw_if_index, enable_ip6=True,
1752 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
1753 self.vapi.ip_reassembly_set(
1754 timeout_ms=0, max_reassemblies=1000,
1755 max_reassembly_length=1000,
1756 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1757 expire_walk_interval_ms=10, is_ip6=1)
1758 self.virtual_sleep(.25)
1759 self.vapi.ip_reassembly_set(
1760 timeout_ms=1000000, max_reassemblies=1000,
1761 max_reassembly_length=1000,
1762 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1763 expire_walk_interval_ms=10000, is_ip6=1)
1767 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1768 self.logger.debug(self.vapi.ppcli("show buffers"))
1770 def test_basic(self):
1771 """ basic reassembly """
1775 while len(payload) < payload_len:
1776 payload += "%u " % counter
1779 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1780 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1781 UDP(sport=1234, dport=5678) /
1783 fragments = fragment_rfc8200(p, 1, payload_len/4)
1785 # send fragment #2 - should be cached inside reassembly
1786 self.pg_enable_capture()
1787 self.src_if.add_stream(fragments[1])
1789 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1790 self.logger.debug(self.vapi.ppcli("show buffers"))
1791 self.logger.debug(self.vapi.ppcli("show trace"))
1792 self.dst_if.assert_nothing_captured()
1794 # send fragment #1 - reassembly is finished now and both fragments
1796 self.pg_enable_capture()
1797 self.src_if.add_stream(fragments[0])
1799 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1800 self.logger.debug(self.vapi.ppcli("show buffers"))
1801 self.logger.debug(self.vapi.ppcli("show trace"))
1802 c = self.dst_if.get_capture(2)
1803 for sent, recvd in zip([fragments[1], fragments[0]], c):
1804 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1805 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1806 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1808 # send rest of fragments - should be immediately forwarded
1809 self.pg_enable_capture()
1810 self.src_if.add_stream(fragments[2:])
1812 c = self.dst_if.get_capture(len(fragments[2:]))
1813 for sent, recvd in zip(fragments[2:], c):
1814 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1815 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1816 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1818 def test_verify_clear_trace_mid_reassembly(self):
1819 """ verify clear trace works mid-reassembly """
1823 while len(payload) < payload_len:
1824 payload += "%u " % counter
1827 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1828 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1829 UDP(sport=1234, dport=5678) /
1831 fragments = fragment_rfc8200(p, 1, payload_len/4)
1833 self.pg_enable_capture()
1834 self.src_if.add_stream(fragments[1])
1837 self.logger.debug(self.vapi.cli("show trace"))
1838 self.vapi.cli("clear trace")
1840 self.pg_enable_capture()
1841 self.src_if.add_stream(fragments[0])
1843 self.dst_if.get_capture(2)
1845 self.logger.debug(self.vapi.cli("show trace"))
1846 self.vapi.cli("clear trace")
1848 self.pg_enable_capture()
1849 self.src_if.add_stream(fragments[2:])
1851 self.dst_if.get_capture(len(fragments[2:]))
1853 def test_timeout(self):
1854 """ reassembly timeout """
1858 while len(payload) < payload_len:
1859 payload += "%u " % counter
1862 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1863 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1864 UDP(sport=1234, dport=5678) /
1866 fragments = fragment_rfc8200(p, 1, payload_len/4)
1868 self.vapi.ip_reassembly_set(
1869 timeout_ms=100, max_reassemblies=1000,
1870 max_reassembly_length=1000,
1871 expire_walk_interval_ms=50,
1873 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
1875 # send fragments #2 and #1 - should be forwarded
1876 self.pg_enable_capture()
1877 self.src_if.add_stream(fragments[0:2])
1879 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
1880 self.logger.debug(self.vapi.ppcli("show buffers"))
1881 self.logger.debug(self.vapi.ppcli("show trace"))
1882 c = self.dst_if.get_capture(2)
1883 for sent, recvd in zip([fragments[1], fragments[0]], c):
1884 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1885 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1886 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1889 self.virtual_sleep(.25, "wait before sending rest of fragments")
1891 # send rest of fragments - shouldn't be forwarded
1892 self.pg_enable_capture()
1893 self.src_if.add_stream(fragments[2:])
1895 self.dst_if.assert_nothing_captured()
1898 """ reassembly reuses LRU element """
1900 self.vapi.ip_reassembly_set(
1901 timeout_ms=1000000, max_reassemblies=1,
1902 max_reassembly_length=1000,
1903 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1904 is_ip6=1, expire_walk_interval_ms=10000)
1909 while len(payload) < payload_len:
1910 payload += "%u " % counter
1916 for i in range(packet_count)
1917 for p in (Ether(dst=self.src_if.local_mac,
1918 src=self.src_if.remote_mac) /
1919 IPv6(src=self.src_if.remote_ip6,
1920 dst=self.dst_if.remote_ip6) /
1921 UDP(sport=1234, dport=5678) /
1923 for f in fragment_rfc8200(p, i, payload_len/4)]
1925 self.pg_enable_capture()
1926 self.src_if.add_stream(fragments)
1928 c = self.dst_if.get_capture(len(fragments))
1929 for sent, recvd in zip(fragments, c):
1930 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1931 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1932 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1934 def test_one_fragment(self):
1935 """ whole packet in one fragment processed independently """
1936 pkt = (Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) /
1937 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1938 ICMPv6EchoRequest()/Raw('X' * 1600))
1939 frags = fragment_rfc8200(pkt, 1, 400)
1941 # send a fragment with known id
1942 self.send_and_expect(self.src_if, [frags[0]], self.dst_if)
1944 # send an atomic fragment with same id - should be reassembled
1945 pkt = (Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) /
1946 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1947 IPv6ExtHdrFragment(id=1)/ICMPv6EchoRequest())
1948 rx = self.send_and_expect(self.src_if, [pkt], self.dst_if)
1950 # now forward packets matching original reassembly, should still work
1951 rx = self.send_and_expect(self.src_if, frags[1:], self.dst_if)
1953 def test_bunch_of_fragments(self):
1954 """ valid fragments followed by rogue fragments and atomic fragment"""
1955 pkt = (Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) /
1956 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1957 ICMPv6EchoRequest()/Raw('X' * 1600))
1958 frags = fragment_rfc8200(pkt, 1, 400)
1959 rx = self.send_and_expect(self.src_if, frags, self.dst_if)
1961 rogue = (Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) /
1962 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1963 IPv6ExtHdrFragment(id=1, nh=58, offset=608)/Raw('X'*308))
1965 self.send_and_expect(self.src_if, rogue*604, self.dst_if)
1967 pkt = (Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) /
1968 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1969 IPv6ExtHdrFragment(id=1)/ICMPv6EchoRequest())
1970 rx = self.send_and_expect(self.src_if, [pkt], self.dst_if)
1972 def test_truncated_fragment(self):
1973 """ truncated fragment """
1974 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1975 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6,
1977 IPv6ExtHdrFragment(nh=6))
1979 self.send_and_assert_no_replies(self.pg0, [pkt], self.pg0)
1982 class TestIPv4ReassemblyLocalNode(VppTestCase):
1983 """ IPv4 Reassembly for packets coming to ip4-local node """
1986 def setUpClass(cls):
1987 super().setUpClass()
1989 cls.create_pg_interfaces([0])
1990 cls.src_dst_if = cls.pg0
1992 # setup all interfaces
1993 for i in cls.pg_interfaces:
1998 cls.padding = " abcdefghijklmn"
2000 cls.create_fragments()
2003 def tearDownClass(cls):
2004 super().tearDownClass()
2007 """ Test setup - force timeout on existing reassemblies """
2009 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
2010 max_reassembly_length=1000,
2011 expire_walk_interval_ms=10)
2012 self.virtual_sleep(.25)
2013 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
2014 max_reassembly_length=1000,
2015 expire_walk_interval_ms=10000)
2020 def show_commands_at_teardown(self):
2021 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
2022 self.logger.debug(self.vapi.ppcli("show buffers"))
2025 def create_stream(cls, packet_count=test_packet_count):
2026 """Create input packet stream for defined interface.
2028 :param list packet_sizes: Required packet sizes.
2030 for i in range(0, packet_count):
2031 info = cls.create_packet_info(cls.src_dst_if, cls.src_dst_if)
2032 payload = cls.info_to_payload(info)
2033 p = (Ether(dst=cls.src_dst_if.local_mac,
2034 src=cls.src_dst_if.remote_mac) /
2035 IP(id=info.index, src=cls.src_dst_if.remote_ip4,
2036 dst=cls.src_dst_if.local_ip4) /
2037 ICMP(type='echo-request', id=1234) /
2039 cls.extend_packet(p, 1518, cls.padding)
2043 def create_fragments(cls):
2044 infos = cls._packet_infos
2046 for index, info in infos.items():
2048 # cls.logger.debug(ppp("Packet:",
2049 # p.__class__(scapy.compat.raw(p))))
2050 fragments_300 = fragment_rfc791(p, 300)
2051 cls.pkt_infos.append((index, fragments_300))
2052 cls.fragments_300 = [x for (_, frags) in cls.pkt_infos for x in frags]
2053 cls.logger.debug("Fragmented %s packets into %s 300-byte fragments" %
2054 (len(infos), len(cls.fragments_300)))
2056 def verify_capture(self, capture):
2057 """Verify captured packet stream.
2059 :param list capture: Captured packet stream.
2063 for packet in capture:
2065 self.logger.debug(ppp("Got packet:", packet))
2068 payload_info = self.payload_to_info(packet[Raw])
2069 packet_index = payload_info.index
2070 if packet_index in seen:
2071 raise Exception(ppp("Duplicate packet received", packet))
2072 seen.add(packet_index)
2073 self.assertEqual(payload_info.dst, self.src_dst_if.sw_if_index)
2074 info = self._packet_infos[packet_index]
2075 self.assertIsNotNone(info)
2076 self.assertEqual(packet_index, info.index)
2077 saved_packet = info.data
2078 self.assertEqual(ip.src, saved_packet[IP].dst)
2079 self.assertEqual(ip.dst, saved_packet[IP].src)
2080 self.assertEqual(icmp.type, 0) # echo reply
2081 self.assertEqual(icmp.id, saved_packet[ICMP].id)
2082 self.assertEqual(icmp.payload, saved_packet[ICMP].payload)
2084 self.logger.error(ppp("Unexpected or invalid packet:", packet))
2086 for index in self._packet_infos:
2087 self.assertIn(index, seen,
2088 "Packet with packet_index %d not received" % index)
2090 def test_reassembly(self):
2091 """ basic reassembly """
2093 self.pg_enable_capture()
2094 self.src_dst_if.add_stream(self.fragments_300)
2097 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
2098 self.verify_capture(packets)
2100 # run it all again to verify correctness
2101 self.pg_enable_capture()
2102 self.src_dst_if.add_stream(self.fragments_300)
2105 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
2106 self.verify_capture(packets)
2109 class TestFIFReassembly(VppTestCase):
2110 """ Fragments in fragments reassembly """
2113 def setUpClass(cls):
2114 super().setUpClass()
2116 cls.create_pg_interfaces([0, 1])
2117 cls.src_if = cls.pg0
2118 cls.dst_if = cls.pg1
2119 for i in cls.pg_interfaces:
2126 cls.packet_sizes = [64, 512, 1518, 9018]
2127 cls.padding = " abcdefghijklmn"
2130 def tearDownClass(cls):
2131 super().tearDownClass()
2134 """ Test setup - force timeout on existing reassemblies """
2136 self.vapi.ip_reassembly_enable_disable(
2137 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
2139 self.vapi.ip_reassembly_enable_disable(
2140 sw_if_index=self.dst_if.sw_if_index, enable_ip4=True,
2142 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
2143 max_reassembly_length=1000,
2144 expire_walk_interval_ms=10)
2145 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
2146 max_reassembly_length=1000,
2147 expire_walk_interval_ms=10, is_ip6=1)
2148 self.virtual_sleep(.25)
2149 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
2150 max_reassembly_length=1000,
2151 expire_walk_interval_ms=10000)
2152 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
2153 max_reassembly_length=1000,
2154 expire_walk_interval_ms=10000, is_ip6=1)
2159 def show_commands_at_teardown(self):
2160 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
2161 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
2162 self.logger.debug(self.vapi.ppcli("show buffers"))
2164 def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):
2165 """Verify captured packet stream.
2167 :param list capture: Captured packet stream.
2171 for packet in capture:
2173 self.logger.debug(ppp("Got packet:", packet))
2174 ip = packet[ip_class]
2176 payload_info = self.payload_to_info(packet[Raw])
2177 packet_index = payload_info.index
2179 packet_index not in dropped_packet_indexes,
2180 ppp("Packet received, but should be dropped:", packet))
2181 if packet_index in seen:
2182 raise Exception(ppp("Duplicate packet received", packet))
2183 seen.add(packet_index)
2184 self.assertEqual(payload_info.dst, self.dst_if.sw_if_index)
2185 info = self._packet_infos[packet_index]
2186 self.assertTrue(info is not None)
2187 self.assertEqual(packet_index, info.index)
2188 saved_packet = info.data
2189 self.assertEqual(ip.src, saved_packet[ip_class].src)
2190 self.assertEqual(ip.dst, saved_packet[ip_class].dst)
2191 self.assertEqual(udp.payload, saved_packet[UDP].payload)
2193 self.logger.error(ppp("Unexpected or invalid packet:", packet))
2195 for index in self._packet_infos:
2196 self.assertTrue(index in seen or index in dropped_packet_indexes,
2197 "Packet with packet_index %d not received" % index)
2199 def test_fif4(self):
2200 """ Fragments in fragments (4o4) """
2202 # TODO this should be ideally in setUpClass, but then we hit a bug
2203 # with VppIpRoute incorrectly reporting it's present when it's not
2204 # so we need to manually remove the vpp config, thus we cannot have
2205 # it shared for multiple test cases
2206 self.tun_ip4 = "1.1.1.2"
2208 self.gre4 = VppGreInterface(self, self.src_if.local_ip4, self.tun_ip4)
2209 self.gre4.add_vpp_config()
2210 self.gre4.admin_up()
2211 self.gre4.config_ip4()
2213 self.vapi.ip_reassembly_enable_disable(
2214 sw_if_index=self.gre4.sw_if_index, enable_ip4=True)
2216 self.route4 = VppIpRoute(self, self.tun_ip4, 32,
2217 [VppRoutePath(self.src_if.remote_ip4,
2218 self.src_if.sw_if_index)])
2219 self.route4.add_vpp_config()
2221 self.reset_packet_infos()
2222 for i in range(test_packet_count):
2223 info = self.create_packet_info(self.src_if, self.dst_if)
2224 payload = self.info_to_payload(info)
2225 # Ethernet header here is only for size calculation, thus it
2226 # doesn't matter how it's initialized. This is to ensure that
2227 # reassembled packet is not > 9000 bytes, so that it's not dropped
2229 IP(id=i, src=self.src_if.remote_ip4,
2230 dst=self.dst_if.remote_ip4) /
2231 UDP(sport=1234, dport=5678) /
2233 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
2234 self.extend_packet(p, size, self.padding)
2235 info.data = p[IP] # use only IP part, without ethernet header
2237 fragments = [x for _, p in self._packet_infos.items()
2238 for x in fragment_rfc791(p.data, 400)]
2240 encapped_fragments = \
2241 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
2242 IP(src=self.tun_ip4, dst=self.src_if.local_ip4) /
2247 fragmented_encapped_fragments = \
2248 [x for p in encapped_fragments
2249 for x in fragment_rfc791(p, 200)]
2251 self.src_if.add_stream(fragmented_encapped_fragments)
2253 self.pg_enable_capture(self.pg_interfaces)
2256 self.src_if.assert_nothing_captured()
2257 packets = self.dst_if.get_capture(len(self._packet_infos))
2258 self.verify_capture(packets, IP)
2260 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
2261 # so that it's query_vpp_config() works as it should
2262 self.gre4.remove_vpp_config()
2263 self.logger.debug(self.vapi.ppcli("show interface"))
2265 def test_fif6(self):
2266 """ Fragments in fragments (6o6) """
2267 # TODO this should be ideally in setUpClass, but then we hit a bug
2268 # with VppIpRoute incorrectly reporting it's present when it's not
2269 # so we need to manually remove the vpp config, thus we cannot have
2270 # it shared for multiple test cases
2271 self.tun_ip6 = "1002::1"
2273 self.gre6 = VppGreInterface(self, self.src_if.local_ip6, self.tun_ip6)
2274 self.gre6.add_vpp_config()
2275 self.gre6.admin_up()
2276 self.gre6.config_ip6()
2278 self.vapi.ip_reassembly_enable_disable(
2279 sw_if_index=self.gre6.sw_if_index, enable_ip6=True)
2281 self.route6 = VppIpRoute(self, self.tun_ip6, 128,
2283 self.src_if.remote_ip6,
2284 self.src_if.sw_if_index)])
2285 self.route6.add_vpp_config()
2287 self.reset_packet_infos()
2288 for i in range(test_packet_count):
2289 info = self.create_packet_info(self.src_if, self.dst_if)
2290 payload = self.info_to_payload(info)
2291 # Ethernet header here is only for size calculation, thus it
2292 # doesn't matter how it's initialized. This is to ensure that
2293 # reassembled packet is not > 9000 bytes, so that it's not dropped
2295 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
2296 UDP(sport=1234, dport=5678) /
2298 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
2299 self.extend_packet(p, size, self.padding)
2300 info.data = p[IPv6] # use only IPv6 part, without ethernet header
2302 fragments = [x for _, i in self._packet_infos.items()
2303 for x in fragment_rfc8200(
2304 i.data, i.index, 400)]
2306 encapped_fragments = \
2307 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
2308 IPv6(src=self.tun_ip6, dst=self.src_if.local_ip6) /
2313 fragmented_encapped_fragments = \
2314 [x for p in encapped_fragments for x in (
2317 2 * len(self._packet_infos) + p[IPv6ExtHdrFragment].id,
2319 if IPv6ExtHdrFragment in p else [p]
2323 self.src_if.add_stream(fragmented_encapped_fragments)
2325 self.pg_enable_capture(self.pg_interfaces)
2328 self.src_if.assert_nothing_captured()
2329 packets = self.dst_if.get_capture(len(self._packet_infos))
2330 self.verify_capture(packets, IPv6)
2332 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
2333 # so that it's query_vpp_config() works as it should
2334 self.gre6.remove_vpp_config()
2337 if __name__ == '__main__':
2338 unittest.main(testRunner=VppTestRunner)