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
68 from scapy.packet import Raw
69 from scapy.layers.l2 import Ether, ARP
70 from scapy.layers.inet import IP, UDP
72 from framework import VppTestCase
73 from asfframework import VppTestRunner
75 from vrf import VRFState
79 """Is packet one of uninteresting IPv4 broadcasts?"""
85 class TestIp4VrfMultiInst(VppTestCase):
86 """IP4 VRF Multi-instance Test Case"""
91 Perform standard class setup (defined by class method setUpClass in
92 class VppTestCase) before running the test case, set test case related
93 variables and configure VPP.
95 super(TestIp4VrfMultiInst, cls).setUpClass()
100 cls.pg_ifs_per_vrf = 3
103 # Create pg interfaces
104 cls.create_pg_interfaces(range(cls.nr_of_vrfs * cls.pg_ifs_per_vrf))
106 # Packet flows mapping pg0 -> pg1, pg2 etc.
108 for i in range(len(cls.pg_interfaces)):
109 multiplicand = i // cls.pg_ifs_per_vrf
111 cls.pg_interfaces[multiplicand * cls.pg_ifs_per_vrf + j]
112 for j in range(cls.pg_ifs_per_vrf)
113 if (multiplicand * cls.pg_ifs_per_vrf + j) != i
115 cls.flows[cls.pg_interfaces[i]] = pg_list
117 # Packet sizes - jumbo packet (9018 bytes) skipped
118 cls.pg_if_packet_sizes = [64, 512, 1518]
120 # Set up all interfaces
121 for pg_if in cls.pg_interfaces:
123 pg_if.generate_remote_hosts(cls.hosts_per_pg)
125 # Create list of VRFs
126 cls.vrf_list = list()
128 # Create list of reset VRFs
129 cls.vrf_reset_list = list()
131 # Create list of pg_interfaces in VRFs
132 cls.pg_in_vrf = list()
134 # Create list of pg_interfaces not in VRFs
135 cls.pg_not_in_vrf = [pg_if for pg_if in cls.pg_interfaces]
137 # Create mapping of pg_interfaces to VRF IDs
138 cls.pg_if_sets = dict()
139 for i in range(cls.nr_of_vrfs):
142 cls.pg_interfaces[i * cls.pg_ifs_per_vrf + j]
143 for j in range(cls.pg_ifs_per_vrf)
145 cls.pg_if_sets[set_id] = pg_list
148 super(TestIp4VrfMultiInst, cls).tearDownClass()
152 def tearDownClass(cls):
153 super(TestIp4VrfMultiInst, cls).tearDownClass()
157 Clear trace and packet infos before running each test.
159 super(TestIp4VrfMultiInst, self).setUp()
160 self.reset_packet_infos()
164 Show various debug prints after each test.
166 super(TestIp4VrfMultiInst, self).tearDown()
168 def show_commands_at_teardown(self):
169 self.logger.info(self.vapi.ppcli("show ip fib"))
170 self.logger.info(self.vapi.ppcli("show ip4 neighbors"))
172 def _assign_interfaces(self, vrf_id, if_set_id):
173 for i in range(self.pg_ifs_per_vrf):
174 pg_if = self.pg_if_sets[if_set_id][i]
175 pg_if.set_table_ip4(vrf_id)
177 "pg-interface %s added to IPv4 VRF ID %d" % (pg_if.name, vrf_id)
179 if pg_if not in self.pg_in_vrf:
180 self.pg_in_vrf.append(pg_if)
181 if pg_if in self.pg_not_in_vrf:
182 self.pg_not_in_vrf.remove(pg_if)
184 pg_if.configure_ipv4_neighbors()
186 def create_vrf_and_assign_interfaces(self, count, start=1):
188 Create required number of FIB tables / VRFs, put 3 pg-ip4 interfaces
189 to every FIB table / VRF.
191 :param int count: Number of FIB tables / VRFs to be created.
192 :param int start: Starting number of the FIB table / VRF ID. \
196 for i in range(count):
198 self.vapi.ip_table_add_del(is_add=1, table={"table_id": vrf_id})
199 self.logger.info("IPv4 VRF ID %d created" % vrf_id)
200 if vrf_id not in self.vrf_list:
201 self.vrf_list.append(vrf_id)
202 if vrf_id in self.vrf_reset_list:
203 self.vrf_reset_list.remove(vrf_id)
204 self._assign_interfaces(vrf_id, vrf_id)
205 self.logger.debug(self.vapi.ppcli("show ip fib"))
206 self.logger.debug(self.vapi.ppcli("show ip4 neighbors"))
208 def create_vrf_by_id_and_assign_interfaces(self, set_id, vrf_id=0xFFFFFFFF):
210 Create a FIB table / VRF by vrf_id, put 3 pg-ip4 interfaces
213 :param int vrf_id: Required table ID / VRF ID. \
214 (Default value = 0xffffffff, ID will be selected automatically)
216 ret = self.vapi.ip_table_allocate(table={"table_id": vrf_id})
217 vrf_id = ret.table.table_id
218 self.logger.info("IPv4 VRF ID %d created" % vrf_id)
219 if vrf_id not in self.vrf_list:
220 self.vrf_list.append(vrf_id)
221 if vrf_id in self.vrf_reset_list:
222 self.vrf_reset_list.remove(vrf_id)
223 self._assign_interfaces(vrf_id, set_id)
224 self.logger.debug(self.vapi.ppcli("show ip fib"))
225 self.logger.debug(self.vapi.ppcli("show ip4 neighbors"))
229 def reset_vrf_and_remove_from_vrf_list(self, vrf_id, if_set_id=None):
231 Reset required FIB table / VRF and remove it from VRF list.
233 :param int vrf_id: The FIB table / VRF ID to be reset.
235 if if_set_id is None:
237 self.vapi.ip_table_flush(table={"table_id": vrf_id})
238 if vrf_id in self.vrf_list:
239 self.vrf_list.remove(vrf_id)
240 if vrf_id not in self.vrf_reset_list:
241 self.vrf_reset_list.append(vrf_id)
242 for j in range(self.pg_ifs_per_vrf):
243 pg_if = self.pg_if_sets[if_set_id][j]
245 if pg_if in self.pg_in_vrf:
246 self.pg_in_vrf.remove(pg_if)
247 if pg_if not in self.pg_not_in_vrf:
248 self.pg_not_in_vrf.append(pg_if)
249 self.logger.info("IPv4 VRF ID %d reset finished" % vrf_id)
250 self.logger.debug(self.vapi.ppcli("show ip fib"))
251 self.logger.debug(self.vapi.ppcli("show ip neighbors"))
252 self.vapi.ip_table_add_del(is_add=0, table={"table_id": vrf_id})
254 def create_stream(self, src_if, packet_sizes):
256 Create input packet stream for defined interface using hosts list.
258 :param object src_if: Interface to create packet stream for.
259 :param list packet_sizes: List of required packet sizes.
260 :return: Stream of packets.
263 src_hosts = src_if.remote_hosts
264 for dst_if in self.flows[src_if]:
265 for dst_host in dst_if.remote_hosts:
266 src_host = random.choice(src_hosts)
267 pkt_info = self.create_packet_info(src_if, dst_if)
268 payload = self.info_to_payload(pkt_info)
270 Ether(dst=src_if.local_mac, src=src_host.mac)
271 / IP(src=src_host.ip4, dst=dst_host.ip4)
272 / UDP(sport=1234, dport=1234)
275 pkt_info.data = p.copy()
276 size = random.choice(packet_sizes)
277 self.extend_packet(p, size)
280 "Input stream created for port %s. Length: %u pkt(s)"
281 % (src_if.name, len(pkts))
285 def create_stream_crosswise_vrf(self, src_if, vrf_id, packet_sizes):
287 Create input packet stream for negative test for leaking across
288 different VRFs for defined interface using hosts list.
290 :param object src_if: Interface to create packet stream for.
291 :param int vrf_id: The FIB table / VRF ID where src_if is assigned.
292 :param list packet_sizes: List of required packet sizes.
293 :return: Stream of packets.
296 src_hosts = src_if.remote_hosts
297 vrf_lst = list(self.vrf_list)
298 vrf_lst.remove(vrf_id)
300 for dst_if in self.pg_if_sets[vrf]:
301 for dst_host in dst_if.remote_hosts:
302 src_host = random.choice(src_hosts)
303 pkt_info = self.create_packet_info(src_if, dst_if)
304 payload = self.info_to_payload(pkt_info)
306 Ether(dst=src_if.local_mac, src=src_host.mac)
307 / IP(src=src_host.ip4, dst=dst_host.ip4)
308 / UDP(sport=1234, dport=1234)
311 pkt_info.data = p.copy()
312 size = random.choice(packet_sizes)
313 self.extend_packet(p, size)
316 "Input stream created for port %s. Length: %u pkt(s)"
317 % (src_if.name, len(pkts))
321 def verify_capture(self, pg_if, capture):
323 Verify captured input packet stream for defined interface.
325 :param object pg_if: Interface to verify captured packet stream for.
326 :param list capture: Captured packet stream.
329 for i in self.pg_interfaces:
330 last_info[i.sw_if_index] = None
331 dst_sw_if_index = pg_if.sw_if_index
332 for packet in capture:
336 payload_info = self.payload_to_info(packet[Raw])
337 packet_index = payload_info.index
338 self.assertEqual(payload_info.dst, dst_sw_if_index)
340 "Got packet on port %s: src=%u (id=%u)"
341 % (pg_if.name, payload_info.src, packet_index)
343 next_info = self.get_next_packet_info_for_interface2(
344 payload_info.src, dst_sw_if_index, last_info[payload_info.src]
346 last_info[payload_info.src] = next_info
347 self.assertIsNotNone(next_info)
348 self.assertEqual(packet_index, next_info.index)
349 saved_packet = next_info.data
350 # Check standard fields
351 self.assertEqual(ip.src, saved_packet[IP].src)
352 self.assertEqual(ip.dst, saved_packet[IP].dst)
353 self.assertEqual(udp.sport, saved_packet[UDP].sport)
354 self.assertEqual(udp.dport, saved_packet[UDP].dport)
356 self.logger.error(ppp("Unexpected or invalid packet:", packet))
358 for i in self.pg_interfaces:
359 remaining_packet = self.get_next_packet_info_for_interface2(
360 i, dst_sw_if_index, last_info[i.sw_if_index]
364 "Port %u: Packet expected from source %u didn't arrive"
365 % (dst_sw_if_index, i.sw_if_index),
368 def verify_vrf(self, vrf_id, if_set_id=None):
370 Check if the FIB table / VRF ID is configured.
372 :param int vrf_id: The FIB table / VRF ID to be verified.
373 :return: 1 if the FIB table / VRF ID is configured, otherwise return 0.
375 if if_set_id is None:
377 ip_fib_dump = self.vapi.ip_route_dump(vrf_id)
378 vrf_exist = len(ip_fib_dump)
380 for ip_fib_details in ip_fib_dump:
381 addr = ip_fib_details.route.prefix.network_address
383 for pg_if in self.pg_if_sets[if_set_id]:
386 for host in pg_if.remote_hosts:
387 if str(addr) == host.ip4:
391 if not vrf_exist and vrf_count == 0:
392 self.logger.info("IPv4 VRF ID %d is not configured" % vrf_id)
393 return VRFState.not_configured
394 elif vrf_exist and vrf_count == 0:
395 self.logger.info("IPv4 VRF ID %d has been reset" % vrf_id)
396 return VRFState.reset
398 self.logger.info("IPv4 VRF ID %d is configured" % vrf_id)
399 return VRFState.configured
401 def run_verify_test(self):
403 Create packet streams for all configured pg interfaces, send all \
404 prepared packet streams and verify that:
405 - all packets received correctly on all pg-ip4 interfaces assigned
407 - no packet received on all pg-ip4 interfaces not assigned to VRFs
409 :raise RuntimeError: If no packet captured on pg-ip4 interface assigned
410 to VRF or if any packet is captured on pg-ip4 interface not
414 # Create incoming packet streams for packet-generator interfaces
415 for pg_if in self.pg_interfaces:
416 pkts = self.create_stream(pg_if, self.pg_if_packet_sizes)
417 pg_if.add_stream(pkts)
419 # Enable packet capture and start packet sending
420 self.pg_enable_capture(self.pg_interfaces)
424 # Verify outgoing packet streams per packet-generator interface
425 for pg_if in self.pg_interfaces:
426 if pg_if in self.pg_in_vrf:
427 capture = pg_if.get_capture(remark="interface is in VRF")
428 self.verify_capture(pg_if, capture)
429 elif pg_if in self.pg_not_in_vrf:
430 pg_if.assert_nothing_captured(
431 remark="interface is not in VRF", filter_out_fn=is_ipv4_misc
433 self.logger.debug("No capture for interface %s" % pg_if.name)
435 raise Exception("Unknown interface: %s" % pg_if.name)
437 def run_crosswise_vrf_test(self):
439 Create packet streams for every pg-ip4 interface in VRF towards all
440 pg-ip4 interfaces in other VRFs, send all prepared packet streams and
443 - no packet received on all configured pg-ip4 interfaces
445 :raise RuntimeError: If any packet is captured on any pg-ip4 interface.
448 # Create incoming packet streams for packet-generator interfaces
449 for vrf_id in self.vrf_list:
450 for pg_if in self.pg_if_sets[vrf_id]:
451 pkts = self.create_stream_crosswise_vrf(
452 pg_if, vrf_id, self.pg_if_packet_sizes
454 pg_if.add_stream(pkts)
456 # Enable packet capture and start packet sending
457 self.pg_enable_capture(self.pg_interfaces)
461 # Verify outgoing packet streams per packet-generator interface
462 for pg_if in self.pg_interfaces:
463 pg_if.assert_nothing_captured(
464 remark="interface is in other VRF", filter_out_fn=is_ipv4_misc
466 self.logger.debug("No capture for interface %s" % pg_if.name)
468 def test_ip4_vrf_01(self):
469 """IP4 VRF Multi-instance test 1 - create 4 VRFs"""
472 self.create_vrf_and_assign_interfaces(4)
475 for vrf_id in self.vrf_list:
476 self.assert_equal(self.verify_vrf(vrf_id), VRFState.configured, VRFState)
479 self.run_verify_test()
480 self.run_crosswise_vrf_test()
482 def test_ip4_vrf_02(self):
483 """IP4 VRF Multi-instance test 2 - reset 2 VRFs"""
486 self.reset_vrf_and_remove_from_vrf_list(1)
487 self.reset_vrf_and_remove_from_vrf_list(2)
490 for vrf_id in self.vrf_reset_list:
491 self.assert_equal(self.verify_vrf(vrf_id), VRFState.reset, VRFState)
492 for vrf_id in self.vrf_list:
493 self.assert_equal(self.verify_vrf(vrf_id), VRFState.configured, VRFState)
496 self.run_verify_test()
497 self.run_crosswise_vrf_test()
499 def test_ip4_vrf_03(self):
500 """IP4 VRF Multi-instance 3 - add 2 VRFs"""
502 # Add 1 of reset VRFs and 1 new VRF
503 self.create_vrf_and_assign_interfaces(1)
504 self.create_vrf_and_assign_interfaces(1, start=5)
507 for vrf_id in self.vrf_reset_list:
508 self.assert_equal(self.verify_vrf(vrf_id), VRFState.reset, VRFState)
509 for vrf_id in self.vrf_list:
510 self.assert_equal(self.verify_vrf(vrf_id), VRFState.configured, VRFState)
513 self.run_verify_test()
514 self.run_crosswise_vrf_test()
516 def test_ip4_vrf_04(self):
517 """IP4 VRF Multi-instance test 4 - reset 4 VRFs"""
519 # Reset all VRFs (i.e. no VRF except VRF=0 configured)
520 for i in range(len(self.vrf_list)):
521 self.reset_vrf_and_remove_from_vrf_list(self.vrf_list[0])
524 for vrf_id in self.vrf_reset_list:
525 self.assert_equal(self.verify_vrf(vrf_id), VRFState.reset, VRFState)
526 vrf_list_length = len(self.vrf_list)
530 "List of configured VRFs is not empty: %s != 0" % vrf_list_length,
534 self.run_verify_test()
535 self.run_crosswise_vrf_test()
537 def test_ip4_vrf_05(self):
538 """IP4 VRF Multi-instance test 5 - id allocation"""
540 # Create several VRFs
541 # Set vrf_id manually first
542 self.create_vrf_by_id_and_assign_interfaces(1, 1)
543 # Set vrf_id automatically a few times
545 self.create_vrf_by_id_and_assign_interfaces(i) for i in range(2, 5)
549 self.assert_equal(self.verify_vrf(1, 1), VRFState.configured, VRFState)
550 for i, vrf in enumerate(auto_vrf_id):
552 self.verify_vrf(vrf, i + 2), VRFState.configured, VRFState
556 self.run_verify_test()
560 self.reset_vrf_and_remove_from_vrf_list(1)
561 for i, vrf in enumerate(auto_vrf_id):
562 self.reset_vrf_and_remove_from_vrf_list(vrf, i + 2)
565 self.assert_equal(self.verify_vrf(1, 1), VRFState.reset, VRFState)
566 for i, vrf in enumerate(auto_vrf_id):
567 self.assert_equal(self.verify_vrf(vrf, i + 2), VRFState.reset, VRFState)
569 vrf_list_length = len(self.vrf_list)
573 "List of configured VRFs is not empty: %s != 0" % vrf_list_length,
577 if __name__ == "__main__":
578 unittest.main(testRunner=VppTestRunner)