blob: e2273877dd0af8d0ee5c4f1f53a01e305bd3c550 [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
Jan Gelety057bb8c2016-12-20 17:32:45 +010068
69from framework import VppTestCase, VppTestRunner
70from util import ppp
Jan Gelety95c87b52017-02-27 10:46:14 +010071from vrf import VRFState
Jan Geletyb5b2ef52017-02-23 15:01:29 +010072
73
74def 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
Jan Gelety057bb8c2016-12-20 17:32:45 +010089
90class 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
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100133 # Create list of reset VRFs
134 cls.vrf_reset_list = list()
Jan Gelety057bb8c2016-12-20 17:32:45 +0100135
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]
Jan Gelety95c87b52017-02-27 10:46:14 +0100183 dest_addr = pg_if.local_ip6n
Jan Gelety057bb8c2016-12-20 17:32:45 +0100184 dest_addr_len = 64
Neale Ranns15002542017-09-10 04:39:11 -0700185 self.vapi.ip_table_add_del(vrf_id, is_add=1, is_ipv6=1)
Jan Gelety057bb8c2016-12-20 17:32:45 +0100186 self.vapi.ip_add_del_route(
187 dest_addr, dest_addr_len, pg_if.local_ip6n, is_ipv6=1,
Neale Ranns15002542017-09-10 04:39:11 -0700188 table_id=vrf_id, is_multipath=1)
Jan Gelety057bb8c2016-12-20 17:32:45 +0100189 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)
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100192 if vrf_id in self.vrf_reset_list:
193 self.vrf_reset_list.remove(vrf_id)
Jan Gelety057bb8c2016-12-20 17:32:45 +0100194 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()
Neale Rannsbaf2e902017-02-25 04:20:00 -0800205 pg_if.configure_ipv6_neighbors()
Jan Gelety057bb8c2016-12-20 17:32:45 +0100206 self.logger.debug(self.vapi.ppcli("show ip6 fib"))
207 self.logger.debug(self.vapi.ppcli("show ip6 neighbors"))
208
Jan Gelety95c87b52017-02-27 10:46:14 +0100209 def reset_vrf_and_remove_from_vrf_list(self, vrf_id):
Jan Gelety057bb8c2016-12-20 17:32:45 +0100210 """
Jan Gelety95c87b52017-02-27 10:46:14 +0100211 Reset required FIB table / VRF and remove it from VRF list.
Jan Gelety057bb8c2016-12-20 17:32:45 +0100212
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100213 :param int vrf_id: The FIB table / VRF ID to be reset.
Jan Gelety057bb8c2016-12-20 17:32:45 +0100214 """
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)
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100219 if vrf_id not in self.vrf_reset_list:
220 self.vrf_reset_list.append(vrf_id)
Jan Gelety057bb8c2016-12-20 17:32:45 +0100221 for j in range(self.pg_ifs_per_vrf):
222 pg_if = self.pg_if_by_vrf_id[vrf_id][j]
Neale Ranns4008ac92017-02-13 23:20:04 -0800223 pg_if.unconfig_ip6()
Jan Gelety057bb8c2016-12-20 17:32:45 +0100224 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)
Jan Gelety95c87b52017-02-27 10:46:14 +0100228 self.logger.info("IPv6 VRF ID %d reset finished" % vrf_id)
Jan Gelety057bb8c2016-12-20 17:32:45 +0100229 self.logger.debug(self.vapi.ppcli("show ip6 fib"))
230 self.logger.debug(self.vapi.ppcli("show ip6 neighbors"))
Neale Ranns15002542017-09-10 04:39:11 -0700231 self.vapi.ip_table_add_del(vrf_id, is_add=0, is_ipv6=1)
Jan Gelety057bb8c2016-12-20 17:32:45 +0100232
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()
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100311 vrf_exist = False
Jan Gelety057bb8c2016-12-20 17:32:45 +0100312 vrf_count = 0
313 for ip6_fib_details in ip6_fib_dump:
Neale Ranns2297af02017-09-12 09:45:04 -0700314 if ip6_fib_details.table_id == vrf_id:
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100315 if not vrf_exist:
316 vrf_exist = True
Neale Ranns2297af02017-09-12 09:45:04 -0700317 addr = inet_ntop(socket.AF_INET6, ip6_fib_details.address)
Jan Gelety95c87b52017-02-27 10:46:14 +0100318 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
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100327 if not vrf_exist and vrf_count == 0:
Jan Gelety057bb8c2016-12-20 17:32:45 +0100328 self.logger.info("IPv6 VRF ID %d is not configured" % vrf_id)
Jan Gelety95c87b52017-02-27 10:46:14 +0100329 return VRFState.not_configured
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100330 elif vrf_exist and vrf_count == 0:
331 self.logger.info("IPv6 VRF ID %d has been reset" % vrf_id)
Jan Gelety95c87b52017-02-27 10:46:14 +0100332 return VRFState.reset
Jan Gelety057bb8c2016-12-20 17:32:45 +0100333 else:
334 self.logger.info("IPv6 VRF ID %d is configured" % vrf_id)
Jan Gelety95c87b52017-02-27 10:46:14 +0100335 return VRFState.configured
Jan Gelety057bb8c2016-12-20 17:32:45 +0100336
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:
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100367 pg_if.assert_nothing_captured(remark="interface is not in VRF",
368 filter_out_fn=is_ipv6_misc_ext)
Jan Gelety057bb8c2016-12-20 17:32:45 +0100369 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:
Jan Gelety95c87b52017-02-27 10:46:14 +0100382 self.assert_equal(self.verify_vrf(vrf_id),
383 VRFState.configured, VRFState)
Jan Gelety057bb8c2016-12-20 17:32:45 +0100384
385 # Test 1
386 self.run_verify_test()
387
Jan Gelety057bb8c2016-12-20 17:32:45 +0100388 def test_ip6_vrf_02(self):
389 """ IP6 VRF Multi-instance test 2 - reset 2 VRFs
390 """
391 # Config 2
392 # Delete 2 VRFs
Jan Gelety95c87b52017-02-27 10:46:14 +0100393 self.reset_vrf_and_remove_from_vrf_list(1)
394 self.reset_vrf_and_remove_from_vrf_list(2)
Jan Gelety057bb8c2016-12-20 17:32:45 +0100395
396 # Verify 2
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100397 for vrf_id in self.vrf_reset_list:
Jan Gelety95c87b52017-02-27 10:46:14 +0100398 self.assert_equal(self.verify_vrf(vrf_id),
399 VRFState.reset, VRFState)
Jan Gelety057bb8c2016-12-20 17:32:45 +0100400 for vrf_id in self.vrf_list:
Jan Gelety95c87b52017-02-27 10:46:14 +0100401 self.assert_equal(self.verify_vrf(vrf_id),
402 VRFState.configured, VRFState)
Jan Gelety057bb8c2016-12-20 17:32:45 +0100403
404 # Test 2
405 self.run_verify_test()
406
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100407 # Reset routes learned from ICMPv6 Neighbor Discovery
408 for vrf_id in self.vrf_reset_list:
Jan Gelety95c87b52017-02-27 10:46:14 +0100409 self.reset_vrf_and_remove_from_vrf_list(vrf_id)
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100410
Jan Gelety057bb8c2016-12-20 17:32:45 +0100411 def test_ip6_vrf_03(self):
412 """ IP6 VRF Multi-instance 3 - add 2 VRFs
413 """
414 # Config 3
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100415 # Add 1 of reset VRFs and 1 new VRF
416 self.create_vrf_and_assign_interfaces(1)
Jan Gelety057bb8c2016-12-20 17:32:45 +0100417 self.create_vrf_and_assign_interfaces(1, start=5)
418
419 # Verify 3
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100420 for vrf_id in self.vrf_reset_list:
Jan Gelety95c87b52017-02-27 10:46:14 +0100421 self.assert_equal(self.verify_vrf(vrf_id),
422 VRFState.reset, VRFState)
Jan Gelety057bb8c2016-12-20 17:32:45 +0100423 for vrf_id in self.vrf_list:
Jan Gelety95c87b52017-02-27 10:46:14 +0100424 self.assert_equal(self.verify_vrf(vrf_id),
425 VRFState.configured, VRFState)
Jan Gelety057bb8c2016-12-20 17:32:45 +0100426
427 # Test 3
428 self.run_verify_test()
429
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100430 # Reset routes learned from ICMPv6 Neighbor Discovery
431 for vrf_id in self.vrf_reset_list:
Jan Gelety95c87b52017-02-27 10:46:14 +0100432 self.reset_vrf_and_remove_from_vrf_list(vrf_id)
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100433
Jan Gelety057bb8c2016-12-20 17:32:45 +0100434 def test_ip6_vrf_04(self):
435 """ IP6 VRF Multi-instance test 4 - reset 4 VRFs
436 """
437 # Config 4
Jan Gelety95c87b52017-02-27 10:46:14 +0100438 # Reset all VRFs (i.e. no VRF except VRF=0 configured)
Jan Gelety057bb8c2016-12-20 17:32:45 +0100439 for i in range(len(self.vrf_list)):
Jan Gelety95c87b52017-02-27 10:46:14 +0100440 self.reset_vrf_and_remove_from_vrf_list(self.vrf_list[0])
Jan Gelety057bb8c2016-12-20 17:32:45 +0100441
442 # Verify 4
Jan Geletyb5b2ef52017-02-23 15:01:29 +0100443 for vrf_id in self.vrf_reset_list:
Jan Gelety95c87b52017-02-27 10:46:14 +0100444 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)
Jan Gelety057bb8c2016-12-20 17:32:45 +0100450
451 # Test 4
452 self.run_verify_test()
453
454
455if __name__ == '__main__':
456 unittest.main(testRunner=VppTestRunner)