blob: 07d5737ffacba50ef4c83fc492e72bc9bc3b9ad9 [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
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
Paul Vinciguerra69555952019-03-01 08:46:29 -080012
Klement Sekera75e7d132017-09-20 08:26:30 +020013from scapy.layers.inet6 import IPv6, IPv6ExtHdrFragment, ICMPv6ParamProblem,\
14 ICMPv6TimeExceeded
Paul Vinciguerra69555952019-03-01 08:46:29 -080015
16from framework import VppTestCase, VppTestRunner
17from util import ppp, fragment_rfc791, fragment_rfc8200
Neale Ranns5a8844b2019-04-16 07:15:35 +000018from vpp_gre_interface import VppGreInterface
Neale Rannsc0a93142018-09-05 15:42:26 -070019from vpp_ip import DpoProto
20from vpp_ip_route import VppIpRoute, VppRoutePath
Klement Sekera75e7d132017-09-20 08:26:30 +020021
Klement Sekerad0f70a32018-12-14 17:24:13 +010022# 35 is enough to have >257 400-byte fragments
23test_packet_count = 35
Klement Sekera75e7d132017-09-20 08:26:30 +020024
Paul Vinciguerra69555952019-03-01 08:46:29 -080025# <class 'scapy.layers.inet.IP'>
26# <class 'scapy.layers.inet6.IPv6'>
27_scapy_ip_family_types = (IP, IPv6)
Klement Sekera75e7d132017-09-20 08:26:30 +020028
Paul Vinciguerra69555952019-03-01 08:46:29 -080029
30def validate_scapy_ip_family(scapy_ip_family):
31
32 if scapy_ip_family not in _scapy_ip_family_types:
33 raise ValueError("'scapy_ip_family' must be of type: %s. Got %s" %
34 (_scapy_ip_family_types, scapy_ip_family))
35
36
37class TestIPReassemblyMixin(object):
38
39 def verify_capture(self, scapy_ip_family, capture,
40 dropped_packet_indexes=None):
41 """Verify captured packet stream.
42
43 :param list capture: Captured packet stream.
44 """
45 validate_scapy_ip_family(scapy_ip_family)
46
47 if dropped_packet_indexes is None:
48 dropped_packet_indexes = []
49 info = None
50 seen = set()
51 for packet in capture:
52 try:
53 self.logger.debug(ppp("Got packet:", packet))
54 ip = packet[scapy_ip_family]
55 udp = packet[UDP]
Paul Vinciguerraeaea4212019-03-06 11:58:06 -080056 payload_info = self.payload_to_info(packet[Raw])
Paul Vinciguerra69555952019-03-01 08:46:29 -080057 packet_index = payload_info.index
58 self.assertTrue(
59 packet_index not in dropped_packet_indexes,
60 ppp("Packet received, but should be dropped:", packet))
61 if packet_index in seen:
62 raise Exception(ppp("Duplicate packet received", packet))
63 seen.add(packet_index)
64 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
65 info = self._packet_infos[packet_index]
66 self.assertTrue(info is not None)
67 self.assertEqual(packet_index, info.index)
68 saved_packet = info.data
69 self.assertEqual(ip.src, saved_packet[scapy_ip_family].src)
70 self.assertEqual(ip.dst, saved_packet[scapy_ip_family].dst)
71 self.assertEqual(udp.payload, saved_packet[UDP].payload)
72 except Exception:
73 self.logger.error(ppp("Unexpected or invalid packet:", packet))
74 raise
75 for index in self._packet_infos:
76 self.assertTrue(index in seen or index in dropped_packet_indexes,
77 "Packet with packet_index %d not received" % index)
78
79 def test_disabled(self, scapy_ip_family, stream,
80 dropped_packet_indexes):
81 """ reassembly disabled """
82 validate_scapy_ip_family(scapy_ip_family)
83 is_ip6 = 1 if scapy_ip_family == IPv6 else 0
84
85 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
86 expire_walk_interval_ms=10000,
87 is_ip6=is_ip6)
88
89 self.pg_enable_capture()
90 self.src_if.add_stream(stream)
91 self.pg_start()
92
93 packets = self.dst_if.get_capture(
94 len(self.pkt_infos) - len(dropped_packet_indexes))
95 self.verify_capture(scapy_ip_family, packets, dropped_packet_indexes)
96 self.src_if.assert_nothing_captured()
97
98 def test_duplicates(self, scapy_ip_family, stream):
99 """ duplicate fragments """
100 validate_scapy_ip_family(scapy_ip_family)
101
102 self.pg_enable_capture()
103 self.src_if.add_stream(stream)
104 self.pg_start()
105
106 packets = self.dst_if.get_capture(len(self.pkt_infos))
107 self.verify_capture(scapy_ip_family, packets)
108 self.src_if.assert_nothing_captured()
109
110 def test_random(self, scapy_ip_family, stream):
111 """ random order reassembly """
112 validate_scapy_ip_family(scapy_ip_family)
113
114 fragments = list(stream)
115 shuffle(fragments)
116
117 self.pg_enable_capture()
118 self.src_if.add_stream(fragments)
119 self.pg_start()
120
121 packets = self.dst_if.get_capture(len(self.packet_infos))
122 self.verify_capture(scapy_ip_family, packets)
123 self.src_if.assert_nothing_captured()
124
125 # run it all again to verify correctness
126 self.pg_enable_capture()
127 self.src_if.add_stream(fragments)
128 self.pg_start()
129
130 packets = self.dst_if.get_capture(len(self.packet_infos))
131 self.verify_capture(scapy_ip_family, packets)
132 self.src_if.assert_nothing_captured()
133
134 def test_reassembly(self, scapy_ip_family, stream):
135 """ basic reassembly """
136 validate_scapy_ip_family(scapy_ip_family)
137
138 self.pg_enable_capture()
139 self.src_if.add_stream(stream)
140 self.pg_start()
141
142 packets = self.dst_if.get_capture(len(self.pkt_infos))
143 self.verify_capture(scapy_ip_family, packets)
144 self.src_if.assert_nothing_captured()
145
146 # run it all again to verify correctness
147 self.pg_enable_capture()
148 self.src_if.add_stream(stream)
149 self.pg_start()
150
151 packets = self.dst_if.get_capture(len(self.pkt_infos))
152 self.verify_capture(scapy_ip_family, packets)
153 self.src_if.assert_nothing_captured()
154
155 def test_reversed(self, scapy_ip_family, stream):
156 """ reverse order reassembly """
157 validate_scapy_ip_family(scapy_ip_family)
158
159 fragments = list(stream)
160 fragments.reverse()
161
162 self.pg_enable_capture()
163 self.src_if.add_stream(fragments)
164 self.pg_start()
165
166 packets = self.dst_if.get_capture(len(self.packet_infos))
167 self.verify_capture(scapy_ip_family, packets)
168 self.src_if.assert_nothing_captured()
169
170 # run it all again to verify correctness
171 self.pg_enable_capture()
172 self.src_if.add_stream(fragments)
173 self.pg_start()
174
175 packets = self.dst_if.get_capture(len(self.packet_infos))
176 self.verify_capture(scapy_ip_family, packets)
177 self.src_if.assert_nothing_captured()
178
179 def test_timeout_inline(self, scapy_ip_family, stream,
180 dropped_packet_indexes):
181 """ timeout (inline) """
182 validate_scapy_ip_family(scapy_ip_family)
183 is_ip6 = 1 if scapy_ip_family == IPv6 else 0
184
185 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
186 expire_walk_interval_ms=10000,
187 is_ip6=is_ip6)
188
189 self.pg_enable_capture()
190 self.src_if.add_stream(stream)
191 self.pg_start()
192
193 packets = self.dst_if.get_capture(
194 len(self.pkt_infos) - len(dropped_packet_indexes))
195 self.verify_capture(scapy_ip_family, packets,
196 dropped_packet_indexes)
197
198
199class TestIPv4Reassembly(TestIPReassemblyMixin, VppTestCase):
Klement Sekera75e7d132017-09-20 08:26:30 +0200200 """ IPv4 Reassembly """
201
202 @classmethod
203 def setUpClass(cls):
204 super(TestIPv4Reassembly, cls).setUpClass()
205
Klement Sekera4c533132018-02-22 11:41:12 +0100206 cls.create_pg_interfaces([0, 1])
207 cls.src_if = cls.pg0
208 cls.dst_if = cls.pg1
Klement Sekera75e7d132017-09-20 08:26:30 +0200209
210 # setup all interfaces
211 for i in cls.pg_interfaces:
212 i.admin_up()
213 i.config_ip4()
214 i.resolve_arp()
215
Klement Sekera75e7d132017-09-20 08:26:30 +0200216 # packet sizes
217 cls.packet_sizes = [64, 512, 1518, 9018]
218 cls.padding = " abcdefghijklmn"
219 cls.create_stream(cls.packet_sizes)
220 cls.create_fragments()
221
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -0700222 @classmethod
223 def tearDownClass(cls):
224 super(TestIPv4Reassembly, cls).tearDownClass()
225
Klement Sekera75e7d132017-09-20 08:26:30 +0200226 def setUp(self):
227 """ Test setup - force timeout on existing reassemblies """
228 super(TestIPv4Reassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +0100229 self.vapi.ip_reassembly_enable_disable(
230 sw_if_index=self.src_if.sw_if_index, enable_ip4=True)
Klement Sekera75e7d132017-09-20 08:26:30 +0200231 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
232 expire_walk_interval_ms=10)
233 self.sleep(.25)
234 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
235 expire_walk_interval_ms=10000)
236
237 def tearDown(self):
238 super(TestIPv4Reassembly, self).tearDown()
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700239
240 def show_commands_at_teardown(self):
Klement Sekera75e7d132017-09-20 08:26:30 +0200241 self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +0100242 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +0200243
244 @classmethod
245 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
Klement Sekerad0f70a32018-12-14 17:24:13 +0100246 """Create input packet stream
Klement Sekera75e7d132017-09-20 08:26:30 +0200247
248 :param list packet_sizes: Required packet sizes.
249 """
250 for i in range(0, packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +0100251 info = cls.create_packet_info(cls.src_if, cls.src_if)
Klement Sekera75e7d132017-09-20 08:26:30 +0200252 payload = cls.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +0100253 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
254 IP(id=info.index, src=cls.src_if.remote_ip4,
255 dst=cls.dst_if.remote_ip4) /
256 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200257 Raw(payload))
258 size = packet_sizes[(i // 2) % len(packet_sizes)]
259 cls.extend_packet(p, size, cls.padding)
260 info.data = p
261
262 @classmethod
263 def create_fragments(cls):
264 infos = cls._packet_infos
265 cls.pkt_infos = []
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -0800266 for index, info in six.iteritems(infos):
Klement Sekera75e7d132017-09-20 08:26:30 +0200267 p = info.data
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700268 # cls.logger.debug(ppp("Packet:",
269 # p.__class__(scapy.compat.raw(p))))
Klement Sekera75e7d132017-09-20 08:26:30 +0200270 fragments_400 = fragment_rfc791(p, 400)
271 fragments_300 = fragment_rfc791(p, 300)
272 fragments_200 = [
273 x for f in fragments_400 for x in fragment_rfc791(f, 200)]
274 cls.pkt_infos.append(
275 (index, fragments_400, fragments_300, fragments_200))
276 cls.fragments_400 = [
277 x for (_, frags, _, _) in cls.pkt_infos for x in frags]
278 cls.fragments_300 = [
279 x for (_, _, frags, _) in cls.pkt_infos for x in frags]
280 cls.fragments_200 = [
281 x for (_, _, _, frags) in cls.pkt_infos for x in frags]
282 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
283 "%s 300-byte fragments and %s 200-byte fragments" %
284 (len(infos), len(cls.fragments_400),
285 len(cls.fragments_300), len(cls.fragments_200)))
286
Paul Vinciguerra69555952019-03-01 08:46:29 -0800287 @parameterized.expand([(IP, None)])
288 def test_reassembly(self, family, stream):
Klement Sekera75e7d132017-09-20 08:26:30 +0200289 """ basic reassembly """
Paul Vinciguerra69555952019-03-01 08:46:29 -0800290 stream = self.__class__.fragments_200
291 super(TestIPv4Reassembly, self).test_reassembly(family, stream)
Klement Sekera75e7d132017-09-20 08:26:30 +0200292
Paul Vinciguerra69555952019-03-01 08:46:29 -0800293 @parameterized.expand([(IP, None)])
294 def test_reversed(self, family, stream):
Klement Sekera75e7d132017-09-20 08:26:30 +0200295 """ reverse order reassembly """
Paul Vinciguerra69555952019-03-01 08:46:29 -0800296 stream = self.__class__.fragments_200
297 super(TestIPv4Reassembly, self).test_reversed(family, stream)
Klement Sekera75e7d132017-09-20 08:26:30 +0200298
Paul Vinciguerra69555952019-03-01 08:46:29 -0800299 @parameterized.expand([(IP, None)])
300 def test_random(self, family, stream):
301 stream = self.__class__.fragments_200
302 super(TestIPv4Reassembly, self).test_random(family, stream)
Klement Sekera75e7d132017-09-20 08:26:30 +0200303
Klement Sekera14d7e902018-12-10 13:46:09 +0100304 def test_5737(self):
305 """ fragment length + ip header size > 65535 """
Klement Sekera4ee633e2018-12-14 12:00:44 +0100306 self.vapi.cli("clear errors")
Klement Sekera14d7e902018-12-10 13:46:09 +0100307 raw = ('E\x00\x00\x88,\xf8\x1f\xfe@\x01\x98\x00\xc0\xa8\n-\xc0\xa8\n'
308 '\x01\x08\x00\xf0J\xed\xcb\xf1\xf5Test-group: IPv4.IPv4.ipv4-'
309 'message.Ethernet-Payload.IPv4-Packet.IPv4-Header.Fragment-Of'
310 'fset; Test-case: 5737')
311
312 malformed_packet = (Ether(dst=self.src_if.local_mac,
313 src=self.src_if.remote_mac) /
314 IP(raw))
315 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
316 IP(id=1000, src=self.src_if.remote_ip4,
317 dst=self.dst_if.remote_ip4) /
318 UDP(sport=1234, dport=5678) /
319 Raw("X" * 1000))
320 valid_fragments = fragment_rfc791(p, 400)
321
322 self.pg_enable_capture()
323 self.src_if.add_stream([malformed_packet] + valid_fragments)
324 self.pg_start()
325
326 self.dst_if.get_capture(1)
Klement Sekera4ee633e2018-12-14 12:00:44 +0100327 self.assert_packet_counter_equal("ip4-reassembly-feature", 1)
328 # TODO remove above, uncomment below once clearing of counters
329 # is supported
330 # self.assert_packet_counter_equal(
331 # "/err/ip4-reassembly-feature/malformed packets", 1)
Klement Sekera14d7e902018-12-10 13:46:09 +0100332
Klement Sekera400f6d82018-12-13 14:35:48 +0100333 def test_44924(self):
334 """ compress tiny fragments """
335 packets = [(Ether(dst=self.src_if.local_mac,
336 src=self.src_if.remote_mac) /
337 IP(id=24339, flags="MF", frag=0, ttl=64,
338 src=self.src_if.remote_ip4,
339 dst=self.dst_if.remote_ip4) /
340 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
341 Raw(load='Test-group: IPv4')),
342 (Ether(dst=self.src_if.local_mac,
343 src=self.src_if.remote_mac) /
344 IP(id=24339, flags="MF", frag=3, ttl=64,
345 src=self.src_if.remote_ip4,
346 dst=self.dst_if.remote_ip4) /
347 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
348 Raw(load='.IPv4.Fragmentation.vali')),
349 (Ether(dst=self.src_if.local_mac,
350 src=self.src_if.remote_mac) /
351 IP(id=24339, frag=6, ttl=64,
352 src=self.src_if.remote_ip4,
353 dst=self.dst_if.remote_ip4) /
354 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
355 Raw(load='d; Test-case: 44924'))
356 ]
357
358 self.pg_enable_capture()
359 self.src_if.add_stream(packets)
360 self.pg_start()
361
362 self.dst_if.get_capture(1)
363
Klement Sekera4ee633e2018-12-14 12:00:44 +0100364 def test_frag_1(self):
365 """ fragment of size 1 """
366 self.vapi.cli("clear errors")
367 malformed_packets = [(Ether(dst=self.src_if.local_mac,
368 src=self.src_if.remote_mac) /
369 IP(id=7, len=21, flags="MF", frag=0, ttl=64,
370 src=self.src_if.remote_ip4,
371 dst=self.dst_if.remote_ip4) /
372 ICMP(type="echo-request")),
373 (Ether(dst=self.src_if.local_mac,
374 src=self.src_if.remote_mac) /
375 IP(id=7, len=21, frag=1, ttl=64,
376 src=self.src_if.remote_ip4,
377 dst=self.dst_if.remote_ip4) /
378 Raw(load='\x08')),
379 ]
380
381 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
382 IP(id=1000, src=self.src_if.remote_ip4,
383 dst=self.dst_if.remote_ip4) /
384 UDP(sport=1234, dport=5678) /
385 Raw("X" * 1000))
386 valid_fragments = fragment_rfc791(p, 400)
387
388 self.pg_enable_capture()
389 self.src_if.add_stream(malformed_packets + valid_fragments)
390 self.pg_start()
391
392 self.dst_if.get_capture(1)
393
394 self.assert_packet_counter_equal("ip4-reassembly-feature", 1)
395 # TODO remove above, uncomment below once clearing of counters
396 # is supported
397 # self.assert_packet_counter_equal(
398 # "/err/ip4-reassembly-feature/malformed packets", 1)
399
Paul Vinciguerra69555952019-03-01 08:46:29 -0800400 @parameterized.expand([(IP, None)])
401 def test_duplicates(self, family, stream):
Klement Sekera75e7d132017-09-20 08:26:30 +0200402 """ duplicate fragments """
Klement Sekera75e7d132017-09-20 08:26:30 +0200403 fragments = [
Paul Vinciguerra69555952019-03-01 08:46:29 -0800404 # IPv4 uses 4 fields in pkt_infos, IPv6 uses 3.
Klement Sekera75e7d132017-09-20 08:26:30 +0200405 x for (_, frags, _, _) in self.pkt_infos
406 for x in frags
407 for _ in range(0, min(2, len(frags)))
408 ]
Paul Vinciguerra69555952019-03-01 08:46:29 -0800409 super(TestIPv4Reassembly, self).test_duplicates(family, fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200410
411 def test_overlap1(self):
412 """ overlapping fragments case #1 """
413
414 fragments = []
415 for _, _, frags_300, frags_200 in self.pkt_infos:
416 if len(frags_300) == 1:
417 fragments.extend(frags_300)
418 else:
419 for i, j in zip(frags_200, frags_300):
420 fragments.extend(i)
421 fragments.extend(j)
422
423 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100424 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200425 self.pg_start()
426
Klement Sekera4c533132018-02-22 11:41:12 +0100427 packets = self.dst_if.get_capture(len(self.pkt_infos))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800428 self.verify_capture(IP, packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100429 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200430
431 # run it all to verify correctness
432 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100433 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200434 self.pg_start()
435
Klement Sekera4c533132018-02-22 11:41:12 +0100436 packets = self.dst_if.get_capture(len(self.pkt_infos))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800437 self.verify_capture(IP, packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100438 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200439
440 def test_overlap2(self):
441 """ overlapping fragments case #2 """
442
443 fragments = []
444 for _, _, frags_300, frags_200 in self.pkt_infos:
445 if len(frags_300) == 1:
446 fragments.extend(frags_300)
447 else:
448 # care must be taken here so that there are no fragments
449 # received by vpp after reassembly is finished, otherwise
450 # new reassemblies will be started and packet generator will
451 # freak out when it detects unfreed buffers
452 zipped = zip(frags_300, frags_200)
453 for i, j in zipped[:-1]:
454 fragments.extend(i)
455 fragments.extend(j)
456 fragments.append(zipped[-1][0])
457
458 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100459 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200460 self.pg_start()
461
Klement Sekera4c533132018-02-22 11:41:12 +0100462 packets = self.dst_if.get_capture(len(self.pkt_infos))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800463 self.verify_capture(IP, packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100464 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200465
466 # run it all to verify correctness
467 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100468 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200469 self.pg_start()
470
Klement Sekera4c533132018-02-22 11:41:12 +0100471 packets = self.dst_if.get_capture(len(self.pkt_infos))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800472 self.verify_capture(IP, packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100473 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200474
Paul Vinciguerra69555952019-03-01 08:46:29 -0800475 @parameterized.expand([(IP, None, None)])
476 def test_timeout_inline(self, family, stream, dropped_packet_indexes):
Klement Sekera75e7d132017-09-20 08:26:30 +0200477 """ timeout (inline) """
Paul Vinciguerra69555952019-03-01 08:46:29 -0800478 stream = self.fragments_400
Klement Sekera75e7d132017-09-20 08:26:30 +0200479
480 dropped_packet_indexes = set(
481 index for (index, frags, _, _) in self.pkt_infos if len(frags) > 1
482 )
Paul Vinciguerra69555952019-03-01 08:46:29 -0800483 super(TestIPv4Reassembly, self).test_timeout_inline(
484 family, stream, dropped_packet_indexes)
Klement Sekera75e7d132017-09-20 08:26:30 +0200485
Klement Sekera4c533132018-02-22 11:41:12 +0100486 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200487
488 def test_timeout_cleanup(self):
489 """ timeout (cleanup) """
490
491 # whole packets + fragmented packets sans last fragment
492 fragments = [
493 x for (_, frags_400, _, _) in self.pkt_infos
494 for x in frags_400[:-1 if len(frags_400) > 1 else None]
495 ]
496
497 # last fragments for fragmented packets
498 fragments2 = [frags_400[-1]
499 for (_, frags_400, _, _) in self.pkt_infos
500 if len(frags_400) > 1]
501
502 dropped_packet_indexes = set(
503 index for (index, frags_400, _, _) in self.pkt_infos
504 if len(frags_400) > 1)
505
506 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
507 expire_walk_interval_ms=50)
508
509 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100510 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200511 self.pg_start()
512
513 self.sleep(.25, "wait before sending rest of fragments")
514
Klement Sekera4c533132018-02-22 11:41:12 +0100515 self.src_if.add_stream(fragments2)
Klement Sekera75e7d132017-09-20 08:26:30 +0200516 self.pg_start()
Klement Sekera75e7d132017-09-20 08:26:30 +0200517
Klement Sekera4c533132018-02-22 11:41:12 +0100518 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200519 len(self.pkt_infos) - len(dropped_packet_indexes))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800520 self.verify_capture(IP, packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100521 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200522
Paul Vinciguerra69555952019-03-01 08:46:29 -0800523 @parameterized.expand([(IP, None, None)])
524 def test_disabled(self, family, stream, dropped_packet_indexes):
Klement Sekera75e7d132017-09-20 08:26:30 +0200525 """ reassembly disabled """
526
Paul Vinciguerra69555952019-03-01 08:46:29 -0800527 stream = self.__class__.fragments_400
Klement Sekera75e7d132017-09-20 08:26:30 +0200528 dropped_packet_indexes = set(
529 index for (index, frags_400, _, _) in self.pkt_infos
530 if len(frags_400) > 1)
Paul Vinciguerra69555952019-03-01 08:46:29 -0800531 super(TestIPv4Reassembly, self).test_disabled(
532 family, stream, dropped_packet_indexes)
Klement Sekera75e7d132017-09-20 08:26:30 +0200533
534
Paul Vinciguerra69555952019-03-01 08:46:29 -0800535class TestIPv6Reassembly(TestIPReassemblyMixin, VppTestCase):
Klement Sekera75e7d132017-09-20 08:26:30 +0200536 """ IPv6 Reassembly """
537
538 @classmethod
539 def setUpClass(cls):
540 super(TestIPv6Reassembly, cls).setUpClass()
541
Klement Sekera4c533132018-02-22 11:41:12 +0100542 cls.create_pg_interfaces([0, 1])
543 cls.src_if = cls.pg0
544 cls.dst_if = cls.pg1
Klement Sekera75e7d132017-09-20 08:26:30 +0200545
546 # setup all interfaces
547 for i in cls.pg_interfaces:
548 i.admin_up()
549 i.config_ip6()
550 i.resolve_ndp()
551
Klement Sekera75e7d132017-09-20 08:26:30 +0200552 # packet sizes
553 cls.packet_sizes = [64, 512, 1518, 9018]
554 cls.padding = " abcdefghijklmn"
555 cls.create_stream(cls.packet_sizes)
556 cls.create_fragments()
557
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -0700558 @classmethod
559 def tearDownClass(cls):
560 super(TestIPv6Reassembly, cls).tearDownClass()
561
Klement Sekera75e7d132017-09-20 08:26:30 +0200562 def setUp(self):
563 """ Test setup - force timeout on existing reassemblies """
564 super(TestIPv6Reassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +0100565 self.vapi.ip_reassembly_enable_disable(
566 sw_if_index=self.src_if.sw_if_index, enable_ip6=True)
Klement Sekera75e7d132017-09-20 08:26:30 +0200567 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
568 expire_walk_interval_ms=10, is_ip6=1)
569 self.sleep(.25)
570 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
571 expire_walk_interval_ms=10000, is_ip6=1)
Klement Sekera4c533132018-02-22 11:41:12 +0100572 self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +0100573 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +0200574
575 def tearDown(self):
576 super(TestIPv6Reassembly, self).tearDown()
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700577
578 def show_commands_at_teardown(self):
Klement Sekera75e7d132017-09-20 08:26:30 +0200579 self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +0100580 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +0200581
582 @classmethod
583 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
584 """Create input packet stream for defined interface.
585
586 :param list packet_sizes: Required packet sizes.
587 """
588 for i in range(0, packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +0100589 info = cls.create_packet_info(cls.src_if, cls.src_if)
Klement Sekera75e7d132017-09-20 08:26:30 +0200590 payload = cls.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +0100591 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
592 IPv6(src=cls.src_if.remote_ip6,
593 dst=cls.dst_if.remote_ip6) /
594 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200595 Raw(payload))
596 size = packet_sizes[(i // 2) % len(packet_sizes)]
597 cls.extend_packet(p, size, cls.padding)
598 info.data = p
599
600 @classmethod
601 def create_fragments(cls):
602 infos = cls._packet_infos
603 cls.pkt_infos = []
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -0800604 for index, info in six.iteritems(infos):
Klement Sekera75e7d132017-09-20 08:26:30 +0200605 p = info.data
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700606 # cls.logger.debug(ppp("Packet:",
607 # p.__class__(scapy.compat.raw(p))))
Klement Sekera75e7d132017-09-20 08:26:30 +0200608 fragments_400 = fragment_rfc8200(p, info.index, 400)
609 fragments_300 = fragment_rfc8200(p, info.index, 300)
610 cls.pkt_infos.append((index, fragments_400, fragments_300))
611 cls.fragments_400 = [
612 x for _, frags, _ in cls.pkt_infos for x in frags]
613 cls.fragments_300 = [
614 x for _, _, frags in cls.pkt_infos for x in frags]
615 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
616 "and %s 300-byte fragments" %
617 (len(infos), len(cls.fragments_400),
618 len(cls.fragments_300)))
619
Paul Vinciguerra69555952019-03-01 08:46:29 -0800620 @parameterized.expand([(IPv6, None)])
621 def test_reassembly(self, family, stream):
Klement Sekera75e7d132017-09-20 08:26:30 +0200622 """ basic reassembly """
Paul Vinciguerra69555952019-03-01 08:46:29 -0800623 stream = self.__class__.fragments_400
624 super(TestIPv6Reassembly, self).test_reassembly(family, stream)
Klement Sekera75e7d132017-09-20 08:26:30 +0200625
Paul Vinciguerra69555952019-03-01 08:46:29 -0800626 @parameterized.expand([(IPv6, None)])
627 def test_reversed(self, family, stream):
Klement Sekera75e7d132017-09-20 08:26:30 +0200628 """ reverse order reassembly """
Paul Vinciguerra69555952019-03-01 08:46:29 -0800629 stream = self.__class__.fragments_400
630 super(TestIPv6Reassembly, self).test_reversed(family, stream)
Klement Sekera75e7d132017-09-20 08:26:30 +0200631
Paul Vinciguerra69555952019-03-01 08:46:29 -0800632 @parameterized.expand([(IPv6, None)])
633 def test_random(self, family, stream):
Klement Sekera75e7d132017-09-20 08:26:30 +0200634 """ random order reassembly """
Paul Vinciguerra69555952019-03-01 08:46:29 -0800635 stream = self.__class__.fragments_400
636 super(TestIPv6Reassembly, self).test_random(family, stream)
Klement Sekera75e7d132017-09-20 08:26:30 +0200637
Paul Vinciguerra69555952019-03-01 08:46:29 -0800638 @parameterized.expand([(IPv6, None)])
639 def test_duplicates(self, family, stream):
Klement Sekera75e7d132017-09-20 08:26:30 +0200640 """ duplicate fragments """
641
642 fragments = [
Paul Vinciguerra69555952019-03-01 08:46:29 -0800643 # IPv4 uses 4 fields in pkt_infos, IPv6 uses 3.
Klement Sekera75e7d132017-09-20 08:26:30 +0200644 x for (_, frags, _) in self.pkt_infos
645 for x in frags
646 for _ in range(0, min(2, len(frags)))
647 ]
Paul Vinciguerra69555952019-03-01 08:46:29 -0800648 super(TestIPv6Reassembly, self).test_duplicates(family, fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200649
650 def test_overlap1(self):
Paul Vinciguerra69555952019-03-01 08:46:29 -0800651 """ overlapping fragments case #1 (differs from IP test case)"""
Klement Sekera75e7d132017-09-20 08:26:30 +0200652
653 fragments = []
654 for _, frags_400, frags_300 in self.pkt_infos:
655 if len(frags_300) == 1:
656 fragments.extend(frags_400)
657 else:
658 for i, j in zip(frags_300, frags_400):
659 fragments.extend(i)
660 fragments.extend(j)
661
662 dropped_packet_indexes = set(
663 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
664 )
665
666 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100667 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200668 self.pg_start()
669
Klement Sekera4c533132018-02-22 11:41:12 +0100670 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200671 len(self.pkt_infos) - len(dropped_packet_indexes))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800672 self.verify_capture(IPv6, packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100673 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200674
675 def test_overlap2(self):
Paul Vinciguerra69555952019-03-01 08:46:29 -0800676 """ overlapping fragments case #2 (differs from IP test case)"""
Klement Sekera75e7d132017-09-20 08:26:30 +0200677
678 fragments = []
Klement Sekera4c533132018-02-22 11:41:12 +0100679 for _, frags_400, frags_300 in self.pkt_infos:
Klement Sekera75e7d132017-09-20 08:26:30 +0200680 if len(frags_400) == 1:
681 fragments.extend(frags_400)
682 else:
683 # care must be taken here so that there are no fragments
684 # received by vpp after reassembly is finished, otherwise
685 # new reassemblies will be started and packet generator will
686 # freak out when it detects unfreed buffers
Klement Sekera4c533132018-02-22 11:41:12 +0100687 zipped = zip(frags_400, frags_300)
Klement Sekera75e7d132017-09-20 08:26:30 +0200688 for i, j in zipped[:-1]:
689 fragments.extend(i)
690 fragments.extend(j)
691 fragments.append(zipped[-1][0])
692
693 dropped_packet_indexes = set(
694 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
695 )
696
697 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100698 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200699 self.pg_start()
700
Klement Sekera4c533132018-02-22 11:41:12 +0100701 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200702 len(self.pkt_infos) - len(dropped_packet_indexes))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800703 self.verify_capture(IPv6, packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100704 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200705
Paul Vinciguerra69555952019-03-01 08:46:29 -0800706 @parameterized.expand([(IPv6, None, None)])
707 def test_timeout_inline(self, family, stream, dropped_packets_index):
Klement Sekera75e7d132017-09-20 08:26:30 +0200708 """ timeout (inline) """
Paul Vinciguerra69555952019-03-01 08:46:29 -0800709 stream = self.__class__.fragments_400
Klement Sekera75e7d132017-09-20 08:26:30 +0200710
711 dropped_packet_indexes = set(
712 index for (index, frags, _) in self.pkt_infos if len(frags) > 1
713 )
Paul Vinciguerra69555952019-03-01 08:46:29 -0800714 super(TestIPv6Reassembly, self).test_timeout_inline(
715 family, stream, dropped_packet_indexes)
Klement Sekera75e7d132017-09-20 08:26:30 +0200716
Klement Sekera4c533132018-02-22 11:41:12 +0100717 pkts = self.src_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200718 expected_count=len(dropped_packet_indexes))
719 for icmp in pkts:
720 self.assertIn(ICMPv6TimeExceeded, icmp)
721 self.assertIn(IPv6ExtHdrFragment, icmp)
722 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
723 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
724
725 def test_timeout_cleanup(self):
726 """ timeout (cleanup) """
727
728 # whole packets + fragmented packets sans last fragment
729 fragments = [
730 x for (_, frags_400, _) in self.pkt_infos
731 for x in frags_400[:-1 if len(frags_400) > 1 else None]
732 ]
733
734 # last fragments for fragmented packets
735 fragments2 = [frags_400[-1]
736 for (_, frags_400, _) in self.pkt_infos
737 if len(frags_400) > 1]
738
739 dropped_packet_indexes = set(
740 index for (index, frags_400, _) in self.pkt_infos
741 if len(frags_400) > 1)
742
743 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
744 expire_walk_interval_ms=50)
745
746 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
747 expire_walk_interval_ms=50, is_ip6=1)
748
749 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100750 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200751 self.pg_start()
752
753 self.sleep(.25, "wait before sending rest of fragments")
754
Klement Sekera4c533132018-02-22 11:41:12 +0100755 self.src_if.add_stream(fragments2)
Klement Sekera75e7d132017-09-20 08:26:30 +0200756 self.pg_start()
Klement Sekera75e7d132017-09-20 08:26:30 +0200757
Klement Sekera4c533132018-02-22 11:41:12 +0100758 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200759 len(self.pkt_infos) - len(dropped_packet_indexes))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800760 self.verify_capture(IPv6, packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100761 pkts = self.src_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200762 expected_count=len(dropped_packet_indexes))
763 for icmp in pkts:
764 self.assertIn(ICMPv6TimeExceeded, icmp)
765 self.assertIn(IPv6ExtHdrFragment, icmp)
766 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
767 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
768
Paul Vinciguerra69555952019-03-01 08:46:29 -0800769 @parameterized.expand([(IPv6, None, None)])
770 def test_disabled(self, family, stream, dropped_packet_indexes):
Klement Sekera75e7d132017-09-20 08:26:30 +0200771 """ reassembly disabled """
772
Paul Vinciguerra69555952019-03-01 08:46:29 -0800773 stream = self.__class__.fragments_400
Klement Sekera75e7d132017-09-20 08:26:30 +0200774 dropped_packet_indexes = set(
775 index for (index, frags_400, _) in self.pkt_infos
776 if len(frags_400) > 1)
Paul Vinciguerra69555952019-03-01 08:46:29 -0800777 super(TestIPv6Reassembly, self).test_disabled(
778 family, stream, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100779 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200780
781 def test_missing_upper(self):
782 """ missing upper layer """
Klement Sekera4c533132018-02-22 11:41:12 +0100783 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
784 IPv6(src=self.src_if.remote_ip6,
785 dst=self.src_if.local_ip6) /
786 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200787 Raw())
788 self.extend_packet(p, 1000, self.padding)
789 fragments = fragment_rfc8200(p, 1, 500)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700790 bad_fragment = p.__class__(scapy.compat.raw(fragments[1]))
Klement Sekera75e7d132017-09-20 08:26:30 +0200791 bad_fragment[IPv6ExtHdrFragment].nh = 59
792 bad_fragment[IPv6ExtHdrFragment].offset = 0
793 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100794 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +0200795 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +0100796 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +0200797 icmp = pkts[0]
798 self.assertIn(ICMPv6ParamProblem, icmp)
799 self.assert_equal(icmp[ICMPv6ParamProblem].code, 3, "ICMP code")
800
801 def test_invalid_frag_size(self):
802 """ fragment size not a multiple of 8 """
Klement Sekera4c533132018-02-22 11:41:12 +0100803 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
804 IPv6(src=self.src_if.remote_ip6,
805 dst=self.src_if.local_ip6) /
806 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200807 Raw())
808 self.extend_packet(p, 1000, self.padding)
809 fragments = fragment_rfc8200(p, 1, 500)
810 bad_fragment = fragments[0]
811 self.extend_packet(bad_fragment, len(bad_fragment) + 5)
812 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100813 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +0200814 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +0100815 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +0200816 icmp = pkts[0]
817 self.assertIn(ICMPv6ParamProblem, icmp)
818 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
819
820 def test_invalid_packet_size(self):
821 """ total packet size > 65535 """
Klement Sekera4c533132018-02-22 11:41:12 +0100822 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
823 IPv6(src=self.src_if.remote_ip6,
824 dst=self.src_if.local_ip6) /
825 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200826 Raw())
827 self.extend_packet(p, 1000, self.padding)
828 fragments = fragment_rfc8200(p, 1, 500)
829 bad_fragment = fragments[1]
830 bad_fragment[IPv6ExtHdrFragment].offset = 65500
831 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100832 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +0200833 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +0100834 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +0200835 icmp = pkts[0]
836 self.assertIn(ICMPv6ParamProblem, icmp)
837 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
838
839
Juraj Sloboda3048b632018-10-02 11:13:53 +0200840class TestIPv4ReassemblyLocalNode(VppTestCase):
841 """ IPv4 Reassembly for packets coming to ip4-local node """
842
843 @classmethod
844 def setUpClass(cls):
845 super(TestIPv4ReassemblyLocalNode, cls).setUpClass()
846
847 cls.create_pg_interfaces([0])
848 cls.src_dst_if = cls.pg0
849
850 # setup all interfaces
851 for i in cls.pg_interfaces:
852 i.admin_up()
853 i.config_ip4()
854 i.resolve_arp()
855
856 cls.padding = " abcdefghijklmn"
857 cls.create_stream()
858 cls.create_fragments()
859
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -0700860 @classmethod
861 def tearDownClass(cls):
862 super(TestIPv4ReassemblyLocalNode, cls).tearDownClass()
863
Juraj Sloboda3048b632018-10-02 11:13:53 +0200864 def setUp(self):
865 """ Test setup - force timeout on existing reassemblies """
866 super(TestIPv4ReassemblyLocalNode, self).setUp()
867 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
868 expire_walk_interval_ms=10)
869 self.sleep(.25)
870 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
871 expire_walk_interval_ms=10000)
872
873 def tearDown(self):
874 super(TestIPv4ReassemblyLocalNode, self).tearDown()
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700875
876 def show_commands_at_teardown(self):
Juraj Sloboda3048b632018-10-02 11:13:53 +0200877 self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +0100878 self.logger.debug(self.vapi.ppcli("show buffers"))
Juraj Sloboda3048b632018-10-02 11:13:53 +0200879
880 @classmethod
881 def create_stream(cls, packet_count=test_packet_count):
882 """Create input packet stream for defined interface.
883
884 :param list packet_sizes: Required packet sizes.
885 """
886 for i in range(0, packet_count):
887 info = cls.create_packet_info(cls.src_dst_if, cls.src_dst_if)
888 payload = cls.info_to_payload(info)
889 p = (Ether(dst=cls.src_dst_if.local_mac,
890 src=cls.src_dst_if.remote_mac) /
891 IP(id=info.index, src=cls.src_dst_if.remote_ip4,
892 dst=cls.src_dst_if.local_ip4) /
893 ICMP(type='echo-request', id=1234) /
894 Raw(payload))
895 cls.extend_packet(p, 1518, cls.padding)
896 info.data = p
897
898 @classmethod
899 def create_fragments(cls):
900 infos = cls._packet_infos
901 cls.pkt_infos = []
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -0800902 for index, info in six.iteritems(infos):
Juraj Sloboda3048b632018-10-02 11:13:53 +0200903 p = info.data
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700904 # cls.logger.debug(ppp("Packet:",
905 # p.__class__(scapy.compat.raw(p))))
Juraj Sloboda3048b632018-10-02 11:13:53 +0200906 fragments_300 = fragment_rfc791(p, 300)
907 cls.pkt_infos.append((index, fragments_300))
908 cls.fragments_300 = [x for (_, frags) in cls.pkt_infos for x in frags]
909 cls.logger.debug("Fragmented %s packets into %s 300-byte fragments" %
910 (len(infos), len(cls.fragments_300)))
911
912 def verify_capture(self, capture):
913 """Verify captured packet stream.
914
915 :param list capture: Captured packet stream.
916 """
917 info = None
918 seen = set()
919 for packet in capture:
920 try:
921 self.logger.debug(ppp("Got packet:", packet))
922 ip = packet[IP]
923 icmp = packet[ICMP]
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800924 payload_info = self.payload_to_info(packet[Raw])
Juraj Sloboda3048b632018-10-02 11:13:53 +0200925 packet_index = payload_info.index
926 if packet_index in seen:
927 raise Exception(ppp("Duplicate packet received", packet))
928 seen.add(packet_index)
929 self.assertEqual(payload_info.dst, self.src_dst_if.sw_if_index)
930 info = self._packet_infos[packet_index]
Klement Sekera14d7e902018-12-10 13:46:09 +0100931 self.assertIsNotNone(info)
Juraj Sloboda3048b632018-10-02 11:13:53 +0200932 self.assertEqual(packet_index, info.index)
933 saved_packet = info.data
934 self.assertEqual(ip.src, saved_packet[IP].dst)
935 self.assertEqual(ip.dst, saved_packet[IP].src)
936 self.assertEqual(icmp.type, 0) # echo reply
937 self.assertEqual(icmp.id, saved_packet[ICMP].id)
938 self.assertEqual(icmp.payload, saved_packet[ICMP].payload)
939 except Exception:
940 self.logger.error(ppp("Unexpected or invalid packet:", packet))
941 raise
942 for index in self._packet_infos:
Klement Sekera14d7e902018-12-10 13:46:09 +0100943 self.assertIn(index, seen,
944 "Packet with packet_index %d not received" % index)
Juraj Sloboda3048b632018-10-02 11:13:53 +0200945
946 def test_reassembly(self):
947 """ basic reassembly """
948
949 self.pg_enable_capture()
950 self.src_dst_if.add_stream(self.fragments_300)
951 self.pg_start()
952
953 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
954 self.verify_capture(packets)
955
956 # run it all again to verify correctness
957 self.pg_enable_capture()
958 self.src_dst_if.add_stream(self.fragments_300)
959 self.pg_start()
960
961 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
962 self.verify_capture(packets)
963
964
Klement Sekera75e7d132017-09-20 08:26:30 +0200965class TestFIFReassembly(VppTestCase):
966 """ Fragments in fragments reassembly """
967
968 @classmethod
969 def setUpClass(cls):
970 super(TestFIFReassembly, 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
975 for i in cls.pg_interfaces:
976 i.admin_up()
977 i.config_ip4()
978 i.resolve_arp()
979 i.config_ip6()
980 i.resolve_ndp()
Klement Sekera75e7d132017-09-20 08:26:30 +0200981
Klement Sekera75e7d132017-09-20 08:26:30 +0200982 cls.packet_sizes = [64, 512, 1518, 9018]
983 cls.padding = " abcdefghijklmn"
984
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -0700985 @classmethod
986 def tearDownClass(cls):
987 super(TestFIFReassembly, cls).tearDownClass()
988
Klement Sekera75e7d132017-09-20 08:26:30 +0200989 def setUp(self):
990 """ Test setup - force timeout on existing reassemblies """
991 super(TestFIFReassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +0100992 self.vapi.ip_reassembly_enable_disable(
993 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
994 enable_ip6=True)
995 self.vapi.ip_reassembly_enable_disable(
996 sw_if_index=self.dst_if.sw_if_index, enable_ip4=True,
997 enable_ip6=True)
Klement Sekera75e7d132017-09-20 08:26:30 +0200998 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
999 expire_walk_interval_ms=10)
1000 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1001 expire_walk_interval_ms=10, is_ip6=1)
1002 self.sleep(.25)
1003 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1004 expire_walk_interval_ms=10000)
1005 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1006 expire_walk_interval_ms=10000, is_ip6=1)
1007
1008 def tearDown(self):
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -07001009 super(TestFIFReassembly, self).tearDown()
1010
1011 def show_commands_at_teardown(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001012 self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
1013 self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +01001014 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +02001015
1016 def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):
1017 """Verify captured packet stream.
1018
1019 :param list capture: Captured packet stream.
1020 """
1021 info = None
1022 seen = set()
1023 for packet in capture:
1024 try:
Klement Sekera4c533132018-02-22 11:41:12 +01001025 self.logger.debug(ppp("Got packet:", packet))
Klement Sekera75e7d132017-09-20 08:26:30 +02001026 ip = packet[ip_class]
1027 udp = packet[UDP]
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001028 payload_info = self.payload_to_info(packet[Raw])
Klement Sekera75e7d132017-09-20 08:26:30 +02001029 packet_index = payload_info.index
1030 self.assertTrue(
1031 packet_index not in dropped_packet_indexes,
1032 ppp("Packet received, but should be dropped:", packet))
1033 if packet_index in seen:
1034 raise Exception(ppp("Duplicate packet received", packet))
1035 seen.add(packet_index)
Klement Sekera4c533132018-02-22 11:41:12 +01001036 self.assertEqual(payload_info.dst, self.dst_if.sw_if_index)
Klement Sekera75e7d132017-09-20 08:26:30 +02001037 info = self._packet_infos[packet_index]
1038 self.assertTrue(info is not None)
1039 self.assertEqual(packet_index, info.index)
1040 saved_packet = info.data
1041 self.assertEqual(ip.src, saved_packet[ip_class].src)
1042 self.assertEqual(ip.dst, saved_packet[ip_class].dst)
1043 self.assertEqual(udp.payload, saved_packet[UDP].payload)
Klement Sekera4c533132018-02-22 11:41:12 +01001044 except Exception:
Klement Sekera75e7d132017-09-20 08:26:30 +02001045 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1046 raise
1047 for index in self._packet_infos:
1048 self.assertTrue(index in seen or index in dropped_packet_indexes,
1049 "Packet with packet_index %d not received" % index)
1050
1051 def test_fif4(self):
1052 """ Fragments in fragments (4o4) """
1053
1054 # TODO this should be ideally in setUpClass, but then we hit a bug
1055 # with VppIpRoute incorrectly reporting it's present when it's not
1056 # so we need to manually remove the vpp config, thus we cannot have
1057 # it shared for multiple test cases
1058 self.tun_ip4 = "1.1.1.2"
1059
Klement Sekera4c533132018-02-22 11:41:12 +01001060 self.gre4 = VppGreInterface(self, self.src_if.local_ip4, self.tun_ip4)
Klement Sekera75e7d132017-09-20 08:26:30 +02001061 self.gre4.add_vpp_config()
1062 self.gre4.admin_up()
1063 self.gre4.config_ip4()
1064
Klement Sekera4c533132018-02-22 11:41:12 +01001065 self.vapi.ip_reassembly_enable_disable(
1066 sw_if_index=self.gre4.sw_if_index, enable_ip4=True)
1067
Klement Sekera75e7d132017-09-20 08:26:30 +02001068 self.route4 = VppIpRoute(self, self.tun_ip4, 32,
Klement Sekera4c533132018-02-22 11:41:12 +01001069 [VppRoutePath(self.src_if.remote_ip4,
1070 self.src_if.sw_if_index)])
Klement Sekera75e7d132017-09-20 08:26:30 +02001071 self.route4.add_vpp_config()
1072
1073 self.reset_packet_infos()
1074 for i in range(test_packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01001075 info = self.create_packet_info(self.src_if, self.dst_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02001076 payload = self.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +01001077 # Ethernet header here is only for size calculation, thus it
1078 # doesn't matter how it's initialized. This is to ensure that
1079 # reassembled packet is not > 9000 bytes, so that it's not dropped
1080 p = (Ether() /
1081 IP(id=i, src=self.src_if.remote_ip4,
1082 dst=self.dst_if.remote_ip4) /
1083 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001084 Raw(payload))
1085 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1086 self.extend_packet(p, size, self.padding)
Klement Sekera4c533132018-02-22 11:41:12 +01001087 info.data = p[IP] # use only IP part, without ethernet header
Klement Sekera75e7d132017-09-20 08:26:30 +02001088
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -08001089 fragments = [x for _, p in six.iteritems(self._packet_infos)
Klement Sekera75e7d132017-09-20 08:26:30 +02001090 for x in fragment_rfc791(p.data, 400)]
1091
1092 encapped_fragments = \
Klement Sekera4c533132018-02-22 11:41:12 +01001093 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1094 IP(src=self.tun_ip4, dst=self.src_if.local_ip4) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001095 GRE() /
1096 p
1097 for p in fragments]
1098
1099 fragmented_encapped_fragments = \
1100 [x for p in encapped_fragments
1101 for x in fragment_rfc791(p, 200)]
1102
Klement Sekera4c533132018-02-22 11:41:12 +01001103 self.src_if.add_stream(fragmented_encapped_fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001104
1105 self.pg_enable_capture(self.pg_interfaces)
1106 self.pg_start()
1107
Klement Sekera4c533132018-02-22 11:41:12 +01001108 self.src_if.assert_nothing_captured()
1109 packets = self.dst_if.get_capture(len(self._packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +02001110 self.verify_capture(packets, IP)
1111
1112 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
1113 # so that it's query_vpp_config() works as it should
1114 self.gre4.remove_vpp_config()
Klement Sekera4c533132018-02-22 11:41:12 +01001115 self.logger.debug(self.vapi.ppcli("show interface"))
Klement Sekera75e7d132017-09-20 08:26:30 +02001116
1117 def test_fif6(self):
1118 """ Fragments in fragments (6o6) """
1119 # TODO this should be ideally in setUpClass, but then we hit a bug
1120 # with VppIpRoute incorrectly reporting it's present when it's not
1121 # so we need to manually remove the vpp config, thus we cannot have
1122 # it shared for multiple test cases
1123 self.tun_ip6 = "1002::1"
1124
Neale Ranns5a8844b2019-04-16 07:15:35 +00001125 self.gre6 = VppGreInterface(self, self.src_if.local_ip6, self.tun_ip6)
Klement Sekera75e7d132017-09-20 08:26:30 +02001126 self.gre6.add_vpp_config()
1127 self.gre6.admin_up()
1128 self.gre6.config_ip6()
1129
Klement Sekera4c533132018-02-22 11:41:12 +01001130 self.vapi.ip_reassembly_enable_disable(
1131 sw_if_index=self.gre6.sw_if_index, enable_ip6=True)
1132
Klement Sekera75e7d132017-09-20 08:26:30 +02001133 self.route6 = VppIpRoute(self, self.tun_ip6, 128,
Klement Sekera4c533132018-02-22 11:41:12 +01001134 [VppRoutePath(self.src_if.remote_ip6,
1135 self.src_if.sw_if_index,
Klement Sekera75e7d132017-09-20 08:26:30 +02001136 proto=DpoProto.DPO_PROTO_IP6)],
1137 is_ip6=1)
1138 self.route6.add_vpp_config()
1139
1140 self.reset_packet_infos()
1141 for i in range(test_packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01001142 info = self.create_packet_info(self.src_if, self.dst_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02001143 payload = self.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +01001144 # Ethernet header here is only for size calculation, thus it
1145 # doesn't matter how it's initialized. This is to ensure that
1146 # reassembled packet is not > 9000 bytes, so that it's not dropped
1147 p = (Ether() /
1148 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1149 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001150 Raw(payload))
1151 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1152 self.extend_packet(p, size, self.padding)
Klement Sekera4c533132018-02-22 11:41:12 +01001153 info.data = p[IPv6] # use only IPv6 part, without ethernet header
Klement Sekera75e7d132017-09-20 08:26:30 +02001154
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -08001155 fragments = [x for _, i in six.iteritems(self._packet_infos)
Klement Sekera75e7d132017-09-20 08:26:30 +02001156 for x in fragment_rfc8200(
1157 i.data, i.index, 400)]
1158
1159 encapped_fragments = \
Klement Sekera4c533132018-02-22 11:41:12 +01001160 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1161 IPv6(src=self.tun_ip6, dst=self.src_if.local_ip6) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001162 GRE() /
1163 p
1164 for p in fragments]
1165
1166 fragmented_encapped_fragments = \
1167 [x for p in encapped_fragments for x in (
1168 fragment_rfc8200(
1169 p,
1170 2 * len(self._packet_infos) + p[IPv6ExtHdrFragment].id,
1171 200)
1172 if IPv6ExtHdrFragment in p else [p]
1173 )
1174 ]
1175
Klement Sekera4c533132018-02-22 11:41:12 +01001176 self.src_if.add_stream(fragmented_encapped_fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001177
1178 self.pg_enable_capture(self.pg_interfaces)
1179 self.pg_start()
1180
Klement Sekera4c533132018-02-22 11:41:12 +01001181 self.src_if.assert_nothing_captured()
1182 packets = self.dst_if.get_capture(len(self._packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +02001183 self.verify_capture(packets, IPv6)
1184
1185 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
1186 # so that it's query_vpp_config() works as it should
Klement Sekera3ecc2212018-03-27 10:34:43 +02001187 self.gre6.remove_vpp_config()
Klement Sekera75e7d132017-09-20 08:26:30 +02001188
1189
1190if __name__ == '__main__':
1191 unittest.main(testRunner=VppTestRunner)