2 """IP4 VRF Multi-instance Test Case HLD:
5 - higher number of pg-ip4 interfaces causes problems => only 15 pg-ip4 \
6 interfaces in 5 VRFs are tested
7 - jumbo packets in configuration with 15 pg-ip4 interfaces leads to \
11 - add 15 pg-ip4 interfaces
12 - configure 5 hosts per pg-ip4 interface
14 - add 3 pg-ip4 interfaces per VRF
17 - send IP4 packets between all pg-ip4 interfaces in all VRF groups
20 - check VRF data by parsing output of ip_fib_dump API command
21 - all packets received correctly in case of pg-ip4 interfaces in the
23 - no packet received in case of pg-ip4 interfaces not in VRF
24 - no packet received in case of pg-ip4 interfaces in different VRFs
30 - send IP4 packets between all pg-ip4 interfaces in all VRF groups
33 - all packets received correctly in case of pg-ip4 interfaces in the
35 - no packet received in case of pg-ip4 interfaces not in VRF
36 - no packet received in case of pg-ip4 interfaces in different VRFs
39 - add 1 of reset VRFs and 1 new VRF
42 - send IP4 packets between all pg-ip4 interfaces in all VRF groups
45 - check VRF data by parsing output of ip_fib_dump API command
46 - all packets received correctly in case of pg-ip4 interfaces in the
48 - no packet received in case of pg-ip4 interfaces not in VRF
49 - no packet received in case of pg-ip4 interfaces in different VRFs
52 - reset all created VRFs
55 - send IP4 packets between all pg-ip4 interfaces in all VRF groups
58 - check VRF data by parsing output of ip_fib_dump API command
59 - all packets received correctly in case of pg-ip4 interfaces in the
61 - no packet received in case of pg-ip4 interfaces not in VRF
62 - no packet received in case of pg-ip4 interfaces in different VRFs
70 from scapy.packet import Raw
71 from scapy.layers.l2 import Ether, ARP
72 from scapy.layers.inet import IP, UDP
74 from framework import VppTestCase, VppTestRunner
76 from vrf import VRFState
80 """Is packet one of uninteresting IPv4 broadcasts?"""
86 class TestIp4VrfMultiInst(VppTestCase):
87 """IP4 VRF Multi-instance Test Case"""
92 Perform standard class setup (defined by class method setUpClass in
93 class VppTestCase) before running the test case, set test case related
94 variables and configure VPP.
96 super(TestIp4VrfMultiInst, cls).setUpClass()
101 cls.pg_ifs_per_vrf = 3
104 # Create pg interfaces
105 cls.create_pg_interfaces(range(cls.nr_of_vrfs * cls.pg_ifs_per_vrf))
107 # Packet flows mapping pg0 -> pg1, pg2 etc.
109 for i in range(len(cls.pg_interfaces)):
110 multiplicand = i // cls.pg_ifs_per_vrf
112 cls.pg_interfaces[multiplicand * cls.pg_ifs_per_vrf + j]
113 for j in range(cls.pg_ifs_per_vrf)
114 if (multiplicand * cls.pg_ifs_per_vrf + j) != i
116 cls.flows[cls.pg_interfaces[i]] = pg_list
118 # Packet sizes - jumbo packet (9018 bytes) skipped
119 cls.pg_if_packet_sizes = [64, 512, 1518]
121 # Set up all interfaces
122 for pg_if in cls.pg_interfaces:
124 pg_if.generate_remote_hosts(cls.hosts_per_pg)
126 # Create list of VRFs
127 cls.vrf_list = list()
129 # Create list of reset VRFs
130 cls.vrf_reset_list = list()
132 # Create list of pg_interfaces in VRFs
133 cls.pg_in_vrf = list()
135 # Create list of pg_interfaces not in VRFs
136 cls.pg_not_in_vrf = [pg_if for pg_if in cls.pg_interfaces]
138 # Create mapping of pg_interfaces to VRF IDs
139 cls.pg_if_sets = dict()
140 for i in range(cls.nr_of_vrfs):
143 cls.pg_interfaces[i * cls.pg_ifs_per_vrf + j]
144 for j in range(cls.pg_ifs_per_vrf)
146 cls.pg_if_sets[set_id] = pg_list
149 super(TestIp4VrfMultiInst, cls).tearDownClass()
153 def tearDownClass(cls):
154 super(TestIp4VrfMultiInst, cls).tearDownClass()
158 Clear trace and packet infos before running each test.
160 super(TestIp4VrfMultiInst, self).setUp()
161 self.reset_packet_infos()
165 Show various debug prints after each test.
167 super(TestIp4VrfMultiInst, self).tearDown()
169 def show_commands_at_teardown(self):
170 self.logger.info(self.vapi.ppcli("show ip fib"))
171 self.logger.info(self.vapi.ppcli("show ip4 neighbors"))
173 def _assign_interfaces(self, vrf_id, if_set_id):
174 for i in range(self.pg_ifs_per_vrf):
175 pg_if = self.pg_if_sets[if_set_id][i]
176 pg_if.set_table_ip4(vrf_id)
178 "pg-interface %s added to IPv4 VRF ID %d" % (pg_if.name, vrf_id)
180 if pg_if not in self.pg_in_vrf:
181 self.pg_in_vrf.append(pg_if)
182 if pg_if in self.pg_not_in_vrf:
183 self.pg_not_in_vrf.remove(pg_if)
185 pg_if.configure_ipv4_neighbors()
187 def create_vrf_and_assign_interfaces(self, count, start=1):
189 Create required number of FIB tables / VRFs, put 3 pg-ip4 interfaces
190 to every FIB table / VRF.
192 :param int count: Number of FIB tables / VRFs to be created.
193 :param int start: Starting number of the FIB table / VRF ID. \
197 for i in range(count):
199 self.vapi.ip_table_add_del(is_add=1, table={"table_id": vrf_id})
200 self.logger.info("IPv4 VRF ID %d created" % vrf_id)
201 if vrf_id not in self.vrf_list:
202 self.vrf_list.append(vrf_id)
203 if vrf_id in self.vrf_reset_list:
204 self.vrf_reset_list.remove(vrf_id)
205 self._assign_interfaces(vrf_id, vrf_id)
206 self.logger.debug(self.vapi.ppcli("show ip fib"))
207 self.logger.debug(self.vapi.ppcli("show ip4 neighbors"))
209 def create_vrf_by_id_and_assign_interfaces(self, set_id, vrf_id=0xFFFFFFFF):
211 Create a FIB table / VRF by vrf_id, put 3 pg-ip4 interfaces
214 :param int vrf_id: Required table ID / VRF ID. \
215 (Default value = 0xffffffff, ID will be selected automatically)
217 ret = self.vapi.ip_table_allocate(table={"table_id": vrf_id})
218 vrf_id = ret.table.table_id
219 self.logger.info("IPv4 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, set_id)
225 self.logger.debug(self.vapi.ppcli("show ip fib"))
226 self.logger.debug(self.vapi.ppcli("show ip4 neighbors"))
230 def reset_vrf_and_remove_from_vrf_list(self, vrf_id, if_set_id=None):
232 Reset required FIB table / VRF and remove it from VRF list.
234 :param int vrf_id: The FIB table / VRF ID to be reset.
236 if if_set_id is None:
238 self.vapi.ip_table_flush(table={"table_id": vrf_id})
239 if vrf_id in self.vrf_list:
240 self.vrf_list.remove(vrf_id)
241 if vrf_id not in self.vrf_reset_list:
242 self.vrf_reset_list.append(vrf_id)
243 for j in range(self.pg_ifs_per_vrf):
244 pg_if = self.pg_if_sets[if_set_id][j]
246 if pg_if in self.pg_in_vrf:
247 self.pg_in_vrf.remove(pg_if)
248 if pg_if not in self.pg_not_in_vrf:
249 self.pg_not_in_vrf.append(pg_if)
250 self.logger.info("IPv4 VRF ID %d reset finished" % vrf_id)
251 self.logger.debug(self.vapi.ppcli("show ip fib"))
252 self.logger.debug(self.vapi.ppcli("show ip neighbors"))
253 self.vapi.ip_table_add_del(is_add=0, table={"table_id": vrf_id})
255 def create_stream(self, src_if, packet_sizes):
257 Create input packet stream for defined interface using hosts list.
259 :param object src_if: Interface to create packet stream for.
260 :param list packet_sizes: List of required packet sizes.
261 :return: Stream of packets.
264 src_hosts = src_if.remote_hosts
265 for dst_if in self.flows[src_if]:
266 for dst_host in dst_if.remote_hosts:
267 src_host = random.choice(src_hosts)
268 pkt_info = self.create_packet_info(src_if, dst_if)
269 payload = self.info_to_payload(pkt_info)
271 Ether(dst=src_if.local_mac, src=src_host.mac)
272 / IP(src=src_host.ip4, dst=dst_host.ip4)
273 / UDP(sport=1234, dport=1234)
276 pkt_info.data = p.copy()
277 size = random.choice(packet_sizes)
278 self.extend_packet(p, size)
281 "Input stream created for port %s. Length: %u pkt(s)"
282 % (src_if.name, len(pkts))
286 def create_stream_crosswise_vrf(self, src_if, vrf_id, packet_sizes):
288 Create input packet stream for negative test for leaking across
289 different VRFs for defined interface using hosts list.
291 :param object src_if: Interface to create packet stream for.
292 :param int vrf_id: The FIB table / VRF ID where src_if is assigned.
293 :param list packet_sizes: List of required packet sizes.
294 :return: Stream of packets.
297 src_hosts = src_if.remote_hosts
298 vrf_lst = list(self.vrf_list)
299 vrf_lst.remove(vrf_id)
301 for dst_if in self.pg_if_sets[vrf]:
302 for dst_host in dst_if.remote_hosts:
303 src_host = random.choice(src_hosts)
304 pkt_info = self.create_packet_info(src_if, dst_if)
305 payload = self.info_to_payload(pkt_info)
307 Ether(dst=src_if.local_mac, src=src_host.mac)
308 / IP(src=src_host.ip4, dst=dst_host.ip4)
309 / UDP(sport=1234, dport=1234)
312 pkt_info.data = p.copy()
313 size = random.choice(packet_sizes)
314 self.extend_packet(p, size)
317 "Input stream created for port %s. Length: %u pkt(s)"
318 % (src_if.name, len(pkts))
322 def verify_capture(self, pg_if, capture):
324 Verify captured input packet stream for defined interface.
326 :param object pg_if: Interface to verify captured packet stream for.
327 :param list capture: Captured packet stream.
330 for i in self.pg_interfaces:
331 last_info[i.sw_if_index] = None
332 dst_sw_if_index = pg_if.sw_if_index
333 for packet in capture:
337 payload_info = self.payload_to_info(packet[Raw])
338 packet_index = payload_info.index
339 self.assertEqual(payload_info.dst, dst_sw_if_index)
341 "Got packet on port %s: src=%u (id=%u)"
342 % (pg_if.name, payload_info.src, packet_index)
344 next_info = self.get_next_packet_info_for_interface2(
345 payload_info.src, dst_sw_if_index, last_info[payload_info.src]
347 last_info[payload_info.src] = next_info
348 self.assertIsNotNone(next_info)
349 self.assertEqual(packet_index, next_info.index)
350 saved_packet = next_info.data
351 # Check standard fields
352 self.assertEqual(ip.src, saved_packet[IP].src)
353 self.assertEqual(ip.dst, saved_packet[IP].dst)
354 self.assertEqual(udp.sport, saved_packet[UDP].sport)
355 self.assertEqual(udp.dport, saved_packet[UDP].dport)
357 self.logger.error(ppp("Unexpected or invalid packet:", packet))
359 for i in self.pg_interfaces:
360 remaining_packet = self.get_next_packet_info_for_interface2(
361 i, dst_sw_if_index, last_info[i.sw_if_index]
365 "Port %u: Packet expected from source %u didn't arrive"
366 % (dst_sw_if_index, i.sw_if_index),
369 def verify_vrf(self, vrf_id, if_set_id=None):
371 Check if the FIB table / VRF ID is configured.
373 :param int vrf_id: The FIB table / VRF ID to be verified.
374 :return: 1 if the FIB table / VRF ID is configured, otherwise return 0.
376 if if_set_id is None:
378 ip_fib_dump = self.vapi.ip_route_dump(vrf_id)
379 vrf_exist = len(ip_fib_dump)
381 for ip_fib_details in ip_fib_dump:
382 addr = ip_fib_details.route.prefix.network_address
384 for pg_if in self.pg_if_sets[if_set_id]:
387 for host in pg_if.remote_hosts:
388 if str(addr) == host.ip4:
392 if not vrf_exist and vrf_count == 0:
393 self.logger.info("IPv4 VRF ID %d is not configured" % vrf_id)
394 return VRFState.not_configured
395 elif vrf_exist and vrf_count == 0:
396 self.logger.info("IPv4 VRF ID %d has been reset" % vrf_id)
397 return VRFState.reset
399 self.logger.info("IPv4 VRF ID %d is configured" % vrf_id)
400 return VRFState.configured
402 def run_verify_test(self):
404 Create packet streams for all configured pg interfaces, send all \
405 prepared packet streams and verify that:
406 - all packets received correctly on all pg-ip4 interfaces assigned
408 - no packet received on all pg-ip4 interfaces not assigned to VRFs
410 :raise RuntimeError: If no packet captured on pg-ip4 interface assigned
411 to VRF or if any packet is captured on pg-ip4 interface not
415 # Create incoming packet streams for packet-generator interfaces
416 for pg_if in self.pg_interfaces:
417 pkts = self.create_stream(pg_if, self.pg_if_packet_sizes)
418 pg_if.add_stream(pkts)
420 # Enable packet capture and start packet sending
421 self.pg_enable_capture(self.pg_interfaces)
425 # Verify outgoing packet streams per packet-generator interface
426 for pg_if in self.pg_interfaces:
427 if pg_if in self.pg_in_vrf:
428 capture = pg_if.get_capture(remark="interface is in VRF")
429 self.verify_capture(pg_if, capture)
430 elif pg_if in self.pg_not_in_vrf:
431 pg_if.assert_nothing_captured(
432 remark="interface is not in VRF", filter_out_fn=is_ipv4_misc
434 self.logger.debug("No capture for interface %s" % pg_if.name)
436 raise Exception("Unknown interface: %s" % pg_if.name)
438 def run_crosswise_vrf_test(self):
440 Create packet streams for every pg-ip4 interface in VRF towards all
441 pg-ip4 interfaces in other VRFs, send all prepared packet streams and
444 - no packet received on all configured pg-ip4 interfaces
446 :raise RuntimeError: If any packet is captured on any pg-ip4 interface.
449 # Create incoming packet streams for packet-generator interfaces
450 for vrf_id in self.vrf_list:
451 for pg_if in self.pg_if_sets[vrf_id]:
452 pkts = self.create_stream_crosswise_vrf(
453 pg_if, vrf_id, self.pg_if_packet_sizes
455 pg_if.add_stream(pkts)
457 # Enable packet capture and start packet sending
458 self.pg_enable_capture(self.pg_interfaces)
462 # Verify outgoing packet streams per packet-generator interface
463 for pg_if in self.pg_interfaces:
464 pg_if.assert_nothing_captured(
465 remark="interface is in other VRF", filter_out_fn=is_ipv4_misc
467 self.logger.debug("No capture for interface %s" % pg_if.name)
469 def test_ip4_vrf_01(self):
470 """IP4 VRF Multi-instance test 1 - create 4 VRFs"""
473 self.create_vrf_and_assign_interfaces(4)
476 for vrf_id in self.vrf_list:
477 self.assert_equal(self.verify_vrf(vrf_id), VRFState.configured, VRFState)
480 self.run_verify_test()
481 self.run_crosswise_vrf_test()
483 def test_ip4_vrf_02(self):
484 """IP4 VRF Multi-instance test 2 - reset 2 VRFs"""
487 self.reset_vrf_and_remove_from_vrf_list(1)
488 self.reset_vrf_and_remove_from_vrf_list(2)
491 for vrf_id in self.vrf_reset_list:
492 self.assert_equal(self.verify_vrf(vrf_id), VRFState.reset, VRFState)
493 for vrf_id in self.vrf_list:
494 self.assert_equal(self.verify_vrf(vrf_id), VRFState.configured, VRFState)
497 self.run_verify_test()
498 self.run_crosswise_vrf_test()
500 def test_ip4_vrf_03(self):
501 """IP4 VRF Multi-instance 3 - add 2 VRFs"""
503 # Add 1 of reset VRFs and 1 new VRF
504 self.create_vrf_and_assign_interfaces(1)
505 self.create_vrf_and_assign_interfaces(1, start=5)
508 for vrf_id in self.vrf_reset_list:
509 self.assert_equal(self.verify_vrf(vrf_id), VRFState.reset, VRFState)
510 for vrf_id in self.vrf_list:
511 self.assert_equal(self.verify_vrf(vrf_id), VRFState.configured, VRFState)
514 self.run_verify_test()
515 self.run_crosswise_vrf_test()
517 def test_ip4_vrf_04(self):
518 """IP4 VRF Multi-instance test 4 - reset 4 VRFs"""
520 # Reset all VRFs (i.e. no VRF except VRF=0 configured)
521 for i in range(len(self.vrf_list)):
522 self.reset_vrf_and_remove_from_vrf_list(self.vrf_list[0])
525 for vrf_id in self.vrf_reset_list:
526 self.assert_equal(self.verify_vrf(vrf_id), VRFState.reset, VRFState)
527 vrf_list_length = len(self.vrf_list)
531 "List of configured VRFs is not empty: %s != 0" % vrf_list_length,
535 self.run_verify_test()
536 self.run_crosswise_vrf_test()
538 def test_ip4_vrf_05(self):
539 """IP4 VRF Multi-instance test 5 - id allocation"""
541 # Create several VRFs
542 # Set vrf_id manually first
543 self.create_vrf_by_id_and_assign_interfaces(1, 1)
544 # Set vrf_id automatically a few times
546 self.create_vrf_by_id_and_assign_interfaces(i) for i in range(2, 5)
550 self.assert_equal(self.verify_vrf(1, 1), VRFState.configured, VRFState)
551 for i, vrf in enumerate(auto_vrf_id):
553 self.verify_vrf(vrf, i + 2), VRFState.configured, VRFState
557 self.run_verify_test()
561 self.reset_vrf_and_remove_from_vrf_list(1)
562 for i, vrf in enumerate(auto_vrf_id):
563 self.reset_vrf_and_remove_from_vrf_list(vrf, i + 2)
566 self.assert_equal(self.verify_vrf(1, 1), VRFState.reset, VRFState)
567 for i, vrf in enumerate(auto_vrf_id):
568 self.assert_equal(self.verify_vrf(vrf, i + 2), VRFState.reset, VRFState)
570 vrf_list_length = len(self.vrf_list)
574 "List of configured VRFs is not empty: %s != 0" % vrf_list_length,
578 if __name__ == "__main__":
579 unittest.main(testRunner=VppTestRunner)