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