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