blob: 254b2ae3833ee899a873ee9c61a6a0dd6311cea9 [file] [log] [blame]
Renato Botelho do Coutoead1e532019-10-31 13:31:07 -05001#!/usr/bin/env python3
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -08002
Klement Sekera75e7d132017-09-20 08:26:30 +02003import unittest
Klement Sekera630ab582019-07-19 09:14:19 +00004from random import shuffle, choice, randrange
Klement Sekera75e7d132017-09-20 08:26:30 +02005
Klement Sekera947a85c2019-07-24 12:40:37 +00006from framework import VppTestCase, VppTestRunner
7
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07008import scapy.compat
Klement Sekera75e7d132017-09-20 08:26:30 +02009from scapy.packet import Raw
10from scapy.layers.l2 import Ether, GRE
Juraj Sloboda3048b632018-10-02 11:13:53 +020011from scapy.layers.inet import IP, UDP, ICMP
Klement Sekera769145c2019-03-06 11:59:57 +010012from scapy.layers.inet6 import HBHOptUnknown, ICMPv6ParamProblem,\
Ole Troan03092c12021-11-23 15:55:39 +010013 ICMPv6TimeExceeded, IPv6, IPv6ExtHdrFragment,\
14 IPv6ExtHdrHopByHop, IPv6ExtHdrDestOpt, PadN, ICMPv6EchoRequest
Paul Vinciguerra69555952019-03-01 08:46:29 -080015from framework import VppTestCase, VppTestRunner
Klement Sekera630ab582019-07-19 09:14:19 +000016from util import ppp, ppc, fragment_rfc791, fragment_rfc8200
Neale Ranns5a8844b2019-04-16 07:15:35 +000017from vpp_gre_interface import VppGreInterface
Neale Rannsc0a93142018-09-05 15:42:26 -070018from vpp_ip import DpoProto
Neale Ranns097fa662018-05-01 05:17:55 -070019from vpp_ip_route import VppIpRoute, VppRoutePath, FibPathProto
Klement Sekera896c8962019-06-24 11:52:49 +000020from vpp_papi import VppEnum
Klement Sekera75e7d132017-09-20 08:26:30 +020021
Klement Sekerad0f70a32018-12-14 17:24:13 +010022# 35 is enough to have >257 400-byte fragments
23test_packet_count = 35
Klement Sekera75e7d132017-09-20 08:26:30 +020024
25
Klement Sekera947a85c2019-07-24 12:40:37 +000026class TestIPv4Reassembly(VppTestCase):
Klement Sekera75e7d132017-09-20 08:26:30 +020027 """ IPv4 Reassembly """
28
29 @classmethod
30 def setUpClass(cls):
31 super(TestIPv4Reassembly, cls).setUpClass()
32
Klement Sekera4c533132018-02-22 11:41:12 +010033 cls.create_pg_interfaces([0, 1])
34 cls.src_if = cls.pg0
35 cls.dst_if = cls.pg1
Klement Sekera75e7d132017-09-20 08:26:30 +020036
37 # setup all interfaces
38 for i in cls.pg_interfaces:
39 i.admin_up()
40 i.config_ip4()
41 i.resolve_arp()
42
Klement Sekera75e7d132017-09-20 08:26:30 +020043 # packet sizes
44 cls.packet_sizes = [64, 512, 1518, 9018]
45 cls.padding = " abcdefghijklmn"
46 cls.create_stream(cls.packet_sizes)
47 cls.create_fragments()
48
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -070049 @classmethod
50 def tearDownClass(cls):
51 super(TestIPv4Reassembly, cls).tearDownClass()
52
Klement Sekera75e7d132017-09-20 08:26:30 +020053 def setUp(self):
54 """ Test setup - force timeout on existing reassemblies """
55 super(TestIPv4Reassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +010056 self.vapi.ip_reassembly_enable_disable(
57 sw_if_index=self.src_if.sw_if_index, enable_ip4=True)
Klement Sekera75e7d132017-09-20 08:26:30 +020058 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +020059 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +020060 expire_walk_interval_ms=10)
BenoƮt Ganne56eccdb2021-08-20 09:18:31 +020061 self.virtual_sleep(.25)
Klement Sekera75e7d132017-09-20 08:26:30 +020062 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +020063 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +020064 expire_walk_interval_ms=10000)
65
66 def tearDown(self):
67 super(TestIPv4Reassembly, self).tearDown()
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -070068
69 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +000070 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +010071 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +020072
73 @classmethod
74 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
Klement Sekerad0f70a32018-12-14 17:24:13 +010075 """Create input packet stream
Klement Sekera75e7d132017-09-20 08:26:30 +020076
77 :param list packet_sizes: Required packet sizes.
78 """
79 for i in range(0, packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +010080 info = cls.create_packet_info(cls.src_if, cls.src_if)
Klement Sekera75e7d132017-09-20 08:26:30 +020081 payload = cls.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +010082 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
83 IP(id=info.index, src=cls.src_if.remote_ip4,
84 dst=cls.dst_if.remote_ip4) /
85 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +020086 Raw(payload))
87 size = packet_sizes[(i // 2) % len(packet_sizes)]
88 cls.extend_packet(p, size, cls.padding)
89 info.data = p
90
91 @classmethod
92 def create_fragments(cls):
93 infos = cls._packet_infos
94 cls.pkt_infos = []
Paul Vinciguerra090096b2020-12-03 00:42:46 -050095 for index, info in infos.items():
Klement Sekera75e7d132017-09-20 08:26:30 +020096 p = info.data
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -070097 # cls.logger.debug(ppp("Packet:",
98 # p.__class__(scapy.compat.raw(p))))
Klement Sekera75e7d132017-09-20 08:26:30 +020099 fragments_400 = fragment_rfc791(p, 400)
100 fragments_300 = fragment_rfc791(p, 300)
101 fragments_200 = [
102 x for f in fragments_400 for x in fragment_rfc791(f, 200)]
103 cls.pkt_infos.append(
104 (index, fragments_400, fragments_300, fragments_200))
105 cls.fragments_400 = [
106 x for (_, frags, _, _) in cls.pkt_infos for x in frags]
107 cls.fragments_300 = [
108 x for (_, _, frags, _) in cls.pkt_infos for x in frags]
109 cls.fragments_200 = [
110 x for (_, _, _, frags) in cls.pkt_infos for x in frags]
111 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
112 "%s 300-byte fragments and %s 200-byte fragments" %
113 (len(infos), len(cls.fragments_400),
114 len(cls.fragments_300), len(cls.fragments_200)))
115
Klement Sekera947a85c2019-07-24 12:40:37 +0000116 def verify_capture(self, capture, dropped_packet_indexes=[]):
117 """Verify captured packet stream.
118
119 :param list capture: Captured packet stream.
120 """
121 info = None
122 seen = set()
123 for packet in capture:
124 try:
125 self.logger.debug(ppp("Got packet:", packet))
126 ip = packet[IP]
127 udp = packet[UDP]
128 payload_info = self.payload_to_info(packet[Raw])
129 packet_index = payload_info.index
130 self.assertTrue(
131 packet_index not in dropped_packet_indexes,
132 ppp("Packet received, but should be dropped:", packet))
133 if packet_index in seen:
134 raise Exception(ppp("Duplicate packet received", packet))
135 seen.add(packet_index)
136 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
137 info = self._packet_infos[packet_index]
138 self.assertTrue(info is not None)
139 self.assertEqual(packet_index, info.index)
140 saved_packet = info.data
141 self.assertEqual(ip.src, saved_packet[IP].src)
142 self.assertEqual(ip.dst, saved_packet[IP].dst)
143 self.assertEqual(udp.payload, saved_packet[UDP].payload)
144 except Exception:
145 self.logger.error(ppp("Unexpected or invalid packet:", packet))
146 raise
147 for index in self._packet_infos:
148 self.assertTrue(index in seen or index in dropped_packet_indexes,
149 "Packet with packet_index %d not received" % index)
150
151 def test_reassembly(self):
Klement Sekera75e7d132017-09-20 08:26:30 +0200152 """ basic reassembly """
153
Klement Sekera947a85c2019-07-24 12:40:37 +0000154 self.pg_enable_capture()
155 self.src_if.add_stream(self.fragments_200)
156 self.pg_start()
157
158 packets = self.dst_if.get_capture(len(self.pkt_infos))
159 self.verify_capture(packets)
160 self.src_if.assert_nothing_captured()
161
162 # run it all again to verify correctness
163 self.pg_enable_capture()
164 self.src_if.add_stream(self.fragments_200)
165 self.pg_start()
166
167 packets = self.dst_if.get_capture(len(self.pkt_infos))
168 self.verify_capture(packets)
169 self.src_if.assert_nothing_captured()
170
Klement Sekera53be16d2020-12-15 21:47:36 +0100171 def test_verify_clear_trace_mid_reassembly(self):
172 """ verify clear trace works mid-reassembly """
173
174 self.pg_enable_capture()
175 self.src_if.add_stream(self.fragments_200[0:-1])
176 self.pg_start()
177
178 self.logger.debug(self.vapi.cli("show trace"))
179 self.vapi.cli("clear trace")
180
181 self.src_if.add_stream(self.fragments_200[-1])
182 self.pg_start()
183 packets = self.dst_if.get_capture(len(self.pkt_infos))
184 self.verify_capture(packets)
185
Klement Sekera947a85c2019-07-24 12:40:37 +0000186 def test_reversed(self):
Klement Sekera75e7d132017-09-20 08:26:30 +0200187 """ reverse order reassembly """
188
Klement Sekera947a85c2019-07-24 12:40:37 +0000189 fragments = list(self.fragments_200)
190 fragments.reverse()
191
192 self.pg_enable_capture()
193 self.src_if.add_stream(fragments)
194 self.pg_start()
195
196 packets = self.dst_if.get_capture(len(self.packet_infos))
197 self.verify_capture(packets)
198 self.src_if.assert_nothing_captured()
199
200 # run it all again to verify correctness
201 self.pg_enable_capture()
202 self.src_if.add_stream(fragments)
203 self.pg_start()
204
205 packets = self.dst_if.get_capture(len(self.packet_infos))
206 self.verify_capture(packets)
207 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200208
Klement Sekera3a343d42019-05-16 14:35:46 +0200209 def test_long_fragment_chain(self):
210 """ long fragment chain """
211
212 error_cnt_str = \
Klement Sekera896c8962019-06-24 11:52:49 +0000213 "/err/ip4-full-reassembly-feature/fragment chain too long (drop)"
Klement Sekera3a343d42019-05-16 14:35:46 +0200214
Klement Sekera34641f22019-05-22 20:18:26 +0200215 error_cnt = self.statistics.get_err_counter(error_cnt_str)
Klement Sekera3a343d42019-05-16 14:35:46 +0200216
217 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
218 max_reassembly_length=3,
219 expire_walk_interval_ms=50)
220
221 p1 = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
222 IP(id=1000, src=self.src_if.remote_ip4,
223 dst=self.dst_if.remote_ip4) /
224 UDP(sport=1234, dport=5678) /
Ole Troan127fbec2019-10-18 15:22:56 +0200225 Raw(b"X" * 1000))
Klement Sekera3a343d42019-05-16 14:35:46 +0200226 p2 = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
227 IP(id=1001, src=self.src_if.remote_ip4,
228 dst=self.dst_if.remote_ip4) /
229 UDP(sport=1234, dport=5678) /
Ole Troan127fbec2019-10-18 15:22:56 +0200230 Raw(b"X" * 1000))
Klement Sekera3a343d42019-05-16 14:35:46 +0200231 frags = fragment_rfc791(p1, 200) + fragment_rfc791(p2, 500)
232
233 self.pg_enable_capture()
234 self.src_if.add_stream(frags)
235 self.pg_start()
236
237 self.dst_if.get_capture(1)
Klement Sekera34641f22019-05-22 20:18:26 +0200238 self.assert_error_counter_equal(error_cnt_str, error_cnt + 1)
Klement Sekera3a343d42019-05-16 14:35:46 +0200239
Klement Sekera14d7e902018-12-10 13:46:09 +0100240 def test_5737(self):
241 """ fragment length + ip header size > 65535 """
Klement Sekera4ee633e2018-12-14 12:00:44 +0100242 self.vapi.cli("clear errors")
Ole Troan127fbec2019-10-18 15:22:56 +0200243 raw = b'''E\x00\x00\x88,\xf8\x1f\xfe@\x01\x98\x00\xc0\xa8\n-\xc0\xa8\n\
244\x01\x08\x00\xf0J\xed\xcb\xf1\xf5Test-group: IPv4.IPv4.ipv4-message.\
245Ethernet-Payload.IPv4-Packet.IPv4-Header.Fragment-Offset; Test-case: 5737'''
Klement Sekera14d7e902018-12-10 13:46:09 +0100246 malformed_packet = (Ether(dst=self.src_if.local_mac,
247 src=self.src_if.remote_mac) /
248 IP(raw))
249 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
250 IP(id=1000, src=self.src_if.remote_ip4,
251 dst=self.dst_if.remote_ip4) /
252 UDP(sport=1234, dport=5678) /
Ole Troan127fbec2019-10-18 15:22:56 +0200253 Raw(b"X" * 1000))
Klement Sekera14d7e902018-12-10 13:46:09 +0100254 valid_fragments = fragment_rfc791(p, 400)
255
Ole Troan127fbec2019-10-18 15:22:56 +0200256 counter = "/err/ip4-full-reassembly-feature/malformed packets"
257 error_counter = self.statistics.get_err_counter(counter)
Klement Sekera14d7e902018-12-10 13:46:09 +0100258 self.pg_enable_capture()
259 self.src_if.add_stream([malformed_packet] + valid_fragments)
260 self.pg_start()
261
262 self.dst_if.get_capture(1)
Klement Sekera896c8962019-06-24 11:52:49 +0000263 self.logger.debug(self.vapi.ppcli("show error"))
Ole Troan127fbec2019-10-18 15:22:56 +0200264 self.assertEqual(self.statistics.get_err_counter(counter),
265 error_counter + 1)
Klement Sekera14d7e902018-12-10 13:46:09 +0100266
Klement Sekera400f6d82018-12-13 14:35:48 +0100267 def test_44924(self):
268 """ compress tiny fragments """
269 packets = [(Ether(dst=self.src_if.local_mac,
270 src=self.src_if.remote_mac) /
271 IP(id=24339, flags="MF", frag=0, ttl=64,
272 src=self.src_if.remote_ip4,
273 dst=self.dst_if.remote_ip4) /
274 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
275 Raw(load='Test-group: IPv4')),
276 (Ether(dst=self.src_if.local_mac,
277 src=self.src_if.remote_mac) /
278 IP(id=24339, flags="MF", frag=3, ttl=64,
279 src=self.src_if.remote_ip4,
280 dst=self.dst_if.remote_ip4) /
281 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
282 Raw(load='.IPv4.Fragmentation.vali')),
283 (Ether(dst=self.src_if.local_mac,
284 src=self.src_if.remote_mac) /
285 IP(id=24339, frag=6, ttl=64,
286 src=self.src_if.remote_ip4,
287 dst=self.dst_if.remote_ip4) /
288 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
289 Raw(load='d; Test-case: 44924'))
290 ]
291
292 self.pg_enable_capture()
293 self.src_if.add_stream(packets)
294 self.pg_start()
295
296 self.dst_if.get_capture(1)
297
Klement Sekera4ee633e2018-12-14 12:00:44 +0100298 def test_frag_1(self):
299 """ fragment of size 1 """
300 self.vapi.cli("clear errors")
301 malformed_packets = [(Ether(dst=self.src_if.local_mac,
302 src=self.src_if.remote_mac) /
303 IP(id=7, len=21, flags="MF", frag=0, ttl=64,
304 src=self.src_if.remote_ip4,
305 dst=self.dst_if.remote_ip4) /
306 ICMP(type="echo-request")),
307 (Ether(dst=self.src_if.local_mac,
308 src=self.src_if.remote_mac) /
309 IP(id=7, len=21, frag=1, ttl=64,
310 src=self.src_if.remote_ip4,
311 dst=self.dst_if.remote_ip4) /
Ole Troan127fbec2019-10-18 15:22:56 +0200312 Raw(load=b'\x08')),
Klement Sekera4ee633e2018-12-14 12:00:44 +0100313 ]
314
315 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
316 IP(id=1000, src=self.src_if.remote_ip4,
317 dst=self.dst_if.remote_ip4) /
318 UDP(sport=1234, dport=5678) /
Ole Troan127fbec2019-10-18 15:22:56 +0200319 Raw(b"X" * 1000))
Klement Sekera4ee633e2018-12-14 12:00:44 +0100320 valid_fragments = fragment_rfc791(p, 400)
321
322 self.pg_enable_capture()
323 self.src_if.add_stream(malformed_packets + valid_fragments)
324 self.pg_start()
325
326 self.dst_if.get_capture(1)
327
Klement Sekera896c8962019-06-24 11:52:49 +0000328 self.assert_packet_counter_equal("ip4-full-reassembly-feature", 1)
Klement Sekera4ee633e2018-12-14 12:00:44 +0100329 # TODO remove above, uncomment below once clearing of counters
330 # is supported
331 # self.assert_packet_counter_equal(
Klement Sekera896c8962019-06-24 11:52:49 +0000332 # "/err/ip4-full-reassembly-feature/malformed packets", 1)
Klement Sekera4ee633e2018-12-14 12:00:44 +0100333
Klement Sekera947a85c2019-07-24 12:40:37 +0000334 def test_random(self):
335 """ random order reassembly """
336
337 fragments = list(self.fragments_200)
338 shuffle(fragments)
339
340 self.pg_enable_capture()
341 self.src_if.add_stream(fragments)
342 self.pg_start()
343
344 packets = self.dst_if.get_capture(len(self.packet_infos))
345 self.verify_capture(packets)
346 self.src_if.assert_nothing_captured()
347
348 # run it all again to verify correctness
349 self.pg_enable_capture()
350 self.src_if.add_stream(fragments)
351 self.pg_start()
352
353 packets = self.dst_if.get_capture(len(self.packet_infos))
354 self.verify_capture(packets)
355 self.src_if.assert_nothing_captured()
356
357 def test_duplicates(self):
Klement Sekera75e7d132017-09-20 08:26:30 +0200358 """ duplicate fragments """
Klement Sekera947a85c2019-07-24 12:40:37 +0000359
Klement Sekera75e7d132017-09-20 08:26:30 +0200360 fragments = [
361 x for (_, frags, _, _) in self.pkt_infos
362 for x in frags
363 for _ in range(0, min(2, len(frags)))
364 ]
Klement Sekera947a85c2019-07-24 12:40:37 +0000365
366 self.pg_enable_capture()
367 self.src_if.add_stream(fragments)
368 self.pg_start()
369
370 packets = self.dst_if.get_capture(len(self.pkt_infos))
371 self.verify_capture(packets)
372 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200373
374 def test_overlap1(self):
375 """ overlapping fragments case #1 """
376
377 fragments = []
378 for _, _, frags_300, frags_200 in self.pkt_infos:
379 if len(frags_300) == 1:
380 fragments.extend(frags_300)
381 else:
382 for i, j in zip(frags_200, frags_300):
383 fragments.extend(i)
384 fragments.extend(j)
385
386 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100387 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200388 self.pg_start()
389
Klement Sekera4c533132018-02-22 11:41:12 +0100390 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera947a85c2019-07-24 12:40:37 +0000391 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100392 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200393
394 # run it all to verify correctness
395 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100396 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200397 self.pg_start()
398
Klement Sekera4c533132018-02-22 11:41:12 +0100399 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera947a85c2019-07-24 12:40:37 +0000400 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100401 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200402
403 def test_overlap2(self):
404 """ overlapping fragments case #2 """
405
406 fragments = []
407 for _, _, frags_300, frags_200 in self.pkt_infos:
408 if len(frags_300) == 1:
409 fragments.extend(frags_300)
410 else:
411 # care must be taken here so that there are no fragments
412 # received by vpp after reassembly is finished, otherwise
413 # new reassemblies will be started and packet generator will
414 # freak out when it detects unfreed buffers
415 zipped = zip(frags_300, frags_200)
Paul Vinciguerrac7834e02019-03-02 10:43:05 -0800416 for i, j in zipped:
Klement Sekera75e7d132017-09-20 08:26:30 +0200417 fragments.extend(i)
418 fragments.extend(j)
Paul Vinciguerrac7834e02019-03-02 10:43:05 -0800419 fragments.pop()
Klement Sekera75e7d132017-09-20 08:26:30 +0200420
421 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100422 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200423 self.pg_start()
424
Klement Sekera4c533132018-02-22 11:41:12 +0100425 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera947a85c2019-07-24 12:40:37 +0000426 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100427 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200428
429 # run it all to verify correctness
430 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100431 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200432 self.pg_start()
433
Klement Sekera4c533132018-02-22 11:41:12 +0100434 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera947a85c2019-07-24 12:40:37 +0000435 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100436 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200437
Klement Sekera947a85c2019-07-24 12:40:37 +0000438 def test_timeout_inline(self):
Klement Sekera75e7d132017-09-20 08:26:30 +0200439 """ timeout (inline) """
440
441 dropped_packet_indexes = set(
442 index for (index, frags, _, _) in self.pkt_infos if len(frags) > 1
443 )
444
Klement Sekera947a85c2019-07-24 12:40:37 +0000445 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
446 max_reassembly_length=3,
447 expire_walk_interval_ms=10000)
448
449 self.pg_enable_capture()
450 self.src_if.add_stream(self.fragments_400)
451 self.pg_start()
452
453 packets = self.dst_if.get_capture(
454 len(self.pkt_infos) - len(dropped_packet_indexes))
455 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100456 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200457
458 def test_timeout_cleanup(self):
459 """ timeout (cleanup) """
460
461 # whole packets + fragmented packets sans last fragment
462 fragments = [
463 x for (_, frags_400, _, _) in self.pkt_infos
464 for x in frags_400[:-1 if len(frags_400) > 1 else None]
465 ]
466
467 # last fragments for fragmented packets
468 fragments2 = [frags_400[-1]
469 for (_, frags_400, _, _) in self.pkt_infos
470 if len(frags_400) > 1]
471
472 dropped_packet_indexes = set(
473 index for (index, frags_400, _, _) in self.pkt_infos
474 if len(frags_400) > 1)
475
476 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +0200477 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +0200478 expire_walk_interval_ms=50)
479
480 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100481 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200482 self.pg_start()
483
BenoƮt Ganne56eccdb2021-08-20 09:18:31 +0200484 self.virtual_sleep(.25, "wait before sending rest of fragments")
Klement Sekera75e7d132017-09-20 08:26:30 +0200485
Klement Sekera4c533132018-02-22 11:41:12 +0100486 self.src_if.add_stream(fragments2)
Klement Sekera75e7d132017-09-20 08:26:30 +0200487 self.pg_start()
Klement Sekera75e7d132017-09-20 08:26:30 +0200488
Klement Sekera4c533132018-02-22 11:41:12 +0100489 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200490 len(self.pkt_infos) - len(dropped_packet_indexes))
Klement Sekera947a85c2019-07-24 12:40:37 +0000491 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100492 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200493
Klement Sekera947a85c2019-07-24 12:40:37 +0000494 def test_disabled(self):
Klement Sekera75e7d132017-09-20 08:26:30 +0200495 """ reassembly disabled """
496
497 dropped_packet_indexes = set(
498 index for (index, frags_400, _, _) in self.pkt_infos
499 if len(frags_400) > 1)
Klement Sekera947a85c2019-07-24 12:40:37 +0000500
501 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
502 max_reassembly_length=3,
503 expire_walk_interval_ms=10000)
504
505 self.pg_enable_capture()
506 self.src_if.add_stream(self.fragments_400)
507 self.pg_start()
508
509 packets = self.dst_if.get_capture(
510 len(self.pkt_infos) - len(dropped_packet_indexes))
511 self.verify_capture(packets, dropped_packet_indexes)
512 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200513
514
Klement Sekerade34c352019-06-25 11:19:22 +0000515class TestIPv4SVReassembly(VppTestCase):
516 """ IPv4 Shallow Virtual Reassembly """
517
518 @classmethod
519 def setUpClass(cls):
520 super(TestIPv4SVReassembly, cls).setUpClass()
521
522 cls.create_pg_interfaces([0, 1])
523 cls.src_if = cls.pg0
524 cls.dst_if = cls.pg1
525
526 # setup all interfaces
527 for i in cls.pg_interfaces:
528 i.admin_up()
529 i.config_ip4()
530 i.resolve_arp()
531
532 def setUp(self):
533 """ Test setup - force timeout on existing reassemblies """
534 super(TestIPv4SVReassembly, self).setUp()
535 self.vapi.ip_reassembly_enable_disable(
536 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
537 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
538 self.vapi.ip_reassembly_set(
539 timeout_ms=0, max_reassemblies=1000,
540 max_reassembly_length=1000,
541 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
542 expire_walk_interval_ms=10)
BenoƮt Ganne56eccdb2021-08-20 09:18:31 +0200543 self.virtual_sleep(.25)
Klement Sekerade34c352019-06-25 11:19:22 +0000544 self.vapi.ip_reassembly_set(
545 timeout_ms=1000000, max_reassemblies=1000,
546 max_reassembly_length=1000,
547 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
548 expire_walk_interval_ms=10000)
549
550 def tearDown(self):
551 super(TestIPv4SVReassembly, self).tearDown()
552 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
553 self.logger.debug(self.vapi.ppcli("show buffers"))
554
555 def test_basic(self):
556 """ basic reassembly """
557 payload_len = 1000
558 payload = ""
559 counter = 0
560 while len(payload) < payload_len:
561 payload += "%u " % counter
562 counter += 1
563
564 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
565 IP(id=1, src=self.src_if.remote_ip4,
566 dst=self.dst_if.remote_ip4) /
567 UDP(sport=1234, dport=5678) /
568 Raw(payload))
569 fragments = fragment_rfc791(p, payload_len/4)
570
571 # send fragment #2 - should be cached inside reassembly
572 self.pg_enable_capture()
573 self.src_if.add_stream(fragments[1])
574 self.pg_start()
575 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
576 self.logger.debug(self.vapi.ppcli("show buffers"))
577 self.logger.debug(self.vapi.ppcli("show trace"))
578 self.dst_if.assert_nothing_captured()
579
580 # send fragment #1 - reassembly is finished now and both fragments
581 # forwarded
582 self.pg_enable_capture()
583 self.src_if.add_stream(fragments[0])
584 self.pg_start()
585 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
586 self.logger.debug(self.vapi.ppcli("show buffers"))
587 self.logger.debug(self.vapi.ppcli("show trace"))
588 c = self.dst_if.get_capture(2)
589 for sent, recvd in zip([fragments[1], fragments[0]], c):
590 self.assertEqual(sent[IP].src, recvd[IP].src)
591 self.assertEqual(sent[IP].dst, recvd[IP].dst)
592 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
593
594 # send rest of fragments - should be immediately forwarded
595 self.pg_enable_capture()
596 self.src_if.add_stream(fragments[2:])
597 self.pg_start()
598 c = self.dst_if.get_capture(len(fragments[2:]))
599 for sent, recvd in zip(fragments[2:], c):
600 self.assertEqual(sent[IP].src, recvd[IP].src)
601 self.assertEqual(sent[IP].dst, recvd[IP].dst)
602 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
603
Klement Sekera53be16d2020-12-15 21:47:36 +0100604 def test_verify_clear_trace_mid_reassembly(self):
605 """ verify clear trace works mid-reassembly """
606 payload_len = 1000
607 payload = ""
608 counter = 0
609 while len(payload) < payload_len:
610 payload += "%u " % counter
611 counter += 1
612
613 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
614 IP(id=1, src=self.src_if.remote_ip4,
615 dst=self.dst_if.remote_ip4) /
616 UDP(sport=1234, dport=5678) /
617 Raw(payload))
618 fragments = fragment_rfc791(p, payload_len/4)
619
620 self.pg_enable_capture()
621 self.src_if.add_stream(fragments[1])
622 self.pg_start()
623
624 self.logger.debug(self.vapi.cli("show trace"))
625 self.vapi.cli("clear trace")
626
627 self.pg_enable_capture()
628 self.src_if.add_stream(fragments[0])
629 self.pg_start()
630 self.dst_if.get_capture(2)
631
632 self.logger.debug(self.vapi.cli("show trace"))
633 self.vapi.cli("clear trace")
634
635 self.pg_enable_capture()
636 self.src_if.add_stream(fragments[2:])
637 self.pg_start()
638 self.dst_if.get_capture(len(fragments[2:]))
639
Klement Sekerade34c352019-06-25 11:19:22 +0000640 def test_timeout(self):
641 """ reassembly timeout """
642 payload_len = 1000
643 payload = ""
644 counter = 0
645 while len(payload) < payload_len:
646 payload += "%u " % counter
647 counter += 1
648
649 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
650 IP(id=1, src=self.src_if.remote_ip4,
651 dst=self.dst_if.remote_ip4) /
652 UDP(sport=1234, dport=5678) /
653 Raw(payload))
654 fragments = fragment_rfc791(p, payload_len/4)
655
656 self.vapi.ip_reassembly_set(
657 timeout_ms=100, max_reassemblies=1000,
658 max_reassembly_length=1000,
659 expire_walk_interval_ms=50,
660 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
661
662 # send fragments #2 and #1 - should be forwarded
663 self.pg_enable_capture()
664 self.src_if.add_stream(fragments[0:2])
665 self.pg_start()
666 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
667 self.logger.debug(self.vapi.ppcli("show buffers"))
668 self.logger.debug(self.vapi.ppcli("show trace"))
669 c = self.dst_if.get_capture(2)
670 for sent, recvd in zip([fragments[1], fragments[0]], c):
671 self.assertEqual(sent[IP].src, recvd[IP].src)
672 self.assertEqual(sent[IP].dst, recvd[IP].dst)
673 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
674
675 # wait for cleanup
BenoƮt Ganne56eccdb2021-08-20 09:18:31 +0200676 self.virtual_sleep(.25, "wait before sending rest of fragments")
Klement Sekerade34c352019-06-25 11:19:22 +0000677
678 # send rest of fragments - shouldn't be forwarded
679 self.pg_enable_capture()
680 self.src_if.add_stream(fragments[2:])
681 self.pg_start()
682 self.dst_if.assert_nothing_captured()
683
684 def test_lru(self):
685 """ reassembly reuses LRU element """
686
687 self.vapi.ip_reassembly_set(
688 timeout_ms=1000000, max_reassemblies=1,
689 max_reassembly_length=1000,
690 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
691 expire_walk_interval_ms=10000)
692
693 payload_len = 1000
694 payload = ""
695 counter = 0
696 while len(payload) < payload_len:
697 payload += "%u " % counter
698 counter += 1
699
700 packet_count = 10
701
702 fragments = [f
703 for i in range(packet_count)
704 for p in (Ether(dst=self.src_if.local_mac,
705 src=self.src_if.remote_mac) /
706 IP(id=i, src=self.src_if.remote_ip4,
707 dst=self.dst_if.remote_ip4) /
708 UDP(sport=1234, dport=5678) /
709 Raw(payload))
710 for f in fragment_rfc791(p, payload_len/4)]
711
712 self.pg_enable_capture()
713 self.src_if.add_stream(fragments)
714 self.pg_start()
715 c = self.dst_if.get_capture(len(fragments))
716 for sent, recvd in zip(fragments, c):
717 self.assertEqual(sent[IP].src, recvd[IP].src)
718 self.assertEqual(sent[IP].dst, recvd[IP].dst)
719 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
720
Klement Sekera18c6cd92020-07-10 09:29:48 +0000721 def send_mixed_and_verify_capture(self, traffic):
722 stream = []
723 for t in traffic:
724 for c in range(t['count']):
725 stream.append(
726 (Ether(dst=self.src_if.local_mac,
727 src=self.src_if.remote_mac) /
728 IP(id=self.counter,
729 flags=t['flags'],
730 src=self.src_if.remote_ip4,
731 dst=self.dst_if.remote_ip4) /
732 UDP(sport=1234, dport=5678) /
733 Raw("abcdef")))
734 self.counter = self.counter + 1
735
736 self.pg_enable_capture()
737 self.src_if.add_stream(stream)
738 self.pg_start()
739 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
740 self.logger.debug(self.vapi.ppcli("show buffers"))
741 self.logger.debug(self.vapi.ppcli("show trace"))
742 self.dst_if.get_capture(len(stream))
743
744 def test_mixed(self):
745 """ mixed traffic correctly passes through SVR """
746 self.counter = 1
747
748 self.send_mixed_and_verify_capture([{'count': 1, 'flags': ''}])
749 self.send_mixed_and_verify_capture([{'count': 2, 'flags': ''}])
750 self.send_mixed_and_verify_capture([{'count': 3, 'flags': ''}])
751 self.send_mixed_and_verify_capture([{'count': 8, 'flags': ''}])
752 self.send_mixed_and_verify_capture([{'count': 257, 'flags': ''}])
753
754 self.send_mixed_and_verify_capture([{'count': 1, 'flags': 'MF'}])
755 self.send_mixed_and_verify_capture([{'count': 2, 'flags': 'MF'}])
756 self.send_mixed_and_verify_capture([{'count': 3, 'flags': 'MF'}])
757 self.send_mixed_and_verify_capture([{'count': 8, 'flags': 'MF'}])
758 self.send_mixed_and_verify_capture([{'count': 257, 'flags': 'MF'}])
759
760 self.send_mixed_and_verify_capture(
761 [{'count': 1, 'flags': ''}, {'count': 1, 'flags': 'MF'}])
762 self.send_mixed_and_verify_capture(
763 [{'count': 2, 'flags': ''}, {'count': 2, 'flags': 'MF'}])
764 self.send_mixed_and_verify_capture(
765 [{'count': 3, 'flags': ''}, {'count': 3, 'flags': 'MF'}])
766 self.send_mixed_and_verify_capture(
767 [{'count': 8, 'flags': ''}, {'count': 8, 'flags': 'MF'}])
768 self.send_mixed_and_verify_capture(
769 [{'count': 129, 'flags': ''}, {'count': 129, 'flags': 'MF'}])
770
771 self.send_mixed_and_verify_capture(
772 [{'count': 1, 'flags': ''}, {'count': 1, 'flags': 'MF'},
773 {'count': 1, 'flags': ''}, {'count': 1, 'flags': 'MF'}])
774 self.send_mixed_and_verify_capture(
775 [{'count': 2, 'flags': ''}, {'count': 2, 'flags': 'MF'},
776 {'count': 2, 'flags': ''}, {'count': 2, 'flags': 'MF'}])
777 self.send_mixed_and_verify_capture(
778 [{'count': 3, 'flags': ''}, {'count': 3, 'flags': 'MF'},
779 {'count': 3, 'flags': ''}, {'count': 3, 'flags': 'MF'}])
780 self.send_mixed_and_verify_capture(
781 [{'count': 8, 'flags': ''}, {'count': 8, 'flags': 'MF'},
782 {'count': 8, 'flags': ''}, {'count': 8, 'flags': 'MF'}])
783 self.send_mixed_and_verify_capture(
784 [{'count': 65, 'flags': ''}, {'count': 65, 'flags': 'MF'},
785 {'count': 65, 'flags': ''}, {'count': 65, 'flags': 'MF'}])
786
Klement Sekerade34c352019-06-25 11:19:22 +0000787
Klement Sekera630ab582019-07-19 09:14:19 +0000788class TestIPv4MWReassembly(VppTestCase):
789 """ IPv4 Reassembly (multiple workers) """
Klement Sekera8d815022021-03-15 16:58:10 +0100790 vpp_worker_count = 3
Klement Sekera630ab582019-07-19 09:14:19 +0000791
792 @classmethod
793 def setUpClass(cls):
794 super(TestIPv4MWReassembly, cls).setUpClass()
795
Klement Sekera8d815022021-03-15 16:58:10 +0100796 cls.create_pg_interfaces(range(cls.vpp_worker_count+1))
Klement Sekera630ab582019-07-19 09:14:19 +0000797 cls.src_if = cls.pg0
798 cls.send_ifs = cls.pg_interfaces[:-1]
799 cls.dst_if = cls.pg_interfaces[-1]
800
801 # setup all interfaces
802 for i in cls.pg_interfaces:
803 i.admin_up()
804 i.config_ip4()
805 i.resolve_arp()
806
807 # packets sizes reduced here because we are generating packets without
808 # Ethernet headers, which are added later (diff fragments go via
809 # different interfaces)
810 cls.packet_sizes = [64-len(Ether()), 512-len(Ether()),
811 1518-len(Ether()), 9018-len(Ether())]
812 cls.padding = " abcdefghijklmn"
813 cls.create_stream(cls.packet_sizes)
814 cls.create_fragments()
815
816 @classmethod
817 def tearDownClass(cls):
818 super(TestIPv4MWReassembly, cls).tearDownClass()
819
820 def setUp(self):
821 """ Test setup - force timeout on existing reassemblies """
822 super(TestIPv4MWReassembly, self).setUp()
823 for intf in self.send_ifs:
824 self.vapi.ip_reassembly_enable_disable(
825 sw_if_index=intf.sw_if_index, enable_ip4=True)
826 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
827 max_reassembly_length=1000,
828 expire_walk_interval_ms=10)
BenoƮt Ganne56eccdb2021-08-20 09:18:31 +0200829 self.virtual_sleep(.25)
Klement Sekera630ab582019-07-19 09:14:19 +0000830 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
831 max_reassembly_length=1000,
832 expire_walk_interval_ms=10000)
833
834 def tearDown(self):
835 super(TestIPv4MWReassembly, self).tearDown()
836
837 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +0000838 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
Klement Sekera630ab582019-07-19 09:14:19 +0000839 self.logger.debug(self.vapi.ppcli("show buffers"))
840
841 @classmethod
842 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
843 """Create input packet stream
844
845 :param list packet_sizes: Required packet sizes.
846 """
847 for i in range(0, packet_count):
848 info = cls.create_packet_info(cls.src_if, cls.src_if)
849 payload = cls.info_to_payload(info)
850 p = (IP(id=info.index, src=cls.src_if.remote_ip4,
851 dst=cls.dst_if.remote_ip4) /
852 UDP(sport=1234, dport=5678) /
853 Raw(payload))
854 size = packet_sizes[(i // 2) % len(packet_sizes)]
855 cls.extend_packet(p, size, cls.padding)
856 info.data = p
857
858 @classmethod
859 def create_fragments(cls):
860 infos = cls._packet_infos
861 cls.pkt_infos = []
Paul Vinciguerra090096b2020-12-03 00:42:46 -0500862 for index, info in infos.items():
Klement Sekera630ab582019-07-19 09:14:19 +0000863 p = info.data
864 # cls.logger.debug(ppp("Packet:",
865 # p.__class__(scapy.compat.raw(p))))
866 fragments_400 = fragment_rfc791(p, 400)
867 cls.pkt_infos.append((index, fragments_400))
868 cls.fragments_400 = [
869 x for (_, frags) in cls.pkt_infos for x in frags]
870 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, " %
871 (len(infos), len(cls.fragments_400)))
872
873 def verify_capture(self, capture, dropped_packet_indexes=[]):
874 """Verify captured packet stream.
875
876 :param list capture: Captured packet stream.
877 """
878 info = None
879 seen = set()
880 for packet in capture:
881 try:
882 self.logger.debug(ppp("Got packet:", packet))
883 ip = packet[IP]
884 udp = packet[UDP]
885 payload_info = self.payload_to_info(packet[Raw])
886 packet_index = payload_info.index
887 self.assertTrue(
888 packet_index not in dropped_packet_indexes,
889 ppp("Packet received, but should be dropped:", packet))
890 if packet_index in seen:
891 raise Exception(ppp("Duplicate packet received", packet))
892 seen.add(packet_index)
893 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
894 info = self._packet_infos[packet_index]
895 self.assertTrue(info is not None)
896 self.assertEqual(packet_index, info.index)
897 saved_packet = info.data
898 self.assertEqual(ip.src, saved_packet[IP].src)
899 self.assertEqual(ip.dst, saved_packet[IP].dst)
900 self.assertEqual(udp.payload, saved_packet[UDP].payload)
901 except Exception:
902 self.logger.error(ppp("Unexpected or invalid packet:", packet))
903 raise
904 for index in self._packet_infos:
905 self.assertTrue(index in seen or index in dropped_packet_indexes,
906 "Packet with packet_index %d not received" % index)
907
908 def send_packets(self, packets):
Klement Sekera8d815022021-03-15 16:58:10 +0100909 for counter in range(self.vpp_worker_count):
Klement Sekera630ab582019-07-19 09:14:19 +0000910 if 0 == len(packets[counter]):
911 continue
912 send_if = self.send_ifs[counter]
913 send_if.add_stream(
914 (Ether(dst=send_if.local_mac, src=send_if.remote_mac) / x
915 for x in packets[counter]),
916 worker=counter)
917 self.pg_start()
918
919 def test_worker_conflict(self):
920 """ 1st and FO=0 fragments on different workers """
921
922 # in first wave we send fragments which don't start at offset 0
923 # then we send fragments with offset 0 on a different thread
924 # then the rest of packets on a random thread
Klement Sekera8d815022021-03-15 16:58:10 +0100925 first_packets = [[] for n in range(self.vpp_worker_count)]
926 second_packets = [[] for n in range(self.vpp_worker_count)]
927 rest_of_packets = [[] for n in range(self.vpp_worker_count)]
Klement Sekera630ab582019-07-19 09:14:19 +0000928 for (_, p) in self.pkt_infos:
Klement Sekera8d815022021-03-15 16:58:10 +0100929 wi = randrange(self.vpp_worker_count)
Klement Sekera630ab582019-07-19 09:14:19 +0000930 second_packets[wi].append(p[0])
931 if len(p) <= 1:
932 continue
933 wi2 = wi
934 while wi2 == wi:
Klement Sekera8d815022021-03-15 16:58:10 +0100935 wi2 = randrange(self.vpp_worker_count)
Klement Sekera630ab582019-07-19 09:14:19 +0000936 first_packets[wi2].append(p[1])
Klement Sekera8d815022021-03-15 16:58:10 +0100937 wi3 = randrange(self.vpp_worker_count)
Klement Sekera630ab582019-07-19 09:14:19 +0000938 rest_of_packets[wi3].extend(p[2:])
939
940 self.pg_enable_capture()
941 self.send_packets(first_packets)
942 self.send_packets(second_packets)
943 self.send_packets(rest_of_packets)
944
945 packets = self.dst_if.get_capture(len(self.pkt_infos))
946 self.verify_capture(packets)
947 for send_if in self.send_ifs:
948 send_if.assert_nothing_captured()
949
Klement Sekera68bae5b2019-10-10 18:57:34 +0000950 self.logger.debug(self.vapi.ppcli("show trace"))
951 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
952 self.logger.debug(self.vapi.ppcli("show buffers"))
953 self.vapi.cli("clear trace")
954
Klement Sekera630ab582019-07-19 09:14:19 +0000955 self.pg_enable_capture()
956 self.send_packets(first_packets)
957 self.send_packets(second_packets)
958 self.send_packets(rest_of_packets)
959
960 packets = self.dst_if.get_capture(len(self.pkt_infos))
961 self.verify_capture(packets)
962 for send_if in self.send_ifs:
963 send_if.assert_nothing_captured()
964
965
Klement Sekera947a85c2019-07-24 12:40:37 +0000966class TestIPv6Reassembly(VppTestCase):
Klement Sekera75e7d132017-09-20 08:26:30 +0200967 """ IPv6 Reassembly """
968
969 @classmethod
970 def setUpClass(cls):
971 super(TestIPv6Reassembly, cls).setUpClass()
972
Klement Sekera4c533132018-02-22 11:41:12 +0100973 cls.create_pg_interfaces([0, 1])
974 cls.src_if = cls.pg0
975 cls.dst_if = cls.pg1
Klement Sekera75e7d132017-09-20 08:26:30 +0200976
977 # setup all interfaces
978 for i in cls.pg_interfaces:
979 i.admin_up()
980 i.config_ip6()
981 i.resolve_ndp()
982
Klement Sekera75e7d132017-09-20 08:26:30 +0200983 # packet sizes
984 cls.packet_sizes = [64, 512, 1518, 9018]
985 cls.padding = " abcdefghijklmn"
986 cls.create_stream(cls.packet_sizes)
987 cls.create_fragments()
988
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -0700989 @classmethod
990 def tearDownClass(cls):
991 super(TestIPv6Reassembly, cls).tearDownClass()
992
Klement Sekera75e7d132017-09-20 08:26:30 +0200993 def setUp(self):
994 """ Test setup - force timeout on existing reassemblies """
995 super(TestIPv6Reassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +0100996 self.vapi.ip_reassembly_enable_disable(
997 sw_if_index=self.src_if.sw_if_index, enable_ip6=True)
Klement Sekera75e7d132017-09-20 08:26:30 +0200998 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +0200999 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001000 expire_walk_interval_ms=10, is_ip6=1)
BenoƮt Ganne56eccdb2021-08-20 09:18:31 +02001001 self.virtual_sleep(.25)
Klement Sekera75e7d132017-09-20 08:26:30 +02001002 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001003 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001004 expire_walk_interval_ms=10000, is_ip6=1)
Klement Sekera896c8962019-06-24 11:52:49 +00001005 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +01001006 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +02001007
1008 def tearDown(self):
1009 super(TestIPv6Reassembly, self).tearDown()
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -07001010
1011 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +00001012 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +01001013 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +02001014
1015 @classmethod
1016 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
1017 """Create input packet stream for defined interface.
1018
1019 :param list packet_sizes: Required packet sizes.
1020 """
1021 for i in range(0, packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01001022 info = cls.create_packet_info(cls.src_if, cls.src_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02001023 payload = cls.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +01001024 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
1025 IPv6(src=cls.src_if.remote_ip6,
1026 dst=cls.dst_if.remote_ip6) /
1027 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001028 Raw(payload))
1029 size = packet_sizes[(i // 2) % len(packet_sizes)]
1030 cls.extend_packet(p, size, cls.padding)
1031 info.data = p
1032
1033 @classmethod
1034 def create_fragments(cls):
1035 infos = cls._packet_infos
1036 cls.pkt_infos = []
Paul Vinciguerra090096b2020-12-03 00:42:46 -05001037 for index, info in infos.items():
Klement Sekera75e7d132017-09-20 08:26:30 +02001038 p = info.data
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001039 # cls.logger.debug(ppp("Packet:",
1040 # p.__class__(scapy.compat.raw(p))))
Klement Sekera75e7d132017-09-20 08:26:30 +02001041 fragments_400 = fragment_rfc8200(p, info.index, 400)
1042 fragments_300 = fragment_rfc8200(p, info.index, 300)
1043 cls.pkt_infos.append((index, fragments_400, fragments_300))
1044 cls.fragments_400 = [
1045 x for _, frags, _ in cls.pkt_infos for x in frags]
1046 cls.fragments_300 = [
1047 x for _, _, frags in cls.pkt_infos for x in frags]
1048 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
1049 "and %s 300-byte fragments" %
1050 (len(infos), len(cls.fragments_400),
1051 len(cls.fragments_300)))
1052
Klement Sekera947a85c2019-07-24 12:40:37 +00001053 def verify_capture(self, capture, dropped_packet_indexes=[]):
1054 """Verify captured packet strea .
1055
1056 :param list capture: Captured packet stream.
1057 """
1058 info = None
1059 seen = set()
1060 for packet in capture:
1061 try:
1062 self.logger.debug(ppp("Got packet:", packet))
1063 ip = packet[IPv6]
1064 udp = packet[UDP]
1065 payload_info = self.payload_to_info(packet[Raw])
1066 packet_index = payload_info.index
1067 self.assertTrue(
1068 packet_index not in dropped_packet_indexes,
1069 ppp("Packet received, but should be dropped:", packet))
1070 if packet_index in seen:
1071 raise Exception(ppp("Duplicate packet received", packet))
1072 seen.add(packet_index)
1073 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
1074 info = self._packet_infos[packet_index]
1075 self.assertTrue(info is not None)
1076 self.assertEqual(packet_index, info.index)
1077 saved_packet = info.data
1078 self.assertEqual(ip.src, saved_packet[IPv6].src)
1079 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
1080 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1081 except Exception:
1082 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1083 raise
1084 for index in self._packet_infos:
1085 self.assertTrue(index in seen or index in dropped_packet_indexes,
1086 "Packet with packet_index %d not received" % index)
1087
1088 def test_reassembly(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001089 """ basic reassembly """
1090
Klement Sekera947a85c2019-07-24 12:40:37 +00001091 self.pg_enable_capture()
1092 self.src_if.add_stream(self.fragments_400)
1093 self.pg_start()
1094
1095 packets = self.dst_if.get_capture(len(self.pkt_infos))
1096 self.verify_capture(packets)
1097 self.src_if.assert_nothing_captured()
1098
1099 # run it all again to verify correctness
1100 self.pg_enable_capture()
1101 self.src_if.add_stream(self.fragments_400)
1102 self.pg_start()
1103
1104 packets = self.dst_if.get_capture(len(self.pkt_infos))
1105 self.verify_capture(packets)
1106 self.src_if.assert_nothing_captured()
1107
Klement Sekera769145c2019-03-06 11:59:57 +01001108 def test_buffer_boundary(self):
1109 """ fragment header crossing buffer boundary """
1110
1111 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1112 IPv6(src=self.src_if.remote_ip6,
1113 dst=self.src_if.local_ip6) /
1114 IPv6ExtHdrHopByHop(
1115 options=[HBHOptUnknown(otype=0xff, optlen=0)] * 1000) /
1116 IPv6ExtHdrFragment(m=1) /
1117 UDP(sport=1234, dport=5678) /
1118 Raw())
1119 self.pg_enable_capture()
1120 self.src_if.add_stream([p])
1121 self.pg_start()
1122 self.src_if.assert_nothing_captured()
1123 self.dst_if.assert_nothing_captured()
1124
Klement Sekera53be16d2020-12-15 21:47:36 +01001125 def test_verify_clear_trace_mid_reassembly(self):
1126 """ verify clear trace works mid-reassembly """
1127
1128 self.pg_enable_capture()
1129 self.src_if.add_stream(self.fragments_400[0:-1])
1130 self.pg_start()
1131
1132 self.logger.debug(self.vapi.cli("show trace"))
1133 self.vapi.cli("clear trace")
1134
1135 self.src_if.add_stream(self.fragments_400[-1])
1136 self.pg_start()
1137 packets = self.dst_if.get_capture(len(self.pkt_infos))
1138 self.verify_capture(packets)
1139
Klement Sekera947a85c2019-07-24 12:40:37 +00001140 def test_reversed(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001141 """ reverse order reassembly """
1142
Klement Sekera947a85c2019-07-24 12:40:37 +00001143 fragments = list(self.fragments_400)
1144 fragments.reverse()
1145
1146 self.pg_enable_capture()
1147 self.src_if.add_stream(fragments)
1148 self.pg_start()
1149
1150 packets = self.dst_if.get_capture(len(self.pkt_infos))
1151 self.verify_capture(packets)
1152 self.src_if.assert_nothing_captured()
1153
1154 # run it all again to verify correctness
1155 self.pg_enable_capture()
1156 self.src_if.add_stream(fragments)
1157 self.pg_start()
1158
1159 packets = self.dst_if.get_capture(len(self.pkt_infos))
1160 self.verify_capture(packets)
1161 self.src_if.assert_nothing_captured()
1162
1163 def test_random(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001164 """ random order reassembly """
1165
Klement Sekera947a85c2019-07-24 12:40:37 +00001166 fragments = list(self.fragments_400)
1167 shuffle(fragments)
1168
1169 self.pg_enable_capture()
1170 self.src_if.add_stream(fragments)
1171 self.pg_start()
1172
1173 packets = self.dst_if.get_capture(len(self.pkt_infos))
1174 self.verify_capture(packets)
1175 self.src_if.assert_nothing_captured()
1176
1177 # run it all again to verify correctness
1178 self.pg_enable_capture()
1179 self.src_if.add_stream(fragments)
1180 self.pg_start()
1181
1182 packets = self.dst_if.get_capture(len(self.pkt_infos))
1183 self.verify_capture(packets)
1184 self.src_if.assert_nothing_captured()
1185
1186 def test_duplicates(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001187 """ duplicate fragments """
1188
1189 fragments = [
1190 x for (_, frags, _) in self.pkt_infos
1191 for x in frags
1192 for _ in range(0, min(2, len(frags)))
1193 ]
Klement Sekera947a85c2019-07-24 12:40:37 +00001194
1195 self.pg_enable_capture()
1196 self.src_if.add_stream(fragments)
1197 self.pg_start()
1198
1199 packets = self.dst_if.get_capture(len(self.pkt_infos))
1200 self.verify_capture(packets)
1201 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +02001202
Klement Sekera3a343d42019-05-16 14:35:46 +02001203 def test_long_fragment_chain(self):
1204 """ long fragment chain """
1205
1206 error_cnt_str = \
Klement Sekera896c8962019-06-24 11:52:49 +00001207 "/err/ip6-full-reassembly-feature/fragment chain too long (drop)"
Klement Sekera3a343d42019-05-16 14:35:46 +02001208
Klement Sekera34641f22019-05-22 20:18:26 +02001209 error_cnt = self.statistics.get_err_counter(error_cnt_str)
Klement Sekera3a343d42019-05-16 14:35:46 +02001210
1211 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
1212 max_reassembly_length=3,
1213 expire_walk_interval_ms=50, is_ip6=1)
1214
1215 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1216 IPv6(src=self.src_if.remote_ip6,
1217 dst=self.dst_if.remote_ip6) /
1218 UDP(sport=1234, dport=5678) /
Ole Troan127fbec2019-10-18 15:22:56 +02001219 Raw(b"X" * 1000))
Klement Sekera3a343d42019-05-16 14:35:46 +02001220 frags = fragment_rfc8200(p, 1, 300) + fragment_rfc8200(p, 2, 500)
1221
1222 self.pg_enable_capture()
1223 self.src_if.add_stream(frags)
1224 self.pg_start()
1225
1226 self.dst_if.get_capture(1)
Klement Sekera34641f22019-05-22 20:18:26 +02001227 self.assert_error_counter_equal(error_cnt_str, error_cnt + 1)
Klement Sekera3a343d42019-05-16 14:35:46 +02001228
Klement Sekera75e7d132017-09-20 08:26:30 +02001229 def test_overlap1(self):
Klement Sekera947a85c2019-07-24 12:40:37 +00001230 """ overlapping fragments case #1 """
Klement Sekera75e7d132017-09-20 08:26:30 +02001231
1232 fragments = []
1233 for _, frags_400, frags_300 in self.pkt_infos:
1234 if len(frags_300) == 1:
1235 fragments.extend(frags_400)
1236 else:
1237 for i, j in zip(frags_300, frags_400):
1238 fragments.extend(i)
1239 fragments.extend(j)
1240
1241 dropped_packet_indexes = set(
1242 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
1243 )
1244
1245 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001246 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001247 self.pg_start()
1248
Klement Sekera4c533132018-02-22 11:41:12 +01001249 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +02001250 len(self.pkt_infos) - len(dropped_packet_indexes))
Klement Sekera947a85c2019-07-24 12:40:37 +00001251 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001252 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +02001253
1254 def test_overlap2(self):
Klement Sekera947a85c2019-07-24 12:40:37 +00001255 """ overlapping fragments case #2 """
Klement Sekera75e7d132017-09-20 08:26:30 +02001256
1257 fragments = []
Klement Sekera4c533132018-02-22 11:41:12 +01001258 for _, frags_400, frags_300 in self.pkt_infos:
Klement Sekera75e7d132017-09-20 08:26:30 +02001259 if len(frags_400) == 1:
1260 fragments.extend(frags_400)
1261 else:
1262 # care must be taken here so that there are no fragments
1263 # received by vpp after reassembly is finished, otherwise
1264 # new reassemblies will be started and packet generator will
1265 # freak out when it detects unfreed buffers
Klement Sekera4c533132018-02-22 11:41:12 +01001266 zipped = zip(frags_400, frags_300)
Paul Vinciguerrac7834e02019-03-02 10:43:05 -08001267 for i, j in zipped:
Klement Sekera75e7d132017-09-20 08:26:30 +02001268 fragments.extend(i)
1269 fragments.extend(j)
Paul Vinciguerrac7834e02019-03-02 10:43:05 -08001270 fragments.pop()
Klement Sekera75e7d132017-09-20 08:26:30 +02001271
1272 dropped_packet_indexes = set(
1273 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
1274 )
1275
1276 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001277 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001278 self.pg_start()
1279
Klement Sekera4c533132018-02-22 11:41:12 +01001280 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +02001281 len(self.pkt_infos) - len(dropped_packet_indexes))
Klement Sekera947a85c2019-07-24 12:40:37 +00001282 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001283 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +02001284
Klement Sekera947a85c2019-07-24 12:40:37 +00001285 def test_timeout_inline(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001286 """ timeout (inline) """
1287
1288 dropped_packet_indexes = set(
1289 index for (index, frags, _) in self.pkt_infos if len(frags) > 1
1290 )
1291
Klement Sekera947a85c2019-07-24 12:40:37 +00001292 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1293 max_reassembly_length=3,
1294 expire_walk_interval_ms=10000, is_ip6=1)
1295
1296 self.pg_enable_capture()
1297 self.src_if.add_stream(self.fragments_400)
1298 self.pg_start()
1299
1300 packets = self.dst_if.get_capture(
1301 len(self.pkt_infos) - len(dropped_packet_indexes))
1302 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001303 pkts = self.src_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +02001304 expected_count=len(dropped_packet_indexes))
1305 for icmp in pkts:
1306 self.assertIn(ICMPv6TimeExceeded, icmp)
1307 self.assertIn(IPv6ExtHdrFragment, icmp)
1308 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
1309 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
1310
1311 def test_timeout_cleanup(self):
1312 """ timeout (cleanup) """
1313
1314 # whole packets + fragmented packets sans last fragment
1315 fragments = [
1316 x for (_, frags_400, _) in self.pkt_infos
1317 for x in frags_400[:-1 if len(frags_400) > 1 else None]
1318 ]
1319
1320 # last fragments for fragmented packets
1321 fragments2 = [frags_400[-1]
1322 for (_, frags_400, _) in self.pkt_infos
1323 if len(frags_400) > 1]
1324
1325 dropped_packet_indexes = set(
1326 index for (index, frags_400, _) in self.pkt_infos
1327 if len(frags_400) > 1)
1328
1329 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001330 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001331 expire_walk_interval_ms=50)
1332
1333 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001334 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001335 expire_walk_interval_ms=50, is_ip6=1)
1336
1337 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001338 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001339 self.pg_start()
1340
BenoƮt Ganne56eccdb2021-08-20 09:18:31 +02001341 self.virtual_sleep(.25, "wait before sending rest of fragments")
Klement Sekera75e7d132017-09-20 08:26:30 +02001342
Klement Sekera4c533132018-02-22 11:41:12 +01001343 self.src_if.add_stream(fragments2)
Klement Sekera75e7d132017-09-20 08:26:30 +02001344 self.pg_start()
Klement Sekera75e7d132017-09-20 08:26:30 +02001345
Klement Sekera4c533132018-02-22 11:41:12 +01001346 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +02001347 len(self.pkt_infos) - len(dropped_packet_indexes))
Klement Sekera947a85c2019-07-24 12:40:37 +00001348 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001349 pkts = self.src_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +02001350 expected_count=len(dropped_packet_indexes))
1351 for icmp in pkts:
1352 self.assertIn(ICMPv6TimeExceeded, icmp)
1353 self.assertIn(IPv6ExtHdrFragment, icmp)
1354 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
1355 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
1356
Klement Sekera947a85c2019-07-24 12:40:37 +00001357 def test_disabled(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001358 """ reassembly disabled """
1359
1360 dropped_packet_indexes = set(
1361 index for (index, frags_400, _) in self.pkt_infos
1362 if len(frags_400) > 1)
Klement Sekera947a85c2019-07-24 12:40:37 +00001363
1364 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
1365 max_reassembly_length=3,
1366 expire_walk_interval_ms=10000, is_ip6=1)
1367
1368 self.pg_enable_capture()
1369 self.src_if.add_stream(self.fragments_400)
1370 self.pg_start()
1371
1372 packets = self.dst_if.get_capture(
1373 len(self.pkt_infos) - len(dropped_packet_indexes))
1374 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001375 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +02001376
1377 def test_missing_upper(self):
1378 """ missing upper layer """
Ole Troan03092c12021-11-23 15:55:39 +01001379 optdata = '\x00' * 100
Klement Sekera4c533132018-02-22 11:41:12 +01001380 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1381 IPv6(src=self.src_if.remote_ip6,
1382 dst=self.src_if.local_ip6) /
Ole Troan03092c12021-11-23 15:55:39 +01001383 IPv6ExtHdrFragment(m=1) /
1384 IPv6ExtHdrDestOpt(nh=17, options=PadN(optdata='\101' * 255) /
1385 PadN(optdata='\102'*255)))
1386
Klement Sekera75e7d132017-09-20 08:26:30 +02001387 self.pg_enable_capture()
Ole Troan03092c12021-11-23 15:55:39 +01001388 self.src_if.add_stream([p])
Klement Sekera75e7d132017-09-20 08:26:30 +02001389 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +01001390 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +02001391 icmp = pkts[0]
1392 self.assertIn(ICMPv6ParamProblem, icmp)
1393 self.assert_equal(icmp[ICMPv6ParamProblem].code, 3, "ICMP code")
1394
1395 def test_invalid_frag_size(self):
1396 """ fragment size not a multiple of 8 """
Klement Sekera4c533132018-02-22 11:41:12 +01001397 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1398 IPv6(src=self.src_if.remote_ip6,
1399 dst=self.src_if.local_ip6) /
1400 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001401 Raw())
1402 self.extend_packet(p, 1000, self.padding)
1403 fragments = fragment_rfc8200(p, 1, 500)
1404 bad_fragment = fragments[0]
1405 self.extend_packet(bad_fragment, len(bad_fragment) + 5)
1406 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001407 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +02001408 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +01001409 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +02001410 icmp = pkts[0]
1411 self.assertIn(ICMPv6ParamProblem, icmp)
1412 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1413
1414 def test_invalid_packet_size(self):
1415 """ total packet size > 65535 """
Klement Sekera4c533132018-02-22 11:41:12 +01001416 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1417 IPv6(src=self.src_if.remote_ip6,
1418 dst=self.src_if.local_ip6) /
1419 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001420 Raw())
1421 self.extend_packet(p, 1000, self.padding)
1422 fragments = fragment_rfc8200(p, 1, 500)
1423 bad_fragment = fragments[1]
1424 bad_fragment[IPv6ExtHdrFragment].offset = 65500
1425 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001426 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +02001427 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +01001428 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +02001429 icmp = pkts[0]
1430 self.assertIn(ICMPv6ParamProblem, icmp)
1431 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1432
Ole Troan03092c12021-11-23 15:55:39 +01001433 def test_atomic_fragment(self):
1434 """ IPv6 atomic fragment """
1435 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1436 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6,
1437 nh=44, plen=65535) /
1438 IPv6ExtHdrFragment(offset=8191, m=1, res1=0xFF, res2=0xFF,
1439 nh=255, id=0xffff)/('X'*1452))
1440
1441 rx = self.send_and_expect(self.pg0, [pkt], self.pg0)
1442 self.assertIn(ICMPv6ParamProblem, rx[0])
1443
1444 def test_truncated_fragment(self):
1445 """ IPv6 truncated fragment header """
1446 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1447 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6,
1448 nh=44, plen=2) /
1449 IPv6ExtHdrFragment(nh=6))
1450
1451 self.send_and_assert_no_replies(self.pg0, [pkt], self.pg0)
1452
1453 pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) /
1454 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.remote_ip6) /
1455 ICMPv6EchoRequest())
1456 rx = self.send_and_expect(self.pg0, [pkt], self.pg0)
1457
Klement Sekera75e7d132017-09-20 08:26:30 +02001458
Klement Sekera630ab582019-07-19 09:14:19 +00001459class TestIPv6MWReassembly(VppTestCase):
1460 """ IPv6 Reassembly (multiple workers) """
Klement Sekera8d815022021-03-15 16:58:10 +01001461 vpp_worker_count = 3
Klement Sekera630ab582019-07-19 09:14:19 +00001462
1463 @classmethod
1464 def setUpClass(cls):
1465 super(TestIPv6MWReassembly, cls).setUpClass()
1466
Klement Sekera8d815022021-03-15 16:58:10 +01001467 cls.create_pg_interfaces(range(cls.vpp_worker_count+1))
Klement Sekera630ab582019-07-19 09:14:19 +00001468 cls.src_if = cls.pg0
1469 cls.send_ifs = cls.pg_interfaces[:-1]
1470 cls.dst_if = cls.pg_interfaces[-1]
1471
1472 # setup all interfaces
1473 for i in cls.pg_interfaces:
1474 i.admin_up()
1475 i.config_ip6()
1476 i.resolve_ndp()
1477
1478 # packets sizes reduced here because we are generating packets without
1479 # Ethernet headers, which are added later (diff fragments go via
1480 # different interfaces)
1481 cls.packet_sizes = [64-len(Ether()), 512-len(Ether()),
1482 1518-len(Ether()), 9018-len(Ether())]
1483 cls.padding = " abcdefghijklmn"
1484 cls.create_stream(cls.packet_sizes)
1485 cls.create_fragments()
1486
1487 @classmethod
1488 def tearDownClass(cls):
1489 super(TestIPv6MWReassembly, cls).tearDownClass()
1490
1491 def setUp(self):
1492 """ Test setup - force timeout on existing reassemblies """
1493 super(TestIPv6MWReassembly, self).setUp()
1494 for intf in self.send_ifs:
1495 self.vapi.ip_reassembly_enable_disable(
1496 sw_if_index=intf.sw_if_index, enable_ip6=True)
1497 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1498 max_reassembly_length=1000,
1499 expire_walk_interval_ms=10, is_ip6=1)
BenoƮt Ganne56eccdb2021-08-20 09:18:31 +02001500 self.virtual_sleep(.25)
Klement Sekera630ab582019-07-19 09:14:19 +00001501 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1502 max_reassembly_length=1000,
1503 expire_walk_interval_ms=1000, is_ip6=1)
1504
1505 def tearDown(self):
1506 super(TestIPv6MWReassembly, self).tearDown()
1507
1508 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +00001509 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
Klement Sekera630ab582019-07-19 09:14:19 +00001510 self.logger.debug(self.vapi.ppcli("show buffers"))
1511
1512 @classmethod
1513 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
1514 """Create input packet stream
1515
1516 :param list packet_sizes: Required packet sizes.
1517 """
1518 for i in range(0, packet_count):
1519 info = cls.create_packet_info(cls.src_if, cls.src_if)
1520 payload = cls.info_to_payload(info)
1521 p = (IPv6(src=cls.src_if.remote_ip6,
1522 dst=cls.dst_if.remote_ip6) /
1523 UDP(sport=1234, dport=5678) /
1524 Raw(payload))
1525 size = packet_sizes[(i // 2) % len(packet_sizes)]
1526 cls.extend_packet(p, size, cls.padding)
1527 info.data = p
1528
1529 @classmethod
1530 def create_fragments(cls):
1531 infos = cls._packet_infos
1532 cls.pkt_infos = []
Paul Vinciguerra090096b2020-12-03 00:42:46 -05001533 for index, info in infos.items():
Klement Sekera630ab582019-07-19 09:14:19 +00001534 p = info.data
1535 # cls.logger.debug(ppp("Packet:",
1536 # p.__class__(scapy.compat.raw(p))))
1537 fragments_400 = fragment_rfc8200(p, index, 400)
1538 cls.pkt_infos.append((index, fragments_400))
1539 cls.fragments_400 = [
1540 x for (_, frags) in cls.pkt_infos for x in frags]
1541 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, " %
1542 (len(infos), len(cls.fragments_400)))
1543
1544 def verify_capture(self, capture, dropped_packet_indexes=[]):
1545 """Verify captured packet strea .
1546
1547 :param list capture: Captured packet stream.
1548 """
1549 info = None
1550 seen = set()
1551 for packet in capture:
1552 try:
1553 self.logger.debug(ppp("Got packet:", packet))
1554 ip = packet[IPv6]
1555 udp = packet[UDP]
1556 payload_info = self.payload_to_info(packet[Raw])
1557 packet_index = payload_info.index
1558 self.assertTrue(
1559 packet_index not in dropped_packet_indexes,
1560 ppp("Packet received, but should be dropped:", packet))
1561 if packet_index in seen:
1562 raise Exception(ppp("Duplicate packet received", packet))
1563 seen.add(packet_index)
1564 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
1565 info = self._packet_infos[packet_index]
1566 self.assertTrue(info is not None)
1567 self.assertEqual(packet_index, info.index)
1568 saved_packet = info.data
1569 self.assertEqual(ip.src, saved_packet[IPv6].src)
1570 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
1571 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1572 except Exception:
1573 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1574 raise
1575 for index in self._packet_infos:
1576 self.assertTrue(index in seen or index in dropped_packet_indexes,
1577 "Packet with packet_index %d not received" % index)
1578
1579 def send_packets(self, packets):
Klement Sekera8d815022021-03-15 16:58:10 +01001580 for counter in range(self.vpp_worker_count):
Klement Sekera630ab582019-07-19 09:14:19 +00001581 if 0 == len(packets[counter]):
1582 continue
1583 send_if = self.send_ifs[counter]
1584 send_if.add_stream(
1585 (Ether(dst=send_if.local_mac, src=send_if.remote_mac) / x
1586 for x in packets[counter]),
1587 worker=counter)
1588 self.pg_start()
1589
1590 def test_worker_conflict(self):
1591 """ 1st and FO=0 fragments on different workers """
1592
1593 # in first wave we send fragments which don't start at offset 0
1594 # then we send fragments with offset 0 on a different thread
1595 # then the rest of packets on a random thread
Klement Sekera8d815022021-03-15 16:58:10 +01001596 first_packets = [[] for n in range(self.vpp_worker_count)]
1597 second_packets = [[] for n in range(self.vpp_worker_count)]
1598 rest_of_packets = [[] for n in range(self.vpp_worker_count)]
Klement Sekera630ab582019-07-19 09:14:19 +00001599 for (_, p) in self.pkt_infos:
Klement Sekera8d815022021-03-15 16:58:10 +01001600 wi = randrange(self.vpp_worker_count)
Klement Sekera630ab582019-07-19 09:14:19 +00001601 second_packets[wi].append(p[0])
1602 if len(p) <= 1:
1603 continue
1604 wi2 = wi
1605 while wi2 == wi:
Klement Sekera8d815022021-03-15 16:58:10 +01001606 wi2 = randrange(self.vpp_worker_count)
Klement Sekera630ab582019-07-19 09:14:19 +00001607 first_packets[wi2].append(p[1])
Klement Sekera8d815022021-03-15 16:58:10 +01001608 wi3 = randrange(self.vpp_worker_count)
Klement Sekera630ab582019-07-19 09:14:19 +00001609 rest_of_packets[wi3].extend(p[2:])
1610
1611 self.pg_enable_capture()
1612 self.send_packets(first_packets)
1613 self.send_packets(second_packets)
1614 self.send_packets(rest_of_packets)
1615
1616 packets = self.dst_if.get_capture(len(self.pkt_infos))
1617 self.verify_capture(packets)
1618 for send_if in self.send_ifs:
1619 send_if.assert_nothing_captured()
1620
Klement Sekera68bae5b2019-10-10 18:57:34 +00001621 self.logger.debug(self.vapi.ppcli("show trace"))
1622 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1623 self.logger.debug(self.vapi.ppcli("show buffers"))
1624 self.vapi.cli("clear trace")
1625
Klement Sekera630ab582019-07-19 09:14:19 +00001626 self.pg_enable_capture()
1627 self.send_packets(first_packets)
1628 self.send_packets(second_packets)
1629 self.send_packets(rest_of_packets)
1630
1631 packets = self.dst_if.get_capture(len(self.pkt_infos))
1632 self.verify_capture(packets)
1633 for send_if in self.send_ifs:
1634 send_if.assert_nothing_captured()
1635
1636
Klement Sekerade34c352019-06-25 11:19:22 +00001637class TestIPv6SVReassembly(VppTestCase):
1638 """ IPv6 Shallow Virtual Reassembly """
1639
1640 @classmethod
1641 def setUpClass(cls):
1642 super(TestIPv6SVReassembly, cls).setUpClass()
1643
1644 cls.create_pg_interfaces([0, 1])
1645 cls.src_if = cls.pg0
1646 cls.dst_if = cls.pg1
1647
1648 # setup all interfaces
1649 for i in cls.pg_interfaces:
1650 i.admin_up()
1651 i.config_ip6()
1652 i.resolve_ndp()
1653
1654 def setUp(self):
1655 """ Test setup - force timeout on existing reassemblies """
1656 super(TestIPv6SVReassembly, self).setUp()
1657 self.vapi.ip_reassembly_enable_disable(
1658 sw_if_index=self.src_if.sw_if_index, enable_ip6=True,
1659 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
1660 self.vapi.ip_reassembly_set(
1661 timeout_ms=0, max_reassemblies=1000,
1662 max_reassembly_length=1000,
1663 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1664 expire_walk_interval_ms=10, is_ip6=1)
BenoƮt Ganne56eccdb2021-08-20 09:18:31 +02001665 self.virtual_sleep(.25)
Klement Sekerade34c352019-06-25 11:19:22 +00001666 self.vapi.ip_reassembly_set(
1667 timeout_ms=1000000, max_reassemblies=1000,
1668 max_reassembly_length=1000,
1669 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1670 expire_walk_interval_ms=10000, is_ip6=1)
1671
1672 def tearDown(self):
1673 super(TestIPv6SVReassembly, self).tearDown()
1674 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1675 self.logger.debug(self.vapi.ppcli("show buffers"))
1676
1677 def test_basic(self):
1678 """ basic reassembly """
1679 payload_len = 1000
1680 payload = ""
1681 counter = 0
1682 while len(payload) < payload_len:
1683 payload += "%u " % counter
1684 counter += 1
1685
1686 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1687 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1688 UDP(sport=1234, dport=5678) /
1689 Raw(payload))
1690 fragments = fragment_rfc8200(p, 1, payload_len/4)
1691
1692 # send fragment #2 - should be cached inside reassembly
1693 self.pg_enable_capture()
1694 self.src_if.add_stream(fragments[1])
1695 self.pg_start()
1696 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1697 self.logger.debug(self.vapi.ppcli("show buffers"))
1698 self.logger.debug(self.vapi.ppcli("show trace"))
1699 self.dst_if.assert_nothing_captured()
1700
1701 # send fragment #1 - reassembly is finished now and both fragments
1702 # forwarded
1703 self.pg_enable_capture()
1704 self.src_if.add_stream(fragments[0])
1705 self.pg_start()
1706 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1707 self.logger.debug(self.vapi.ppcli("show buffers"))
1708 self.logger.debug(self.vapi.ppcli("show trace"))
1709 c = self.dst_if.get_capture(2)
1710 for sent, recvd in zip([fragments[1], fragments[0]], c):
1711 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1712 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1713 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1714
1715 # send rest of fragments - should be immediately forwarded
1716 self.pg_enable_capture()
1717 self.src_if.add_stream(fragments[2:])
1718 self.pg_start()
1719 c = self.dst_if.get_capture(len(fragments[2:]))
1720 for sent, recvd in zip(fragments[2:], c):
1721 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1722 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1723 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1724
Klement Sekera53be16d2020-12-15 21:47:36 +01001725 def test_verify_clear_trace_mid_reassembly(self):
1726 """ verify clear trace works mid-reassembly """
1727 payload_len = 1000
1728 payload = ""
1729 counter = 0
1730 while len(payload) < payload_len:
1731 payload += "%u " % counter
1732 counter += 1
1733
1734 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1735 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1736 UDP(sport=1234, dport=5678) /
1737 Raw(payload))
1738 fragments = fragment_rfc8200(p, 1, payload_len/4)
1739
1740 self.pg_enable_capture()
1741 self.src_if.add_stream(fragments[1])
1742 self.pg_start()
1743
1744 self.logger.debug(self.vapi.cli("show trace"))
1745 self.vapi.cli("clear trace")
1746
1747 self.pg_enable_capture()
1748 self.src_if.add_stream(fragments[0])
1749 self.pg_start()
1750 self.dst_if.get_capture(2)
1751
1752 self.logger.debug(self.vapi.cli("show trace"))
1753 self.vapi.cli("clear trace")
1754
1755 self.pg_enable_capture()
1756 self.src_if.add_stream(fragments[2:])
1757 self.pg_start()
1758 self.dst_if.get_capture(len(fragments[2:]))
1759
Klement Sekerade34c352019-06-25 11:19:22 +00001760 def test_timeout(self):
1761 """ reassembly timeout """
1762 payload_len = 1000
1763 payload = ""
1764 counter = 0
1765 while len(payload) < payload_len:
1766 payload += "%u " % counter
1767 counter += 1
1768
1769 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1770 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1771 UDP(sport=1234, dport=5678) /
1772 Raw(payload))
1773 fragments = fragment_rfc8200(p, 1, payload_len/4)
1774
1775 self.vapi.ip_reassembly_set(
1776 timeout_ms=100, max_reassemblies=1000,
1777 max_reassembly_length=1000,
1778 expire_walk_interval_ms=50,
1779 is_ip6=1,
1780 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
1781
1782 # send fragments #2 and #1 - should be forwarded
1783 self.pg_enable_capture()
1784 self.src_if.add_stream(fragments[0:2])
1785 self.pg_start()
1786 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
1787 self.logger.debug(self.vapi.ppcli("show buffers"))
1788 self.logger.debug(self.vapi.ppcli("show trace"))
1789 c = self.dst_if.get_capture(2)
1790 for sent, recvd in zip([fragments[1], fragments[0]], c):
1791 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1792 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1793 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1794
1795 # wait for cleanup
BenoƮt Ganne56eccdb2021-08-20 09:18:31 +02001796 self.virtual_sleep(.25, "wait before sending rest of fragments")
Klement Sekerade34c352019-06-25 11:19:22 +00001797
1798 # send rest of fragments - shouldn't be forwarded
1799 self.pg_enable_capture()
1800 self.src_if.add_stream(fragments[2:])
1801 self.pg_start()
1802 self.dst_if.assert_nothing_captured()
1803
1804 def test_lru(self):
1805 """ reassembly reuses LRU element """
1806
1807 self.vapi.ip_reassembly_set(
1808 timeout_ms=1000000, max_reassemblies=1,
1809 max_reassembly_length=1000,
1810 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1811 is_ip6=1, expire_walk_interval_ms=10000)
1812
1813 payload_len = 1000
1814 payload = ""
1815 counter = 0
1816 while len(payload) < payload_len:
1817 payload += "%u " % counter
1818 counter += 1
1819
1820 packet_count = 10
1821
1822 fragments = [f
1823 for i in range(packet_count)
1824 for p in (Ether(dst=self.src_if.local_mac,
1825 src=self.src_if.remote_mac) /
1826 IPv6(src=self.src_if.remote_ip6,
1827 dst=self.dst_if.remote_ip6) /
1828 UDP(sport=1234, dport=5678) /
1829 Raw(payload))
1830 for f in fragment_rfc8200(p, i, payload_len/4)]
1831
1832 self.pg_enable_capture()
1833 self.src_if.add_stream(fragments)
1834 self.pg_start()
1835 c = self.dst_if.get_capture(len(fragments))
1836 for sent, recvd in zip(fragments, c):
1837 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1838 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1839 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1840
1841
Juraj Sloboda3048b632018-10-02 11:13:53 +02001842class TestIPv4ReassemblyLocalNode(VppTestCase):
1843 """ IPv4 Reassembly for packets coming to ip4-local node """
1844
1845 @classmethod
1846 def setUpClass(cls):
1847 super(TestIPv4ReassemblyLocalNode, cls).setUpClass()
1848
1849 cls.create_pg_interfaces([0])
1850 cls.src_dst_if = cls.pg0
1851
1852 # setup all interfaces
1853 for i in cls.pg_interfaces:
1854 i.admin_up()
1855 i.config_ip4()
1856 i.resolve_arp()
1857
1858 cls.padding = " abcdefghijklmn"
1859 cls.create_stream()
1860 cls.create_fragments()
1861
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -07001862 @classmethod
1863 def tearDownClass(cls):
1864 super(TestIPv4ReassemblyLocalNode, cls).tearDownClass()
1865
Juraj Sloboda3048b632018-10-02 11:13:53 +02001866 def setUp(self):
1867 """ Test setup - force timeout on existing reassemblies """
1868 super(TestIPv4ReassemblyLocalNode, self).setUp()
1869 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001870 max_reassembly_length=1000,
Juraj Sloboda3048b632018-10-02 11:13:53 +02001871 expire_walk_interval_ms=10)
BenoƮt Ganne56eccdb2021-08-20 09:18:31 +02001872 self.virtual_sleep(.25)
Juraj Sloboda3048b632018-10-02 11:13:53 +02001873 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001874 max_reassembly_length=1000,
Juraj Sloboda3048b632018-10-02 11:13:53 +02001875 expire_walk_interval_ms=10000)
1876
1877 def tearDown(self):
1878 super(TestIPv4ReassemblyLocalNode, self).tearDown()
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -07001879
1880 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +00001881 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +01001882 self.logger.debug(self.vapi.ppcli("show buffers"))
Juraj Sloboda3048b632018-10-02 11:13:53 +02001883
1884 @classmethod
1885 def create_stream(cls, packet_count=test_packet_count):
1886 """Create input packet stream for defined interface.
1887
1888 :param list packet_sizes: Required packet sizes.
1889 """
1890 for i in range(0, packet_count):
1891 info = cls.create_packet_info(cls.src_dst_if, cls.src_dst_if)
1892 payload = cls.info_to_payload(info)
1893 p = (Ether(dst=cls.src_dst_if.local_mac,
1894 src=cls.src_dst_if.remote_mac) /
1895 IP(id=info.index, src=cls.src_dst_if.remote_ip4,
1896 dst=cls.src_dst_if.local_ip4) /
1897 ICMP(type='echo-request', id=1234) /
1898 Raw(payload))
1899 cls.extend_packet(p, 1518, cls.padding)
1900 info.data = p
1901
1902 @classmethod
1903 def create_fragments(cls):
1904 infos = cls._packet_infos
1905 cls.pkt_infos = []
Paul Vinciguerra090096b2020-12-03 00:42:46 -05001906 for index, info in infos.items():
Juraj Sloboda3048b632018-10-02 11:13:53 +02001907 p = info.data
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001908 # cls.logger.debug(ppp("Packet:",
1909 # p.__class__(scapy.compat.raw(p))))
Juraj Sloboda3048b632018-10-02 11:13:53 +02001910 fragments_300 = fragment_rfc791(p, 300)
1911 cls.pkt_infos.append((index, fragments_300))
1912 cls.fragments_300 = [x for (_, frags) in cls.pkt_infos for x in frags]
1913 cls.logger.debug("Fragmented %s packets into %s 300-byte fragments" %
1914 (len(infos), len(cls.fragments_300)))
1915
1916 def verify_capture(self, capture):
1917 """Verify captured packet stream.
1918
1919 :param list capture: Captured packet stream.
1920 """
1921 info = None
1922 seen = set()
1923 for packet in capture:
1924 try:
1925 self.logger.debug(ppp("Got packet:", packet))
1926 ip = packet[IP]
1927 icmp = packet[ICMP]
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001928 payload_info = self.payload_to_info(packet[Raw])
Juraj Sloboda3048b632018-10-02 11:13:53 +02001929 packet_index = payload_info.index
1930 if packet_index in seen:
1931 raise Exception(ppp("Duplicate packet received", packet))
1932 seen.add(packet_index)
1933 self.assertEqual(payload_info.dst, self.src_dst_if.sw_if_index)
1934 info = self._packet_infos[packet_index]
Klement Sekera14d7e902018-12-10 13:46:09 +01001935 self.assertIsNotNone(info)
Juraj Sloboda3048b632018-10-02 11:13:53 +02001936 self.assertEqual(packet_index, info.index)
1937 saved_packet = info.data
1938 self.assertEqual(ip.src, saved_packet[IP].dst)
1939 self.assertEqual(ip.dst, saved_packet[IP].src)
1940 self.assertEqual(icmp.type, 0) # echo reply
1941 self.assertEqual(icmp.id, saved_packet[ICMP].id)
1942 self.assertEqual(icmp.payload, saved_packet[ICMP].payload)
1943 except Exception:
1944 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1945 raise
1946 for index in self._packet_infos:
Klement Sekera14d7e902018-12-10 13:46:09 +01001947 self.assertIn(index, seen,
1948 "Packet with packet_index %d not received" % index)
Juraj Sloboda3048b632018-10-02 11:13:53 +02001949
1950 def test_reassembly(self):
1951 """ basic reassembly """
1952
1953 self.pg_enable_capture()
1954 self.src_dst_if.add_stream(self.fragments_300)
1955 self.pg_start()
1956
1957 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
1958 self.verify_capture(packets)
1959
1960 # run it all again to verify correctness
1961 self.pg_enable_capture()
1962 self.src_dst_if.add_stream(self.fragments_300)
1963 self.pg_start()
1964
1965 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
1966 self.verify_capture(packets)
1967
1968
Klement Sekera75e7d132017-09-20 08:26:30 +02001969class TestFIFReassembly(VppTestCase):
1970 """ Fragments in fragments reassembly """
1971
1972 @classmethod
1973 def setUpClass(cls):
1974 super(TestFIFReassembly, cls).setUpClass()
1975
Klement Sekera4c533132018-02-22 11:41:12 +01001976 cls.create_pg_interfaces([0, 1])
1977 cls.src_if = cls.pg0
1978 cls.dst_if = cls.pg1
1979 for i in cls.pg_interfaces:
1980 i.admin_up()
1981 i.config_ip4()
1982 i.resolve_arp()
1983 i.config_ip6()
1984 i.resolve_ndp()
Klement Sekera75e7d132017-09-20 08:26:30 +02001985
Klement Sekera75e7d132017-09-20 08:26:30 +02001986 cls.packet_sizes = [64, 512, 1518, 9018]
1987 cls.padding = " abcdefghijklmn"
1988
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -07001989 @classmethod
1990 def tearDownClass(cls):
1991 super(TestFIFReassembly, cls).tearDownClass()
1992
Klement Sekera75e7d132017-09-20 08:26:30 +02001993 def setUp(self):
1994 """ Test setup - force timeout on existing reassemblies """
1995 super(TestFIFReassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +01001996 self.vapi.ip_reassembly_enable_disable(
1997 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
1998 enable_ip6=True)
1999 self.vapi.ip_reassembly_enable_disable(
2000 sw_if_index=self.dst_if.sw_if_index, enable_ip4=True,
2001 enable_ip6=True)
Klement Sekera75e7d132017-09-20 08:26:30 +02002002 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02002003 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02002004 expire_walk_interval_ms=10)
2005 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02002006 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02002007 expire_walk_interval_ms=10, is_ip6=1)
BenoƮt Ganne56eccdb2021-08-20 09:18:31 +02002008 self.virtual_sleep(.25)
Klement Sekera75e7d132017-09-20 08:26:30 +02002009 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02002010 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02002011 expire_walk_interval_ms=10000)
2012 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02002013 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02002014 expire_walk_interval_ms=10000, is_ip6=1)
2015
2016 def tearDown(self):
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -07002017 super(TestFIFReassembly, self).tearDown()
2018
2019 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +00002020 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
2021 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +01002022 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +02002023
2024 def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):
2025 """Verify captured packet stream.
2026
2027 :param list capture: Captured packet stream.
2028 """
2029 info = None
2030 seen = set()
2031 for packet in capture:
2032 try:
Klement Sekera4c533132018-02-22 11:41:12 +01002033 self.logger.debug(ppp("Got packet:", packet))
Klement Sekera75e7d132017-09-20 08:26:30 +02002034 ip = packet[ip_class]
2035 udp = packet[UDP]
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08002036 payload_info = self.payload_to_info(packet[Raw])
Klement Sekera75e7d132017-09-20 08:26:30 +02002037 packet_index = payload_info.index
2038 self.assertTrue(
2039 packet_index not in dropped_packet_indexes,
2040 ppp("Packet received, but should be dropped:", packet))
2041 if packet_index in seen:
2042 raise Exception(ppp("Duplicate packet received", packet))
2043 seen.add(packet_index)
Klement Sekera4c533132018-02-22 11:41:12 +01002044 self.assertEqual(payload_info.dst, self.dst_if.sw_if_index)
Klement Sekera75e7d132017-09-20 08:26:30 +02002045 info = self._packet_infos[packet_index]
2046 self.assertTrue(info is not None)
2047 self.assertEqual(packet_index, info.index)
2048 saved_packet = info.data
2049 self.assertEqual(ip.src, saved_packet[ip_class].src)
2050 self.assertEqual(ip.dst, saved_packet[ip_class].dst)
2051 self.assertEqual(udp.payload, saved_packet[UDP].payload)
Klement Sekera4c533132018-02-22 11:41:12 +01002052 except Exception:
Klement Sekera75e7d132017-09-20 08:26:30 +02002053 self.logger.error(ppp("Unexpected or invalid packet:", packet))
2054 raise
2055 for index in self._packet_infos:
2056 self.assertTrue(index in seen or index in dropped_packet_indexes,
2057 "Packet with packet_index %d not received" % index)
2058
2059 def test_fif4(self):
2060 """ Fragments in fragments (4o4) """
2061
2062 # TODO this should be ideally in setUpClass, but then we hit a bug
2063 # with VppIpRoute incorrectly reporting it's present when it's not
2064 # so we need to manually remove the vpp config, thus we cannot have
2065 # it shared for multiple test cases
2066 self.tun_ip4 = "1.1.1.2"
2067
Klement Sekera4c533132018-02-22 11:41:12 +01002068 self.gre4 = VppGreInterface(self, self.src_if.local_ip4, self.tun_ip4)
Klement Sekera75e7d132017-09-20 08:26:30 +02002069 self.gre4.add_vpp_config()
2070 self.gre4.admin_up()
2071 self.gre4.config_ip4()
2072
Klement Sekera4c533132018-02-22 11:41:12 +01002073 self.vapi.ip_reassembly_enable_disable(
2074 sw_if_index=self.gre4.sw_if_index, enable_ip4=True)
2075
Klement Sekera75e7d132017-09-20 08:26:30 +02002076 self.route4 = VppIpRoute(self, self.tun_ip4, 32,
Klement Sekera4c533132018-02-22 11:41:12 +01002077 [VppRoutePath(self.src_if.remote_ip4,
2078 self.src_if.sw_if_index)])
Klement Sekera75e7d132017-09-20 08:26:30 +02002079 self.route4.add_vpp_config()
2080
2081 self.reset_packet_infos()
2082 for i in range(test_packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01002083 info = self.create_packet_info(self.src_if, self.dst_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02002084 payload = self.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +01002085 # Ethernet header here is only for size calculation, thus it
2086 # doesn't matter how it's initialized. This is to ensure that
2087 # reassembled packet is not > 9000 bytes, so that it's not dropped
2088 p = (Ether() /
2089 IP(id=i, src=self.src_if.remote_ip4,
2090 dst=self.dst_if.remote_ip4) /
2091 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02002092 Raw(payload))
2093 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
2094 self.extend_packet(p, size, self.padding)
Klement Sekera4c533132018-02-22 11:41:12 +01002095 info.data = p[IP] # use only IP part, without ethernet header
Klement Sekera75e7d132017-09-20 08:26:30 +02002096
Paul Vinciguerra090096b2020-12-03 00:42:46 -05002097 fragments = [x for _, p in self._packet_infos.items()
Klement Sekera75e7d132017-09-20 08:26:30 +02002098 for x in fragment_rfc791(p.data, 400)]
2099
2100 encapped_fragments = \
Klement Sekera4c533132018-02-22 11:41:12 +01002101 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
2102 IP(src=self.tun_ip4, dst=self.src_if.local_ip4) /
Klement Sekera75e7d132017-09-20 08:26:30 +02002103 GRE() /
2104 p
2105 for p in fragments]
2106
2107 fragmented_encapped_fragments = \
2108 [x for p in encapped_fragments
2109 for x in fragment_rfc791(p, 200)]
2110
Klement Sekera4c533132018-02-22 11:41:12 +01002111 self.src_if.add_stream(fragmented_encapped_fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02002112
2113 self.pg_enable_capture(self.pg_interfaces)
2114 self.pg_start()
2115
Klement Sekera4c533132018-02-22 11:41:12 +01002116 self.src_if.assert_nothing_captured()
2117 packets = self.dst_if.get_capture(len(self._packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +02002118 self.verify_capture(packets, IP)
2119
2120 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
2121 # so that it's query_vpp_config() works as it should
2122 self.gre4.remove_vpp_config()
Klement Sekera4c533132018-02-22 11:41:12 +01002123 self.logger.debug(self.vapi.ppcli("show interface"))
Klement Sekera75e7d132017-09-20 08:26:30 +02002124
2125 def test_fif6(self):
2126 """ Fragments in fragments (6o6) """
2127 # TODO this should be ideally in setUpClass, but then we hit a bug
2128 # with VppIpRoute incorrectly reporting it's present when it's not
2129 # so we need to manually remove the vpp config, thus we cannot have
2130 # it shared for multiple test cases
2131 self.tun_ip6 = "1002::1"
2132
Neale Ranns5a8844b2019-04-16 07:15:35 +00002133 self.gre6 = VppGreInterface(self, self.src_if.local_ip6, self.tun_ip6)
Klement Sekera75e7d132017-09-20 08:26:30 +02002134 self.gre6.add_vpp_config()
2135 self.gre6.admin_up()
2136 self.gre6.config_ip6()
2137
Klement Sekera4c533132018-02-22 11:41:12 +01002138 self.vapi.ip_reassembly_enable_disable(
2139 sw_if_index=self.gre6.sw_if_index, enable_ip6=True)
2140
Klement Sekera75e7d132017-09-20 08:26:30 +02002141 self.route6 = VppIpRoute(self, self.tun_ip6, 128,
Neale Ranns097fa662018-05-01 05:17:55 -07002142 [VppRoutePath(
2143 self.src_if.remote_ip6,
2144 self.src_if.sw_if_index)])
Klement Sekera75e7d132017-09-20 08:26:30 +02002145 self.route6.add_vpp_config()
2146
2147 self.reset_packet_infos()
2148 for i in range(test_packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01002149 info = self.create_packet_info(self.src_if, self.dst_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02002150 payload = self.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +01002151 # Ethernet header here is only for size calculation, thus it
2152 # doesn't matter how it's initialized. This is to ensure that
2153 # reassembled packet is not > 9000 bytes, so that it's not dropped
2154 p = (Ether() /
2155 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
2156 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02002157 Raw(payload))
2158 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
2159 self.extend_packet(p, size, self.padding)
Klement Sekera4c533132018-02-22 11:41:12 +01002160 info.data = p[IPv6] # use only IPv6 part, without ethernet header
Klement Sekera75e7d132017-09-20 08:26:30 +02002161
Paul Vinciguerra090096b2020-12-03 00:42:46 -05002162 fragments = [x for _, i in self._packet_infos.items()
Klement Sekera75e7d132017-09-20 08:26:30 +02002163 for x in fragment_rfc8200(
2164 i.data, i.index, 400)]
2165
2166 encapped_fragments = \
Klement Sekera4c533132018-02-22 11:41:12 +01002167 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
2168 IPv6(src=self.tun_ip6, dst=self.src_if.local_ip6) /
Klement Sekera75e7d132017-09-20 08:26:30 +02002169 GRE() /
2170 p
2171 for p in fragments]
2172
2173 fragmented_encapped_fragments = \
2174 [x for p in encapped_fragments for x in (
2175 fragment_rfc8200(
2176 p,
2177 2 * len(self._packet_infos) + p[IPv6ExtHdrFragment].id,
2178 200)
2179 if IPv6ExtHdrFragment in p else [p]
2180 )
2181 ]
2182
Klement Sekera4c533132018-02-22 11:41:12 +01002183 self.src_if.add_stream(fragmented_encapped_fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02002184
2185 self.pg_enable_capture(self.pg_interfaces)
2186 self.pg_start()
2187
Klement Sekera4c533132018-02-22 11:41:12 +01002188 self.src_if.assert_nothing_captured()
2189 packets = self.dst_if.get_capture(len(self._packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +02002190 self.verify_capture(packets, IPv6)
2191
2192 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
2193 # so that it's query_vpp_config() works as it should
Klement Sekera3ecc2212018-03-27 10:34:43 +02002194 self.gre6.remove_vpp_config()
Klement Sekera75e7d132017-09-20 08:26:30 +02002195
2196
2197if __name__ == '__main__':
2198 unittest.main(testRunner=VppTestRunner)