blob: 96e00c6bb608ae521f57ad1a3e13fde6cd8178e0 [file] [log] [blame]
Klement Sekera75e7d132017-09-20 08:26:30 +02001#!/usr/bin/env python
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -08002
3import six
Klement Sekera75e7d132017-09-20 08:26:30 +02004import unittest
5from random import shuffle
6
juraj.linkes68ebc832018-11-29 09:37:08 +01007from framework import VppTestCase, VppTestRunner, is_skip_aarch64_set,\
8 is_platform_aarch64
Klement Sekera75e7d132017-09-20 08:26:30 +02009
10from scapy.packet import Raw
11from scapy.layers.l2 import Ether, GRE
Juraj Sloboda3048b632018-10-02 11:13:53 +020012from scapy.layers.inet import IP, UDP, ICMP
Klement Sekera75e7d132017-09-20 08:26:30 +020013from util import ppp, fragment_rfc791, fragment_rfc8200
Klement Sekera75e7d132017-09-20 08:26:30 +020014from scapy.layers.inet6 import IPv6, IPv6ExtHdrFragment, ICMPv6ParamProblem,\
15 ICMPv6TimeExceeded
16from vpp_gre_interface import VppGreInterface, VppGre6Interface
Neale Rannsc0a93142018-09-05 15:42:26 -070017from vpp_ip import DpoProto
18from vpp_ip_route import VppIpRoute, VppRoutePath
Klement Sekera75e7d132017-09-20 08:26:30 +020019
20test_packet_count = 257
21
22
23class TestIPv4Reassembly(VppTestCase):
24 """ IPv4 Reassembly """
25
26 @classmethod
27 def setUpClass(cls):
28 super(TestIPv4Reassembly, cls).setUpClass()
29
Klement Sekera4c533132018-02-22 11:41:12 +010030 cls.create_pg_interfaces([0, 1])
31 cls.src_if = cls.pg0
32 cls.dst_if = cls.pg1
Klement Sekera75e7d132017-09-20 08:26:30 +020033
34 # setup all interfaces
35 for i in cls.pg_interfaces:
36 i.admin_up()
37 i.config_ip4()
38 i.resolve_arp()
39
Klement Sekera75e7d132017-09-20 08:26:30 +020040 # packet sizes
41 cls.packet_sizes = [64, 512, 1518, 9018]
42 cls.padding = " abcdefghijklmn"
43 cls.create_stream(cls.packet_sizes)
44 cls.create_fragments()
45
46 def setUp(self):
47 """ Test setup - force timeout on existing reassemblies """
48 super(TestIPv4Reassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +010049 self.vapi.ip_reassembly_enable_disable(
50 sw_if_index=self.src_if.sw_if_index, enable_ip4=True)
Klement Sekera75e7d132017-09-20 08:26:30 +020051 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
52 expire_walk_interval_ms=10)
53 self.sleep(.25)
54 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
55 expire_walk_interval_ms=10000)
56
57 def tearDown(self):
58 super(TestIPv4Reassembly, self).tearDown()
59 self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
60
61 @classmethod
62 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
63 """Create input packet stream for defined interface.
64
65 :param list packet_sizes: Required packet sizes.
66 """
67 for i in range(0, packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +010068 info = cls.create_packet_info(cls.src_if, cls.src_if)
Klement Sekera75e7d132017-09-20 08:26:30 +020069 payload = cls.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +010070 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
71 IP(id=info.index, src=cls.src_if.remote_ip4,
72 dst=cls.dst_if.remote_ip4) /
73 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +020074 Raw(payload))
75 size = packet_sizes[(i // 2) % len(packet_sizes)]
76 cls.extend_packet(p, size, cls.padding)
77 info.data = p
78
79 @classmethod
80 def create_fragments(cls):
81 infos = cls._packet_infos
82 cls.pkt_infos = []
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -080083 for index, info in six.iteritems(infos):
Klement Sekera75e7d132017-09-20 08:26:30 +020084 p = info.data
Klement Sekera4c533132018-02-22 11:41:12 +010085 # cls.logger.debug(ppp("Packet:", p.__class__(str(p))))
Klement Sekera75e7d132017-09-20 08:26:30 +020086 fragments_400 = fragment_rfc791(p, 400)
87 fragments_300 = fragment_rfc791(p, 300)
88 fragments_200 = [
89 x for f in fragments_400 for x in fragment_rfc791(f, 200)]
90 cls.pkt_infos.append(
91 (index, fragments_400, fragments_300, fragments_200))
92 cls.fragments_400 = [
93 x for (_, frags, _, _) in cls.pkt_infos for x in frags]
94 cls.fragments_300 = [
95 x for (_, _, frags, _) in cls.pkt_infos for x in frags]
96 cls.fragments_200 = [
97 x for (_, _, _, frags) in cls.pkt_infos for x in frags]
98 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
99 "%s 300-byte fragments and %s 200-byte fragments" %
100 (len(infos), len(cls.fragments_400),
101 len(cls.fragments_300), len(cls.fragments_200)))
102
103 def verify_capture(self, capture, dropped_packet_indexes=[]):
104 """Verify captured packet stream.
105
106 :param list capture: Captured packet stream.
107 """
108 info = None
109 seen = set()
110 for packet in capture:
111 try:
Klement Sekera4c533132018-02-22 11:41:12 +0100112 self.logger.debug(ppp("Got packet:", packet))
Klement Sekera75e7d132017-09-20 08:26:30 +0200113 ip = packet[IP]
114 udp = packet[UDP]
115 payload_info = self.payload_to_info(str(packet[Raw]))
116 packet_index = payload_info.index
117 self.assertTrue(
118 packet_index not in dropped_packet_indexes,
119 ppp("Packet received, but should be dropped:", packet))
120 if packet_index in seen:
121 raise Exception(ppp("Duplicate packet received", packet))
122 seen.add(packet_index)
Klement Sekera4c533132018-02-22 11:41:12 +0100123 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
Klement Sekera75e7d132017-09-20 08:26:30 +0200124 info = self._packet_infos[packet_index]
125 self.assertTrue(info is not None)
126 self.assertEqual(packet_index, info.index)
127 saved_packet = info.data
128 self.assertEqual(ip.src, saved_packet[IP].src)
129 self.assertEqual(ip.dst, saved_packet[IP].dst)
130 self.assertEqual(udp.payload, saved_packet[UDP].payload)
Klement Sekera4c533132018-02-22 11:41:12 +0100131 except Exception:
Klement Sekera75e7d132017-09-20 08:26:30 +0200132 self.logger.error(ppp("Unexpected or invalid packet:", packet))
133 raise
134 for index in self._packet_infos:
135 self.assertTrue(index in seen or index in dropped_packet_indexes,
136 "Packet with packet_index %d not received" % index)
137
138 def test_reassembly(self):
139 """ basic reassembly """
140
141 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100142 self.src_if.add_stream(self.fragments_200)
Klement Sekera75e7d132017-09-20 08:26:30 +0200143 self.pg_start()
144
Klement Sekera4c533132018-02-22 11:41:12 +0100145 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200146 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100147 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200148
149 # run it all again to verify correctness
150 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100151 self.src_if.add_stream(self.fragments_200)
Klement Sekera75e7d132017-09-20 08:26:30 +0200152 self.pg_start()
153
Klement Sekera4c533132018-02-22 11:41:12 +0100154 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200155 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100156 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200157
158 def test_reversed(self):
159 """ reverse order reassembly """
160
161 fragments = list(self.fragments_200)
162 fragments.reverse()
163
164 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100165 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200166 self.pg_start()
167
Klement Sekera4c533132018-02-22 11:41:12 +0100168 packets = self.dst_if.get_capture(len(self.packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200169 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100170 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200171
172 # run it all again to verify correctness
173 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100174 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200175 self.pg_start()
176
Klement Sekera4c533132018-02-22 11:41:12 +0100177 packets = self.dst_if.get_capture(len(self.packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200178 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100179 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200180
Klement Sekera14d7e902018-12-10 13:46:09 +0100181 def test_5737(self):
182 """ fragment length + ip header size > 65535 """
Klement Sekera4ee633e2018-12-14 12:00:44 +0100183 self.vapi.cli("clear errors")
Klement Sekera14d7e902018-12-10 13:46:09 +0100184 raw = ('E\x00\x00\x88,\xf8\x1f\xfe@\x01\x98\x00\xc0\xa8\n-\xc0\xa8\n'
185 '\x01\x08\x00\xf0J\xed\xcb\xf1\xf5Test-group: IPv4.IPv4.ipv4-'
186 'message.Ethernet-Payload.IPv4-Packet.IPv4-Header.Fragment-Of'
187 'fset; Test-case: 5737')
188
189 malformed_packet = (Ether(dst=self.src_if.local_mac,
190 src=self.src_if.remote_mac) /
191 IP(raw))
192 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
193 IP(id=1000, src=self.src_if.remote_ip4,
194 dst=self.dst_if.remote_ip4) /
195 UDP(sport=1234, dport=5678) /
196 Raw("X" * 1000))
197 valid_fragments = fragment_rfc791(p, 400)
198
199 self.pg_enable_capture()
200 self.src_if.add_stream([malformed_packet] + valid_fragments)
201 self.pg_start()
202
203 self.dst_if.get_capture(1)
Klement Sekera4ee633e2018-12-14 12:00:44 +0100204 self.assert_packet_counter_equal("ip4-reassembly-feature", 1)
205 # TODO remove above, uncomment below once clearing of counters
206 # is supported
207 # self.assert_packet_counter_equal(
208 # "/err/ip4-reassembly-feature/malformed packets", 1)
Klement Sekera14d7e902018-12-10 13:46:09 +0100209
Klement Sekera400f6d82018-12-13 14:35:48 +0100210 def test_44924(self):
211 """ compress tiny fragments """
212 packets = [(Ether(dst=self.src_if.local_mac,
213 src=self.src_if.remote_mac) /
214 IP(id=24339, flags="MF", frag=0, ttl=64,
215 src=self.src_if.remote_ip4,
216 dst=self.dst_if.remote_ip4) /
217 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
218 Raw(load='Test-group: IPv4')),
219 (Ether(dst=self.src_if.local_mac,
220 src=self.src_if.remote_mac) /
221 IP(id=24339, flags="MF", frag=3, ttl=64,
222 src=self.src_if.remote_ip4,
223 dst=self.dst_if.remote_ip4) /
224 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
225 Raw(load='.IPv4.Fragmentation.vali')),
226 (Ether(dst=self.src_if.local_mac,
227 src=self.src_if.remote_mac) /
228 IP(id=24339, frag=6, ttl=64,
229 src=self.src_if.remote_ip4,
230 dst=self.dst_if.remote_ip4) /
231 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
232 Raw(load='d; Test-case: 44924'))
233 ]
234
235 self.pg_enable_capture()
236 self.src_if.add_stream(packets)
237 self.pg_start()
238
239 self.dst_if.get_capture(1)
240
Klement Sekera4ee633e2018-12-14 12:00:44 +0100241 def test_frag_1(self):
242 """ fragment of size 1 """
243 self.vapi.cli("clear errors")
244 malformed_packets = [(Ether(dst=self.src_if.local_mac,
245 src=self.src_if.remote_mac) /
246 IP(id=7, len=21, flags="MF", frag=0, ttl=64,
247 src=self.src_if.remote_ip4,
248 dst=self.dst_if.remote_ip4) /
249 ICMP(type="echo-request")),
250 (Ether(dst=self.src_if.local_mac,
251 src=self.src_if.remote_mac) /
252 IP(id=7, len=21, frag=1, ttl=64,
253 src=self.src_if.remote_ip4,
254 dst=self.dst_if.remote_ip4) /
255 Raw(load='\x08')),
256 ]
257
258 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
259 IP(id=1000, src=self.src_if.remote_ip4,
260 dst=self.dst_if.remote_ip4) /
261 UDP(sport=1234, dport=5678) /
262 Raw("X" * 1000))
263 valid_fragments = fragment_rfc791(p, 400)
264
265 self.pg_enable_capture()
266 self.src_if.add_stream(malformed_packets + valid_fragments)
267 self.pg_start()
268
269 self.dst_if.get_capture(1)
270
271 self.assert_packet_counter_equal("ip4-reassembly-feature", 1)
272 # TODO remove above, uncomment below once clearing of counters
273 # is supported
274 # self.assert_packet_counter_equal(
275 # "/err/ip4-reassembly-feature/malformed packets", 1)
276
juraj.linkes68ebc832018-11-29 09:37:08 +0100277 @unittest.skipIf(is_skip_aarch64_set() and is_platform_aarch64(),
278 "test doesn't work on aarch64")
Klement Sekera75e7d132017-09-20 08:26:30 +0200279 def test_random(self):
280 """ random order reassembly """
281
282 fragments = list(self.fragments_200)
283 shuffle(fragments)
284
285 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100286 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200287 self.pg_start()
288
Klement Sekera4c533132018-02-22 11:41:12 +0100289 packets = self.dst_if.get_capture(len(self.packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200290 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100291 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200292
293 # run it all again to verify correctness
294 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100295 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200296 self.pg_start()
297
Klement Sekera4c533132018-02-22 11:41:12 +0100298 packets = self.dst_if.get_capture(len(self.packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200299 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100300 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200301
302 def test_duplicates(self):
303 """ duplicate fragments """
304
305 fragments = [
306 x for (_, frags, _, _) in self.pkt_infos
307 for x in frags
308 for _ in range(0, min(2, len(frags)))
309 ]
310
311 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100312 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200313 self.pg_start()
314
Klement Sekera4c533132018-02-22 11:41:12 +0100315 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200316 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100317 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200318
319 def test_overlap1(self):
320 """ overlapping fragments case #1 """
321
322 fragments = []
323 for _, _, frags_300, frags_200 in self.pkt_infos:
324 if len(frags_300) == 1:
325 fragments.extend(frags_300)
326 else:
327 for i, j in zip(frags_200, frags_300):
328 fragments.extend(i)
329 fragments.extend(j)
330
331 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100332 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200333 self.pg_start()
334
Klement Sekera4c533132018-02-22 11:41:12 +0100335 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200336 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100337 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200338
339 # run it all to verify correctness
340 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100341 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200342 self.pg_start()
343
Klement Sekera4c533132018-02-22 11:41:12 +0100344 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200345 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100346 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200347
348 def test_overlap2(self):
349 """ overlapping fragments case #2 """
350
351 fragments = []
352 for _, _, frags_300, frags_200 in self.pkt_infos:
353 if len(frags_300) == 1:
354 fragments.extend(frags_300)
355 else:
356 # care must be taken here so that there are no fragments
357 # received by vpp after reassembly is finished, otherwise
358 # new reassemblies will be started and packet generator will
359 # freak out when it detects unfreed buffers
360 zipped = zip(frags_300, frags_200)
361 for i, j in zipped[:-1]:
362 fragments.extend(i)
363 fragments.extend(j)
364 fragments.append(zipped[-1][0])
365
366 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100367 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200368 self.pg_start()
369
Klement Sekera4c533132018-02-22 11:41:12 +0100370 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200371 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100372 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200373
374 # run it all to verify correctness
375 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100376 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200377 self.pg_start()
378
Klement Sekera4c533132018-02-22 11:41:12 +0100379 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200380 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100381 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200382
383 def test_timeout_inline(self):
384 """ timeout (inline) """
385
386 dropped_packet_indexes = set(
387 index for (index, frags, _, _) in self.pkt_infos if len(frags) > 1
388 )
389
390 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
391 expire_walk_interval_ms=10000)
392
393 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100394 self.src_if.add_stream(self.fragments_400)
Klement Sekera75e7d132017-09-20 08:26:30 +0200395 self.pg_start()
396
Klement Sekera4c533132018-02-22 11:41:12 +0100397 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200398 len(self.pkt_infos) - len(dropped_packet_indexes))
399 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100400 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200401
402 def test_timeout_cleanup(self):
403 """ timeout (cleanup) """
404
405 # whole packets + fragmented packets sans last fragment
406 fragments = [
407 x for (_, frags_400, _, _) in self.pkt_infos
408 for x in frags_400[:-1 if len(frags_400) > 1 else None]
409 ]
410
411 # last fragments for fragmented packets
412 fragments2 = [frags_400[-1]
413 for (_, frags_400, _, _) in self.pkt_infos
414 if len(frags_400) > 1]
415
416 dropped_packet_indexes = set(
417 index for (index, frags_400, _, _) in self.pkt_infos
418 if len(frags_400) > 1)
419
420 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
421 expire_walk_interval_ms=50)
422
423 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100424 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200425 self.pg_start()
426
427 self.sleep(.25, "wait before sending rest of fragments")
428
Klement Sekera4c533132018-02-22 11:41:12 +0100429 self.src_if.add_stream(fragments2)
Klement Sekera75e7d132017-09-20 08:26:30 +0200430 self.pg_start()
Klement Sekera75e7d132017-09-20 08:26:30 +0200431
Klement Sekera4c533132018-02-22 11:41:12 +0100432 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200433 len(self.pkt_infos) - len(dropped_packet_indexes))
434 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100435 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200436
437 def test_disabled(self):
438 """ reassembly disabled """
439
440 dropped_packet_indexes = set(
441 index for (index, frags_400, _, _) in self.pkt_infos
442 if len(frags_400) > 1)
443
444 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
445 expire_walk_interval_ms=10000)
446
447 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100448 self.src_if.add_stream(self.fragments_400)
Klement Sekera75e7d132017-09-20 08:26:30 +0200449 self.pg_start()
450
Klement Sekera4c533132018-02-22 11:41:12 +0100451 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200452 len(self.pkt_infos) - len(dropped_packet_indexes))
453 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100454 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200455
456
457class TestIPv6Reassembly(VppTestCase):
458 """ IPv6 Reassembly """
459
460 @classmethod
461 def setUpClass(cls):
462 super(TestIPv6Reassembly, cls).setUpClass()
463
Klement Sekera4c533132018-02-22 11:41:12 +0100464 cls.create_pg_interfaces([0, 1])
465 cls.src_if = cls.pg0
466 cls.dst_if = cls.pg1
Klement Sekera75e7d132017-09-20 08:26:30 +0200467
468 # setup all interfaces
469 for i in cls.pg_interfaces:
470 i.admin_up()
471 i.config_ip6()
472 i.resolve_ndp()
473
Klement Sekera75e7d132017-09-20 08:26:30 +0200474 # packet sizes
475 cls.packet_sizes = [64, 512, 1518, 9018]
476 cls.padding = " abcdefghijklmn"
477 cls.create_stream(cls.packet_sizes)
478 cls.create_fragments()
479
480 def setUp(self):
481 """ Test setup - force timeout on existing reassemblies """
482 super(TestIPv6Reassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +0100483 self.vapi.ip_reassembly_enable_disable(
484 sw_if_index=self.src_if.sw_if_index, enable_ip6=True)
Klement Sekera75e7d132017-09-20 08:26:30 +0200485 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
486 expire_walk_interval_ms=10, is_ip6=1)
487 self.sleep(.25)
488 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
489 expire_walk_interval_ms=10000, is_ip6=1)
Klement Sekera4c533132018-02-22 11:41:12 +0100490 self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
Klement Sekera75e7d132017-09-20 08:26:30 +0200491
492 def tearDown(self):
493 super(TestIPv6Reassembly, self).tearDown()
494 self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
495
496 @classmethod
497 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
498 """Create input packet stream for defined interface.
499
500 :param list packet_sizes: Required packet sizes.
501 """
502 for i in range(0, packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +0100503 info = cls.create_packet_info(cls.src_if, cls.src_if)
Klement Sekera75e7d132017-09-20 08:26:30 +0200504 payload = cls.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +0100505 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
506 IPv6(src=cls.src_if.remote_ip6,
507 dst=cls.dst_if.remote_ip6) /
508 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200509 Raw(payload))
510 size = packet_sizes[(i // 2) % len(packet_sizes)]
511 cls.extend_packet(p, size, cls.padding)
512 info.data = p
513
514 @classmethod
515 def create_fragments(cls):
516 infos = cls._packet_infos
517 cls.pkt_infos = []
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -0800518 for index, info in six.iteritems(infos):
Klement Sekera75e7d132017-09-20 08:26:30 +0200519 p = info.data
Klement Sekera4c533132018-02-22 11:41:12 +0100520 # cls.logger.debug(ppp("Packet:", p.__class__(str(p))))
Klement Sekera75e7d132017-09-20 08:26:30 +0200521 fragments_400 = fragment_rfc8200(p, info.index, 400)
522 fragments_300 = fragment_rfc8200(p, info.index, 300)
523 cls.pkt_infos.append((index, fragments_400, fragments_300))
524 cls.fragments_400 = [
525 x for _, frags, _ in cls.pkt_infos for x in frags]
526 cls.fragments_300 = [
527 x for _, _, frags in cls.pkt_infos for x in frags]
528 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
529 "and %s 300-byte fragments" %
530 (len(infos), len(cls.fragments_400),
531 len(cls.fragments_300)))
532
533 def verify_capture(self, capture, dropped_packet_indexes=[]):
534 """Verify captured packet strea .
535
536 :param list capture: Captured packet stream.
537 """
538 info = None
539 seen = set()
540 for packet in capture:
541 try:
Klement Sekera4c533132018-02-22 11:41:12 +0100542 self.logger.debug(ppp("Got packet:", packet))
Klement Sekera75e7d132017-09-20 08:26:30 +0200543 ip = packet[IPv6]
544 udp = packet[UDP]
545 payload_info = self.payload_to_info(str(packet[Raw]))
546 packet_index = payload_info.index
547 self.assertTrue(
548 packet_index not in dropped_packet_indexes,
549 ppp("Packet received, but should be dropped:", packet))
550 if packet_index in seen:
551 raise Exception(ppp("Duplicate packet received", packet))
552 seen.add(packet_index)
Klement Sekera4c533132018-02-22 11:41:12 +0100553 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
Klement Sekera75e7d132017-09-20 08:26:30 +0200554 info = self._packet_infos[packet_index]
555 self.assertTrue(info is not None)
556 self.assertEqual(packet_index, info.index)
557 saved_packet = info.data
558 self.assertEqual(ip.src, saved_packet[IPv6].src)
559 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
560 self.assertEqual(udp.payload, saved_packet[UDP].payload)
Klement Sekera4c533132018-02-22 11:41:12 +0100561 except Exception:
Klement Sekera75e7d132017-09-20 08:26:30 +0200562 self.logger.error(ppp("Unexpected or invalid packet:", packet))
563 raise
564 for index in self._packet_infos:
565 self.assertTrue(index in seen or index in dropped_packet_indexes,
566 "Packet with packet_index %d not received" % index)
567
568 def test_reassembly(self):
569 """ basic reassembly """
570
571 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100572 self.src_if.add_stream(self.fragments_400)
Klement Sekera75e7d132017-09-20 08:26:30 +0200573 self.pg_start()
574
Klement Sekera4c533132018-02-22 11:41:12 +0100575 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200576 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100577 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200578
579 # run it all again to verify correctness
580 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100581 self.src_if.add_stream(self.fragments_400)
Klement Sekera75e7d132017-09-20 08:26:30 +0200582 self.pg_start()
583
Klement Sekera4c533132018-02-22 11:41:12 +0100584 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200585 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100586 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200587
588 def test_reversed(self):
589 """ reverse order reassembly """
590
591 fragments = list(self.fragments_400)
592 fragments.reverse()
593
594 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100595 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200596 self.pg_start()
597
Klement Sekera4c533132018-02-22 11:41:12 +0100598 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200599 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100600 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200601
602 # run it all again to verify correctness
603 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100604 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200605 self.pg_start()
606
Klement Sekera4c533132018-02-22 11:41:12 +0100607 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200608 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100609 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200610
611 def test_random(self):
612 """ random order reassembly """
613
614 fragments = list(self.fragments_400)
615 shuffle(fragments)
616
617 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100618 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200619 self.pg_start()
620
Klement Sekera4c533132018-02-22 11:41:12 +0100621 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200622 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100623 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200624
625 # run it all again to verify correctness
626 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100627 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200628 self.pg_start()
629
Klement Sekera4c533132018-02-22 11:41:12 +0100630 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200631 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100632 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200633
634 def test_duplicates(self):
635 """ duplicate fragments """
636
637 fragments = [
638 x for (_, frags, _) in self.pkt_infos
639 for x in frags
640 for _ in range(0, min(2, len(frags)))
641 ]
642
643 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100644 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200645 self.pg_start()
646
Klement Sekera4c533132018-02-22 11:41:12 +0100647 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200648 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100649 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200650
651 def test_overlap1(self):
652 """ overlapping fragments case #1 """
653
654 fragments = []
655 for _, frags_400, frags_300 in self.pkt_infos:
656 if len(frags_300) == 1:
657 fragments.extend(frags_400)
658 else:
659 for i, j in zip(frags_300, frags_400):
660 fragments.extend(i)
661 fragments.extend(j)
662
663 dropped_packet_indexes = set(
664 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
665 )
666
667 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100668 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200669 self.pg_start()
670
Klement Sekera4c533132018-02-22 11:41:12 +0100671 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200672 len(self.pkt_infos) - len(dropped_packet_indexes))
673 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100674 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200675
676 def test_overlap2(self):
677 """ overlapping fragments case #2 """
678
679 fragments = []
Klement Sekera4c533132018-02-22 11:41:12 +0100680 for _, frags_400, frags_300 in self.pkt_infos:
Klement Sekera75e7d132017-09-20 08:26:30 +0200681 if len(frags_400) == 1:
682 fragments.extend(frags_400)
683 else:
684 # care must be taken here so that there are no fragments
685 # received by vpp after reassembly is finished, otherwise
686 # new reassemblies will be started and packet generator will
687 # freak out when it detects unfreed buffers
Klement Sekera4c533132018-02-22 11:41:12 +0100688 zipped = zip(frags_400, frags_300)
Klement Sekera75e7d132017-09-20 08:26:30 +0200689 for i, j in zipped[:-1]:
690 fragments.extend(i)
691 fragments.extend(j)
692 fragments.append(zipped[-1][0])
693
694 dropped_packet_indexes = set(
695 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
696 )
697
698 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100699 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200700 self.pg_start()
701
Klement Sekera4c533132018-02-22 11:41:12 +0100702 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200703 len(self.pkt_infos) - len(dropped_packet_indexes))
704 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100705 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200706
707 def test_timeout_inline(self):
708 """ timeout (inline) """
709
710 dropped_packet_indexes = set(
711 index for (index, frags, _) in self.pkt_infos if len(frags) > 1
712 )
713
714 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
715 expire_walk_interval_ms=10000, is_ip6=1)
716
717 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100718 self.src_if.add_stream(self.fragments_400)
Klement Sekera75e7d132017-09-20 08:26:30 +0200719 self.pg_start()
720
Klement Sekera4c533132018-02-22 11:41:12 +0100721 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200722 len(self.pkt_infos) - len(dropped_packet_indexes))
723 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100724 pkts = self.src_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200725 expected_count=len(dropped_packet_indexes))
726 for icmp in pkts:
727 self.assertIn(ICMPv6TimeExceeded, icmp)
728 self.assertIn(IPv6ExtHdrFragment, icmp)
729 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
730 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
731
732 def test_timeout_cleanup(self):
733 """ timeout (cleanup) """
734
735 # whole packets + fragmented packets sans last fragment
736 fragments = [
737 x for (_, frags_400, _) in self.pkt_infos
738 for x in frags_400[:-1 if len(frags_400) > 1 else None]
739 ]
740
741 # last fragments for fragmented packets
742 fragments2 = [frags_400[-1]
743 for (_, frags_400, _) in self.pkt_infos
744 if len(frags_400) > 1]
745
746 dropped_packet_indexes = set(
747 index for (index, frags_400, _) in self.pkt_infos
748 if len(frags_400) > 1)
749
750 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
751 expire_walk_interval_ms=50)
752
753 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
754 expire_walk_interval_ms=50, is_ip6=1)
755
756 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100757 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200758 self.pg_start()
759
760 self.sleep(.25, "wait before sending rest of fragments")
761
Klement Sekera4c533132018-02-22 11:41:12 +0100762 self.src_if.add_stream(fragments2)
Klement Sekera75e7d132017-09-20 08:26:30 +0200763 self.pg_start()
Klement Sekera75e7d132017-09-20 08:26:30 +0200764
Klement Sekera4c533132018-02-22 11:41:12 +0100765 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200766 len(self.pkt_infos) - len(dropped_packet_indexes))
767 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100768 pkts = self.src_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200769 expected_count=len(dropped_packet_indexes))
770 for icmp in pkts:
771 self.assertIn(ICMPv6TimeExceeded, icmp)
772 self.assertIn(IPv6ExtHdrFragment, icmp)
773 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
774 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
775
776 def test_disabled(self):
777 """ reassembly disabled """
778
779 dropped_packet_indexes = set(
780 index for (index, frags_400, _) in self.pkt_infos
781 if len(frags_400) > 1)
782
783 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
784 expire_walk_interval_ms=10000, is_ip6=1)
785
786 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100787 self.src_if.add_stream(self.fragments_400)
Klement Sekera75e7d132017-09-20 08:26:30 +0200788 self.pg_start()
789
Klement Sekera4c533132018-02-22 11:41:12 +0100790 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200791 len(self.pkt_infos) - len(dropped_packet_indexes))
792 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100793 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200794
795 def test_missing_upper(self):
796 """ missing upper layer """
Klement Sekera4c533132018-02-22 11:41:12 +0100797 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
798 IPv6(src=self.src_if.remote_ip6,
799 dst=self.src_if.local_ip6) /
800 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200801 Raw())
802 self.extend_packet(p, 1000, self.padding)
803 fragments = fragment_rfc8200(p, 1, 500)
804 bad_fragment = p.__class__(str(fragments[1]))
805 bad_fragment[IPv6ExtHdrFragment].nh = 59
806 bad_fragment[IPv6ExtHdrFragment].offset = 0
807 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100808 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +0200809 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +0100810 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +0200811 icmp = pkts[0]
812 self.assertIn(ICMPv6ParamProblem, icmp)
813 self.assert_equal(icmp[ICMPv6ParamProblem].code, 3, "ICMP code")
814
815 def test_invalid_frag_size(self):
816 """ fragment size not a multiple of 8 """
Klement Sekera4c533132018-02-22 11:41:12 +0100817 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
818 IPv6(src=self.src_if.remote_ip6,
819 dst=self.src_if.local_ip6) /
820 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200821 Raw())
822 self.extend_packet(p, 1000, self.padding)
823 fragments = fragment_rfc8200(p, 1, 500)
824 bad_fragment = fragments[0]
825 self.extend_packet(bad_fragment, len(bad_fragment) + 5)
826 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100827 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +0200828 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +0100829 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +0200830 icmp = pkts[0]
831 self.assertIn(ICMPv6ParamProblem, icmp)
832 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
833
834 def test_invalid_packet_size(self):
835 """ total packet size > 65535 """
Klement Sekera4c533132018-02-22 11:41:12 +0100836 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
837 IPv6(src=self.src_if.remote_ip6,
838 dst=self.src_if.local_ip6) /
839 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200840 Raw())
841 self.extend_packet(p, 1000, self.padding)
842 fragments = fragment_rfc8200(p, 1, 500)
843 bad_fragment = fragments[1]
844 bad_fragment[IPv6ExtHdrFragment].offset = 65500
845 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100846 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +0200847 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +0100848 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +0200849 icmp = pkts[0]
850 self.assertIn(ICMPv6ParamProblem, icmp)
851 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
852
853
Juraj Sloboda3048b632018-10-02 11:13:53 +0200854class TestIPv4ReassemblyLocalNode(VppTestCase):
855 """ IPv4 Reassembly for packets coming to ip4-local node """
856
857 @classmethod
858 def setUpClass(cls):
859 super(TestIPv4ReassemblyLocalNode, cls).setUpClass()
860
861 cls.create_pg_interfaces([0])
862 cls.src_dst_if = cls.pg0
863
864 # setup all interfaces
865 for i in cls.pg_interfaces:
866 i.admin_up()
867 i.config_ip4()
868 i.resolve_arp()
869
870 cls.padding = " abcdefghijklmn"
871 cls.create_stream()
872 cls.create_fragments()
873
874 def setUp(self):
875 """ Test setup - force timeout on existing reassemblies """
876 super(TestIPv4ReassemblyLocalNode, self).setUp()
877 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
878 expire_walk_interval_ms=10)
879 self.sleep(.25)
880 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
881 expire_walk_interval_ms=10000)
882
883 def tearDown(self):
884 super(TestIPv4ReassemblyLocalNode, self).tearDown()
885 self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
886
887 @classmethod
888 def create_stream(cls, packet_count=test_packet_count):
889 """Create input packet stream for defined interface.
890
891 :param list packet_sizes: Required packet sizes.
892 """
893 for i in range(0, packet_count):
894 info = cls.create_packet_info(cls.src_dst_if, cls.src_dst_if)
895 payload = cls.info_to_payload(info)
896 p = (Ether(dst=cls.src_dst_if.local_mac,
897 src=cls.src_dst_if.remote_mac) /
898 IP(id=info.index, src=cls.src_dst_if.remote_ip4,
899 dst=cls.src_dst_if.local_ip4) /
900 ICMP(type='echo-request', id=1234) /
901 Raw(payload))
902 cls.extend_packet(p, 1518, cls.padding)
903 info.data = p
904
905 @classmethod
906 def create_fragments(cls):
907 infos = cls._packet_infos
908 cls.pkt_infos = []
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -0800909 for index, info in six.iteritems(infos):
Juraj Sloboda3048b632018-10-02 11:13:53 +0200910 p = info.data
911 # cls.logger.debug(ppp("Packet:", p.__class__(str(p))))
912 fragments_300 = fragment_rfc791(p, 300)
913 cls.pkt_infos.append((index, fragments_300))
914 cls.fragments_300 = [x for (_, frags) in cls.pkt_infos for x in frags]
915 cls.logger.debug("Fragmented %s packets into %s 300-byte fragments" %
916 (len(infos), len(cls.fragments_300)))
917
918 def verify_capture(self, capture):
919 """Verify captured packet stream.
920
921 :param list capture: Captured packet stream.
922 """
923 info = None
924 seen = set()
925 for packet in capture:
926 try:
927 self.logger.debug(ppp("Got packet:", packet))
928 ip = packet[IP]
929 icmp = packet[ICMP]
930 payload_info = self.payload_to_info(str(packet[Raw]))
931 packet_index = payload_info.index
932 if packet_index in seen:
933 raise Exception(ppp("Duplicate packet received", packet))
934 seen.add(packet_index)
935 self.assertEqual(payload_info.dst, self.src_dst_if.sw_if_index)
936 info = self._packet_infos[packet_index]
Klement Sekera14d7e902018-12-10 13:46:09 +0100937 self.assertIsNotNone(info)
Juraj Sloboda3048b632018-10-02 11:13:53 +0200938 self.assertEqual(packet_index, info.index)
939 saved_packet = info.data
940 self.assertEqual(ip.src, saved_packet[IP].dst)
941 self.assertEqual(ip.dst, saved_packet[IP].src)
942 self.assertEqual(icmp.type, 0) # echo reply
943 self.assertEqual(icmp.id, saved_packet[ICMP].id)
944 self.assertEqual(icmp.payload, saved_packet[ICMP].payload)
945 except Exception:
946 self.logger.error(ppp("Unexpected or invalid packet:", packet))
947 raise
948 for index in self._packet_infos:
Klement Sekera14d7e902018-12-10 13:46:09 +0100949 self.assertIn(index, seen,
950 "Packet with packet_index %d not received" % index)
Juraj Sloboda3048b632018-10-02 11:13:53 +0200951
952 def test_reassembly(self):
953 """ basic reassembly """
954
955 self.pg_enable_capture()
956 self.src_dst_if.add_stream(self.fragments_300)
957 self.pg_start()
958
959 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
960 self.verify_capture(packets)
961
962 # run it all again to verify correctness
963 self.pg_enable_capture()
964 self.src_dst_if.add_stream(self.fragments_300)
965 self.pg_start()
966
967 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
968 self.verify_capture(packets)
969
970
Klement Sekera75e7d132017-09-20 08:26:30 +0200971class TestFIFReassembly(VppTestCase):
972 """ Fragments in fragments reassembly """
973
974 @classmethod
975 def setUpClass(cls):
976 super(TestFIFReassembly, cls).setUpClass()
977
Klement Sekera4c533132018-02-22 11:41:12 +0100978 cls.create_pg_interfaces([0, 1])
979 cls.src_if = cls.pg0
980 cls.dst_if = cls.pg1
981 for i in cls.pg_interfaces:
982 i.admin_up()
983 i.config_ip4()
984 i.resolve_arp()
985 i.config_ip6()
986 i.resolve_ndp()
Klement Sekera75e7d132017-09-20 08:26:30 +0200987
Klement Sekera75e7d132017-09-20 08:26:30 +0200988 cls.packet_sizes = [64, 512, 1518, 9018]
989 cls.padding = " abcdefghijklmn"
990
991 def setUp(self):
992 """ Test setup - force timeout on existing reassemblies """
993 super(TestFIFReassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +0100994 self.vapi.ip_reassembly_enable_disable(
995 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
996 enable_ip6=True)
997 self.vapi.ip_reassembly_enable_disable(
998 sw_if_index=self.dst_if.sw_if_index, enable_ip4=True,
999 enable_ip6=True)
Klement Sekera75e7d132017-09-20 08:26:30 +02001000 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1001 expire_walk_interval_ms=10)
1002 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1003 expire_walk_interval_ms=10, is_ip6=1)
1004 self.sleep(.25)
1005 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1006 expire_walk_interval_ms=10000)
1007 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1008 expire_walk_interval_ms=10000, is_ip6=1)
1009
1010 def tearDown(self):
1011 self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
1012 self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
1013 super(TestFIFReassembly, self).tearDown()
1014
1015 def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):
1016 """Verify captured packet stream.
1017
1018 :param list capture: Captured packet stream.
1019 """
1020 info = None
1021 seen = set()
1022 for packet in capture:
1023 try:
Klement Sekera4c533132018-02-22 11:41:12 +01001024 self.logger.debug(ppp("Got packet:", packet))
Klement Sekera75e7d132017-09-20 08:26:30 +02001025 ip = packet[ip_class]
1026 udp = packet[UDP]
1027 payload_info = self.payload_to_info(str(packet[Raw]))
1028 packet_index = payload_info.index
1029 self.assertTrue(
1030 packet_index not in dropped_packet_indexes,
1031 ppp("Packet received, but should be dropped:", packet))
1032 if packet_index in seen:
1033 raise Exception(ppp("Duplicate packet received", packet))
1034 seen.add(packet_index)
Klement Sekera4c533132018-02-22 11:41:12 +01001035 self.assertEqual(payload_info.dst, self.dst_if.sw_if_index)
Klement Sekera75e7d132017-09-20 08:26:30 +02001036 info = self._packet_infos[packet_index]
1037 self.assertTrue(info is not None)
1038 self.assertEqual(packet_index, info.index)
1039 saved_packet = info.data
1040 self.assertEqual(ip.src, saved_packet[ip_class].src)
1041 self.assertEqual(ip.dst, saved_packet[ip_class].dst)
1042 self.assertEqual(udp.payload, saved_packet[UDP].payload)
Klement Sekera4c533132018-02-22 11:41:12 +01001043 except Exception:
Klement Sekera75e7d132017-09-20 08:26:30 +02001044 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1045 raise
1046 for index in self._packet_infos:
1047 self.assertTrue(index in seen or index in dropped_packet_indexes,
1048 "Packet with packet_index %d not received" % index)
1049
1050 def test_fif4(self):
1051 """ Fragments in fragments (4o4) """
1052
1053 # TODO this should be ideally in setUpClass, but then we hit a bug
1054 # with VppIpRoute incorrectly reporting it's present when it's not
1055 # so we need to manually remove the vpp config, thus we cannot have
1056 # it shared for multiple test cases
1057 self.tun_ip4 = "1.1.1.2"
1058
Klement Sekera4c533132018-02-22 11:41:12 +01001059 self.gre4 = VppGreInterface(self, self.src_if.local_ip4, self.tun_ip4)
Klement Sekera75e7d132017-09-20 08:26:30 +02001060 self.gre4.add_vpp_config()
1061 self.gre4.admin_up()
1062 self.gre4.config_ip4()
1063
Klement Sekera4c533132018-02-22 11:41:12 +01001064 self.vapi.ip_reassembly_enable_disable(
1065 sw_if_index=self.gre4.sw_if_index, enable_ip4=True)
1066
Klement Sekera75e7d132017-09-20 08:26:30 +02001067 self.route4 = VppIpRoute(self, self.tun_ip4, 32,
Klement Sekera4c533132018-02-22 11:41:12 +01001068 [VppRoutePath(self.src_if.remote_ip4,
1069 self.src_if.sw_if_index)])
Klement Sekera75e7d132017-09-20 08:26:30 +02001070 self.route4.add_vpp_config()
1071
1072 self.reset_packet_infos()
1073 for i in range(test_packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01001074 info = self.create_packet_info(self.src_if, self.dst_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02001075 payload = self.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +01001076 # Ethernet header here is only for size calculation, thus it
1077 # doesn't matter how it's initialized. This is to ensure that
1078 # reassembled packet is not > 9000 bytes, so that it's not dropped
1079 p = (Ether() /
1080 IP(id=i, src=self.src_if.remote_ip4,
1081 dst=self.dst_if.remote_ip4) /
1082 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001083 Raw(payload))
1084 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1085 self.extend_packet(p, size, self.padding)
Klement Sekera4c533132018-02-22 11:41:12 +01001086 info.data = p[IP] # use only IP part, without ethernet header
Klement Sekera75e7d132017-09-20 08:26:30 +02001087
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -08001088 fragments = [x for _, p in six.iteritems(self._packet_infos)
Klement Sekera75e7d132017-09-20 08:26:30 +02001089 for x in fragment_rfc791(p.data, 400)]
1090
1091 encapped_fragments = \
Klement Sekera4c533132018-02-22 11:41:12 +01001092 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1093 IP(src=self.tun_ip4, dst=self.src_if.local_ip4) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001094 GRE() /
1095 p
1096 for p in fragments]
1097
1098 fragmented_encapped_fragments = \
1099 [x for p in encapped_fragments
1100 for x in fragment_rfc791(p, 200)]
1101
Klement Sekera4c533132018-02-22 11:41:12 +01001102 self.src_if.add_stream(fragmented_encapped_fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001103
1104 self.pg_enable_capture(self.pg_interfaces)
1105 self.pg_start()
1106
Klement Sekera4c533132018-02-22 11:41:12 +01001107 self.src_if.assert_nothing_captured()
1108 packets = self.dst_if.get_capture(len(self._packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +02001109 self.verify_capture(packets, IP)
1110
1111 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
1112 # so that it's query_vpp_config() works as it should
1113 self.gre4.remove_vpp_config()
Klement Sekera4c533132018-02-22 11:41:12 +01001114 self.logger.debug(self.vapi.ppcli("show interface"))
Klement Sekera75e7d132017-09-20 08:26:30 +02001115
1116 def test_fif6(self):
1117 """ Fragments in fragments (6o6) """
1118 # TODO this should be ideally in setUpClass, but then we hit a bug
1119 # with VppIpRoute incorrectly reporting it's present when it's not
1120 # so we need to manually remove the vpp config, thus we cannot have
1121 # it shared for multiple test cases
1122 self.tun_ip6 = "1002::1"
1123
Klement Sekera4c533132018-02-22 11:41:12 +01001124 self.gre6 = VppGre6Interface(self, self.src_if.local_ip6, self.tun_ip6)
Klement Sekera75e7d132017-09-20 08:26:30 +02001125 self.gre6.add_vpp_config()
1126 self.gre6.admin_up()
1127 self.gre6.config_ip6()
1128
Klement Sekera4c533132018-02-22 11:41:12 +01001129 self.vapi.ip_reassembly_enable_disable(
1130 sw_if_index=self.gre6.sw_if_index, enable_ip6=True)
1131
Klement Sekera75e7d132017-09-20 08:26:30 +02001132 self.route6 = VppIpRoute(self, self.tun_ip6, 128,
Klement Sekera4c533132018-02-22 11:41:12 +01001133 [VppRoutePath(self.src_if.remote_ip6,
1134 self.src_if.sw_if_index,
Klement Sekera75e7d132017-09-20 08:26:30 +02001135 proto=DpoProto.DPO_PROTO_IP6)],
1136 is_ip6=1)
1137 self.route6.add_vpp_config()
1138
1139 self.reset_packet_infos()
1140 for i in range(test_packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01001141 info = self.create_packet_info(self.src_if, self.dst_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02001142 payload = self.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +01001143 # Ethernet header here is only for size calculation, thus it
1144 # doesn't matter how it's initialized. This is to ensure that
1145 # reassembled packet is not > 9000 bytes, so that it's not dropped
1146 p = (Ether() /
1147 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1148 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001149 Raw(payload))
1150 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1151 self.extend_packet(p, size, self.padding)
Klement Sekera4c533132018-02-22 11:41:12 +01001152 info.data = p[IPv6] # use only IPv6 part, without ethernet header
Klement Sekera75e7d132017-09-20 08:26:30 +02001153
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -08001154 fragments = [x for _, i in six.iteritems(self._packet_infos)
Klement Sekera75e7d132017-09-20 08:26:30 +02001155 for x in fragment_rfc8200(
1156 i.data, i.index, 400)]
1157
1158 encapped_fragments = \
Klement Sekera4c533132018-02-22 11:41:12 +01001159 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1160 IPv6(src=self.tun_ip6, dst=self.src_if.local_ip6) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001161 GRE() /
1162 p
1163 for p in fragments]
1164
1165 fragmented_encapped_fragments = \
1166 [x for p in encapped_fragments for x in (
1167 fragment_rfc8200(
1168 p,
1169 2 * len(self._packet_infos) + p[IPv6ExtHdrFragment].id,
1170 200)
1171 if IPv6ExtHdrFragment in p else [p]
1172 )
1173 ]
1174
Klement Sekera4c533132018-02-22 11:41:12 +01001175 self.src_if.add_stream(fragmented_encapped_fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001176
1177 self.pg_enable_capture(self.pg_interfaces)
1178 self.pg_start()
1179
Klement Sekera4c533132018-02-22 11:41:12 +01001180 self.src_if.assert_nothing_captured()
1181 packets = self.dst_if.get_capture(len(self._packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +02001182 self.verify_capture(packets, IPv6)
1183
1184 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
1185 # so that it's query_vpp_config() works as it should
Klement Sekera3ecc2212018-03-27 10:34:43 +02001186 self.gre6.remove_vpp_config()
Klement Sekera75e7d132017-09-20 08:26:30 +02001187
1188
1189if __name__ == '__main__':
1190 unittest.main(testRunner=VppTestRunner)