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 same
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 same
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 same
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 same
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
72 from scapy.layers.inet import IP, UDP, ARP
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(
106 range(cls.nr_of_vrfs * cls.pg_ifs_per_vrf))
108 # Packet flows mapping pg0 -> pg1, pg2 etc.
110 for i in range(len(cls.pg_interfaces)):
111 multiplicand = i / cls.pg_ifs_per_vrf
113 cls.pg_interfaces[multiplicand * cls.pg_ifs_per_vrf + j]
114 for j in range(cls.pg_ifs_per_vrf)
115 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_by_vrf_id = 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)]
145 cls.pg_if_by_vrf_id[vrf_id] = pg_list
148 super(TestIp4VrfMultiInst, cls).tearDownClass()
153 Clear trace and packet infos before running each test.
155 super(TestIp4VrfMultiInst, self).setUp()
156 self.reset_packet_infos()
160 Show various debug prints after each test.
162 super(TestIp4VrfMultiInst, self).tearDown()
163 if not self.vpp_dead:
164 self.logger.info(self.vapi.ppcli("show ip fib"))
165 self.logger.info(self.vapi.ppcli("show ip arp"))
167 def create_vrf_and_assign_interfaces(self, count, start=1):
169 Create required number of FIB tables / VRFs, put 3 pg-ip4 interfaces
170 to every FIB table / VRF.
172 :param int count: Number of FIB tables / VRFs to be created.
173 :param int start: Starting number of the FIB table / VRF ID. \
177 for i in range(count):
179 pg_if = self.pg_if_by_vrf_id[vrf_id][0]
180 dest_addr = pg_if.local_ip4n
182 self.vapi.ip_table_add_del(is_add=1, table_id=vrf_id)
183 self.logger.info("IPv4 VRF ID %d created" % vrf_id)
184 if vrf_id not in self.vrf_list:
185 self.vrf_list.append(vrf_id)
186 if vrf_id in self.vrf_reset_list:
187 self.vrf_reset_list.remove(vrf_id)
188 for j in range(self.pg_ifs_per_vrf):
189 pg_if = self.pg_if_by_vrf_id[vrf_id][j]
190 pg_if.set_table_ip4(vrf_id)
191 self.logger.info("pg-interface %s added to IPv4 VRF ID %d"
192 % (pg_if.name, vrf_id))
193 if pg_if not in self.pg_in_vrf:
194 self.pg_in_vrf.append(pg_if)
195 if pg_if in self.pg_not_in_vrf:
196 self.pg_not_in_vrf.remove(pg_if)
198 pg_if.configure_ipv4_neighbors()
199 self.logger.debug(self.vapi.ppcli("show ip fib"))
200 self.logger.debug(self.vapi.ppcli("show ip arp"))
202 def reset_vrf_and_remove_from_vrf_list(self, vrf_id):
204 Reset required FIB table / VRF and remove it from VRF list.
206 :param int vrf_id: The FIB table / VRF ID to be reset.
208 # self.vapi.reset_vrf(vrf_id, is_ipv6=0)
209 self.vapi.reset_fib(vrf_id, is_ipv6=0)
210 if vrf_id in self.vrf_list:
211 self.vrf_list.remove(vrf_id)
212 if vrf_id not in self.vrf_reset_list:
213 self.vrf_reset_list.append(vrf_id)
214 for j in range(self.pg_ifs_per_vrf):
215 pg_if = self.pg_if_by_vrf_id[vrf_id][j]
217 if pg_if in self.pg_in_vrf:
218 self.pg_in_vrf.remove(pg_if)
219 if pg_if not in self.pg_not_in_vrf:
220 self.pg_not_in_vrf.append(pg_if)
221 self.logger.info("IPv4 VRF ID %d reset finished" % vrf_id)
222 self.logger.debug(self.vapi.ppcli("show ip fib"))
223 self.logger.debug(self.vapi.ppcli("show ip arp"))
224 self.vapi.ip_table_add_del(is_add=0, table_id=vrf_id)
226 def create_stream(self, src_if, packet_sizes):
228 Create input packet stream for defined interface using hosts list.
230 :param object src_if: Interface to create packet stream for.
231 :param list packet_sizes: List of required packet sizes.
232 :return: Stream of packets.
235 src_hosts = src_if.remote_hosts
236 for dst_if in self.flows[src_if]:
237 for dst_host in dst_if.remote_hosts:
238 src_host = random.choice(src_hosts)
239 pkt_info = self.create_packet_info(src_if, dst_if)
240 payload = self.info_to_payload(pkt_info)
241 p = (Ether(dst=src_if.local_mac, src=src_host.mac) /
242 IP(src=src_host.ip4, dst=dst_host.ip4) /
243 UDP(sport=1234, dport=1234) /
245 pkt_info.data = p.copy()
246 size = random.choice(packet_sizes)
247 self.extend_packet(p, size)
249 self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
250 % (src_if.name, len(pkts)))
253 def create_stream_crosswise_vrf(self, src_if, vrf_id, packet_sizes):
255 Create input packet stream for negative test for leaking across
256 different VRFs for defined interface using hosts list.
258 :param object src_if: Interface to create packet stream for.
259 :param int vrf_id: The FIB table / VRF ID where src_if is assigned.
260 :param list packet_sizes: List of required packet sizes.
261 :return: Stream of packets.
264 src_hosts = src_if.remote_hosts
265 vrf_lst = list(self.vrf_list)
266 vrf_lst.remove(vrf_id)
268 for dst_if in self.pg_if_by_vrf_id[vrf]:
269 for dst_host in dst_if.remote_hosts:
270 src_host = random.choice(src_hosts)
271 pkt_info = self.create_packet_info(src_if, dst_if)
272 payload = self.info_to_payload(pkt_info)
273 p = (Ether(dst=src_if.local_mac, src=src_host.mac) /
274 IP(src=src_host.ip4, dst=dst_host.ip4) /
275 UDP(sport=1234, dport=1234) /
277 pkt_info.data = p.copy()
278 size = random.choice(packet_sizes)
279 self.extend_packet(p, size)
281 self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
282 % (src_if.name, len(pkts)))
285 def verify_capture(self, pg_if, capture):
287 Verify captured input packet stream for defined interface.
289 :param object pg_if: Interface to verify captured packet stream for.
290 :param list capture: Captured packet stream.
293 for i in self.pg_interfaces:
294 last_info[i.sw_if_index] = None
295 dst_sw_if_index = pg_if.sw_if_index
296 for packet in capture:
300 payload_info = self.payload_to_info(packet[Raw])
301 packet_index = payload_info.index
302 self.assertEqual(payload_info.dst, dst_sw_if_index)
303 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
304 (pg_if.name, payload_info.src, packet_index))
305 next_info = self.get_next_packet_info_for_interface2(
306 payload_info.src, dst_sw_if_index,
307 last_info[payload_info.src])
308 last_info[payload_info.src] = next_info
309 self.assertIsNotNone(next_info)
310 self.assertEqual(packet_index, next_info.index)
311 saved_packet = next_info.data
312 # Check standard fields
313 self.assertEqual(ip.src, saved_packet[IP].src)
314 self.assertEqual(ip.dst, saved_packet[IP].dst)
315 self.assertEqual(udp.sport, saved_packet[UDP].sport)
316 self.assertEqual(udp.dport, saved_packet[UDP].dport)
318 self.logger.error(ppp("Unexpected or invalid packet:", packet))
320 for i in self.pg_interfaces:
321 remaining_packet = self.get_next_packet_info_for_interface2(
322 i, dst_sw_if_index, last_info[i.sw_if_index])
325 "Port %u: Packet expected from source %u didn't arrive" %
326 (dst_sw_if_index, i.sw_if_index))
328 def verify_vrf(self, vrf_id):
330 Check if the FIB table / VRF ID is configured.
332 :param int vrf_id: The FIB table / VRF ID to be verified.
333 :return: 1 if the FIB table / VRF ID is configured, otherwise return 0.
335 ip_fib_dump = self.vapi.ip_fib_dump()
338 for ip_fib_details in ip_fib_dump:
339 if ip_fib_details.table_id == vrf_id:
342 addr = socket.inet_ntoa(ip_fib_details.address)
344 for pg_if in self.pg_if_by_vrf_id[vrf_id]:
347 for host in pg_if.remote_hosts:
348 if scapy.compat.raw(addr) == \
349 scapy.compat.raw(host.ip4):
353 if not vrf_exist and vrf_count == 0:
354 self.logger.info("IPv4 VRF ID %d is not configured" % vrf_id)
355 return VRFState.not_configured
356 elif vrf_exist and vrf_count == 0:
357 self.logger.info("IPv4 VRF ID %d has been reset" % vrf_id)
358 return VRFState.reset
360 self.logger.info("IPv4 VRF ID %d is configured" % vrf_id)
361 return VRFState.configured
363 def run_verify_test(self):
365 Create packet streams for all configured pg interfaces, send all \
366 prepared packet streams and verify that:
367 - all packets received correctly on all pg-ip4 interfaces assigned
369 - no packet received on all pg-ip4 interfaces not assigned to VRFs
371 :raise RuntimeError: If no packet captured on pg-ip4 interface assigned
372 to VRF or if any packet is captured on pg-ip4 interface not
376 # Create incoming packet streams for packet-generator interfaces
377 for pg_if in self.pg_interfaces:
378 pkts = self.create_stream(pg_if, self.pg_if_packet_sizes)
379 pg_if.add_stream(pkts)
381 # Enable packet capture and start packet sending
382 self.pg_enable_capture(self.pg_interfaces)
386 # Verify outgoing packet streams per packet-generator interface
387 for pg_if in self.pg_interfaces:
388 if pg_if in self.pg_in_vrf:
389 capture = pg_if.get_capture(remark="interface is in VRF")
390 self.verify_capture(pg_if, capture)
391 elif pg_if in self.pg_not_in_vrf:
392 pg_if.assert_nothing_captured(remark="interface is not in VRF",
393 filter_out_fn=is_ipv4_misc)
394 self.logger.debug("No capture for interface %s" % pg_if.name)
396 raise Exception("Unknown interface: %s" % pg_if.name)
398 def run_crosswise_vrf_test(self):
400 Create packet streams for every pg-ip4 interface in VRF towards all
401 pg-ip4 interfaces in other VRFs, send all prepared packet streams and \
403 - no packet received on all configured pg-ip4 interfaces
405 :raise RuntimeError: If any packet is captured on any pg-ip4 interface.
408 # Create incoming packet streams for packet-generator interfaces
409 for vrf_id in self.vrf_list:
410 for pg_if in self.pg_if_by_vrf_id[vrf_id]:
411 pkts = self.create_stream_crosswise_vrf(
412 pg_if, vrf_id, self.pg_if_packet_sizes)
413 pg_if.add_stream(pkts)
415 # Enable packet capture and start packet sending
416 self.pg_enable_capture(self.pg_interfaces)
420 # Verify outgoing packet streams per packet-generator interface
421 for pg_if in self.pg_interfaces:
422 pg_if.assert_nothing_captured(remark="interface is in other VRF",
423 filter_out_fn=is_ipv4_misc)
424 self.logger.debug("No capture for interface %s" % pg_if.name)
426 def test_ip4_vrf_01(self):
427 """ IP4 VRF Multi-instance test 1 - create 4 VRFs
431 self.create_vrf_and_assign_interfaces(4)
434 for vrf_id in self.vrf_list:
435 self.assert_equal(self.verify_vrf(vrf_id),
436 VRFState.configured, VRFState)
439 self.run_verify_test()
440 self.run_crosswise_vrf_test()
442 def test_ip4_vrf_02(self):
443 """ IP4 VRF Multi-instance test 2 - reset 2 VRFs
447 self.reset_vrf_and_remove_from_vrf_list(1)
448 self.reset_vrf_and_remove_from_vrf_list(2)
451 for vrf_id in self.vrf_reset_list:
452 self.assert_equal(self.verify_vrf(vrf_id),
453 VRFState.reset, VRFState)
454 for vrf_id in self.vrf_list:
455 self.assert_equal(self.verify_vrf(vrf_id),
456 VRFState.configured, VRFState)
459 self.run_verify_test()
460 self.run_crosswise_vrf_test()
462 def test_ip4_vrf_03(self):
463 """ IP4 VRF Multi-instance 3 - add 2 VRFs
466 # Add 1 of reset VRFs and 1 new VRF
467 self.create_vrf_and_assign_interfaces(1)
468 self.create_vrf_and_assign_interfaces(1, start=5)
471 for vrf_id in self.vrf_reset_list:
472 self.assert_equal(self.verify_vrf(vrf_id),
473 VRFState.reset, VRFState)
474 for vrf_id in self.vrf_list:
475 self.assert_equal(self.verify_vrf(vrf_id),
476 VRFState.configured, VRFState)
479 self.run_verify_test()
480 self.run_crosswise_vrf_test()
482 def test_ip4_vrf_04(self):
483 """ IP4 VRF Multi-instance test 4 - reset 4 VRFs
486 # Reset all VRFs (i.e. no VRF except VRF=0 configured)
487 for i in range(len(self.vrf_list)):
488 self.reset_vrf_and_remove_from_vrf_list(self.vrf_list[0])
491 for vrf_id in self.vrf_reset_list:
492 self.assert_equal(self.verify_vrf(vrf_id),
493 VRFState.reset, VRFState)
494 vrf_list_length = len(self.vrf_list)
497 "List of configured VRFs is not empty: %s != 0" % vrf_list_length)
500 self.run_verify_test()
501 self.run_crosswise_vrf_test()
504 if __name__ == '__main__':
505 unittest.main(testRunner=VppTestRunner)