blob: 5fa912b631e60f3b83928155a798abc125ad3747 [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
Klement Sekera75e7d132017-09-20 08:26:30 +020018from vpp_gre_interface import VppGreInterface, VppGre6Interface
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()
239 self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +0100240 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +0200241
242 @classmethod
243 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
Klement Sekerad0f70a32018-12-14 17:24:13 +0100244 """Create input packet stream
Klement Sekera75e7d132017-09-20 08:26:30 +0200245
246 :param list packet_sizes: Required packet sizes.
247 """
248 for i in range(0, packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +0100249 info = cls.create_packet_info(cls.src_if, cls.src_if)
Klement Sekera75e7d132017-09-20 08:26:30 +0200250 payload = cls.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +0100251 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
252 IP(id=info.index, src=cls.src_if.remote_ip4,
253 dst=cls.dst_if.remote_ip4) /
254 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200255 Raw(payload))
256 size = packet_sizes[(i // 2) % len(packet_sizes)]
257 cls.extend_packet(p, size, cls.padding)
258 info.data = p
259
260 @classmethod
261 def create_fragments(cls):
262 infos = cls._packet_infos
263 cls.pkt_infos = []
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -0800264 for index, info in six.iteritems(infos):
Klement Sekera75e7d132017-09-20 08:26:30 +0200265 p = info.data
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700266 # cls.logger.debug(ppp("Packet:",
267 # p.__class__(scapy.compat.raw(p))))
Klement Sekera75e7d132017-09-20 08:26:30 +0200268 fragments_400 = fragment_rfc791(p, 400)
269 fragments_300 = fragment_rfc791(p, 300)
270 fragments_200 = [
271 x for f in fragments_400 for x in fragment_rfc791(f, 200)]
272 cls.pkt_infos.append(
273 (index, fragments_400, fragments_300, fragments_200))
274 cls.fragments_400 = [
275 x for (_, frags, _, _) in cls.pkt_infos for x in frags]
276 cls.fragments_300 = [
277 x for (_, _, frags, _) in cls.pkt_infos for x in frags]
278 cls.fragments_200 = [
279 x for (_, _, _, frags) in cls.pkt_infos for x in frags]
280 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
281 "%s 300-byte fragments and %s 200-byte fragments" %
282 (len(infos), len(cls.fragments_400),
283 len(cls.fragments_300), len(cls.fragments_200)))
284
Paul Vinciguerra69555952019-03-01 08:46:29 -0800285 @parameterized.expand([(IP, None)])
286 def test_reassembly(self, family, stream):
Klement Sekera75e7d132017-09-20 08:26:30 +0200287 """ basic reassembly """
Paul Vinciguerra69555952019-03-01 08:46:29 -0800288 stream = self.__class__.fragments_200
289 super(TestIPv4Reassembly, self).test_reassembly(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_reversed(self, family, stream):
Klement Sekera75e7d132017-09-20 08:26:30 +0200293 """ reverse order reassembly """
Paul Vinciguerra69555952019-03-01 08:46:29 -0800294 stream = self.__class__.fragments_200
295 super(TestIPv4Reassembly, self).test_reversed(family, stream)
Klement Sekera75e7d132017-09-20 08:26:30 +0200296
Paul Vinciguerra69555952019-03-01 08:46:29 -0800297 @parameterized.expand([(IP, None)])
298 def test_random(self, family, stream):
299 stream = self.__class__.fragments_200
300 super(TestIPv4Reassembly, self).test_random(family, stream)
Klement Sekera75e7d132017-09-20 08:26:30 +0200301
Klement Sekera14d7e902018-12-10 13:46:09 +0100302 def test_5737(self):
303 """ fragment length + ip header size > 65535 """
Klement Sekera4ee633e2018-12-14 12:00:44 +0100304 self.vapi.cli("clear errors")
Klement Sekera14d7e902018-12-10 13:46:09 +0100305 raw = ('E\x00\x00\x88,\xf8\x1f\xfe@\x01\x98\x00\xc0\xa8\n-\xc0\xa8\n'
306 '\x01\x08\x00\xf0J\xed\xcb\xf1\xf5Test-group: IPv4.IPv4.ipv4-'
307 'message.Ethernet-Payload.IPv4-Packet.IPv4-Header.Fragment-Of'
308 'fset; Test-case: 5737')
309
310 malformed_packet = (Ether(dst=self.src_if.local_mac,
311 src=self.src_if.remote_mac) /
312 IP(raw))
313 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
314 IP(id=1000, src=self.src_if.remote_ip4,
315 dst=self.dst_if.remote_ip4) /
316 UDP(sport=1234, dport=5678) /
317 Raw("X" * 1000))
318 valid_fragments = fragment_rfc791(p, 400)
319
320 self.pg_enable_capture()
321 self.src_if.add_stream([malformed_packet] + valid_fragments)
322 self.pg_start()
323
324 self.dst_if.get_capture(1)
Klement Sekera4ee633e2018-12-14 12:00:44 +0100325 self.assert_packet_counter_equal("ip4-reassembly-feature", 1)
326 # TODO remove above, uncomment below once clearing of counters
327 # is supported
328 # self.assert_packet_counter_equal(
329 # "/err/ip4-reassembly-feature/malformed packets", 1)
Klement Sekera14d7e902018-12-10 13:46:09 +0100330
Klement Sekera400f6d82018-12-13 14:35:48 +0100331 def test_44924(self):
332 """ compress tiny fragments """
333 packets = [(Ether(dst=self.src_if.local_mac,
334 src=self.src_if.remote_mac) /
335 IP(id=24339, flags="MF", frag=0, ttl=64,
336 src=self.src_if.remote_ip4,
337 dst=self.dst_if.remote_ip4) /
338 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
339 Raw(load='Test-group: IPv4')),
340 (Ether(dst=self.src_if.local_mac,
341 src=self.src_if.remote_mac) /
342 IP(id=24339, flags="MF", frag=3, ttl=64,
343 src=self.src_if.remote_ip4,
344 dst=self.dst_if.remote_ip4) /
345 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
346 Raw(load='.IPv4.Fragmentation.vali')),
347 (Ether(dst=self.src_if.local_mac,
348 src=self.src_if.remote_mac) /
349 IP(id=24339, frag=6, ttl=64,
350 src=self.src_if.remote_ip4,
351 dst=self.dst_if.remote_ip4) /
352 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
353 Raw(load='d; Test-case: 44924'))
354 ]
355
356 self.pg_enable_capture()
357 self.src_if.add_stream(packets)
358 self.pg_start()
359
360 self.dst_if.get_capture(1)
361
Klement Sekera4ee633e2018-12-14 12:00:44 +0100362 def test_frag_1(self):
363 """ fragment of size 1 """
364 self.vapi.cli("clear errors")
365 malformed_packets = [(Ether(dst=self.src_if.local_mac,
366 src=self.src_if.remote_mac) /
367 IP(id=7, len=21, flags="MF", frag=0, ttl=64,
368 src=self.src_if.remote_ip4,
369 dst=self.dst_if.remote_ip4) /
370 ICMP(type="echo-request")),
371 (Ether(dst=self.src_if.local_mac,
372 src=self.src_if.remote_mac) /
373 IP(id=7, len=21, frag=1, ttl=64,
374 src=self.src_if.remote_ip4,
375 dst=self.dst_if.remote_ip4) /
376 Raw(load='\x08')),
377 ]
378
379 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
380 IP(id=1000, src=self.src_if.remote_ip4,
381 dst=self.dst_if.remote_ip4) /
382 UDP(sport=1234, dport=5678) /
383 Raw("X" * 1000))
384 valid_fragments = fragment_rfc791(p, 400)
385
386 self.pg_enable_capture()
387 self.src_if.add_stream(malformed_packets + valid_fragments)
388 self.pg_start()
389
390 self.dst_if.get_capture(1)
391
392 self.assert_packet_counter_equal("ip4-reassembly-feature", 1)
393 # TODO remove above, uncomment below once clearing of counters
394 # is supported
395 # self.assert_packet_counter_equal(
396 # "/err/ip4-reassembly-feature/malformed packets", 1)
397
Paul Vinciguerra69555952019-03-01 08:46:29 -0800398 @parameterized.expand([(IP, None)])
399 def test_duplicates(self, family, stream):
Klement Sekera75e7d132017-09-20 08:26:30 +0200400 """ duplicate fragments """
Klement Sekera75e7d132017-09-20 08:26:30 +0200401 fragments = [
Paul Vinciguerra69555952019-03-01 08:46:29 -0800402 # IPv4 uses 4 fields in pkt_infos, IPv6 uses 3.
Klement Sekera75e7d132017-09-20 08:26:30 +0200403 x for (_, frags, _, _) in self.pkt_infos
404 for x in frags
405 for _ in range(0, min(2, len(frags)))
406 ]
Paul Vinciguerra69555952019-03-01 08:46:29 -0800407 super(TestIPv4Reassembly, self).test_duplicates(family, fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200408
409 def test_overlap1(self):
410 """ overlapping fragments case #1 """
411
412 fragments = []
413 for _, _, frags_300, frags_200 in self.pkt_infos:
414 if len(frags_300) == 1:
415 fragments.extend(frags_300)
416 else:
417 for i, j in zip(frags_200, frags_300):
418 fragments.extend(i)
419 fragments.extend(j)
420
421 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100422 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200423 self.pg_start()
424
Klement Sekera4c533132018-02-22 11:41:12 +0100425 packets = self.dst_if.get_capture(len(self.pkt_infos))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800426 self.verify_capture(IP, packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100427 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200428
429 # run it all to verify correctness
430 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100431 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200432 self.pg_start()
433
Klement Sekera4c533132018-02-22 11:41:12 +0100434 packets = self.dst_if.get_capture(len(self.pkt_infos))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800435 self.verify_capture(IP, packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100436 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200437
438 def test_overlap2(self):
439 """ overlapping fragments case #2 """
440
441 fragments = []
442 for _, _, frags_300, frags_200 in self.pkt_infos:
443 if len(frags_300) == 1:
444 fragments.extend(frags_300)
445 else:
446 # care must be taken here so that there are no fragments
447 # received by vpp after reassembly is finished, otherwise
448 # new reassemblies will be started and packet generator will
449 # freak out when it detects unfreed buffers
450 zipped = zip(frags_300, frags_200)
451 for i, j in zipped[:-1]:
452 fragments.extend(i)
453 fragments.extend(j)
454 fragments.append(zipped[-1][0])
455
456 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100457 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200458 self.pg_start()
459
Klement Sekera4c533132018-02-22 11:41:12 +0100460 packets = self.dst_if.get_capture(len(self.pkt_infos))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800461 self.verify_capture(IP, packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100462 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200463
464 # run it all to verify correctness
465 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100466 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200467 self.pg_start()
468
Klement Sekera4c533132018-02-22 11:41:12 +0100469 packets = self.dst_if.get_capture(len(self.pkt_infos))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800470 self.verify_capture(IP, packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100471 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200472
Paul Vinciguerra69555952019-03-01 08:46:29 -0800473 @parameterized.expand([(IP, None, None)])
474 def test_timeout_inline(self, family, stream, dropped_packet_indexes):
Klement Sekera75e7d132017-09-20 08:26:30 +0200475 """ timeout (inline) """
Paul Vinciguerra69555952019-03-01 08:46:29 -0800476 stream = self.fragments_400
Klement Sekera75e7d132017-09-20 08:26:30 +0200477
478 dropped_packet_indexes = set(
479 index for (index, frags, _, _) in self.pkt_infos if len(frags) > 1
480 )
Paul Vinciguerra69555952019-03-01 08:46:29 -0800481 super(TestIPv4Reassembly, self).test_timeout_inline(
482 family, stream, dropped_packet_indexes)
Klement Sekera75e7d132017-09-20 08:26:30 +0200483
Klement Sekera4c533132018-02-22 11:41:12 +0100484 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200485
486 def test_timeout_cleanup(self):
487 """ timeout (cleanup) """
488
489 # whole packets + fragmented packets sans last fragment
490 fragments = [
491 x for (_, frags_400, _, _) in self.pkt_infos
492 for x in frags_400[:-1 if len(frags_400) > 1 else None]
493 ]
494
495 # last fragments for fragmented packets
496 fragments2 = [frags_400[-1]
497 for (_, frags_400, _, _) in self.pkt_infos
498 if len(frags_400) > 1]
499
500 dropped_packet_indexes = set(
501 index for (index, frags_400, _, _) in self.pkt_infos
502 if len(frags_400) > 1)
503
504 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
505 expire_walk_interval_ms=50)
506
507 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100508 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200509 self.pg_start()
510
511 self.sleep(.25, "wait before sending rest of fragments")
512
Klement Sekera4c533132018-02-22 11:41:12 +0100513 self.src_if.add_stream(fragments2)
Klement Sekera75e7d132017-09-20 08:26:30 +0200514 self.pg_start()
Klement Sekera75e7d132017-09-20 08:26:30 +0200515
Klement Sekera4c533132018-02-22 11:41:12 +0100516 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200517 len(self.pkt_infos) - len(dropped_packet_indexes))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800518 self.verify_capture(IP, packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100519 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200520
Paul Vinciguerra69555952019-03-01 08:46:29 -0800521 @parameterized.expand([(IP, None, None)])
522 def test_disabled(self, family, stream, dropped_packet_indexes):
Klement Sekera75e7d132017-09-20 08:26:30 +0200523 """ reassembly disabled """
524
Paul Vinciguerra69555952019-03-01 08:46:29 -0800525 stream = self.__class__.fragments_400
Klement Sekera75e7d132017-09-20 08:26:30 +0200526 dropped_packet_indexes = set(
527 index for (index, frags_400, _, _) in self.pkt_infos
528 if len(frags_400) > 1)
Paul Vinciguerra69555952019-03-01 08:46:29 -0800529 super(TestIPv4Reassembly, self).test_disabled(
530 family, stream, dropped_packet_indexes)
Klement Sekera75e7d132017-09-20 08:26:30 +0200531
532
Paul Vinciguerra69555952019-03-01 08:46:29 -0800533class TestIPv6Reassembly(TestIPReassemblyMixin, VppTestCase):
Klement Sekera75e7d132017-09-20 08:26:30 +0200534 """ IPv6 Reassembly """
535
536 @classmethod
537 def setUpClass(cls):
538 super(TestIPv6Reassembly, cls).setUpClass()
539
Klement Sekera4c533132018-02-22 11:41:12 +0100540 cls.create_pg_interfaces([0, 1])
541 cls.src_if = cls.pg0
542 cls.dst_if = cls.pg1
Klement Sekera75e7d132017-09-20 08:26:30 +0200543
544 # setup all interfaces
545 for i in cls.pg_interfaces:
546 i.admin_up()
547 i.config_ip6()
548 i.resolve_ndp()
549
Klement Sekera75e7d132017-09-20 08:26:30 +0200550 # packet sizes
551 cls.packet_sizes = [64, 512, 1518, 9018]
552 cls.padding = " abcdefghijklmn"
553 cls.create_stream(cls.packet_sizes)
554 cls.create_fragments()
555
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -0700556 @classmethod
557 def tearDownClass(cls):
558 super(TestIPv6Reassembly, cls).tearDownClass()
559
Klement Sekera75e7d132017-09-20 08:26:30 +0200560 def setUp(self):
561 """ Test setup - force timeout on existing reassemblies """
562 super(TestIPv6Reassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +0100563 self.vapi.ip_reassembly_enable_disable(
564 sw_if_index=self.src_if.sw_if_index, enable_ip6=True)
Klement Sekera75e7d132017-09-20 08:26:30 +0200565 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
566 expire_walk_interval_ms=10, is_ip6=1)
567 self.sleep(.25)
568 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
569 expire_walk_interval_ms=10000, is_ip6=1)
Klement Sekera4c533132018-02-22 11:41:12 +0100570 self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +0100571 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +0200572
573 def tearDown(self):
574 super(TestIPv6Reassembly, self).tearDown()
575 self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +0100576 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +0200577
578 @classmethod
579 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
580 """Create input packet stream for defined interface.
581
582 :param list packet_sizes: Required packet sizes.
583 """
584 for i in range(0, packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +0100585 info = cls.create_packet_info(cls.src_if, cls.src_if)
Klement Sekera75e7d132017-09-20 08:26:30 +0200586 payload = cls.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +0100587 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
588 IPv6(src=cls.src_if.remote_ip6,
589 dst=cls.dst_if.remote_ip6) /
590 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200591 Raw(payload))
592 size = packet_sizes[(i // 2) % len(packet_sizes)]
593 cls.extend_packet(p, size, cls.padding)
594 info.data = p
595
596 @classmethod
597 def create_fragments(cls):
598 infos = cls._packet_infos
599 cls.pkt_infos = []
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -0800600 for index, info in six.iteritems(infos):
Klement Sekera75e7d132017-09-20 08:26:30 +0200601 p = info.data
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700602 # cls.logger.debug(ppp("Packet:",
603 # p.__class__(scapy.compat.raw(p))))
Klement Sekera75e7d132017-09-20 08:26:30 +0200604 fragments_400 = fragment_rfc8200(p, info.index, 400)
605 fragments_300 = fragment_rfc8200(p, info.index, 300)
606 cls.pkt_infos.append((index, fragments_400, fragments_300))
607 cls.fragments_400 = [
608 x for _, frags, _ in cls.pkt_infos for x in frags]
609 cls.fragments_300 = [
610 x for _, _, frags in cls.pkt_infos for x in frags]
611 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
612 "and %s 300-byte fragments" %
613 (len(infos), len(cls.fragments_400),
614 len(cls.fragments_300)))
615
Paul Vinciguerra69555952019-03-01 08:46:29 -0800616 @parameterized.expand([(IPv6, None)])
617 def test_reassembly(self, family, stream):
Klement Sekera75e7d132017-09-20 08:26:30 +0200618 """ basic reassembly """
Paul Vinciguerra69555952019-03-01 08:46:29 -0800619 stream = self.__class__.fragments_400
620 super(TestIPv6Reassembly, self).test_reassembly(family, stream)
Klement Sekera75e7d132017-09-20 08:26:30 +0200621
Paul Vinciguerra69555952019-03-01 08:46:29 -0800622 @parameterized.expand([(IPv6, None)])
623 def test_reversed(self, family, stream):
Klement Sekera75e7d132017-09-20 08:26:30 +0200624 """ reverse order reassembly """
Paul Vinciguerra69555952019-03-01 08:46:29 -0800625 stream = self.__class__.fragments_400
626 super(TestIPv6Reassembly, self).test_reversed(family, stream)
Klement Sekera75e7d132017-09-20 08:26:30 +0200627
Paul Vinciguerra69555952019-03-01 08:46:29 -0800628 @parameterized.expand([(IPv6, None)])
629 def test_random(self, family, stream):
Klement Sekera75e7d132017-09-20 08:26:30 +0200630 """ random order reassembly """
Paul Vinciguerra69555952019-03-01 08:46:29 -0800631 stream = self.__class__.fragments_400
632 super(TestIPv6Reassembly, self).test_random(family, stream)
Klement Sekera75e7d132017-09-20 08:26:30 +0200633
Paul Vinciguerra69555952019-03-01 08:46:29 -0800634 @parameterized.expand([(IPv6, None)])
635 def test_duplicates(self, family, stream):
Klement Sekera75e7d132017-09-20 08:26:30 +0200636 """ duplicate fragments """
637
638 fragments = [
Paul Vinciguerra69555952019-03-01 08:46:29 -0800639 # IPv4 uses 4 fields in pkt_infos, IPv6 uses 3.
Klement Sekera75e7d132017-09-20 08:26:30 +0200640 x for (_, frags, _) in self.pkt_infos
641 for x in frags
642 for _ in range(0, min(2, len(frags)))
643 ]
Paul Vinciguerra69555952019-03-01 08:46:29 -0800644 super(TestIPv6Reassembly, self).test_duplicates(family, fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200645
646 def test_overlap1(self):
Paul Vinciguerra69555952019-03-01 08:46:29 -0800647 """ overlapping fragments case #1 (differs from IP test case)"""
Klement Sekera75e7d132017-09-20 08:26:30 +0200648
649 fragments = []
650 for _, frags_400, frags_300 in self.pkt_infos:
651 if len(frags_300) == 1:
652 fragments.extend(frags_400)
653 else:
654 for i, j in zip(frags_300, frags_400):
655 fragments.extend(i)
656 fragments.extend(j)
657
658 dropped_packet_indexes = set(
659 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
660 )
661
662 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100663 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200664 self.pg_start()
665
Klement Sekera4c533132018-02-22 11:41:12 +0100666 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200667 len(self.pkt_infos) - len(dropped_packet_indexes))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800668 self.verify_capture(IPv6, packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100669 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200670
671 def test_overlap2(self):
Paul Vinciguerra69555952019-03-01 08:46:29 -0800672 """ overlapping fragments case #2 (differs from IP test case)"""
Klement Sekera75e7d132017-09-20 08:26:30 +0200673
674 fragments = []
Klement Sekera4c533132018-02-22 11:41:12 +0100675 for _, frags_400, frags_300 in self.pkt_infos:
Klement Sekera75e7d132017-09-20 08:26:30 +0200676 if len(frags_400) == 1:
677 fragments.extend(frags_400)
678 else:
679 # care must be taken here so that there are no fragments
680 # received by vpp after reassembly is finished, otherwise
681 # new reassemblies will be started and packet generator will
682 # freak out when it detects unfreed buffers
Klement Sekera4c533132018-02-22 11:41:12 +0100683 zipped = zip(frags_400, frags_300)
Klement Sekera75e7d132017-09-20 08:26:30 +0200684 for i, j in zipped[:-1]:
685 fragments.extend(i)
686 fragments.extend(j)
687 fragments.append(zipped[-1][0])
688
689 dropped_packet_indexes = set(
690 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
691 )
692
693 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100694 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200695 self.pg_start()
696
Klement Sekera4c533132018-02-22 11:41:12 +0100697 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200698 len(self.pkt_infos) - len(dropped_packet_indexes))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800699 self.verify_capture(IPv6, packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100700 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200701
Paul Vinciguerra69555952019-03-01 08:46:29 -0800702 @parameterized.expand([(IPv6, None, None)])
703 def test_timeout_inline(self, family, stream, dropped_packets_index):
Klement Sekera75e7d132017-09-20 08:26:30 +0200704 """ timeout (inline) """
Paul Vinciguerra69555952019-03-01 08:46:29 -0800705 stream = self.__class__.fragments_400
Klement Sekera75e7d132017-09-20 08:26:30 +0200706
707 dropped_packet_indexes = set(
708 index for (index, frags, _) in self.pkt_infos if len(frags) > 1
709 )
Paul Vinciguerra69555952019-03-01 08:46:29 -0800710 super(TestIPv6Reassembly, self).test_timeout_inline(
711 family, stream, dropped_packet_indexes)
Klement Sekera75e7d132017-09-20 08:26:30 +0200712
Klement Sekera4c533132018-02-22 11:41:12 +0100713 pkts = self.src_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200714 expected_count=len(dropped_packet_indexes))
715 for icmp in pkts:
716 self.assertIn(ICMPv6TimeExceeded, icmp)
717 self.assertIn(IPv6ExtHdrFragment, icmp)
718 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
719 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
720
721 def test_timeout_cleanup(self):
722 """ timeout (cleanup) """
723
724 # whole packets + fragmented packets sans last fragment
725 fragments = [
726 x for (_, frags_400, _) in self.pkt_infos
727 for x in frags_400[:-1 if len(frags_400) > 1 else None]
728 ]
729
730 # last fragments for fragmented packets
731 fragments2 = [frags_400[-1]
732 for (_, frags_400, _) in self.pkt_infos
733 if len(frags_400) > 1]
734
735 dropped_packet_indexes = set(
736 index for (index, frags_400, _) in self.pkt_infos
737 if len(frags_400) > 1)
738
739 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
740 expire_walk_interval_ms=50)
741
742 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
743 expire_walk_interval_ms=50, is_ip6=1)
744
745 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100746 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200747 self.pg_start()
748
749 self.sleep(.25, "wait before sending rest of fragments")
750
Klement Sekera4c533132018-02-22 11:41:12 +0100751 self.src_if.add_stream(fragments2)
Klement Sekera75e7d132017-09-20 08:26:30 +0200752 self.pg_start()
Klement Sekera75e7d132017-09-20 08:26:30 +0200753
Klement Sekera4c533132018-02-22 11:41:12 +0100754 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200755 len(self.pkt_infos) - len(dropped_packet_indexes))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800756 self.verify_capture(IPv6, packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100757 pkts = self.src_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200758 expected_count=len(dropped_packet_indexes))
759 for icmp in pkts:
760 self.assertIn(ICMPv6TimeExceeded, icmp)
761 self.assertIn(IPv6ExtHdrFragment, icmp)
762 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
763 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
764
Paul Vinciguerra69555952019-03-01 08:46:29 -0800765 @parameterized.expand([(IPv6, None, None)])
766 def test_disabled(self, family, stream, dropped_packet_indexes):
Klement Sekera75e7d132017-09-20 08:26:30 +0200767 """ reassembly disabled """
768
Paul Vinciguerra69555952019-03-01 08:46:29 -0800769 stream = self.__class__.fragments_400
Klement Sekera75e7d132017-09-20 08:26:30 +0200770 dropped_packet_indexes = set(
771 index for (index, frags_400, _) in self.pkt_infos
772 if len(frags_400) > 1)
Paul Vinciguerra69555952019-03-01 08:46:29 -0800773 super(TestIPv6Reassembly, self).test_disabled(
774 family, stream, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100775 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200776
777 def test_missing_upper(self):
778 """ missing upper layer """
Klement Sekera4c533132018-02-22 11:41:12 +0100779 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
780 IPv6(src=self.src_if.remote_ip6,
781 dst=self.src_if.local_ip6) /
782 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200783 Raw())
784 self.extend_packet(p, 1000, self.padding)
785 fragments = fragment_rfc8200(p, 1, 500)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700786 bad_fragment = p.__class__(scapy.compat.raw(fragments[1]))
Klement Sekera75e7d132017-09-20 08:26:30 +0200787 bad_fragment[IPv6ExtHdrFragment].nh = 59
788 bad_fragment[IPv6ExtHdrFragment].offset = 0
789 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100790 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +0200791 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +0100792 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +0200793 icmp = pkts[0]
794 self.assertIn(ICMPv6ParamProblem, icmp)
795 self.assert_equal(icmp[ICMPv6ParamProblem].code, 3, "ICMP code")
796
797 def test_invalid_frag_size(self):
798 """ fragment size not a multiple of 8 """
Klement Sekera4c533132018-02-22 11:41:12 +0100799 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
800 IPv6(src=self.src_if.remote_ip6,
801 dst=self.src_if.local_ip6) /
802 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200803 Raw())
804 self.extend_packet(p, 1000, self.padding)
805 fragments = fragment_rfc8200(p, 1, 500)
806 bad_fragment = fragments[0]
807 self.extend_packet(bad_fragment, len(bad_fragment) + 5)
808 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100809 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +0200810 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +0100811 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +0200812 icmp = pkts[0]
813 self.assertIn(ICMPv6ParamProblem, icmp)
814 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
815
816 def test_invalid_packet_size(self):
817 """ total packet size > 65535 """
Klement Sekera4c533132018-02-22 11:41:12 +0100818 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
819 IPv6(src=self.src_if.remote_ip6,
820 dst=self.src_if.local_ip6) /
821 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200822 Raw())
823 self.extend_packet(p, 1000, self.padding)
824 fragments = fragment_rfc8200(p, 1, 500)
825 bad_fragment = fragments[1]
826 bad_fragment[IPv6ExtHdrFragment].offset = 65500
827 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100828 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +0200829 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +0100830 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +0200831 icmp = pkts[0]
832 self.assertIn(ICMPv6ParamProblem, icmp)
833 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
834
835
Juraj Sloboda3048b632018-10-02 11:13:53 +0200836class TestIPv4ReassemblyLocalNode(VppTestCase):
837 """ IPv4 Reassembly for packets coming to ip4-local node """
838
839 @classmethod
840 def setUpClass(cls):
841 super(TestIPv4ReassemblyLocalNode, cls).setUpClass()
842
843 cls.create_pg_interfaces([0])
844 cls.src_dst_if = cls.pg0
845
846 # setup all interfaces
847 for i in cls.pg_interfaces:
848 i.admin_up()
849 i.config_ip4()
850 i.resolve_arp()
851
852 cls.padding = " abcdefghijklmn"
853 cls.create_stream()
854 cls.create_fragments()
855
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -0700856 @classmethod
857 def tearDownClass(cls):
858 super(TestIPv4ReassemblyLocalNode, cls).tearDownClass()
859
Juraj Sloboda3048b632018-10-02 11:13:53 +0200860 def setUp(self):
861 """ Test setup - force timeout on existing reassemblies """
862 super(TestIPv4ReassemblyLocalNode, self).setUp()
863 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
864 expire_walk_interval_ms=10)
865 self.sleep(.25)
866 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
867 expire_walk_interval_ms=10000)
868
869 def tearDown(self):
870 super(TestIPv4ReassemblyLocalNode, self).tearDown()
871 self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +0100872 self.logger.debug(self.vapi.ppcli("show buffers"))
Juraj Sloboda3048b632018-10-02 11:13:53 +0200873
874 @classmethod
875 def create_stream(cls, packet_count=test_packet_count):
876 """Create input packet stream for defined interface.
877
878 :param list packet_sizes: Required packet sizes.
879 """
880 for i in range(0, packet_count):
881 info = cls.create_packet_info(cls.src_dst_if, cls.src_dst_if)
882 payload = cls.info_to_payload(info)
883 p = (Ether(dst=cls.src_dst_if.local_mac,
884 src=cls.src_dst_if.remote_mac) /
885 IP(id=info.index, src=cls.src_dst_if.remote_ip4,
886 dst=cls.src_dst_if.local_ip4) /
887 ICMP(type='echo-request', id=1234) /
888 Raw(payload))
889 cls.extend_packet(p, 1518, cls.padding)
890 info.data = p
891
892 @classmethod
893 def create_fragments(cls):
894 infos = cls._packet_infos
895 cls.pkt_infos = []
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -0800896 for index, info in six.iteritems(infos):
Juraj Sloboda3048b632018-10-02 11:13:53 +0200897 p = info.data
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700898 # cls.logger.debug(ppp("Packet:",
899 # p.__class__(scapy.compat.raw(p))))
Juraj Sloboda3048b632018-10-02 11:13:53 +0200900 fragments_300 = fragment_rfc791(p, 300)
901 cls.pkt_infos.append((index, fragments_300))
902 cls.fragments_300 = [x for (_, frags) in cls.pkt_infos for x in frags]
903 cls.logger.debug("Fragmented %s packets into %s 300-byte fragments" %
904 (len(infos), len(cls.fragments_300)))
905
906 def verify_capture(self, capture):
907 """Verify captured packet stream.
908
909 :param list capture: Captured packet stream.
910 """
911 info = None
912 seen = set()
913 for packet in capture:
914 try:
915 self.logger.debug(ppp("Got packet:", packet))
916 ip = packet[IP]
917 icmp = packet[ICMP]
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800918 payload_info = self.payload_to_info(packet[Raw])
Juraj Sloboda3048b632018-10-02 11:13:53 +0200919 packet_index = payload_info.index
920 if packet_index in seen:
921 raise Exception(ppp("Duplicate packet received", packet))
922 seen.add(packet_index)
923 self.assertEqual(payload_info.dst, self.src_dst_if.sw_if_index)
924 info = self._packet_infos[packet_index]
Klement Sekera14d7e902018-12-10 13:46:09 +0100925 self.assertIsNotNone(info)
Juraj Sloboda3048b632018-10-02 11:13:53 +0200926 self.assertEqual(packet_index, info.index)
927 saved_packet = info.data
928 self.assertEqual(ip.src, saved_packet[IP].dst)
929 self.assertEqual(ip.dst, saved_packet[IP].src)
930 self.assertEqual(icmp.type, 0) # echo reply
931 self.assertEqual(icmp.id, saved_packet[ICMP].id)
932 self.assertEqual(icmp.payload, saved_packet[ICMP].payload)
933 except Exception:
934 self.logger.error(ppp("Unexpected or invalid packet:", packet))
935 raise
936 for index in self._packet_infos:
Klement Sekera14d7e902018-12-10 13:46:09 +0100937 self.assertIn(index, seen,
938 "Packet with packet_index %d not received" % index)
Juraj Sloboda3048b632018-10-02 11:13:53 +0200939
940 def test_reassembly(self):
941 """ basic reassembly """
942
943 self.pg_enable_capture()
944 self.src_dst_if.add_stream(self.fragments_300)
945 self.pg_start()
946
947 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
948 self.verify_capture(packets)
949
950 # run it all again to verify correctness
951 self.pg_enable_capture()
952 self.src_dst_if.add_stream(self.fragments_300)
953 self.pg_start()
954
955 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
956 self.verify_capture(packets)
957
958
Klement Sekera75e7d132017-09-20 08:26:30 +0200959class TestFIFReassembly(VppTestCase):
960 """ Fragments in fragments reassembly """
961
962 @classmethod
963 def setUpClass(cls):
964 super(TestFIFReassembly, cls).setUpClass()
965
Klement Sekera4c533132018-02-22 11:41:12 +0100966 cls.create_pg_interfaces([0, 1])
967 cls.src_if = cls.pg0
968 cls.dst_if = cls.pg1
969 for i in cls.pg_interfaces:
970 i.admin_up()
971 i.config_ip4()
972 i.resolve_arp()
973 i.config_ip6()
974 i.resolve_ndp()
Klement Sekera75e7d132017-09-20 08:26:30 +0200975
Klement Sekera75e7d132017-09-20 08:26:30 +0200976 cls.packet_sizes = [64, 512, 1518, 9018]
977 cls.padding = " abcdefghijklmn"
978
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -0700979 @classmethod
980 def tearDownClass(cls):
981 super(TestFIFReassembly, cls).tearDownClass()
982
Klement Sekera75e7d132017-09-20 08:26:30 +0200983 def setUp(self):
984 """ Test setup - force timeout on existing reassemblies """
985 super(TestFIFReassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +0100986 self.vapi.ip_reassembly_enable_disable(
987 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
988 enable_ip6=True)
989 self.vapi.ip_reassembly_enable_disable(
990 sw_if_index=self.dst_if.sw_if_index, enable_ip4=True,
991 enable_ip6=True)
Klement Sekera75e7d132017-09-20 08:26:30 +0200992 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
993 expire_walk_interval_ms=10)
994 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
995 expire_walk_interval_ms=10, is_ip6=1)
996 self.sleep(.25)
997 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
998 expire_walk_interval_ms=10000)
999 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1000 expire_walk_interval_ms=10000, is_ip6=1)
1001
1002 def tearDown(self):
1003 self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
1004 self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +01001005 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +02001006 super(TestFIFReassembly, self).tearDown()
1007
1008 def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):
1009 """Verify captured packet stream.
1010
1011 :param list capture: Captured packet stream.
1012 """
1013 info = None
1014 seen = set()
1015 for packet in capture:
1016 try:
Klement Sekera4c533132018-02-22 11:41:12 +01001017 self.logger.debug(ppp("Got packet:", packet))
Klement Sekera75e7d132017-09-20 08:26:30 +02001018 ip = packet[ip_class]
1019 udp = packet[UDP]
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001020 payload_info = self.payload_to_info(packet[Raw])
Klement Sekera75e7d132017-09-20 08:26:30 +02001021 packet_index = payload_info.index
1022 self.assertTrue(
1023 packet_index not in dropped_packet_indexes,
1024 ppp("Packet received, but should be dropped:", packet))
1025 if packet_index in seen:
1026 raise Exception(ppp("Duplicate packet received", packet))
1027 seen.add(packet_index)
Klement Sekera4c533132018-02-22 11:41:12 +01001028 self.assertEqual(payload_info.dst, self.dst_if.sw_if_index)
Klement Sekera75e7d132017-09-20 08:26:30 +02001029 info = self._packet_infos[packet_index]
1030 self.assertTrue(info is not None)
1031 self.assertEqual(packet_index, info.index)
1032 saved_packet = info.data
1033 self.assertEqual(ip.src, saved_packet[ip_class].src)
1034 self.assertEqual(ip.dst, saved_packet[ip_class].dst)
1035 self.assertEqual(udp.payload, saved_packet[UDP].payload)
Klement Sekera4c533132018-02-22 11:41:12 +01001036 except Exception:
Klement Sekera75e7d132017-09-20 08:26:30 +02001037 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1038 raise
1039 for index in self._packet_infos:
1040 self.assertTrue(index in seen or index in dropped_packet_indexes,
1041 "Packet with packet_index %d not received" % index)
1042
1043 def test_fif4(self):
1044 """ Fragments in fragments (4o4) """
1045
1046 # TODO this should be ideally in setUpClass, but then we hit a bug
1047 # with VppIpRoute incorrectly reporting it's present when it's not
1048 # so we need to manually remove the vpp config, thus we cannot have
1049 # it shared for multiple test cases
1050 self.tun_ip4 = "1.1.1.2"
1051
Klement Sekera4c533132018-02-22 11:41:12 +01001052 self.gre4 = VppGreInterface(self, self.src_if.local_ip4, self.tun_ip4)
Klement Sekera75e7d132017-09-20 08:26:30 +02001053 self.gre4.add_vpp_config()
1054 self.gre4.admin_up()
1055 self.gre4.config_ip4()
1056
Klement Sekera4c533132018-02-22 11:41:12 +01001057 self.vapi.ip_reassembly_enable_disable(
1058 sw_if_index=self.gre4.sw_if_index, enable_ip4=True)
1059
Klement Sekera75e7d132017-09-20 08:26:30 +02001060 self.route4 = VppIpRoute(self, self.tun_ip4, 32,
Klement Sekera4c533132018-02-22 11:41:12 +01001061 [VppRoutePath(self.src_if.remote_ip4,
1062 self.src_if.sw_if_index)])
Klement Sekera75e7d132017-09-20 08:26:30 +02001063 self.route4.add_vpp_config()
1064
1065 self.reset_packet_infos()
1066 for i in range(test_packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01001067 info = self.create_packet_info(self.src_if, self.dst_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02001068 payload = self.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +01001069 # Ethernet header here is only for size calculation, thus it
1070 # doesn't matter how it's initialized. This is to ensure that
1071 # reassembled packet is not > 9000 bytes, so that it's not dropped
1072 p = (Ether() /
1073 IP(id=i, src=self.src_if.remote_ip4,
1074 dst=self.dst_if.remote_ip4) /
1075 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001076 Raw(payload))
1077 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1078 self.extend_packet(p, size, self.padding)
Klement Sekera4c533132018-02-22 11:41:12 +01001079 info.data = p[IP] # use only IP part, without ethernet header
Klement Sekera75e7d132017-09-20 08:26:30 +02001080
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -08001081 fragments = [x for _, p in six.iteritems(self._packet_infos)
Klement Sekera75e7d132017-09-20 08:26:30 +02001082 for x in fragment_rfc791(p.data, 400)]
1083
1084 encapped_fragments = \
Klement Sekera4c533132018-02-22 11:41:12 +01001085 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1086 IP(src=self.tun_ip4, dst=self.src_if.local_ip4) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001087 GRE() /
1088 p
1089 for p in fragments]
1090
1091 fragmented_encapped_fragments = \
1092 [x for p in encapped_fragments
1093 for x in fragment_rfc791(p, 200)]
1094
Klement Sekera4c533132018-02-22 11:41:12 +01001095 self.src_if.add_stream(fragmented_encapped_fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001096
1097 self.pg_enable_capture(self.pg_interfaces)
1098 self.pg_start()
1099
Klement Sekera4c533132018-02-22 11:41:12 +01001100 self.src_if.assert_nothing_captured()
1101 packets = self.dst_if.get_capture(len(self._packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +02001102 self.verify_capture(packets, IP)
1103
1104 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
1105 # so that it's query_vpp_config() works as it should
1106 self.gre4.remove_vpp_config()
Klement Sekera4c533132018-02-22 11:41:12 +01001107 self.logger.debug(self.vapi.ppcli("show interface"))
Klement Sekera75e7d132017-09-20 08:26:30 +02001108
1109 def test_fif6(self):
1110 """ Fragments in fragments (6o6) """
1111 # TODO this should be ideally in setUpClass, but then we hit a bug
1112 # with VppIpRoute incorrectly reporting it's present when it's not
1113 # so we need to manually remove the vpp config, thus we cannot have
1114 # it shared for multiple test cases
1115 self.tun_ip6 = "1002::1"
1116
Klement Sekera4c533132018-02-22 11:41:12 +01001117 self.gre6 = VppGre6Interface(self, self.src_if.local_ip6, self.tun_ip6)
Klement Sekera75e7d132017-09-20 08:26:30 +02001118 self.gre6.add_vpp_config()
1119 self.gre6.admin_up()
1120 self.gre6.config_ip6()
1121
Klement Sekera4c533132018-02-22 11:41:12 +01001122 self.vapi.ip_reassembly_enable_disable(
1123 sw_if_index=self.gre6.sw_if_index, enable_ip6=True)
1124
Klement Sekera75e7d132017-09-20 08:26:30 +02001125 self.route6 = VppIpRoute(self, self.tun_ip6, 128,
Klement Sekera4c533132018-02-22 11:41:12 +01001126 [VppRoutePath(self.src_if.remote_ip6,
1127 self.src_if.sw_if_index,
Klement Sekera75e7d132017-09-20 08:26:30 +02001128 proto=DpoProto.DPO_PROTO_IP6)],
1129 is_ip6=1)
1130 self.route6.add_vpp_config()
1131
1132 self.reset_packet_infos()
1133 for i in range(test_packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01001134 info = self.create_packet_info(self.src_if, self.dst_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02001135 payload = self.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +01001136 # Ethernet header here is only for size calculation, thus it
1137 # doesn't matter how it's initialized. This is to ensure that
1138 # reassembled packet is not > 9000 bytes, so that it's not dropped
1139 p = (Ether() /
1140 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1141 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001142 Raw(payload))
1143 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1144 self.extend_packet(p, size, self.padding)
Klement Sekera4c533132018-02-22 11:41:12 +01001145 info.data = p[IPv6] # use only IPv6 part, without ethernet header
Klement Sekera75e7d132017-09-20 08:26:30 +02001146
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -08001147 fragments = [x for _, i in six.iteritems(self._packet_infos)
Klement Sekera75e7d132017-09-20 08:26:30 +02001148 for x in fragment_rfc8200(
1149 i.data, i.index, 400)]
1150
1151 encapped_fragments = \
Klement Sekera4c533132018-02-22 11:41:12 +01001152 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1153 IPv6(src=self.tun_ip6, dst=self.src_if.local_ip6) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001154 GRE() /
1155 p
1156 for p in fragments]
1157
1158 fragmented_encapped_fragments = \
1159 [x for p in encapped_fragments for x in (
1160 fragment_rfc8200(
1161 p,
1162 2 * len(self._packet_infos) + p[IPv6ExtHdrFragment].id,
1163 200)
1164 if IPv6ExtHdrFragment in p else [p]
1165 )
1166 ]
1167
Klement Sekera4c533132018-02-22 11:41:12 +01001168 self.src_if.add_stream(fragmented_encapped_fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001169
1170 self.pg_enable_capture(self.pg_interfaces)
1171 self.pg_start()
1172
Klement Sekera4c533132018-02-22 11:41:12 +01001173 self.src_if.assert_nothing_captured()
1174 packets = self.dst_if.get_capture(len(self._packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +02001175 self.verify_capture(packets, IPv6)
1176
1177 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
1178 # so that it's query_vpp_config() works as it should
Klement Sekera3ecc2212018-03-27 10:34:43 +02001179 self.gre6.remove_vpp_config()
Klement Sekera75e7d132017-09-20 08:26:30 +02001180
1181
1182if __name__ == '__main__':
1183 unittest.main(testRunner=VppTestRunner)