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
69 from scapy.packet import Raw
70 from scapy.layers.l2 import Ether
71 from scapy.layers.inet6 import (
79 from scapy.utils6 import in6_ismaddr, in6_isllsnmaddr
81 from framework import VppTestCase
82 from asfframework import VppTestRunner
84 from vrf import VRFState
87 def is_ipv6_misc_ext(p):
88 """Is packet one of uninteresting IPv6 broadcasts (extended to filter out
89 ICMPv6 Neighbor Discovery - Neighbor Advertisement packets too)?"""
90 if p.haslayer(ICMPv6ND_RA):
91 if in6_ismaddr(p[IPv6].dst):
93 if p.haslayer(ICMPv6ND_NS):
94 if in6_isllsnmaddr(p[IPv6].dst):
96 if p.haslayer(IPv6ExtHdrHopByHop):
97 for o in p[IPv6ExtHdrHopByHop].options:
98 if isinstance(o, RouterAlert):
103 class TestIP6VrfMultiInst(VppTestCase):
104 """IP6 VRF Multi-instance Test Case"""
109 Perform standard class setup (defined by class method setUpClass in
110 class VppTestCase) before running the test case, set test case related
111 variables and configure VPP.
113 super(TestIP6VrfMultiInst, cls).setUpClass()
118 cls.pg_ifs_per_vrf = 3
121 # Create pg interfaces
122 cls.create_pg_interfaces(range(cls.nr_of_vrfs * cls.pg_ifs_per_vrf))
124 # Packet flows mapping pg0 -> pg1, pg2 etc.
126 for i in range(len(cls.pg_interfaces)):
127 multiplicand = i // cls.pg_ifs_per_vrf
129 cls.pg_interfaces[multiplicand * cls.pg_ifs_per_vrf + j]
130 for j in range(cls.pg_ifs_per_vrf)
131 if (multiplicand * cls.pg_ifs_per_vrf + j) != i
133 cls.flows[cls.pg_interfaces[i]] = pg_list
135 # Packet sizes - jumbo packet (9018 bytes) skipped
136 cls.pg_if_packet_sizes = [64, 512, 1518]
138 # Set up all interfaces
139 for pg_if in cls.pg_interfaces:
141 pg_if.generate_remote_hosts(cls.hosts_per_pg)
143 # Create list of VRFs
144 cls.vrf_list = list()
146 # Create list of reset VRFs
147 cls.vrf_reset_list = list()
149 # Create list of pg_interfaces in VRFs
150 cls.pg_in_vrf = list()
152 # Create list of pg_interfaces not in VRFs
153 cls.pg_not_in_vrf = [pg_if for pg_if in cls.pg_interfaces]
155 # Create mapping of pg_interfaces to VRF IDs
156 cls.pg_if_sets = dict()
157 for i in range(cls.nr_of_vrfs):
160 cls.pg_interfaces[i * cls.pg_ifs_per_vrf + j]
161 for j in range(cls.pg_ifs_per_vrf)
163 cls.pg_if_sets[set_id] = pg_list
166 super(TestIP6VrfMultiInst, cls).tearDownClass()
170 def tearDownClass(cls):
171 super(TestIP6VrfMultiInst, cls).tearDownClass()
175 Clear trace and packet infos before running each test.
177 super(TestIP6VrfMultiInst, self).setUp()
178 self.reset_packet_infos()
182 Show various debug prints after each test.
184 super(TestIP6VrfMultiInst, self).tearDown()
186 def show_commands_at_teardown(self):
187 self.logger.info(self.vapi.ppcli("show ip6 fib"))
188 self.logger.info(self.vapi.ppcli("show ip6 neighbors"))
190 def _assign_interfaces(self, vrf_id, if_set_id):
191 for i in range(self.pg_ifs_per_vrf):
192 pg_if = self.pg_if_sets[if_set_id][i]
193 pg_if.set_table_ip6(vrf_id)
195 "pg-interface %s added to IPv6 VRF ID %d" % (pg_if.name, vrf_id)
197 if pg_if not in self.pg_in_vrf:
198 self.pg_in_vrf.append(pg_if)
199 if pg_if in self.pg_not_in_vrf:
200 self.pg_not_in_vrf.remove(pg_if)
202 pg_if.disable_ipv6_ra()
203 pg_if.configure_ipv6_neighbors()
205 def create_vrf_and_assign_interfaces(self, count, start=1):
207 Create required number of FIB tables / VRFs, put 3 pg-ip6 interfaces
208 to every FIB table / VRF.
210 :param int count: Number of FIB tables / VRFs to be created.
211 :param int start: Starting number of the FIB table / VRF ID. \
214 for i in range(count):
216 self.vapi.ip_table_add_del(
217 is_add=1, table={"table_id": vrf_id, "is_ip6": 1}
219 self.logger.info("IPv6 VRF ID %d created" % vrf_id)
220 if vrf_id not in self.vrf_list:
221 self.vrf_list.append(vrf_id)
222 if vrf_id in self.vrf_reset_list:
223 self.vrf_reset_list.remove(vrf_id)
224 self._assign_interfaces(vrf_id, vrf_id)
225 self.logger.debug(self.vapi.ppcli("show ip6 fib"))
226 self.logger.debug(self.vapi.ppcli("show ip6 neighbors"))
228 def create_vrf_by_id_and_assign_interfaces(self, set_id, vrf_id=0xFFFFFFFF):
230 Create a FIB table / VRF by vrf_id, put 3 pg-ip6 interfaces
233 :param int vrf_id: Required table ID / VRF ID. \
234 (Default value = 0xffffffff, ID will be selected automatically)
236 ret = self.vapi.ip_table_allocate(table={"table_id": vrf_id, "is_ip6": 1})
237 vrf_id = ret.table.table_id
238 self.logger.info("IPv6 VRF ID %d created" % vrf_id)
239 if vrf_id not in self.vrf_list:
240 self.vrf_list.append(vrf_id)
241 if vrf_id in self.vrf_reset_list:
242 self.vrf_reset_list.remove(vrf_id)
243 self._assign_interfaces(vrf_id, set_id)
244 self.logger.debug(self.vapi.ppcli("show ip6 fib"))
245 self.logger.debug(self.vapi.ppcli("show ip6 neighbors"))
249 def reset_vrf_and_remove_from_vrf_list(self, vrf_id, if_set_id=None):
251 Reset required FIB table / VRF and remove it from VRF list.
253 :param int vrf_id: The FIB table / VRF ID to be reset.
255 if if_set_id is None:
257 self.vapi.ip_table_flush(table={"table_id": vrf_id, "is_ip6": 1})
258 if vrf_id in self.vrf_list:
259 self.vrf_list.remove(vrf_id)
260 if vrf_id not in self.vrf_reset_list:
261 self.vrf_reset_list.append(vrf_id)
262 for j in range(self.pg_ifs_per_vrf):
263 pg_if = self.pg_if_sets[if_set_id][j]
265 pg_if.set_table_ip6(0)
266 if pg_if in self.pg_in_vrf:
267 self.pg_in_vrf.remove(pg_if)
268 if pg_if not in self.pg_not_in_vrf:
269 self.pg_not_in_vrf.append(pg_if)
270 self.logger.info("IPv6 VRF ID %d reset finished" % vrf_id)
271 self.logger.debug(self.vapi.ppcli("show ip6 fib"))
272 self.logger.debug(self.vapi.ppcli("show ip6 neighbors"))
274 def delete_vrf(self, vrf_id):
275 if vrf_id in self.vrf_list:
276 self.vrf_list.remove(vrf_id)
277 if vrf_id in self.vrf_reset_list:
278 self.vrf_reset_list.remove(vrf_id)
279 self.vapi.ip_table_add_del(is_add=0, table={"table_id": vrf_id, "is_ip6": 1})
281 def create_stream(self, src_if, packet_sizes):
283 Create input packet stream for defined interface using hosts list.
285 :param object src_if: Interface to create packet stream for.
286 :param list packet_sizes: List of required packet sizes.
287 :return: Stream of packets.
290 src_hosts = src_if.remote_hosts
291 for dst_if in self.flows[src_if]:
292 for dst_host in dst_if.remote_hosts:
293 src_host = random.choice(src_hosts)
294 pkt_info = self.create_packet_info(src_if, dst_if)
295 payload = self.info_to_payload(pkt_info)
297 Ether(dst=src_if.local_mac, src=src_host.mac)
298 / IPv6(src=src_host.ip6, dst=dst_host.ip6)
299 / UDP(sport=1234, dport=1234)
302 pkt_info.data = p.copy()
303 size = random.choice(packet_sizes)
304 self.extend_packet(p, size)
307 "Input stream created for port %s. Length: %u pkt(s)"
308 % (src_if.name, len(pkts))
312 def create_stream_crosswise_vrf(self, src_if, vrf_id, packet_sizes):
314 Create input packet stream for negative test for leaking across
315 different VRFs for defined interface using hosts list.
317 :param object src_if: Interface to create packet stream for.
318 :param int vrf_id: The FIB table / VRF ID where src_if is assigned.
319 :param list packet_sizes: List of required packet sizes.
320 :return: Stream of packets.
323 src_hosts = src_if.remote_hosts
324 vrf_lst = list(self.vrf_list)
325 vrf_lst.remove(vrf_id)
327 for dst_if in self.pg_if_sets[vrf]:
328 for dst_host in dst_if.remote_hosts:
329 src_host = random.choice(src_hosts)
330 pkt_info = self.create_packet_info(src_if, dst_if)
331 payload = self.info_to_payload(pkt_info)
333 Ether(dst=src_if.local_mac, src=src_host.mac)
334 / IPv6(src=src_host.ip6, dst=dst_host.ip6)
335 / UDP(sport=1234, dport=1234)
338 pkt_info.data = p.copy()
339 size = random.choice(packet_sizes)
340 self.extend_packet(p, size)
343 "Input stream created for port %s. Length: %u pkt(s)"
344 % (src_if.name, len(pkts))
348 def verify_capture(self, pg_if, capture):
350 Verify captured input packet stream for defined interface.
352 :param object pg_if: Interface to verify captured packet stream for.
353 :param list capture: Captured packet stream.
356 for i in self.pg_interfaces:
357 last_info[i.sw_if_index] = None
358 dst_sw_if_index = pg_if.sw_if_index
359 for packet in capture:
363 payload_info = self.payload_to_info(packet[Raw])
364 packet_index = payload_info.index
365 self.assertEqual(payload_info.dst, dst_sw_if_index)
367 "Got packet on port %s: src=%u (id=%u)"
368 % (pg_if.name, payload_info.src, packet_index)
370 next_info = self.get_next_packet_info_for_interface2(
371 payload_info.src, dst_sw_if_index, last_info[payload_info.src]
373 last_info[payload_info.src] = next_info
374 self.assertIsNotNone(next_info)
375 self.assertEqual(packet_index, next_info.index)
376 saved_packet = next_info.data
377 # Check standard fields
378 self.assertEqual(ip.src, saved_packet[IPv6].src)
379 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
380 self.assertEqual(udp.sport, saved_packet[UDP].sport)
381 self.assertEqual(udp.dport, saved_packet[UDP].dport)
383 self.logger.error(ppp("Unexpected or invalid packet:", packet))
385 for i in self.pg_interfaces:
386 remaining_packet = self.get_next_packet_info_for_interface2(
387 i, dst_sw_if_index, last_info[i.sw_if_index]
391 "Port %u: Packet expected from source %u didn't arrive"
392 % (dst_sw_if_index, i.sw_if_index),
395 def verify_vrf(self, vrf_id, if_set_id=None):
397 Check if the FIB table / VRF ID is configured.
399 :param int vrf_id: The FIB table / VRF ID to be verified.
400 :return: 1 if the FIB table / VRF ID is configured, otherwise return 0.
402 if if_set_id is None:
404 ip6_fib_dump = self.vapi.ip_route_dump(vrf_id, True)
405 vrf_exist = len(ip6_fib_dump)
407 for ip6_fib_details in ip6_fib_dump:
408 addr = ip6_fib_details.route.prefix.network_address
410 for pg_if in self.pg_if_sets[if_set_id]:
413 for host in pg_if.remote_hosts:
414 if str(addr) == host.ip6:
418 if not vrf_exist and vrf_count == 0:
419 self.logger.info("IPv6 VRF ID %d is not configured" % vrf_id)
420 return VRFState.not_configured
421 elif vrf_exist and vrf_count == 0:
422 self.logger.info("IPv6 VRF ID %d has been reset" % vrf_id)
423 return VRFState.reset
425 self.logger.info("IPv6 VRF ID %d is configured" % vrf_id)
426 return VRFState.configured
428 def run_verify_test(self):
430 Create packet streams for all configured pg interfaces, send all \
431 prepared packet streams and verify that:
432 - all packets received correctly on all pg-ip6 interfaces assigned
434 - no packet received on all pg-ip6 interfaces not assigned to VRFs
436 :raise RuntimeError: If no packet captured on pg-ip6 interface assigned
437 to VRF or if any packet is captured on pg-ip6 interface not
441 # Create incoming packet streams for packet-generator interfaces
442 for pg_if in self.pg_interfaces:
443 pkts = self.create_stream(pg_if, self.pg_if_packet_sizes)
444 pg_if.add_stream(pkts)
446 # Enable packet capture and start packet sending
447 self.pg_enable_capture(self.pg_interfaces)
451 # Verify outgoing packet streams per packet-generator interface
452 for pg_if in self.pg_interfaces:
453 if pg_if in self.pg_in_vrf:
454 capture = pg_if.get_capture(remark="interface is in VRF")
455 self.verify_capture(pg_if, capture)
456 elif pg_if in self.pg_not_in_vrf:
457 pg_if.assert_nothing_captured(
458 remark="interface is not in VRF", filter_out_fn=is_ipv6_misc_ext
460 self.logger.debug("No capture for interface %s" % pg_if.name)
462 raise Exception("Unknown interface: %s" % pg_if.name)
464 def run_crosswise_vrf_test(self):
466 Create packet streams for every pg-ip6 interface in VRF towards all
467 pg-ip6 interfaces in other VRFs, send all prepared packet streams and
470 - no packet received on all configured pg-ip6 interfaces
472 :raise RuntimeError: If any packet is captured on any pg-ip6 interface.
475 # Create incoming packet streams for packet-generator interfaces
476 for vrf_id in self.vrf_list:
477 for pg_if in self.pg_if_sets[vrf_id]:
478 pkts = self.create_stream_crosswise_vrf(
479 pg_if, vrf_id, self.pg_if_packet_sizes
481 pg_if.add_stream(pkts)
483 # Enable packet capture and start packet sending
484 self.pg_enable_capture(self.pg_interfaces)
488 # Verify outgoing packet streams per packet-generator interface
489 for pg_if in self.pg_interfaces:
490 pg_if.assert_nothing_captured(
491 remark="interface is in other VRF", filter_out_fn=is_ipv6_misc_ext
493 self.logger.debug("No capture for interface %s" % pg_if.name)
495 def test_ip6_vrf_01(self):
496 """IP6 VRF Multi-instance test 1 - create 4 VRFs"""
499 self.create_vrf_and_assign_interfaces(4)
502 for vrf_id in self.vrf_list:
503 self.assert_equal(self.verify_vrf(vrf_id), VRFState.configured, VRFState)
506 self.run_verify_test()
507 self.run_crosswise_vrf_test()
509 def test_ip6_vrf_02(self):
510 """IP6 VRF Multi-instance test 2 - reset 2 VRFs"""
513 self.reset_vrf_and_remove_from_vrf_list(1)
514 self.reset_vrf_and_remove_from_vrf_list(2)
517 for vrf_id in self.vrf_reset_list:
518 self.assert_equal(self.verify_vrf(vrf_id), VRFState.reset, VRFState)
519 for vrf_id in self.vrf_list:
520 self.assert_equal(self.verify_vrf(vrf_id), VRFState.configured, VRFState)
523 self.run_verify_test()
524 self.run_crosswise_vrf_test()
526 # Reset routes learned from ICMPv6 Neighbor Discovery
527 # for vrf_id in self.vrf_reset_list:
528 # self.reset_vrf_and_remove_from_vrf_list(vrf_id)
530 def test_ip6_vrf_03(self):
531 """IP6 VRF Multi-instance 3 - add 2 VRFs"""
533 # Add 1 of reset VRFs and 1 new VRF
534 self.create_vrf_and_assign_interfaces(1)
535 self.create_vrf_and_assign_interfaces(1, start=5)
538 for vrf_id in self.vrf_reset_list:
539 self.assert_equal(self.verify_vrf(vrf_id), VRFState.reset, VRFState)
540 for vrf_id in self.vrf_list:
541 self.assert_equal(self.verify_vrf(vrf_id), VRFState.configured, VRFState)
544 self.run_verify_test()
545 self.run_crosswise_vrf_test()
547 # Reset routes learned from ICMPv6 Neighbor Discovery
548 # for vrf_id in self.vrf_reset_list:
549 # self.reset_vrf_and_remove_from_vrf_list(vrf_id)
551 def test_ip6_vrf_04(self):
552 """IP6 VRF Multi-instance test 4 - reset 4 VRFs"""
554 # Reset all VRFs (i.e. no VRF except VRF=0 configured)
555 for i in range(len(self.vrf_list)):
556 # This call removes the first item of vrf_list as a side effect
557 self.reset_vrf_and_remove_from_vrf_list(self.vrf_list[0])
560 for vrf_id in self.vrf_reset_list:
561 self.assert_equal(self.verify_vrf(vrf_id), VRFState.reset, VRFState)
562 vrf_list_length = len(self.vrf_list)
566 "List of configured VRFs is not empty: %s != 0" % vrf_list_length,
570 self.run_verify_test()
571 self.run_crosswise_vrf_test()
573 def test_ip6_vrf_05(self):
574 """IP6 VRF Multi-instance test 5 - auto allocate vrf id"""
576 # Create several VRFs
577 # Set vrf_id manually first
578 self.create_vrf_by_id_and_assign_interfaces(1, 10)
579 # Set vrf_id automatically a few times
581 self.create_vrf_by_id_and_assign_interfaces(i) for i in range(2, 5)
585 self.assert_equal(self.verify_vrf(10, 1), VRFState.configured, VRFState)
586 for i, vrf in enumerate(auto_vrf_id):
588 self.verify_vrf(vrf, i + 2), VRFState.configured, VRFState
592 self.run_verify_test()
596 self.reset_vrf_and_remove_from_vrf_list(10, 1)
597 for i, vrf in enumerate(auto_vrf_id):
598 self.reset_vrf_and_remove_from_vrf_list(vrf, i + 2)
601 self.assert_equal(self.verify_vrf(10, 1), VRFState.reset, VRFState)
602 for i, vrf in enumerate(auto_vrf_id):
603 self.assert_equal(self.verify_vrf(vrf, i + 2), VRFState.reset, VRFState)
605 vrf_list_length = len(self.vrf_list)
609 "List of configured VRFs is not empty: %s != 0" % vrf_list_length,
612 # Cleanup our extra created VRFs
613 for vrf in auto_vrf_id:
618 def test_ip6_vrf_06(self):
619 """IP6 VRF Multi-instance test 6 - recreate 4 VRFs"""
620 # Reconfigure all the VRFs
621 self.create_vrf_and_assign_interfaces(4)
623 for vrf_id in self.vrf_list:
624 self.assert_equal(self.verify_vrf(vrf_id), VRFState.configured, VRFState)
626 self.run_verify_test()
627 self.run_crosswise_vrf_test()
629 for i in range(len(self.vrf_list)):
630 self.reset_vrf_and_remove_from_vrf_list(self.vrf_list[0])
632 for vrf_id in self.vrf_reset_list:
633 self.assert_equal(self.verify_vrf(vrf_id), VRFState.reset, VRFState)
634 vrf_list_length = len(self.vrf_list)
638 "List of configured VRFs is not empty: %s != 0" % vrf_list_length,
641 self.run_verify_test()
642 self.run_crosswise_vrf_test()
645 if __name__ == "__main__":
646 unittest.main(testRunner=VppTestRunner)