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