blob: cb6b8ff5d3f8c036909074ee630864fb041f41e2 [file] [log] [blame]
Renato Botelho do Coutoead1e532019-10-31 13:31:07 -05001#!/usr/bin/env python3
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -08002
3import six
Klement Sekera75e7d132017-09-20 08:26:30 +02004import unittest
Klement Sekera630ab582019-07-19 09:14:19 +00005from random import shuffle, choice, randrange
Klement Sekera75e7d132017-09-20 08:26:30 +02006
Klement Sekera947a85c2019-07-24 12:40:37 +00007from framework import VppTestCase, VppTestRunner
8
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07009import scapy.compat
Klement Sekera75e7d132017-09-20 08:26:30 +020010from scapy.packet import Raw
11from scapy.layers.l2 import Ether, GRE
Juraj Sloboda3048b632018-10-02 11:13:53 +020012from scapy.layers.inet import IP, UDP, ICMP
Klement Sekera769145c2019-03-06 11:59:57 +010013from scapy.layers.inet6 import HBHOptUnknown, ICMPv6ParamProblem,\
Klement Sekerade34c352019-06-25 11:19:22 +000014 ICMPv6TimeExceeded, IPv6, IPv6ExtHdrFragment, IPv6ExtHdrHopByHop
Paul Vinciguerra69555952019-03-01 08:46:29 -080015from framework import VppTestCase, VppTestRunner
Klement Sekera630ab582019-07-19 09:14:19 +000016from util import ppp, ppc, fragment_rfc791, fragment_rfc8200
Neale Ranns5a8844b2019-04-16 07:15:35 +000017from vpp_gre_interface import VppGreInterface
Neale Rannsc0a93142018-09-05 15:42:26 -070018from vpp_ip import DpoProto
Neale Ranns097fa662018-05-01 05:17:55 -070019from vpp_ip_route import VppIpRoute, VppRoutePath, FibPathProto
Klement Sekera896c8962019-06-24 11:52:49 +000020from vpp_papi import VppEnum
Klement Sekera75e7d132017-09-20 08:26:30 +020021
Klement Sekerad0f70a32018-12-14 17:24:13 +010022# 35 is enough to have >257 400-byte fragments
23test_packet_count = 35
Klement Sekera75e7d132017-09-20 08:26:30 +020024
Klement Sekera630ab582019-07-19 09:14:19 +000025# number of workers used for multi-worker test cases
26worker_count = 3
27
Klement Sekera75e7d132017-09-20 08:26:30 +020028
Klement Sekera947a85c2019-07-24 12:40:37 +000029class TestIPv4Reassembly(VppTestCase):
Klement Sekera75e7d132017-09-20 08:26:30 +020030 """ IPv4 Reassembly """
31
32 @classmethod
33 def setUpClass(cls):
34 super(TestIPv4Reassembly, cls).setUpClass()
35
Klement Sekera4c533132018-02-22 11:41:12 +010036 cls.create_pg_interfaces([0, 1])
37 cls.src_if = cls.pg0
38 cls.dst_if = cls.pg1
Klement Sekera75e7d132017-09-20 08:26:30 +020039
40 # setup all interfaces
41 for i in cls.pg_interfaces:
42 i.admin_up()
43 i.config_ip4()
44 i.resolve_arp()
45
Klement Sekera75e7d132017-09-20 08:26:30 +020046 # 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
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -070052 @classmethod
53 def tearDownClass(cls):
54 super(TestIPv4Reassembly, cls).tearDownClass()
55
Klement Sekera75e7d132017-09-20 08:26:30 +020056 def setUp(self):
57 """ Test setup - force timeout on existing reassemblies """
58 super(TestIPv4Reassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +010059 self.vapi.ip_reassembly_enable_disable(
60 sw_if_index=self.src_if.sw_if_index, enable_ip4=True)
Klement Sekera75e7d132017-09-20 08:26:30 +020061 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +020062 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +020063 expire_walk_interval_ms=10)
64 self.sleep(.25)
65 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +020066 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +020067 expire_walk_interval_ms=10000)
68
69 def tearDown(self):
70 super(TestIPv4Reassembly, self).tearDown()
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -070071
72 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +000073 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +010074 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +020075
76 @classmethod
77 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
Klement Sekerad0f70a32018-12-14 17:24:13 +010078 """Create input packet stream
Klement Sekera75e7d132017-09-20 08:26:30 +020079
80 :param list packet_sizes: Required packet sizes.
81 """
82 for i in range(0, packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +010083 info = cls.create_packet_info(cls.src_if, cls.src_if)
Klement Sekera75e7d132017-09-20 08:26:30 +020084 payload = cls.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +010085 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) /
Klement Sekera75e7d132017-09-20 08:26:30 +020089 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 = []
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -080098 for index, info in six.iteritems(infos):
Klement Sekera75e7d132017-09-20 08:26:30 +020099 p = info.data
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700100 # cls.logger.debug(ppp("Packet:",
101 # p.__class__(scapy.compat.raw(p))))
Klement Sekera75e7d132017-09-20 08:26:30 +0200102 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
Klement Sekera947a85c2019-07-24 12:40:37 +0000119 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):
Klement Sekera75e7d132017-09-20 08:26:30 +0200155 """ basic reassembly """
156
Klement Sekera947a85c2019-07-24 12:40:37 +0000157 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):
Klement Sekera75e7d132017-09-20 08:26:30 +0200175 """ reverse order reassembly """
176
Klement Sekera947a85c2019-07-24 12:40:37 +0000177 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()
Klement Sekera75e7d132017-09-20 08:26:30 +0200196
Klement Sekera3a343d42019-05-16 14:35:46 +0200197 def test_long_fragment_chain(self):
198 """ long fragment chain """
199
200 error_cnt_str = \
Klement Sekera896c8962019-06-24 11:52:49 +0000201 "/err/ip4-full-reassembly-feature/fragment chain too long (drop)"
Klement Sekera3a343d42019-05-16 14:35:46 +0200202
Klement Sekera34641f22019-05-22 20:18:26 +0200203 error_cnt = self.statistics.get_err_counter(error_cnt_str)
Klement Sekera3a343d42019-05-16 14:35:46 +0200204
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) /
Ole Troan127fbec2019-10-18 15:22:56 +0200213 Raw(b"X" * 1000))
Klement Sekera3a343d42019-05-16 14:35:46 +0200214 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) /
Ole Troan127fbec2019-10-18 15:22:56 +0200218 Raw(b"X" * 1000))
Klement Sekera3a343d42019-05-16 14:35:46 +0200219 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)
Klement Sekera34641f22019-05-22 20:18:26 +0200226 self.assert_error_counter_equal(error_cnt_str, error_cnt + 1)
Klement Sekera3a343d42019-05-16 14:35:46 +0200227
Klement Sekera14d7e902018-12-10 13:46:09 +0100228 def test_5737(self):
229 """ fragment length + ip header size > 65535 """
Klement Sekera4ee633e2018-12-14 12:00:44 +0100230 self.vapi.cli("clear errors")
Ole Troan127fbec2019-10-18 15:22:56 +0200231 raw = b'''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-message.\
233Ethernet-Payload.IPv4-Packet.IPv4-Header.Fragment-Offset; Test-case: 5737'''
Klement Sekera14d7e902018-12-10 13:46:09 +0100234 malformed_packet = (Ether(dst=self.src_if.local_mac,
235 src=self.src_if.remote_mac) /
236 IP(raw))
237 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
238 IP(id=1000, src=self.src_if.remote_ip4,
239 dst=self.dst_if.remote_ip4) /
240 UDP(sport=1234, dport=5678) /
Ole Troan127fbec2019-10-18 15:22:56 +0200241 Raw(b"X" * 1000))
Klement Sekera14d7e902018-12-10 13:46:09 +0100242 valid_fragments = fragment_rfc791(p, 400)
243
Ole Troan127fbec2019-10-18 15:22:56 +0200244 counter = "/err/ip4-full-reassembly-feature/malformed packets"
245 error_counter = self.statistics.get_err_counter(counter)
Klement Sekera14d7e902018-12-10 13:46:09 +0100246 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)
Klement Sekera896c8962019-06-24 11:52:49 +0000251 self.logger.debug(self.vapi.ppcli("show error"))
Ole Troan127fbec2019-10-18 15:22:56 +0200252 self.assertEqual(self.statistics.get_err_counter(counter),
253 error_counter + 1)
Klement Sekera14d7e902018-12-10 13:46:09 +0100254
Klement Sekera400f6d82018-12-13 14:35:48 +0100255 def test_44924(self):
256 """ compress tiny fragments """
257 packets = [(Ether(dst=self.src_if.local_mac,
258 src=self.src_if.remote_mac) /
259 IP(id=24339, flags="MF", frag=0, ttl=64,
260 src=self.src_if.remote_ip4,
261 dst=self.dst_if.remote_ip4) /
262 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
263 Raw(load='Test-group: IPv4')),
264 (Ether(dst=self.src_if.local_mac,
265 src=self.src_if.remote_mac) /
266 IP(id=24339, flags="MF", frag=3, ttl=64,
267 src=self.src_if.remote_ip4,
268 dst=self.dst_if.remote_ip4) /
269 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
270 Raw(load='.IPv4.Fragmentation.vali')),
271 (Ether(dst=self.src_if.local_mac,
272 src=self.src_if.remote_mac) /
273 IP(id=24339, frag=6, ttl=64,
274 src=self.src_if.remote_ip4,
275 dst=self.dst_if.remote_ip4) /
276 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
277 Raw(load='d; Test-case: 44924'))
278 ]
279
280 self.pg_enable_capture()
281 self.src_if.add_stream(packets)
282 self.pg_start()
283
284 self.dst_if.get_capture(1)
285
Klement Sekera4ee633e2018-12-14 12:00:44 +0100286 def test_frag_1(self):
287 """ fragment of size 1 """
288 self.vapi.cli("clear errors")
289 malformed_packets = [(Ether(dst=self.src_if.local_mac,
290 src=self.src_if.remote_mac) /
291 IP(id=7, len=21, flags="MF", frag=0, ttl=64,
292 src=self.src_if.remote_ip4,
293 dst=self.dst_if.remote_ip4) /
294 ICMP(type="echo-request")),
295 (Ether(dst=self.src_if.local_mac,
296 src=self.src_if.remote_mac) /
297 IP(id=7, len=21, frag=1, ttl=64,
298 src=self.src_if.remote_ip4,
299 dst=self.dst_if.remote_ip4) /
Ole Troan127fbec2019-10-18 15:22:56 +0200300 Raw(load=b'\x08')),
Klement Sekera4ee633e2018-12-14 12:00:44 +0100301 ]
302
303 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
304 IP(id=1000, src=self.src_if.remote_ip4,
305 dst=self.dst_if.remote_ip4) /
306 UDP(sport=1234, dport=5678) /
Ole Troan127fbec2019-10-18 15:22:56 +0200307 Raw(b"X" * 1000))
Klement Sekera4ee633e2018-12-14 12:00:44 +0100308 valid_fragments = fragment_rfc791(p, 400)
309
310 self.pg_enable_capture()
311 self.src_if.add_stream(malformed_packets + valid_fragments)
312 self.pg_start()
313
314 self.dst_if.get_capture(1)
315
Klement Sekera896c8962019-06-24 11:52:49 +0000316 self.assert_packet_counter_equal("ip4-full-reassembly-feature", 1)
Klement Sekera4ee633e2018-12-14 12:00:44 +0100317 # TODO remove above, uncomment below once clearing of counters
318 # is supported
319 # self.assert_packet_counter_equal(
Klement Sekera896c8962019-06-24 11:52:49 +0000320 # "/err/ip4-full-reassembly-feature/malformed packets", 1)
Klement Sekera4ee633e2018-12-14 12:00:44 +0100321
Klement Sekera947a85c2019-07-24 12:40:37 +0000322 def test_random(self):
323 """ random order reassembly """
324
325 fragments = list(self.fragments_200)
326 shuffle(fragments)
327
328 self.pg_enable_capture()
329 self.src_if.add_stream(fragments)
330 self.pg_start()
331
332 packets = self.dst_if.get_capture(len(self.packet_infos))
333 self.verify_capture(packets)
334 self.src_if.assert_nothing_captured()
335
336 # run it all again to verify correctness
337 self.pg_enable_capture()
338 self.src_if.add_stream(fragments)
339 self.pg_start()
340
341 packets = self.dst_if.get_capture(len(self.packet_infos))
342 self.verify_capture(packets)
343 self.src_if.assert_nothing_captured()
344
345 def test_duplicates(self):
Klement Sekera75e7d132017-09-20 08:26:30 +0200346 """ duplicate fragments """
Klement Sekera947a85c2019-07-24 12:40:37 +0000347
Klement Sekera75e7d132017-09-20 08:26:30 +0200348 fragments = [
349 x for (_, frags, _, _) in self.pkt_infos
350 for x in frags
351 for _ in range(0, min(2, len(frags)))
352 ]
Klement Sekera947a85c2019-07-24 12:40:37 +0000353
354 self.pg_enable_capture()
355 self.src_if.add_stream(fragments)
356 self.pg_start()
357
358 packets = self.dst_if.get_capture(len(self.pkt_infos))
359 self.verify_capture(packets)
360 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200361
362 def test_overlap1(self):
363 """ overlapping fragments case #1 """
364
365 fragments = []
366 for _, _, frags_300, frags_200 in self.pkt_infos:
367 if len(frags_300) == 1:
368 fragments.extend(frags_300)
369 else:
370 for i, j in zip(frags_200, frags_300):
371 fragments.extend(i)
372 fragments.extend(j)
373
374 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100375 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200376 self.pg_start()
377
Klement Sekera4c533132018-02-22 11:41:12 +0100378 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera947a85c2019-07-24 12:40:37 +0000379 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100380 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200381
382 # run it all to verify correctness
383 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100384 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200385 self.pg_start()
386
Klement Sekera4c533132018-02-22 11:41:12 +0100387 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera947a85c2019-07-24 12:40:37 +0000388 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100389 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200390
391 def test_overlap2(self):
392 """ overlapping fragments case #2 """
393
394 fragments = []
395 for _, _, frags_300, frags_200 in self.pkt_infos:
396 if len(frags_300) == 1:
397 fragments.extend(frags_300)
398 else:
399 # care must be taken here so that there are no fragments
400 # received by vpp after reassembly is finished, otherwise
401 # new reassemblies will be started and packet generator will
402 # freak out when it detects unfreed buffers
403 zipped = zip(frags_300, frags_200)
Paul Vinciguerrac7834e02019-03-02 10:43:05 -0800404 for i, j in zipped:
Klement Sekera75e7d132017-09-20 08:26:30 +0200405 fragments.extend(i)
406 fragments.extend(j)
Paul Vinciguerrac7834e02019-03-02 10:43:05 -0800407 fragments.pop()
Klement Sekera75e7d132017-09-20 08:26:30 +0200408
409 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100410 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200411 self.pg_start()
412
Klement Sekera4c533132018-02-22 11:41:12 +0100413 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera947a85c2019-07-24 12:40:37 +0000414 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100415 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200416
417 # run it all to verify correctness
418 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100419 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200420 self.pg_start()
421
Klement Sekera4c533132018-02-22 11:41:12 +0100422 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera947a85c2019-07-24 12:40:37 +0000423 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100424 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200425
Klement Sekera947a85c2019-07-24 12:40:37 +0000426 def test_timeout_inline(self):
Klement Sekera75e7d132017-09-20 08:26:30 +0200427 """ timeout (inline) """
428
429 dropped_packet_indexes = set(
430 index for (index, frags, _, _) in self.pkt_infos if len(frags) > 1
431 )
432
Klement Sekera947a85c2019-07-24 12:40:37 +0000433 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
434 max_reassembly_length=3,
435 expire_walk_interval_ms=10000)
436
437 self.pg_enable_capture()
438 self.src_if.add_stream(self.fragments_400)
439 self.pg_start()
440
441 packets = self.dst_if.get_capture(
442 len(self.pkt_infos) - len(dropped_packet_indexes))
443 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100444 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200445
446 def test_timeout_cleanup(self):
447 """ timeout (cleanup) """
448
449 # whole packets + fragmented packets sans last fragment
450 fragments = [
451 x for (_, frags_400, _, _) in self.pkt_infos
452 for x in frags_400[:-1 if len(frags_400) > 1 else None]
453 ]
454
455 # last fragments for fragmented packets
456 fragments2 = [frags_400[-1]
457 for (_, frags_400, _, _) in self.pkt_infos
458 if len(frags_400) > 1]
459
460 dropped_packet_indexes = set(
461 index for (index, frags_400, _, _) in self.pkt_infos
462 if len(frags_400) > 1)
463
464 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +0200465 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +0200466 expire_walk_interval_ms=50)
467
468 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100469 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200470 self.pg_start()
471
472 self.sleep(.25, "wait before sending rest of fragments")
473
Klement Sekera4c533132018-02-22 11:41:12 +0100474 self.src_if.add_stream(fragments2)
Klement Sekera75e7d132017-09-20 08:26:30 +0200475 self.pg_start()
Klement Sekera75e7d132017-09-20 08:26:30 +0200476
Klement Sekera4c533132018-02-22 11:41:12 +0100477 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200478 len(self.pkt_infos) - len(dropped_packet_indexes))
Klement Sekera947a85c2019-07-24 12:40:37 +0000479 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100480 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200481
Klement Sekera947a85c2019-07-24 12:40:37 +0000482 def test_disabled(self):
Klement Sekera75e7d132017-09-20 08:26:30 +0200483 """ reassembly disabled """
484
485 dropped_packet_indexes = set(
486 index for (index, frags_400, _, _) in self.pkt_infos
487 if len(frags_400) > 1)
Klement Sekera947a85c2019-07-24 12:40:37 +0000488
489 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
490 max_reassembly_length=3,
491 expire_walk_interval_ms=10000)
492
493 self.pg_enable_capture()
494 self.src_if.add_stream(self.fragments_400)
495 self.pg_start()
496
497 packets = self.dst_if.get_capture(
498 len(self.pkt_infos) - len(dropped_packet_indexes))
499 self.verify_capture(packets, dropped_packet_indexes)
500 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200501
502
Klement Sekerade34c352019-06-25 11:19:22 +0000503class TestIPv4SVReassembly(VppTestCase):
504 """ IPv4 Shallow Virtual Reassembly """
505
506 @classmethod
507 def setUpClass(cls):
508 super(TestIPv4SVReassembly, cls).setUpClass()
509
510 cls.create_pg_interfaces([0, 1])
511 cls.src_if = cls.pg0
512 cls.dst_if = cls.pg1
513
514 # setup all interfaces
515 for i in cls.pg_interfaces:
516 i.admin_up()
517 i.config_ip4()
518 i.resolve_arp()
519
520 def setUp(self):
521 """ Test setup - force timeout on existing reassemblies """
522 super(TestIPv4SVReassembly, self).setUp()
523 self.vapi.ip_reassembly_enable_disable(
524 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
525 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
526 self.vapi.ip_reassembly_set(
527 timeout_ms=0, max_reassemblies=1000,
528 max_reassembly_length=1000,
529 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
530 expire_walk_interval_ms=10)
531 self.sleep(.25)
532 self.vapi.ip_reassembly_set(
533 timeout_ms=1000000, max_reassemblies=1000,
534 max_reassembly_length=1000,
535 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
536 expire_walk_interval_ms=10000)
537
538 def tearDown(self):
539 super(TestIPv4SVReassembly, self).tearDown()
540 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
541 self.logger.debug(self.vapi.ppcli("show buffers"))
542
543 def test_basic(self):
544 """ basic reassembly """
545 payload_len = 1000
546 payload = ""
547 counter = 0
548 while len(payload) < payload_len:
549 payload += "%u " % counter
550 counter += 1
551
552 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
553 IP(id=1, src=self.src_if.remote_ip4,
554 dst=self.dst_if.remote_ip4) /
555 UDP(sport=1234, dport=5678) /
556 Raw(payload))
557 fragments = fragment_rfc791(p, payload_len/4)
558
559 # send fragment #2 - should be cached inside reassembly
560 self.pg_enable_capture()
561 self.src_if.add_stream(fragments[1])
562 self.pg_start()
563 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
564 self.logger.debug(self.vapi.ppcli("show buffers"))
565 self.logger.debug(self.vapi.ppcli("show trace"))
566 self.dst_if.assert_nothing_captured()
567
568 # send fragment #1 - reassembly is finished now and both fragments
569 # forwarded
570 self.pg_enable_capture()
571 self.src_if.add_stream(fragments[0])
572 self.pg_start()
573 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
574 self.logger.debug(self.vapi.ppcli("show buffers"))
575 self.logger.debug(self.vapi.ppcli("show trace"))
576 c = self.dst_if.get_capture(2)
577 for sent, recvd in zip([fragments[1], fragments[0]], c):
578 self.assertEqual(sent[IP].src, recvd[IP].src)
579 self.assertEqual(sent[IP].dst, recvd[IP].dst)
580 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
581
582 # send rest of fragments - should be immediately forwarded
583 self.pg_enable_capture()
584 self.src_if.add_stream(fragments[2:])
585 self.pg_start()
586 c = self.dst_if.get_capture(len(fragments[2:]))
587 for sent, recvd in zip(fragments[2:], c):
588 self.assertEqual(sent[IP].src, recvd[IP].src)
589 self.assertEqual(sent[IP].dst, recvd[IP].dst)
590 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
591
592 def test_timeout(self):
593 """ reassembly timeout """
594 payload_len = 1000
595 payload = ""
596 counter = 0
597 while len(payload) < payload_len:
598 payload += "%u " % counter
599 counter += 1
600
601 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
602 IP(id=1, src=self.src_if.remote_ip4,
603 dst=self.dst_if.remote_ip4) /
604 UDP(sport=1234, dport=5678) /
605 Raw(payload))
606 fragments = fragment_rfc791(p, payload_len/4)
607
608 self.vapi.ip_reassembly_set(
609 timeout_ms=100, max_reassemblies=1000,
610 max_reassembly_length=1000,
611 expire_walk_interval_ms=50,
612 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
613
614 # send fragments #2 and #1 - should be forwarded
615 self.pg_enable_capture()
616 self.src_if.add_stream(fragments[0:2])
617 self.pg_start()
618 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
619 self.logger.debug(self.vapi.ppcli("show buffers"))
620 self.logger.debug(self.vapi.ppcli("show trace"))
621 c = self.dst_if.get_capture(2)
622 for sent, recvd in zip([fragments[1], fragments[0]], c):
623 self.assertEqual(sent[IP].src, recvd[IP].src)
624 self.assertEqual(sent[IP].dst, recvd[IP].dst)
625 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
626
627 # wait for cleanup
628 self.sleep(.25, "wait before sending rest of fragments")
629
630 # send rest of fragments - shouldn't be forwarded
631 self.pg_enable_capture()
632 self.src_if.add_stream(fragments[2:])
633 self.pg_start()
634 self.dst_if.assert_nothing_captured()
635
636 def test_lru(self):
637 """ reassembly reuses LRU element """
638
639 self.vapi.ip_reassembly_set(
640 timeout_ms=1000000, max_reassemblies=1,
641 max_reassembly_length=1000,
642 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
643 expire_walk_interval_ms=10000)
644
645 payload_len = 1000
646 payload = ""
647 counter = 0
648 while len(payload) < payload_len:
649 payload += "%u " % counter
650 counter += 1
651
652 packet_count = 10
653
654 fragments = [f
655 for i in range(packet_count)
656 for p in (Ether(dst=self.src_if.local_mac,
657 src=self.src_if.remote_mac) /
658 IP(id=i, src=self.src_if.remote_ip4,
659 dst=self.dst_if.remote_ip4) /
660 UDP(sport=1234, dport=5678) /
661 Raw(payload))
662 for f in fragment_rfc791(p, payload_len/4)]
663
664 self.pg_enable_capture()
665 self.src_if.add_stream(fragments)
666 self.pg_start()
667 c = self.dst_if.get_capture(len(fragments))
668 for sent, recvd in zip(fragments, c):
669 self.assertEqual(sent[IP].src, recvd[IP].src)
670 self.assertEqual(sent[IP].dst, recvd[IP].dst)
671 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
672
Klement Sekera18c6cd92020-07-10 09:29:48 +0000673 def send_mixed_and_verify_capture(self, traffic):
674 stream = []
675 for t in traffic:
676 for c in range(t['count']):
677 stream.append(
678 (Ether(dst=self.src_if.local_mac,
679 src=self.src_if.remote_mac) /
680 IP(id=self.counter,
681 flags=t['flags'],
682 src=self.src_if.remote_ip4,
683 dst=self.dst_if.remote_ip4) /
684 UDP(sport=1234, dport=5678) /
685 Raw("abcdef")))
686 self.counter = self.counter + 1
687
688 self.pg_enable_capture()
689 self.src_if.add_stream(stream)
690 self.pg_start()
691 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
692 self.logger.debug(self.vapi.ppcli("show buffers"))
693 self.logger.debug(self.vapi.ppcli("show trace"))
694 self.dst_if.get_capture(len(stream))
695
696 def test_mixed(self):
697 """ mixed traffic correctly passes through SVR """
698 self.counter = 1
699
700 self.send_mixed_and_verify_capture([{'count': 1, 'flags': ''}])
701 self.send_mixed_and_verify_capture([{'count': 2, 'flags': ''}])
702 self.send_mixed_and_verify_capture([{'count': 3, 'flags': ''}])
703 self.send_mixed_and_verify_capture([{'count': 8, 'flags': ''}])
704 self.send_mixed_and_verify_capture([{'count': 257, 'flags': ''}])
705
706 self.send_mixed_and_verify_capture([{'count': 1, 'flags': 'MF'}])
707 self.send_mixed_and_verify_capture([{'count': 2, 'flags': 'MF'}])
708 self.send_mixed_and_verify_capture([{'count': 3, 'flags': 'MF'}])
709 self.send_mixed_and_verify_capture([{'count': 8, 'flags': 'MF'}])
710 self.send_mixed_and_verify_capture([{'count': 257, 'flags': 'MF'}])
711
712 self.send_mixed_and_verify_capture(
713 [{'count': 1, 'flags': ''}, {'count': 1, 'flags': 'MF'}])
714 self.send_mixed_and_verify_capture(
715 [{'count': 2, 'flags': ''}, {'count': 2, 'flags': 'MF'}])
716 self.send_mixed_and_verify_capture(
717 [{'count': 3, 'flags': ''}, {'count': 3, 'flags': 'MF'}])
718 self.send_mixed_and_verify_capture(
719 [{'count': 8, 'flags': ''}, {'count': 8, 'flags': 'MF'}])
720 self.send_mixed_and_verify_capture(
721 [{'count': 129, 'flags': ''}, {'count': 129, 'flags': 'MF'}])
722
723 self.send_mixed_and_verify_capture(
724 [{'count': 1, 'flags': ''}, {'count': 1, 'flags': 'MF'},
725 {'count': 1, 'flags': ''}, {'count': 1, 'flags': 'MF'}])
726 self.send_mixed_and_verify_capture(
727 [{'count': 2, 'flags': ''}, {'count': 2, 'flags': 'MF'},
728 {'count': 2, 'flags': ''}, {'count': 2, 'flags': 'MF'}])
729 self.send_mixed_and_verify_capture(
730 [{'count': 3, 'flags': ''}, {'count': 3, 'flags': 'MF'},
731 {'count': 3, 'flags': ''}, {'count': 3, 'flags': 'MF'}])
732 self.send_mixed_and_verify_capture(
733 [{'count': 8, 'flags': ''}, {'count': 8, 'flags': 'MF'},
734 {'count': 8, 'flags': ''}, {'count': 8, 'flags': 'MF'}])
735 self.send_mixed_and_verify_capture(
736 [{'count': 65, 'flags': ''}, {'count': 65, 'flags': 'MF'},
737 {'count': 65, 'flags': ''}, {'count': 65, 'flags': 'MF'}])
738
Klement Sekerade34c352019-06-25 11:19:22 +0000739
Klement Sekera630ab582019-07-19 09:14:19 +0000740class TestIPv4MWReassembly(VppTestCase):
741 """ IPv4 Reassembly (multiple workers) """
742 worker_config = "workers %d" % worker_count
743
744 @classmethod
745 def setUpClass(cls):
746 super(TestIPv4MWReassembly, cls).setUpClass()
747
748 cls.create_pg_interfaces(range(worker_count+1))
749 cls.src_if = cls.pg0
750 cls.send_ifs = cls.pg_interfaces[:-1]
751 cls.dst_if = cls.pg_interfaces[-1]
752
753 # setup all interfaces
754 for i in cls.pg_interfaces:
755 i.admin_up()
756 i.config_ip4()
757 i.resolve_arp()
758
759 # packets sizes reduced here because we are generating packets without
760 # Ethernet headers, which are added later (diff fragments go via
761 # different interfaces)
762 cls.packet_sizes = [64-len(Ether()), 512-len(Ether()),
763 1518-len(Ether()), 9018-len(Ether())]
764 cls.padding = " abcdefghijklmn"
765 cls.create_stream(cls.packet_sizes)
766 cls.create_fragments()
767
768 @classmethod
769 def tearDownClass(cls):
770 super(TestIPv4MWReassembly, cls).tearDownClass()
771
772 def setUp(self):
773 """ Test setup - force timeout on existing reassemblies """
774 super(TestIPv4MWReassembly, self).setUp()
775 for intf in self.send_ifs:
776 self.vapi.ip_reassembly_enable_disable(
777 sw_if_index=intf.sw_if_index, enable_ip4=True)
778 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
779 max_reassembly_length=1000,
780 expire_walk_interval_ms=10)
781 self.sleep(.25)
782 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
783 max_reassembly_length=1000,
784 expire_walk_interval_ms=10000)
785
786 def tearDown(self):
787 super(TestIPv4MWReassembly, self).tearDown()
788
789 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +0000790 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
Klement Sekera630ab582019-07-19 09:14:19 +0000791 self.logger.debug(self.vapi.ppcli("show buffers"))
792
793 @classmethod
794 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
795 """Create input packet stream
796
797 :param list packet_sizes: Required packet sizes.
798 """
799 for i in range(0, packet_count):
800 info = cls.create_packet_info(cls.src_if, cls.src_if)
801 payload = cls.info_to_payload(info)
802 p = (IP(id=info.index, src=cls.src_if.remote_ip4,
803 dst=cls.dst_if.remote_ip4) /
804 UDP(sport=1234, dport=5678) /
805 Raw(payload))
806 size = packet_sizes[(i // 2) % len(packet_sizes)]
807 cls.extend_packet(p, size, cls.padding)
808 info.data = p
809
810 @classmethod
811 def create_fragments(cls):
812 infos = cls._packet_infos
813 cls.pkt_infos = []
814 for index, info in six.iteritems(infos):
815 p = info.data
816 # cls.logger.debug(ppp("Packet:",
817 # p.__class__(scapy.compat.raw(p))))
818 fragments_400 = fragment_rfc791(p, 400)
819 cls.pkt_infos.append((index, fragments_400))
820 cls.fragments_400 = [
821 x for (_, frags) in cls.pkt_infos for x in frags]
822 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, " %
823 (len(infos), len(cls.fragments_400)))
824
825 def verify_capture(self, capture, dropped_packet_indexes=[]):
826 """Verify captured packet stream.
827
828 :param list capture: Captured packet stream.
829 """
830 info = None
831 seen = set()
832 for packet in capture:
833 try:
834 self.logger.debug(ppp("Got packet:", packet))
835 ip = packet[IP]
836 udp = packet[UDP]
837 payload_info = self.payload_to_info(packet[Raw])
838 packet_index = payload_info.index
839 self.assertTrue(
840 packet_index not in dropped_packet_indexes,
841 ppp("Packet received, but should be dropped:", packet))
842 if packet_index in seen:
843 raise Exception(ppp("Duplicate packet received", packet))
844 seen.add(packet_index)
845 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
846 info = self._packet_infos[packet_index]
847 self.assertTrue(info is not None)
848 self.assertEqual(packet_index, info.index)
849 saved_packet = info.data
850 self.assertEqual(ip.src, saved_packet[IP].src)
851 self.assertEqual(ip.dst, saved_packet[IP].dst)
852 self.assertEqual(udp.payload, saved_packet[UDP].payload)
853 except Exception:
854 self.logger.error(ppp("Unexpected or invalid packet:", packet))
855 raise
856 for index in self._packet_infos:
857 self.assertTrue(index in seen or index in dropped_packet_indexes,
858 "Packet with packet_index %d not received" % index)
859
860 def send_packets(self, packets):
861 for counter in range(worker_count):
862 if 0 == len(packets[counter]):
863 continue
864 send_if = self.send_ifs[counter]
865 send_if.add_stream(
866 (Ether(dst=send_if.local_mac, src=send_if.remote_mac) / x
867 for x in packets[counter]),
868 worker=counter)
869 self.pg_start()
870
871 def test_worker_conflict(self):
872 """ 1st and FO=0 fragments on different workers """
873
874 # in first wave we send fragments which don't start at offset 0
875 # then we send fragments with offset 0 on a different thread
876 # then the rest of packets on a random thread
877 first_packets = [[] for n in range(worker_count)]
878 second_packets = [[] for n in range(worker_count)]
879 rest_of_packets = [[] for n in range(worker_count)]
880 for (_, p) in self.pkt_infos:
881 wi = randrange(worker_count)
882 second_packets[wi].append(p[0])
883 if len(p) <= 1:
884 continue
885 wi2 = wi
886 while wi2 == wi:
887 wi2 = randrange(worker_count)
888 first_packets[wi2].append(p[1])
889 wi3 = randrange(worker_count)
890 rest_of_packets[wi3].extend(p[2:])
891
892 self.pg_enable_capture()
893 self.send_packets(first_packets)
894 self.send_packets(second_packets)
895 self.send_packets(rest_of_packets)
896
897 packets = self.dst_if.get_capture(len(self.pkt_infos))
898 self.verify_capture(packets)
899 for send_if in self.send_ifs:
900 send_if.assert_nothing_captured()
901
Klement Sekera68bae5b2019-10-10 18:57:34 +0000902 self.logger.debug(self.vapi.ppcli("show trace"))
903 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
904 self.logger.debug(self.vapi.ppcli("show buffers"))
905 self.vapi.cli("clear trace")
906
Klement Sekera630ab582019-07-19 09:14:19 +0000907 self.pg_enable_capture()
908 self.send_packets(first_packets)
909 self.send_packets(second_packets)
910 self.send_packets(rest_of_packets)
911
912 packets = self.dst_if.get_capture(len(self.pkt_infos))
913 self.verify_capture(packets)
914 for send_if in self.send_ifs:
915 send_if.assert_nothing_captured()
916
917
Klement Sekera947a85c2019-07-24 12:40:37 +0000918class TestIPv6Reassembly(VppTestCase):
Klement Sekera75e7d132017-09-20 08:26:30 +0200919 """ IPv6 Reassembly """
920
921 @classmethod
922 def setUpClass(cls):
923 super(TestIPv6Reassembly, cls).setUpClass()
924
Klement Sekera4c533132018-02-22 11:41:12 +0100925 cls.create_pg_interfaces([0, 1])
926 cls.src_if = cls.pg0
927 cls.dst_if = cls.pg1
Klement Sekera75e7d132017-09-20 08:26:30 +0200928
929 # setup all interfaces
930 for i in cls.pg_interfaces:
931 i.admin_up()
932 i.config_ip6()
933 i.resolve_ndp()
934
Klement Sekera75e7d132017-09-20 08:26:30 +0200935 # packet sizes
936 cls.packet_sizes = [64, 512, 1518, 9018]
937 cls.padding = " abcdefghijklmn"
938 cls.create_stream(cls.packet_sizes)
939 cls.create_fragments()
940
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -0700941 @classmethod
942 def tearDownClass(cls):
943 super(TestIPv6Reassembly, cls).tearDownClass()
944
Klement Sekera75e7d132017-09-20 08:26:30 +0200945 def setUp(self):
946 """ Test setup - force timeout on existing reassemblies """
947 super(TestIPv6Reassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +0100948 self.vapi.ip_reassembly_enable_disable(
949 sw_if_index=self.src_if.sw_if_index, enable_ip6=True)
Klement Sekera75e7d132017-09-20 08:26:30 +0200950 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +0200951 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +0200952 expire_walk_interval_ms=10, is_ip6=1)
953 self.sleep(.25)
954 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +0200955 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +0200956 expire_walk_interval_ms=10000, is_ip6=1)
Klement Sekera896c8962019-06-24 11:52:49 +0000957 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +0100958 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +0200959
960 def tearDown(self):
961 super(TestIPv6Reassembly, self).tearDown()
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700962
963 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +0000964 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +0100965 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +0200966
967 @classmethod
968 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
969 """Create input packet stream for defined interface.
970
971 :param list packet_sizes: Required packet sizes.
972 """
973 for i in range(0, packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +0100974 info = cls.create_packet_info(cls.src_if, cls.src_if)
Klement Sekera75e7d132017-09-20 08:26:30 +0200975 payload = cls.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +0100976 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
977 IPv6(src=cls.src_if.remote_ip6,
978 dst=cls.dst_if.remote_ip6) /
979 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200980 Raw(payload))
981 size = packet_sizes[(i // 2) % len(packet_sizes)]
982 cls.extend_packet(p, size, cls.padding)
983 info.data = p
984
985 @classmethod
986 def create_fragments(cls):
987 infos = cls._packet_infos
988 cls.pkt_infos = []
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -0800989 for index, info in six.iteritems(infos):
Klement Sekera75e7d132017-09-20 08:26:30 +0200990 p = info.data
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700991 # cls.logger.debug(ppp("Packet:",
992 # p.__class__(scapy.compat.raw(p))))
Klement Sekera75e7d132017-09-20 08:26:30 +0200993 fragments_400 = fragment_rfc8200(p, info.index, 400)
994 fragments_300 = fragment_rfc8200(p, info.index, 300)
995 cls.pkt_infos.append((index, fragments_400, fragments_300))
996 cls.fragments_400 = [
997 x for _, frags, _ in cls.pkt_infos for x in frags]
998 cls.fragments_300 = [
999 x for _, _, frags in cls.pkt_infos for x in frags]
1000 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
1001 "and %s 300-byte fragments" %
1002 (len(infos), len(cls.fragments_400),
1003 len(cls.fragments_300)))
1004
Klement Sekera947a85c2019-07-24 12:40:37 +00001005 def verify_capture(self, capture, dropped_packet_indexes=[]):
1006 """Verify captured packet strea .
1007
1008 :param list capture: Captured packet stream.
1009 """
1010 info = None
1011 seen = set()
1012 for packet in capture:
1013 try:
1014 self.logger.debug(ppp("Got packet:", packet))
1015 ip = packet[IPv6]
1016 udp = packet[UDP]
1017 payload_info = self.payload_to_info(packet[Raw])
1018 packet_index = payload_info.index
1019 self.assertTrue(
1020 packet_index not in dropped_packet_indexes,
1021 ppp("Packet received, but should be dropped:", packet))
1022 if packet_index in seen:
1023 raise Exception(ppp("Duplicate packet received", packet))
1024 seen.add(packet_index)
1025 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
1026 info = self._packet_infos[packet_index]
1027 self.assertTrue(info is not None)
1028 self.assertEqual(packet_index, info.index)
1029 saved_packet = info.data
1030 self.assertEqual(ip.src, saved_packet[IPv6].src)
1031 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
1032 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1033 except Exception:
1034 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1035 raise
1036 for index in self._packet_infos:
1037 self.assertTrue(index in seen or index in dropped_packet_indexes,
1038 "Packet with packet_index %d not received" % index)
1039
1040 def test_reassembly(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001041 """ basic reassembly """
1042
Klement Sekera947a85c2019-07-24 12:40:37 +00001043 self.pg_enable_capture()
1044 self.src_if.add_stream(self.fragments_400)
1045 self.pg_start()
1046
1047 packets = self.dst_if.get_capture(len(self.pkt_infos))
1048 self.verify_capture(packets)
1049 self.src_if.assert_nothing_captured()
1050
1051 # run it all again to verify correctness
1052 self.pg_enable_capture()
1053 self.src_if.add_stream(self.fragments_400)
1054 self.pg_start()
1055
1056 packets = self.dst_if.get_capture(len(self.pkt_infos))
1057 self.verify_capture(packets)
1058 self.src_if.assert_nothing_captured()
1059
Klement Sekera769145c2019-03-06 11:59:57 +01001060 def test_buffer_boundary(self):
1061 """ fragment header crossing buffer boundary """
1062
1063 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1064 IPv6(src=self.src_if.remote_ip6,
1065 dst=self.src_if.local_ip6) /
1066 IPv6ExtHdrHopByHop(
1067 options=[HBHOptUnknown(otype=0xff, optlen=0)] * 1000) /
1068 IPv6ExtHdrFragment(m=1) /
1069 UDP(sport=1234, dport=5678) /
1070 Raw())
1071 self.pg_enable_capture()
1072 self.src_if.add_stream([p])
1073 self.pg_start()
1074 self.src_if.assert_nothing_captured()
1075 self.dst_if.assert_nothing_captured()
1076
Klement Sekera947a85c2019-07-24 12:40:37 +00001077 def test_reversed(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001078 """ reverse order reassembly """
1079
Klement Sekera947a85c2019-07-24 12:40:37 +00001080 fragments = list(self.fragments_400)
1081 fragments.reverse()
1082
1083 self.pg_enable_capture()
1084 self.src_if.add_stream(fragments)
1085 self.pg_start()
1086
1087 packets = self.dst_if.get_capture(len(self.pkt_infos))
1088 self.verify_capture(packets)
1089 self.src_if.assert_nothing_captured()
1090
1091 # run it all again to verify correctness
1092 self.pg_enable_capture()
1093 self.src_if.add_stream(fragments)
1094 self.pg_start()
1095
1096 packets = self.dst_if.get_capture(len(self.pkt_infos))
1097 self.verify_capture(packets)
1098 self.src_if.assert_nothing_captured()
1099
1100 def test_random(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001101 """ random order reassembly """
1102
Klement Sekera947a85c2019-07-24 12:40:37 +00001103 fragments = list(self.fragments_400)
1104 shuffle(fragments)
1105
1106 self.pg_enable_capture()
1107 self.src_if.add_stream(fragments)
1108 self.pg_start()
1109
1110 packets = self.dst_if.get_capture(len(self.pkt_infos))
1111 self.verify_capture(packets)
1112 self.src_if.assert_nothing_captured()
1113
1114 # run it all again to verify correctness
1115 self.pg_enable_capture()
1116 self.src_if.add_stream(fragments)
1117 self.pg_start()
1118
1119 packets = self.dst_if.get_capture(len(self.pkt_infos))
1120 self.verify_capture(packets)
1121 self.src_if.assert_nothing_captured()
1122
1123 def test_duplicates(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001124 """ duplicate fragments """
1125
1126 fragments = [
1127 x for (_, frags, _) in self.pkt_infos
1128 for x in frags
1129 for _ in range(0, min(2, len(frags)))
1130 ]
Klement Sekera947a85c2019-07-24 12:40:37 +00001131
1132 self.pg_enable_capture()
1133 self.src_if.add_stream(fragments)
1134 self.pg_start()
1135
1136 packets = self.dst_if.get_capture(len(self.pkt_infos))
1137 self.verify_capture(packets)
1138 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +02001139
Klement Sekera3a343d42019-05-16 14:35:46 +02001140 def test_long_fragment_chain(self):
1141 """ long fragment chain """
1142
1143 error_cnt_str = \
Klement Sekera896c8962019-06-24 11:52:49 +00001144 "/err/ip6-full-reassembly-feature/fragment chain too long (drop)"
Klement Sekera3a343d42019-05-16 14:35:46 +02001145
Klement Sekera34641f22019-05-22 20:18:26 +02001146 error_cnt = self.statistics.get_err_counter(error_cnt_str)
Klement Sekera3a343d42019-05-16 14:35:46 +02001147
1148 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
1149 max_reassembly_length=3,
1150 expire_walk_interval_ms=50, is_ip6=1)
1151
1152 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1153 IPv6(src=self.src_if.remote_ip6,
1154 dst=self.dst_if.remote_ip6) /
1155 UDP(sport=1234, dport=5678) /
Ole Troan127fbec2019-10-18 15:22:56 +02001156 Raw(b"X" * 1000))
Klement Sekera3a343d42019-05-16 14:35:46 +02001157 frags = fragment_rfc8200(p, 1, 300) + fragment_rfc8200(p, 2, 500)
1158
1159 self.pg_enable_capture()
1160 self.src_if.add_stream(frags)
1161 self.pg_start()
1162
1163 self.dst_if.get_capture(1)
Klement Sekera34641f22019-05-22 20:18:26 +02001164 self.assert_error_counter_equal(error_cnt_str, error_cnt + 1)
Klement Sekera3a343d42019-05-16 14:35:46 +02001165
Klement Sekera75e7d132017-09-20 08:26:30 +02001166 def test_overlap1(self):
Klement Sekera947a85c2019-07-24 12:40:37 +00001167 """ overlapping fragments case #1 """
Klement Sekera75e7d132017-09-20 08:26:30 +02001168
1169 fragments = []
1170 for _, frags_400, frags_300 in self.pkt_infos:
1171 if len(frags_300) == 1:
1172 fragments.extend(frags_400)
1173 else:
1174 for i, j in zip(frags_300, frags_400):
1175 fragments.extend(i)
1176 fragments.extend(j)
1177
1178 dropped_packet_indexes = set(
1179 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
1180 )
1181
1182 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001183 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001184 self.pg_start()
1185
Klement Sekera4c533132018-02-22 11:41:12 +01001186 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +02001187 len(self.pkt_infos) - len(dropped_packet_indexes))
Klement Sekera947a85c2019-07-24 12:40:37 +00001188 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001189 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +02001190
1191 def test_overlap2(self):
Klement Sekera947a85c2019-07-24 12:40:37 +00001192 """ overlapping fragments case #2 """
Klement Sekera75e7d132017-09-20 08:26:30 +02001193
1194 fragments = []
Klement Sekera4c533132018-02-22 11:41:12 +01001195 for _, frags_400, frags_300 in self.pkt_infos:
Klement Sekera75e7d132017-09-20 08:26:30 +02001196 if len(frags_400) == 1:
1197 fragments.extend(frags_400)
1198 else:
1199 # care must be taken here so that there are no fragments
1200 # received by vpp after reassembly is finished, otherwise
1201 # new reassemblies will be started and packet generator will
1202 # freak out when it detects unfreed buffers
Klement Sekera4c533132018-02-22 11:41:12 +01001203 zipped = zip(frags_400, frags_300)
Paul Vinciguerrac7834e02019-03-02 10:43:05 -08001204 for i, j in zipped:
Klement Sekera75e7d132017-09-20 08:26:30 +02001205 fragments.extend(i)
1206 fragments.extend(j)
Paul Vinciguerrac7834e02019-03-02 10:43:05 -08001207 fragments.pop()
Klement Sekera75e7d132017-09-20 08:26:30 +02001208
1209 dropped_packet_indexes = set(
1210 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
1211 )
1212
1213 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001214 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001215 self.pg_start()
1216
Klement Sekera4c533132018-02-22 11:41:12 +01001217 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +02001218 len(self.pkt_infos) - len(dropped_packet_indexes))
Klement Sekera947a85c2019-07-24 12:40:37 +00001219 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001220 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +02001221
Klement Sekera947a85c2019-07-24 12:40:37 +00001222 def test_timeout_inline(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001223 """ timeout (inline) """
1224
1225 dropped_packet_indexes = set(
1226 index for (index, frags, _) in self.pkt_infos if len(frags) > 1
1227 )
1228
Klement Sekera947a85c2019-07-24 12:40:37 +00001229 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1230 max_reassembly_length=3,
1231 expire_walk_interval_ms=10000, is_ip6=1)
1232
1233 self.pg_enable_capture()
1234 self.src_if.add_stream(self.fragments_400)
1235 self.pg_start()
1236
1237 packets = self.dst_if.get_capture(
1238 len(self.pkt_infos) - len(dropped_packet_indexes))
1239 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001240 pkts = self.src_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +02001241 expected_count=len(dropped_packet_indexes))
1242 for icmp in pkts:
1243 self.assertIn(ICMPv6TimeExceeded, icmp)
1244 self.assertIn(IPv6ExtHdrFragment, icmp)
1245 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
1246 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
1247
1248 def test_timeout_cleanup(self):
1249 """ timeout (cleanup) """
1250
1251 # whole packets + fragmented packets sans last fragment
1252 fragments = [
1253 x for (_, frags_400, _) in self.pkt_infos
1254 for x in frags_400[:-1 if len(frags_400) > 1 else None]
1255 ]
1256
1257 # last fragments for fragmented packets
1258 fragments2 = [frags_400[-1]
1259 for (_, frags_400, _) in self.pkt_infos
1260 if len(frags_400) > 1]
1261
1262 dropped_packet_indexes = set(
1263 index for (index, frags_400, _) in self.pkt_infos
1264 if len(frags_400) > 1)
1265
1266 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001267 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001268 expire_walk_interval_ms=50)
1269
1270 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001271 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001272 expire_walk_interval_ms=50, is_ip6=1)
1273
1274 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001275 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001276 self.pg_start()
1277
1278 self.sleep(.25, "wait before sending rest of fragments")
1279
Klement Sekera4c533132018-02-22 11:41:12 +01001280 self.src_if.add_stream(fragments2)
Klement Sekera75e7d132017-09-20 08:26:30 +02001281 self.pg_start()
Klement Sekera75e7d132017-09-20 08:26:30 +02001282
Klement Sekera4c533132018-02-22 11:41:12 +01001283 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +02001284 len(self.pkt_infos) - len(dropped_packet_indexes))
Klement Sekera947a85c2019-07-24 12:40:37 +00001285 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001286 pkts = self.src_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +02001287 expected_count=len(dropped_packet_indexes))
1288 for icmp in pkts:
1289 self.assertIn(ICMPv6TimeExceeded, icmp)
1290 self.assertIn(IPv6ExtHdrFragment, icmp)
1291 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
1292 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
1293
Klement Sekera947a85c2019-07-24 12:40:37 +00001294 def test_disabled(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001295 """ reassembly disabled """
1296
1297 dropped_packet_indexes = set(
1298 index for (index, frags_400, _) in self.pkt_infos
1299 if len(frags_400) > 1)
Klement Sekera947a85c2019-07-24 12:40:37 +00001300
1301 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
1302 max_reassembly_length=3,
1303 expire_walk_interval_ms=10000, is_ip6=1)
1304
1305 self.pg_enable_capture()
1306 self.src_if.add_stream(self.fragments_400)
1307 self.pg_start()
1308
1309 packets = self.dst_if.get_capture(
1310 len(self.pkt_infos) - len(dropped_packet_indexes))
1311 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001312 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +02001313
1314 def test_missing_upper(self):
1315 """ missing upper layer """
Klement Sekera4c533132018-02-22 11:41:12 +01001316 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1317 IPv6(src=self.src_if.remote_ip6,
1318 dst=self.src_if.local_ip6) /
1319 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001320 Raw())
1321 self.extend_packet(p, 1000, self.padding)
1322 fragments = fragment_rfc8200(p, 1, 500)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001323 bad_fragment = p.__class__(scapy.compat.raw(fragments[1]))
Klement Sekera75e7d132017-09-20 08:26:30 +02001324 bad_fragment[IPv6ExtHdrFragment].nh = 59
1325 bad_fragment[IPv6ExtHdrFragment].offset = 0
1326 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001327 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +02001328 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +01001329 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +02001330 icmp = pkts[0]
1331 self.assertIn(ICMPv6ParamProblem, icmp)
1332 self.assert_equal(icmp[ICMPv6ParamProblem].code, 3, "ICMP code")
1333
1334 def test_invalid_frag_size(self):
1335 """ fragment size not a multiple of 8 """
Klement Sekera4c533132018-02-22 11:41:12 +01001336 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1337 IPv6(src=self.src_if.remote_ip6,
1338 dst=self.src_if.local_ip6) /
1339 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001340 Raw())
1341 self.extend_packet(p, 1000, self.padding)
1342 fragments = fragment_rfc8200(p, 1, 500)
1343 bad_fragment = fragments[0]
1344 self.extend_packet(bad_fragment, len(bad_fragment) + 5)
1345 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001346 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +02001347 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +01001348 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +02001349 icmp = pkts[0]
1350 self.assertIn(ICMPv6ParamProblem, icmp)
1351 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1352
1353 def test_invalid_packet_size(self):
1354 """ total packet size > 65535 """
Klement Sekera4c533132018-02-22 11:41:12 +01001355 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1356 IPv6(src=self.src_if.remote_ip6,
1357 dst=self.src_if.local_ip6) /
1358 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001359 Raw())
1360 self.extend_packet(p, 1000, self.padding)
1361 fragments = fragment_rfc8200(p, 1, 500)
1362 bad_fragment = fragments[1]
1363 bad_fragment[IPv6ExtHdrFragment].offset = 65500
1364 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001365 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +02001366 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +01001367 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +02001368 icmp = pkts[0]
1369 self.assertIn(ICMPv6ParamProblem, icmp)
1370 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1371
1372
Klement Sekera630ab582019-07-19 09:14:19 +00001373class TestIPv6MWReassembly(VppTestCase):
1374 """ IPv6 Reassembly (multiple workers) """
1375 worker_config = "workers %d" % worker_count
1376
1377 @classmethod
1378 def setUpClass(cls):
1379 super(TestIPv6MWReassembly, cls).setUpClass()
1380
1381 cls.create_pg_interfaces(range(worker_count+1))
1382 cls.src_if = cls.pg0
1383 cls.send_ifs = cls.pg_interfaces[:-1]
1384 cls.dst_if = cls.pg_interfaces[-1]
1385
1386 # setup all interfaces
1387 for i in cls.pg_interfaces:
1388 i.admin_up()
1389 i.config_ip6()
1390 i.resolve_ndp()
1391
1392 # packets sizes reduced here because we are generating packets without
1393 # Ethernet headers, which are added later (diff fragments go via
1394 # different interfaces)
1395 cls.packet_sizes = [64-len(Ether()), 512-len(Ether()),
1396 1518-len(Ether()), 9018-len(Ether())]
1397 cls.padding = " abcdefghijklmn"
1398 cls.create_stream(cls.packet_sizes)
1399 cls.create_fragments()
1400
1401 @classmethod
1402 def tearDownClass(cls):
1403 super(TestIPv6MWReassembly, cls).tearDownClass()
1404
1405 def setUp(self):
1406 """ Test setup - force timeout on existing reassemblies """
1407 super(TestIPv6MWReassembly, self).setUp()
1408 for intf in self.send_ifs:
1409 self.vapi.ip_reassembly_enable_disable(
1410 sw_if_index=intf.sw_if_index, enable_ip6=True)
1411 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1412 max_reassembly_length=1000,
1413 expire_walk_interval_ms=10, is_ip6=1)
1414 self.sleep(.25)
1415 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1416 max_reassembly_length=1000,
1417 expire_walk_interval_ms=1000, is_ip6=1)
1418
1419 def tearDown(self):
1420 super(TestIPv6MWReassembly, self).tearDown()
1421
1422 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +00001423 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
Klement Sekera630ab582019-07-19 09:14:19 +00001424 self.logger.debug(self.vapi.ppcli("show buffers"))
1425
1426 @classmethod
1427 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
1428 """Create input packet stream
1429
1430 :param list packet_sizes: Required packet sizes.
1431 """
1432 for i in range(0, packet_count):
1433 info = cls.create_packet_info(cls.src_if, cls.src_if)
1434 payload = cls.info_to_payload(info)
1435 p = (IPv6(src=cls.src_if.remote_ip6,
1436 dst=cls.dst_if.remote_ip6) /
1437 UDP(sport=1234, dport=5678) /
1438 Raw(payload))
1439 size = packet_sizes[(i // 2) % len(packet_sizes)]
1440 cls.extend_packet(p, size, cls.padding)
1441 info.data = p
1442
1443 @classmethod
1444 def create_fragments(cls):
1445 infos = cls._packet_infos
1446 cls.pkt_infos = []
1447 for index, info in six.iteritems(infos):
1448 p = info.data
1449 # cls.logger.debug(ppp("Packet:",
1450 # p.__class__(scapy.compat.raw(p))))
1451 fragments_400 = fragment_rfc8200(p, index, 400)
1452 cls.pkt_infos.append((index, fragments_400))
1453 cls.fragments_400 = [
1454 x for (_, frags) in cls.pkt_infos for x in frags]
1455 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, " %
1456 (len(infos), len(cls.fragments_400)))
1457
1458 def verify_capture(self, capture, dropped_packet_indexes=[]):
1459 """Verify captured packet strea .
1460
1461 :param list capture: Captured packet stream.
1462 """
1463 info = None
1464 seen = set()
1465 for packet in capture:
1466 try:
1467 self.logger.debug(ppp("Got packet:", packet))
1468 ip = packet[IPv6]
1469 udp = packet[UDP]
1470 payload_info = self.payload_to_info(packet[Raw])
1471 packet_index = payload_info.index
1472 self.assertTrue(
1473 packet_index not in dropped_packet_indexes,
1474 ppp("Packet received, but should be dropped:", packet))
1475 if packet_index in seen:
1476 raise Exception(ppp("Duplicate packet received", packet))
1477 seen.add(packet_index)
1478 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
1479 info = self._packet_infos[packet_index]
1480 self.assertTrue(info is not None)
1481 self.assertEqual(packet_index, info.index)
1482 saved_packet = info.data
1483 self.assertEqual(ip.src, saved_packet[IPv6].src)
1484 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
1485 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1486 except Exception:
1487 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1488 raise
1489 for index in self._packet_infos:
1490 self.assertTrue(index in seen or index in dropped_packet_indexes,
1491 "Packet with packet_index %d not received" % index)
1492
1493 def send_packets(self, packets):
1494 for counter in range(worker_count):
1495 if 0 == len(packets[counter]):
1496 continue
1497 send_if = self.send_ifs[counter]
1498 send_if.add_stream(
1499 (Ether(dst=send_if.local_mac, src=send_if.remote_mac) / x
1500 for x in packets[counter]),
1501 worker=counter)
1502 self.pg_start()
1503
1504 def test_worker_conflict(self):
1505 """ 1st and FO=0 fragments on different workers """
1506
1507 # in first wave we send fragments which don't start at offset 0
1508 # then we send fragments with offset 0 on a different thread
1509 # then the rest of packets on a random thread
1510 first_packets = [[] for n in range(worker_count)]
1511 second_packets = [[] for n in range(worker_count)]
1512 rest_of_packets = [[] for n in range(worker_count)]
1513 for (_, p) in self.pkt_infos:
1514 wi = randrange(worker_count)
1515 second_packets[wi].append(p[0])
1516 if len(p) <= 1:
1517 continue
1518 wi2 = wi
1519 while wi2 == wi:
1520 wi2 = randrange(worker_count)
1521 first_packets[wi2].append(p[1])
1522 wi3 = randrange(worker_count)
1523 rest_of_packets[wi3].extend(p[2:])
1524
1525 self.pg_enable_capture()
1526 self.send_packets(first_packets)
1527 self.send_packets(second_packets)
1528 self.send_packets(rest_of_packets)
1529
1530 packets = self.dst_if.get_capture(len(self.pkt_infos))
1531 self.verify_capture(packets)
1532 for send_if in self.send_ifs:
1533 send_if.assert_nothing_captured()
1534
Klement Sekera68bae5b2019-10-10 18:57:34 +00001535 self.logger.debug(self.vapi.ppcli("show trace"))
1536 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1537 self.logger.debug(self.vapi.ppcli("show buffers"))
1538 self.vapi.cli("clear trace")
1539
Klement Sekera630ab582019-07-19 09:14:19 +00001540 self.pg_enable_capture()
1541 self.send_packets(first_packets)
1542 self.send_packets(second_packets)
1543 self.send_packets(rest_of_packets)
1544
1545 packets = self.dst_if.get_capture(len(self.pkt_infos))
1546 self.verify_capture(packets)
1547 for send_if in self.send_ifs:
1548 send_if.assert_nothing_captured()
1549
1550
Klement Sekerade34c352019-06-25 11:19:22 +00001551class TestIPv6SVReassembly(VppTestCase):
1552 """ IPv6 Shallow Virtual Reassembly """
1553
1554 @classmethod
1555 def setUpClass(cls):
1556 super(TestIPv6SVReassembly, cls).setUpClass()
1557
1558 cls.create_pg_interfaces([0, 1])
1559 cls.src_if = cls.pg0
1560 cls.dst_if = cls.pg1
1561
1562 # setup all interfaces
1563 for i in cls.pg_interfaces:
1564 i.admin_up()
1565 i.config_ip6()
1566 i.resolve_ndp()
1567
1568 def setUp(self):
1569 """ Test setup - force timeout on existing reassemblies """
1570 super(TestIPv6SVReassembly, self).setUp()
1571 self.vapi.ip_reassembly_enable_disable(
1572 sw_if_index=self.src_if.sw_if_index, enable_ip6=True,
1573 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
1574 self.vapi.ip_reassembly_set(
1575 timeout_ms=0, max_reassemblies=1000,
1576 max_reassembly_length=1000,
1577 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1578 expire_walk_interval_ms=10, is_ip6=1)
1579 self.sleep(.25)
1580 self.vapi.ip_reassembly_set(
1581 timeout_ms=1000000, max_reassemblies=1000,
1582 max_reassembly_length=1000,
1583 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1584 expire_walk_interval_ms=10000, is_ip6=1)
1585
1586 def tearDown(self):
1587 super(TestIPv6SVReassembly, self).tearDown()
1588 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1589 self.logger.debug(self.vapi.ppcli("show buffers"))
1590
1591 def test_basic(self):
1592 """ basic reassembly """
1593 payload_len = 1000
1594 payload = ""
1595 counter = 0
1596 while len(payload) < payload_len:
1597 payload += "%u " % counter
1598 counter += 1
1599
1600 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1601 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1602 UDP(sport=1234, dport=5678) /
1603 Raw(payload))
1604 fragments = fragment_rfc8200(p, 1, payload_len/4)
1605
1606 # send fragment #2 - should be cached inside reassembly
1607 self.pg_enable_capture()
1608 self.src_if.add_stream(fragments[1])
1609 self.pg_start()
1610 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1611 self.logger.debug(self.vapi.ppcli("show buffers"))
1612 self.logger.debug(self.vapi.ppcli("show trace"))
1613 self.dst_if.assert_nothing_captured()
1614
1615 # send fragment #1 - reassembly is finished now and both fragments
1616 # forwarded
1617 self.pg_enable_capture()
1618 self.src_if.add_stream(fragments[0])
1619 self.pg_start()
1620 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1621 self.logger.debug(self.vapi.ppcli("show buffers"))
1622 self.logger.debug(self.vapi.ppcli("show trace"))
1623 c = self.dst_if.get_capture(2)
1624 for sent, recvd in zip([fragments[1], fragments[0]], c):
1625 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1626 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1627 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1628
1629 # send rest of fragments - should be immediately forwarded
1630 self.pg_enable_capture()
1631 self.src_if.add_stream(fragments[2:])
1632 self.pg_start()
1633 c = self.dst_if.get_capture(len(fragments[2:]))
1634 for sent, recvd in zip(fragments[2:], c):
1635 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1636 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1637 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1638
1639 def test_timeout(self):
1640 """ reassembly timeout """
1641 payload_len = 1000
1642 payload = ""
1643 counter = 0
1644 while len(payload) < payload_len:
1645 payload += "%u " % counter
1646 counter += 1
1647
1648 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1649 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1650 UDP(sport=1234, dport=5678) /
1651 Raw(payload))
1652 fragments = fragment_rfc8200(p, 1, payload_len/4)
1653
1654 self.vapi.ip_reassembly_set(
1655 timeout_ms=100, max_reassemblies=1000,
1656 max_reassembly_length=1000,
1657 expire_walk_interval_ms=50,
1658 is_ip6=1,
1659 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
1660
1661 # send fragments #2 and #1 - should be forwarded
1662 self.pg_enable_capture()
1663 self.src_if.add_stream(fragments[0:2])
1664 self.pg_start()
1665 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
1666 self.logger.debug(self.vapi.ppcli("show buffers"))
1667 self.logger.debug(self.vapi.ppcli("show trace"))
1668 c = self.dst_if.get_capture(2)
1669 for sent, recvd in zip([fragments[1], fragments[0]], c):
1670 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1671 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1672 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1673
1674 # wait for cleanup
1675 self.sleep(.25, "wait before sending rest of fragments")
1676
1677 # send rest of fragments - shouldn't be forwarded
1678 self.pg_enable_capture()
1679 self.src_if.add_stream(fragments[2:])
1680 self.pg_start()
1681 self.dst_if.assert_nothing_captured()
1682
1683 def test_lru(self):
1684 """ reassembly reuses LRU element """
1685
1686 self.vapi.ip_reassembly_set(
1687 timeout_ms=1000000, max_reassemblies=1,
1688 max_reassembly_length=1000,
1689 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1690 is_ip6=1, expire_walk_interval_ms=10000)
1691
1692 payload_len = 1000
1693 payload = ""
1694 counter = 0
1695 while len(payload) < payload_len:
1696 payload += "%u " % counter
1697 counter += 1
1698
1699 packet_count = 10
1700
1701 fragments = [f
1702 for i in range(packet_count)
1703 for p in (Ether(dst=self.src_if.local_mac,
1704 src=self.src_if.remote_mac) /
1705 IPv6(src=self.src_if.remote_ip6,
1706 dst=self.dst_if.remote_ip6) /
1707 UDP(sport=1234, dport=5678) /
1708 Raw(payload))
1709 for f in fragment_rfc8200(p, i, payload_len/4)]
1710
1711 self.pg_enable_capture()
1712 self.src_if.add_stream(fragments)
1713 self.pg_start()
1714 c = self.dst_if.get_capture(len(fragments))
1715 for sent, recvd in zip(fragments, c):
1716 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1717 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1718 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1719
1720
Juraj Sloboda3048b632018-10-02 11:13:53 +02001721class TestIPv4ReassemblyLocalNode(VppTestCase):
1722 """ IPv4 Reassembly for packets coming to ip4-local node """
1723
1724 @classmethod
1725 def setUpClass(cls):
1726 super(TestIPv4ReassemblyLocalNode, cls).setUpClass()
1727
1728 cls.create_pg_interfaces([0])
1729 cls.src_dst_if = cls.pg0
1730
1731 # setup all interfaces
1732 for i in cls.pg_interfaces:
1733 i.admin_up()
1734 i.config_ip4()
1735 i.resolve_arp()
1736
1737 cls.padding = " abcdefghijklmn"
1738 cls.create_stream()
1739 cls.create_fragments()
1740
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -07001741 @classmethod
1742 def tearDownClass(cls):
1743 super(TestIPv4ReassemblyLocalNode, cls).tearDownClass()
1744
Juraj Sloboda3048b632018-10-02 11:13:53 +02001745 def setUp(self):
1746 """ Test setup - force timeout on existing reassemblies """
1747 super(TestIPv4ReassemblyLocalNode, self).setUp()
1748 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001749 max_reassembly_length=1000,
Juraj Sloboda3048b632018-10-02 11:13:53 +02001750 expire_walk_interval_ms=10)
1751 self.sleep(.25)
1752 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001753 max_reassembly_length=1000,
Juraj Sloboda3048b632018-10-02 11:13:53 +02001754 expire_walk_interval_ms=10000)
1755
1756 def tearDown(self):
1757 super(TestIPv4ReassemblyLocalNode, self).tearDown()
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -07001758
1759 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +00001760 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +01001761 self.logger.debug(self.vapi.ppcli("show buffers"))
Juraj Sloboda3048b632018-10-02 11:13:53 +02001762
1763 @classmethod
1764 def create_stream(cls, packet_count=test_packet_count):
1765 """Create input packet stream for defined interface.
1766
1767 :param list packet_sizes: Required packet sizes.
1768 """
1769 for i in range(0, packet_count):
1770 info = cls.create_packet_info(cls.src_dst_if, cls.src_dst_if)
1771 payload = cls.info_to_payload(info)
1772 p = (Ether(dst=cls.src_dst_if.local_mac,
1773 src=cls.src_dst_if.remote_mac) /
1774 IP(id=info.index, src=cls.src_dst_if.remote_ip4,
1775 dst=cls.src_dst_if.local_ip4) /
1776 ICMP(type='echo-request', id=1234) /
1777 Raw(payload))
1778 cls.extend_packet(p, 1518, cls.padding)
1779 info.data = p
1780
1781 @classmethod
1782 def create_fragments(cls):
1783 infos = cls._packet_infos
1784 cls.pkt_infos = []
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -08001785 for index, info in six.iteritems(infos):
Juraj Sloboda3048b632018-10-02 11:13:53 +02001786 p = info.data
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001787 # cls.logger.debug(ppp("Packet:",
1788 # p.__class__(scapy.compat.raw(p))))
Juraj Sloboda3048b632018-10-02 11:13:53 +02001789 fragments_300 = fragment_rfc791(p, 300)
1790 cls.pkt_infos.append((index, fragments_300))
1791 cls.fragments_300 = [x for (_, frags) in cls.pkt_infos for x in frags]
1792 cls.logger.debug("Fragmented %s packets into %s 300-byte fragments" %
1793 (len(infos), len(cls.fragments_300)))
1794
1795 def verify_capture(self, capture):
1796 """Verify captured packet stream.
1797
1798 :param list capture: Captured packet stream.
1799 """
1800 info = None
1801 seen = set()
1802 for packet in capture:
1803 try:
1804 self.logger.debug(ppp("Got packet:", packet))
1805 ip = packet[IP]
1806 icmp = packet[ICMP]
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001807 payload_info = self.payload_to_info(packet[Raw])
Juraj Sloboda3048b632018-10-02 11:13:53 +02001808 packet_index = payload_info.index
1809 if packet_index in seen:
1810 raise Exception(ppp("Duplicate packet received", packet))
1811 seen.add(packet_index)
1812 self.assertEqual(payload_info.dst, self.src_dst_if.sw_if_index)
1813 info = self._packet_infos[packet_index]
Klement Sekera14d7e902018-12-10 13:46:09 +01001814 self.assertIsNotNone(info)
Juraj Sloboda3048b632018-10-02 11:13:53 +02001815 self.assertEqual(packet_index, info.index)
1816 saved_packet = info.data
1817 self.assertEqual(ip.src, saved_packet[IP].dst)
1818 self.assertEqual(ip.dst, saved_packet[IP].src)
1819 self.assertEqual(icmp.type, 0) # echo reply
1820 self.assertEqual(icmp.id, saved_packet[ICMP].id)
1821 self.assertEqual(icmp.payload, saved_packet[ICMP].payload)
1822 except Exception:
1823 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1824 raise
1825 for index in self._packet_infos:
Klement Sekera14d7e902018-12-10 13:46:09 +01001826 self.assertIn(index, seen,
1827 "Packet with packet_index %d not received" % index)
Juraj Sloboda3048b632018-10-02 11:13:53 +02001828
1829 def test_reassembly(self):
1830 """ basic reassembly """
1831
1832 self.pg_enable_capture()
1833 self.src_dst_if.add_stream(self.fragments_300)
1834 self.pg_start()
1835
1836 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
1837 self.verify_capture(packets)
1838
1839 # run it all again to verify correctness
1840 self.pg_enable_capture()
1841 self.src_dst_if.add_stream(self.fragments_300)
1842 self.pg_start()
1843
1844 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
1845 self.verify_capture(packets)
1846
1847
Klement Sekera75e7d132017-09-20 08:26:30 +02001848class TestFIFReassembly(VppTestCase):
1849 """ Fragments in fragments reassembly """
1850
1851 @classmethod
1852 def setUpClass(cls):
1853 super(TestFIFReassembly, cls).setUpClass()
1854
Klement Sekera4c533132018-02-22 11:41:12 +01001855 cls.create_pg_interfaces([0, 1])
1856 cls.src_if = cls.pg0
1857 cls.dst_if = cls.pg1
1858 for i in cls.pg_interfaces:
1859 i.admin_up()
1860 i.config_ip4()
1861 i.resolve_arp()
1862 i.config_ip6()
1863 i.resolve_ndp()
Klement Sekera75e7d132017-09-20 08:26:30 +02001864
Klement Sekera75e7d132017-09-20 08:26:30 +02001865 cls.packet_sizes = [64, 512, 1518, 9018]
1866 cls.padding = " abcdefghijklmn"
1867
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -07001868 @classmethod
1869 def tearDownClass(cls):
1870 super(TestFIFReassembly, cls).tearDownClass()
1871
Klement Sekera75e7d132017-09-20 08:26:30 +02001872 def setUp(self):
1873 """ Test setup - force timeout on existing reassemblies """
1874 super(TestFIFReassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +01001875 self.vapi.ip_reassembly_enable_disable(
1876 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
1877 enable_ip6=True)
1878 self.vapi.ip_reassembly_enable_disable(
1879 sw_if_index=self.dst_if.sw_if_index, enable_ip4=True,
1880 enable_ip6=True)
Klement Sekera75e7d132017-09-20 08:26:30 +02001881 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001882 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001883 expire_walk_interval_ms=10)
1884 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001885 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001886 expire_walk_interval_ms=10, is_ip6=1)
1887 self.sleep(.25)
1888 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001889 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001890 expire_walk_interval_ms=10000)
1891 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001892 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001893 expire_walk_interval_ms=10000, is_ip6=1)
1894
1895 def tearDown(self):
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -07001896 super(TestFIFReassembly, self).tearDown()
1897
1898 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +00001899 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
1900 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +01001901 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +02001902
1903 def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):
1904 """Verify captured packet stream.
1905
1906 :param list capture: Captured packet stream.
1907 """
1908 info = None
1909 seen = set()
1910 for packet in capture:
1911 try:
Klement Sekera4c533132018-02-22 11:41:12 +01001912 self.logger.debug(ppp("Got packet:", packet))
Klement Sekera75e7d132017-09-20 08:26:30 +02001913 ip = packet[ip_class]
1914 udp = packet[UDP]
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001915 payload_info = self.payload_to_info(packet[Raw])
Klement Sekera75e7d132017-09-20 08:26:30 +02001916 packet_index = payload_info.index
1917 self.assertTrue(
1918 packet_index not in dropped_packet_indexes,
1919 ppp("Packet received, but should be dropped:", packet))
1920 if packet_index in seen:
1921 raise Exception(ppp("Duplicate packet received", packet))
1922 seen.add(packet_index)
Klement Sekera4c533132018-02-22 11:41:12 +01001923 self.assertEqual(payload_info.dst, self.dst_if.sw_if_index)
Klement Sekera75e7d132017-09-20 08:26:30 +02001924 info = self._packet_infos[packet_index]
1925 self.assertTrue(info is not None)
1926 self.assertEqual(packet_index, info.index)
1927 saved_packet = info.data
1928 self.assertEqual(ip.src, saved_packet[ip_class].src)
1929 self.assertEqual(ip.dst, saved_packet[ip_class].dst)
1930 self.assertEqual(udp.payload, saved_packet[UDP].payload)
Klement Sekera4c533132018-02-22 11:41:12 +01001931 except Exception:
Klement Sekera75e7d132017-09-20 08:26:30 +02001932 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1933 raise
1934 for index in self._packet_infos:
1935 self.assertTrue(index in seen or index in dropped_packet_indexes,
1936 "Packet with packet_index %d not received" % index)
1937
1938 def test_fif4(self):
1939 """ Fragments in fragments (4o4) """
1940
1941 # TODO this should be ideally in setUpClass, but then we hit a bug
1942 # with VppIpRoute incorrectly reporting it's present when it's not
1943 # so we need to manually remove the vpp config, thus we cannot have
1944 # it shared for multiple test cases
1945 self.tun_ip4 = "1.1.1.2"
1946
Klement Sekera4c533132018-02-22 11:41:12 +01001947 self.gre4 = VppGreInterface(self, self.src_if.local_ip4, self.tun_ip4)
Klement Sekera75e7d132017-09-20 08:26:30 +02001948 self.gre4.add_vpp_config()
1949 self.gre4.admin_up()
1950 self.gre4.config_ip4()
1951
Klement Sekera4c533132018-02-22 11:41:12 +01001952 self.vapi.ip_reassembly_enable_disable(
1953 sw_if_index=self.gre4.sw_if_index, enable_ip4=True)
1954
Klement Sekera75e7d132017-09-20 08:26:30 +02001955 self.route4 = VppIpRoute(self, self.tun_ip4, 32,
Klement Sekera4c533132018-02-22 11:41:12 +01001956 [VppRoutePath(self.src_if.remote_ip4,
1957 self.src_if.sw_if_index)])
Klement Sekera75e7d132017-09-20 08:26:30 +02001958 self.route4.add_vpp_config()
1959
1960 self.reset_packet_infos()
1961 for i in range(test_packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01001962 info = self.create_packet_info(self.src_if, self.dst_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02001963 payload = self.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +01001964 # Ethernet header here is only for size calculation, thus it
1965 # doesn't matter how it's initialized. This is to ensure that
1966 # reassembled packet is not > 9000 bytes, so that it's not dropped
1967 p = (Ether() /
1968 IP(id=i, src=self.src_if.remote_ip4,
1969 dst=self.dst_if.remote_ip4) /
1970 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001971 Raw(payload))
1972 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1973 self.extend_packet(p, size, self.padding)
Klement Sekera4c533132018-02-22 11:41:12 +01001974 info.data = p[IP] # use only IP part, without ethernet header
Klement Sekera75e7d132017-09-20 08:26:30 +02001975
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -08001976 fragments = [x for _, p in six.iteritems(self._packet_infos)
Klement Sekera75e7d132017-09-20 08:26:30 +02001977 for x in fragment_rfc791(p.data, 400)]
1978
1979 encapped_fragments = \
Klement Sekera4c533132018-02-22 11:41:12 +01001980 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1981 IP(src=self.tun_ip4, dst=self.src_if.local_ip4) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001982 GRE() /
1983 p
1984 for p in fragments]
1985
1986 fragmented_encapped_fragments = \
1987 [x for p in encapped_fragments
1988 for x in fragment_rfc791(p, 200)]
1989
Klement Sekera4c533132018-02-22 11:41:12 +01001990 self.src_if.add_stream(fragmented_encapped_fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001991
1992 self.pg_enable_capture(self.pg_interfaces)
1993 self.pg_start()
1994
Klement Sekera4c533132018-02-22 11:41:12 +01001995 self.src_if.assert_nothing_captured()
1996 packets = self.dst_if.get_capture(len(self._packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +02001997 self.verify_capture(packets, IP)
1998
1999 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
2000 # so that it's query_vpp_config() works as it should
2001 self.gre4.remove_vpp_config()
Klement Sekera4c533132018-02-22 11:41:12 +01002002 self.logger.debug(self.vapi.ppcli("show interface"))
Klement Sekera75e7d132017-09-20 08:26:30 +02002003
2004 def test_fif6(self):
2005 """ Fragments in fragments (6o6) """
2006 # TODO this should be ideally in setUpClass, but then we hit a bug
2007 # with VppIpRoute incorrectly reporting it's present when it's not
2008 # so we need to manually remove the vpp config, thus we cannot have
2009 # it shared for multiple test cases
2010 self.tun_ip6 = "1002::1"
2011
Neale Ranns5a8844b2019-04-16 07:15:35 +00002012 self.gre6 = VppGreInterface(self, self.src_if.local_ip6, self.tun_ip6)
Klement Sekera75e7d132017-09-20 08:26:30 +02002013 self.gre6.add_vpp_config()
2014 self.gre6.admin_up()
2015 self.gre6.config_ip6()
2016
Klement Sekera4c533132018-02-22 11:41:12 +01002017 self.vapi.ip_reassembly_enable_disable(
2018 sw_if_index=self.gre6.sw_if_index, enable_ip6=True)
2019
Klement Sekera75e7d132017-09-20 08:26:30 +02002020 self.route6 = VppIpRoute(self, self.tun_ip6, 128,
Neale Ranns097fa662018-05-01 05:17:55 -07002021 [VppRoutePath(
2022 self.src_if.remote_ip6,
2023 self.src_if.sw_if_index)])
Klement Sekera75e7d132017-09-20 08:26:30 +02002024 self.route6.add_vpp_config()
2025
2026 self.reset_packet_infos()
2027 for i in range(test_packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01002028 info = self.create_packet_info(self.src_if, self.dst_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02002029 payload = self.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +01002030 # Ethernet header here is only for size calculation, thus it
2031 # doesn't matter how it's initialized. This is to ensure that
2032 # reassembled packet is not > 9000 bytes, so that it's not dropped
2033 p = (Ether() /
2034 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
2035 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02002036 Raw(payload))
2037 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
2038 self.extend_packet(p, size, self.padding)
Klement Sekera4c533132018-02-22 11:41:12 +01002039 info.data = p[IPv6] # use only IPv6 part, without ethernet header
Klement Sekera75e7d132017-09-20 08:26:30 +02002040
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -08002041 fragments = [x for _, i in six.iteritems(self._packet_infos)
Klement Sekera75e7d132017-09-20 08:26:30 +02002042 for x in fragment_rfc8200(
2043 i.data, i.index, 400)]
2044
2045 encapped_fragments = \
Klement Sekera4c533132018-02-22 11:41:12 +01002046 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
2047 IPv6(src=self.tun_ip6, dst=self.src_if.local_ip6) /
Klement Sekera75e7d132017-09-20 08:26:30 +02002048 GRE() /
2049 p
2050 for p in fragments]
2051
2052 fragmented_encapped_fragments = \
2053 [x for p in encapped_fragments for x in (
2054 fragment_rfc8200(
2055 p,
2056 2 * len(self._packet_infos) + p[IPv6ExtHdrFragment].id,
2057 200)
2058 if IPv6ExtHdrFragment in p else [p]
2059 )
2060 ]
2061
Klement Sekera4c533132018-02-22 11:41:12 +01002062 self.src_if.add_stream(fragmented_encapped_fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02002063
2064 self.pg_enable_capture(self.pg_interfaces)
2065 self.pg_start()
2066
Klement Sekera4c533132018-02-22 11:41:12 +01002067 self.src_if.assert_nothing_captured()
2068 packets = self.dst_if.get_capture(len(self._packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +02002069 self.verify_capture(packets, IPv6)
2070
2071 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
2072 # so that it's query_vpp_config() works as it should
Klement Sekera3ecc2212018-03-27 10:34:43 +02002073 self.gre6.remove_vpp_config()
Klement Sekera75e7d132017-09-20 08:26:30 +02002074
2075
2076if __name__ == '__main__':
2077 unittest.main(testRunner=VppTestRunner)