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