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