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