build: remove unnecessary executable bits
[vpp.git] / test / test_ip4_vrf_multi_instance.py
1 #!/usr/bin/env python3
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
22       same 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
34       same 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
47       same 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
60       same 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, ARP
72 from scapy.layers.inet import IP, UDP
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_sets = dict()
140             for i in range(cls.nr_of_vrfs):
141                 set_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_sets[set_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
168     def show_commands_at_teardown(self):
169         self.logger.info(self.vapi.ppcli("show ip fib"))
170         self.logger.info(self.vapi.ppcli("show ip4 neighbors"))
171
172     def _assign_interfaces(self, vrf_id, if_set_id):
173         for i in range(self.pg_ifs_per_vrf):
174             pg_if = self.pg_if_sets[if_set_id][i]
175             pg_if.set_table_ip4(vrf_id)
176             self.logger.info("pg-interface %s added to IPv4 VRF ID %d"
177                              % (pg_if.name, vrf_id))
178             if pg_if not in self.pg_in_vrf:
179                 self.pg_in_vrf.append(pg_if)
180             if pg_if in self.pg_not_in_vrf:
181                 self.pg_not_in_vrf.remove(pg_if)
182             pg_if.config_ip4()
183             pg_if.configure_ipv4_neighbors()
184
185     def create_vrf_and_assign_interfaces(self, count, start=1):
186         """
187         Create required number of FIB tables / VRFs, put 3 pg-ip4 interfaces
188         to every FIB table / VRF.
189
190         :param int count: Number of FIB tables / VRFs to be created.
191         :param int start: Starting number of the FIB table / VRF ID. \
192         (Default value = 1)
193         """
194
195         for i in range(count):
196             vrf_id = i + start
197             self.vapi.ip_table_add_del(is_add=1, table={'table_id': vrf_id})
198             self.logger.info("IPv4 VRF ID %d created" % vrf_id)
199             if vrf_id not in self.vrf_list:
200                 self.vrf_list.append(vrf_id)
201             if vrf_id in self.vrf_reset_list:
202                 self.vrf_reset_list.remove(vrf_id)
203             self._assign_interfaces(vrf_id, vrf_id)
204         self.logger.debug(self.vapi.ppcli("show ip fib"))
205         self.logger.debug(self.vapi.ppcli("show ip4 neighbors"))
206
207     def create_vrf_by_id_and_assign_interfaces(self, set_id,
208                                                vrf_id=0xffffffff):
209         """
210         Create a FIB table / VRF by vrf_id, put 3 pg-ip4 interfaces
211         to FIB table / VRF.
212
213         :param int vrf_id: Required table ID / VRF ID. \
214         (Default value = 0xffffffff, ID will be selected automatically)
215         """
216         ret = self.vapi.ip_table_allocate(table={'table_id': vrf_id})
217         vrf_id = ret.table.table_id
218         self.logger.info("IPv4 VRF ID %d created" % vrf_id)
219         if vrf_id not in self.vrf_list:
220             self.vrf_list.append(vrf_id)
221         if vrf_id in self.vrf_reset_list:
222             self.vrf_reset_list.remove(vrf_id)
223         self._assign_interfaces(vrf_id, set_id)
224         self.logger.debug(self.vapi.ppcli("show ip fib"))
225         self.logger.debug(self.vapi.ppcli("show ip4 neighbors"))
226
227         return vrf_id
228
229     def reset_vrf_and_remove_from_vrf_list(self, vrf_id, if_set_id=None):
230         """
231         Reset required FIB table / VRF and remove it from VRF list.
232
233         :param int vrf_id: The FIB table / VRF ID to be reset.
234         """
235         if if_set_id is None:
236             if_set_id = vrf_id
237         self.vapi.ip_table_flush(table={'table_id': vrf_id})
238         if vrf_id in self.vrf_list:
239             self.vrf_list.remove(vrf_id)
240         if vrf_id not in self.vrf_reset_list:
241             self.vrf_reset_list.append(vrf_id)
242         for j in range(self.pg_ifs_per_vrf):
243             pg_if = self.pg_if_sets[if_set_id][j]
244             pg_if.unconfig_ip4()
245             if pg_if in self.pg_in_vrf:
246                 self.pg_in_vrf.remove(pg_if)
247             if pg_if not in self.pg_not_in_vrf:
248                 self.pg_not_in_vrf.append(pg_if)
249         self.logger.info("IPv4 VRF ID %d reset finished" % vrf_id)
250         self.logger.debug(self.vapi.ppcli("show ip fib"))
251         self.logger.debug(self.vapi.ppcli("show ip neighbors"))
252         self.vapi.ip_table_add_del(is_add=0, table={'table_id': vrf_id})
253
254     def create_stream(self, src_if, packet_sizes):
255         """
256         Create input packet stream for defined interface using hosts list.
257
258         :param object src_if: Interface to create packet stream for.
259         :param list packet_sizes: List of required packet sizes.
260         :return: Stream of packets.
261         """
262         pkts = []
263         src_hosts = src_if.remote_hosts
264         for dst_if in self.flows[src_if]:
265             for dst_host in dst_if.remote_hosts:
266                 src_host = random.choice(src_hosts)
267                 pkt_info = self.create_packet_info(src_if, dst_if)
268                 payload = self.info_to_payload(pkt_info)
269                 p = (Ether(dst=src_if.local_mac, src=src_host.mac) /
270                      IP(src=src_host.ip4, dst=dst_host.ip4) /
271                      UDP(sport=1234, dport=1234) /
272                      Raw(payload))
273                 pkt_info.data = p.copy()
274                 size = random.choice(packet_sizes)
275                 self.extend_packet(p, size)
276                 pkts.append(p)
277         self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
278                           % (src_if.name, len(pkts)))
279         return pkts
280
281     def create_stream_crosswise_vrf(self, src_if, vrf_id, packet_sizes):
282         """
283         Create input packet stream for negative test for leaking across
284         different VRFs for defined interface using hosts list.
285
286         :param object src_if: Interface to create packet stream for.
287         :param int vrf_id: The FIB table / VRF ID where src_if is assigned.
288         :param list packet_sizes: List of required packet sizes.
289         :return: Stream of packets.
290         """
291         pkts = []
292         src_hosts = src_if.remote_hosts
293         vrf_lst = list(self.vrf_list)
294         vrf_lst.remove(vrf_id)
295         for vrf in vrf_lst:
296             for dst_if in self.pg_if_sets[vrf]:
297                 for dst_host in dst_if.remote_hosts:
298                     src_host = random.choice(src_hosts)
299                     pkt_info = self.create_packet_info(src_if, dst_if)
300                     payload = self.info_to_payload(pkt_info)
301                     p = (Ether(dst=src_if.local_mac, src=src_host.mac) /
302                          IP(src=src_host.ip4, dst=dst_host.ip4) /
303                          UDP(sport=1234, dport=1234) /
304                          Raw(payload))
305                     pkt_info.data = p.copy()
306                     size = random.choice(packet_sizes)
307                     self.extend_packet(p, size)
308                     pkts.append(p)
309         self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
310                           % (src_if.name, len(pkts)))
311         return pkts
312
313     def verify_capture(self, pg_if, capture):
314         """
315         Verify captured input packet stream for defined interface.
316
317         :param object pg_if: Interface to verify captured packet stream for.
318         :param list capture: Captured packet stream.
319         """
320         last_info = dict()
321         for i in self.pg_interfaces:
322             last_info[i.sw_if_index] = None
323         dst_sw_if_index = pg_if.sw_if_index
324         for packet in capture:
325             try:
326                 ip = packet[IP]
327                 udp = packet[UDP]
328                 payload_info = self.payload_to_info(packet[Raw])
329                 packet_index = payload_info.index
330                 self.assertEqual(payload_info.dst, dst_sw_if_index)
331                 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
332                                   (pg_if.name, payload_info.src, packet_index))
333                 next_info = self.get_next_packet_info_for_interface2(
334                     payload_info.src, dst_sw_if_index,
335                     last_info[payload_info.src])
336                 last_info[payload_info.src] = next_info
337                 self.assertIsNotNone(next_info)
338                 self.assertEqual(packet_index, next_info.index)
339                 saved_packet = next_info.data
340                 # Check standard fields
341                 self.assertEqual(ip.src, saved_packet[IP].src)
342                 self.assertEqual(ip.dst, saved_packet[IP].dst)
343                 self.assertEqual(udp.sport, saved_packet[UDP].sport)
344                 self.assertEqual(udp.dport, saved_packet[UDP].dport)
345             except:
346                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
347                 raise
348         for i in self.pg_interfaces:
349             remaining_packet = self.get_next_packet_info_for_interface2(
350                 i, dst_sw_if_index, last_info[i.sw_if_index])
351             self.assertIsNone(
352                 remaining_packet,
353                 "Port %u: Packet expected from source %u didn't arrive" %
354                 (dst_sw_if_index, i.sw_if_index))
355
356     def verify_vrf(self, vrf_id, if_set_id=None):
357         """
358         Check if the FIB table / VRF ID is configured.
359
360         :param int vrf_id: The FIB table / VRF ID to be verified.
361         :return: 1 if the FIB table / VRF ID is configured, otherwise return 0.
362         """
363         if if_set_id is None:
364             if_set_id = vrf_id
365         ip_fib_dump = self.vapi.ip_route_dump(vrf_id)
366         vrf_exist = len(ip_fib_dump)
367         vrf_count = 0
368         for ip_fib_details in ip_fib_dump:
369             addr = ip_fib_details.route.prefix.network_address
370             found = False
371             for pg_if in self.pg_if_sets[if_set_id]:
372                 if found:
373                     break
374                 for host in pg_if.remote_hosts:
375                     if str(addr) == host.ip4:
376                         vrf_count += 1
377                         found = True
378                         break
379         if not vrf_exist and vrf_count == 0:
380             self.logger.info("IPv4 VRF ID %d is not configured" % vrf_id)
381             return VRFState.not_configured
382         elif vrf_exist and vrf_count == 0:
383             self.logger.info("IPv4 VRF ID %d has been reset" % vrf_id)
384             return VRFState.reset
385         else:
386             self.logger.info("IPv4 VRF ID %d is configured" % vrf_id)
387             return VRFState.configured
388
389     def run_verify_test(self):
390         """
391         Create packet streams for all configured pg interfaces, send all \
392         prepared packet streams and verify that:
393             - all packets received correctly on all pg-ip4 interfaces assigned
394               to VRFs
395             - no packet received on all pg-ip4 interfaces not assigned to VRFs
396
397         :raise RuntimeError: If no packet captured on pg-ip4 interface assigned
398             to VRF or if any packet is captured on pg-ip4 interface not
399             assigned to VRF.
400         """
401         # Test
402         # Create incoming packet streams for packet-generator interfaces
403         for pg_if in self.pg_interfaces:
404             pkts = self.create_stream(pg_if, self.pg_if_packet_sizes)
405             pg_if.add_stream(pkts)
406
407         # Enable packet capture and start packet sending
408         self.pg_enable_capture(self.pg_interfaces)
409         self.pg_start()
410
411         # Verify
412         # Verify outgoing packet streams per packet-generator interface
413         for pg_if in self.pg_interfaces:
414             if pg_if in self.pg_in_vrf:
415                 capture = pg_if.get_capture(remark="interface is in VRF")
416                 self.verify_capture(pg_if, capture)
417             elif pg_if in self.pg_not_in_vrf:
418                 pg_if.assert_nothing_captured(remark="interface is not in VRF",
419                                               filter_out_fn=is_ipv4_misc)
420                 self.logger.debug("No capture for interface %s" % pg_if.name)
421             else:
422                 raise Exception("Unknown interface: %s" % pg_if.name)
423
424     def run_crosswise_vrf_test(self):
425         """
426         Create packet streams for every pg-ip4 interface in VRF towards all
427         pg-ip4 interfaces in other VRFs, send all prepared packet streams and
428         verify that:
429
430         - no packet received on all configured pg-ip4 interfaces
431
432         :raise RuntimeError: If any packet is captured on any pg-ip4 interface.
433         """
434         # Test
435         # Create incoming packet streams for packet-generator interfaces
436         for vrf_id in self.vrf_list:
437             for pg_if in self.pg_if_sets[vrf_id]:
438                 pkts = self.create_stream_crosswise_vrf(
439                     pg_if, vrf_id, self.pg_if_packet_sizes)
440                 pg_if.add_stream(pkts)
441
442         # Enable packet capture and start packet sending
443         self.pg_enable_capture(self.pg_interfaces)
444         self.pg_start()
445
446         # Verify
447         # Verify outgoing packet streams per packet-generator interface
448         for pg_if in self.pg_interfaces:
449             pg_if.assert_nothing_captured(remark="interface is in other VRF",
450                                           filter_out_fn=is_ipv4_misc)
451             self.logger.debug("No capture for interface %s" % pg_if.name)
452
453     def test_ip4_vrf_01(self):
454         """ IP4 VRF  Multi-instance test 1 - create 4 VRFs
455         """
456         # Config 1
457         # Create 4 VRFs
458         self.create_vrf_and_assign_interfaces(4)
459
460         # Verify 1
461         for vrf_id in self.vrf_list:
462             self.assert_equal(self.verify_vrf(vrf_id),
463                               VRFState.configured, VRFState)
464
465         # Test 1
466         self.run_verify_test()
467         self.run_crosswise_vrf_test()
468
469     def test_ip4_vrf_02(self):
470         """ IP4 VRF  Multi-instance test 2 - reset 2 VRFs
471         """
472         # Config 2
473         # Reset 2 VRFs
474         self.reset_vrf_and_remove_from_vrf_list(1)
475         self.reset_vrf_and_remove_from_vrf_list(2)
476
477         # Verify 2
478         for vrf_id in self.vrf_reset_list:
479             self.assert_equal(self.verify_vrf(vrf_id),
480                               VRFState.reset, VRFState)
481         for vrf_id in self.vrf_list:
482             self.assert_equal(self.verify_vrf(vrf_id),
483                               VRFState.configured, VRFState)
484
485         # Test 2
486         self.run_verify_test()
487         self.run_crosswise_vrf_test()
488
489     def test_ip4_vrf_03(self):
490         """ IP4 VRF  Multi-instance 3 - add 2 VRFs
491         """
492         # Config 3
493         # Add 1 of reset VRFs and 1 new VRF
494         self.create_vrf_and_assign_interfaces(1)
495         self.create_vrf_and_assign_interfaces(1, start=5)
496
497         # Verify 3
498         for vrf_id in self.vrf_reset_list:
499             self.assert_equal(self.verify_vrf(vrf_id),
500                               VRFState.reset, VRFState)
501         for vrf_id in self.vrf_list:
502             self.assert_equal(self.verify_vrf(vrf_id),
503                               VRFState.configured, VRFState)
504
505         # Test 3
506         self.run_verify_test()
507         self.run_crosswise_vrf_test()
508
509     def test_ip4_vrf_04(self):
510         """ IP4 VRF  Multi-instance test 4 - reset 4 VRFs
511         """
512         # Config 4
513         # Reset all VRFs (i.e. no VRF except VRF=0 configured)
514         for i in range(len(self.vrf_list)):
515             self.reset_vrf_and_remove_from_vrf_list(self.vrf_list[0])
516
517         # Verify 4
518         for vrf_id in self.vrf_reset_list:
519             self.assert_equal(self.verify_vrf(vrf_id),
520                               VRFState.reset, VRFState)
521         vrf_list_length = len(self.vrf_list)
522         self.assertEqual(
523             vrf_list_length, 0,
524             "List of configured VRFs is not empty: %s != 0" % vrf_list_length)
525
526         # Test 4
527         self.run_verify_test()
528         self.run_crosswise_vrf_test()
529
530     def test_ip4_vrf_05(self):
531         """ IP4 VRF  Multi-instance test 5 - id allocation
532         """
533         # Config 5
534         # Create several VRFs
535         # Set vrf_id manually first
536         self.create_vrf_by_id_and_assign_interfaces(1, 1)
537         # Set vrf_id automatically a few times
538         auto_vrf_id = [
539             self.create_vrf_by_id_and_assign_interfaces(i) for i in range(2, 5)
540         ]
541
542         # Verify 5
543         self.assert_equal(self.verify_vrf(1, 1), VRFState.configured, VRFState)
544         for i, vrf in enumerate(auto_vrf_id):
545             self.assert_equal(self.verify_vrf(vrf, i+2),
546                               VRFState.configured, VRFState)
547
548         # Test 5
549         self.run_verify_test()
550
551         # Config 5.1
552         # Reset VRFs
553         self.reset_vrf_and_remove_from_vrf_list(1)
554         for i, vrf in enumerate(auto_vrf_id):
555             self.reset_vrf_and_remove_from_vrf_list(vrf, i+2)
556
557         # Verify 5.1
558         self.assert_equal(self.verify_vrf(1, 1), VRFState.reset, VRFState)
559         for i, vrf in enumerate(auto_vrf_id):
560             self.assert_equal(self.verify_vrf(vrf, i+2),
561                               VRFState.reset, VRFState)
562
563         vrf_list_length = len(self.vrf_list)
564         self.assertEqual(
565             vrf_list_length, 0,
566             "List of configured VRFs is not empty: %s != 0" % vrf_list_length)
567
568 if __name__ == '__main__':
569     unittest.main(testRunner=VppTestRunner)