ethernet: add sanity checks to p2p_ethernet_add/del
[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 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, 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              - 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)