blob: 124fec0a820e4ec6ca1b81a201cbd02f01fa4b6e [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
24
Klement Sekera947a85c2019-07-24 12:40:37 +000025class TestIPv4Reassembly(VppTestCase):
Klement Sekera75e7d132017-09-20 08:26:30 +020026 """ IPv4 Reassembly """
27
28 @classmethod
29 def setUpClass(cls):
30 super(TestIPv4Reassembly, cls).setUpClass()
31
Klement Sekera4c533132018-02-22 11:41:12 +010032 cls.create_pg_interfaces([0, 1])
33 cls.src_if = cls.pg0
34 cls.dst_if = cls.pg1
Klement Sekera75e7d132017-09-20 08:26:30 +020035
36 # setup all interfaces
37 for i in cls.pg_interfaces:
38 i.admin_up()
39 i.config_ip4()
40 i.resolve_arp()
41
Klement Sekera75e7d132017-09-20 08:26:30 +020042 # packet sizes
43 cls.packet_sizes = [64, 512, 1518, 9018]
44 cls.padding = " abcdefghijklmn"
45 cls.create_stream(cls.packet_sizes)
46 cls.create_fragments()
47
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -070048 @classmethod
49 def tearDownClass(cls):
50 super(TestIPv4Reassembly, cls).tearDownClass()
51
Klement Sekera75e7d132017-09-20 08:26:30 +020052 def setUp(self):
53 """ Test setup - force timeout on existing reassemblies """
54 super(TestIPv4Reassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +010055 self.vapi.ip_reassembly_enable_disable(
56 sw_if_index=self.src_if.sw_if_index, enable_ip4=True)
Klement Sekera75e7d132017-09-20 08:26:30 +020057 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +020058 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +020059 expire_walk_interval_ms=10)
60 self.sleep(.25)
61 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +020062 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +020063 expire_walk_interval_ms=10000)
64
65 def tearDown(self):
66 super(TestIPv4Reassembly, self).tearDown()
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -070067
68 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +000069 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +010070 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +020071
72 @classmethod
73 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
Klement Sekerad0f70a32018-12-14 17:24:13 +010074 """Create input packet stream
Klement Sekera75e7d132017-09-20 08:26:30 +020075
76 :param list packet_sizes: Required packet sizes.
77 """
78 for i in range(0, packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +010079 info = cls.create_packet_info(cls.src_if, cls.src_if)
Klement Sekera75e7d132017-09-20 08:26:30 +020080 payload = cls.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +010081 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
82 IP(id=info.index, src=cls.src_if.remote_ip4,
83 dst=cls.dst_if.remote_ip4) /
84 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +020085 Raw(payload))
86 size = packet_sizes[(i // 2) % len(packet_sizes)]
87 cls.extend_packet(p, size, cls.padding)
88 info.data = p
89
90 @classmethod
91 def create_fragments(cls):
92 infos = cls._packet_infos
93 cls.pkt_infos = []
Paul Vinciguerra090096b2020-12-03 00:42:46 -050094 for index, info in infos.items():
Klement Sekera75e7d132017-09-20 08:26:30 +020095 p = info.data
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -070096 # cls.logger.debug(ppp("Packet:",
97 # p.__class__(scapy.compat.raw(p))))
Klement Sekera75e7d132017-09-20 08:26:30 +020098 fragments_400 = fragment_rfc791(p, 400)
99 fragments_300 = fragment_rfc791(p, 300)
100 fragments_200 = [
101 x for f in fragments_400 for x in fragment_rfc791(f, 200)]
102 cls.pkt_infos.append(
103 (index, fragments_400, fragments_300, fragments_200))
104 cls.fragments_400 = [
105 x for (_, frags, _, _) in cls.pkt_infos for x in frags]
106 cls.fragments_300 = [
107 x for (_, _, frags, _) in cls.pkt_infos for x in frags]
108 cls.fragments_200 = [
109 x for (_, _, _, frags) in cls.pkt_infos for x in frags]
110 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
111 "%s 300-byte fragments and %s 200-byte fragments" %
112 (len(infos), len(cls.fragments_400),
113 len(cls.fragments_300), len(cls.fragments_200)))
114
Klement Sekera947a85c2019-07-24 12:40:37 +0000115 def verify_capture(self, capture, dropped_packet_indexes=[]):
116 """Verify captured packet stream.
117
118 :param list capture: Captured packet stream.
119 """
120 info = None
121 seen = set()
122 for packet in capture:
123 try:
124 self.logger.debug(ppp("Got packet:", packet))
125 ip = packet[IP]
126 udp = packet[UDP]
127 payload_info = self.payload_to_info(packet[Raw])
128 packet_index = payload_info.index
129 self.assertTrue(
130 packet_index not in dropped_packet_indexes,
131 ppp("Packet received, but should be dropped:", packet))
132 if packet_index in seen:
133 raise Exception(ppp("Duplicate packet received", packet))
134 seen.add(packet_index)
135 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
136 info = self._packet_infos[packet_index]
137 self.assertTrue(info is not None)
138 self.assertEqual(packet_index, info.index)
139 saved_packet = info.data
140 self.assertEqual(ip.src, saved_packet[IP].src)
141 self.assertEqual(ip.dst, saved_packet[IP].dst)
142 self.assertEqual(udp.payload, saved_packet[UDP].payload)
143 except Exception:
144 self.logger.error(ppp("Unexpected or invalid packet:", packet))
145 raise
146 for index in self._packet_infos:
147 self.assertTrue(index in seen or index in dropped_packet_indexes,
148 "Packet with packet_index %d not received" % index)
149
150 def test_reassembly(self):
Klement Sekera75e7d132017-09-20 08:26:30 +0200151 """ basic reassembly """
152
Klement Sekera947a85c2019-07-24 12:40:37 +0000153 self.pg_enable_capture()
154 self.src_if.add_stream(self.fragments_200)
155 self.pg_start()
156
157 packets = self.dst_if.get_capture(len(self.pkt_infos))
158 self.verify_capture(packets)
159 self.src_if.assert_nothing_captured()
160
161 # run it all again to verify correctness
162 self.pg_enable_capture()
163 self.src_if.add_stream(self.fragments_200)
164 self.pg_start()
165
166 packets = self.dst_if.get_capture(len(self.pkt_infos))
167 self.verify_capture(packets)
168 self.src_if.assert_nothing_captured()
169
Klement Sekera53be16d2020-12-15 21:47:36 +0100170 def test_verify_clear_trace_mid_reassembly(self):
171 """ verify clear trace works mid-reassembly """
172
173 self.pg_enable_capture()
174 self.src_if.add_stream(self.fragments_200[0:-1])
175 self.pg_start()
176
177 self.logger.debug(self.vapi.cli("show trace"))
178 self.vapi.cli("clear trace")
179
180 self.src_if.add_stream(self.fragments_200[-1])
181 self.pg_start()
182 packets = self.dst_if.get_capture(len(self.pkt_infos))
183 self.verify_capture(packets)
184
Klement Sekera947a85c2019-07-24 12:40:37 +0000185 def test_reversed(self):
Klement Sekera75e7d132017-09-20 08:26:30 +0200186 """ reverse order reassembly """
187
Klement Sekera947a85c2019-07-24 12:40:37 +0000188 fragments = list(self.fragments_200)
189 fragments.reverse()
190
191 self.pg_enable_capture()
192 self.src_if.add_stream(fragments)
193 self.pg_start()
194
195 packets = self.dst_if.get_capture(len(self.packet_infos))
196 self.verify_capture(packets)
197 self.src_if.assert_nothing_captured()
198
199 # run it all again to verify correctness
200 self.pg_enable_capture()
201 self.src_if.add_stream(fragments)
202 self.pg_start()
203
204 packets = self.dst_if.get_capture(len(self.packet_infos))
205 self.verify_capture(packets)
206 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200207
Klement Sekera3a343d42019-05-16 14:35:46 +0200208 def test_long_fragment_chain(self):
209 """ long fragment chain """
210
211 error_cnt_str = \
Klement Sekera896c8962019-06-24 11:52:49 +0000212 "/err/ip4-full-reassembly-feature/fragment chain too long (drop)"
Klement Sekera3a343d42019-05-16 14:35:46 +0200213
Klement Sekera34641f22019-05-22 20:18:26 +0200214 error_cnt = self.statistics.get_err_counter(error_cnt_str)
Klement Sekera3a343d42019-05-16 14:35:46 +0200215
216 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
217 max_reassembly_length=3,
218 expire_walk_interval_ms=50)
219
220 p1 = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
221 IP(id=1000, src=self.src_if.remote_ip4,
222 dst=self.dst_if.remote_ip4) /
223 UDP(sport=1234, dport=5678) /
Ole Troan127fbec2019-10-18 15:22:56 +0200224 Raw(b"X" * 1000))
Klement Sekera3a343d42019-05-16 14:35:46 +0200225 p2 = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
226 IP(id=1001, src=self.src_if.remote_ip4,
227 dst=self.dst_if.remote_ip4) /
228 UDP(sport=1234, dport=5678) /
Ole Troan127fbec2019-10-18 15:22:56 +0200229 Raw(b"X" * 1000))
Klement Sekera3a343d42019-05-16 14:35:46 +0200230 frags = fragment_rfc791(p1, 200) + fragment_rfc791(p2, 500)
231
232 self.pg_enable_capture()
233 self.src_if.add_stream(frags)
234 self.pg_start()
235
236 self.dst_if.get_capture(1)
Klement Sekera34641f22019-05-22 20:18:26 +0200237 self.assert_error_counter_equal(error_cnt_str, error_cnt + 1)
Klement Sekera3a343d42019-05-16 14:35:46 +0200238
Klement Sekera14d7e902018-12-10 13:46:09 +0100239 def test_5737(self):
240 """ fragment length + ip header size > 65535 """
Klement Sekera4ee633e2018-12-14 12:00:44 +0100241 self.vapi.cli("clear errors")
Ole Troan127fbec2019-10-18 15:22:56 +0200242 raw = b'''E\x00\x00\x88,\xf8\x1f\xfe@\x01\x98\x00\xc0\xa8\n-\xc0\xa8\n\
243\x01\x08\x00\xf0J\xed\xcb\xf1\xf5Test-group: IPv4.IPv4.ipv4-message.\
244Ethernet-Payload.IPv4-Packet.IPv4-Header.Fragment-Offset; Test-case: 5737'''
Klement Sekera14d7e902018-12-10 13:46:09 +0100245 malformed_packet = (Ether(dst=self.src_if.local_mac,
246 src=self.src_if.remote_mac) /
247 IP(raw))
248 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
249 IP(id=1000, src=self.src_if.remote_ip4,
250 dst=self.dst_if.remote_ip4) /
251 UDP(sport=1234, dport=5678) /
Ole Troan127fbec2019-10-18 15:22:56 +0200252 Raw(b"X" * 1000))
Klement Sekera14d7e902018-12-10 13:46:09 +0100253 valid_fragments = fragment_rfc791(p, 400)
254
Ole Troan127fbec2019-10-18 15:22:56 +0200255 counter = "/err/ip4-full-reassembly-feature/malformed packets"
256 error_counter = self.statistics.get_err_counter(counter)
Klement Sekera14d7e902018-12-10 13:46:09 +0100257 self.pg_enable_capture()
258 self.src_if.add_stream([malformed_packet] + valid_fragments)
259 self.pg_start()
260
261 self.dst_if.get_capture(1)
Klement Sekera896c8962019-06-24 11:52:49 +0000262 self.logger.debug(self.vapi.ppcli("show error"))
Ole Troan127fbec2019-10-18 15:22:56 +0200263 self.assertEqual(self.statistics.get_err_counter(counter),
264 error_counter + 1)
Klement Sekera14d7e902018-12-10 13:46:09 +0100265
Klement Sekera400f6d82018-12-13 14:35:48 +0100266 def test_44924(self):
267 """ compress tiny fragments """
268 packets = [(Ether(dst=self.src_if.local_mac,
269 src=self.src_if.remote_mac) /
270 IP(id=24339, flags="MF", frag=0, ttl=64,
271 src=self.src_if.remote_ip4,
272 dst=self.dst_if.remote_ip4) /
273 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
274 Raw(load='Test-group: IPv4')),
275 (Ether(dst=self.src_if.local_mac,
276 src=self.src_if.remote_mac) /
277 IP(id=24339, flags="MF", frag=3, ttl=64,
278 src=self.src_if.remote_ip4,
279 dst=self.dst_if.remote_ip4) /
280 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
281 Raw(load='.IPv4.Fragmentation.vali')),
282 (Ether(dst=self.src_if.local_mac,
283 src=self.src_if.remote_mac) /
284 IP(id=24339, frag=6, ttl=64,
285 src=self.src_if.remote_ip4,
286 dst=self.dst_if.remote_ip4) /
287 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
288 Raw(load='d; Test-case: 44924'))
289 ]
290
291 self.pg_enable_capture()
292 self.src_if.add_stream(packets)
293 self.pg_start()
294
295 self.dst_if.get_capture(1)
296
Klement Sekera4ee633e2018-12-14 12:00:44 +0100297 def test_frag_1(self):
298 """ fragment of size 1 """
299 self.vapi.cli("clear errors")
300 malformed_packets = [(Ether(dst=self.src_if.local_mac,
301 src=self.src_if.remote_mac) /
302 IP(id=7, len=21, flags="MF", frag=0, ttl=64,
303 src=self.src_if.remote_ip4,
304 dst=self.dst_if.remote_ip4) /
305 ICMP(type="echo-request")),
306 (Ether(dst=self.src_if.local_mac,
307 src=self.src_if.remote_mac) /
308 IP(id=7, len=21, frag=1, ttl=64,
309 src=self.src_if.remote_ip4,
310 dst=self.dst_if.remote_ip4) /
Ole Troan127fbec2019-10-18 15:22:56 +0200311 Raw(load=b'\x08')),
Klement Sekera4ee633e2018-12-14 12:00:44 +0100312 ]
313
314 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
315 IP(id=1000, src=self.src_if.remote_ip4,
316 dst=self.dst_if.remote_ip4) /
317 UDP(sport=1234, dport=5678) /
Ole Troan127fbec2019-10-18 15:22:56 +0200318 Raw(b"X" * 1000))
Klement Sekera4ee633e2018-12-14 12:00:44 +0100319 valid_fragments = fragment_rfc791(p, 400)
320
321 self.pg_enable_capture()
322 self.src_if.add_stream(malformed_packets + valid_fragments)
323 self.pg_start()
324
325 self.dst_if.get_capture(1)
326
Klement Sekera896c8962019-06-24 11:52:49 +0000327 self.assert_packet_counter_equal("ip4-full-reassembly-feature", 1)
Klement Sekera4ee633e2018-12-14 12:00:44 +0100328 # TODO remove above, uncomment below once clearing of counters
329 # is supported
330 # self.assert_packet_counter_equal(
Klement Sekera896c8962019-06-24 11:52:49 +0000331 # "/err/ip4-full-reassembly-feature/malformed packets", 1)
Klement Sekera4ee633e2018-12-14 12:00:44 +0100332
Klement Sekera947a85c2019-07-24 12:40:37 +0000333 def test_random(self):
334 """ random order reassembly """
335
336 fragments = list(self.fragments_200)
337 shuffle(fragments)
338
339 self.pg_enable_capture()
340 self.src_if.add_stream(fragments)
341 self.pg_start()
342
343 packets = self.dst_if.get_capture(len(self.packet_infos))
344 self.verify_capture(packets)
345 self.src_if.assert_nothing_captured()
346
347 # run it all again to verify correctness
348 self.pg_enable_capture()
349 self.src_if.add_stream(fragments)
350 self.pg_start()
351
352 packets = self.dst_if.get_capture(len(self.packet_infos))
353 self.verify_capture(packets)
354 self.src_if.assert_nothing_captured()
355
356 def test_duplicates(self):
Klement Sekera75e7d132017-09-20 08:26:30 +0200357 """ duplicate fragments """
Klement Sekera947a85c2019-07-24 12:40:37 +0000358
Klement Sekera75e7d132017-09-20 08:26:30 +0200359 fragments = [
360 x for (_, frags, _, _) in self.pkt_infos
361 for x in frags
362 for _ in range(0, min(2, len(frags)))
363 ]
Klement Sekera947a85c2019-07-24 12:40:37 +0000364
365 self.pg_enable_capture()
366 self.src_if.add_stream(fragments)
367 self.pg_start()
368
369 packets = self.dst_if.get_capture(len(self.pkt_infos))
370 self.verify_capture(packets)
371 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200372
373 def test_overlap1(self):
374 """ overlapping fragments case #1 """
375
376 fragments = []
377 for _, _, frags_300, frags_200 in self.pkt_infos:
378 if len(frags_300) == 1:
379 fragments.extend(frags_300)
380 else:
381 for i, j in zip(frags_200, frags_300):
382 fragments.extend(i)
383 fragments.extend(j)
384
385 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100386 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200387 self.pg_start()
388
Klement Sekera4c533132018-02-22 11:41:12 +0100389 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera947a85c2019-07-24 12:40:37 +0000390 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100391 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200392
393 # run it all to verify correctness
394 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100395 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200396 self.pg_start()
397
Klement Sekera4c533132018-02-22 11:41:12 +0100398 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera947a85c2019-07-24 12:40:37 +0000399 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100400 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200401
402 def test_overlap2(self):
403 """ overlapping fragments case #2 """
404
405 fragments = []
406 for _, _, frags_300, frags_200 in self.pkt_infos:
407 if len(frags_300) == 1:
408 fragments.extend(frags_300)
409 else:
410 # care must be taken here so that there are no fragments
411 # received by vpp after reassembly is finished, otherwise
412 # new reassemblies will be started and packet generator will
413 # freak out when it detects unfreed buffers
414 zipped = zip(frags_300, frags_200)
Paul Vinciguerrac7834e02019-03-02 10:43:05 -0800415 for i, j in zipped:
Klement Sekera75e7d132017-09-20 08:26:30 +0200416 fragments.extend(i)
417 fragments.extend(j)
Paul Vinciguerrac7834e02019-03-02 10:43:05 -0800418 fragments.pop()
Klement Sekera75e7d132017-09-20 08:26:30 +0200419
420 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100421 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200422 self.pg_start()
423
Klement Sekera4c533132018-02-22 11:41:12 +0100424 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera947a85c2019-07-24 12:40:37 +0000425 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100426 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200427
428 # run it all to verify correctness
429 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100430 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200431 self.pg_start()
432
Klement Sekera4c533132018-02-22 11:41:12 +0100433 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera947a85c2019-07-24 12:40:37 +0000434 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100435 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200436
Klement Sekera947a85c2019-07-24 12:40:37 +0000437 def test_timeout_inline(self):
Klement Sekera75e7d132017-09-20 08:26:30 +0200438 """ timeout (inline) """
439
440 dropped_packet_indexes = set(
441 index for (index, frags, _, _) in self.pkt_infos if len(frags) > 1
442 )
443
Klement Sekera947a85c2019-07-24 12:40:37 +0000444 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
445 max_reassembly_length=3,
446 expire_walk_interval_ms=10000)
447
448 self.pg_enable_capture()
449 self.src_if.add_stream(self.fragments_400)
450 self.pg_start()
451
452 packets = self.dst_if.get_capture(
453 len(self.pkt_infos) - len(dropped_packet_indexes))
454 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100455 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200456
457 def test_timeout_cleanup(self):
458 """ timeout (cleanup) """
459
460 # whole packets + fragmented packets sans last fragment
461 fragments = [
462 x for (_, frags_400, _, _) in self.pkt_infos
463 for x in frags_400[:-1 if len(frags_400) > 1 else None]
464 ]
465
466 # last fragments for fragmented packets
467 fragments2 = [frags_400[-1]
468 for (_, frags_400, _, _) in self.pkt_infos
469 if len(frags_400) > 1]
470
471 dropped_packet_indexes = set(
472 index for (index, frags_400, _, _) in self.pkt_infos
473 if len(frags_400) > 1)
474
475 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +0200476 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +0200477 expire_walk_interval_ms=50)
478
479 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100480 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200481 self.pg_start()
482
483 self.sleep(.25, "wait before sending rest of fragments")
484
Klement Sekera4c533132018-02-22 11:41:12 +0100485 self.src_if.add_stream(fragments2)
Klement Sekera75e7d132017-09-20 08:26:30 +0200486 self.pg_start()
Klement Sekera75e7d132017-09-20 08:26:30 +0200487
Klement Sekera4c533132018-02-22 11:41:12 +0100488 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200489 len(self.pkt_infos) - len(dropped_packet_indexes))
Klement Sekera947a85c2019-07-24 12:40:37 +0000490 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100491 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200492
Klement Sekera947a85c2019-07-24 12:40:37 +0000493 def test_disabled(self):
Klement Sekera75e7d132017-09-20 08:26:30 +0200494 """ reassembly disabled """
495
496 dropped_packet_indexes = set(
497 index for (index, frags_400, _, _) in self.pkt_infos
498 if len(frags_400) > 1)
Klement Sekera947a85c2019-07-24 12:40:37 +0000499
500 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
501 max_reassembly_length=3,
502 expire_walk_interval_ms=10000)
503
504 self.pg_enable_capture()
505 self.src_if.add_stream(self.fragments_400)
506 self.pg_start()
507
508 packets = self.dst_if.get_capture(
509 len(self.pkt_infos) - len(dropped_packet_indexes))
510 self.verify_capture(packets, dropped_packet_indexes)
511 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200512
513
Klement Sekerade34c352019-06-25 11:19:22 +0000514class TestIPv4SVReassembly(VppTestCase):
515 """ IPv4 Shallow Virtual Reassembly """
516
517 @classmethod
518 def setUpClass(cls):
519 super(TestIPv4SVReassembly, cls).setUpClass()
520
521 cls.create_pg_interfaces([0, 1])
522 cls.src_if = cls.pg0
523 cls.dst_if = cls.pg1
524
525 # setup all interfaces
526 for i in cls.pg_interfaces:
527 i.admin_up()
528 i.config_ip4()
529 i.resolve_arp()
530
531 def setUp(self):
532 """ Test setup - force timeout on existing reassemblies """
533 super(TestIPv4SVReassembly, self).setUp()
534 self.vapi.ip_reassembly_enable_disable(
535 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
536 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
537 self.vapi.ip_reassembly_set(
538 timeout_ms=0, max_reassemblies=1000,
539 max_reassembly_length=1000,
540 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
541 expire_walk_interval_ms=10)
542 self.sleep(.25)
543 self.vapi.ip_reassembly_set(
544 timeout_ms=1000000, max_reassemblies=1000,
545 max_reassembly_length=1000,
546 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
547 expire_walk_interval_ms=10000)
548
549 def tearDown(self):
550 super(TestIPv4SVReassembly, self).tearDown()
551 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
552 self.logger.debug(self.vapi.ppcli("show buffers"))
553
554 def test_basic(self):
555 """ basic reassembly """
556 payload_len = 1000
557 payload = ""
558 counter = 0
559 while len(payload) < payload_len:
560 payload += "%u " % counter
561 counter += 1
562
563 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
564 IP(id=1, src=self.src_if.remote_ip4,
565 dst=self.dst_if.remote_ip4) /
566 UDP(sport=1234, dport=5678) /
567 Raw(payload))
568 fragments = fragment_rfc791(p, payload_len/4)
569
570 # send fragment #2 - should be cached inside reassembly
571 self.pg_enable_capture()
572 self.src_if.add_stream(fragments[1])
573 self.pg_start()
574 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
575 self.logger.debug(self.vapi.ppcli("show buffers"))
576 self.logger.debug(self.vapi.ppcli("show trace"))
577 self.dst_if.assert_nothing_captured()
578
579 # send fragment #1 - reassembly is finished now and both fragments
580 # forwarded
581 self.pg_enable_capture()
582 self.src_if.add_stream(fragments[0])
583 self.pg_start()
584 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
585 self.logger.debug(self.vapi.ppcli("show buffers"))
586 self.logger.debug(self.vapi.ppcli("show trace"))
587 c = self.dst_if.get_capture(2)
588 for sent, recvd in zip([fragments[1], fragments[0]], c):
589 self.assertEqual(sent[IP].src, recvd[IP].src)
590 self.assertEqual(sent[IP].dst, recvd[IP].dst)
591 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
592
593 # send rest of fragments - should be immediately forwarded
594 self.pg_enable_capture()
595 self.src_if.add_stream(fragments[2:])
596 self.pg_start()
597 c = self.dst_if.get_capture(len(fragments[2:]))
598 for sent, recvd in zip(fragments[2:], c):
599 self.assertEqual(sent[IP].src, recvd[IP].src)
600 self.assertEqual(sent[IP].dst, recvd[IP].dst)
601 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
602
Klement Sekera53be16d2020-12-15 21:47:36 +0100603 def test_verify_clear_trace_mid_reassembly(self):
604 """ verify clear trace works mid-reassembly """
605 payload_len = 1000
606 payload = ""
607 counter = 0
608 while len(payload) < payload_len:
609 payload += "%u " % counter
610 counter += 1
611
612 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
613 IP(id=1, src=self.src_if.remote_ip4,
614 dst=self.dst_if.remote_ip4) /
615 UDP(sport=1234, dport=5678) /
616 Raw(payload))
617 fragments = fragment_rfc791(p, payload_len/4)
618
619 self.pg_enable_capture()
620 self.src_if.add_stream(fragments[1])
621 self.pg_start()
622
623 self.logger.debug(self.vapi.cli("show trace"))
624 self.vapi.cli("clear trace")
625
626 self.pg_enable_capture()
627 self.src_if.add_stream(fragments[0])
628 self.pg_start()
629 self.dst_if.get_capture(2)
630
631 self.logger.debug(self.vapi.cli("show trace"))
632 self.vapi.cli("clear trace")
633
634 self.pg_enable_capture()
635 self.src_if.add_stream(fragments[2:])
636 self.pg_start()
637 self.dst_if.get_capture(len(fragments[2:]))
638
Klement Sekerade34c352019-06-25 11:19:22 +0000639 def test_timeout(self):
640 """ reassembly timeout """
641 payload_len = 1000
642 payload = ""
643 counter = 0
644 while len(payload) < payload_len:
645 payload += "%u " % counter
646 counter += 1
647
648 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
649 IP(id=1, src=self.src_if.remote_ip4,
650 dst=self.dst_if.remote_ip4) /
651 UDP(sport=1234, dport=5678) /
652 Raw(payload))
653 fragments = fragment_rfc791(p, payload_len/4)
654
655 self.vapi.ip_reassembly_set(
656 timeout_ms=100, max_reassemblies=1000,
657 max_reassembly_length=1000,
658 expire_walk_interval_ms=50,
659 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
660
661 # send fragments #2 and #1 - should be forwarded
662 self.pg_enable_capture()
663 self.src_if.add_stream(fragments[0:2])
664 self.pg_start()
665 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
666 self.logger.debug(self.vapi.ppcli("show buffers"))
667 self.logger.debug(self.vapi.ppcli("show trace"))
668 c = self.dst_if.get_capture(2)
669 for sent, recvd in zip([fragments[1], fragments[0]], c):
670 self.assertEqual(sent[IP].src, recvd[IP].src)
671 self.assertEqual(sent[IP].dst, recvd[IP].dst)
672 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
673
674 # wait for cleanup
675 self.sleep(.25, "wait before sending rest of fragments")
676
677 # send rest of fragments - shouldn't be forwarded
678 self.pg_enable_capture()
679 self.src_if.add_stream(fragments[2:])
680 self.pg_start()
681 self.dst_if.assert_nothing_captured()
682
683 def test_lru(self):
684 """ reassembly reuses LRU element """
685
686 self.vapi.ip_reassembly_set(
687 timeout_ms=1000000, max_reassemblies=1,
688 max_reassembly_length=1000,
689 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
690 expire_walk_interval_ms=10000)
691
692 payload_len = 1000
693 payload = ""
694 counter = 0
695 while len(payload) < payload_len:
696 payload += "%u " % counter
697 counter += 1
698
699 packet_count = 10
700
701 fragments = [f
702 for i in range(packet_count)
703 for p in (Ether(dst=self.src_if.local_mac,
704 src=self.src_if.remote_mac) /
705 IP(id=i, src=self.src_if.remote_ip4,
706 dst=self.dst_if.remote_ip4) /
707 UDP(sport=1234, dport=5678) /
708 Raw(payload))
709 for f in fragment_rfc791(p, payload_len/4)]
710
711 self.pg_enable_capture()
712 self.src_if.add_stream(fragments)
713 self.pg_start()
714 c = self.dst_if.get_capture(len(fragments))
715 for sent, recvd in zip(fragments, c):
716 self.assertEqual(sent[IP].src, recvd[IP].src)
717 self.assertEqual(sent[IP].dst, recvd[IP].dst)
718 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
719
Klement Sekera18c6cd92020-07-10 09:29:48 +0000720 def send_mixed_and_verify_capture(self, traffic):
721 stream = []
722 for t in traffic:
723 for c in range(t['count']):
724 stream.append(
725 (Ether(dst=self.src_if.local_mac,
726 src=self.src_if.remote_mac) /
727 IP(id=self.counter,
728 flags=t['flags'],
729 src=self.src_if.remote_ip4,
730 dst=self.dst_if.remote_ip4) /
731 UDP(sport=1234, dport=5678) /
732 Raw("abcdef")))
733 self.counter = self.counter + 1
734
735 self.pg_enable_capture()
736 self.src_if.add_stream(stream)
737 self.pg_start()
738 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
739 self.logger.debug(self.vapi.ppcli("show buffers"))
740 self.logger.debug(self.vapi.ppcli("show trace"))
741 self.dst_if.get_capture(len(stream))
742
743 def test_mixed(self):
744 """ mixed traffic correctly passes through SVR """
745 self.counter = 1
746
747 self.send_mixed_and_verify_capture([{'count': 1, 'flags': ''}])
748 self.send_mixed_and_verify_capture([{'count': 2, 'flags': ''}])
749 self.send_mixed_and_verify_capture([{'count': 3, 'flags': ''}])
750 self.send_mixed_and_verify_capture([{'count': 8, 'flags': ''}])
751 self.send_mixed_and_verify_capture([{'count': 257, 'flags': ''}])
752
753 self.send_mixed_and_verify_capture([{'count': 1, 'flags': 'MF'}])
754 self.send_mixed_and_verify_capture([{'count': 2, 'flags': 'MF'}])
755 self.send_mixed_and_verify_capture([{'count': 3, 'flags': 'MF'}])
756 self.send_mixed_and_verify_capture([{'count': 8, 'flags': 'MF'}])
757 self.send_mixed_and_verify_capture([{'count': 257, 'flags': 'MF'}])
758
759 self.send_mixed_and_verify_capture(
760 [{'count': 1, 'flags': ''}, {'count': 1, 'flags': 'MF'}])
761 self.send_mixed_and_verify_capture(
762 [{'count': 2, 'flags': ''}, {'count': 2, 'flags': 'MF'}])
763 self.send_mixed_and_verify_capture(
764 [{'count': 3, 'flags': ''}, {'count': 3, 'flags': 'MF'}])
765 self.send_mixed_and_verify_capture(
766 [{'count': 8, 'flags': ''}, {'count': 8, 'flags': 'MF'}])
767 self.send_mixed_and_verify_capture(
768 [{'count': 129, 'flags': ''}, {'count': 129, 'flags': 'MF'}])
769
770 self.send_mixed_and_verify_capture(
771 [{'count': 1, 'flags': ''}, {'count': 1, 'flags': 'MF'},
772 {'count': 1, 'flags': ''}, {'count': 1, 'flags': 'MF'}])
773 self.send_mixed_and_verify_capture(
774 [{'count': 2, 'flags': ''}, {'count': 2, 'flags': 'MF'},
775 {'count': 2, 'flags': ''}, {'count': 2, 'flags': 'MF'}])
776 self.send_mixed_and_verify_capture(
777 [{'count': 3, 'flags': ''}, {'count': 3, 'flags': 'MF'},
778 {'count': 3, 'flags': ''}, {'count': 3, 'flags': 'MF'}])
779 self.send_mixed_and_verify_capture(
780 [{'count': 8, 'flags': ''}, {'count': 8, 'flags': 'MF'},
781 {'count': 8, 'flags': ''}, {'count': 8, 'flags': 'MF'}])
782 self.send_mixed_and_verify_capture(
783 [{'count': 65, 'flags': ''}, {'count': 65, 'flags': 'MF'},
784 {'count': 65, 'flags': ''}, {'count': 65, 'flags': 'MF'}])
785
Klement Sekerade34c352019-06-25 11:19:22 +0000786
Klement Sekera630ab582019-07-19 09:14:19 +0000787class TestIPv4MWReassembly(VppTestCase):
788 """ IPv4 Reassembly (multiple workers) """
Klement Sekera8d815022021-03-15 16:58:10 +0100789 vpp_worker_count = 3
Klement Sekera630ab582019-07-19 09:14:19 +0000790
791 @classmethod
792 def setUpClass(cls):
793 super(TestIPv4MWReassembly, cls).setUpClass()
794
Klement Sekera8d815022021-03-15 16:58:10 +0100795 cls.create_pg_interfaces(range(cls.vpp_worker_count+1))
Klement Sekera630ab582019-07-19 09:14:19 +0000796 cls.src_if = cls.pg0
797 cls.send_ifs = cls.pg_interfaces[:-1]
798 cls.dst_if = cls.pg_interfaces[-1]
799
800 # setup all interfaces
801 for i in cls.pg_interfaces:
802 i.admin_up()
803 i.config_ip4()
804 i.resolve_arp()
805
806 # packets sizes reduced here because we are generating packets without
807 # Ethernet headers, which are added later (diff fragments go via
808 # different interfaces)
809 cls.packet_sizes = [64-len(Ether()), 512-len(Ether()),
810 1518-len(Ether()), 9018-len(Ether())]
811 cls.padding = " abcdefghijklmn"
812 cls.create_stream(cls.packet_sizes)
813 cls.create_fragments()
814
815 @classmethod
816 def tearDownClass(cls):
817 super(TestIPv4MWReassembly, cls).tearDownClass()
818
819 def setUp(self):
820 """ Test setup - force timeout on existing reassemblies """
821 super(TestIPv4MWReassembly, self).setUp()
822 for intf in self.send_ifs:
823 self.vapi.ip_reassembly_enable_disable(
824 sw_if_index=intf.sw_if_index, enable_ip4=True)
825 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
826 max_reassembly_length=1000,
827 expire_walk_interval_ms=10)
828 self.sleep(.25)
829 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
830 max_reassembly_length=1000,
831 expire_walk_interval_ms=10000)
832
833 def tearDown(self):
834 super(TestIPv4MWReassembly, self).tearDown()
835
836 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +0000837 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
Klement Sekera630ab582019-07-19 09:14:19 +0000838 self.logger.debug(self.vapi.ppcli("show buffers"))
839
840 @classmethod
841 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
842 """Create input packet stream
843
844 :param list packet_sizes: Required packet sizes.
845 """
846 for i in range(0, packet_count):
847 info = cls.create_packet_info(cls.src_if, cls.src_if)
848 payload = cls.info_to_payload(info)
849 p = (IP(id=info.index, src=cls.src_if.remote_ip4,
850 dst=cls.dst_if.remote_ip4) /
851 UDP(sport=1234, dport=5678) /
852 Raw(payload))
853 size = packet_sizes[(i // 2) % len(packet_sizes)]
854 cls.extend_packet(p, size, cls.padding)
855 info.data = p
856
857 @classmethod
858 def create_fragments(cls):
859 infos = cls._packet_infos
860 cls.pkt_infos = []
Paul Vinciguerra090096b2020-12-03 00:42:46 -0500861 for index, info in infos.items():
Klement Sekera630ab582019-07-19 09:14:19 +0000862 p = info.data
863 # cls.logger.debug(ppp("Packet:",
864 # p.__class__(scapy.compat.raw(p))))
865 fragments_400 = fragment_rfc791(p, 400)
866 cls.pkt_infos.append((index, fragments_400))
867 cls.fragments_400 = [
868 x for (_, frags) in cls.pkt_infos for x in frags]
869 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, " %
870 (len(infos), len(cls.fragments_400)))
871
872 def verify_capture(self, capture, dropped_packet_indexes=[]):
873 """Verify captured packet stream.
874
875 :param list capture: Captured packet stream.
876 """
877 info = None
878 seen = set()
879 for packet in capture:
880 try:
881 self.logger.debug(ppp("Got packet:", packet))
882 ip = packet[IP]
883 udp = packet[UDP]
884 payload_info = self.payload_to_info(packet[Raw])
885 packet_index = payload_info.index
886 self.assertTrue(
887 packet_index not in dropped_packet_indexes,
888 ppp("Packet received, but should be dropped:", packet))
889 if packet_index in seen:
890 raise Exception(ppp("Duplicate packet received", packet))
891 seen.add(packet_index)
892 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
893 info = self._packet_infos[packet_index]
894 self.assertTrue(info is not None)
895 self.assertEqual(packet_index, info.index)
896 saved_packet = info.data
897 self.assertEqual(ip.src, saved_packet[IP].src)
898 self.assertEqual(ip.dst, saved_packet[IP].dst)
899 self.assertEqual(udp.payload, saved_packet[UDP].payload)
900 except Exception:
901 self.logger.error(ppp("Unexpected or invalid packet:", packet))
902 raise
903 for index in self._packet_infos:
904 self.assertTrue(index in seen or index in dropped_packet_indexes,
905 "Packet with packet_index %d not received" % index)
906
907 def send_packets(self, packets):
Klement Sekera8d815022021-03-15 16:58:10 +0100908 for counter in range(self.vpp_worker_count):
Klement Sekera630ab582019-07-19 09:14:19 +0000909 if 0 == len(packets[counter]):
910 continue
911 send_if = self.send_ifs[counter]
912 send_if.add_stream(
913 (Ether(dst=send_if.local_mac, src=send_if.remote_mac) / x
914 for x in packets[counter]),
915 worker=counter)
916 self.pg_start()
917
918 def test_worker_conflict(self):
919 """ 1st and FO=0 fragments on different workers """
920
921 # in first wave we send fragments which don't start at offset 0
922 # then we send fragments with offset 0 on a different thread
923 # then the rest of packets on a random thread
Klement Sekera8d815022021-03-15 16:58:10 +0100924 first_packets = [[] for n in range(self.vpp_worker_count)]
925 second_packets = [[] for n in range(self.vpp_worker_count)]
926 rest_of_packets = [[] for n in range(self.vpp_worker_count)]
Klement Sekera630ab582019-07-19 09:14:19 +0000927 for (_, p) in self.pkt_infos:
Klement Sekera8d815022021-03-15 16:58:10 +0100928 wi = randrange(self.vpp_worker_count)
Klement Sekera630ab582019-07-19 09:14:19 +0000929 second_packets[wi].append(p[0])
930 if len(p) <= 1:
931 continue
932 wi2 = wi
933 while wi2 == wi:
Klement Sekera8d815022021-03-15 16:58:10 +0100934 wi2 = randrange(self.vpp_worker_count)
Klement Sekera630ab582019-07-19 09:14:19 +0000935 first_packets[wi2].append(p[1])
Klement Sekera8d815022021-03-15 16:58:10 +0100936 wi3 = randrange(self.vpp_worker_count)
Klement Sekera630ab582019-07-19 09:14:19 +0000937 rest_of_packets[wi3].extend(p[2:])
938
939 self.pg_enable_capture()
940 self.send_packets(first_packets)
941 self.send_packets(second_packets)
942 self.send_packets(rest_of_packets)
943
944 packets = self.dst_if.get_capture(len(self.pkt_infos))
945 self.verify_capture(packets)
946 for send_if in self.send_ifs:
947 send_if.assert_nothing_captured()
948
Klement Sekera68bae5b2019-10-10 18:57:34 +0000949 self.logger.debug(self.vapi.ppcli("show trace"))
950 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
951 self.logger.debug(self.vapi.ppcli("show buffers"))
952 self.vapi.cli("clear trace")
953
Klement Sekera630ab582019-07-19 09:14:19 +0000954 self.pg_enable_capture()
955 self.send_packets(first_packets)
956 self.send_packets(second_packets)
957 self.send_packets(rest_of_packets)
958
959 packets = self.dst_if.get_capture(len(self.pkt_infos))
960 self.verify_capture(packets)
961 for send_if in self.send_ifs:
962 send_if.assert_nothing_captured()
963
964
Klement Sekera947a85c2019-07-24 12:40:37 +0000965class TestIPv6Reassembly(VppTestCase):
Klement Sekera75e7d132017-09-20 08:26:30 +0200966 """ IPv6 Reassembly """
967
968 @classmethod
969 def setUpClass(cls):
970 super(TestIPv6Reassembly, cls).setUpClass()
971
Klement Sekera4c533132018-02-22 11:41:12 +0100972 cls.create_pg_interfaces([0, 1])
973 cls.src_if = cls.pg0
974 cls.dst_if = cls.pg1
Klement Sekera75e7d132017-09-20 08:26:30 +0200975
976 # setup all interfaces
977 for i in cls.pg_interfaces:
978 i.admin_up()
979 i.config_ip6()
980 i.resolve_ndp()
981
Klement Sekera75e7d132017-09-20 08:26:30 +0200982 # packet sizes
983 cls.packet_sizes = [64, 512, 1518, 9018]
984 cls.padding = " abcdefghijklmn"
985 cls.create_stream(cls.packet_sizes)
986 cls.create_fragments()
987
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -0700988 @classmethod
989 def tearDownClass(cls):
990 super(TestIPv6Reassembly, cls).tearDownClass()
991
Klement Sekera75e7d132017-09-20 08:26:30 +0200992 def setUp(self):
993 """ Test setup - force timeout on existing reassemblies """
994 super(TestIPv6Reassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +0100995 self.vapi.ip_reassembly_enable_disable(
996 sw_if_index=self.src_if.sw_if_index, enable_ip6=True)
Klement Sekera75e7d132017-09-20 08:26:30 +0200997 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +0200998 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +0200999 expire_walk_interval_ms=10, is_ip6=1)
1000 self.sleep(.25)
1001 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001002 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001003 expire_walk_interval_ms=10000, is_ip6=1)
Klement Sekera896c8962019-06-24 11:52:49 +00001004 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +01001005 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +02001006
1007 def tearDown(self):
1008 super(TestIPv6Reassembly, self).tearDown()
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -07001009
1010 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +00001011 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +01001012 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +02001013
1014 @classmethod
1015 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
1016 """Create input packet stream for defined interface.
1017
1018 :param list packet_sizes: Required packet sizes.
1019 """
1020 for i in range(0, packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01001021 info = cls.create_packet_info(cls.src_if, cls.src_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02001022 payload = cls.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +01001023 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
1024 IPv6(src=cls.src_if.remote_ip6,
1025 dst=cls.dst_if.remote_ip6) /
1026 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001027 Raw(payload))
1028 size = packet_sizes[(i // 2) % len(packet_sizes)]
1029 cls.extend_packet(p, size, cls.padding)
1030 info.data = p
1031
1032 @classmethod
1033 def create_fragments(cls):
1034 infos = cls._packet_infos
1035 cls.pkt_infos = []
Paul Vinciguerra090096b2020-12-03 00:42:46 -05001036 for index, info in infos.items():
Klement Sekera75e7d132017-09-20 08:26:30 +02001037 p = info.data
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001038 # cls.logger.debug(ppp("Packet:",
1039 # p.__class__(scapy.compat.raw(p))))
Klement Sekera75e7d132017-09-20 08:26:30 +02001040 fragments_400 = fragment_rfc8200(p, info.index, 400)
1041 fragments_300 = fragment_rfc8200(p, info.index, 300)
1042 cls.pkt_infos.append((index, fragments_400, fragments_300))
1043 cls.fragments_400 = [
1044 x for _, frags, _ in cls.pkt_infos for x in frags]
1045 cls.fragments_300 = [
1046 x for _, _, frags in cls.pkt_infos for x in frags]
1047 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
1048 "and %s 300-byte fragments" %
1049 (len(infos), len(cls.fragments_400),
1050 len(cls.fragments_300)))
1051
Klement Sekera947a85c2019-07-24 12:40:37 +00001052 def verify_capture(self, capture, dropped_packet_indexes=[]):
1053 """Verify captured packet strea .
1054
1055 :param list capture: Captured packet stream.
1056 """
1057 info = None
1058 seen = set()
1059 for packet in capture:
1060 try:
1061 self.logger.debug(ppp("Got packet:", packet))
1062 ip = packet[IPv6]
1063 udp = packet[UDP]
1064 payload_info = self.payload_to_info(packet[Raw])
1065 packet_index = payload_info.index
1066 self.assertTrue(
1067 packet_index not in dropped_packet_indexes,
1068 ppp("Packet received, but should be dropped:", packet))
1069 if packet_index in seen:
1070 raise Exception(ppp("Duplicate packet received", packet))
1071 seen.add(packet_index)
1072 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
1073 info = self._packet_infos[packet_index]
1074 self.assertTrue(info is not None)
1075 self.assertEqual(packet_index, info.index)
1076 saved_packet = info.data
1077 self.assertEqual(ip.src, saved_packet[IPv6].src)
1078 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
1079 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1080 except Exception:
1081 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1082 raise
1083 for index in self._packet_infos:
1084 self.assertTrue(index in seen or index in dropped_packet_indexes,
1085 "Packet with packet_index %d not received" % index)
1086
1087 def test_reassembly(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001088 """ basic reassembly """
1089
Klement Sekera947a85c2019-07-24 12:40:37 +00001090 self.pg_enable_capture()
1091 self.src_if.add_stream(self.fragments_400)
1092 self.pg_start()
1093
1094 packets = self.dst_if.get_capture(len(self.pkt_infos))
1095 self.verify_capture(packets)
1096 self.src_if.assert_nothing_captured()
1097
1098 # run it all again to verify correctness
1099 self.pg_enable_capture()
1100 self.src_if.add_stream(self.fragments_400)
1101 self.pg_start()
1102
1103 packets = self.dst_if.get_capture(len(self.pkt_infos))
1104 self.verify_capture(packets)
1105 self.src_if.assert_nothing_captured()
1106
Klement Sekera769145c2019-03-06 11:59:57 +01001107 def test_buffer_boundary(self):
1108 """ fragment header crossing buffer boundary """
1109
1110 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1111 IPv6(src=self.src_if.remote_ip6,
1112 dst=self.src_if.local_ip6) /
1113 IPv6ExtHdrHopByHop(
1114 options=[HBHOptUnknown(otype=0xff, optlen=0)] * 1000) /
1115 IPv6ExtHdrFragment(m=1) /
1116 UDP(sport=1234, dport=5678) /
1117 Raw())
1118 self.pg_enable_capture()
1119 self.src_if.add_stream([p])
1120 self.pg_start()
1121 self.src_if.assert_nothing_captured()
1122 self.dst_if.assert_nothing_captured()
1123
Klement Sekera53be16d2020-12-15 21:47:36 +01001124 def test_verify_clear_trace_mid_reassembly(self):
1125 """ verify clear trace works mid-reassembly """
1126
1127 self.pg_enable_capture()
1128 self.src_if.add_stream(self.fragments_400[0:-1])
1129 self.pg_start()
1130
1131 self.logger.debug(self.vapi.cli("show trace"))
1132 self.vapi.cli("clear trace")
1133
1134 self.src_if.add_stream(self.fragments_400[-1])
1135 self.pg_start()
1136 packets = self.dst_if.get_capture(len(self.pkt_infos))
1137 self.verify_capture(packets)
1138
Klement Sekera947a85c2019-07-24 12:40:37 +00001139 def test_reversed(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001140 """ reverse order reassembly """
1141
Klement Sekera947a85c2019-07-24 12:40:37 +00001142 fragments = list(self.fragments_400)
1143 fragments.reverse()
1144
1145 self.pg_enable_capture()
1146 self.src_if.add_stream(fragments)
1147 self.pg_start()
1148
1149 packets = self.dst_if.get_capture(len(self.pkt_infos))
1150 self.verify_capture(packets)
1151 self.src_if.assert_nothing_captured()
1152
1153 # run it all again to verify correctness
1154 self.pg_enable_capture()
1155 self.src_if.add_stream(fragments)
1156 self.pg_start()
1157
1158 packets = self.dst_if.get_capture(len(self.pkt_infos))
1159 self.verify_capture(packets)
1160 self.src_if.assert_nothing_captured()
1161
1162 def test_random(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001163 """ random order reassembly """
1164
Klement Sekera947a85c2019-07-24 12:40:37 +00001165 fragments = list(self.fragments_400)
1166 shuffle(fragments)
1167
1168 self.pg_enable_capture()
1169 self.src_if.add_stream(fragments)
1170 self.pg_start()
1171
1172 packets = self.dst_if.get_capture(len(self.pkt_infos))
1173 self.verify_capture(packets)
1174 self.src_if.assert_nothing_captured()
1175
1176 # run it all again to verify correctness
1177 self.pg_enable_capture()
1178 self.src_if.add_stream(fragments)
1179 self.pg_start()
1180
1181 packets = self.dst_if.get_capture(len(self.pkt_infos))
1182 self.verify_capture(packets)
1183 self.src_if.assert_nothing_captured()
1184
1185 def test_duplicates(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001186 """ duplicate fragments """
1187
1188 fragments = [
1189 x for (_, frags, _) in self.pkt_infos
1190 for x in frags
1191 for _ in range(0, min(2, len(frags)))
1192 ]
Klement Sekera947a85c2019-07-24 12:40:37 +00001193
1194 self.pg_enable_capture()
1195 self.src_if.add_stream(fragments)
1196 self.pg_start()
1197
1198 packets = self.dst_if.get_capture(len(self.pkt_infos))
1199 self.verify_capture(packets)
1200 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +02001201
Klement Sekera3a343d42019-05-16 14:35:46 +02001202 def test_long_fragment_chain(self):
1203 """ long fragment chain """
1204
1205 error_cnt_str = \
Klement Sekera896c8962019-06-24 11:52:49 +00001206 "/err/ip6-full-reassembly-feature/fragment chain too long (drop)"
Klement Sekera3a343d42019-05-16 14:35:46 +02001207
Klement Sekera34641f22019-05-22 20:18:26 +02001208 error_cnt = self.statistics.get_err_counter(error_cnt_str)
Klement Sekera3a343d42019-05-16 14:35:46 +02001209
1210 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
1211 max_reassembly_length=3,
1212 expire_walk_interval_ms=50, is_ip6=1)
1213
1214 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1215 IPv6(src=self.src_if.remote_ip6,
1216 dst=self.dst_if.remote_ip6) /
1217 UDP(sport=1234, dport=5678) /
Ole Troan127fbec2019-10-18 15:22:56 +02001218 Raw(b"X" * 1000))
Klement Sekera3a343d42019-05-16 14:35:46 +02001219 frags = fragment_rfc8200(p, 1, 300) + fragment_rfc8200(p, 2, 500)
1220
1221 self.pg_enable_capture()
1222 self.src_if.add_stream(frags)
1223 self.pg_start()
1224
1225 self.dst_if.get_capture(1)
Klement Sekera34641f22019-05-22 20:18:26 +02001226 self.assert_error_counter_equal(error_cnt_str, error_cnt + 1)
Klement Sekera3a343d42019-05-16 14:35:46 +02001227
Klement Sekera75e7d132017-09-20 08:26:30 +02001228 def test_overlap1(self):
Klement Sekera947a85c2019-07-24 12:40:37 +00001229 """ overlapping fragments case #1 """
Klement Sekera75e7d132017-09-20 08:26:30 +02001230
1231 fragments = []
1232 for _, frags_400, frags_300 in self.pkt_infos:
1233 if len(frags_300) == 1:
1234 fragments.extend(frags_400)
1235 else:
1236 for i, j in zip(frags_300, frags_400):
1237 fragments.extend(i)
1238 fragments.extend(j)
1239
1240 dropped_packet_indexes = set(
1241 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
1242 )
1243
1244 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001245 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001246 self.pg_start()
1247
Klement Sekera4c533132018-02-22 11:41:12 +01001248 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +02001249 len(self.pkt_infos) - len(dropped_packet_indexes))
Klement Sekera947a85c2019-07-24 12:40:37 +00001250 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001251 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +02001252
1253 def test_overlap2(self):
Klement Sekera947a85c2019-07-24 12:40:37 +00001254 """ overlapping fragments case #2 """
Klement Sekera75e7d132017-09-20 08:26:30 +02001255
1256 fragments = []
Klement Sekera4c533132018-02-22 11:41:12 +01001257 for _, frags_400, frags_300 in self.pkt_infos:
Klement Sekera75e7d132017-09-20 08:26:30 +02001258 if len(frags_400) == 1:
1259 fragments.extend(frags_400)
1260 else:
1261 # care must be taken here so that there are no fragments
1262 # received by vpp after reassembly is finished, otherwise
1263 # new reassemblies will be started and packet generator will
1264 # freak out when it detects unfreed buffers
Klement Sekera4c533132018-02-22 11:41:12 +01001265 zipped = zip(frags_400, frags_300)
Paul Vinciguerrac7834e02019-03-02 10:43:05 -08001266 for i, j in zipped:
Klement Sekera75e7d132017-09-20 08:26:30 +02001267 fragments.extend(i)
1268 fragments.extend(j)
Paul Vinciguerrac7834e02019-03-02 10:43:05 -08001269 fragments.pop()
Klement Sekera75e7d132017-09-20 08:26:30 +02001270
1271 dropped_packet_indexes = set(
1272 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
1273 )
1274
1275 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001276 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001277 self.pg_start()
1278
Klement Sekera4c533132018-02-22 11:41:12 +01001279 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +02001280 len(self.pkt_infos) - len(dropped_packet_indexes))
Klement Sekera947a85c2019-07-24 12:40:37 +00001281 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001282 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +02001283
Klement Sekera947a85c2019-07-24 12:40:37 +00001284 def test_timeout_inline(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001285 """ timeout (inline) """
1286
1287 dropped_packet_indexes = set(
1288 index for (index, frags, _) in self.pkt_infos if len(frags) > 1
1289 )
1290
Klement Sekera947a85c2019-07-24 12:40:37 +00001291 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1292 max_reassembly_length=3,
1293 expire_walk_interval_ms=10000, is_ip6=1)
1294
1295 self.pg_enable_capture()
1296 self.src_if.add_stream(self.fragments_400)
1297 self.pg_start()
1298
1299 packets = self.dst_if.get_capture(
1300 len(self.pkt_infos) - len(dropped_packet_indexes))
1301 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001302 pkts = self.src_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +02001303 expected_count=len(dropped_packet_indexes))
1304 for icmp in pkts:
1305 self.assertIn(ICMPv6TimeExceeded, icmp)
1306 self.assertIn(IPv6ExtHdrFragment, icmp)
1307 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
1308 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
1309
1310 def test_timeout_cleanup(self):
1311 """ timeout (cleanup) """
1312
1313 # whole packets + fragmented packets sans last fragment
1314 fragments = [
1315 x for (_, frags_400, _) in self.pkt_infos
1316 for x in frags_400[:-1 if len(frags_400) > 1 else None]
1317 ]
1318
1319 # last fragments for fragmented packets
1320 fragments2 = [frags_400[-1]
1321 for (_, frags_400, _) in self.pkt_infos
1322 if len(frags_400) > 1]
1323
1324 dropped_packet_indexes = set(
1325 index for (index, frags_400, _) in self.pkt_infos
1326 if len(frags_400) > 1)
1327
1328 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001329 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001330 expire_walk_interval_ms=50)
1331
1332 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001333 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001334 expire_walk_interval_ms=50, is_ip6=1)
1335
1336 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001337 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001338 self.pg_start()
1339
1340 self.sleep(.25, "wait before sending rest of fragments")
1341
Klement Sekera4c533132018-02-22 11:41:12 +01001342 self.src_if.add_stream(fragments2)
Klement Sekera75e7d132017-09-20 08:26:30 +02001343 self.pg_start()
Klement Sekera75e7d132017-09-20 08:26:30 +02001344
Klement Sekera4c533132018-02-22 11:41:12 +01001345 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +02001346 len(self.pkt_infos) - len(dropped_packet_indexes))
Klement Sekera947a85c2019-07-24 12:40:37 +00001347 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001348 pkts = self.src_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +02001349 expected_count=len(dropped_packet_indexes))
1350 for icmp in pkts:
1351 self.assertIn(ICMPv6TimeExceeded, icmp)
1352 self.assertIn(IPv6ExtHdrFragment, icmp)
1353 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
1354 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
1355
Klement Sekera947a85c2019-07-24 12:40:37 +00001356 def test_disabled(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001357 """ reassembly disabled """
1358
1359 dropped_packet_indexes = set(
1360 index for (index, frags_400, _) in self.pkt_infos
1361 if len(frags_400) > 1)
Klement Sekera947a85c2019-07-24 12:40:37 +00001362
1363 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
1364 max_reassembly_length=3,
1365 expire_walk_interval_ms=10000, is_ip6=1)
1366
1367 self.pg_enable_capture()
1368 self.src_if.add_stream(self.fragments_400)
1369 self.pg_start()
1370
1371 packets = self.dst_if.get_capture(
1372 len(self.pkt_infos) - len(dropped_packet_indexes))
1373 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001374 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +02001375
1376 def test_missing_upper(self):
1377 """ missing upper layer """
Klement Sekera4c533132018-02-22 11:41:12 +01001378 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1379 IPv6(src=self.src_if.remote_ip6,
1380 dst=self.src_if.local_ip6) /
1381 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001382 Raw())
1383 self.extend_packet(p, 1000, self.padding)
1384 fragments = fragment_rfc8200(p, 1, 500)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001385 bad_fragment = p.__class__(scapy.compat.raw(fragments[1]))
Klement Sekera75e7d132017-09-20 08:26:30 +02001386 bad_fragment[IPv6ExtHdrFragment].nh = 59
1387 bad_fragment[IPv6ExtHdrFragment].offset = 0
1388 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001389 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +02001390 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +01001391 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +02001392 icmp = pkts[0]
1393 self.assertIn(ICMPv6ParamProblem, icmp)
1394 self.assert_equal(icmp[ICMPv6ParamProblem].code, 3, "ICMP code")
1395
1396 def test_invalid_frag_size(self):
1397 """ fragment size not a multiple of 8 """
Klement Sekera4c533132018-02-22 11:41:12 +01001398 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1399 IPv6(src=self.src_if.remote_ip6,
1400 dst=self.src_if.local_ip6) /
1401 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001402 Raw())
1403 self.extend_packet(p, 1000, self.padding)
1404 fragments = fragment_rfc8200(p, 1, 500)
1405 bad_fragment = fragments[0]
1406 self.extend_packet(bad_fragment, len(bad_fragment) + 5)
1407 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001408 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +02001409 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +01001410 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +02001411 icmp = pkts[0]
1412 self.assertIn(ICMPv6ParamProblem, icmp)
1413 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1414
1415 def test_invalid_packet_size(self):
1416 """ total packet size > 65535 """
Klement Sekera4c533132018-02-22 11:41:12 +01001417 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1418 IPv6(src=self.src_if.remote_ip6,
1419 dst=self.src_if.local_ip6) /
1420 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001421 Raw())
1422 self.extend_packet(p, 1000, self.padding)
1423 fragments = fragment_rfc8200(p, 1, 500)
1424 bad_fragment = fragments[1]
1425 bad_fragment[IPv6ExtHdrFragment].offset = 65500
1426 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001427 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +02001428 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +01001429 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +02001430 icmp = pkts[0]
1431 self.assertIn(ICMPv6ParamProblem, icmp)
1432 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1433
1434
Klement Sekera630ab582019-07-19 09:14:19 +00001435class TestIPv6MWReassembly(VppTestCase):
1436 """ IPv6 Reassembly (multiple workers) """
Klement Sekera8d815022021-03-15 16:58:10 +01001437 vpp_worker_count = 3
Klement Sekera630ab582019-07-19 09:14:19 +00001438
1439 @classmethod
1440 def setUpClass(cls):
1441 super(TestIPv6MWReassembly, cls).setUpClass()
1442
Klement Sekera8d815022021-03-15 16:58:10 +01001443 cls.create_pg_interfaces(range(cls.vpp_worker_count+1))
Klement Sekera630ab582019-07-19 09:14:19 +00001444 cls.src_if = cls.pg0
1445 cls.send_ifs = cls.pg_interfaces[:-1]
1446 cls.dst_if = cls.pg_interfaces[-1]
1447
1448 # setup all interfaces
1449 for i in cls.pg_interfaces:
1450 i.admin_up()
1451 i.config_ip6()
1452 i.resolve_ndp()
1453
1454 # packets sizes reduced here because we are generating packets without
1455 # Ethernet headers, which are added later (diff fragments go via
1456 # different interfaces)
1457 cls.packet_sizes = [64-len(Ether()), 512-len(Ether()),
1458 1518-len(Ether()), 9018-len(Ether())]
1459 cls.padding = " abcdefghijklmn"
1460 cls.create_stream(cls.packet_sizes)
1461 cls.create_fragments()
1462
1463 @classmethod
1464 def tearDownClass(cls):
1465 super(TestIPv6MWReassembly, cls).tearDownClass()
1466
1467 def setUp(self):
1468 """ Test setup - force timeout on existing reassemblies """
1469 super(TestIPv6MWReassembly, self).setUp()
1470 for intf in self.send_ifs:
1471 self.vapi.ip_reassembly_enable_disable(
1472 sw_if_index=intf.sw_if_index, enable_ip6=True)
1473 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1474 max_reassembly_length=1000,
1475 expire_walk_interval_ms=10, is_ip6=1)
1476 self.sleep(.25)
1477 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1478 max_reassembly_length=1000,
1479 expire_walk_interval_ms=1000, is_ip6=1)
1480
1481 def tearDown(self):
1482 super(TestIPv6MWReassembly, self).tearDown()
1483
1484 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +00001485 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
Klement Sekera630ab582019-07-19 09:14:19 +00001486 self.logger.debug(self.vapi.ppcli("show buffers"))
1487
1488 @classmethod
1489 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
1490 """Create input packet stream
1491
1492 :param list packet_sizes: Required packet sizes.
1493 """
1494 for i in range(0, packet_count):
1495 info = cls.create_packet_info(cls.src_if, cls.src_if)
1496 payload = cls.info_to_payload(info)
1497 p = (IPv6(src=cls.src_if.remote_ip6,
1498 dst=cls.dst_if.remote_ip6) /
1499 UDP(sport=1234, dport=5678) /
1500 Raw(payload))
1501 size = packet_sizes[(i // 2) % len(packet_sizes)]
1502 cls.extend_packet(p, size, cls.padding)
1503 info.data = p
1504
1505 @classmethod
1506 def create_fragments(cls):
1507 infos = cls._packet_infos
1508 cls.pkt_infos = []
Paul Vinciguerra090096b2020-12-03 00:42:46 -05001509 for index, info in infos.items():
Klement Sekera630ab582019-07-19 09:14:19 +00001510 p = info.data
1511 # cls.logger.debug(ppp("Packet:",
1512 # p.__class__(scapy.compat.raw(p))))
1513 fragments_400 = fragment_rfc8200(p, index, 400)
1514 cls.pkt_infos.append((index, fragments_400))
1515 cls.fragments_400 = [
1516 x for (_, frags) in cls.pkt_infos for x in frags]
1517 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, " %
1518 (len(infos), len(cls.fragments_400)))
1519
1520 def verify_capture(self, capture, dropped_packet_indexes=[]):
1521 """Verify captured packet strea .
1522
1523 :param list capture: Captured packet stream.
1524 """
1525 info = None
1526 seen = set()
1527 for packet in capture:
1528 try:
1529 self.logger.debug(ppp("Got packet:", packet))
1530 ip = packet[IPv6]
1531 udp = packet[UDP]
1532 payload_info = self.payload_to_info(packet[Raw])
1533 packet_index = payload_info.index
1534 self.assertTrue(
1535 packet_index not in dropped_packet_indexes,
1536 ppp("Packet received, but should be dropped:", packet))
1537 if packet_index in seen:
1538 raise Exception(ppp("Duplicate packet received", packet))
1539 seen.add(packet_index)
1540 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
1541 info = self._packet_infos[packet_index]
1542 self.assertTrue(info is not None)
1543 self.assertEqual(packet_index, info.index)
1544 saved_packet = info.data
1545 self.assertEqual(ip.src, saved_packet[IPv6].src)
1546 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
1547 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1548 except Exception:
1549 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1550 raise
1551 for index in self._packet_infos:
1552 self.assertTrue(index in seen or index in dropped_packet_indexes,
1553 "Packet with packet_index %d not received" % index)
1554
1555 def send_packets(self, packets):
Klement Sekera8d815022021-03-15 16:58:10 +01001556 for counter in range(self.vpp_worker_count):
Klement Sekera630ab582019-07-19 09:14:19 +00001557 if 0 == len(packets[counter]):
1558 continue
1559 send_if = self.send_ifs[counter]
1560 send_if.add_stream(
1561 (Ether(dst=send_if.local_mac, src=send_if.remote_mac) / x
1562 for x in packets[counter]),
1563 worker=counter)
1564 self.pg_start()
1565
1566 def test_worker_conflict(self):
1567 """ 1st and FO=0 fragments on different workers """
1568
1569 # in first wave we send fragments which don't start at offset 0
1570 # then we send fragments with offset 0 on a different thread
1571 # then the rest of packets on a random thread
Klement Sekera8d815022021-03-15 16:58:10 +01001572 first_packets = [[] for n in range(self.vpp_worker_count)]
1573 second_packets = [[] for n in range(self.vpp_worker_count)]
1574 rest_of_packets = [[] for n in range(self.vpp_worker_count)]
Klement Sekera630ab582019-07-19 09:14:19 +00001575 for (_, p) in self.pkt_infos:
Klement Sekera8d815022021-03-15 16:58:10 +01001576 wi = randrange(self.vpp_worker_count)
Klement Sekera630ab582019-07-19 09:14:19 +00001577 second_packets[wi].append(p[0])
1578 if len(p) <= 1:
1579 continue
1580 wi2 = wi
1581 while wi2 == wi:
Klement Sekera8d815022021-03-15 16:58:10 +01001582 wi2 = randrange(self.vpp_worker_count)
Klement Sekera630ab582019-07-19 09:14:19 +00001583 first_packets[wi2].append(p[1])
Klement Sekera8d815022021-03-15 16:58:10 +01001584 wi3 = randrange(self.vpp_worker_count)
Klement Sekera630ab582019-07-19 09:14:19 +00001585 rest_of_packets[wi3].extend(p[2:])
1586
1587 self.pg_enable_capture()
1588 self.send_packets(first_packets)
1589 self.send_packets(second_packets)
1590 self.send_packets(rest_of_packets)
1591
1592 packets = self.dst_if.get_capture(len(self.pkt_infos))
1593 self.verify_capture(packets)
1594 for send_if in self.send_ifs:
1595 send_if.assert_nothing_captured()
1596
Klement Sekera68bae5b2019-10-10 18:57:34 +00001597 self.logger.debug(self.vapi.ppcli("show trace"))
1598 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1599 self.logger.debug(self.vapi.ppcli("show buffers"))
1600 self.vapi.cli("clear trace")
1601
Klement Sekera630ab582019-07-19 09:14:19 +00001602 self.pg_enable_capture()
1603 self.send_packets(first_packets)
1604 self.send_packets(second_packets)
1605 self.send_packets(rest_of_packets)
1606
1607 packets = self.dst_if.get_capture(len(self.pkt_infos))
1608 self.verify_capture(packets)
1609 for send_if in self.send_ifs:
1610 send_if.assert_nothing_captured()
1611
1612
Klement Sekerade34c352019-06-25 11:19:22 +00001613class TestIPv6SVReassembly(VppTestCase):
1614 """ IPv6 Shallow Virtual Reassembly """
1615
1616 @classmethod
1617 def setUpClass(cls):
1618 super(TestIPv6SVReassembly, cls).setUpClass()
1619
1620 cls.create_pg_interfaces([0, 1])
1621 cls.src_if = cls.pg0
1622 cls.dst_if = cls.pg1
1623
1624 # setup all interfaces
1625 for i in cls.pg_interfaces:
1626 i.admin_up()
1627 i.config_ip6()
1628 i.resolve_ndp()
1629
1630 def setUp(self):
1631 """ Test setup - force timeout on existing reassemblies """
1632 super(TestIPv6SVReassembly, self).setUp()
1633 self.vapi.ip_reassembly_enable_disable(
1634 sw_if_index=self.src_if.sw_if_index, enable_ip6=True,
1635 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
1636 self.vapi.ip_reassembly_set(
1637 timeout_ms=0, max_reassemblies=1000,
1638 max_reassembly_length=1000,
1639 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1640 expire_walk_interval_ms=10, is_ip6=1)
1641 self.sleep(.25)
1642 self.vapi.ip_reassembly_set(
1643 timeout_ms=1000000, max_reassemblies=1000,
1644 max_reassembly_length=1000,
1645 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1646 expire_walk_interval_ms=10000, is_ip6=1)
1647
1648 def tearDown(self):
1649 super(TestIPv6SVReassembly, self).tearDown()
1650 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1651 self.logger.debug(self.vapi.ppcli("show buffers"))
1652
1653 def test_basic(self):
1654 """ basic reassembly """
1655 payload_len = 1000
1656 payload = ""
1657 counter = 0
1658 while len(payload) < payload_len:
1659 payload += "%u " % counter
1660 counter += 1
1661
1662 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1663 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1664 UDP(sport=1234, dport=5678) /
1665 Raw(payload))
1666 fragments = fragment_rfc8200(p, 1, payload_len/4)
1667
1668 # send fragment #2 - should be cached inside reassembly
1669 self.pg_enable_capture()
1670 self.src_if.add_stream(fragments[1])
1671 self.pg_start()
1672 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1673 self.logger.debug(self.vapi.ppcli("show buffers"))
1674 self.logger.debug(self.vapi.ppcli("show trace"))
1675 self.dst_if.assert_nothing_captured()
1676
1677 # send fragment #1 - reassembly is finished now and both fragments
1678 # forwarded
1679 self.pg_enable_capture()
1680 self.src_if.add_stream(fragments[0])
1681 self.pg_start()
1682 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1683 self.logger.debug(self.vapi.ppcli("show buffers"))
1684 self.logger.debug(self.vapi.ppcli("show trace"))
1685 c = self.dst_if.get_capture(2)
1686 for sent, recvd in zip([fragments[1], fragments[0]], c):
1687 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1688 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1689 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1690
1691 # send rest of fragments - should be immediately forwarded
1692 self.pg_enable_capture()
1693 self.src_if.add_stream(fragments[2:])
1694 self.pg_start()
1695 c = self.dst_if.get_capture(len(fragments[2:]))
1696 for sent, recvd in zip(fragments[2:], c):
1697 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1698 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1699 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1700
Klement Sekera53be16d2020-12-15 21:47:36 +01001701 def test_verify_clear_trace_mid_reassembly(self):
1702 """ verify clear trace works mid-reassembly """
1703 payload_len = 1000
1704 payload = ""
1705 counter = 0
1706 while len(payload) < payload_len:
1707 payload += "%u " % counter
1708 counter += 1
1709
1710 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1711 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1712 UDP(sport=1234, dport=5678) /
1713 Raw(payload))
1714 fragments = fragment_rfc8200(p, 1, payload_len/4)
1715
1716 self.pg_enable_capture()
1717 self.src_if.add_stream(fragments[1])
1718 self.pg_start()
1719
1720 self.logger.debug(self.vapi.cli("show trace"))
1721 self.vapi.cli("clear trace")
1722
1723 self.pg_enable_capture()
1724 self.src_if.add_stream(fragments[0])
1725 self.pg_start()
1726 self.dst_if.get_capture(2)
1727
1728 self.logger.debug(self.vapi.cli("show trace"))
1729 self.vapi.cli("clear trace")
1730
1731 self.pg_enable_capture()
1732 self.src_if.add_stream(fragments[2:])
1733 self.pg_start()
1734 self.dst_if.get_capture(len(fragments[2:]))
1735
Klement Sekerade34c352019-06-25 11:19:22 +00001736 def test_timeout(self):
1737 """ reassembly timeout """
1738 payload_len = 1000
1739 payload = ""
1740 counter = 0
1741 while len(payload) < payload_len:
1742 payload += "%u " % counter
1743 counter += 1
1744
1745 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1746 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1747 UDP(sport=1234, dport=5678) /
1748 Raw(payload))
1749 fragments = fragment_rfc8200(p, 1, payload_len/4)
1750
1751 self.vapi.ip_reassembly_set(
1752 timeout_ms=100, max_reassemblies=1000,
1753 max_reassembly_length=1000,
1754 expire_walk_interval_ms=50,
1755 is_ip6=1,
1756 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
1757
1758 # send fragments #2 and #1 - should be forwarded
1759 self.pg_enable_capture()
1760 self.src_if.add_stream(fragments[0:2])
1761 self.pg_start()
1762 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
1763 self.logger.debug(self.vapi.ppcli("show buffers"))
1764 self.logger.debug(self.vapi.ppcli("show trace"))
1765 c = self.dst_if.get_capture(2)
1766 for sent, recvd in zip([fragments[1], fragments[0]], c):
1767 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1768 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1769 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1770
1771 # wait for cleanup
1772 self.sleep(.25, "wait before sending rest of fragments")
1773
1774 # send rest of fragments - shouldn't be forwarded
1775 self.pg_enable_capture()
1776 self.src_if.add_stream(fragments[2:])
1777 self.pg_start()
1778 self.dst_if.assert_nothing_captured()
1779
1780 def test_lru(self):
1781 """ reassembly reuses LRU element """
1782
1783 self.vapi.ip_reassembly_set(
1784 timeout_ms=1000000, max_reassemblies=1,
1785 max_reassembly_length=1000,
1786 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1787 is_ip6=1, expire_walk_interval_ms=10000)
1788
1789 payload_len = 1000
1790 payload = ""
1791 counter = 0
1792 while len(payload) < payload_len:
1793 payload += "%u " % counter
1794 counter += 1
1795
1796 packet_count = 10
1797
1798 fragments = [f
1799 for i in range(packet_count)
1800 for p in (Ether(dst=self.src_if.local_mac,
1801 src=self.src_if.remote_mac) /
1802 IPv6(src=self.src_if.remote_ip6,
1803 dst=self.dst_if.remote_ip6) /
1804 UDP(sport=1234, dport=5678) /
1805 Raw(payload))
1806 for f in fragment_rfc8200(p, i, payload_len/4)]
1807
1808 self.pg_enable_capture()
1809 self.src_if.add_stream(fragments)
1810 self.pg_start()
1811 c = self.dst_if.get_capture(len(fragments))
1812 for sent, recvd in zip(fragments, c):
1813 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1814 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1815 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1816
1817
Juraj Sloboda3048b632018-10-02 11:13:53 +02001818class TestIPv4ReassemblyLocalNode(VppTestCase):
1819 """ IPv4 Reassembly for packets coming to ip4-local node """
1820
1821 @classmethod
1822 def setUpClass(cls):
1823 super(TestIPv4ReassemblyLocalNode, cls).setUpClass()
1824
1825 cls.create_pg_interfaces([0])
1826 cls.src_dst_if = cls.pg0
1827
1828 # setup all interfaces
1829 for i in cls.pg_interfaces:
1830 i.admin_up()
1831 i.config_ip4()
1832 i.resolve_arp()
1833
1834 cls.padding = " abcdefghijklmn"
1835 cls.create_stream()
1836 cls.create_fragments()
1837
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -07001838 @classmethod
1839 def tearDownClass(cls):
1840 super(TestIPv4ReassemblyLocalNode, cls).tearDownClass()
1841
Juraj Sloboda3048b632018-10-02 11:13:53 +02001842 def setUp(self):
1843 """ Test setup - force timeout on existing reassemblies """
1844 super(TestIPv4ReassemblyLocalNode, self).setUp()
1845 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001846 max_reassembly_length=1000,
Juraj Sloboda3048b632018-10-02 11:13:53 +02001847 expire_walk_interval_ms=10)
1848 self.sleep(.25)
1849 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001850 max_reassembly_length=1000,
Juraj Sloboda3048b632018-10-02 11:13:53 +02001851 expire_walk_interval_ms=10000)
1852
1853 def tearDown(self):
1854 super(TestIPv4ReassemblyLocalNode, self).tearDown()
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -07001855
1856 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +00001857 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +01001858 self.logger.debug(self.vapi.ppcli("show buffers"))
Juraj Sloboda3048b632018-10-02 11:13:53 +02001859
1860 @classmethod
1861 def create_stream(cls, packet_count=test_packet_count):
1862 """Create input packet stream for defined interface.
1863
1864 :param list packet_sizes: Required packet sizes.
1865 """
1866 for i in range(0, packet_count):
1867 info = cls.create_packet_info(cls.src_dst_if, cls.src_dst_if)
1868 payload = cls.info_to_payload(info)
1869 p = (Ether(dst=cls.src_dst_if.local_mac,
1870 src=cls.src_dst_if.remote_mac) /
1871 IP(id=info.index, src=cls.src_dst_if.remote_ip4,
1872 dst=cls.src_dst_if.local_ip4) /
1873 ICMP(type='echo-request', id=1234) /
1874 Raw(payload))
1875 cls.extend_packet(p, 1518, cls.padding)
1876 info.data = p
1877
1878 @classmethod
1879 def create_fragments(cls):
1880 infos = cls._packet_infos
1881 cls.pkt_infos = []
Paul Vinciguerra090096b2020-12-03 00:42:46 -05001882 for index, info in infos.items():
Juraj Sloboda3048b632018-10-02 11:13:53 +02001883 p = info.data
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001884 # cls.logger.debug(ppp("Packet:",
1885 # p.__class__(scapy.compat.raw(p))))
Juraj Sloboda3048b632018-10-02 11:13:53 +02001886 fragments_300 = fragment_rfc791(p, 300)
1887 cls.pkt_infos.append((index, fragments_300))
1888 cls.fragments_300 = [x for (_, frags) in cls.pkt_infos for x in frags]
1889 cls.logger.debug("Fragmented %s packets into %s 300-byte fragments" %
1890 (len(infos), len(cls.fragments_300)))
1891
1892 def verify_capture(self, capture):
1893 """Verify captured packet stream.
1894
1895 :param list capture: Captured packet stream.
1896 """
1897 info = None
1898 seen = set()
1899 for packet in capture:
1900 try:
1901 self.logger.debug(ppp("Got packet:", packet))
1902 ip = packet[IP]
1903 icmp = packet[ICMP]
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001904 payload_info = self.payload_to_info(packet[Raw])
Juraj Sloboda3048b632018-10-02 11:13:53 +02001905 packet_index = payload_info.index
1906 if packet_index in seen:
1907 raise Exception(ppp("Duplicate packet received", packet))
1908 seen.add(packet_index)
1909 self.assertEqual(payload_info.dst, self.src_dst_if.sw_if_index)
1910 info = self._packet_infos[packet_index]
Klement Sekera14d7e902018-12-10 13:46:09 +01001911 self.assertIsNotNone(info)
Juraj Sloboda3048b632018-10-02 11:13:53 +02001912 self.assertEqual(packet_index, info.index)
1913 saved_packet = info.data
1914 self.assertEqual(ip.src, saved_packet[IP].dst)
1915 self.assertEqual(ip.dst, saved_packet[IP].src)
1916 self.assertEqual(icmp.type, 0) # echo reply
1917 self.assertEqual(icmp.id, saved_packet[ICMP].id)
1918 self.assertEqual(icmp.payload, saved_packet[ICMP].payload)
1919 except Exception:
1920 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1921 raise
1922 for index in self._packet_infos:
Klement Sekera14d7e902018-12-10 13:46:09 +01001923 self.assertIn(index, seen,
1924 "Packet with packet_index %d not received" % index)
Juraj Sloboda3048b632018-10-02 11:13:53 +02001925
1926 def test_reassembly(self):
1927 """ basic reassembly """
1928
1929 self.pg_enable_capture()
1930 self.src_dst_if.add_stream(self.fragments_300)
1931 self.pg_start()
1932
1933 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
1934 self.verify_capture(packets)
1935
1936 # run it all again to verify correctness
1937 self.pg_enable_capture()
1938 self.src_dst_if.add_stream(self.fragments_300)
1939 self.pg_start()
1940
1941 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
1942 self.verify_capture(packets)
1943
1944
Klement Sekera75e7d132017-09-20 08:26:30 +02001945class TestFIFReassembly(VppTestCase):
1946 """ Fragments in fragments reassembly """
1947
1948 @classmethod
1949 def setUpClass(cls):
1950 super(TestFIFReassembly, cls).setUpClass()
1951
Klement Sekera4c533132018-02-22 11:41:12 +01001952 cls.create_pg_interfaces([0, 1])
1953 cls.src_if = cls.pg0
1954 cls.dst_if = cls.pg1
1955 for i in cls.pg_interfaces:
1956 i.admin_up()
1957 i.config_ip4()
1958 i.resolve_arp()
1959 i.config_ip6()
1960 i.resolve_ndp()
Klement Sekera75e7d132017-09-20 08:26:30 +02001961
Klement Sekera75e7d132017-09-20 08:26:30 +02001962 cls.packet_sizes = [64, 512, 1518, 9018]
1963 cls.padding = " abcdefghijklmn"
1964
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -07001965 @classmethod
1966 def tearDownClass(cls):
1967 super(TestFIFReassembly, cls).tearDownClass()
1968
Klement Sekera75e7d132017-09-20 08:26:30 +02001969 def setUp(self):
1970 """ Test setup - force timeout on existing reassemblies """
1971 super(TestFIFReassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +01001972 self.vapi.ip_reassembly_enable_disable(
1973 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
1974 enable_ip6=True)
1975 self.vapi.ip_reassembly_enable_disable(
1976 sw_if_index=self.dst_if.sw_if_index, enable_ip4=True,
1977 enable_ip6=True)
Klement Sekera75e7d132017-09-20 08:26:30 +02001978 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001979 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001980 expire_walk_interval_ms=10)
1981 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, is_ip6=1)
1984 self.sleep(.25)
1985 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001986 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001987 expire_walk_interval_ms=10000)
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, is_ip6=1)
1991
1992 def tearDown(self):
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -07001993 super(TestFIFReassembly, self).tearDown()
1994
1995 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +00001996 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
1997 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +01001998 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +02001999
2000 def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):
2001 """Verify captured packet stream.
2002
2003 :param list capture: Captured packet stream.
2004 """
2005 info = None
2006 seen = set()
2007 for packet in capture:
2008 try:
Klement Sekera4c533132018-02-22 11:41:12 +01002009 self.logger.debug(ppp("Got packet:", packet))
Klement Sekera75e7d132017-09-20 08:26:30 +02002010 ip = packet[ip_class]
2011 udp = packet[UDP]
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08002012 payload_info = self.payload_to_info(packet[Raw])
Klement Sekera75e7d132017-09-20 08:26:30 +02002013 packet_index = payload_info.index
2014 self.assertTrue(
2015 packet_index not in dropped_packet_indexes,
2016 ppp("Packet received, but should be dropped:", packet))
2017 if packet_index in seen:
2018 raise Exception(ppp("Duplicate packet received", packet))
2019 seen.add(packet_index)
Klement Sekera4c533132018-02-22 11:41:12 +01002020 self.assertEqual(payload_info.dst, self.dst_if.sw_if_index)
Klement Sekera75e7d132017-09-20 08:26:30 +02002021 info = self._packet_infos[packet_index]
2022 self.assertTrue(info is not None)
2023 self.assertEqual(packet_index, info.index)
2024 saved_packet = info.data
2025 self.assertEqual(ip.src, saved_packet[ip_class].src)
2026 self.assertEqual(ip.dst, saved_packet[ip_class].dst)
2027 self.assertEqual(udp.payload, saved_packet[UDP].payload)
Klement Sekera4c533132018-02-22 11:41:12 +01002028 except Exception:
Klement Sekera75e7d132017-09-20 08:26:30 +02002029 self.logger.error(ppp("Unexpected or invalid packet:", packet))
2030 raise
2031 for index in self._packet_infos:
2032 self.assertTrue(index in seen or index in dropped_packet_indexes,
2033 "Packet with packet_index %d not received" % index)
2034
2035 def test_fif4(self):
2036 """ Fragments in fragments (4o4) """
2037
2038 # TODO this should be ideally in setUpClass, but then we hit a bug
2039 # with VppIpRoute incorrectly reporting it's present when it's not
2040 # so we need to manually remove the vpp config, thus we cannot have
2041 # it shared for multiple test cases
2042 self.tun_ip4 = "1.1.1.2"
2043
Klement Sekera4c533132018-02-22 11:41:12 +01002044 self.gre4 = VppGreInterface(self, self.src_if.local_ip4, self.tun_ip4)
Klement Sekera75e7d132017-09-20 08:26:30 +02002045 self.gre4.add_vpp_config()
2046 self.gre4.admin_up()
2047 self.gre4.config_ip4()
2048
Klement Sekera4c533132018-02-22 11:41:12 +01002049 self.vapi.ip_reassembly_enable_disable(
2050 sw_if_index=self.gre4.sw_if_index, enable_ip4=True)
2051
Klement Sekera75e7d132017-09-20 08:26:30 +02002052 self.route4 = VppIpRoute(self, self.tun_ip4, 32,
Klement Sekera4c533132018-02-22 11:41:12 +01002053 [VppRoutePath(self.src_if.remote_ip4,
2054 self.src_if.sw_if_index)])
Klement Sekera75e7d132017-09-20 08:26:30 +02002055 self.route4.add_vpp_config()
2056
2057 self.reset_packet_infos()
2058 for i in range(test_packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01002059 info = self.create_packet_info(self.src_if, self.dst_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02002060 payload = self.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +01002061 # Ethernet header here is only for size calculation, thus it
2062 # doesn't matter how it's initialized. This is to ensure that
2063 # reassembled packet is not > 9000 bytes, so that it's not dropped
2064 p = (Ether() /
2065 IP(id=i, src=self.src_if.remote_ip4,
2066 dst=self.dst_if.remote_ip4) /
2067 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02002068 Raw(payload))
2069 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
2070 self.extend_packet(p, size, self.padding)
Klement Sekera4c533132018-02-22 11:41:12 +01002071 info.data = p[IP] # use only IP part, without ethernet header
Klement Sekera75e7d132017-09-20 08:26:30 +02002072
Paul Vinciguerra090096b2020-12-03 00:42:46 -05002073 fragments = [x for _, p in self._packet_infos.items()
Klement Sekera75e7d132017-09-20 08:26:30 +02002074 for x in fragment_rfc791(p.data, 400)]
2075
2076 encapped_fragments = \
Klement Sekera4c533132018-02-22 11:41:12 +01002077 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
2078 IP(src=self.tun_ip4, dst=self.src_if.local_ip4) /
Klement Sekera75e7d132017-09-20 08:26:30 +02002079 GRE() /
2080 p
2081 for p in fragments]
2082
2083 fragmented_encapped_fragments = \
2084 [x for p in encapped_fragments
2085 for x in fragment_rfc791(p, 200)]
2086
Klement Sekera4c533132018-02-22 11:41:12 +01002087 self.src_if.add_stream(fragmented_encapped_fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02002088
2089 self.pg_enable_capture(self.pg_interfaces)
2090 self.pg_start()
2091
Klement Sekera4c533132018-02-22 11:41:12 +01002092 self.src_if.assert_nothing_captured()
2093 packets = self.dst_if.get_capture(len(self._packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +02002094 self.verify_capture(packets, IP)
2095
2096 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
2097 # so that it's query_vpp_config() works as it should
2098 self.gre4.remove_vpp_config()
Klement Sekera4c533132018-02-22 11:41:12 +01002099 self.logger.debug(self.vapi.ppcli("show interface"))
Klement Sekera75e7d132017-09-20 08:26:30 +02002100
2101 def test_fif6(self):
2102 """ Fragments in fragments (6o6) """
2103 # TODO this should be ideally in setUpClass, but then we hit a bug
2104 # with VppIpRoute incorrectly reporting it's present when it's not
2105 # so we need to manually remove the vpp config, thus we cannot have
2106 # it shared for multiple test cases
2107 self.tun_ip6 = "1002::1"
2108
Neale Ranns5a8844b2019-04-16 07:15:35 +00002109 self.gre6 = VppGreInterface(self, self.src_if.local_ip6, self.tun_ip6)
Klement Sekera75e7d132017-09-20 08:26:30 +02002110 self.gre6.add_vpp_config()
2111 self.gre6.admin_up()
2112 self.gre6.config_ip6()
2113
Klement Sekera4c533132018-02-22 11:41:12 +01002114 self.vapi.ip_reassembly_enable_disable(
2115 sw_if_index=self.gre6.sw_if_index, enable_ip6=True)
2116
Klement Sekera75e7d132017-09-20 08:26:30 +02002117 self.route6 = VppIpRoute(self, self.tun_ip6, 128,
Neale Ranns097fa662018-05-01 05:17:55 -07002118 [VppRoutePath(
2119 self.src_if.remote_ip6,
2120 self.src_if.sw_if_index)])
Klement Sekera75e7d132017-09-20 08:26:30 +02002121 self.route6.add_vpp_config()
2122
2123 self.reset_packet_infos()
2124 for i in range(test_packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01002125 info = self.create_packet_info(self.src_if, self.dst_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02002126 payload = self.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +01002127 # Ethernet header here is only for size calculation, thus it
2128 # doesn't matter how it's initialized. This is to ensure that
2129 # reassembled packet is not > 9000 bytes, so that it's not dropped
2130 p = (Ether() /
2131 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
2132 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02002133 Raw(payload))
2134 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
2135 self.extend_packet(p, size, self.padding)
Klement Sekera4c533132018-02-22 11:41:12 +01002136 info.data = p[IPv6] # use only IPv6 part, without ethernet header
Klement Sekera75e7d132017-09-20 08:26:30 +02002137
Paul Vinciguerra090096b2020-12-03 00:42:46 -05002138 fragments = [x for _, i in self._packet_infos.items()
Klement Sekera75e7d132017-09-20 08:26:30 +02002139 for x in fragment_rfc8200(
2140 i.data, i.index, 400)]
2141
2142 encapped_fragments = \
Klement Sekera4c533132018-02-22 11:41:12 +01002143 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
2144 IPv6(src=self.tun_ip6, dst=self.src_if.local_ip6) /
Klement Sekera75e7d132017-09-20 08:26:30 +02002145 GRE() /
2146 p
2147 for p in fragments]
2148
2149 fragmented_encapped_fragments = \
2150 [x for p in encapped_fragments for x in (
2151 fragment_rfc8200(
2152 p,
2153 2 * len(self._packet_infos) + p[IPv6ExtHdrFragment].id,
2154 200)
2155 if IPv6ExtHdrFragment in p else [p]
2156 )
2157 ]
2158
Klement Sekera4c533132018-02-22 11:41:12 +01002159 self.src_if.add_stream(fragmented_encapped_fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02002160
2161 self.pg_enable_capture(self.pg_interfaces)
2162 self.pg_start()
2163
Klement Sekera4c533132018-02-22 11:41:12 +01002164 self.src_if.assert_nothing_captured()
2165 packets = self.dst_if.get_capture(len(self._packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +02002166 self.verify_capture(packets, IPv6)
2167
2168 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
2169 # so that it's query_vpp_config() works as it should
Klement Sekera3ecc2212018-03-27 10:34:43 +02002170 self.gre6.remove_vpp_config()
Klement Sekera75e7d132017-09-20 08:26:30 +02002171
2172
2173if __name__ == '__main__':
2174 unittest.main(testRunner=VppTestRunner)