test: ip6 vrf instances multi-context test (CSIT-497)
[vpp.git] / test / test_ip6_vrf_multi_instance.py
1 #!/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
61 import unittest
62 import random
63
64 from scapy.packet import Raw
65 from scapy.layers.l2 import Ether
66 from scapy.layers.inet6 import IPv6, UDP
67
68 from framework import VppTestCase, VppTestRunner
69 from util import ppp
70
71
72 class 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
406 if __name__ == '__main__':
407     unittest.main(testRunner=VppTestRunner)