af80b5ba0655e35300c5ce1716abd3a9b11e82e2
[vpp.git] / test / test_ip6_vrf_multi_instance.py
1 #!/usr/bin/env python
2 """IP6 VRF Multi-instance Test Case HLD:
3
4 **NOTES:**
5     - higher number of pg-ip6 interfaces causes problems => only 15 pg-ip6 \
6     interfaces in 5 VRFs are tested
7     - jumbo packets in configuration with 15 pg-ip6 interfaces leads to \
8     problems too
9
10 **config 1**
11     - add 15 pg-ip6 interfaces
12     - configure 5 hosts per pg-ip6 interface
13     - configure 4 VRFs
14     - add 3 pg-ip6 interfaces per VRF
15
16 **test 1**
17     - send IP6 packets between all pg-ip6 interfaces in all VRF groups
18
19 **verify 1**
20     - check VRF data by parsing output of ip6_fib_dump API command
21     - all packets received correctly in case of pg-ip6 interfaces in VRF
22     - no packet received in case of pg-ip6 interfaces not in VRF
23
24 **config 2**
25     - reset 2 VRFs
26
27 **test 2**
28     - send IP6 packets between all pg-ip6 interfaces in all VRF groups
29
30 **verify 2**
31     - check VRF data by parsing output of ip6_fib_dump API command
32     - all packets received correctly in case of pg-ip6 interfaces in VRF
33     - no packet received in case of pg-ip6 interfaces not in VRF
34
35 **config 3**
36     - add 1 of reset VRFs and 1 new VRF
37
38 **test 3**
39     - send IP6 packets between all pg-ip6 interfaces in all VRF groups
40
41 **verify 3**
42     - check VRF data by parsing output of ip6_fib_dump API command
43     - all packets received correctly in case of pg-ip6 interfaces in VRF
44     - no packet received in case of pg-ip6 interfaces not in VRF
45
46 **config 4**
47     - reset all VRFs (i.e. no VRF except VRF=0 created)
48
49 **test 4**
50     - send IP6 packets between all pg-ip6 interfaces in all VRF groups
51
52 **verify 4**
53     - check VRF data by parsing output of ip6_fib_dump API command
54     - all packets received correctly in case of pg-ip6 interfaces in VRF
55     - no packet received in case of pg-ip6 interfaces not in VRF
56 """
57
58 import unittest
59 import random
60 import socket
61
62 from scapy.packet import Raw
63 from scapy.layers.l2 import Ether
64 from scapy.layers.inet6 import UDP, IPv6, ICMPv6ND_NS, ICMPv6ND_RA, \
65     RouterAlert, IPv6ExtHdrHopByHop
66 from scapy.utils6 import in6_ismaddr, in6_isllsnmaddr, in6_getAddrType
67 from scapy.pton_ntop import inet_ntop
68 from scapy.data import IPV6_ADDR_UNICAST
69
70 from framework import VppTestCase, VppTestRunner
71 from util import ppp
72
73 # VRF status constants
74 VRF_NOT_CONFIGURED = 0
75 VRF_CONFIGURED = 1
76 VRF_RESET = 2
77
78
79 def is_ipv6_misc_ext(p):
80     """ Is packet one of uninteresting IPv6 broadcasts (extended to filter out
81     ICMPv6 Neighbor Discovery - Neighbor Advertisement packets too)? """
82     if p.haslayer(ICMPv6ND_RA):
83         if in6_ismaddr(p[IPv6].dst):
84             return True
85     if p.haslayer(ICMPv6ND_NS):
86         if in6_isllsnmaddr(p[IPv6].dst):
87             return True
88     if p.haslayer(IPv6ExtHdrHopByHop):
89         for o in p[IPv6ExtHdrHopByHop].options:
90             if isinstance(o, RouterAlert):
91                 return True
92     return False
93
94
95 class TestIP6VrfMultiInst(VppTestCase):
96     """ IP6 VRF  Multi-instance Test Case """
97
98     @classmethod
99     def setUpClass(cls):
100         """
101         Perform standard class setup (defined by class method setUpClass in
102         class VppTestCase) before running the test case, set test case related
103         variables and configure VPP.
104         """
105         super(TestIP6VrfMultiInst, cls).setUpClass()
106
107         # Test variables
108         cls.hosts_per_pg = 5
109         cls.nr_of_vrfs = 5
110         cls.pg_ifs_per_vrf = 3
111
112         try:
113             # Create pg interfaces
114             cls.create_pg_interfaces(
115                 range(cls.nr_of_vrfs * cls.pg_ifs_per_vrf))
116
117             # Packet flows mapping pg0 -> pg1, pg2 etc.
118             cls.flows = dict()
119             for i in range(len(cls.pg_interfaces)):
120                 multiplicand = i / cls.pg_ifs_per_vrf
121                 pg_list = [
122                     cls.pg_interfaces[multiplicand * cls.pg_ifs_per_vrf + j]
123                     for j in range(cls.pg_ifs_per_vrf)
124                     if (multiplicand * cls.pg_ifs_per_vrf + j) != i]
125                 cls.flows[cls.pg_interfaces[i]] = pg_list
126
127             # Packet sizes - jumbo packet (9018 bytes) skipped
128             cls.pg_if_packet_sizes = [64, 512, 1518]
129
130             # Set up all interfaces
131             for pg_if in cls.pg_interfaces:
132                 pg_if.admin_up()
133                 pg_if.generate_remote_hosts(cls.hosts_per_pg)
134
135             # Create list of VRFs
136             cls.vrf_list = list()
137
138             # Create list of reset VRFs
139             cls.vrf_reset_list = list()
140
141             # Create list of pg_interfaces in VRFs
142             cls.pg_in_vrf = list()
143
144             # Create list of pg_interfaces not in BDs
145             cls.pg_not_in_vrf = [pg_if for pg_if in cls.pg_interfaces]
146
147             # Create mapping of pg_interfaces to VRF IDs
148             cls.pg_if_by_vrf_id = dict()
149             for i in range(cls.nr_of_vrfs):
150                 vrf_id = i + 1
151                 pg_list = [
152                     cls.pg_interfaces[i * cls.pg_ifs_per_vrf + j]
153                     for j in range(cls.pg_ifs_per_vrf)]
154                 cls.pg_if_by_vrf_id[vrf_id] = pg_list
155
156         except Exception:
157             super(TestIP6VrfMultiInst, cls).tearDownClass()
158             raise
159
160     def setUp(self):
161         """
162         Clear trace and packet infos before running each test.
163         """
164         super(TestIP6VrfMultiInst, self).setUp()
165         self.reset_packet_infos()
166
167     def tearDown(self):
168         """
169         Show various debug prints after each test.
170         """
171         super(TestIP6VrfMultiInst, self).tearDown()
172         if not self.vpp_dead:
173             self.logger.info(self.vapi.ppcli("show ip6 fib"))
174             self.logger.info(self.vapi.ppcli("show ip6 neighbors"))
175
176     def create_vrf_and_assign_interfaces(self, count, start=1):
177         """
178         Create required number of FIB tables / VRFs, put 3 l2-pg interfaces
179         to every FIB table / VRF.
180
181         :param int count: Number of FIB tables / VRFs to be created.
182         :param int start: Starting number of the FIB table / VRF ID. \
183         (Default value = 1)
184         """
185         for i in range(count):
186             vrf_id = i + start
187             pg_if = self.pg_if_by_vrf_id[vrf_id][0]
188             dest_addr = pg_if.remote_hosts[0].ip6n
189             dest_addr_len = 64
190             self.vapi.ip_add_del_route(
191                 dest_addr, dest_addr_len, pg_if.local_ip6n, is_ipv6=1,
192                 table_id=vrf_id, create_vrf_if_needed=1, is_multipath=1)
193             self.logger.info("IPv6 VRF ID %d created" % vrf_id)
194             if vrf_id not in self.vrf_list:
195                 self.vrf_list.append(vrf_id)
196             if vrf_id in self.vrf_reset_list:
197                 self.vrf_reset_list.remove(vrf_id)
198             for j in range(self.pg_ifs_per_vrf):
199                 pg_if = self.pg_if_by_vrf_id[vrf_id][j]
200                 pg_if.set_table_ip6(vrf_id)
201                 self.logger.info("pg-interface %s added to IPv6 VRF ID %d"
202                                  % (pg_if.name, vrf_id))
203                 if pg_if not in self.pg_in_vrf:
204                     self.pg_in_vrf.append(pg_if)
205                 if pg_if in self.pg_not_in_vrf:
206                     self.pg_not_in_vrf.remove(pg_if)
207                 pg_if.config_ip6()
208                 pg_if.disable_ipv6_ra()
209                 pg_if.configure_ipv6_neighbors()
210         self.logger.debug(self.vapi.ppcli("show ip6 fib"))
211         self.logger.debug(self.vapi.ppcli("show ip6 neighbors"))
212
213     def reset_vrf(self, vrf_id):
214         """
215         Reset required FIB table / VRF.
216
217         :param int vrf_id: The FIB table / VRF ID to be reset.
218         """
219         # self.vapi.reset_vrf(vrf_id, is_ipv6=1)
220         self.vapi.reset_fib(vrf_id, is_ipv6=1)
221         if vrf_id in self.vrf_list:
222             self.vrf_list.remove(vrf_id)
223         if vrf_id not in self.vrf_reset_list:
224             self.vrf_reset_list.append(vrf_id)
225         for j in range(self.pg_ifs_per_vrf):
226             pg_if = self.pg_if_by_vrf_id[vrf_id][j]
227             pg_if.unconfig_ip6()
228             if pg_if in self.pg_in_vrf:
229                 self.pg_in_vrf.remove(pg_if)
230             if pg_if not in self.pg_not_in_vrf:
231                 self.pg_not_in_vrf.append(pg_if)
232         self.logger.info("IPv6 VRF ID %d reset" % vrf_id)
233         self.logger.debug(self.vapi.ppcli("show ip6 fib"))
234         self.logger.debug(self.vapi.ppcli("show ip6 neighbors"))
235
236     def create_stream(self, src_if, packet_sizes):
237         """
238         Create input packet stream for defined interface using hosts list.
239
240         :param object src_if: Interface to create packet stream for.
241         :param list packet_sizes: List of required packet sizes.
242         :return: Stream of packets.
243         """
244         pkts = []
245         src_hosts = src_if.remote_hosts
246         for dst_if in self.flows[src_if]:
247             for dst_host in dst_if.remote_hosts:
248                 src_host = random.choice(src_hosts)
249                 pkt_info = self.create_packet_info(src_if, dst_if)
250                 payload = self.info_to_payload(pkt_info)
251                 p = (Ether(dst=src_if.local_mac, src=src_host.mac) /
252                      IPv6(src=src_host.ip6, dst=dst_host.ip6) /
253                      UDP(sport=1234, dport=1234) /
254                      Raw(payload))
255                 pkt_info.data = p.copy()
256                 size = random.choice(packet_sizes)
257                 self.extend_packet(p, size)
258                 pkts.append(p)
259         self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
260                           % (src_if.name, len(pkts)))
261         return pkts
262
263     def verify_capture(self, pg_if, capture):
264         """
265         Verify captured input packet stream for defined interface.
266
267         :param object pg_if: Interface to verify captured packet stream for.
268         :param list capture: Captured packet stream.
269         """
270         last_info = dict()
271         for i in self.pg_interfaces:
272             last_info[i.sw_if_index] = None
273         dst_sw_if_index = pg_if.sw_if_index
274         for packet in capture:
275             try:
276                 ip = packet[IPv6]
277                 udp = packet[UDP]
278                 payload_info = self.payload_to_info(str(packet[Raw]))
279                 packet_index = payload_info.index
280                 self.assertEqual(payload_info.dst, dst_sw_if_index)
281                 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
282                                   (pg_if.name, payload_info.src, packet_index))
283                 next_info = self.get_next_packet_info_for_interface2(
284                     payload_info.src, dst_sw_if_index,
285                     last_info[payload_info.src])
286                 last_info[payload_info.src] = next_info
287                 self.assertIsNotNone(next_info)
288                 self.assertEqual(packet_index, next_info.index)
289                 saved_packet = next_info.data
290                 # Check standard fields
291                 self.assertEqual(ip.src, saved_packet[IPv6].src)
292                 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
293                 self.assertEqual(udp.sport, saved_packet[UDP].sport)
294                 self.assertEqual(udp.dport, saved_packet[UDP].dport)
295             except:
296                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
297                 raise
298         for i in self.pg_interfaces:
299             remaining_packet = self.get_next_packet_info_for_interface2(
300                 i, dst_sw_if_index, last_info[i.sw_if_index])
301             self.assertIsNone(
302                 remaining_packet,
303                 "Port %u: Packet expected from source %u didn't arrive" %
304                 (dst_sw_if_index, i.sw_if_index))
305
306     def verify_vrf(self, vrf_id):
307         """
308         Check if the FIB table / VRF ID is configured.
309
310         :param int vrf_id: The FIB table / VRF ID to be verified.
311         :return: 1 if the FIB table / VRF ID is configured, otherwise return 0.
312         """
313         ip6_fib_dump = self.vapi.ip6_fib_dump()
314         vrf_exist = False
315         vrf_count = 0
316         for ip6_fib_details in ip6_fib_dump:
317             if ip6_fib_details[2] == vrf_id:
318                 if not vrf_exist:
319                     vrf_exist = True
320                 addr = inet_ntop(socket.AF_INET6, ip6_fib_details[4])
321                 addrtype = in6_getAddrType(addr)
322                 vrf_count += 1 if addrtype == IPV6_ADDR_UNICAST else 0
323         if not vrf_exist and vrf_count == 0:
324             self.logger.info("IPv6 VRF ID %d is not configured" % vrf_id)
325             return VRF_NOT_CONFIGURED
326         elif vrf_exist and vrf_count == 0:
327             self.logger.info("IPv6 VRF ID %d has been reset" % vrf_id)
328             return VRF_RESET
329         else:
330             self.logger.info("IPv6 VRF ID %d is configured" % vrf_id)
331             return VRF_CONFIGURED
332
333     def run_verify_test(self):
334         """
335         Create packet streams for all configured l2-pg interfaces, send all \
336         prepared packet streams and verify that:
337             - all packets received correctly on all pg-l2 interfaces assigned
338               to bridge domains
339             - no packet received on all pg-l2 interfaces not assigned to bridge
340               domains
341
342         :raise RuntimeError: If no packet captured on l2-pg interface assigned
343             to the bridge domain or if any packet is captured on l2-pg
344             interface not assigned to the bridge domain.
345         """
346         # Test
347         # Create incoming packet streams for packet-generator interfaces
348         for pg_if in self.pg_interfaces:
349             pkts = self.create_stream(pg_if, self.pg_if_packet_sizes)
350             pg_if.add_stream(pkts)
351
352         # Enable packet capture and start packet sending
353         self.pg_enable_capture(self.pg_interfaces)
354         self.pg_start()
355
356         # Verify
357         # Verify outgoing packet streams per packet-generator interface
358         for pg_if in self.pg_interfaces:
359             if pg_if in self.pg_in_vrf:
360                 capture = pg_if.get_capture(remark="interface is in VRF")
361                 self.verify_capture(pg_if, capture)
362             elif pg_if in self.pg_not_in_vrf:
363                 pg_if.assert_nothing_captured(remark="interface is not in VRF",
364                                               filter_out_fn=is_ipv6_misc_ext)
365                 self.logger.debug("No capture for interface %s" % pg_if.name)
366             else:
367                 raise Exception("Unknown interface: %s" % pg_if.name)
368
369     def test_ip6_vrf_01(self):
370         """ IP6 VRF  Multi-instance test 1 - create 4 VRFs
371         """
372         # Config 1
373         # Create 4 VRFs
374         self.create_vrf_and_assign_interfaces(4)
375
376         # Verify 1
377         for vrf_id in self.vrf_list:
378             self.assertEqual(self.verify_vrf(vrf_id), VRF_CONFIGURED)
379
380         # Test 1
381         self.run_verify_test()
382
383     def test_ip6_vrf_02(self):
384         """ IP6 VRF  Multi-instance test 2 - reset 2 VRFs
385         """
386         # Config 2
387         # Delete 2 VRFs
388         self.reset_vrf(1)
389         self.reset_vrf(2)
390
391         # Verify 2
392         for vrf_id in self.vrf_reset_list:
393             self.assertEqual(self.verify_vrf(vrf_id), VRF_RESET)
394         for vrf_id in self.vrf_list:
395             self.assertEqual(self.verify_vrf(vrf_id), VRF_CONFIGURED)
396
397         # Test 2
398         self.run_verify_test()
399
400         # Reset routes learned from ICMPv6 Neighbor Discovery
401         for vrf_id in self.vrf_reset_list:
402             self.reset_vrf(vrf_id)
403
404     def test_ip6_vrf_03(self):
405         """ IP6 VRF  Multi-instance 3 - add 2 VRFs
406         """
407         # Config 3
408         # Add 1 of reset VRFs and 1 new VRF
409         self.create_vrf_and_assign_interfaces(1)
410         self.create_vrf_and_assign_interfaces(1, start=5)
411
412         # Verify 3
413         for vrf_id in self.vrf_reset_list:
414             self.assertEqual(self.verify_vrf(vrf_id), VRF_RESET)
415         for vrf_id in self.vrf_list:
416             self.assertEqual(self.verify_vrf(vrf_id), VRF_CONFIGURED)
417
418         # Test 3
419         self.run_verify_test()
420
421         # Reset routes learned from ICMPv6 Neighbor Discovery
422         for vrf_id in self.vrf_reset_list:
423             self.reset_vrf(vrf_id)
424
425     def test_ip6_vrf_04(self):
426         """ IP6 VRF  Multi-instance test 4 - reset 4 VRFs
427         """
428         # Config 4
429         # Reset all VRFs (i.e. no VRF except VRF=0 created)
430         for i in range(len(self.vrf_list)):
431             self.reset_vrf(self.vrf_list[0])
432
433         # Verify 4
434         for vrf_id in self.vrf_reset_list:
435             self.assertEqual(self.verify_vrf(vrf_id), VRF_RESET)
436         for vrf_id in self.vrf_list:
437             self.assertEqual(self.verify_vrf(vrf_id), VRF_CONFIGURED)
438
439         # Test 4
440         self.run_verify_test()
441
442
443 if __name__ == '__main__':
444     unittest.main(testRunner=VppTestRunner)