blob: 797fb527415ea9b1669ede4ede5d75b77e06d0b6 [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
Klement Sekera75e7d132017-09-20 08:26:30 +02003import unittest
Klement Sekera630ab582019-07-19 09:14:19 +00004from random import shuffle, choice, randrange
Klement Sekera75e7d132017-09-20 08:26:30 +02005
Klement Sekera947a85c2019-07-24 12:40:37 +00006from framework import VppTestCase, VppTestRunner
7
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07008import scapy.compat
Klement Sekera75e7d132017-09-20 08:26:30 +02009from scapy.packet import Raw
10from scapy.layers.l2 import Ether, GRE
Juraj Sloboda3048b632018-10-02 11:13:53 +020011from scapy.layers.inet import IP, UDP, ICMP
Klement Sekera769145c2019-03-06 11:59:57 +010012from scapy.layers.inet6 import HBHOptUnknown, ICMPv6ParamProblem,\
Klement Sekerade34c352019-06-25 11:19:22 +000013 ICMPv6TimeExceeded, IPv6, IPv6ExtHdrFragment, IPv6ExtHdrHopByHop
Paul Vinciguerra69555952019-03-01 08:46:29 -080014from framework import VppTestCase, VppTestRunner
Klement Sekera630ab582019-07-19 09:14:19 +000015from util import ppp, ppc, fragment_rfc791, fragment_rfc8200
Neale Ranns5a8844b2019-04-16 07:15:35 +000016from vpp_gre_interface import VppGreInterface
Neale Rannsc0a93142018-09-05 15:42:26 -070017from vpp_ip import DpoProto
Neale Ranns097fa662018-05-01 05:17:55 -070018from vpp_ip_route import VppIpRoute, VppRoutePath, FibPathProto
Klement Sekera896c8962019-06-24 11:52:49 +000019from vpp_papi import VppEnum
Klement Sekera75e7d132017-09-20 08:26:30 +020020
Klement Sekerad0f70a32018-12-14 17:24:13 +010021# 35 is enough to have >257 400-byte fragments
22test_packet_count = 35
Klement Sekera75e7d132017-09-20 08:26:30 +020023
Klement Sekera630ab582019-07-19 09:14:19 +000024# number of workers used for multi-worker test cases
25worker_count = 3
26
Klement Sekera75e7d132017-09-20 08:26:30 +020027
Klement Sekera947a85c2019-07-24 12:40:37 +000028class TestIPv4Reassembly(VppTestCase):
Klement Sekera75e7d132017-09-20 08:26:30 +020029 """ IPv4 Reassembly """
30
31 @classmethod
32 def setUpClass(cls):
33 super(TestIPv4Reassembly, cls).setUpClass()
34
Klement Sekera4c533132018-02-22 11:41:12 +010035 cls.create_pg_interfaces([0, 1])
36 cls.src_if = cls.pg0
37 cls.dst_if = cls.pg1
Klement Sekera75e7d132017-09-20 08:26:30 +020038
39 # setup all interfaces
40 for i in cls.pg_interfaces:
41 i.admin_up()
42 i.config_ip4()
43 i.resolve_arp()
44
Klement Sekera75e7d132017-09-20 08:26:30 +020045 # packet sizes
46 cls.packet_sizes = [64, 512, 1518, 9018]
47 cls.padding = " abcdefghijklmn"
48 cls.create_stream(cls.packet_sizes)
49 cls.create_fragments()
50
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -070051 @classmethod
52 def tearDownClass(cls):
53 super(TestIPv4Reassembly, cls).tearDownClass()
54
Klement Sekera75e7d132017-09-20 08:26:30 +020055 def setUp(self):
56 """ Test setup - force timeout on existing reassemblies """
57 super(TestIPv4Reassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +010058 self.vapi.ip_reassembly_enable_disable(
59 sw_if_index=self.src_if.sw_if_index, enable_ip4=True)
Klement Sekera75e7d132017-09-20 08:26:30 +020060 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +020061 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +020062 expire_walk_interval_ms=10)
63 self.sleep(.25)
64 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +020065 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +020066 expire_walk_interval_ms=10000)
67
68 def tearDown(self):
69 super(TestIPv4Reassembly, self).tearDown()
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -070070
71 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +000072 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +010073 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +020074
75 @classmethod
76 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
Klement Sekerad0f70a32018-12-14 17:24:13 +010077 """Create input packet stream
Klement Sekera75e7d132017-09-20 08:26:30 +020078
79 :param list packet_sizes: Required packet sizes.
80 """
81 for i in range(0, packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +010082 info = cls.create_packet_info(cls.src_if, cls.src_if)
Klement Sekera75e7d132017-09-20 08:26:30 +020083 payload = cls.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +010084 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
85 IP(id=info.index, src=cls.src_if.remote_ip4,
86 dst=cls.dst_if.remote_ip4) /
87 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +020088 Raw(payload))
89 size = packet_sizes[(i // 2) % len(packet_sizes)]
90 cls.extend_packet(p, size, cls.padding)
91 info.data = p
92
93 @classmethod
94 def create_fragments(cls):
95 infos = cls._packet_infos
96 cls.pkt_infos = []
Paul Vinciguerra090096b2020-12-03 00:42:46 -050097 for index, info in infos.items():
Klement Sekera75e7d132017-09-20 08:26:30 +020098 p = info.data
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -070099 # cls.logger.debug(ppp("Packet:",
100 # p.__class__(scapy.compat.raw(p))))
Klement Sekera75e7d132017-09-20 08:26:30 +0200101 fragments_400 = fragment_rfc791(p, 400)
102 fragments_300 = fragment_rfc791(p, 300)
103 fragments_200 = [
104 x for f in fragments_400 for x in fragment_rfc791(f, 200)]
105 cls.pkt_infos.append(
106 (index, fragments_400, fragments_300, fragments_200))
107 cls.fragments_400 = [
108 x for (_, frags, _, _) in cls.pkt_infos for x in frags]
109 cls.fragments_300 = [
110 x for (_, _, frags, _) in cls.pkt_infos for x in frags]
111 cls.fragments_200 = [
112 x for (_, _, _, frags) in cls.pkt_infos for x in frags]
113 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
114 "%s 300-byte fragments and %s 200-byte fragments" %
115 (len(infos), len(cls.fragments_400),
116 len(cls.fragments_300), len(cls.fragments_200)))
117
Klement Sekera947a85c2019-07-24 12:40:37 +0000118 def verify_capture(self, capture, dropped_packet_indexes=[]):
119 """Verify captured packet stream.
120
121 :param list capture: Captured packet stream.
122 """
123 info = None
124 seen = set()
125 for packet in capture:
126 try:
127 self.logger.debug(ppp("Got packet:", packet))
128 ip = packet[IP]
129 udp = packet[UDP]
130 payload_info = self.payload_to_info(packet[Raw])
131 packet_index = payload_info.index
132 self.assertTrue(
133 packet_index not in dropped_packet_indexes,
134 ppp("Packet received, but should be dropped:", packet))
135 if packet_index in seen:
136 raise Exception(ppp("Duplicate packet received", packet))
137 seen.add(packet_index)
138 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
139 info = self._packet_infos[packet_index]
140 self.assertTrue(info is not None)
141 self.assertEqual(packet_index, info.index)
142 saved_packet = info.data
143 self.assertEqual(ip.src, saved_packet[IP].src)
144 self.assertEqual(ip.dst, saved_packet[IP].dst)
145 self.assertEqual(udp.payload, saved_packet[UDP].payload)
146 except Exception:
147 self.logger.error(ppp("Unexpected or invalid packet:", packet))
148 raise
149 for index in self._packet_infos:
150 self.assertTrue(index in seen or index in dropped_packet_indexes,
151 "Packet with packet_index %d not received" % index)
152
153 def test_reassembly(self):
Klement Sekera75e7d132017-09-20 08:26:30 +0200154 """ basic reassembly """
155
Klement Sekera947a85c2019-07-24 12:40:37 +0000156 self.pg_enable_capture()
157 self.src_if.add_stream(self.fragments_200)
158 self.pg_start()
159
160 packets = self.dst_if.get_capture(len(self.pkt_infos))
161 self.verify_capture(packets)
162 self.src_if.assert_nothing_captured()
163
164 # run it all again to verify correctness
165 self.pg_enable_capture()
166 self.src_if.add_stream(self.fragments_200)
167 self.pg_start()
168
169 packets = self.dst_if.get_capture(len(self.pkt_infos))
170 self.verify_capture(packets)
171 self.src_if.assert_nothing_captured()
172
173 def test_reversed(self):
Klement Sekera75e7d132017-09-20 08:26:30 +0200174 """ reverse order reassembly """
175
Klement Sekera947a85c2019-07-24 12:40:37 +0000176 fragments = list(self.fragments_200)
177 fragments.reverse()
178
179 self.pg_enable_capture()
180 self.src_if.add_stream(fragments)
181 self.pg_start()
182
183 packets = self.dst_if.get_capture(len(self.packet_infos))
184 self.verify_capture(packets)
185 self.src_if.assert_nothing_captured()
186
187 # run it all again to verify correctness
188 self.pg_enable_capture()
189 self.src_if.add_stream(fragments)
190 self.pg_start()
191
192 packets = self.dst_if.get_capture(len(self.packet_infos))
193 self.verify_capture(packets)
194 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200195
Klement Sekera3a343d42019-05-16 14:35:46 +0200196 def test_long_fragment_chain(self):
197 """ long fragment chain """
198
199 error_cnt_str = \
Klement Sekera896c8962019-06-24 11:52:49 +0000200 "/err/ip4-full-reassembly-feature/fragment chain too long (drop)"
Klement Sekera3a343d42019-05-16 14:35:46 +0200201
Klement Sekera34641f22019-05-22 20:18:26 +0200202 error_cnt = self.statistics.get_err_counter(error_cnt_str)
Klement Sekera3a343d42019-05-16 14:35:46 +0200203
204 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
205 max_reassembly_length=3,
206 expire_walk_interval_ms=50)
207
208 p1 = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
209 IP(id=1000, src=self.src_if.remote_ip4,
210 dst=self.dst_if.remote_ip4) /
211 UDP(sport=1234, dport=5678) /
Ole Troan127fbec2019-10-18 15:22:56 +0200212 Raw(b"X" * 1000))
Klement Sekera3a343d42019-05-16 14:35:46 +0200213 p2 = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
214 IP(id=1001, src=self.src_if.remote_ip4,
215 dst=self.dst_if.remote_ip4) /
216 UDP(sport=1234, dport=5678) /
Ole Troan127fbec2019-10-18 15:22:56 +0200217 Raw(b"X" * 1000))
Klement Sekera3a343d42019-05-16 14:35:46 +0200218 frags = fragment_rfc791(p1, 200) + fragment_rfc791(p2, 500)
219
220 self.pg_enable_capture()
221 self.src_if.add_stream(frags)
222 self.pg_start()
223
224 self.dst_if.get_capture(1)
Klement Sekera34641f22019-05-22 20:18:26 +0200225 self.assert_error_counter_equal(error_cnt_str, error_cnt + 1)
Klement Sekera3a343d42019-05-16 14:35:46 +0200226
Klement Sekera14d7e902018-12-10 13:46:09 +0100227 def test_5737(self):
228 """ fragment length + ip header size > 65535 """
Klement Sekera4ee633e2018-12-14 12:00:44 +0100229 self.vapi.cli("clear errors")
Ole Troan127fbec2019-10-18 15:22:56 +0200230 raw = b'''E\x00\x00\x88,\xf8\x1f\xfe@\x01\x98\x00\xc0\xa8\n-\xc0\xa8\n\
231\x01\x08\x00\xf0J\xed\xcb\xf1\xf5Test-group: IPv4.IPv4.ipv4-message.\
232Ethernet-Payload.IPv4-Packet.IPv4-Header.Fragment-Offset; Test-case: 5737'''
Klement Sekera14d7e902018-12-10 13:46:09 +0100233 malformed_packet = (Ether(dst=self.src_if.local_mac,
234 src=self.src_if.remote_mac) /
235 IP(raw))
236 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
237 IP(id=1000, src=self.src_if.remote_ip4,
238 dst=self.dst_if.remote_ip4) /
239 UDP(sport=1234, dport=5678) /
Ole Troan127fbec2019-10-18 15:22:56 +0200240 Raw(b"X" * 1000))
Klement Sekera14d7e902018-12-10 13:46:09 +0100241 valid_fragments = fragment_rfc791(p, 400)
242
Ole Troan127fbec2019-10-18 15:22:56 +0200243 counter = "/err/ip4-full-reassembly-feature/malformed packets"
244 error_counter = self.statistics.get_err_counter(counter)
Klement Sekera14d7e902018-12-10 13:46:09 +0100245 self.pg_enable_capture()
246 self.src_if.add_stream([malformed_packet] + valid_fragments)
247 self.pg_start()
248
249 self.dst_if.get_capture(1)
Klement Sekera896c8962019-06-24 11:52:49 +0000250 self.logger.debug(self.vapi.ppcli("show error"))
Ole Troan127fbec2019-10-18 15:22:56 +0200251 self.assertEqual(self.statistics.get_err_counter(counter),
252 error_counter + 1)
Klement Sekera14d7e902018-12-10 13:46:09 +0100253
Klement Sekera400f6d82018-12-13 14:35:48 +0100254 def test_44924(self):
255 """ compress tiny fragments """
256 packets = [(Ether(dst=self.src_if.local_mac,
257 src=self.src_if.remote_mac) /
258 IP(id=24339, flags="MF", frag=0, ttl=64,
259 src=self.src_if.remote_ip4,
260 dst=self.dst_if.remote_ip4) /
261 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
262 Raw(load='Test-group: IPv4')),
263 (Ether(dst=self.src_if.local_mac,
264 src=self.src_if.remote_mac) /
265 IP(id=24339, flags="MF", frag=3, ttl=64,
266 src=self.src_if.remote_ip4,
267 dst=self.dst_if.remote_ip4) /
268 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
269 Raw(load='.IPv4.Fragmentation.vali')),
270 (Ether(dst=self.src_if.local_mac,
271 src=self.src_if.remote_mac) /
272 IP(id=24339, frag=6, ttl=64,
273 src=self.src_if.remote_ip4,
274 dst=self.dst_if.remote_ip4) /
275 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
276 Raw(load='d; Test-case: 44924'))
277 ]
278
279 self.pg_enable_capture()
280 self.src_if.add_stream(packets)
281 self.pg_start()
282
283 self.dst_if.get_capture(1)
284
Klement Sekera4ee633e2018-12-14 12:00:44 +0100285 def test_frag_1(self):
286 """ fragment of size 1 """
287 self.vapi.cli("clear errors")
288 malformed_packets = [(Ether(dst=self.src_if.local_mac,
289 src=self.src_if.remote_mac) /
290 IP(id=7, len=21, flags="MF", frag=0, ttl=64,
291 src=self.src_if.remote_ip4,
292 dst=self.dst_if.remote_ip4) /
293 ICMP(type="echo-request")),
294 (Ether(dst=self.src_if.local_mac,
295 src=self.src_if.remote_mac) /
296 IP(id=7, len=21, frag=1, ttl=64,
297 src=self.src_if.remote_ip4,
298 dst=self.dst_if.remote_ip4) /
Ole Troan127fbec2019-10-18 15:22:56 +0200299 Raw(load=b'\x08')),
Klement Sekera4ee633e2018-12-14 12:00:44 +0100300 ]
301
302 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
303 IP(id=1000, src=self.src_if.remote_ip4,
304 dst=self.dst_if.remote_ip4) /
305 UDP(sport=1234, dport=5678) /
Ole Troan127fbec2019-10-18 15:22:56 +0200306 Raw(b"X" * 1000))
Klement Sekera4ee633e2018-12-14 12:00:44 +0100307 valid_fragments = fragment_rfc791(p, 400)
308
309 self.pg_enable_capture()
310 self.src_if.add_stream(malformed_packets + valid_fragments)
311 self.pg_start()
312
313 self.dst_if.get_capture(1)
314
Klement Sekera896c8962019-06-24 11:52:49 +0000315 self.assert_packet_counter_equal("ip4-full-reassembly-feature", 1)
Klement Sekera4ee633e2018-12-14 12:00:44 +0100316 # TODO remove above, uncomment below once clearing of counters
317 # is supported
318 # self.assert_packet_counter_equal(
Klement Sekera896c8962019-06-24 11:52:49 +0000319 # "/err/ip4-full-reassembly-feature/malformed packets", 1)
Klement Sekera4ee633e2018-12-14 12:00:44 +0100320
Klement Sekera947a85c2019-07-24 12:40:37 +0000321 def test_random(self):
322 """ random order reassembly """
323
324 fragments = list(self.fragments_200)
325 shuffle(fragments)
326
327 self.pg_enable_capture()
328 self.src_if.add_stream(fragments)
329 self.pg_start()
330
331 packets = self.dst_if.get_capture(len(self.packet_infos))
332 self.verify_capture(packets)
333 self.src_if.assert_nothing_captured()
334
335 # run it all again to verify correctness
336 self.pg_enable_capture()
337 self.src_if.add_stream(fragments)
338 self.pg_start()
339
340 packets = self.dst_if.get_capture(len(self.packet_infos))
341 self.verify_capture(packets)
342 self.src_if.assert_nothing_captured()
343
344 def test_duplicates(self):
Klement Sekera75e7d132017-09-20 08:26:30 +0200345 """ duplicate fragments """
Klement Sekera947a85c2019-07-24 12:40:37 +0000346
Klement Sekera75e7d132017-09-20 08:26:30 +0200347 fragments = [
348 x for (_, frags, _, _) in self.pkt_infos
349 for x in frags
350 for _ in range(0, min(2, len(frags)))
351 ]
Klement Sekera947a85c2019-07-24 12:40:37 +0000352
353 self.pg_enable_capture()
354 self.src_if.add_stream(fragments)
355 self.pg_start()
356
357 packets = self.dst_if.get_capture(len(self.pkt_infos))
358 self.verify_capture(packets)
359 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200360
361 def test_overlap1(self):
362 """ overlapping fragments case #1 """
363
364 fragments = []
365 for _, _, frags_300, frags_200 in self.pkt_infos:
366 if len(frags_300) == 1:
367 fragments.extend(frags_300)
368 else:
369 for i, j in zip(frags_200, frags_300):
370 fragments.extend(i)
371 fragments.extend(j)
372
373 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100374 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200375 self.pg_start()
376
Klement Sekera4c533132018-02-22 11:41:12 +0100377 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera947a85c2019-07-24 12:40:37 +0000378 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100379 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200380
381 # run it all to verify correctness
382 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100383 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200384 self.pg_start()
385
Klement Sekera4c533132018-02-22 11:41:12 +0100386 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera947a85c2019-07-24 12:40:37 +0000387 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100388 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200389
390 def test_overlap2(self):
391 """ overlapping fragments case #2 """
392
393 fragments = []
394 for _, _, frags_300, frags_200 in self.pkt_infos:
395 if len(frags_300) == 1:
396 fragments.extend(frags_300)
397 else:
398 # care must be taken here so that there are no fragments
399 # received by vpp after reassembly is finished, otherwise
400 # new reassemblies will be started and packet generator will
401 # freak out when it detects unfreed buffers
402 zipped = zip(frags_300, frags_200)
Paul Vinciguerrac7834e02019-03-02 10:43:05 -0800403 for i, j in zipped:
Klement Sekera75e7d132017-09-20 08:26:30 +0200404 fragments.extend(i)
405 fragments.extend(j)
Paul Vinciguerrac7834e02019-03-02 10:43:05 -0800406 fragments.pop()
Klement Sekera75e7d132017-09-20 08:26:30 +0200407
408 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100409 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200410 self.pg_start()
411
Klement Sekera4c533132018-02-22 11:41:12 +0100412 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera947a85c2019-07-24 12:40:37 +0000413 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100414 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200415
416 # run it all to verify correctness
417 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100418 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200419 self.pg_start()
420
Klement Sekera4c533132018-02-22 11:41:12 +0100421 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera947a85c2019-07-24 12:40:37 +0000422 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100423 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200424
Klement Sekera947a85c2019-07-24 12:40:37 +0000425 def test_timeout_inline(self):
Klement Sekera75e7d132017-09-20 08:26:30 +0200426 """ timeout (inline) """
427
428 dropped_packet_indexes = set(
429 index for (index, frags, _, _) in self.pkt_infos if len(frags) > 1
430 )
431
Klement Sekera947a85c2019-07-24 12:40:37 +0000432 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
433 max_reassembly_length=3,
434 expire_walk_interval_ms=10000)
435
436 self.pg_enable_capture()
437 self.src_if.add_stream(self.fragments_400)
438 self.pg_start()
439
440 packets = self.dst_if.get_capture(
441 len(self.pkt_infos) - len(dropped_packet_indexes))
442 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100443 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200444
445 def test_timeout_cleanup(self):
446 """ timeout (cleanup) """
447
448 # whole packets + fragmented packets sans last fragment
449 fragments = [
450 x for (_, frags_400, _, _) in self.pkt_infos
451 for x in frags_400[:-1 if len(frags_400) > 1 else None]
452 ]
453
454 # last fragments for fragmented packets
455 fragments2 = [frags_400[-1]
456 for (_, frags_400, _, _) in self.pkt_infos
457 if len(frags_400) > 1]
458
459 dropped_packet_indexes = set(
460 index for (index, frags_400, _, _) in self.pkt_infos
461 if len(frags_400) > 1)
462
463 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +0200464 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +0200465 expire_walk_interval_ms=50)
466
467 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100468 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200469 self.pg_start()
470
471 self.sleep(.25, "wait before sending rest of fragments")
472
Klement Sekera4c533132018-02-22 11:41:12 +0100473 self.src_if.add_stream(fragments2)
Klement Sekera75e7d132017-09-20 08:26:30 +0200474 self.pg_start()
Klement Sekera75e7d132017-09-20 08:26:30 +0200475
Klement Sekera4c533132018-02-22 11:41:12 +0100476 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200477 len(self.pkt_infos) - len(dropped_packet_indexes))
Klement Sekera947a85c2019-07-24 12:40:37 +0000478 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100479 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200480
Klement Sekera947a85c2019-07-24 12:40:37 +0000481 def test_disabled(self):
Klement Sekera75e7d132017-09-20 08:26:30 +0200482 """ reassembly disabled """
483
484 dropped_packet_indexes = set(
485 index for (index, frags_400, _, _) in self.pkt_infos
486 if len(frags_400) > 1)
Klement Sekera947a85c2019-07-24 12:40:37 +0000487
488 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
489 max_reassembly_length=3,
490 expire_walk_interval_ms=10000)
491
492 self.pg_enable_capture()
493 self.src_if.add_stream(self.fragments_400)
494 self.pg_start()
495
496 packets = self.dst_if.get_capture(
497 len(self.pkt_infos) - len(dropped_packet_indexes))
498 self.verify_capture(packets, dropped_packet_indexes)
499 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200500
501
Klement Sekerade34c352019-06-25 11:19:22 +0000502class TestIPv4SVReassembly(VppTestCase):
503 """ IPv4 Shallow Virtual Reassembly """
504
505 @classmethod
506 def setUpClass(cls):
507 super(TestIPv4SVReassembly, cls).setUpClass()
508
509 cls.create_pg_interfaces([0, 1])
510 cls.src_if = cls.pg0
511 cls.dst_if = cls.pg1
512
513 # setup all interfaces
514 for i in cls.pg_interfaces:
515 i.admin_up()
516 i.config_ip4()
517 i.resolve_arp()
518
519 def setUp(self):
520 """ Test setup - force timeout on existing reassemblies """
521 super(TestIPv4SVReassembly, self).setUp()
522 self.vapi.ip_reassembly_enable_disable(
523 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
524 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
525 self.vapi.ip_reassembly_set(
526 timeout_ms=0, max_reassemblies=1000,
527 max_reassembly_length=1000,
528 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
529 expire_walk_interval_ms=10)
530 self.sleep(.25)
531 self.vapi.ip_reassembly_set(
532 timeout_ms=1000000, max_reassemblies=1000,
533 max_reassembly_length=1000,
534 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
535 expire_walk_interval_ms=10000)
536
537 def tearDown(self):
538 super(TestIPv4SVReassembly, self).tearDown()
539 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
540 self.logger.debug(self.vapi.ppcli("show buffers"))
541
542 def test_basic(self):
543 """ basic reassembly """
544 payload_len = 1000
545 payload = ""
546 counter = 0
547 while len(payload) < payload_len:
548 payload += "%u " % counter
549 counter += 1
550
551 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
552 IP(id=1, src=self.src_if.remote_ip4,
553 dst=self.dst_if.remote_ip4) /
554 UDP(sport=1234, dport=5678) /
555 Raw(payload))
556 fragments = fragment_rfc791(p, payload_len/4)
557
558 # send fragment #2 - should be cached inside reassembly
559 self.pg_enable_capture()
560 self.src_if.add_stream(fragments[1])
561 self.pg_start()
562 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
563 self.logger.debug(self.vapi.ppcli("show buffers"))
564 self.logger.debug(self.vapi.ppcli("show trace"))
565 self.dst_if.assert_nothing_captured()
566
567 # send fragment #1 - reassembly is finished now and both fragments
568 # forwarded
569 self.pg_enable_capture()
570 self.src_if.add_stream(fragments[0])
571 self.pg_start()
572 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
573 self.logger.debug(self.vapi.ppcli("show buffers"))
574 self.logger.debug(self.vapi.ppcli("show trace"))
575 c = self.dst_if.get_capture(2)
576 for sent, recvd in zip([fragments[1], fragments[0]], c):
577 self.assertEqual(sent[IP].src, recvd[IP].src)
578 self.assertEqual(sent[IP].dst, recvd[IP].dst)
579 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
580
581 # send rest of fragments - should be immediately forwarded
582 self.pg_enable_capture()
583 self.src_if.add_stream(fragments[2:])
584 self.pg_start()
585 c = self.dst_if.get_capture(len(fragments[2:]))
586 for sent, recvd in zip(fragments[2:], c):
587 self.assertEqual(sent[IP].src, recvd[IP].src)
588 self.assertEqual(sent[IP].dst, recvd[IP].dst)
589 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
590
591 def test_timeout(self):
592 """ reassembly timeout """
593 payload_len = 1000
594 payload = ""
595 counter = 0
596 while len(payload) < payload_len:
597 payload += "%u " % counter
598 counter += 1
599
600 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
601 IP(id=1, src=self.src_if.remote_ip4,
602 dst=self.dst_if.remote_ip4) /
603 UDP(sport=1234, dport=5678) /
604 Raw(payload))
605 fragments = fragment_rfc791(p, payload_len/4)
606
607 self.vapi.ip_reassembly_set(
608 timeout_ms=100, max_reassemblies=1000,
609 max_reassembly_length=1000,
610 expire_walk_interval_ms=50,
611 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
612
613 # send fragments #2 and #1 - should be forwarded
614 self.pg_enable_capture()
615 self.src_if.add_stream(fragments[0:2])
616 self.pg_start()
617 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
618 self.logger.debug(self.vapi.ppcli("show buffers"))
619 self.logger.debug(self.vapi.ppcli("show trace"))
620 c = self.dst_if.get_capture(2)
621 for sent, recvd in zip([fragments[1], fragments[0]], c):
622 self.assertEqual(sent[IP].src, recvd[IP].src)
623 self.assertEqual(sent[IP].dst, recvd[IP].dst)
624 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
625
626 # wait for cleanup
627 self.sleep(.25, "wait before sending rest of fragments")
628
629 # send rest of fragments - shouldn't be forwarded
630 self.pg_enable_capture()
631 self.src_if.add_stream(fragments[2:])
632 self.pg_start()
633 self.dst_if.assert_nothing_captured()
634
635 def test_lru(self):
636 """ reassembly reuses LRU element """
637
638 self.vapi.ip_reassembly_set(
639 timeout_ms=1000000, max_reassemblies=1,
640 max_reassembly_length=1000,
641 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
642 expire_walk_interval_ms=10000)
643
644 payload_len = 1000
645 payload = ""
646 counter = 0
647 while len(payload) < payload_len:
648 payload += "%u " % counter
649 counter += 1
650
651 packet_count = 10
652
653 fragments = [f
654 for i in range(packet_count)
655 for p in (Ether(dst=self.src_if.local_mac,
656 src=self.src_if.remote_mac) /
657 IP(id=i, src=self.src_if.remote_ip4,
658 dst=self.dst_if.remote_ip4) /
659 UDP(sport=1234, dport=5678) /
660 Raw(payload))
661 for f in fragment_rfc791(p, payload_len/4)]
662
663 self.pg_enable_capture()
664 self.src_if.add_stream(fragments)
665 self.pg_start()
666 c = self.dst_if.get_capture(len(fragments))
667 for sent, recvd in zip(fragments, c):
668 self.assertEqual(sent[IP].src, recvd[IP].src)
669 self.assertEqual(sent[IP].dst, recvd[IP].dst)
670 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
671
Klement Sekera18c6cd92020-07-10 09:29:48 +0000672 def send_mixed_and_verify_capture(self, traffic):
673 stream = []
674 for t in traffic:
675 for c in range(t['count']):
676 stream.append(
677 (Ether(dst=self.src_if.local_mac,
678 src=self.src_if.remote_mac) /
679 IP(id=self.counter,
680 flags=t['flags'],
681 src=self.src_if.remote_ip4,
682 dst=self.dst_if.remote_ip4) /
683 UDP(sport=1234, dport=5678) /
684 Raw("abcdef")))
685 self.counter = self.counter + 1
686
687 self.pg_enable_capture()
688 self.src_if.add_stream(stream)
689 self.pg_start()
690 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
691 self.logger.debug(self.vapi.ppcli("show buffers"))
692 self.logger.debug(self.vapi.ppcli("show trace"))
693 self.dst_if.get_capture(len(stream))
694
695 def test_mixed(self):
696 """ mixed traffic correctly passes through SVR """
697 self.counter = 1
698
699 self.send_mixed_and_verify_capture([{'count': 1, 'flags': ''}])
700 self.send_mixed_and_verify_capture([{'count': 2, 'flags': ''}])
701 self.send_mixed_and_verify_capture([{'count': 3, 'flags': ''}])
702 self.send_mixed_and_verify_capture([{'count': 8, 'flags': ''}])
703 self.send_mixed_and_verify_capture([{'count': 257, 'flags': ''}])
704
705 self.send_mixed_and_verify_capture([{'count': 1, 'flags': 'MF'}])
706 self.send_mixed_and_verify_capture([{'count': 2, 'flags': 'MF'}])
707 self.send_mixed_and_verify_capture([{'count': 3, 'flags': 'MF'}])
708 self.send_mixed_and_verify_capture([{'count': 8, 'flags': 'MF'}])
709 self.send_mixed_and_verify_capture([{'count': 257, 'flags': 'MF'}])
710
711 self.send_mixed_and_verify_capture(
712 [{'count': 1, 'flags': ''}, {'count': 1, 'flags': 'MF'}])
713 self.send_mixed_and_verify_capture(
714 [{'count': 2, 'flags': ''}, {'count': 2, 'flags': 'MF'}])
715 self.send_mixed_and_verify_capture(
716 [{'count': 3, 'flags': ''}, {'count': 3, 'flags': 'MF'}])
717 self.send_mixed_and_verify_capture(
718 [{'count': 8, 'flags': ''}, {'count': 8, 'flags': 'MF'}])
719 self.send_mixed_and_verify_capture(
720 [{'count': 129, 'flags': ''}, {'count': 129, 'flags': 'MF'}])
721
722 self.send_mixed_and_verify_capture(
723 [{'count': 1, 'flags': ''}, {'count': 1, 'flags': 'MF'},
724 {'count': 1, 'flags': ''}, {'count': 1, 'flags': 'MF'}])
725 self.send_mixed_and_verify_capture(
726 [{'count': 2, 'flags': ''}, {'count': 2, 'flags': 'MF'},
727 {'count': 2, 'flags': ''}, {'count': 2, 'flags': 'MF'}])
728 self.send_mixed_and_verify_capture(
729 [{'count': 3, 'flags': ''}, {'count': 3, 'flags': 'MF'},
730 {'count': 3, 'flags': ''}, {'count': 3, 'flags': 'MF'}])
731 self.send_mixed_and_verify_capture(
732 [{'count': 8, 'flags': ''}, {'count': 8, 'flags': 'MF'},
733 {'count': 8, 'flags': ''}, {'count': 8, 'flags': 'MF'}])
734 self.send_mixed_and_verify_capture(
735 [{'count': 65, 'flags': ''}, {'count': 65, 'flags': 'MF'},
736 {'count': 65, 'flags': ''}, {'count': 65, 'flags': 'MF'}])
737
Klement Sekerade34c352019-06-25 11:19:22 +0000738
Klement Sekera630ab582019-07-19 09:14:19 +0000739class TestIPv4MWReassembly(VppTestCase):
740 """ IPv4 Reassembly (multiple workers) """
741 worker_config = "workers %d" % worker_count
742
743 @classmethod
744 def setUpClass(cls):
745 super(TestIPv4MWReassembly, cls).setUpClass()
746
747 cls.create_pg_interfaces(range(worker_count+1))
748 cls.src_if = cls.pg0
749 cls.send_ifs = cls.pg_interfaces[:-1]
750 cls.dst_if = cls.pg_interfaces[-1]
751
752 # setup all interfaces
753 for i in cls.pg_interfaces:
754 i.admin_up()
755 i.config_ip4()
756 i.resolve_arp()
757
758 # packets sizes reduced here because we are generating packets without
759 # Ethernet headers, which are added later (diff fragments go via
760 # different interfaces)
761 cls.packet_sizes = [64-len(Ether()), 512-len(Ether()),
762 1518-len(Ether()), 9018-len(Ether())]
763 cls.padding = " abcdefghijklmn"
764 cls.create_stream(cls.packet_sizes)
765 cls.create_fragments()
766
767 @classmethod
768 def tearDownClass(cls):
769 super(TestIPv4MWReassembly, cls).tearDownClass()
770
771 def setUp(self):
772 """ Test setup - force timeout on existing reassemblies """
773 super(TestIPv4MWReassembly, self).setUp()
774 for intf in self.send_ifs:
775 self.vapi.ip_reassembly_enable_disable(
776 sw_if_index=intf.sw_if_index, enable_ip4=True)
777 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
778 max_reassembly_length=1000,
779 expire_walk_interval_ms=10)
780 self.sleep(.25)
781 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
782 max_reassembly_length=1000,
783 expire_walk_interval_ms=10000)
784
785 def tearDown(self):
786 super(TestIPv4MWReassembly, self).tearDown()
787
788 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +0000789 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
Klement Sekera630ab582019-07-19 09:14:19 +0000790 self.logger.debug(self.vapi.ppcli("show buffers"))
791
792 @classmethod
793 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
794 """Create input packet stream
795
796 :param list packet_sizes: Required packet sizes.
797 """
798 for i in range(0, packet_count):
799 info = cls.create_packet_info(cls.src_if, cls.src_if)
800 payload = cls.info_to_payload(info)
801 p = (IP(id=info.index, src=cls.src_if.remote_ip4,
802 dst=cls.dst_if.remote_ip4) /
803 UDP(sport=1234, dport=5678) /
804 Raw(payload))
805 size = packet_sizes[(i // 2) % len(packet_sizes)]
806 cls.extend_packet(p, size, cls.padding)
807 info.data = p
808
809 @classmethod
810 def create_fragments(cls):
811 infos = cls._packet_infos
812 cls.pkt_infos = []
Paul Vinciguerra090096b2020-12-03 00:42:46 -0500813 for index, info in infos.items():
Klement Sekera630ab582019-07-19 09:14:19 +0000814 p = info.data
815 # cls.logger.debug(ppp("Packet:",
816 # p.__class__(scapy.compat.raw(p))))
817 fragments_400 = fragment_rfc791(p, 400)
818 cls.pkt_infos.append((index, fragments_400))
819 cls.fragments_400 = [
820 x for (_, frags) in cls.pkt_infos for x in frags]
821 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, " %
822 (len(infos), len(cls.fragments_400)))
823
824 def verify_capture(self, capture, dropped_packet_indexes=[]):
825 """Verify captured packet stream.
826
827 :param list capture: Captured packet stream.
828 """
829 info = None
830 seen = set()
831 for packet in capture:
832 try:
833 self.logger.debug(ppp("Got packet:", packet))
834 ip = packet[IP]
835 udp = packet[UDP]
836 payload_info = self.payload_to_info(packet[Raw])
837 packet_index = payload_info.index
838 self.assertTrue(
839 packet_index not in dropped_packet_indexes,
840 ppp("Packet received, but should be dropped:", packet))
841 if packet_index in seen:
842 raise Exception(ppp("Duplicate packet received", packet))
843 seen.add(packet_index)
844 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
845 info = self._packet_infos[packet_index]
846 self.assertTrue(info is not None)
847 self.assertEqual(packet_index, info.index)
848 saved_packet = info.data
849 self.assertEqual(ip.src, saved_packet[IP].src)
850 self.assertEqual(ip.dst, saved_packet[IP].dst)
851 self.assertEqual(udp.payload, saved_packet[UDP].payload)
852 except Exception:
853 self.logger.error(ppp("Unexpected or invalid packet:", packet))
854 raise
855 for index in self._packet_infos:
856 self.assertTrue(index in seen or index in dropped_packet_indexes,
857 "Packet with packet_index %d not received" % index)
858
859 def send_packets(self, packets):
860 for counter in range(worker_count):
861 if 0 == len(packets[counter]):
862 continue
863 send_if = self.send_ifs[counter]
864 send_if.add_stream(
865 (Ether(dst=send_if.local_mac, src=send_if.remote_mac) / x
866 for x in packets[counter]),
867 worker=counter)
868 self.pg_start()
869
870 def test_worker_conflict(self):
871 """ 1st and FO=0 fragments on different workers """
872
873 # in first wave we send fragments which don't start at offset 0
874 # then we send fragments with offset 0 on a different thread
875 # then the rest of packets on a random thread
876 first_packets = [[] for n in range(worker_count)]
877 second_packets = [[] for n in range(worker_count)]
878 rest_of_packets = [[] for n in range(worker_count)]
879 for (_, p) in self.pkt_infos:
880 wi = randrange(worker_count)
881 second_packets[wi].append(p[0])
882 if len(p) <= 1:
883 continue
884 wi2 = wi
885 while wi2 == wi:
886 wi2 = randrange(worker_count)
887 first_packets[wi2].append(p[1])
888 wi3 = randrange(worker_count)
889 rest_of_packets[wi3].extend(p[2:])
890
891 self.pg_enable_capture()
892 self.send_packets(first_packets)
893 self.send_packets(second_packets)
894 self.send_packets(rest_of_packets)
895
896 packets = self.dst_if.get_capture(len(self.pkt_infos))
897 self.verify_capture(packets)
898 for send_if in self.send_ifs:
899 send_if.assert_nothing_captured()
900
Klement Sekera68bae5b2019-10-10 18:57:34 +0000901 self.logger.debug(self.vapi.ppcli("show trace"))
902 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
903 self.logger.debug(self.vapi.ppcli("show buffers"))
904 self.vapi.cli("clear trace")
905
Klement Sekera630ab582019-07-19 09:14:19 +0000906 self.pg_enable_capture()
907 self.send_packets(first_packets)
908 self.send_packets(second_packets)
909 self.send_packets(rest_of_packets)
910
911 packets = self.dst_if.get_capture(len(self.pkt_infos))
912 self.verify_capture(packets)
913 for send_if in self.send_ifs:
914 send_if.assert_nothing_captured()
915
916
Klement Sekera947a85c2019-07-24 12:40:37 +0000917class TestIPv6Reassembly(VppTestCase):
Klement Sekera75e7d132017-09-20 08:26:30 +0200918 """ IPv6 Reassembly """
919
920 @classmethod
921 def setUpClass(cls):
922 super(TestIPv6Reassembly, cls).setUpClass()
923
Klement Sekera4c533132018-02-22 11:41:12 +0100924 cls.create_pg_interfaces([0, 1])
925 cls.src_if = cls.pg0
926 cls.dst_if = cls.pg1
Klement Sekera75e7d132017-09-20 08:26:30 +0200927
928 # setup all interfaces
929 for i in cls.pg_interfaces:
930 i.admin_up()
931 i.config_ip6()
932 i.resolve_ndp()
933
Klement Sekera75e7d132017-09-20 08:26:30 +0200934 # packet sizes
935 cls.packet_sizes = [64, 512, 1518, 9018]
936 cls.padding = " abcdefghijklmn"
937 cls.create_stream(cls.packet_sizes)
938 cls.create_fragments()
939
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -0700940 @classmethod
941 def tearDownClass(cls):
942 super(TestIPv6Reassembly, cls).tearDownClass()
943
Klement Sekera75e7d132017-09-20 08:26:30 +0200944 def setUp(self):
945 """ Test setup - force timeout on existing reassemblies """
946 super(TestIPv6Reassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +0100947 self.vapi.ip_reassembly_enable_disable(
948 sw_if_index=self.src_if.sw_if_index, enable_ip6=True)
Klement Sekera75e7d132017-09-20 08:26:30 +0200949 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +0200950 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +0200951 expire_walk_interval_ms=10, is_ip6=1)
952 self.sleep(.25)
953 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +0200954 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +0200955 expire_walk_interval_ms=10000, is_ip6=1)
Klement Sekera896c8962019-06-24 11:52:49 +0000956 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +0100957 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +0200958
959 def tearDown(self):
960 super(TestIPv6Reassembly, self).tearDown()
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700961
962 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +0000963 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +0100964 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +0200965
966 @classmethod
967 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
968 """Create input packet stream for defined interface.
969
970 :param list packet_sizes: Required packet sizes.
971 """
972 for i in range(0, packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +0100973 info = cls.create_packet_info(cls.src_if, cls.src_if)
Klement Sekera75e7d132017-09-20 08:26:30 +0200974 payload = cls.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +0100975 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
976 IPv6(src=cls.src_if.remote_ip6,
977 dst=cls.dst_if.remote_ip6) /
978 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200979 Raw(payload))
980 size = packet_sizes[(i // 2) % len(packet_sizes)]
981 cls.extend_packet(p, size, cls.padding)
982 info.data = p
983
984 @classmethod
985 def create_fragments(cls):
986 infos = cls._packet_infos
987 cls.pkt_infos = []
Paul Vinciguerra090096b2020-12-03 00:42:46 -0500988 for index, info in infos.items():
Klement Sekera75e7d132017-09-20 08:26:30 +0200989 p = info.data
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700990 # cls.logger.debug(ppp("Packet:",
991 # p.__class__(scapy.compat.raw(p))))
Klement Sekera75e7d132017-09-20 08:26:30 +0200992 fragments_400 = fragment_rfc8200(p, info.index, 400)
993 fragments_300 = fragment_rfc8200(p, info.index, 300)
994 cls.pkt_infos.append((index, fragments_400, fragments_300))
995 cls.fragments_400 = [
996 x for _, frags, _ in cls.pkt_infos for x in frags]
997 cls.fragments_300 = [
998 x for _, _, frags in cls.pkt_infos for x in frags]
999 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
1000 "and %s 300-byte fragments" %
1001 (len(infos), len(cls.fragments_400),
1002 len(cls.fragments_300)))
1003
Klement Sekera947a85c2019-07-24 12:40:37 +00001004 def verify_capture(self, capture, dropped_packet_indexes=[]):
1005 """Verify captured packet strea .
1006
1007 :param list capture: Captured packet stream.
1008 """
1009 info = None
1010 seen = set()
1011 for packet in capture:
1012 try:
1013 self.logger.debug(ppp("Got packet:", packet))
1014 ip = packet[IPv6]
1015 udp = packet[UDP]
1016 payload_info = self.payload_to_info(packet[Raw])
1017 packet_index = payload_info.index
1018 self.assertTrue(
1019 packet_index not in dropped_packet_indexes,
1020 ppp("Packet received, but should be dropped:", packet))
1021 if packet_index in seen:
1022 raise Exception(ppp("Duplicate packet received", packet))
1023 seen.add(packet_index)
1024 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
1025 info = self._packet_infos[packet_index]
1026 self.assertTrue(info is not None)
1027 self.assertEqual(packet_index, info.index)
1028 saved_packet = info.data
1029 self.assertEqual(ip.src, saved_packet[IPv6].src)
1030 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
1031 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1032 except Exception:
1033 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1034 raise
1035 for index in self._packet_infos:
1036 self.assertTrue(index in seen or index in dropped_packet_indexes,
1037 "Packet with packet_index %d not received" % index)
1038
1039 def test_reassembly(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001040 """ basic reassembly """
1041
Klement Sekera947a85c2019-07-24 12:40:37 +00001042 self.pg_enable_capture()
1043 self.src_if.add_stream(self.fragments_400)
1044 self.pg_start()
1045
1046 packets = self.dst_if.get_capture(len(self.pkt_infos))
1047 self.verify_capture(packets)
1048 self.src_if.assert_nothing_captured()
1049
1050 # run it all again to verify correctness
1051 self.pg_enable_capture()
1052 self.src_if.add_stream(self.fragments_400)
1053 self.pg_start()
1054
1055 packets = self.dst_if.get_capture(len(self.pkt_infos))
1056 self.verify_capture(packets)
1057 self.src_if.assert_nothing_captured()
1058
Klement Sekera769145c2019-03-06 11:59:57 +01001059 def test_buffer_boundary(self):
1060 """ fragment header crossing buffer boundary """
1061
1062 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1063 IPv6(src=self.src_if.remote_ip6,
1064 dst=self.src_if.local_ip6) /
1065 IPv6ExtHdrHopByHop(
1066 options=[HBHOptUnknown(otype=0xff, optlen=0)] * 1000) /
1067 IPv6ExtHdrFragment(m=1) /
1068 UDP(sport=1234, dport=5678) /
1069 Raw())
1070 self.pg_enable_capture()
1071 self.src_if.add_stream([p])
1072 self.pg_start()
1073 self.src_if.assert_nothing_captured()
1074 self.dst_if.assert_nothing_captured()
1075
Klement Sekera947a85c2019-07-24 12:40:37 +00001076 def test_reversed(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001077 """ reverse order reassembly """
1078
Klement Sekera947a85c2019-07-24 12:40:37 +00001079 fragments = list(self.fragments_400)
1080 fragments.reverse()
1081
1082 self.pg_enable_capture()
1083 self.src_if.add_stream(fragments)
1084 self.pg_start()
1085
1086 packets = self.dst_if.get_capture(len(self.pkt_infos))
1087 self.verify_capture(packets)
1088 self.src_if.assert_nothing_captured()
1089
1090 # run it all again to verify correctness
1091 self.pg_enable_capture()
1092 self.src_if.add_stream(fragments)
1093 self.pg_start()
1094
1095 packets = self.dst_if.get_capture(len(self.pkt_infos))
1096 self.verify_capture(packets)
1097 self.src_if.assert_nothing_captured()
1098
1099 def test_random(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001100 """ random order reassembly """
1101
Klement Sekera947a85c2019-07-24 12:40:37 +00001102 fragments = list(self.fragments_400)
1103 shuffle(fragments)
1104
1105 self.pg_enable_capture()
1106 self.src_if.add_stream(fragments)
1107 self.pg_start()
1108
1109 packets = self.dst_if.get_capture(len(self.pkt_infos))
1110 self.verify_capture(packets)
1111 self.src_if.assert_nothing_captured()
1112
1113 # run it all again to verify correctness
1114 self.pg_enable_capture()
1115 self.src_if.add_stream(fragments)
1116 self.pg_start()
1117
1118 packets = self.dst_if.get_capture(len(self.pkt_infos))
1119 self.verify_capture(packets)
1120 self.src_if.assert_nothing_captured()
1121
1122 def test_duplicates(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001123 """ duplicate fragments """
1124
1125 fragments = [
1126 x for (_, frags, _) in self.pkt_infos
1127 for x in frags
1128 for _ in range(0, min(2, len(frags)))
1129 ]
Klement Sekera947a85c2019-07-24 12:40:37 +00001130
1131 self.pg_enable_capture()
1132 self.src_if.add_stream(fragments)
1133 self.pg_start()
1134
1135 packets = self.dst_if.get_capture(len(self.pkt_infos))
1136 self.verify_capture(packets)
1137 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +02001138
Klement Sekera3a343d42019-05-16 14:35:46 +02001139 def test_long_fragment_chain(self):
1140 """ long fragment chain """
1141
1142 error_cnt_str = \
Klement Sekera896c8962019-06-24 11:52:49 +00001143 "/err/ip6-full-reassembly-feature/fragment chain too long (drop)"
Klement Sekera3a343d42019-05-16 14:35:46 +02001144
Klement Sekera34641f22019-05-22 20:18:26 +02001145 error_cnt = self.statistics.get_err_counter(error_cnt_str)
Klement Sekera3a343d42019-05-16 14:35:46 +02001146
1147 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
1148 max_reassembly_length=3,
1149 expire_walk_interval_ms=50, is_ip6=1)
1150
1151 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1152 IPv6(src=self.src_if.remote_ip6,
1153 dst=self.dst_if.remote_ip6) /
1154 UDP(sport=1234, dport=5678) /
Ole Troan127fbec2019-10-18 15:22:56 +02001155 Raw(b"X" * 1000))
Klement Sekera3a343d42019-05-16 14:35:46 +02001156 frags = fragment_rfc8200(p, 1, 300) + fragment_rfc8200(p, 2, 500)
1157
1158 self.pg_enable_capture()
1159 self.src_if.add_stream(frags)
1160 self.pg_start()
1161
1162 self.dst_if.get_capture(1)
Klement Sekera34641f22019-05-22 20:18:26 +02001163 self.assert_error_counter_equal(error_cnt_str, error_cnt + 1)
Klement Sekera3a343d42019-05-16 14:35:46 +02001164
Klement Sekera75e7d132017-09-20 08:26:30 +02001165 def test_overlap1(self):
Klement Sekera947a85c2019-07-24 12:40:37 +00001166 """ overlapping fragments case #1 """
Klement Sekera75e7d132017-09-20 08:26:30 +02001167
1168 fragments = []
1169 for _, frags_400, frags_300 in self.pkt_infos:
1170 if len(frags_300) == 1:
1171 fragments.extend(frags_400)
1172 else:
1173 for i, j in zip(frags_300, frags_400):
1174 fragments.extend(i)
1175 fragments.extend(j)
1176
1177 dropped_packet_indexes = set(
1178 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
1179 )
1180
1181 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001182 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001183 self.pg_start()
1184
Klement Sekera4c533132018-02-22 11:41:12 +01001185 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +02001186 len(self.pkt_infos) - len(dropped_packet_indexes))
Klement Sekera947a85c2019-07-24 12:40:37 +00001187 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001188 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +02001189
1190 def test_overlap2(self):
Klement Sekera947a85c2019-07-24 12:40:37 +00001191 """ overlapping fragments case #2 """
Klement Sekera75e7d132017-09-20 08:26:30 +02001192
1193 fragments = []
Klement Sekera4c533132018-02-22 11:41:12 +01001194 for _, frags_400, frags_300 in self.pkt_infos:
Klement Sekera75e7d132017-09-20 08:26:30 +02001195 if len(frags_400) == 1:
1196 fragments.extend(frags_400)
1197 else:
1198 # care must be taken here so that there are no fragments
1199 # received by vpp after reassembly is finished, otherwise
1200 # new reassemblies will be started and packet generator will
1201 # freak out when it detects unfreed buffers
Klement Sekera4c533132018-02-22 11:41:12 +01001202 zipped = zip(frags_400, frags_300)
Paul Vinciguerrac7834e02019-03-02 10:43:05 -08001203 for i, j in zipped:
Klement Sekera75e7d132017-09-20 08:26:30 +02001204 fragments.extend(i)
1205 fragments.extend(j)
Paul Vinciguerrac7834e02019-03-02 10:43:05 -08001206 fragments.pop()
Klement Sekera75e7d132017-09-20 08:26:30 +02001207
1208 dropped_packet_indexes = set(
1209 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
1210 )
1211
1212 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001213 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001214 self.pg_start()
1215
Klement Sekera4c533132018-02-22 11:41:12 +01001216 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +02001217 len(self.pkt_infos) - len(dropped_packet_indexes))
Klement Sekera947a85c2019-07-24 12:40:37 +00001218 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001219 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +02001220
Klement Sekera947a85c2019-07-24 12:40:37 +00001221 def test_timeout_inline(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001222 """ timeout (inline) """
1223
1224 dropped_packet_indexes = set(
1225 index for (index, frags, _) in self.pkt_infos if len(frags) > 1
1226 )
1227
Klement Sekera947a85c2019-07-24 12:40:37 +00001228 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1229 max_reassembly_length=3,
1230 expire_walk_interval_ms=10000, is_ip6=1)
1231
1232 self.pg_enable_capture()
1233 self.src_if.add_stream(self.fragments_400)
1234 self.pg_start()
1235
1236 packets = self.dst_if.get_capture(
1237 len(self.pkt_infos) - len(dropped_packet_indexes))
1238 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001239 pkts = self.src_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +02001240 expected_count=len(dropped_packet_indexes))
1241 for icmp in pkts:
1242 self.assertIn(ICMPv6TimeExceeded, icmp)
1243 self.assertIn(IPv6ExtHdrFragment, icmp)
1244 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
1245 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
1246
1247 def test_timeout_cleanup(self):
1248 """ timeout (cleanup) """
1249
1250 # whole packets + fragmented packets sans last fragment
1251 fragments = [
1252 x for (_, frags_400, _) in self.pkt_infos
1253 for x in frags_400[:-1 if len(frags_400) > 1 else None]
1254 ]
1255
1256 # last fragments for fragmented packets
1257 fragments2 = [frags_400[-1]
1258 for (_, frags_400, _) in self.pkt_infos
1259 if len(frags_400) > 1]
1260
1261 dropped_packet_indexes = set(
1262 index for (index, frags_400, _) in self.pkt_infos
1263 if len(frags_400) > 1)
1264
1265 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001266 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001267 expire_walk_interval_ms=50)
1268
1269 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001270 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001271 expire_walk_interval_ms=50, is_ip6=1)
1272
1273 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001274 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001275 self.pg_start()
1276
1277 self.sleep(.25, "wait before sending rest of fragments")
1278
Klement Sekera4c533132018-02-22 11:41:12 +01001279 self.src_if.add_stream(fragments2)
Klement Sekera75e7d132017-09-20 08:26:30 +02001280 self.pg_start()
Klement Sekera75e7d132017-09-20 08:26:30 +02001281
Klement Sekera4c533132018-02-22 11:41:12 +01001282 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +02001283 len(self.pkt_infos) - len(dropped_packet_indexes))
Klement Sekera947a85c2019-07-24 12:40:37 +00001284 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001285 pkts = self.src_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +02001286 expected_count=len(dropped_packet_indexes))
1287 for icmp in pkts:
1288 self.assertIn(ICMPv6TimeExceeded, icmp)
1289 self.assertIn(IPv6ExtHdrFragment, icmp)
1290 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
1291 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
1292
Klement Sekera947a85c2019-07-24 12:40:37 +00001293 def test_disabled(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001294 """ reassembly disabled """
1295
1296 dropped_packet_indexes = set(
1297 index for (index, frags_400, _) in self.pkt_infos
1298 if len(frags_400) > 1)
Klement Sekera947a85c2019-07-24 12:40:37 +00001299
1300 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
1301 max_reassembly_length=3,
1302 expire_walk_interval_ms=10000, is_ip6=1)
1303
1304 self.pg_enable_capture()
1305 self.src_if.add_stream(self.fragments_400)
1306 self.pg_start()
1307
1308 packets = self.dst_if.get_capture(
1309 len(self.pkt_infos) - len(dropped_packet_indexes))
1310 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001311 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +02001312
1313 def test_missing_upper(self):
1314 """ missing upper layer """
Klement Sekera4c533132018-02-22 11:41:12 +01001315 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1316 IPv6(src=self.src_if.remote_ip6,
1317 dst=self.src_if.local_ip6) /
1318 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001319 Raw())
1320 self.extend_packet(p, 1000, self.padding)
1321 fragments = fragment_rfc8200(p, 1, 500)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001322 bad_fragment = p.__class__(scapy.compat.raw(fragments[1]))
Klement Sekera75e7d132017-09-20 08:26:30 +02001323 bad_fragment[IPv6ExtHdrFragment].nh = 59
1324 bad_fragment[IPv6ExtHdrFragment].offset = 0
1325 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001326 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +02001327 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +01001328 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +02001329 icmp = pkts[0]
1330 self.assertIn(ICMPv6ParamProblem, icmp)
1331 self.assert_equal(icmp[ICMPv6ParamProblem].code, 3, "ICMP code")
1332
1333 def test_invalid_frag_size(self):
1334 """ fragment size not a multiple of 8 """
Klement Sekera4c533132018-02-22 11:41:12 +01001335 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1336 IPv6(src=self.src_if.remote_ip6,
1337 dst=self.src_if.local_ip6) /
1338 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001339 Raw())
1340 self.extend_packet(p, 1000, self.padding)
1341 fragments = fragment_rfc8200(p, 1, 500)
1342 bad_fragment = fragments[0]
1343 self.extend_packet(bad_fragment, len(bad_fragment) + 5)
1344 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001345 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +02001346 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +01001347 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +02001348 icmp = pkts[0]
1349 self.assertIn(ICMPv6ParamProblem, icmp)
1350 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1351
1352 def test_invalid_packet_size(self):
1353 """ total packet size > 65535 """
Klement Sekera4c533132018-02-22 11:41:12 +01001354 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1355 IPv6(src=self.src_if.remote_ip6,
1356 dst=self.src_if.local_ip6) /
1357 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001358 Raw())
1359 self.extend_packet(p, 1000, self.padding)
1360 fragments = fragment_rfc8200(p, 1, 500)
1361 bad_fragment = fragments[1]
1362 bad_fragment[IPv6ExtHdrFragment].offset = 65500
1363 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001364 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +02001365 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +01001366 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +02001367 icmp = pkts[0]
1368 self.assertIn(ICMPv6ParamProblem, icmp)
1369 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1370
1371
Klement Sekera630ab582019-07-19 09:14:19 +00001372class TestIPv6MWReassembly(VppTestCase):
1373 """ IPv6 Reassembly (multiple workers) """
1374 worker_config = "workers %d" % worker_count
1375
1376 @classmethod
1377 def setUpClass(cls):
1378 super(TestIPv6MWReassembly, cls).setUpClass()
1379
1380 cls.create_pg_interfaces(range(worker_count+1))
1381 cls.src_if = cls.pg0
1382 cls.send_ifs = cls.pg_interfaces[:-1]
1383 cls.dst_if = cls.pg_interfaces[-1]
1384
1385 # setup all interfaces
1386 for i in cls.pg_interfaces:
1387 i.admin_up()
1388 i.config_ip6()
1389 i.resolve_ndp()
1390
1391 # packets sizes reduced here because we are generating packets without
1392 # Ethernet headers, which are added later (diff fragments go via
1393 # different interfaces)
1394 cls.packet_sizes = [64-len(Ether()), 512-len(Ether()),
1395 1518-len(Ether()), 9018-len(Ether())]
1396 cls.padding = " abcdefghijklmn"
1397 cls.create_stream(cls.packet_sizes)
1398 cls.create_fragments()
1399
1400 @classmethod
1401 def tearDownClass(cls):
1402 super(TestIPv6MWReassembly, cls).tearDownClass()
1403
1404 def setUp(self):
1405 """ Test setup - force timeout on existing reassemblies """
1406 super(TestIPv6MWReassembly, self).setUp()
1407 for intf in self.send_ifs:
1408 self.vapi.ip_reassembly_enable_disable(
1409 sw_if_index=intf.sw_if_index, enable_ip6=True)
1410 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1411 max_reassembly_length=1000,
1412 expire_walk_interval_ms=10, is_ip6=1)
1413 self.sleep(.25)
1414 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1415 max_reassembly_length=1000,
1416 expire_walk_interval_ms=1000, is_ip6=1)
1417
1418 def tearDown(self):
1419 super(TestIPv6MWReassembly, self).tearDown()
1420
1421 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +00001422 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
Klement Sekera630ab582019-07-19 09:14:19 +00001423 self.logger.debug(self.vapi.ppcli("show buffers"))
1424
1425 @classmethod
1426 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
1427 """Create input packet stream
1428
1429 :param list packet_sizes: Required packet sizes.
1430 """
1431 for i in range(0, packet_count):
1432 info = cls.create_packet_info(cls.src_if, cls.src_if)
1433 payload = cls.info_to_payload(info)
1434 p = (IPv6(src=cls.src_if.remote_ip6,
1435 dst=cls.dst_if.remote_ip6) /
1436 UDP(sport=1234, dport=5678) /
1437 Raw(payload))
1438 size = packet_sizes[(i // 2) % len(packet_sizes)]
1439 cls.extend_packet(p, size, cls.padding)
1440 info.data = p
1441
1442 @classmethod
1443 def create_fragments(cls):
1444 infos = cls._packet_infos
1445 cls.pkt_infos = []
Paul Vinciguerra090096b2020-12-03 00:42:46 -05001446 for index, info in infos.items():
Klement Sekera630ab582019-07-19 09:14:19 +00001447 p = info.data
1448 # cls.logger.debug(ppp("Packet:",
1449 # p.__class__(scapy.compat.raw(p))))
1450 fragments_400 = fragment_rfc8200(p, index, 400)
1451 cls.pkt_infos.append((index, fragments_400))
1452 cls.fragments_400 = [
1453 x for (_, frags) in cls.pkt_infos for x in frags]
1454 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, " %
1455 (len(infos), len(cls.fragments_400)))
1456
1457 def verify_capture(self, capture, dropped_packet_indexes=[]):
1458 """Verify captured packet strea .
1459
1460 :param list capture: Captured packet stream.
1461 """
1462 info = None
1463 seen = set()
1464 for packet in capture:
1465 try:
1466 self.logger.debug(ppp("Got packet:", packet))
1467 ip = packet[IPv6]
1468 udp = packet[UDP]
1469 payload_info = self.payload_to_info(packet[Raw])
1470 packet_index = payload_info.index
1471 self.assertTrue(
1472 packet_index not in dropped_packet_indexes,
1473 ppp("Packet received, but should be dropped:", packet))
1474 if packet_index in seen:
1475 raise Exception(ppp("Duplicate packet received", packet))
1476 seen.add(packet_index)
1477 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
1478 info = self._packet_infos[packet_index]
1479 self.assertTrue(info is not None)
1480 self.assertEqual(packet_index, info.index)
1481 saved_packet = info.data
1482 self.assertEqual(ip.src, saved_packet[IPv6].src)
1483 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
1484 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1485 except Exception:
1486 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1487 raise
1488 for index in self._packet_infos:
1489 self.assertTrue(index in seen or index in dropped_packet_indexes,
1490 "Packet with packet_index %d not received" % index)
1491
1492 def send_packets(self, packets):
1493 for counter in range(worker_count):
1494 if 0 == len(packets[counter]):
1495 continue
1496 send_if = self.send_ifs[counter]
1497 send_if.add_stream(
1498 (Ether(dst=send_if.local_mac, src=send_if.remote_mac) / x
1499 for x in packets[counter]),
1500 worker=counter)
1501 self.pg_start()
1502
1503 def test_worker_conflict(self):
1504 """ 1st and FO=0 fragments on different workers """
1505
1506 # in first wave we send fragments which don't start at offset 0
1507 # then we send fragments with offset 0 on a different thread
1508 # then the rest of packets on a random thread
1509 first_packets = [[] for n in range(worker_count)]
1510 second_packets = [[] for n in range(worker_count)]
1511 rest_of_packets = [[] for n in range(worker_count)]
1512 for (_, p) in self.pkt_infos:
1513 wi = randrange(worker_count)
1514 second_packets[wi].append(p[0])
1515 if len(p) <= 1:
1516 continue
1517 wi2 = wi
1518 while wi2 == wi:
1519 wi2 = randrange(worker_count)
1520 first_packets[wi2].append(p[1])
1521 wi3 = randrange(worker_count)
1522 rest_of_packets[wi3].extend(p[2:])
1523
1524 self.pg_enable_capture()
1525 self.send_packets(first_packets)
1526 self.send_packets(second_packets)
1527 self.send_packets(rest_of_packets)
1528
1529 packets = self.dst_if.get_capture(len(self.pkt_infos))
1530 self.verify_capture(packets)
1531 for send_if in self.send_ifs:
1532 send_if.assert_nothing_captured()
1533
Klement Sekera68bae5b2019-10-10 18:57:34 +00001534 self.logger.debug(self.vapi.ppcli("show trace"))
1535 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1536 self.logger.debug(self.vapi.ppcli("show buffers"))
1537 self.vapi.cli("clear trace")
1538
Klement Sekera630ab582019-07-19 09:14:19 +00001539 self.pg_enable_capture()
1540 self.send_packets(first_packets)
1541 self.send_packets(second_packets)
1542 self.send_packets(rest_of_packets)
1543
1544 packets = self.dst_if.get_capture(len(self.pkt_infos))
1545 self.verify_capture(packets)
1546 for send_if in self.send_ifs:
1547 send_if.assert_nothing_captured()
1548
1549
Klement Sekerade34c352019-06-25 11:19:22 +00001550class TestIPv6SVReassembly(VppTestCase):
1551 """ IPv6 Shallow Virtual Reassembly """
1552
1553 @classmethod
1554 def setUpClass(cls):
1555 super(TestIPv6SVReassembly, cls).setUpClass()
1556
1557 cls.create_pg_interfaces([0, 1])
1558 cls.src_if = cls.pg0
1559 cls.dst_if = cls.pg1
1560
1561 # setup all interfaces
1562 for i in cls.pg_interfaces:
1563 i.admin_up()
1564 i.config_ip6()
1565 i.resolve_ndp()
1566
1567 def setUp(self):
1568 """ Test setup - force timeout on existing reassemblies """
1569 super(TestIPv6SVReassembly, self).setUp()
1570 self.vapi.ip_reassembly_enable_disable(
1571 sw_if_index=self.src_if.sw_if_index, enable_ip6=True,
1572 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
1573 self.vapi.ip_reassembly_set(
1574 timeout_ms=0, max_reassemblies=1000,
1575 max_reassembly_length=1000,
1576 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1577 expire_walk_interval_ms=10, is_ip6=1)
1578 self.sleep(.25)
1579 self.vapi.ip_reassembly_set(
1580 timeout_ms=1000000, max_reassemblies=1000,
1581 max_reassembly_length=1000,
1582 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1583 expire_walk_interval_ms=10000, is_ip6=1)
1584
1585 def tearDown(self):
1586 super(TestIPv6SVReassembly, self).tearDown()
1587 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1588 self.logger.debug(self.vapi.ppcli("show buffers"))
1589
1590 def test_basic(self):
1591 """ basic reassembly """
1592 payload_len = 1000
1593 payload = ""
1594 counter = 0
1595 while len(payload) < payload_len:
1596 payload += "%u " % counter
1597 counter += 1
1598
1599 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1600 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1601 UDP(sport=1234, dport=5678) /
1602 Raw(payload))
1603 fragments = fragment_rfc8200(p, 1, payload_len/4)
1604
1605 # send fragment #2 - should be cached inside reassembly
1606 self.pg_enable_capture()
1607 self.src_if.add_stream(fragments[1])
1608 self.pg_start()
1609 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1610 self.logger.debug(self.vapi.ppcli("show buffers"))
1611 self.logger.debug(self.vapi.ppcli("show trace"))
1612 self.dst_if.assert_nothing_captured()
1613
1614 # send fragment #1 - reassembly is finished now and both fragments
1615 # forwarded
1616 self.pg_enable_capture()
1617 self.src_if.add_stream(fragments[0])
1618 self.pg_start()
1619 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1620 self.logger.debug(self.vapi.ppcli("show buffers"))
1621 self.logger.debug(self.vapi.ppcli("show trace"))
1622 c = self.dst_if.get_capture(2)
1623 for sent, recvd in zip([fragments[1], fragments[0]], c):
1624 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1625 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1626 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1627
1628 # send rest of fragments - should be immediately forwarded
1629 self.pg_enable_capture()
1630 self.src_if.add_stream(fragments[2:])
1631 self.pg_start()
1632 c = self.dst_if.get_capture(len(fragments[2:]))
1633 for sent, recvd in zip(fragments[2:], c):
1634 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1635 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1636 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1637
1638 def test_timeout(self):
1639 """ reassembly timeout """
1640 payload_len = 1000
1641 payload = ""
1642 counter = 0
1643 while len(payload) < payload_len:
1644 payload += "%u " % counter
1645 counter += 1
1646
1647 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1648 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1649 UDP(sport=1234, dport=5678) /
1650 Raw(payload))
1651 fragments = fragment_rfc8200(p, 1, payload_len/4)
1652
1653 self.vapi.ip_reassembly_set(
1654 timeout_ms=100, max_reassemblies=1000,
1655 max_reassembly_length=1000,
1656 expire_walk_interval_ms=50,
1657 is_ip6=1,
1658 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
1659
1660 # send fragments #2 and #1 - should be forwarded
1661 self.pg_enable_capture()
1662 self.src_if.add_stream(fragments[0:2])
1663 self.pg_start()
1664 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
1665 self.logger.debug(self.vapi.ppcli("show buffers"))
1666 self.logger.debug(self.vapi.ppcli("show trace"))
1667 c = self.dst_if.get_capture(2)
1668 for sent, recvd in zip([fragments[1], fragments[0]], c):
1669 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1670 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1671 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1672
1673 # wait for cleanup
1674 self.sleep(.25, "wait before sending rest of fragments")
1675
1676 # send rest of fragments - shouldn't be forwarded
1677 self.pg_enable_capture()
1678 self.src_if.add_stream(fragments[2:])
1679 self.pg_start()
1680 self.dst_if.assert_nothing_captured()
1681
1682 def test_lru(self):
1683 """ reassembly reuses LRU element """
1684
1685 self.vapi.ip_reassembly_set(
1686 timeout_ms=1000000, max_reassemblies=1,
1687 max_reassembly_length=1000,
1688 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1689 is_ip6=1, expire_walk_interval_ms=10000)
1690
1691 payload_len = 1000
1692 payload = ""
1693 counter = 0
1694 while len(payload) < payload_len:
1695 payload += "%u " % counter
1696 counter += 1
1697
1698 packet_count = 10
1699
1700 fragments = [f
1701 for i in range(packet_count)
1702 for p in (Ether(dst=self.src_if.local_mac,
1703 src=self.src_if.remote_mac) /
1704 IPv6(src=self.src_if.remote_ip6,
1705 dst=self.dst_if.remote_ip6) /
1706 UDP(sport=1234, dport=5678) /
1707 Raw(payload))
1708 for f in fragment_rfc8200(p, i, payload_len/4)]
1709
1710 self.pg_enable_capture()
1711 self.src_if.add_stream(fragments)
1712 self.pg_start()
1713 c = self.dst_if.get_capture(len(fragments))
1714 for sent, recvd in zip(fragments, c):
1715 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1716 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1717 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1718
1719
Juraj Sloboda3048b632018-10-02 11:13:53 +02001720class TestIPv4ReassemblyLocalNode(VppTestCase):
1721 """ IPv4 Reassembly for packets coming to ip4-local node """
1722
1723 @classmethod
1724 def setUpClass(cls):
1725 super(TestIPv4ReassemblyLocalNode, cls).setUpClass()
1726
1727 cls.create_pg_interfaces([0])
1728 cls.src_dst_if = cls.pg0
1729
1730 # setup all interfaces
1731 for i in cls.pg_interfaces:
1732 i.admin_up()
1733 i.config_ip4()
1734 i.resolve_arp()
1735
1736 cls.padding = " abcdefghijklmn"
1737 cls.create_stream()
1738 cls.create_fragments()
1739
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -07001740 @classmethod
1741 def tearDownClass(cls):
1742 super(TestIPv4ReassemblyLocalNode, cls).tearDownClass()
1743
Juraj Sloboda3048b632018-10-02 11:13:53 +02001744 def setUp(self):
1745 """ Test setup - force timeout on existing reassemblies """
1746 super(TestIPv4ReassemblyLocalNode, self).setUp()
1747 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001748 max_reassembly_length=1000,
Juraj Sloboda3048b632018-10-02 11:13:53 +02001749 expire_walk_interval_ms=10)
1750 self.sleep(.25)
1751 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001752 max_reassembly_length=1000,
Juraj Sloboda3048b632018-10-02 11:13:53 +02001753 expire_walk_interval_ms=10000)
1754
1755 def tearDown(self):
1756 super(TestIPv4ReassemblyLocalNode, self).tearDown()
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -07001757
1758 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +00001759 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +01001760 self.logger.debug(self.vapi.ppcli("show buffers"))
Juraj Sloboda3048b632018-10-02 11:13:53 +02001761
1762 @classmethod
1763 def create_stream(cls, packet_count=test_packet_count):
1764 """Create input packet stream for defined interface.
1765
1766 :param list packet_sizes: Required packet sizes.
1767 """
1768 for i in range(0, packet_count):
1769 info = cls.create_packet_info(cls.src_dst_if, cls.src_dst_if)
1770 payload = cls.info_to_payload(info)
1771 p = (Ether(dst=cls.src_dst_if.local_mac,
1772 src=cls.src_dst_if.remote_mac) /
1773 IP(id=info.index, src=cls.src_dst_if.remote_ip4,
1774 dst=cls.src_dst_if.local_ip4) /
1775 ICMP(type='echo-request', id=1234) /
1776 Raw(payload))
1777 cls.extend_packet(p, 1518, cls.padding)
1778 info.data = p
1779
1780 @classmethod
1781 def create_fragments(cls):
1782 infos = cls._packet_infos
1783 cls.pkt_infos = []
Paul Vinciguerra090096b2020-12-03 00:42:46 -05001784 for index, info in infos.items():
Juraj Sloboda3048b632018-10-02 11:13:53 +02001785 p = info.data
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001786 # cls.logger.debug(ppp("Packet:",
1787 # p.__class__(scapy.compat.raw(p))))
Juraj Sloboda3048b632018-10-02 11:13:53 +02001788 fragments_300 = fragment_rfc791(p, 300)
1789 cls.pkt_infos.append((index, fragments_300))
1790 cls.fragments_300 = [x for (_, frags) in cls.pkt_infos for x in frags]
1791 cls.logger.debug("Fragmented %s packets into %s 300-byte fragments" %
1792 (len(infos), len(cls.fragments_300)))
1793
1794 def verify_capture(self, capture):
1795 """Verify captured packet stream.
1796
1797 :param list capture: Captured packet stream.
1798 """
1799 info = None
1800 seen = set()
1801 for packet in capture:
1802 try:
1803 self.logger.debug(ppp("Got packet:", packet))
1804 ip = packet[IP]
1805 icmp = packet[ICMP]
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001806 payload_info = self.payload_to_info(packet[Raw])
Juraj Sloboda3048b632018-10-02 11:13:53 +02001807 packet_index = payload_info.index
1808 if packet_index in seen:
1809 raise Exception(ppp("Duplicate packet received", packet))
1810 seen.add(packet_index)
1811 self.assertEqual(payload_info.dst, self.src_dst_if.sw_if_index)
1812 info = self._packet_infos[packet_index]
Klement Sekera14d7e902018-12-10 13:46:09 +01001813 self.assertIsNotNone(info)
Juraj Sloboda3048b632018-10-02 11:13:53 +02001814 self.assertEqual(packet_index, info.index)
1815 saved_packet = info.data
1816 self.assertEqual(ip.src, saved_packet[IP].dst)
1817 self.assertEqual(ip.dst, saved_packet[IP].src)
1818 self.assertEqual(icmp.type, 0) # echo reply
1819 self.assertEqual(icmp.id, saved_packet[ICMP].id)
1820 self.assertEqual(icmp.payload, saved_packet[ICMP].payload)
1821 except Exception:
1822 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1823 raise
1824 for index in self._packet_infos:
Klement Sekera14d7e902018-12-10 13:46:09 +01001825 self.assertIn(index, seen,
1826 "Packet with packet_index %d not received" % index)
Juraj Sloboda3048b632018-10-02 11:13:53 +02001827
1828 def test_reassembly(self):
1829 """ basic reassembly """
1830
1831 self.pg_enable_capture()
1832 self.src_dst_if.add_stream(self.fragments_300)
1833 self.pg_start()
1834
1835 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
1836 self.verify_capture(packets)
1837
1838 # run it all again to verify correctness
1839 self.pg_enable_capture()
1840 self.src_dst_if.add_stream(self.fragments_300)
1841 self.pg_start()
1842
1843 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
1844 self.verify_capture(packets)
1845
1846
Klement Sekera75e7d132017-09-20 08:26:30 +02001847class TestFIFReassembly(VppTestCase):
1848 """ Fragments in fragments reassembly """
1849
1850 @classmethod
1851 def setUpClass(cls):
1852 super(TestFIFReassembly, cls).setUpClass()
1853
Klement Sekera4c533132018-02-22 11:41:12 +01001854 cls.create_pg_interfaces([0, 1])
1855 cls.src_if = cls.pg0
1856 cls.dst_if = cls.pg1
1857 for i in cls.pg_interfaces:
1858 i.admin_up()
1859 i.config_ip4()
1860 i.resolve_arp()
1861 i.config_ip6()
1862 i.resolve_ndp()
Klement Sekera75e7d132017-09-20 08:26:30 +02001863
Klement Sekera75e7d132017-09-20 08:26:30 +02001864 cls.packet_sizes = [64, 512, 1518, 9018]
1865 cls.padding = " abcdefghijklmn"
1866
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -07001867 @classmethod
1868 def tearDownClass(cls):
1869 super(TestFIFReassembly, cls).tearDownClass()
1870
Klement Sekera75e7d132017-09-20 08:26:30 +02001871 def setUp(self):
1872 """ Test setup - force timeout on existing reassemblies """
1873 super(TestFIFReassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +01001874 self.vapi.ip_reassembly_enable_disable(
1875 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
1876 enable_ip6=True)
1877 self.vapi.ip_reassembly_enable_disable(
1878 sw_if_index=self.dst_if.sw_if_index, enable_ip4=True,
1879 enable_ip6=True)
Klement Sekera75e7d132017-09-20 08:26:30 +02001880 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001881 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001882 expire_walk_interval_ms=10)
1883 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001884 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001885 expire_walk_interval_ms=10, is_ip6=1)
1886 self.sleep(.25)
1887 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001888 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001889 expire_walk_interval_ms=10000)
1890 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001891 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001892 expire_walk_interval_ms=10000, is_ip6=1)
1893
1894 def tearDown(self):
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -07001895 super(TestFIFReassembly, self).tearDown()
1896
1897 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +00001898 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
1899 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +01001900 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +02001901
1902 def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):
1903 """Verify captured packet stream.
1904
1905 :param list capture: Captured packet stream.
1906 """
1907 info = None
1908 seen = set()
1909 for packet in capture:
1910 try:
Klement Sekera4c533132018-02-22 11:41:12 +01001911 self.logger.debug(ppp("Got packet:", packet))
Klement Sekera75e7d132017-09-20 08:26:30 +02001912 ip = packet[ip_class]
1913 udp = packet[UDP]
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001914 payload_info = self.payload_to_info(packet[Raw])
Klement Sekera75e7d132017-09-20 08:26:30 +02001915 packet_index = payload_info.index
1916 self.assertTrue(
1917 packet_index not in dropped_packet_indexes,
1918 ppp("Packet received, but should be dropped:", packet))
1919 if packet_index in seen:
1920 raise Exception(ppp("Duplicate packet received", packet))
1921 seen.add(packet_index)
Klement Sekera4c533132018-02-22 11:41:12 +01001922 self.assertEqual(payload_info.dst, self.dst_if.sw_if_index)
Klement Sekera75e7d132017-09-20 08:26:30 +02001923 info = self._packet_infos[packet_index]
1924 self.assertTrue(info is not None)
1925 self.assertEqual(packet_index, info.index)
1926 saved_packet = info.data
1927 self.assertEqual(ip.src, saved_packet[ip_class].src)
1928 self.assertEqual(ip.dst, saved_packet[ip_class].dst)
1929 self.assertEqual(udp.payload, saved_packet[UDP].payload)
Klement Sekera4c533132018-02-22 11:41:12 +01001930 except Exception:
Klement Sekera75e7d132017-09-20 08:26:30 +02001931 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1932 raise
1933 for index in self._packet_infos:
1934 self.assertTrue(index in seen or index in dropped_packet_indexes,
1935 "Packet with packet_index %d not received" % index)
1936
1937 def test_fif4(self):
1938 """ Fragments in fragments (4o4) """
1939
1940 # TODO this should be ideally in setUpClass, but then we hit a bug
1941 # with VppIpRoute incorrectly reporting it's present when it's not
1942 # so we need to manually remove the vpp config, thus we cannot have
1943 # it shared for multiple test cases
1944 self.tun_ip4 = "1.1.1.2"
1945
Klement Sekera4c533132018-02-22 11:41:12 +01001946 self.gre4 = VppGreInterface(self, self.src_if.local_ip4, self.tun_ip4)
Klement Sekera75e7d132017-09-20 08:26:30 +02001947 self.gre4.add_vpp_config()
1948 self.gre4.admin_up()
1949 self.gre4.config_ip4()
1950
Klement Sekera4c533132018-02-22 11:41:12 +01001951 self.vapi.ip_reassembly_enable_disable(
1952 sw_if_index=self.gre4.sw_if_index, enable_ip4=True)
1953
Klement Sekera75e7d132017-09-20 08:26:30 +02001954 self.route4 = VppIpRoute(self, self.tun_ip4, 32,
Klement Sekera4c533132018-02-22 11:41:12 +01001955 [VppRoutePath(self.src_if.remote_ip4,
1956 self.src_if.sw_if_index)])
Klement Sekera75e7d132017-09-20 08:26:30 +02001957 self.route4.add_vpp_config()
1958
1959 self.reset_packet_infos()
1960 for i in range(test_packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01001961 info = self.create_packet_info(self.src_if, self.dst_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02001962 payload = self.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +01001963 # Ethernet header here is only for size calculation, thus it
1964 # doesn't matter how it's initialized. This is to ensure that
1965 # reassembled packet is not > 9000 bytes, so that it's not dropped
1966 p = (Ether() /
1967 IP(id=i, src=self.src_if.remote_ip4,
1968 dst=self.dst_if.remote_ip4) /
1969 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001970 Raw(payload))
1971 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1972 self.extend_packet(p, size, self.padding)
Klement Sekera4c533132018-02-22 11:41:12 +01001973 info.data = p[IP] # use only IP part, without ethernet header
Klement Sekera75e7d132017-09-20 08:26:30 +02001974
Paul Vinciguerra090096b2020-12-03 00:42:46 -05001975 fragments = [x for _, p in self._packet_infos.items()
Klement Sekera75e7d132017-09-20 08:26:30 +02001976 for x in fragment_rfc791(p.data, 400)]
1977
1978 encapped_fragments = \
Klement Sekera4c533132018-02-22 11:41:12 +01001979 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1980 IP(src=self.tun_ip4, dst=self.src_if.local_ip4) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001981 GRE() /
1982 p
1983 for p in fragments]
1984
1985 fragmented_encapped_fragments = \
1986 [x for p in encapped_fragments
1987 for x in fragment_rfc791(p, 200)]
1988
Klement Sekera4c533132018-02-22 11:41:12 +01001989 self.src_if.add_stream(fragmented_encapped_fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001990
1991 self.pg_enable_capture(self.pg_interfaces)
1992 self.pg_start()
1993
Klement Sekera4c533132018-02-22 11:41:12 +01001994 self.src_if.assert_nothing_captured()
1995 packets = self.dst_if.get_capture(len(self._packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +02001996 self.verify_capture(packets, IP)
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.gre4.remove_vpp_config()
Klement Sekera4c533132018-02-22 11:41:12 +01002001 self.logger.debug(self.vapi.ppcli("show interface"))
Klement Sekera75e7d132017-09-20 08:26:30 +02002002
2003 def test_fif6(self):
2004 """ Fragments in fragments (6o6) """
2005 # TODO this should be ideally in setUpClass, but then we hit a bug
2006 # with VppIpRoute incorrectly reporting it's present when it's not
2007 # so we need to manually remove the vpp config, thus we cannot have
2008 # it shared for multiple test cases
2009 self.tun_ip6 = "1002::1"
2010
Neale Ranns5a8844b2019-04-16 07:15:35 +00002011 self.gre6 = VppGreInterface(self, self.src_if.local_ip6, self.tun_ip6)
Klement Sekera75e7d132017-09-20 08:26:30 +02002012 self.gre6.add_vpp_config()
2013 self.gre6.admin_up()
2014 self.gre6.config_ip6()
2015
Klement Sekera4c533132018-02-22 11:41:12 +01002016 self.vapi.ip_reassembly_enable_disable(
2017 sw_if_index=self.gre6.sw_if_index, enable_ip6=True)
2018
Klement Sekera75e7d132017-09-20 08:26:30 +02002019 self.route6 = VppIpRoute(self, self.tun_ip6, 128,
Neale Ranns097fa662018-05-01 05:17:55 -07002020 [VppRoutePath(
2021 self.src_if.remote_ip6,
2022 self.src_if.sw_if_index)])
Klement Sekera75e7d132017-09-20 08:26:30 +02002023 self.route6.add_vpp_config()
2024
2025 self.reset_packet_infos()
2026 for i in range(test_packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01002027 info = self.create_packet_info(self.src_if, self.dst_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02002028 payload = self.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +01002029 # Ethernet header here is only for size calculation, thus it
2030 # doesn't matter how it's initialized. This is to ensure that
2031 # reassembled packet is not > 9000 bytes, so that it's not dropped
2032 p = (Ether() /
2033 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
2034 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02002035 Raw(payload))
2036 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
2037 self.extend_packet(p, size, self.padding)
Klement Sekera4c533132018-02-22 11:41:12 +01002038 info.data = p[IPv6] # use only IPv6 part, without ethernet header
Klement Sekera75e7d132017-09-20 08:26:30 +02002039
Paul Vinciguerra090096b2020-12-03 00:42:46 -05002040 fragments = [x for _, i in self._packet_infos.items()
Klement Sekera75e7d132017-09-20 08:26:30 +02002041 for x in fragment_rfc8200(
2042 i.data, i.index, 400)]
2043
2044 encapped_fragments = \
Klement Sekera4c533132018-02-22 11:41:12 +01002045 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
2046 IPv6(src=self.tun_ip6, dst=self.src_if.local_ip6) /
Klement Sekera75e7d132017-09-20 08:26:30 +02002047 GRE() /
2048 p
2049 for p in fragments]
2050
2051 fragmented_encapped_fragments = \
2052 [x for p in encapped_fragments for x in (
2053 fragment_rfc8200(
2054 p,
2055 2 * len(self._packet_infos) + p[IPv6ExtHdrFragment].id,
2056 200)
2057 if IPv6ExtHdrFragment in p else [p]
2058 )
2059 ]
2060
Klement Sekera4c533132018-02-22 11:41:12 +01002061 self.src_if.add_stream(fragmented_encapped_fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02002062
2063 self.pg_enable_capture(self.pg_interfaces)
2064 self.pg_start()
2065
Klement Sekera4c533132018-02-22 11:41:12 +01002066 self.src_if.assert_nothing_captured()
2067 packets = self.dst_if.get_capture(len(self._packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +02002068 self.verify_capture(packets, IPv6)
2069
2070 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
2071 # so that it's query_vpp_config() works as it should
Klement Sekera3ecc2212018-03-27 10:34:43 +02002072 self.gre6.remove_vpp_config()
Klement Sekera75e7d132017-09-20 08:26:30 +02002073
2074
2075if __name__ == '__main__':
2076 unittest.main(testRunner=VppTestRunner)