2 """IP6 VRF Multi-instance Test Case HLD:
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 \
11 - add 15 pg-ip6 interfaces
12 - configure 5 hosts per pg-ip6 interface
14 - add 3 pg-ip6 interfaces per VRF
17 - send IP6 packets between all pg-ip6 interfaces in all VRF groups
20 - check VRF data by parsing output of ip_route_dump API command
21 - all packets received correctly in case of pg-ip6 interfaces in the
23 - no packet received in case of pg-ip6 interfaces not in VRF
24 - no packet received in case of pg-ip6 interfaces in different VRFs
30 - send IP6 packets between all pg-ip6 interfaces in all VRF groups
33 - check VRF data by parsing output of ip_route_dump API command
34 - all packets received correctly in case of pg-ip6 interfaces in the
36 - no packet received in case of pg-ip6 interfaces not in VRF
37 - no packet received in case of pg-ip6 interfaces in different VRFs
40 - add 1 of reset VRFs and 1 new VRF
43 - send IP6 packets between all pg-ip6 interfaces in all VRF groups
46 - check VRF data by parsing output of ip_route_dump API command
47 - all packets received correctly in case of pg-ip6 interfaces in the
49 - no packet received in case of pg-ip6 interfaces not in VRF
50 - no packet received in case of pg-ip6 interfaces in different VRFs
53 - reset all VRFs (i.e. no VRF except VRF=0 created)
56 - send IP6 packets between all pg-ip6 interfaces in all VRF groups
59 - check VRF data by parsing output of ip_route_dump API command
60 - all packets received correctly in case of pg-ip6 interfaces in the
62 - no packet received in case of pg-ip6 interfaces not in VRF
63 - no packet received in case of pg-ip6 interfaces in different VRFs
70 from scapy.packet import Raw
71 from scapy.layers.l2 import Ether
72 from scapy.layers.inet6 import UDP, IPv6, ICMPv6ND_NS, ICMPv6ND_RA, \
73 RouterAlert, IPv6ExtHdrHopByHop
74 from scapy.utils6 import in6_ismaddr, in6_isllsnmaddr, in6_getAddrType
75 from scapy.pton_ntop import inet_ntop
77 from framework import VppTestCase, VppTestRunner
79 from vrf import VRFState
82 def is_ipv6_misc_ext(p):
83 """ Is packet one of uninteresting IPv6 broadcasts (extended to filter out
84 ICMPv6 Neighbor Discovery - Neighbor Advertisement packets too)? """
85 if p.haslayer(ICMPv6ND_RA):
86 if in6_ismaddr(p[IPv6].dst):
88 if p.haslayer(ICMPv6ND_NS):
89 if in6_isllsnmaddr(p[IPv6].dst):
91 if p.haslayer(IPv6ExtHdrHopByHop):
92 for o in p[IPv6ExtHdrHopByHop].options:
93 if isinstance(o, RouterAlert):
98 class TestIP6VrfMultiInst(VppTestCase):
99 """ IP6 VRF Multi-instance Test Case """
104 Perform standard class setup (defined by class method setUpClass in
105 class VppTestCase) before running the test case, set test case related
106 variables and configure VPP.
108 super(TestIP6VrfMultiInst, cls).setUpClass()
113 cls.pg_ifs_per_vrf = 3
116 # Create pg interfaces
117 cls.create_pg_interfaces(
118 range(cls.nr_of_vrfs * cls.pg_ifs_per_vrf))
120 # Packet flows mapping pg0 -> pg1, pg2 etc.
122 for i in range(len(cls.pg_interfaces)):
123 multiplicand = i // cls.pg_ifs_per_vrf
125 cls.pg_interfaces[multiplicand * cls.pg_ifs_per_vrf + j]
126 for j in range(cls.pg_ifs_per_vrf)
127 if (multiplicand * cls.pg_ifs_per_vrf + j) != i]
128 cls.flows[cls.pg_interfaces[i]] = pg_list
130 # Packet sizes - jumbo packet (9018 bytes) skipped
131 cls.pg_if_packet_sizes = [64, 512, 1518]
133 # Set up all interfaces
134 for pg_if in cls.pg_interfaces:
136 pg_if.generate_remote_hosts(cls.hosts_per_pg)
138 # Create list of VRFs
139 cls.vrf_list = list()
141 # Create list of reset VRFs
142 cls.vrf_reset_list = list()
144 # Create list of pg_interfaces in VRFs
145 cls.pg_in_vrf = list()
147 # Create list of pg_interfaces not in VRFs
148 cls.pg_not_in_vrf = [pg_if for pg_if in cls.pg_interfaces]
150 # Create mapping of pg_interfaces to VRF IDs
151 cls.pg_if_sets = dict()
152 for i in range(cls.nr_of_vrfs):
155 cls.pg_interfaces[i * cls.pg_ifs_per_vrf + j]
156 for j in range(cls.pg_ifs_per_vrf)]
157 cls.pg_if_sets[set_id] = pg_list
160 super(TestIP6VrfMultiInst, cls).tearDownClass()
164 def tearDownClass(cls):
165 super(TestIP6VrfMultiInst, cls).tearDownClass()
169 Clear trace and packet infos before running each test.
171 super(TestIP6VrfMultiInst, self).setUp()
172 self.reset_packet_infos()
176 Show various debug prints after each test.
178 super(TestIP6VrfMultiInst, self).tearDown()
180 def show_commands_at_teardown(self):
181 self.logger.info(self.vapi.ppcli("show ip6 fib"))
182 self.logger.info(self.vapi.ppcli("show ip6 neighbors"))
184 def _assign_interfaces(self, vrf_id, if_set_id):
185 for i in range(self.pg_ifs_per_vrf):
186 pg_if = self.pg_if_sets[if_set_id][i]
187 pg_if.set_table_ip6(vrf_id)
188 self.logger.info("pg-interface %s added to IPv6 VRF ID %d"
189 % (pg_if.name, vrf_id))
190 if pg_if not in self.pg_in_vrf:
191 self.pg_in_vrf.append(pg_if)
192 if pg_if in self.pg_not_in_vrf:
193 self.pg_not_in_vrf.remove(pg_if)
195 pg_if.disable_ipv6_ra()
196 pg_if.configure_ipv6_neighbors()
198 def create_vrf_and_assign_interfaces(self, count, start=1):
200 Create required number of FIB tables / VRFs, put 3 pg-ip6 interfaces
201 to every FIB table / VRF.
203 :param int count: Number of FIB tables / VRFs to be created.
204 :param int start: Starting number of the FIB table / VRF ID. \
207 for i in range(count):
209 self.vapi.ip_table_add_del(is_add=1,
210 table={'table_id': vrf_id, 'is_ip6': 1})
211 self.logger.info("IPv6 VRF ID %d created" % vrf_id)
212 if vrf_id not in self.vrf_list:
213 self.vrf_list.append(vrf_id)
214 if vrf_id in self.vrf_reset_list:
215 self.vrf_reset_list.remove(vrf_id)
216 self._assign_interfaces(vrf_id, vrf_id)
217 self.logger.debug(self.vapi.ppcli("show ip6 fib"))
218 self.logger.debug(self.vapi.ppcli("show ip6 neighbors"))
220 def create_vrf_by_id_and_assign_interfaces(self, set_id,
223 Create a FIB table / VRF by vrf_id, put 3 pg-ip6 interfaces
226 :param int vrf_id: Required table ID / VRF ID. \
227 (Default value = 0xffffffff, ID will be selected automatically)
229 ret = self.vapi.ip_table_allocate(table={'table_id': vrf_id,
231 vrf_id = ret.table.table_id
232 self.logger.info("IPv6 VRF ID %d created" % vrf_id)
233 if vrf_id not in self.vrf_list:
234 self.vrf_list.append(vrf_id)
235 if vrf_id in self.vrf_reset_list:
236 self.vrf_reset_list.remove(vrf_id)
237 self._assign_interfaces(vrf_id, set_id)
238 self.logger.debug(self.vapi.ppcli("show ip6 fib"))
239 self.logger.debug(self.vapi.ppcli("show ip6 neighbors"))
243 def reset_vrf_and_remove_from_vrf_list(self, vrf_id, if_set_id=None):
245 Reset required FIB table / VRF and remove it from VRF list.
247 :param int vrf_id: The FIB table / VRF ID to be reset.
249 if if_set_id is None:
251 self.vapi.ip_table_flush(table={'table_id': vrf_id, 'is_ip6': 1})
252 if vrf_id in self.vrf_list:
253 self.vrf_list.remove(vrf_id)
254 if vrf_id not in self.vrf_reset_list:
255 self.vrf_reset_list.append(vrf_id)
256 for j in range(self.pg_ifs_per_vrf):
257 pg_if = self.pg_if_sets[if_set_id][j]
259 pg_if.set_table_ip6(0)
260 if pg_if in self.pg_in_vrf:
261 self.pg_in_vrf.remove(pg_if)
262 if pg_if not in self.pg_not_in_vrf:
263 self.pg_not_in_vrf.append(pg_if)
264 self.logger.info("IPv6 VRF ID %d reset finished" % vrf_id)
265 self.logger.debug(self.vapi.ppcli("show ip6 fib"))
266 self.logger.debug(self.vapi.ppcli("show ip6 neighbors"))
268 def delete_vrf(self, vrf_id):
269 if vrf_id in self.vrf_list:
270 self.vrf_list.remove(vrf_id)
271 if vrf_id in self.vrf_reset_list:
272 self.vrf_reset_list.remove(vrf_id)
273 self.vapi.ip_table_add_del(is_add=0,
274 table={'table_id': vrf_id, 'is_ip6': 1})
276 def create_stream(self, src_if, packet_sizes):
278 Create input packet stream for defined interface using hosts list.
280 :param object src_if: Interface to create packet stream for.
281 :param list packet_sizes: List of required packet sizes.
282 :return: Stream of packets.
285 src_hosts = src_if.remote_hosts
286 for dst_if in self.flows[src_if]:
287 for dst_host in dst_if.remote_hosts:
288 src_host = random.choice(src_hosts)
289 pkt_info = self.create_packet_info(src_if, dst_if)
290 payload = self.info_to_payload(pkt_info)
291 p = (Ether(dst=src_if.local_mac, src=src_host.mac) /
292 IPv6(src=src_host.ip6, dst=dst_host.ip6) /
293 UDP(sport=1234, dport=1234) /
295 pkt_info.data = p.copy()
296 size = random.choice(packet_sizes)
297 self.extend_packet(p, size)
299 self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
300 % (src_if.name, len(pkts)))
303 def create_stream_crosswise_vrf(self, src_if, vrf_id, packet_sizes):
305 Create input packet stream for negative test for leaking across
306 different VRFs for defined interface using hosts list.
308 :param object src_if: Interface to create packet stream for.
309 :param int vrf_id: The FIB table / VRF ID where src_if is assigned.
310 :param list packet_sizes: List of required packet sizes.
311 :return: Stream of packets.
314 src_hosts = src_if.remote_hosts
315 vrf_lst = list(self.vrf_list)
316 vrf_lst.remove(vrf_id)
318 for dst_if in self.pg_if_sets[vrf]:
319 for dst_host in dst_if.remote_hosts:
320 src_host = random.choice(src_hosts)
321 pkt_info = self.create_packet_info(src_if, dst_if)
322 payload = self.info_to_payload(pkt_info)
323 p = (Ether(dst=src_if.local_mac, src=src_host.mac) /
324 IPv6(src=src_host.ip6, dst=dst_host.ip6) /
325 UDP(sport=1234, dport=1234) /
327 pkt_info.data = p.copy()
328 size = random.choice(packet_sizes)
329 self.extend_packet(p, size)
331 self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
332 % (src_if.name, len(pkts)))
335 def verify_capture(self, pg_if, capture):
337 Verify captured input packet stream for defined interface.
339 :param object pg_if: Interface to verify captured packet stream for.
340 :param list capture: Captured packet stream.
343 for i in self.pg_interfaces:
344 last_info[i.sw_if_index] = None
345 dst_sw_if_index = pg_if.sw_if_index
346 for packet in capture:
350 payload_info = self.payload_to_info(packet[Raw])
351 packet_index = payload_info.index
352 self.assertEqual(payload_info.dst, dst_sw_if_index)
353 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
354 (pg_if.name, payload_info.src, packet_index))
355 next_info = self.get_next_packet_info_for_interface2(
356 payload_info.src, dst_sw_if_index,
357 last_info[payload_info.src])
358 last_info[payload_info.src] = next_info
359 self.assertIsNotNone(next_info)
360 self.assertEqual(packet_index, next_info.index)
361 saved_packet = next_info.data
362 # Check standard fields
363 self.assertEqual(ip.src, saved_packet[IPv6].src)
364 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
365 self.assertEqual(udp.sport, saved_packet[UDP].sport)
366 self.assertEqual(udp.dport, saved_packet[UDP].dport)
368 self.logger.error(ppp("Unexpected or invalid packet:", packet))
370 for i in self.pg_interfaces:
371 remaining_packet = self.get_next_packet_info_for_interface2(
372 i, dst_sw_if_index, last_info[i.sw_if_index])
375 "Port %u: Packet expected from source %u didn't arrive" %
376 (dst_sw_if_index, i.sw_if_index))
378 def verify_vrf(self, vrf_id, if_set_id=None):
380 Check if the FIB table / VRF ID is configured.
382 :param int vrf_id: The FIB table / VRF ID to be verified.
383 :return: 1 if the FIB table / VRF ID is configured, otherwise return 0.
385 if if_set_id is None:
387 ip6_fib_dump = self.vapi.ip_route_dump(vrf_id, True)
388 vrf_exist = len(ip6_fib_dump)
390 for ip6_fib_details in ip6_fib_dump:
391 addr = ip6_fib_details.route.prefix.network_address
393 for pg_if in self.pg_if_sets[if_set_id]:
396 for host in pg_if.remote_hosts:
397 if str(addr) == host.ip6:
401 if not vrf_exist and vrf_count == 0:
402 self.logger.info("IPv6 VRF ID %d is not configured" % vrf_id)
403 return VRFState.not_configured
404 elif vrf_exist and vrf_count == 0:
405 self.logger.info("IPv6 VRF ID %d has been reset" % vrf_id)
406 return VRFState.reset
408 self.logger.info("IPv6 VRF ID %d is configured" % vrf_id)
409 return VRFState.configured
411 def run_verify_test(self):
413 Create packet streams for all configured pg interfaces, send all \
414 prepared packet streams and verify that:
415 - all packets received correctly on all pg-ip6 interfaces assigned
417 - no packet received on all pg-ip6 interfaces not assigned to VRFs
419 :raise RuntimeError: If no packet captured on pg-ip6 interface assigned
420 to VRF or if any packet is captured on pg-ip6 interface not
424 # Create incoming packet streams for packet-generator interfaces
425 for pg_if in self.pg_interfaces:
426 pkts = self.create_stream(pg_if, self.pg_if_packet_sizes)
427 pg_if.add_stream(pkts)
429 # Enable packet capture and start packet sending
430 self.pg_enable_capture(self.pg_interfaces)
434 # Verify outgoing packet streams per packet-generator interface
435 for pg_if in self.pg_interfaces:
436 if pg_if in self.pg_in_vrf:
437 capture = pg_if.get_capture(remark="interface is in VRF")
438 self.verify_capture(pg_if, capture)
439 elif pg_if in self.pg_not_in_vrf:
440 pg_if.assert_nothing_captured(remark="interface is not in VRF",
441 filter_out_fn=is_ipv6_misc_ext)
442 self.logger.debug("No capture for interface %s" % pg_if.name)
444 raise Exception("Unknown interface: %s" % pg_if.name)
446 def run_crosswise_vrf_test(self):
448 Create packet streams for every pg-ip6 interface in VRF towards all
449 pg-ip6 interfaces in other VRFs, send all prepared packet streams and
452 - no packet received on all configured pg-ip6 interfaces
454 :raise RuntimeError: If any packet is captured on any pg-ip6 interface.
457 # Create incoming packet streams for packet-generator interfaces
458 for vrf_id in self.vrf_list:
459 for pg_if in self.pg_if_sets[vrf_id]:
460 pkts = self.create_stream_crosswise_vrf(
461 pg_if, vrf_id, self.pg_if_packet_sizes)
462 pg_if.add_stream(pkts)
464 # Enable packet capture and start packet sending
465 self.pg_enable_capture(self.pg_interfaces)
469 # Verify outgoing packet streams per packet-generator interface
470 for pg_if in self.pg_interfaces:
471 pg_if.assert_nothing_captured(remark="interface is in other VRF",
472 filter_out_fn=is_ipv6_misc_ext)
473 self.logger.debug("No capture for interface %s" % pg_if.name)
475 def test_ip6_vrf_01(self):
476 """ IP6 VRF Multi-instance test 1 - create 4 VRFs
480 self.create_vrf_and_assign_interfaces(4)
483 for vrf_id in self.vrf_list:
484 self.assert_equal(self.verify_vrf(vrf_id),
485 VRFState.configured, VRFState)
488 self.run_verify_test()
489 self.run_crosswise_vrf_test()
491 def test_ip6_vrf_02(self):
492 """ IP6 VRF Multi-instance test 2 - reset 2 VRFs
496 self.reset_vrf_and_remove_from_vrf_list(1)
497 self.reset_vrf_and_remove_from_vrf_list(2)
500 for vrf_id in self.vrf_reset_list:
501 self.assert_equal(self.verify_vrf(vrf_id),
502 VRFState.reset, VRFState)
503 for vrf_id in self.vrf_list:
504 self.assert_equal(self.verify_vrf(vrf_id),
505 VRFState.configured, VRFState)
508 self.run_verify_test()
509 self.run_crosswise_vrf_test()
511 # Reset routes learned from ICMPv6 Neighbor Discovery
512 # for vrf_id in self.vrf_reset_list:
513 # self.reset_vrf_and_remove_from_vrf_list(vrf_id)
515 def test_ip6_vrf_03(self):
516 """ IP6 VRF Multi-instance 3 - add 2 VRFs
519 # Add 1 of reset VRFs and 1 new VRF
520 self.create_vrf_and_assign_interfaces(1)
521 self.create_vrf_and_assign_interfaces(1, start=5)
524 for vrf_id in self.vrf_reset_list:
525 self.assert_equal(self.verify_vrf(vrf_id),
526 VRFState.reset, VRFState)
527 for vrf_id in self.vrf_list:
528 self.assert_equal(self.verify_vrf(vrf_id),
529 VRFState.configured, VRFState)
532 self.run_verify_test()
533 self.run_crosswise_vrf_test()
535 # Reset routes learned from ICMPv6 Neighbor Discovery
536 # for vrf_id in self.vrf_reset_list:
537 # self.reset_vrf_and_remove_from_vrf_list(vrf_id)
539 def test_ip6_vrf_04(self):
540 """ IP6 VRF Multi-instance test 4 - reset 4 VRFs
543 # Reset all VRFs (i.e. no VRF except VRF=0 configured)
544 for i in range(len(self.vrf_list)):
545 # This call removes the first item of vrf_list as a side effect
546 self.reset_vrf_and_remove_from_vrf_list(self.vrf_list[0])
549 for vrf_id in self.vrf_reset_list:
550 self.assert_equal(self.verify_vrf(vrf_id),
551 VRFState.reset, VRFState)
552 vrf_list_length = len(self.vrf_list)
555 "List of configured VRFs is not empty: %s != 0" % vrf_list_length)
558 self.run_verify_test()
559 self.run_crosswise_vrf_test()
561 def test_ip6_vrf_05(self):
562 """ IP6 VRF Multi-instance test 5 - auto allocate vrf id
565 # Create several VRFs
566 # Set vrf_id manually first
567 self.create_vrf_by_id_and_assign_interfaces(1, 10)
568 # Set vrf_id automatically a few times
570 self.create_vrf_by_id_and_assign_interfaces(i) for i in range(2, 5)
574 self.assert_equal(self.verify_vrf(10, 1), VRFState.configured,
576 for i, vrf in enumerate(auto_vrf_id):
577 self.assert_equal(self.verify_vrf(vrf, i+2),
578 VRFState.configured, VRFState)
581 self.run_verify_test()
585 self.reset_vrf_and_remove_from_vrf_list(10, 1)
586 for i, vrf in enumerate(auto_vrf_id):
587 self.reset_vrf_and_remove_from_vrf_list(vrf, i+2)
590 self.assert_equal(self.verify_vrf(10, 1), VRFState.reset, VRFState)
591 for i, vrf in enumerate(auto_vrf_id):
592 self.assert_equal(self.verify_vrf(vrf, i+2),
593 VRFState.reset, VRFState)
595 vrf_list_length = len(self.vrf_list)
598 "List of configured VRFs is not empty: %s != 0" % vrf_list_length)
600 # Cleanup our extra created VRFs
601 for vrf in auto_vrf_id:
606 def test_ip6_vrf_06(self):
607 """ IP6 VRF Multi-instance test 6 - recreate 4 VRFs
609 # Reconfigure all the VRFs
610 self.create_vrf_and_assign_interfaces(4)
612 for vrf_id in self.vrf_list:
613 self.assert_equal(self.verify_vrf(vrf_id),
614 VRFState.configured, VRFState)
616 self.run_verify_test()
617 self.run_crosswise_vrf_test()
619 for i in range(len(self.vrf_list)):
620 self.reset_vrf_and_remove_from_vrf_list(self.vrf_list[0])
622 for vrf_id in self.vrf_reset_list:
623 self.assert_equal(self.verify_vrf(vrf_id),
624 VRFState.reset, VRFState)
625 vrf_list_length = len(self.vrf_list)
628 "List of configured VRFs is not empty: %s != 0" % vrf_list_length)
630 self.run_verify_test()
631 self.run_crosswise_vrf_test()
634 if __name__ == '__main__':
635 unittest.main(testRunner=VppTestRunner)