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 ip6_fib_dump API command
21 - all packets received correctly in case of pg-ip6 interfaces in the same
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 ip6_fib_dump API command
34 - all packets received correctly in case of pg-ip6 interfaces in the same
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 ip6_fib_dump API command
47 - all packets received correctly in case of pg-ip6 interfaces in the same
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 ip6_fib_dump API command
60 - all packets received correctly in case of pg-ip6 interfaces in the same
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_by_vrf_id = 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_by_vrf_id[vrf_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()
179 if not self.vpp_dead:
180 self.logger.info(self.vapi.ppcli("show ip6 fib"))
181 self.logger.info(self.vapi.ppcli("show ip6 neighbors"))
183 def create_vrf_and_assign_interfaces(self, count, start=1):
185 Create required number of FIB tables / VRFs, put 3 pg-ip6 interfaces
186 to every FIB table / VRF.
188 :param int count: Number of FIB tables / VRFs to be created.
189 :param int start: Starting number of the FIB table / VRF ID. \
192 for i in range(count):
194 pg_if = self.pg_if_by_vrf_id[vrf_id][0]
195 dest_addr = pg_if.local_ip6n
197 self.vapi.ip_table_add_del(is_ipv6=1, is_add=1, table_id=vrf_id)
198 self.logger.info("IPv6 VRF ID %d created" % vrf_id)
199 if vrf_id not in self.vrf_list:
200 self.vrf_list.append(vrf_id)
201 if vrf_id in self.vrf_reset_list:
202 self.vrf_reset_list.remove(vrf_id)
203 for j in range(self.pg_ifs_per_vrf):
204 pg_if = self.pg_if_by_vrf_id[vrf_id][j]
205 pg_if.set_table_ip6(vrf_id)
206 self.logger.info("pg-interface %s added to IPv6 VRF ID %d"
207 % (pg_if.name, vrf_id))
208 if pg_if not in self.pg_in_vrf:
209 self.pg_in_vrf.append(pg_if)
210 if pg_if in self.pg_not_in_vrf:
211 self.pg_not_in_vrf.remove(pg_if)
213 pg_if.disable_ipv6_ra()
214 pg_if.configure_ipv6_neighbors()
215 self.logger.debug(self.vapi.ppcli("show ip6 fib"))
216 self.logger.debug(self.vapi.ppcli("show ip6 neighbors"))
218 def reset_vrf_and_remove_from_vrf_list(self, vrf_id):
220 Reset required FIB table / VRF and remove it from VRF list.
222 :param int vrf_id: The FIB table / VRF ID to be reset.
224 # self.vapi.reset_vrf(vrf_id, is_ipv6=1)
225 self.vapi.reset_fib(vrf_id, is_ipv6=1)
226 if vrf_id in self.vrf_list:
227 self.vrf_list.remove(vrf_id)
228 if vrf_id not in self.vrf_reset_list:
229 self.vrf_reset_list.append(vrf_id)
230 for j in range(self.pg_ifs_per_vrf):
231 pg_if = self.pg_if_by_vrf_id[vrf_id][j]
233 if pg_if in self.pg_in_vrf:
234 self.pg_in_vrf.remove(pg_if)
235 if pg_if not in self.pg_not_in_vrf:
236 self.pg_not_in_vrf.append(pg_if)
237 self.logger.info("IPv6 VRF ID %d reset finished" % vrf_id)
238 self.logger.debug(self.vapi.ppcli("show ip6 fib"))
239 self.logger.debug(self.vapi.ppcli("show ip6 neighbors"))
240 self.vapi.ip_table_add_del(is_ipv6=1, is_add=0, table_id=vrf_id)
242 def create_stream(self, src_if, packet_sizes):
244 Create input packet stream for defined interface using hosts list.
246 :param object src_if: Interface to create packet stream for.
247 :param list packet_sizes: List of required packet sizes.
248 :return: Stream of packets.
251 src_hosts = src_if.remote_hosts
252 for dst_if in self.flows[src_if]:
253 for dst_host in dst_if.remote_hosts:
254 src_host = random.choice(src_hosts)
255 pkt_info = self.create_packet_info(src_if, dst_if)
256 payload = self.info_to_payload(pkt_info)
257 p = (Ether(dst=src_if.local_mac, src=src_host.mac) /
258 IPv6(src=src_host.ip6, dst=dst_host.ip6) /
259 UDP(sport=1234, dport=1234) /
261 pkt_info.data = p.copy()
262 size = random.choice(packet_sizes)
263 self.extend_packet(p, size)
265 self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
266 % (src_if.name, len(pkts)))
269 def create_stream_crosswise_vrf(self, src_if, vrf_id, packet_sizes):
271 Create input packet stream for negative test for leaking across
272 different VRFs for defined interface using hosts list.
274 :param object src_if: Interface to create packet stream for.
275 :param int vrf_id: The FIB table / VRF ID where src_if is assigned.
276 :param list packet_sizes: List of required packet sizes.
277 :return: Stream of packets.
280 src_hosts = src_if.remote_hosts
281 vrf_lst = list(self.vrf_list)
282 vrf_lst.remove(vrf_id)
284 for dst_if in self.pg_if_by_vrf_id[vrf]:
285 for dst_host in dst_if.remote_hosts:
286 src_host = random.choice(src_hosts)
287 pkt_info = self.create_packet_info(src_if, dst_if)
288 payload = self.info_to_payload(pkt_info)
289 p = (Ether(dst=src_if.local_mac, src=src_host.mac) /
290 IPv6(src=src_host.ip6, dst=dst_host.ip6) /
291 UDP(sport=1234, dport=1234) /
293 pkt_info.data = p.copy()
294 size = random.choice(packet_sizes)
295 self.extend_packet(p, size)
297 self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
298 % (src_if.name, len(pkts)))
301 def verify_capture(self, pg_if, capture):
303 Verify captured input packet stream for defined interface.
305 :param object pg_if: Interface to verify captured packet stream for.
306 :param list capture: Captured packet stream.
309 for i in self.pg_interfaces:
310 last_info[i.sw_if_index] = None
311 dst_sw_if_index = pg_if.sw_if_index
312 for packet in capture:
316 payload_info = self.payload_to_info(packet[Raw])
317 packet_index = payload_info.index
318 self.assertEqual(payload_info.dst, dst_sw_if_index)
319 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
320 (pg_if.name, payload_info.src, packet_index))
321 next_info = self.get_next_packet_info_for_interface2(
322 payload_info.src, dst_sw_if_index,
323 last_info[payload_info.src])
324 last_info[payload_info.src] = next_info
325 self.assertIsNotNone(next_info)
326 self.assertEqual(packet_index, next_info.index)
327 saved_packet = next_info.data
328 # Check standard fields
329 self.assertEqual(ip.src, saved_packet[IPv6].src)
330 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
331 self.assertEqual(udp.sport, saved_packet[UDP].sport)
332 self.assertEqual(udp.dport, saved_packet[UDP].dport)
334 self.logger.error(ppp("Unexpected or invalid packet:", packet))
336 for i in self.pg_interfaces:
337 remaining_packet = self.get_next_packet_info_for_interface2(
338 i, dst_sw_if_index, last_info[i.sw_if_index])
341 "Port %u: Packet expected from source %u didn't arrive" %
342 (dst_sw_if_index, i.sw_if_index))
344 def verify_vrf(self, vrf_id):
346 Check if the FIB table / VRF ID is configured.
348 :param int vrf_id: The FIB table / VRF ID to be verified.
349 :return: 1 if the FIB table / VRF ID is configured, otherwise return 0.
351 ip6_fib_dump = self.vapi.ip6_fib_dump()
354 for ip6_fib_details in ip6_fib_dump:
355 if ip6_fib_details.table_id == vrf_id:
358 addr = inet_ntop(socket.AF_INET6, ip6_fib_details.address)
360 for pg_if in self.pg_if_by_vrf_id[vrf_id]:
363 for host in pg_if.remote_hosts:
364 if str(addr) == str(host.ip6):
368 if not vrf_exist and vrf_count == 0:
369 self.logger.info("IPv6 VRF ID %d is not configured" % vrf_id)
370 return VRFState.not_configured
371 elif vrf_exist and vrf_count == 0:
372 self.logger.info("IPv6 VRF ID %d has been reset" % vrf_id)
373 return VRFState.reset
375 self.logger.info("IPv6 VRF ID %d is configured" % vrf_id)
376 return VRFState.configured
378 def run_verify_test(self):
380 Create packet streams for all configured pg interfaces, send all \
381 prepared packet streams and verify that:
382 - all packets received correctly on all pg-ip6 interfaces assigned
384 - no packet received on all pg-ip6 interfaces not assigned to VRFs
386 :raise RuntimeError: If no packet captured on pg-ip6 interface assigned
387 to VRF or if any packet is captured on pg-ip6 interface not
391 # Create incoming packet streams for packet-generator interfaces
392 for pg_if in self.pg_interfaces:
393 pkts = self.create_stream(pg_if, self.pg_if_packet_sizes)
394 pg_if.add_stream(pkts)
396 # Enable packet capture and start packet sending
397 self.pg_enable_capture(self.pg_interfaces)
401 # Verify outgoing packet streams per packet-generator interface
402 for pg_if in self.pg_interfaces:
403 if pg_if in self.pg_in_vrf:
404 capture = pg_if.get_capture(remark="interface is in VRF")
405 self.verify_capture(pg_if, capture)
406 elif pg_if in self.pg_not_in_vrf:
407 pg_if.assert_nothing_captured(remark="interface is not in VRF",
408 filter_out_fn=is_ipv6_misc_ext)
409 self.logger.debug("No capture for interface %s" % pg_if.name)
411 raise Exception("Unknown interface: %s" % pg_if.name)
413 def run_crosswise_vrf_test(self):
415 Create packet streams for every pg-ip6 interface in VRF towards all
416 pg-ip6 interfaces in other VRFs, send all prepared packet streams and \
418 - no packet received on all configured pg-ip6 interfaces
420 :raise RuntimeError: If any packet is captured on any pg-ip6 interface.
423 # Create incoming packet streams for packet-generator interfaces
424 for vrf_id in self.vrf_list:
425 for pg_if in self.pg_if_by_vrf_id[vrf_id]:
426 pkts = self.create_stream_crosswise_vrf(
427 pg_if, vrf_id, self.pg_if_packet_sizes)
428 pg_if.add_stream(pkts)
430 # Enable packet capture and start packet sending
431 self.pg_enable_capture(self.pg_interfaces)
435 # Verify outgoing packet streams per packet-generator interface
436 for pg_if in self.pg_interfaces:
437 pg_if.assert_nothing_captured(remark="interface is in other VRF",
438 filter_out_fn=is_ipv6_misc_ext)
439 self.logger.debug("No capture for interface %s" % pg_if.name)
441 def test_ip6_vrf_01(self):
442 """ IP6 VRF Multi-instance test 1 - create 4 VRFs
446 self.create_vrf_and_assign_interfaces(4)
449 for vrf_id in self.vrf_list:
450 self.assert_equal(self.verify_vrf(vrf_id),
451 VRFState.configured, VRFState)
454 self.run_verify_test()
455 self.run_crosswise_vrf_test()
457 def test_ip6_vrf_02(self):
458 """ IP6 VRF Multi-instance test 2 - reset 2 VRFs
462 self.reset_vrf_and_remove_from_vrf_list(1)
463 self.reset_vrf_and_remove_from_vrf_list(2)
466 for vrf_id in self.vrf_reset_list:
467 self.assert_equal(self.verify_vrf(vrf_id),
468 VRFState.reset, VRFState)
469 for vrf_id in self.vrf_list:
470 self.assert_equal(self.verify_vrf(vrf_id),
471 VRFState.configured, VRFState)
474 self.run_verify_test()
475 self.run_crosswise_vrf_test()
477 # Reset routes learned from ICMPv6 Neighbor Discovery
478 for vrf_id in self.vrf_reset_list:
479 self.reset_vrf_and_remove_from_vrf_list(vrf_id)
481 def test_ip6_vrf_03(self):
482 """ IP6 VRF Multi-instance 3 - add 2 VRFs
485 # Add 1 of reset VRFs and 1 new VRF
486 self.create_vrf_and_assign_interfaces(1)
487 self.create_vrf_and_assign_interfaces(1, start=5)
490 for vrf_id in self.vrf_reset_list:
491 self.assert_equal(self.verify_vrf(vrf_id),
492 VRFState.reset, VRFState)
493 for vrf_id in self.vrf_list:
494 self.assert_equal(self.verify_vrf(vrf_id),
495 VRFState.configured, VRFState)
498 self.run_verify_test()
499 self.run_crosswise_vrf_test()
501 # Reset routes learned from ICMPv6 Neighbor Discovery
502 for vrf_id in self.vrf_reset_list:
503 self.reset_vrf_and_remove_from_vrf_list(vrf_id)
505 def test_ip6_vrf_04(self):
506 """ IP6 VRF Multi-instance test 4 - reset 4 VRFs
509 # Reset all VRFs (i.e. no VRF except VRF=0 configured)
510 for i in range(len(self.vrf_list)):
511 self.reset_vrf_and_remove_from_vrf_list(self.vrf_list[0])
514 for vrf_id in self.vrf_reset_list:
515 self.assert_equal(self.verify_vrf(vrf_id),
516 VRFState.reset, VRFState)
517 vrf_list_length = len(self.vrf_list)
520 "List of configured VRFs is not empty: %s != 0" % vrf_list_length)
523 self.run_verify_test()
524 self.run_crosswise_vrf_test()
527 if __name__ == '__main__':
528 unittest.main(testRunner=VppTestRunner)