blob: a8dc60a2df661477ebcf10e720b60a2ed3abd047 [file] [log] [blame]
Renato Botelho do Coutoead1e532019-10-31 13:31:07 -05001#!/usr/bin/env python3
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -08002
Klement Sekera75e7d132017-09-20 08:26:30 +02003import unittest
Klement Sekera630ab582019-07-19 09:14:19 +00004from random import shuffle, choice, randrange
Klement Sekera75e7d132017-09-20 08:26:30 +02005
Klement Sekera947a85c2019-07-24 12:40:37 +00006from framework import VppTestCase, VppTestRunner
7
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07008import scapy.compat
Klement Sekera75e7d132017-09-20 08:26:30 +02009from scapy.packet import Raw
10from scapy.layers.l2 import Ether, GRE
Juraj Sloboda3048b632018-10-02 11:13:53 +020011from scapy.layers.inet import IP, UDP, ICMP
Klement Sekera769145c2019-03-06 11:59:57 +010012from scapy.layers.inet6 import HBHOptUnknown, ICMPv6ParamProblem,\
Klement Sekerade34c352019-06-25 11:19:22 +000013 ICMPv6TimeExceeded, IPv6, IPv6ExtHdrFragment, IPv6ExtHdrHopByHop
Paul Vinciguerra69555952019-03-01 08:46:29 -080014from framework import VppTestCase, VppTestRunner
Klement Sekera630ab582019-07-19 09:14:19 +000015from util import ppp, ppc, fragment_rfc791, fragment_rfc8200
Neale Ranns5a8844b2019-04-16 07:15:35 +000016from vpp_gre_interface import VppGreInterface
Neale Rannsc0a93142018-09-05 15:42:26 -070017from vpp_ip import DpoProto
Neale Ranns097fa662018-05-01 05:17:55 -070018from vpp_ip_route import VppIpRoute, VppRoutePath, FibPathProto
Klement Sekera896c8962019-06-24 11:52:49 +000019from vpp_papi import VppEnum
Klement Sekera75e7d132017-09-20 08:26:30 +020020
Klement Sekerad0f70a32018-12-14 17:24:13 +010021# 35 is enough to have >257 400-byte fragments
22test_packet_count = 35
Klement Sekera75e7d132017-09-20 08:26:30 +020023
Klement Sekera630ab582019-07-19 09:14:19 +000024# number of workers used for multi-worker test cases
25worker_count = 3
26
Klement Sekera75e7d132017-09-20 08:26:30 +020027
Klement Sekera947a85c2019-07-24 12:40:37 +000028class TestIPv4Reassembly(VppTestCase):
Klement Sekera75e7d132017-09-20 08:26:30 +020029 """ IPv4 Reassembly """
30
31 @classmethod
32 def setUpClass(cls):
33 super(TestIPv4Reassembly, cls).setUpClass()
34
Klement Sekera4c533132018-02-22 11:41:12 +010035 cls.create_pg_interfaces([0, 1])
36 cls.src_if = cls.pg0
37 cls.dst_if = cls.pg1
Klement Sekera75e7d132017-09-20 08:26:30 +020038
39 # setup all interfaces
40 for i in cls.pg_interfaces:
41 i.admin_up()
42 i.config_ip4()
43 i.resolve_arp()
44
Klement Sekera75e7d132017-09-20 08:26:30 +020045 # packet sizes
46 cls.packet_sizes = [64, 512, 1518, 9018]
47 cls.padding = " abcdefghijklmn"
48 cls.create_stream(cls.packet_sizes)
49 cls.create_fragments()
50
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -070051 @classmethod
52 def tearDownClass(cls):
53 super(TestIPv4Reassembly, cls).tearDownClass()
54
Klement Sekera75e7d132017-09-20 08:26:30 +020055 def setUp(self):
56 """ Test setup - force timeout on existing reassemblies """
57 super(TestIPv4Reassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +010058 self.vapi.ip_reassembly_enable_disable(
59 sw_if_index=self.src_if.sw_if_index, enable_ip4=True)
Klement Sekera75e7d132017-09-20 08:26:30 +020060 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +020061 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +020062 expire_walk_interval_ms=10)
63 self.sleep(.25)
64 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +020065 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +020066 expire_walk_interval_ms=10000)
67
68 def tearDown(self):
69 super(TestIPv4Reassembly, self).tearDown()
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -070070
71 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +000072 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +010073 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +020074
75 @classmethod
76 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
Klement Sekerad0f70a32018-12-14 17:24:13 +010077 """Create input packet stream
Klement Sekera75e7d132017-09-20 08:26:30 +020078
79 :param list packet_sizes: Required packet sizes.
80 """
81 for i in range(0, packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +010082 info = cls.create_packet_info(cls.src_if, cls.src_if)
Klement Sekera75e7d132017-09-20 08:26:30 +020083 payload = cls.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +010084 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
85 IP(id=info.index, src=cls.src_if.remote_ip4,
86 dst=cls.dst_if.remote_ip4) /
87 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +020088 Raw(payload))
89 size = packet_sizes[(i // 2) % len(packet_sizes)]
90 cls.extend_packet(p, size, cls.padding)
91 info.data = p
92
93 @classmethod
94 def create_fragments(cls):
95 infos = cls._packet_infos
96 cls.pkt_infos = []
Paul Vinciguerra090096b2020-12-03 00:42:46 -050097 for index, info in infos.items():
Klement Sekera75e7d132017-09-20 08:26:30 +020098 p = info.data
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -070099 # cls.logger.debug(ppp("Packet:",
100 # p.__class__(scapy.compat.raw(p))))
Klement Sekera75e7d132017-09-20 08:26:30 +0200101 fragments_400 = fragment_rfc791(p, 400)
102 fragments_300 = fragment_rfc791(p, 300)
103 fragments_200 = [
104 x for f in fragments_400 for x in fragment_rfc791(f, 200)]
105 cls.pkt_infos.append(
106 (index, fragments_400, fragments_300, fragments_200))
107 cls.fragments_400 = [
108 x for (_, frags, _, _) in cls.pkt_infos for x in frags]
109 cls.fragments_300 = [
110 x for (_, _, frags, _) in cls.pkt_infos for x in frags]
111 cls.fragments_200 = [
112 x for (_, _, _, frags) in cls.pkt_infos for x in frags]
113 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
114 "%s 300-byte fragments and %s 200-byte fragments" %
115 (len(infos), len(cls.fragments_400),
116 len(cls.fragments_300), len(cls.fragments_200)))
117
Klement Sekera947a85c2019-07-24 12:40:37 +0000118 def verify_capture(self, capture, dropped_packet_indexes=[]):
119 """Verify captured packet stream.
120
121 :param list capture: Captured packet stream.
122 """
123 info = None
124 seen = set()
125 for packet in capture:
126 try:
127 self.logger.debug(ppp("Got packet:", packet))
128 ip = packet[IP]
129 udp = packet[UDP]
130 payload_info = self.payload_to_info(packet[Raw])
131 packet_index = payload_info.index
132 self.assertTrue(
133 packet_index not in dropped_packet_indexes,
134 ppp("Packet received, but should be dropped:", packet))
135 if packet_index in seen:
136 raise Exception(ppp("Duplicate packet received", packet))
137 seen.add(packet_index)
138 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
139 info = self._packet_infos[packet_index]
140 self.assertTrue(info is not None)
141 self.assertEqual(packet_index, info.index)
142 saved_packet = info.data
143 self.assertEqual(ip.src, saved_packet[IP].src)
144 self.assertEqual(ip.dst, saved_packet[IP].dst)
145 self.assertEqual(udp.payload, saved_packet[UDP].payload)
146 except Exception:
147 self.logger.error(ppp("Unexpected or invalid packet:", packet))
148 raise
149 for index in self._packet_infos:
150 self.assertTrue(index in seen or index in dropped_packet_indexes,
151 "Packet with packet_index %d not received" % index)
152
153 def test_reassembly(self):
Klement Sekera75e7d132017-09-20 08:26:30 +0200154 """ basic reassembly """
155
Klement Sekera947a85c2019-07-24 12:40:37 +0000156 self.pg_enable_capture()
157 self.src_if.add_stream(self.fragments_200)
158 self.pg_start()
159
160 packets = self.dst_if.get_capture(len(self.pkt_infos))
161 self.verify_capture(packets)
162 self.src_if.assert_nothing_captured()
163
164 # run it all again to verify correctness
165 self.pg_enable_capture()
166 self.src_if.add_stream(self.fragments_200)
167 self.pg_start()
168
169 packets = self.dst_if.get_capture(len(self.pkt_infos))
170 self.verify_capture(packets)
171 self.src_if.assert_nothing_captured()
172
Klement Sekera53be16d2020-12-15 21:47:36 +0100173 def test_verify_clear_trace_mid_reassembly(self):
174 """ verify clear trace works mid-reassembly """
175
176 self.pg_enable_capture()
177 self.src_if.add_stream(self.fragments_200[0:-1])
178 self.pg_start()
179
180 self.logger.debug(self.vapi.cli("show trace"))
181 self.vapi.cli("clear trace")
182
183 self.src_if.add_stream(self.fragments_200[-1])
184 self.pg_start()
185 packets = self.dst_if.get_capture(len(self.pkt_infos))
186 self.verify_capture(packets)
187
Klement Sekera947a85c2019-07-24 12:40:37 +0000188 def test_reversed(self):
Klement Sekera75e7d132017-09-20 08:26:30 +0200189 """ reverse order reassembly """
190
Klement Sekera947a85c2019-07-24 12:40:37 +0000191 fragments = list(self.fragments_200)
192 fragments.reverse()
193
194 self.pg_enable_capture()
195 self.src_if.add_stream(fragments)
196 self.pg_start()
197
198 packets = self.dst_if.get_capture(len(self.packet_infos))
199 self.verify_capture(packets)
200 self.src_if.assert_nothing_captured()
201
202 # run it all again to verify correctness
203 self.pg_enable_capture()
204 self.src_if.add_stream(fragments)
205 self.pg_start()
206
207 packets = self.dst_if.get_capture(len(self.packet_infos))
208 self.verify_capture(packets)
209 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200210
Klement Sekera3a343d42019-05-16 14:35:46 +0200211 def test_long_fragment_chain(self):
212 """ long fragment chain """
213
214 error_cnt_str = \
Klement Sekera896c8962019-06-24 11:52:49 +0000215 "/err/ip4-full-reassembly-feature/fragment chain too long (drop)"
Klement Sekera3a343d42019-05-16 14:35:46 +0200216
Klement Sekera34641f22019-05-22 20:18:26 +0200217 error_cnt = self.statistics.get_err_counter(error_cnt_str)
Klement Sekera3a343d42019-05-16 14:35:46 +0200218
219 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
220 max_reassembly_length=3,
221 expire_walk_interval_ms=50)
222
223 p1 = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
224 IP(id=1000, src=self.src_if.remote_ip4,
225 dst=self.dst_if.remote_ip4) /
226 UDP(sport=1234, dport=5678) /
Ole Troan127fbec2019-10-18 15:22:56 +0200227 Raw(b"X" * 1000))
Klement Sekera3a343d42019-05-16 14:35:46 +0200228 p2 = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
229 IP(id=1001, src=self.src_if.remote_ip4,
230 dst=self.dst_if.remote_ip4) /
231 UDP(sport=1234, dport=5678) /
Ole Troan127fbec2019-10-18 15:22:56 +0200232 Raw(b"X" * 1000))
Klement Sekera3a343d42019-05-16 14:35:46 +0200233 frags = fragment_rfc791(p1, 200) + fragment_rfc791(p2, 500)
234
235 self.pg_enable_capture()
236 self.src_if.add_stream(frags)
237 self.pg_start()
238
239 self.dst_if.get_capture(1)
Klement Sekera34641f22019-05-22 20:18:26 +0200240 self.assert_error_counter_equal(error_cnt_str, error_cnt + 1)
Klement Sekera3a343d42019-05-16 14:35:46 +0200241
Klement Sekera14d7e902018-12-10 13:46:09 +0100242 def test_5737(self):
243 """ fragment length + ip header size > 65535 """
Klement Sekera4ee633e2018-12-14 12:00:44 +0100244 self.vapi.cli("clear errors")
Ole Troan127fbec2019-10-18 15:22:56 +0200245 raw = b'''E\x00\x00\x88,\xf8\x1f\xfe@\x01\x98\x00\xc0\xa8\n-\xc0\xa8\n\
246\x01\x08\x00\xf0J\xed\xcb\xf1\xf5Test-group: IPv4.IPv4.ipv4-message.\
247Ethernet-Payload.IPv4-Packet.IPv4-Header.Fragment-Offset; Test-case: 5737'''
Klement Sekera14d7e902018-12-10 13:46:09 +0100248 malformed_packet = (Ether(dst=self.src_if.local_mac,
249 src=self.src_if.remote_mac) /
250 IP(raw))
251 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
252 IP(id=1000, src=self.src_if.remote_ip4,
253 dst=self.dst_if.remote_ip4) /
254 UDP(sport=1234, dport=5678) /
Ole Troan127fbec2019-10-18 15:22:56 +0200255 Raw(b"X" * 1000))
Klement Sekera14d7e902018-12-10 13:46:09 +0100256 valid_fragments = fragment_rfc791(p, 400)
257
Ole Troan127fbec2019-10-18 15:22:56 +0200258 counter = "/err/ip4-full-reassembly-feature/malformed packets"
259 error_counter = self.statistics.get_err_counter(counter)
Klement Sekera14d7e902018-12-10 13:46:09 +0100260 self.pg_enable_capture()
261 self.src_if.add_stream([malformed_packet] + valid_fragments)
262 self.pg_start()
263
264 self.dst_if.get_capture(1)
Klement Sekera896c8962019-06-24 11:52:49 +0000265 self.logger.debug(self.vapi.ppcli("show error"))
Ole Troan127fbec2019-10-18 15:22:56 +0200266 self.assertEqual(self.statistics.get_err_counter(counter),
267 error_counter + 1)
Klement Sekera14d7e902018-12-10 13:46:09 +0100268
Klement Sekera400f6d82018-12-13 14:35:48 +0100269 def test_44924(self):
270 """ compress tiny fragments """
271 packets = [(Ether(dst=self.src_if.local_mac,
272 src=self.src_if.remote_mac) /
273 IP(id=24339, flags="MF", frag=0, ttl=64,
274 src=self.src_if.remote_ip4,
275 dst=self.dst_if.remote_ip4) /
276 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
277 Raw(load='Test-group: IPv4')),
278 (Ether(dst=self.src_if.local_mac,
279 src=self.src_if.remote_mac) /
280 IP(id=24339, flags="MF", frag=3, ttl=64,
281 src=self.src_if.remote_ip4,
282 dst=self.dst_if.remote_ip4) /
283 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
284 Raw(load='.IPv4.Fragmentation.vali')),
285 (Ether(dst=self.src_if.local_mac,
286 src=self.src_if.remote_mac) /
287 IP(id=24339, frag=6, ttl=64,
288 src=self.src_if.remote_ip4,
289 dst=self.dst_if.remote_ip4) /
290 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
291 Raw(load='d; Test-case: 44924'))
292 ]
293
294 self.pg_enable_capture()
295 self.src_if.add_stream(packets)
296 self.pg_start()
297
298 self.dst_if.get_capture(1)
299
Klement Sekera4ee633e2018-12-14 12:00:44 +0100300 def test_frag_1(self):
301 """ fragment of size 1 """
302 self.vapi.cli("clear errors")
303 malformed_packets = [(Ether(dst=self.src_if.local_mac,
304 src=self.src_if.remote_mac) /
305 IP(id=7, len=21, flags="MF", frag=0, ttl=64,
306 src=self.src_if.remote_ip4,
307 dst=self.dst_if.remote_ip4) /
308 ICMP(type="echo-request")),
309 (Ether(dst=self.src_if.local_mac,
310 src=self.src_if.remote_mac) /
311 IP(id=7, len=21, frag=1, ttl=64,
312 src=self.src_if.remote_ip4,
313 dst=self.dst_if.remote_ip4) /
Ole Troan127fbec2019-10-18 15:22:56 +0200314 Raw(load=b'\x08')),
Klement Sekera4ee633e2018-12-14 12:00:44 +0100315 ]
316
317 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
318 IP(id=1000, src=self.src_if.remote_ip4,
319 dst=self.dst_if.remote_ip4) /
320 UDP(sport=1234, dport=5678) /
Ole Troan127fbec2019-10-18 15:22:56 +0200321 Raw(b"X" * 1000))
Klement Sekera4ee633e2018-12-14 12:00:44 +0100322 valid_fragments = fragment_rfc791(p, 400)
323
324 self.pg_enable_capture()
325 self.src_if.add_stream(malformed_packets + valid_fragments)
326 self.pg_start()
327
328 self.dst_if.get_capture(1)
329
Klement Sekera896c8962019-06-24 11:52:49 +0000330 self.assert_packet_counter_equal("ip4-full-reassembly-feature", 1)
Klement Sekera4ee633e2018-12-14 12:00:44 +0100331 # TODO remove above, uncomment below once clearing of counters
332 # is supported
333 # self.assert_packet_counter_equal(
Klement Sekera896c8962019-06-24 11:52:49 +0000334 # "/err/ip4-full-reassembly-feature/malformed packets", 1)
Klement Sekera4ee633e2018-12-14 12:00:44 +0100335
Klement Sekera947a85c2019-07-24 12:40:37 +0000336 def test_random(self):
337 """ random order reassembly """
338
339 fragments = list(self.fragments_200)
340 shuffle(fragments)
341
342 self.pg_enable_capture()
343 self.src_if.add_stream(fragments)
344 self.pg_start()
345
346 packets = self.dst_if.get_capture(len(self.packet_infos))
347 self.verify_capture(packets)
348 self.src_if.assert_nothing_captured()
349
350 # run it all again to verify correctness
351 self.pg_enable_capture()
352 self.src_if.add_stream(fragments)
353 self.pg_start()
354
355 packets = self.dst_if.get_capture(len(self.packet_infos))
356 self.verify_capture(packets)
357 self.src_if.assert_nothing_captured()
358
359 def test_duplicates(self):
Klement Sekera75e7d132017-09-20 08:26:30 +0200360 """ duplicate fragments """
Klement Sekera947a85c2019-07-24 12:40:37 +0000361
Klement Sekera75e7d132017-09-20 08:26:30 +0200362 fragments = [
363 x for (_, frags, _, _) in self.pkt_infos
364 for x in frags
365 for _ in range(0, min(2, len(frags)))
366 ]
Klement Sekera947a85c2019-07-24 12:40:37 +0000367
368 self.pg_enable_capture()
369 self.src_if.add_stream(fragments)
370 self.pg_start()
371
372 packets = self.dst_if.get_capture(len(self.pkt_infos))
373 self.verify_capture(packets)
374 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200375
376 def test_overlap1(self):
377 """ overlapping fragments case #1 """
378
379 fragments = []
380 for _, _, frags_300, frags_200 in self.pkt_infos:
381 if len(frags_300) == 1:
382 fragments.extend(frags_300)
383 else:
384 for i, j in zip(frags_200, frags_300):
385 fragments.extend(i)
386 fragments.extend(j)
387
388 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100389 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200390 self.pg_start()
391
Klement Sekera4c533132018-02-22 11:41:12 +0100392 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera947a85c2019-07-24 12:40:37 +0000393 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100394 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200395
396 # run it all to verify correctness
397 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100398 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200399 self.pg_start()
400
Klement Sekera4c533132018-02-22 11:41:12 +0100401 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera947a85c2019-07-24 12:40:37 +0000402 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100403 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200404
405 def test_overlap2(self):
406 """ overlapping fragments case #2 """
407
408 fragments = []
409 for _, _, frags_300, frags_200 in self.pkt_infos:
410 if len(frags_300) == 1:
411 fragments.extend(frags_300)
412 else:
413 # care must be taken here so that there are no fragments
414 # received by vpp after reassembly is finished, otherwise
415 # new reassemblies will be started and packet generator will
416 # freak out when it detects unfreed buffers
417 zipped = zip(frags_300, frags_200)
Paul Vinciguerrac7834e02019-03-02 10:43:05 -0800418 for i, j in zipped:
Klement Sekera75e7d132017-09-20 08:26:30 +0200419 fragments.extend(i)
420 fragments.extend(j)
Paul Vinciguerrac7834e02019-03-02 10:43:05 -0800421 fragments.pop()
Klement Sekera75e7d132017-09-20 08:26:30 +0200422
423 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100424 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200425 self.pg_start()
426
Klement Sekera4c533132018-02-22 11:41:12 +0100427 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera947a85c2019-07-24 12:40:37 +0000428 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100429 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200430
431 # run it all to verify correctness
432 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100433 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200434 self.pg_start()
435
Klement Sekera4c533132018-02-22 11:41:12 +0100436 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera947a85c2019-07-24 12:40:37 +0000437 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100438 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200439
Klement Sekera947a85c2019-07-24 12:40:37 +0000440 def test_timeout_inline(self):
Klement Sekera75e7d132017-09-20 08:26:30 +0200441 """ timeout (inline) """
442
443 dropped_packet_indexes = set(
444 index for (index, frags, _, _) in self.pkt_infos if len(frags) > 1
445 )
446
Klement Sekera947a85c2019-07-24 12:40:37 +0000447 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
448 max_reassembly_length=3,
449 expire_walk_interval_ms=10000)
450
451 self.pg_enable_capture()
452 self.src_if.add_stream(self.fragments_400)
453 self.pg_start()
454
455 packets = self.dst_if.get_capture(
456 len(self.pkt_infos) - len(dropped_packet_indexes))
457 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100458 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200459
460 def test_timeout_cleanup(self):
461 """ timeout (cleanup) """
462
463 # whole packets + fragmented packets sans last fragment
464 fragments = [
465 x for (_, frags_400, _, _) in self.pkt_infos
466 for x in frags_400[:-1 if len(frags_400) > 1 else None]
467 ]
468
469 # last fragments for fragmented packets
470 fragments2 = [frags_400[-1]
471 for (_, frags_400, _, _) in self.pkt_infos
472 if len(frags_400) > 1]
473
474 dropped_packet_indexes = set(
475 index for (index, frags_400, _, _) in self.pkt_infos
476 if len(frags_400) > 1)
477
478 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +0200479 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +0200480 expire_walk_interval_ms=50)
481
482 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100483 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200484 self.pg_start()
485
486 self.sleep(.25, "wait before sending rest of fragments")
487
Klement Sekera4c533132018-02-22 11:41:12 +0100488 self.src_if.add_stream(fragments2)
Klement Sekera75e7d132017-09-20 08:26:30 +0200489 self.pg_start()
Klement Sekera75e7d132017-09-20 08:26:30 +0200490
Klement Sekera4c533132018-02-22 11:41:12 +0100491 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200492 len(self.pkt_infos) - len(dropped_packet_indexes))
Klement Sekera947a85c2019-07-24 12:40:37 +0000493 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100494 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200495
Klement Sekera947a85c2019-07-24 12:40:37 +0000496 def test_disabled(self):
Klement Sekera75e7d132017-09-20 08:26:30 +0200497 """ reassembly disabled """
498
499 dropped_packet_indexes = set(
500 index for (index, frags_400, _, _) in self.pkt_infos
501 if len(frags_400) > 1)
Klement Sekera947a85c2019-07-24 12:40:37 +0000502
503 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
504 max_reassembly_length=3,
505 expire_walk_interval_ms=10000)
506
507 self.pg_enable_capture()
508 self.src_if.add_stream(self.fragments_400)
509 self.pg_start()
510
511 packets = self.dst_if.get_capture(
512 len(self.pkt_infos) - len(dropped_packet_indexes))
513 self.verify_capture(packets, dropped_packet_indexes)
514 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200515
516
Klement Sekerade34c352019-06-25 11:19:22 +0000517class TestIPv4SVReassembly(VppTestCase):
518 """ IPv4 Shallow Virtual Reassembly """
519
520 @classmethod
521 def setUpClass(cls):
522 super(TestIPv4SVReassembly, cls).setUpClass()
523
524 cls.create_pg_interfaces([0, 1])
525 cls.src_if = cls.pg0
526 cls.dst_if = cls.pg1
527
528 # setup all interfaces
529 for i in cls.pg_interfaces:
530 i.admin_up()
531 i.config_ip4()
532 i.resolve_arp()
533
534 def setUp(self):
535 """ Test setup - force timeout on existing reassemblies """
536 super(TestIPv4SVReassembly, self).setUp()
537 self.vapi.ip_reassembly_enable_disable(
538 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
539 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
540 self.vapi.ip_reassembly_set(
541 timeout_ms=0, max_reassemblies=1000,
542 max_reassembly_length=1000,
543 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
544 expire_walk_interval_ms=10)
545 self.sleep(.25)
546 self.vapi.ip_reassembly_set(
547 timeout_ms=1000000, max_reassemblies=1000,
548 max_reassembly_length=1000,
549 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
550 expire_walk_interval_ms=10000)
551
552 def tearDown(self):
553 super(TestIPv4SVReassembly, self).tearDown()
554 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
555 self.logger.debug(self.vapi.ppcli("show buffers"))
556
557 def test_basic(self):
558 """ basic reassembly """
559 payload_len = 1000
560 payload = ""
561 counter = 0
562 while len(payload) < payload_len:
563 payload += "%u " % counter
564 counter += 1
565
566 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
567 IP(id=1, src=self.src_if.remote_ip4,
568 dst=self.dst_if.remote_ip4) /
569 UDP(sport=1234, dport=5678) /
570 Raw(payload))
571 fragments = fragment_rfc791(p, payload_len/4)
572
573 # send fragment #2 - should be cached inside reassembly
574 self.pg_enable_capture()
575 self.src_if.add_stream(fragments[1])
576 self.pg_start()
577 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
578 self.logger.debug(self.vapi.ppcli("show buffers"))
579 self.logger.debug(self.vapi.ppcli("show trace"))
580 self.dst_if.assert_nothing_captured()
581
582 # send fragment #1 - reassembly is finished now and both fragments
583 # forwarded
584 self.pg_enable_capture()
585 self.src_if.add_stream(fragments[0])
586 self.pg_start()
587 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
588 self.logger.debug(self.vapi.ppcli("show buffers"))
589 self.logger.debug(self.vapi.ppcli("show trace"))
590 c = self.dst_if.get_capture(2)
591 for sent, recvd in zip([fragments[1], fragments[0]], c):
592 self.assertEqual(sent[IP].src, recvd[IP].src)
593 self.assertEqual(sent[IP].dst, recvd[IP].dst)
594 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
595
596 # send rest of fragments - should be immediately forwarded
597 self.pg_enable_capture()
598 self.src_if.add_stream(fragments[2:])
599 self.pg_start()
600 c = self.dst_if.get_capture(len(fragments[2:]))
601 for sent, recvd in zip(fragments[2:], c):
602 self.assertEqual(sent[IP].src, recvd[IP].src)
603 self.assertEqual(sent[IP].dst, recvd[IP].dst)
604 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
605
Klement Sekera53be16d2020-12-15 21:47:36 +0100606 def test_verify_clear_trace_mid_reassembly(self):
607 """ verify clear trace works mid-reassembly """
608 payload_len = 1000
609 payload = ""
610 counter = 0
611 while len(payload) < payload_len:
612 payload += "%u " % counter
613 counter += 1
614
615 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
616 IP(id=1, src=self.src_if.remote_ip4,
617 dst=self.dst_if.remote_ip4) /
618 UDP(sport=1234, dport=5678) /
619 Raw(payload))
620 fragments = fragment_rfc791(p, payload_len/4)
621
622 self.pg_enable_capture()
623 self.src_if.add_stream(fragments[1])
624 self.pg_start()
625
626 self.logger.debug(self.vapi.cli("show trace"))
627 self.vapi.cli("clear trace")
628
629 self.pg_enable_capture()
630 self.src_if.add_stream(fragments[0])
631 self.pg_start()
632 self.dst_if.get_capture(2)
633
634 self.logger.debug(self.vapi.cli("show trace"))
635 self.vapi.cli("clear trace")
636
637 self.pg_enable_capture()
638 self.src_if.add_stream(fragments[2:])
639 self.pg_start()
640 self.dst_if.get_capture(len(fragments[2:]))
641
Klement Sekerade34c352019-06-25 11:19:22 +0000642 def test_timeout(self):
643 """ reassembly timeout """
644 payload_len = 1000
645 payload = ""
646 counter = 0
647 while len(payload) < payload_len:
648 payload += "%u " % counter
649 counter += 1
650
651 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
652 IP(id=1, src=self.src_if.remote_ip4,
653 dst=self.dst_if.remote_ip4) /
654 UDP(sport=1234, dport=5678) /
655 Raw(payload))
656 fragments = fragment_rfc791(p, payload_len/4)
657
658 self.vapi.ip_reassembly_set(
659 timeout_ms=100, max_reassemblies=1000,
660 max_reassembly_length=1000,
661 expire_walk_interval_ms=50,
662 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
663
664 # send fragments #2 and #1 - should be forwarded
665 self.pg_enable_capture()
666 self.src_if.add_stream(fragments[0:2])
667 self.pg_start()
668 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
669 self.logger.debug(self.vapi.ppcli("show buffers"))
670 self.logger.debug(self.vapi.ppcli("show trace"))
671 c = self.dst_if.get_capture(2)
672 for sent, recvd in zip([fragments[1], fragments[0]], c):
673 self.assertEqual(sent[IP].src, recvd[IP].src)
674 self.assertEqual(sent[IP].dst, recvd[IP].dst)
675 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
676
677 # wait for cleanup
678 self.sleep(.25, "wait before sending rest of fragments")
679
680 # send rest of fragments - shouldn't be forwarded
681 self.pg_enable_capture()
682 self.src_if.add_stream(fragments[2:])
683 self.pg_start()
684 self.dst_if.assert_nothing_captured()
685
686 def test_lru(self):
687 """ reassembly reuses LRU element """
688
689 self.vapi.ip_reassembly_set(
690 timeout_ms=1000000, max_reassemblies=1,
691 max_reassembly_length=1000,
692 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
693 expire_walk_interval_ms=10000)
694
695 payload_len = 1000
696 payload = ""
697 counter = 0
698 while len(payload) < payload_len:
699 payload += "%u " % counter
700 counter += 1
701
702 packet_count = 10
703
704 fragments = [f
705 for i in range(packet_count)
706 for p in (Ether(dst=self.src_if.local_mac,
707 src=self.src_if.remote_mac) /
708 IP(id=i, src=self.src_if.remote_ip4,
709 dst=self.dst_if.remote_ip4) /
710 UDP(sport=1234, dport=5678) /
711 Raw(payload))
712 for f in fragment_rfc791(p, payload_len/4)]
713
714 self.pg_enable_capture()
715 self.src_if.add_stream(fragments)
716 self.pg_start()
717 c = self.dst_if.get_capture(len(fragments))
718 for sent, recvd in zip(fragments, c):
719 self.assertEqual(sent[IP].src, recvd[IP].src)
720 self.assertEqual(sent[IP].dst, recvd[IP].dst)
721 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
722
Klement Sekera18c6cd92020-07-10 09:29:48 +0000723 def send_mixed_and_verify_capture(self, traffic):
724 stream = []
725 for t in traffic:
726 for c in range(t['count']):
727 stream.append(
728 (Ether(dst=self.src_if.local_mac,
729 src=self.src_if.remote_mac) /
730 IP(id=self.counter,
731 flags=t['flags'],
732 src=self.src_if.remote_ip4,
733 dst=self.dst_if.remote_ip4) /
734 UDP(sport=1234, dport=5678) /
735 Raw("abcdef")))
736 self.counter = self.counter + 1
737
738 self.pg_enable_capture()
739 self.src_if.add_stream(stream)
740 self.pg_start()
741 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
742 self.logger.debug(self.vapi.ppcli("show buffers"))
743 self.logger.debug(self.vapi.ppcli("show trace"))
744 self.dst_if.get_capture(len(stream))
745
746 def test_mixed(self):
747 """ mixed traffic correctly passes through SVR """
748 self.counter = 1
749
750 self.send_mixed_and_verify_capture([{'count': 1, 'flags': ''}])
751 self.send_mixed_and_verify_capture([{'count': 2, 'flags': ''}])
752 self.send_mixed_and_verify_capture([{'count': 3, 'flags': ''}])
753 self.send_mixed_and_verify_capture([{'count': 8, 'flags': ''}])
754 self.send_mixed_and_verify_capture([{'count': 257, 'flags': ''}])
755
756 self.send_mixed_and_verify_capture([{'count': 1, 'flags': 'MF'}])
757 self.send_mixed_and_verify_capture([{'count': 2, 'flags': 'MF'}])
758 self.send_mixed_and_verify_capture([{'count': 3, 'flags': 'MF'}])
759 self.send_mixed_and_verify_capture([{'count': 8, 'flags': 'MF'}])
760 self.send_mixed_and_verify_capture([{'count': 257, 'flags': 'MF'}])
761
762 self.send_mixed_and_verify_capture(
763 [{'count': 1, 'flags': ''}, {'count': 1, 'flags': 'MF'}])
764 self.send_mixed_and_verify_capture(
765 [{'count': 2, 'flags': ''}, {'count': 2, 'flags': 'MF'}])
766 self.send_mixed_and_verify_capture(
767 [{'count': 3, 'flags': ''}, {'count': 3, 'flags': 'MF'}])
768 self.send_mixed_and_verify_capture(
769 [{'count': 8, 'flags': ''}, {'count': 8, 'flags': 'MF'}])
770 self.send_mixed_and_verify_capture(
771 [{'count': 129, 'flags': ''}, {'count': 129, 'flags': 'MF'}])
772
773 self.send_mixed_and_verify_capture(
774 [{'count': 1, 'flags': ''}, {'count': 1, 'flags': 'MF'},
775 {'count': 1, 'flags': ''}, {'count': 1, 'flags': 'MF'}])
776 self.send_mixed_and_verify_capture(
777 [{'count': 2, 'flags': ''}, {'count': 2, 'flags': 'MF'},
778 {'count': 2, 'flags': ''}, {'count': 2, 'flags': 'MF'}])
779 self.send_mixed_and_verify_capture(
780 [{'count': 3, 'flags': ''}, {'count': 3, 'flags': 'MF'},
781 {'count': 3, 'flags': ''}, {'count': 3, 'flags': 'MF'}])
782 self.send_mixed_and_verify_capture(
783 [{'count': 8, 'flags': ''}, {'count': 8, 'flags': 'MF'},
784 {'count': 8, 'flags': ''}, {'count': 8, 'flags': 'MF'}])
785 self.send_mixed_and_verify_capture(
786 [{'count': 65, 'flags': ''}, {'count': 65, 'flags': 'MF'},
787 {'count': 65, 'flags': ''}, {'count': 65, 'flags': 'MF'}])
788
Klement Sekerade34c352019-06-25 11:19:22 +0000789
Klement Sekera630ab582019-07-19 09:14:19 +0000790class TestIPv4MWReassembly(VppTestCase):
791 """ IPv4 Reassembly (multiple workers) """
792 worker_config = "workers %d" % worker_count
793
794 @classmethod
795 def setUpClass(cls):
796 super(TestIPv4MWReassembly, cls).setUpClass()
797
798 cls.create_pg_interfaces(range(worker_count+1))
799 cls.src_if = cls.pg0
800 cls.send_ifs = cls.pg_interfaces[:-1]
801 cls.dst_if = cls.pg_interfaces[-1]
802
803 # setup all interfaces
804 for i in cls.pg_interfaces:
805 i.admin_up()
806 i.config_ip4()
807 i.resolve_arp()
808
809 # packets sizes reduced here because we are generating packets without
810 # Ethernet headers, which are added later (diff fragments go via
811 # different interfaces)
812 cls.packet_sizes = [64-len(Ether()), 512-len(Ether()),
813 1518-len(Ether()), 9018-len(Ether())]
814 cls.padding = " abcdefghijklmn"
815 cls.create_stream(cls.packet_sizes)
816 cls.create_fragments()
817
818 @classmethod
819 def tearDownClass(cls):
820 super(TestIPv4MWReassembly, cls).tearDownClass()
821
822 def setUp(self):
823 """ Test setup - force timeout on existing reassemblies """
824 super(TestIPv4MWReassembly, self).setUp()
825 for intf in self.send_ifs:
826 self.vapi.ip_reassembly_enable_disable(
827 sw_if_index=intf.sw_if_index, enable_ip4=True)
828 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
829 max_reassembly_length=1000,
830 expire_walk_interval_ms=10)
831 self.sleep(.25)
832 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
833 max_reassembly_length=1000,
834 expire_walk_interval_ms=10000)
835
836 def tearDown(self):
837 super(TestIPv4MWReassembly, self).tearDown()
838
839 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +0000840 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
Klement Sekera630ab582019-07-19 09:14:19 +0000841 self.logger.debug(self.vapi.ppcli("show buffers"))
842
843 @classmethod
844 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
845 """Create input packet stream
846
847 :param list packet_sizes: Required packet sizes.
848 """
849 for i in range(0, packet_count):
850 info = cls.create_packet_info(cls.src_if, cls.src_if)
851 payload = cls.info_to_payload(info)
852 p = (IP(id=info.index, src=cls.src_if.remote_ip4,
853 dst=cls.dst_if.remote_ip4) /
854 UDP(sport=1234, dport=5678) /
855 Raw(payload))
856 size = packet_sizes[(i // 2) % len(packet_sizes)]
857 cls.extend_packet(p, size, cls.padding)
858 info.data = p
859
860 @classmethod
861 def create_fragments(cls):
862 infos = cls._packet_infos
863 cls.pkt_infos = []
Paul Vinciguerra090096b2020-12-03 00:42:46 -0500864 for index, info in infos.items():
Klement Sekera630ab582019-07-19 09:14:19 +0000865 p = info.data
866 # cls.logger.debug(ppp("Packet:",
867 # p.__class__(scapy.compat.raw(p))))
868 fragments_400 = fragment_rfc791(p, 400)
869 cls.pkt_infos.append((index, fragments_400))
870 cls.fragments_400 = [
871 x for (_, frags) in cls.pkt_infos for x in frags]
872 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, " %
873 (len(infos), len(cls.fragments_400)))
874
875 def verify_capture(self, capture, dropped_packet_indexes=[]):
876 """Verify captured packet stream.
877
878 :param list capture: Captured packet stream.
879 """
880 info = None
881 seen = set()
882 for packet in capture:
883 try:
884 self.logger.debug(ppp("Got packet:", packet))
885 ip = packet[IP]
886 udp = packet[UDP]
887 payload_info = self.payload_to_info(packet[Raw])
888 packet_index = payload_info.index
889 self.assertTrue(
890 packet_index not in dropped_packet_indexes,
891 ppp("Packet received, but should be dropped:", packet))
892 if packet_index in seen:
893 raise Exception(ppp("Duplicate packet received", packet))
894 seen.add(packet_index)
895 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
896 info = self._packet_infos[packet_index]
897 self.assertTrue(info is not None)
898 self.assertEqual(packet_index, info.index)
899 saved_packet = info.data
900 self.assertEqual(ip.src, saved_packet[IP].src)
901 self.assertEqual(ip.dst, saved_packet[IP].dst)
902 self.assertEqual(udp.payload, saved_packet[UDP].payload)
903 except Exception:
904 self.logger.error(ppp("Unexpected or invalid packet:", packet))
905 raise
906 for index in self._packet_infos:
907 self.assertTrue(index in seen or index in dropped_packet_indexes,
908 "Packet with packet_index %d not received" % index)
909
910 def send_packets(self, packets):
911 for counter in range(worker_count):
912 if 0 == len(packets[counter]):
913 continue
914 send_if = self.send_ifs[counter]
915 send_if.add_stream(
916 (Ether(dst=send_if.local_mac, src=send_if.remote_mac) / x
917 for x in packets[counter]),
918 worker=counter)
919 self.pg_start()
920
921 def test_worker_conflict(self):
922 """ 1st and FO=0 fragments on different workers """
923
924 # in first wave we send fragments which don't start at offset 0
925 # then we send fragments with offset 0 on a different thread
926 # then the rest of packets on a random thread
927 first_packets = [[] for n in range(worker_count)]
928 second_packets = [[] for n in range(worker_count)]
929 rest_of_packets = [[] for n in range(worker_count)]
930 for (_, p) in self.pkt_infos:
931 wi = randrange(worker_count)
932 second_packets[wi].append(p[0])
933 if len(p) <= 1:
934 continue
935 wi2 = wi
936 while wi2 == wi:
937 wi2 = randrange(worker_count)
938 first_packets[wi2].append(p[1])
939 wi3 = randrange(worker_count)
940 rest_of_packets[wi3].extend(p[2:])
941
942 self.pg_enable_capture()
943 self.send_packets(first_packets)
944 self.send_packets(second_packets)
945 self.send_packets(rest_of_packets)
946
947 packets = self.dst_if.get_capture(len(self.pkt_infos))
948 self.verify_capture(packets)
949 for send_if in self.send_ifs:
950 send_if.assert_nothing_captured()
951
Klement Sekera68bae5b2019-10-10 18:57:34 +0000952 self.logger.debug(self.vapi.ppcli("show trace"))
953 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
954 self.logger.debug(self.vapi.ppcli("show buffers"))
955 self.vapi.cli("clear trace")
956
Klement Sekera630ab582019-07-19 09:14:19 +0000957 self.pg_enable_capture()
958 self.send_packets(first_packets)
959 self.send_packets(second_packets)
960 self.send_packets(rest_of_packets)
961
962 packets = self.dst_if.get_capture(len(self.pkt_infos))
963 self.verify_capture(packets)
964 for send_if in self.send_ifs:
965 send_if.assert_nothing_captured()
966
967
Klement Sekera947a85c2019-07-24 12:40:37 +0000968class TestIPv6Reassembly(VppTestCase):
Klement Sekera75e7d132017-09-20 08:26:30 +0200969 """ IPv6 Reassembly """
970
971 @classmethod
972 def setUpClass(cls):
973 super(TestIPv6Reassembly, cls).setUpClass()
974
Klement Sekera4c533132018-02-22 11:41:12 +0100975 cls.create_pg_interfaces([0, 1])
976 cls.src_if = cls.pg0
977 cls.dst_if = cls.pg1
Klement Sekera75e7d132017-09-20 08:26:30 +0200978
979 # setup all interfaces
980 for i in cls.pg_interfaces:
981 i.admin_up()
982 i.config_ip6()
983 i.resolve_ndp()
984
Klement Sekera75e7d132017-09-20 08:26:30 +0200985 # packet sizes
986 cls.packet_sizes = [64, 512, 1518, 9018]
987 cls.padding = " abcdefghijklmn"
988 cls.create_stream(cls.packet_sizes)
989 cls.create_fragments()
990
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -0700991 @classmethod
992 def tearDownClass(cls):
993 super(TestIPv6Reassembly, cls).tearDownClass()
994
Klement Sekera75e7d132017-09-20 08:26:30 +0200995 def setUp(self):
996 """ Test setup - force timeout on existing reassemblies """
997 super(TestIPv6Reassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +0100998 self.vapi.ip_reassembly_enable_disable(
999 sw_if_index=self.src_if.sw_if_index, enable_ip6=True)
Klement Sekera75e7d132017-09-20 08:26:30 +02001000 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001001 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001002 expire_walk_interval_ms=10, is_ip6=1)
1003 self.sleep(.25)
1004 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001005 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001006 expire_walk_interval_ms=10000, is_ip6=1)
Klement Sekera896c8962019-06-24 11:52:49 +00001007 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +01001008 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +02001009
1010 def tearDown(self):
1011 super(TestIPv6Reassembly, self).tearDown()
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -07001012
1013 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +00001014 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +01001015 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +02001016
1017 @classmethod
1018 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
1019 """Create input packet stream for defined interface.
1020
1021 :param list packet_sizes: Required packet sizes.
1022 """
1023 for i in range(0, packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01001024 info = cls.create_packet_info(cls.src_if, cls.src_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02001025 payload = cls.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +01001026 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
1027 IPv6(src=cls.src_if.remote_ip6,
1028 dst=cls.dst_if.remote_ip6) /
1029 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001030 Raw(payload))
1031 size = packet_sizes[(i // 2) % len(packet_sizes)]
1032 cls.extend_packet(p, size, cls.padding)
1033 info.data = p
1034
1035 @classmethod
1036 def create_fragments(cls):
1037 infos = cls._packet_infos
1038 cls.pkt_infos = []
Paul Vinciguerra090096b2020-12-03 00:42:46 -05001039 for index, info in infos.items():
Klement Sekera75e7d132017-09-20 08:26:30 +02001040 p = info.data
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001041 # cls.logger.debug(ppp("Packet:",
1042 # p.__class__(scapy.compat.raw(p))))
Klement Sekera75e7d132017-09-20 08:26:30 +02001043 fragments_400 = fragment_rfc8200(p, info.index, 400)
1044 fragments_300 = fragment_rfc8200(p, info.index, 300)
1045 cls.pkt_infos.append((index, fragments_400, fragments_300))
1046 cls.fragments_400 = [
1047 x for _, frags, _ in cls.pkt_infos for x in frags]
1048 cls.fragments_300 = [
1049 x for _, _, frags in cls.pkt_infos for x in frags]
1050 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
1051 "and %s 300-byte fragments" %
1052 (len(infos), len(cls.fragments_400),
1053 len(cls.fragments_300)))
1054
Klement Sekera947a85c2019-07-24 12:40:37 +00001055 def verify_capture(self, capture, dropped_packet_indexes=[]):
1056 """Verify captured packet strea .
1057
1058 :param list capture: Captured packet stream.
1059 """
1060 info = None
1061 seen = set()
1062 for packet in capture:
1063 try:
1064 self.logger.debug(ppp("Got packet:", packet))
1065 ip = packet[IPv6]
1066 udp = packet[UDP]
1067 payload_info = self.payload_to_info(packet[Raw])
1068 packet_index = payload_info.index
1069 self.assertTrue(
1070 packet_index not in dropped_packet_indexes,
1071 ppp("Packet received, but should be dropped:", packet))
1072 if packet_index in seen:
1073 raise Exception(ppp("Duplicate packet received", packet))
1074 seen.add(packet_index)
1075 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
1076 info = self._packet_infos[packet_index]
1077 self.assertTrue(info is not None)
1078 self.assertEqual(packet_index, info.index)
1079 saved_packet = info.data
1080 self.assertEqual(ip.src, saved_packet[IPv6].src)
1081 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
1082 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1083 except Exception:
1084 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1085 raise
1086 for index in self._packet_infos:
1087 self.assertTrue(index in seen or index in dropped_packet_indexes,
1088 "Packet with packet_index %d not received" % index)
1089
1090 def test_reassembly(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001091 """ basic reassembly """
1092
Klement Sekera947a85c2019-07-24 12:40:37 +00001093 self.pg_enable_capture()
1094 self.src_if.add_stream(self.fragments_400)
1095 self.pg_start()
1096
1097 packets = self.dst_if.get_capture(len(self.pkt_infos))
1098 self.verify_capture(packets)
1099 self.src_if.assert_nothing_captured()
1100
1101 # run it all again to verify correctness
1102 self.pg_enable_capture()
1103 self.src_if.add_stream(self.fragments_400)
1104 self.pg_start()
1105
1106 packets = self.dst_if.get_capture(len(self.pkt_infos))
1107 self.verify_capture(packets)
1108 self.src_if.assert_nothing_captured()
1109
Klement Sekera769145c2019-03-06 11:59:57 +01001110 def test_buffer_boundary(self):
1111 """ fragment header crossing buffer boundary """
1112
1113 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1114 IPv6(src=self.src_if.remote_ip6,
1115 dst=self.src_if.local_ip6) /
1116 IPv6ExtHdrHopByHop(
1117 options=[HBHOptUnknown(otype=0xff, optlen=0)] * 1000) /
1118 IPv6ExtHdrFragment(m=1) /
1119 UDP(sport=1234, dport=5678) /
1120 Raw())
1121 self.pg_enable_capture()
1122 self.src_if.add_stream([p])
1123 self.pg_start()
1124 self.src_if.assert_nothing_captured()
1125 self.dst_if.assert_nothing_captured()
1126
Klement Sekera53be16d2020-12-15 21:47:36 +01001127 def test_verify_clear_trace_mid_reassembly(self):
1128 """ verify clear trace works mid-reassembly """
1129
1130 self.pg_enable_capture()
1131 self.src_if.add_stream(self.fragments_400[0:-1])
1132 self.pg_start()
1133
1134 self.logger.debug(self.vapi.cli("show trace"))
1135 self.vapi.cli("clear trace")
1136
1137 self.src_if.add_stream(self.fragments_400[-1])
1138 self.pg_start()
1139 packets = self.dst_if.get_capture(len(self.pkt_infos))
1140 self.verify_capture(packets)
1141
Klement Sekera947a85c2019-07-24 12:40:37 +00001142 def test_reversed(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001143 """ reverse order reassembly """
1144
Klement Sekera947a85c2019-07-24 12:40:37 +00001145 fragments = list(self.fragments_400)
1146 fragments.reverse()
1147
1148 self.pg_enable_capture()
1149 self.src_if.add_stream(fragments)
1150 self.pg_start()
1151
1152 packets = self.dst_if.get_capture(len(self.pkt_infos))
1153 self.verify_capture(packets)
1154 self.src_if.assert_nothing_captured()
1155
1156 # run it all again to verify correctness
1157 self.pg_enable_capture()
1158 self.src_if.add_stream(fragments)
1159 self.pg_start()
1160
1161 packets = self.dst_if.get_capture(len(self.pkt_infos))
1162 self.verify_capture(packets)
1163 self.src_if.assert_nothing_captured()
1164
1165 def test_random(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001166 """ random order reassembly """
1167
Klement Sekera947a85c2019-07-24 12:40:37 +00001168 fragments = list(self.fragments_400)
1169 shuffle(fragments)
1170
1171 self.pg_enable_capture()
1172 self.src_if.add_stream(fragments)
1173 self.pg_start()
1174
1175 packets = self.dst_if.get_capture(len(self.pkt_infos))
1176 self.verify_capture(packets)
1177 self.src_if.assert_nothing_captured()
1178
1179 # run it all again to verify correctness
1180 self.pg_enable_capture()
1181 self.src_if.add_stream(fragments)
1182 self.pg_start()
1183
1184 packets = self.dst_if.get_capture(len(self.pkt_infos))
1185 self.verify_capture(packets)
1186 self.src_if.assert_nothing_captured()
1187
1188 def test_duplicates(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001189 """ duplicate fragments """
1190
1191 fragments = [
1192 x for (_, frags, _) in self.pkt_infos
1193 for x in frags
1194 for _ in range(0, min(2, len(frags)))
1195 ]
Klement Sekera947a85c2019-07-24 12:40:37 +00001196
1197 self.pg_enable_capture()
1198 self.src_if.add_stream(fragments)
1199 self.pg_start()
1200
1201 packets = self.dst_if.get_capture(len(self.pkt_infos))
1202 self.verify_capture(packets)
1203 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +02001204
Klement Sekera3a343d42019-05-16 14:35:46 +02001205 def test_long_fragment_chain(self):
1206 """ long fragment chain """
1207
1208 error_cnt_str = \
Klement Sekera896c8962019-06-24 11:52:49 +00001209 "/err/ip6-full-reassembly-feature/fragment chain too long (drop)"
Klement Sekera3a343d42019-05-16 14:35:46 +02001210
Klement Sekera34641f22019-05-22 20:18:26 +02001211 error_cnt = self.statistics.get_err_counter(error_cnt_str)
Klement Sekera3a343d42019-05-16 14:35:46 +02001212
1213 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
1214 max_reassembly_length=3,
1215 expire_walk_interval_ms=50, is_ip6=1)
1216
1217 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1218 IPv6(src=self.src_if.remote_ip6,
1219 dst=self.dst_if.remote_ip6) /
1220 UDP(sport=1234, dport=5678) /
Ole Troan127fbec2019-10-18 15:22:56 +02001221 Raw(b"X" * 1000))
Klement Sekera3a343d42019-05-16 14:35:46 +02001222 frags = fragment_rfc8200(p, 1, 300) + fragment_rfc8200(p, 2, 500)
1223
1224 self.pg_enable_capture()
1225 self.src_if.add_stream(frags)
1226 self.pg_start()
1227
1228 self.dst_if.get_capture(1)
Klement Sekera34641f22019-05-22 20:18:26 +02001229 self.assert_error_counter_equal(error_cnt_str, error_cnt + 1)
Klement Sekera3a343d42019-05-16 14:35:46 +02001230
Klement Sekera75e7d132017-09-20 08:26:30 +02001231 def test_overlap1(self):
Klement Sekera947a85c2019-07-24 12:40:37 +00001232 """ overlapping fragments case #1 """
Klement Sekera75e7d132017-09-20 08:26:30 +02001233
1234 fragments = []
1235 for _, frags_400, frags_300 in self.pkt_infos:
1236 if len(frags_300) == 1:
1237 fragments.extend(frags_400)
1238 else:
1239 for i, j in zip(frags_300, frags_400):
1240 fragments.extend(i)
1241 fragments.extend(j)
1242
1243 dropped_packet_indexes = set(
1244 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
1245 )
1246
1247 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001248 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001249 self.pg_start()
1250
Klement Sekera4c533132018-02-22 11:41:12 +01001251 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +02001252 len(self.pkt_infos) - len(dropped_packet_indexes))
Klement Sekera947a85c2019-07-24 12:40:37 +00001253 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001254 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +02001255
1256 def test_overlap2(self):
Klement Sekera947a85c2019-07-24 12:40:37 +00001257 """ overlapping fragments case #2 """
Klement Sekera75e7d132017-09-20 08:26:30 +02001258
1259 fragments = []
Klement Sekera4c533132018-02-22 11:41:12 +01001260 for _, frags_400, frags_300 in self.pkt_infos:
Klement Sekera75e7d132017-09-20 08:26:30 +02001261 if len(frags_400) == 1:
1262 fragments.extend(frags_400)
1263 else:
1264 # care must be taken here so that there are no fragments
1265 # received by vpp after reassembly is finished, otherwise
1266 # new reassemblies will be started and packet generator will
1267 # freak out when it detects unfreed buffers
Klement Sekera4c533132018-02-22 11:41:12 +01001268 zipped = zip(frags_400, frags_300)
Paul Vinciguerrac7834e02019-03-02 10:43:05 -08001269 for i, j in zipped:
Klement Sekera75e7d132017-09-20 08:26:30 +02001270 fragments.extend(i)
1271 fragments.extend(j)
Paul Vinciguerrac7834e02019-03-02 10:43:05 -08001272 fragments.pop()
Klement Sekera75e7d132017-09-20 08:26:30 +02001273
1274 dropped_packet_indexes = set(
1275 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
1276 )
1277
1278 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001279 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001280 self.pg_start()
1281
Klement Sekera4c533132018-02-22 11:41:12 +01001282 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +02001283 len(self.pkt_infos) - len(dropped_packet_indexes))
Klement Sekera947a85c2019-07-24 12:40:37 +00001284 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001285 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +02001286
Klement Sekera947a85c2019-07-24 12:40:37 +00001287 def test_timeout_inline(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001288 """ timeout (inline) """
1289
1290 dropped_packet_indexes = set(
1291 index for (index, frags, _) in self.pkt_infos if len(frags) > 1
1292 )
1293
Klement Sekera947a85c2019-07-24 12:40:37 +00001294 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1295 max_reassembly_length=3,
1296 expire_walk_interval_ms=10000, is_ip6=1)
1297
1298 self.pg_enable_capture()
1299 self.src_if.add_stream(self.fragments_400)
1300 self.pg_start()
1301
1302 packets = self.dst_if.get_capture(
1303 len(self.pkt_infos) - len(dropped_packet_indexes))
1304 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001305 pkts = self.src_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +02001306 expected_count=len(dropped_packet_indexes))
1307 for icmp in pkts:
1308 self.assertIn(ICMPv6TimeExceeded, icmp)
1309 self.assertIn(IPv6ExtHdrFragment, icmp)
1310 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
1311 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
1312
1313 def test_timeout_cleanup(self):
1314 """ timeout (cleanup) """
1315
1316 # whole packets + fragmented packets sans last fragment
1317 fragments = [
1318 x for (_, frags_400, _) in self.pkt_infos
1319 for x in frags_400[:-1 if len(frags_400) > 1 else None]
1320 ]
1321
1322 # last fragments for fragmented packets
1323 fragments2 = [frags_400[-1]
1324 for (_, frags_400, _) in self.pkt_infos
1325 if len(frags_400) > 1]
1326
1327 dropped_packet_indexes = set(
1328 index for (index, frags_400, _) in self.pkt_infos
1329 if len(frags_400) > 1)
1330
1331 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001332 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001333 expire_walk_interval_ms=50)
1334
1335 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001336 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001337 expire_walk_interval_ms=50, is_ip6=1)
1338
1339 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001340 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001341 self.pg_start()
1342
1343 self.sleep(.25, "wait before sending rest of fragments")
1344
Klement Sekera4c533132018-02-22 11:41:12 +01001345 self.src_if.add_stream(fragments2)
Klement Sekera75e7d132017-09-20 08:26:30 +02001346 self.pg_start()
Klement Sekera75e7d132017-09-20 08:26:30 +02001347
Klement Sekera4c533132018-02-22 11:41:12 +01001348 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +02001349 len(self.pkt_infos) - len(dropped_packet_indexes))
Klement Sekera947a85c2019-07-24 12:40:37 +00001350 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001351 pkts = self.src_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +02001352 expected_count=len(dropped_packet_indexes))
1353 for icmp in pkts:
1354 self.assertIn(ICMPv6TimeExceeded, icmp)
1355 self.assertIn(IPv6ExtHdrFragment, icmp)
1356 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
1357 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
1358
Klement Sekera947a85c2019-07-24 12:40:37 +00001359 def test_disabled(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001360 """ reassembly disabled """
1361
1362 dropped_packet_indexes = set(
1363 index for (index, frags_400, _) in self.pkt_infos
1364 if len(frags_400) > 1)
Klement Sekera947a85c2019-07-24 12:40:37 +00001365
1366 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
1367 max_reassembly_length=3,
1368 expire_walk_interval_ms=10000, is_ip6=1)
1369
1370 self.pg_enable_capture()
1371 self.src_if.add_stream(self.fragments_400)
1372 self.pg_start()
1373
1374 packets = self.dst_if.get_capture(
1375 len(self.pkt_infos) - len(dropped_packet_indexes))
1376 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001377 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +02001378
1379 def test_missing_upper(self):
1380 """ missing upper layer """
Klement Sekera4c533132018-02-22 11:41:12 +01001381 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1382 IPv6(src=self.src_if.remote_ip6,
1383 dst=self.src_if.local_ip6) /
1384 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001385 Raw())
1386 self.extend_packet(p, 1000, self.padding)
1387 fragments = fragment_rfc8200(p, 1, 500)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001388 bad_fragment = p.__class__(scapy.compat.raw(fragments[1]))
Klement Sekera75e7d132017-09-20 08:26:30 +02001389 bad_fragment[IPv6ExtHdrFragment].nh = 59
1390 bad_fragment[IPv6ExtHdrFragment].offset = 0
1391 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001392 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +02001393 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +01001394 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +02001395 icmp = pkts[0]
1396 self.assertIn(ICMPv6ParamProblem, icmp)
1397 self.assert_equal(icmp[ICMPv6ParamProblem].code, 3, "ICMP code")
1398
1399 def test_invalid_frag_size(self):
1400 """ fragment size not a multiple of 8 """
Klement Sekera4c533132018-02-22 11:41:12 +01001401 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1402 IPv6(src=self.src_if.remote_ip6,
1403 dst=self.src_if.local_ip6) /
1404 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001405 Raw())
1406 self.extend_packet(p, 1000, self.padding)
1407 fragments = fragment_rfc8200(p, 1, 500)
1408 bad_fragment = fragments[0]
1409 self.extend_packet(bad_fragment, len(bad_fragment) + 5)
1410 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001411 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +02001412 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +01001413 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +02001414 icmp = pkts[0]
1415 self.assertIn(ICMPv6ParamProblem, icmp)
1416 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1417
1418 def test_invalid_packet_size(self):
1419 """ total packet size > 65535 """
Klement Sekera4c533132018-02-22 11:41:12 +01001420 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1421 IPv6(src=self.src_if.remote_ip6,
1422 dst=self.src_if.local_ip6) /
1423 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001424 Raw())
1425 self.extend_packet(p, 1000, self.padding)
1426 fragments = fragment_rfc8200(p, 1, 500)
1427 bad_fragment = fragments[1]
1428 bad_fragment[IPv6ExtHdrFragment].offset = 65500
1429 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001430 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +02001431 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +01001432 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +02001433 icmp = pkts[0]
1434 self.assertIn(ICMPv6ParamProblem, icmp)
1435 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1436
1437
Klement Sekera630ab582019-07-19 09:14:19 +00001438class TestIPv6MWReassembly(VppTestCase):
1439 """ IPv6 Reassembly (multiple workers) """
1440 worker_config = "workers %d" % worker_count
1441
1442 @classmethod
1443 def setUpClass(cls):
1444 super(TestIPv6MWReassembly, cls).setUpClass()
1445
1446 cls.create_pg_interfaces(range(worker_count+1))
1447 cls.src_if = cls.pg0
1448 cls.send_ifs = cls.pg_interfaces[:-1]
1449 cls.dst_if = cls.pg_interfaces[-1]
1450
1451 # setup all interfaces
1452 for i in cls.pg_interfaces:
1453 i.admin_up()
1454 i.config_ip6()
1455 i.resolve_ndp()
1456
1457 # packets sizes reduced here because we are generating packets without
1458 # Ethernet headers, which are added later (diff fragments go via
1459 # different interfaces)
1460 cls.packet_sizes = [64-len(Ether()), 512-len(Ether()),
1461 1518-len(Ether()), 9018-len(Ether())]
1462 cls.padding = " abcdefghijklmn"
1463 cls.create_stream(cls.packet_sizes)
1464 cls.create_fragments()
1465
1466 @classmethod
1467 def tearDownClass(cls):
1468 super(TestIPv6MWReassembly, cls).tearDownClass()
1469
1470 def setUp(self):
1471 """ Test setup - force timeout on existing reassemblies """
1472 super(TestIPv6MWReassembly, self).setUp()
1473 for intf in self.send_ifs:
1474 self.vapi.ip_reassembly_enable_disable(
1475 sw_if_index=intf.sw_if_index, enable_ip6=True)
1476 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1477 max_reassembly_length=1000,
1478 expire_walk_interval_ms=10, is_ip6=1)
1479 self.sleep(.25)
1480 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1481 max_reassembly_length=1000,
1482 expire_walk_interval_ms=1000, is_ip6=1)
1483
1484 def tearDown(self):
1485 super(TestIPv6MWReassembly, self).tearDown()
1486
1487 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +00001488 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
Klement Sekera630ab582019-07-19 09:14:19 +00001489 self.logger.debug(self.vapi.ppcli("show buffers"))
1490
1491 @classmethod
1492 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
1493 """Create input packet stream
1494
1495 :param list packet_sizes: Required packet sizes.
1496 """
1497 for i in range(0, packet_count):
1498 info = cls.create_packet_info(cls.src_if, cls.src_if)
1499 payload = cls.info_to_payload(info)
1500 p = (IPv6(src=cls.src_if.remote_ip6,
1501 dst=cls.dst_if.remote_ip6) /
1502 UDP(sport=1234, dport=5678) /
1503 Raw(payload))
1504 size = packet_sizes[(i // 2) % len(packet_sizes)]
1505 cls.extend_packet(p, size, cls.padding)
1506 info.data = p
1507
1508 @classmethod
1509 def create_fragments(cls):
1510 infos = cls._packet_infos
1511 cls.pkt_infos = []
Paul Vinciguerra090096b2020-12-03 00:42:46 -05001512 for index, info in infos.items():
Klement Sekera630ab582019-07-19 09:14:19 +00001513 p = info.data
1514 # cls.logger.debug(ppp("Packet:",
1515 # p.__class__(scapy.compat.raw(p))))
1516 fragments_400 = fragment_rfc8200(p, index, 400)
1517 cls.pkt_infos.append((index, fragments_400))
1518 cls.fragments_400 = [
1519 x for (_, frags) in cls.pkt_infos for x in frags]
1520 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, " %
1521 (len(infos), len(cls.fragments_400)))
1522
1523 def verify_capture(self, capture, dropped_packet_indexes=[]):
1524 """Verify captured packet strea .
1525
1526 :param list capture: Captured packet stream.
1527 """
1528 info = None
1529 seen = set()
1530 for packet in capture:
1531 try:
1532 self.logger.debug(ppp("Got packet:", packet))
1533 ip = packet[IPv6]
1534 udp = packet[UDP]
1535 payload_info = self.payload_to_info(packet[Raw])
1536 packet_index = payload_info.index
1537 self.assertTrue(
1538 packet_index not in dropped_packet_indexes,
1539 ppp("Packet received, but should be dropped:", packet))
1540 if packet_index in seen:
1541 raise Exception(ppp("Duplicate packet received", packet))
1542 seen.add(packet_index)
1543 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
1544 info = self._packet_infos[packet_index]
1545 self.assertTrue(info is not None)
1546 self.assertEqual(packet_index, info.index)
1547 saved_packet = info.data
1548 self.assertEqual(ip.src, saved_packet[IPv6].src)
1549 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
1550 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1551 except Exception:
1552 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1553 raise
1554 for index in self._packet_infos:
1555 self.assertTrue(index in seen or index in dropped_packet_indexes,
1556 "Packet with packet_index %d not received" % index)
1557
1558 def send_packets(self, packets):
1559 for counter in range(worker_count):
1560 if 0 == len(packets[counter]):
1561 continue
1562 send_if = self.send_ifs[counter]
1563 send_if.add_stream(
1564 (Ether(dst=send_if.local_mac, src=send_if.remote_mac) / x
1565 for x in packets[counter]),
1566 worker=counter)
1567 self.pg_start()
1568
1569 def test_worker_conflict(self):
1570 """ 1st and FO=0 fragments on different workers """
1571
1572 # in first wave we send fragments which don't start at offset 0
1573 # then we send fragments with offset 0 on a different thread
1574 # then the rest of packets on a random thread
1575 first_packets = [[] for n in range(worker_count)]
1576 second_packets = [[] for n in range(worker_count)]
1577 rest_of_packets = [[] for n in range(worker_count)]
1578 for (_, p) in self.pkt_infos:
1579 wi = randrange(worker_count)
1580 second_packets[wi].append(p[0])
1581 if len(p) <= 1:
1582 continue
1583 wi2 = wi
1584 while wi2 == wi:
1585 wi2 = randrange(worker_count)
1586 first_packets[wi2].append(p[1])
1587 wi3 = randrange(worker_count)
1588 rest_of_packets[wi3].extend(p[2:])
1589
1590 self.pg_enable_capture()
1591 self.send_packets(first_packets)
1592 self.send_packets(second_packets)
1593 self.send_packets(rest_of_packets)
1594
1595 packets = self.dst_if.get_capture(len(self.pkt_infos))
1596 self.verify_capture(packets)
1597 for send_if in self.send_ifs:
1598 send_if.assert_nothing_captured()
1599
Klement Sekera68bae5b2019-10-10 18:57:34 +00001600 self.logger.debug(self.vapi.ppcli("show trace"))
1601 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1602 self.logger.debug(self.vapi.ppcli("show buffers"))
1603 self.vapi.cli("clear trace")
1604
Klement Sekera630ab582019-07-19 09:14:19 +00001605 self.pg_enable_capture()
1606 self.send_packets(first_packets)
1607 self.send_packets(second_packets)
1608 self.send_packets(rest_of_packets)
1609
1610 packets = self.dst_if.get_capture(len(self.pkt_infos))
1611 self.verify_capture(packets)
1612 for send_if in self.send_ifs:
1613 send_if.assert_nothing_captured()
1614
1615
Klement Sekerade34c352019-06-25 11:19:22 +00001616class TestIPv6SVReassembly(VppTestCase):
1617 """ IPv6 Shallow Virtual Reassembly """
1618
1619 @classmethod
1620 def setUpClass(cls):
1621 super(TestIPv6SVReassembly, cls).setUpClass()
1622
1623 cls.create_pg_interfaces([0, 1])
1624 cls.src_if = cls.pg0
1625 cls.dst_if = cls.pg1
1626
1627 # setup all interfaces
1628 for i in cls.pg_interfaces:
1629 i.admin_up()
1630 i.config_ip6()
1631 i.resolve_ndp()
1632
1633 def setUp(self):
1634 """ Test setup - force timeout on existing reassemblies """
1635 super(TestIPv6SVReassembly, self).setUp()
1636 self.vapi.ip_reassembly_enable_disable(
1637 sw_if_index=self.src_if.sw_if_index, enable_ip6=True,
1638 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
1639 self.vapi.ip_reassembly_set(
1640 timeout_ms=0, max_reassemblies=1000,
1641 max_reassembly_length=1000,
1642 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1643 expire_walk_interval_ms=10, is_ip6=1)
1644 self.sleep(.25)
1645 self.vapi.ip_reassembly_set(
1646 timeout_ms=1000000, max_reassemblies=1000,
1647 max_reassembly_length=1000,
1648 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1649 expire_walk_interval_ms=10000, is_ip6=1)
1650
1651 def tearDown(self):
1652 super(TestIPv6SVReassembly, self).tearDown()
1653 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1654 self.logger.debug(self.vapi.ppcli("show buffers"))
1655
1656 def test_basic(self):
1657 """ basic reassembly """
1658 payload_len = 1000
1659 payload = ""
1660 counter = 0
1661 while len(payload) < payload_len:
1662 payload += "%u " % counter
1663 counter += 1
1664
1665 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1666 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1667 UDP(sport=1234, dport=5678) /
1668 Raw(payload))
1669 fragments = fragment_rfc8200(p, 1, payload_len/4)
1670
1671 # send fragment #2 - should be cached inside reassembly
1672 self.pg_enable_capture()
1673 self.src_if.add_stream(fragments[1])
1674 self.pg_start()
1675 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1676 self.logger.debug(self.vapi.ppcli("show buffers"))
1677 self.logger.debug(self.vapi.ppcli("show trace"))
1678 self.dst_if.assert_nothing_captured()
1679
1680 # send fragment #1 - reassembly is finished now and both fragments
1681 # forwarded
1682 self.pg_enable_capture()
1683 self.src_if.add_stream(fragments[0])
1684 self.pg_start()
1685 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1686 self.logger.debug(self.vapi.ppcli("show buffers"))
1687 self.logger.debug(self.vapi.ppcli("show trace"))
1688 c = self.dst_if.get_capture(2)
1689 for sent, recvd in zip([fragments[1], fragments[0]], c):
1690 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1691 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1692 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1693
1694 # send rest of fragments - should be immediately forwarded
1695 self.pg_enable_capture()
1696 self.src_if.add_stream(fragments[2:])
1697 self.pg_start()
1698 c = self.dst_if.get_capture(len(fragments[2:]))
1699 for sent, recvd in zip(fragments[2:], c):
1700 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1701 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1702 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1703
Klement Sekera53be16d2020-12-15 21:47:36 +01001704 def test_verify_clear_trace_mid_reassembly(self):
1705 """ verify clear trace works mid-reassembly """
1706 payload_len = 1000
1707 payload = ""
1708 counter = 0
1709 while len(payload) < payload_len:
1710 payload += "%u " % counter
1711 counter += 1
1712
1713 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1714 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1715 UDP(sport=1234, dport=5678) /
1716 Raw(payload))
1717 fragments = fragment_rfc8200(p, 1, payload_len/4)
1718
1719 self.pg_enable_capture()
1720 self.src_if.add_stream(fragments[1])
1721 self.pg_start()
1722
1723 self.logger.debug(self.vapi.cli("show trace"))
1724 self.vapi.cli("clear trace")
1725
1726 self.pg_enable_capture()
1727 self.src_if.add_stream(fragments[0])
1728 self.pg_start()
1729 self.dst_if.get_capture(2)
1730
1731 self.logger.debug(self.vapi.cli("show trace"))
1732 self.vapi.cli("clear trace")
1733
1734 self.pg_enable_capture()
1735 self.src_if.add_stream(fragments[2:])
1736 self.pg_start()
1737 self.dst_if.get_capture(len(fragments[2:]))
1738
Klement Sekerade34c352019-06-25 11:19:22 +00001739 def test_timeout(self):
1740 """ reassembly timeout """
1741 payload_len = 1000
1742 payload = ""
1743 counter = 0
1744 while len(payload) < payload_len:
1745 payload += "%u " % counter
1746 counter += 1
1747
1748 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1749 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1750 UDP(sport=1234, dport=5678) /
1751 Raw(payload))
1752 fragments = fragment_rfc8200(p, 1, payload_len/4)
1753
1754 self.vapi.ip_reassembly_set(
1755 timeout_ms=100, max_reassemblies=1000,
1756 max_reassembly_length=1000,
1757 expire_walk_interval_ms=50,
1758 is_ip6=1,
1759 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
1760
1761 # send fragments #2 and #1 - should be forwarded
1762 self.pg_enable_capture()
1763 self.src_if.add_stream(fragments[0:2])
1764 self.pg_start()
1765 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
1766 self.logger.debug(self.vapi.ppcli("show buffers"))
1767 self.logger.debug(self.vapi.ppcli("show trace"))
1768 c = self.dst_if.get_capture(2)
1769 for sent, recvd in zip([fragments[1], fragments[0]], c):
1770 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1771 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1772 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1773
1774 # wait for cleanup
1775 self.sleep(.25, "wait before sending rest of fragments")
1776
1777 # send rest of fragments - shouldn't be forwarded
1778 self.pg_enable_capture()
1779 self.src_if.add_stream(fragments[2:])
1780 self.pg_start()
1781 self.dst_if.assert_nothing_captured()
1782
1783 def test_lru(self):
1784 """ reassembly reuses LRU element """
1785
1786 self.vapi.ip_reassembly_set(
1787 timeout_ms=1000000, max_reassemblies=1,
1788 max_reassembly_length=1000,
1789 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1790 is_ip6=1, expire_walk_interval_ms=10000)
1791
1792 payload_len = 1000
1793 payload = ""
1794 counter = 0
1795 while len(payload) < payload_len:
1796 payload += "%u " % counter
1797 counter += 1
1798
1799 packet_count = 10
1800
1801 fragments = [f
1802 for i in range(packet_count)
1803 for p in (Ether(dst=self.src_if.local_mac,
1804 src=self.src_if.remote_mac) /
1805 IPv6(src=self.src_if.remote_ip6,
1806 dst=self.dst_if.remote_ip6) /
1807 UDP(sport=1234, dport=5678) /
1808 Raw(payload))
1809 for f in fragment_rfc8200(p, i, payload_len/4)]
1810
1811 self.pg_enable_capture()
1812 self.src_if.add_stream(fragments)
1813 self.pg_start()
1814 c = self.dst_if.get_capture(len(fragments))
1815 for sent, recvd in zip(fragments, c):
1816 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1817 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1818 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1819
1820
Juraj Sloboda3048b632018-10-02 11:13:53 +02001821class TestIPv4ReassemblyLocalNode(VppTestCase):
1822 """ IPv4 Reassembly for packets coming to ip4-local node """
1823
1824 @classmethod
1825 def setUpClass(cls):
1826 super(TestIPv4ReassemblyLocalNode, cls).setUpClass()
1827
1828 cls.create_pg_interfaces([0])
1829 cls.src_dst_if = cls.pg0
1830
1831 # setup all interfaces
1832 for i in cls.pg_interfaces:
1833 i.admin_up()
1834 i.config_ip4()
1835 i.resolve_arp()
1836
1837 cls.padding = " abcdefghijklmn"
1838 cls.create_stream()
1839 cls.create_fragments()
1840
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -07001841 @classmethod
1842 def tearDownClass(cls):
1843 super(TestIPv4ReassemblyLocalNode, cls).tearDownClass()
1844
Juraj Sloboda3048b632018-10-02 11:13:53 +02001845 def setUp(self):
1846 """ Test setup - force timeout on existing reassemblies """
1847 super(TestIPv4ReassemblyLocalNode, self).setUp()
1848 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001849 max_reassembly_length=1000,
Juraj Sloboda3048b632018-10-02 11:13:53 +02001850 expire_walk_interval_ms=10)
1851 self.sleep(.25)
1852 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001853 max_reassembly_length=1000,
Juraj Sloboda3048b632018-10-02 11:13:53 +02001854 expire_walk_interval_ms=10000)
1855
1856 def tearDown(self):
1857 super(TestIPv4ReassemblyLocalNode, self).tearDown()
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -07001858
1859 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +00001860 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +01001861 self.logger.debug(self.vapi.ppcli("show buffers"))
Juraj Sloboda3048b632018-10-02 11:13:53 +02001862
1863 @classmethod
1864 def create_stream(cls, packet_count=test_packet_count):
1865 """Create input packet stream for defined interface.
1866
1867 :param list packet_sizes: Required packet sizes.
1868 """
1869 for i in range(0, packet_count):
1870 info = cls.create_packet_info(cls.src_dst_if, cls.src_dst_if)
1871 payload = cls.info_to_payload(info)
1872 p = (Ether(dst=cls.src_dst_if.local_mac,
1873 src=cls.src_dst_if.remote_mac) /
1874 IP(id=info.index, src=cls.src_dst_if.remote_ip4,
1875 dst=cls.src_dst_if.local_ip4) /
1876 ICMP(type='echo-request', id=1234) /
1877 Raw(payload))
1878 cls.extend_packet(p, 1518, cls.padding)
1879 info.data = p
1880
1881 @classmethod
1882 def create_fragments(cls):
1883 infos = cls._packet_infos
1884 cls.pkt_infos = []
Paul Vinciguerra090096b2020-12-03 00:42:46 -05001885 for index, info in infos.items():
Juraj Sloboda3048b632018-10-02 11:13:53 +02001886 p = info.data
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001887 # cls.logger.debug(ppp("Packet:",
1888 # p.__class__(scapy.compat.raw(p))))
Juraj Sloboda3048b632018-10-02 11:13:53 +02001889 fragments_300 = fragment_rfc791(p, 300)
1890 cls.pkt_infos.append((index, fragments_300))
1891 cls.fragments_300 = [x for (_, frags) in cls.pkt_infos for x in frags]
1892 cls.logger.debug("Fragmented %s packets into %s 300-byte fragments" %
1893 (len(infos), len(cls.fragments_300)))
1894
1895 def verify_capture(self, capture):
1896 """Verify captured packet stream.
1897
1898 :param list capture: Captured packet stream.
1899 """
1900 info = None
1901 seen = set()
1902 for packet in capture:
1903 try:
1904 self.logger.debug(ppp("Got packet:", packet))
1905 ip = packet[IP]
1906 icmp = packet[ICMP]
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001907 payload_info = self.payload_to_info(packet[Raw])
Juraj Sloboda3048b632018-10-02 11:13:53 +02001908 packet_index = payload_info.index
1909 if packet_index in seen:
1910 raise Exception(ppp("Duplicate packet received", packet))
1911 seen.add(packet_index)
1912 self.assertEqual(payload_info.dst, self.src_dst_if.sw_if_index)
1913 info = self._packet_infos[packet_index]
Klement Sekera14d7e902018-12-10 13:46:09 +01001914 self.assertIsNotNone(info)
Juraj Sloboda3048b632018-10-02 11:13:53 +02001915 self.assertEqual(packet_index, info.index)
1916 saved_packet = info.data
1917 self.assertEqual(ip.src, saved_packet[IP].dst)
1918 self.assertEqual(ip.dst, saved_packet[IP].src)
1919 self.assertEqual(icmp.type, 0) # echo reply
1920 self.assertEqual(icmp.id, saved_packet[ICMP].id)
1921 self.assertEqual(icmp.payload, saved_packet[ICMP].payload)
1922 except Exception:
1923 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1924 raise
1925 for index in self._packet_infos:
Klement Sekera14d7e902018-12-10 13:46:09 +01001926 self.assertIn(index, seen,
1927 "Packet with packet_index %d not received" % index)
Juraj Sloboda3048b632018-10-02 11:13:53 +02001928
1929 def test_reassembly(self):
1930 """ basic reassembly """
1931
1932 self.pg_enable_capture()
1933 self.src_dst_if.add_stream(self.fragments_300)
1934 self.pg_start()
1935
1936 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
1937 self.verify_capture(packets)
1938
1939 # run it all again to verify correctness
1940 self.pg_enable_capture()
1941 self.src_dst_if.add_stream(self.fragments_300)
1942 self.pg_start()
1943
1944 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
1945 self.verify_capture(packets)
1946
1947
Klement Sekera75e7d132017-09-20 08:26:30 +02001948class TestFIFReassembly(VppTestCase):
1949 """ Fragments in fragments reassembly """
1950
1951 @classmethod
1952 def setUpClass(cls):
1953 super(TestFIFReassembly, cls).setUpClass()
1954
Klement Sekera4c533132018-02-22 11:41:12 +01001955 cls.create_pg_interfaces([0, 1])
1956 cls.src_if = cls.pg0
1957 cls.dst_if = cls.pg1
1958 for i in cls.pg_interfaces:
1959 i.admin_up()
1960 i.config_ip4()
1961 i.resolve_arp()
1962 i.config_ip6()
1963 i.resolve_ndp()
Klement Sekera75e7d132017-09-20 08:26:30 +02001964
Klement Sekera75e7d132017-09-20 08:26:30 +02001965 cls.packet_sizes = [64, 512, 1518, 9018]
1966 cls.padding = " abcdefghijklmn"
1967
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -07001968 @classmethod
1969 def tearDownClass(cls):
1970 super(TestFIFReassembly, cls).tearDownClass()
1971
Klement Sekera75e7d132017-09-20 08:26:30 +02001972 def setUp(self):
1973 """ Test setup - force timeout on existing reassemblies """
1974 super(TestFIFReassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +01001975 self.vapi.ip_reassembly_enable_disable(
1976 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
1977 enable_ip6=True)
1978 self.vapi.ip_reassembly_enable_disable(
1979 sw_if_index=self.dst_if.sw_if_index, enable_ip4=True,
1980 enable_ip6=True)
Klement Sekera75e7d132017-09-20 08:26:30 +02001981 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001982 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001983 expire_walk_interval_ms=10)
1984 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001985 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001986 expire_walk_interval_ms=10, is_ip6=1)
1987 self.sleep(.25)
1988 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001989 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001990 expire_walk_interval_ms=10000)
1991 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001992 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001993 expire_walk_interval_ms=10000, is_ip6=1)
1994
1995 def tearDown(self):
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -07001996 super(TestFIFReassembly, self).tearDown()
1997
1998 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +00001999 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
2000 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +01002001 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +02002002
2003 def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):
2004 """Verify captured packet stream.
2005
2006 :param list capture: Captured packet stream.
2007 """
2008 info = None
2009 seen = set()
2010 for packet in capture:
2011 try:
Klement Sekera4c533132018-02-22 11:41:12 +01002012 self.logger.debug(ppp("Got packet:", packet))
Klement Sekera75e7d132017-09-20 08:26:30 +02002013 ip = packet[ip_class]
2014 udp = packet[UDP]
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08002015 payload_info = self.payload_to_info(packet[Raw])
Klement Sekera75e7d132017-09-20 08:26:30 +02002016 packet_index = payload_info.index
2017 self.assertTrue(
2018 packet_index not in dropped_packet_indexes,
2019 ppp("Packet received, but should be dropped:", packet))
2020 if packet_index in seen:
2021 raise Exception(ppp("Duplicate packet received", packet))
2022 seen.add(packet_index)
Klement Sekera4c533132018-02-22 11:41:12 +01002023 self.assertEqual(payload_info.dst, self.dst_if.sw_if_index)
Klement Sekera75e7d132017-09-20 08:26:30 +02002024 info = self._packet_infos[packet_index]
2025 self.assertTrue(info is not None)
2026 self.assertEqual(packet_index, info.index)
2027 saved_packet = info.data
2028 self.assertEqual(ip.src, saved_packet[ip_class].src)
2029 self.assertEqual(ip.dst, saved_packet[ip_class].dst)
2030 self.assertEqual(udp.payload, saved_packet[UDP].payload)
Klement Sekera4c533132018-02-22 11:41:12 +01002031 except Exception:
Klement Sekera75e7d132017-09-20 08:26:30 +02002032 self.logger.error(ppp("Unexpected or invalid packet:", packet))
2033 raise
2034 for index in self._packet_infos:
2035 self.assertTrue(index in seen or index in dropped_packet_indexes,
2036 "Packet with packet_index %d not received" % index)
2037
2038 def test_fif4(self):
2039 """ Fragments in fragments (4o4) """
2040
2041 # TODO this should be ideally in setUpClass, but then we hit a bug
2042 # with VppIpRoute incorrectly reporting it's present when it's not
2043 # so we need to manually remove the vpp config, thus we cannot have
2044 # it shared for multiple test cases
2045 self.tun_ip4 = "1.1.1.2"
2046
Klement Sekera4c533132018-02-22 11:41:12 +01002047 self.gre4 = VppGreInterface(self, self.src_if.local_ip4, self.tun_ip4)
Klement Sekera75e7d132017-09-20 08:26:30 +02002048 self.gre4.add_vpp_config()
2049 self.gre4.admin_up()
2050 self.gre4.config_ip4()
2051
Klement Sekera4c533132018-02-22 11:41:12 +01002052 self.vapi.ip_reassembly_enable_disable(
2053 sw_if_index=self.gre4.sw_if_index, enable_ip4=True)
2054
Klement Sekera75e7d132017-09-20 08:26:30 +02002055 self.route4 = VppIpRoute(self, self.tun_ip4, 32,
Klement Sekera4c533132018-02-22 11:41:12 +01002056 [VppRoutePath(self.src_if.remote_ip4,
2057 self.src_if.sw_if_index)])
Klement Sekera75e7d132017-09-20 08:26:30 +02002058 self.route4.add_vpp_config()
2059
2060 self.reset_packet_infos()
2061 for i in range(test_packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01002062 info = self.create_packet_info(self.src_if, self.dst_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02002063 payload = self.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +01002064 # Ethernet header here is only for size calculation, thus it
2065 # doesn't matter how it's initialized. This is to ensure that
2066 # reassembled packet is not > 9000 bytes, so that it's not dropped
2067 p = (Ether() /
2068 IP(id=i, src=self.src_if.remote_ip4,
2069 dst=self.dst_if.remote_ip4) /
2070 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02002071 Raw(payload))
2072 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
2073 self.extend_packet(p, size, self.padding)
Klement Sekera4c533132018-02-22 11:41:12 +01002074 info.data = p[IP] # use only IP part, without ethernet header
Klement Sekera75e7d132017-09-20 08:26:30 +02002075
Paul Vinciguerra090096b2020-12-03 00:42:46 -05002076 fragments = [x for _, p in self._packet_infos.items()
Klement Sekera75e7d132017-09-20 08:26:30 +02002077 for x in fragment_rfc791(p.data, 400)]
2078
2079 encapped_fragments = \
Klement Sekera4c533132018-02-22 11:41:12 +01002080 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
2081 IP(src=self.tun_ip4, dst=self.src_if.local_ip4) /
Klement Sekera75e7d132017-09-20 08:26:30 +02002082 GRE() /
2083 p
2084 for p in fragments]
2085
2086 fragmented_encapped_fragments = \
2087 [x for p in encapped_fragments
2088 for x in fragment_rfc791(p, 200)]
2089
Klement Sekera4c533132018-02-22 11:41:12 +01002090 self.src_if.add_stream(fragmented_encapped_fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02002091
2092 self.pg_enable_capture(self.pg_interfaces)
2093 self.pg_start()
2094
Klement Sekera4c533132018-02-22 11:41:12 +01002095 self.src_if.assert_nothing_captured()
2096 packets = self.dst_if.get_capture(len(self._packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +02002097 self.verify_capture(packets, IP)
2098
2099 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
2100 # so that it's query_vpp_config() works as it should
2101 self.gre4.remove_vpp_config()
Klement Sekera4c533132018-02-22 11:41:12 +01002102 self.logger.debug(self.vapi.ppcli("show interface"))
Klement Sekera75e7d132017-09-20 08:26:30 +02002103
2104 def test_fif6(self):
2105 """ Fragments in fragments (6o6) """
2106 # TODO this should be ideally in setUpClass, but then we hit a bug
2107 # with VppIpRoute incorrectly reporting it's present when it's not
2108 # so we need to manually remove the vpp config, thus we cannot have
2109 # it shared for multiple test cases
2110 self.tun_ip6 = "1002::1"
2111
Neale Ranns5a8844b2019-04-16 07:15:35 +00002112 self.gre6 = VppGreInterface(self, self.src_if.local_ip6, self.tun_ip6)
Klement Sekera75e7d132017-09-20 08:26:30 +02002113 self.gre6.add_vpp_config()
2114 self.gre6.admin_up()
2115 self.gre6.config_ip6()
2116
Klement Sekera4c533132018-02-22 11:41:12 +01002117 self.vapi.ip_reassembly_enable_disable(
2118 sw_if_index=self.gre6.sw_if_index, enable_ip6=True)
2119
Klement Sekera75e7d132017-09-20 08:26:30 +02002120 self.route6 = VppIpRoute(self, self.tun_ip6, 128,
Neale Ranns097fa662018-05-01 05:17:55 -07002121 [VppRoutePath(
2122 self.src_if.remote_ip6,
2123 self.src_if.sw_if_index)])
Klement Sekera75e7d132017-09-20 08:26:30 +02002124 self.route6.add_vpp_config()
2125
2126 self.reset_packet_infos()
2127 for i in range(test_packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01002128 info = self.create_packet_info(self.src_if, self.dst_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02002129 payload = self.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +01002130 # Ethernet header here is only for size calculation, thus it
2131 # doesn't matter how it's initialized. This is to ensure that
2132 # reassembled packet is not > 9000 bytes, so that it's not dropped
2133 p = (Ether() /
2134 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
2135 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02002136 Raw(payload))
2137 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
2138 self.extend_packet(p, size, self.padding)
Klement Sekera4c533132018-02-22 11:41:12 +01002139 info.data = p[IPv6] # use only IPv6 part, without ethernet header
Klement Sekera75e7d132017-09-20 08:26:30 +02002140
Paul Vinciguerra090096b2020-12-03 00:42:46 -05002141 fragments = [x for _, i in self._packet_infos.items()
Klement Sekera75e7d132017-09-20 08:26:30 +02002142 for x in fragment_rfc8200(
2143 i.data, i.index, 400)]
2144
2145 encapped_fragments = \
Klement Sekera4c533132018-02-22 11:41:12 +01002146 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
2147 IPv6(src=self.tun_ip6, dst=self.src_if.local_ip6) /
Klement Sekera75e7d132017-09-20 08:26:30 +02002148 GRE() /
2149 p
2150 for p in fragments]
2151
2152 fragmented_encapped_fragments = \
2153 [x for p in encapped_fragments for x in (
2154 fragment_rfc8200(
2155 p,
2156 2 * len(self._packet_infos) + p[IPv6ExtHdrFragment].id,
2157 200)
2158 if IPv6ExtHdrFragment in p else [p]
2159 )
2160 ]
2161
Klement Sekera4c533132018-02-22 11:41:12 +01002162 self.src_if.add_stream(fragmented_encapped_fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02002163
2164 self.pg_enable_capture(self.pg_interfaces)
2165 self.pg_start()
2166
Klement Sekera4c533132018-02-22 11:41:12 +01002167 self.src_if.assert_nothing_captured()
2168 packets = self.dst_if.get_capture(len(self._packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +02002169 self.verify_capture(packets, IPv6)
2170
2171 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
2172 # so that it's query_vpp_config() works as it should
Klement Sekera3ecc2212018-03-27 10:34:43 +02002173 self.gre6.remove_vpp_config()
Klement Sekera75e7d132017-09-20 08:26:30 +02002174
2175
2176if __name__ == '__main__':
2177 unittest.main(testRunner=VppTestRunner)