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
69 from scapy.packet import Raw
70 from scapy.layers.l2 import Ether
71 from scapy.layers.inet import IP, UDP, ARP
73 from framework import VppTestCase, 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(
105 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]
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_by_vrf_id = 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)]
144 cls.pg_if_by_vrf_id[vrf_id] = pg_list
147 super(TestIp4VrfMultiInst, cls).tearDownClass()
152 Clear trace and packet infos before running each test.
154 super(TestIp4VrfMultiInst, self).setUp()
155 self.reset_packet_infos()
159 Show various debug prints after each test.
161 super(TestIp4VrfMultiInst, self).tearDown()
162 if not self.vpp_dead:
163 self.logger.info(self.vapi.ppcli("show ip fib"))
164 self.logger.info(self.vapi.ppcli("show ip arp"))
166 def create_vrf_and_assign_interfaces(self, count, start=1):
168 Create required number of FIB tables / VRFs, put 3 pg-ip4 interfaces
169 to every FIB table / VRF.
171 :param int count: Number of FIB tables / VRFs to be created.
172 :param int start: Starting number of the FIB table / VRF ID. \
176 for i in range(count):
178 pg_if = self.pg_if_by_vrf_id[vrf_id][0]
179 dest_addr = pg_if.local_ip4n
181 self.vapi.ip_table_add_del(vrf_id, is_add=1)
182 self.logger.info("IPv4 VRF ID %d created" % vrf_id)
183 if vrf_id not in self.vrf_list:
184 self.vrf_list.append(vrf_id)
185 if vrf_id in self.vrf_reset_list:
186 self.vrf_reset_list.remove(vrf_id)
187 for j in range(self.pg_ifs_per_vrf):
188 pg_if = self.pg_if_by_vrf_id[vrf_id][j]
189 pg_if.set_table_ip4(vrf_id)
190 self.logger.info("pg-interface %s added to IPv4 VRF ID %d"
191 % (pg_if.name, vrf_id))
192 if pg_if not in self.pg_in_vrf:
193 self.pg_in_vrf.append(pg_if)
194 if pg_if in self.pg_not_in_vrf:
195 self.pg_not_in_vrf.remove(pg_if)
197 pg_if.configure_ipv4_neighbors()
198 self.logger.debug(self.vapi.ppcli("show ip fib"))
199 self.logger.debug(self.vapi.ppcli("show ip arp"))
201 def reset_vrf_and_remove_from_vrf_list(self, vrf_id):
203 Reset required FIB table / VRF and remove it from VRF list.
205 :param int vrf_id: The FIB table / VRF ID to be reset.
207 # self.vapi.reset_vrf(vrf_id, is_ipv6=0)
208 self.vapi.reset_fib(vrf_id, is_ipv6=0)
209 if vrf_id in self.vrf_list:
210 self.vrf_list.remove(vrf_id)
211 if vrf_id not in self.vrf_reset_list:
212 self.vrf_reset_list.append(vrf_id)
213 for j in range(self.pg_ifs_per_vrf):
214 pg_if = self.pg_if_by_vrf_id[vrf_id][j]
216 if pg_if in self.pg_in_vrf:
217 self.pg_in_vrf.remove(pg_if)
218 if pg_if not in self.pg_not_in_vrf:
219 self.pg_not_in_vrf.append(pg_if)
220 self.logger.info("IPv4 VRF ID %d reset finished" % vrf_id)
221 self.logger.debug(self.vapi.ppcli("show ip fib"))
222 self.logger.debug(self.vapi.ppcli("show ip arp"))
223 self.vapi.ip_table_add_del(vrf_id, is_add=0)
225 def create_stream(self, src_if, packet_sizes):
227 Create input packet stream for defined interface using hosts list.
229 :param object src_if: Interface to create packet stream for.
230 :param list packet_sizes: List of required packet sizes.
231 :return: Stream of packets.
234 src_hosts = src_if.remote_hosts
235 for dst_if in self.flows[src_if]:
236 for dst_host in dst_if.remote_hosts:
237 src_host = random.choice(src_hosts)
238 pkt_info = self.create_packet_info(src_if, dst_if)
239 payload = self.info_to_payload(pkt_info)
240 p = (Ether(dst=src_if.local_mac, src=src_host.mac) /
241 IP(src=src_host.ip4, dst=dst_host.ip4) /
242 UDP(sport=1234, dport=1234) /
244 pkt_info.data = p.copy()
245 size = random.choice(packet_sizes)
246 self.extend_packet(p, size)
248 self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
249 % (src_if.name, len(pkts)))
252 def create_stream_crosswise_vrf(self, src_if, vrf_id, packet_sizes):
254 Create input packet stream for negative test for leaking across
255 different VRFs for defined interface using hosts list.
257 :param object src_if: Interface to create packet stream for.
258 :param int vrf_id: The FIB table / VRF ID where src_if is assigned.
259 :param list packet_sizes: List of required packet sizes.
260 :return: Stream of packets.
263 src_hosts = src_if.remote_hosts
264 vrf_lst = list(self.vrf_list)
265 vrf_lst.remove(vrf_id)
267 for dst_if in self.pg_if_by_vrf_id[vrf]:
268 for dst_host in dst_if.remote_hosts:
269 src_host = random.choice(src_hosts)
270 pkt_info = self.create_packet_info(src_if, dst_if)
271 payload = self.info_to_payload(pkt_info)
272 p = (Ether(dst=src_if.local_mac, src=src_host.mac) /
273 IP(src=src_host.ip4, dst=dst_host.ip4) /
274 UDP(sport=1234, dport=1234) /
276 pkt_info.data = p.copy()
277 size = random.choice(packet_sizes)
278 self.extend_packet(p, size)
280 self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
281 % (src_if.name, len(pkts)))
284 def verify_capture(self, pg_if, capture):
286 Verify captured input packet stream for defined interface.
288 :param object pg_if: Interface to verify captured packet stream for.
289 :param list capture: Captured packet stream.
292 for i in self.pg_interfaces:
293 last_info[i.sw_if_index] = None
294 dst_sw_if_index = pg_if.sw_if_index
295 for packet in capture:
299 payload_info = self.payload_to_info(str(packet[Raw]))
300 packet_index = payload_info.index
301 self.assertEqual(payload_info.dst, dst_sw_if_index)
302 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
303 (pg_if.name, payload_info.src, packet_index))
304 next_info = self.get_next_packet_info_for_interface2(
305 payload_info.src, dst_sw_if_index,
306 last_info[payload_info.src])
307 last_info[payload_info.src] = next_info
308 self.assertIsNotNone(next_info)
309 self.assertEqual(packet_index, next_info.index)
310 saved_packet = next_info.data
311 # Check standard fields
312 self.assertEqual(ip.src, saved_packet[IP].src)
313 self.assertEqual(ip.dst, saved_packet[IP].dst)
314 self.assertEqual(udp.sport, saved_packet[UDP].sport)
315 self.assertEqual(udp.dport, saved_packet[UDP].dport)
317 self.logger.error(ppp("Unexpected or invalid packet:", packet))
319 for i in self.pg_interfaces:
320 remaining_packet = self.get_next_packet_info_for_interface2(
321 i, dst_sw_if_index, last_info[i.sw_if_index])
324 "Port %u: Packet expected from source %u didn't arrive" %
325 (dst_sw_if_index, i.sw_if_index))
327 def verify_vrf(self, vrf_id):
329 Check if the FIB table / VRF ID is configured.
331 :param int vrf_id: The FIB table / VRF ID to be verified.
332 :return: 1 if the FIB table / VRF ID is configured, otherwise return 0.
334 ip_fib_dump = self.vapi.ip_fib_dump()
337 for ip_fib_details in ip_fib_dump:
338 if ip_fib_details.table_id == vrf_id:
341 addr = socket.inet_ntoa(ip_fib_details.address)
343 for pg_if in self.pg_if_by_vrf_id[vrf_id]:
346 for host in pg_if.remote_hosts:
347 if str(addr) == str(host.ip4):
351 if not vrf_exist and vrf_count == 0:
352 self.logger.info("IPv4 VRF ID %d is not configured" % vrf_id)
353 return VRFState.not_configured
354 elif vrf_exist and vrf_count == 0:
355 self.logger.info("IPv4 VRF ID %d has been reset" % vrf_id)
356 return VRFState.reset
358 self.logger.info("IPv4 VRF ID %d is configured" % vrf_id)
359 return VRFState.configured
361 def run_verify_test(self):
363 Create packet streams for all configured pg interfaces, send all \
364 prepared packet streams and verify that:
365 - all packets received correctly on all pg-ip4 interfaces assigned
367 - no packet received on all pg-ip4 interfaces not assigned to VRFs
369 :raise RuntimeError: If no packet captured on pg-ip4 interface assigned
370 to VRF or if any packet is captured on pg-ip4 interface not
374 # Create incoming packet streams for packet-generator interfaces
375 for pg_if in self.pg_interfaces:
376 pkts = self.create_stream(pg_if, self.pg_if_packet_sizes)
377 pg_if.add_stream(pkts)
379 # Enable packet capture and start packet sending
380 self.pg_enable_capture(self.pg_interfaces)
384 # Verify outgoing packet streams per packet-generator interface
385 for pg_if in self.pg_interfaces:
386 if pg_if in self.pg_in_vrf:
387 capture = pg_if.get_capture(remark="interface is in VRF")
388 self.verify_capture(pg_if, capture)
389 elif pg_if in self.pg_not_in_vrf:
390 pg_if.assert_nothing_captured(remark="interface is not in VRF",
391 filter_out_fn=is_ipv4_misc)
392 self.logger.debug("No capture for interface %s" % pg_if.name)
394 raise Exception("Unknown interface: %s" % pg_if.name)
396 def run_crosswise_vrf_test(self):
398 Create packet streams for every pg-ip4 interface in VRF towards all
399 pg-ip4 interfaces in other VRFs, send all prepared packet streams and \
401 - no packet received on all configured pg-ip4 interfaces
403 :raise RuntimeError: If any packet is captured on any pg-ip4 interface.
406 # Create incoming packet streams for packet-generator interfaces
407 for vrf_id in self.vrf_list:
408 for pg_if in self.pg_if_by_vrf_id[vrf_id]:
409 pkts = self.create_stream_crosswise_vrf(
410 pg_if, vrf_id, self.pg_if_packet_sizes)
411 pg_if.add_stream(pkts)
413 # Enable packet capture and start packet sending
414 self.pg_enable_capture(self.pg_interfaces)
418 # Verify outgoing packet streams per packet-generator interface
419 for pg_if in self.pg_interfaces:
420 pg_if.assert_nothing_captured(remark="interface is in other VRF",
421 filter_out_fn=is_ipv4_misc)
422 self.logger.debug("No capture for interface %s" % pg_if.name)
424 def test_ip4_vrf_01(self):
425 """ IP4 VRF Multi-instance test 1 - create 4 VRFs
429 self.create_vrf_and_assign_interfaces(4)
432 for vrf_id in self.vrf_list:
433 self.assert_equal(self.verify_vrf(vrf_id),
434 VRFState.configured, VRFState)
437 self.run_verify_test()
438 self.run_crosswise_vrf_test()
440 def test_ip4_vrf_02(self):
441 """ IP4 VRF Multi-instance test 2 - reset 2 VRFs
445 self.reset_vrf_and_remove_from_vrf_list(1)
446 self.reset_vrf_and_remove_from_vrf_list(2)
449 for vrf_id in self.vrf_reset_list:
450 self.assert_equal(self.verify_vrf(vrf_id),
451 VRFState.reset, VRFState)
452 for vrf_id in self.vrf_list:
453 self.assert_equal(self.verify_vrf(vrf_id),
454 VRFState.configured, VRFState)
457 self.run_verify_test()
458 self.run_crosswise_vrf_test()
460 def test_ip4_vrf_03(self):
461 """ IP4 VRF Multi-instance 3 - add 2 VRFs
464 # Add 1 of reset VRFs and 1 new VRF
465 self.create_vrf_and_assign_interfaces(1)
466 self.create_vrf_and_assign_interfaces(1, start=5)
469 for vrf_id in self.vrf_reset_list:
470 self.assert_equal(self.verify_vrf(vrf_id),
471 VRFState.reset, VRFState)
472 for vrf_id in self.vrf_list:
473 self.assert_equal(self.verify_vrf(vrf_id),
474 VRFState.configured, VRFState)
477 self.run_verify_test()
478 self.run_crosswise_vrf_test()
480 def test_ip4_vrf_04(self):
481 """ IP4 VRF Multi-instance test 4 - reset 4 VRFs
484 # Reset all VRFs (i.e. no VRF except VRF=0 configured)
485 for i in range(len(self.vrf_list)):
486 self.reset_vrf_and_remove_from_vrf_list(self.vrf_list[0])
489 for vrf_id in self.vrf_reset_list:
490 self.assert_equal(self.verify_vrf(vrf_id),
491 VRFState.reset, VRFState)
492 vrf_list_length = len(self.vrf_list)
495 "List of configured VRFs is not empty: %s != 0" % vrf_list_length)
498 self.run_verify_test()
499 self.run_crosswise_vrf_test()
502 if __name__ == '__main__':
503 unittest.main(testRunner=VppTestRunner)