blob: caac7ce4df1d762cce5db528d994a5f033399957 [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
Dave Wallace8800f732023-08-31 00:47:44 -04004from random import shuffle, randrange
Klement Sekera75e7d132017-09-20 08:26:30 +02005
Dave Wallace8800f732023-08-31 00:47:44 -04006from framework import VppTestCase
7from asfframework import VppTestRunner
Klement Sekera947a85c2019-07-24 12:40:37 +00008
Klement Sekera75e7d132017-09-20 08:26:30 +02009from scapy.packet import Raw
10from scapy.layers.l2 import Ether, GRE
Klement Sekera01c1fa42021-12-14 18:25:11 +000011from scapy.layers.inet import IP, UDP, ICMP, icmptypes
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020012from scapy.layers.inet6 import (
13 HBHOptUnknown,
14 ICMPv6ParamProblem,
15 ICMPv6TimeExceeded,
16 IPv6,
17 IPv6ExtHdrFragment,
18 IPv6ExtHdrHopByHop,
19 IPv6ExtHdrDestOpt,
20 PadN,
21 ICMPv6EchoRequest,
22 ICMPv6EchoReply,
23)
Dave Wallace8800f732023-08-31 00:47:44 -040024from util import ppp, fragment_rfc791, fragment_rfc8200
Neale Ranns5a8844b2019-04-16 07:15:35 +000025from vpp_gre_interface import VppGreInterface
Dave Wallace8800f732023-08-31 00:47:44 -040026from vpp_ip_route import VppIpRoute, VppRoutePath
Klement Sekera896c8962019-06-24 11:52:49 +000027from vpp_papi import VppEnum
Dmitry Valter34fa0ce2024-03-11 10:38:46 +000028from config import config
Klement Sekera75e7d132017-09-20 08:26:30 +020029
Klement Sekerad0f70a32018-12-14 17:24:13 +010030# 35 is enough to have >257 400-byte fragments
31test_packet_count = 35
Klement Sekera75e7d132017-09-20 08:26:30 +020032
33
Klement Sekera947a85c2019-07-24 12:40:37 +000034class TestIPv4Reassembly(VppTestCase):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020035 """IPv4 Reassembly"""
Klement Sekera75e7d132017-09-20 08:26:30 +020036
37 @classmethod
38 def setUpClass(cls):
Klement Sekera01c1fa42021-12-14 18:25:11 +000039 super().setUpClass()
Klement Sekera75e7d132017-09-20 08:26:30 +020040
Klement Sekera4c533132018-02-22 11:41:12 +010041 cls.create_pg_interfaces([0, 1])
42 cls.src_if = cls.pg0
43 cls.dst_if = cls.pg1
Klement Sekera75e7d132017-09-20 08:26:30 +020044
45 # setup all interfaces
46 for i in cls.pg_interfaces:
47 i.admin_up()
48 i.config_ip4()
49 i.resolve_arp()
50
Klement Sekera75e7d132017-09-20 08:26:30 +020051 # packet sizes
52 cls.packet_sizes = [64, 512, 1518, 9018]
53 cls.padding = " abcdefghijklmn"
54 cls.create_stream(cls.packet_sizes)
55 cls.create_fragments()
56
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -070057 @classmethod
58 def tearDownClass(cls):
Klement Sekera01c1fa42021-12-14 18:25:11 +000059 super().tearDownClass()
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -070060
Klement Sekera75e7d132017-09-20 08:26:30 +020061 def setUp(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020062 """Test setup - force timeout on existing reassemblies"""
Klement Sekera01c1fa42021-12-14 18:25:11 +000063 super().setUp()
Klement Sekera4c533132018-02-22 11:41:12 +010064 self.vapi.ip_reassembly_enable_disable(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020065 sw_if_index=self.src_if.sw_if_index, enable_ip4=True
66 )
67 self.vapi.ip_reassembly_set(
68 timeout_ms=0,
69 max_reassemblies=1000,
70 max_reassembly_length=1000,
71 expire_walk_interval_ms=10,
72 )
73 self.virtual_sleep(0.25)
74 self.vapi.ip_reassembly_set(
75 timeout_ms=1000000,
76 max_reassemblies=1000,
77 max_reassembly_length=1000,
78 expire_walk_interval_ms=10000,
79 )
Klement Sekera75e7d132017-09-20 08:26:30 +020080
81 def tearDown(self):
Klement Sekera01c1fa42021-12-14 18:25:11 +000082 self.vapi.ip_reassembly_enable_disable(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020083 sw_if_index=self.src_if.sw_if_index, enable_ip4=False
84 )
Klement Sekera01c1fa42021-12-14 18:25:11 +000085 super().tearDown()
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -070086
87 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +000088 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +010089 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +020090
91 @classmethod
92 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
Klement Sekerad0f70a32018-12-14 17:24:13 +010093 """Create input packet stream
Klement Sekera75e7d132017-09-20 08:26:30 +020094
95 :param list packet_sizes: Required packet sizes.
96 """
97 for i in range(0, packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +010098 info = cls.create_packet_info(cls.src_if, cls.src_if)
Klement Sekera75e7d132017-09-20 08:26:30 +020099 payload = cls.info_to_payload(info)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200100 p = (
101 Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac)
102 / IP(
103 id=info.index, src=cls.src_if.remote_ip4, dst=cls.dst_if.remote_ip4
104 )
105 / UDP(sport=1234, dport=5678)
106 / Raw(payload)
107 )
Klement Sekera75e7d132017-09-20 08:26:30 +0200108 size = packet_sizes[(i // 2) % len(packet_sizes)]
109 cls.extend_packet(p, size, cls.padding)
110 info.data = p
111
112 @classmethod
113 def create_fragments(cls):
114 infos = cls._packet_infos
115 cls.pkt_infos = []
Paul Vinciguerra090096b2020-12-03 00:42:46 -0500116 for index, info in infos.items():
Klement Sekera75e7d132017-09-20 08:26:30 +0200117 p = info.data
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700118 # cls.logger.debug(ppp("Packet:",
119 # p.__class__(scapy.compat.raw(p))))
Klement Sekera75e7d132017-09-20 08:26:30 +0200120 fragments_400 = fragment_rfc791(p, 400)
121 fragments_300 = fragment_rfc791(p, 300)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200122 fragments_200 = [x for f in fragments_400 for x in fragment_rfc791(f, 200)]
123 cls.pkt_infos.append((index, fragments_400, fragments_300, fragments_200))
124 cls.fragments_400 = [x for (_, frags, _, _) in cls.pkt_infos for x in frags]
125 cls.fragments_300 = [x for (_, _, frags, _) in cls.pkt_infos for x in frags]
126 cls.fragments_200 = [x for (_, _, _, frags) in cls.pkt_infos for x in frags]
127 cls.logger.debug(
128 "Fragmented %s packets into %s 400-byte fragments, "
129 "%s 300-byte fragments and %s 200-byte fragments"
130 % (
131 len(infos),
132 len(cls.fragments_400),
133 len(cls.fragments_300),
134 len(cls.fragments_200),
135 )
136 )
Klement Sekera75e7d132017-09-20 08:26:30 +0200137
Klement Sekera947a85c2019-07-24 12:40:37 +0000138 def verify_capture(self, capture, dropped_packet_indexes=[]):
139 """Verify captured packet stream.
140
141 :param list capture: Captured packet stream.
142 """
143 info = None
144 seen = set()
145 for packet in capture:
146 try:
147 self.logger.debug(ppp("Got packet:", packet))
148 ip = packet[IP]
149 udp = packet[UDP]
150 payload_info = self.payload_to_info(packet[Raw])
151 packet_index = payload_info.index
152 self.assertTrue(
153 packet_index not in dropped_packet_indexes,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200154 ppp("Packet received, but should be dropped:", packet),
155 )
Klement Sekera947a85c2019-07-24 12:40:37 +0000156 if packet_index in seen:
157 raise Exception(ppp("Duplicate packet received", packet))
158 seen.add(packet_index)
159 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
160 info = self._packet_infos[packet_index]
161 self.assertTrue(info is not None)
162 self.assertEqual(packet_index, info.index)
163 saved_packet = info.data
164 self.assertEqual(ip.src, saved_packet[IP].src)
165 self.assertEqual(ip.dst, saved_packet[IP].dst)
166 self.assertEqual(udp.payload, saved_packet[UDP].payload)
167 except Exception:
168 self.logger.error(ppp("Unexpected or invalid packet:", packet))
169 raise
170 for index in self._packet_infos:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200171 self.assertTrue(
172 index in seen or index in dropped_packet_indexes,
173 "Packet with packet_index %d not received" % index,
174 )
Klement Sekera947a85c2019-07-24 12:40:37 +0000175
176 def test_reassembly(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200177 """basic reassembly"""
Klement Sekera75e7d132017-09-20 08:26:30 +0200178
Klement Sekera947a85c2019-07-24 12:40:37 +0000179 self.pg_enable_capture()
180 self.src_if.add_stream(self.fragments_200)
181 self.pg_start()
182
183 packets = self.dst_if.get_capture(len(self.pkt_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(self.fragments_200)
190 self.pg_start()
191
192 packets = self.dst_if.get_capture(len(self.pkt_infos))
193 self.verify_capture(packets)
194 self.src_if.assert_nothing_captured()
195
Klement Sekera53be16d2020-12-15 21:47:36 +0100196 def test_verify_clear_trace_mid_reassembly(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200197 """verify clear trace works mid-reassembly"""
Klement Sekera53be16d2020-12-15 21:47:36 +0100198
199 self.pg_enable_capture()
200 self.src_if.add_stream(self.fragments_200[0:-1])
201 self.pg_start()
202
203 self.logger.debug(self.vapi.cli("show trace"))
204 self.vapi.cli("clear trace")
205
206 self.src_if.add_stream(self.fragments_200[-1])
207 self.pg_start()
208 packets = self.dst_if.get_capture(len(self.pkt_infos))
209 self.verify_capture(packets)
210
Klement Sekera947a85c2019-07-24 12:40:37 +0000211 def test_reversed(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200212 """reverse order reassembly"""
Klement Sekera75e7d132017-09-20 08:26:30 +0200213
Klement Sekera947a85c2019-07-24 12:40:37 +0000214 fragments = list(self.fragments_200)
215 fragments.reverse()
216
217 self.pg_enable_capture()
218 self.src_if.add_stream(fragments)
219 self.pg_start()
220
221 packets = self.dst_if.get_capture(len(self.packet_infos))
222 self.verify_capture(packets)
223 self.src_if.assert_nothing_captured()
224
225 # run it all again to verify correctness
226 self.pg_enable_capture()
227 self.src_if.add_stream(fragments)
228 self.pg_start()
229
230 packets = self.dst_if.get_capture(len(self.packet_infos))
231 self.verify_capture(packets)
232 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200233
Klement Sekera3a343d42019-05-16 14:35:46 +0200234 def test_long_fragment_chain(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200235 """long fragment chain"""
Klement Sekera3a343d42019-05-16 14:35:46 +0200236
Neale Rannse22a7042022-08-09 03:03:29 +0000237 error_cnt_str = "/err/ip4-full-reassembly-feature/reass_fragment_chain_too_long"
Klement Sekera3a343d42019-05-16 14:35:46 +0200238
Klement Sekera34641f22019-05-22 20:18:26 +0200239 error_cnt = self.statistics.get_err_counter(error_cnt_str)
Klement Sekera3a343d42019-05-16 14:35:46 +0200240
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200241 self.vapi.ip_reassembly_set(
242 timeout_ms=100,
243 max_reassemblies=1000,
244 max_reassembly_length=3,
245 expire_walk_interval_ms=50,
246 )
Klement Sekera3a343d42019-05-16 14:35:46 +0200247
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200248 p1 = (
249 Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac)
250 / IP(id=1000, src=self.src_if.remote_ip4, dst=self.dst_if.remote_ip4)
251 / UDP(sport=1234, dport=5678)
252 / Raw(b"X" * 1000)
253 )
254 p2 = (
255 Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac)
256 / IP(id=1001, src=self.src_if.remote_ip4, dst=self.dst_if.remote_ip4)
257 / UDP(sport=1234, dport=5678)
258 / Raw(b"X" * 1000)
259 )
Klement Sekera3a343d42019-05-16 14:35:46 +0200260 frags = fragment_rfc791(p1, 200) + fragment_rfc791(p2, 500)
261
262 self.pg_enable_capture()
263 self.src_if.add_stream(frags)
264 self.pg_start()
265
266 self.dst_if.get_capture(1)
Klement Sekera34641f22019-05-22 20:18:26 +0200267 self.assert_error_counter_equal(error_cnt_str, error_cnt + 1)
Klement Sekera3a343d42019-05-16 14:35:46 +0200268
Klement Sekera14d7e902018-12-10 13:46:09 +0100269 def test_5737(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200270 """fragment length + ip header size > 65535"""
Klement Sekera4ee633e2018-12-14 12:00:44 +0100271 self.vapi.cli("clear errors")
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200272 raw = b"""E\x00\x00\x88,\xf8\x1f\xfe@\x01\x98\x00\xc0\xa8\n-\xc0\xa8\n\
Ole Troan127fbec2019-10-18 15:22:56 +0200273\x01\x08\x00\xf0J\xed\xcb\xf1\xf5Test-group: IPv4.IPv4.ipv4-message.\
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200274Ethernet-Payload.IPv4-Packet.IPv4-Header.Fragment-Offset; Test-case: 5737"""
275 malformed_packet = Ether(
276 dst=self.src_if.local_mac, src=self.src_if.remote_mac
277 ) / IP(raw)
278 p = (
279 Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac)
280 / IP(id=1000, src=self.src_if.remote_ip4, dst=self.dst_if.remote_ip4)
281 / UDP(sport=1234, dport=5678)
282 / Raw(b"X" * 1000)
283 )
Klement Sekera14d7e902018-12-10 13:46:09 +0100284 valid_fragments = fragment_rfc791(p, 400)
285
Neale Rannse22a7042022-08-09 03:03:29 +0000286 counter = "/err/ip4-full-reassembly-feature/reass_malformed_packet"
Ole Troan127fbec2019-10-18 15:22:56 +0200287 error_counter = self.statistics.get_err_counter(counter)
Klement Sekera14d7e902018-12-10 13:46:09 +0100288 self.pg_enable_capture()
289 self.src_if.add_stream([malformed_packet] + valid_fragments)
290 self.pg_start()
291
292 self.dst_if.get_capture(1)
Klement Sekera896c8962019-06-24 11:52:49 +0000293 self.logger.debug(self.vapi.ppcli("show error"))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200294 self.assertEqual(self.statistics.get_err_counter(counter), error_counter + 1)
Klement Sekera14d7e902018-12-10 13:46:09 +0100295
Klement Sekera400f6d82018-12-13 14:35:48 +0100296 def test_44924(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200297 """compress tiny fragments"""
298 packets = [
299 (
300 Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac)
301 / IP(
302 id=24339,
303 flags="MF",
304 frag=0,
305 ttl=64,
306 src=self.src_if.remote_ip4,
307 dst=self.dst_if.remote_ip4,
308 )
309 / ICMP(type="echo-request", code=0, id=0x1FE6, seq=0x2407)
310 / Raw(load="Test-group: IPv4")
311 ),
312 (
313 Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac)
314 / IP(
315 id=24339,
316 flags="MF",
317 frag=3,
318 ttl=64,
319 src=self.src_if.remote_ip4,
320 dst=self.dst_if.remote_ip4,
321 )
322 / ICMP(type="echo-request", code=0, id=0x1FE6, seq=0x2407)
323 / Raw(load=".IPv4.Fragmentation.vali")
324 ),
325 (
326 Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac)
327 / IP(
328 id=24339,
329 frag=6,
330 ttl=64,
331 src=self.src_if.remote_ip4,
332 dst=self.dst_if.remote_ip4,
333 )
334 / ICMP(type="echo-request", code=0, id=0x1FE6, seq=0x2407)
335 / Raw(load="d; Test-case: 44924")
336 ),
337 ]
Klement Sekera400f6d82018-12-13 14:35:48 +0100338
339 self.pg_enable_capture()
340 self.src_if.add_stream(packets)
341 self.pg_start()
342
343 self.dst_if.get_capture(1)
344
Klement Sekera4ee633e2018-12-14 12:00:44 +0100345 def test_frag_1(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200346 """fragment of size 1"""
Klement Sekera4ee633e2018-12-14 12:00:44 +0100347 self.vapi.cli("clear errors")
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200348 malformed_packets = [
349 (
350 Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac)
351 / IP(
352 id=7,
353 len=21,
354 flags="MF",
355 frag=0,
356 ttl=64,
357 src=self.src_if.remote_ip4,
358 dst=self.dst_if.remote_ip4,
359 )
360 / ICMP(type="echo-request")
361 ),
362 (
363 Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac)
364 / IP(
365 id=7,
366 len=21,
367 frag=1,
368 ttl=64,
369 src=self.src_if.remote_ip4,
370 dst=self.dst_if.remote_ip4,
371 )
372 / Raw(load=b"\x08")
373 ),
374 ]
Klement Sekera4ee633e2018-12-14 12:00:44 +0100375
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200376 p = (
377 Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac)
378 / IP(id=1000, src=self.src_if.remote_ip4, dst=self.dst_if.remote_ip4)
379 / UDP(sport=1234, dport=5678)
380 / Raw(b"X" * 1000)
381 )
Klement Sekera4ee633e2018-12-14 12:00:44 +0100382 valid_fragments = fragment_rfc791(p, 400)
383
384 self.pg_enable_capture()
385 self.src_if.add_stream(malformed_packets + valid_fragments)
386 self.pg_start()
387
388 self.dst_if.get_capture(1)
389
Klement Sekera896c8962019-06-24 11:52:49 +0000390 self.assert_packet_counter_equal("ip4-full-reassembly-feature", 1)
Klement Sekera4ee633e2018-12-14 12:00:44 +0100391 # TODO remove above, uncomment below once clearing of counters
392 # is supported
393 # self.assert_packet_counter_equal(
Neale Rannse22a7042022-08-09 03:03:29 +0000394 # "/err/ip4-full-reassembly-feature/reass_malformed_packet", 1)
Klement Sekera4ee633e2018-12-14 12:00:44 +0100395
Klement Sekera947a85c2019-07-24 12:40:37 +0000396 def test_random(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200397 """random order reassembly"""
Klement Sekera947a85c2019-07-24 12:40:37 +0000398
399 fragments = list(self.fragments_200)
400 shuffle(fragments)
401
402 self.pg_enable_capture()
403 self.src_if.add_stream(fragments)
404 self.pg_start()
405
406 packets = self.dst_if.get_capture(len(self.packet_infos))
407 self.verify_capture(packets)
408 self.src_if.assert_nothing_captured()
409
410 # run it all again to verify correctness
411 self.pg_enable_capture()
412 self.src_if.add_stream(fragments)
413 self.pg_start()
414
415 packets = self.dst_if.get_capture(len(self.packet_infos))
416 self.verify_capture(packets)
417 self.src_if.assert_nothing_captured()
418
419 def test_duplicates(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200420 """duplicate fragments"""
Klement Sekera947a85c2019-07-24 12:40:37 +0000421
Klement Sekera75e7d132017-09-20 08:26:30 +0200422 fragments = [
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200423 x
424 for (_, frags, _, _) in self.pkt_infos
Klement Sekera75e7d132017-09-20 08:26:30 +0200425 for x in frags
426 for _ in range(0, min(2, len(frags)))
427 ]
Klement Sekera947a85c2019-07-24 12:40:37 +0000428
429 self.pg_enable_capture()
430 self.src_if.add_stream(fragments)
431 self.pg_start()
432
433 packets = self.dst_if.get_capture(len(self.pkt_infos))
434 self.verify_capture(packets)
435 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200436
437 def test_overlap1(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200438 """overlapping fragments case #1"""
Klement Sekera75e7d132017-09-20 08:26:30 +0200439
440 fragments = []
441 for _, _, frags_300, frags_200 in self.pkt_infos:
442 if len(frags_300) == 1:
443 fragments.extend(frags_300)
444 else:
445 for i, j in zip(frags_200, frags_300):
446 fragments.extend(i)
447 fragments.extend(j)
448
449 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100450 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200451 self.pg_start()
452
Klement Sekera4c533132018-02-22 11:41:12 +0100453 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera947a85c2019-07-24 12:40:37 +0000454 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100455 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200456
457 # run it all to verify correctness
458 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100459 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200460 self.pg_start()
461
Klement Sekera4c533132018-02-22 11:41:12 +0100462 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera947a85c2019-07-24 12:40:37 +0000463 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100464 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200465
466 def test_overlap2(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200467 """overlapping fragments case #2"""
Klement Sekera75e7d132017-09-20 08:26:30 +0200468
469 fragments = []
470 for _, _, frags_300, frags_200 in self.pkt_infos:
471 if len(frags_300) == 1:
472 fragments.extend(frags_300)
473 else:
474 # care must be taken here so that there are no fragments
475 # received by vpp after reassembly is finished, otherwise
476 # new reassemblies will be started and packet generator will
477 # freak out when it detects unfreed buffers
478 zipped = zip(frags_300, frags_200)
Paul Vinciguerrac7834e02019-03-02 10:43:05 -0800479 for i, j in zipped:
Klement Sekera75e7d132017-09-20 08:26:30 +0200480 fragments.extend(i)
481 fragments.extend(j)
Paul Vinciguerrac7834e02019-03-02 10:43:05 -0800482 fragments.pop()
Klement Sekera75e7d132017-09-20 08:26:30 +0200483
484 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100485 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200486 self.pg_start()
487
Klement Sekera4c533132018-02-22 11:41:12 +0100488 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera947a85c2019-07-24 12:40:37 +0000489 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100490 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200491
492 # run it all to verify correctness
493 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100494 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200495 self.pg_start()
496
Klement Sekera4c533132018-02-22 11:41:12 +0100497 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera947a85c2019-07-24 12:40:37 +0000498 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100499 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200500
Klement Sekera947a85c2019-07-24 12:40:37 +0000501 def test_timeout_inline(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200502 """timeout (inline)"""
Klement Sekera75e7d132017-09-20 08:26:30 +0200503
504 dropped_packet_indexes = set(
505 index for (index, frags, _, _) in self.pkt_infos if len(frags) > 1
506 )
507
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200508 self.vapi.ip_reassembly_set(
509 timeout_ms=0,
510 max_reassemblies=1000,
511 max_reassembly_length=3,
512 expire_walk_interval_ms=10000,
513 )
Klement Sekera947a85c2019-07-24 12:40:37 +0000514
515 self.pg_enable_capture()
516 self.src_if.add_stream(self.fragments_400)
517 self.pg_start()
518
519 packets = self.dst_if.get_capture(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200520 len(self.pkt_infos) - len(dropped_packet_indexes)
521 )
Klement Sekera947a85c2019-07-24 12:40:37 +0000522 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100523 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200524
525 def test_timeout_cleanup(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200526 """timeout (cleanup)"""
Klement Sekera75e7d132017-09-20 08:26:30 +0200527
528 # whole packets + fragmented packets sans last fragment
529 fragments = [
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200530 x
531 for (_, frags_400, _, _) in self.pkt_infos
532 for x in frags_400[: -1 if len(frags_400) > 1 else None]
Klement Sekera75e7d132017-09-20 08:26:30 +0200533 ]
534
535 # last fragments for fragmented packets
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200536 fragments2 = [
537 frags_400[-1]
538 for (_, frags_400, _, _) in self.pkt_infos
539 if len(frags_400) > 1
540 ]
Klement Sekera75e7d132017-09-20 08:26:30 +0200541
542 dropped_packet_indexes = set(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200543 index for (index, frags_400, _, _) in self.pkt_infos if len(frags_400) > 1
544 )
Klement Sekera75e7d132017-09-20 08:26:30 +0200545
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200546 self.vapi.ip_reassembly_set(
547 timeout_ms=100,
548 max_reassemblies=1000,
549 max_reassembly_length=1000,
550 expire_walk_interval_ms=50,
551 )
Klement Sekera75e7d132017-09-20 08:26:30 +0200552
553 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100554 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200555 self.pg_start()
556
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200557 self.virtual_sleep(0.25, "wait before sending rest of fragments")
Klement Sekera75e7d132017-09-20 08:26:30 +0200558
Klement Sekera4c533132018-02-22 11:41:12 +0100559 self.src_if.add_stream(fragments2)
Klement Sekera75e7d132017-09-20 08:26:30 +0200560 self.pg_start()
Klement Sekera75e7d132017-09-20 08:26:30 +0200561
Klement Sekera4c533132018-02-22 11:41:12 +0100562 packets = self.dst_if.get_capture(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200563 len(self.pkt_infos) - len(dropped_packet_indexes)
564 )
Klement Sekera947a85c2019-07-24 12:40:37 +0000565 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100566 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200567
Klement Sekera947a85c2019-07-24 12:40:37 +0000568 def test_disabled(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200569 """reassembly disabled"""
Klement Sekera75e7d132017-09-20 08:26:30 +0200570
571 dropped_packet_indexes = set(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200572 index for (index, frags_400, _, _) in self.pkt_infos if len(frags_400) > 1
573 )
Klement Sekera947a85c2019-07-24 12:40:37 +0000574
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200575 self.vapi.ip_reassembly_set(
576 timeout_ms=1000,
577 max_reassemblies=0,
578 max_reassembly_length=3,
579 expire_walk_interval_ms=10000,
580 )
Klement Sekera947a85c2019-07-24 12:40:37 +0000581
582 self.pg_enable_capture()
583 self.src_if.add_stream(self.fragments_400)
584 self.pg_start()
585
586 packets = self.dst_if.get_capture(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200587 len(self.pkt_infos) - len(dropped_packet_indexes)
588 )
Klement Sekera947a85c2019-07-24 12:40:37 +0000589 self.verify_capture(packets, dropped_packet_indexes)
590 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200591
Dmitry Valter34fa0ce2024-03-11 10:38:46 +0000592 @unittest.skipIf(
593 "ping" in config.excluded_plugins, "Exclude tests requiring Ping plugin"
594 )
Klement Sekera01c1fa42021-12-14 18:25:11 +0000595 def test_local_enable_disable(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200596 """local reassembly enabled/disable"""
Klement Sekera01c1fa42021-12-14 18:25:11 +0000597 self.vapi.ip_reassembly_enable_disable(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200598 sw_if_index=self.src_if.sw_if_index, enable_ip4=False
599 )
Klement Sekera01c1fa42021-12-14 18:25:11 +0000600 self.vapi.ip_local_reass_enable_disable(enable_ip4=True)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200601 p = (
602 Ether(src=self.src_if.remote_mac, dst=self.src_if.local_mac)
603 / IP(src=self.src_if.remote_ip4, dst=self.src_if.local_ip4)
604 / ICMP(id=1234, type="echo-request")
605 / Raw("x" * 1000)
606 )
Klement Sekera01c1fa42021-12-14 18:25:11 +0000607 frags = fragment_rfc791(p, 400)
608 r = self.send_and_expect(self.src_if, frags, self.src_if, n_rx=1)[0]
609 self.assertEqual(1234, r[ICMP].id)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200610 self.assertEqual(icmptypes[r[ICMP].type], "echo-reply")
Klement Sekera01c1fa42021-12-14 18:25:11 +0000611 self.vapi.ip_local_reass_enable_disable()
612
613 self.send_and_assert_no_replies(self.src_if, frags)
614 self.vapi.ip_local_reass_enable_disable(enable_ip4=True)
615
Klement Sekera75e7d132017-09-20 08:26:30 +0200616
Klement Sekerade34c352019-06-25 11:19:22 +0000617class TestIPv4SVReassembly(VppTestCase):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200618 """IPv4 Shallow Virtual Reassembly"""
Klement Sekerade34c352019-06-25 11:19:22 +0000619
620 @classmethod
621 def setUpClass(cls):
Klement Sekera01c1fa42021-12-14 18:25:11 +0000622 super().setUpClass()
Klement Sekerade34c352019-06-25 11:19:22 +0000623
624 cls.create_pg_interfaces([0, 1])
625 cls.src_if = cls.pg0
626 cls.dst_if = cls.pg1
627
628 # setup all interfaces
629 for i in cls.pg_interfaces:
630 i.admin_up()
631 i.config_ip4()
632 i.resolve_arp()
633
634 def setUp(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200635 """Test setup - force timeout on existing reassemblies"""
Klement Sekera01c1fa42021-12-14 18:25:11 +0000636 super().setUp()
Klement Sekerade34c352019-06-25 11:19:22 +0000637 self.vapi.ip_reassembly_enable_disable(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200638 sw_if_index=self.src_if.sw_if_index,
639 enable_ip4=True,
640 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
641 )
Klement Sekerade34c352019-06-25 11:19:22 +0000642 self.vapi.ip_reassembly_set(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200643 timeout_ms=0,
644 max_reassemblies=1000,
Klement Sekerade34c352019-06-25 11:19:22 +0000645 max_reassembly_length=1000,
646 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200647 expire_walk_interval_ms=10,
648 )
649 self.virtual_sleep(0.25)
Klement Sekerade34c352019-06-25 11:19:22 +0000650 self.vapi.ip_reassembly_set(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200651 timeout_ms=1000000,
652 max_reassemblies=1000,
Klement Sekerade34c352019-06-25 11:19:22 +0000653 max_reassembly_length=1000,
654 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200655 expire_walk_interval_ms=10000,
656 )
Klement Sekerade34c352019-06-25 11:19:22 +0000657
658 def tearDown(self):
Klement Sekera01c1fa42021-12-14 18:25:11 +0000659 super().tearDown()
Klement Sekerade34c352019-06-25 11:19:22 +0000660 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
661 self.logger.debug(self.vapi.ppcli("show buffers"))
662
663 def test_basic(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200664 """basic reassembly"""
Klement Sekerade34c352019-06-25 11:19:22 +0000665 payload_len = 1000
666 payload = ""
667 counter = 0
668 while len(payload) < payload_len:
669 payload += "%u " % counter
670 counter += 1
671
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200672 p = (
673 Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac)
674 / IP(id=1, src=self.src_if.remote_ip4, dst=self.dst_if.remote_ip4)
675 / UDP(sport=1234, dport=5678)
676 / Raw(payload)
677 )
678 fragments = fragment_rfc791(p, payload_len / 4)
Klement Sekerade34c352019-06-25 11:19:22 +0000679
680 # send fragment #2 - should be cached inside reassembly
681 self.pg_enable_capture()
682 self.src_if.add_stream(fragments[1])
683 self.pg_start()
684 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
685 self.logger.debug(self.vapi.ppcli("show buffers"))
686 self.logger.debug(self.vapi.ppcli("show trace"))
687 self.dst_if.assert_nothing_captured()
688
689 # send fragment #1 - reassembly is finished now and both fragments
690 # forwarded
691 self.pg_enable_capture()
692 self.src_if.add_stream(fragments[0])
693 self.pg_start()
694 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
695 self.logger.debug(self.vapi.ppcli("show buffers"))
696 self.logger.debug(self.vapi.ppcli("show trace"))
697 c = self.dst_if.get_capture(2)
698 for sent, recvd in zip([fragments[1], fragments[0]], c):
699 self.assertEqual(sent[IP].src, recvd[IP].src)
700 self.assertEqual(sent[IP].dst, recvd[IP].dst)
701 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
702
703 # send rest of fragments - should be immediately forwarded
704 self.pg_enable_capture()
705 self.src_if.add_stream(fragments[2:])
706 self.pg_start()
707 c = self.dst_if.get_capture(len(fragments[2:]))
708 for sent, recvd in zip(fragments[2:], c):
709 self.assertEqual(sent[IP].src, recvd[IP].src)
710 self.assertEqual(sent[IP].dst, recvd[IP].dst)
711 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
712
Klement Sekera53be16d2020-12-15 21:47:36 +0100713 def test_verify_clear_trace_mid_reassembly(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200714 """verify clear trace works mid-reassembly"""
Klement Sekera53be16d2020-12-15 21:47:36 +0100715 payload_len = 1000
716 payload = ""
717 counter = 0
718 while len(payload) < payload_len:
719 payload += "%u " % counter
720 counter += 1
721
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200722 p = (
723 Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac)
724 / IP(id=1, src=self.src_if.remote_ip4, dst=self.dst_if.remote_ip4)
725 / UDP(sport=1234, dport=5678)
726 / Raw(payload)
727 )
728 fragments = fragment_rfc791(p, payload_len / 4)
Klement Sekera53be16d2020-12-15 21:47:36 +0100729
730 self.pg_enable_capture()
731 self.src_if.add_stream(fragments[1])
732 self.pg_start()
733
734 self.logger.debug(self.vapi.cli("show trace"))
735 self.vapi.cli("clear trace")
736
737 self.pg_enable_capture()
738 self.src_if.add_stream(fragments[0])
739 self.pg_start()
740 self.dst_if.get_capture(2)
741
742 self.logger.debug(self.vapi.cli("show trace"))
743 self.vapi.cli("clear trace")
744
745 self.pg_enable_capture()
746 self.src_if.add_stream(fragments[2:])
747 self.pg_start()
748 self.dst_if.get_capture(len(fragments[2:]))
749
Klement Sekerade34c352019-06-25 11:19:22 +0000750 def test_timeout(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200751 """reassembly timeout"""
Klement Sekerade34c352019-06-25 11:19:22 +0000752 payload_len = 1000
753 payload = ""
754 counter = 0
755 while len(payload) < payload_len:
756 payload += "%u " % counter
757 counter += 1
758
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200759 p = (
760 Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac)
761 / IP(id=1, src=self.src_if.remote_ip4, dst=self.dst_if.remote_ip4)
762 / UDP(sport=1234, dport=5678)
763 / Raw(payload)
764 )
765 fragments = fragment_rfc791(p, payload_len / 4)
Klement Sekerade34c352019-06-25 11:19:22 +0000766
767 self.vapi.ip_reassembly_set(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200768 timeout_ms=100,
769 max_reassemblies=1000,
Klement Sekerade34c352019-06-25 11:19:22 +0000770 max_reassembly_length=1000,
771 expire_walk_interval_ms=50,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200772 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
773 )
Klement Sekerade34c352019-06-25 11:19:22 +0000774
775 # send fragments #2 and #1 - should be forwarded
776 self.pg_enable_capture()
777 self.src_if.add_stream(fragments[0:2])
778 self.pg_start()
779 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
780 self.logger.debug(self.vapi.ppcli("show buffers"))
781 self.logger.debug(self.vapi.ppcli("show trace"))
782 c = self.dst_if.get_capture(2)
783 for sent, recvd in zip([fragments[1], fragments[0]], c):
784 self.assertEqual(sent[IP].src, recvd[IP].src)
785 self.assertEqual(sent[IP].dst, recvd[IP].dst)
786 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
787
788 # wait for cleanup
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200789 self.virtual_sleep(0.25, "wait before sending rest of fragments")
Klement Sekerade34c352019-06-25 11:19:22 +0000790
791 # send rest of fragments - shouldn't be forwarded
792 self.pg_enable_capture()
793 self.src_if.add_stream(fragments[2:])
794 self.pg_start()
795 self.dst_if.assert_nothing_captured()
796
797 def test_lru(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200798 """reassembly reuses LRU element"""
Klement Sekerade34c352019-06-25 11:19:22 +0000799
800 self.vapi.ip_reassembly_set(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200801 timeout_ms=1000000,
802 max_reassemblies=1,
Klement Sekerade34c352019-06-25 11:19:22 +0000803 max_reassembly_length=1000,
804 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200805 expire_walk_interval_ms=10000,
806 )
Klement Sekerade34c352019-06-25 11:19:22 +0000807
808 payload_len = 1000
809 payload = ""
810 counter = 0
811 while len(payload) < payload_len:
812 payload += "%u " % counter
813 counter += 1
814
815 packet_count = 10
816
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200817 fragments = [
818 f
819 for i in range(packet_count)
820 for p in (
821 Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac)
822 / IP(id=i, src=self.src_if.remote_ip4, dst=self.dst_if.remote_ip4)
823 / UDP(sport=1234, dport=5678)
824 / Raw(payload)
825 )
826 for f in fragment_rfc791(p, payload_len / 4)
827 ]
Klement Sekerade34c352019-06-25 11:19:22 +0000828
829 self.pg_enable_capture()
830 self.src_if.add_stream(fragments)
831 self.pg_start()
832 c = self.dst_if.get_capture(len(fragments))
833 for sent, recvd in zip(fragments, c):
834 self.assertEqual(sent[IP].src, recvd[IP].src)
835 self.assertEqual(sent[IP].dst, recvd[IP].dst)
836 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
837
Klement Sekera18c6cd92020-07-10 09:29:48 +0000838 def send_mixed_and_verify_capture(self, traffic):
839 stream = []
840 for t in traffic:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200841 for c in range(t["count"]):
Klement Sekera18c6cd92020-07-10 09:29:48 +0000842 stream.append(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200843 (
844 Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac)
845 / IP(
846 id=self.counter,
847 flags=t["flags"],
848 src=self.src_if.remote_ip4,
849 dst=self.dst_if.remote_ip4,
850 )
851 / UDP(sport=1234, dport=5678)
852 / Raw("abcdef")
853 )
854 )
Klement Sekera18c6cd92020-07-10 09:29:48 +0000855 self.counter = self.counter + 1
856
857 self.pg_enable_capture()
858 self.src_if.add_stream(stream)
859 self.pg_start()
860 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
861 self.logger.debug(self.vapi.ppcli("show buffers"))
862 self.logger.debug(self.vapi.ppcli("show trace"))
863 self.dst_if.get_capture(len(stream))
864
865 def test_mixed(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200866 """mixed traffic correctly passes through SVR"""
Klement Sekera18c6cd92020-07-10 09:29:48 +0000867 self.counter = 1
868
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200869 self.send_mixed_and_verify_capture([{"count": 1, "flags": ""}])
870 self.send_mixed_and_verify_capture([{"count": 2, "flags": ""}])
871 self.send_mixed_and_verify_capture([{"count": 3, "flags": ""}])
872 self.send_mixed_and_verify_capture([{"count": 8, "flags": ""}])
873 self.send_mixed_and_verify_capture([{"count": 257, "flags": ""}])
Klement Sekera18c6cd92020-07-10 09:29:48 +0000874
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200875 self.send_mixed_and_verify_capture([{"count": 1, "flags": "MF"}])
876 self.send_mixed_and_verify_capture([{"count": 2, "flags": "MF"}])
877 self.send_mixed_and_verify_capture([{"count": 3, "flags": "MF"}])
878 self.send_mixed_and_verify_capture([{"count": 8, "flags": "MF"}])
879 self.send_mixed_and_verify_capture([{"count": 257, "flags": "MF"}])
Klement Sekera18c6cd92020-07-10 09:29:48 +0000880
881 self.send_mixed_and_verify_capture(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200882 [{"count": 1, "flags": ""}, {"count": 1, "flags": "MF"}]
883 )
Klement Sekera18c6cd92020-07-10 09:29:48 +0000884 self.send_mixed_and_verify_capture(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200885 [{"count": 2, "flags": ""}, {"count": 2, "flags": "MF"}]
886 )
Klement Sekera18c6cd92020-07-10 09:29:48 +0000887 self.send_mixed_and_verify_capture(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200888 [{"count": 3, "flags": ""}, {"count": 3, "flags": "MF"}]
889 )
Klement Sekera18c6cd92020-07-10 09:29:48 +0000890 self.send_mixed_and_verify_capture(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200891 [{"count": 8, "flags": ""}, {"count": 8, "flags": "MF"}]
892 )
Klement Sekera18c6cd92020-07-10 09:29:48 +0000893 self.send_mixed_and_verify_capture(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200894 [{"count": 129, "flags": ""}, {"count": 129, "flags": "MF"}]
895 )
Klement Sekera18c6cd92020-07-10 09:29:48 +0000896
897 self.send_mixed_and_verify_capture(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200898 [
899 {"count": 1, "flags": ""},
900 {"count": 1, "flags": "MF"},
901 {"count": 1, "flags": ""},
902 {"count": 1, "flags": "MF"},
903 ]
904 )
Klement Sekera18c6cd92020-07-10 09:29:48 +0000905 self.send_mixed_and_verify_capture(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200906 [
907 {"count": 2, "flags": ""},
908 {"count": 2, "flags": "MF"},
909 {"count": 2, "flags": ""},
910 {"count": 2, "flags": "MF"},
911 ]
912 )
Klement Sekera18c6cd92020-07-10 09:29:48 +0000913 self.send_mixed_and_verify_capture(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200914 [
915 {"count": 3, "flags": ""},
916 {"count": 3, "flags": "MF"},
917 {"count": 3, "flags": ""},
918 {"count": 3, "flags": "MF"},
919 ]
920 )
Klement Sekera18c6cd92020-07-10 09:29:48 +0000921 self.send_mixed_and_verify_capture(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200922 [
923 {"count": 8, "flags": ""},
924 {"count": 8, "flags": "MF"},
925 {"count": 8, "flags": ""},
926 {"count": 8, "flags": "MF"},
927 ]
928 )
Klement Sekera18c6cd92020-07-10 09:29:48 +0000929 self.send_mixed_and_verify_capture(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200930 [
931 {"count": 65, "flags": ""},
932 {"count": 65, "flags": "MF"},
933 {"count": 65, "flags": ""},
934 {"count": 65, "flags": "MF"},
935 ]
936 )
Klement Sekera18c6cd92020-07-10 09:29:48 +0000937
Klement Sekerade34c352019-06-25 11:19:22 +0000938
Klement Sekera630ab582019-07-19 09:14:19 +0000939class TestIPv4MWReassembly(VppTestCase):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200940 """IPv4 Reassembly (multiple workers)"""
941
Klement Sekera8d815022021-03-15 16:58:10 +0100942 vpp_worker_count = 3
Klement Sekera630ab582019-07-19 09:14:19 +0000943
944 @classmethod
945 def setUpClass(cls):
Klement Sekera01c1fa42021-12-14 18:25:11 +0000946 super().setUpClass()
Klement Sekera630ab582019-07-19 09:14:19 +0000947
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200948 cls.create_pg_interfaces(range(cls.vpp_worker_count + 1))
Klement Sekera630ab582019-07-19 09:14:19 +0000949 cls.src_if = cls.pg0
950 cls.send_ifs = cls.pg_interfaces[:-1]
951 cls.dst_if = cls.pg_interfaces[-1]
952
953 # setup all interfaces
954 for i in cls.pg_interfaces:
955 i.admin_up()
956 i.config_ip4()
957 i.resolve_arp()
958
959 # packets sizes reduced here because we are generating packets without
960 # Ethernet headers, which are added later (diff fragments go via
961 # different interfaces)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200962 cls.packet_sizes = [
963 64 - len(Ether()),
964 512 - len(Ether()),
965 1518 - len(Ether()),
966 9018 - len(Ether()),
967 ]
Klement Sekera630ab582019-07-19 09:14:19 +0000968 cls.padding = " abcdefghijklmn"
969 cls.create_stream(cls.packet_sizes)
970 cls.create_fragments()
971
972 @classmethod
973 def tearDownClass(cls):
Klement Sekera01c1fa42021-12-14 18:25:11 +0000974 super().tearDownClass()
Klement Sekera630ab582019-07-19 09:14:19 +0000975
976 def setUp(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200977 """Test setup - force timeout on existing reassemblies"""
Klement Sekera01c1fa42021-12-14 18:25:11 +0000978 super().setUp()
Klement Sekera630ab582019-07-19 09:14:19 +0000979 for intf in self.send_ifs:
980 self.vapi.ip_reassembly_enable_disable(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200981 sw_if_index=intf.sw_if_index, enable_ip4=True
982 )
983 self.vapi.ip_reassembly_set(
984 timeout_ms=0,
985 max_reassemblies=1000,
986 max_reassembly_length=1000,
987 expire_walk_interval_ms=10,
988 )
989 self.virtual_sleep(0.25)
990 self.vapi.ip_reassembly_set(
991 timeout_ms=1000000,
992 max_reassemblies=1000,
993 max_reassembly_length=1000,
994 expire_walk_interval_ms=10000,
995 )
Klement Sekera630ab582019-07-19 09:14:19 +0000996
997 def tearDown(self):
Klement Sekera01c1fa42021-12-14 18:25:11 +0000998 for intf in self.send_ifs:
999 self.vapi.ip_reassembly_enable_disable(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001000 sw_if_index=intf.sw_if_index, enable_ip4=False
1001 )
Klement Sekera01c1fa42021-12-14 18:25:11 +00001002 super().tearDown()
Klement Sekera630ab582019-07-19 09:14:19 +00001003
1004 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +00001005 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
Klement Sekera630ab582019-07-19 09:14:19 +00001006 self.logger.debug(self.vapi.ppcli("show buffers"))
1007
1008 @classmethod
1009 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
1010 """Create input packet stream
1011
1012 :param list packet_sizes: Required packet sizes.
1013 """
1014 for i in range(0, packet_count):
1015 info = cls.create_packet_info(cls.src_if, cls.src_if)
1016 payload = cls.info_to_payload(info)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001017 p = (
1018 IP(id=info.index, src=cls.src_if.remote_ip4, dst=cls.dst_if.remote_ip4)
1019 / UDP(sport=1234, dport=5678)
1020 / Raw(payload)
1021 )
Klement Sekera630ab582019-07-19 09:14:19 +00001022 size = packet_sizes[(i // 2) % len(packet_sizes)]
1023 cls.extend_packet(p, size, cls.padding)
1024 info.data = p
1025
1026 @classmethod
1027 def create_fragments(cls):
1028 infos = cls._packet_infos
1029 cls.pkt_infos = []
Paul Vinciguerra090096b2020-12-03 00:42:46 -05001030 for index, info in infos.items():
Klement Sekera630ab582019-07-19 09:14:19 +00001031 p = info.data
1032 # cls.logger.debug(ppp("Packet:",
1033 # p.__class__(scapy.compat.raw(p))))
1034 fragments_400 = fragment_rfc791(p, 400)
1035 cls.pkt_infos.append((index, fragments_400))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001036 cls.fragments_400 = [x for (_, frags) in cls.pkt_infos for x in frags]
1037 cls.logger.debug(
1038 "Fragmented %s packets into %s 400-byte fragments, "
1039 % (len(infos), len(cls.fragments_400))
1040 )
Klement Sekera630ab582019-07-19 09:14:19 +00001041
1042 def verify_capture(self, capture, dropped_packet_indexes=[]):
1043 """Verify captured packet stream.
1044
1045 :param list capture: Captured packet stream.
1046 """
1047 info = None
1048 seen = set()
1049 for packet in capture:
1050 try:
1051 self.logger.debug(ppp("Got packet:", packet))
1052 ip = packet[IP]
1053 udp = packet[UDP]
1054 payload_info = self.payload_to_info(packet[Raw])
1055 packet_index = payload_info.index
1056 self.assertTrue(
1057 packet_index not in dropped_packet_indexes,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001058 ppp("Packet received, but should be dropped:", packet),
1059 )
Klement Sekera630ab582019-07-19 09:14:19 +00001060 if packet_index in seen:
1061 raise Exception(ppp("Duplicate packet received", packet))
1062 seen.add(packet_index)
1063 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
1064 info = self._packet_infos[packet_index]
1065 self.assertTrue(info is not None)
1066 self.assertEqual(packet_index, info.index)
1067 saved_packet = info.data
1068 self.assertEqual(ip.src, saved_packet[IP].src)
1069 self.assertEqual(ip.dst, saved_packet[IP].dst)
1070 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1071 except Exception:
1072 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1073 raise
1074 for index in self._packet_infos:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001075 self.assertTrue(
1076 index in seen or index in dropped_packet_indexes,
1077 "Packet with packet_index %d not received" % index,
1078 )
Klement Sekera630ab582019-07-19 09:14:19 +00001079
1080 def send_packets(self, packets):
Klement Sekera8d815022021-03-15 16:58:10 +01001081 for counter in range(self.vpp_worker_count):
Klement Sekera630ab582019-07-19 09:14:19 +00001082 if 0 == len(packets[counter]):
1083 continue
1084 send_if = self.send_ifs[counter]
1085 send_if.add_stream(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001086 (
1087 Ether(dst=send_if.local_mac, src=send_if.remote_mac) / x
1088 for x in packets[counter]
1089 ),
1090 worker=counter,
1091 )
Klement Sekera630ab582019-07-19 09:14:19 +00001092 self.pg_start()
1093
1094 def test_worker_conflict(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001095 """1st and FO=0 fragments on different workers"""
Klement Sekera630ab582019-07-19 09:14:19 +00001096
1097 # in first wave we send fragments which don't start at offset 0
1098 # then we send fragments with offset 0 on a different thread
1099 # then the rest of packets on a random thread
Klement Sekera8d815022021-03-15 16:58:10 +01001100 first_packets = [[] for n in range(self.vpp_worker_count)]
1101 second_packets = [[] for n in range(self.vpp_worker_count)]
1102 rest_of_packets = [[] for n in range(self.vpp_worker_count)]
Dave Wallace7b8b4652023-08-15 19:05:26 -04001103 for _, p in self.pkt_infos:
Klement Sekera8d815022021-03-15 16:58:10 +01001104 wi = randrange(self.vpp_worker_count)
Klement Sekera630ab582019-07-19 09:14:19 +00001105 second_packets[wi].append(p[0])
1106 if len(p) <= 1:
1107 continue
1108 wi2 = wi
1109 while wi2 == wi:
Klement Sekera8d815022021-03-15 16:58:10 +01001110 wi2 = randrange(self.vpp_worker_count)
Klement Sekera630ab582019-07-19 09:14:19 +00001111 first_packets[wi2].append(p[1])
Klement Sekera8d815022021-03-15 16:58:10 +01001112 wi3 = randrange(self.vpp_worker_count)
Klement Sekera630ab582019-07-19 09:14:19 +00001113 rest_of_packets[wi3].extend(p[2:])
1114
1115 self.pg_enable_capture()
1116 self.send_packets(first_packets)
1117 self.send_packets(second_packets)
1118 self.send_packets(rest_of_packets)
1119
1120 packets = self.dst_if.get_capture(len(self.pkt_infos))
1121 self.verify_capture(packets)
1122 for send_if in self.send_ifs:
1123 send_if.assert_nothing_captured()
1124
Klement Sekera68bae5b2019-10-10 18:57:34 +00001125 self.logger.debug(self.vapi.ppcli("show trace"))
1126 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
1127 self.logger.debug(self.vapi.ppcli("show buffers"))
1128 self.vapi.cli("clear trace")
1129
Klement Sekera630ab582019-07-19 09:14:19 +00001130 self.pg_enable_capture()
1131 self.send_packets(first_packets)
1132 self.send_packets(second_packets)
1133 self.send_packets(rest_of_packets)
1134
1135 packets = self.dst_if.get_capture(len(self.pkt_infos))
1136 self.verify_capture(packets)
1137 for send_if in self.send_ifs:
1138 send_if.assert_nothing_captured()
1139
1140
Klement Sekera947a85c2019-07-24 12:40:37 +00001141class TestIPv6Reassembly(VppTestCase):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001142 """IPv6 Reassembly"""
Klement Sekera75e7d132017-09-20 08:26:30 +02001143
1144 @classmethod
1145 def setUpClass(cls):
Klement Sekera01c1fa42021-12-14 18:25:11 +00001146 super().setUpClass()
Klement Sekera75e7d132017-09-20 08:26:30 +02001147
Klement Sekera4c533132018-02-22 11:41:12 +01001148 cls.create_pg_interfaces([0, 1])
1149 cls.src_if = cls.pg0
1150 cls.dst_if = cls.pg1
Klement Sekera75e7d132017-09-20 08:26:30 +02001151
1152 # setup all interfaces
1153 for i in cls.pg_interfaces:
1154 i.admin_up()
1155 i.config_ip6()
1156 i.resolve_ndp()
1157
Klement Sekera75e7d132017-09-20 08:26:30 +02001158 # packet sizes
1159 cls.packet_sizes = [64, 512, 1518, 9018]
1160 cls.padding = " abcdefghijklmn"
1161 cls.create_stream(cls.packet_sizes)
1162 cls.create_fragments()
1163
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -07001164 @classmethod
1165 def tearDownClass(cls):
Klement Sekera01c1fa42021-12-14 18:25:11 +00001166 super().tearDownClass()
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -07001167
Klement Sekera75e7d132017-09-20 08:26:30 +02001168 def setUp(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001169 """Test setup - force timeout on existing reassemblies"""
Klement Sekera01c1fa42021-12-14 18:25:11 +00001170 super().setUp()
Klement Sekera4c533132018-02-22 11:41:12 +01001171 self.vapi.ip_reassembly_enable_disable(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001172 sw_if_index=self.src_if.sw_if_index, enable_ip6=True
1173 )
1174 self.vapi.ip_reassembly_set(
1175 timeout_ms=0,
1176 max_reassemblies=1000,
1177 max_reassembly_length=1000,
1178 expire_walk_interval_ms=10,
1179 is_ip6=1,
1180 )
1181 self.virtual_sleep(0.25)
1182 self.vapi.ip_reassembly_set(
1183 timeout_ms=1000000,
1184 max_reassemblies=1000,
1185 max_reassembly_length=1000,
1186 expire_walk_interval_ms=10000,
1187 is_ip6=1,
1188 )
Klement Sekera896c8962019-06-24 11:52:49 +00001189 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +01001190 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +02001191
1192 def tearDown(self):
Klement Sekera01c1fa42021-12-14 18:25:11 +00001193 self.vapi.ip_reassembly_enable_disable(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001194 sw_if_index=self.src_if.sw_if_index, enable_ip6=False
1195 )
Klement Sekera01c1fa42021-12-14 18:25:11 +00001196 super().tearDown()
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -07001197
1198 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +00001199 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +01001200 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +02001201
1202 @classmethod
1203 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
1204 """Create input packet stream for defined interface.
1205
1206 :param list packet_sizes: Required packet sizes.
1207 """
1208 for i in range(0, packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01001209 info = cls.create_packet_info(cls.src_if, cls.src_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02001210 payload = cls.info_to_payload(info)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001211 p = (
1212 Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac)
1213 / IPv6(src=cls.src_if.remote_ip6, dst=cls.dst_if.remote_ip6)
1214 / UDP(sport=1234, dport=5678)
1215 / Raw(payload)
1216 )
Klement Sekera75e7d132017-09-20 08:26:30 +02001217 size = packet_sizes[(i // 2) % len(packet_sizes)]
1218 cls.extend_packet(p, size, cls.padding)
1219 info.data = p
1220
1221 @classmethod
1222 def create_fragments(cls):
1223 infos = cls._packet_infos
1224 cls.pkt_infos = []
Paul Vinciguerra090096b2020-12-03 00:42:46 -05001225 for index, info in infos.items():
Klement Sekera75e7d132017-09-20 08:26:30 +02001226 p = info.data
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001227 # cls.logger.debug(ppp("Packet:",
1228 # p.__class__(scapy.compat.raw(p))))
Klement Sekera75e7d132017-09-20 08:26:30 +02001229 fragments_400 = fragment_rfc8200(p, info.index, 400)
1230 fragments_300 = fragment_rfc8200(p, info.index, 300)
1231 cls.pkt_infos.append((index, fragments_400, fragments_300))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001232 cls.fragments_400 = [x for _, frags, _ in cls.pkt_infos for x in frags]
1233 cls.fragments_300 = [x for _, _, frags in cls.pkt_infos for x in frags]
1234 cls.logger.debug(
1235 "Fragmented %s packets into %s 400-byte fragments, "
1236 "and %s 300-byte fragments"
1237 % (len(infos), len(cls.fragments_400), len(cls.fragments_300))
1238 )
Klement Sekera75e7d132017-09-20 08:26:30 +02001239
Klement Sekera947a85c2019-07-24 12:40:37 +00001240 def verify_capture(self, capture, dropped_packet_indexes=[]):
1241 """Verify captured packet strea .
1242
1243 :param list capture: Captured packet stream.
1244 """
1245 info = None
1246 seen = set()
1247 for packet in capture:
1248 try:
1249 self.logger.debug(ppp("Got packet:", packet))
1250 ip = packet[IPv6]
1251 udp = packet[UDP]
1252 payload_info = self.payload_to_info(packet[Raw])
1253 packet_index = payload_info.index
1254 self.assertTrue(
1255 packet_index not in dropped_packet_indexes,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001256 ppp("Packet received, but should be dropped:", packet),
1257 )
Klement Sekera947a85c2019-07-24 12:40:37 +00001258 if packet_index in seen:
1259 raise Exception(ppp("Duplicate packet received", packet))
1260 seen.add(packet_index)
1261 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
1262 info = self._packet_infos[packet_index]
1263 self.assertTrue(info is not None)
1264 self.assertEqual(packet_index, info.index)
1265 saved_packet = info.data
1266 self.assertEqual(ip.src, saved_packet[IPv6].src)
1267 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
1268 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1269 except Exception:
1270 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1271 raise
1272 for index in self._packet_infos:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001273 self.assertTrue(
1274 index in seen or index in dropped_packet_indexes,
1275 "Packet with packet_index %d not received" % index,
1276 )
Klement Sekera947a85c2019-07-24 12:40:37 +00001277
1278 def test_reassembly(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001279 """basic reassembly"""
Klement Sekera75e7d132017-09-20 08:26:30 +02001280
Klement Sekera947a85c2019-07-24 12:40:37 +00001281 self.pg_enable_capture()
1282 self.src_if.add_stream(self.fragments_400)
1283 self.pg_start()
1284
1285 packets = self.dst_if.get_capture(len(self.pkt_infos))
1286 self.verify_capture(packets)
1287 self.src_if.assert_nothing_captured()
1288
1289 # run it all again to verify correctness
1290 self.pg_enable_capture()
1291 self.src_if.add_stream(self.fragments_400)
1292 self.pg_start()
1293
1294 packets = self.dst_if.get_capture(len(self.pkt_infos))
1295 self.verify_capture(packets)
1296 self.src_if.assert_nothing_captured()
1297
Klement Sekera769145c2019-03-06 11:59:57 +01001298 def test_buffer_boundary(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001299 """fragment header crossing buffer boundary"""
Klement Sekera769145c2019-03-06 11:59:57 +01001300
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001301 p = (
1302 Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac)
1303 / IPv6(src=self.src_if.remote_ip6, dst=self.src_if.local_ip6)
1304 / IPv6ExtHdrHopByHop(options=[HBHOptUnknown(otype=0xFF, optlen=0)] * 1000)
1305 / IPv6ExtHdrFragment(m=1)
1306 / UDP(sport=1234, dport=5678)
1307 / Raw()
1308 )
Klement Sekera769145c2019-03-06 11:59:57 +01001309 self.pg_enable_capture()
1310 self.src_if.add_stream([p])
1311 self.pg_start()
1312 self.src_if.assert_nothing_captured()
1313 self.dst_if.assert_nothing_captured()
1314
Klement Sekera53be16d2020-12-15 21:47:36 +01001315 def test_verify_clear_trace_mid_reassembly(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001316 """verify clear trace works mid-reassembly"""
Klement Sekera53be16d2020-12-15 21:47:36 +01001317
1318 self.pg_enable_capture()
1319 self.src_if.add_stream(self.fragments_400[0:-1])
1320 self.pg_start()
1321
1322 self.logger.debug(self.vapi.cli("show trace"))
1323 self.vapi.cli("clear trace")
1324
1325 self.src_if.add_stream(self.fragments_400[-1])
1326 self.pg_start()
1327 packets = self.dst_if.get_capture(len(self.pkt_infos))
1328 self.verify_capture(packets)
1329
Klement Sekera947a85c2019-07-24 12:40:37 +00001330 def test_reversed(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001331 """reverse order reassembly"""
Klement Sekera75e7d132017-09-20 08:26:30 +02001332
Klement Sekera947a85c2019-07-24 12:40:37 +00001333 fragments = list(self.fragments_400)
1334 fragments.reverse()
1335
1336 self.pg_enable_capture()
1337 self.src_if.add_stream(fragments)
1338 self.pg_start()
1339
1340 packets = self.dst_if.get_capture(len(self.pkt_infos))
1341 self.verify_capture(packets)
1342 self.src_if.assert_nothing_captured()
1343
1344 # run it all again to verify correctness
1345 self.pg_enable_capture()
1346 self.src_if.add_stream(fragments)
1347 self.pg_start()
1348
1349 packets = self.dst_if.get_capture(len(self.pkt_infos))
1350 self.verify_capture(packets)
1351 self.src_if.assert_nothing_captured()
1352
1353 def test_random(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001354 """random order reassembly"""
Klement Sekera75e7d132017-09-20 08:26:30 +02001355
Klement Sekera947a85c2019-07-24 12:40:37 +00001356 fragments = list(self.fragments_400)
1357 shuffle(fragments)
1358
1359 self.pg_enable_capture()
1360 self.src_if.add_stream(fragments)
1361 self.pg_start()
1362
1363 packets = self.dst_if.get_capture(len(self.pkt_infos))
1364 self.verify_capture(packets)
1365 self.src_if.assert_nothing_captured()
1366
1367 # run it all again to verify correctness
1368 self.pg_enable_capture()
1369 self.src_if.add_stream(fragments)
1370 self.pg_start()
1371
1372 packets = self.dst_if.get_capture(len(self.pkt_infos))
1373 self.verify_capture(packets)
1374 self.src_if.assert_nothing_captured()
1375
1376 def test_duplicates(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001377 """duplicate fragments"""
Klement Sekera75e7d132017-09-20 08:26:30 +02001378
1379 fragments = [
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001380 x
1381 for (_, frags, _) in self.pkt_infos
Klement Sekera75e7d132017-09-20 08:26:30 +02001382 for x in frags
1383 for _ in range(0, min(2, len(frags)))
1384 ]
Klement Sekera947a85c2019-07-24 12:40:37 +00001385
1386 self.pg_enable_capture()
1387 self.src_if.add_stream(fragments)
1388 self.pg_start()
1389
1390 packets = self.dst_if.get_capture(len(self.pkt_infos))
1391 self.verify_capture(packets)
1392 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +02001393
Klement Sekera3a343d42019-05-16 14:35:46 +02001394 def test_long_fragment_chain(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001395 """long fragment chain"""
Klement Sekera3a343d42019-05-16 14:35:46 +02001396
Neale Rannse22a7042022-08-09 03:03:29 +00001397 error_cnt_str = "/err/ip6-full-reassembly-feature/reass_fragment_chain_too_long"
Klement Sekera3a343d42019-05-16 14:35:46 +02001398
Klement Sekera34641f22019-05-22 20:18:26 +02001399 error_cnt = self.statistics.get_err_counter(error_cnt_str)
Klement Sekera3a343d42019-05-16 14:35:46 +02001400
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001401 self.vapi.ip_reassembly_set(
1402 timeout_ms=100,
1403 max_reassemblies=1000,
1404 max_reassembly_length=3,
1405 expire_walk_interval_ms=50,
1406 is_ip6=1,
1407 )
Klement Sekera3a343d42019-05-16 14:35:46 +02001408
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001409 p = (
1410 Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac)
1411 / IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6)
1412 / UDP(sport=1234, dport=5678)
1413 / Raw(b"X" * 1000)
1414 )
Klement Sekera3a343d42019-05-16 14:35:46 +02001415 frags = fragment_rfc8200(p, 1, 300) + fragment_rfc8200(p, 2, 500)
1416
1417 self.pg_enable_capture()
1418 self.src_if.add_stream(frags)
1419 self.pg_start()
1420
1421 self.dst_if.get_capture(1)
Klement Sekera34641f22019-05-22 20:18:26 +02001422 self.assert_error_counter_equal(error_cnt_str, error_cnt + 1)
Klement Sekera3a343d42019-05-16 14:35:46 +02001423
Klement Sekera75e7d132017-09-20 08:26:30 +02001424 def test_overlap1(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001425 """overlapping fragments case #1"""
Klement Sekera75e7d132017-09-20 08:26:30 +02001426
1427 fragments = []
1428 for _, frags_400, frags_300 in self.pkt_infos:
1429 if len(frags_300) == 1:
1430 fragments.extend(frags_400)
1431 else:
1432 for i, j in zip(frags_300, frags_400):
1433 fragments.extend(i)
1434 fragments.extend(j)
1435
1436 dropped_packet_indexes = set(
1437 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
1438 )
1439
1440 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001441 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001442 self.pg_start()
1443
Klement Sekera4c533132018-02-22 11:41:12 +01001444 packets = self.dst_if.get_capture(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001445 len(self.pkt_infos) - len(dropped_packet_indexes)
1446 )
Klement Sekera947a85c2019-07-24 12:40:37 +00001447 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001448 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +02001449
1450 def test_overlap2(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001451 """overlapping fragments case #2"""
Klement Sekera75e7d132017-09-20 08:26:30 +02001452
1453 fragments = []
Klement Sekera4c533132018-02-22 11:41:12 +01001454 for _, frags_400, frags_300 in self.pkt_infos:
Klement Sekera75e7d132017-09-20 08:26:30 +02001455 if len(frags_400) == 1:
1456 fragments.extend(frags_400)
1457 else:
1458 # care must be taken here so that there are no fragments
1459 # received by vpp after reassembly is finished, otherwise
1460 # new reassemblies will be started and packet generator will
1461 # freak out when it detects unfreed buffers
Klement Sekera4c533132018-02-22 11:41:12 +01001462 zipped = zip(frags_400, frags_300)
Paul Vinciguerrac7834e02019-03-02 10:43:05 -08001463 for i, j in zipped:
Klement Sekera75e7d132017-09-20 08:26:30 +02001464 fragments.extend(i)
1465 fragments.extend(j)
Paul Vinciguerrac7834e02019-03-02 10:43:05 -08001466 fragments.pop()
Klement Sekera75e7d132017-09-20 08:26:30 +02001467
1468 dropped_packet_indexes = set(
1469 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
1470 )
1471
1472 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001473 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001474 self.pg_start()
1475
Klement Sekera4c533132018-02-22 11:41:12 +01001476 packets = self.dst_if.get_capture(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001477 len(self.pkt_infos) - len(dropped_packet_indexes)
1478 )
Klement Sekera947a85c2019-07-24 12:40:37 +00001479 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001480 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +02001481
Klement Sekera947a85c2019-07-24 12:40:37 +00001482 def test_timeout_inline(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001483 """timeout (inline)"""
Klement Sekera75e7d132017-09-20 08:26:30 +02001484
1485 dropped_packet_indexes = set(
1486 index for (index, frags, _) in self.pkt_infos if len(frags) > 1
1487 )
1488
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001489 self.vapi.ip_reassembly_set(
1490 timeout_ms=0,
1491 max_reassemblies=1000,
1492 max_reassembly_length=3,
1493 expire_walk_interval_ms=10000,
1494 is_ip6=1,
1495 )
Klement Sekera947a85c2019-07-24 12:40:37 +00001496
1497 self.pg_enable_capture()
1498 self.src_if.add_stream(self.fragments_400)
1499 self.pg_start()
1500
1501 packets = self.dst_if.get_capture(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001502 len(self.pkt_infos) - len(dropped_packet_indexes)
1503 )
Klement Sekera947a85c2019-07-24 12:40:37 +00001504 self.verify_capture(packets, dropped_packet_indexes)
Neale Ranns5c6dd172022-02-17 09:08:47 +00001505 pkts = self.src_if._get_capture(1)
Klement Sekera75e7d132017-09-20 08:26:30 +02001506 for icmp in pkts:
1507 self.assertIn(ICMPv6TimeExceeded, icmp)
1508 self.assertIn(IPv6ExtHdrFragment, icmp)
1509 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
1510 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
1511
1512 def test_timeout_cleanup(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001513 """timeout (cleanup)"""
Klement Sekera75e7d132017-09-20 08:26:30 +02001514
1515 # whole packets + fragmented packets sans last fragment
1516 fragments = [
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001517 x
1518 for (_, frags_400, _) in self.pkt_infos
1519 for x in frags_400[: -1 if len(frags_400) > 1 else None]
Klement Sekera75e7d132017-09-20 08:26:30 +02001520 ]
1521
1522 # last fragments for fragmented packets
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001523 fragments2 = [
1524 frags_400[-1] for (_, frags_400, _) in self.pkt_infos if len(frags_400) > 1
1525 ]
Klement Sekera75e7d132017-09-20 08:26:30 +02001526
1527 dropped_packet_indexes = set(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001528 index for (index, frags_400, _) in self.pkt_infos if len(frags_400) > 1
1529 )
Klement Sekera75e7d132017-09-20 08:26:30 +02001530
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001531 self.vapi.ip_reassembly_set(
1532 timeout_ms=100,
1533 max_reassemblies=1000,
1534 max_reassembly_length=1000,
1535 expire_walk_interval_ms=50,
1536 )
Klement Sekera75e7d132017-09-20 08:26:30 +02001537
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001538 self.vapi.ip_reassembly_set(
1539 timeout_ms=100,
1540 max_reassemblies=1000,
1541 max_reassembly_length=1000,
1542 expire_walk_interval_ms=50,
1543 is_ip6=1,
1544 )
Klement Sekera75e7d132017-09-20 08:26:30 +02001545
1546 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001547 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001548 self.pg_start()
1549
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001550 self.virtual_sleep(0.25, "wait before sending rest of fragments")
Klement Sekera75e7d132017-09-20 08:26:30 +02001551
Klement Sekera4c533132018-02-22 11:41:12 +01001552 self.src_if.add_stream(fragments2)
Klement Sekera75e7d132017-09-20 08:26:30 +02001553 self.pg_start()
Klement Sekera75e7d132017-09-20 08:26:30 +02001554
Klement Sekera4c533132018-02-22 11:41:12 +01001555 packets = self.dst_if.get_capture(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001556 len(self.pkt_infos) - len(dropped_packet_indexes)
1557 )
Klement Sekera947a85c2019-07-24 12:40:37 +00001558 self.verify_capture(packets, dropped_packet_indexes)
Neale Ranns5c6dd172022-02-17 09:08:47 +00001559 pkts = self.src_if._get_capture(1)
Klement Sekera75e7d132017-09-20 08:26:30 +02001560 for icmp in pkts:
1561 self.assertIn(ICMPv6TimeExceeded, icmp)
1562 self.assertIn(IPv6ExtHdrFragment, icmp)
1563 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
1564 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
1565
Klement Sekera947a85c2019-07-24 12:40:37 +00001566 def test_disabled(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001567 """reassembly disabled"""
Klement Sekera75e7d132017-09-20 08:26:30 +02001568
1569 dropped_packet_indexes = set(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001570 index for (index, frags_400, _) in self.pkt_infos if len(frags_400) > 1
1571 )
Klement Sekera947a85c2019-07-24 12:40:37 +00001572
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001573 self.vapi.ip_reassembly_set(
1574 timeout_ms=1000,
1575 max_reassemblies=0,
1576 max_reassembly_length=3,
1577 expire_walk_interval_ms=10000,
1578 is_ip6=1,
1579 )
Klement Sekera947a85c2019-07-24 12:40:37 +00001580
1581 self.pg_enable_capture()
1582 self.src_if.add_stream(self.fragments_400)
1583 self.pg_start()
1584
1585 packets = self.dst_if.get_capture(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001586 len(self.pkt_infos) - len(dropped_packet_indexes)
1587 )
Klement Sekera947a85c2019-07-24 12:40:37 +00001588 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001589 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +02001590
1591 def test_missing_upper(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001592 """missing upper layer"""
1593 optdata = "\x00" * 100
1594 p = (
1595 Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac)
1596 / IPv6(src=self.src_if.remote_ip6, dst=self.src_if.local_ip6)
1597 / IPv6ExtHdrFragment(m=1)
1598 / IPv6ExtHdrDestOpt(
1599 nh=17, options=PadN(optdata="\101" * 255) / PadN(optdata="\102" * 255)
1600 )
1601 )
Ole Troan03092c12021-11-23 15:55:39 +01001602
Klement Sekera75e7d132017-09-20 08:26:30 +02001603 self.pg_enable_capture()
Ole Troan03092c12021-11-23 15:55:39 +01001604 self.src_if.add_stream([p])
Klement Sekera75e7d132017-09-20 08:26:30 +02001605 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +01001606 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +02001607 icmp = pkts[0]
1608 self.assertIn(ICMPv6ParamProblem, icmp)
1609 self.assert_equal(icmp[ICMPv6ParamProblem].code, 3, "ICMP code")
1610
1611 def test_invalid_frag_size(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001612 """fragment size not a multiple of 8"""
1613 p = (
1614 Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac)
1615 / IPv6(src=self.src_if.remote_ip6, dst=self.src_if.local_ip6)
1616 / UDP(sport=1234, dport=5678)
1617 / Raw()
1618 )
Klement Sekera75e7d132017-09-20 08:26:30 +02001619 self.extend_packet(p, 1000, self.padding)
1620 fragments = fragment_rfc8200(p, 1, 500)
1621 bad_fragment = fragments[0]
1622 self.extend_packet(bad_fragment, len(bad_fragment) + 5)
1623 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001624 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +02001625 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +01001626 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +02001627 icmp = pkts[0]
1628 self.assertIn(ICMPv6ParamProblem, icmp)
1629 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1630
1631 def test_invalid_packet_size(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001632 """total packet size > 65535"""
1633 p = (
1634 Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac)
1635 / IPv6(src=self.src_if.remote_ip6, dst=self.src_if.local_ip6)
1636 / UDP(sport=1234, dport=5678)
1637 / Raw()
1638 )
Klement Sekera75e7d132017-09-20 08:26:30 +02001639 self.extend_packet(p, 1000, self.padding)
1640 fragments = fragment_rfc8200(p, 1, 500)
1641 bad_fragment = fragments[1]
1642 bad_fragment[IPv6ExtHdrFragment].offset = 65500
1643 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001644 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +02001645 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +01001646 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +02001647 icmp = pkts[0]
1648 self.assertIn(ICMPv6ParamProblem, icmp)
1649 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1650
Ole Troan03092c12021-11-23 15:55:39 +01001651 def test_atomic_fragment(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001652 """IPv6 atomic fragment"""
1653 pkt = (
Steven Luonge4238aa2024-04-19 09:49:20 -07001654 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001655 / IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6, nh=44, plen=65535)
1656 / IPv6ExtHdrFragment(
1657 offset=8191, m=1, res1=0xFF, res2=0xFF, nh=255, id=0xFFFF
1658 )
1659 / ("X" * 1452)
1660 )
Ole Troan03092c12021-11-23 15:55:39 +01001661
1662 rx = self.send_and_expect(self.pg0, [pkt], self.pg0)
1663 self.assertIn(ICMPv6ParamProblem, rx[0])
1664
1665 def test_truncated_fragment(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001666 """IPv6 truncated fragment header"""
1667 pkt = (
1668 Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac)
1669 / IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6, nh=44, plen=2)
1670 / IPv6ExtHdrFragment(nh=6)
1671 )
Ole Troan03092c12021-11-23 15:55:39 +01001672
Klement Sekera7c3275e2021-12-07 09:49:53 +00001673 self.send_and_assert_no_replies(self.pg0, [pkt])
Ole Troan03092c12021-11-23 15:55:39 +01001674
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001675 pkt = (
Steven Luonge4238aa2024-04-19 09:49:20 -07001676 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001677 / IPv6(src=self.pg0.remote_ip6, dst=self.pg0.remote_ip6)
1678 / ICMPv6EchoRequest()
1679 )
Ole Troan03092c12021-11-23 15:55:39 +01001680 rx = self.send_and_expect(self.pg0, [pkt], self.pg0)
1681
Dmitry Valter34fa0ce2024-03-11 10:38:46 +00001682 @unittest.skipIf(
1683 "ping" in config.excluded_plugins, "Exclude tests requiring Ping plugin"
1684 )
Klement Sekera7c3275e2021-12-07 09:49:53 +00001685 def test_one_fragment(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001686 """whole packet in one fragment processed independently"""
1687 pkt = (
Steven Luonge4238aa2024-04-19 09:49:20 -07001688 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001689 / IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6)
1690 / ICMPv6EchoRequest()
1691 / Raw("X" * 1600)
1692 )
Klement Sekera7c3275e2021-12-07 09:49:53 +00001693 frags = fragment_rfc8200(pkt, 1, 400)
1694
1695 # send a fragment with known id
1696 self.send_and_assert_no_replies(self.pg0, [frags[0]])
1697
1698 # send an atomic fragment with same id - should be reassembled
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001699 pkt = (
Steven Luonge4238aa2024-04-19 09:49:20 -07001700 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001701 / IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6)
1702 / IPv6ExtHdrFragment(id=1)
1703 / ICMPv6EchoRequest()
1704 )
Klement Sekera7c3275e2021-12-07 09:49:53 +00001705 rx = self.send_and_expect(self.pg0, [pkt], self.pg0)
1706 self.assertNotIn(IPv6ExtHdrFragment, rx)
1707
1708 # now finish the original reassembly, this should still be possible
1709 rx = self.send_and_expect(self.pg0, frags[1:], self.pg0, n_rx=1)
1710 self.assertNotIn(IPv6ExtHdrFragment, rx)
1711
Dmitry Valter34fa0ce2024-03-11 10:38:46 +00001712 @unittest.skipIf(
1713 "ping" in config.excluded_plugins, "Exclude tests requiring Ping plugin"
1714 )
Klement Sekera7c3275e2021-12-07 09:49:53 +00001715 def test_bunch_of_fragments(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001716 """valid fragments followed by rogue fragments and atomic fragment"""
1717 pkt = (
Steven Luonge4238aa2024-04-19 09:49:20 -07001718 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001719 / IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6)
1720 / ICMPv6EchoRequest()
1721 / Raw("X" * 1600)
1722 )
Klement Sekera7c3275e2021-12-07 09:49:53 +00001723 frags = fragment_rfc8200(pkt, 1, 400)
1724 self.send_and_expect(self.pg0, frags, self.pg0, n_rx=1)
1725
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001726 inc_frag = (
1727 Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac)
1728 / IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6)
1729 / IPv6ExtHdrFragment(id=1, nh=58, offset=608)
1730 / Raw("X" * 308)
1731 )
Klement Sekera7c3275e2021-12-07 09:49:53 +00001732
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001733 self.send_and_assert_no_replies(self.pg0, inc_frag * 604)
Klement Sekera7c3275e2021-12-07 09:49:53 +00001734
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001735 pkt = (
Steven Luonge4238aa2024-04-19 09:49:20 -07001736 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001737 / IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6)
1738 / IPv6ExtHdrFragment(id=1)
1739 / ICMPv6EchoRequest()
1740 )
Klement Sekera7c3275e2021-12-07 09:49:53 +00001741 rx = self.send_and_expect(self.pg0, [pkt], self.pg0)
1742 self.assertNotIn(IPv6ExtHdrFragment, rx)
1743
Dmitry Valter34fa0ce2024-03-11 10:38:46 +00001744 @unittest.skipIf(
1745 "ping" in config.excluded_plugins, "Exclude tests requiring Ping plugin"
1746 )
Klement Sekera01c1fa42021-12-14 18:25:11 +00001747 def test_local_enable_disable(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001748 """local reassembly enabled/disable"""
Klement Sekera01c1fa42021-12-14 18:25:11 +00001749 self.vapi.ip_reassembly_enable_disable(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001750 sw_if_index=self.src_if.sw_if_index, enable_ip6=False
1751 )
Klement Sekera01c1fa42021-12-14 18:25:11 +00001752 self.vapi.ip_local_reass_enable_disable(enable_ip6=True)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001753 pkt = (
Steven Luonge4238aa2024-04-19 09:49:20 -07001754 Ether(src=self.src_if.remote_mac, dst=self.src_if.local_mac)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001755 / IPv6(src=self.src_if.remote_ip6, dst=self.src_if.local_ip6)
1756 / ICMPv6EchoRequest(id=1234)
1757 / Raw("X" * 1600)
1758 )
Klement Sekera01c1fa42021-12-14 18:25:11 +00001759 frags = fragment_rfc8200(pkt, 1, 400)
1760 r = self.send_and_expect(self.src_if, frags, self.src_if, n_rx=1)[0]
1761 self.assertEqual(1234, r[ICMPv6EchoReply].id)
1762 self.vapi.ip_local_reass_enable_disable()
1763
1764 self.send_and_assert_no_replies(self.src_if, frags)
1765 self.vapi.ip_local_reass_enable_disable(enable_ip6=True)
1766
Klement Sekera75e7d132017-09-20 08:26:30 +02001767
Klement Sekera630ab582019-07-19 09:14:19 +00001768class TestIPv6MWReassembly(VppTestCase):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001769 """IPv6 Reassembly (multiple workers)"""
1770
Klement Sekera8d815022021-03-15 16:58:10 +01001771 vpp_worker_count = 3
Klement Sekera630ab582019-07-19 09:14:19 +00001772
1773 @classmethod
1774 def setUpClass(cls):
Klement Sekera01c1fa42021-12-14 18:25:11 +00001775 super().setUpClass()
Klement Sekera630ab582019-07-19 09:14:19 +00001776
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001777 cls.create_pg_interfaces(range(cls.vpp_worker_count + 1))
Klement Sekera630ab582019-07-19 09:14:19 +00001778 cls.src_if = cls.pg0
1779 cls.send_ifs = cls.pg_interfaces[:-1]
1780 cls.dst_if = cls.pg_interfaces[-1]
1781
1782 # setup all interfaces
1783 for i in cls.pg_interfaces:
1784 i.admin_up()
1785 i.config_ip6()
1786 i.resolve_ndp()
1787
1788 # packets sizes reduced here because we are generating packets without
1789 # Ethernet headers, which are added later (diff fragments go via
1790 # different interfaces)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001791 cls.packet_sizes = [
1792 64 - len(Ether()),
1793 512 - len(Ether()),
1794 1518 - len(Ether()),
1795 9018 - len(Ether()),
1796 ]
Klement Sekera630ab582019-07-19 09:14:19 +00001797 cls.padding = " abcdefghijklmn"
1798 cls.create_stream(cls.packet_sizes)
1799 cls.create_fragments()
1800
1801 @classmethod
1802 def tearDownClass(cls):
Klement Sekera01c1fa42021-12-14 18:25:11 +00001803 super().tearDownClass()
Klement Sekera630ab582019-07-19 09:14:19 +00001804
1805 def setUp(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001806 """Test setup - force timeout on existing reassemblies"""
Klement Sekera01c1fa42021-12-14 18:25:11 +00001807 super().setUp()
Klement Sekera630ab582019-07-19 09:14:19 +00001808 for intf in self.send_ifs:
1809 self.vapi.ip_reassembly_enable_disable(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001810 sw_if_index=intf.sw_if_index, enable_ip6=True
1811 )
1812 self.vapi.ip_reassembly_set(
1813 timeout_ms=0,
1814 max_reassemblies=1000,
1815 max_reassembly_length=1000,
1816 expire_walk_interval_ms=10,
1817 is_ip6=1,
1818 )
1819 self.virtual_sleep(0.25)
1820 self.vapi.ip_reassembly_set(
1821 timeout_ms=1000000,
1822 max_reassemblies=1000,
1823 max_reassembly_length=1000,
1824 expire_walk_interval_ms=1000,
1825 is_ip6=1,
1826 )
Klement Sekera630ab582019-07-19 09:14:19 +00001827
1828 def tearDown(self):
Klement Sekera01c1fa42021-12-14 18:25:11 +00001829 for intf in self.send_ifs:
1830 self.vapi.ip_reassembly_enable_disable(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001831 sw_if_index=intf.sw_if_index, enable_ip6=False
1832 )
Klement Sekera01c1fa42021-12-14 18:25:11 +00001833 super().tearDown()
Klement Sekera630ab582019-07-19 09:14:19 +00001834
1835 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +00001836 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
Klement Sekera630ab582019-07-19 09:14:19 +00001837 self.logger.debug(self.vapi.ppcli("show buffers"))
1838
1839 @classmethod
1840 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
1841 """Create input packet stream
1842
1843 :param list packet_sizes: Required packet sizes.
1844 """
1845 for i in range(0, packet_count):
1846 info = cls.create_packet_info(cls.src_if, cls.src_if)
1847 payload = cls.info_to_payload(info)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001848 p = (
1849 IPv6(src=cls.src_if.remote_ip6, dst=cls.dst_if.remote_ip6)
1850 / UDP(sport=1234, dport=5678)
1851 / Raw(payload)
1852 )
Klement Sekera630ab582019-07-19 09:14:19 +00001853 size = packet_sizes[(i // 2) % len(packet_sizes)]
1854 cls.extend_packet(p, size, cls.padding)
1855 info.data = p
1856
1857 @classmethod
1858 def create_fragments(cls):
1859 infos = cls._packet_infos
1860 cls.pkt_infos = []
Paul Vinciguerra090096b2020-12-03 00:42:46 -05001861 for index, info in infos.items():
Klement Sekera630ab582019-07-19 09:14:19 +00001862 p = info.data
1863 # cls.logger.debug(ppp("Packet:",
1864 # p.__class__(scapy.compat.raw(p))))
1865 fragments_400 = fragment_rfc8200(p, index, 400)
1866 cls.pkt_infos.append((index, fragments_400))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001867 cls.fragments_400 = [x for (_, frags) in cls.pkt_infos for x in frags]
1868 cls.logger.debug(
1869 "Fragmented %s packets into %s 400-byte fragments, "
1870 % (len(infos), len(cls.fragments_400))
1871 )
Klement Sekera630ab582019-07-19 09:14:19 +00001872
1873 def verify_capture(self, capture, dropped_packet_indexes=[]):
1874 """Verify captured packet strea .
1875
1876 :param list capture: Captured packet stream.
1877 """
1878 info = None
1879 seen = set()
1880 for packet in capture:
1881 try:
1882 self.logger.debug(ppp("Got packet:", packet))
1883 ip = packet[IPv6]
1884 udp = packet[UDP]
1885 payload_info = self.payload_to_info(packet[Raw])
1886 packet_index = payload_info.index
1887 self.assertTrue(
1888 packet_index not in dropped_packet_indexes,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001889 ppp("Packet received, but should be dropped:", packet),
1890 )
Klement Sekera630ab582019-07-19 09:14:19 +00001891 if packet_index in seen:
1892 raise Exception(ppp("Duplicate packet received", packet))
1893 seen.add(packet_index)
1894 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
1895 info = self._packet_infos[packet_index]
1896 self.assertTrue(info is not None)
1897 self.assertEqual(packet_index, info.index)
1898 saved_packet = info.data
1899 self.assertEqual(ip.src, saved_packet[IPv6].src)
1900 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
1901 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1902 except Exception:
1903 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1904 raise
1905 for index in self._packet_infos:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001906 self.assertTrue(
1907 index in seen or index in dropped_packet_indexes,
1908 "Packet with packet_index %d not received" % index,
1909 )
Klement Sekera630ab582019-07-19 09:14:19 +00001910
1911 def send_packets(self, packets):
Klement Sekera8d815022021-03-15 16:58:10 +01001912 for counter in range(self.vpp_worker_count):
Klement Sekera630ab582019-07-19 09:14:19 +00001913 if 0 == len(packets[counter]):
1914 continue
1915 send_if = self.send_ifs[counter]
1916 send_if.add_stream(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001917 (
1918 Ether(dst=send_if.local_mac, src=send_if.remote_mac) / x
1919 for x in packets[counter]
1920 ),
1921 worker=counter,
1922 )
Klement Sekera630ab582019-07-19 09:14:19 +00001923 self.pg_start()
1924
1925 def test_worker_conflict(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001926 """1st and FO=0 fragments on different workers"""
Klement Sekera630ab582019-07-19 09:14:19 +00001927
1928 # in first wave we send fragments which don't start at offset 0
1929 # then we send fragments with offset 0 on a different thread
1930 # then the rest of packets on a random thread
Klement Sekera8d815022021-03-15 16:58:10 +01001931 first_packets = [[] for n in range(self.vpp_worker_count)]
1932 second_packets = [[] for n in range(self.vpp_worker_count)]
1933 rest_of_packets = [[] for n in range(self.vpp_worker_count)]
Dave Wallace7b8b4652023-08-15 19:05:26 -04001934 for _, p in self.pkt_infos:
Klement Sekera8d815022021-03-15 16:58:10 +01001935 wi = randrange(self.vpp_worker_count)
Klement Sekera630ab582019-07-19 09:14:19 +00001936 second_packets[wi].append(p[0])
1937 if len(p) <= 1:
1938 continue
1939 wi2 = wi
1940 while wi2 == wi:
Klement Sekera8d815022021-03-15 16:58:10 +01001941 wi2 = randrange(self.vpp_worker_count)
Klement Sekera630ab582019-07-19 09:14:19 +00001942 first_packets[wi2].append(p[1])
Klement Sekera8d815022021-03-15 16:58:10 +01001943 wi3 = randrange(self.vpp_worker_count)
Klement Sekera630ab582019-07-19 09:14:19 +00001944 rest_of_packets[wi3].extend(p[2:])
1945
1946 self.pg_enable_capture()
1947 self.send_packets(first_packets)
1948 self.send_packets(second_packets)
1949 self.send_packets(rest_of_packets)
1950
1951 packets = self.dst_if.get_capture(len(self.pkt_infos))
1952 self.verify_capture(packets)
1953 for send_if in self.send_ifs:
1954 send_if.assert_nothing_captured()
1955
Klement Sekera68bae5b2019-10-10 18:57:34 +00001956 self.logger.debug(self.vapi.ppcli("show trace"))
1957 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1958 self.logger.debug(self.vapi.ppcli("show buffers"))
1959 self.vapi.cli("clear trace")
1960
Klement Sekera630ab582019-07-19 09:14:19 +00001961 self.pg_enable_capture()
1962 self.send_packets(first_packets)
1963 self.send_packets(second_packets)
1964 self.send_packets(rest_of_packets)
1965
1966 packets = self.dst_if.get_capture(len(self.pkt_infos))
1967 self.verify_capture(packets)
1968 for send_if in self.send_ifs:
1969 send_if.assert_nothing_captured()
1970
1971
Klement Sekerade34c352019-06-25 11:19:22 +00001972class TestIPv6SVReassembly(VppTestCase):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001973 """IPv6 Shallow Virtual Reassembly"""
Klement Sekerade34c352019-06-25 11:19:22 +00001974
1975 @classmethod
1976 def setUpClass(cls):
Klement Sekera01c1fa42021-12-14 18:25:11 +00001977 super().setUpClass()
Klement Sekerade34c352019-06-25 11:19:22 +00001978
1979 cls.create_pg_interfaces([0, 1])
1980 cls.src_if = cls.pg0
1981 cls.dst_if = cls.pg1
1982
1983 # setup all interfaces
1984 for i in cls.pg_interfaces:
1985 i.admin_up()
1986 i.config_ip6()
1987 i.resolve_ndp()
1988
1989 def setUp(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001990 """Test setup - force timeout on existing reassemblies"""
Klement Sekera01c1fa42021-12-14 18:25:11 +00001991 super().setUp()
Klement Sekerade34c352019-06-25 11:19:22 +00001992 self.vapi.ip_reassembly_enable_disable(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001993 sw_if_index=self.src_if.sw_if_index,
1994 enable_ip6=True,
1995 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1996 )
Klement Sekerade34c352019-06-25 11:19:22 +00001997 self.vapi.ip_reassembly_set(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001998 timeout_ms=0,
1999 max_reassemblies=1000,
Klement Sekerade34c352019-06-25 11:19:22 +00002000 max_reassembly_length=1000,
2001 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002002 expire_walk_interval_ms=10,
2003 is_ip6=1,
2004 )
2005 self.virtual_sleep(0.25)
Klement Sekerade34c352019-06-25 11:19:22 +00002006 self.vapi.ip_reassembly_set(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002007 timeout_ms=1000000,
2008 max_reassemblies=1000,
Klement Sekerade34c352019-06-25 11:19:22 +00002009 max_reassembly_length=1000,
2010 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002011 expire_walk_interval_ms=10000,
2012 is_ip6=1,
2013 )
Klement Sekerade34c352019-06-25 11:19:22 +00002014
2015 def tearDown(self):
Klement Sekera01c1fa42021-12-14 18:25:11 +00002016 super().tearDown()
Klement Sekerade34c352019-06-25 11:19:22 +00002017 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
2018 self.logger.debug(self.vapi.ppcli("show buffers"))
2019
2020 def test_basic(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002021 """basic reassembly"""
Klement Sekerade34c352019-06-25 11:19:22 +00002022 payload_len = 1000
2023 payload = ""
2024 counter = 0
2025 while len(payload) < payload_len:
2026 payload += "%u " % counter
2027 counter += 1
2028
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002029 p = (
2030 Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac)
2031 / IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6)
2032 / UDP(sport=1234, dport=5678)
2033 / Raw(payload)
2034 )
2035 fragments = fragment_rfc8200(p, 1, payload_len / 4)
Klement Sekerade34c352019-06-25 11:19:22 +00002036
2037 # send fragment #2 - should be cached inside reassembly
2038 self.pg_enable_capture()
2039 self.src_if.add_stream(fragments[1])
2040 self.pg_start()
2041 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
2042 self.logger.debug(self.vapi.ppcli("show buffers"))
2043 self.logger.debug(self.vapi.ppcli("show trace"))
2044 self.dst_if.assert_nothing_captured()
2045
2046 # send fragment #1 - reassembly is finished now and both fragments
2047 # forwarded
2048 self.pg_enable_capture()
2049 self.src_if.add_stream(fragments[0])
2050 self.pg_start()
2051 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
2052 self.logger.debug(self.vapi.ppcli("show buffers"))
2053 self.logger.debug(self.vapi.ppcli("show trace"))
2054 c = self.dst_if.get_capture(2)
2055 for sent, recvd in zip([fragments[1], fragments[0]], c):
2056 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
2057 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
2058 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
2059
2060 # send rest of fragments - should be immediately forwarded
2061 self.pg_enable_capture()
2062 self.src_if.add_stream(fragments[2:])
2063 self.pg_start()
2064 c = self.dst_if.get_capture(len(fragments[2:]))
2065 for sent, recvd in zip(fragments[2:], c):
2066 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
2067 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
2068 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
2069
Klement Sekera53be16d2020-12-15 21:47:36 +01002070 def test_verify_clear_trace_mid_reassembly(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002071 """verify clear trace works mid-reassembly"""
Klement Sekera53be16d2020-12-15 21:47:36 +01002072 payload_len = 1000
2073 payload = ""
2074 counter = 0
2075 while len(payload) < payload_len:
2076 payload += "%u " % counter
2077 counter += 1
2078
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002079 p = (
2080 Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac)
2081 / IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6)
2082 / UDP(sport=1234, dport=5678)
2083 / Raw(payload)
2084 )
2085 fragments = fragment_rfc8200(p, 1, payload_len / 4)
Klement Sekera53be16d2020-12-15 21:47:36 +01002086
2087 self.pg_enable_capture()
2088 self.src_if.add_stream(fragments[1])
2089 self.pg_start()
2090
2091 self.logger.debug(self.vapi.cli("show trace"))
2092 self.vapi.cli("clear trace")
2093
2094 self.pg_enable_capture()
2095 self.src_if.add_stream(fragments[0])
2096 self.pg_start()
2097 self.dst_if.get_capture(2)
2098
2099 self.logger.debug(self.vapi.cli("show trace"))
2100 self.vapi.cli("clear trace")
2101
2102 self.pg_enable_capture()
2103 self.src_if.add_stream(fragments[2:])
2104 self.pg_start()
2105 self.dst_if.get_capture(len(fragments[2:]))
2106
Klement Sekerade34c352019-06-25 11:19:22 +00002107 def test_timeout(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002108 """reassembly timeout"""
Klement Sekerade34c352019-06-25 11:19:22 +00002109 payload_len = 1000
2110 payload = ""
2111 counter = 0
2112 while len(payload) < payload_len:
2113 payload += "%u " % counter
2114 counter += 1
2115
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002116 p = (
2117 Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac)
2118 / IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6)
2119 / UDP(sport=1234, dport=5678)
2120 / Raw(payload)
2121 )
2122 fragments = fragment_rfc8200(p, 1, payload_len / 4)
Klement Sekerade34c352019-06-25 11:19:22 +00002123
2124 self.vapi.ip_reassembly_set(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002125 timeout_ms=100,
2126 max_reassemblies=1000,
Klement Sekerade34c352019-06-25 11:19:22 +00002127 max_reassembly_length=1000,
2128 expire_walk_interval_ms=50,
2129 is_ip6=1,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002130 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
2131 )
Klement Sekerade34c352019-06-25 11:19:22 +00002132
2133 # send fragments #2 and #1 - should be forwarded
2134 self.pg_enable_capture()
2135 self.src_if.add_stream(fragments[0:2])
2136 self.pg_start()
2137 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
2138 self.logger.debug(self.vapi.ppcli("show buffers"))
2139 self.logger.debug(self.vapi.ppcli("show trace"))
2140 c = self.dst_if.get_capture(2)
2141 for sent, recvd in zip([fragments[1], fragments[0]], c):
2142 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
2143 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
2144 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
2145
2146 # wait for cleanup
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002147 self.virtual_sleep(0.25, "wait before sending rest of fragments")
Klement Sekerade34c352019-06-25 11:19:22 +00002148
2149 # send rest of fragments - shouldn't be forwarded
2150 self.pg_enable_capture()
2151 self.src_if.add_stream(fragments[2:])
2152 self.pg_start()
2153 self.dst_if.assert_nothing_captured()
2154
2155 def test_lru(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002156 """reassembly reuses LRU element"""
Klement Sekerade34c352019-06-25 11:19:22 +00002157
2158 self.vapi.ip_reassembly_set(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002159 timeout_ms=1000000,
2160 max_reassemblies=1,
Klement Sekerade34c352019-06-25 11:19:22 +00002161 max_reassembly_length=1000,
2162 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002163 is_ip6=1,
2164 expire_walk_interval_ms=10000,
2165 )
Klement Sekerade34c352019-06-25 11:19:22 +00002166
2167 payload_len = 1000
2168 payload = ""
2169 counter = 0
2170 while len(payload) < payload_len:
2171 payload += "%u " % counter
2172 counter += 1
2173
2174 packet_count = 10
2175
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002176 fragments = [
2177 f
2178 for i in range(packet_count)
2179 for p in (
2180 Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac)
2181 / IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6)
2182 / UDP(sport=1234, dport=5678)
2183 / Raw(payload)
2184 )
2185 for f in fragment_rfc8200(p, i, payload_len / 4)
2186 ]
Klement Sekerade34c352019-06-25 11:19:22 +00002187
2188 self.pg_enable_capture()
2189 self.src_if.add_stream(fragments)
2190 self.pg_start()
2191 c = self.dst_if.get_capture(len(fragments))
2192 for sent, recvd in zip(fragments, c):
2193 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
2194 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
2195 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
2196
Klement Sekera7c3275e2021-12-07 09:49:53 +00002197 def test_one_fragment(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002198 """whole packet in one fragment processed independently"""
2199 pkt = (
Steven Luonge4238aa2024-04-19 09:49:20 -07002200 Ether(src=self.src_if.remote_mac, dst=self.src_if.local_mac)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002201 / IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6)
2202 / ICMPv6EchoRequest()
2203 / Raw("X" * 1600)
2204 )
Klement Sekera7c3275e2021-12-07 09:49:53 +00002205 frags = fragment_rfc8200(pkt, 1, 400)
2206
2207 # send a fragment with known id
2208 self.send_and_expect(self.src_if, [frags[0]], self.dst_if)
2209
2210 # send an atomic fragment with same id - should be reassembled
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002211 pkt = (
Steven Luonge4238aa2024-04-19 09:49:20 -07002212 Ether(src=self.src_if.remote_mac, dst=self.src_if.local_mac)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002213 / IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6)
2214 / IPv6ExtHdrFragment(id=1)
2215 / ICMPv6EchoRequest()
2216 )
Klement Sekera7c3275e2021-12-07 09:49:53 +00002217 rx = self.send_and_expect(self.src_if, [pkt], self.dst_if)
2218
2219 # now forward packets matching original reassembly, should still work
2220 rx = self.send_and_expect(self.src_if, frags[1:], self.dst_if)
2221
2222 def test_bunch_of_fragments(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002223 """valid fragments followed by rogue fragments and atomic fragment"""
2224 pkt = (
Steven Luonge4238aa2024-04-19 09:49:20 -07002225 Ether(src=self.src_if.remote_mac, dst=self.src_if.local_mac)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002226 / IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6)
2227 / ICMPv6EchoRequest()
2228 / Raw("X" * 1600)
2229 )
Klement Sekera7c3275e2021-12-07 09:49:53 +00002230 frags = fragment_rfc8200(pkt, 1, 400)
2231 rx = self.send_and_expect(self.src_if, frags, self.dst_if)
2232
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002233 rogue = (
Steven Luonge4238aa2024-04-19 09:49:20 -07002234 Ether(src=self.src_if.remote_mac, dst=self.src_if.local_mac)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002235 / IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6)
2236 / IPv6ExtHdrFragment(id=1, nh=58, offset=608)
2237 / Raw("X" * 308)
2238 )
Klement Sekera7c3275e2021-12-07 09:49:53 +00002239
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002240 self.send_and_expect(self.src_if, rogue * 604, self.dst_if)
Klement Sekera7c3275e2021-12-07 09:49:53 +00002241
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002242 pkt = (
Steven Luonge4238aa2024-04-19 09:49:20 -07002243 Ether(src=self.src_if.remote_mac, dst=self.src_if.local_mac)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002244 / IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6)
2245 / IPv6ExtHdrFragment(id=1)
2246 / ICMPv6EchoRequest()
2247 )
Klement Sekera7c3275e2021-12-07 09:49:53 +00002248 rx = self.send_and_expect(self.src_if, [pkt], self.dst_if)
2249
Klement Sekera755042d2021-12-01 10:14:38 +00002250 def test_truncated_fragment(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002251 """truncated fragment"""
2252 pkt = (
2253 Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac)
2254 / IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6, nh=44, plen=2)
2255 / IPv6ExtHdrFragment(nh=6)
2256 )
Klement Sekera755042d2021-12-01 10:14:38 +00002257
2258 self.send_and_assert_no_replies(self.pg0, [pkt], self.pg0)
2259
Klement Sekerade34c352019-06-25 11:19:22 +00002260
Juraj Sloboda3048b632018-10-02 11:13:53 +02002261class TestIPv4ReassemblyLocalNode(VppTestCase):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002262 """IPv4 Reassembly for packets coming to ip4-local node"""
Juraj Sloboda3048b632018-10-02 11:13:53 +02002263
2264 @classmethod
2265 def setUpClass(cls):
Klement Sekera01c1fa42021-12-14 18:25:11 +00002266 super().setUpClass()
Juraj Sloboda3048b632018-10-02 11:13:53 +02002267
2268 cls.create_pg_interfaces([0])
2269 cls.src_dst_if = cls.pg0
2270
2271 # setup all interfaces
2272 for i in cls.pg_interfaces:
2273 i.admin_up()
2274 i.config_ip4()
2275 i.resolve_arp()
2276
2277 cls.padding = " abcdefghijklmn"
2278 cls.create_stream()
2279 cls.create_fragments()
2280
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -07002281 @classmethod
2282 def tearDownClass(cls):
Klement Sekera01c1fa42021-12-14 18:25:11 +00002283 super().tearDownClass()
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -07002284
Juraj Sloboda3048b632018-10-02 11:13:53 +02002285 def setUp(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002286 """Test setup - force timeout on existing reassemblies"""
Klement Sekera01c1fa42021-12-14 18:25:11 +00002287 super().setUp()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002288 self.vapi.ip_reassembly_set(
2289 timeout_ms=0,
2290 max_reassemblies=1000,
2291 max_reassembly_length=1000,
2292 expire_walk_interval_ms=10,
2293 )
2294 self.virtual_sleep(0.25)
2295 self.vapi.ip_reassembly_set(
2296 timeout_ms=1000000,
2297 max_reassemblies=1000,
2298 max_reassembly_length=1000,
2299 expire_walk_interval_ms=10000,
2300 )
Juraj Sloboda3048b632018-10-02 11:13:53 +02002301
2302 def tearDown(self):
Klement Sekera01c1fa42021-12-14 18:25:11 +00002303 super().tearDown()
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -07002304
2305 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +00002306 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +01002307 self.logger.debug(self.vapi.ppcli("show buffers"))
Juraj Sloboda3048b632018-10-02 11:13:53 +02002308
2309 @classmethod
2310 def create_stream(cls, packet_count=test_packet_count):
2311 """Create input packet stream for defined interface.
2312
2313 :param list packet_sizes: Required packet sizes.
2314 """
2315 for i in range(0, packet_count):
2316 info = cls.create_packet_info(cls.src_dst_if, cls.src_dst_if)
2317 payload = cls.info_to_payload(info)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002318 p = (
2319 Ether(dst=cls.src_dst_if.local_mac, src=cls.src_dst_if.remote_mac)
2320 / IP(
2321 id=info.index,
2322 src=cls.src_dst_if.remote_ip4,
2323 dst=cls.src_dst_if.local_ip4,
2324 )
2325 / ICMP(type="echo-request", id=1234)
2326 / Raw(payload)
2327 )
Juraj Sloboda3048b632018-10-02 11:13:53 +02002328 cls.extend_packet(p, 1518, cls.padding)
2329 info.data = p
2330
2331 @classmethod
2332 def create_fragments(cls):
2333 infos = cls._packet_infos
2334 cls.pkt_infos = []
Paul Vinciguerra090096b2020-12-03 00:42:46 -05002335 for index, info in infos.items():
Juraj Sloboda3048b632018-10-02 11:13:53 +02002336 p = info.data
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07002337 # cls.logger.debug(ppp("Packet:",
2338 # p.__class__(scapy.compat.raw(p))))
Juraj Sloboda3048b632018-10-02 11:13:53 +02002339 fragments_300 = fragment_rfc791(p, 300)
2340 cls.pkt_infos.append((index, fragments_300))
2341 cls.fragments_300 = [x for (_, frags) in cls.pkt_infos for x in frags]
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002342 cls.logger.debug(
2343 "Fragmented %s packets into %s 300-byte fragments"
2344 % (len(infos), len(cls.fragments_300))
2345 )
Juraj Sloboda3048b632018-10-02 11:13:53 +02002346
2347 def verify_capture(self, capture):
2348 """Verify captured packet stream.
2349
2350 :param list capture: Captured packet stream.
2351 """
2352 info = None
2353 seen = set()
2354 for packet in capture:
2355 try:
2356 self.logger.debug(ppp("Got packet:", packet))
2357 ip = packet[IP]
2358 icmp = packet[ICMP]
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08002359 payload_info = self.payload_to_info(packet[Raw])
Juraj Sloboda3048b632018-10-02 11:13:53 +02002360 packet_index = payload_info.index
2361 if packet_index in seen:
2362 raise Exception(ppp("Duplicate packet received", packet))
2363 seen.add(packet_index)
2364 self.assertEqual(payload_info.dst, self.src_dst_if.sw_if_index)
2365 info = self._packet_infos[packet_index]
Klement Sekera14d7e902018-12-10 13:46:09 +01002366 self.assertIsNotNone(info)
Juraj Sloboda3048b632018-10-02 11:13:53 +02002367 self.assertEqual(packet_index, info.index)
2368 saved_packet = info.data
2369 self.assertEqual(ip.src, saved_packet[IP].dst)
2370 self.assertEqual(ip.dst, saved_packet[IP].src)
2371 self.assertEqual(icmp.type, 0) # echo reply
2372 self.assertEqual(icmp.id, saved_packet[ICMP].id)
2373 self.assertEqual(icmp.payload, saved_packet[ICMP].payload)
2374 except Exception:
2375 self.logger.error(ppp("Unexpected or invalid packet:", packet))
2376 raise
2377 for index in self._packet_infos:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002378 self.assertIn(
2379 index, seen, "Packet with packet_index %d not received" % index
2380 )
Juraj Sloboda3048b632018-10-02 11:13:53 +02002381
Dmitry Valter34fa0ce2024-03-11 10:38:46 +00002382 @unittest.skipIf(
2383 "ping" in config.excluded_plugins, "Exclude tests requiring Ping plugin"
2384 )
Juraj Sloboda3048b632018-10-02 11:13:53 +02002385 def test_reassembly(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002386 """basic reassembly"""
Juraj Sloboda3048b632018-10-02 11:13:53 +02002387
2388 self.pg_enable_capture()
2389 self.src_dst_if.add_stream(self.fragments_300)
2390 self.pg_start()
2391
2392 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
2393 self.verify_capture(packets)
2394
2395 # run it all again to verify correctness
2396 self.pg_enable_capture()
2397 self.src_dst_if.add_stream(self.fragments_300)
2398 self.pg_start()
2399
2400 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
2401 self.verify_capture(packets)
2402
2403
Klement Sekera75e7d132017-09-20 08:26:30 +02002404class TestFIFReassembly(VppTestCase):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002405 """Fragments in fragments reassembly"""
Klement Sekera75e7d132017-09-20 08:26:30 +02002406
2407 @classmethod
2408 def setUpClass(cls):
Klement Sekera01c1fa42021-12-14 18:25:11 +00002409 super().setUpClass()
Klement Sekera75e7d132017-09-20 08:26:30 +02002410
Klement Sekera4c533132018-02-22 11:41:12 +01002411 cls.create_pg_interfaces([0, 1])
2412 cls.src_if = cls.pg0
2413 cls.dst_if = cls.pg1
2414 for i in cls.pg_interfaces:
2415 i.admin_up()
2416 i.config_ip4()
2417 i.resolve_arp()
2418 i.config_ip6()
2419 i.resolve_ndp()
Klement Sekera75e7d132017-09-20 08:26:30 +02002420
Klement Sekera75e7d132017-09-20 08:26:30 +02002421 cls.packet_sizes = [64, 512, 1518, 9018]
2422 cls.padding = " abcdefghijklmn"
2423
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -07002424 @classmethod
2425 def tearDownClass(cls):
Klement Sekera01c1fa42021-12-14 18:25:11 +00002426 super().tearDownClass()
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -07002427
Klement Sekera75e7d132017-09-20 08:26:30 +02002428 def setUp(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002429 """Test setup - force timeout on existing reassemblies"""
Klement Sekera01c1fa42021-12-14 18:25:11 +00002430 super().setUp()
Klement Sekera4c533132018-02-22 11:41:12 +01002431 self.vapi.ip_reassembly_enable_disable(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002432 sw_if_index=self.src_if.sw_if_index, enable_ip4=True, enable_ip6=True
2433 )
Klement Sekera4c533132018-02-22 11:41:12 +01002434 self.vapi.ip_reassembly_enable_disable(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002435 sw_if_index=self.dst_if.sw_if_index, enable_ip4=True, enable_ip6=True
2436 )
2437 self.vapi.ip_reassembly_set(
2438 timeout_ms=0,
2439 max_reassemblies=1000,
2440 max_reassembly_length=1000,
2441 expire_walk_interval_ms=10,
2442 )
2443 self.vapi.ip_reassembly_set(
2444 timeout_ms=0,
2445 max_reassemblies=1000,
2446 max_reassembly_length=1000,
2447 expire_walk_interval_ms=10,
2448 is_ip6=1,
2449 )
2450 self.virtual_sleep(0.25)
2451 self.vapi.ip_reassembly_set(
2452 timeout_ms=1000000,
2453 max_reassemblies=1000,
2454 max_reassembly_length=1000,
2455 expire_walk_interval_ms=10000,
2456 )
2457 self.vapi.ip_reassembly_set(
2458 timeout_ms=1000000,
2459 max_reassemblies=1000,
2460 max_reassembly_length=1000,
2461 expire_walk_interval_ms=10000,
2462 is_ip6=1,
2463 )
Klement Sekera75e7d132017-09-20 08:26:30 +02002464
2465 def tearDown(self):
Klement Sekera01c1fa42021-12-14 18:25:11 +00002466 super().tearDown()
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -07002467
2468 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +00002469 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
2470 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +01002471 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +02002472
2473 def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):
2474 """Verify captured packet stream.
2475
2476 :param list capture: Captured packet stream.
2477 """
2478 info = None
2479 seen = set()
2480 for packet in capture:
2481 try:
Klement Sekera4c533132018-02-22 11:41:12 +01002482 self.logger.debug(ppp("Got packet:", packet))
Klement Sekera75e7d132017-09-20 08:26:30 +02002483 ip = packet[ip_class]
2484 udp = packet[UDP]
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08002485 payload_info = self.payload_to_info(packet[Raw])
Klement Sekera75e7d132017-09-20 08:26:30 +02002486 packet_index = payload_info.index
2487 self.assertTrue(
2488 packet_index not in dropped_packet_indexes,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002489 ppp("Packet received, but should be dropped:", packet),
2490 )
Klement Sekera75e7d132017-09-20 08:26:30 +02002491 if packet_index in seen:
2492 raise Exception(ppp("Duplicate packet received", packet))
2493 seen.add(packet_index)
Klement Sekera4c533132018-02-22 11:41:12 +01002494 self.assertEqual(payload_info.dst, self.dst_if.sw_if_index)
Klement Sekera75e7d132017-09-20 08:26:30 +02002495 info = self._packet_infos[packet_index]
2496 self.assertTrue(info is not None)
2497 self.assertEqual(packet_index, info.index)
2498 saved_packet = info.data
2499 self.assertEqual(ip.src, saved_packet[ip_class].src)
2500 self.assertEqual(ip.dst, saved_packet[ip_class].dst)
2501 self.assertEqual(udp.payload, saved_packet[UDP].payload)
Klement Sekera4c533132018-02-22 11:41:12 +01002502 except Exception:
Klement Sekera75e7d132017-09-20 08:26:30 +02002503 self.logger.error(ppp("Unexpected or invalid packet:", packet))
2504 raise
2505 for index in self._packet_infos:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002506 self.assertTrue(
2507 index in seen or index in dropped_packet_indexes,
2508 "Packet with packet_index %d not received" % index,
2509 )
Klement Sekera75e7d132017-09-20 08:26:30 +02002510
Dmitry Valter34fa0ce2024-03-11 10:38:46 +00002511 @unittest.skipIf(
2512 "gre" in config.excluded_plugins, "Exclude tests requiring GRE plugin"
2513 )
Klement Sekera75e7d132017-09-20 08:26:30 +02002514 def test_fif4(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002515 """Fragments in fragments (4o4)"""
Klement Sekera75e7d132017-09-20 08:26:30 +02002516
2517 # TODO this should be ideally in setUpClass, but then we hit a bug
2518 # with VppIpRoute incorrectly reporting it's present when it's not
2519 # so we need to manually remove the vpp config, thus we cannot have
2520 # it shared for multiple test cases
2521 self.tun_ip4 = "1.1.1.2"
2522
Klement Sekera4c533132018-02-22 11:41:12 +01002523 self.gre4 = VppGreInterface(self, self.src_if.local_ip4, self.tun_ip4)
Klement Sekera75e7d132017-09-20 08:26:30 +02002524 self.gre4.add_vpp_config()
2525 self.gre4.admin_up()
2526 self.gre4.config_ip4()
2527
Klement Sekera4c533132018-02-22 11:41:12 +01002528 self.vapi.ip_reassembly_enable_disable(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002529 sw_if_index=self.gre4.sw_if_index, enable_ip4=True
2530 )
Klement Sekera4c533132018-02-22 11:41:12 +01002531
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002532 self.route4 = VppIpRoute(
2533 self,
2534 self.tun_ip4,
2535 32,
2536 [VppRoutePath(self.src_if.remote_ip4, self.src_if.sw_if_index)],
2537 )
Klement Sekera75e7d132017-09-20 08:26:30 +02002538 self.route4.add_vpp_config()
2539
2540 self.reset_packet_infos()
2541 for i in range(test_packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01002542 info = self.create_packet_info(self.src_if, self.dst_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02002543 payload = self.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +01002544 # Ethernet header here is only for size calculation, thus it
2545 # doesn't matter how it's initialized. This is to ensure that
2546 # reassembled packet is not > 9000 bytes, so that it's not dropped
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002547 p = (
2548 Ether()
2549 / IP(id=i, src=self.src_if.remote_ip4, dst=self.dst_if.remote_ip4)
2550 / UDP(sport=1234, dport=5678)
2551 / Raw(payload)
2552 )
Klement Sekera75e7d132017-09-20 08:26:30 +02002553 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
2554 self.extend_packet(p, size, self.padding)
Klement Sekera4c533132018-02-22 11:41:12 +01002555 info.data = p[IP] # use only IP part, without ethernet header
Klement Sekera75e7d132017-09-20 08:26:30 +02002556
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002557 fragments = [
2558 x
2559 for _, p in self._packet_infos.items()
2560 for x in fragment_rfc791(p.data, 400)
2561 ]
Klement Sekera75e7d132017-09-20 08:26:30 +02002562
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002563 encapped_fragments = [
2564 Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac)
2565 / IP(src=self.tun_ip4, dst=self.src_if.local_ip4)
2566 / GRE()
2567 / p
2568 for p in fragments
2569 ]
Klement Sekera75e7d132017-09-20 08:26:30 +02002570
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002571 fragmented_encapped_fragments = [
2572 x for p in encapped_fragments for x in fragment_rfc791(p, 200)
2573 ]
Klement Sekera75e7d132017-09-20 08:26:30 +02002574
Klement Sekera4c533132018-02-22 11:41:12 +01002575 self.src_if.add_stream(fragmented_encapped_fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02002576
2577 self.pg_enable_capture(self.pg_interfaces)
2578 self.pg_start()
2579
Klement Sekera4c533132018-02-22 11:41:12 +01002580 self.src_if.assert_nothing_captured()
2581 packets = self.dst_if.get_capture(len(self._packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +02002582 self.verify_capture(packets, IP)
2583
2584 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
2585 # so that it's query_vpp_config() works as it should
2586 self.gre4.remove_vpp_config()
Klement Sekera4c533132018-02-22 11:41:12 +01002587 self.logger.debug(self.vapi.ppcli("show interface"))
Klement Sekera75e7d132017-09-20 08:26:30 +02002588
Dmitry Valter34fa0ce2024-03-11 10:38:46 +00002589 @unittest.skipIf(
2590 "gre" in config.excluded_plugins, "Exclude tests requiring GRE plugin"
2591 )
Klement Sekera75e7d132017-09-20 08:26:30 +02002592 def test_fif6(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002593 """Fragments in fragments (6o6)"""
Klement Sekera75e7d132017-09-20 08:26:30 +02002594 # TODO this should be ideally in setUpClass, but then we hit a bug
2595 # with VppIpRoute incorrectly reporting it's present when it's not
2596 # so we need to manually remove the vpp config, thus we cannot have
2597 # it shared for multiple test cases
2598 self.tun_ip6 = "1002::1"
2599
Neale Ranns5a8844b2019-04-16 07:15:35 +00002600 self.gre6 = VppGreInterface(self, self.src_if.local_ip6, self.tun_ip6)
Klement Sekera75e7d132017-09-20 08:26:30 +02002601 self.gre6.add_vpp_config()
2602 self.gre6.admin_up()
2603 self.gre6.config_ip6()
2604
Klement Sekera4c533132018-02-22 11:41:12 +01002605 self.vapi.ip_reassembly_enable_disable(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002606 sw_if_index=self.gre6.sw_if_index, enable_ip6=True
2607 )
Klement Sekera4c533132018-02-22 11:41:12 +01002608
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002609 self.route6 = VppIpRoute(
2610 self,
2611 self.tun_ip6,
2612 128,
2613 [VppRoutePath(self.src_if.remote_ip6, self.src_if.sw_if_index)],
2614 )
Klement Sekera75e7d132017-09-20 08:26:30 +02002615 self.route6.add_vpp_config()
2616
2617 self.reset_packet_infos()
2618 for i in range(test_packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01002619 info = self.create_packet_info(self.src_if, self.dst_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02002620 payload = self.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +01002621 # Ethernet header here is only for size calculation, thus it
2622 # doesn't matter how it's initialized. This is to ensure that
2623 # reassembled packet is not > 9000 bytes, so that it's not dropped
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002624 p = (
2625 Ether()
2626 / IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6)
2627 / UDP(sport=1234, dport=5678)
2628 / Raw(payload)
2629 )
Klement Sekera75e7d132017-09-20 08:26:30 +02002630 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
2631 self.extend_packet(p, size, self.padding)
Klement Sekera4c533132018-02-22 11:41:12 +01002632 info.data = p[IPv6] # use only IPv6 part, without ethernet header
Klement Sekera75e7d132017-09-20 08:26:30 +02002633
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002634 fragments = [
2635 x
2636 for _, i in self._packet_infos.items()
2637 for x in fragment_rfc8200(i.data, i.index, 400)
2638 ]
Klement Sekera75e7d132017-09-20 08:26:30 +02002639
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002640 encapped_fragments = [
2641 Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac)
2642 / IPv6(src=self.tun_ip6, dst=self.src_if.local_ip6)
2643 / GRE()
2644 / p
2645 for p in fragments
2646 ]
Klement Sekera75e7d132017-09-20 08:26:30 +02002647
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002648 fragmented_encapped_fragments = [
2649 x
2650 for p in encapped_fragments
2651 for x in (
Klement Sekera75e7d132017-09-20 08:26:30 +02002652 fragment_rfc8200(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002653 p, 2 * len(self._packet_infos) + p[IPv6ExtHdrFragment].id, 200
2654 )
2655 if IPv6ExtHdrFragment in p
2656 else [p]
Klement Sekera75e7d132017-09-20 08:26:30 +02002657 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002658 ]
Klement Sekera75e7d132017-09-20 08:26:30 +02002659
Klement Sekera4c533132018-02-22 11:41:12 +01002660 self.src_if.add_stream(fragmented_encapped_fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02002661
2662 self.pg_enable_capture(self.pg_interfaces)
2663 self.pg_start()
2664
Klement Sekera4c533132018-02-22 11:41:12 +01002665 self.src_if.assert_nothing_captured()
2666 packets = self.dst_if.get_capture(len(self._packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +02002667 self.verify_capture(packets, IPv6)
2668
2669 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
2670 # so that it's query_vpp_config() works as it should
Klement Sekera3ecc2212018-03-27 10:34:43 +02002671 self.gre6.remove_vpp_config()
Klement Sekera75e7d132017-09-20 08:26:30 +02002672
2673
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002674if __name__ == "__main__":
Klement Sekera75e7d132017-09-20 08:26:30 +02002675 unittest.main(testRunner=VppTestRunner)