API: Remove legacy vlibsocket code.
[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.vapi.ip_add_del_route(
183                 dest_addr, dest_addr_len, pg_if.local_ip4n,
184                 table_id=vrf_id, is_multipath=1)
185             self.logger.info("IPv4 VRF ID %d created" % vrf_id)
186             if vrf_id not in self.vrf_list:
187                 self.vrf_list.append(vrf_id)
188             if vrf_id in self.vrf_reset_list:
189                 self.vrf_reset_list.remove(vrf_id)
190             for j in range(self.pg_ifs_per_vrf):
191                 pg_if = self.pg_if_by_vrf_id[vrf_id][j]
192                 pg_if.set_table_ip4(vrf_id)
193                 self.logger.info("pg-interface %s added to IPv4 VRF ID %d"
194                                  % (pg_if.name, vrf_id))
195                 if pg_if not in self.pg_in_vrf:
196                     self.pg_in_vrf.append(pg_if)
197                 if pg_if in self.pg_not_in_vrf:
198                     self.pg_not_in_vrf.remove(pg_if)
199                 pg_if.config_ip4()
200                 pg_if.configure_ipv4_neighbors()
201         self.logger.debug(self.vapi.ppcli("show ip fib"))
202         self.logger.debug(self.vapi.ppcli("show ip arp"))
203
204     def reset_vrf_and_remove_from_vrf_list(self, vrf_id):
205         """
206         Reset required FIB table / VRF and remove it from VRF list.
207
208         :param int vrf_id: The FIB table / VRF ID to be reset.
209         """
210         # self.vapi.reset_vrf(vrf_id, is_ipv6=0)
211         self.vapi.reset_fib(vrf_id, is_ipv6=0)
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 arp"))
226         self.vapi.ip_table_add_del(vrf_id, is_add=0)
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(str(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_fib_dump()
338         vrf_exist = False
339         vrf_count = 0
340         for ip_fib_details in ip_fib_dump:
341             if ip_fib_details.table_id == vrf_id:
342                 if not vrf_exist:
343                     vrf_exist = True
344                 addr = socket.inet_ntoa(ip_fib_details.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) == str(host.ip4):
351                             vrf_count += 1
352                             found = True
353                             break
354         if not vrf_exist and vrf_count == 0:
355             self.logger.info("IPv4 VRF ID %d is not configured" % vrf_id)
356             return VRFState.not_configured
357         elif vrf_exist and vrf_count == 0:
358             self.logger.info("IPv4 VRF ID %d has been reset" % vrf_id)
359             return VRFState.reset
360         else:
361             self.logger.info("IPv4 VRF ID %d is configured" % vrf_id)
362             return VRFState.configured
363
364     def run_verify_test(self):
365         """
366         Create packet streams for all configured pg interfaces, send all \
367         prepared packet streams and verify that:
368             - all packets received correctly on all pg-ip4 interfaces assigned
369               to VRFs
370             - no packet received on all pg-ip4 interfaces not assigned to VRFs
371
372         :raise RuntimeError: If no packet captured on pg-ip4 interface assigned
373             to VRF or if any packet is captured on pg-ip4 interface not
374             assigned to VRF.
375         """
376         # Test
377         # Create incoming packet streams for packet-generator interfaces
378         for pg_if in self.pg_interfaces:
379             pkts = self.create_stream(pg_if, self.pg_if_packet_sizes)
380             pg_if.add_stream(pkts)
381
382         # Enable packet capture and start packet sending
383         self.pg_enable_capture(self.pg_interfaces)
384         self.pg_start()
385
386         # Verify
387         # Verify outgoing packet streams per packet-generator interface
388         for pg_if in self.pg_interfaces:
389             if pg_if in self.pg_in_vrf:
390                 capture = pg_if.get_capture(remark="interface is in VRF")
391                 self.verify_capture(pg_if, capture)
392             elif pg_if in self.pg_not_in_vrf:
393                 pg_if.assert_nothing_captured(remark="interface is not in VRF",
394                                               filter_out_fn=is_ipv4_misc)
395                 self.logger.debug("No capture for interface %s" % pg_if.name)
396             else:
397                 raise Exception("Unknown interface: %s" % pg_if.name)
398
399     def run_crosswise_vrf_test(self):
400         """
401         Create packet streams for every pg-ip4 interface in VRF towards all
402         pg-ip4 interfaces in other VRFs, send all prepared packet streams and \
403         verify that:
404              - no packet received on all configured pg-ip4 interfaces
405
406         :raise RuntimeError: If any packet is captured on any pg-ip4 interface.
407         """
408         # Test
409         # Create incoming packet streams for packet-generator interfaces
410         for vrf_id in self.vrf_list:
411             for pg_if in self.pg_if_by_vrf_id[vrf_id]:
412                 pkts = self.create_stream_crosswise_vrf(
413                     pg_if, vrf_id, self.pg_if_packet_sizes)
414                 pg_if.add_stream(pkts)
415
416         # Enable packet capture and start packet sending
417         self.pg_enable_capture(self.pg_interfaces)
418         self.pg_start()
419
420         # Verify
421         # Verify outgoing packet streams per packet-generator interface
422         for pg_if in self.pg_interfaces:
423             pg_if.assert_nothing_captured(remark="interface is in other VRF",
424                                           filter_out_fn=is_ipv4_misc)
425             self.logger.debug("No capture for interface %s" % pg_if.name)
426
427     def test_ip4_vrf_01(self):
428         """ IP4 VRF  Multi-instance test 1 - create 4 VRFs
429         """
430         # Config 1
431         # Create 4 VRFs
432         self.create_vrf_and_assign_interfaces(4)
433
434         # Verify 1
435         for vrf_id in self.vrf_list:
436             self.assert_equal(self.verify_vrf(vrf_id),
437                               VRFState.configured, VRFState)
438
439         # Test 1
440         self.run_verify_test()
441         self.run_crosswise_vrf_test()
442
443     def test_ip4_vrf_02(self):
444         """ IP4 VRF  Multi-instance test 2 - reset 2 VRFs
445         """
446         # Config 2
447         # Reset 2 VRFs
448         self.reset_vrf_and_remove_from_vrf_list(1)
449         self.reset_vrf_and_remove_from_vrf_list(2)
450
451         # Verify 2
452         for vrf_id in self.vrf_reset_list:
453             self.assert_equal(self.verify_vrf(vrf_id),
454                               VRFState.reset, VRFState)
455         for vrf_id in self.vrf_list:
456             self.assert_equal(self.verify_vrf(vrf_id),
457                               VRFState.configured, VRFState)
458
459         # Test 2
460         self.run_verify_test()
461         self.run_crosswise_vrf_test()
462
463     def test_ip4_vrf_03(self):
464         """ IP4 VRF  Multi-instance 3 - add 2 VRFs
465         """
466         # Config 3
467         # Add 1 of reset VRFs and 1 new VRF
468         self.create_vrf_and_assign_interfaces(1)
469         self.create_vrf_and_assign_interfaces(1, start=5)
470
471         # Verify 3
472         for vrf_id in self.vrf_reset_list:
473             self.assert_equal(self.verify_vrf(vrf_id),
474                               VRFState.reset, VRFState)
475         for vrf_id in self.vrf_list:
476             self.assert_equal(self.verify_vrf(vrf_id),
477                               VRFState.configured, VRFState)
478
479         # Test 3
480         self.run_verify_test()
481         self.run_crosswise_vrf_test()
482
483     def test_ip4_vrf_04(self):
484         """ IP4 VRF  Multi-instance test 4 - reset 4 VRFs
485         """
486         # Config 4
487         # Reset all VRFs (i.e. no VRF except VRF=0 configured)
488         for i in range(len(self.vrf_list)):
489             self.reset_vrf_and_remove_from_vrf_list(self.vrf_list[0])
490
491         # Verify 4
492         for vrf_id in self.vrf_reset_list:
493             self.assert_equal(self.verify_vrf(vrf_id),
494                               VRFState.reset, VRFState)
495         vrf_list_length = len(self.vrf_list)
496         self.assertEqual(
497             vrf_list_length, 0,
498             "List of configured VRFs is not empty: %s != 0" % vrf_list_length)
499
500         # Test 4
501         self.run_verify_test()
502         self.run_crosswise_vrf_test()
503
504
505 if __name__ == '__main__':
506     unittest.main(testRunner=VppTestRunner)