blob: aee67b185ccd1df503e2e1be1d80cbae1c1e4d9a [file] [log] [blame]
Klement Sekera75e7d132017-09-20 08:26:30 +02001#!/usr/bin/env python
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -08002
Paul Vinciguerra69555952019-03-01 08:46:29 -08003from random import shuffle
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -08004import six
Klement Sekera75e7d132017-09-20 08:26:30 +02005import unittest
Klement Sekera75e7d132017-09-20 08:26:30 +02006
Paul Vinciguerra69555952019-03-01 08:46:29 -08007from parameterized import parameterized
Klement Sekera75e7d132017-09-20 08:26:30 +02008from scapy.packet import Raw
9from scapy.layers.l2 import Ether, GRE
Juraj Sloboda3048b632018-10-02 11:13:53 +020010from scapy.layers.inet import IP, UDP, ICMP
Paul Vinciguerra69555952019-03-01 08:46:29 -080011
Klement Sekera75e7d132017-09-20 08:26:30 +020012from scapy.layers.inet6 import IPv6, IPv6ExtHdrFragment, ICMPv6ParamProblem,\
13 ICMPv6TimeExceeded
Paul Vinciguerra69555952019-03-01 08:46:29 -080014
15from framework import VppTestCase, VppTestRunner
16from util import ppp, fragment_rfc791, fragment_rfc8200
Klement Sekera75e7d132017-09-20 08:26:30 +020017from vpp_gre_interface import VppGreInterface, VppGre6Interface
Neale Rannsc0a93142018-09-05 15:42:26 -070018from vpp_ip import DpoProto
19from vpp_ip_route import VppIpRoute, VppRoutePath
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
Paul Vinciguerra69555952019-03-01 08:46:29 -080024# <class 'scapy.layers.inet.IP'>
25# <class 'scapy.layers.inet6.IPv6'>
26_scapy_ip_family_types = (IP, IPv6)
Klement Sekera75e7d132017-09-20 08:26:30 +020027
Paul Vinciguerra69555952019-03-01 08:46:29 -080028
29def validate_scapy_ip_family(scapy_ip_family):
30
31 if scapy_ip_family not in _scapy_ip_family_types:
32 raise ValueError("'scapy_ip_family' must be of type: %s. Got %s" %
33 (_scapy_ip_family_types, scapy_ip_family))
34
35
36class TestIPReassemblyMixin(object):
37
38 def verify_capture(self, scapy_ip_family, capture,
39 dropped_packet_indexes=None):
40 """Verify captured packet stream.
41
42 :param list capture: Captured packet stream.
43 """
44 validate_scapy_ip_family(scapy_ip_family)
45
46 if dropped_packet_indexes is None:
47 dropped_packet_indexes = []
48 info = None
49 seen = set()
50 for packet in capture:
51 try:
52 self.logger.debug(ppp("Got packet:", packet))
53 ip = packet[scapy_ip_family]
54 udp = packet[UDP]
55 payload_info = self.payload_to_info(str(packet[Raw]))
56 packet_index = payload_info.index
57 self.assertTrue(
58 packet_index not in dropped_packet_indexes,
59 ppp("Packet received, but should be dropped:", packet))
60 if packet_index in seen:
61 raise Exception(ppp("Duplicate packet received", packet))
62 seen.add(packet_index)
63 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
64 info = self._packet_infos[packet_index]
65 self.assertTrue(info is not None)
66 self.assertEqual(packet_index, info.index)
67 saved_packet = info.data
68 self.assertEqual(ip.src, saved_packet[scapy_ip_family].src)
69 self.assertEqual(ip.dst, saved_packet[scapy_ip_family].dst)
70 self.assertEqual(udp.payload, saved_packet[UDP].payload)
71 except Exception:
72 self.logger.error(ppp("Unexpected or invalid packet:", packet))
73 raise
74 for index in self._packet_infos:
75 self.assertTrue(index in seen or index in dropped_packet_indexes,
76 "Packet with packet_index %d not received" % index)
77
78 def test_disabled(self, scapy_ip_family, stream,
79 dropped_packet_indexes):
80 """ reassembly disabled """
81 validate_scapy_ip_family(scapy_ip_family)
82 is_ip6 = 1 if scapy_ip_family == IPv6 else 0
83
84 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
85 expire_walk_interval_ms=10000,
86 is_ip6=is_ip6)
87
88 self.pg_enable_capture()
89 self.src_if.add_stream(stream)
90 self.pg_start()
91
92 packets = self.dst_if.get_capture(
93 len(self.pkt_infos) - len(dropped_packet_indexes))
94 self.verify_capture(scapy_ip_family, packets, dropped_packet_indexes)
95 self.src_if.assert_nothing_captured()
96
97 def test_duplicates(self, scapy_ip_family, stream):
98 """ duplicate fragments """
99 validate_scapy_ip_family(scapy_ip_family)
100
101 self.pg_enable_capture()
102 self.src_if.add_stream(stream)
103 self.pg_start()
104
105 packets = self.dst_if.get_capture(len(self.pkt_infos))
106 self.verify_capture(scapy_ip_family, packets)
107 self.src_if.assert_nothing_captured()
108
109 def test_random(self, scapy_ip_family, stream):
110 """ random order reassembly """
111 validate_scapy_ip_family(scapy_ip_family)
112
113 fragments = list(stream)
114 shuffle(fragments)
115
116 self.pg_enable_capture()
117 self.src_if.add_stream(fragments)
118 self.pg_start()
119
120 packets = self.dst_if.get_capture(len(self.packet_infos))
121 self.verify_capture(scapy_ip_family, packets)
122 self.src_if.assert_nothing_captured()
123
124 # run it all again to verify correctness
125 self.pg_enable_capture()
126 self.src_if.add_stream(fragments)
127 self.pg_start()
128
129 packets = self.dst_if.get_capture(len(self.packet_infos))
130 self.verify_capture(scapy_ip_family, packets)
131 self.src_if.assert_nothing_captured()
132
133 def test_reassembly(self, scapy_ip_family, stream):
134 """ basic reassembly """
135 validate_scapy_ip_family(scapy_ip_family)
136
137 self.pg_enable_capture()
138 self.src_if.add_stream(stream)
139 self.pg_start()
140
141 packets = self.dst_if.get_capture(len(self.pkt_infos))
142 self.verify_capture(scapy_ip_family, packets)
143 self.src_if.assert_nothing_captured()
144
145 # run it all again to verify correctness
146 self.pg_enable_capture()
147 self.src_if.add_stream(stream)
148 self.pg_start()
149
150 packets = self.dst_if.get_capture(len(self.pkt_infos))
151 self.verify_capture(scapy_ip_family, packets)
152 self.src_if.assert_nothing_captured()
153
154 def test_reversed(self, scapy_ip_family, stream):
155 """ reverse order reassembly """
156 validate_scapy_ip_family(scapy_ip_family)
157
158 fragments = list(stream)
159 fragments.reverse()
160
161 self.pg_enable_capture()
162 self.src_if.add_stream(fragments)
163 self.pg_start()
164
165 packets = self.dst_if.get_capture(len(self.packet_infos))
166 self.verify_capture(scapy_ip_family, packets)
167 self.src_if.assert_nothing_captured()
168
169 # run it all again to verify correctness
170 self.pg_enable_capture()
171 self.src_if.add_stream(fragments)
172 self.pg_start()
173
174 packets = self.dst_if.get_capture(len(self.packet_infos))
175 self.verify_capture(scapy_ip_family, packets)
176 self.src_if.assert_nothing_captured()
177
178 def test_timeout_inline(self, scapy_ip_family, stream,
179 dropped_packet_indexes):
180 """ timeout (inline) """
181 validate_scapy_ip_family(scapy_ip_family)
182 is_ip6 = 1 if scapy_ip_family == IPv6 else 0
183
184 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
185 expire_walk_interval_ms=10000,
186 is_ip6=is_ip6)
187
188 self.pg_enable_capture()
189 self.src_if.add_stream(stream)
190 self.pg_start()
191
192 packets = self.dst_if.get_capture(
193 len(self.pkt_infos) - len(dropped_packet_indexes))
194 self.verify_capture(scapy_ip_family, packets,
195 dropped_packet_indexes)
196
197
198class TestIPv4Reassembly(TestIPReassemblyMixin, VppTestCase):
Klement Sekera75e7d132017-09-20 08:26:30 +0200199 """ IPv4 Reassembly """
200
201 @classmethod
202 def setUpClass(cls):
203 super(TestIPv4Reassembly, cls).setUpClass()
204
Klement Sekera4c533132018-02-22 11:41:12 +0100205 cls.create_pg_interfaces([0, 1])
206 cls.src_if = cls.pg0
207 cls.dst_if = cls.pg1
Klement Sekera75e7d132017-09-20 08:26:30 +0200208
209 # setup all interfaces
210 for i in cls.pg_interfaces:
211 i.admin_up()
212 i.config_ip4()
213 i.resolve_arp()
214
Klement Sekera75e7d132017-09-20 08:26:30 +0200215 # packet sizes
216 cls.packet_sizes = [64, 512, 1518, 9018]
217 cls.padding = " abcdefghijklmn"
218 cls.create_stream(cls.packet_sizes)
219 cls.create_fragments()
220
221 def setUp(self):
222 """ Test setup - force timeout on existing reassemblies """
223 super(TestIPv4Reassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +0100224 self.vapi.ip_reassembly_enable_disable(
225 sw_if_index=self.src_if.sw_if_index, enable_ip4=True)
Klement Sekera75e7d132017-09-20 08:26:30 +0200226 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
227 expire_walk_interval_ms=10)
228 self.sleep(.25)
229 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
230 expire_walk_interval_ms=10000)
231
232 def tearDown(self):
233 super(TestIPv4Reassembly, self).tearDown()
234 self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +0100235 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +0200236
237 @classmethod
238 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
Klement Sekerad0f70a32018-12-14 17:24:13 +0100239 """Create input packet stream
Klement Sekera75e7d132017-09-20 08:26:30 +0200240
241 :param list packet_sizes: Required packet sizes.
242 """
243 for i in range(0, packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +0100244 info = cls.create_packet_info(cls.src_if, cls.src_if)
Klement Sekera75e7d132017-09-20 08:26:30 +0200245 payload = cls.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +0100246 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
247 IP(id=info.index, src=cls.src_if.remote_ip4,
248 dst=cls.dst_if.remote_ip4) /
249 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200250 Raw(payload))
251 size = packet_sizes[(i // 2) % len(packet_sizes)]
252 cls.extend_packet(p, size, cls.padding)
253 info.data = p
254
255 @classmethod
256 def create_fragments(cls):
257 infos = cls._packet_infos
258 cls.pkt_infos = []
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -0800259 for index, info in six.iteritems(infos):
Klement Sekera75e7d132017-09-20 08:26:30 +0200260 p = info.data
Klement Sekera4c533132018-02-22 11:41:12 +0100261 # cls.logger.debug(ppp("Packet:", p.__class__(str(p))))
Klement Sekera75e7d132017-09-20 08:26:30 +0200262 fragments_400 = fragment_rfc791(p, 400)
263 fragments_300 = fragment_rfc791(p, 300)
264 fragments_200 = [
265 x for f in fragments_400 for x in fragment_rfc791(f, 200)]
266 cls.pkt_infos.append(
267 (index, fragments_400, fragments_300, fragments_200))
268 cls.fragments_400 = [
269 x for (_, frags, _, _) in cls.pkt_infos for x in frags]
270 cls.fragments_300 = [
271 x for (_, _, frags, _) in cls.pkt_infos for x in frags]
272 cls.fragments_200 = [
273 x for (_, _, _, frags) in cls.pkt_infos for x in frags]
274 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
275 "%s 300-byte fragments and %s 200-byte fragments" %
276 (len(infos), len(cls.fragments_400),
277 len(cls.fragments_300), len(cls.fragments_200)))
278
Paul Vinciguerra69555952019-03-01 08:46:29 -0800279 @parameterized.expand([(IP, None)])
280 def test_reassembly(self, family, stream):
Klement Sekera75e7d132017-09-20 08:26:30 +0200281 """ basic reassembly """
Paul Vinciguerra69555952019-03-01 08:46:29 -0800282 stream = self.__class__.fragments_200
283 super(TestIPv4Reassembly, self).test_reassembly(family, stream)
Klement Sekera75e7d132017-09-20 08:26:30 +0200284
Paul Vinciguerra69555952019-03-01 08:46:29 -0800285 @parameterized.expand([(IP, None)])
286 def test_reversed(self, family, stream):
Klement Sekera75e7d132017-09-20 08:26:30 +0200287 """ reverse order reassembly """
Paul Vinciguerra69555952019-03-01 08:46:29 -0800288 stream = self.__class__.fragments_200
289 super(TestIPv4Reassembly, self).test_reversed(family, stream)
Klement Sekera75e7d132017-09-20 08:26:30 +0200290
Paul Vinciguerra69555952019-03-01 08:46:29 -0800291 @parameterized.expand([(IP, None)])
292 def test_random(self, family, stream):
293 stream = self.__class__.fragments_200
294 super(TestIPv4Reassembly, self).test_random(family, stream)
Klement Sekera75e7d132017-09-20 08:26:30 +0200295
Klement Sekera14d7e902018-12-10 13:46:09 +0100296 def test_5737(self):
297 """ fragment length + ip header size > 65535 """
Klement Sekera4ee633e2018-12-14 12:00:44 +0100298 self.vapi.cli("clear errors")
Klement Sekera14d7e902018-12-10 13:46:09 +0100299 raw = ('E\x00\x00\x88,\xf8\x1f\xfe@\x01\x98\x00\xc0\xa8\n-\xc0\xa8\n'
300 '\x01\x08\x00\xf0J\xed\xcb\xf1\xf5Test-group: IPv4.IPv4.ipv4-'
301 'message.Ethernet-Payload.IPv4-Packet.IPv4-Header.Fragment-Of'
302 'fset; Test-case: 5737')
303
304 malformed_packet = (Ether(dst=self.src_if.local_mac,
305 src=self.src_if.remote_mac) /
306 IP(raw))
307 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
308 IP(id=1000, src=self.src_if.remote_ip4,
309 dst=self.dst_if.remote_ip4) /
310 UDP(sport=1234, dport=5678) /
311 Raw("X" * 1000))
312 valid_fragments = fragment_rfc791(p, 400)
313
314 self.pg_enable_capture()
315 self.src_if.add_stream([malformed_packet] + valid_fragments)
316 self.pg_start()
317
318 self.dst_if.get_capture(1)
Klement Sekera4ee633e2018-12-14 12:00:44 +0100319 self.assert_packet_counter_equal("ip4-reassembly-feature", 1)
320 # TODO remove above, uncomment below once clearing of counters
321 # is supported
322 # self.assert_packet_counter_equal(
323 # "/err/ip4-reassembly-feature/malformed packets", 1)
Klement Sekera14d7e902018-12-10 13:46:09 +0100324
Klement Sekera400f6d82018-12-13 14:35:48 +0100325 def test_44924(self):
326 """ compress tiny fragments """
327 packets = [(Ether(dst=self.src_if.local_mac,
328 src=self.src_if.remote_mac) /
329 IP(id=24339, flags="MF", frag=0, ttl=64,
330 src=self.src_if.remote_ip4,
331 dst=self.dst_if.remote_ip4) /
332 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
333 Raw(load='Test-group: IPv4')),
334 (Ether(dst=self.src_if.local_mac,
335 src=self.src_if.remote_mac) /
336 IP(id=24339, flags="MF", frag=3, ttl=64,
337 src=self.src_if.remote_ip4,
338 dst=self.dst_if.remote_ip4) /
339 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
340 Raw(load='.IPv4.Fragmentation.vali')),
341 (Ether(dst=self.src_if.local_mac,
342 src=self.src_if.remote_mac) /
343 IP(id=24339, frag=6, ttl=64,
344 src=self.src_if.remote_ip4,
345 dst=self.dst_if.remote_ip4) /
346 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
347 Raw(load='d; Test-case: 44924'))
348 ]
349
350 self.pg_enable_capture()
351 self.src_if.add_stream(packets)
352 self.pg_start()
353
354 self.dst_if.get_capture(1)
355
Klement Sekera4ee633e2018-12-14 12:00:44 +0100356 def test_frag_1(self):
357 """ fragment of size 1 """
358 self.vapi.cli("clear errors")
359 malformed_packets = [(Ether(dst=self.src_if.local_mac,
360 src=self.src_if.remote_mac) /
361 IP(id=7, len=21, flags="MF", frag=0, ttl=64,
362 src=self.src_if.remote_ip4,
363 dst=self.dst_if.remote_ip4) /
364 ICMP(type="echo-request")),
365 (Ether(dst=self.src_if.local_mac,
366 src=self.src_if.remote_mac) /
367 IP(id=7, len=21, frag=1, ttl=64,
368 src=self.src_if.remote_ip4,
369 dst=self.dst_if.remote_ip4) /
370 Raw(load='\x08')),
371 ]
372
373 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
374 IP(id=1000, src=self.src_if.remote_ip4,
375 dst=self.dst_if.remote_ip4) /
376 UDP(sport=1234, dport=5678) /
377 Raw("X" * 1000))
378 valid_fragments = fragment_rfc791(p, 400)
379
380 self.pg_enable_capture()
381 self.src_if.add_stream(malformed_packets + valid_fragments)
382 self.pg_start()
383
384 self.dst_if.get_capture(1)
385
386 self.assert_packet_counter_equal("ip4-reassembly-feature", 1)
387 # TODO remove above, uncomment below once clearing of counters
388 # is supported
389 # self.assert_packet_counter_equal(
390 # "/err/ip4-reassembly-feature/malformed packets", 1)
391
Paul Vinciguerra69555952019-03-01 08:46:29 -0800392 @parameterized.expand([(IP, None)])
393 def test_duplicates(self, family, stream):
Klement Sekera75e7d132017-09-20 08:26:30 +0200394 """ duplicate fragments """
Klement Sekera75e7d132017-09-20 08:26:30 +0200395 fragments = [
Paul Vinciguerra69555952019-03-01 08:46:29 -0800396 # IPv4 uses 4 fields in pkt_infos, IPv6 uses 3.
Klement Sekera75e7d132017-09-20 08:26:30 +0200397 x for (_, frags, _, _) in self.pkt_infos
398 for x in frags
399 for _ in range(0, min(2, len(frags)))
400 ]
Paul Vinciguerra69555952019-03-01 08:46:29 -0800401 super(TestIPv4Reassembly, self).test_duplicates(family, fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200402
403 def test_overlap1(self):
404 """ overlapping fragments case #1 """
405
406 fragments = []
407 for _, _, frags_300, frags_200 in self.pkt_infos:
408 if len(frags_300) == 1:
409 fragments.extend(frags_300)
410 else:
411 for i, j in zip(frags_200, frags_300):
412 fragments.extend(i)
413 fragments.extend(j)
414
415 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100416 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200417 self.pg_start()
418
Klement Sekera4c533132018-02-22 11:41:12 +0100419 packets = self.dst_if.get_capture(len(self.pkt_infos))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800420 self.verify_capture(IP, packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100421 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200422
423 # run it all to verify correctness
424 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100425 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200426 self.pg_start()
427
Klement Sekera4c533132018-02-22 11:41:12 +0100428 packets = self.dst_if.get_capture(len(self.pkt_infos))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800429 self.verify_capture(IP, packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100430 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200431
432 def test_overlap2(self):
433 """ overlapping fragments case #2 """
434
435 fragments = []
436 for _, _, frags_300, frags_200 in self.pkt_infos:
437 if len(frags_300) == 1:
438 fragments.extend(frags_300)
439 else:
440 # care must be taken here so that there are no fragments
441 # received by vpp after reassembly is finished, otherwise
442 # new reassemblies will be started and packet generator will
443 # freak out when it detects unfreed buffers
444 zipped = zip(frags_300, frags_200)
445 for i, j in zipped[:-1]:
446 fragments.extend(i)
447 fragments.extend(j)
448 fragments.append(zipped[-1][0])
449
450 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100451 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200452 self.pg_start()
453
Klement Sekera4c533132018-02-22 11:41:12 +0100454 packets = self.dst_if.get_capture(len(self.pkt_infos))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800455 self.verify_capture(IP, packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100456 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200457
458 # run it all to verify correctness
459 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100460 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200461 self.pg_start()
462
Klement Sekera4c533132018-02-22 11:41:12 +0100463 packets = self.dst_if.get_capture(len(self.pkt_infos))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800464 self.verify_capture(IP, packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100465 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200466
Paul Vinciguerra69555952019-03-01 08:46:29 -0800467 @parameterized.expand([(IP, None, None)])
468 def test_timeout_inline(self, family, stream, dropped_packet_indexes):
Klement Sekera75e7d132017-09-20 08:26:30 +0200469 """ timeout (inline) """
Paul Vinciguerra69555952019-03-01 08:46:29 -0800470 stream = self.fragments_400
Klement Sekera75e7d132017-09-20 08:26:30 +0200471
472 dropped_packet_indexes = set(
473 index for (index, frags, _, _) in self.pkt_infos if len(frags) > 1
474 )
Paul Vinciguerra69555952019-03-01 08:46:29 -0800475 super(TestIPv4Reassembly, self).test_timeout_inline(
476 family, stream, dropped_packet_indexes)
Klement Sekera75e7d132017-09-20 08:26:30 +0200477
Klement Sekera4c533132018-02-22 11:41:12 +0100478 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200479
480 def test_timeout_cleanup(self):
481 """ timeout (cleanup) """
482
483 # whole packets + fragmented packets sans last fragment
484 fragments = [
485 x for (_, frags_400, _, _) in self.pkt_infos
486 for x in frags_400[:-1 if len(frags_400) > 1 else None]
487 ]
488
489 # last fragments for fragmented packets
490 fragments2 = [frags_400[-1]
491 for (_, frags_400, _, _) in self.pkt_infos
492 if len(frags_400) > 1]
493
494 dropped_packet_indexes = set(
495 index for (index, frags_400, _, _) in self.pkt_infos
496 if len(frags_400) > 1)
497
498 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
499 expire_walk_interval_ms=50)
500
501 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100502 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200503 self.pg_start()
504
505 self.sleep(.25, "wait before sending rest of fragments")
506
Klement Sekera4c533132018-02-22 11:41:12 +0100507 self.src_if.add_stream(fragments2)
Klement Sekera75e7d132017-09-20 08:26:30 +0200508 self.pg_start()
Klement Sekera75e7d132017-09-20 08:26:30 +0200509
Klement Sekera4c533132018-02-22 11:41:12 +0100510 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200511 len(self.pkt_infos) - len(dropped_packet_indexes))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800512 self.verify_capture(IP, packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100513 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200514
Paul Vinciguerra69555952019-03-01 08:46:29 -0800515 @parameterized.expand([(IP, None, None)])
516 def test_disabled(self, family, stream, dropped_packet_indexes):
Klement Sekera75e7d132017-09-20 08:26:30 +0200517 """ reassembly disabled """
518
Paul Vinciguerra69555952019-03-01 08:46:29 -0800519 stream = self.__class__.fragments_400
Klement Sekera75e7d132017-09-20 08:26:30 +0200520 dropped_packet_indexes = set(
521 index for (index, frags_400, _, _) in self.pkt_infos
522 if len(frags_400) > 1)
Paul Vinciguerra69555952019-03-01 08:46:29 -0800523 super(TestIPv4Reassembly, self).test_disabled(
524 family, stream, dropped_packet_indexes)
Klement Sekera75e7d132017-09-20 08:26:30 +0200525
526
Paul Vinciguerra69555952019-03-01 08:46:29 -0800527class TestIPv6Reassembly(TestIPReassemblyMixin, VppTestCase):
Klement Sekera75e7d132017-09-20 08:26:30 +0200528 """ IPv6 Reassembly """
529
530 @classmethod
531 def setUpClass(cls):
532 super(TestIPv6Reassembly, cls).setUpClass()
533
Klement Sekera4c533132018-02-22 11:41:12 +0100534 cls.create_pg_interfaces([0, 1])
535 cls.src_if = cls.pg0
536 cls.dst_if = cls.pg1
Klement Sekera75e7d132017-09-20 08:26:30 +0200537
538 # setup all interfaces
539 for i in cls.pg_interfaces:
540 i.admin_up()
541 i.config_ip6()
542 i.resolve_ndp()
543
Klement Sekera75e7d132017-09-20 08:26:30 +0200544 # packet sizes
545 cls.packet_sizes = [64, 512, 1518, 9018]
546 cls.padding = " abcdefghijklmn"
547 cls.create_stream(cls.packet_sizes)
548 cls.create_fragments()
549
550 def setUp(self):
551 """ Test setup - force timeout on existing reassemblies """
552 super(TestIPv6Reassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +0100553 self.vapi.ip_reassembly_enable_disable(
554 sw_if_index=self.src_if.sw_if_index, enable_ip6=True)
Klement Sekera75e7d132017-09-20 08:26:30 +0200555 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
556 expire_walk_interval_ms=10, is_ip6=1)
557 self.sleep(.25)
558 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
559 expire_walk_interval_ms=10000, is_ip6=1)
Klement Sekera4c533132018-02-22 11:41:12 +0100560 self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +0100561 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +0200562
563 def tearDown(self):
564 super(TestIPv6Reassembly, self).tearDown()
565 self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +0100566 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +0200567
568 @classmethod
569 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
570 """Create input packet stream for defined interface.
571
572 :param list packet_sizes: Required packet sizes.
573 """
574 for i in range(0, packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +0100575 info = cls.create_packet_info(cls.src_if, cls.src_if)
Klement Sekera75e7d132017-09-20 08:26:30 +0200576 payload = cls.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +0100577 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
578 IPv6(src=cls.src_if.remote_ip6,
579 dst=cls.dst_if.remote_ip6) /
580 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200581 Raw(payload))
582 size = packet_sizes[(i // 2) % len(packet_sizes)]
583 cls.extend_packet(p, size, cls.padding)
584 info.data = p
585
586 @classmethod
587 def create_fragments(cls):
588 infos = cls._packet_infos
589 cls.pkt_infos = []
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -0800590 for index, info in six.iteritems(infos):
Klement Sekera75e7d132017-09-20 08:26:30 +0200591 p = info.data
Klement Sekera4c533132018-02-22 11:41:12 +0100592 # cls.logger.debug(ppp("Packet:", p.__class__(str(p))))
Klement Sekera75e7d132017-09-20 08:26:30 +0200593 fragments_400 = fragment_rfc8200(p, info.index, 400)
594 fragments_300 = fragment_rfc8200(p, info.index, 300)
595 cls.pkt_infos.append((index, fragments_400, fragments_300))
596 cls.fragments_400 = [
597 x for _, frags, _ in cls.pkt_infos for x in frags]
598 cls.fragments_300 = [
599 x for _, _, frags in cls.pkt_infos for x in frags]
600 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
601 "and %s 300-byte fragments" %
602 (len(infos), len(cls.fragments_400),
603 len(cls.fragments_300)))
604
Paul Vinciguerra69555952019-03-01 08:46:29 -0800605 @parameterized.expand([(IPv6, None)])
606 def test_reassembly(self, family, stream):
Klement Sekera75e7d132017-09-20 08:26:30 +0200607 """ basic reassembly """
Paul Vinciguerra69555952019-03-01 08:46:29 -0800608 stream = self.__class__.fragments_400
609 super(TestIPv6Reassembly, self).test_reassembly(family, stream)
Klement Sekera75e7d132017-09-20 08:26:30 +0200610
Paul Vinciguerra69555952019-03-01 08:46:29 -0800611 @parameterized.expand([(IPv6, None)])
612 def test_reversed(self, family, stream):
Klement Sekera75e7d132017-09-20 08:26:30 +0200613 """ reverse order reassembly """
Paul Vinciguerra69555952019-03-01 08:46:29 -0800614 stream = self.__class__.fragments_400
615 super(TestIPv6Reassembly, self).test_reversed(family, stream)
Klement Sekera75e7d132017-09-20 08:26:30 +0200616
Paul Vinciguerra69555952019-03-01 08:46:29 -0800617 @parameterized.expand([(IPv6, None)])
618 def test_random(self, family, stream):
Klement Sekera75e7d132017-09-20 08:26:30 +0200619 """ random order reassembly """
Paul Vinciguerra69555952019-03-01 08:46:29 -0800620 stream = self.__class__.fragments_400
621 super(TestIPv6Reassembly, self).test_random(family, stream)
Klement Sekera75e7d132017-09-20 08:26:30 +0200622
Paul Vinciguerra69555952019-03-01 08:46:29 -0800623 @parameterized.expand([(IPv6, None)])
624 def test_duplicates(self, family, stream):
Klement Sekera75e7d132017-09-20 08:26:30 +0200625 """ duplicate fragments """
626
627 fragments = [
Paul Vinciguerra69555952019-03-01 08:46:29 -0800628 # IPv4 uses 4 fields in pkt_infos, IPv6 uses 3.
Klement Sekera75e7d132017-09-20 08:26:30 +0200629 x for (_, frags, _) in self.pkt_infos
630 for x in frags
631 for _ in range(0, min(2, len(frags)))
632 ]
Paul Vinciguerra69555952019-03-01 08:46:29 -0800633 super(TestIPv6Reassembly, self).test_duplicates(family, fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200634
635 def test_overlap1(self):
Paul Vinciguerra69555952019-03-01 08:46:29 -0800636 """ overlapping fragments case #1 (differs from IP test case)"""
Klement Sekera75e7d132017-09-20 08:26:30 +0200637
638 fragments = []
639 for _, frags_400, frags_300 in self.pkt_infos:
640 if len(frags_300) == 1:
641 fragments.extend(frags_400)
642 else:
643 for i, j in zip(frags_300, frags_400):
644 fragments.extend(i)
645 fragments.extend(j)
646
647 dropped_packet_indexes = set(
648 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
649 )
650
651 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100652 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200653 self.pg_start()
654
Klement Sekera4c533132018-02-22 11:41:12 +0100655 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200656 len(self.pkt_infos) - len(dropped_packet_indexes))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800657 self.verify_capture(IPv6, packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100658 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200659
660 def test_overlap2(self):
Paul Vinciguerra69555952019-03-01 08:46:29 -0800661 """ overlapping fragments case #2 (differs from IP test case)"""
Klement Sekera75e7d132017-09-20 08:26:30 +0200662
663 fragments = []
Klement Sekera4c533132018-02-22 11:41:12 +0100664 for _, frags_400, frags_300 in self.pkt_infos:
Klement Sekera75e7d132017-09-20 08:26:30 +0200665 if len(frags_400) == 1:
666 fragments.extend(frags_400)
667 else:
668 # care must be taken here so that there are no fragments
669 # received by vpp after reassembly is finished, otherwise
670 # new reassemblies will be started and packet generator will
671 # freak out when it detects unfreed buffers
Klement Sekera4c533132018-02-22 11:41:12 +0100672 zipped = zip(frags_400, frags_300)
Klement Sekera75e7d132017-09-20 08:26:30 +0200673 for i, j in zipped[:-1]:
674 fragments.extend(i)
675 fragments.extend(j)
676 fragments.append(zipped[-1][0])
677
678 dropped_packet_indexes = set(
679 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
680 )
681
682 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100683 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200684 self.pg_start()
685
Klement Sekera4c533132018-02-22 11:41:12 +0100686 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200687 len(self.pkt_infos) - len(dropped_packet_indexes))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800688 self.verify_capture(IPv6, packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100689 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200690
Paul Vinciguerra69555952019-03-01 08:46:29 -0800691 @parameterized.expand([(IPv6, None, None)])
692 def test_timeout_inline(self, family, stream, dropped_packets_index):
Klement Sekera75e7d132017-09-20 08:26:30 +0200693 """ timeout (inline) """
Paul Vinciguerra69555952019-03-01 08:46:29 -0800694 stream = self.__class__.fragments_400
Klement Sekera75e7d132017-09-20 08:26:30 +0200695
696 dropped_packet_indexes = set(
697 index for (index, frags, _) in self.pkt_infos if len(frags) > 1
698 )
Paul Vinciguerra69555952019-03-01 08:46:29 -0800699 super(TestIPv6Reassembly, self).test_timeout_inline(
700 family, stream, dropped_packet_indexes)
Klement Sekera75e7d132017-09-20 08:26:30 +0200701
Klement Sekera4c533132018-02-22 11:41:12 +0100702 pkts = self.src_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200703 expected_count=len(dropped_packet_indexes))
704 for icmp in pkts:
705 self.assertIn(ICMPv6TimeExceeded, icmp)
706 self.assertIn(IPv6ExtHdrFragment, icmp)
707 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
708 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
709
710 def test_timeout_cleanup(self):
711 """ timeout (cleanup) """
712
713 # whole packets + fragmented packets sans last fragment
714 fragments = [
715 x for (_, frags_400, _) in self.pkt_infos
716 for x in frags_400[:-1 if len(frags_400) > 1 else None]
717 ]
718
719 # last fragments for fragmented packets
720 fragments2 = [frags_400[-1]
721 for (_, frags_400, _) in self.pkt_infos
722 if len(frags_400) > 1]
723
724 dropped_packet_indexes = set(
725 index for (index, frags_400, _) in self.pkt_infos
726 if len(frags_400) > 1)
727
728 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
729 expire_walk_interval_ms=50)
730
731 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
732 expire_walk_interval_ms=50, is_ip6=1)
733
734 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100735 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200736 self.pg_start()
737
738 self.sleep(.25, "wait before sending rest of fragments")
739
Klement Sekera4c533132018-02-22 11:41:12 +0100740 self.src_if.add_stream(fragments2)
Klement Sekera75e7d132017-09-20 08:26:30 +0200741 self.pg_start()
Klement Sekera75e7d132017-09-20 08:26:30 +0200742
Klement Sekera4c533132018-02-22 11:41:12 +0100743 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200744 len(self.pkt_infos) - len(dropped_packet_indexes))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800745 self.verify_capture(IPv6, packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100746 pkts = self.src_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200747 expected_count=len(dropped_packet_indexes))
748 for icmp in pkts:
749 self.assertIn(ICMPv6TimeExceeded, icmp)
750 self.assertIn(IPv6ExtHdrFragment, icmp)
751 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
752 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
753
Paul Vinciguerra69555952019-03-01 08:46:29 -0800754 @parameterized.expand([(IPv6, None, None)])
755 def test_disabled(self, family, stream, dropped_packet_indexes):
Klement Sekera75e7d132017-09-20 08:26:30 +0200756 """ reassembly disabled """
757
Paul Vinciguerra69555952019-03-01 08:46:29 -0800758 stream = self.__class__.fragments_400
Klement Sekera75e7d132017-09-20 08:26:30 +0200759 dropped_packet_indexes = set(
760 index for (index, frags_400, _) in self.pkt_infos
761 if len(frags_400) > 1)
Paul Vinciguerra69555952019-03-01 08:46:29 -0800762 super(TestIPv6Reassembly, self).test_disabled(
763 family, stream, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100764 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200765
766 def test_missing_upper(self):
767 """ missing upper layer """
Klement Sekera4c533132018-02-22 11:41:12 +0100768 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
769 IPv6(src=self.src_if.remote_ip6,
770 dst=self.src_if.local_ip6) /
771 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200772 Raw())
773 self.extend_packet(p, 1000, self.padding)
774 fragments = fragment_rfc8200(p, 1, 500)
775 bad_fragment = p.__class__(str(fragments[1]))
776 bad_fragment[IPv6ExtHdrFragment].nh = 59
777 bad_fragment[IPv6ExtHdrFragment].offset = 0
778 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100779 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +0200780 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +0100781 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +0200782 icmp = pkts[0]
783 self.assertIn(ICMPv6ParamProblem, icmp)
784 self.assert_equal(icmp[ICMPv6ParamProblem].code, 3, "ICMP code")
785
786 def test_invalid_frag_size(self):
787 """ fragment size not a multiple of 8 """
Klement Sekera4c533132018-02-22 11:41:12 +0100788 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
789 IPv6(src=self.src_if.remote_ip6,
790 dst=self.src_if.local_ip6) /
791 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200792 Raw())
793 self.extend_packet(p, 1000, self.padding)
794 fragments = fragment_rfc8200(p, 1, 500)
795 bad_fragment = fragments[0]
796 self.extend_packet(bad_fragment, len(bad_fragment) + 5)
797 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100798 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +0200799 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +0100800 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +0200801 icmp = pkts[0]
802 self.assertIn(ICMPv6ParamProblem, icmp)
803 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
804
805 def test_invalid_packet_size(self):
806 """ total packet size > 65535 """
Klement Sekera4c533132018-02-22 11:41:12 +0100807 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
808 IPv6(src=self.src_if.remote_ip6,
809 dst=self.src_if.local_ip6) /
810 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200811 Raw())
812 self.extend_packet(p, 1000, self.padding)
813 fragments = fragment_rfc8200(p, 1, 500)
814 bad_fragment = fragments[1]
815 bad_fragment[IPv6ExtHdrFragment].offset = 65500
816 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100817 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +0200818 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +0100819 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +0200820 icmp = pkts[0]
821 self.assertIn(ICMPv6ParamProblem, icmp)
822 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
823
824
Juraj Sloboda3048b632018-10-02 11:13:53 +0200825class TestIPv4ReassemblyLocalNode(VppTestCase):
826 """ IPv4 Reassembly for packets coming to ip4-local node """
827
828 @classmethod
829 def setUpClass(cls):
830 super(TestIPv4ReassemblyLocalNode, cls).setUpClass()
831
832 cls.create_pg_interfaces([0])
833 cls.src_dst_if = cls.pg0
834
835 # setup all interfaces
836 for i in cls.pg_interfaces:
837 i.admin_up()
838 i.config_ip4()
839 i.resolve_arp()
840
841 cls.padding = " abcdefghijklmn"
842 cls.create_stream()
843 cls.create_fragments()
844
845 def setUp(self):
846 """ Test setup - force timeout on existing reassemblies """
847 super(TestIPv4ReassemblyLocalNode, self).setUp()
848 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
849 expire_walk_interval_ms=10)
850 self.sleep(.25)
851 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
852 expire_walk_interval_ms=10000)
853
854 def tearDown(self):
855 super(TestIPv4ReassemblyLocalNode, self).tearDown()
856 self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +0100857 self.logger.debug(self.vapi.ppcli("show buffers"))
Juraj Sloboda3048b632018-10-02 11:13:53 +0200858
859 @classmethod
860 def create_stream(cls, packet_count=test_packet_count):
861 """Create input packet stream for defined interface.
862
863 :param list packet_sizes: Required packet sizes.
864 """
865 for i in range(0, packet_count):
866 info = cls.create_packet_info(cls.src_dst_if, cls.src_dst_if)
867 payload = cls.info_to_payload(info)
868 p = (Ether(dst=cls.src_dst_if.local_mac,
869 src=cls.src_dst_if.remote_mac) /
870 IP(id=info.index, src=cls.src_dst_if.remote_ip4,
871 dst=cls.src_dst_if.local_ip4) /
872 ICMP(type='echo-request', id=1234) /
873 Raw(payload))
874 cls.extend_packet(p, 1518, cls.padding)
875 info.data = p
876
877 @classmethod
878 def create_fragments(cls):
879 infos = cls._packet_infos
880 cls.pkt_infos = []
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -0800881 for index, info in six.iteritems(infos):
Juraj Sloboda3048b632018-10-02 11:13:53 +0200882 p = info.data
883 # cls.logger.debug(ppp("Packet:", p.__class__(str(p))))
884 fragments_300 = fragment_rfc791(p, 300)
885 cls.pkt_infos.append((index, fragments_300))
886 cls.fragments_300 = [x for (_, frags) in cls.pkt_infos for x in frags]
887 cls.logger.debug("Fragmented %s packets into %s 300-byte fragments" %
888 (len(infos), len(cls.fragments_300)))
889
890 def verify_capture(self, capture):
891 """Verify captured packet stream.
892
893 :param list capture: Captured packet stream.
894 """
895 info = None
896 seen = set()
897 for packet in capture:
898 try:
899 self.logger.debug(ppp("Got packet:", packet))
900 ip = packet[IP]
901 icmp = packet[ICMP]
902 payload_info = self.payload_to_info(str(packet[Raw]))
903 packet_index = payload_info.index
904 if packet_index in seen:
905 raise Exception(ppp("Duplicate packet received", packet))
906 seen.add(packet_index)
907 self.assertEqual(payload_info.dst, self.src_dst_if.sw_if_index)
908 info = self._packet_infos[packet_index]
Klement Sekera14d7e902018-12-10 13:46:09 +0100909 self.assertIsNotNone(info)
Juraj Sloboda3048b632018-10-02 11:13:53 +0200910 self.assertEqual(packet_index, info.index)
911 saved_packet = info.data
912 self.assertEqual(ip.src, saved_packet[IP].dst)
913 self.assertEqual(ip.dst, saved_packet[IP].src)
914 self.assertEqual(icmp.type, 0) # echo reply
915 self.assertEqual(icmp.id, saved_packet[ICMP].id)
916 self.assertEqual(icmp.payload, saved_packet[ICMP].payload)
917 except Exception:
918 self.logger.error(ppp("Unexpected or invalid packet:", packet))
919 raise
920 for index in self._packet_infos:
Klement Sekera14d7e902018-12-10 13:46:09 +0100921 self.assertIn(index, seen,
922 "Packet with packet_index %d not received" % index)
Juraj Sloboda3048b632018-10-02 11:13:53 +0200923
924 def test_reassembly(self):
925 """ basic reassembly """
926
927 self.pg_enable_capture()
928 self.src_dst_if.add_stream(self.fragments_300)
929 self.pg_start()
930
931 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
932 self.verify_capture(packets)
933
934 # run it all again to verify correctness
935 self.pg_enable_capture()
936 self.src_dst_if.add_stream(self.fragments_300)
937 self.pg_start()
938
939 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
940 self.verify_capture(packets)
941
942
Klement Sekera75e7d132017-09-20 08:26:30 +0200943class TestFIFReassembly(VppTestCase):
944 """ Fragments in fragments reassembly """
945
946 @classmethod
947 def setUpClass(cls):
948 super(TestFIFReassembly, cls).setUpClass()
949
Klement Sekera4c533132018-02-22 11:41:12 +0100950 cls.create_pg_interfaces([0, 1])
951 cls.src_if = cls.pg0
952 cls.dst_if = cls.pg1
953 for i in cls.pg_interfaces:
954 i.admin_up()
955 i.config_ip4()
956 i.resolve_arp()
957 i.config_ip6()
958 i.resolve_ndp()
Klement Sekera75e7d132017-09-20 08:26:30 +0200959
Klement Sekera75e7d132017-09-20 08:26:30 +0200960 cls.packet_sizes = [64, 512, 1518, 9018]
961 cls.padding = " abcdefghijklmn"
962
963 def setUp(self):
964 """ Test setup - force timeout on existing reassemblies """
965 super(TestFIFReassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +0100966 self.vapi.ip_reassembly_enable_disable(
967 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
968 enable_ip6=True)
969 self.vapi.ip_reassembly_enable_disable(
970 sw_if_index=self.dst_if.sw_if_index, enable_ip4=True,
971 enable_ip6=True)
Klement Sekera75e7d132017-09-20 08:26:30 +0200972 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
973 expire_walk_interval_ms=10)
974 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
975 expire_walk_interval_ms=10, is_ip6=1)
976 self.sleep(.25)
977 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
978 expire_walk_interval_ms=10000)
979 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
980 expire_walk_interval_ms=10000, is_ip6=1)
981
982 def tearDown(self):
983 self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
984 self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +0100985 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +0200986 super(TestFIFReassembly, self).tearDown()
987
988 def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):
989 """Verify captured packet stream.
990
991 :param list capture: Captured packet stream.
992 """
993 info = None
994 seen = set()
995 for packet in capture:
996 try:
Klement Sekera4c533132018-02-22 11:41:12 +0100997 self.logger.debug(ppp("Got packet:", packet))
Klement Sekera75e7d132017-09-20 08:26:30 +0200998 ip = packet[ip_class]
999 udp = packet[UDP]
1000 payload_info = self.payload_to_info(str(packet[Raw]))
1001 packet_index = payload_info.index
1002 self.assertTrue(
1003 packet_index not in dropped_packet_indexes,
1004 ppp("Packet received, but should be dropped:", packet))
1005 if packet_index in seen:
1006 raise Exception(ppp("Duplicate packet received", packet))
1007 seen.add(packet_index)
Klement Sekera4c533132018-02-22 11:41:12 +01001008 self.assertEqual(payload_info.dst, self.dst_if.sw_if_index)
Klement Sekera75e7d132017-09-20 08:26:30 +02001009 info = self._packet_infos[packet_index]
1010 self.assertTrue(info is not None)
1011 self.assertEqual(packet_index, info.index)
1012 saved_packet = info.data
1013 self.assertEqual(ip.src, saved_packet[ip_class].src)
1014 self.assertEqual(ip.dst, saved_packet[ip_class].dst)
1015 self.assertEqual(udp.payload, saved_packet[UDP].payload)
Klement Sekera4c533132018-02-22 11:41:12 +01001016 except Exception:
Klement Sekera75e7d132017-09-20 08:26:30 +02001017 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1018 raise
1019 for index in self._packet_infos:
1020 self.assertTrue(index in seen or index in dropped_packet_indexes,
1021 "Packet with packet_index %d not received" % index)
1022
1023 def test_fif4(self):
1024 """ Fragments in fragments (4o4) """
1025
1026 # TODO this should be ideally in setUpClass, but then we hit a bug
1027 # with VppIpRoute incorrectly reporting it's present when it's not
1028 # so we need to manually remove the vpp config, thus we cannot have
1029 # it shared for multiple test cases
1030 self.tun_ip4 = "1.1.1.2"
1031
Klement Sekera4c533132018-02-22 11:41:12 +01001032 self.gre4 = VppGreInterface(self, self.src_if.local_ip4, self.tun_ip4)
Klement Sekera75e7d132017-09-20 08:26:30 +02001033 self.gre4.add_vpp_config()
1034 self.gre4.admin_up()
1035 self.gre4.config_ip4()
1036
Klement Sekera4c533132018-02-22 11:41:12 +01001037 self.vapi.ip_reassembly_enable_disable(
1038 sw_if_index=self.gre4.sw_if_index, enable_ip4=True)
1039
Klement Sekera75e7d132017-09-20 08:26:30 +02001040 self.route4 = VppIpRoute(self, self.tun_ip4, 32,
Klement Sekera4c533132018-02-22 11:41:12 +01001041 [VppRoutePath(self.src_if.remote_ip4,
1042 self.src_if.sw_if_index)])
Klement Sekera75e7d132017-09-20 08:26:30 +02001043 self.route4.add_vpp_config()
1044
1045 self.reset_packet_infos()
1046 for i in range(test_packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01001047 info = self.create_packet_info(self.src_if, self.dst_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02001048 payload = self.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +01001049 # Ethernet header here is only for size calculation, thus it
1050 # doesn't matter how it's initialized. This is to ensure that
1051 # reassembled packet is not > 9000 bytes, so that it's not dropped
1052 p = (Ether() /
1053 IP(id=i, src=self.src_if.remote_ip4,
1054 dst=self.dst_if.remote_ip4) /
1055 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001056 Raw(payload))
1057 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1058 self.extend_packet(p, size, self.padding)
Klement Sekera4c533132018-02-22 11:41:12 +01001059 info.data = p[IP] # use only IP part, without ethernet header
Klement Sekera75e7d132017-09-20 08:26:30 +02001060
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -08001061 fragments = [x for _, p in six.iteritems(self._packet_infos)
Klement Sekera75e7d132017-09-20 08:26:30 +02001062 for x in fragment_rfc791(p.data, 400)]
1063
1064 encapped_fragments = \
Klement Sekera4c533132018-02-22 11:41:12 +01001065 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1066 IP(src=self.tun_ip4, dst=self.src_if.local_ip4) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001067 GRE() /
1068 p
1069 for p in fragments]
1070
1071 fragmented_encapped_fragments = \
1072 [x for p in encapped_fragments
1073 for x in fragment_rfc791(p, 200)]
1074
Klement Sekera4c533132018-02-22 11:41:12 +01001075 self.src_if.add_stream(fragmented_encapped_fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001076
1077 self.pg_enable_capture(self.pg_interfaces)
1078 self.pg_start()
1079
Klement Sekera4c533132018-02-22 11:41:12 +01001080 self.src_if.assert_nothing_captured()
1081 packets = self.dst_if.get_capture(len(self._packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +02001082 self.verify_capture(packets, IP)
1083
1084 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
1085 # so that it's query_vpp_config() works as it should
1086 self.gre4.remove_vpp_config()
Klement Sekera4c533132018-02-22 11:41:12 +01001087 self.logger.debug(self.vapi.ppcli("show interface"))
Klement Sekera75e7d132017-09-20 08:26:30 +02001088
1089 def test_fif6(self):
1090 """ Fragments in fragments (6o6) """
1091 # TODO this should be ideally in setUpClass, but then we hit a bug
1092 # with VppIpRoute incorrectly reporting it's present when it's not
1093 # so we need to manually remove the vpp config, thus we cannot have
1094 # it shared for multiple test cases
1095 self.tun_ip6 = "1002::1"
1096
Klement Sekera4c533132018-02-22 11:41:12 +01001097 self.gre6 = VppGre6Interface(self, self.src_if.local_ip6, self.tun_ip6)
Klement Sekera75e7d132017-09-20 08:26:30 +02001098 self.gre6.add_vpp_config()
1099 self.gre6.admin_up()
1100 self.gre6.config_ip6()
1101
Klement Sekera4c533132018-02-22 11:41:12 +01001102 self.vapi.ip_reassembly_enable_disable(
1103 sw_if_index=self.gre6.sw_if_index, enable_ip6=True)
1104
Klement Sekera75e7d132017-09-20 08:26:30 +02001105 self.route6 = VppIpRoute(self, self.tun_ip6, 128,
Klement Sekera4c533132018-02-22 11:41:12 +01001106 [VppRoutePath(self.src_if.remote_ip6,
1107 self.src_if.sw_if_index,
Klement Sekera75e7d132017-09-20 08:26:30 +02001108 proto=DpoProto.DPO_PROTO_IP6)],
1109 is_ip6=1)
1110 self.route6.add_vpp_config()
1111
1112 self.reset_packet_infos()
1113 for i in range(test_packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01001114 info = self.create_packet_info(self.src_if, self.dst_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02001115 payload = self.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +01001116 # Ethernet header here is only for size calculation, thus it
1117 # doesn't matter how it's initialized. This is to ensure that
1118 # reassembled packet is not > 9000 bytes, so that it's not dropped
1119 p = (Ether() /
1120 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1121 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001122 Raw(payload))
1123 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1124 self.extend_packet(p, size, self.padding)
Klement Sekera4c533132018-02-22 11:41:12 +01001125 info.data = p[IPv6] # use only IPv6 part, without ethernet header
Klement Sekera75e7d132017-09-20 08:26:30 +02001126
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -08001127 fragments = [x for _, i in six.iteritems(self._packet_infos)
Klement Sekera75e7d132017-09-20 08:26:30 +02001128 for x in fragment_rfc8200(
1129 i.data, i.index, 400)]
1130
1131 encapped_fragments = \
Klement Sekera4c533132018-02-22 11:41:12 +01001132 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1133 IPv6(src=self.tun_ip6, dst=self.src_if.local_ip6) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001134 GRE() /
1135 p
1136 for p in fragments]
1137
1138 fragmented_encapped_fragments = \
1139 [x for p in encapped_fragments for x in (
1140 fragment_rfc8200(
1141 p,
1142 2 * len(self._packet_infos) + p[IPv6ExtHdrFragment].id,
1143 200)
1144 if IPv6ExtHdrFragment in p else [p]
1145 )
1146 ]
1147
Klement Sekera4c533132018-02-22 11:41:12 +01001148 self.src_if.add_stream(fragmented_encapped_fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001149
1150 self.pg_enable_capture(self.pg_interfaces)
1151 self.pg_start()
1152
Klement Sekera4c533132018-02-22 11:41:12 +01001153 self.src_if.assert_nothing_captured()
1154 packets = self.dst_if.get_capture(len(self._packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +02001155 self.verify_capture(packets, IPv6)
1156
1157 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
1158 # so that it's query_vpp_config() works as it should
Klement Sekera3ecc2212018-03-27 10:34:43 +02001159 self.gre6.remove_vpp_config()
Klement Sekera75e7d132017-09-20 08:26:30 +02001160
1161
1162if __name__ == '__main__':
1163 unittest.main(testRunner=VppTestRunner)