FIB table add/delete 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_table_add_del(vrf_id, is_add=1)
176             self.vapi.ip_add_del_route(
177                 dest_addr, dest_addr_len, pg_if.local_ip4n,
178                 table_id=vrf_id, is_multipath=1)
179             self.logger.info("IPv4 VRF ID %d created" % vrf_id)
180             if vrf_id not in self.vrf_list:
181                 self.vrf_list.append(vrf_id)
182             if vrf_id in self.vrf_deleted_list:
183                 self.vrf_deleted_list.remove(vrf_id)
184             for j in range(self.pg_ifs_per_vrf):
185                 pg_if = self.pg_if_by_vrf_id[vrf_id][j]
186                 pg_if.set_table_ip4(vrf_id)
187                 self.logger.info("pg-interface %s added to IPv4 VRF ID %d"
188                                  % (pg_if.name, vrf_id))
189                 if pg_if not in self.pg_in_vrf:
190                     self.pg_in_vrf.append(pg_if)
191                 if pg_if in self.pg_not_in_vrf:
192                     self.pg_not_in_vrf.remove(pg_if)
193                 pg_if.config_ip4()
194                 pg_if.configure_ipv4_neighbors()
195         self.logger.debug(self.vapi.ppcli("show ip fib"))
196         self.logger.debug(self.vapi.ppcli("show ip arp"))
197
198     def delete_vrf(self, vrf_id):
199         """
200         Delete required FIB table / VRF.
201
202         :param int vrf_id: The FIB table / VRF ID to be deleted.
203         """
204         # self.vapi.reset_vrf(vrf_id, is_ipv6=0)
205         self.vapi.reset_fib(vrf_id, is_ipv6=0)
206         if vrf_id in self.vrf_list:
207             self.vrf_list.remove(vrf_id)
208         if vrf_id not in self.vrf_deleted_list:
209             self.vrf_deleted_list.append(vrf_id)
210         for j in range(self.pg_ifs_per_vrf):
211             pg_if = self.pg_if_by_vrf_id[vrf_id][j]
212             pg_if.unconfig_ip4()
213             if pg_if in self.pg_in_vrf:
214                 self.pg_in_vrf.remove(pg_if)
215             if pg_if not in self.pg_not_in_vrf:
216                 self.pg_not_in_vrf.append(pg_if)
217         self.logger.info("IPv4 VRF ID %d reset" % vrf_id)
218         self.logger.debug(self.vapi.ppcli("show ip fib"))
219         self.logger.debug(self.vapi.ppcli("show ip arp"))
220         self.vapi.ip_table_add_del(vrf_id, is_add=0)
221
222     def create_stream(self, src_if, packet_sizes):
223         """
224         Create input packet stream for defined interface using hosts list.
225
226         :param object src_if: Interface to create packet stream for.
227         :param list packet_sizes: List of required packet sizes.
228         :return: Stream of packets.
229         """
230         pkts = []
231         src_hosts = src_if.remote_hosts
232         for dst_if in self.flows[src_if]:
233             for dst_host in dst_if.remote_hosts:
234                 src_host = random.choice(src_hosts)
235                 pkt_info = self.create_packet_info(src_if, dst_if)
236                 payload = self.info_to_payload(pkt_info)
237                 p = (Ether(dst=src_if.local_mac, src=src_host.mac) /
238                      IP(src=src_host.ip4, dst=dst_host.ip4) /
239                      UDP(sport=1234, dport=1234) /
240                      Raw(payload))
241                 pkt_info.data = p.copy()
242                 size = random.choice(packet_sizes)
243                 self.extend_packet(p, size)
244                 pkts.append(p)
245         self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
246                           % (src_if.name, len(pkts)))
247         return pkts
248
249     def verify_capture(self, pg_if, capture):
250         """
251         Verify captured input packet stream for defined interface.
252
253         :param object pg_if: Interface to verify captured packet stream for.
254         :param list capture: Captured packet stream.
255         """
256         last_info = dict()
257         for i in self.pg_interfaces:
258             last_info[i.sw_if_index] = None
259         dst_sw_if_index = pg_if.sw_if_index
260         for packet in capture:
261             try:
262                 ip = packet[IP]
263                 udp = packet[UDP]
264                 payload_info = self.payload_to_info(str(packet[Raw]))
265                 packet_index = payload_info.index
266                 self.assertEqual(payload_info.dst, dst_sw_if_index)
267                 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
268                                   (pg_if.name, payload_info.src, packet_index))
269                 next_info = self.get_next_packet_info_for_interface2(
270                     payload_info.src, dst_sw_if_index,
271                     last_info[payload_info.src])
272                 last_info[payload_info.src] = next_info
273                 self.assertIsNotNone(next_info)
274                 self.assertEqual(packet_index, next_info.index)
275                 saved_packet = next_info.data
276                 # Check standard fields
277                 self.assertEqual(ip.src, saved_packet[IP].src)
278                 self.assertEqual(ip.dst, saved_packet[IP].dst)
279                 self.assertEqual(udp.sport, saved_packet[UDP].sport)
280                 self.assertEqual(udp.dport, saved_packet[UDP].dport)
281             except:
282                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
283                 raise
284         for i in self.pg_interfaces:
285             remaining_packet = self.get_next_packet_info_for_interface2(
286                 i, dst_sw_if_index, last_info[i.sw_if_index])
287             self.assertIsNone(
288                 remaining_packet,
289                 "Port %u: Packet expected from source %u didn't arrive" %
290                 (dst_sw_if_index, i.sw_if_index))
291
292     def verify_vrf(self, vrf_id):
293         """
294         Check if the FIB table / VRF ID is configured.
295
296         :param int vrf_id: The FIB table / VRF ID to be verified.
297         :return: 1 if the FIB table / VRF ID is configured, otherwise return 0.
298         """
299         ip_fib_dump = self.vapi.ip_fib_dump()
300         vrf_count = 0
301         for ip_fib_details in ip_fib_dump:
302             if ip_fib_details[2] == vrf_id:
303                 vrf_count += 1
304         if vrf_count == 0:
305             self.logger.info("IPv4 VRF ID %d is not configured" % vrf_id)
306             return 0
307         else:
308             self.logger.info("IPv4 VRF ID %d is configured" % vrf_id)
309             return 1
310
311     def run_verify_test(self):
312         """
313         Create packet streams for all configured l2-pg interfaces, send all \
314         prepared packet streams and verify that:
315             - all packets received correctly on all pg-l2 interfaces assigned
316               to bridge domains
317             - no packet received on all pg-l2 interfaces not assigned to bridge
318               domains
319
320         :raise RuntimeError: If no packet captured on l2-pg interface assigned
321             to the bridge domain or if any packet is captured on l2-pg
322             interface not assigned to the bridge domain.
323         """
324         # Test
325         # Create incoming packet streams for packet-generator interfaces
326         for pg_if in self.pg_interfaces:
327             pkts = self.create_stream(pg_if, self.pg_if_packet_sizes)
328             pg_if.add_stream(pkts)
329
330         # Enable packet capture and start packet sending
331         self.pg_enable_capture(self.pg_interfaces)
332         self.pg_start()
333
334         # Verify
335         # Verify outgoing packet streams per packet-generator interface
336         for pg_if in self.pg_interfaces:
337             if pg_if in self.pg_in_vrf:
338                 capture = pg_if.get_capture(remark="interface is in VRF")
339                 self.verify_capture(pg_if, capture)
340             elif pg_if in self.pg_not_in_vrf:
341                 pg_if.assert_nothing_captured(remark="interface is not in VRF",
342                                               filter_out_fn=is_ipv4_misc)
343                 self.logger.debug("No capture for interface %s" % pg_if.name)
344             else:
345                 raise Exception("Unknown interface: %s" % pg_if.name)
346
347     def test_ip4_vrf_01(self):
348         """ IP4 VRF  Multi-instance test 1 - create 5 BDs
349         """
350         # Config 1
351         # Create 4 VRFs
352         self.create_vrf_and_assign_interfaces(4)
353
354         # Verify 1
355         for vrf_id in self.vrf_list:
356             self.assertEqual(self.verify_vrf(vrf_id), 1)
357
358         # Test 1
359         self.run_verify_test()
360
361     def test_ip4_vrf_02(self):
362         """ IP4 VRF  Multi-instance test 2 - delete 2 VRFs
363         """
364         # Config 2
365         # Delete 2 VRFs
366         self.delete_vrf(1)
367         self.delete_vrf(2)
368
369         # Verify 2
370         # for vrf_id in self.vrf_deleted_list:
371         #     self.assertEqual(self.verify_vrf(vrf_id), 0)
372         for vrf_id in self.vrf_list:
373             self.assertEqual(self.verify_vrf(vrf_id), 1)
374
375         # Test 2
376         self.run_verify_test()
377
378     def test_ip4_vrf_03(self):
379         """ IP4 VRF  Multi-instance 3 - add 2 VRFs
380         """
381         # Config 3
382         # Add 1 of deleted VRFs and 1 new VRF
383         self.create_vrf_and_assign_interfaces(1)
384         self.create_vrf_and_assign_interfaces(1, start=5)
385
386         # Verify 3
387         # for vrf_id in self.vrf_deleted_list:
388         #     self.assertEqual(self.verify_vrf(vrf_id), 0)
389         for vrf_id in self.vrf_list:
390             self.assertEqual(self.verify_vrf(vrf_id), 1)
391
392         # Test 3
393         self.run_verify_test()
394
395     def test_ip4_vrf_04(self):
396         """ IP4 VRF  Multi-instance test 4 - delete 4 VRFs
397         """
398         # Config 4
399         # Delete all VRFs (i.e. no VRF except VRF=0 created)
400         for i in range(len(self.vrf_list)):
401             self.delete_vrf(self.vrf_list[0])
402
403         # Verify 4
404         # for vrf_id in self.vrf_deleted_list:
405         #     self.assertEqual(self.verify_vrf(vrf_id), 0)
406         for vrf_id in self.vrf_list:
407             self.assertEqual(self.verify_vrf(vrf_id), 1)
408
409         # Test 4
410         self.run_verify_test()
411
412
413 if __name__ == '__main__':
414     unittest.main(testRunner=VppTestRunner)