blob: 828c4d1a56e749b0da6ae390dfe4f0991cf77e15 [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
Klement Sekerad0f70a32018-12-14 17:24:13 +010020# 35 is enough to have >257 400-byte fragments
21test_packet_count = 35
Klement Sekera75e7d132017-09-20 08:26:30 +020022
23
24class TestIPv4Reassembly(VppTestCase):
25 """ IPv4 Reassembly """
26
27 @classmethod
28 def setUpClass(cls):
29 super(TestIPv4Reassembly, cls).setUpClass()
30
Klement Sekera4c533132018-02-22 11:41:12 +010031 cls.create_pg_interfaces([0, 1])
32 cls.src_if = cls.pg0
33 cls.dst_if = cls.pg1
Klement Sekera75e7d132017-09-20 08:26:30 +020034
35 # setup all interfaces
36 for i in cls.pg_interfaces:
37 i.admin_up()
38 i.config_ip4()
39 i.resolve_arp()
40
Klement Sekera75e7d132017-09-20 08:26:30 +020041 # packet sizes
42 cls.packet_sizes = [64, 512, 1518, 9018]
43 cls.padding = " abcdefghijklmn"
44 cls.create_stream(cls.packet_sizes)
45 cls.create_fragments()
46
47 def setUp(self):
48 """ Test setup - force timeout on existing reassemblies """
49 super(TestIPv4Reassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +010050 self.vapi.ip_reassembly_enable_disable(
51 sw_if_index=self.src_if.sw_if_index, enable_ip4=True)
Klement Sekera75e7d132017-09-20 08:26:30 +020052 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
53 expire_walk_interval_ms=10)
54 self.sleep(.25)
55 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
56 expire_walk_interval_ms=10000)
57
58 def tearDown(self):
59 super(TestIPv4Reassembly, self).tearDown()
60 self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
61
62 @classmethod
63 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
Klement Sekerad0f70a32018-12-14 17:24:13 +010064 """Create input packet stream
Klement Sekera75e7d132017-09-20 08:26:30 +020065
66 :param list packet_sizes: Required packet sizes.
67 """
68 for i in range(0, packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +010069 info = cls.create_packet_info(cls.src_if, cls.src_if)
Klement Sekera75e7d132017-09-20 08:26:30 +020070 payload = cls.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +010071 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
72 IP(id=info.index, src=cls.src_if.remote_ip4,
73 dst=cls.dst_if.remote_ip4) /
74 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +020075 Raw(payload))
76 size = packet_sizes[(i // 2) % len(packet_sizes)]
77 cls.extend_packet(p, size, cls.padding)
78 info.data = p
79
80 @classmethod
81 def create_fragments(cls):
82 infos = cls._packet_infos
83 cls.pkt_infos = []
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -080084 for index, info in six.iteritems(infos):
Klement Sekera75e7d132017-09-20 08:26:30 +020085 p = info.data
Klement Sekera4c533132018-02-22 11:41:12 +010086 # cls.logger.debug(ppp("Packet:", p.__class__(str(p))))
Klement Sekera75e7d132017-09-20 08:26:30 +020087 fragments_400 = fragment_rfc791(p, 400)
88 fragments_300 = fragment_rfc791(p, 300)
89 fragments_200 = [
90 x for f in fragments_400 for x in fragment_rfc791(f, 200)]
91 cls.pkt_infos.append(
92 (index, fragments_400, fragments_300, fragments_200))
93 cls.fragments_400 = [
94 x for (_, frags, _, _) in cls.pkt_infos for x in frags]
95 cls.fragments_300 = [
96 x for (_, _, frags, _) in cls.pkt_infos for x in frags]
97 cls.fragments_200 = [
98 x for (_, _, _, frags) in cls.pkt_infos for x in frags]
99 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
100 "%s 300-byte fragments and %s 200-byte fragments" %
101 (len(infos), len(cls.fragments_400),
102 len(cls.fragments_300), len(cls.fragments_200)))
103
104 def verify_capture(self, capture, dropped_packet_indexes=[]):
105 """Verify captured packet stream.
106
107 :param list capture: Captured packet stream.
108 """
109 info = None
110 seen = set()
111 for packet in capture:
112 try:
Klement Sekera4c533132018-02-22 11:41:12 +0100113 self.logger.debug(ppp("Got packet:", packet))
Klement Sekera75e7d132017-09-20 08:26:30 +0200114 ip = packet[IP]
115 udp = packet[UDP]
116 payload_info = self.payload_to_info(str(packet[Raw]))
117 packet_index = payload_info.index
118 self.assertTrue(
119 packet_index not in dropped_packet_indexes,
120 ppp("Packet received, but should be dropped:", packet))
121 if packet_index in seen:
122 raise Exception(ppp("Duplicate packet received", packet))
123 seen.add(packet_index)
Klement Sekera4c533132018-02-22 11:41:12 +0100124 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
Klement Sekera75e7d132017-09-20 08:26:30 +0200125 info = self._packet_infos[packet_index]
126 self.assertTrue(info is not None)
127 self.assertEqual(packet_index, info.index)
128 saved_packet = info.data
129 self.assertEqual(ip.src, saved_packet[IP].src)
130 self.assertEqual(ip.dst, saved_packet[IP].dst)
131 self.assertEqual(udp.payload, saved_packet[UDP].payload)
Klement Sekera4c533132018-02-22 11:41:12 +0100132 except Exception:
Klement Sekera75e7d132017-09-20 08:26:30 +0200133 self.logger.error(ppp("Unexpected or invalid packet:", packet))
134 raise
135 for index in self._packet_infos:
136 self.assertTrue(index in seen or index in dropped_packet_indexes,
137 "Packet with packet_index %d not received" % index)
138
139 def test_reassembly(self):
140 """ basic reassembly """
141
142 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100143 self.src_if.add_stream(self.fragments_200)
Klement Sekera75e7d132017-09-20 08:26:30 +0200144 self.pg_start()
145
Klement Sekera4c533132018-02-22 11:41:12 +0100146 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200147 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100148 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200149
150 # run it all again to verify correctness
151 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100152 self.src_if.add_stream(self.fragments_200)
Klement Sekera75e7d132017-09-20 08:26:30 +0200153 self.pg_start()
154
Klement Sekera4c533132018-02-22 11:41:12 +0100155 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200156 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100157 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200158
159 def test_reversed(self):
160 """ reverse order reassembly """
161
162 fragments = list(self.fragments_200)
163 fragments.reverse()
164
165 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100166 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200167 self.pg_start()
168
Klement Sekera4c533132018-02-22 11:41:12 +0100169 packets = self.dst_if.get_capture(len(self.packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200170 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100171 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200172
173 # run it all again to verify correctness
174 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100175 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200176 self.pg_start()
177
Klement Sekera4c533132018-02-22 11:41:12 +0100178 packets = self.dst_if.get_capture(len(self.packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200179 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100180 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200181
Klement Sekera14d7e902018-12-10 13:46:09 +0100182 def test_5737(self):
183 """ fragment length + ip header size > 65535 """
Klement Sekera4ee633e2018-12-14 12:00:44 +0100184 self.vapi.cli("clear errors")
Klement Sekera14d7e902018-12-10 13:46:09 +0100185 raw = ('E\x00\x00\x88,\xf8\x1f\xfe@\x01\x98\x00\xc0\xa8\n-\xc0\xa8\n'
186 '\x01\x08\x00\xf0J\xed\xcb\xf1\xf5Test-group: IPv4.IPv4.ipv4-'
187 'message.Ethernet-Payload.IPv4-Packet.IPv4-Header.Fragment-Of'
188 'fset; Test-case: 5737')
189
190 malformed_packet = (Ether(dst=self.src_if.local_mac,
191 src=self.src_if.remote_mac) /
192 IP(raw))
193 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
194 IP(id=1000, src=self.src_if.remote_ip4,
195 dst=self.dst_if.remote_ip4) /
196 UDP(sport=1234, dport=5678) /
197 Raw("X" * 1000))
198 valid_fragments = fragment_rfc791(p, 400)
199
200 self.pg_enable_capture()
201 self.src_if.add_stream([malformed_packet] + valid_fragments)
202 self.pg_start()
203
204 self.dst_if.get_capture(1)
Klement Sekera4ee633e2018-12-14 12:00:44 +0100205 self.assert_packet_counter_equal("ip4-reassembly-feature", 1)
206 # TODO remove above, uncomment below once clearing of counters
207 # is supported
208 # self.assert_packet_counter_equal(
209 # "/err/ip4-reassembly-feature/malformed packets", 1)
Klement Sekera14d7e902018-12-10 13:46:09 +0100210
Klement Sekera400f6d82018-12-13 14:35:48 +0100211 def test_44924(self):
212 """ compress tiny fragments """
213 packets = [(Ether(dst=self.src_if.local_mac,
214 src=self.src_if.remote_mac) /
215 IP(id=24339, flags="MF", frag=0, ttl=64,
216 src=self.src_if.remote_ip4,
217 dst=self.dst_if.remote_ip4) /
218 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
219 Raw(load='Test-group: IPv4')),
220 (Ether(dst=self.src_if.local_mac,
221 src=self.src_if.remote_mac) /
222 IP(id=24339, flags="MF", frag=3, ttl=64,
223 src=self.src_if.remote_ip4,
224 dst=self.dst_if.remote_ip4) /
225 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
226 Raw(load='.IPv4.Fragmentation.vali')),
227 (Ether(dst=self.src_if.local_mac,
228 src=self.src_if.remote_mac) /
229 IP(id=24339, frag=6, ttl=64,
230 src=self.src_if.remote_ip4,
231 dst=self.dst_if.remote_ip4) /
232 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
233 Raw(load='d; Test-case: 44924'))
234 ]
235
236 self.pg_enable_capture()
237 self.src_if.add_stream(packets)
238 self.pg_start()
239
240 self.dst_if.get_capture(1)
241
Klement Sekera4ee633e2018-12-14 12:00:44 +0100242 def test_frag_1(self):
243 """ fragment of size 1 """
244 self.vapi.cli("clear errors")
245 malformed_packets = [(Ether(dst=self.src_if.local_mac,
246 src=self.src_if.remote_mac) /
247 IP(id=7, len=21, flags="MF", frag=0, ttl=64,
248 src=self.src_if.remote_ip4,
249 dst=self.dst_if.remote_ip4) /
250 ICMP(type="echo-request")),
251 (Ether(dst=self.src_if.local_mac,
252 src=self.src_if.remote_mac) /
253 IP(id=7, len=21, frag=1, ttl=64,
254 src=self.src_if.remote_ip4,
255 dst=self.dst_if.remote_ip4) /
256 Raw(load='\x08')),
257 ]
258
259 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
260 IP(id=1000, src=self.src_if.remote_ip4,
261 dst=self.dst_if.remote_ip4) /
262 UDP(sport=1234, dport=5678) /
263 Raw("X" * 1000))
264 valid_fragments = fragment_rfc791(p, 400)
265
266 self.pg_enable_capture()
267 self.src_if.add_stream(malformed_packets + valid_fragments)
268 self.pg_start()
269
270 self.dst_if.get_capture(1)
271
272 self.assert_packet_counter_equal("ip4-reassembly-feature", 1)
273 # TODO remove above, uncomment below once clearing of counters
274 # is supported
275 # self.assert_packet_counter_equal(
276 # "/err/ip4-reassembly-feature/malformed packets", 1)
277
juraj.linkes68ebc832018-11-29 09:37:08 +0100278 @unittest.skipIf(is_skip_aarch64_set() and is_platform_aarch64(),
279 "test doesn't work on aarch64")
Klement Sekera75e7d132017-09-20 08:26:30 +0200280 def test_random(self):
281 """ random order reassembly """
282
283 fragments = list(self.fragments_200)
284 shuffle(fragments)
285
286 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100287 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200288 self.pg_start()
289
Klement Sekera4c533132018-02-22 11:41:12 +0100290 packets = self.dst_if.get_capture(len(self.packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200291 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100292 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200293
294 # run it all again to verify correctness
295 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100296 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200297 self.pg_start()
298
Klement Sekera4c533132018-02-22 11:41:12 +0100299 packets = self.dst_if.get_capture(len(self.packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200300 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100301 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200302
303 def test_duplicates(self):
304 """ duplicate fragments """
305
306 fragments = [
307 x for (_, frags, _, _) in self.pkt_infos
308 for x in frags
309 for _ in range(0, min(2, len(frags)))
310 ]
311
312 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100313 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200314 self.pg_start()
315
Klement Sekera4c533132018-02-22 11:41:12 +0100316 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200317 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100318 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200319
320 def test_overlap1(self):
321 """ overlapping fragments case #1 """
322
323 fragments = []
324 for _, _, frags_300, frags_200 in self.pkt_infos:
325 if len(frags_300) == 1:
326 fragments.extend(frags_300)
327 else:
328 for i, j in zip(frags_200, frags_300):
329 fragments.extend(i)
330 fragments.extend(j)
331
332 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100333 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200334 self.pg_start()
335
Klement Sekera4c533132018-02-22 11:41:12 +0100336 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200337 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100338 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200339
340 # run it all to verify correctness
341 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100342 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200343 self.pg_start()
344
Klement Sekera4c533132018-02-22 11:41:12 +0100345 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200346 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100347 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200348
349 def test_overlap2(self):
350 """ overlapping fragments case #2 """
351
352 fragments = []
353 for _, _, frags_300, frags_200 in self.pkt_infos:
354 if len(frags_300) == 1:
355 fragments.extend(frags_300)
356 else:
357 # care must be taken here so that there are no fragments
358 # received by vpp after reassembly is finished, otherwise
359 # new reassemblies will be started and packet generator will
360 # freak out when it detects unfreed buffers
361 zipped = zip(frags_300, frags_200)
362 for i, j in zipped[:-1]:
363 fragments.extend(i)
364 fragments.extend(j)
365 fragments.append(zipped[-1][0])
366
367 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100368 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200369 self.pg_start()
370
Klement Sekera4c533132018-02-22 11:41:12 +0100371 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200372 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100373 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200374
375 # run it all to verify correctness
376 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100377 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200378 self.pg_start()
379
Klement Sekera4c533132018-02-22 11:41:12 +0100380 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200381 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100382 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200383
384 def test_timeout_inline(self):
385 """ timeout (inline) """
386
387 dropped_packet_indexes = set(
388 index for (index, frags, _, _) in self.pkt_infos if len(frags) > 1
389 )
390
391 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
392 expire_walk_interval_ms=10000)
393
394 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100395 self.src_if.add_stream(self.fragments_400)
Klement Sekera75e7d132017-09-20 08:26:30 +0200396 self.pg_start()
397
Klement Sekera4c533132018-02-22 11:41:12 +0100398 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200399 len(self.pkt_infos) - len(dropped_packet_indexes))
400 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100401 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200402
403 def test_timeout_cleanup(self):
404 """ timeout (cleanup) """
405
406 # whole packets + fragmented packets sans last fragment
407 fragments = [
408 x for (_, frags_400, _, _) in self.pkt_infos
409 for x in frags_400[:-1 if len(frags_400) > 1 else None]
410 ]
411
412 # last fragments for fragmented packets
413 fragments2 = [frags_400[-1]
414 for (_, frags_400, _, _) in self.pkt_infos
415 if len(frags_400) > 1]
416
417 dropped_packet_indexes = set(
418 index for (index, frags_400, _, _) in self.pkt_infos
419 if len(frags_400) > 1)
420
421 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
422 expire_walk_interval_ms=50)
423
424 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100425 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200426 self.pg_start()
427
428 self.sleep(.25, "wait before sending rest of fragments")
429
Klement Sekera4c533132018-02-22 11:41:12 +0100430 self.src_if.add_stream(fragments2)
Klement Sekera75e7d132017-09-20 08:26:30 +0200431 self.pg_start()
Klement Sekera75e7d132017-09-20 08:26:30 +0200432
Klement Sekera4c533132018-02-22 11:41:12 +0100433 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200434 len(self.pkt_infos) - len(dropped_packet_indexes))
435 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100436 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200437
438 def test_disabled(self):
439 """ reassembly disabled """
440
441 dropped_packet_indexes = set(
442 index for (index, frags_400, _, _) in self.pkt_infos
443 if len(frags_400) > 1)
444
445 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
446 expire_walk_interval_ms=10000)
447
448 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100449 self.src_if.add_stream(self.fragments_400)
Klement Sekera75e7d132017-09-20 08:26:30 +0200450 self.pg_start()
451
Klement Sekera4c533132018-02-22 11:41:12 +0100452 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200453 len(self.pkt_infos) - len(dropped_packet_indexes))
454 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100455 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200456
457
458class TestIPv6Reassembly(VppTestCase):
459 """ IPv6 Reassembly """
460
461 @classmethod
462 def setUpClass(cls):
463 super(TestIPv6Reassembly, cls).setUpClass()
464
Klement Sekera4c533132018-02-22 11:41:12 +0100465 cls.create_pg_interfaces([0, 1])
466 cls.src_if = cls.pg0
467 cls.dst_if = cls.pg1
Klement Sekera75e7d132017-09-20 08:26:30 +0200468
469 # setup all interfaces
470 for i in cls.pg_interfaces:
471 i.admin_up()
472 i.config_ip6()
473 i.resolve_ndp()
474
Klement Sekera75e7d132017-09-20 08:26:30 +0200475 # packet sizes
476 cls.packet_sizes = [64, 512, 1518, 9018]
477 cls.padding = " abcdefghijklmn"
478 cls.create_stream(cls.packet_sizes)
479 cls.create_fragments()
480
481 def setUp(self):
482 """ Test setup - force timeout on existing reassemblies """
483 super(TestIPv6Reassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +0100484 self.vapi.ip_reassembly_enable_disable(
485 sw_if_index=self.src_if.sw_if_index, enable_ip6=True)
Klement Sekera75e7d132017-09-20 08:26:30 +0200486 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
487 expire_walk_interval_ms=10, is_ip6=1)
488 self.sleep(.25)
489 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
490 expire_walk_interval_ms=10000, is_ip6=1)
Klement Sekera4c533132018-02-22 11:41:12 +0100491 self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
Klement Sekera75e7d132017-09-20 08:26:30 +0200492
493 def tearDown(self):
494 super(TestIPv6Reassembly, self).tearDown()
495 self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
496
497 @classmethod
498 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
499 """Create input packet stream for defined interface.
500
501 :param list packet_sizes: Required packet sizes.
502 """
503 for i in range(0, packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +0100504 info = cls.create_packet_info(cls.src_if, cls.src_if)
Klement Sekera75e7d132017-09-20 08:26:30 +0200505 payload = cls.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +0100506 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
507 IPv6(src=cls.src_if.remote_ip6,
508 dst=cls.dst_if.remote_ip6) /
509 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200510 Raw(payload))
511 size = packet_sizes[(i // 2) % len(packet_sizes)]
512 cls.extend_packet(p, size, cls.padding)
513 info.data = p
514
515 @classmethod
516 def create_fragments(cls):
517 infos = cls._packet_infos
518 cls.pkt_infos = []
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -0800519 for index, info in six.iteritems(infos):
Klement Sekera75e7d132017-09-20 08:26:30 +0200520 p = info.data
Klement Sekera4c533132018-02-22 11:41:12 +0100521 # cls.logger.debug(ppp("Packet:", p.__class__(str(p))))
Klement Sekera75e7d132017-09-20 08:26:30 +0200522 fragments_400 = fragment_rfc8200(p, info.index, 400)
523 fragments_300 = fragment_rfc8200(p, info.index, 300)
524 cls.pkt_infos.append((index, fragments_400, fragments_300))
525 cls.fragments_400 = [
526 x for _, frags, _ in cls.pkt_infos for x in frags]
527 cls.fragments_300 = [
528 x for _, _, frags in cls.pkt_infos for x in frags]
529 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
530 "and %s 300-byte fragments" %
531 (len(infos), len(cls.fragments_400),
532 len(cls.fragments_300)))
533
534 def verify_capture(self, capture, dropped_packet_indexes=[]):
535 """Verify captured packet strea .
536
537 :param list capture: Captured packet stream.
538 """
539 info = None
540 seen = set()
541 for packet in capture:
542 try:
Klement Sekera4c533132018-02-22 11:41:12 +0100543 self.logger.debug(ppp("Got packet:", packet))
Klement Sekera75e7d132017-09-20 08:26:30 +0200544 ip = packet[IPv6]
545 udp = packet[UDP]
546 payload_info = self.payload_to_info(str(packet[Raw]))
547 packet_index = payload_info.index
548 self.assertTrue(
549 packet_index not in dropped_packet_indexes,
550 ppp("Packet received, but should be dropped:", packet))
551 if packet_index in seen:
552 raise Exception(ppp("Duplicate packet received", packet))
553 seen.add(packet_index)
Klement Sekera4c533132018-02-22 11:41:12 +0100554 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
Klement Sekera75e7d132017-09-20 08:26:30 +0200555 info = self._packet_infos[packet_index]
556 self.assertTrue(info is not None)
557 self.assertEqual(packet_index, info.index)
558 saved_packet = info.data
559 self.assertEqual(ip.src, saved_packet[IPv6].src)
560 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
561 self.assertEqual(udp.payload, saved_packet[UDP].payload)
Klement Sekera4c533132018-02-22 11:41:12 +0100562 except Exception:
Klement Sekera75e7d132017-09-20 08:26:30 +0200563 self.logger.error(ppp("Unexpected or invalid packet:", packet))
564 raise
565 for index in self._packet_infos:
566 self.assertTrue(index in seen or index in dropped_packet_indexes,
567 "Packet with packet_index %d not received" % index)
568
569 def test_reassembly(self):
570 """ basic reassembly """
571
572 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100573 self.src_if.add_stream(self.fragments_400)
Klement Sekera75e7d132017-09-20 08:26:30 +0200574 self.pg_start()
575
Klement Sekera4c533132018-02-22 11:41:12 +0100576 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200577 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100578 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200579
580 # run it all again to verify correctness
581 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100582 self.src_if.add_stream(self.fragments_400)
Klement Sekera75e7d132017-09-20 08:26:30 +0200583 self.pg_start()
584
Klement Sekera4c533132018-02-22 11:41:12 +0100585 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200586 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100587 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200588
589 def test_reversed(self):
590 """ reverse order reassembly """
591
592 fragments = list(self.fragments_400)
593 fragments.reverse()
594
595 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100596 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200597 self.pg_start()
598
Klement Sekera4c533132018-02-22 11:41:12 +0100599 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200600 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100601 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200602
603 # run it all again to verify correctness
604 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100605 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200606 self.pg_start()
607
Klement Sekera4c533132018-02-22 11:41:12 +0100608 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200609 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100610 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200611
612 def test_random(self):
613 """ random order reassembly """
614
615 fragments = list(self.fragments_400)
616 shuffle(fragments)
617
618 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100619 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200620 self.pg_start()
621
Klement Sekera4c533132018-02-22 11:41:12 +0100622 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200623 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100624 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200625
626 # run it all again to verify correctness
627 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100628 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200629 self.pg_start()
630
Klement Sekera4c533132018-02-22 11:41:12 +0100631 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200632 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100633 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200634
635 def test_duplicates(self):
636 """ duplicate fragments """
637
638 fragments = [
639 x for (_, frags, _) in self.pkt_infos
640 for x in frags
641 for _ in range(0, min(2, len(frags)))
642 ]
643
644 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100645 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200646 self.pg_start()
647
Klement Sekera4c533132018-02-22 11:41:12 +0100648 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200649 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100650 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200651
652 def test_overlap1(self):
653 """ overlapping fragments case #1 """
654
655 fragments = []
656 for _, frags_400, frags_300 in self.pkt_infos:
657 if len(frags_300) == 1:
658 fragments.extend(frags_400)
659 else:
660 for i, j in zip(frags_300, frags_400):
661 fragments.extend(i)
662 fragments.extend(j)
663
664 dropped_packet_indexes = set(
665 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
666 )
667
668 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100669 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200670 self.pg_start()
671
Klement Sekera4c533132018-02-22 11:41:12 +0100672 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200673 len(self.pkt_infos) - len(dropped_packet_indexes))
674 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100675 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200676
677 def test_overlap2(self):
678 """ overlapping fragments case #2 """
679
680 fragments = []
Klement Sekera4c533132018-02-22 11:41:12 +0100681 for _, frags_400, frags_300 in self.pkt_infos:
Klement Sekera75e7d132017-09-20 08:26:30 +0200682 if len(frags_400) == 1:
683 fragments.extend(frags_400)
684 else:
685 # care must be taken here so that there are no fragments
686 # received by vpp after reassembly is finished, otherwise
687 # new reassemblies will be started and packet generator will
688 # freak out when it detects unfreed buffers
Klement Sekera4c533132018-02-22 11:41:12 +0100689 zipped = zip(frags_400, frags_300)
Klement Sekera75e7d132017-09-20 08:26:30 +0200690 for i, j in zipped[:-1]:
691 fragments.extend(i)
692 fragments.extend(j)
693 fragments.append(zipped[-1][0])
694
695 dropped_packet_indexes = set(
696 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
697 )
698
699 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100700 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200701 self.pg_start()
702
Klement Sekera4c533132018-02-22 11:41:12 +0100703 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200704 len(self.pkt_infos) - len(dropped_packet_indexes))
705 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100706 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200707
708 def test_timeout_inline(self):
709 """ timeout (inline) """
710
711 dropped_packet_indexes = set(
712 index for (index, frags, _) in self.pkt_infos if len(frags) > 1
713 )
714
715 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
716 expire_walk_interval_ms=10000, is_ip6=1)
717
718 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100719 self.src_if.add_stream(self.fragments_400)
Klement Sekera75e7d132017-09-20 08:26:30 +0200720 self.pg_start()
721
Klement Sekera4c533132018-02-22 11:41:12 +0100722 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200723 len(self.pkt_infos) - len(dropped_packet_indexes))
724 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100725 pkts = self.src_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200726 expected_count=len(dropped_packet_indexes))
727 for icmp in pkts:
728 self.assertIn(ICMPv6TimeExceeded, icmp)
729 self.assertIn(IPv6ExtHdrFragment, icmp)
730 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
731 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
732
733 def test_timeout_cleanup(self):
734 """ timeout (cleanup) """
735
736 # whole packets + fragmented packets sans last fragment
737 fragments = [
738 x for (_, frags_400, _) in self.pkt_infos
739 for x in frags_400[:-1 if len(frags_400) > 1 else None]
740 ]
741
742 # last fragments for fragmented packets
743 fragments2 = [frags_400[-1]
744 for (_, frags_400, _) in self.pkt_infos
745 if len(frags_400) > 1]
746
747 dropped_packet_indexes = set(
748 index for (index, frags_400, _) in self.pkt_infos
749 if len(frags_400) > 1)
750
751 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
752 expire_walk_interval_ms=50)
753
754 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
755 expire_walk_interval_ms=50, is_ip6=1)
756
757 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100758 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200759 self.pg_start()
760
761 self.sleep(.25, "wait before sending rest of fragments")
762
Klement Sekera4c533132018-02-22 11:41:12 +0100763 self.src_if.add_stream(fragments2)
Klement Sekera75e7d132017-09-20 08:26:30 +0200764 self.pg_start()
Klement Sekera75e7d132017-09-20 08:26:30 +0200765
Klement Sekera4c533132018-02-22 11:41:12 +0100766 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200767 len(self.pkt_infos) - len(dropped_packet_indexes))
768 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100769 pkts = self.src_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200770 expected_count=len(dropped_packet_indexes))
771 for icmp in pkts:
772 self.assertIn(ICMPv6TimeExceeded, icmp)
773 self.assertIn(IPv6ExtHdrFragment, icmp)
774 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
775 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
776
777 def test_disabled(self):
778 """ reassembly disabled """
779
780 dropped_packet_indexes = set(
781 index for (index, frags_400, _) in self.pkt_infos
782 if len(frags_400) > 1)
783
784 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
785 expire_walk_interval_ms=10000, is_ip6=1)
786
787 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100788 self.src_if.add_stream(self.fragments_400)
Klement Sekera75e7d132017-09-20 08:26:30 +0200789 self.pg_start()
790
Klement Sekera4c533132018-02-22 11:41:12 +0100791 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200792 len(self.pkt_infos) - len(dropped_packet_indexes))
793 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100794 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200795
796 def test_missing_upper(self):
797 """ missing upper layer """
Klement Sekera4c533132018-02-22 11:41:12 +0100798 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
799 IPv6(src=self.src_if.remote_ip6,
800 dst=self.src_if.local_ip6) /
801 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200802 Raw())
803 self.extend_packet(p, 1000, self.padding)
804 fragments = fragment_rfc8200(p, 1, 500)
805 bad_fragment = p.__class__(str(fragments[1]))
806 bad_fragment[IPv6ExtHdrFragment].nh = 59
807 bad_fragment[IPv6ExtHdrFragment].offset = 0
808 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100809 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +0200810 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +0100811 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +0200812 icmp = pkts[0]
813 self.assertIn(ICMPv6ParamProblem, icmp)
814 self.assert_equal(icmp[ICMPv6ParamProblem].code, 3, "ICMP code")
815
816 def test_invalid_frag_size(self):
817 """ fragment size not a multiple of 8 """
Klement Sekera4c533132018-02-22 11:41:12 +0100818 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
819 IPv6(src=self.src_if.remote_ip6,
820 dst=self.src_if.local_ip6) /
821 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200822 Raw())
823 self.extend_packet(p, 1000, self.padding)
824 fragments = fragment_rfc8200(p, 1, 500)
825 bad_fragment = fragments[0]
826 self.extend_packet(bad_fragment, len(bad_fragment) + 5)
827 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100828 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +0200829 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +0100830 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +0200831 icmp = pkts[0]
832 self.assertIn(ICMPv6ParamProblem, icmp)
833 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
834
835 def test_invalid_packet_size(self):
836 """ total packet size > 65535 """
Klement Sekera4c533132018-02-22 11:41:12 +0100837 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
838 IPv6(src=self.src_if.remote_ip6,
839 dst=self.src_if.local_ip6) /
840 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200841 Raw())
842 self.extend_packet(p, 1000, self.padding)
843 fragments = fragment_rfc8200(p, 1, 500)
844 bad_fragment = fragments[1]
845 bad_fragment[IPv6ExtHdrFragment].offset = 65500
846 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100847 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +0200848 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +0100849 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +0200850 icmp = pkts[0]
851 self.assertIn(ICMPv6ParamProblem, icmp)
852 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
853
854
Juraj Sloboda3048b632018-10-02 11:13:53 +0200855class TestIPv4ReassemblyLocalNode(VppTestCase):
856 """ IPv4 Reassembly for packets coming to ip4-local node """
857
858 @classmethod
859 def setUpClass(cls):
860 super(TestIPv4ReassemblyLocalNode, cls).setUpClass()
861
862 cls.create_pg_interfaces([0])
863 cls.src_dst_if = cls.pg0
864
865 # setup all interfaces
866 for i in cls.pg_interfaces:
867 i.admin_up()
868 i.config_ip4()
869 i.resolve_arp()
870
871 cls.padding = " abcdefghijklmn"
872 cls.create_stream()
873 cls.create_fragments()
874
875 def setUp(self):
876 """ Test setup - force timeout on existing reassemblies """
877 super(TestIPv4ReassemblyLocalNode, self).setUp()
878 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
879 expire_walk_interval_ms=10)
880 self.sleep(.25)
881 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
882 expire_walk_interval_ms=10000)
883
884 def tearDown(self):
885 super(TestIPv4ReassemblyLocalNode, self).tearDown()
886 self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
887
888 @classmethod
889 def create_stream(cls, packet_count=test_packet_count):
890 """Create input packet stream for defined interface.
891
892 :param list packet_sizes: Required packet sizes.
893 """
894 for i in range(0, packet_count):
895 info = cls.create_packet_info(cls.src_dst_if, cls.src_dst_if)
896 payload = cls.info_to_payload(info)
897 p = (Ether(dst=cls.src_dst_if.local_mac,
898 src=cls.src_dst_if.remote_mac) /
899 IP(id=info.index, src=cls.src_dst_if.remote_ip4,
900 dst=cls.src_dst_if.local_ip4) /
901 ICMP(type='echo-request', id=1234) /
902 Raw(payload))
903 cls.extend_packet(p, 1518, cls.padding)
904 info.data = p
905
906 @classmethod
907 def create_fragments(cls):
908 infos = cls._packet_infos
909 cls.pkt_infos = []
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -0800910 for index, info in six.iteritems(infos):
Juraj Sloboda3048b632018-10-02 11:13:53 +0200911 p = info.data
912 # cls.logger.debug(ppp("Packet:", p.__class__(str(p))))
913 fragments_300 = fragment_rfc791(p, 300)
914 cls.pkt_infos.append((index, fragments_300))
915 cls.fragments_300 = [x for (_, frags) in cls.pkt_infos for x in frags]
916 cls.logger.debug("Fragmented %s packets into %s 300-byte fragments" %
917 (len(infos), len(cls.fragments_300)))
918
919 def verify_capture(self, capture):
920 """Verify captured packet stream.
921
922 :param list capture: Captured packet stream.
923 """
924 info = None
925 seen = set()
926 for packet in capture:
927 try:
928 self.logger.debug(ppp("Got packet:", packet))
929 ip = packet[IP]
930 icmp = packet[ICMP]
931 payload_info = self.payload_to_info(str(packet[Raw]))
932 packet_index = payload_info.index
933 if packet_index in seen:
934 raise Exception(ppp("Duplicate packet received", packet))
935 seen.add(packet_index)
936 self.assertEqual(payload_info.dst, self.src_dst_if.sw_if_index)
937 info = self._packet_infos[packet_index]
Klement Sekera14d7e902018-12-10 13:46:09 +0100938 self.assertIsNotNone(info)
Juraj Sloboda3048b632018-10-02 11:13:53 +0200939 self.assertEqual(packet_index, info.index)
940 saved_packet = info.data
941 self.assertEqual(ip.src, saved_packet[IP].dst)
942 self.assertEqual(ip.dst, saved_packet[IP].src)
943 self.assertEqual(icmp.type, 0) # echo reply
944 self.assertEqual(icmp.id, saved_packet[ICMP].id)
945 self.assertEqual(icmp.payload, saved_packet[ICMP].payload)
946 except Exception:
947 self.logger.error(ppp("Unexpected or invalid packet:", packet))
948 raise
949 for index in self._packet_infos:
Klement Sekera14d7e902018-12-10 13:46:09 +0100950 self.assertIn(index, seen,
951 "Packet with packet_index %d not received" % index)
Juraj Sloboda3048b632018-10-02 11:13:53 +0200952
953 def test_reassembly(self):
954 """ basic reassembly """
955
956 self.pg_enable_capture()
957 self.src_dst_if.add_stream(self.fragments_300)
958 self.pg_start()
959
960 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
961 self.verify_capture(packets)
962
963 # run it all again to verify correctness
964 self.pg_enable_capture()
965 self.src_dst_if.add_stream(self.fragments_300)
966 self.pg_start()
967
968 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
969 self.verify_capture(packets)
970
971
Klement Sekera75e7d132017-09-20 08:26:30 +0200972class TestFIFReassembly(VppTestCase):
973 """ Fragments in fragments reassembly """
974
975 @classmethod
976 def setUpClass(cls):
977 super(TestFIFReassembly, cls).setUpClass()
978
Klement Sekera4c533132018-02-22 11:41:12 +0100979 cls.create_pg_interfaces([0, 1])
980 cls.src_if = cls.pg0
981 cls.dst_if = cls.pg1
982 for i in cls.pg_interfaces:
983 i.admin_up()
984 i.config_ip4()
985 i.resolve_arp()
986 i.config_ip6()
987 i.resolve_ndp()
Klement Sekera75e7d132017-09-20 08:26:30 +0200988
Klement Sekera75e7d132017-09-20 08:26:30 +0200989 cls.packet_sizes = [64, 512, 1518, 9018]
990 cls.padding = " abcdefghijklmn"
991
992 def setUp(self):
993 """ Test setup - force timeout on existing reassemblies """
994 super(TestFIFReassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +0100995 self.vapi.ip_reassembly_enable_disable(
996 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
997 enable_ip6=True)
998 self.vapi.ip_reassembly_enable_disable(
999 sw_if_index=self.dst_if.sw_if_index, enable_ip4=True,
1000 enable_ip6=True)
Klement Sekera75e7d132017-09-20 08:26:30 +02001001 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1002 expire_walk_interval_ms=10)
1003 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1004 expire_walk_interval_ms=10, is_ip6=1)
1005 self.sleep(.25)
1006 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1007 expire_walk_interval_ms=10000)
1008 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1009 expire_walk_interval_ms=10000, is_ip6=1)
1010
1011 def tearDown(self):
1012 self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
1013 self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
1014 super(TestFIFReassembly, self).tearDown()
1015
1016 def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):
1017 """Verify captured packet stream.
1018
1019 :param list capture: Captured packet stream.
1020 """
1021 info = None
1022 seen = set()
1023 for packet in capture:
1024 try:
Klement Sekera4c533132018-02-22 11:41:12 +01001025 self.logger.debug(ppp("Got packet:", packet))
Klement Sekera75e7d132017-09-20 08:26:30 +02001026 ip = packet[ip_class]
1027 udp = packet[UDP]
1028 payload_info = self.payload_to_info(str(packet[Raw]))
1029 packet_index = payload_info.index
1030 self.assertTrue(
1031 packet_index not in dropped_packet_indexes,
1032 ppp("Packet received, but should be dropped:", packet))
1033 if packet_index in seen:
1034 raise Exception(ppp("Duplicate packet received", packet))
1035 seen.add(packet_index)
Klement Sekera4c533132018-02-22 11:41:12 +01001036 self.assertEqual(payload_info.dst, self.dst_if.sw_if_index)
Klement Sekera75e7d132017-09-20 08:26:30 +02001037 info = self._packet_infos[packet_index]
1038 self.assertTrue(info is not None)
1039 self.assertEqual(packet_index, info.index)
1040 saved_packet = info.data
1041 self.assertEqual(ip.src, saved_packet[ip_class].src)
1042 self.assertEqual(ip.dst, saved_packet[ip_class].dst)
1043 self.assertEqual(udp.payload, saved_packet[UDP].payload)
Klement Sekera4c533132018-02-22 11:41:12 +01001044 except Exception:
Klement Sekera75e7d132017-09-20 08:26:30 +02001045 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1046 raise
1047 for index in self._packet_infos:
1048 self.assertTrue(index in seen or index in dropped_packet_indexes,
1049 "Packet with packet_index %d not received" % index)
1050
1051 def test_fif4(self):
1052 """ Fragments in fragments (4o4) """
1053
1054 # TODO this should be ideally in setUpClass, but then we hit a bug
1055 # with VppIpRoute incorrectly reporting it's present when it's not
1056 # so we need to manually remove the vpp config, thus we cannot have
1057 # it shared for multiple test cases
1058 self.tun_ip4 = "1.1.1.2"
1059
Klement Sekera4c533132018-02-22 11:41:12 +01001060 self.gre4 = VppGreInterface(self, self.src_if.local_ip4, self.tun_ip4)
Klement Sekera75e7d132017-09-20 08:26:30 +02001061 self.gre4.add_vpp_config()
1062 self.gre4.admin_up()
1063 self.gre4.config_ip4()
1064
Klement Sekera4c533132018-02-22 11:41:12 +01001065 self.vapi.ip_reassembly_enable_disable(
1066 sw_if_index=self.gre4.sw_if_index, enable_ip4=True)
1067
Klement Sekera75e7d132017-09-20 08:26:30 +02001068 self.route4 = VppIpRoute(self, self.tun_ip4, 32,
Klement Sekera4c533132018-02-22 11:41:12 +01001069 [VppRoutePath(self.src_if.remote_ip4,
1070 self.src_if.sw_if_index)])
Klement Sekera75e7d132017-09-20 08:26:30 +02001071 self.route4.add_vpp_config()
1072
1073 self.reset_packet_infos()
1074 for i in range(test_packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01001075 info = self.create_packet_info(self.src_if, self.dst_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02001076 payload = self.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +01001077 # Ethernet header here is only for size calculation, thus it
1078 # doesn't matter how it's initialized. This is to ensure that
1079 # reassembled packet is not > 9000 bytes, so that it's not dropped
1080 p = (Ether() /
1081 IP(id=i, src=self.src_if.remote_ip4,
1082 dst=self.dst_if.remote_ip4) /
1083 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001084 Raw(payload))
1085 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1086 self.extend_packet(p, size, self.padding)
Klement Sekera4c533132018-02-22 11:41:12 +01001087 info.data = p[IP] # use only IP part, without ethernet header
Klement Sekera75e7d132017-09-20 08:26:30 +02001088
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -08001089 fragments = [x for _, p in six.iteritems(self._packet_infos)
Klement Sekera75e7d132017-09-20 08:26:30 +02001090 for x in fragment_rfc791(p.data, 400)]
1091
1092 encapped_fragments = \
Klement Sekera4c533132018-02-22 11:41:12 +01001093 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1094 IP(src=self.tun_ip4, dst=self.src_if.local_ip4) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001095 GRE() /
1096 p
1097 for p in fragments]
1098
1099 fragmented_encapped_fragments = \
1100 [x for p in encapped_fragments
1101 for x in fragment_rfc791(p, 200)]
1102
Klement Sekera4c533132018-02-22 11:41:12 +01001103 self.src_if.add_stream(fragmented_encapped_fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001104
1105 self.pg_enable_capture(self.pg_interfaces)
1106 self.pg_start()
1107
Klement Sekera4c533132018-02-22 11:41:12 +01001108 self.src_if.assert_nothing_captured()
1109 packets = self.dst_if.get_capture(len(self._packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +02001110 self.verify_capture(packets, IP)
1111
1112 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
1113 # so that it's query_vpp_config() works as it should
1114 self.gre4.remove_vpp_config()
Klement Sekera4c533132018-02-22 11:41:12 +01001115 self.logger.debug(self.vapi.ppcli("show interface"))
Klement Sekera75e7d132017-09-20 08:26:30 +02001116
1117 def test_fif6(self):
1118 """ Fragments in fragments (6o6) """
1119 # TODO this should be ideally in setUpClass, but then we hit a bug
1120 # with VppIpRoute incorrectly reporting it's present when it's not
1121 # so we need to manually remove the vpp config, thus we cannot have
1122 # it shared for multiple test cases
1123 self.tun_ip6 = "1002::1"
1124
Klement Sekera4c533132018-02-22 11:41:12 +01001125 self.gre6 = VppGre6Interface(self, self.src_if.local_ip6, self.tun_ip6)
Klement Sekera75e7d132017-09-20 08:26:30 +02001126 self.gre6.add_vpp_config()
1127 self.gre6.admin_up()
1128 self.gre6.config_ip6()
1129
Klement Sekera4c533132018-02-22 11:41:12 +01001130 self.vapi.ip_reassembly_enable_disable(
1131 sw_if_index=self.gre6.sw_if_index, enable_ip6=True)
1132
Klement Sekera75e7d132017-09-20 08:26:30 +02001133 self.route6 = VppIpRoute(self, self.tun_ip6, 128,
Klement Sekera4c533132018-02-22 11:41:12 +01001134 [VppRoutePath(self.src_if.remote_ip6,
1135 self.src_if.sw_if_index,
Klement Sekera75e7d132017-09-20 08:26:30 +02001136 proto=DpoProto.DPO_PROTO_IP6)],
1137 is_ip6=1)
1138 self.route6.add_vpp_config()
1139
1140 self.reset_packet_infos()
1141 for i in range(test_packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01001142 info = self.create_packet_info(self.src_if, self.dst_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02001143 payload = self.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +01001144 # Ethernet header here is only for size calculation, thus it
1145 # doesn't matter how it's initialized. This is to ensure that
1146 # reassembled packet is not > 9000 bytes, so that it's not dropped
1147 p = (Ether() /
1148 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1149 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001150 Raw(payload))
1151 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1152 self.extend_packet(p, size, self.padding)
Klement Sekera4c533132018-02-22 11:41:12 +01001153 info.data = p[IPv6] # use only IPv6 part, without ethernet header
Klement Sekera75e7d132017-09-20 08:26:30 +02001154
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -08001155 fragments = [x for _, i in six.iteritems(self._packet_infos)
Klement Sekera75e7d132017-09-20 08:26:30 +02001156 for x in fragment_rfc8200(
1157 i.data, i.index, 400)]
1158
1159 encapped_fragments = \
Klement Sekera4c533132018-02-22 11:41:12 +01001160 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1161 IPv6(src=self.tun_ip6, dst=self.src_if.local_ip6) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001162 GRE() /
1163 p
1164 for p in fragments]
1165
1166 fragmented_encapped_fragments = \
1167 [x for p in encapped_fragments for x in (
1168 fragment_rfc8200(
1169 p,
1170 2 * len(self._packet_infos) + p[IPv6ExtHdrFragment].id,
1171 200)
1172 if IPv6ExtHdrFragment in p else [p]
1173 )
1174 ]
1175
Klement Sekera4c533132018-02-22 11:41:12 +01001176 self.src_if.add_stream(fragmented_encapped_fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001177
1178 self.pg_enable_capture(self.pg_interfaces)
1179 self.pg_start()
1180
Klement Sekera4c533132018-02-22 11:41:12 +01001181 self.src_if.assert_nothing_captured()
1182 packets = self.dst_if.get_capture(len(self._packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +02001183 self.verify_capture(packets, IPv6)
1184
1185 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
1186 # so that it's query_vpp_config() works as it should
Klement Sekera3ecc2212018-03-27 10:34:43 +02001187 self.gre6.remove_vpp_config()
Klement Sekera75e7d132017-09-20 08:26:30 +02001188
1189
1190if __name__ == '__main__':
1191 unittest.main(testRunner=VppTestRunner)