tls: remove api boilerplate
[vpp.git] / test / test_reassembly.py
1 #!/usr/bin/env python
2
3 import six
4 import unittest
5 from random import shuffle, choice, randrange
6
7 from framework import VppTestCase, VppTestRunner
8
9 import scapy.compat
10 from scapy.packet import Raw
11 from scapy.layers.l2 import Ether, GRE
12 from scapy.layers.inet import IP, UDP, ICMP
13 from scapy.layers.inet6 import HBHOptUnknown, ICMPv6ParamProblem,\
14     ICMPv6TimeExceeded, IPv6, IPv6ExtHdrFragment, IPv6ExtHdrHopByHop
15 from framework import VppTestCase, VppTestRunner
16 from util import ppp, ppc, fragment_rfc791, fragment_rfc8200
17 from vpp_gre_interface import VppGreInterface
18 from vpp_ip import DpoProto
19 from vpp_ip_route import VppIpRoute, VppRoutePath, FibPathProto
20 from vpp_papi import VppEnum
21
22 # 35 is enough to have >257 400-byte fragments
23 test_packet_count = 35
24
25 # number of workers used for multi-worker test cases
26 worker_count = 3
27
28
29 class TestIPv4Reassembly(VppTestCase):
30     """ IPv4 Reassembly """
31
32     @classmethod
33     def setUpClass(cls):
34         super(TestIPv4Reassembly, cls).setUpClass()
35
36         cls.create_pg_interfaces([0, 1])
37         cls.src_if = cls.pg0
38         cls.dst_if = cls.pg1
39
40         # setup all interfaces
41         for i in cls.pg_interfaces:
42             i.admin_up()
43             i.config_ip4()
44             i.resolve_arp()
45
46         # packet sizes
47         cls.packet_sizes = [64, 512, 1518, 9018]
48         cls.padding = " abcdefghijklmn"
49         cls.create_stream(cls.packet_sizes)
50         cls.create_fragments()
51
52     @classmethod
53     def tearDownClass(cls):
54         super(TestIPv4Reassembly, cls).tearDownClass()
55
56     def setUp(self):
57         """ Test setup - force timeout on existing reassemblies """
58         super(TestIPv4Reassembly, self).setUp()
59         self.vapi.ip_reassembly_enable_disable(
60             sw_if_index=self.src_if.sw_if_index, enable_ip4=True)
61         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
62                                     max_reassembly_length=1000,
63                                     expire_walk_interval_ms=10)
64         self.sleep(.25)
65         self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
66                                     max_reassembly_length=1000,
67                                     expire_walk_interval_ms=10000)
68
69     def tearDown(self):
70         super(TestIPv4Reassembly, self).tearDown()
71
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"))
75
76     @classmethod
77     def create_stream(cls, packet_sizes, packet_count=test_packet_count):
78         """Create input packet stream
79
80         :param list packet_sizes: Required packet sizes.
81         """
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) /
89                  Raw(payload))
90             size = packet_sizes[(i // 2) % len(packet_sizes)]
91             cls.extend_packet(p, size, cls.padding)
92             info.data = p
93
94     @classmethod
95     def create_fragments(cls):
96         infos = cls._packet_infos
97         cls.pkt_infos = []
98         for index, info in six.iteritems(infos):
99             p = info.data
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)
104             fragments_200 = [
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)))
118
119     def verify_capture(self, capture, dropped_packet_indexes=[]):
120         """Verify captured packet stream.
121
122         :param list capture: Captured packet stream.
123         """
124         info = None
125         seen = set()
126         for packet in capture:
127             try:
128                 self.logger.debug(ppp("Got packet:", packet))
129                 ip = packet[IP]
130                 udp = packet[UDP]
131                 payload_info = self.payload_to_info(packet[Raw])
132                 packet_index = payload_info.index
133                 self.assertTrue(
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)
147             except Exception:
148                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
149                 raise
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)
153
154     def test_reassembly(self):
155         """ basic reassembly """
156
157         self.pg_enable_capture()
158         self.src_if.add_stream(self.fragments_200)
159         self.pg_start()
160
161         packets = self.dst_if.get_capture(len(self.pkt_infos))
162         self.verify_capture(packets)
163         self.src_if.assert_nothing_captured()
164
165         # run it all again to verify correctness
166         self.pg_enable_capture()
167         self.src_if.add_stream(self.fragments_200)
168         self.pg_start()
169
170         packets = self.dst_if.get_capture(len(self.pkt_infos))
171         self.verify_capture(packets)
172         self.src_if.assert_nothing_captured()
173
174     def test_reversed(self):
175         """ reverse order reassembly """
176
177         fragments = list(self.fragments_200)
178         fragments.reverse()
179
180         self.pg_enable_capture()
181         self.src_if.add_stream(fragments)
182         self.pg_start()
183
184         packets = self.dst_if.get_capture(len(self.packet_infos))
185         self.verify_capture(packets)
186         self.src_if.assert_nothing_captured()
187
188         # run it all again to verify correctness
189         self.pg_enable_capture()
190         self.src_if.add_stream(fragments)
191         self.pg_start()
192
193         packets = self.dst_if.get_capture(len(self.packet_infos))
194         self.verify_capture(packets)
195         self.src_if.assert_nothing_captured()
196
197     def test_long_fragment_chain(self):
198         """ long fragment chain """
199
200         error_cnt_str = \
201             "/err/ip4-full-reassembly-feature/fragment chain too long (drop)"
202
203         error_cnt = self.statistics.get_err_counter(error_cnt_str)
204
205         self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
206                                     max_reassembly_length=3,
207                                     expire_walk_interval_ms=50)
208
209         p1 = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
210               IP(id=1000, src=self.src_if.remote_ip4,
211                  dst=self.dst_if.remote_ip4) /
212               UDP(sport=1234, dport=5678) /
213               Raw("X" * 1000))
214         p2 = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
215               IP(id=1001, src=self.src_if.remote_ip4,
216                  dst=self.dst_if.remote_ip4) /
217               UDP(sport=1234, dport=5678) /
218               Raw("X" * 1000))
219         frags = fragment_rfc791(p1, 200) + fragment_rfc791(p2, 500)
220
221         self.pg_enable_capture()
222         self.src_if.add_stream(frags)
223         self.pg_start()
224
225         self.dst_if.get_capture(1)
226         self.assert_error_counter_equal(error_cnt_str, error_cnt + 1)
227
228     def test_5737(self):
229         """ fragment length + ip header size > 65535 """
230         self.vapi.cli("clear errors")
231         raw = ('E\x00\x00\x88,\xf8\x1f\xfe@\x01\x98\x00\xc0\xa8\n-\xc0\xa8\n'
232                '\x01\x08\x00\xf0J\xed\xcb\xf1\xf5Test-group: IPv4.IPv4.ipv4-'
233                'message.Ethernet-Payload.IPv4-Packet.IPv4-Header.Fragment-Of'
234                'fset; Test-case: 5737')
235
236         malformed_packet = (Ether(dst=self.src_if.local_mac,
237                                   src=self.src_if.remote_mac) /
238                             IP(raw))
239         p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
240              IP(id=1000, src=self.src_if.remote_ip4,
241                 dst=self.dst_if.remote_ip4) /
242              UDP(sport=1234, dport=5678) /
243              Raw("X" * 1000))
244         valid_fragments = fragment_rfc791(p, 400)
245
246         self.pg_enable_capture()
247         self.src_if.add_stream([malformed_packet] + valid_fragments)
248         self.pg_start()
249
250         self.dst_if.get_capture(1)
251         self.logger.debug(self.vapi.ppcli("show error"))
252         self.assert_packet_counter_equal("ip4-full-reassembly-feature", 1)
253         # TODO remove above, uncomment below once clearing of counters
254         # is supported
255         # self.assert_packet_counter_equal(
256         #     "/err/ip4-full-reassembly-feature/malformed packets", 1)
257
258     def test_44924(self):
259         """ compress tiny fragments """
260         packets = [(Ether(dst=self.src_if.local_mac,
261                           src=self.src_if.remote_mac) /
262                     IP(id=24339, flags="MF", frag=0, ttl=64,
263                        src=self.src_if.remote_ip4,
264                        dst=self.dst_if.remote_ip4) /
265                     ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
266                     Raw(load='Test-group: IPv4')),
267                    (Ether(dst=self.src_if.local_mac,
268                           src=self.src_if.remote_mac) /
269                     IP(id=24339, flags="MF", frag=3, ttl=64,
270                        src=self.src_if.remote_ip4,
271                        dst=self.dst_if.remote_ip4) /
272                     ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
273                     Raw(load='.IPv4.Fragmentation.vali')),
274                    (Ether(dst=self.src_if.local_mac,
275                           src=self.src_if.remote_mac) /
276                     IP(id=24339, frag=6, ttl=64,
277                        src=self.src_if.remote_ip4,
278                        dst=self.dst_if.remote_ip4) /
279                     ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
280                     Raw(load='d; Test-case: 44924'))
281                    ]
282
283         self.pg_enable_capture()
284         self.src_if.add_stream(packets)
285         self.pg_start()
286
287         self.dst_if.get_capture(1)
288
289     def test_frag_1(self):
290         """ fragment of size 1 """
291         self.vapi.cli("clear errors")
292         malformed_packets = [(Ether(dst=self.src_if.local_mac,
293                                     src=self.src_if.remote_mac) /
294                               IP(id=7, len=21, flags="MF", frag=0, ttl=64,
295                                  src=self.src_if.remote_ip4,
296                                  dst=self.dst_if.remote_ip4) /
297                               ICMP(type="echo-request")),
298                              (Ether(dst=self.src_if.local_mac,
299                                     src=self.src_if.remote_mac) /
300                               IP(id=7, len=21, frag=1, ttl=64,
301                                  src=self.src_if.remote_ip4,
302                                  dst=self.dst_if.remote_ip4) /
303                               Raw(load='\x08')),
304                              ]
305
306         p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
307              IP(id=1000, src=self.src_if.remote_ip4,
308                 dst=self.dst_if.remote_ip4) /
309              UDP(sport=1234, dport=5678) /
310              Raw("X" * 1000))
311         valid_fragments = fragment_rfc791(p, 400)
312
313         self.pg_enable_capture()
314         self.src_if.add_stream(malformed_packets + valid_fragments)
315         self.pg_start()
316
317         self.dst_if.get_capture(1)
318
319         self.assert_packet_counter_equal("ip4-full-reassembly-feature", 1)
320         # TODO remove above, uncomment below once clearing of counters
321         # is supported
322         # self.assert_packet_counter_equal(
323         #     "/err/ip4-full-reassembly-feature/malformed packets", 1)
324
325     def test_random(self):
326         """ random order reassembly """
327
328         fragments = list(self.fragments_200)
329         shuffle(fragments)
330
331         self.pg_enable_capture()
332         self.src_if.add_stream(fragments)
333         self.pg_start()
334
335         packets = self.dst_if.get_capture(len(self.packet_infos))
336         self.verify_capture(packets)
337         self.src_if.assert_nothing_captured()
338
339         # run it all again to verify correctness
340         self.pg_enable_capture()
341         self.src_if.add_stream(fragments)
342         self.pg_start()
343
344         packets = self.dst_if.get_capture(len(self.packet_infos))
345         self.verify_capture(packets)
346         self.src_if.assert_nothing_captured()
347
348     def test_duplicates(self):
349         """ duplicate fragments """
350
351         fragments = [
352             x for (_, frags, _, _) in self.pkt_infos
353             for x in frags
354             for _ in range(0, min(2, len(frags)))
355         ]
356
357         self.pg_enable_capture()
358         self.src_if.add_stream(fragments)
359         self.pg_start()
360
361         packets = self.dst_if.get_capture(len(self.pkt_infos))
362         self.verify_capture(packets)
363         self.src_if.assert_nothing_captured()
364
365     def test_overlap1(self):
366         """ overlapping fragments case #1 """
367
368         fragments = []
369         for _, _, frags_300, frags_200 in self.pkt_infos:
370             if len(frags_300) == 1:
371                 fragments.extend(frags_300)
372             else:
373                 for i, j in zip(frags_200, frags_300):
374                     fragments.extend(i)
375                     fragments.extend(j)
376
377         self.pg_enable_capture()
378         self.src_if.add_stream(fragments)
379         self.pg_start()
380
381         packets = self.dst_if.get_capture(len(self.pkt_infos))
382         self.verify_capture(packets)
383         self.src_if.assert_nothing_captured()
384
385         # run it all to verify correctness
386         self.pg_enable_capture()
387         self.src_if.add_stream(fragments)
388         self.pg_start()
389
390         packets = self.dst_if.get_capture(len(self.pkt_infos))
391         self.verify_capture(packets)
392         self.src_if.assert_nothing_captured()
393
394     def test_overlap2(self):
395         """ overlapping fragments case #2 """
396
397         fragments = []
398         for _, _, frags_300, frags_200 in self.pkt_infos:
399             if len(frags_300) == 1:
400                 fragments.extend(frags_300)
401             else:
402                 # care must be taken here so that there are no fragments
403                 # received by vpp after reassembly is finished, otherwise
404                 # new reassemblies will be started and packet generator will
405                 # freak out when it detects unfreed buffers
406                 zipped = zip(frags_300, frags_200)
407                 for i, j in zipped:
408                     fragments.extend(i)
409                     fragments.extend(j)
410                 fragments.pop()
411
412         self.pg_enable_capture()
413         self.src_if.add_stream(fragments)
414         self.pg_start()
415
416         packets = self.dst_if.get_capture(len(self.pkt_infos))
417         self.verify_capture(packets)
418         self.src_if.assert_nothing_captured()
419
420         # run it all to verify correctness
421         self.pg_enable_capture()
422         self.src_if.add_stream(fragments)
423         self.pg_start()
424
425         packets = self.dst_if.get_capture(len(self.pkt_infos))
426         self.verify_capture(packets)
427         self.src_if.assert_nothing_captured()
428
429     def test_timeout_inline(self):
430         """ timeout (inline) """
431
432         dropped_packet_indexes = set(
433             index for (index, frags, _, _) in self.pkt_infos if len(frags) > 1
434         )
435
436         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
437                                     max_reassembly_length=3,
438                                     expire_walk_interval_ms=10000)
439
440         self.pg_enable_capture()
441         self.src_if.add_stream(self.fragments_400)
442         self.pg_start()
443
444         packets = self.dst_if.get_capture(
445             len(self.pkt_infos) - len(dropped_packet_indexes))
446         self.verify_capture(packets, dropped_packet_indexes)
447         self.src_if.assert_nothing_captured()
448
449     def test_timeout_cleanup(self):
450         """ timeout (cleanup) """
451
452         # whole packets + fragmented packets sans last fragment
453         fragments = [
454             x for (_, frags_400, _, _) in self.pkt_infos
455             for x in frags_400[:-1 if len(frags_400) > 1 else None]
456         ]
457
458         # last fragments for fragmented packets
459         fragments2 = [frags_400[-1]
460                       for (_, frags_400, _, _) in self.pkt_infos
461                       if len(frags_400) > 1]
462
463         dropped_packet_indexes = set(
464             index for (index, frags_400, _, _) in self.pkt_infos
465             if len(frags_400) > 1)
466
467         self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
468                                     max_reassembly_length=1000,
469                                     expire_walk_interval_ms=50)
470
471         self.pg_enable_capture()
472         self.src_if.add_stream(fragments)
473         self.pg_start()
474
475         self.sleep(.25, "wait before sending rest of fragments")
476
477         self.src_if.add_stream(fragments2)
478         self.pg_start()
479
480         packets = self.dst_if.get_capture(
481             len(self.pkt_infos) - len(dropped_packet_indexes))
482         self.verify_capture(packets, dropped_packet_indexes)
483         self.src_if.assert_nothing_captured()
484
485     def test_disabled(self):
486         """ reassembly disabled """
487
488         dropped_packet_indexes = set(
489             index for (index, frags_400, _, _) in self.pkt_infos
490             if len(frags_400) > 1)
491
492         self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
493                                     max_reassembly_length=3,
494                                     expire_walk_interval_ms=10000)
495
496         self.pg_enable_capture()
497         self.src_if.add_stream(self.fragments_400)
498         self.pg_start()
499
500         packets = self.dst_if.get_capture(
501             len(self.pkt_infos) - len(dropped_packet_indexes))
502         self.verify_capture(packets, dropped_packet_indexes)
503         self.src_if.assert_nothing_captured()
504
505
506 class TestIPv4SVReassembly(VppTestCase):
507     """ IPv4 Shallow Virtual Reassembly """
508
509     @classmethod
510     def setUpClass(cls):
511         super(TestIPv4SVReassembly, cls).setUpClass()
512
513         cls.create_pg_interfaces([0, 1])
514         cls.src_if = cls.pg0
515         cls.dst_if = cls.pg1
516
517         # setup all interfaces
518         for i in cls.pg_interfaces:
519             i.admin_up()
520             i.config_ip4()
521             i.resolve_arp()
522
523     def setUp(self):
524         """ Test setup - force timeout on existing reassemblies """
525         super(TestIPv4SVReassembly, self).setUp()
526         self.vapi.ip_reassembly_enable_disable(
527             sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
528             type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
529         self.vapi.ip_reassembly_set(
530             timeout_ms=0, max_reassemblies=1000,
531             max_reassembly_length=1000,
532             type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
533             expire_walk_interval_ms=10)
534         self.sleep(.25)
535         self.vapi.ip_reassembly_set(
536             timeout_ms=1000000, max_reassemblies=1000,
537             max_reassembly_length=1000,
538             type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
539             expire_walk_interval_ms=10000)
540
541     def tearDown(self):
542         super(TestIPv4SVReassembly, self).tearDown()
543         self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
544         self.logger.debug(self.vapi.ppcli("show buffers"))
545
546     def test_basic(self):
547         """ basic reassembly """
548         payload_len = 1000
549         payload = ""
550         counter = 0
551         while len(payload) < payload_len:
552             payload += "%u " % counter
553             counter += 1
554
555         p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
556              IP(id=1, src=self.src_if.remote_ip4,
557                 dst=self.dst_if.remote_ip4) /
558              UDP(sport=1234, dport=5678) /
559              Raw(payload))
560         fragments = fragment_rfc791(p, payload_len/4)
561
562         # send fragment #2 - should be cached inside reassembly
563         self.pg_enable_capture()
564         self.src_if.add_stream(fragments[1])
565         self.pg_start()
566         self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
567         self.logger.debug(self.vapi.ppcli("show buffers"))
568         self.logger.debug(self.vapi.ppcli("show trace"))
569         self.dst_if.assert_nothing_captured()
570
571         # send fragment #1 - reassembly is finished now and both fragments
572         # forwarded
573         self.pg_enable_capture()
574         self.src_if.add_stream(fragments[0])
575         self.pg_start()
576         self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
577         self.logger.debug(self.vapi.ppcli("show buffers"))
578         self.logger.debug(self.vapi.ppcli("show trace"))
579         c = self.dst_if.get_capture(2)
580         for sent, recvd in zip([fragments[1], fragments[0]], c):
581             self.assertEqual(sent[IP].src, recvd[IP].src)
582             self.assertEqual(sent[IP].dst, recvd[IP].dst)
583             self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
584
585         # send rest of fragments - should be immediately forwarded
586         self.pg_enable_capture()
587         self.src_if.add_stream(fragments[2:])
588         self.pg_start()
589         c = self.dst_if.get_capture(len(fragments[2:]))
590         for sent, recvd in zip(fragments[2:], c):
591             self.assertEqual(sent[IP].src, recvd[IP].src)
592             self.assertEqual(sent[IP].dst, recvd[IP].dst)
593             self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
594
595     def test_timeout(self):
596         """ reassembly timeout """
597         payload_len = 1000
598         payload = ""
599         counter = 0
600         while len(payload) < payload_len:
601             payload += "%u " % counter
602             counter += 1
603
604         p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
605              IP(id=1, src=self.src_if.remote_ip4,
606                 dst=self.dst_if.remote_ip4) /
607              UDP(sport=1234, dport=5678) /
608              Raw(payload))
609         fragments = fragment_rfc791(p, payload_len/4)
610
611         self.vapi.ip_reassembly_set(
612             timeout_ms=100, max_reassemblies=1000,
613             max_reassembly_length=1000,
614             expire_walk_interval_ms=50,
615             type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
616
617         # send fragments #2 and #1 - should be forwarded
618         self.pg_enable_capture()
619         self.src_if.add_stream(fragments[0:2])
620         self.pg_start()
621         self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
622         self.logger.debug(self.vapi.ppcli("show buffers"))
623         self.logger.debug(self.vapi.ppcli("show trace"))
624         c = self.dst_if.get_capture(2)
625         for sent, recvd in zip([fragments[1], fragments[0]], c):
626             self.assertEqual(sent[IP].src, recvd[IP].src)
627             self.assertEqual(sent[IP].dst, recvd[IP].dst)
628             self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
629
630         # wait for cleanup
631         self.sleep(.25, "wait before sending rest of fragments")
632
633         # send rest of fragments - shouldn't be forwarded
634         self.pg_enable_capture()
635         self.src_if.add_stream(fragments[2:])
636         self.pg_start()
637         self.dst_if.assert_nothing_captured()
638
639     def test_lru(self):
640         """ reassembly reuses LRU element """
641
642         self.vapi.ip_reassembly_set(
643             timeout_ms=1000000, max_reassemblies=1,
644             max_reassembly_length=1000,
645             type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
646             expire_walk_interval_ms=10000)
647
648         payload_len = 1000
649         payload = ""
650         counter = 0
651         while len(payload) < payload_len:
652             payload += "%u " % counter
653             counter += 1
654
655         packet_count = 10
656
657         fragments = [f
658                      for i in range(packet_count)
659                      for p in (Ether(dst=self.src_if.local_mac,
660                                      src=self.src_if.remote_mac) /
661                                IP(id=i, src=self.src_if.remote_ip4,
662                                    dst=self.dst_if.remote_ip4) /
663                                UDP(sport=1234, dport=5678) /
664                                Raw(payload))
665                      for f in fragment_rfc791(p, payload_len/4)]
666
667         self.pg_enable_capture()
668         self.src_if.add_stream(fragments)
669         self.pg_start()
670         c = self.dst_if.get_capture(len(fragments))
671         for sent, recvd in zip(fragments, c):
672             self.assertEqual(sent[IP].src, recvd[IP].src)
673             self.assertEqual(sent[IP].dst, recvd[IP].dst)
674             self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
675
676
677 class TestIPv4MWReassembly(VppTestCase):
678     """ IPv4 Reassembly (multiple workers) """
679     worker_config = "workers %d" % worker_count
680
681     @classmethod
682     def setUpClass(cls):
683         super(TestIPv4MWReassembly, cls).setUpClass()
684
685         cls.create_pg_interfaces(range(worker_count+1))
686         cls.src_if = cls.pg0
687         cls.send_ifs = cls.pg_interfaces[:-1]
688         cls.dst_if = cls.pg_interfaces[-1]
689
690         # setup all interfaces
691         for i in cls.pg_interfaces:
692             i.admin_up()
693             i.config_ip4()
694             i.resolve_arp()
695
696         # packets sizes reduced here because we are generating packets without
697         # Ethernet headers, which are added later (diff fragments go via
698         # different interfaces)
699         cls.packet_sizes = [64-len(Ether()), 512-len(Ether()),
700                             1518-len(Ether()), 9018-len(Ether())]
701         cls.padding = " abcdefghijklmn"
702         cls.create_stream(cls.packet_sizes)
703         cls.create_fragments()
704
705     @classmethod
706     def tearDownClass(cls):
707         super(TestIPv4MWReassembly, cls).tearDownClass()
708
709     def setUp(self):
710         """ Test setup - force timeout on existing reassemblies """
711         super(TestIPv4MWReassembly, self).setUp()
712         for intf in self.send_ifs:
713             self.vapi.ip_reassembly_enable_disable(
714                 sw_if_index=intf.sw_if_index, enable_ip4=True)
715         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
716                                     max_reassembly_length=1000,
717                                     expire_walk_interval_ms=10)
718         self.sleep(.25)
719         self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
720                                     max_reassembly_length=1000,
721                                     expire_walk_interval_ms=10000)
722
723     def tearDown(self):
724         super(TestIPv4MWReassembly, self).tearDown()
725
726     def show_commands_at_teardown(self):
727         self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
728         self.logger.debug(self.vapi.ppcli("show buffers"))
729
730     @classmethod
731     def create_stream(cls, packet_sizes, packet_count=test_packet_count):
732         """Create input packet stream
733
734         :param list packet_sizes: Required packet sizes.
735         """
736         for i in range(0, packet_count):
737             info = cls.create_packet_info(cls.src_if, cls.src_if)
738             payload = cls.info_to_payload(info)
739             p = (IP(id=info.index, src=cls.src_if.remote_ip4,
740                     dst=cls.dst_if.remote_ip4) /
741                  UDP(sport=1234, dport=5678) /
742                  Raw(payload))
743             size = packet_sizes[(i // 2) % len(packet_sizes)]
744             cls.extend_packet(p, size, cls.padding)
745             info.data = p
746
747     @classmethod
748     def create_fragments(cls):
749         infos = cls._packet_infos
750         cls.pkt_infos = []
751         for index, info in six.iteritems(infos):
752             p = info.data
753             # cls.logger.debug(ppp("Packet:",
754             #                      p.__class__(scapy.compat.raw(p))))
755             fragments_400 = fragment_rfc791(p, 400)
756             cls.pkt_infos.append((index, fragments_400))
757         cls.fragments_400 = [
758             x for (_, frags) in cls.pkt_infos for x in frags]
759         cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, " %
760                          (len(infos), len(cls.fragments_400)))
761
762     def verify_capture(self, capture, dropped_packet_indexes=[]):
763         """Verify captured packet stream.
764
765         :param list capture: Captured packet stream.
766         """
767         info = None
768         seen = set()
769         for packet in capture:
770             try:
771                 self.logger.debug(ppp("Got packet:", packet))
772                 ip = packet[IP]
773                 udp = packet[UDP]
774                 payload_info = self.payload_to_info(packet[Raw])
775                 packet_index = payload_info.index
776                 self.assertTrue(
777                     packet_index not in dropped_packet_indexes,
778                     ppp("Packet received, but should be dropped:", packet))
779                 if packet_index in seen:
780                     raise Exception(ppp("Duplicate packet received", packet))
781                 seen.add(packet_index)
782                 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
783                 info = self._packet_infos[packet_index]
784                 self.assertTrue(info is not None)
785                 self.assertEqual(packet_index, info.index)
786                 saved_packet = info.data
787                 self.assertEqual(ip.src, saved_packet[IP].src)
788                 self.assertEqual(ip.dst, saved_packet[IP].dst)
789                 self.assertEqual(udp.payload, saved_packet[UDP].payload)
790             except Exception:
791                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
792                 raise
793         for index in self._packet_infos:
794             self.assertTrue(index in seen or index in dropped_packet_indexes,
795                             "Packet with packet_index %d not received" % index)
796
797     def send_packets(self, packets):
798         for counter in range(worker_count):
799             if 0 == len(packets[counter]):
800                 continue
801             send_if = self.send_ifs[counter]
802             send_if.add_stream(
803                 (Ether(dst=send_if.local_mac, src=send_if.remote_mac) / x
804                  for x in packets[counter]),
805                 worker=counter)
806         self.pg_start()
807
808     def test_worker_conflict(self):
809         """ 1st and FO=0 fragments on different workers """
810
811         # in first wave we send fragments which don't start at offset 0
812         # then we send fragments with offset 0 on a different thread
813         # then the rest of packets on a random thread
814         first_packets = [[] for n in range(worker_count)]
815         second_packets = [[] for n in range(worker_count)]
816         rest_of_packets = [[] for n in range(worker_count)]
817         for (_, p) in self.pkt_infos:
818             wi = randrange(worker_count)
819             second_packets[wi].append(p[0])
820             if len(p) <= 1:
821                 continue
822             wi2 = wi
823             while wi2 == wi:
824                 wi2 = randrange(worker_count)
825             first_packets[wi2].append(p[1])
826             wi3 = randrange(worker_count)
827             rest_of_packets[wi3].extend(p[2:])
828
829         self.pg_enable_capture()
830         self.send_packets(first_packets)
831         self.send_packets(second_packets)
832         self.send_packets(rest_of_packets)
833
834         packets = self.dst_if.get_capture(len(self.pkt_infos))
835         self.verify_capture(packets)
836         for send_if in self.send_ifs:
837             send_if.assert_nothing_captured()
838
839         self.pg_enable_capture()
840         self.send_packets(first_packets)
841         self.send_packets(second_packets)
842         self.send_packets(rest_of_packets)
843
844         packets = self.dst_if.get_capture(len(self.pkt_infos))
845         self.verify_capture(packets)
846         for send_if in self.send_ifs:
847             send_if.assert_nothing_captured()
848
849
850 class TestIPv6Reassembly(VppTestCase):
851     """ IPv6 Reassembly """
852
853     @classmethod
854     def setUpClass(cls):
855         super(TestIPv6Reassembly, cls).setUpClass()
856
857         cls.create_pg_interfaces([0, 1])
858         cls.src_if = cls.pg0
859         cls.dst_if = cls.pg1
860
861         # setup all interfaces
862         for i in cls.pg_interfaces:
863             i.admin_up()
864             i.config_ip6()
865             i.resolve_ndp()
866
867         # packet sizes
868         cls.packet_sizes = [64, 512, 1518, 9018]
869         cls.padding = " abcdefghijklmn"
870         cls.create_stream(cls.packet_sizes)
871         cls.create_fragments()
872
873     @classmethod
874     def tearDownClass(cls):
875         super(TestIPv6Reassembly, cls).tearDownClass()
876
877     def setUp(self):
878         """ Test setup - force timeout on existing reassemblies """
879         super(TestIPv6Reassembly, self).setUp()
880         self.vapi.ip_reassembly_enable_disable(
881             sw_if_index=self.src_if.sw_if_index, enable_ip6=True)
882         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
883                                     max_reassembly_length=1000,
884                                     expire_walk_interval_ms=10, is_ip6=1)
885         self.sleep(.25)
886         self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
887                                     max_reassembly_length=1000,
888                                     expire_walk_interval_ms=10000, is_ip6=1)
889         self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
890         self.logger.debug(self.vapi.ppcli("show buffers"))
891
892     def tearDown(self):
893         super(TestIPv6Reassembly, self).tearDown()
894
895     def show_commands_at_teardown(self):
896         self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
897         self.logger.debug(self.vapi.ppcli("show buffers"))
898
899     @classmethod
900     def create_stream(cls, packet_sizes, packet_count=test_packet_count):
901         """Create input packet stream for defined interface.
902
903         :param list packet_sizes: Required packet sizes.
904         """
905         for i in range(0, packet_count):
906             info = cls.create_packet_info(cls.src_if, cls.src_if)
907             payload = cls.info_to_payload(info)
908             p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
909                  IPv6(src=cls.src_if.remote_ip6,
910                       dst=cls.dst_if.remote_ip6) /
911                  UDP(sport=1234, dport=5678) /
912                  Raw(payload))
913             size = packet_sizes[(i // 2) % len(packet_sizes)]
914             cls.extend_packet(p, size, cls.padding)
915             info.data = p
916
917     @classmethod
918     def create_fragments(cls):
919         infos = cls._packet_infos
920         cls.pkt_infos = []
921         for index, info in six.iteritems(infos):
922             p = info.data
923             # cls.logger.debug(ppp("Packet:",
924             #                      p.__class__(scapy.compat.raw(p))))
925             fragments_400 = fragment_rfc8200(p, info.index, 400)
926             fragments_300 = fragment_rfc8200(p, info.index, 300)
927             cls.pkt_infos.append((index, fragments_400, fragments_300))
928         cls.fragments_400 = [
929             x for _, frags, _ in cls.pkt_infos for x in frags]
930         cls.fragments_300 = [
931             x for _, _, frags in cls.pkt_infos for x in frags]
932         cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
933                          "and %s 300-byte fragments" %
934                          (len(infos), len(cls.fragments_400),
935                              len(cls.fragments_300)))
936
937     def verify_capture(self, capture, dropped_packet_indexes=[]):
938         """Verify captured packet strea .
939
940         :param list capture: Captured packet stream.
941         """
942         info = None
943         seen = set()
944         for packet in capture:
945             try:
946                 self.logger.debug(ppp("Got packet:", packet))
947                 ip = packet[IPv6]
948                 udp = packet[UDP]
949                 payload_info = self.payload_to_info(packet[Raw])
950                 packet_index = payload_info.index
951                 self.assertTrue(
952                     packet_index not in dropped_packet_indexes,
953                     ppp("Packet received, but should be dropped:", packet))
954                 if packet_index in seen:
955                     raise Exception(ppp("Duplicate packet received", packet))
956                 seen.add(packet_index)
957                 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
958                 info = self._packet_infos[packet_index]
959                 self.assertTrue(info is not None)
960                 self.assertEqual(packet_index, info.index)
961                 saved_packet = info.data
962                 self.assertEqual(ip.src, saved_packet[IPv6].src)
963                 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
964                 self.assertEqual(udp.payload, saved_packet[UDP].payload)
965             except Exception:
966                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
967                 raise
968         for index in self._packet_infos:
969             self.assertTrue(index in seen or index in dropped_packet_indexes,
970                             "Packet with packet_index %d not received" % index)
971
972     def test_reassembly(self):
973         """ basic reassembly """
974
975         self.pg_enable_capture()
976         self.src_if.add_stream(self.fragments_400)
977         self.pg_start()
978
979         packets = self.dst_if.get_capture(len(self.pkt_infos))
980         self.verify_capture(packets)
981         self.src_if.assert_nothing_captured()
982
983         # run it all again to verify correctness
984         self.pg_enable_capture()
985         self.src_if.add_stream(self.fragments_400)
986         self.pg_start()
987
988         packets = self.dst_if.get_capture(len(self.pkt_infos))
989         self.verify_capture(packets)
990         self.src_if.assert_nothing_captured()
991
992     def test_buffer_boundary(self):
993         """ fragment header crossing buffer boundary """
994
995         p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
996              IPv6(src=self.src_if.remote_ip6,
997                   dst=self.src_if.local_ip6) /
998              IPv6ExtHdrHopByHop(
999                  options=[HBHOptUnknown(otype=0xff, optlen=0)] * 1000) /
1000              IPv6ExtHdrFragment(m=1) /
1001              UDP(sport=1234, dport=5678) /
1002              Raw())
1003         self.pg_enable_capture()
1004         self.src_if.add_stream([p])
1005         self.pg_start()
1006         self.src_if.assert_nothing_captured()
1007         self.dst_if.assert_nothing_captured()
1008
1009     def test_reversed(self):
1010         """ reverse order reassembly """
1011
1012         fragments = list(self.fragments_400)
1013         fragments.reverse()
1014
1015         self.pg_enable_capture()
1016         self.src_if.add_stream(fragments)
1017         self.pg_start()
1018
1019         packets = self.dst_if.get_capture(len(self.pkt_infos))
1020         self.verify_capture(packets)
1021         self.src_if.assert_nothing_captured()
1022
1023         # run it all again to verify correctness
1024         self.pg_enable_capture()
1025         self.src_if.add_stream(fragments)
1026         self.pg_start()
1027
1028         packets = self.dst_if.get_capture(len(self.pkt_infos))
1029         self.verify_capture(packets)
1030         self.src_if.assert_nothing_captured()
1031
1032     def test_random(self):
1033         """ random order reassembly """
1034
1035         fragments = list(self.fragments_400)
1036         shuffle(fragments)
1037
1038         self.pg_enable_capture()
1039         self.src_if.add_stream(fragments)
1040         self.pg_start()
1041
1042         packets = self.dst_if.get_capture(len(self.pkt_infos))
1043         self.verify_capture(packets)
1044         self.src_if.assert_nothing_captured()
1045
1046         # run it all again to verify correctness
1047         self.pg_enable_capture()
1048         self.src_if.add_stream(fragments)
1049         self.pg_start()
1050
1051         packets = self.dst_if.get_capture(len(self.pkt_infos))
1052         self.verify_capture(packets)
1053         self.src_if.assert_nothing_captured()
1054
1055     def test_duplicates(self):
1056         """ duplicate fragments """
1057
1058         fragments = [
1059             x for (_, frags, _) in self.pkt_infos
1060             for x in frags
1061             for _ in range(0, min(2, len(frags)))
1062         ]
1063
1064         self.pg_enable_capture()
1065         self.src_if.add_stream(fragments)
1066         self.pg_start()
1067
1068         packets = self.dst_if.get_capture(len(self.pkt_infos))
1069         self.verify_capture(packets)
1070         self.src_if.assert_nothing_captured()
1071
1072     def test_long_fragment_chain(self):
1073         """ long fragment chain """
1074
1075         error_cnt_str = \
1076             "/err/ip6-full-reassembly-feature/fragment chain too long (drop)"
1077
1078         error_cnt = self.statistics.get_err_counter(error_cnt_str)
1079
1080         self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
1081                                     max_reassembly_length=3,
1082                                     expire_walk_interval_ms=50, is_ip6=1)
1083
1084         p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1085              IPv6(src=self.src_if.remote_ip6,
1086                   dst=self.dst_if.remote_ip6) /
1087              UDP(sport=1234, dport=5678) /
1088              Raw("X" * 1000))
1089         frags = fragment_rfc8200(p, 1, 300) + fragment_rfc8200(p, 2, 500)
1090
1091         self.pg_enable_capture()
1092         self.src_if.add_stream(frags)
1093         self.pg_start()
1094
1095         self.dst_if.get_capture(1)
1096         self.assert_error_counter_equal(error_cnt_str, error_cnt + 1)
1097
1098     def test_overlap1(self):
1099         """ overlapping fragments case #1 """
1100
1101         fragments = []
1102         for _, frags_400, frags_300 in self.pkt_infos:
1103             if len(frags_300) == 1:
1104                 fragments.extend(frags_400)
1105             else:
1106                 for i, j in zip(frags_300, frags_400):
1107                     fragments.extend(i)
1108                     fragments.extend(j)
1109
1110         dropped_packet_indexes = set(
1111             index for (index, _, frags) in self.pkt_infos if len(frags) > 1
1112         )
1113
1114         self.pg_enable_capture()
1115         self.src_if.add_stream(fragments)
1116         self.pg_start()
1117
1118         packets = self.dst_if.get_capture(
1119             len(self.pkt_infos) - len(dropped_packet_indexes))
1120         self.verify_capture(packets, dropped_packet_indexes)
1121         self.src_if.assert_nothing_captured()
1122
1123     def test_overlap2(self):
1124         """ overlapping fragments case #2 """
1125
1126         fragments = []
1127         for _, frags_400, frags_300 in self.pkt_infos:
1128             if len(frags_400) == 1:
1129                 fragments.extend(frags_400)
1130             else:
1131                 # care must be taken here so that there are no fragments
1132                 # received by vpp after reassembly is finished, otherwise
1133                 # new reassemblies will be started and packet generator will
1134                 # freak out when it detects unfreed buffers
1135                 zipped = zip(frags_400, frags_300)
1136                 for i, j in zipped:
1137                     fragments.extend(i)
1138                     fragments.extend(j)
1139                 fragments.pop()
1140
1141         dropped_packet_indexes = set(
1142             index for (index, _, frags) in self.pkt_infos if len(frags) > 1
1143         )
1144
1145         self.pg_enable_capture()
1146         self.src_if.add_stream(fragments)
1147         self.pg_start()
1148
1149         packets = self.dst_if.get_capture(
1150             len(self.pkt_infos) - len(dropped_packet_indexes))
1151         self.verify_capture(packets, dropped_packet_indexes)
1152         self.src_if.assert_nothing_captured()
1153
1154     def test_timeout_inline(self):
1155         """ timeout (inline) """
1156
1157         dropped_packet_indexes = set(
1158             index for (index, frags, _) in self.pkt_infos if len(frags) > 1
1159         )
1160
1161         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1162                                     max_reassembly_length=3,
1163                                     expire_walk_interval_ms=10000, is_ip6=1)
1164
1165         self.pg_enable_capture()
1166         self.src_if.add_stream(self.fragments_400)
1167         self.pg_start()
1168
1169         packets = self.dst_if.get_capture(
1170             len(self.pkt_infos) - len(dropped_packet_indexes))
1171         self.verify_capture(packets, dropped_packet_indexes)
1172         pkts = self.src_if.get_capture(
1173             expected_count=len(dropped_packet_indexes))
1174         for icmp in pkts:
1175             self.assertIn(ICMPv6TimeExceeded, icmp)
1176             self.assertIn(IPv6ExtHdrFragment, icmp)
1177             self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
1178             dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
1179
1180     def test_timeout_cleanup(self):
1181         """ timeout (cleanup) """
1182
1183         # whole packets + fragmented packets sans last fragment
1184         fragments = [
1185             x for (_, frags_400, _) in self.pkt_infos
1186             for x in frags_400[:-1 if len(frags_400) > 1 else None]
1187         ]
1188
1189         # last fragments for fragmented packets
1190         fragments2 = [frags_400[-1]
1191                       for (_, frags_400, _) in self.pkt_infos
1192                       if len(frags_400) > 1]
1193
1194         dropped_packet_indexes = set(
1195             index for (index, frags_400, _) in self.pkt_infos
1196             if len(frags_400) > 1)
1197
1198         self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
1199                                     max_reassembly_length=1000,
1200                                     expire_walk_interval_ms=50)
1201
1202         self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
1203                                     max_reassembly_length=1000,
1204                                     expire_walk_interval_ms=50, is_ip6=1)
1205
1206         self.pg_enable_capture()
1207         self.src_if.add_stream(fragments)
1208         self.pg_start()
1209
1210         self.sleep(.25, "wait before sending rest of fragments")
1211
1212         self.src_if.add_stream(fragments2)
1213         self.pg_start()
1214
1215         packets = self.dst_if.get_capture(
1216             len(self.pkt_infos) - len(dropped_packet_indexes))
1217         self.verify_capture(packets, dropped_packet_indexes)
1218         pkts = self.src_if.get_capture(
1219             expected_count=len(dropped_packet_indexes))
1220         for icmp in pkts:
1221             self.assertIn(ICMPv6TimeExceeded, icmp)
1222             self.assertIn(IPv6ExtHdrFragment, icmp)
1223             self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
1224             dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
1225
1226     def test_disabled(self):
1227         """ reassembly disabled """
1228
1229         dropped_packet_indexes = set(
1230             index for (index, frags_400, _) in self.pkt_infos
1231             if len(frags_400) > 1)
1232
1233         self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
1234                                     max_reassembly_length=3,
1235                                     expire_walk_interval_ms=10000, is_ip6=1)
1236
1237         self.pg_enable_capture()
1238         self.src_if.add_stream(self.fragments_400)
1239         self.pg_start()
1240
1241         packets = self.dst_if.get_capture(
1242             len(self.pkt_infos) - len(dropped_packet_indexes))
1243         self.verify_capture(packets, dropped_packet_indexes)
1244         self.src_if.assert_nothing_captured()
1245
1246     def test_missing_upper(self):
1247         """ missing upper layer """
1248         p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1249              IPv6(src=self.src_if.remote_ip6,
1250                   dst=self.src_if.local_ip6) /
1251              UDP(sport=1234, dport=5678) /
1252              Raw())
1253         self.extend_packet(p, 1000, self.padding)
1254         fragments = fragment_rfc8200(p, 1, 500)
1255         bad_fragment = p.__class__(scapy.compat.raw(fragments[1]))
1256         bad_fragment[IPv6ExtHdrFragment].nh = 59
1257         bad_fragment[IPv6ExtHdrFragment].offset = 0
1258         self.pg_enable_capture()
1259         self.src_if.add_stream([bad_fragment])
1260         self.pg_start()
1261         pkts = self.src_if.get_capture(expected_count=1)
1262         icmp = pkts[0]
1263         self.assertIn(ICMPv6ParamProblem, icmp)
1264         self.assert_equal(icmp[ICMPv6ParamProblem].code, 3, "ICMP code")
1265
1266     def test_invalid_frag_size(self):
1267         """ fragment size not a multiple of 8 """
1268         p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1269              IPv6(src=self.src_if.remote_ip6,
1270                   dst=self.src_if.local_ip6) /
1271              UDP(sport=1234, dport=5678) /
1272              Raw())
1273         self.extend_packet(p, 1000, self.padding)
1274         fragments = fragment_rfc8200(p, 1, 500)
1275         bad_fragment = fragments[0]
1276         self.extend_packet(bad_fragment, len(bad_fragment) + 5)
1277         self.pg_enable_capture()
1278         self.src_if.add_stream([bad_fragment])
1279         self.pg_start()
1280         pkts = self.src_if.get_capture(expected_count=1)
1281         icmp = pkts[0]
1282         self.assertIn(ICMPv6ParamProblem, icmp)
1283         self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1284
1285     def test_invalid_packet_size(self):
1286         """ total packet size > 65535 """
1287         p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1288              IPv6(src=self.src_if.remote_ip6,
1289                   dst=self.src_if.local_ip6) /
1290              UDP(sport=1234, dport=5678) /
1291              Raw())
1292         self.extend_packet(p, 1000, self.padding)
1293         fragments = fragment_rfc8200(p, 1, 500)
1294         bad_fragment = fragments[1]
1295         bad_fragment[IPv6ExtHdrFragment].offset = 65500
1296         self.pg_enable_capture()
1297         self.src_if.add_stream([bad_fragment])
1298         self.pg_start()
1299         pkts = self.src_if.get_capture(expected_count=1)
1300         icmp = pkts[0]
1301         self.assertIn(ICMPv6ParamProblem, icmp)
1302         self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1303
1304
1305 class TestIPv6MWReassembly(VppTestCase):
1306     """ IPv6 Reassembly (multiple workers) """
1307     worker_config = "workers %d" % worker_count
1308
1309     @classmethod
1310     def setUpClass(cls):
1311         super(TestIPv6MWReassembly, cls).setUpClass()
1312
1313         cls.create_pg_interfaces(range(worker_count+1))
1314         cls.src_if = cls.pg0
1315         cls.send_ifs = cls.pg_interfaces[:-1]
1316         cls.dst_if = cls.pg_interfaces[-1]
1317
1318         # setup all interfaces
1319         for i in cls.pg_interfaces:
1320             i.admin_up()
1321             i.config_ip6()
1322             i.resolve_ndp()
1323
1324         # packets sizes reduced here because we are generating packets without
1325         # Ethernet headers, which are added later (diff fragments go via
1326         # different interfaces)
1327         cls.packet_sizes = [64-len(Ether()), 512-len(Ether()),
1328                             1518-len(Ether()), 9018-len(Ether())]
1329         cls.padding = " abcdefghijklmn"
1330         cls.create_stream(cls.packet_sizes)
1331         cls.create_fragments()
1332
1333     @classmethod
1334     def tearDownClass(cls):
1335         super(TestIPv6MWReassembly, cls).tearDownClass()
1336
1337     def setUp(self):
1338         """ Test setup - force timeout on existing reassemblies """
1339         super(TestIPv6MWReassembly, self).setUp()
1340         for intf in self.send_ifs:
1341             self.vapi.ip_reassembly_enable_disable(
1342                 sw_if_index=intf.sw_if_index, enable_ip6=True)
1343         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1344                                     max_reassembly_length=1000,
1345                                     expire_walk_interval_ms=10, is_ip6=1)
1346         self.sleep(.25)
1347         self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1348                                     max_reassembly_length=1000,
1349                                     expire_walk_interval_ms=1000, is_ip6=1)
1350
1351     def tearDown(self):
1352         super(TestIPv6MWReassembly, self).tearDown()
1353
1354     def show_commands_at_teardown(self):
1355         self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1356         self.logger.debug(self.vapi.ppcli("show buffers"))
1357
1358     @classmethod
1359     def create_stream(cls, packet_sizes, packet_count=test_packet_count):
1360         """Create input packet stream
1361
1362         :param list packet_sizes: Required packet sizes.
1363         """
1364         for i in range(0, packet_count):
1365             info = cls.create_packet_info(cls.src_if, cls.src_if)
1366             payload = cls.info_to_payload(info)
1367             p = (IPv6(src=cls.src_if.remote_ip6,
1368                       dst=cls.dst_if.remote_ip6) /
1369                  UDP(sport=1234, dport=5678) /
1370                  Raw(payload))
1371             size = packet_sizes[(i // 2) % len(packet_sizes)]
1372             cls.extend_packet(p, size, cls.padding)
1373             info.data = p
1374
1375     @classmethod
1376     def create_fragments(cls):
1377         infos = cls._packet_infos
1378         cls.pkt_infos = []
1379         for index, info in six.iteritems(infos):
1380             p = info.data
1381             # cls.logger.debug(ppp("Packet:",
1382             #                      p.__class__(scapy.compat.raw(p))))
1383             fragments_400 = fragment_rfc8200(p, index, 400)
1384             cls.pkt_infos.append((index, fragments_400))
1385         cls.fragments_400 = [
1386             x for (_, frags) in cls.pkt_infos for x in frags]
1387         cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, " %
1388                          (len(infos), len(cls.fragments_400)))
1389
1390     def verify_capture(self, capture, dropped_packet_indexes=[]):
1391         """Verify captured packet strea .
1392
1393         :param list capture: Captured packet stream.
1394         """
1395         info = None
1396         seen = set()
1397         for packet in capture:
1398             try:
1399                 self.logger.debug(ppp("Got packet:", packet))
1400                 ip = packet[IPv6]
1401                 udp = packet[UDP]
1402                 payload_info = self.payload_to_info(packet[Raw])
1403                 packet_index = payload_info.index
1404                 self.assertTrue(
1405                     packet_index not in dropped_packet_indexes,
1406                     ppp("Packet received, but should be dropped:", packet))
1407                 if packet_index in seen:
1408                     raise Exception(ppp("Duplicate packet received", packet))
1409                 seen.add(packet_index)
1410                 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
1411                 info = self._packet_infos[packet_index]
1412                 self.assertTrue(info is not None)
1413                 self.assertEqual(packet_index, info.index)
1414                 saved_packet = info.data
1415                 self.assertEqual(ip.src, saved_packet[IPv6].src)
1416                 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
1417                 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1418             except Exception:
1419                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1420                 raise
1421         for index in self._packet_infos:
1422             self.assertTrue(index in seen or index in dropped_packet_indexes,
1423                             "Packet with packet_index %d not received" % index)
1424
1425     def send_packets(self, packets):
1426         for counter in range(worker_count):
1427             if 0 == len(packets[counter]):
1428                 continue
1429             send_if = self.send_ifs[counter]
1430             send_if.add_stream(
1431                 (Ether(dst=send_if.local_mac, src=send_if.remote_mac) / x
1432                  for x in packets[counter]),
1433                 worker=counter)
1434         self.pg_start()
1435
1436     def test_worker_conflict(self):
1437         """ 1st and FO=0 fragments on different workers """
1438
1439         # in first wave we send fragments which don't start at offset 0
1440         # then we send fragments with offset 0 on a different thread
1441         # then the rest of packets on a random thread
1442         first_packets = [[] for n in range(worker_count)]
1443         second_packets = [[] for n in range(worker_count)]
1444         rest_of_packets = [[] for n in range(worker_count)]
1445         for (_, p) in self.pkt_infos:
1446             wi = randrange(worker_count)
1447             second_packets[wi].append(p[0])
1448             if len(p) <= 1:
1449                 continue
1450             wi2 = wi
1451             while wi2 == wi:
1452                 wi2 = randrange(worker_count)
1453             first_packets[wi2].append(p[1])
1454             wi3 = randrange(worker_count)
1455             rest_of_packets[wi3].extend(p[2:])
1456
1457         self.pg_enable_capture()
1458         self.send_packets(first_packets)
1459         self.send_packets(second_packets)
1460         self.send_packets(rest_of_packets)
1461
1462         packets = self.dst_if.get_capture(len(self.pkt_infos))
1463         self.verify_capture(packets)
1464         for send_if in self.send_ifs:
1465             send_if.assert_nothing_captured()
1466
1467         self.pg_enable_capture()
1468         self.send_packets(first_packets)
1469         self.send_packets(second_packets)
1470         self.send_packets(rest_of_packets)
1471
1472         packets = self.dst_if.get_capture(len(self.pkt_infos))
1473         self.verify_capture(packets)
1474         for send_if in self.send_ifs:
1475             send_if.assert_nothing_captured()
1476
1477
1478 class TestIPv6SVReassembly(VppTestCase):
1479     """ IPv6 Shallow Virtual Reassembly """
1480
1481     @classmethod
1482     def setUpClass(cls):
1483         super(TestIPv6SVReassembly, cls).setUpClass()
1484
1485         cls.create_pg_interfaces([0, 1])
1486         cls.src_if = cls.pg0
1487         cls.dst_if = cls.pg1
1488
1489         # setup all interfaces
1490         for i in cls.pg_interfaces:
1491             i.admin_up()
1492             i.config_ip6()
1493             i.resolve_ndp()
1494
1495     def setUp(self):
1496         """ Test setup - force timeout on existing reassemblies """
1497         super(TestIPv6SVReassembly, self).setUp()
1498         self.vapi.ip_reassembly_enable_disable(
1499             sw_if_index=self.src_if.sw_if_index, enable_ip6=True,
1500             type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
1501         self.vapi.ip_reassembly_set(
1502             timeout_ms=0, max_reassemblies=1000,
1503             max_reassembly_length=1000,
1504             type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1505             expire_walk_interval_ms=10, is_ip6=1)
1506         self.sleep(.25)
1507         self.vapi.ip_reassembly_set(
1508             timeout_ms=1000000, max_reassemblies=1000,
1509             max_reassembly_length=1000,
1510             type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1511             expire_walk_interval_ms=10000, is_ip6=1)
1512
1513     def tearDown(self):
1514         super(TestIPv6SVReassembly, self).tearDown()
1515         self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1516         self.logger.debug(self.vapi.ppcli("show buffers"))
1517
1518     def test_basic(self):
1519         """ basic reassembly """
1520         payload_len = 1000
1521         payload = ""
1522         counter = 0
1523         while len(payload) < payload_len:
1524             payload += "%u " % counter
1525             counter += 1
1526
1527         p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1528              IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1529              UDP(sport=1234, dport=5678) /
1530              Raw(payload))
1531         fragments = fragment_rfc8200(p, 1, payload_len/4)
1532
1533         # send fragment #2 - should be cached inside reassembly
1534         self.pg_enable_capture()
1535         self.src_if.add_stream(fragments[1])
1536         self.pg_start()
1537         self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1538         self.logger.debug(self.vapi.ppcli("show buffers"))
1539         self.logger.debug(self.vapi.ppcli("show trace"))
1540         self.dst_if.assert_nothing_captured()
1541
1542         # send fragment #1 - reassembly is finished now and both fragments
1543         # forwarded
1544         self.pg_enable_capture()
1545         self.src_if.add_stream(fragments[0])
1546         self.pg_start()
1547         self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1548         self.logger.debug(self.vapi.ppcli("show buffers"))
1549         self.logger.debug(self.vapi.ppcli("show trace"))
1550         c = self.dst_if.get_capture(2)
1551         for sent, recvd in zip([fragments[1], fragments[0]], c):
1552             self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1553             self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1554             self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1555
1556         # send rest of fragments - should be immediately forwarded
1557         self.pg_enable_capture()
1558         self.src_if.add_stream(fragments[2:])
1559         self.pg_start()
1560         c = self.dst_if.get_capture(len(fragments[2:]))
1561         for sent, recvd in zip(fragments[2:], c):
1562             self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1563             self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1564             self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1565
1566     def test_timeout(self):
1567         """ reassembly timeout """
1568         payload_len = 1000
1569         payload = ""
1570         counter = 0
1571         while len(payload) < payload_len:
1572             payload += "%u " % counter
1573             counter += 1
1574
1575         p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1576              IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1577              UDP(sport=1234, dport=5678) /
1578              Raw(payload))
1579         fragments = fragment_rfc8200(p, 1, payload_len/4)
1580
1581         self.vapi.ip_reassembly_set(
1582             timeout_ms=100, max_reassemblies=1000,
1583             max_reassembly_length=1000,
1584             expire_walk_interval_ms=50,
1585             is_ip6=1,
1586             type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
1587
1588         # send fragments #2 and #1 - should be forwarded
1589         self.pg_enable_capture()
1590         self.src_if.add_stream(fragments[0:2])
1591         self.pg_start()
1592         self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
1593         self.logger.debug(self.vapi.ppcli("show buffers"))
1594         self.logger.debug(self.vapi.ppcli("show trace"))
1595         c = self.dst_if.get_capture(2)
1596         for sent, recvd in zip([fragments[1], fragments[0]], c):
1597             self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1598             self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1599             self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1600
1601         # wait for cleanup
1602         self.sleep(.25, "wait before sending rest of fragments")
1603
1604         # send rest of fragments - shouldn't be forwarded
1605         self.pg_enable_capture()
1606         self.src_if.add_stream(fragments[2:])
1607         self.pg_start()
1608         self.dst_if.assert_nothing_captured()
1609
1610     def test_lru(self):
1611         """ reassembly reuses LRU element """
1612
1613         self.vapi.ip_reassembly_set(
1614             timeout_ms=1000000, max_reassemblies=1,
1615             max_reassembly_length=1000,
1616             type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1617             is_ip6=1, expire_walk_interval_ms=10000)
1618
1619         payload_len = 1000
1620         payload = ""
1621         counter = 0
1622         while len(payload) < payload_len:
1623             payload += "%u " % counter
1624             counter += 1
1625
1626         packet_count = 10
1627
1628         fragments = [f
1629                      for i in range(packet_count)
1630                      for p in (Ether(dst=self.src_if.local_mac,
1631                                      src=self.src_if.remote_mac) /
1632                                IPv6(src=self.src_if.remote_ip6,
1633                                     dst=self.dst_if.remote_ip6) /
1634                                UDP(sport=1234, dport=5678) /
1635                                Raw(payload))
1636                      for f in fragment_rfc8200(p, i, payload_len/4)]
1637
1638         self.pg_enable_capture()
1639         self.src_if.add_stream(fragments)
1640         self.pg_start()
1641         c = self.dst_if.get_capture(len(fragments))
1642         for sent, recvd in zip(fragments, c):
1643             self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1644             self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1645             self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1646
1647
1648 class TestIPv4ReassemblyLocalNode(VppTestCase):
1649     """ IPv4 Reassembly for packets coming to ip4-local node """
1650
1651     @classmethod
1652     def setUpClass(cls):
1653         super(TestIPv4ReassemblyLocalNode, cls).setUpClass()
1654
1655         cls.create_pg_interfaces([0])
1656         cls.src_dst_if = cls.pg0
1657
1658         # setup all interfaces
1659         for i in cls.pg_interfaces:
1660             i.admin_up()
1661             i.config_ip4()
1662             i.resolve_arp()
1663
1664         cls.padding = " abcdefghijklmn"
1665         cls.create_stream()
1666         cls.create_fragments()
1667
1668     @classmethod
1669     def tearDownClass(cls):
1670         super(TestIPv4ReassemblyLocalNode, cls).tearDownClass()
1671
1672     def setUp(self):
1673         """ Test setup - force timeout on existing reassemblies """
1674         super(TestIPv4ReassemblyLocalNode, self).setUp()
1675         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1676                                     max_reassembly_length=1000,
1677                                     expire_walk_interval_ms=10)
1678         self.sleep(.25)
1679         self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1680                                     max_reassembly_length=1000,
1681                                     expire_walk_interval_ms=10000)
1682
1683     def tearDown(self):
1684         super(TestIPv4ReassemblyLocalNode, self).tearDown()
1685
1686     def show_commands_at_teardown(self):
1687         self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
1688         self.logger.debug(self.vapi.ppcli("show buffers"))
1689
1690     @classmethod
1691     def create_stream(cls, packet_count=test_packet_count):
1692         """Create input packet stream for defined interface.
1693
1694         :param list packet_sizes: Required packet sizes.
1695         """
1696         for i in range(0, packet_count):
1697             info = cls.create_packet_info(cls.src_dst_if, cls.src_dst_if)
1698             payload = cls.info_to_payload(info)
1699             p = (Ether(dst=cls.src_dst_if.local_mac,
1700                        src=cls.src_dst_if.remote_mac) /
1701                  IP(id=info.index, src=cls.src_dst_if.remote_ip4,
1702                     dst=cls.src_dst_if.local_ip4) /
1703                  ICMP(type='echo-request', id=1234) /
1704                  Raw(payload))
1705             cls.extend_packet(p, 1518, cls.padding)
1706             info.data = p
1707
1708     @classmethod
1709     def create_fragments(cls):
1710         infos = cls._packet_infos
1711         cls.pkt_infos = []
1712         for index, info in six.iteritems(infos):
1713             p = info.data
1714             # cls.logger.debug(ppp("Packet:",
1715             #                      p.__class__(scapy.compat.raw(p))))
1716             fragments_300 = fragment_rfc791(p, 300)
1717             cls.pkt_infos.append((index, fragments_300))
1718         cls.fragments_300 = [x for (_, frags) in cls.pkt_infos for x in frags]
1719         cls.logger.debug("Fragmented %s packets into %s 300-byte fragments" %
1720                          (len(infos), len(cls.fragments_300)))
1721
1722     def verify_capture(self, capture):
1723         """Verify captured packet stream.
1724
1725         :param list capture: Captured packet stream.
1726         """
1727         info = None
1728         seen = set()
1729         for packet in capture:
1730             try:
1731                 self.logger.debug(ppp("Got packet:", packet))
1732                 ip = packet[IP]
1733                 icmp = packet[ICMP]
1734                 payload_info = self.payload_to_info(packet[Raw])
1735                 packet_index = payload_info.index
1736                 if packet_index in seen:
1737                     raise Exception(ppp("Duplicate packet received", packet))
1738                 seen.add(packet_index)
1739                 self.assertEqual(payload_info.dst, self.src_dst_if.sw_if_index)
1740                 info = self._packet_infos[packet_index]
1741                 self.assertIsNotNone(info)
1742                 self.assertEqual(packet_index, info.index)
1743                 saved_packet = info.data
1744                 self.assertEqual(ip.src, saved_packet[IP].dst)
1745                 self.assertEqual(ip.dst, saved_packet[IP].src)
1746                 self.assertEqual(icmp.type, 0)  # echo reply
1747                 self.assertEqual(icmp.id, saved_packet[ICMP].id)
1748                 self.assertEqual(icmp.payload, saved_packet[ICMP].payload)
1749             except Exception:
1750                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1751                 raise
1752         for index in self._packet_infos:
1753             self.assertIn(index, seen,
1754                           "Packet with packet_index %d not received" % index)
1755
1756     def test_reassembly(self):
1757         """ basic reassembly """
1758
1759         self.pg_enable_capture()
1760         self.src_dst_if.add_stream(self.fragments_300)
1761         self.pg_start()
1762
1763         packets = self.src_dst_if.get_capture(len(self.pkt_infos))
1764         self.verify_capture(packets)
1765
1766         # run it all again to verify correctness
1767         self.pg_enable_capture()
1768         self.src_dst_if.add_stream(self.fragments_300)
1769         self.pg_start()
1770
1771         packets = self.src_dst_if.get_capture(len(self.pkt_infos))
1772         self.verify_capture(packets)
1773
1774
1775 class TestFIFReassembly(VppTestCase):
1776     """ Fragments in fragments reassembly """
1777
1778     @classmethod
1779     def setUpClass(cls):
1780         super(TestFIFReassembly, cls).setUpClass()
1781
1782         cls.create_pg_interfaces([0, 1])
1783         cls.src_if = cls.pg0
1784         cls.dst_if = cls.pg1
1785         for i in cls.pg_interfaces:
1786             i.admin_up()
1787             i.config_ip4()
1788             i.resolve_arp()
1789             i.config_ip6()
1790             i.resolve_ndp()
1791
1792         cls.packet_sizes = [64, 512, 1518, 9018]
1793         cls.padding = " abcdefghijklmn"
1794
1795     @classmethod
1796     def tearDownClass(cls):
1797         super(TestFIFReassembly, cls).tearDownClass()
1798
1799     def setUp(self):
1800         """ Test setup - force timeout on existing reassemblies """
1801         super(TestFIFReassembly, self).setUp()
1802         self.vapi.ip_reassembly_enable_disable(
1803             sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
1804             enable_ip6=True)
1805         self.vapi.ip_reassembly_enable_disable(
1806             sw_if_index=self.dst_if.sw_if_index, enable_ip4=True,
1807             enable_ip6=True)
1808         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1809                                     max_reassembly_length=1000,
1810                                     expire_walk_interval_ms=10)
1811         self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1812                                     max_reassembly_length=1000,
1813                                     expire_walk_interval_ms=10, is_ip6=1)
1814         self.sleep(.25)
1815         self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1816                                     max_reassembly_length=1000,
1817                                     expire_walk_interval_ms=10000)
1818         self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1819                                     max_reassembly_length=1000,
1820                                     expire_walk_interval_ms=10000, is_ip6=1)
1821
1822     def tearDown(self):
1823         super(TestFIFReassembly, self).tearDown()
1824
1825     def show_commands_at_teardown(self):
1826         self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
1827         self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1828         self.logger.debug(self.vapi.ppcli("show buffers"))
1829
1830     def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):
1831         """Verify captured packet stream.
1832
1833         :param list capture: Captured packet stream.
1834         """
1835         info = None
1836         seen = set()
1837         for packet in capture:
1838             try:
1839                 self.logger.debug(ppp("Got packet:", packet))
1840                 ip = packet[ip_class]
1841                 udp = packet[UDP]
1842                 payload_info = self.payload_to_info(packet[Raw])
1843                 packet_index = payload_info.index
1844                 self.assertTrue(
1845                     packet_index not in dropped_packet_indexes,
1846                     ppp("Packet received, but should be dropped:", packet))
1847                 if packet_index in seen:
1848                     raise Exception(ppp("Duplicate packet received", packet))
1849                 seen.add(packet_index)
1850                 self.assertEqual(payload_info.dst, self.dst_if.sw_if_index)
1851                 info = self._packet_infos[packet_index]
1852                 self.assertTrue(info is not None)
1853                 self.assertEqual(packet_index, info.index)
1854                 saved_packet = info.data
1855                 self.assertEqual(ip.src, saved_packet[ip_class].src)
1856                 self.assertEqual(ip.dst, saved_packet[ip_class].dst)
1857                 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1858             except Exception:
1859                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1860                 raise
1861         for index in self._packet_infos:
1862             self.assertTrue(index in seen or index in dropped_packet_indexes,
1863                             "Packet with packet_index %d not received" % index)
1864
1865     def test_fif4(self):
1866         """ Fragments in fragments (4o4) """
1867
1868         # TODO this should be ideally in setUpClass, but then we hit a bug
1869         # with VppIpRoute incorrectly reporting it's present when it's not
1870         # so we need to manually remove the vpp config, thus we cannot have
1871         # it shared for multiple test cases
1872         self.tun_ip4 = "1.1.1.2"
1873
1874         self.gre4 = VppGreInterface(self, self.src_if.local_ip4, self.tun_ip4)
1875         self.gre4.add_vpp_config()
1876         self.gre4.admin_up()
1877         self.gre4.config_ip4()
1878
1879         self.vapi.ip_reassembly_enable_disable(
1880             sw_if_index=self.gre4.sw_if_index, enable_ip4=True)
1881
1882         self.route4 = VppIpRoute(self, self.tun_ip4, 32,
1883                                  [VppRoutePath(self.src_if.remote_ip4,
1884                                                self.src_if.sw_if_index)])
1885         self.route4.add_vpp_config()
1886
1887         self.reset_packet_infos()
1888         for i in range(test_packet_count):
1889             info = self.create_packet_info(self.src_if, self.dst_if)
1890             payload = self.info_to_payload(info)
1891             # Ethernet header here is only for size calculation, thus it
1892             # doesn't matter how it's initialized. This is to ensure that
1893             # reassembled packet is not > 9000 bytes, so that it's not dropped
1894             p = (Ether() /
1895                  IP(id=i, src=self.src_if.remote_ip4,
1896                     dst=self.dst_if.remote_ip4) /
1897                  UDP(sport=1234, dport=5678) /
1898                  Raw(payload))
1899             size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1900             self.extend_packet(p, size, self.padding)
1901             info.data = p[IP]  # use only IP part, without ethernet header
1902
1903         fragments = [x for _, p in six.iteritems(self._packet_infos)
1904                      for x in fragment_rfc791(p.data, 400)]
1905
1906         encapped_fragments = \
1907             [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1908              IP(src=self.tun_ip4, dst=self.src_if.local_ip4) /
1909                 GRE() /
1910                 p
1911                 for p in fragments]
1912
1913         fragmented_encapped_fragments = \
1914             [x for p in encapped_fragments
1915              for x in fragment_rfc791(p, 200)]
1916
1917         self.src_if.add_stream(fragmented_encapped_fragments)
1918
1919         self.pg_enable_capture(self.pg_interfaces)
1920         self.pg_start()
1921
1922         self.src_if.assert_nothing_captured()
1923         packets = self.dst_if.get_capture(len(self._packet_infos))
1924         self.verify_capture(packets, IP)
1925
1926         # TODO remove gre vpp config by hand until VppIpRoute gets fixed
1927         # so that it's query_vpp_config() works as it should
1928         self.gre4.remove_vpp_config()
1929         self.logger.debug(self.vapi.ppcli("show interface"))
1930
1931     def test_fif6(self):
1932         """ Fragments in fragments (6o6) """
1933         # TODO this should be ideally in setUpClass, but then we hit a bug
1934         # with VppIpRoute incorrectly reporting it's present when it's not
1935         # so we need to manually remove the vpp config, thus we cannot have
1936         # it shared for multiple test cases
1937         self.tun_ip6 = "1002::1"
1938
1939         self.gre6 = VppGreInterface(self, self.src_if.local_ip6, self.tun_ip6)
1940         self.gre6.add_vpp_config()
1941         self.gre6.admin_up()
1942         self.gre6.config_ip6()
1943
1944         self.vapi.ip_reassembly_enable_disable(
1945             sw_if_index=self.gre6.sw_if_index, enable_ip6=True)
1946
1947         self.route6 = VppIpRoute(self, self.tun_ip6, 128,
1948                                  [VppRoutePath(
1949                                      self.src_if.remote_ip6,
1950                                      self.src_if.sw_if_index)])
1951         self.route6.add_vpp_config()
1952
1953         self.reset_packet_infos()
1954         for i in range(test_packet_count):
1955             info = self.create_packet_info(self.src_if, self.dst_if)
1956             payload = self.info_to_payload(info)
1957             # Ethernet header here is only for size calculation, thus it
1958             # doesn't matter how it's initialized. This is to ensure that
1959             # reassembled packet is not > 9000 bytes, so that it's not dropped
1960             p = (Ether() /
1961                  IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1962                  UDP(sport=1234, dport=5678) /
1963                  Raw(payload))
1964             size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1965             self.extend_packet(p, size, self.padding)
1966             info.data = p[IPv6]  # use only IPv6 part, without ethernet header
1967
1968         fragments = [x for _, i in six.iteritems(self._packet_infos)
1969                      for x in fragment_rfc8200(
1970                          i.data, i.index, 400)]
1971
1972         encapped_fragments = \
1973             [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1974              IPv6(src=self.tun_ip6, dst=self.src_if.local_ip6) /
1975                 GRE() /
1976                 p
1977                 for p in fragments]
1978
1979         fragmented_encapped_fragments = \
1980             [x for p in encapped_fragments for x in (
1981                 fragment_rfc8200(
1982                     p,
1983                     2 * len(self._packet_infos) + p[IPv6ExtHdrFragment].id,
1984                     200)
1985                 if IPv6ExtHdrFragment in p else [p]
1986             )
1987             ]
1988
1989         self.src_if.add_stream(fragmented_encapped_fragments)
1990
1991         self.pg_enable_capture(self.pg_interfaces)
1992         self.pg_start()
1993
1994         self.src_if.assert_nothing_captured()
1995         packets = self.dst_if.get_capture(len(self._packet_infos))
1996         self.verify_capture(packets, IPv6)
1997
1998         # TODO remove gre vpp config by hand until VppIpRoute gets fixed
1999         # so that it's query_vpp_config() works as it should
2000         self.gre6.remove_vpp_config()
2001
2002
2003 if __name__ == '__main__':
2004     unittest.main(testRunner=VppTestRunner)