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