blob: a2d77d493de649435e6d7528f4ba6f6de19981f2 [file] [log] [blame]
Klement Sekera75e7d132017-09-20 08:26:30 +02001#!/usr/bin/env python
2import unittest
3from random import shuffle
4
5from framework import VppTestCase, VppTestRunner
6
7from scapy.packet import Raw
8from scapy.layers.l2 import Ether, GRE
9from scapy.layers.inet import IP, UDP
10from util import ppp, fragment_rfc791, fragment_rfc8200
11from vpp_punt_socket import VppUDSPuntSocket
12from scapy.layers.inet6 import IPv6, IPv6ExtHdrFragment, ICMPv6ParamProblem,\
13 ICMPv6TimeExceeded
14from vpp_gre_interface import VppGreInterface, VppGre6Interface
15from vpp_ip_route import VppIpRoute, VppRoutePath, DpoProto
16
17test_packet_count = 257
18
19
20class TestIPv4Reassembly(VppTestCase):
21 """ IPv4 Reassembly """
22
23 @classmethod
24 def setUpClass(cls):
25 super(TestIPv4Reassembly, cls).setUpClass()
26
27 cls.create_pg_interfaces([0])
28 cls.pg_if = cls.pg0
29
30 # setup all interfaces
31 for i in cls.pg_interfaces:
32 i.admin_up()
33 i.config_ip4()
34 i.resolve_arp()
35
36 cls.punt_port = 9999
37 cls.punt_socket = VppUDSPuntSocket(cls, cls.punt_port)
38
39 # packet sizes
40 cls.packet_sizes = [64, 512, 1518, 9018]
41 cls.padding = " abcdefghijklmn"
42 cls.create_stream(cls.packet_sizes)
43 cls.create_fragments()
44
45 def setUp(self):
46 """ Test setup - force timeout on existing reassemblies """
47 super(TestIPv4Reassembly, self).setUp()
48 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
49 expire_walk_interval_ms=10)
50 self.sleep(.25)
51 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
52 expire_walk_interval_ms=10000)
53
54 def tearDown(self):
55 super(TestIPv4Reassembly, self).tearDown()
56 self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
57
58 @classmethod
59 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
60 """Create input packet stream for defined interface.
61
62 :param list packet_sizes: Required packet sizes.
63 """
64 for i in range(0, packet_count):
65 info = cls.create_packet_info(cls.pg_if, cls.pg_if)
66 payload = cls.info_to_payload(info)
67 p = (Ether(dst=cls.pg_if.local_mac, src=cls.pg_if.remote_mac) /
68 IP(id=info.index, src=cls.pg_if.remote_ip4,
69 dst=cls.pg_if.local_ip4) /
70 UDP(sport=1234, dport=cls.punt_port) /
71 Raw(payload))
72 size = packet_sizes[(i // 2) % len(packet_sizes)]
73 cls.extend_packet(p, size, cls.padding)
74 info.data = p
75
76 @classmethod
77 def create_fragments(cls):
78 infos = cls._packet_infos
79 cls.pkt_infos = []
80 for index, info in infos.iteritems():
81 p = info.data
82 # self.logger.debug(ppp("Packet:", p.__class__(str(p))))
83 fragments_400 = fragment_rfc791(p, 400)
84 fragments_300 = fragment_rfc791(p, 300)
85 fragments_200 = [
86 x for f in fragments_400 for x in fragment_rfc791(f, 200)]
87 cls.pkt_infos.append(
88 (index, fragments_400, fragments_300, fragments_200))
89 cls.fragments_400 = [
90 x for (_, frags, _, _) in cls.pkt_infos for x in frags]
91 cls.fragments_300 = [
92 x for (_, _, frags, _) in cls.pkt_infos for x in frags]
93 cls.fragments_200 = [
94 x for (_, _, _, frags) in cls.pkt_infos for x in frags]
95 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
96 "%s 300-byte fragments and %s 200-byte fragments" %
97 (len(infos), len(cls.fragments_400),
98 len(cls.fragments_300), len(cls.fragments_200)))
99
100 def verify_capture(self, capture, dropped_packet_indexes=[]):
101 """Verify captured packet stream.
102
103 :param list capture: Captured packet stream.
104 """
105 info = None
106 seen = set()
107 for packet in capture:
108 try:
109 sw_if_index = packet['sw_if_index']
110 punt_action = packet['punt_action']
111 packet = Ether(packet['packet'])
112 self.logger.debug(ppp("Got packet from %s, action %s" %
113 (sw_if_index, punt_action), packet))
114 ip = packet[IP]
115 udp = packet[UDP]
116 payload_info = self.payload_to_info(str(packet[Raw]))
117 packet_index = payload_info.index
118 self.assertTrue(
119 packet_index not in dropped_packet_indexes,
120 ppp("Packet received, but should be dropped:", packet))
121 if packet_index in seen:
122 raise Exception(ppp("Duplicate packet received", packet))
123 seen.add(packet_index)
124 self.assertEqual(payload_info.dst, self.pg_if.sw_if_index)
125 info = self._packet_infos[packet_index]
126 self.assertTrue(info is not None)
127 self.assertEqual(packet_index, info.index)
128 saved_packet = info.data
129 self.assertEqual(ip.src, saved_packet[IP].src)
130 self.assertEqual(ip.dst, saved_packet[IP].dst)
131 self.assertEqual(udp.payload, saved_packet[UDP].payload)
132 except:
133 self.logger.error(ppp("Unexpected or invalid packet:", packet))
134 raise
135 for index in self._packet_infos:
136 self.assertTrue(index in seen or index in dropped_packet_indexes,
137 "Packet with packet_index %d not received" % index)
138
139 def test_reassembly(self):
140 """ basic reassembly """
141
142 self.pg_enable_capture()
143 self.pg_if.add_stream(self.fragments_200)
144 self.pg_start()
145
146 packets = self.punt_socket.wait_for_packets(len(self.pkt_infos))
147 self.verify_capture(packets)
148 self.pg_if.assert_nothing_captured()
149
150 # run it all again to verify correctness
151 self.pg_enable_capture()
152 self.pg_if.add_stream(self.fragments_200)
153 self.pg_start()
154
155 packets = self.punt_socket.wait_for_packets(len(self.pkt_infos))
156 self.verify_capture(packets)
157 self.pg_if.assert_nothing_captured()
158
159 def test_reversed(self):
160 """ reverse order reassembly """
161
162 fragments = list(self.fragments_200)
163 fragments.reverse()
164
165 self.pg_enable_capture()
166 self.pg_if.add_stream(fragments)
167 self.pg_start()
168
169 packets = self.punt_socket.wait_for_packets(len(self.packet_infos))
170 self.verify_capture(packets)
171 self.pg_if.assert_nothing_captured()
172
173 # run it all again to verify correctness
174 self.pg_enable_capture()
175 self.pg_if.add_stream(fragments)
176 self.pg_start()
177
178 packets = self.punt_socket.wait_for_packets(len(self.packet_infos))
179 self.verify_capture(packets)
180 self.pg_if.assert_nothing_captured()
181
182 def test_random(self):
183 """ random order reassembly """
184
185 fragments = list(self.fragments_200)
186 shuffle(fragments)
187
188 self.pg_enable_capture()
189 self.pg_if.add_stream(fragments)
190 self.pg_start()
191
192 packets = self.punt_socket.wait_for_packets(len(self.packet_infos))
193 self.verify_capture(packets)
194 self.pg_if.assert_nothing_captured()
195
196 # run it all again to verify correctness
197 self.pg_enable_capture()
198 self.pg_if.add_stream(fragments)
199 self.pg_start()
200
201 packets = self.punt_socket.wait_for_packets(len(self.packet_infos))
202 self.verify_capture(packets)
203 self.pg_if.assert_nothing_captured()
204
205 def test_duplicates(self):
206 """ duplicate fragments """
207
208 fragments = [
209 x for (_, frags, _, _) in self.pkt_infos
210 for x in frags
211 for _ in range(0, min(2, len(frags)))
212 ]
213
214 self.pg_enable_capture()
215 self.pg_if.add_stream(fragments)
216 self.pg_start()
217
218 packets = self.punt_socket.wait_for_packets(len(self.pkt_infos))
219 self.verify_capture(packets)
220 self.pg_if.assert_nothing_captured()
221
222 def test_overlap1(self):
223 """ overlapping fragments case #1 """
224
225 fragments = []
226 for _, _, frags_300, frags_200 in self.pkt_infos:
227 if len(frags_300) == 1:
228 fragments.extend(frags_300)
229 else:
230 for i, j in zip(frags_200, frags_300):
231 fragments.extend(i)
232 fragments.extend(j)
233
234 self.pg_enable_capture()
235 self.pg_if.add_stream(fragments)
236 self.pg_start()
237
238 packets = self.punt_socket.wait_for_packets(len(self.pkt_infos))
239 self.verify_capture(packets)
240 self.pg_if.assert_nothing_captured()
241
242 # run it all to verify correctness
243 self.pg_enable_capture()
244 self.pg_if.add_stream(fragments)
245 self.pg_start()
246
247 packets = self.punt_socket.wait_for_packets(len(self.pkt_infos))
248 self.verify_capture(packets)
249 self.pg_if.assert_nothing_captured()
250
251 def test_overlap2(self):
252 """ overlapping fragments case #2 """
253
254 fragments = []
255 for _, _, frags_300, frags_200 in self.pkt_infos:
256 if len(frags_300) == 1:
257 fragments.extend(frags_300)
258 else:
259 # care must be taken here so that there are no fragments
260 # received by vpp after reassembly is finished, otherwise
261 # new reassemblies will be started and packet generator will
262 # freak out when it detects unfreed buffers
263 zipped = zip(frags_300, frags_200)
264 for i, j in zipped[:-1]:
265 fragments.extend(i)
266 fragments.extend(j)
267 fragments.append(zipped[-1][0])
268
269 self.pg_enable_capture()
270 self.pg_if.add_stream(fragments)
271 self.pg_start()
272
273 packets = self.punt_socket.wait_for_packets(len(self.pkt_infos))
274 self.verify_capture(packets)
275 self.pg_if.assert_nothing_captured()
276
277 # run it all to verify correctness
278 self.pg_enable_capture()
279 self.pg_if.add_stream(fragments)
280 self.pg_start()
281
282 packets = self.punt_socket.wait_for_packets(len(self.pkt_infos))
283 self.verify_capture(packets)
284 self.pg_if.assert_nothing_captured()
285
286 def test_timeout_inline(self):
287 """ timeout (inline) """
288
289 dropped_packet_indexes = set(
290 index for (index, frags, _, _) in self.pkt_infos if len(frags) > 1
291 )
292
293 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
294 expire_walk_interval_ms=10000)
295
296 self.pg_enable_capture()
297 self.pg_if.add_stream(self.fragments_400)
298 self.pg_start()
299
300 packets = self.punt_socket.wait_for_packets(
301 len(self.pkt_infos) - len(dropped_packet_indexes))
302 self.verify_capture(packets, dropped_packet_indexes)
303 self.pg_if.assert_nothing_captured()
304
305 def test_timeout_cleanup(self):
306 """ timeout (cleanup) """
307
308 # whole packets + fragmented packets sans last fragment
309 fragments = [
310 x for (_, frags_400, _, _) in self.pkt_infos
311 for x in frags_400[:-1 if len(frags_400) > 1 else None]
312 ]
313
314 # last fragments for fragmented packets
315 fragments2 = [frags_400[-1]
316 for (_, frags_400, _, _) in self.pkt_infos
317 if len(frags_400) > 1]
318
319 dropped_packet_indexes = set(
320 index for (index, frags_400, _, _) in self.pkt_infos
321 if len(frags_400) > 1)
322
323 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
324 expire_walk_interval_ms=50)
325
326 self.pg_enable_capture()
327 self.pg_if.add_stream(fragments)
328 self.pg_start()
329
330 self.sleep(.25, "wait before sending rest of fragments")
331
332 self.pg_if.add_stream(fragments2)
333 self.pg_start()
334 self.sleep(.25, "wait for vpp to process packets")
335
336 packets = self.punt_socket.wait_for_packets(
337 len(self.pkt_infos) - len(dropped_packet_indexes))
338 self.verify_capture(packets, dropped_packet_indexes)
339 self.pg_if.assert_nothing_captured()
340
341 def test_disabled(self):
342 """ reassembly disabled """
343
344 dropped_packet_indexes = set(
345 index for (index, frags_400, _, _) in self.pkt_infos
346 if len(frags_400) > 1)
347
348 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
349 expire_walk_interval_ms=10000)
350
351 self.pg_enable_capture()
352 self.pg_if.add_stream(self.fragments_400)
353 self.pg_start()
354
355 packets = self.punt_socket.wait_for_packets(
356 len(self.pkt_infos) - len(dropped_packet_indexes))
357 self.verify_capture(packets, dropped_packet_indexes)
358 self.pg_if.assert_nothing_captured()
359
360
361class TestIPv6Reassembly(VppTestCase):
362 """ IPv6 Reassembly """
363
364 @classmethod
365 def setUpClass(cls):
366 super(TestIPv6Reassembly, cls).setUpClass()
367
368 cls.create_pg_interfaces([0])
369 cls.pg_if = cls.pg0
370
371 # setup all interfaces
372 for i in cls.pg_interfaces:
373 i.admin_up()
374 i.config_ip6()
375 i.resolve_ndp()
376
377 cls.punt_port = 9999
378 cls.punt_socket = VppUDSPuntSocket(cls, cls.punt_port, is_ip4=0)
379
380 # packet sizes
381 cls.packet_sizes = [64, 512, 1518, 9018]
382 cls.padding = " abcdefghijklmn"
383 cls.create_stream(cls.packet_sizes)
384 cls.create_fragments()
385
386 def setUp(self):
387 """ Test setup - force timeout on existing reassemblies """
388 super(TestIPv6Reassembly, self).setUp()
389 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
390 expire_walk_interval_ms=10, is_ip6=1)
391 self.sleep(.25)
392 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
393 expire_walk_interval_ms=10000, is_ip6=1)
394
395 def tearDown(self):
396 super(TestIPv6Reassembly, self).tearDown()
397 self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
398
399 @classmethod
400 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
401 """Create input packet stream for defined interface.
402
403 :param list packet_sizes: Required packet sizes.
404 """
405 for i in range(0, packet_count):
406 info = cls.create_packet_info(cls.pg_if, cls.pg_if)
407 payload = cls.info_to_payload(info)
408 p = (Ether(dst=cls.pg_if.local_mac, src=cls.pg_if.remote_mac) /
409 IPv6(src=cls.pg_if.remote_ip6,
410 dst=cls.pg_if.local_ip6) /
411 UDP(sport=1234, dport=cls.punt_port) /
412 Raw(payload))
413 size = packet_sizes[(i // 2) % len(packet_sizes)]
414 cls.extend_packet(p, size, cls.padding)
415 info.data = p
416
417 @classmethod
418 def create_fragments(cls):
419 infos = cls._packet_infos
420 cls.pkt_infos = []
421 for index, info in infos.iteritems():
422 p = info.data
423 # self.logger.debug(ppp("Packet:", p.__class__(str(p))))
424 fragments_400 = fragment_rfc8200(p, info.index, 400)
425 fragments_300 = fragment_rfc8200(p, info.index, 300)
426 cls.pkt_infos.append((index, fragments_400, fragments_300))
427 cls.fragments_400 = [
428 x for _, frags, _ in cls.pkt_infos for x in frags]
429 cls.fragments_300 = [
430 x for _, _, frags in cls.pkt_infos for x in frags]
431 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
432 "and %s 300-byte fragments" %
433 (len(infos), len(cls.fragments_400),
434 len(cls.fragments_300)))
435
436 def verify_capture(self, capture, dropped_packet_indexes=[]):
437 """Verify captured packet strea .
438
439 :param list capture: Captured packet stream.
440 """
441 info = None
442 seen = set()
443 for packet in capture:
444 try:
445 sw_if_index = packet['sw_if_index']
446 punt_action = packet['punt_action']
447 packet = Ether(packet['packet'])
448 self.logger.debug(ppp("Got packet from %s, action %s" %
449 (sw_if_index, punt_action), packet))
450 ip = packet[IPv6]
451 udp = packet[UDP]
452 payload_info = self.payload_to_info(str(packet[Raw]))
453 packet_index = payload_info.index
454 self.assertTrue(
455 packet_index not in dropped_packet_indexes,
456 ppp("Packet received, but should be dropped:", packet))
457 if packet_index in seen:
458 raise Exception(ppp("Duplicate packet received", packet))
459 seen.add(packet_index)
460 self.assertEqual(payload_info.dst, self.pg_if.sw_if_index)
461 info = self._packet_infos[packet_index]
462 self.assertTrue(info is not None)
463 self.assertEqual(packet_index, info.index)
464 saved_packet = info.data
465 self.assertEqual(ip.src, saved_packet[IPv6].src)
466 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
467 self.assertEqual(udp.payload, saved_packet[UDP].payload)
468 except:
469 self.logger.error(ppp("Unexpected or invalid packet:", packet))
470 raise
471 for index in self._packet_infos:
472 self.assertTrue(index in seen or index in dropped_packet_indexes,
473 "Packet with packet_index %d not received" % index)
474
475 def test_reassembly(self):
476 """ basic reassembly """
477
478 self.pg_enable_capture()
479 self.pg_if.add_stream(self.fragments_400)
480 self.pg_start()
481
482 packets = self.punt_socket.wait_for_packets(len(self.pkt_infos))
483 self.verify_capture(packets)
484 self.pg_if.assert_nothing_captured()
485
486 # run it all again to verify correctness
487 self.pg_enable_capture()
488 self.pg_if.add_stream(self.fragments_400)
489 self.pg_start()
490
491 packets = self.punt_socket.wait_for_packets(len(self.pkt_infos))
492 self.verify_capture(packets)
493 self.pg_if.assert_nothing_captured()
494
495 def test_reversed(self):
496 """ reverse order reassembly """
497
498 fragments = list(self.fragments_400)
499 fragments.reverse()
500
501 self.pg_enable_capture()
502 self.pg_if.add_stream(fragments)
503 self.pg_start()
504
505 packets = self.punt_socket.wait_for_packets(len(self.pkt_infos))
506 self.verify_capture(packets)
507 self.pg_if.assert_nothing_captured()
508
509 # run it all again to verify correctness
510 self.pg_enable_capture()
511 self.pg_if.add_stream(fragments)
512 self.pg_start()
513
514 packets = self.punt_socket.wait_for_packets(len(self.pkt_infos))
515 self.verify_capture(packets)
516 self.pg_if.assert_nothing_captured()
517
518 def test_random(self):
519 """ random order reassembly """
520
521 fragments = list(self.fragments_400)
522 shuffle(fragments)
523
524 self.pg_enable_capture()
525 self.pg_if.add_stream(fragments)
526 self.pg_start()
527
528 packets = self.punt_socket.wait_for_packets(len(self.pkt_infos))
529 self.verify_capture(packets)
530 self.pg_if.assert_nothing_captured()
531
532 # run it all again to verify correctness
533 self.pg_enable_capture()
534 self.pg_if.add_stream(fragments)
535 self.pg_start()
536
537 packets = self.punt_socket.wait_for_packets(len(self.pkt_infos))
538 self.verify_capture(packets)
539 self.pg_if.assert_nothing_captured()
540
541 def test_duplicates(self):
542 """ duplicate fragments """
543
544 fragments = [
545 x for (_, frags, _) in self.pkt_infos
546 for x in frags
547 for _ in range(0, min(2, len(frags)))
548 ]
549
550 self.pg_enable_capture()
551 self.pg_if.add_stream(fragments)
552 self.pg_start()
553
554 packets = self.punt_socket.wait_for_packets(len(self.pkt_infos))
555 self.verify_capture(packets)
556 self.pg_if.assert_nothing_captured()
557
558 def test_overlap1(self):
559 """ overlapping fragments case #1 """
560
561 fragments = []
562 for _, frags_400, frags_300 in self.pkt_infos:
563 if len(frags_300) == 1:
564 fragments.extend(frags_400)
565 else:
566 for i, j in zip(frags_300, frags_400):
567 fragments.extend(i)
568 fragments.extend(j)
569
570 dropped_packet_indexes = set(
571 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
572 )
573
574 self.pg_enable_capture()
575 self.pg_if.add_stream(fragments)
576 self.pg_start()
577
578 self.sleep(.1, "wait for vpp to process packets")
579 packets = self.punt_socket.wait_for_packets(
580 len(self.pkt_infos) - len(dropped_packet_indexes))
581 self.verify_capture(packets, dropped_packet_indexes)
582 self.pg_if.assert_nothing_captured()
583
584 def test_overlap2(self):
585 """ overlapping fragments case #2 """
586
587 fragments = []
588 for _, frags_400, frags_30 in self.pkt_infos:
589 if len(frags_400) == 1:
590 fragments.extend(frags_400)
591 else:
592 # care must be taken here so that there are no fragments
593 # received by vpp after reassembly is finished, otherwise
594 # new reassemblies will be started and packet generator will
595 # freak out when it detects unfreed buffers
596 zipped = zip(frags_400, frags_30)
597 for i, j in zipped[:-1]:
598 fragments.extend(i)
599 fragments.extend(j)
600 fragments.append(zipped[-1][0])
601
602 dropped_packet_indexes = set(
603 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
604 )
605
606 self.pg_enable_capture()
607 self.pg_if.add_stream(fragments)
608 self.pg_start()
609
610 self.sleep(.1, "wait for vpp to process packets")
611 packets = self.punt_socket.wait_for_packets(
612 len(self.pkt_infos) - len(dropped_packet_indexes))
613 self.verify_capture(packets, dropped_packet_indexes)
614 self.pg_if.assert_nothing_captured()
615
616 def test_timeout_inline(self):
617 """ timeout (inline) """
618
619 dropped_packet_indexes = set(
620 index for (index, frags, _) in self.pkt_infos if len(frags) > 1
621 )
622
623 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
624 expire_walk_interval_ms=10000, is_ip6=1)
625
626 self.pg_enable_capture()
627 self.pg_if.add_stream(self.fragments_400)
628 self.pg_start()
629
630 packets = self.punt_socket.wait_for_packets(
631 len(self.pkt_infos) - len(dropped_packet_indexes))
632 self.verify_capture(packets, dropped_packet_indexes)
633 pkts = self.pg_if.get_capture(
634 expected_count=len(dropped_packet_indexes))
635 for icmp in pkts:
636 self.assertIn(ICMPv6TimeExceeded, icmp)
637 self.assertIn(IPv6ExtHdrFragment, icmp)
638 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
639 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
640
641 def test_timeout_cleanup(self):
642 """ timeout (cleanup) """
643
644 # whole packets + fragmented packets sans last fragment
645 fragments = [
646 x for (_, frags_400, _) in self.pkt_infos
647 for x in frags_400[:-1 if len(frags_400) > 1 else None]
648 ]
649
650 # last fragments for fragmented packets
651 fragments2 = [frags_400[-1]
652 for (_, frags_400, _) in self.pkt_infos
653 if len(frags_400) > 1]
654
655 dropped_packet_indexes = set(
656 index for (index, frags_400, _) in self.pkt_infos
657 if len(frags_400) > 1)
658
659 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
660 expire_walk_interval_ms=50)
661
662 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
663 expire_walk_interval_ms=50, is_ip6=1)
664
665 self.pg_enable_capture()
666 self.pg_if.add_stream(fragments)
667 self.pg_start()
668
669 self.sleep(.25, "wait before sending rest of fragments")
670
671 self.pg_if.add_stream(fragments2)
672 self.pg_start()
673 self.sleep(.25, "wait for vpp to process packets")
674
675 packets = self.punt_socket.wait_for_packets(
676 len(self.pkt_infos) - len(dropped_packet_indexes))
677 self.verify_capture(packets, dropped_packet_indexes)
678 pkts = self.pg_if.get_capture(
679 expected_count=len(dropped_packet_indexes))
680 for icmp in pkts:
681 self.assertIn(ICMPv6TimeExceeded, icmp)
682 self.assertIn(IPv6ExtHdrFragment, icmp)
683 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
684 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
685
686 def test_disabled(self):
687 """ reassembly disabled """
688
689 dropped_packet_indexes = set(
690 index for (index, frags_400, _) in self.pkt_infos
691 if len(frags_400) > 1)
692
693 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
694 expire_walk_interval_ms=10000, is_ip6=1)
695
696 self.pg_enable_capture()
697 self.pg_if.add_stream(self.fragments_400)
698 self.pg_start()
699
700 packets = self.punt_socket.wait_for_packets(
701 len(self.pkt_infos) - len(dropped_packet_indexes))
702 self.verify_capture(packets, dropped_packet_indexes)
703 self.pg_if.assert_nothing_captured()
704
705 def test_missing_upper(self):
706 """ missing upper layer """
707 p = (Ether(dst=self.pg_if.local_mac, src=self.pg_if.remote_mac) /
708 IPv6(src=self.pg_if.remote_ip6,
709 dst=self.pg_if.local_ip6) /
710 UDP(sport=1234, dport=self.punt_port) /
711 Raw())
712 self.extend_packet(p, 1000, self.padding)
713 fragments = fragment_rfc8200(p, 1, 500)
714 bad_fragment = p.__class__(str(fragments[1]))
715 bad_fragment[IPv6ExtHdrFragment].nh = 59
716 bad_fragment[IPv6ExtHdrFragment].offset = 0
717 self.pg_enable_capture()
718 self.pg_if.add_stream([bad_fragment])
719 self.pg_start()
720 pkts = self.pg_if.get_capture(expected_count=1)
721 icmp = pkts[0]
722 self.assertIn(ICMPv6ParamProblem, icmp)
723 self.assert_equal(icmp[ICMPv6ParamProblem].code, 3, "ICMP code")
724
725 def test_invalid_frag_size(self):
726 """ fragment size not a multiple of 8 """
727 p = (Ether(dst=self.pg_if.local_mac, src=self.pg_if.remote_mac) /
728 IPv6(src=self.pg_if.remote_ip6,
729 dst=self.pg_if.local_ip6) /
730 UDP(sport=1234, dport=self.punt_port) /
731 Raw())
732 self.extend_packet(p, 1000, self.padding)
733 fragments = fragment_rfc8200(p, 1, 500)
734 bad_fragment = fragments[0]
735 self.extend_packet(bad_fragment, len(bad_fragment) + 5)
736 self.pg_enable_capture()
737 self.pg_if.add_stream([bad_fragment])
738 self.pg_start()
739 pkts = self.pg_if.get_capture(expected_count=1)
740 icmp = pkts[0]
741 self.assertIn(ICMPv6ParamProblem, icmp)
742 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
743
744 def test_invalid_packet_size(self):
745 """ total packet size > 65535 """
746 p = (Ether(dst=self.pg_if.local_mac, src=self.pg_if.remote_mac) /
747 IPv6(src=self.pg_if.remote_ip6,
748 dst=self.pg_if.local_ip6) /
749 UDP(sport=1234, dport=self.punt_port) /
750 Raw())
751 self.extend_packet(p, 1000, self.padding)
752 fragments = fragment_rfc8200(p, 1, 500)
753 bad_fragment = fragments[1]
754 bad_fragment[IPv6ExtHdrFragment].offset = 65500
755 self.pg_enable_capture()
756 self.pg_if.add_stream([bad_fragment])
757 self.pg_start()
758 pkts = self.pg_if.get_capture(expected_count=1)
759 icmp = pkts[0]
760 self.assertIn(ICMPv6ParamProblem, icmp)
761 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
762
763
764class TestFIFReassembly(VppTestCase):
765 """ Fragments in fragments reassembly """
766
767 @classmethod
768 def setUpClass(cls):
769 super(TestFIFReassembly, cls).setUpClass()
770
771 cls.create_pg_interfaces([0])
772 cls.pg_if = cls.pg0
773 cls.pg_if.admin_up()
774 cls.pg_if.config_ip4()
775 cls.pg_if.resolve_arp()
776 cls.pg_if.config_ip6()
777 cls.pg_if.resolve_ndp()
778
779 cls.punt_port = 9999
780 cls.punt4_socket = VppUDSPuntSocket(cls, cls.punt_port)
781 cls.punt6_socket = VppUDSPuntSocket(cls, cls.punt_port, is_ip4=0)
782 cls.packet_sizes = [64, 512, 1518, 9018]
783 cls.padding = " abcdefghijklmn"
784
785 def setUp(self):
786 """ Test setup - force timeout on existing reassemblies """
787 super(TestFIFReassembly, self).setUp()
788 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
789 expire_walk_interval_ms=10)
790 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
791 expire_walk_interval_ms=10, is_ip6=1)
792 self.sleep(.25)
793 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
794 expire_walk_interval_ms=10000)
795 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
796 expire_walk_interval_ms=10000, is_ip6=1)
797
798 def tearDown(self):
799 self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
800 self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
801 super(TestFIFReassembly, self).tearDown()
802
803 def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):
804 """Verify captured packet stream.
805
806 :param list capture: Captured packet stream.
807 """
808 info = None
809 seen = set()
810 for packet in capture:
811 try:
812 sw_if_index = packet['sw_if_index']
813 punt_action = packet['punt_action']
814 packet = Ether(packet['packet'])
815 self.logger.debug(ppp("Got packet from %s, action %s" %
816 (sw_if_index, punt_action), packet))
817 ip = packet[ip_class]
818 udp = packet[UDP]
819 payload_info = self.payload_to_info(str(packet[Raw]))
820 packet_index = payload_info.index
821 self.assertTrue(
822 packet_index not in dropped_packet_indexes,
823 ppp("Packet received, but should be dropped:", packet))
824 if packet_index in seen:
825 raise Exception(ppp("Duplicate packet received", packet))
826 seen.add(packet_index)
827 self.assertEqual(payload_info.dst, self.pg_if.sw_if_index)
828 info = self._packet_infos[packet_index]
829 self.assertTrue(info is not None)
830 self.assertEqual(packet_index, info.index)
831 saved_packet = info.data
832 self.assertEqual(ip.src, saved_packet[ip_class].src)
833 self.assertEqual(ip.dst, saved_packet[ip_class].dst)
834 self.assertEqual(udp.payload, saved_packet[UDP].payload)
835 except:
836 self.logger.error(ppp("Unexpected or invalid packet:", packet))
837 raise
838 for index in self._packet_infos:
839 self.assertTrue(index in seen or index in dropped_packet_indexes,
840 "Packet with packet_index %d not received" % index)
841
842 def test_fif4(self):
843 """ Fragments in fragments (4o4) """
844
845 # TODO this should be ideally in setUpClass, but then we hit a bug
846 # with VppIpRoute incorrectly reporting it's present when it's not
847 # so we need to manually remove the vpp config, thus we cannot have
848 # it shared for multiple test cases
849 self.tun_ip4 = "1.1.1.2"
850
851 self.gre4 = VppGreInterface(self, self.pg0.local_ip4, self.tun_ip4)
852 self.gre4.add_vpp_config()
853 self.gre4.admin_up()
854 self.gre4.config_ip4()
855
856 self.route4 = VppIpRoute(self, self.tun_ip4, 32,
857 [VppRoutePath(self.pg0.remote_ip4,
858 self.pg0.sw_if_index)])
859 self.route4.add_vpp_config()
860
861 self.reset_packet_infos()
862 for i in range(test_packet_count):
863 info = self.create_packet_info(self.pg0, self.pg0)
864 payload = self.info_to_payload(info)
865 p = (IP(id=i, src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
866 UDP(sport=1234, dport=self.punt_port) /
867 Raw(payload))
868 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
869 self.extend_packet(p, size, self.padding)
870 info.data = p
871
872 fragments = [x for _, p in self._packet_infos.iteritems()
873 for x in fragment_rfc791(p.data, 400)]
874
875 encapped_fragments = \
876 [Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
877 IP(src=self.tun_ip4, dst=self.pg0.local_ip4) /
878 GRE() /
879 p
880 for p in fragments]
881
882 fragmented_encapped_fragments = \
883 [x for p in encapped_fragments
884 for x in fragment_rfc791(p, 200)]
885
886 self.pg0.add_stream(fragmented_encapped_fragments)
887
888 self.pg_enable_capture(self.pg_interfaces)
889 self.pg_start()
890
891 self.pg0.assert_nothing_captured()
892 packets = self.punt4_socket.wait_for_packets(len(self._packet_infos))
893 self.verify_capture(packets, IP)
894
895 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
896 # so that it's query_vpp_config() works as it should
897 self.gre4.remove_vpp_config()
898
899 def test_fif6(self):
900 """ Fragments in fragments (6o6) """
901 # TODO this should be ideally in setUpClass, but then we hit a bug
902 # with VppIpRoute incorrectly reporting it's present when it's not
903 # so we need to manually remove the vpp config, thus we cannot have
904 # it shared for multiple test cases
905 self.tun_ip6 = "1002::1"
906
907 self.gre6 = VppGre6Interface(self, self.pg0.local_ip6, self.tun_ip6)
908 self.gre6.add_vpp_config()
909 self.gre6.admin_up()
910 self.gre6.config_ip6()
911
912 self.route6 = VppIpRoute(self, self.tun_ip6, 128,
913 [VppRoutePath(self.pg0.remote_ip6,
914 self.pg0.sw_if_index,
915 proto=DpoProto.DPO_PROTO_IP6)],
916 is_ip6=1)
917 self.route6.add_vpp_config()
918
919 self.reset_packet_infos()
920 for i in range(test_packet_count):
921 info = self.create_packet_info(self.pg0, self.pg0)
922 payload = self.info_to_payload(info)
923 p = (IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) /
924 UDP(sport=1234, dport=self.punt_port) /
925 Raw(payload))
926 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
927 self.extend_packet(p, size, self.padding)
928 info.data = p
929
930 fragments = [x for _, i in self._packet_infos.iteritems()
931 for x in fragment_rfc8200(
932 i.data, i.index, 400)]
933
934 encapped_fragments = \
935 [Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
936 IPv6(src=self.tun_ip6, dst=self.pg0.local_ip6) /
937 GRE() /
938 p
939 for p in fragments]
940
941 fragmented_encapped_fragments = \
942 [x for p in encapped_fragments for x in (
943 fragment_rfc8200(
944 p,
945 2 * len(self._packet_infos) + p[IPv6ExtHdrFragment].id,
946 200)
947 if IPv6ExtHdrFragment in p else [p]
948 )
949 ]
950
951 self.pg0.add_stream(fragmented_encapped_fragments)
952
953 self.pg_enable_capture(self.pg_interfaces)
954 self.pg_start()
955
956 self.pg0.assert_nothing_captured()
957 packets = self.punt6_socket.wait_for_packets(len(self._packet_infos))
958 self.verify_capture(packets, IPv6)
959
960 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
961 # so that it's query_vpp_config() works as it should
962 self.gre6.remove_vpp_config()
963
964
965if __name__ == '__main__':
966 unittest.main(testRunner=VppTestRunner)