blob: 94185358cb53dcfc01d41ac509f25c557c2b6041 [file] [log] [blame]
Jan Gelety057bb8c2016-12-20 17:32:45 +01001#!/usr/bin/env python
2"""IP6 VRF Multi-instance Test Case HLD:
3
4**NOTES:**
5 - higher number of pg-ip6 interfaces causes problems => only 15 pg-ip6 \
6 interfaces in 5 VRFs are tested
7 - jumbo packets in configuration with 15 pg-ip6 interfaces leads to \
8 problems too
9 - Reset of FIB table / VRF does not remove routes from IP FIB (see Jira \
10 ticket https://jira.fd.io/browse/VPP-560) so checks of reset VRF tables \
11 are skipped in tests 2, 3 and 4
12
13**config 1**
14 - add 15 pg-ip6 interfaces
15 - configure 5 hosts per pg-ip6 interface
16 - configure 4 VRFs
17 - add 3 pg-ip6 interfaces per VRF
18
19**test 1**
20 - send IP6 packets between all pg-ip6 interfaces in all VRF groups
21
22**verify 1**
23 - check VRF data by parsing output of ip6_fib_dump API command
24 - all packets received correctly in case of pg-ip6 interfaces in VRF
25 - no packet received in case of pg-ip6 interfaces not in VRF
26
27**config 2**
28 - delete 2 VRFs
29
30**test 2**
31 - send IP6 packets between all pg-ip6 interfaces in all VRF groups
32
33**verify 2**
34 - check VRF data by parsing output of ip6_fib_dump API command
35 - all packets received correctly in case of pg-ip6 interfaces in VRF
36 - no packet received in case of pg-ip6 interfaces not in VRF
37
38**config 3**
39 - add 1 of deleted VRFs and 1 new VRF
40
41**test 3**
42 - send IP6 packets between all pg-ip6 interfaces in all VRF groups
43
44**verify 3**
45 - check VRF data by parsing output of ip6_fib_dump API command
46 - all packets received correctly in case of pg-ip6 interfaces in VRF
47 - no packet received in case of pg-ip6 interfaces not in VRF
48
49**config 4**
50 - delete all VRFs (i.e. no VRF except VRF=0 created)
51
52**test 4**
53 - send IP6 packets between all pg-ip6 interfaces in all VRF groups
54
55**verify 4**
56 - check VRF data by parsing output of ip6_fib_dump API command
57 - all packets received correctly in case of pg-ip6 interfaces in VRF
58 - no packet received in case of pg-ip6 interfaces not in VRF
59"""
60
61import unittest
62import random
63
64from scapy.packet import Raw
65from scapy.layers.l2 import Ether
66from scapy.layers.inet6 import IPv6, UDP
67
68from framework import VppTestCase, VppTestRunner
69from util import ppp
70
71
72class TestIP6VrfMultiInst(VppTestCase):
73 """ IP6 VRF Multi-instance Test Case """
74
75 @classmethod
76 def setUpClass(cls):
77 """
78 Perform standard class setup (defined by class method setUpClass in
79 class VppTestCase) before running the test case, set test case related
80 variables and configure VPP.
81 """
82 super(TestIP6VrfMultiInst, cls).setUpClass()
83
84 # Test variables
85 cls.hosts_per_pg = 5
86 cls.nr_of_vrfs = 5
87 cls.pg_ifs_per_vrf = 3
88
89 try:
90 # Create pg interfaces
91 cls.create_pg_interfaces(
92 range(cls.nr_of_vrfs * cls.pg_ifs_per_vrf))
93
94 # Packet flows mapping pg0 -> pg1, pg2 etc.
95 cls.flows = dict()
96 for i in range(len(cls.pg_interfaces)):
97 multiplicand = i / cls.pg_ifs_per_vrf
98 pg_list = [
99 cls.pg_interfaces[multiplicand * cls.pg_ifs_per_vrf + j]
100 for j in range(cls.pg_ifs_per_vrf)
101 if (multiplicand * cls.pg_ifs_per_vrf + j) != i]
102 cls.flows[cls.pg_interfaces[i]] = pg_list
103
104 # Packet sizes - jumbo packet (9018 bytes) skipped
105 cls.pg_if_packet_sizes = [64, 512, 1518]
106
107 # Set up all interfaces
108 for pg_if in cls.pg_interfaces:
109 pg_if.admin_up()
110 pg_if.generate_remote_hosts(cls.hosts_per_pg)
111
112 # Create list of VRFs
113 cls.vrf_list = list()
114
115 # Create list of deleted VRFs
116 cls.vrf_deleted_list = list()
117
118 # Create list of pg_interfaces in VRFs
119 cls.pg_in_vrf = list()
120
121 # Create list of pg_interfaces not in BDs
122 cls.pg_not_in_vrf = [pg_if for pg_if in cls.pg_interfaces]
123
124 # Create mapping of pg_interfaces to VRF IDs
125 cls.pg_if_by_vrf_id = dict()
126 for i in range(cls.nr_of_vrfs):
127 vrf_id = i + 1
128 pg_list = [
129 cls.pg_interfaces[i * cls.pg_ifs_per_vrf + j]
130 for j in range(cls.pg_ifs_per_vrf)]
131 cls.pg_if_by_vrf_id[vrf_id] = pg_list
132
133 except Exception:
134 super(TestIP6VrfMultiInst, cls).tearDownClass()
135 raise
136
137 def setUp(self):
138 """
139 Clear trace and packet infos before running each test.
140 """
141 super(TestIP6VrfMultiInst, self).setUp()
142 self.reset_packet_infos()
143
144 def tearDown(self):
145 """
146 Show various debug prints after each test.
147 """
148 super(TestIP6VrfMultiInst, self).tearDown()
149 if not self.vpp_dead:
150 self.logger.info(self.vapi.ppcli("show ip6 fib"))
151 self.logger.info(self.vapi.ppcli("show ip6 neighbors"))
152
153 def create_vrf_and_assign_interfaces(self, count, start=1):
154 """
155 Create required number of FIB tables / VRFs, put 3 l2-pg interfaces
156 to every FIB table / VRF.
157
158 :param int count: Number of FIB tables / VRFs to be created.
159 :param int start: Starting number of the FIB table / VRF ID. \
160 (Default value = 1)
161 """
162 for i in range(count):
163 vrf_id = i + start
164 pg_if = self.pg_if_by_vrf_id[vrf_id][0]
165 dest_addr = pg_if.remote_hosts[0].ip6n
166 dest_addr_len = 64
167 self.vapi.ip_add_del_route(
168 dest_addr, dest_addr_len, pg_if.local_ip6n, is_ipv6=1,
169 table_id=vrf_id, create_vrf_if_needed=1, is_multipath=1)
170 self.logger.info("IPv6 VRF ID %d created" % vrf_id)
171 if vrf_id not in self.vrf_list:
172 self.vrf_list.append(vrf_id)
173 if vrf_id in self.vrf_deleted_list:
174 self.vrf_deleted_list.remove(vrf_id)
175 for j in range(self.pg_ifs_per_vrf):
176 pg_if = self.pg_if_by_vrf_id[vrf_id][j]
177 pg_if.set_table_ip6(vrf_id)
178 self.logger.info("pg-interface %s added to IPv6 VRF ID %d"
179 % (pg_if.name, vrf_id))
180 if pg_if not in self.pg_in_vrf:
181 self.pg_in_vrf.append(pg_if)
182 if pg_if in self.pg_not_in_vrf:
183 self.pg_not_in_vrf.remove(pg_if)
184 pg_if.config_ip6()
185 pg_if.disable_ipv6_ra()
186 pg_if.configure_ipv6_neighbors(vrf_id)
187 self.logger.debug(self.vapi.ppcli("show ip6 fib"))
188 self.logger.debug(self.vapi.ppcli("show ip6 neighbors"))
189
190 def reset_vrf(self, vrf_id):
191 """
192 Delete required FIB table / VRF.
193
194 :param int vrf_id: The FIB table / VRF ID to be deleted.
195 """
196 # self.vapi.reset_vrf(vrf_id, is_ipv6=1)
197 self.vapi.reset_fib(vrf_id, is_ipv6=1)
198 if vrf_id in self.vrf_list:
199 self.vrf_list.remove(vrf_id)
200 if vrf_id not in self.vrf_deleted_list:
201 self.vrf_deleted_list.append(vrf_id)
202 for j in range(self.pg_ifs_per_vrf):
203 pg_if = self.pg_if_by_vrf_id[vrf_id][j]
204 if pg_if in self.pg_in_vrf:
205 self.pg_in_vrf.remove(pg_if)
206 if pg_if not in self.pg_not_in_vrf:
207 self.pg_not_in_vrf.append(pg_if)
208 self.logger.info("IPv6 VRF ID %d reset" % vrf_id)
209 self.logger.debug(self.vapi.ppcli("show ip6 fib"))
210 self.logger.debug(self.vapi.ppcli("show ip6 neighbors"))
211
212 def create_stream(self, src_if, packet_sizes):
213 """
214 Create input packet stream for defined interface using hosts list.
215
216 :param object src_if: Interface to create packet stream for.
217 :param list packet_sizes: List of required packet sizes.
218 :return: Stream of packets.
219 """
220 pkts = []
221 src_hosts = src_if.remote_hosts
222 for dst_if in self.flows[src_if]:
223 for dst_host in dst_if.remote_hosts:
224 src_host = random.choice(src_hosts)
225 pkt_info = self.create_packet_info(src_if, dst_if)
226 payload = self.info_to_payload(pkt_info)
227 p = (Ether(dst=src_if.local_mac, src=src_host.mac) /
228 IPv6(src=src_host.ip6, dst=dst_host.ip6) /
229 UDP(sport=1234, dport=1234) /
230 Raw(payload))
231 pkt_info.data = p.copy()
232 size = random.choice(packet_sizes)
233 self.extend_packet(p, size)
234 pkts.append(p)
235 self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
236 % (src_if.name, len(pkts)))
237 return pkts
238
239 def verify_capture(self, pg_if, capture):
240 """
241 Verify captured input packet stream for defined interface.
242
243 :param object pg_if: Interface to verify captured packet stream for.
244 :param list capture: Captured packet stream.
245 """
246 last_info = dict()
247 for i in self.pg_interfaces:
248 last_info[i.sw_if_index] = None
249 dst_sw_if_index = pg_if.sw_if_index
250 for packet in capture:
251 try:
252 ip = packet[IPv6]
253 udp = packet[UDP]
254 payload_info = self.payload_to_info(str(packet[Raw]))
255 packet_index = payload_info.index
256 self.assertEqual(payload_info.dst, dst_sw_if_index)
257 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
258 (pg_if.name, payload_info.src, packet_index))
259 next_info = self.get_next_packet_info_for_interface2(
260 payload_info.src, dst_sw_if_index,
261 last_info[payload_info.src])
262 last_info[payload_info.src] = next_info
263 self.assertIsNotNone(next_info)
264 self.assertEqual(packet_index, next_info.index)
265 saved_packet = next_info.data
266 # Check standard fields
267 self.assertEqual(ip.src, saved_packet[IPv6].src)
268 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
269 self.assertEqual(udp.sport, saved_packet[UDP].sport)
270 self.assertEqual(udp.dport, saved_packet[UDP].dport)
271 except:
272 self.logger.error(ppp("Unexpected or invalid packet:", packet))
273 raise
274 for i in self.pg_interfaces:
275 remaining_packet = self.get_next_packet_info_for_interface2(
276 i, dst_sw_if_index, last_info[i.sw_if_index])
277 self.assertIsNone(
278 remaining_packet,
279 "Port %u: Packet expected from source %u didn't arrive" %
280 (dst_sw_if_index, i.sw_if_index))
281
282 def verify_vrf(self, vrf_id):
283 """
284 Check if the FIB table / VRF ID is configured.
285
286 :param int vrf_id: The FIB table / VRF ID to be verified.
287 :return: 1 if the FIB table / VRF ID is configured, otherwise return 0.
288 """
289 ip6_fib_dump = self.vapi.ip6_fib_dump()
290 vrf_count = 0
291 for ip6_fib_details in ip6_fib_dump:
292 if ip6_fib_details[2] == vrf_id:
293 vrf_count += 1
294 if vrf_count == 0:
295 self.logger.info("IPv6 VRF ID %d is not configured" % vrf_id)
296 return 0
297 else:
298 self.logger.info("IPv6 VRF ID %d is configured" % vrf_id)
299 return 1
300
301 def run_verify_test(self):
302 """
303 Create packet streams for all configured l2-pg interfaces, send all \
304 prepared packet streams and verify that:
305 - all packets received correctly on all pg-l2 interfaces assigned
306 to bridge domains
307 - no packet received on all pg-l2 interfaces not assigned to bridge
308 domains
309
310 :raise RuntimeError: If no packet captured on l2-pg interface assigned
311 to the bridge domain or if any packet is captured on l2-pg
312 interface not assigned to the bridge domain.
313 """
314 # Test
315 # Create incoming packet streams for packet-generator interfaces
316 for pg_if in self.pg_interfaces:
317 pkts = self.create_stream(pg_if, self.pg_if_packet_sizes)
318 pg_if.add_stream(pkts)
319
320 # Enable packet capture and start packet sending
321 self.pg_enable_capture(self.pg_interfaces)
322 self.pg_start()
323
324 # Verify
325 # Verify outgoing packet streams per packet-generator interface
326 for pg_if in self.pg_interfaces:
327 if pg_if in self.pg_in_vrf:
328 capture = pg_if.get_capture(remark="interface is in VRF")
329 self.verify_capture(pg_if, capture)
330 elif pg_if in self.pg_not_in_vrf:
331 pg_if.assert_nothing_captured(remark="interface is not in VRF")
332 self.logger.debug("No capture for interface %s" % pg_if.name)
333 else:
334 raise Exception("Unknown interface: %s" % pg_if.name)
335
336 def test_ip6_vrf_01(self):
337 """ IP6 VRF Multi-instance test 1 - create 4 VRFs
338 """
339 # Config 1
340 # Create 4 VRFs
341 self.create_vrf_and_assign_interfaces(4)
342
343 # Verify 1
344 for vrf_id in self.vrf_list:
345 self.assertEqual(self.verify_vrf(vrf_id), 1)
346
347 # Test 1
348 self.run_verify_test()
349
350 @unittest.skip("IPv6 FIB reset leads to crash of VPP - Jira ticket "
351 "https://jira.fd.io/browse/VPP-643")
352 def test_ip6_vrf_02(self):
353 """ IP6 VRF Multi-instance test 2 - reset 2 VRFs
354 """
355 # Config 2
356 # Delete 2 VRFs
357 self.reset_vrf(1)
358 self.reset_vrf(2)
359
360 # Verify 2
361 # for vrf_id in self.vrf_deleted_list:
362 # self.assertEqual(self.verify_vrf(vrf_id), 0)
363 for vrf_id in self.vrf_list:
364 self.assertEqual(self.verify_vrf(vrf_id), 1)
365
366 # Test 2
367 self.run_verify_test()
368
369 def test_ip6_vrf_03(self):
370 """ IP6 VRF Multi-instance 3 - add 2 VRFs
371 """
372 # Config 3
373 # Add 1 of deleted VRFs and 1 new VRF
374 # self.create_vrf_and_assign_interfaces(1)
375 self.create_vrf_and_assign_interfaces(1, start=5)
376
377 # Verify 3
378 # for vrf_id in self.vrf_deleted_list:
379 # self.assertEqual(self.verify_vrf(vrf_id), 0)
380 for vrf_id in self.vrf_list:
381 self.assertEqual(self.verify_vrf(vrf_id), 1)
382
383 # Test 3
384 self.run_verify_test()
385
386 @unittest.skip("IPv6 FIB reset leads to crash of VPP - Jira ticket "
387 "https://jira.fd.io/browse/VPP-643")
388 def test_ip6_vrf_04(self):
389 """ IP6 VRF Multi-instance test 4 - reset 4 VRFs
390 """
391 # Config 4
392 # Delete all VRFs (i.e. no VRF except VRF=0 created)
393 for i in range(len(self.vrf_list)):
394 self.reset_vrf(self.vrf_list[0])
395
396 # Verify 4
397 # for vrf_id in self.vrf_deleted_list:
398 # self.assertEqual(self.verify_vrf(vrf_id), 0)
399 for vrf_id in self.vrf_list:
400 self.assertEqual(self.verify_vrf(vrf_id), 1)
401
402 # Test 4
403 self.run_verify_test()
404
405
406if __name__ == '__main__':
407 unittest.main(testRunner=VppTestRunner)