blob: 6c0513ee76d5cef6d04c722525fbd3f0c6b490dd [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.linkese33cab32018-12-20 14:53:27 +01007from framework import VppTestCase, VppTestRunner
Klement Sekera75e7d132017-09-20 08:26:30 +02008
9from scapy.packet import Raw
10from scapy.layers.l2 import Ether, GRE
Juraj Sloboda3048b632018-10-02 11:13:53 +020011from scapy.layers.inet import IP, UDP, ICMP
Klement Sekera75e7d132017-09-20 08:26:30 +020012from util import ppp, fragment_rfc791, fragment_rfc8200
Klement Sekera75e7d132017-09-20 08:26:30 +020013from scapy.layers.inet6 import IPv6, IPv6ExtHdrFragment, ICMPv6ParamProblem,\
14 ICMPv6TimeExceeded
15from vpp_gre_interface import VppGreInterface, VppGre6Interface
Neale Rannsc0a93142018-09-05 15:42:26 -070016from vpp_ip import DpoProto
17from vpp_ip_route import VppIpRoute, VppRoutePath
Klement Sekera75e7d132017-09-20 08:26:30 +020018
Klement Sekerad0f70a32018-12-14 17:24:13 +010019# 35 is enough to have >257 400-byte fragments
20test_packet_count = 35
Klement Sekera75e7d132017-09-20 08:26:30 +020021
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):
Klement Sekerad0f70a32018-12-14 17:24:13 +010063 """Create input packet stream
Klement Sekera75e7d132017-09-20 08:26:30 +020064
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
Klement Sekera75e7d132017-09-20 08:26:30 +0200277 def test_random(self):
278 """ random order reassembly """
279
280 fragments = list(self.fragments_200)
281 shuffle(fragments)
282
283 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100284 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200285 self.pg_start()
286
Klement Sekera4c533132018-02-22 11:41:12 +0100287 packets = self.dst_if.get_capture(len(self.packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200288 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100289 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200290
291 # run it all again to verify correctness
292 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100293 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200294 self.pg_start()
295
Klement Sekera4c533132018-02-22 11:41:12 +0100296 packets = self.dst_if.get_capture(len(self.packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200297 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100298 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200299
300 def test_duplicates(self):
301 """ duplicate fragments """
302
303 fragments = [
304 x for (_, frags, _, _) in self.pkt_infos
305 for x in frags
306 for _ in range(0, min(2, len(frags)))
307 ]
308
309 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100310 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200311 self.pg_start()
312
Klement Sekera4c533132018-02-22 11:41:12 +0100313 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200314 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100315 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200316
317 def test_overlap1(self):
318 """ overlapping fragments case #1 """
319
320 fragments = []
321 for _, _, frags_300, frags_200 in self.pkt_infos:
322 if len(frags_300) == 1:
323 fragments.extend(frags_300)
324 else:
325 for i, j in zip(frags_200, frags_300):
326 fragments.extend(i)
327 fragments.extend(j)
328
329 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100330 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200331 self.pg_start()
332
Klement Sekera4c533132018-02-22 11:41:12 +0100333 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200334 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100335 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200336
337 # run it all to verify correctness
338 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100339 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200340 self.pg_start()
341
Klement Sekera4c533132018-02-22 11:41:12 +0100342 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200343 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100344 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200345
346 def test_overlap2(self):
347 """ overlapping fragments case #2 """
348
349 fragments = []
350 for _, _, frags_300, frags_200 in self.pkt_infos:
351 if len(frags_300) == 1:
352 fragments.extend(frags_300)
353 else:
354 # care must be taken here so that there are no fragments
355 # received by vpp after reassembly is finished, otherwise
356 # new reassemblies will be started and packet generator will
357 # freak out when it detects unfreed buffers
358 zipped = zip(frags_300, frags_200)
359 for i, j in zipped[:-1]:
360 fragments.extend(i)
361 fragments.extend(j)
362 fragments.append(zipped[-1][0])
363
364 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100365 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200366 self.pg_start()
367
Klement Sekera4c533132018-02-22 11:41:12 +0100368 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200369 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100370 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200371
372 # run it all to verify correctness
373 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100374 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200375 self.pg_start()
376
Klement Sekera4c533132018-02-22 11:41:12 +0100377 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200378 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100379 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200380
381 def test_timeout_inline(self):
382 """ timeout (inline) """
383
384 dropped_packet_indexes = set(
385 index for (index, frags, _, _) in self.pkt_infos if len(frags) > 1
386 )
387
388 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
389 expire_walk_interval_ms=10000)
390
391 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100392 self.src_if.add_stream(self.fragments_400)
Klement Sekera75e7d132017-09-20 08:26:30 +0200393 self.pg_start()
394
Klement Sekera4c533132018-02-22 11:41:12 +0100395 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200396 len(self.pkt_infos) - len(dropped_packet_indexes))
397 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100398 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200399
400 def test_timeout_cleanup(self):
401 """ timeout (cleanup) """
402
403 # whole packets + fragmented packets sans last fragment
404 fragments = [
405 x for (_, frags_400, _, _) in self.pkt_infos
406 for x in frags_400[:-1 if len(frags_400) > 1 else None]
407 ]
408
409 # last fragments for fragmented packets
410 fragments2 = [frags_400[-1]
411 for (_, frags_400, _, _) in self.pkt_infos
412 if len(frags_400) > 1]
413
414 dropped_packet_indexes = set(
415 index for (index, frags_400, _, _) in self.pkt_infos
416 if len(frags_400) > 1)
417
418 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
419 expire_walk_interval_ms=50)
420
421 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100422 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200423 self.pg_start()
424
425 self.sleep(.25, "wait before sending rest of fragments")
426
Klement Sekera4c533132018-02-22 11:41:12 +0100427 self.src_if.add_stream(fragments2)
Klement Sekera75e7d132017-09-20 08:26:30 +0200428 self.pg_start()
Klement Sekera75e7d132017-09-20 08:26:30 +0200429
Klement Sekera4c533132018-02-22 11:41:12 +0100430 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200431 len(self.pkt_infos) - len(dropped_packet_indexes))
432 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100433 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200434
435 def test_disabled(self):
436 """ reassembly disabled """
437
438 dropped_packet_indexes = set(
439 index for (index, frags_400, _, _) in self.pkt_infos
440 if len(frags_400) > 1)
441
442 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
443 expire_walk_interval_ms=10000)
444
445 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100446 self.src_if.add_stream(self.fragments_400)
Klement Sekera75e7d132017-09-20 08:26:30 +0200447 self.pg_start()
448
Klement Sekera4c533132018-02-22 11:41:12 +0100449 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200450 len(self.pkt_infos) - len(dropped_packet_indexes))
451 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100452 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200453
454
455class TestIPv6Reassembly(VppTestCase):
456 """ IPv6 Reassembly """
457
458 @classmethod
459 def setUpClass(cls):
460 super(TestIPv6Reassembly, cls).setUpClass()
461
Klement Sekera4c533132018-02-22 11:41:12 +0100462 cls.create_pg_interfaces([0, 1])
463 cls.src_if = cls.pg0
464 cls.dst_if = cls.pg1
Klement Sekera75e7d132017-09-20 08:26:30 +0200465
466 # setup all interfaces
467 for i in cls.pg_interfaces:
468 i.admin_up()
469 i.config_ip6()
470 i.resolve_ndp()
471
Klement Sekera75e7d132017-09-20 08:26:30 +0200472 # packet sizes
473 cls.packet_sizes = [64, 512, 1518, 9018]
474 cls.padding = " abcdefghijklmn"
475 cls.create_stream(cls.packet_sizes)
476 cls.create_fragments()
477
478 def setUp(self):
479 """ Test setup - force timeout on existing reassemblies """
480 super(TestIPv6Reassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +0100481 self.vapi.ip_reassembly_enable_disable(
482 sw_if_index=self.src_if.sw_if_index, enable_ip6=True)
Klement Sekera75e7d132017-09-20 08:26:30 +0200483 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
484 expire_walk_interval_ms=10, is_ip6=1)
485 self.sleep(.25)
486 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
487 expire_walk_interval_ms=10000, is_ip6=1)
Klement Sekera4c533132018-02-22 11:41:12 +0100488 self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
Klement Sekera75e7d132017-09-20 08:26:30 +0200489
490 def tearDown(self):
491 super(TestIPv6Reassembly, self).tearDown()
492 self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
493
494 @classmethod
495 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
496 """Create input packet stream for defined interface.
497
498 :param list packet_sizes: Required packet sizes.
499 """
500 for i in range(0, packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +0100501 info = cls.create_packet_info(cls.src_if, cls.src_if)
Klement Sekera75e7d132017-09-20 08:26:30 +0200502 payload = cls.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +0100503 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
504 IPv6(src=cls.src_if.remote_ip6,
505 dst=cls.dst_if.remote_ip6) /
506 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200507 Raw(payload))
508 size = packet_sizes[(i // 2) % len(packet_sizes)]
509 cls.extend_packet(p, size, cls.padding)
510 info.data = p
511
512 @classmethod
513 def create_fragments(cls):
514 infos = cls._packet_infos
515 cls.pkt_infos = []
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -0800516 for index, info in six.iteritems(infos):
Klement Sekera75e7d132017-09-20 08:26:30 +0200517 p = info.data
Klement Sekera4c533132018-02-22 11:41:12 +0100518 # cls.logger.debug(ppp("Packet:", p.__class__(str(p))))
Klement Sekera75e7d132017-09-20 08:26:30 +0200519 fragments_400 = fragment_rfc8200(p, info.index, 400)
520 fragments_300 = fragment_rfc8200(p, info.index, 300)
521 cls.pkt_infos.append((index, fragments_400, fragments_300))
522 cls.fragments_400 = [
523 x for _, frags, _ in cls.pkt_infos for x in frags]
524 cls.fragments_300 = [
525 x for _, _, frags in cls.pkt_infos for x in frags]
526 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
527 "and %s 300-byte fragments" %
528 (len(infos), len(cls.fragments_400),
529 len(cls.fragments_300)))
530
531 def verify_capture(self, capture, dropped_packet_indexes=[]):
532 """Verify captured packet strea .
533
534 :param list capture: Captured packet stream.
535 """
536 info = None
537 seen = set()
538 for packet in capture:
539 try:
Klement Sekera4c533132018-02-22 11:41:12 +0100540 self.logger.debug(ppp("Got packet:", packet))
Klement Sekera75e7d132017-09-20 08:26:30 +0200541 ip = packet[IPv6]
542 udp = packet[UDP]
543 payload_info = self.payload_to_info(str(packet[Raw]))
544 packet_index = payload_info.index
545 self.assertTrue(
546 packet_index not in dropped_packet_indexes,
547 ppp("Packet received, but should be dropped:", packet))
548 if packet_index in seen:
549 raise Exception(ppp("Duplicate packet received", packet))
550 seen.add(packet_index)
Klement Sekera4c533132018-02-22 11:41:12 +0100551 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
Klement Sekera75e7d132017-09-20 08:26:30 +0200552 info = self._packet_infos[packet_index]
553 self.assertTrue(info is not None)
554 self.assertEqual(packet_index, info.index)
555 saved_packet = info.data
556 self.assertEqual(ip.src, saved_packet[IPv6].src)
557 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
558 self.assertEqual(udp.payload, saved_packet[UDP].payload)
Klement Sekera4c533132018-02-22 11:41:12 +0100559 except Exception:
Klement Sekera75e7d132017-09-20 08:26:30 +0200560 self.logger.error(ppp("Unexpected or invalid packet:", packet))
561 raise
562 for index in self._packet_infos:
563 self.assertTrue(index in seen or index in dropped_packet_indexes,
564 "Packet with packet_index %d not received" % index)
565
566 def test_reassembly(self):
567 """ basic reassembly """
568
569 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100570 self.src_if.add_stream(self.fragments_400)
Klement Sekera75e7d132017-09-20 08:26:30 +0200571 self.pg_start()
572
Klement Sekera4c533132018-02-22 11:41:12 +0100573 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200574 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100575 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200576
577 # run it all again to verify correctness
578 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100579 self.src_if.add_stream(self.fragments_400)
Klement Sekera75e7d132017-09-20 08:26:30 +0200580 self.pg_start()
581
Klement Sekera4c533132018-02-22 11:41:12 +0100582 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200583 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100584 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200585
586 def test_reversed(self):
587 """ reverse order reassembly """
588
589 fragments = list(self.fragments_400)
590 fragments.reverse()
591
592 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100593 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200594 self.pg_start()
595
Klement Sekera4c533132018-02-22 11:41:12 +0100596 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200597 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100598 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200599
600 # run it all again to verify correctness
601 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100602 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200603 self.pg_start()
604
Klement Sekera4c533132018-02-22 11:41:12 +0100605 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200606 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100607 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200608
609 def test_random(self):
610 """ random order reassembly """
611
612 fragments = list(self.fragments_400)
613 shuffle(fragments)
614
615 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100616 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200617 self.pg_start()
618
Klement Sekera4c533132018-02-22 11:41:12 +0100619 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200620 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100621 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200622
623 # run it all again to verify correctness
624 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100625 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200626 self.pg_start()
627
Klement Sekera4c533132018-02-22 11:41:12 +0100628 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200629 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100630 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200631
632 def test_duplicates(self):
633 """ duplicate fragments """
634
635 fragments = [
636 x for (_, frags, _) in self.pkt_infos
637 for x in frags
638 for _ in range(0, min(2, len(frags)))
639 ]
640
641 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100642 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200643 self.pg_start()
644
Klement Sekera4c533132018-02-22 11:41:12 +0100645 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +0200646 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100647 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200648
649 def test_overlap1(self):
650 """ overlapping fragments case #1 """
651
652 fragments = []
653 for _, frags_400, frags_300 in self.pkt_infos:
654 if len(frags_300) == 1:
655 fragments.extend(frags_400)
656 else:
657 for i, j in zip(frags_300, frags_400):
658 fragments.extend(i)
659 fragments.extend(j)
660
661 dropped_packet_indexes = set(
662 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
663 )
664
665 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100666 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200667 self.pg_start()
668
Klement Sekera4c533132018-02-22 11:41:12 +0100669 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200670 len(self.pkt_infos) - len(dropped_packet_indexes))
671 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100672 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200673
674 def test_overlap2(self):
675 """ overlapping fragments case #2 """
676
677 fragments = []
Klement Sekera4c533132018-02-22 11:41:12 +0100678 for _, frags_400, frags_300 in self.pkt_infos:
Klement Sekera75e7d132017-09-20 08:26:30 +0200679 if len(frags_400) == 1:
680 fragments.extend(frags_400)
681 else:
682 # care must be taken here so that there are no fragments
683 # received by vpp after reassembly is finished, otherwise
684 # new reassemblies will be started and packet generator will
685 # freak out when it detects unfreed buffers
Klement Sekera4c533132018-02-22 11:41:12 +0100686 zipped = zip(frags_400, frags_300)
Klement Sekera75e7d132017-09-20 08:26:30 +0200687 for i, j in zipped[:-1]:
688 fragments.extend(i)
689 fragments.extend(j)
690 fragments.append(zipped[-1][0])
691
692 dropped_packet_indexes = set(
693 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
694 )
695
696 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100697 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200698 self.pg_start()
699
Klement Sekera4c533132018-02-22 11:41:12 +0100700 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200701 len(self.pkt_infos) - len(dropped_packet_indexes))
702 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100703 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200704
705 def test_timeout_inline(self):
706 """ timeout (inline) """
707
708 dropped_packet_indexes = set(
709 index for (index, frags, _) in self.pkt_infos if len(frags) > 1
710 )
711
712 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
713 expire_walk_interval_ms=10000, is_ip6=1)
714
715 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100716 self.src_if.add_stream(self.fragments_400)
Klement Sekera75e7d132017-09-20 08:26:30 +0200717 self.pg_start()
718
Klement Sekera4c533132018-02-22 11:41:12 +0100719 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200720 len(self.pkt_infos) - len(dropped_packet_indexes))
721 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100722 pkts = self.src_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200723 expected_count=len(dropped_packet_indexes))
724 for icmp in pkts:
725 self.assertIn(ICMPv6TimeExceeded, icmp)
726 self.assertIn(IPv6ExtHdrFragment, icmp)
727 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
728 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
729
730 def test_timeout_cleanup(self):
731 """ timeout (cleanup) """
732
733 # whole packets + fragmented packets sans last fragment
734 fragments = [
735 x for (_, frags_400, _) in self.pkt_infos
736 for x in frags_400[:-1 if len(frags_400) > 1 else None]
737 ]
738
739 # last fragments for fragmented packets
740 fragments2 = [frags_400[-1]
741 for (_, frags_400, _) in self.pkt_infos
742 if len(frags_400) > 1]
743
744 dropped_packet_indexes = set(
745 index for (index, frags_400, _) in self.pkt_infos
746 if len(frags_400) > 1)
747
748 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
749 expire_walk_interval_ms=50)
750
751 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
752 expire_walk_interval_ms=50, is_ip6=1)
753
754 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100755 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200756 self.pg_start()
757
758 self.sleep(.25, "wait before sending rest of fragments")
759
Klement Sekera4c533132018-02-22 11:41:12 +0100760 self.src_if.add_stream(fragments2)
Klement Sekera75e7d132017-09-20 08:26:30 +0200761 self.pg_start()
Klement Sekera75e7d132017-09-20 08:26:30 +0200762
Klement Sekera4c533132018-02-22 11:41:12 +0100763 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200764 len(self.pkt_infos) - len(dropped_packet_indexes))
765 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100766 pkts = self.src_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200767 expected_count=len(dropped_packet_indexes))
768 for icmp in pkts:
769 self.assertIn(ICMPv6TimeExceeded, icmp)
770 self.assertIn(IPv6ExtHdrFragment, icmp)
771 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
772 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
773
774 def test_disabled(self):
775 """ reassembly disabled """
776
777 dropped_packet_indexes = set(
778 index for (index, frags_400, _) in self.pkt_infos
779 if len(frags_400) > 1)
780
781 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
782 expire_walk_interval_ms=10000, is_ip6=1)
783
784 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100785 self.src_if.add_stream(self.fragments_400)
Klement Sekera75e7d132017-09-20 08:26:30 +0200786 self.pg_start()
787
Klement Sekera4c533132018-02-22 11:41:12 +0100788 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200789 len(self.pkt_infos) - len(dropped_packet_indexes))
790 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100791 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200792
793 def test_missing_upper(self):
794 """ missing upper layer """
Klement Sekera4c533132018-02-22 11:41:12 +0100795 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
796 IPv6(src=self.src_if.remote_ip6,
797 dst=self.src_if.local_ip6) /
798 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200799 Raw())
800 self.extend_packet(p, 1000, self.padding)
801 fragments = fragment_rfc8200(p, 1, 500)
802 bad_fragment = p.__class__(str(fragments[1]))
803 bad_fragment[IPv6ExtHdrFragment].nh = 59
804 bad_fragment[IPv6ExtHdrFragment].offset = 0
805 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100806 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +0200807 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +0100808 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +0200809 icmp = pkts[0]
810 self.assertIn(ICMPv6ParamProblem, icmp)
811 self.assert_equal(icmp[ICMPv6ParamProblem].code, 3, "ICMP code")
812
813 def test_invalid_frag_size(self):
814 """ fragment size not a multiple of 8 """
Klement Sekera4c533132018-02-22 11:41:12 +0100815 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
816 IPv6(src=self.src_if.remote_ip6,
817 dst=self.src_if.local_ip6) /
818 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200819 Raw())
820 self.extend_packet(p, 1000, self.padding)
821 fragments = fragment_rfc8200(p, 1, 500)
822 bad_fragment = fragments[0]
823 self.extend_packet(bad_fragment, len(bad_fragment) + 5)
824 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100825 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +0200826 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +0100827 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +0200828 icmp = pkts[0]
829 self.assertIn(ICMPv6ParamProblem, icmp)
830 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
831
832 def test_invalid_packet_size(self):
833 """ total packet size > 65535 """
Klement Sekera4c533132018-02-22 11:41:12 +0100834 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
835 IPv6(src=self.src_if.remote_ip6,
836 dst=self.src_if.local_ip6) /
837 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200838 Raw())
839 self.extend_packet(p, 1000, self.padding)
840 fragments = fragment_rfc8200(p, 1, 500)
841 bad_fragment = fragments[1]
842 bad_fragment[IPv6ExtHdrFragment].offset = 65500
843 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100844 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +0200845 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +0100846 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +0200847 icmp = pkts[0]
848 self.assertIn(ICMPv6ParamProblem, icmp)
849 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
850
851
Juraj Sloboda3048b632018-10-02 11:13:53 +0200852class TestIPv4ReassemblyLocalNode(VppTestCase):
853 """ IPv4 Reassembly for packets coming to ip4-local node """
854
855 @classmethod
856 def setUpClass(cls):
857 super(TestIPv4ReassemblyLocalNode, cls).setUpClass()
858
859 cls.create_pg_interfaces([0])
860 cls.src_dst_if = cls.pg0
861
862 # setup all interfaces
863 for i in cls.pg_interfaces:
864 i.admin_up()
865 i.config_ip4()
866 i.resolve_arp()
867
868 cls.padding = " abcdefghijklmn"
869 cls.create_stream()
870 cls.create_fragments()
871
872 def setUp(self):
873 """ Test setup - force timeout on existing reassemblies """
874 super(TestIPv4ReassemblyLocalNode, self).setUp()
875 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
876 expire_walk_interval_ms=10)
877 self.sleep(.25)
878 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
879 expire_walk_interval_ms=10000)
880
881 def tearDown(self):
882 super(TestIPv4ReassemblyLocalNode, self).tearDown()
883 self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
884
885 @classmethod
886 def create_stream(cls, packet_count=test_packet_count):
887 """Create input packet stream for defined interface.
888
889 :param list packet_sizes: Required packet sizes.
890 """
891 for i in range(0, packet_count):
892 info = cls.create_packet_info(cls.src_dst_if, cls.src_dst_if)
893 payload = cls.info_to_payload(info)
894 p = (Ether(dst=cls.src_dst_if.local_mac,
895 src=cls.src_dst_if.remote_mac) /
896 IP(id=info.index, src=cls.src_dst_if.remote_ip4,
897 dst=cls.src_dst_if.local_ip4) /
898 ICMP(type='echo-request', id=1234) /
899 Raw(payload))
900 cls.extend_packet(p, 1518, cls.padding)
901 info.data = p
902
903 @classmethod
904 def create_fragments(cls):
905 infos = cls._packet_infos
906 cls.pkt_infos = []
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -0800907 for index, info in six.iteritems(infos):
Juraj Sloboda3048b632018-10-02 11:13:53 +0200908 p = info.data
909 # cls.logger.debug(ppp("Packet:", p.__class__(str(p))))
910 fragments_300 = fragment_rfc791(p, 300)
911 cls.pkt_infos.append((index, fragments_300))
912 cls.fragments_300 = [x for (_, frags) in cls.pkt_infos for x in frags]
913 cls.logger.debug("Fragmented %s packets into %s 300-byte fragments" %
914 (len(infos), len(cls.fragments_300)))
915
916 def verify_capture(self, capture):
917 """Verify captured packet stream.
918
919 :param list capture: Captured packet stream.
920 """
921 info = None
922 seen = set()
923 for packet in capture:
924 try:
925 self.logger.debug(ppp("Got packet:", packet))
926 ip = packet[IP]
927 icmp = packet[ICMP]
928 payload_info = self.payload_to_info(str(packet[Raw]))
929 packet_index = payload_info.index
930 if packet_index in seen:
931 raise Exception(ppp("Duplicate packet received", packet))
932 seen.add(packet_index)
933 self.assertEqual(payload_info.dst, self.src_dst_if.sw_if_index)
934 info = self._packet_infos[packet_index]
Klement Sekera14d7e902018-12-10 13:46:09 +0100935 self.assertIsNotNone(info)
Juraj Sloboda3048b632018-10-02 11:13:53 +0200936 self.assertEqual(packet_index, info.index)
937 saved_packet = info.data
938 self.assertEqual(ip.src, saved_packet[IP].dst)
939 self.assertEqual(ip.dst, saved_packet[IP].src)
940 self.assertEqual(icmp.type, 0) # echo reply
941 self.assertEqual(icmp.id, saved_packet[ICMP].id)
942 self.assertEqual(icmp.payload, saved_packet[ICMP].payload)
943 except Exception:
944 self.logger.error(ppp("Unexpected or invalid packet:", packet))
945 raise
946 for index in self._packet_infos:
Klement Sekera14d7e902018-12-10 13:46:09 +0100947 self.assertIn(index, seen,
948 "Packet with packet_index %d not received" % index)
Juraj Sloboda3048b632018-10-02 11:13:53 +0200949
950 def test_reassembly(self):
951 """ basic reassembly """
952
953 self.pg_enable_capture()
954 self.src_dst_if.add_stream(self.fragments_300)
955 self.pg_start()
956
957 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
958 self.verify_capture(packets)
959
960 # run it all again to verify correctness
961 self.pg_enable_capture()
962 self.src_dst_if.add_stream(self.fragments_300)
963 self.pg_start()
964
965 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
966 self.verify_capture(packets)
967
968
Klement Sekera75e7d132017-09-20 08:26:30 +0200969class TestFIFReassembly(VppTestCase):
970 """ Fragments in fragments reassembly """
971
972 @classmethod
973 def setUpClass(cls):
974 super(TestFIFReassembly, cls).setUpClass()
975
Klement Sekera4c533132018-02-22 11:41:12 +0100976 cls.create_pg_interfaces([0, 1])
977 cls.src_if = cls.pg0
978 cls.dst_if = cls.pg1
979 for i in cls.pg_interfaces:
980 i.admin_up()
981 i.config_ip4()
982 i.resolve_arp()
983 i.config_ip6()
984 i.resolve_ndp()
Klement Sekera75e7d132017-09-20 08:26:30 +0200985
Klement Sekera75e7d132017-09-20 08:26:30 +0200986 cls.packet_sizes = [64, 512, 1518, 9018]
987 cls.padding = " abcdefghijklmn"
988
989 def setUp(self):
990 """ Test setup - force timeout on existing reassemblies """
991 super(TestFIFReassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +0100992 self.vapi.ip_reassembly_enable_disable(
993 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
994 enable_ip6=True)
995 self.vapi.ip_reassembly_enable_disable(
996 sw_if_index=self.dst_if.sw_if_index, enable_ip4=True,
997 enable_ip6=True)
Klement Sekera75e7d132017-09-20 08:26:30 +0200998 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
999 expire_walk_interval_ms=10)
1000 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1001 expire_walk_interval_ms=10, is_ip6=1)
1002 self.sleep(.25)
1003 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1004 expire_walk_interval_ms=10000)
1005 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1006 expire_walk_interval_ms=10000, is_ip6=1)
1007
1008 def tearDown(self):
1009 self.logger.debug(self.vapi.ppcli("show ip4-reassembly details"))
1010 self.logger.debug(self.vapi.ppcli("show ip6-reassembly details"))
1011 super(TestFIFReassembly, self).tearDown()
1012
1013 def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):
1014 """Verify captured packet stream.
1015
1016 :param list capture: Captured packet stream.
1017 """
1018 info = None
1019 seen = set()
1020 for packet in capture:
1021 try:
Klement Sekera4c533132018-02-22 11:41:12 +01001022 self.logger.debug(ppp("Got packet:", packet))
Klement Sekera75e7d132017-09-20 08:26:30 +02001023 ip = packet[ip_class]
1024 udp = packet[UDP]
1025 payload_info = self.payload_to_info(str(packet[Raw]))
1026 packet_index = payload_info.index
1027 self.assertTrue(
1028 packet_index not in dropped_packet_indexes,
1029 ppp("Packet received, but should be dropped:", packet))
1030 if packet_index in seen:
1031 raise Exception(ppp("Duplicate packet received", packet))
1032 seen.add(packet_index)
Klement Sekera4c533132018-02-22 11:41:12 +01001033 self.assertEqual(payload_info.dst, self.dst_if.sw_if_index)
Klement Sekera75e7d132017-09-20 08:26:30 +02001034 info = self._packet_infos[packet_index]
1035 self.assertTrue(info is not None)
1036 self.assertEqual(packet_index, info.index)
1037 saved_packet = info.data
1038 self.assertEqual(ip.src, saved_packet[ip_class].src)
1039 self.assertEqual(ip.dst, saved_packet[ip_class].dst)
1040 self.assertEqual(udp.payload, saved_packet[UDP].payload)
Klement Sekera4c533132018-02-22 11:41:12 +01001041 except Exception:
Klement Sekera75e7d132017-09-20 08:26:30 +02001042 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1043 raise
1044 for index in self._packet_infos:
1045 self.assertTrue(index in seen or index in dropped_packet_indexes,
1046 "Packet with packet_index %d not received" % index)
1047
1048 def test_fif4(self):
1049 """ Fragments in fragments (4o4) """
1050
1051 # TODO this should be ideally in setUpClass, but then we hit a bug
1052 # with VppIpRoute incorrectly reporting it's present when it's not
1053 # so we need to manually remove the vpp config, thus we cannot have
1054 # it shared for multiple test cases
1055 self.tun_ip4 = "1.1.1.2"
1056
Klement Sekera4c533132018-02-22 11:41:12 +01001057 self.gre4 = VppGreInterface(self, self.src_if.local_ip4, self.tun_ip4)
Klement Sekera75e7d132017-09-20 08:26:30 +02001058 self.gre4.add_vpp_config()
1059 self.gre4.admin_up()
1060 self.gre4.config_ip4()
1061
Klement Sekera4c533132018-02-22 11:41:12 +01001062 self.vapi.ip_reassembly_enable_disable(
1063 sw_if_index=self.gre4.sw_if_index, enable_ip4=True)
1064
Klement Sekera75e7d132017-09-20 08:26:30 +02001065 self.route4 = VppIpRoute(self, self.tun_ip4, 32,
Klement Sekera4c533132018-02-22 11:41:12 +01001066 [VppRoutePath(self.src_if.remote_ip4,
1067 self.src_if.sw_if_index)])
Klement Sekera75e7d132017-09-20 08:26:30 +02001068 self.route4.add_vpp_config()
1069
1070 self.reset_packet_infos()
1071 for i in range(test_packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01001072 info = self.create_packet_info(self.src_if, self.dst_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02001073 payload = self.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +01001074 # Ethernet header here is only for size calculation, thus it
1075 # doesn't matter how it's initialized. This is to ensure that
1076 # reassembled packet is not > 9000 bytes, so that it's not dropped
1077 p = (Ether() /
1078 IP(id=i, src=self.src_if.remote_ip4,
1079 dst=self.dst_if.remote_ip4) /
1080 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001081 Raw(payload))
1082 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1083 self.extend_packet(p, size, self.padding)
Klement Sekera4c533132018-02-22 11:41:12 +01001084 info.data = p[IP] # use only IP part, without ethernet header
Klement Sekera75e7d132017-09-20 08:26:30 +02001085
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -08001086 fragments = [x for _, p in six.iteritems(self._packet_infos)
Klement Sekera75e7d132017-09-20 08:26:30 +02001087 for x in fragment_rfc791(p.data, 400)]
1088
1089 encapped_fragments = \
Klement Sekera4c533132018-02-22 11:41:12 +01001090 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1091 IP(src=self.tun_ip4, dst=self.src_if.local_ip4) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001092 GRE() /
1093 p
1094 for p in fragments]
1095
1096 fragmented_encapped_fragments = \
1097 [x for p in encapped_fragments
1098 for x in fragment_rfc791(p, 200)]
1099
Klement Sekera4c533132018-02-22 11:41:12 +01001100 self.src_if.add_stream(fragmented_encapped_fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001101
1102 self.pg_enable_capture(self.pg_interfaces)
1103 self.pg_start()
1104
Klement Sekera4c533132018-02-22 11:41:12 +01001105 self.src_if.assert_nothing_captured()
1106 packets = self.dst_if.get_capture(len(self._packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +02001107 self.verify_capture(packets, IP)
1108
1109 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
1110 # so that it's query_vpp_config() works as it should
1111 self.gre4.remove_vpp_config()
Klement Sekera4c533132018-02-22 11:41:12 +01001112 self.logger.debug(self.vapi.ppcli("show interface"))
Klement Sekera75e7d132017-09-20 08:26:30 +02001113
1114 def test_fif6(self):
1115 """ Fragments in fragments (6o6) """
1116 # TODO this should be ideally in setUpClass, but then we hit a bug
1117 # with VppIpRoute incorrectly reporting it's present when it's not
1118 # so we need to manually remove the vpp config, thus we cannot have
1119 # it shared for multiple test cases
1120 self.tun_ip6 = "1002::1"
1121
Klement Sekera4c533132018-02-22 11:41:12 +01001122 self.gre6 = VppGre6Interface(self, self.src_if.local_ip6, self.tun_ip6)
Klement Sekera75e7d132017-09-20 08:26:30 +02001123 self.gre6.add_vpp_config()
1124 self.gre6.admin_up()
1125 self.gre6.config_ip6()
1126
Klement Sekera4c533132018-02-22 11:41:12 +01001127 self.vapi.ip_reassembly_enable_disable(
1128 sw_if_index=self.gre6.sw_if_index, enable_ip6=True)
1129
Klement Sekera75e7d132017-09-20 08:26:30 +02001130 self.route6 = VppIpRoute(self, self.tun_ip6, 128,
Klement Sekera4c533132018-02-22 11:41:12 +01001131 [VppRoutePath(self.src_if.remote_ip6,
1132 self.src_if.sw_if_index,
Klement Sekera75e7d132017-09-20 08:26:30 +02001133 proto=DpoProto.DPO_PROTO_IP6)],
1134 is_ip6=1)
1135 self.route6.add_vpp_config()
1136
1137 self.reset_packet_infos()
1138 for i in range(test_packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01001139 info = self.create_packet_info(self.src_if, self.dst_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02001140 payload = self.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +01001141 # Ethernet header here is only for size calculation, thus it
1142 # doesn't matter how it's initialized. This is to ensure that
1143 # reassembled packet is not > 9000 bytes, so that it's not dropped
1144 p = (Ether() /
1145 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1146 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001147 Raw(payload))
1148 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1149 self.extend_packet(p, size, self.padding)
Klement Sekera4c533132018-02-22 11:41:12 +01001150 info.data = p[IPv6] # use only IPv6 part, without ethernet header
Klement Sekera75e7d132017-09-20 08:26:30 +02001151
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -08001152 fragments = [x for _, i in six.iteritems(self._packet_infos)
Klement Sekera75e7d132017-09-20 08:26:30 +02001153 for x in fragment_rfc8200(
1154 i.data, i.index, 400)]
1155
1156 encapped_fragments = \
Klement Sekera4c533132018-02-22 11:41:12 +01001157 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1158 IPv6(src=self.tun_ip6, dst=self.src_if.local_ip6) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001159 GRE() /
1160 p
1161 for p in fragments]
1162
1163 fragmented_encapped_fragments = \
1164 [x for p in encapped_fragments for x in (
1165 fragment_rfc8200(
1166 p,
1167 2 * len(self._packet_infos) + p[IPv6ExtHdrFragment].id,
1168 200)
1169 if IPv6ExtHdrFragment in p else [p]
1170 )
1171 ]
1172
Klement Sekera4c533132018-02-22 11:41:12 +01001173 self.src_if.add_stream(fragmented_encapped_fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001174
1175 self.pg_enable_capture(self.pg_interfaces)
1176 self.pg_start()
1177
Klement Sekera4c533132018-02-22 11:41:12 +01001178 self.src_if.assert_nothing_captured()
1179 packets = self.dst_if.get_capture(len(self._packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +02001180 self.verify_capture(packets, IPv6)
1181
1182 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
1183 # so that it's query_vpp_config() works as it should
Klement Sekera3ecc2212018-03-27 10:34:43 +02001184 self.gre6.remove_vpp_config()
Klement Sekera75e7d132017-09-20 08:26:30 +02001185
1186
1187if __name__ == '__main__':
1188 unittest.main(testRunner=VppTestRunner)