blob: e9a3af3a0ffed5110326c4e51e92e5b8b89c08f2 [file] [log] [blame]
Renato Botelho do Coutoead1e532019-10-31 13:31:07 -05001#!/usr/bin/env python3
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -08002
3import six
Klement Sekera75e7d132017-09-20 08:26:30 +02004import unittest
Klement Sekera630ab582019-07-19 09:14:19 +00005from random import shuffle, choice, randrange
Klement Sekera75e7d132017-09-20 08:26:30 +02006
Klement Sekera947a85c2019-07-24 12:40:37 +00007from framework import VppTestCase, VppTestRunner
8
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07009import scapy.compat
Klement Sekera75e7d132017-09-20 08:26:30 +020010from 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 Sekera769145c2019-03-06 11:59:57 +010013from scapy.layers.inet6 import HBHOptUnknown, ICMPv6ParamProblem,\
Klement Sekerade34c352019-06-25 11:19:22 +000014 ICMPv6TimeExceeded, IPv6, IPv6ExtHdrFragment, IPv6ExtHdrHopByHop
Paul Vinciguerra69555952019-03-01 08:46:29 -080015from framework import VppTestCase, VppTestRunner
Klement Sekera630ab582019-07-19 09:14:19 +000016from util import ppp, ppc, fragment_rfc791, fragment_rfc8200
Neale Ranns5a8844b2019-04-16 07:15:35 +000017from vpp_gre_interface import VppGreInterface
Neale Rannsc0a93142018-09-05 15:42:26 -070018from vpp_ip import DpoProto
Neale Ranns097fa662018-05-01 05:17:55 -070019from vpp_ip_route import VppIpRoute, VppRoutePath, FibPathProto
Klement Sekera896c8962019-06-24 11:52:49 +000020from vpp_papi import VppEnum
Klement Sekera75e7d132017-09-20 08:26:30 +020021
Klement Sekerad0f70a32018-12-14 17:24:13 +010022# 35 is enough to have >257 400-byte fragments
23test_packet_count = 35
Klement Sekera75e7d132017-09-20 08:26:30 +020024
Klement Sekera630ab582019-07-19 09:14:19 +000025# number of workers used for multi-worker test cases
26worker_count = 3
27
Klement Sekera75e7d132017-09-20 08:26:30 +020028
Klement Sekera947a85c2019-07-24 12:40:37 +000029class TestIPv4Reassembly(VppTestCase):
Klement Sekera75e7d132017-09-20 08:26:30 +020030 """ IPv4 Reassembly """
31
32 @classmethod
33 def setUpClass(cls):
34 super(TestIPv4Reassembly, cls).setUpClass()
35
Klement Sekera4c533132018-02-22 11:41:12 +010036 cls.create_pg_interfaces([0, 1])
37 cls.src_if = cls.pg0
38 cls.dst_if = cls.pg1
Klement Sekera75e7d132017-09-20 08:26:30 +020039
40 # setup all interfaces
41 for i in cls.pg_interfaces:
42 i.admin_up()
43 i.config_ip4()
44 i.resolve_arp()
45
Klement Sekera75e7d132017-09-20 08:26:30 +020046 # packet sizes
47 cls.packet_sizes = [64, 512, 1518, 9018]
48 cls.padding = " abcdefghijklmn"
49 cls.create_stream(cls.packet_sizes)
50 cls.create_fragments()
51
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -070052 @classmethod
53 def tearDownClass(cls):
54 super(TestIPv4Reassembly, cls).tearDownClass()
55
Klement Sekera75e7d132017-09-20 08:26:30 +020056 def setUp(self):
57 """ Test setup - force timeout on existing reassemblies """
58 super(TestIPv4Reassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +010059 self.vapi.ip_reassembly_enable_disable(
60 sw_if_index=self.src_if.sw_if_index, enable_ip4=True)
Klement Sekera75e7d132017-09-20 08:26:30 +020061 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +020062 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +020063 expire_walk_interval_ms=10)
64 self.sleep(.25)
65 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +020066 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +020067 expire_walk_interval_ms=10000)
68
69 def tearDown(self):
70 super(TestIPv4Reassembly, self).tearDown()
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -070071
72 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +000073 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +010074 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +020075
76 @classmethod
77 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
Klement Sekerad0f70a32018-12-14 17:24:13 +010078 """Create input packet stream
Klement Sekera75e7d132017-09-20 08:26:30 +020079
80 :param list packet_sizes: Required packet sizes.
81 """
82 for i in range(0, packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +010083 info = cls.create_packet_info(cls.src_if, cls.src_if)
Klement Sekera75e7d132017-09-20 08:26:30 +020084 payload = cls.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +010085 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
86 IP(id=info.index, src=cls.src_if.remote_ip4,
87 dst=cls.dst_if.remote_ip4) /
88 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +020089 Raw(payload))
90 size = packet_sizes[(i // 2) % len(packet_sizes)]
91 cls.extend_packet(p, size, cls.padding)
92 info.data = p
93
94 @classmethod
95 def create_fragments(cls):
96 infos = cls._packet_infos
97 cls.pkt_infos = []
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -080098 for index, info in six.iteritems(infos):
Klement Sekera75e7d132017-09-20 08:26:30 +020099 p = info.data
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700100 # cls.logger.debug(ppp("Packet:",
101 # p.__class__(scapy.compat.raw(p))))
Klement Sekera75e7d132017-09-20 08:26:30 +0200102 fragments_400 = fragment_rfc791(p, 400)
103 fragments_300 = fragment_rfc791(p, 300)
104 fragments_200 = [
105 x for f in fragments_400 for x in fragment_rfc791(f, 200)]
106 cls.pkt_infos.append(
107 (index, fragments_400, fragments_300, fragments_200))
108 cls.fragments_400 = [
109 x for (_, frags, _, _) in cls.pkt_infos for x in frags]
110 cls.fragments_300 = [
111 x for (_, _, frags, _) in cls.pkt_infos for x in frags]
112 cls.fragments_200 = [
113 x for (_, _, _, frags) in cls.pkt_infos for x in frags]
114 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
115 "%s 300-byte fragments and %s 200-byte fragments" %
116 (len(infos), len(cls.fragments_400),
117 len(cls.fragments_300), len(cls.fragments_200)))
118
Klement Sekera947a85c2019-07-24 12:40:37 +0000119 def verify_capture(self, capture, dropped_packet_indexes=[]):
120 """Verify captured packet stream.
121
122 :param list capture: Captured packet stream.
123 """
124 info = None
125 seen = set()
126 for packet in capture:
127 try:
128 self.logger.debug(ppp("Got packet:", packet))
129 ip = packet[IP]
130 udp = packet[UDP]
131 payload_info = self.payload_to_info(packet[Raw])
132 packet_index = payload_info.index
133 self.assertTrue(
134 packet_index not in dropped_packet_indexes,
135 ppp("Packet received, but should be dropped:", packet))
136 if packet_index in seen:
137 raise Exception(ppp("Duplicate packet received", packet))
138 seen.add(packet_index)
139 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
140 info = self._packet_infos[packet_index]
141 self.assertTrue(info is not None)
142 self.assertEqual(packet_index, info.index)
143 saved_packet = info.data
144 self.assertEqual(ip.src, saved_packet[IP].src)
145 self.assertEqual(ip.dst, saved_packet[IP].dst)
146 self.assertEqual(udp.payload, saved_packet[UDP].payload)
147 except Exception:
148 self.logger.error(ppp("Unexpected or invalid packet:", packet))
149 raise
150 for index in self._packet_infos:
151 self.assertTrue(index in seen or index in dropped_packet_indexes,
152 "Packet with packet_index %d not received" % index)
153
154 def test_reassembly(self):
Klement Sekera75e7d132017-09-20 08:26:30 +0200155 """ basic reassembly """
156
Klement Sekera947a85c2019-07-24 12:40:37 +0000157 self.pg_enable_capture()
158 self.src_if.add_stream(self.fragments_200)
159 self.pg_start()
160
161 packets = self.dst_if.get_capture(len(self.pkt_infos))
162 self.verify_capture(packets)
163 self.src_if.assert_nothing_captured()
164
165 # run it all again to verify correctness
166 self.pg_enable_capture()
167 self.src_if.add_stream(self.fragments_200)
168 self.pg_start()
169
170 packets = self.dst_if.get_capture(len(self.pkt_infos))
171 self.verify_capture(packets)
172 self.src_if.assert_nothing_captured()
173
174 def test_reversed(self):
Klement Sekera75e7d132017-09-20 08:26:30 +0200175 """ reverse order reassembly """
176
Klement Sekera947a85c2019-07-24 12:40:37 +0000177 fragments = list(self.fragments_200)
178 fragments.reverse()
179
180 self.pg_enable_capture()
181 self.src_if.add_stream(fragments)
182 self.pg_start()
183
184 packets = self.dst_if.get_capture(len(self.packet_infos))
185 self.verify_capture(packets)
186 self.src_if.assert_nothing_captured()
187
188 # run it all again to verify correctness
189 self.pg_enable_capture()
190 self.src_if.add_stream(fragments)
191 self.pg_start()
192
193 packets = self.dst_if.get_capture(len(self.packet_infos))
194 self.verify_capture(packets)
195 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200196
Klement Sekera3a343d42019-05-16 14:35:46 +0200197 def test_long_fragment_chain(self):
198 """ long fragment chain """
199
200 error_cnt_str = \
Klement Sekera896c8962019-06-24 11:52:49 +0000201 "/err/ip4-full-reassembly-feature/fragment chain too long (drop)"
Klement Sekera3a343d42019-05-16 14:35:46 +0200202
Klement Sekera34641f22019-05-22 20:18:26 +0200203 error_cnt = self.statistics.get_err_counter(error_cnt_str)
Klement Sekera3a343d42019-05-16 14:35:46 +0200204
205 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
206 max_reassembly_length=3,
207 expire_walk_interval_ms=50)
208
209 p1 = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
210 IP(id=1000, src=self.src_if.remote_ip4,
211 dst=self.dst_if.remote_ip4) /
212 UDP(sport=1234, dport=5678) /
Ole Troan127fbec2019-10-18 15:22:56 +0200213 Raw(b"X" * 1000))
Klement Sekera3a343d42019-05-16 14:35:46 +0200214 p2 = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
215 IP(id=1001, src=self.src_if.remote_ip4,
216 dst=self.dst_if.remote_ip4) /
217 UDP(sport=1234, dport=5678) /
Ole Troan127fbec2019-10-18 15:22:56 +0200218 Raw(b"X" * 1000))
Klement Sekera3a343d42019-05-16 14:35:46 +0200219 frags = fragment_rfc791(p1, 200) + fragment_rfc791(p2, 500)
220
221 self.pg_enable_capture()
222 self.src_if.add_stream(frags)
223 self.pg_start()
224
225 self.dst_if.get_capture(1)
Klement Sekera34641f22019-05-22 20:18:26 +0200226 self.assert_error_counter_equal(error_cnt_str, error_cnt + 1)
Klement Sekera3a343d42019-05-16 14:35:46 +0200227
Klement Sekera14d7e902018-12-10 13:46:09 +0100228 def test_5737(self):
229 """ fragment length + ip header size > 65535 """
Klement Sekera4ee633e2018-12-14 12:00:44 +0100230 self.vapi.cli("clear errors")
Ole Troan127fbec2019-10-18 15:22:56 +0200231 raw = b'''E\x00\x00\x88,\xf8\x1f\xfe@\x01\x98\x00\xc0\xa8\n-\xc0\xa8\n\
232\x01\x08\x00\xf0J\xed\xcb\xf1\xf5Test-group: IPv4.IPv4.ipv4-message.\
233Ethernet-Payload.IPv4-Packet.IPv4-Header.Fragment-Offset; Test-case: 5737'''
Klement Sekera14d7e902018-12-10 13:46:09 +0100234 malformed_packet = (Ether(dst=self.src_if.local_mac,
235 src=self.src_if.remote_mac) /
236 IP(raw))
237 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
238 IP(id=1000, src=self.src_if.remote_ip4,
239 dst=self.dst_if.remote_ip4) /
240 UDP(sport=1234, dport=5678) /
Ole Troan127fbec2019-10-18 15:22:56 +0200241 Raw(b"X" * 1000))
Klement Sekera14d7e902018-12-10 13:46:09 +0100242 valid_fragments = fragment_rfc791(p, 400)
243
Ole Troan127fbec2019-10-18 15:22:56 +0200244 counter = "/err/ip4-full-reassembly-feature/malformed packets"
245 error_counter = self.statistics.get_err_counter(counter)
Klement Sekera14d7e902018-12-10 13:46:09 +0100246 self.pg_enable_capture()
247 self.src_if.add_stream([malformed_packet] + valid_fragments)
248 self.pg_start()
249
250 self.dst_if.get_capture(1)
Klement Sekera896c8962019-06-24 11:52:49 +0000251 self.logger.debug(self.vapi.ppcli("show error"))
Ole Troan127fbec2019-10-18 15:22:56 +0200252 self.assertEqual(self.statistics.get_err_counter(counter),
253 error_counter + 1)
Klement Sekera14d7e902018-12-10 13:46:09 +0100254
Klement Sekera400f6d82018-12-13 14:35:48 +0100255 def test_44924(self):
256 """ compress tiny fragments """
257 packets = [(Ether(dst=self.src_if.local_mac,
258 src=self.src_if.remote_mac) /
259 IP(id=24339, flags="MF", frag=0, ttl=64,
260 src=self.src_if.remote_ip4,
261 dst=self.dst_if.remote_ip4) /
262 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
263 Raw(load='Test-group: IPv4')),
264 (Ether(dst=self.src_if.local_mac,
265 src=self.src_if.remote_mac) /
266 IP(id=24339, flags="MF", frag=3, ttl=64,
267 src=self.src_if.remote_ip4,
268 dst=self.dst_if.remote_ip4) /
269 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
270 Raw(load='.IPv4.Fragmentation.vali')),
271 (Ether(dst=self.src_if.local_mac,
272 src=self.src_if.remote_mac) /
273 IP(id=24339, frag=6, ttl=64,
274 src=self.src_if.remote_ip4,
275 dst=self.dst_if.remote_ip4) /
276 ICMP(type="echo-request", code=0, id=0x1fe6, seq=0x2407) /
277 Raw(load='d; Test-case: 44924'))
278 ]
279
280 self.pg_enable_capture()
281 self.src_if.add_stream(packets)
282 self.pg_start()
283
284 self.dst_if.get_capture(1)
285
Klement Sekera4ee633e2018-12-14 12:00:44 +0100286 def test_frag_1(self):
287 """ fragment of size 1 """
288 self.vapi.cli("clear errors")
289 malformed_packets = [(Ether(dst=self.src_if.local_mac,
290 src=self.src_if.remote_mac) /
291 IP(id=7, len=21, flags="MF", frag=0, ttl=64,
292 src=self.src_if.remote_ip4,
293 dst=self.dst_if.remote_ip4) /
294 ICMP(type="echo-request")),
295 (Ether(dst=self.src_if.local_mac,
296 src=self.src_if.remote_mac) /
297 IP(id=7, len=21, frag=1, ttl=64,
298 src=self.src_if.remote_ip4,
299 dst=self.dst_if.remote_ip4) /
Ole Troan127fbec2019-10-18 15:22:56 +0200300 Raw(load=b'\x08')),
Klement Sekera4ee633e2018-12-14 12:00:44 +0100301 ]
302
303 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
304 IP(id=1000, src=self.src_if.remote_ip4,
305 dst=self.dst_if.remote_ip4) /
306 UDP(sport=1234, dport=5678) /
Ole Troan127fbec2019-10-18 15:22:56 +0200307 Raw(b"X" * 1000))
Klement Sekera4ee633e2018-12-14 12:00:44 +0100308 valid_fragments = fragment_rfc791(p, 400)
309
310 self.pg_enable_capture()
311 self.src_if.add_stream(malformed_packets + valid_fragments)
312 self.pg_start()
313
314 self.dst_if.get_capture(1)
315
Klement Sekera896c8962019-06-24 11:52:49 +0000316 self.assert_packet_counter_equal("ip4-full-reassembly-feature", 1)
Klement Sekera4ee633e2018-12-14 12:00:44 +0100317 # TODO remove above, uncomment below once clearing of counters
318 # is supported
319 # self.assert_packet_counter_equal(
Klement Sekera896c8962019-06-24 11:52:49 +0000320 # "/err/ip4-full-reassembly-feature/malformed packets", 1)
Klement Sekera4ee633e2018-12-14 12:00:44 +0100321
Klement Sekera947a85c2019-07-24 12:40:37 +0000322 def test_random(self):
323 """ random order reassembly """
324
325 fragments = list(self.fragments_200)
326 shuffle(fragments)
327
328 self.pg_enable_capture()
329 self.src_if.add_stream(fragments)
330 self.pg_start()
331
332 packets = self.dst_if.get_capture(len(self.packet_infos))
333 self.verify_capture(packets)
334 self.src_if.assert_nothing_captured()
335
336 # run it all again to verify correctness
337 self.pg_enable_capture()
338 self.src_if.add_stream(fragments)
339 self.pg_start()
340
341 packets = self.dst_if.get_capture(len(self.packet_infos))
342 self.verify_capture(packets)
343 self.src_if.assert_nothing_captured()
344
345 def test_duplicates(self):
Klement Sekera75e7d132017-09-20 08:26:30 +0200346 """ duplicate fragments """
Klement Sekera947a85c2019-07-24 12:40:37 +0000347
Klement Sekera75e7d132017-09-20 08:26:30 +0200348 fragments = [
349 x for (_, frags, _, _) in self.pkt_infos
350 for x in frags
351 for _ in range(0, min(2, len(frags)))
352 ]
Klement Sekera947a85c2019-07-24 12:40:37 +0000353
354 self.pg_enable_capture()
355 self.src_if.add_stream(fragments)
356 self.pg_start()
357
358 packets = self.dst_if.get_capture(len(self.pkt_infos))
359 self.verify_capture(packets)
360 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200361
362 def test_overlap1(self):
363 """ overlapping fragments case #1 """
364
365 fragments = []
366 for _, _, frags_300, frags_200 in self.pkt_infos:
367 if len(frags_300) == 1:
368 fragments.extend(frags_300)
369 else:
370 for i, j in zip(frags_200, frags_300):
371 fragments.extend(i)
372 fragments.extend(j)
373
374 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100375 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200376 self.pg_start()
377
Klement Sekera4c533132018-02-22 11:41:12 +0100378 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera947a85c2019-07-24 12:40:37 +0000379 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100380 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200381
382 # run it all to verify correctness
383 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100384 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200385 self.pg_start()
386
Klement Sekera4c533132018-02-22 11:41:12 +0100387 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera947a85c2019-07-24 12:40:37 +0000388 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100389 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200390
391 def test_overlap2(self):
392 """ overlapping fragments case #2 """
393
394 fragments = []
395 for _, _, frags_300, frags_200 in self.pkt_infos:
396 if len(frags_300) == 1:
397 fragments.extend(frags_300)
398 else:
399 # care must be taken here so that there are no fragments
400 # received by vpp after reassembly is finished, otherwise
401 # new reassemblies will be started and packet generator will
402 # freak out when it detects unfreed buffers
403 zipped = zip(frags_300, frags_200)
Paul Vinciguerrac7834e02019-03-02 10:43:05 -0800404 for i, j in zipped:
Klement Sekera75e7d132017-09-20 08:26:30 +0200405 fragments.extend(i)
406 fragments.extend(j)
Paul Vinciguerrac7834e02019-03-02 10:43:05 -0800407 fragments.pop()
Klement Sekera75e7d132017-09-20 08:26:30 +0200408
409 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100410 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200411 self.pg_start()
412
Klement Sekera4c533132018-02-22 11:41:12 +0100413 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera947a85c2019-07-24 12:40:37 +0000414 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100415 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200416
417 # run it all to verify correctness
418 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100419 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200420 self.pg_start()
421
Klement Sekera4c533132018-02-22 11:41:12 +0100422 packets = self.dst_if.get_capture(len(self.pkt_infos))
Klement Sekera947a85c2019-07-24 12:40:37 +0000423 self.verify_capture(packets)
Klement Sekera4c533132018-02-22 11:41:12 +0100424 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200425
Klement Sekera947a85c2019-07-24 12:40:37 +0000426 def test_timeout_inline(self):
Klement Sekera75e7d132017-09-20 08:26:30 +0200427 """ timeout (inline) """
428
429 dropped_packet_indexes = set(
430 index for (index, frags, _, _) in self.pkt_infos if len(frags) > 1
431 )
432
Klement Sekera947a85c2019-07-24 12:40:37 +0000433 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
434 max_reassembly_length=3,
435 expire_walk_interval_ms=10000)
436
437 self.pg_enable_capture()
438 self.src_if.add_stream(self.fragments_400)
439 self.pg_start()
440
441 packets = self.dst_if.get_capture(
442 len(self.pkt_infos) - len(dropped_packet_indexes))
443 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100444 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200445
446 def test_timeout_cleanup(self):
447 """ timeout (cleanup) """
448
449 # whole packets + fragmented packets sans last fragment
450 fragments = [
451 x for (_, frags_400, _, _) in self.pkt_infos
452 for x in frags_400[:-1 if len(frags_400) > 1 else None]
453 ]
454
455 # last fragments for fragmented packets
456 fragments2 = [frags_400[-1]
457 for (_, frags_400, _, _) in self.pkt_infos
458 if len(frags_400) > 1]
459
460 dropped_packet_indexes = set(
461 index for (index, frags_400, _, _) in self.pkt_infos
462 if len(frags_400) > 1)
463
464 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +0200465 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +0200466 expire_walk_interval_ms=50)
467
468 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +0100469 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +0200470 self.pg_start()
471
472 self.sleep(.25, "wait before sending rest of fragments")
473
Klement Sekera4c533132018-02-22 11:41:12 +0100474 self.src_if.add_stream(fragments2)
Klement Sekera75e7d132017-09-20 08:26:30 +0200475 self.pg_start()
Klement Sekera75e7d132017-09-20 08:26:30 +0200476
Klement Sekera4c533132018-02-22 11:41:12 +0100477 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +0200478 len(self.pkt_infos) - len(dropped_packet_indexes))
Klement Sekera947a85c2019-07-24 12:40:37 +0000479 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +0100480 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200481
Klement Sekera947a85c2019-07-24 12:40:37 +0000482 def test_disabled(self):
Klement Sekera75e7d132017-09-20 08:26:30 +0200483 """ reassembly disabled """
484
485 dropped_packet_indexes = set(
486 index for (index, frags_400, _, _) in self.pkt_infos
487 if len(frags_400) > 1)
Klement Sekera947a85c2019-07-24 12:40:37 +0000488
489 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
490 max_reassembly_length=3,
491 expire_walk_interval_ms=10000)
492
493 self.pg_enable_capture()
494 self.src_if.add_stream(self.fragments_400)
495 self.pg_start()
496
497 packets = self.dst_if.get_capture(
498 len(self.pkt_infos) - len(dropped_packet_indexes))
499 self.verify_capture(packets, dropped_packet_indexes)
500 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +0200501
502
Klement Sekerade34c352019-06-25 11:19:22 +0000503class TestIPv4SVReassembly(VppTestCase):
504 """ IPv4 Shallow Virtual Reassembly """
505
506 @classmethod
507 def setUpClass(cls):
508 super(TestIPv4SVReassembly, cls).setUpClass()
509
510 cls.create_pg_interfaces([0, 1])
511 cls.src_if = cls.pg0
512 cls.dst_if = cls.pg1
513
514 # setup all interfaces
515 for i in cls.pg_interfaces:
516 i.admin_up()
517 i.config_ip4()
518 i.resolve_arp()
519
520 def setUp(self):
521 """ Test setup - force timeout on existing reassemblies """
522 super(TestIPv4SVReassembly, self).setUp()
523 self.vapi.ip_reassembly_enable_disable(
524 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
525 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
526 self.vapi.ip_reassembly_set(
527 timeout_ms=0, max_reassemblies=1000,
528 max_reassembly_length=1000,
529 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
530 expire_walk_interval_ms=10)
531 self.sleep(.25)
532 self.vapi.ip_reassembly_set(
533 timeout_ms=1000000, max_reassemblies=1000,
534 max_reassembly_length=1000,
535 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
536 expire_walk_interval_ms=10000)
537
538 def tearDown(self):
539 super(TestIPv4SVReassembly, self).tearDown()
540 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
541 self.logger.debug(self.vapi.ppcli("show buffers"))
542
543 def test_basic(self):
544 """ basic reassembly """
545 payload_len = 1000
546 payload = ""
547 counter = 0
548 while len(payload) < payload_len:
549 payload += "%u " % counter
550 counter += 1
551
552 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
553 IP(id=1, src=self.src_if.remote_ip4,
554 dst=self.dst_if.remote_ip4) /
555 UDP(sport=1234, dport=5678) /
556 Raw(payload))
557 fragments = fragment_rfc791(p, payload_len/4)
558
559 # send fragment #2 - should be cached inside reassembly
560 self.pg_enable_capture()
561 self.src_if.add_stream(fragments[1])
562 self.pg_start()
563 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
564 self.logger.debug(self.vapi.ppcli("show buffers"))
565 self.logger.debug(self.vapi.ppcli("show trace"))
566 self.dst_if.assert_nothing_captured()
567
568 # send fragment #1 - reassembly is finished now and both fragments
569 # forwarded
570 self.pg_enable_capture()
571 self.src_if.add_stream(fragments[0])
572 self.pg_start()
573 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
574 self.logger.debug(self.vapi.ppcli("show buffers"))
575 self.logger.debug(self.vapi.ppcli("show trace"))
576 c = self.dst_if.get_capture(2)
577 for sent, recvd in zip([fragments[1], fragments[0]], c):
578 self.assertEqual(sent[IP].src, recvd[IP].src)
579 self.assertEqual(sent[IP].dst, recvd[IP].dst)
580 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
581
582 # send rest of fragments - should be immediately forwarded
583 self.pg_enable_capture()
584 self.src_if.add_stream(fragments[2:])
585 self.pg_start()
586 c = self.dst_if.get_capture(len(fragments[2:]))
587 for sent, recvd in zip(fragments[2:], c):
588 self.assertEqual(sent[IP].src, recvd[IP].src)
589 self.assertEqual(sent[IP].dst, recvd[IP].dst)
590 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
591
592 def test_timeout(self):
593 """ reassembly timeout """
594 payload_len = 1000
595 payload = ""
596 counter = 0
597 while len(payload) < payload_len:
598 payload += "%u " % counter
599 counter += 1
600
601 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
602 IP(id=1, src=self.src_if.remote_ip4,
603 dst=self.dst_if.remote_ip4) /
604 UDP(sport=1234, dport=5678) /
605 Raw(payload))
606 fragments = fragment_rfc791(p, payload_len/4)
607
608 self.vapi.ip_reassembly_set(
609 timeout_ms=100, max_reassemblies=1000,
610 max_reassembly_length=1000,
611 expire_walk_interval_ms=50,
612 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
613
614 # send fragments #2 and #1 - should be forwarded
615 self.pg_enable_capture()
616 self.src_if.add_stream(fragments[0:2])
617 self.pg_start()
618 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
619 self.logger.debug(self.vapi.ppcli("show buffers"))
620 self.logger.debug(self.vapi.ppcli("show trace"))
621 c = self.dst_if.get_capture(2)
622 for sent, recvd in zip([fragments[1], fragments[0]], c):
623 self.assertEqual(sent[IP].src, recvd[IP].src)
624 self.assertEqual(sent[IP].dst, recvd[IP].dst)
625 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
626
627 # wait for cleanup
628 self.sleep(.25, "wait before sending rest of fragments")
629
630 # send rest of fragments - shouldn't be forwarded
631 self.pg_enable_capture()
632 self.src_if.add_stream(fragments[2:])
633 self.pg_start()
634 self.dst_if.assert_nothing_captured()
635
636 def test_lru(self):
637 """ reassembly reuses LRU element """
638
639 self.vapi.ip_reassembly_set(
640 timeout_ms=1000000, max_reassemblies=1,
641 max_reassembly_length=1000,
642 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
643 expire_walk_interval_ms=10000)
644
645 payload_len = 1000
646 payload = ""
647 counter = 0
648 while len(payload) < payload_len:
649 payload += "%u " % counter
650 counter += 1
651
652 packet_count = 10
653
654 fragments = [f
655 for i in range(packet_count)
656 for p in (Ether(dst=self.src_if.local_mac,
657 src=self.src_if.remote_mac) /
658 IP(id=i, src=self.src_if.remote_ip4,
659 dst=self.dst_if.remote_ip4) /
660 UDP(sport=1234, dport=5678) /
661 Raw(payload))
662 for f in fragment_rfc791(p, payload_len/4)]
663
664 self.pg_enable_capture()
665 self.src_if.add_stream(fragments)
666 self.pg_start()
667 c = self.dst_if.get_capture(len(fragments))
668 for sent, recvd in zip(fragments, c):
669 self.assertEqual(sent[IP].src, recvd[IP].src)
670 self.assertEqual(sent[IP].dst, recvd[IP].dst)
671 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
672
673
Klement Sekera630ab582019-07-19 09:14:19 +0000674class TestIPv4MWReassembly(VppTestCase):
675 """ IPv4 Reassembly (multiple workers) """
676 worker_config = "workers %d" % worker_count
677
678 @classmethod
679 def setUpClass(cls):
680 super(TestIPv4MWReassembly, cls).setUpClass()
681
682 cls.create_pg_interfaces(range(worker_count+1))
683 cls.src_if = cls.pg0
684 cls.send_ifs = cls.pg_interfaces[:-1]
685 cls.dst_if = cls.pg_interfaces[-1]
686
687 # setup all interfaces
688 for i in cls.pg_interfaces:
689 i.admin_up()
690 i.config_ip4()
691 i.resolve_arp()
692
693 # packets sizes reduced here because we are generating packets without
694 # Ethernet headers, which are added later (diff fragments go via
695 # different interfaces)
696 cls.packet_sizes = [64-len(Ether()), 512-len(Ether()),
697 1518-len(Ether()), 9018-len(Ether())]
698 cls.padding = " abcdefghijklmn"
699 cls.create_stream(cls.packet_sizes)
700 cls.create_fragments()
701
702 @classmethod
703 def tearDownClass(cls):
704 super(TestIPv4MWReassembly, cls).tearDownClass()
705
706 def setUp(self):
707 """ Test setup - force timeout on existing reassemblies """
708 super(TestIPv4MWReassembly, self).setUp()
709 for intf in self.send_ifs:
710 self.vapi.ip_reassembly_enable_disable(
711 sw_if_index=intf.sw_if_index, enable_ip4=True)
712 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
713 max_reassembly_length=1000,
714 expire_walk_interval_ms=10)
715 self.sleep(.25)
716 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
717 max_reassembly_length=1000,
718 expire_walk_interval_ms=10000)
719
720 def tearDown(self):
721 super(TestIPv4MWReassembly, self).tearDown()
722
723 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +0000724 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
Klement Sekera630ab582019-07-19 09:14:19 +0000725 self.logger.debug(self.vapi.ppcli("show buffers"))
726
727 @classmethod
728 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
729 """Create input packet stream
730
731 :param list packet_sizes: Required packet sizes.
732 """
733 for i in range(0, packet_count):
734 info = cls.create_packet_info(cls.src_if, cls.src_if)
735 payload = cls.info_to_payload(info)
736 p = (IP(id=info.index, src=cls.src_if.remote_ip4,
737 dst=cls.dst_if.remote_ip4) /
738 UDP(sport=1234, dport=5678) /
739 Raw(payload))
740 size = packet_sizes[(i // 2) % len(packet_sizes)]
741 cls.extend_packet(p, size, cls.padding)
742 info.data = p
743
744 @classmethod
745 def create_fragments(cls):
746 infos = cls._packet_infos
747 cls.pkt_infos = []
748 for index, info in six.iteritems(infos):
749 p = info.data
750 # cls.logger.debug(ppp("Packet:",
751 # p.__class__(scapy.compat.raw(p))))
752 fragments_400 = fragment_rfc791(p, 400)
753 cls.pkt_infos.append((index, fragments_400))
754 cls.fragments_400 = [
755 x for (_, frags) in cls.pkt_infos for x in frags]
756 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, " %
757 (len(infos), len(cls.fragments_400)))
758
759 def verify_capture(self, capture, dropped_packet_indexes=[]):
760 """Verify captured packet stream.
761
762 :param list capture: Captured packet stream.
763 """
764 info = None
765 seen = set()
766 for packet in capture:
767 try:
768 self.logger.debug(ppp("Got packet:", packet))
769 ip = packet[IP]
770 udp = packet[UDP]
771 payload_info = self.payload_to_info(packet[Raw])
772 packet_index = payload_info.index
773 self.assertTrue(
774 packet_index not in dropped_packet_indexes,
775 ppp("Packet received, but should be dropped:", packet))
776 if packet_index in seen:
777 raise Exception(ppp("Duplicate packet received", packet))
778 seen.add(packet_index)
779 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
780 info = self._packet_infos[packet_index]
781 self.assertTrue(info is not None)
782 self.assertEqual(packet_index, info.index)
783 saved_packet = info.data
784 self.assertEqual(ip.src, saved_packet[IP].src)
785 self.assertEqual(ip.dst, saved_packet[IP].dst)
786 self.assertEqual(udp.payload, saved_packet[UDP].payload)
787 except Exception:
788 self.logger.error(ppp("Unexpected or invalid packet:", packet))
789 raise
790 for index in self._packet_infos:
791 self.assertTrue(index in seen or index in dropped_packet_indexes,
792 "Packet with packet_index %d not received" % index)
793
794 def send_packets(self, packets):
795 for counter in range(worker_count):
796 if 0 == len(packets[counter]):
797 continue
798 send_if = self.send_ifs[counter]
799 send_if.add_stream(
800 (Ether(dst=send_if.local_mac, src=send_if.remote_mac) / x
801 for x in packets[counter]),
802 worker=counter)
803 self.pg_start()
804
805 def test_worker_conflict(self):
806 """ 1st and FO=0 fragments on different workers """
807
808 # in first wave we send fragments which don't start at offset 0
809 # then we send fragments with offset 0 on a different thread
810 # then the rest of packets on a random thread
811 first_packets = [[] for n in range(worker_count)]
812 second_packets = [[] for n in range(worker_count)]
813 rest_of_packets = [[] for n in range(worker_count)]
814 for (_, p) in self.pkt_infos:
815 wi = randrange(worker_count)
816 second_packets[wi].append(p[0])
817 if len(p) <= 1:
818 continue
819 wi2 = wi
820 while wi2 == wi:
821 wi2 = randrange(worker_count)
822 first_packets[wi2].append(p[1])
823 wi3 = randrange(worker_count)
824 rest_of_packets[wi3].extend(p[2:])
825
826 self.pg_enable_capture()
827 self.send_packets(first_packets)
828 self.send_packets(second_packets)
829 self.send_packets(rest_of_packets)
830
831 packets = self.dst_if.get_capture(len(self.pkt_infos))
832 self.verify_capture(packets)
833 for send_if in self.send_ifs:
834 send_if.assert_nothing_captured()
835
Klement Sekera68bae5b2019-10-10 18:57:34 +0000836 self.logger.debug(self.vapi.ppcli("show trace"))
837 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
838 self.logger.debug(self.vapi.ppcli("show buffers"))
839 self.vapi.cli("clear trace")
840
Klement Sekera630ab582019-07-19 09:14:19 +0000841 self.pg_enable_capture()
842 self.send_packets(first_packets)
843 self.send_packets(second_packets)
844 self.send_packets(rest_of_packets)
845
846 packets = self.dst_if.get_capture(len(self.pkt_infos))
847 self.verify_capture(packets)
848 for send_if in self.send_ifs:
849 send_if.assert_nothing_captured()
850
851
Klement Sekera947a85c2019-07-24 12:40:37 +0000852class TestIPv6Reassembly(VppTestCase):
Klement Sekera75e7d132017-09-20 08:26:30 +0200853 """ IPv6 Reassembly """
854
855 @classmethod
856 def setUpClass(cls):
857 super(TestIPv6Reassembly, cls).setUpClass()
858
Klement Sekera4c533132018-02-22 11:41:12 +0100859 cls.create_pg_interfaces([0, 1])
860 cls.src_if = cls.pg0
861 cls.dst_if = cls.pg1
Klement Sekera75e7d132017-09-20 08:26:30 +0200862
863 # setup all interfaces
864 for i in cls.pg_interfaces:
865 i.admin_up()
866 i.config_ip6()
867 i.resolve_ndp()
868
Klement Sekera75e7d132017-09-20 08:26:30 +0200869 # packet sizes
870 cls.packet_sizes = [64, 512, 1518, 9018]
871 cls.padding = " abcdefghijklmn"
872 cls.create_stream(cls.packet_sizes)
873 cls.create_fragments()
874
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -0700875 @classmethod
876 def tearDownClass(cls):
877 super(TestIPv6Reassembly, cls).tearDownClass()
878
Klement Sekera75e7d132017-09-20 08:26:30 +0200879 def setUp(self):
880 """ Test setup - force timeout on existing reassemblies """
881 super(TestIPv6Reassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +0100882 self.vapi.ip_reassembly_enable_disable(
883 sw_if_index=self.src_if.sw_if_index, enable_ip6=True)
Klement Sekera75e7d132017-09-20 08:26:30 +0200884 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +0200885 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +0200886 expire_walk_interval_ms=10, is_ip6=1)
887 self.sleep(.25)
888 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +0200889 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +0200890 expire_walk_interval_ms=10000, is_ip6=1)
Klement Sekera896c8962019-06-24 11:52:49 +0000891 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +0100892 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +0200893
894 def tearDown(self):
895 super(TestIPv6Reassembly, self).tearDown()
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700896
897 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +0000898 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +0100899 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +0200900
901 @classmethod
902 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
903 """Create input packet stream for defined interface.
904
905 :param list packet_sizes: Required packet sizes.
906 """
907 for i in range(0, packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +0100908 info = cls.create_packet_info(cls.src_if, cls.src_if)
Klement Sekera75e7d132017-09-20 08:26:30 +0200909 payload = cls.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +0100910 p = (Ether(dst=cls.src_if.local_mac, src=cls.src_if.remote_mac) /
911 IPv6(src=cls.src_if.remote_ip6,
912 dst=cls.dst_if.remote_ip6) /
913 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +0200914 Raw(payload))
915 size = packet_sizes[(i // 2) % len(packet_sizes)]
916 cls.extend_packet(p, size, cls.padding)
917 info.data = p
918
919 @classmethod
920 def create_fragments(cls):
921 infos = cls._packet_infos
922 cls.pkt_infos = []
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -0800923 for index, info in six.iteritems(infos):
Klement Sekera75e7d132017-09-20 08:26:30 +0200924 p = info.data
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700925 # cls.logger.debug(ppp("Packet:",
926 # p.__class__(scapy.compat.raw(p))))
Klement Sekera75e7d132017-09-20 08:26:30 +0200927 fragments_400 = fragment_rfc8200(p, info.index, 400)
928 fragments_300 = fragment_rfc8200(p, info.index, 300)
929 cls.pkt_infos.append((index, fragments_400, fragments_300))
930 cls.fragments_400 = [
931 x for _, frags, _ in cls.pkt_infos for x in frags]
932 cls.fragments_300 = [
933 x for _, _, frags in cls.pkt_infos for x in frags]
934 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, "
935 "and %s 300-byte fragments" %
936 (len(infos), len(cls.fragments_400),
937 len(cls.fragments_300)))
938
Klement Sekera947a85c2019-07-24 12:40:37 +0000939 def verify_capture(self, capture, dropped_packet_indexes=[]):
940 """Verify captured packet strea .
941
942 :param list capture: Captured packet stream.
943 """
944 info = None
945 seen = set()
946 for packet in capture:
947 try:
948 self.logger.debug(ppp("Got packet:", packet))
949 ip = packet[IPv6]
950 udp = packet[UDP]
951 payload_info = self.payload_to_info(packet[Raw])
952 packet_index = payload_info.index
953 self.assertTrue(
954 packet_index not in dropped_packet_indexes,
955 ppp("Packet received, but should be dropped:", packet))
956 if packet_index in seen:
957 raise Exception(ppp("Duplicate packet received", packet))
958 seen.add(packet_index)
959 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
960 info = self._packet_infos[packet_index]
961 self.assertTrue(info is not None)
962 self.assertEqual(packet_index, info.index)
963 saved_packet = info.data
964 self.assertEqual(ip.src, saved_packet[IPv6].src)
965 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
966 self.assertEqual(udp.payload, saved_packet[UDP].payload)
967 except Exception:
968 self.logger.error(ppp("Unexpected or invalid packet:", packet))
969 raise
970 for index in self._packet_infos:
971 self.assertTrue(index in seen or index in dropped_packet_indexes,
972 "Packet with packet_index %d not received" % index)
973
974 def test_reassembly(self):
Klement Sekera75e7d132017-09-20 08:26:30 +0200975 """ basic reassembly """
976
Klement Sekera947a85c2019-07-24 12:40:37 +0000977 self.pg_enable_capture()
978 self.src_if.add_stream(self.fragments_400)
979 self.pg_start()
980
981 packets = self.dst_if.get_capture(len(self.pkt_infos))
982 self.verify_capture(packets)
983 self.src_if.assert_nothing_captured()
984
985 # run it all again to verify correctness
986 self.pg_enable_capture()
987 self.src_if.add_stream(self.fragments_400)
988 self.pg_start()
989
990 packets = self.dst_if.get_capture(len(self.pkt_infos))
991 self.verify_capture(packets)
992 self.src_if.assert_nothing_captured()
993
Klement Sekera769145c2019-03-06 11:59:57 +0100994 def test_buffer_boundary(self):
995 """ fragment header crossing buffer boundary """
996
997 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
998 IPv6(src=self.src_if.remote_ip6,
999 dst=self.src_if.local_ip6) /
1000 IPv6ExtHdrHopByHop(
1001 options=[HBHOptUnknown(otype=0xff, optlen=0)] * 1000) /
1002 IPv6ExtHdrFragment(m=1) /
1003 UDP(sport=1234, dport=5678) /
1004 Raw())
1005 self.pg_enable_capture()
1006 self.src_if.add_stream([p])
1007 self.pg_start()
1008 self.src_if.assert_nothing_captured()
1009 self.dst_if.assert_nothing_captured()
1010
Klement Sekera947a85c2019-07-24 12:40:37 +00001011 def test_reversed(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001012 """ reverse order reassembly """
1013
Klement Sekera947a85c2019-07-24 12:40:37 +00001014 fragments = list(self.fragments_400)
1015 fragments.reverse()
1016
1017 self.pg_enable_capture()
1018 self.src_if.add_stream(fragments)
1019 self.pg_start()
1020
1021 packets = self.dst_if.get_capture(len(self.pkt_infos))
1022 self.verify_capture(packets)
1023 self.src_if.assert_nothing_captured()
1024
1025 # run it all again to verify correctness
1026 self.pg_enable_capture()
1027 self.src_if.add_stream(fragments)
1028 self.pg_start()
1029
1030 packets = self.dst_if.get_capture(len(self.pkt_infos))
1031 self.verify_capture(packets)
1032 self.src_if.assert_nothing_captured()
1033
1034 def test_random(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001035 """ random order reassembly """
1036
Klement Sekera947a85c2019-07-24 12:40:37 +00001037 fragments = list(self.fragments_400)
1038 shuffle(fragments)
1039
1040 self.pg_enable_capture()
1041 self.src_if.add_stream(fragments)
1042 self.pg_start()
1043
1044 packets = self.dst_if.get_capture(len(self.pkt_infos))
1045 self.verify_capture(packets)
1046 self.src_if.assert_nothing_captured()
1047
1048 # run it all again to verify correctness
1049 self.pg_enable_capture()
1050 self.src_if.add_stream(fragments)
1051 self.pg_start()
1052
1053 packets = self.dst_if.get_capture(len(self.pkt_infos))
1054 self.verify_capture(packets)
1055 self.src_if.assert_nothing_captured()
1056
1057 def test_duplicates(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001058 """ duplicate fragments """
1059
1060 fragments = [
1061 x for (_, frags, _) in self.pkt_infos
1062 for x in frags
1063 for _ in range(0, min(2, len(frags)))
1064 ]
Klement Sekera947a85c2019-07-24 12:40:37 +00001065
1066 self.pg_enable_capture()
1067 self.src_if.add_stream(fragments)
1068 self.pg_start()
1069
1070 packets = self.dst_if.get_capture(len(self.pkt_infos))
1071 self.verify_capture(packets)
1072 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +02001073
Klement Sekera3a343d42019-05-16 14:35:46 +02001074 def test_long_fragment_chain(self):
1075 """ long fragment chain """
1076
1077 error_cnt_str = \
Klement Sekera896c8962019-06-24 11:52:49 +00001078 "/err/ip6-full-reassembly-feature/fragment chain too long (drop)"
Klement Sekera3a343d42019-05-16 14:35:46 +02001079
Klement Sekera34641f22019-05-22 20:18:26 +02001080 error_cnt = self.statistics.get_err_counter(error_cnt_str)
Klement Sekera3a343d42019-05-16 14:35:46 +02001081
1082 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
1083 max_reassembly_length=3,
1084 expire_walk_interval_ms=50, is_ip6=1)
1085
1086 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1087 IPv6(src=self.src_if.remote_ip6,
1088 dst=self.dst_if.remote_ip6) /
1089 UDP(sport=1234, dport=5678) /
Ole Troan127fbec2019-10-18 15:22:56 +02001090 Raw(b"X" * 1000))
Klement Sekera3a343d42019-05-16 14:35:46 +02001091 frags = fragment_rfc8200(p, 1, 300) + fragment_rfc8200(p, 2, 500)
1092
1093 self.pg_enable_capture()
1094 self.src_if.add_stream(frags)
1095 self.pg_start()
1096
1097 self.dst_if.get_capture(1)
Klement Sekera34641f22019-05-22 20:18:26 +02001098 self.assert_error_counter_equal(error_cnt_str, error_cnt + 1)
Klement Sekera3a343d42019-05-16 14:35:46 +02001099
Klement Sekera75e7d132017-09-20 08:26:30 +02001100 def test_overlap1(self):
Klement Sekera947a85c2019-07-24 12:40:37 +00001101 """ overlapping fragments case #1 """
Klement Sekera75e7d132017-09-20 08:26:30 +02001102
1103 fragments = []
1104 for _, frags_400, frags_300 in self.pkt_infos:
1105 if len(frags_300) == 1:
1106 fragments.extend(frags_400)
1107 else:
1108 for i, j in zip(frags_300, frags_400):
1109 fragments.extend(i)
1110 fragments.extend(j)
1111
1112 dropped_packet_indexes = set(
1113 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
1114 )
1115
1116 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001117 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001118 self.pg_start()
1119
Klement Sekera4c533132018-02-22 11:41:12 +01001120 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +02001121 len(self.pkt_infos) - len(dropped_packet_indexes))
Klement Sekera947a85c2019-07-24 12:40:37 +00001122 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001123 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +02001124
1125 def test_overlap2(self):
Klement Sekera947a85c2019-07-24 12:40:37 +00001126 """ overlapping fragments case #2 """
Klement Sekera75e7d132017-09-20 08:26:30 +02001127
1128 fragments = []
Klement Sekera4c533132018-02-22 11:41:12 +01001129 for _, frags_400, frags_300 in self.pkt_infos:
Klement Sekera75e7d132017-09-20 08:26:30 +02001130 if len(frags_400) == 1:
1131 fragments.extend(frags_400)
1132 else:
1133 # care must be taken here so that there are no fragments
1134 # received by vpp after reassembly is finished, otherwise
1135 # new reassemblies will be started and packet generator will
1136 # freak out when it detects unfreed buffers
Klement Sekera4c533132018-02-22 11:41:12 +01001137 zipped = zip(frags_400, frags_300)
Paul Vinciguerrac7834e02019-03-02 10:43:05 -08001138 for i, j in zipped:
Klement Sekera75e7d132017-09-20 08:26:30 +02001139 fragments.extend(i)
1140 fragments.extend(j)
Paul Vinciguerrac7834e02019-03-02 10:43:05 -08001141 fragments.pop()
Klement Sekera75e7d132017-09-20 08:26:30 +02001142
1143 dropped_packet_indexes = set(
1144 index for (index, _, frags) in self.pkt_infos if len(frags) > 1
1145 )
1146
1147 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001148 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001149 self.pg_start()
1150
Klement Sekera4c533132018-02-22 11:41:12 +01001151 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +02001152 len(self.pkt_infos) - len(dropped_packet_indexes))
Klement Sekera947a85c2019-07-24 12:40:37 +00001153 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001154 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +02001155
Klement Sekera947a85c2019-07-24 12:40:37 +00001156 def test_timeout_inline(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001157 """ timeout (inline) """
1158
1159 dropped_packet_indexes = set(
1160 index for (index, frags, _) in self.pkt_infos if len(frags) > 1
1161 )
1162
Klement Sekera947a85c2019-07-24 12:40:37 +00001163 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1164 max_reassembly_length=3,
1165 expire_walk_interval_ms=10000, is_ip6=1)
1166
1167 self.pg_enable_capture()
1168 self.src_if.add_stream(self.fragments_400)
1169 self.pg_start()
1170
1171 packets = self.dst_if.get_capture(
1172 len(self.pkt_infos) - len(dropped_packet_indexes))
1173 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001174 pkts = self.src_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +02001175 expected_count=len(dropped_packet_indexes))
1176 for icmp in pkts:
1177 self.assertIn(ICMPv6TimeExceeded, icmp)
1178 self.assertIn(IPv6ExtHdrFragment, icmp)
1179 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
1180 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
1181
1182 def test_timeout_cleanup(self):
1183 """ timeout (cleanup) """
1184
1185 # whole packets + fragmented packets sans last fragment
1186 fragments = [
1187 x for (_, frags_400, _) in self.pkt_infos
1188 for x in frags_400[:-1 if len(frags_400) > 1 else None]
1189 ]
1190
1191 # last fragments for fragmented packets
1192 fragments2 = [frags_400[-1]
1193 for (_, frags_400, _) in self.pkt_infos
1194 if len(frags_400) > 1]
1195
1196 dropped_packet_indexes = set(
1197 index for (index, frags_400, _) in self.pkt_infos
1198 if len(frags_400) > 1)
1199
1200 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001201 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001202 expire_walk_interval_ms=50)
1203
1204 self.vapi.ip_reassembly_set(timeout_ms=100, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001205 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001206 expire_walk_interval_ms=50, is_ip6=1)
1207
1208 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001209 self.src_if.add_stream(fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001210 self.pg_start()
1211
1212 self.sleep(.25, "wait before sending rest of fragments")
1213
Klement Sekera4c533132018-02-22 11:41:12 +01001214 self.src_if.add_stream(fragments2)
Klement Sekera75e7d132017-09-20 08:26:30 +02001215 self.pg_start()
Klement Sekera75e7d132017-09-20 08:26:30 +02001216
Klement Sekera4c533132018-02-22 11:41:12 +01001217 packets = self.dst_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +02001218 len(self.pkt_infos) - len(dropped_packet_indexes))
Klement Sekera947a85c2019-07-24 12:40:37 +00001219 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001220 pkts = self.src_if.get_capture(
Klement Sekera75e7d132017-09-20 08:26:30 +02001221 expected_count=len(dropped_packet_indexes))
1222 for icmp in pkts:
1223 self.assertIn(ICMPv6TimeExceeded, icmp)
1224 self.assertIn(IPv6ExtHdrFragment, icmp)
1225 self.assertIn(icmp[IPv6ExtHdrFragment].id, dropped_packet_indexes)
1226 dropped_packet_indexes.remove(icmp[IPv6ExtHdrFragment].id)
1227
Klement Sekera947a85c2019-07-24 12:40:37 +00001228 def test_disabled(self):
Klement Sekera75e7d132017-09-20 08:26:30 +02001229 """ reassembly disabled """
1230
1231 dropped_packet_indexes = set(
1232 index for (index, frags_400, _) in self.pkt_infos
1233 if len(frags_400) > 1)
Klement Sekera947a85c2019-07-24 12:40:37 +00001234
1235 self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=0,
1236 max_reassembly_length=3,
1237 expire_walk_interval_ms=10000, is_ip6=1)
1238
1239 self.pg_enable_capture()
1240 self.src_if.add_stream(self.fragments_400)
1241 self.pg_start()
1242
1243 packets = self.dst_if.get_capture(
1244 len(self.pkt_infos) - len(dropped_packet_indexes))
1245 self.verify_capture(packets, dropped_packet_indexes)
Klement Sekera4c533132018-02-22 11:41:12 +01001246 self.src_if.assert_nothing_captured()
Klement Sekera75e7d132017-09-20 08:26:30 +02001247
1248 def test_missing_upper(self):
1249 """ missing upper layer """
Klement Sekera4c533132018-02-22 11:41:12 +01001250 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1251 IPv6(src=self.src_if.remote_ip6,
1252 dst=self.src_if.local_ip6) /
1253 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001254 Raw())
1255 self.extend_packet(p, 1000, self.padding)
1256 fragments = fragment_rfc8200(p, 1, 500)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001257 bad_fragment = p.__class__(scapy.compat.raw(fragments[1]))
Klement Sekera75e7d132017-09-20 08:26:30 +02001258 bad_fragment[IPv6ExtHdrFragment].nh = 59
1259 bad_fragment[IPv6ExtHdrFragment].offset = 0
1260 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001261 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +02001262 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +01001263 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +02001264 icmp = pkts[0]
1265 self.assertIn(ICMPv6ParamProblem, icmp)
1266 self.assert_equal(icmp[ICMPv6ParamProblem].code, 3, "ICMP code")
1267
1268 def test_invalid_frag_size(self):
1269 """ fragment size not a multiple of 8 """
Klement Sekera4c533132018-02-22 11:41:12 +01001270 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1271 IPv6(src=self.src_if.remote_ip6,
1272 dst=self.src_if.local_ip6) /
1273 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001274 Raw())
1275 self.extend_packet(p, 1000, self.padding)
1276 fragments = fragment_rfc8200(p, 1, 500)
1277 bad_fragment = fragments[0]
1278 self.extend_packet(bad_fragment, len(bad_fragment) + 5)
1279 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001280 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +02001281 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +01001282 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +02001283 icmp = pkts[0]
1284 self.assertIn(ICMPv6ParamProblem, icmp)
1285 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1286
1287 def test_invalid_packet_size(self):
1288 """ total packet size > 65535 """
Klement Sekera4c533132018-02-22 11:41:12 +01001289 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1290 IPv6(src=self.src_if.remote_ip6,
1291 dst=self.src_if.local_ip6) /
1292 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001293 Raw())
1294 self.extend_packet(p, 1000, self.padding)
1295 fragments = fragment_rfc8200(p, 1, 500)
1296 bad_fragment = fragments[1]
1297 bad_fragment[IPv6ExtHdrFragment].offset = 65500
1298 self.pg_enable_capture()
Klement Sekera4c533132018-02-22 11:41:12 +01001299 self.src_if.add_stream([bad_fragment])
Klement Sekera75e7d132017-09-20 08:26:30 +02001300 self.pg_start()
Klement Sekera4c533132018-02-22 11:41:12 +01001301 pkts = self.src_if.get_capture(expected_count=1)
Klement Sekera75e7d132017-09-20 08:26:30 +02001302 icmp = pkts[0]
1303 self.assertIn(ICMPv6ParamProblem, icmp)
1304 self.assert_equal(icmp[ICMPv6ParamProblem].code, 0, "ICMP code")
1305
1306
Klement Sekera630ab582019-07-19 09:14:19 +00001307class TestIPv6MWReassembly(VppTestCase):
1308 """ IPv6 Reassembly (multiple workers) """
1309 worker_config = "workers %d" % worker_count
1310
1311 @classmethod
1312 def setUpClass(cls):
1313 super(TestIPv6MWReassembly, cls).setUpClass()
1314
1315 cls.create_pg_interfaces(range(worker_count+1))
1316 cls.src_if = cls.pg0
1317 cls.send_ifs = cls.pg_interfaces[:-1]
1318 cls.dst_if = cls.pg_interfaces[-1]
1319
1320 # setup all interfaces
1321 for i in cls.pg_interfaces:
1322 i.admin_up()
1323 i.config_ip6()
1324 i.resolve_ndp()
1325
1326 # packets sizes reduced here because we are generating packets without
1327 # Ethernet headers, which are added later (diff fragments go via
1328 # different interfaces)
1329 cls.packet_sizes = [64-len(Ether()), 512-len(Ether()),
1330 1518-len(Ether()), 9018-len(Ether())]
1331 cls.padding = " abcdefghijklmn"
1332 cls.create_stream(cls.packet_sizes)
1333 cls.create_fragments()
1334
1335 @classmethod
1336 def tearDownClass(cls):
1337 super(TestIPv6MWReassembly, cls).tearDownClass()
1338
1339 def setUp(self):
1340 """ Test setup - force timeout on existing reassemblies """
1341 super(TestIPv6MWReassembly, self).setUp()
1342 for intf in self.send_ifs:
1343 self.vapi.ip_reassembly_enable_disable(
1344 sw_if_index=intf.sw_if_index, enable_ip6=True)
1345 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
1346 max_reassembly_length=1000,
1347 expire_walk_interval_ms=10, is_ip6=1)
1348 self.sleep(.25)
1349 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
1350 max_reassembly_length=1000,
1351 expire_walk_interval_ms=1000, is_ip6=1)
1352
1353 def tearDown(self):
1354 super(TestIPv6MWReassembly, self).tearDown()
1355
1356 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +00001357 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
Klement Sekera630ab582019-07-19 09:14:19 +00001358 self.logger.debug(self.vapi.ppcli("show buffers"))
1359
1360 @classmethod
1361 def create_stream(cls, packet_sizes, packet_count=test_packet_count):
1362 """Create input packet stream
1363
1364 :param list packet_sizes: Required packet sizes.
1365 """
1366 for i in range(0, packet_count):
1367 info = cls.create_packet_info(cls.src_if, cls.src_if)
1368 payload = cls.info_to_payload(info)
1369 p = (IPv6(src=cls.src_if.remote_ip6,
1370 dst=cls.dst_if.remote_ip6) /
1371 UDP(sport=1234, dport=5678) /
1372 Raw(payload))
1373 size = packet_sizes[(i // 2) % len(packet_sizes)]
1374 cls.extend_packet(p, size, cls.padding)
1375 info.data = p
1376
1377 @classmethod
1378 def create_fragments(cls):
1379 infos = cls._packet_infos
1380 cls.pkt_infos = []
1381 for index, info in six.iteritems(infos):
1382 p = info.data
1383 # cls.logger.debug(ppp("Packet:",
1384 # p.__class__(scapy.compat.raw(p))))
1385 fragments_400 = fragment_rfc8200(p, index, 400)
1386 cls.pkt_infos.append((index, fragments_400))
1387 cls.fragments_400 = [
1388 x for (_, frags) in cls.pkt_infos for x in frags]
1389 cls.logger.debug("Fragmented %s packets into %s 400-byte fragments, " %
1390 (len(infos), len(cls.fragments_400)))
1391
1392 def verify_capture(self, capture, dropped_packet_indexes=[]):
1393 """Verify captured packet strea .
1394
1395 :param list capture: Captured packet stream.
1396 """
1397 info = None
1398 seen = set()
1399 for packet in capture:
1400 try:
1401 self.logger.debug(ppp("Got packet:", packet))
1402 ip = packet[IPv6]
1403 udp = packet[UDP]
1404 payload_info = self.payload_to_info(packet[Raw])
1405 packet_index = payload_info.index
1406 self.assertTrue(
1407 packet_index not in dropped_packet_indexes,
1408 ppp("Packet received, but should be dropped:", packet))
1409 if packet_index in seen:
1410 raise Exception(ppp("Duplicate packet received", packet))
1411 seen.add(packet_index)
1412 self.assertEqual(payload_info.dst, self.src_if.sw_if_index)
1413 info = self._packet_infos[packet_index]
1414 self.assertTrue(info is not None)
1415 self.assertEqual(packet_index, info.index)
1416 saved_packet = info.data
1417 self.assertEqual(ip.src, saved_packet[IPv6].src)
1418 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
1419 self.assertEqual(udp.payload, saved_packet[UDP].payload)
1420 except Exception:
1421 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1422 raise
1423 for index in self._packet_infos:
1424 self.assertTrue(index in seen or index in dropped_packet_indexes,
1425 "Packet with packet_index %d not received" % index)
1426
1427 def send_packets(self, packets):
1428 for counter in range(worker_count):
1429 if 0 == len(packets[counter]):
1430 continue
1431 send_if = self.send_ifs[counter]
1432 send_if.add_stream(
1433 (Ether(dst=send_if.local_mac, src=send_if.remote_mac) / x
1434 for x in packets[counter]),
1435 worker=counter)
1436 self.pg_start()
1437
1438 def test_worker_conflict(self):
1439 """ 1st and FO=0 fragments on different workers """
1440
1441 # in first wave we send fragments which don't start at offset 0
1442 # then we send fragments with offset 0 on a different thread
1443 # then the rest of packets on a random thread
1444 first_packets = [[] for n in range(worker_count)]
1445 second_packets = [[] for n in range(worker_count)]
1446 rest_of_packets = [[] for n in range(worker_count)]
1447 for (_, p) in self.pkt_infos:
1448 wi = randrange(worker_count)
1449 second_packets[wi].append(p[0])
1450 if len(p) <= 1:
1451 continue
1452 wi2 = wi
1453 while wi2 == wi:
1454 wi2 = randrange(worker_count)
1455 first_packets[wi2].append(p[1])
1456 wi3 = randrange(worker_count)
1457 rest_of_packets[wi3].extend(p[2:])
1458
1459 self.pg_enable_capture()
1460 self.send_packets(first_packets)
1461 self.send_packets(second_packets)
1462 self.send_packets(rest_of_packets)
1463
1464 packets = self.dst_if.get_capture(len(self.pkt_infos))
1465 self.verify_capture(packets)
1466 for send_if in self.send_ifs:
1467 send_if.assert_nothing_captured()
1468
Klement Sekera68bae5b2019-10-10 18:57:34 +00001469 self.logger.debug(self.vapi.ppcli("show trace"))
1470 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
1471 self.logger.debug(self.vapi.ppcli("show buffers"))
1472 self.vapi.cli("clear trace")
1473
Klement Sekera630ab582019-07-19 09:14:19 +00001474 self.pg_enable_capture()
1475 self.send_packets(first_packets)
1476 self.send_packets(second_packets)
1477 self.send_packets(rest_of_packets)
1478
1479 packets = self.dst_if.get_capture(len(self.pkt_infos))
1480 self.verify_capture(packets)
1481 for send_if in self.send_ifs:
1482 send_if.assert_nothing_captured()
1483
1484
Klement Sekerade34c352019-06-25 11:19:22 +00001485class TestIPv6SVReassembly(VppTestCase):
1486 """ IPv6 Shallow Virtual Reassembly """
1487
1488 @classmethod
1489 def setUpClass(cls):
1490 super(TestIPv6SVReassembly, cls).setUpClass()
1491
1492 cls.create_pg_interfaces([0, 1])
1493 cls.src_if = cls.pg0
1494 cls.dst_if = cls.pg1
1495
1496 # setup all interfaces
1497 for i in cls.pg_interfaces:
1498 i.admin_up()
1499 i.config_ip6()
1500 i.resolve_ndp()
1501
1502 def setUp(self):
1503 """ Test setup - force timeout on existing reassemblies """
1504 super(TestIPv6SVReassembly, self).setUp()
1505 self.vapi.ip_reassembly_enable_disable(
1506 sw_if_index=self.src_if.sw_if_index, enable_ip6=True,
1507 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
1508 self.vapi.ip_reassembly_set(
1509 timeout_ms=0, max_reassemblies=1000,
1510 max_reassembly_length=1000,
1511 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1512 expire_walk_interval_ms=10, is_ip6=1)
1513 self.sleep(.25)
1514 self.vapi.ip_reassembly_set(
1515 timeout_ms=1000000, max_reassemblies=1000,
1516 max_reassembly_length=1000,
1517 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1518 expire_walk_interval_ms=10000, is_ip6=1)
1519
1520 def tearDown(self):
1521 super(TestIPv6SVReassembly, self).tearDown()
1522 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1523 self.logger.debug(self.vapi.ppcli("show buffers"))
1524
1525 def test_basic(self):
1526 """ basic reassembly """
1527 payload_len = 1000
1528 payload = ""
1529 counter = 0
1530 while len(payload) < payload_len:
1531 payload += "%u " % counter
1532 counter += 1
1533
1534 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1535 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1536 UDP(sport=1234, dport=5678) /
1537 Raw(payload))
1538 fragments = fragment_rfc8200(p, 1, payload_len/4)
1539
1540 # send fragment #2 - should be cached inside reassembly
1541 self.pg_enable_capture()
1542 self.src_if.add_stream(fragments[1])
1543 self.pg_start()
1544 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1545 self.logger.debug(self.vapi.ppcli("show buffers"))
1546 self.logger.debug(self.vapi.ppcli("show trace"))
1547 self.dst_if.assert_nothing_captured()
1548
1549 # send fragment #1 - reassembly is finished now and both fragments
1550 # forwarded
1551 self.pg_enable_capture()
1552 self.src_if.add_stream(fragments[0])
1553 self.pg_start()
1554 self.logger.debug(self.vapi.ppcli("show ip6-sv-reassembly details"))
1555 self.logger.debug(self.vapi.ppcli("show buffers"))
1556 self.logger.debug(self.vapi.ppcli("show trace"))
1557 c = self.dst_if.get_capture(2)
1558 for sent, recvd in zip([fragments[1], fragments[0]], c):
1559 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1560 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1561 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1562
1563 # send rest of fragments - should be immediately forwarded
1564 self.pg_enable_capture()
1565 self.src_if.add_stream(fragments[2:])
1566 self.pg_start()
1567 c = self.dst_if.get_capture(len(fragments[2:]))
1568 for sent, recvd in zip(fragments[2:], c):
1569 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1570 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1571 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1572
1573 def test_timeout(self):
1574 """ reassembly timeout """
1575 payload_len = 1000
1576 payload = ""
1577 counter = 0
1578 while len(payload) < payload_len:
1579 payload += "%u " % counter
1580 counter += 1
1581
1582 p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1583 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1584 UDP(sport=1234, dport=5678) /
1585 Raw(payload))
1586 fragments = fragment_rfc8200(p, 1, payload_len/4)
1587
1588 self.vapi.ip_reassembly_set(
1589 timeout_ms=100, max_reassemblies=1000,
1590 max_reassembly_length=1000,
1591 expire_walk_interval_ms=50,
1592 is_ip6=1,
1593 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL)
1594
1595 # send fragments #2 and #1 - should be forwarded
1596 self.pg_enable_capture()
1597 self.src_if.add_stream(fragments[0:2])
1598 self.pg_start()
1599 self.logger.debug(self.vapi.ppcli("show ip4-sv-reassembly details"))
1600 self.logger.debug(self.vapi.ppcli("show buffers"))
1601 self.logger.debug(self.vapi.ppcli("show trace"))
1602 c = self.dst_if.get_capture(2)
1603 for sent, recvd in zip([fragments[1], fragments[0]], c):
1604 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1605 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1606 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1607
1608 # wait for cleanup
1609 self.sleep(.25, "wait before sending rest of fragments")
1610
1611 # send rest of fragments - shouldn't be forwarded
1612 self.pg_enable_capture()
1613 self.src_if.add_stream(fragments[2:])
1614 self.pg_start()
1615 self.dst_if.assert_nothing_captured()
1616
1617 def test_lru(self):
1618 """ reassembly reuses LRU element """
1619
1620 self.vapi.ip_reassembly_set(
1621 timeout_ms=1000000, max_reassemblies=1,
1622 max_reassembly_length=1000,
1623 type=VppEnum.vl_api_ip_reass_type_t.IP_REASS_TYPE_SHALLOW_VIRTUAL,
1624 is_ip6=1, expire_walk_interval_ms=10000)
1625
1626 payload_len = 1000
1627 payload = ""
1628 counter = 0
1629 while len(payload) < payload_len:
1630 payload += "%u " % counter
1631 counter += 1
1632
1633 packet_count = 10
1634
1635 fragments = [f
1636 for i in range(packet_count)
1637 for p in (Ether(dst=self.src_if.local_mac,
1638 src=self.src_if.remote_mac) /
1639 IPv6(src=self.src_if.remote_ip6,
1640 dst=self.dst_if.remote_ip6) /
1641 UDP(sport=1234, dport=5678) /
1642 Raw(payload))
1643 for f in fragment_rfc8200(p, i, payload_len/4)]
1644
1645 self.pg_enable_capture()
1646 self.src_if.add_stream(fragments)
1647 self.pg_start()
1648 c = self.dst_if.get_capture(len(fragments))
1649 for sent, recvd in zip(fragments, c):
1650 self.assertEqual(sent[IPv6].src, recvd[IPv6].src)
1651 self.assertEqual(sent[IPv6].dst, recvd[IPv6].dst)
1652 self.assertEqual(sent[Raw].payload, recvd[Raw].payload)
1653
1654
Juraj Sloboda3048b632018-10-02 11:13:53 +02001655class TestIPv4ReassemblyLocalNode(VppTestCase):
1656 """ IPv4 Reassembly for packets coming to ip4-local node """
1657
1658 @classmethod
1659 def setUpClass(cls):
1660 super(TestIPv4ReassemblyLocalNode, cls).setUpClass()
1661
1662 cls.create_pg_interfaces([0])
1663 cls.src_dst_if = cls.pg0
1664
1665 # setup all interfaces
1666 for i in cls.pg_interfaces:
1667 i.admin_up()
1668 i.config_ip4()
1669 i.resolve_arp()
1670
1671 cls.padding = " abcdefghijklmn"
1672 cls.create_stream()
1673 cls.create_fragments()
1674
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -07001675 @classmethod
1676 def tearDownClass(cls):
1677 super(TestIPv4ReassemblyLocalNode, cls).tearDownClass()
1678
Juraj Sloboda3048b632018-10-02 11:13:53 +02001679 def setUp(self):
1680 """ Test setup - force timeout on existing reassemblies """
1681 super(TestIPv4ReassemblyLocalNode, self).setUp()
1682 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001683 max_reassembly_length=1000,
Juraj Sloboda3048b632018-10-02 11:13:53 +02001684 expire_walk_interval_ms=10)
1685 self.sleep(.25)
1686 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001687 max_reassembly_length=1000,
Juraj Sloboda3048b632018-10-02 11:13:53 +02001688 expire_walk_interval_ms=10000)
1689
1690 def tearDown(self):
1691 super(TestIPv4ReassemblyLocalNode, self).tearDown()
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -07001692
1693 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +00001694 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +01001695 self.logger.debug(self.vapi.ppcli("show buffers"))
Juraj Sloboda3048b632018-10-02 11:13:53 +02001696
1697 @classmethod
1698 def create_stream(cls, packet_count=test_packet_count):
1699 """Create input packet stream for defined interface.
1700
1701 :param list packet_sizes: Required packet sizes.
1702 """
1703 for i in range(0, packet_count):
1704 info = cls.create_packet_info(cls.src_dst_if, cls.src_dst_if)
1705 payload = cls.info_to_payload(info)
1706 p = (Ether(dst=cls.src_dst_if.local_mac,
1707 src=cls.src_dst_if.remote_mac) /
1708 IP(id=info.index, src=cls.src_dst_if.remote_ip4,
1709 dst=cls.src_dst_if.local_ip4) /
1710 ICMP(type='echo-request', id=1234) /
1711 Raw(payload))
1712 cls.extend_packet(p, 1518, cls.padding)
1713 info.data = p
1714
1715 @classmethod
1716 def create_fragments(cls):
1717 infos = cls._packet_infos
1718 cls.pkt_infos = []
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -08001719 for index, info in six.iteritems(infos):
Juraj Sloboda3048b632018-10-02 11:13:53 +02001720 p = info.data
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001721 # cls.logger.debug(ppp("Packet:",
1722 # p.__class__(scapy.compat.raw(p))))
Juraj Sloboda3048b632018-10-02 11:13:53 +02001723 fragments_300 = fragment_rfc791(p, 300)
1724 cls.pkt_infos.append((index, fragments_300))
1725 cls.fragments_300 = [x for (_, frags) in cls.pkt_infos for x in frags]
1726 cls.logger.debug("Fragmented %s packets into %s 300-byte fragments" %
1727 (len(infos), len(cls.fragments_300)))
1728
1729 def verify_capture(self, capture):
1730 """Verify captured packet stream.
1731
1732 :param list capture: Captured packet stream.
1733 """
1734 info = None
1735 seen = set()
1736 for packet in capture:
1737 try:
1738 self.logger.debug(ppp("Got packet:", packet))
1739 ip = packet[IP]
1740 icmp = packet[ICMP]
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001741 payload_info = self.payload_to_info(packet[Raw])
Juraj Sloboda3048b632018-10-02 11:13:53 +02001742 packet_index = payload_info.index
1743 if packet_index in seen:
1744 raise Exception(ppp("Duplicate packet received", packet))
1745 seen.add(packet_index)
1746 self.assertEqual(payload_info.dst, self.src_dst_if.sw_if_index)
1747 info = self._packet_infos[packet_index]
Klement Sekera14d7e902018-12-10 13:46:09 +01001748 self.assertIsNotNone(info)
Juraj Sloboda3048b632018-10-02 11:13:53 +02001749 self.assertEqual(packet_index, info.index)
1750 saved_packet = info.data
1751 self.assertEqual(ip.src, saved_packet[IP].dst)
1752 self.assertEqual(ip.dst, saved_packet[IP].src)
1753 self.assertEqual(icmp.type, 0) # echo reply
1754 self.assertEqual(icmp.id, saved_packet[ICMP].id)
1755 self.assertEqual(icmp.payload, saved_packet[ICMP].payload)
1756 except Exception:
1757 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1758 raise
1759 for index in self._packet_infos:
Klement Sekera14d7e902018-12-10 13:46:09 +01001760 self.assertIn(index, seen,
1761 "Packet with packet_index %d not received" % index)
Juraj Sloboda3048b632018-10-02 11:13:53 +02001762
1763 def test_reassembly(self):
1764 """ basic reassembly """
1765
1766 self.pg_enable_capture()
1767 self.src_dst_if.add_stream(self.fragments_300)
1768 self.pg_start()
1769
1770 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
1771 self.verify_capture(packets)
1772
1773 # run it all again to verify correctness
1774 self.pg_enable_capture()
1775 self.src_dst_if.add_stream(self.fragments_300)
1776 self.pg_start()
1777
1778 packets = self.src_dst_if.get_capture(len(self.pkt_infos))
1779 self.verify_capture(packets)
1780
1781
Klement Sekera75e7d132017-09-20 08:26:30 +02001782class TestFIFReassembly(VppTestCase):
1783 """ Fragments in fragments reassembly """
1784
1785 @classmethod
1786 def setUpClass(cls):
1787 super(TestFIFReassembly, cls).setUpClass()
1788
Klement Sekera4c533132018-02-22 11:41:12 +01001789 cls.create_pg_interfaces([0, 1])
1790 cls.src_if = cls.pg0
1791 cls.dst_if = cls.pg1
1792 for i in cls.pg_interfaces:
1793 i.admin_up()
1794 i.config_ip4()
1795 i.resolve_arp()
1796 i.config_ip6()
1797 i.resolve_ndp()
Klement Sekera75e7d132017-09-20 08:26:30 +02001798
Klement Sekera75e7d132017-09-20 08:26:30 +02001799 cls.packet_sizes = [64, 512, 1518, 9018]
1800 cls.padding = " abcdefghijklmn"
1801
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -07001802 @classmethod
1803 def tearDownClass(cls):
1804 super(TestFIFReassembly, cls).tearDownClass()
1805
Klement Sekera75e7d132017-09-20 08:26:30 +02001806 def setUp(self):
1807 """ Test setup - force timeout on existing reassemblies """
1808 super(TestFIFReassembly, self).setUp()
Klement Sekera4c533132018-02-22 11:41:12 +01001809 self.vapi.ip_reassembly_enable_disable(
1810 sw_if_index=self.src_if.sw_if_index, enable_ip4=True,
1811 enable_ip6=True)
1812 self.vapi.ip_reassembly_enable_disable(
1813 sw_if_index=self.dst_if.sw_if_index, enable_ip4=True,
1814 enable_ip6=True)
Klement Sekera75e7d132017-09-20 08:26:30 +02001815 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001816 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001817 expire_walk_interval_ms=10)
1818 self.vapi.ip_reassembly_set(timeout_ms=0, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001819 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001820 expire_walk_interval_ms=10, is_ip6=1)
1821 self.sleep(.25)
1822 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001823 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001824 expire_walk_interval_ms=10000)
1825 self.vapi.ip_reassembly_set(timeout_ms=1000000, max_reassemblies=1000,
Klement Sekera3a343d42019-05-16 14:35:46 +02001826 max_reassembly_length=1000,
Klement Sekera75e7d132017-09-20 08:26:30 +02001827 expire_walk_interval_ms=10000, is_ip6=1)
1828
1829 def tearDown(self):
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -07001830 super(TestFIFReassembly, self).tearDown()
1831
1832 def show_commands_at_teardown(self):
Klement Sekera896c8962019-06-24 11:52:49 +00001833 self.logger.debug(self.vapi.ppcli("show ip4-full-reassembly details"))
1834 self.logger.debug(self.vapi.ppcli("show ip6-full-reassembly details"))
Klement Sekeracae98b72019-02-19 13:53:43 +01001835 self.logger.debug(self.vapi.ppcli("show buffers"))
Klement Sekera75e7d132017-09-20 08:26:30 +02001836
1837 def verify_capture(self, capture, ip_class, dropped_packet_indexes=[]):
1838 """Verify captured packet stream.
1839
1840 :param list capture: Captured packet stream.
1841 """
1842 info = None
1843 seen = set()
1844 for packet in capture:
1845 try:
Klement Sekera4c533132018-02-22 11:41:12 +01001846 self.logger.debug(ppp("Got packet:", packet))
Klement Sekera75e7d132017-09-20 08:26:30 +02001847 ip = packet[ip_class]
1848 udp = packet[UDP]
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001849 payload_info = self.payload_to_info(packet[Raw])
Klement Sekera75e7d132017-09-20 08:26:30 +02001850 packet_index = payload_info.index
1851 self.assertTrue(
1852 packet_index not in dropped_packet_indexes,
1853 ppp("Packet received, but should be dropped:", packet))
1854 if packet_index in seen:
1855 raise Exception(ppp("Duplicate packet received", packet))
1856 seen.add(packet_index)
Klement Sekera4c533132018-02-22 11:41:12 +01001857 self.assertEqual(payload_info.dst, self.dst_if.sw_if_index)
Klement Sekera75e7d132017-09-20 08:26:30 +02001858 info = self._packet_infos[packet_index]
1859 self.assertTrue(info is not None)
1860 self.assertEqual(packet_index, info.index)
1861 saved_packet = info.data
1862 self.assertEqual(ip.src, saved_packet[ip_class].src)
1863 self.assertEqual(ip.dst, saved_packet[ip_class].dst)
1864 self.assertEqual(udp.payload, saved_packet[UDP].payload)
Klement Sekera4c533132018-02-22 11:41:12 +01001865 except Exception:
Klement Sekera75e7d132017-09-20 08:26:30 +02001866 self.logger.error(ppp("Unexpected or invalid packet:", packet))
1867 raise
1868 for index in self._packet_infos:
1869 self.assertTrue(index in seen or index in dropped_packet_indexes,
1870 "Packet with packet_index %d not received" % index)
1871
1872 def test_fif4(self):
1873 """ Fragments in fragments (4o4) """
1874
1875 # TODO this should be ideally in setUpClass, but then we hit a bug
1876 # with VppIpRoute incorrectly reporting it's present when it's not
1877 # so we need to manually remove the vpp config, thus we cannot have
1878 # it shared for multiple test cases
1879 self.tun_ip4 = "1.1.1.2"
1880
Klement Sekera4c533132018-02-22 11:41:12 +01001881 self.gre4 = VppGreInterface(self, self.src_if.local_ip4, self.tun_ip4)
Klement Sekera75e7d132017-09-20 08:26:30 +02001882 self.gre4.add_vpp_config()
1883 self.gre4.admin_up()
1884 self.gre4.config_ip4()
1885
Klement Sekera4c533132018-02-22 11:41:12 +01001886 self.vapi.ip_reassembly_enable_disable(
1887 sw_if_index=self.gre4.sw_if_index, enable_ip4=True)
1888
Klement Sekera75e7d132017-09-20 08:26:30 +02001889 self.route4 = VppIpRoute(self, self.tun_ip4, 32,
Klement Sekera4c533132018-02-22 11:41:12 +01001890 [VppRoutePath(self.src_if.remote_ip4,
1891 self.src_if.sw_if_index)])
Klement Sekera75e7d132017-09-20 08:26:30 +02001892 self.route4.add_vpp_config()
1893
1894 self.reset_packet_infos()
1895 for i in range(test_packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01001896 info = self.create_packet_info(self.src_if, self.dst_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02001897 payload = self.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +01001898 # Ethernet header here is only for size calculation, thus it
1899 # doesn't matter how it's initialized. This is to ensure that
1900 # reassembled packet is not > 9000 bytes, so that it's not dropped
1901 p = (Ether() /
1902 IP(id=i, src=self.src_if.remote_ip4,
1903 dst=self.dst_if.remote_ip4) /
1904 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001905 Raw(payload))
1906 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1907 self.extend_packet(p, size, self.padding)
Klement Sekera4c533132018-02-22 11:41:12 +01001908 info.data = p[IP] # use only IP part, without ethernet header
Klement Sekera75e7d132017-09-20 08:26:30 +02001909
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -08001910 fragments = [x for _, p in six.iteritems(self._packet_infos)
Klement Sekera75e7d132017-09-20 08:26:30 +02001911 for x in fragment_rfc791(p.data, 400)]
1912
1913 encapped_fragments = \
Klement Sekera4c533132018-02-22 11:41:12 +01001914 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1915 IP(src=self.tun_ip4, dst=self.src_if.local_ip4) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001916 GRE() /
1917 p
1918 for p in fragments]
1919
1920 fragmented_encapped_fragments = \
1921 [x for p in encapped_fragments
1922 for x in fragment_rfc791(p, 200)]
1923
Klement Sekera4c533132018-02-22 11:41:12 +01001924 self.src_if.add_stream(fragmented_encapped_fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001925
1926 self.pg_enable_capture(self.pg_interfaces)
1927 self.pg_start()
1928
Klement Sekera4c533132018-02-22 11:41:12 +01001929 self.src_if.assert_nothing_captured()
1930 packets = self.dst_if.get_capture(len(self._packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +02001931 self.verify_capture(packets, IP)
1932
1933 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
1934 # so that it's query_vpp_config() works as it should
1935 self.gre4.remove_vpp_config()
Klement Sekera4c533132018-02-22 11:41:12 +01001936 self.logger.debug(self.vapi.ppcli("show interface"))
Klement Sekera75e7d132017-09-20 08:26:30 +02001937
1938 def test_fif6(self):
1939 """ Fragments in fragments (6o6) """
1940 # TODO this should be ideally in setUpClass, but then we hit a bug
1941 # with VppIpRoute incorrectly reporting it's present when it's not
1942 # so we need to manually remove the vpp config, thus we cannot have
1943 # it shared for multiple test cases
1944 self.tun_ip6 = "1002::1"
1945
Neale Ranns5a8844b2019-04-16 07:15:35 +00001946 self.gre6 = VppGreInterface(self, self.src_if.local_ip6, self.tun_ip6)
Klement Sekera75e7d132017-09-20 08:26:30 +02001947 self.gre6.add_vpp_config()
1948 self.gre6.admin_up()
1949 self.gre6.config_ip6()
1950
Klement Sekera4c533132018-02-22 11:41:12 +01001951 self.vapi.ip_reassembly_enable_disable(
1952 sw_if_index=self.gre6.sw_if_index, enable_ip6=True)
1953
Klement Sekera75e7d132017-09-20 08:26:30 +02001954 self.route6 = VppIpRoute(self, self.tun_ip6, 128,
Neale Ranns097fa662018-05-01 05:17:55 -07001955 [VppRoutePath(
1956 self.src_if.remote_ip6,
1957 self.src_if.sw_if_index)])
Klement Sekera75e7d132017-09-20 08:26:30 +02001958 self.route6.add_vpp_config()
1959
1960 self.reset_packet_infos()
1961 for i in range(test_packet_count):
Klement Sekera4c533132018-02-22 11:41:12 +01001962 info = self.create_packet_info(self.src_if, self.dst_if)
Klement Sekera75e7d132017-09-20 08:26:30 +02001963 payload = self.info_to_payload(info)
Klement Sekera4c533132018-02-22 11:41:12 +01001964 # Ethernet header here is only for size calculation, thus it
1965 # doesn't matter how it's initialized. This is to ensure that
1966 # reassembled packet is not > 9000 bytes, so that it's not dropped
1967 p = (Ether() /
1968 IPv6(src=self.src_if.remote_ip6, dst=self.dst_if.remote_ip6) /
1969 UDP(sport=1234, dport=5678) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001970 Raw(payload))
1971 size = self.packet_sizes[(i // 2) % len(self.packet_sizes)]
1972 self.extend_packet(p, size, self.padding)
Klement Sekera4c533132018-02-22 11:41:12 +01001973 info.data = p[IPv6] # use only IPv6 part, without ethernet header
Klement Sekera75e7d132017-09-20 08:26:30 +02001974
Paul Vinciguerraf1f2aa62018-11-25 08:36:47 -08001975 fragments = [x for _, i in six.iteritems(self._packet_infos)
Klement Sekera75e7d132017-09-20 08:26:30 +02001976 for x in fragment_rfc8200(
1977 i.data, i.index, 400)]
1978
1979 encapped_fragments = \
Klement Sekera4c533132018-02-22 11:41:12 +01001980 [Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1981 IPv6(src=self.tun_ip6, dst=self.src_if.local_ip6) /
Klement Sekera75e7d132017-09-20 08:26:30 +02001982 GRE() /
1983 p
1984 for p in fragments]
1985
1986 fragmented_encapped_fragments = \
1987 [x for p in encapped_fragments for x in (
1988 fragment_rfc8200(
1989 p,
1990 2 * len(self._packet_infos) + p[IPv6ExtHdrFragment].id,
1991 200)
1992 if IPv6ExtHdrFragment in p else [p]
1993 )
1994 ]
1995
Klement Sekera4c533132018-02-22 11:41:12 +01001996 self.src_if.add_stream(fragmented_encapped_fragments)
Klement Sekera75e7d132017-09-20 08:26:30 +02001997
1998 self.pg_enable_capture(self.pg_interfaces)
1999 self.pg_start()
2000
Klement Sekera4c533132018-02-22 11:41:12 +01002001 self.src_if.assert_nothing_captured()
2002 packets = self.dst_if.get_capture(len(self._packet_infos))
Klement Sekera75e7d132017-09-20 08:26:30 +02002003 self.verify_capture(packets, IPv6)
2004
2005 # TODO remove gre vpp config by hand until VppIpRoute gets fixed
2006 # so that it's query_vpp_config() works as it should
Klement Sekera3ecc2212018-03-27 10:34:43 +02002007 self.gre6.remove_vpp_config()
Klement Sekera75e7d132017-09-20 08:26:30 +02002008
2009
2010if __name__ == '__main__':
2011 unittest.main(testRunner=VppTestRunner)