Tests Cleanup: Fix missing calls to setUpClass/tearDownClass.
[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
10 **config 1**
11     - add 15 pg-ip4 interfaces
12     - configure 5 hosts per pg-ip4 interface
13     - configure 4 VRFs
14     - add 3 pg-ip4 interfaces per VRF
15
16 **test 1**
17     - send IP4 packets between all pg-ip4 interfaces in all VRF groups
18
19 **verify 1**
20     - check VRF data by parsing output of ip_fib_dump API command
21     - all packets received correctly in case of pg-ip4 interfaces in the same
22     VRF
23     - no packet received in case of pg-ip4 interfaces not in VRF
24     - no packet received in case of pg-ip4 interfaces in different VRFs
25
26 **config 2**
27     - reset 2 VRFs
28
29 **test 2**
30     - send IP4 packets between all pg-ip4 interfaces in all VRF groups
31
32 **verify 2**
33     - all packets received correctly in case of pg-ip4 interfaces in the same
34     VRF
35     - no packet received in case of pg-ip4 interfaces not in VRF
36     - no packet received in case of pg-ip4 interfaces in different VRFs
37
38 **config 3**
39     - add 1 of reset 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 the same
47     VRF
48     - no packet received in case of pg-ip4 interfaces not in VRF
49     - no packet received in case of pg-ip4 interfaces in different VRFs
50
51 **config 4**
52     - reset all created VRFs
53
54 **test 4**
55     - send IP4 packets between all pg-ip4 interfaces in all VRF groups
56
57 **verify 4**
58     - check VRF data by parsing output of ip_fib_dump API command
59     - all packets received correctly in case of pg-ip4 interfaces in the same
60     VRF
61     - no packet received in case of pg-ip4 interfaces not in VRF
62     - no packet received in case of pg-ip4 interfaces in different VRFs
63 """
64
65 import unittest
66 import random
67 import socket
68
69 import scapy.compat
70 from scapy.packet import Raw
71 from scapy.layers.l2 import Ether
72 from scapy.layers.inet import IP, UDP, ARP
73
74 from framework import VppTestCase, VppTestRunner
75 from util import ppp
76 from vrf import VRFState
77
78
79 def is_ipv4_misc(p):
80     """ Is packet one of uninteresting IPv4 broadcasts? """
81     if p.haslayer(ARP):
82         return True
83     return False
84
85
86 class TestIp4VrfMultiInst(VppTestCase):
87     """ IP4 VRF  Multi-instance Test Case """
88
89     @classmethod
90     def setUpClass(cls):
91         """
92         Perform standard class setup (defined by class method setUpClass in
93         class VppTestCase) before running the test case, set test case related
94         variables and configure VPP.
95         """
96         super(TestIp4VrfMultiInst, cls).setUpClass()
97
98         # Test variables
99         cls.hosts_per_pg = 5
100         cls.nr_of_vrfs = 5
101         cls.pg_ifs_per_vrf = 3
102
103         try:
104             # Create pg interfaces
105             cls.create_pg_interfaces(
106                 range(cls.nr_of_vrfs * cls.pg_ifs_per_vrf))
107
108             # Packet flows mapping pg0 -> pg1, pg2 etc.
109             cls.flows = dict()
110             for i in range(len(cls.pg_interfaces)):
111                 multiplicand = i / cls.pg_ifs_per_vrf
112                 pg_list = [
113                     cls.pg_interfaces[multiplicand * cls.pg_ifs_per_vrf + j]
114                     for j in range(cls.pg_ifs_per_vrf)
115                     if (multiplicand * cls.pg_ifs_per_vrf + j) != i]
116                 cls.flows[cls.pg_interfaces[i]] = pg_list
117
118             # Packet sizes - jumbo packet (9018 bytes) skipped
119             cls.pg_if_packet_sizes = [64, 512, 1518]
120
121             # Set up all interfaces
122             for pg_if in cls.pg_interfaces:
123                 pg_if.admin_up()
124                 pg_if.generate_remote_hosts(cls.hosts_per_pg)
125
126             # Create list of VRFs
127             cls.vrf_list = list()
128
129             # Create list of reset VRFs
130             cls.vrf_reset_list = list()
131
132             # Create list of pg_interfaces in VRFs
133             cls.pg_in_vrf = list()
134
135             # Create list of pg_interfaces not in VRFs
136             cls.pg_not_in_vrf = [pg_if for pg_if in cls.pg_interfaces]
137
138             # Create mapping of pg_interfaces to VRF IDs
139             cls.pg_if_by_vrf_id = dict()
140             for i in range(cls.nr_of_vrfs):
141                 vrf_id = i + 1
142                 pg_list = [
143                     cls.pg_interfaces[i * cls.pg_ifs_per_vrf + j]
144                     for j in range(cls.pg_ifs_per_vrf)]
145                 cls.pg_if_by_vrf_id[vrf_id] = pg_list
146
147         except Exception:
148             super(TestIp4VrfMultiInst, cls).tearDownClass()
149             raise
150
151     @classmethod
152     def tearDownClass(cls):
153         super(TestIp4VrfMultiInst, cls).tearDownClass()
154
155     def setUp(self):
156         """
157         Clear trace and packet infos before running each test.
158         """
159         super(TestIp4VrfMultiInst, self).setUp()
160         self.reset_packet_infos()
161
162     def tearDown(self):
163         """
164         Show various debug prints after each test.
165         """
166         super(TestIp4VrfMultiInst, self).tearDown()
167         if not self.vpp_dead:
168             self.logger.info(self.vapi.ppcli("show ip fib"))
169             self.logger.info(self.vapi.ppcli("show ip arp"))
170
171     def create_vrf_and_assign_interfaces(self, count, start=1):
172         """
173         Create required number of FIB tables / VRFs, put 3 pg-ip4 interfaces
174         to every FIB table / VRF.
175
176         :param int count: Number of FIB tables / VRFs to be created.
177         :param int start: Starting number of the FIB table / VRF ID. \
178         (Default value = 1)
179         """
180
181         for i in range(count):
182             vrf_id = i + start
183             pg_if = self.pg_if_by_vrf_id[vrf_id][0]
184             dest_addr = pg_if.local_ip4n
185             dest_addr_len = 24
186             self.vapi.ip_table_add_del(is_add=1, table_id=vrf_id)
187             self.logger.info("IPv4 VRF ID %d created" % vrf_id)
188             if vrf_id not in self.vrf_list:
189                 self.vrf_list.append(vrf_id)
190             if vrf_id in self.vrf_reset_list:
191                 self.vrf_reset_list.remove(vrf_id)
192             for j in range(self.pg_ifs_per_vrf):
193                 pg_if = self.pg_if_by_vrf_id[vrf_id][j]
194                 pg_if.set_table_ip4(vrf_id)
195                 self.logger.info("pg-interface %s added to IPv4 VRF ID %d"
196                                  % (pg_if.name, vrf_id))
197                 if pg_if not in self.pg_in_vrf:
198                     self.pg_in_vrf.append(pg_if)
199                 if pg_if in self.pg_not_in_vrf:
200                     self.pg_not_in_vrf.remove(pg_if)
201                 pg_if.config_ip4()
202                 pg_if.configure_ipv4_neighbors()
203         self.logger.debug(self.vapi.ppcli("show ip fib"))
204         self.logger.debug(self.vapi.ppcli("show ip arp"))
205
206     def reset_vrf_and_remove_from_vrf_list(self, vrf_id):
207         """
208         Reset required FIB table / VRF and remove it from VRF list.
209
210         :param int vrf_id: The FIB table / VRF ID to be reset.
211         """
212         # self.vapi.reset_vrf(vrf_id, is_ipv6=0)
213         self.vapi.reset_fib(vrf_id, is_ipv6=0)
214         if vrf_id in self.vrf_list:
215             self.vrf_list.remove(vrf_id)
216         if vrf_id not in self.vrf_reset_list:
217             self.vrf_reset_list.append(vrf_id)
218         for j in range(self.pg_ifs_per_vrf):
219             pg_if = self.pg_if_by_vrf_id[vrf_id][j]
220             pg_if.unconfig_ip4()
221             if pg_if in self.pg_in_vrf:
222                 self.pg_in_vrf.remove(pg_if)
223             if pg_if not in self.pg_not_in_vrf:
224                 self.pg_not_in_vrf.append(pg_if)
225         self.logger.info("IPv4 VRF ID %d reset finished" % vrf_id)
226         self.logger.debug(self.vapi.ppcli("show ip fib"))
227         self.logger.debug(self.vapi.ppcli("show ip arp"))
228         self.vapi.ip_table_add_del(is_add=0, table_id=vrf_id)
229
230     def create_stream(self, src_if, packet_sizes):
231         """
232         Create input packet stream for defined interface using hosts list.
233
234         :param object src_if: Interface to create packet stream for.
235         :param list packet_sizes: List of required packet sizes.
236         :return: Stream of packets.
237         """
238         pkts = []
239         src_hosts = src_if.remote_hosts
240         for dst_if in self.flows[src_if]:
241             for dst_host in dst_if.remote_hosts:
242                 src_host = random.choice(src_hosts)
243                 pkt_info = self.create_packet_info(src_if, dst_if)
244                 payload = self.info_to_payload(pkt_info)
245                 p = (Ether(dst=src_if.local_mac, src=src_host.mac) /
246                      IP(src=src_host.ip4, dst=dst_host.ip4) /
247                      UDP(sport=1234, dport=1234) /
248                      Raw(payload))
249                 pkt_info.data = p.copy()
250                 size = random.choice(packet_sizes)
251                 self.extend_packet(p, size)
252                 pkts.append(p)
253         self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
254                           % (src_if.name, len(pkts)))
255         return pkts
256
257     def create_stream_crosswise_vrf(self, src_if, vrf_id, packet_sizes):
258         """
259         Create input packet stream for negative test for leaking across
260         different VRFs for defined interface using hosts list.
261
262         :param object src_if: Interface to create packet stream for.
263         :param int vrf_id: The FIB table / VRF ID where src_if is assigned.
264         :param list packet_sizes: List of required packet sizes.
265         :return: Stream of packets.
266         """
267         pkts = []
268         src_hosts = src_if.remote_hosts
269         vrf_lst = list(self.vrf_list)
270         vrf_lst.remove(vrf_id)
271         for vrf in vrf_lst:
272             for dst_if in self.pg_if_by_vrf_id[vrf]:
273                 for dst_host in dst_if.remote_hosts:
274                     src_host = random.choice(src_hosts)
275                     pkt_info = self.create_packet_info(src_if, dst_if)
276                     payload = self.info_to_payload(pkt_info)
277                     p = (Ether(dst=src_if.local_mac, src=src_host.mac) /
278                          IP(src=src_host.ip4, dst=dst_host.ip4) /
279                          UDP(sport=1234, dport=1234) /
280                          Raw(payload))
281                     pkt_info.data = p.copy()
282                     size = random.choice(packet_sizes)
283                     self.extend_packet(p, size)
284                     pkts.append(p)
285         self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
286                           % (src_if.name, len(pkts)))
287         return pkts
288
289     def verify_capture(self, pg_if, capture):
290         """
291         Verify captured input packet stream for defined interface.
292
293         :param object pg_if: Interface to verify captured packet stream for.
294         :param list capture: Captured packet stream.
295         """
296         last_info = dict()
297         for i in self.pg_interfaces:
298             last_info[i.sw_if_index] = None
299         dst_sw_if_index = pg_if.sw_if_index
300         for packet in capture:
301             try:
302                 ip = packet[IP]
303                 udp = packet[UDP]
304                 payload_info = self.payload_to_info(packet[Raw])
305                 packet_index = payload_info.index
306                 self.assertEqual(payload_info.dst, dst_sw_if_index)
307                 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
308                                   (pg_if.name, payload_info.src, packet_index))
309                 next_info = self.get_next_packet_info_for_interface2(
310                     payload_info.src, dst_sw_if_index,
311                     last_info[payload_info.src])
312                 last_info[payload_info.src] = next_info
313                 self.assertIsNotNone(next_info)
314                 self.assertEqual(packet_index, next_info.index)
315                 saved_packet = next_info.data
316                 # Check standard fields
317                 self.assertEqual(ip.src, saved_packet[IP].src)
318                 self.assertEqual(ip.dst, saved_packet[IP].dst)
319                 self.assertEqual(udp.sport, saved_packet[UDP].sport)
320                 self.assertEqual(udp.dport, saved_packet[UDP].dport)
321             except:
322                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
323                 raise
324         for i in self.pg_interfaces:
325             remaining_packet = self.get_next_packet_info_for_interface2(
326                 i, dst_sw_if_index, last_info[i.sw_if_index])
327             self.assertIsNone(
328                 remaining_packet,
329                 "Port %u: Packet expected from source %u didn't arrive" %
330                 (dst_sw_if_index, i.sw_if_index))
331
332     def verify_vrf(self, vrf_id):
333         """
334         Check if the FIB table / VRF ID is configured.
335
336         :param int vrf_id: The FIB table / VRF ID to be verified.
337         :return: 1 if the FIB table / VRF ID is configured, otherwise return 0.
338         """
339         ip_fib_dump = self.vapi.ip_fib_dump()
340         vrf_exist = False
341         vrf_count = 0
342         for ip_fib_details in ip_fib_dump:
343             if ip_fib_details.table_id == vrf_id:
344                 if not vrf_exist:
345                     vrf_exist = True
346                 addr = socket.inet_ntoa(ip_fib_details.address)
347                 found = False
348                 for pg_if in self.pg_if_by_vrf_id[vrf_id]:
349                     if found:
350                         break
351                     for host in pg_if.remote_hosts:
352                         if scapy.compat.raw(addr) == \
353                                 scapy.compat.raw(host.ip4):
354                             vrf_count += 1
355                             found = True
356                             break
357         if not vrf_exist and vrf_count == 0:
358             self.logger.info("IPv4 VRF ID %d is not configured" % vrf_id)
359             return VRFState.not_configured
360         elif vrf_exist and vrf_count == 0:
361             self.logger.info("IPv4 VRF ID %d has been reset" % vrf_id)
362             return VRFState.reset
363         else:
364             self.logger.info("IPv4 VRF ID %d is configured" % vrf_id)
365             return VRFState.configured
366
367     def run_verify_test(self):
368         """
369         Create packet streams for all configured pg interfaces, send all \
370         prepared packet streams and verify that:
371             - all packets received correctly on all pg-ip4 interfaces assigned
372               to VRFs
373             - no packet received on all pg-ip4 interfaces not assigned to VRFs
374
375         :raise RuntimeError: If no packet captured on pg-ip4 interface assigned
376             to VRF or if any packet is captured on pg-ip4 interface not
377             assigned to VRF.
378         """
379         # Test
380         # Create incoming packet streams for packet-generator interfaces
381         for pg_if in self.pg_interfaces:
382             pkts = self.create_stream(pg_if, self.pg_if_packet_sizes)
383             pg_if.add_stream(pkts)
384
385         # Enable packet capture and start packet sending
386         self.pg_enable_capture(self.pg_interfaces)
387         self.pg_start()
388
389         # Verify
390         # Verify outgoing packet streams per packet-generator interface
391         for pg_if in self.pg_interfaces:
392             if pg_if in self.pg_in_vrf:
393                 capture = pg_if.get_capture(remark="interface is in VRF")
394                 self.verify_capture(pg_if, capture)
395             elif pg_if in self.pg_not_in_vrf:
396                 pg_if.assert_nothing_captured(remark="interface is not in VRF",
397                                               filter_out_fn=is_ipv4_misc)
398                 self.logger.debug("No capture for interface %s" % pg_if.name)
399             else:
400                 raise Exception("Unknown interface: %s" % pg_if.name)
401
402     def run_crosswise_vrf_test(self):
403         """
404         Create packet streams for every pg-ip4 interface in VRF towards all
405         pg-ip4 interfaces in other VRFs, send all prepared packet streams and \
406         verify that:
407              - no packet received on all configured pg-ip4 interfaces
408
409         :raise RuntimeError: If any packet is captured on any pg-ip4 interface.
410         """
411         # Test
412         # Create incoming packet streams for packet-generator interfaces
413         for vrf_id in self.vrf_list:
414             for pg_if in self.pg_if_by_vrf_id[vrf_id]:
415                 pkts = self.create_stream_crosswise_vrf(
416                     pg_if, vrf_id, self.pg_if_packet_sizes)
417                 pg_if.add_stream(pkts)
418
419         # Enable packet capture and start packet sending
420         self.pg_enable_capture(self.pg_interfaces)
421         self.pg_start()
422
423         # Verify
424         # Verify outgoing packet streams per packet-generator interface
425         for pg_if in self.pg_interfaces:
426             pg_if.assert_nothing_captured(remark="interface is in other VRF",
427                                           filter_out_fn=is_ipv4_misc)
428             self.logger.debug("No capture for interface %s" % pg_if.name)
429
430     def test_ip4_vrf_01(self):
431         """ IP4 VRF  Multi-instance test 1 - create 4 VRFs
432         """
433         # Config 1
434         # Create 4 VRFs
435         self.create_vrf_and_assign_interfaces(4)
436
437         # Verify 1
438         for vrf_id in self.vrf_list:
439             self.assert_equal(self.verify_vrf(vrf_id),
440                               VRFState.configured, VRFState)
441
442         # Test 1
443         self.run_verify_test()
444         self.run_crosswise_vrf_test()
445
446     def test_ip4_vrf_02(self):
447         """ IP4 VRF  Multi-instance test 2 - reset 2 VRFs
448         """
449         # Config 2
450         # Reset 2 VRFs
451         self.reset_vrf_and_remove_from_vrf_list(1)
452         self.reset_vrf_and_remove_from_vrf_list(2)
453
454         # Verify 2
455         for vrf_id in self.vrf_reset_list:
456             self.assert_equal(self.verify_vrf(vrf_id),
457                               VRFState.reset, VRFState)
458         for vrf_id in self.vrf_list:
459             self.assert_equal(self.verify_vrf(vrf_id),
460                               VRFState.configured, VRFState)
461
462         # Test 2
463         self.run_verify_test()
464         self.run_crosswise_vrf_test()
465
466     def test_ip4_vrf_03(self):
467         """ IP4 VRF  Multi-instance 3 - add 2 VRFs
468         """
469         # Config 3
470         # Add 1 of reset VRFs and 1 new VRF
471         self.create_vrf_and_assign_interfaces(1)
472         self.create_vrf_and_assign_interfaces(1, start=5)
473
474         # Verify 3
475         for vrf_id in self.vrf_reset_list:
476             self.assert_equal(self.verify_vrf(vrf_id),
477                               VRFState.reset, VRFState)
478         for vrf_id in self.vrf_list:
479             self.assert_equal(self.verify_vrf(vrf_id),
480                               VRFState.configured, VRFState)
481
482         # Test 3
483         self.run_verify_test()
484         self.run_crosswise_vrf_test()
485
486     def test_ip4_vrf_04(self):
487         """ IP4 VRF  Multi-instance test 4 - reset 4 VRFs
488         """
489         # Config 4
490         # Reset all VRFs (i.e. no VRF except VRF=0 configured)
491         for i in range(len(self.vrf_list)):
492             self.reset_vrf_and_remove_from_vrf_list(self.vrf_list[0])
493
494         # Verify 4
495         for vrf_id in self.vrf_reset_list:
496             self.assert_equal(self.verify_vrf(vrf_id),
497                               VRFState.reset, VRFState)
498         vrf_list_length = len(self.vrf_list)
499         self.assertEqual(
500             vrf_list_length, 0,
501             "List of configured VRFs is not empty: %s != 0" % vrf_list_length)
502
503         # Test 4
504         self.run_verify_test()
505         self.run_crosswise_vrf_test()
506
507
508 if __name__ == '__main__':
509     unittest.main(testRunner=VppTestRunner)