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