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