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(
1330 expected_count=len(dropped_packet_indexes))
1332 self.assertIn(ICMPv6TimeExceeded, icmp)
1333 self.assertIn(IPv6ExtHdrFragment, icmp)
1334 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
1335 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
1337 def test_timeout_cleanup(self):
1338 """ timeout (cleanup) """
1340 # whole packets + fragmented packets sans last fragment
1342 x for (_, frags_400, _) in self.pkt_infos
1343 for x in frags_400[:-1 if len(frags_400) > 1 else None]
1346 # last fragments for fragmented packets
1347 fragments2 = [frags_400[-1]
1348 for (_, frags_400, _) in self.pkt_infos
1349 if len(frags_400) > 1]
1351 dropped_packet_indexes = set(
1352 index for (index, frags_400, _) in self.pkt_infos
1353 if len(frags_400) > 1)
1355 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
1356 max_reassembly_length=1000,
1357 expire_walk_interval_ms=50)
1359 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
1360 max_reassembly_length=1000,
1361 expire_walk_interval_ms=50, is_ip6=1)
1363 self.pg_enable_capture()
1364 self.src_if.add_stream(fragments)
1367 self.virtual_sleep(.25, "wait before sending rest of fragments")
1369 self.src_if.add_stream(fragments2)
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 pkts = self.src_if.get_capture(
1376 expected_count=len(dropped_packet_indexes))
1378 self.assertIn(ICMPv6TimeExceeded, icmp)
1379 self.assertIn(IPv6ExtHdrFragment, icmp)
1380 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
1381 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
1383 def test_disabled(self):
1384 """ reassembly disabled """
1386 dropped_packet_indexes = set(
1387 index for (index, frags_400, _) in self.pkt_infos
1388 if len(frags_400) > 1)
1390 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
1391 max_reassembly_length=3,
1392 expire_walk_interval_ms=10000, is_ip6=1)
1394 self.pg_enable_capture()
1395 self.src_if.add_stream(self.fragments_400)
1398 packets = self.dst_if.get_capture(
1399 len(self.pkt_infos) - len(dropped_packet_indexes))
1400 self.verify_capture(packets, dropped_packet_indexes)
1401 self.src_if.assert_nothing_captured()
1403 def test_missing_upper(self):
1404 """ missing upper layer """
1405 optdata = '\x00' * 100
1406 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1407 IPv6(src=self.src_if.remote_ip6,
1408 dst=self.src_if.local_ip6) /
1409 IPv6ExtHdrFragment(m=1) /
1410 IPv6ExtHdrDestOpt(nh=17, options=PadN(optdata='\101' * 255) /
1411 PadN(optdata='\102'*255)))
1413 self.pg_enable_capture()
1414 self.src_if.add_stream([p])
1416 pkts = self.src_if.get_capture(expected_count=1)
1418 self.assertIn(ICMPv6ParamProblem, icmp)
1419 self.assert_equal(icmp[ICMPv6ParamProblem].code, 3, "ICMP code")
1421 def test_truncated_fragment(self):
1422 """ truncated fragment """
1423 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1424 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6,
1426 IPv6ExtHdrFragment(nh=6))
1428 self.send_and_assert_no_replies(self.pg0, [pkt], self.pg0)
1430 def test_invalid_frag_size(self):
1431 """ fragment size not a multiple of 8 """
1432 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1433 IPv6(src=self.src_if.remote_ip6,
1434 dst=self.src_if.local_ip6) /
1435 UDP(sport=1234, dport=5678) /
1437 self.extend_packet(p, 1000, self.padding)
1438 fragments = fragment_rfc8200(p, 1, 500)
1439 bad_fragment = fragments[0]
1440 self.extend_packet(bad_fragment, len(bad_fragment) + 5)
1441 self.pg_enable_capture()
1442 self.src_if.add_stream([bad_fragment])
1444 pkts = self.src_if.get_capture(expected_count=1)
1446 self.assertIn(ICMPv6ParamProblem, icmp)
1447 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1449 def test_invalid_packet_size(self):
1450 """ total packet size > 65535 """
1451 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1452 IPv6(src=self.src_if.remote_ip6,
1453 dst=self.src_if.local_ip6) /
1454 UDP(sport=1234, dport=5678) /
1456 self.extend_packet(p, 1000, self.padding)
1457 fragments = fragment_rfc8200(p, 1, 500)
1458 bad_fragment = fragments[1]
1459 bad_fragment[IPv6ExtHdrFragment].offset = 65500
1460 self.pg_enable_capture()
1461 self.src_if.add_stream([bad_fragment])
1463 pkts = self.src_if.get_capture(expected_count=1)
1465 self.assertIn(ICMPv6ParamProblem, icmp)
1466 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1468 def test_atomic_fragment(self):
1469 """ IPv6 atomic fragment """
1470 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1471 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6,
1472 nh=44, plen=65535) /
1473 IPv6ExtHdrFragment(offset=8191, m=1, res1=0xFF, res2=0xFF,
1474 nh=255, id=0xffff)/('X'*1452))
1476 rx = self.send_and_expect(self.pg0, [pkt], self.pg0)
1477 self.assertIn(ICMPv6ParamProblem, rx[0])
1479 def test_truncated_fragment(self):
1480 """ IPv6 truncated fragment header """
1481 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1482 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6,
1484 IPv6ExtHdrFragment(nh=6))
1486 self.send_and_assert_no_replies(self.pg0, [pkt])
1488 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1489 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.remote_ip6) /
1490 ICMPv6EchoRequest())
1491 rx = self.send_and_expect(self.pg0, [pkt], self.pg0)
1493 def test_one_fragment(self):
1494 """ whole packet in one fragment processed independently """
1495 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1496 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) /
1497 ICMPv6EchoRequest()/Raw('X' * 1600))
1498 frags = fragment_rfc8200(pkt, 1, 400)
1500 # send a fragment with known id
1501 self.send_and_assert_no_replies(self.pg0, [frags[0]])
1503 # send an atomic fragment with same id - should be reassembled
1504 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1505 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) /
1506 IPv6ExtHdrFragment(id=1)/ICMPv6EchoRequest())
1507 rx = self.send_and_expect(self.pg0, [pkt], self.pg0)
1508 self.assertNotIn(IPv6ExtHdrFragment, rx)
1510 # now finish the original reassembly, this should still be possible
1511 rx = self.send_and_expect(self.pg0, frags[1:], self.pg0, n_rx=1)
1512 self.assertNotIn(IPv6ExtHdrFragment, rx)
1514 def test_bunch_of_fragments(self):
1515 """ valid fragments followed by rogue fragments and atomic fragment"""
1516 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1517 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) /
1518 ICMPv6EchoRequest()/Raw('X' * 1600))
1519 frags = fragment_rfc8200(pkt, 1, 400)
1520 self.send_and_expect(self.pg0, frags, self.pg0, n_rx=1)
1522 inc_frag = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1523 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) /
1524 IPv6ExtHdrFragment(id=1, nh=58, offset=608)/Raw('X'*308))
1526 self.send_and_assert_no_replies(self.pg0, inc_frag*604)
1528 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1529 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) /
1530 IPv6ExtHdrFragment(id=1)/ICMPv6EchoRequest())
1531 rx = self.send_and_expect(self.pg0, [pkt], self.pg0)
1532 self.assertNotIn(IPv6ExtHdrFragment, rx)
1534 def test_local_enable_disable(self):
1535 """ local reassembly enabled/disable """
1536 self.vapi.ip_reassembly_enable_disable(
1537 sw_if_index=self.src_if.sw_if_index, enable_ip6=False)
1538 self.vapi.ip_local_reass_enable_disable(enable_ip6=True)
1539 pkt = (Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) /
1540 IPv6(src=self.src_if.remote_ip6, dst=self.src_if.local_ip6) /
1541 ICMPv6EchoRequest(id=1234)/Raw('X' * 1600))
1542 frags = fragment_rfc8200(pkt, 1, 400)
1543 r = self.send_and_expect(self.src_if, frags, self.src_if, n_rx=1)[0]
1544 self.assertEqual(1234, r[ICMPv6EchoReply].id)
1545 self.vapi.ip_local_reass_enable_disable()
1547 self.send_and_assert_no_replies(self.src_if, frags)
1548 self.vapi.ip_local_reass_enable_disable(enable_ip6=True)
1551 class TestIPv6MWReassembly(VppTestCase):
1552 """ IPv6 Reassembly (multiple workers) """
1553 vpp_worker_count = 3
1556 def setUpClass(cls):
1557 super().setUpClass()
1559 cls.create_pg_interfaces(range(cls.vpp_worker_count+1))
1560 cls.src_if = cls.pg0
1561 cls.send_ifs = cls.pg_interfaces[:-1]
1562 cls.dst_if = cls.pg_interfaces[-1]
1564 # setup all interfaces
1565 for i in cls.pg_interfaces:
1570 # packets sizes reduced here because we are generating packets without
1571 # Ethernet headers, which are added later (diff fragments go via
1572 # different interfaces)
1573 cls.packet_sizes = [64-len(Ether()), 512-len(Ether()),
1574 1518-len(Ether()), 9018-len(Ether())]
1575 cls.padding = " abcdefghijklmn"
1576 cls.create_stream(cls.packet_sizes)
1577 cls.create_fragments()
1580 def tearDownClass(cls):
1581 super().tearDownClass()
1584 """ Test setup - force timeout on existing reassemblies """
1586 for intf in self.send_ifs:
1587 self.vapi.ip_reassembly_enable_disable(
1588 sw_if_index=intf.sw_if_index, enable_ip6=True)
1589 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1590 max_reassembly_length=1000,
1591 expire_walk_interval_ms=10, is_ip6=1)
1592 self.virtual_sleep(.25)
1593 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1594 max_reassembly_length=1000,
1595 expire_walk_interval_ms=1000, is_ip6=1)
1598 for intf in self.send_ifs:
1599 self.vapi.ip_reassembly_enable_disable(
1600 sw_if_index=intf.sw_if_index, enable_ip6=False)
1603 def show_commands_at_teardown(self):
1604 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1605 self.logger.debug(self.vapi.ppcli("show buffers"))
1608 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
1609 """Create input packet stream
1611 :param list packet_sizes: Required packet sizes.
1613 for i in range(0, packet_count):
1614 info = cls.create_packet_info(cls.src_if, cls.src_if)
1615 payload = cls.info_to_payload(info)
1616 p = (IPv6(src=cls.src_if.remote_ip6,
1617 dst=cls.dst_if.remote_ip6) /
1618 UDP(sport=1234, dport=5678) /
1620 size = packet_sizes[(i // 2) % len(packet_sizes)]
1621 cls.extend_packet(p, size, cls.padding)
1625 def create_fragments(cls):
1626 infos = cls._packet_infos
1628 for index, info in infos.items():
1630 # cls.logger.debug(ppp("Packet:",
1631 # p.__class__(scapy.compat.raw(p))))
1632 fragments_400 = fragment_rfc8200(p, index, 400)
1633 cls.pkt_infos.append((index, fragments_400))
1634 cls.fragments_400 = [
1635 x for (_, frags) in cls.pkt_infos for x in frags]
1636 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, " %
1637 (len(infos), len(cls.fragments_400)))
1639 def verify_capture(self, capture, dropped_packet_indexes=[]):
1640 """Verify captured packet strea .
1642 :param list capture: Captured packet stream.
1646 for packet in capture:
1648 self.logger.debug(ppp("Got packet:", packet))
1651 payload_info = self.payload_to_info(packet[Raw])
1652 packet_index = payload_info.index
1654 packet_index not in dropped_packet_indexes,
1655 ppp("Packet received, but should be dropped:", packet))
1656 if packet_index in seen:
1657 raise Exception(ppp("Duplicate packet received", packet))
1658 seen.add(packet_index)
1659 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
1660 info = self._packet_infos[packet_index]
1661 self.assertTrue(info is not None)
1662 self.assertEqual(packet_index, info.index)
1663 saved_packet = info.data
1664 self.assertEqual(ip.src, saved_packet[IPv6].src)
1665 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
1666 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1668 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1670 for index in self._packet_infos:
1671 self.assertTrue(index in seen or index in dropped_packet_indexes,
1672 "Packet with packet_index %d not received" % index)
1674 def send_packets(self, packets):
1675 for counter in range(self.vpp_worker_count):
1676 if 0 == len(packets[counter]):
1678 send_if = self.send_ifs[counter]
1680 (Ether(dst=send_if.local_mac, src=send_if.remote_mac) / x
1681 for x in packets[counter]),
1685 def test_worker_conflict(self):
1686 """ 1st and FO=0 fragments on different workers """
1688 # in first wave we send fragments which don't start at offset 0
1689 # then we send fragments with offset 0 on a different thread
1690 # then the rest of packets on a random thread
1691 first_packets = [[] for n in range(self.vpp_worker_count)]
1692 second_packets = [[] for n in range(self.vpp_worker_count)]
1693 rest_of_packets = [[] for n in range(self.vpp_worker_count)]
1694 for (_, p) in self.pkt_infos:
1695 wi = randrange(self.vpp_worker_count)
1696 second_packets[wi].append(p[0])
1701 wi2 = randrange(self.vpp_worker_count)
1702 first_packets[wi2].append(p[1])
1703 wi3 = randrange(self.vpp_worker_count)
1704 rest_of_packets[wi3].extend(p[2:])
1706 self.pg_enable_capture()
1707 self.send_packets(first_packets)
1708 self.send_packets(second_packets)
1709 self.send_packets(rest_of_packets)
1711 packets = self.dst_if.get_capture(len(self.pkt_infos))
1712 self.verify_capture(packets)
1713 for send_if in self.send_ifs:
1714 send_if.assert_nothing_captured()
1716 self.logger.debug(self.vapi.ppcli("show trace"))
1717 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1718 self.logger.debug(self.vapi.ppcli("show buffers"))
1719 self.vapi.cli("clear trace")
1721 self.pg_enable_capture()
1722 self.send_packets(first_packets)
1723 self.send_packets(second_packets)
1724 self.send_packets(rest_of_packets)
1726 packets = self.dst_if.get_capture(len(self.pkt_infos))
1727 self.verify_capture(packets)
1728 for send_if in self.send_ifs:
1729 send_if.assert_nothing_captured()
1732 class TestIPv6SVReassembly(VppTestCase):
1733 """ IPv6 Shallow Virtual Reassembly """
1736 def setUpClass(cls):
1737 super().setUpClass()
1739 cls.create_pg_interfaces([0, 1])
1740 cls.src_if = cls.pg0
1741 cls.dst_if = cls.pg1
1743 # setup all interfaces
1744 for i in cls.pg_interfaces:
1750 """ Test setup - force timeout on existing reassemblies """
1752 self.vapi.ip_reassembly_enable_disable(
1753 sw_if_index=self.src_if.sw_if_index, enable_ip6=True,
1754 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
1755 self.vapi.ip_reassembly_set(
1756 timeout_ms=0, max_reassemblies=1000,
1757 max_reassembly_length=1000,
1758 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1759 expire_walk_interval_ms=10, is_ip6=1)
1760 self.virtual_sleep(.25)
1761 self.vapi.ip_reassembly_set(
1762 timeout_ms=1000000, max_reassemblies=1000,
1763 max_reassembly_length=1000,
1764 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1765 expire_walk_interval_ms=10000, is_ip6=1)
1769 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1770 self.logger.debug(self.vapi.ppcli("show buffers"))
1772 def test_basic(self):
1773 """ basic reassembly """
1777 while len(payload) < payload_len:
1778 payload += "%u " % counter
1781 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1782 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1783 UDP(sport=1234, dport=5678) /
1785 fragments = fragment_rfc8200(p, 1, payload_len/4)
1787 # send fragment #2 - should be cached inside reassembly
1788 self.pg_enable_capture()
1789 self.src_if.add_stream(fragments[1])
1791 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1792 self.logger.debug(self.vapi.ppcli("show buffers"))
1793 self.logger.debug(self.vapi.ppcli("show trace"))
1794 self.dst_if.assert_nothing_captured()
1796 # send fragment #1 - reassembly is finished now and both fragments
1798 self.pg_enable_capture()
1799 self.src_if.add_stream(fragments[0])
1801 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1802 self.logger.debug(self.vapi.ppcli("show buffers"))
1803 self.logger.debug(self.vapi.ppcli("show trace"))
1804 c = self.dst_if.get_capture(2)
1805 for sent, recvd in zip([fragments[1], fragments[0]], c):
1806 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1807 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1808 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1810 # send rest of fragments - should be immediately forwarded
1811 self.pg_enable_capture()
1812 self.src_if.add_stream(fragments[2:])
1814 c = self.dst_if.get_capture(len(fragments[2:]))
1815 for sent, recvd in zip(fragments[2:], c):
1816 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1817 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1818 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1820 def test_verify_clear_trace_mid_reassembly(self):
1821 """ verify clear trace works mid-reassembly """
1825 while len(payload) < payload_len:
1826 payload += "%u " % counter
1829 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1830 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1831 UDP(sport=1234, dport=5678) /
1833 fragments = fragment_rfc8200(p, 1, payload_len/4)
1835 self.pg_enable_capture()
1836 self.src_if.add_stream(fragments[1])
1839 self.logger.debug(self.vapi.cli("show trace"))
1840 self.vapi.cli("clear trace")
1842 self.pg_enable_capture()
1843 self.src_if.add_stream(fragments[0])
1845 self.dst_if.get_capture(2)
1847 self.logger.debug(self.vapi.cli("show trace"))
1848 self.vapi.cli("clear trace")
1850 self.pg_enable_capture()
1851 self.src_if.add_stream(fragments[2:])
1853 self.dst_if.get_capture(len(fragments[2:]))
1855 def test_timeout(self):
1856 """ reassembly timeout """
1860 while len(payload) < payload_len:
1861 payload += "%u " % counter
1864 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1865 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1866 UDP(sport=1234, dport=5678) /
1868 fragments = fragment_rfc8200(p, 1, payload_len/4)
1870 self.vapi.ip_reassembly_set(
1871 timeout_ms=100, max_reassemblies=1000,
1872 max_reassembly_length=1000,
1873 expire_walk_interval_ms=50,
1875 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
1877 # send fragments #2 and #1 - should be forwarded
1878 self.pg_enable_capture()
1879 self.src_if.add_stream(fragments[0:2])
1881 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
1882 self.logger.debug(self.vapi.ppcli("show buffers"))
1883 self.logger.debug(self.vapi.ppcli("show trace"))
1884 c = self.dst_if.get_capture(2)
1885 for sent, recvd in zip([fragments[1], fragments[0]], c):
1886 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1887 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1888 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1891 self.virtual_sleep(.25, "wait before sending rest of fragments")
1893 # send rest of fragments - shouldn't be forwarded
1894 self.pg_enable_capture()
1895 self.src_if.add_stream(fragments[2:])
1897 self.dst_if.assert_nothing_captured()
1900 """ reassembly reuses LRU element """
1902 self.vapi.ip_reassembly_set(
1903 timeout_ms=1000000, max_reassemblies=1,
1904 max_reassembly_length=1000,
1905 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1906 is_ip6=1, expire_walk_interval_ms=10000)
1911 while len(payload) < payload_len:
1912 payload += "%u " % counter
1918 for i in range(packet_count)
1919 for p in (Ether(dst=self.src_if.local_mac,
1920 src=self.src_if.remote_mac) /
1921 IPv6(src=self.src_if.remote_ip6,
1922 dst=self.dst_if.remote_ip6) /
1923 UDP(sport=1234, dport=5678) /
1925 for f in fragment_rfc8200(p, i, payload_len/4)]
1927 self.pg_enable_capture()
1928 self.src_if.add_stream(fragments)
1930 c = self.dst_if.get_capture(len(fragments))
1931 for sent, recvd in zip(fragments, c):
1932 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1933 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1934 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1936 def test_one_fragment(self):
1937 """ whole packet in one fragment processed independently """
1938 pkt = (Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) /
1939 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1940 ICMPv6EchoRequest()/Raw('X' * 1600))
1941 frags = fragment_rfc8200(pkt, 1, 400)
1943 # send a fragment with known id
1944 self.send_and_expect(self.src_if, [frags[0]], self.dst_if)
1946 # send an atomic fragment with same id - should be reassembled
1947 pkt = (Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) /
1948 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1949 IPv6ExtHdrFragment(id=1)/ICMPv6EchoRequest())
1950 rx = self.send_and_expect(self.src_if, [pkt], self.dst_if)
1952 # now forward packets matching original reassembly, should still work
1953 rx = self.send_and_expect(self.src_if, frags[1:], self.dst_if)
1955 def test_bunch_of_fragments(self):
1956 """ valid fragments followed by rogue fragments and atomic fragment"""
1957 pkt = (Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) /
1958 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1959 ICMPv6EchoRequest()/Raw('X' * 1600))
1960 frags = fragment_rfc8200(pkt, 1, 400)
1961 rx = self.send_and_expect(self.src_if, frags, self.dst_if)
1963 rogue = (Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) /
1964 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1965 IPv6ExtHdrFragment(id=1, nh=58, offset=608)/Raw('X'*308))
1967 self.send_and_expect(self.src_if, rogue*604, self.dst_if)
1969 pkt = (Ether(src=self.src_if.local_mac, dst=self.src_if.remote_mac) /
1970 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1971 IPv6ExtHdrFragment(id=1)/ICMPv6EchoRequest())
1972 rx = self.send_and_expect(self.src_if, [pkt], self.dst_if)
1974 def test_truncated_fragment(self):
1975 """ truncated fragment """
1976 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1977 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6,
1979 IPv6ExtHdrFragment(nh=6))
1981 self.send_and_assert_no_replies(self.pg0, [pkt], self.pg0)
1984 class TestIPv4ReassemblyLocalNode(VppTestCase):
1985 """ IPv4 Reassembly for packets coming to ip4-local node """
1988 def setUpClass(cls):
1989 super().setUpClass()
1991 cls.create_pg_interfaces([0])
1992 cls.src_dst_if = cls.pg0
1994 # setup all interfaces
1995 for i in cls.pg_interfaces:
2000 cls.padding = " abcdefghijklmn"
2002 cls.create_fragments()
2005 def tearDownClass(cls):
2006 super().tearDownClass()
2009 """ Test setup - force timeout on existing reassemblies """
2011 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
2012 max_reassembly_length=1000,
2013 expire_walk_interval_ms=10)
2014 self.virtual_sleep(.25)
2015 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
2016 max_reassembly_length=1000,
2017 expire_walk_interval_ms=10000)
2022 def show_commands_at_teardown(self):
2023 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
2024 self.logger.debug(self.vapi.ppcli("show buffers"))
2027 def create_stream(cls, packet_count=test_packet_count):
2028 """Create input packet stream for defined interface.
2030 :param list packet_sizes: Required packet sizes.
2032 for i in range(0, packet_count):
2033 info = cls.create_packet_info(cls.src_dst_if, cls.src_dst_if)
2034 payload = cls.info_to_payload(info)
2035 p = (Ether(dst=cls.src_dst_if.local_mac,
2036 src=cls.src_dst_if.remote_mac) /
2037 IP(id=info.index, src=cls.src_dst_if.remote_ip4,
2038 dst=cls.src_dst_if.local_ip4) /
2039 ICMP(type='echo-request', id=1234) /
2041 cls.extend_packet(p, 1518, cls.padding)
2045 def create_fragments(cls):
2046 infos = cls._packet_infos
2048 for index, info in infos.items():
2050 # cls.logger.debug(ppp("Packet:",
2051 # p.__class__(scapy.compat.raw(p))))
2052 fragments_300 = fragment_rfc791(p, 300)
2053 cls.pkt_infos.append((index, fragments_300))
2054 cls.fragments_300 = [x for (_, frags) in cls.pkt_infos for x in frags]
2055 cls.logger.debug("Fragmented %s packets into %s 300-byte fragments" %
2056 (len(infos), len(cls.fragments_300)))
2058 def verify_capture(self, capture):
2059 """Verify captured packet stream.
2061 :param list capture: Captured packet stream.
2065 for packet in capture:
2067 self.logger.debug(ppp("Got packet:", packet))
2070 payload_info = self.payload_to_info(packet[Raw])
2071 packet_index = payload_info.index
2072 if packet_index in seen:
2073 raise Exception(ppp("Duplicate packet received", packet))
2074 seen.add(packet_index)
2075 self.assertEqual(payload_info.dst, self.src_dst_if.sw_if_index)
2076 info = self._packet_infos[packet_index]
2077 self.assertIsNotNone(info)
2078 self.assertEqual(packet_index, info.index)
2079 saved_packet = info.data
2080 self.assertEqual(ip.src, saved_packet[IP].dst)
2081 self.assertEqual(ip.dst, saved_packet[IP].src)
2082 self.assertEqual(icmp.type, 0) # echo reply
2083 self.assertEqual(icmp.id, saved_packet[ICMP].id)
2084 self.assertEqual(icmp.payload, saved_packet[ICMP].payload)
2086 self.logger.error(ppp("Unexpected or invalid packet:", packet))
2088 for index in self._packet_infos:
2089 self.assertIn(index, seen,
2090 "Packet with packet_index %d not received" % index)
2092 def test_reassembly(self):
2093 """ basic reassembly """
2095 self.pg_enable_capture()
2096 self.src_dst_if.add_stream(self.fragments_300)
2099 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
2100 self.verify_capture(packets)
2102 # run it all again to verify correctness
2103 self.pg_enable_capture()
2104 self.src_dst_if.add_stream(self.fragments_300)
2107 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
2108 self.verify_capture(packets)
2111 class TestFIFReassembly(VppTestCase):
2112 """ Fragments in fragments reassembly """
2115 def setUpClass(cls):
2116 super().setUpClass()
2118 cls.create_pg_interfaces([0, 1])
2119 cls.src_if = cls.pg0
2120 cls.dst_if = cls.pg1
2121 for i in cls.pg_interfaces:
2128 cls.packet_sizes = [64, 512, 1518, 9018]
2129 cls.padding = " abcdefghijklmn"
2132 def tearDownClass(cls):
2133 super().tearDownClass()
2136 """ Test setup - force timeout on existing reassemblies """
2138 self.vapi.ip_reassembly_enable_disable(
2139 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
2141 self.vapi.ip_reassembly_enable_disable(
2142 sw_if_index=self.dst_if.sw_if_index, enable_ip4=True,
2144 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
2145 max_reassembly_length=1000,
2146 expire_walk_interval_ms=10)
2147 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
2148 max_reassembly_length=1000,
2149 expire_walk_interval_ms=10, is_ip6=1)
2150 self.virtual_sleep(.25)
2151 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
2152 max_reassembly_length=1000,
2153 expire_walk_interval_ms=10000)
2154 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
2155 max_reassembly_length=1000,
2156 expire_walk_interval_ms=10000, is_ip6=1)
2161 def show_commands_at_teardown(self):
2162 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
2163 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
2164 self.logger.debug(self.vapi.ppcli("show buffers"))
2166 def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):
2167 """Verify captured packet stream.
2169 :param list capture: Captured packet stream.
2173 for packet in capture:
2175 self.logger.debug(ppp("Got packet:", packet))
2176 ip = packet[ip_class]
2178 payload_info = self.payload_to_info(packet[Raw])
2179 packet_index = payload_info.index
2181 packet_index not in dropped_packet_indexes,
2182 ppp("Packet received, but should be dropped:", packet))
2183 if packet_index in seen:
2184 raise Exception(ppp("Duplicate packet received", packet))
2185 seen.add(packet_index)
2186 self.assertEqual(payload_info.dst, self.dst_if.sw_if_index)
2187 info = self._packet_infos[packet_index]
2188 self.assertTrue(info is not None)
2189 self.assertEqual(packet_index, info.index)
2190 saved_packet = info.data
2191 self.assertEqual(ip.src, saved_packet[ip_class].src)
2192 self.assertEqual(ip.dst, saved_packet[ip_class].dst)
2193 self.assertEqual(udp.payload, saved_packet[UDP].payload)
2195 self.logger.error(ppp("Unexpected or invalid packet:", packet))
2197 for index in self._packet_infos:
2198 self.assertTrue(index in seen or index in dropped_packet_indexes,
2199 "Packet with packet_index %d not received" % index)
2201 def test_fif4(self):
2202 """ Fragments in fragments (4o4) """
2204 # TODO this should be ideally in setUpClass, but then we hit a bug
2205 # with VppIpRoute incorrectly reporting it's present when it's not
2206 # so we need to manually remove the vpp config, thus we cannot have
2207 # it shared for multiple test cases
2208 self.tun_ip4 = "1.1.1.2"
2210 self.gre4 = VppGreInterface(self, self.src_if.local_ip4, self.tun_ip4)
2211 self.gre4.add_vpp_config()
2212 self.gre4.admin_up()
2213 self.gre4.config_ip4()
2215 self.vapi.ip_reassembly_enable_disable(
2216 sw_if_index=self.gre4.sw_if_index, enable_ip4=True)
2218 self.route4 = VppIpRoute(self, self.tun_ip4, 32,
2219 [VppRoutePath(self.src_if.remote_ip4,
2220 self.src_if.sw_if_index)])
2221 self.route4.add_vpp_config()
2223 self.reset_packet_infos()
2224 for i in range(test_packet_count):
2225 info = self.create_packet_info(self.src_if, self.dst_if)
2226 payload = self.info_to_payload(info)
2227 # Ethernet header here is only for size calculation, thus it
2228 # doesn't matter how it's initialized. This is to ensure that
2229 # reassembled packet is not > 9000 bytes, so that it's not dropped
2231 IP(id=i, src=self.src_if.remote_ip4,
2232 dst=self.dst_if.remote_ip4) /
2233 UDP(sport=1234, dport=5678) /
2235 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
2236 self.extend_packet(p, size, self.padding)
2237 info.data = p[IP] # use only IP part, without ethernet header
2239 fragments = [x for _, p in self._packet_infos.items()
2240 for x in fragment_rfc791(p.data, 400)]
2242 encapped_fragments = \
2243 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
2244 IP(src=self.tun_ip4, dst=self.src_if.local_ip4) /
2249 fragmented_encapped_fragments = \
2250 [x for p in encapped_fragments
2251 for x in fragment_rfc791(p, 200)]
2253 self.src_if.add_stream(fragmented_encapped_fragments)
2255 self.pg_enable_capture(self.pg_interfaces)
2258 self.src_if.assert_nothing_captured()
2259 packets = self.dst_if.get_capture(len(self._packet_infos))
2260 self.verify_capture(packets, IP)
2262 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
2263 # so that it's query_vpp_config() works as it should
2264 self.gre4.remove_vpp_config()
2265 self.logger.debug(self.vapi.ppcli("show interface"))
2267 def test_fif6(self):
2268 """ Fragments in fragments (6o6) """
2269 # TODO this should be ideally in setUpClass, but then we hit a bug
2270 # with VppIpRoute incorrectly reporting it's present when it's not
2271 # so we need to manually remove the vpp config, thus we cannot have
2272 # it shared for multiple test cases
2273 self.tun_ip6 = "1002::1"
2275 self.gre6 = VppGreInterface(self, self.src_if.local_ip6, self.tun_ip6)
2276 self.gre6.add_vpp_config()
2277 self.gre6.admin_up()
2278 self.gre6.config_ip6()
2280 self.vapi.ip_reassembly_enable_disable(
2281 sw_if_index=self.gre6.sw_if_index, enable_ip6=True)
2283 self.route6 = VppIpRoute(self, self.tun_ip6, 128,
2285 self.src_if.remote_ip6,
2286 self.src_if.sw_if_index)])
2287 self.route6.add_vpp_config()
2289 self.reset_packet_infos()
2290 for i in range(test_packet_count):
2291 info = self.create_packet_info(self.src_if, self.dst_if)
2292 payload = self.info_to_payload(info)
2293 # Ethernet header here is only for size calculation, thus it
2294 # doesn't matter how it's initialized. This is to ensure that
2295 # reassembled packet is not > 9000 bytes, so that it's not dropped
2297 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
2298 UDP(sport=1234, dport=5678) /
2300 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
2301 self.extend_packet(p, size, self.padding)
2302 info.data = p[IPv6] # use only IPv6 part, without ethernet header
2304 fragments = [x for _, i in self._packet_infos.items()
2305 for x in fragment_rfc8200(
2306 i.data, i.index, 400)]
2308 encapped_fragments = \
2309 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
2310 IPv6(src=self.tun_ip6, dst=self.src_if.local_ip6) /
2315 fragmented_encapped_fragments = \
2316 [x for p in encapped_fragments for x in (
2319 2 * len(self._packet_infos) + p[IPv6ExtHdrFragment].id,
2321 if IPv6ExtHdrFragment in p else [p]
2325 self.src_if.add_stream(fragmented_encapped_fragments)
2327 self.pg_enable_capture(self.pg_interfaces)
2330 self.src_if.assert_nothing_captured()
2331 packets = self.dst_if.get_capture(len(self._packet_infos))
2332 self.verify_capture(packets, IPv6)
2334 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
2335 # so that it's query_vpp_config() works as it should
2336 self.gre6.remove_vpp_config()
2339 if __name__ == '__main__':
2340 unittest.main(testRunner=VppTestRunner)