blob: f3d983dc3033c32b7c86a4b76e56aeb69d049aa9 [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
222 def setUp(self):
223 """ Test setup - force timeout on existing reassemblies """
224 super(TestIPv4Reassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +0100225 self.vapi.ip_reassembly_enable_disable(
226 sw_if_index=self.src_if.sw_if_index, enable_ip4=True)
Klement Sekera75e7d132017-09-20 08:26:30 +0200227 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
228 expire_walk_interval_ms=10)
229 self.sleep(.25)
230 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
231 expire_walk_interval_ms=10000)
232
233 def tearDown(self):
234 super(TestIPv4Reassembly, self).tearDown()
235 self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +0100236 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +0200237
238 @classmethod
239 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
Klement Sekerad0f70a32018-12-14 17:24:13 +0100240 """Create input packet stream
Klement Sekera75e7d132017-09-20 08:26:30 +0200241
242 :param list packet_sizes: Required packet sizes.
243 """
244 for i in range(0, packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +0100245 info = cls.create_packet_info(cls.src_if, cls.src_if)
Klement Sekera75e7d132017-09-20 08:26:30 +0200246 payload = cls.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +0100247 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
248 IP(id=info.index, src=cls.src_if.remote_ip4,
249 dst=cls.dst_if.remote_ip4) /
250 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200251 Raw(payload))
252 size = packet_sizes[(i // 2) % len(packet_sizes)]
253 cls.extend_packet(p, size, cls.padding)
254 info.data = p
255
256 @classmethod
257 def create_fragments(cls):
258 infos = cls._packet_infos
259 cls.pkt_infos = []
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -0800260 for index, info in six.iteritems(infos):
Klement Sekera75e7d132017-09-20 08:26:30 +0200261 p = info.data
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700262 # cls.logger.debug(ppp("Packet:",
263 # p.__class__(scapy.compat.raw(p))))
Klement Sekera75e7d132017-09-20 08:26:30 +0200264 fragments_400 = fragment_rfc791(p, 400)
265 fragments_300 = fragment_rfc791(p, 300)
266 fragments_200 = [
267 x for f in fragments_400 for x in fragment_rfc791(f, 200)]
268 cls.pkt_infos.append(
269 (index, fragments_400, fragments_300, fragments_200))
270 cls.fragments_400 = [
271 x for (_, frags, _, _) in cls.pkt_infos for x in frags]
272 cls.fragments_300 = [
273 x for (_, _, frags, _) in cls.pkt_infos for x in frags]
274 cls.fragments_200 = [
275 x for (_, _, _, frags) in cls.pkt_infos for x in frags]
276 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
277 "%s 300-byte fragments and %s 200-byte fragments" %
278 (len(infos), len(cls.fragments_400),
279 len(cls.fragments_300), len(cls.fragments_200)))
280
Paul Vinciguerra69555952019-03-01 08:46:29 -0800281 @parameterized.expand([(IP, None)])
282 def test_reassembly(self, family, stream):
Klement Sekera75e7d132017-09-20 08:26:30 +0200283 """ basic reassembly """
Paul Vinciguerra69555952019-03-01 08:46:29 -0800284 stream = self.__class__.fragments_200
285 super(TestIPv4Reassembly, self).test_reassembly(family, stream)
Klement Sekera75e7d132017-09-20 08:26:30 +0200286
Paul Vinciguerra69555952019-03-01 08:46:29 -0800287 @parameterized.expand([(IP, None)])
288 def test_reversed(self, family, stream):
Klement Sekera75e7d132017-09-20 08:26:30 +0200289 """ reverse order reassembly """
Paul Vinciguerra69555952019-03-01 08:46:29 -0800290 stream = self.__class__.fragments_200
291 super(TestIPv4Reassembly, self).test_reversed(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_random(self, family, stream):
295 stream = self.__class__.fragments_200
296 super(TestIPv4Reassembly, self).test_random(family, stream)
Klement Sekera75e7d132017-09-20 08:26:30 +0200297
Klement Sekera14d7e902018-12-10 13:46:09 +0100298 def test_5737(self):
299 """ fragment length + ip header size > 65535 """
Klement Sekera4ee633e2018-12-14 12:00:44 +0100300 self.vapi.cli("clear errors")
Klement Sekera14d7e902018-12-10 13:46:09 +0100301 raw = ('E\x00\x00\x88,\xf8\x1f\xfe@\x01\x98\x00\xc0\xa8\n-\xc0\xa8\n'
302 '\x01\x08\x00\xf0J\xed\xcb\xf1\xf5Test-group: IPv4.IPv4.ipv4-'
303 'message.Ethernet-Payload.IPv4-Packet.IPv4-Header.Fragment-Of'
304 'fset; Test-case: 5737')
305
306 malformed_packet = (Ether(dst=self.src_if.local_mac,
307 src=self.src_if.remote_mac) /
308 IP(raw))
309 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
310 IP(id=1000, src=self.src_if.remote_ip4,
311 dst=self.dst_if.remote_ip4) /
312 UDP(sport=1234, dport=5678) /
313 Raw("X" * 1000))
314 valid_fragments = fragment_rfc791(p, 400)
315
316 self.pg_enable_capture()
317 self.src_if.add_stream([malformed_packet] + valid_fragments)
318 self.pg_start()
319
320 self.dst_if.get_capture(1)
Klement Sekera4ee633e2018-12-14 12:00:44 +0100321 self.assert_packet_counter_equal("ip4-reassembly-feature", 1)
322 # TODO remove above, uncomment below once clearing of counters
323 # is supported
324 # self.assert_packet_counter_equal(
325 # "/err/ip4-reassembly-feature/malformed packets", 1)
Klement Sekera14d7e902018-12-10 13:46:09 +0100326
Klement Sekera400f6d82018-12-13 14:35:48 +0100327 def test_44924(self):
328 """ compress tiny fragments """
329 packets = [(Ether(dst=self.src_if.local_mac,
330 src=self.src_if.remote_mac) /
331 IP(id=24339, flags="MF", frag=0, ttl=64,
332 src=self.src_if.remote_ip4,
333 dst=self.dst_if.remote_ip4) /
334 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
335 Raw(load='Test-group: IPv4')),
336 (Ether(dst=self.src_if.local_mac,
337 src=self.src_if.remote_mac) /
338 IP(id=24339, flags="MF", frag=3, ttl=64,
339 src=self.src_if.remote_ip4,
340 dst=self.dst_if.remote_ip4) /
341 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
342 Raw(load='.IPv4.Fragmentation.vali')),
343 (Ether(dst=self.src_if.local_mac,
344 src=self.src_if.remote_mac) /
345 IP(id=24339, frag=6, ttl=64,
346 src=self.src_if.remote_ip4,
347 dst=self.dst_if.remote_ip4) /
348 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
349 Raw(load='d; Test-case: 44924'))
350 ]
351
352 self.pg_enable_capture()
353 self.src_if.add_stream(packets)
354 self.pg_start()
355
356 self.dst_if.get_capture(1)
357
Klement Sekera4ee633e2018-12-14 12:00:44 +0100358 def test_frag_1(self):
359 """ fragment of size 1 """
360 self.vapi.cli("clear errors")
361 malformed_packets = [(Ether(dst=self.src_if.local_mac,
362 src=self.src_if.remote_mac) /
363 IP(id=7, len=21, flags="MF", frag=0, ttl=64,
364 src=self.src_if.remote_ip4,
365 dst=self.dst_if.remote_ip4) /
366 ICMP(type="echo-request")),
367 (Ether(dst=self.src_if.local_mac,
368 src=self.src_if.remote_mac) /
369 IP(id=7, len=21, frag=1, ttl=64,
370 src=self.src_if.remote_ip4,
371 dst=self.dst_if.remote_ip4) /
372 Raw(load='\x08')),
373 ]
374
375 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
376 IP(id=1000, src=self.src_if.remote_ip4,
377 dst=self.dst_if.remote_ip4) /
378 UDP(sport=1234, dport=5678) /
379 Raw("X" * 1000))
380 valid_fragments = fragment_rfc791(p, 400)
381
382 self.pg_enable_capture()
383 self.src_if.add_stream(malformed_packets + valid_fragments)
384 self.pg_start()
385
386 self.dst_if.get_capture(1)
387
388 self.assert_packet_counter_equal("ip4-reassembly-feature", 1)
389 # TODO remove above, uncomment below once clearing of counters
390 # is supported
391 # self.assert_packet_counter_equal(
392 # "/err/ip4-reassembly-feature/malformed packets", 1)
393
Paul Vinciguerra69555952019-03-01 08:46:29 -0800394 @parameterized.expand([(IP, None)])
395 def test_duplicates(self, family, stream):
Klement Sekera75e7d132017-09-20 08:26:30 +0200396 """ duplicate fragments """
Klement Sekera75e7d132017-09-20 08:26:30 +0200397 fragments = [
Paul Vinciguerra69555952019-03-01 08:46:29 -0800398 # IPv4 uses 4 fields in pkt_infos, IPv6 uses 3.
Klement Sekera75e7d132017-09-20 08:26:30 +0200399 x for (_, frags, _, _) in self.pkt_infos
400 for x in frags
401 for _ in range(0, min(2, len(frags)))
402 ]
Paul Vinciguerra69555952019-03-01 08:46:29 -0800403 super(TestIPv4Reassembly, self).test_duplicates(family, fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200404
405 def test_overlap1(self):
406 """ overlapping fragments case #1 """
407
408 fragments = []
409 for _, _, frags_300, frags_200 in self.pkt_infos:
410 if len(frags_300) == 1:
411 fragments.extend(frags_300)
412 else:
413 for i, j in zip(frags_200, frags_300):
414 fragments.extend(i)
415 fragments.extend(j)
416
417 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100418 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200419 self.pg_start()
420
Klement Sekera4c533132018-02-22 11:41:12 +0100421 packets = self.dst_if.get_capture(len(self.pkt_infos))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800422 self.verify_capture(IP, packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100423 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200424
425 # run it all to verify correctness
426 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100427 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200428 self.pg_start()
429
Klement Sekera4c533132018-02-22 11:41:12 +0100430 packets = self.dst_if.get_capture(len(self.pkt_infos))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800431 self.verify_capture(IP, packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100432 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200433
434 def test_overlap2(self):
435 """ overlapping fragments case #2 """
436
437 fragments = []
438 for _, _, frags_300, frags_200 in self.pkt_infos:
439 if len(frags_300) == 1:
440 fragments.extend(frags_300)
441 else:
442 # care must be taken here so that there are no fragments
443 # received by vpp after reassembly is finished, otherwise
444 # new reassemblies will be started and packet generator will
445 # freak out when it detects unfreed buffers
446 zipped = zip(frags_300, frags_200)
447 for i, j in zipped[:-1]:
448 fragments.extend(i)
449 fragments.extend(j)
450 fragments.append(zipped[-1][0])
451
452 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100453 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200454 self.pg_start()
455
Klement Sekera4c533132018-02-22 11:41:12 +0100456 packets = self.dst_if.get_capture(len(self.pkt_infos))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800457 self.verify_capture(IP, packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100458 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200459
460 # run it all to verify correctness
461 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100462 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200463 self.pg_start()
464
Klement Sekera4c533132018-02-22 11:41:12 +0100465 packets = self.dst_if.get_capture(len(self.pkt_infos))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800466 self.verify_capture(IP, packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100467 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200468
Paul Vinciguerra69555952019-03-01 08:46:29 -0800469 @parameterized.expand([(IP, None, None)])
470 def test_timeout_inline(self, family, stream, dropped_packet_indexes):
Klement Sekera75e7d132017-09-20 08:26:30 +0200471 """ timeout (inline) """
Paul Vinciguerra69555952019-03-01 08:46:29 -0800472 stream = self.fragments_400
Klement Sekera75e7d132017-09-20 08:26:30 +0200473
474 dropped_packet_indexes = set(
475 index for (index, frags, _, _) in self.pkt_infos if len(frags) > 1
476 )
Paul Vinciguerra69555952019-03-01 08:46:29 -0800477 super(TestIPv4Reassembly, self).test_timeout_inline(
478 family, stream, dropped_packet_indexes)
Klement Sekera75e7d132017-09-20 08:26:30 +0200479
Klement Sekera4c533132018-02-22 11:41:12 +0100480 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200481
482 def test_timeout_cleanup(self):
483 """ timeout (cleanup) """
484
485 # whole packets + fragmented packets sans last fragment
486 fragments = [
487 x for (_, frags_400, _, _) in self.pkt_infos
488 for x in frags_400[:-1 if len(frags_400) > 1 else None]
489 ]
490
491 # last fragments for fragmented packets
492 fragments2 = [frags_400[-1]
493 for (_, frags_400, _, _) in self.pkt_infos
494 if len(frags_400) > 1]
495
496 dropped_packet_indexes = set(
497 index for (index, frags_400, _, _) in self.pkt_infos
498 if len(frags_400) > 1)
499
500 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
501 expire_walk_interval_ms=50)
502
503 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100504 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200505 self.pg_start()
506
507 self.sleep(.25, "wait before sending rest of fragments")
508
Klement Sekera4c533132018-02-22 11:41:12 +0100509 self.src_if.add_stream(fragments2)
Klement Sekera75e7d132017-09-20 08:26:30 +0200510 self.pg_start()
Klement Sekera75e7d132017-09-20 08:26:30 +0200511
Klement Sekera4c533132018-02-22 11:41:12 +0100512 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200513 len(self.pkt_infos) - len(dropped_packet_indexes))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800514 self.verify_capture(IP, packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100515 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200516
Paul Vinciguerra69555952019-03-01 08:46:29 -0800517 @parameterized.expand([(IP, None, None)])
518 def test_disabled(self, family, stream, dropped_packet_indexes):
Klement Sekera75e7d132017-09-20 08:26:30 +0200519 """ reassembly disabled """
520
Paul Vinciguerra69555952019-03-01 08:46:29 -0800521 stream = self.__class__.fragments_400
Klement Sekera75e7d132017-09-20 08:26:30 +0200522 dropped_packet_indexes = set(
523 index for (index, frags_400, _, _) in self.pkt_infos
524 if len(frags_400) > 1)
Paul Vinciguerra69555952019-03-01 08:46:29 -0800525 super(TestIPv4Reassembly, self).test_disabled(
526 family, stream, dropped_packet_indexes)
Klement Sekera75e7d132017-09-20 08:26:30 +0200527
528
Paul Vinciguerra69555952019-03-01 08:46:29 -0800529class TestIPv6Reassembly(TestIPReassemblyMixin, VppTestCase):
Klement Sekera75e7d132017-09-20 08:26:30 +0200530 """ IPv6 Reassembly """
531
532 @classmethod
533 def setUpClass(cls):
534 super(TestIPv6Reassembly, cls).setUpClass()
535
Klement Sekera4c533132018-02-22 11:41:12 +0100536 cls.create_pg_interfaces([0, 1])
537 cls.src_if = cls.pg0
538 cls.dst_if = cls.pg1
Klement Sekera75e7d132017-09-20 08:26:30 +0200539
540 # setup all interfaces
541 for i in cls.pg_interfaces:
542 i.admin_up()
543 i.config_ip6()
544 i.resolve_ndp()
545
Klement Sekera75e7d132017-09-20 08:26:30 +0200546 # packet sizes
547 cls.packet_sizes = [64, 512, 1518, 9018]
548 cls.padding = " abcdefghijklmn"
549 cls.create_stream(cls.packet_sizes)
550 cls.create_fragments()
551
552 def setUp(self):
553 """ Test setup - force timeout on existing reassemblies """
554 super(TestIPv6Reassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +0100555 self.vapi.ip_reassembly_enable_disable(
556 sw_if_index=self.src_if.sw_if_index, enable_ip6=True)
Klement Sekera75e7d132017-09-20 08:26:30 +0200557 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
558 expire_walk_interval_ms=10, is_ip6=1)
559 self.sleep(.25)
560 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
561 expire_walk_interval_ms=10000, is_ip6=1)
Klement Sekera4c533132018-02-22 11:41:12 +0100562 self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +0100563 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +0200564
565 def tearDown(self):
566 super(TestIPv6Reassembly, self).tearDown()
567 self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +0100568 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +0200569
570 @classmethod
571 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
572 """Create input packet stream for defined interface.
573
574 :param list packet_sizes: Required packet sizes.
575 """
576 for i in range(0, packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +0100577 info = cls.create_packet_info(cls.src_if, cls.src_if)
Klement Sekera75e7d132017-09-20 08:26:30 +0200578 payload = cls.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +0100579 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
580 IPv6(src=cls.src_if.remote_ip6,
581 dst=cls.dst_if.remote_ip6) /
582 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200583 Raw(payload))
584 size = packet_sizes[(i // 2) % len(packet_sizes)]
585 cls.extend_packet(p, size, cls.padding)
586 info.data = p
587
588 @classmethod
589 def create_fragments(cls):
590 infos = cls._packet_infos
591 cls.pkt_infos = []
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -0800592 for index, info in six.iteritems(infos):
Klement Sekera75e7d132017-09-20 08:26:30 +0200593 p = info.data
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700594 # cls.logger.debug(ppp("Packet:",
595 # p.__class__(scapy.compat.raw(p))))
Klement Sekera75e7d132017-09-20 08:26:30 +0200596 fragments_400 = fragment_rfc8200(p, info.index, 400)
597 fragments_300 = fragment_rfc8200(p, info.index, 300)
598 cls.pkt_infos.append((index, fragments_400, fragments_300))
599 cls.fragments_400 = [
600 x for _, frags, _ in cls.pkt_infos for x in frags]
601 cls.fragments_300 = [
602 x for _, _, frags in cls.pkt_infos for x in frags]
603 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
604 "and %s 300-byte fragments" %
605 (len(infos), len(cls.fragments_400),
606 len(cls.fragments_300)))
607
Paul Vinciguerra69555952019-03-01 08:46:29 -0800608 @parameterized.expand([(IPv6, None)])
609 def test_reassembly(self, family, stream):
Klement Sekera75e7d132017-09-20 08:26:30 +0200610 """ basic reassembly """
Paul Vinciguerra69555952019-03-01 08:46:29 -0800611 stream = self.__class__.fragments_400
612 super(TestIPv6Reassembly, self).test_reassembly(family, stream)
Klement Sekera75e7d132017-09-20 08:26:30 +0200613
Paul Vinciguerra69555952019-03-01 08:46:29 -0800614 @parameterized.expand([(IPv6, None)])
615 def test_reversed(self, family, stream):
Klement Sekera75e7d132017-09-20 08:26:30 +0200616 """ reverse order reassembly """
Paul Vinciguerra69555952019-03-01 08:46:29 -0800617 stream = self.__class__.fragments_400
618 super(TestIPv6Reassembly, self).test_reversed(family, stream)
Klement Sekera75e7d132017-09-20 08:26:30 +0200619
Paul Vinciguerra69555952019-03-01 08:46:29 -0800620 @parameterized.expand([(IPv6, None)])
621 def test_random(self, family, stream):
Klement Sekera75e7d132017-09-20 08:26:30 +0200622 """ random order reassembly """
Paul Vinciguerra69555952019-03-01 08:46:29 -0800623 stream = self.__class__.fragments_400
624 super(TestIPv6Reassembly, self).test_random(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_duplicates(self, family, stream):
Klement Sekera75e7d132017-09-20 08:26:30 +0200628 """ duplicate fragments """
629
630 fragments = [
Paul Vinciguerra69555952019-03-01 08:46:29 -0800631 # IPv4 uses 4 fields in pkt_infos, IPv6 uses 3.
Klement Sekera75e7d132017-09-20 08:26:30 +0200632 x for (_, frags, _) in self.pkt_infos
633 for x in frags
634 for _ in range(0, min(2, len(frags)))
635 ]
Paul Vinciguerra69555952019-03-01 08:46:29 -0800636 super(TestIPv6Reassembly, self).test_duplicates(family, fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200637
638 def test_overlap1(self):
Paul Vinciguerra69555952019-03-01 08:46:29 -0800639 """ overlapping fragments case #1 (differs from IP test case)"""
Klement Sekera75e7d132017-09-20 08:26:30 +0200640
641 fragments = []
642 for _, frags_400, frags_300 in self.pkt_infos:
643 if len(frags_300) == 1:
644 fragments.extend(frags_400)
645 else:
646 for i, j in zip(frags_300, frags_400):
647 fragments.extend(i)
648 fragments.extend(j)
649
650 dropped_packet_indexes = set(
651 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
652 )
653
654 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100655 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200656 self.pg_start()
657
Klement Sekera4c533132018-02-22 11:41:12 +0100658 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200659 len(self.pkt_infos) - len(dropped_packet_indexes))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800660 self.verify_capture(IPv6, packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100661 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200662
663 def test_overlap2(self):
Paul Vinciguerra69555952019-03-01 08:46:29 -0800664 """ overlapping fragments case #2 (differs from IP test case)"""
Klement Sekera75e7d132017-09-20 08:26:30 +0200665
666 fragments = []
Klement Sekera4c533132018-02-22 11:41:12 +0100667 for _, frags_400, frags_300 in self.pkt_infos:
Klement Sekera75e7d132017-09-20 08:26:30 +0200668 if len(frags_400) == 1:
669 fragments.extend(frags_400)
670 else:
671 # care must be taken here so that there are no fragments
672 # received by vpp after reassembly is finished, otherwise
673 # new reassemblies will be started and packet generator will
674 # freak out when it detects unfreed buffers
Klement Sekera4c533132018-02-22 11:41:12 +0100675 zipped = zip(frags_400, frags_300)
Klement Sekera75e7d132017-09-20 08:26:30 +0200676 for i, j in zipped[:-1]:
677 fragments.extend(i)
678 fragments.extend(j)
679 fragments.append(zipped[-1][0])
680
681 dropped_packet_indexes = set(
682 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
683 )
684
685 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100686 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200687 self.pg_start()
688
Klement Sekera4c533132018-02-22 11:41:12 +0100689 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200690 len(self.pkt_infos) - len(dropped_packet_indexes))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800691 self.verify_capture(IPv6, packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100692 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200693
Paul Vinciguerra69555952019-03-01 08:46:29 -0800694 @parameterized.expand([(IPv6, None, None)])
695 def test_timeout_inline(self, family, stream, dropped_packets_index):
Klement Sekera75e7d132017-09-20 08:26:30 +0200696 """ timeout (inline) """
Paul Vinciguerra69555952019-03-01 08:46:29 -0800697 stream = self.__class__.fragments_400
Klement Sekera75e7d132017-09-20 08:26:30 +0200698
699 dropped_packet_indexes = set(
700 index for (index, frags, _) in self.pkt_infos if len(frags) > 1
701 )
Paul Vinciguerra69555952019-03-01 08:46:29 -0800702 super(TestIPv6Reassembly, self).test_timeout_inline(
703 family, stream, dropped_packet_indexes)
Klement Sekera75e7d132017-09-20 08:26:30 +0200704
Klement Sekera4c533132018-02-22 11:41:12 +0100705 pkts = self.src_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200706 expected_count=len(dropped_packet_indexes))
707 for icmp in pkts:
708 self.assertIn(ICMPv6TimeExceeded, icmp)
709 self.assertIn(IPv6ExtHdrFragment, icmp)
710 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
711 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
712
713 def test_timeout_cleanup(self):
714 """ timeout (cleanup) """
715
716 # whole packets + fragmented packets sans last fragment
717 fragments = [
718 x for (_, frags_400, _) in self.pkt_infos
719 for x in frags_400[:-1 if len(frags_400) > 1 else None]
720 ]
721
722 # last fragments for fragmented packets
723 fragments2 = [frags_400[-1]
724 for (_, frags_400, _) in self.pkt_infos
725 if len(frags_400) > 1]
726
727 dropped_packet_indexes = set(
728 index for (index, frags_400, _) in self.pkt_infos
729 if len(frags_400) > 1)
730
731 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
732 expire_walk_interval_ms=50)
733
734 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
735 expire_walk_interval_ms=50, is_ip6=1)
736
737 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100738 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200739 self.pg_start()
740
741 self.sleep(.25, "wait before sending rest of fragments")
742
Klement Sekera4c533132018-02-22 11:41:12 +0100743 self.src_if.add_stream(fragments2)
Klement Sekera75e7d132017-09-20 08:26:30 +0200744 self.pg_start()
Klement Sekera75e7d132017-09-20 08:26:30 +0200745
Klement Sekera4c533132018-02-22 11:41:12 +0100746 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200747 len(self.pkt_infos) - len(dropped_packet_indexes))
Paul Vinciguerra69555952019-03-01 08:46:29 -0800748 self.verify_capture(IPv6, packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100749 pkts = self.src_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200750 expected_count=len(dropped_packet_indexes))
751 for icmp in pkts:
752 self.assertIn(ICMPv6TimeExceeded, icmp)
753 self.assertIn(IPv6ExtHdrFragment, icmp)
754 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
755 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
756
Paul Vinciguerra69555952019-03-01 08:46:29 -0800757 @parameterized.expand([(IPv6, None, None)])
758 def test_disabled(self, family, stream, dropped_packet_indexes):
Klement Sekera75e7d132017-09-20 08:26:30 +0200759 """ reassembly disabled """
760
Paul Vinciguerra69555952019-03-01 08:46:29 -0800761 stream = self.__class__.fragments_400
Klement Sekera75e7d132017-09-20 08:26:30 +0200762 dropped_packet_indexes = set(
763 index for (index, frags_400, _) in self.pkt_infos
764 if len(frags_400) > 1)
Paul Vinciguerra69555952019-03-01 08:46:29 -0800765 super(TestIPv6Reassembly, self).test_disabled(
766 family, stream, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100767 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200768
769 def test_missing_upper(self):
770 """ missing upper layer """
Klement Sekera4c533132018-02-22 11:41:12 +0100771 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
772 IPv6(src=self.src_if.remote_ip6,
773 dst=self.src_if.local_ip6) /
774 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200775 Raw())
776 self.extend_packet(p, 1000, self.padding)
777 fragments = fragment_rfc8200(p, 1, 500)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700778 bad_fragment = p.__class__(scapy.compat.raw(fragments[1]))
Klement Sekera75e7d132017-09-20 08:26:30 +0200779 bad_fragment[IPv6ExtHdrFragment].nh = 59
780 bad_fragment[IPv6ExtHdrFragment].offset = 0
781 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100782 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +0200783 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +0100784 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +0200785 icmp = pkts[0]
786 self.assertIn(ICMPv6ParamProblem, icmp)
787 self.assert_equal(icmp[ICMPv6ParamProblem].code, 3, "ICMP code")
788
789 def test_invalid_frag_size(self):
790 """ fragment size not a multiple of 8 """
Klement Sekera4c533132018-02-22 11:41:12 +0100791 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
792 IPv6(src=self.src_if.remote_ip6,
793 dst=self.src_if.local_ip6) /
794 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200795 Raw())
796 self.extend_packet(p, 1000, self.padding)
797 fragments = fragment_rfc8200(p, 1, 500)
798 bad_fragment = fragments[0]
799 self.extend_packet(bad_fragment, len(bad_fragment) + 5)
800 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100801 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +0200802 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +0100803 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +0200804 icmp = pkts[0]
805 self.assertIn(ICMPv6ParamProblem, icmp)
806 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
807
808 def test_invalid_packet_size(self):
809 """ total packet size > 65535 """
Klement Sekera4c533132018-02-22 11:41:12 +0100810 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
811 IPv6(src=self.src_if.remote_ip6,
812 dst=self.src_if.local_ip6) /
813 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200814 Raw())
815 self.extend_packet(p, 1000, self.padding)
816 fragments = fragment_rfc8200(p, 1, 500)
817 bad_fragment = fragments[1]
818 bad_fragment[IPv6ExtHdrFragment].offset = 65500
819 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100820 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +0200821 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +0100822 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +0200823 icmp = pkts[0]
824 self.assertIn(ICMPv6ParamProblem, icmp)
825 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
826
827
Juraj Sloboda3048b632018-10-02 11:13:53 +0200828class TestIPv4ReassemblyLocalNode(VppTestCase):
829 """ IPv4 Reassembly for packets coming to ip4-local node """
830
831 @classmethod
832 def setUpClass(cls):
833 super(TestIPv4ReassemblyLocalNode, cls).setUpClass()
834
835 cls.create_pg_interfaces([0])
836 cls.src_dst_if = cls.pg0
837
838 # setup all interfaces
839 for i in cls.pg_interfaces:
840 i.admin_up()
841 i.config_ip4()
842 i.resolve_arp()
843
844 cls.padding = " abcdefghijklmn"
845 cls.create_stream()
846 cls.create_fragments()
847
848 def setUp(self):
849 """ Test setup - force timeout on existing reassemblies """
850 super(TestIPv4ReassemblyLocalNode, self).setUp()
851 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
852 expire_walk_interval_ms=10)
853 self.sleep(.25)
854 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
855 expire_walk_interval_ms=10000)
856
857 def tearDown(self):
858 super(TestIPv4ReassemblyLocalNode, self).tearDown()
859 self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +0100860 self.logger.debug(self.vapi.ppcli("show buffers"))
Juraj Sloboda3048b632018-10-02 11:13:53 +0200861
862 @classmethod
863 def create_stream(cls, packet_count=test_packet_count):
864 """Create input packet stream for defined interface.
865
866 :param list packet_sizes: Required packet sizes.
867 """
868 for i in range(0, packet_count):
869 info = cls.create_packet_info(cls.src_dst_if, cls.src_dst_if)
870 payload = cls.info_to_payload(info)
871 p = (Ether(dst=cls.src_dst_if.local_mac,
872 src=cls.src_dst_if.remote_mac) /
873 IP(id=info.index, src=cls.src_dst_if.remote_ip4,
874 dst=cls.src_dst_if.local_ip4) /
875 ICMP(type='echo-request', id=1234) /
876 Raw(payload))
877 cls.extend_packet(p, 1518, cls.padding)
878 info.data = p
879
880 @classmethod
881 def create_fragments(cls):
882 infos = cls._packet_infos
883 cls.pkt_infos = []
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -0800884 for index, info in six.iteritems(infos):
Juraj Sloboda3048b632018-10-02 11:13:53 +0200885 p = info.data
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700886 # cls.logger.debug(ppp("Packet:",
887 # p.__class__(scapy.compat.raw(p))))
Juraj Sloboda3048b632018-10-02 11:13:53 +0200888 fragments_300 = fragment_rfc791(p, 300)
889 cls.pkt_infos.append((index, fragments_300))
890 cls.fragments_300 = [x for (_, frags) in cls.pkt_infos for x in frags]
891 cls.logger.debug("Fragmented %s packets into %s 300-byte fragments" %
892 (len(infos), len(cls.fragments_300)))
893
894 def verify_capture(self, capture):
895 """Verify captured packet stream.
896
897 :param list capture: Captured packet stream.
898 """
899 info = None
900 seen = set()
901 for packet in capture:
902 try:
903 self.logger.debug(ppp("Got packet:", packet))
904 ip = packet[IP]
905 icmp = packet[ICMP]
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800906 payload_info = self.payload_to_info(packet[Raw])
Juraj Sloboda3048b632018-10-02 11:13:53 +0200907 packet_index = payload_info.index
908 if packet_index in seen:
909 raise Exception(ppp("Duplicate packet received", packet))
910 seen.add(packet_index)
911 self.assertEqual(payload_info.dst, self.src_dst_if.sw_if_index)
912 info = self._packet_infos[packet_index]
Klement Sekera14d7e902018-12-10 13:46:09 +0100913 self.assertIsNotNone(info)
Juraj Sloboda3048b632018-10-02 11:13:53 +0200914 self.assertEqual(packet_index, info.index)
915 saved_packet = info.data
916 self.assertEqual(ip.src, saved_packet[IP].dst)
917 self.assertEqual(ip.dst, saved_packet[IP].src)
918 self.assertEqual(icmp.type, 0) # echo reply
919 self.assertEqual(icmp.id, saved_packet[ICMP].id)
920 self.assertEqual(icmp.payload, saved_packet[ICMP].payload)
921 except Exception:
922 self.logger.error(ppp("Unexpected or invalid packet:", packet))
923 raise
924 for index in self._packet_infos:
Klement Sekera14d7e902018-12-10 13:46:09 +0100925 self.assertIn(index, seen,
926 "Packet with packet_index %d not received" % index)
Juraj Sloboda3048b632018-10-02 11:13:53 +0200927
928 def test_reassembly(self):
929 """ basic reassembly """
930
931 self.pg_enable_capture()
932 self.src_dst_if.add_stream(self.fragments_300)
933 self.pg_start()
934
935 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
936 self.verify_capture(packets)
937
938 # run it all again to verify correctness
939 self.pg_enable_capture()
940 self.src_dst_if.add_stream(self.fragments_300)
941 self.pg_start()
942
943 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
944 self.verify_capture(packets)
945
946
Klement Sekera75e7d132017-09-20 08:26:30 +0200947class TestFIFReassembly(VppTestCase):
948 """ Fragments in fragments reassembly """
949
950 @classmethod
951 def setUpClass(cls):
952 super(TestFIFReassembly, cls).setUpClass()
953
Klement Sekera4c533132018-02-22 11:41:12 +0100954 cls.create_pg_interfaces([0, 1])
955 cls.src_if = cls.pg0
956 cls.dst_if = cls.pg1
957 for i in cls.pg_interfaces:
958 i.admin_up()
959 i.config_ip4()
960 i.resolve_arp()
961 i.config_ip6()
962 i.resolve_ndp()
Klement Sekera75e7d132017-09-20 08:26:30 +0200963
Klement Sekera75e7d132017-09-20 08:26:30 +0200964 cls.packet_sizes = [64, 512, 1518, 9018]
965 cls.padding = " abcdefghijklmn"
966
967 def setUp(self):
968 """ Test setup - force timeout on existing reassemblies """
969 super(TestFIFReassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +0100970 self.vapi.ip_reassembly_enable_disable(
971 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
972 enable_ip6=True)
973 self.vapi.ip_reassembly_enable_disable(
974 sw_if_index=self.dst_if.sw_if_index, enable_ip4=True,
975 enable_ip6=True)
Klement Sekera75e7d132017-09-20 08:26:30 +0200976 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
977 expire_walk_interval_ms=10)
978 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
979 expire_walk_interval_ms=10, is_ip6=1)
980 self.sleep(.25)
981 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
982 expire_walk_interval_ms=10000)
983 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
984 expire_walk_interval_ms=10000, is_ip6=1)
985
986 def tearDown(self):
987 self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
988 self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +0100989 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +0200990 super(TestFIFReassembly, self).tearDown()
991
992 def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):
993 """Verify captured packet stream.
994
995 :param list capture: Captured packet stream.
996 """
997 info = None
998 seen = set()
999 for packet in capture:
1000 try:
Klement Sekera4c533132018-02-22 11:41:12 +01001001 self.logger.debug(ppp("Got packet:", packet))
Klement Sekera75e7d132017-09-20 08:26:30 +02001002 ip = packet[ip_class]
1003 udp = packet[UDP]
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001004 payload_info = self.payload_to_info(packet[Raw])
Klement Sekera75e7d132017-09-20 08:26:30 +02001005 packet_index = payload_info.index
1006 self.assertTrue(
1007 packet_index not in dropped_packet_indexes,
1008 ppp("Packet received, but should be dropped:", packet))
1009 if packet_index in seen:
1010 raise Exception(ppp("Duplicate packet received", packet))
1011 seen.add(packet_index)
Klement Sekera4c533132018-02-22 11:41:12 +01001012 self.assertEqual(payload_info.dst, self.dst_if.sw_if_index)
Klement Sekera75e7d132017-09-20 08:26:30 +02001013 info = self._packet_infos[packet_index]
1014 self.assertTrue(info is not None)
1015 self.assertEqual(packet_index, info.index)
1016 saved_packet = info.data
1017 self.assertEqual(ip.src, saved_packet[ip_class].src)
1018 self.assertEqual(ip.dst, saved_packet[ip_class].dst)
1019 self.assertEqual(udp.payload, saved_packet[UDP].payload)
Klement Sekera4c533132018-02-22 11:41:12 +01001020 except Exception:
Klement Sekera75e7d132017-09-20 08:26:30 +02001021 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1022 raise
1023 for index in self._packet_infos:
1024 self.assertTrue(index in seen or index in dropped_packet_indexes,
1025 "Packet with packet_index %d not received" % index)
1026
1027 def test_fif4(self):
1028 """ Fragments in fragments (4o4) """
1029
1030 # TODO this should be ideally in setUpClass, but then we hit a bug
1031 # with VppIpRoute incorrectly reporting it's present when it's not
1032 # so we need to manually remove the vpp config, thus we cannot have
1033 # it shared for multiple test cases
1034 self.tun_ip4 = "1.1.1.2"
1035
Klement Sekera4c533132018-02-22 11:41:12 +01001036 self.gre4 = VppGreInterface(self, self.src_if.local_ip4, self.tun_ip4)
Klement Sekera75e7d132017-09-20 08:26:30 +02001037 self.gre4.add_vpp_config()
1038 self.gre4.admin_up()
1039 self.gre4.config_ip4()
1040
Klement Sekera4c533132018-02-22 11:41:12 +01001041 self.vapi.ip_reassembly_enable_disable(
1042 sw_if_index=self.gre4.sw_if_index, enable_ip4=True)
1043
Klement Sekera75e7d132017-09-20 08:26:30 +02001044 self.route4 = VppIpRoute(self, self.tun_ip4, 32,
Klement Sekera4c533132018-02-22 11:41:12 +01001045 [VppRoutePath(self.src_if.remote_ip4,
1046 self.src_if.sw_if_index)])
Klement Sekera75e7d132017-09-20 08:26:30 +02001047 self.route4.add_vpp_config()
1048
1049 self.reset_packet_infos()
1050 for i in range(test_packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01001051 info = self.create_packet_info(self.src_if, self.dst_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02001052 payload = self.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +01001053 # Ethernet header here is only for size calculation, thus it
1054 # doesn't matter how it's initialized. This is to ensure that
1055 # reassembled packet is not > 9000 bytes, so that it's not dropped
1056 p = (Ether() /
1057 IP(id=i, src=self.src_if.remote_ip4,
1058 dst=self.dst_if.remote_ip4) /
1059 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001060 Raw(payload))
1061 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1062 self.extend_packet(p, size, self.padding)
Klement Sekera4c533132018-02-22 11:41:12 +01001063 info.data = p[IP] # use only IP part, without ethernet header
Klement Sekera75e7d132017-09-20 08:26:30 +02001064
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -08001065 fragments = [x for _, p in six.iteritems(self._packet_infos)
Klement Sekera75e7d132017-09-20 08:26:30 +02001066 for x in fragment_rfc791(p.data, 400)]
1067
1068 encapped_fragments = \
Klement Sekera4c533132018-02-22 11:41:12 +01001069 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1070 IP(src=self.tun_ip4, dst=self.src_if.local_ip4) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001071 GRE() /
1072 p
1073 for p in fragments]
1074
1075 fragmented_encapped_fragments = \
1076 [x for p in encapped_fragments
1077 for x in fragment_rfc791(p, 200)]
1078
Klement Sekera4c533132018-02-22 11:41:12 +01001079 self.src_if.add_stream(fragmented_encapped_fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001080
1081 self.pg_enable_capture(self.pg_interfaces)
1082 self.pg_start()
1083
Klement Sekera4c533132018-02-22 11:41:12 +01001084 self.src_if.assert_nothing_captured()
1085 packets = self.dst_if.get_capture(len(self._packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +02001086 self.verify_capture(packets, IP)
1087
1088 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
1089 # so that it's query_vpp_config() works as it should
1090 self.gre4.remove_vpp_config()
Klement Sekera4c533132018-02-22 11:41:12 +01001091 self.logger.debug(self.vapi.ppcli("show interface"))
Klement Sekera75e7d132017-09-20 08:26:30 +02001092
1093 def test_fif6(self):
1094 """ Fragments in fragments (6o6) """
1095 # TODO this should be ideally in setUpClass, but then we hit a bug
1096 # with VppIpRoute incorrectly reporting it's present when it's not
1097 # so we need to manually remove the vpp config, thus we cannot have
1098 # it shared for multiple test cases
1099 self.tun_ip6 = "1002::1"
1100
Klement Sekera4c533132018-02-22 11:41:12 +01001101 self.gre6 = VppGre6Interface(self, self.src_if.local_ip6, self.tun_ip6)
Klement Sekera75e7d132017-09-20 08:26:30 +02001102 self.gre6.add_vpp_config()
1103 self.gre6.admin_up()
1104 self.gre6.config_ip6()
1105
Klement Sekera4c533132018-02-22 11:41:12 +01001106 self.vapi.ip_reassembly_enable_disable(
1107 sw_if_index=self.gre6.sw_if_index, enable_ip6=True)
1108
Klement Sekera75e7d132017-09-20 08:26:30 +02001109 self.route6 = VppIpRoute(self, self.tun_ip6, 128,
Klement Sekera4c533132018-02-22 11:41:12 +01001110 [VppRoutePath(self.src_if.remote_ip6,
1111 self.src_if.sw_if_index,
Klement Sekera75e7d132017-09-20 08:26:30 +02001112 proto=DpoProto.DPO_PROTO_IP6)],
1113 is_ip6=1)
1114 self.route6.add_vpp_config()
1115
1116 self.reset_packet_infos()
1117 for i in range(test_packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01001118 info = self.create_packet_info(self.src_if, self.dst_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02001119 payload = self.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +01001120 # Ethernet header here is only for size calculation, thus it
1121 # doesn't matter how it's initialized. This is to ensure that
1122 # reassembled packet is not > 9000 bytes, so that it's not dropped
1123 p = (Ether() /
1124 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1125 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001126 Raw(payload))
1127 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1128 self.extend_packet(p, size, self.padding)
Klement Sekera4c533132018-02-22 11:41:12 +01001129 info.data = p[IPv6] # use only IPv6 part, without ethernet header
Klement Sekera75e7d132017-09-20 08:26:30 +02001130
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -08001131 fragments = [x for _, i in six.iteritems(self._packet_infos)
Klement Sekera75e7d132017-09-20 08:26:30 +02001132 for x in fragment_rfc8200(
1133 i.data, i.index, 400)]
1134
1135 encapped_fragments = \
Klement Sekera4c533132018-02-22 11:41:12 +01001136 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1137 IPv6(src=self.tun_ip6, dst=self.src_if.local_ip6) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001138 GRE() /
1139 p
1140 for p in fragments]
1141
1142 fragmented_encapped_fragments = \
1143 [x for p in encapped_fragments for x in (
1144 fragment_rfc8200(
1145 p,
1146 2 * len(self._packet_infos) + p[IPv6ExtHdrFragment].id,
1147 200)
1148 if IPv6ExtHdrFragment in p else [p]
1149 )
1150 ]
1151
Klement Sekera4c533132018-02-22 11:41:12 +01001152 self.src_if.add_stream(fragmented_encapped_fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001153
1154 self.pg_enable_capture(self.pg_interfaces)
1155 self.pg_start()
1156
Klement Sekera4c533132018-02-22 11:41:12 +01001157 self.src_if.assert_nothing_captured()
1158 packets = self.dst_if.get_capture(len(self._packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +02001159 self.verify_capture(packets, IPv6)
1160
1161 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
1162 # so that it's query_vpp_config() works as it should
Klement Sekera3ecc2212018-03-27 10:34:43 +02001163 self.gre6.remove_vpp_config()
Klement Sekera75e7d132017-09-20 08:26:30 +02001164
1165
1166if __name__ == '__main__':
1167 unittest.main(testRunner=VppTestRunner)