blob: af80b5ba0655e35300c5ce1716abd3a9b11e82e2 [file] [log] [blame]
Jan Gelety057bb8c2016-12-20 17:32:45 +01001#!/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
Jan Gelety057bb8c2016-12-20 17:32:45 +01009
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**
Jan Geletyb5b2ef52017-02-23 15:01:29 +010025 - reset 2 VRFs
Jan Gelety057bb8c2016-12-20 17:32:45 +010026
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**
Jan Geletyb5b2ef52017-02-23 15:01:29 +010036 - add 1 of reset VRFs and 1 new VRF
Jan Gelety057bb8c2016-12-20 17:32:45 +010037
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**
Jan Geletyb5b2ef52017-02-23 15:01:29 +010047 - reset all VRFs (i.e. no VRF except VRF=0 created)
Jan Gelety057bb8c2016-12-20 17:32:45 +010048
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
58import unittest
59import random
Jan Geletyb5b2ef52017-02-23 15:01:29 +010060import socket
Jan Gelety057bb8c2016-12-20 17:32:45 +010061
62from scapy.packet import Raw
63from scapy.layers.l2 import Ether
Jan Geletyb5b2ef52017-02-23 15:01:29 +010064from scapy.layers.inet6 import UDP, IPv6, ICMPv6ND_NS, ICMPv6ND_RA, \
65 RouterAlert, IPv6ExtHdrHopByHop
66from scapy.utils6 import in6_ismaddr, in6_isllsnmaddr, in6_getAddrType
67from scapy.pton_ntop import inet_ntop
68from scapy.data import IPV6_ADDR_UNICAST
Jan Gelety057bb8c2016-12-20 17:32:45 +010069
70from framework import VppTestCase, VppTestRunner
71from util import ppp
72
Jan Geletyb5b2ef52017-02-23 15:01:29 +010073# VRF status constants
74VRF_NOT_CONFIGURED = 0
75VRF_CONFIGURED = 1
76VRF_RESET = 2
77
78
79def 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
Jan Gelety057bb8c2016-12-20 17:32:45 +010094
95class 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
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100138 # Create list of reset VRFs
139 cls.vrf_reset_list = list()
Jan Gelety057bb8c2016-12-20 17:32:45 +0100140
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)
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100196 if vrf_id in self.vrf_reset_list:
197 self.vrf_reset_list.remove(vrf_id)
Jan Gelety057bb8c2016-12-20 17:32:45 +0100198 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()
Neale Rannsbaf2e902017-02-25 04:20:00 -0800209 pg_if.configure_ipv6_neighbors()
Jan Gelety057bb8c2016-12-20 17:32:45 +0100210 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 """
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100215 Reset required FIB table / VRF.
Jan Gelety057bb8c2016-12-20 17:32:45 +0100216
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100217 :param int vrf_id: The FIB table / VRF ID to be reset.
Jan Gelety057bb8c2016-12-20 17:32:45 +0100218 """
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)
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100223 if vrf_id not in self.vrf_reset_list:
224 self.vrf_reset_list.append(vrf_id)
Jan Gelety057bb8c2016-12-20 17:32:45 +0100225 for j in range(self.pg_ifs_per_vrf):
226 pg_if = self.pg_if_by_vrf_id[vrf_id][j]
Neale Ranns4008ac92017-02-13 23:20:04 -0800227 pg_if.unconfig_ip6()
Jan Gelety057bb8c2016-12-20 17:32:45 +0100228 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()
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100314 vrf_exist = False
Jan Gelety057bb8c2016-12-20 17:32:45 +0100315 vrf_count = 0
316 for ip6_fib_details in ip6_fib_dump:
317 if ip6_fib_details[2] == vrf_id:
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100318 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:
Jan Gelety057bb8c2016-12-20 17:32:45 +0100324 self.logger.info("IPv6 VRF ID %d is not configured" % vrf_id)
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100325 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
Jan Gelety057bb8c2016-12-20 17:32:45 +0100329 else:
330 self.logger.info("IPv6 VRF ID %d is configured" % vrf_id)
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100331 return VRF_CONFIGURED
Jan Gelety057bb8c2016-12-20 17:32:45 +0100332
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:
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100363 pg_if.assert_nothing_captured(remark="interface is not in VRF",
364 filter_out_fn=is_ipv6_misc_ext)
Jan Gelety057bb8c2016-12-20 17:32:45 +0100365 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:
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100378 self.assertEqual(self.verify_vrf(vrf_id), VRF_CONFIGURED)
Jan Gelety057bb8c2016-12-20 17:32:45 +0100379
380 # Test 1
381 self.run_verify_test()
382
Jan Gelety057bb8c2016-12-20 17:32:45 +0100383 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
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100392 for vrf_id in self.vrf_reset_list:
393 self.assertEqual(self.verify_vrf(vrf_id), VRF_RESET)
Jan Gelety057bb8c2016-12-20 17:32:45 +0100394 for vrf_id in self.vrf_list:
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100395 self.assertEqual(self.verify_vrf(vrf_id), VRF_CONFIGURED)
Jan Gelety057bb8c2016-12-20 17:32:45 +0100396
397 # Test 2
398 self.run_verify_test()
399
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100400 # Reset routes learned from ICMPv6 Neighbor Discovery
401 for vrf_id in self.vrf_reset_list:
402 self.reset_vrf(vrf_id)
403
Jan Gelety057bb8c2016-12-20 17:32:45 +0100404 def test_ip6_vrf_03(self):
405 """ IP6 VRF Multi-instance 3 - add 2 VRFs
406 """
407 # Config 3
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100408 # Add 1 of reset VRFs and 1 new VRF
409 self.create_vrf_and_assign_interfaces(1)
Jan Gelety057bb8c2016-12-20 17:32:45 +0100410 self.create_vrf_and_assign_interfaces(1, start=5)
411
412 # Verify 3
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100413 for vrf_id in self.vrf_reset_list:
414 self.assertEqual(self.verify_vrf(vrf_id), VRF_RESET)
Jan Gelety057bb8c2016-12-20 17:32:45 +0100415 for vrf_id in self.vrf_list:
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100416 self.assertEqual(self.verify_vrf(vrf_id), VRF_CONFIGURED)
Jan Gelety057bb8c2016-12-20 17:32:45 +0100417
418 # Test 3
419 self.run_verify_test()
420
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100421 # Reset routes learned from ICMPv6 Neighbor Discovery
422 for vrf_id in self.vrf_reset_list:
423 self.reset_vrf(vrf_id)
424
Jan Gelety057bb8c2016-12-20 17:32:45 +0100425 def test_ip6_vrf_04(self):
426 """ IP6 VRF Multi-instance test 4 - reset 4 VRFs
427 """
428 # Config 4
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100429 # Reset all VRFs (i.e. no VRF except VRF=0 created)
Jan Gelety057bb8c2016-12-20 17:32:45 +0100430 for i in range(len(self.vrf_list)):
431 self.reset_vrf(self.vrf_list[0])
432
433 # Verify 4
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100434 for vrf_id in self.vrf_reset_list:
435 self.assertEqual(self.verify_vrf(vrf_id), VRF_RESET)
Jan Gelety057bb8c2016-12-20 17:32:45 +0100436 for vrf_id in self.vrf_list:
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100437 self.assertEqual(self.verify_vrf(vrf_id), VRF_CONFIGURED)
Jan Gelety057bb8c2016-12-20 17:32:45 +0100438
439 # Test 4
440 self.run_verify_test()
441
442
443if __name__ == '__main__':
444 unittest.main(testRunner=VppTestRunner)