virtio: Add RX queue full statisitics
[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
68 from scapy.packet import Raw
69 from scapy.layers.l2 import Ether, ARP
70 from scapy.layers.inet import IP, UDP
71
72 from framework import VppTestCase
73 from asfframework import VppTestRunner
74 from util import ppp
75 from vrf import VRFState
76
77
78 def is_ipv4_misc(p):
79     """Is packet one of uninteresting IPv4 broadcasts?"""
80     if p.haslayer(ARP):
81         return True
82     return False
83
84
85 class TestIp4VrfMultiInst(VppTestCase):
86     """IP4 VRF  Multi-instance Test Case"""
87
88     @classmethod
89     def setUpClass(cls):
90         """
91         Perform standard class setup (defined by class method setUpClass in
92         class VppTestCase) before running the test case, set test case related
93         variables and configure VPP.
94         """
95         super(TestIp4VrfMultiInst, cls).setUpClass()
96
97         # Test variables
98         cls.hosts_per_pg = 5
99         cls.nr_of_vrfs = 5
100         cls.pg_ifs_per_vrf = 3
101
102         try:
103             # Create pg interfaces
104             cls.create_pg_interfaces(range(cls.nr_of_vrfs * cls.pg_ifs_per_vrf))
105
106             # Packet flows mapping pg0 -> pg1, pg2 etc.
107             cls.flows = dict()
108             for i in range(len(cls.pg_interfaces)):
109                 multiplicand = i // cls.pg_ifs_per_vrf
110                 pg_list = [
111                     cls.pg_interfaces[multiplicand * cls.pg_ifs_per_vrf + j]
112                     for j in range(cls.pg_ifs_per_vrf)
113                     if (multiplicand * cls.pg_ifs_per_vrf + j) != i
114                 ]
115                 cls.flows[cls.pg_interfaces[i]] = pg_list
116
117             # Packet sizes - jumbo packet (9018 bytes) skipped
118             cls.pg_if_packet_sizes = [64, 512, 1518]
119
120             # Set up all interfaces
121             for pg_if in cls.pg_interfaces:
122                 pg_if.admin_up()
123                 pg_if.generate_remote_hosts(cls.hosts_per_pg)
124
125             # Create list of VRFs
126             cls.vrf_list = list()
127
128             # Create list of reset VRFs
129             cls.vrf_reset_list = list()
130
131             # Create list of pg_interfaces in VRFs
132             cls.pg_in_vrf = list()
133
134             # Create list of pg_interfaces not in VRFs
135             cls.pg_not_in_vrf = [pg_if for pg_if in cls.pg_interfaces]
136
137             # Create mapping of pg_interfaces to VRF IDs
138             cls.pg_if_sets = dict()
139             for i in range(cls.nr_of_vrfs):
140                 set_id = i + 1
141                 pg_list = [
142                     cls.pg_interfaces[i * cls.pg_ifs_per_vrf + j]
143                     for j in range(cls.pg_ifs_per_vrf)
144                 ]
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(
177                 "pg-interface %s added to IPv4 VRF ID %d" % (pg_if.name, vrf_id)
178             )
179             if pg_if not in self.pg_in_vrf:
180                 self.pg_in_vrf.append(pg_if)
181             if pg_if in self.pg_not_in_vrf:
182                 self.pg_not_in_vrf.remove(pg_if)
183             pg_if.config_ip4()
184             pg_if.configure_ipv4_neighbors()
185
186     def create_vrf_and_assign_interfaces(self, count, start=1):
187         """
188         Create required number of FIB tables / VRFs, put 3 pg-ip4 interfaces
189         to every FIB table / VRF.
190
191         :param int count: Number of FIB tables / VRFs to be created.
192         :param int start: Starting number of the FIB table / VRF ID. \
193         (Default value = 1)
194         """
195
196         for i in range(count):
197             vrf_id = i + start
198             self.vapi.ip_table_add_del(is_add=1, table={"table_id": vrf_id})
199             self.logger.info("IPv4 VRF ID %d created" % vrf_id)
200             if vrf_id not in self.vrf_list:
201                 self.vrf_list.append(vrf_id)
202             if vrf_id in self.vrf_reset_list:
203                 self.vrf_reset_list.remove(vrf_id)
204             self._assign_interfaces(vrf_id, vrf_id)
205         self.logger.debug(self.vapi.ppcli("show ip fib"))
206         self.logger.debug(self.vapi.ppcli("show ip4 neighbors"))
207
208     def create_vrf_by_id_and_assign_interfaces(self, set_id, 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 = (
270                     Ether(dst=src_if.local_mac, src=src_host.mac)
271                     / IP(src=src_host.ip4, dst=dst_host.ip4)
272                     / UDP(sport=1234, dport=1234)
273                     / Raw(payload)
274                 )
275                 pkt_info.data = p.copy()
276                 size = random.choice(packet_sizes)
277                 self.extend_packet(p, size)
278                 pkts.append(p)
279         self.logger.debug(
280             "Input stream created for port %s. Length: %u pkt(s)"
281             % (src_if.name, len(pkts))
282         )
283         return pkts
284
285     def create_stream_crosswise_vrf(self, src_if, vrf_id, packet_sizes):
286         """
287         Create input packet stream for negative test for leaking across
288         different VRFs for defined interface using hosts list.
289
290         :param object src_if: Interface to create packet stream for.
291         :param int vrf_id: The FIB table / VRF ID where src_if is assigned.
292         :param list packet_sizes: List of required packet sizes.
293         :return: Stream of packets.
294         """
295         pkts = []
296         src_hosts = src_if.remote_hosts
297         vrf_lst = list(self.vrf_list)
298         vrf_lst.remove(vrf_id)
299         for vrf in vrf_lst:
300             for dst_if in self.pg_if_sets[vrf]:
301                 for dst_host in dst_if.remote_hosts:
302                     src_host = random.choice(src_hosts)
303                     pkt_info = self.create_packet_info(src_if, dst_if)
304                     payload = self.info_to_payload(pkt_info)
305                     p = (
306                         Ether(dst=src_if.local_mac, src=src_host.mac)
307                         / IP(src=src_host.ip4, dst=dst_host.ip4)
308                         / UDP(sport=1234, dport=1234)
309                         / Raw(payload)
310                     )
311                     pkt_info.data = p.copy()
312                     size = random.choice(packet_sizes)
313                     self.extend_packet(p, size)
314                     pkts.append(p)
315         self.logger.debug(
316             "Input stream created for port %s. Length: %u pkt(s)"
317             % (src_if.name, len(pkts))
318         )
319         return pkts
320
321     def verify_capture(self, pg_if, capture):
322         """
323         Verify captured input packet stream for defined interface.
324
325         :param object pg_if: Interface to verify captured packet stream for.
326         :param list capture: Captured packet stream.
327         """
328         last_info = dict()
329         for i in self.pg_interfaces:
330             last_info[i.sw_if_index] = None
331         dst_sw_if_index = pg_if.sw_if_index
332         for packet in capture:
333             try:
334                 ip = packet[IP]
335                 udp = packet[UDP]
336                 payload_info = self.payload_to_info(packet[Raw])
337                 packet_index = payload_info.index
338                 self.assertEqual(payload_info.dst, dst_sw_if_index)
339                 self.logger.debug(
340                     "Got packet on port %s: src=%u (id=%u)"
341                     % (pg_if.name, payload_info.src, packet_index)
342                 )
343                 next_info = self.get_next_packet_info_for_interface2(
344                     payload_info.src, dst_sw_if_index, last_info[payload_info.src]
345                 )
346                 last_info[payload_info.src] = next_info
347                 self.assertIsNotNone(next_info)
348                 self.assertEqual(packet_index, next_info.index)
349                 saved_packet = next_info.data
350                 # Check standard fields
351                 self.assertEqual(ip.src, saved_packet[IP].src)
352                 self.assertEqual(ip.dst, saved_packet[IP].dst)
353                 self.assertEqual(udp.sport, saved_packet[UDP].sport)
354                 self.assertEqual(udp.dport, saved_packet[UDP].dport)
355             except:
356                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
357                 raise
358         for i in self.pg_interfaces:
359             remaining_packet = self.get_next_packet_info_for_interface2(
360                 i, dst_sw_if_index, last_info[i.sw_if_index]
361             )
362             self.assertIsNone(
363                 remaining_packet,
364                 "Port %u: Packet expected from source %u didn't arrive"
365                 % (dst_sw_if_index, i.sw_if_index),
366             )
367
368     def verify_vrf(self, vrf_id, if_set_id=None):
369         """
370         Check if the FIB table / VRF ID is configured.
371
372         :param int vrf_id: The FIB table / VRF ID to be verified.
373         :return: 1 if the FIB table / VRF ID is configured, otherwise return 0.
374         """
375         if if_set_id is None:
376             if_set_id = vrf_id
377         ip_fib_dump = self.vapi.ip_route_dump(vrf_id)
378         vrf_exist = len(ip_fib_dump)
379         vrf_count = 0
380         for ip_fib_details in ip_fib_dump:
381             addr = ip_fib_details.route.prefix.network_address
382             found = False
383             for pg_if in self.pg_if_sets[if_set_id]:
384                 if found:
385                     break
386                 for host in pg_if.remote_hosts:
387                     if str(addr) == host.ip4:
388                         vrf_count += 1
389                         found = True
390                         break
391         if not vrf_exist and vrf_count == 0:
392             self.logger.info("IPv4 VRF ID %d is not configured" % vrf_id)
393             return VRFState.not_configured
394         elif vrf_exist and vrf_count == 0:
395             self.logger.info("IPv4 VRF ID %d has been reset" % vrf_id)
396             return VRFState.reset
397         else:
398             self.logger.info("IPv4 VRF ID %d is configured" % vrf_id)
399             return VRFState.configured
400
401     def run_verify_test(self):
402         """
403         Create packet streams for all configured pg interfaces, send all \
404         prepared packet streams and verify that:
405             - all packets received correctly on all pg-ip4 interfaces assigned
406               to VRFs
407             - no packet received on all pg-ip4 interfaces not assigned to VRFs
408
409         :raise RuntimeError: If no packet captured on pg-ip4 interface assigned
410             to VRF or if any packet is captured on pg-ip4 interface not
411             assigned to VRF.
412         """
413         # Test
414         # Create incoming packet streams for packet-generator interfaces
415         for pg_if in self.pg_interfaces:
416             pkts = self.create_stream(pg_if, 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             if pg_if in self.pg_in_vrf:
427                 capture = pg_if.get_capture(remark="interface is in VRF")
428                 self.verify_capture(pg_if, capture)
429             elif pg_if in self.pg_not_in_vrf:
430                 pg_if.assert_nothing_captured(
431                     remark="interface is not in VRF", filter_out_fn=is_ipv4_misc
432                 )
433                 self.logger.debug("No capture for interface %s" % pg_if.name)
434             else:
435                 raise Exception("Unknown interface: %s" % pg_if.name)
436
437     def run_crosswise_vrf_test(self):
438         """
439         Create packet streams for every pg-ip4 interface in VRF towards all
440         pg-ip4 interfaces in other VRFs, send all prepared packet streams and
441         verify that:
442
443         - no packet received on all configured pg-ip4 interfaces
444
445         :raise RuntimeError: If any packet is captured on any pg-ip4 interface.
446         """
447         # Test
448         # Create incoming packet streams for packet-generator interfaces
449         for vrf_id in self.vrf_list:
450             for pg_if in self.pg_if_sets[vrf_id]:
451                 pkts = self.create_stream_crosswise_vrf(
452                     pg_if, vrf_id, self.pg_if_packet_sizes
453                 )
454                 pg_if.add_stream(pkts)
455
456         # Enable packet capture and start packet sending
457         self.pg_enable_capture(self.pg_interfaces)
458         self.pg_start()
459
460         # Verify
461         # Verify outgoing packet streams per packet-generator interface
462         for pg_if in self.pg_interfaces:
463             pg_if.assert_nothing_captured(
464                 remark="interface is in other VRF", filter_out_fn=is_ipv4_misc
465             )
466             self.logger.debug("No capture for interface %s" % pg_if.name)
467
468     def test_ip4_vrf_01(self):
469         """IP4 VRF  Multi-instance test 1 - create 4 VRFs"""
470         # Config 1
471         # Create 4 VRFs
472         self.create_vrf_and_assign_interfaces(4)
473
474         # Verify 1
475         for vrf_id in self.vrf_list:
476             self.assert_equal(self.verify_vrf(vrf_id), VRFState.configured, VRFState)
477
478         # Test 1
479         self.run_verify_test()
480         self.run_crosswise_vrf_test()
481
482     def test_ip4_vrf_02(self):
483         """IP4 VRF  Multi-instance test 2 - reset 2 VRFs"""
484         # Config 2
485         # Reset 2 VRFs
486         self.reset_vrf_and_remove_from_vrf_list(1)
487         self.reset_vrf_and_remove_from_vrf_list(2)
488
489         # Verify 2
490         for vrf_id in self.vrf_reset_list:
491             self.assert_equal(self.verify_vrf(vrf_id), VRFState.reset, VRFState)
492         for vrf_id in self.vrf_list:
493             self.assert_equal(self.verify_vrf(vrf_id), VRFState.configured, VRFState)
494
495         # Test 2
496         self.run_verify_test()
497         self.run_crosswise_vrf_test()
498
499     def test_ip4_vrf_03(self):
500         """IP4 VRF  Multi-instance 3 - add 2 VRFs"""
501         # Config 3
502         # Add 1 of reset VRFs and 1 new VRF
503         self.create_vrf_and_assign_interfaces(1)
504         self.create_vrf_and_assign_interfaces(1, start=5)
505
506         # Verify 3
507         for vrf_id in self.vrf_reset_list:
508             self.assert_equal(self.verify_vrf(vrf_id), VRFState.reset, VRFState)
509         for vrf_id in self.vrf_list:
510             self.assert_equal(self.verify_vrf(vrf_id), VRFState.configured, VRFState)
511
512         # Test 3
513         self.run_verify_test()
514         self.run_crosswise_vrf_test()
515
516     def test_ip4_vrf_04(self):
517         """IP4 VRF  Multi-instance test 4 - reset 4 VRFs"""
518         # Config 4
519         # Reset all VRFs (i.e. no VRF except VRF=0 configured)
520         for i in range(len(self.vrf_list)):
521             self.reset_vrf_and_remove_from_vrf_list(self.vrf_list[0])
522
523         # Verify 4
524         for vrf_id in self.vrf_reset_list:
525             self.assert_equal(self.verify_vrf(vrf_id), VRFState.reset, VRFState)
526         vrf_list_length = len(self.vrf_list)
527         self.assertEqual(
528             vrf_list_length,
529             0,
530             "List of configured VRFs is not empty: %s != 0" % vrf_list_length,
531         )
532
533         # Test 4
534         self.run_verify_test()
535         self.run_crosswise_vrf_test()
536
537     def test_ip4_vrf_05(self):
538         """IP4 VRF  Multi-instance test 5 - id allocation"""
539         # Config 5
540         # Create several VRFs
541         # Set vrf_id manually first
542         self.create_vrf_by_id_and_assign_interfaces(1, 1)
543         # Set vrf_id automatically a few times
544         auto_vrf_id = [
545             self.create_vrf_by_id_and_assign_interfaces(i) for i in range(2, 5)
546         ]
547
548         # Verify 5
549         self.assert_equal(self.verify_vrf(1, 1), VRFState.configured, VRFState)
550         for i, vrf in enumerate(auto_vrf_id):
551             self.assert_equal(
552                 self.verify_vrf(vrf, i + 2), VRFState.configured, VRFState
553             )
554
555         # Test 5
556         self.run_verify_test()
557
558         # Config 5.1
559         # Reset VRFs
560         self.reset_vrf_and_remove_from_vrf_list(1)
561         for i, vrf in enumerate(auto_vrf_id):
562             self.reset_vrf_and_remove_from_vrf_list(vrf, i + 2)
563
564         # Verify 5.1
565         self.assert_equal(self.verify_vrf(1, 1), VRFState.reset, VRFState)
566         for i, vrf in enumerate(auto_vrf_id):
567             self.assert_equal(self.verify_vrf(vrf, i + 2), VRFState.reset, VRFState)
568
569         vrf_list_length = len(self.vrf_list)
570         self.assertEqual(
571             vrf_list_length,
572             0,
573             "List of configured VRFs is not empty: %s != 0" % vrf_list_length,
574         )
575
576
577 if __name__ == "__main__":
578     unittest.main(testRunner=VppTestRunner)