Gratuitous ARP packet handling
[vpp.git] / test / test_ip4_vrf_multi_instance.py
1 #!/usr/bin/env python
2 """IP4 VRF Multi-instance Test Case HLD:
3
4 **NOTES:**
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 \
8     problems too
9
10 **config 1**
11     - add 15 pg-ip4 interfaces
12     - configure 5 hosts per pg-ip4 interface
13     - configure 4 VRFs
14     - add 3 pg-ip4 interfaces per VRF
15
16 **test 1**
17     - send IP4 packets between all pg-ip4 interfaces in all VRF groups
18
19 **verify 1**
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 VRF
22     - no packet received in case of pg-ip4 interfaces not in VRF
23
24 **config 2**
25     - reset 2 VRFs
26
27 **test 2**
28     - send IP4 packets between all pg-ip4 interfaces in all VRF groups
29
30 **verify 2**
31     - check VRF data by parsing output of ip_fib_dump API command
32     - all packets received correctly in case of pg-ip4 interfaces in VRF
33     - no packet received in case of pg-ip4 interfaces not in VRF
34
35 **config 3**
36     - add 1 of reset VRFs and 1 new VRF
37
38 **test 3**
39     - send IP4 packets between all pg-ip4 interfaces in all VRF groups
40
41 **verify 3**
42     - check VRF data by parsing output of ip_fib_dump API command
43     - all packets received correctly in case of pg-ip4 interfaces in VRF
44     - no packet received in case of pg-ip4 interfaces not in VRF
45
46 **config 4**
47     - reset all created VRFs
48
49 **test 4**
50     - send IP4 packets between all pg-ip4 interfaces in all VRF groups
51
52 **verify 4**
53     - check VRF data by parsing output of ip_fib_dump API command
54     - all packets received correctly in case of pg-ip4 interfaces in VRF
55     - no packet received in case of pg-ip4 interfaces not in VRF
56 """
57
58 import unittest
59 import random
60 import socket
61
62 from scapy.packet import Raw
63 from scapy.layers.l2 import Ether
64 from scapy.layers.inet import IP, UDP, ARP
65
66 from framework import VppTestCase, VppTestRunner
67 from util import ppp
68 from vrf import VRFState
69
70
71 def is_ipv4_misc(p):
72     """ Is packet one of uninteresting IPv4 broadcasts? """
73     if p.haslayer(ARP):
74         return True
75     return False
76
77
78 class TestIp4VrfMultiInst(VppTestCase):
79     """ IP4 VRF  Multi-instance Test Case """
80
81     @classmethod
82     def setUpClass(cls):
83         """
84         Perform standard class setup (defined by class method setUpClass in
85         class VppTestCase) before running the test case, set test case related
86         variables and configure VPP.
87         """
88         super(TestIp4VrfMultiInst, cls).setUpClass()
89
90         # Test variables
91         cls.hosts_per_pg = 5
92         cls.nr_of_vrfs = 5
93         cls.pg_ifs_per_vrf = 3
94
95         try:
96             # Create pg interfaces
97             cls.create_pg_interfaces(
98                 range(cls.nr_of_vrfs * cls.pg_ifs_per_vrf))
99
100             # Packet flows mapping pg0 -> pg1, pg2 etc.
101             cls.flows = dict()
102             for i in range(len(cls.pg_interfaces)):
103                 multiplicand = i / cls.pg_ifs_per_vrf
104                 pg_list = [
105                     cls.pg_interfaces[multiplicand * cls.pg_ifs_per_vrf + j]
106                     for j in range(cls.pg_ifs_per_vrf)
107                     if (multiplicand * cls.pg_ifs_per_vrf + j) != i]
108                 cls.flows[cls.pg_interfaces[i]] = pg_list
109
110             # Packet sizes - jumbo packet (9018 bytes) skipped
111             cls.pg_if_packet_sizes = [64, 512, 1518]
112
113             # Set up all interfaces
114             for pg_if in cls.pg_interfaces:
115                 pg_if.admin_up()
116                 pg_if.generate_remote_hosts(cls.hosts_per_pg)
117
118             # Create list of VRFs
119             cls.vrf_list = list()
120
121             # Create list of reset VRFs
122             cls.vrf_reset_list = list()
123
124             # Create list of pg_interfaces in VRFs
125             cls.pg_in_vrf = list()
126
127             # Create list of pg_interfaces not in BDs
128             cls.pg_not_in_vrf = [pg_if for pg_if in cls.pg_interfaces]
129
130             # Create mapping of pg_interfaces to VRF IDs
131             cls.pg_if_by_vrf_id = dict()
132             for i in range(cls.nr_of_vrfs):
133                 vrf_id = i + 1
134                 pg_list = [
135                     cls.pg_interfaces[i * cls.pg_ifs_per_vrf + j]
136                     for j in range(cls.pg_ifs_per_vrf)]
137                 cls.pg_if_by_vrf_id[vrf_id] = pg_list
138
139         except Exception:
140             super(TestIp4VrfMultiInst, cls).tearDownClass()
141             raise
142
143     def setUp(self):
144         """
145         Clear trace and packet infos before running each test.
146         """
147         super(TestIp4VrfMultiInst, self).setUp()
148         self.reset_packet_infos()
149
150     def tearDown(self):
151         """
152         Show various debug prints after each test.
153         """
154         super(TestIp4VrfMultiInst, self).tearDown()
155         if not self.vpp_dead:
156             self.logger.info(self.vapi.ppcli("show ip fib"))
157             self.logger.info(self.vapi.ppcli("show ip arp"))
158
159     def create_vrf_and_assign_interfaces(self, count, start=1):
160         """
161         Create required number of FIB tables / VRFs, put 3 l2-pg interfaces
162         to every FIB table / VRF.
163
164         :param int count: Number of FIB tables / VRFs to be created.
165         :param int start: Starting number of the FIB table / VRF ID. \
166         (Default value = 1)
167         """
168
169         for i in range(count):
170             vrf_id = i + start
171             pg_if = self.pg_if_by_vrf_id[vrf_id][0]
172             dest_addr = pg_if.local_ip4n
173             dest_addr_len = 24
174             self.vapi.ip_table_add_del(vrf_id, is_add=1)
175             self.vapi.ip_add_del_route(
176                 dest_addr, dest_addr_len, pg_if.local_ip4n,
177                 table_id=vrf_id, is_multipath=1)
178             self.logger.info("IPv4 VRF ID %d created" % vrf_id)
179             if vrf_id not in self.vrf_list:
180                 self.vrf_list.append(vrf_id)
181             if vrf_id in self.vrf_reset_list:
182                 self.vrf_reset_list.remove(vrf_id)
183             for j in range(self.pg_ifs_per_vrf):
184                 pg_if = self.pg_if_by_vrf_id[vrf_id][j]
185                 pg_if.set_table_ip4(vrf_id)
186                 self.logger.info("pg-interface %s added to IPv4 VRF ID %d"
187                                  % (pg_if.name, vrf_id))
188                 if pg_if not in self.pg_in_vrf:
189                     self.pg_in_vrf.append(pg_if)
190                 if pg_if in self.pg_not_in_vrf:
191                     self.pg_not_in_vrf.remove(pg_if)
192                 pg_if.config_ip4()
193                 pg_if.configure_ipv4_neighbors()
194         self.logger.debug(self.vapi.ppcli("show ip fib"))
195         self.logger.debug(self.vapi.ppcli("show ip arp"))
196
197     def reset_vrf_and_remove_from_vrf_list(self, vrf_id):
198         """
199         Reset required FIB table / VRF and remove it from VRF list.
200
201         :param int vrf_id: The FIB table / VRF ID to be reset.
202         """
203         # self.vapi.reset_vrf(vrf_id, is_ipv6=0)
204         self.vapi.reset_fib(vrf_id, is_ipv6=0)
205         if vrf_id in self.vrf_list:
206             self.vrf_list.remove(vrf_id)
207         if vrf_id not in self.vrf_reset_list:
208             self.vrf_reset_list.append(vrf_id)
209         for j in range(self.pg_ifs_per_vrf):
210             pg_if = self.pg_if_by_vrf_id[vrf_id][j]
211             pg_if.unconfig_ip4()
212             if pg_if in self.pg_in_vrf:
213                 self.pg_in_vrf.remove(pg_if)
214             if pg_if not in self.pg_not_in_vrf:
215                 self.pg_not_in_vrf.append(pg_if)
216         self.logger.info("IPv4 VRF ID %d reset finished" % vrf_id)
217         self.logger.debug(self.vapi.ppcli("show ip fib"))
218         self.logger.debug(self.vapi.ppcli("show ip arp"))
219         self.vapi.ip_table_add_del(vrf_id, is_add=0)
220
221     def create_stream(self, src_if, packet_sizes):
222         """
223         Create input packet stream for defined interface using hosts list.
224
225         :param object src_if: Interface to create packet stream for.
226         :param list packet_sizes: List of required packet sizes.
227         :return: Stream of packets.
228         """
229         pkts = []
230         src_hosts = src_if.remote_hosts
231         for dst_if in self.flows[src_if]:
232             for dst_host in dst_if.remote_hosts:
233                 src_host = random.choice(src_hosts)
234                 pkt_info = self.create_packet_info(src_if, dst_if)
235                 payload = self.info_to_payload(pkt_info)
236                 p = (Ether(dst=src_if.local_mac, src=src_host.mac) /
237                      IP(src=src_host.ip4, dst=dst_host.ip4) /
238                      UDP(sport=1234, dport=1234) /
239                      Raw(payload))
240                 pkt_info.data = p.copy()
241                 size = random.choice(packet_sizes)
242                 self.extend_packet(p, size)
243                 pkts.append(p)
244         self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
245                           % (src_if.name, len(pkts)))
246         return pkts
247
248     def verify_capture(self, pg_if, capture):
249         """
250         Verify captured input packet stream for defined interface.
251
252         :param object pg_if: Interface to verify captured packet stream for.
253         :param list capture: Captured packet stream.
254         """
255         last_info = dict()
256         for i in self.pg_interfaces:
257             last_info[i.sw_if_index] = None
258         dst_sw_if_index = pg_if.sw_if_index
259         for packet in capture:
260             try:
261                 ip = packet[IP]
262                 udp = packet[UDP]
263                 payload_info = self.payload_to_info(str(packet[Raw]))
264                 packet_index = payload_info.index
265                 self.assertEqual(payload_info.dst, dst_sw_if_index)
266                 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
267                                   (pg_if.name, payload_info.src, packet_index))
268                 next_info = self.get_next_packet_info_for_interface2(
269                     payload_info.src, dst_sw_if_index,
270                     last_info[payload_info.src])
271                 last_info[payload_info.src] = next_info
272                 self.assertIsNotNone(next_info)
273                 self.assertEqual(packet_index, next_info.index)
274                 saved_packet = next_info.data
275                 # Check standard fields
276                 self.assertEqual(ip.src, saved_packet[IP].src)
277                 self.assertEqual(ip.dst, saved_packet[IP].dst)
278                 self.assertEqual(udp.sport, saved_packet[UDP].sport)
279                 self.assertEqual(udp.dport, saved_packet[UDP].dport)
280             except:
281                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
282                 raise
283         for i in self.pg_interfaces:
284             remaining_packet = self.get_next_packet_info_for_interface2(
285                 i, dst_sw_if_index, last_info[i.sw_if_index])
286             self.assertIsNone(
287                 remaining_packet,
288                 "Port %u: Packet expected from source %u didn't arrive" %
289                 (dst_sw_if_index, i.sw_if_index))
290
291     def verify_vrf(self, vrf_id):
292         """
293         Check if the FIB table / VRF ID is configured.
294
295         :param int vrf_id: The FIB table / VRF ID to be verified.
296         :return: 1 if the FIB table / VRF ID is configured, otherwise return 0.
297         """
298         ip_fib_dump = self.vapi.ip_fib_dump()
299         vrf_exist = False
300         vrf_count = 0
301         for ip_fib_details in ip_fib_dump:
302             if ip_fib_details.table_id == vrf_id:
303                 if not vrf_exist:
304                     vrf_exist = True
305                 addr = socket.inet_ntoa(ip_fib_details.address)
306                 found = False
307                 for pg_if in self.pg_if_by_vrf_id[vrf_id]:
308                     if found:
309                         break
310                     for host in pg_if.remote_hosts:
311                         if str(addr) == str(host.ip4):
312                             vrf_count += 1
313                             found = True
314                             break
315         if not vrf_exist and vrf_count == 0:
316             self.logger.info("IPv4 VRF ID %d is not configured" % vrf_id)
317             return VRFState.not_configured
318         elif vrf_exist and vrf_count == 0:
319             self.logger.info("IPv4 VRF ID %d has been reset" % vrf_id)
320             return VRFState.reset
321         else:
322             self.logger.info("IPv4 VRF ID %d is configured" % vrf_id)
323             return VRFState.configured
324
325     def run_verify_test(self):
326         """
327         Create packet streams for all configured l2-pg interfaces, send all \
328         prepared packet streams and verify that:
329             - all packets received correctly on all pg-l2 interfaces assigned
330               to bridge domains
331             - no packet received on all pg-l2 interfaces not assigned to bridge
332               domains
333
334         :raise RuntimeError: If no packet captured on l2-pg interface assigned
335             to the bridge domain or if any packet is captured on l2-pg
336             interface not assigned to the bridge domain.
337         """
338         # Test
339         # Create incoming packet streams for packet-generator interfaces
340         for pg_if in self.pg_interfaces:
341             pkts = self.create_stream(pg_if, self.pg_if_packet_sizes)
342             pg_if.add_stream(pkts)
343
344         # Enable packet capture and start packet sending
345         self.pg_enable_capture(self.pg_interfaces)
346         self.pg_start()
347
348         # Verify
349         # Verify outgoing packet streams per packet-generator interface
350         for pg_if in self.pg_interfaces:
351             if pg_if in self.pg_in_vrf:
352                 capture = pg_if.get_capture(remark="interface is in VRF")
353                 self.verify_capture(pg_if, capture)
354             elif pg_if in self.pg_not_in_vrf:
355                 pg_if.assert_nothing_captured(remark="interface is not in VRF",
356                                               filter_out_fn=is_ipv4_misc)
357                 self.logger.debug("No capture for interface %s" % pg_if.name)
358             else:
359                 raise Exception("Unknown interface: %s" % pg_if.name)
360
361     def test_ip4_vrf_01(self):
362         """ IP4 VRF  Multi-instance test 1 - create 4 VRFs
363         """
364         # Config 1
365         # Create 4 VRFs
366         self.create_vrf_and_assign_interfaces(4)
367
368         # Verify 1
369         for vrf_id in self.vrf_list:
370             self.assert_equal(self.verify_vrf(vrf_id),
371                               VRFState.configured, VRFState)
372
373         # Test 1
374         self.run_verify_test()
375
376     def test_ip4_vrf_02(self):
377         """ IP4 VRF  Multi-instance test 2 - reset 2 VRFs
378         """
379         # Config 2
380         # Reset 2 VRFs
381         self.reset_vrf_and_remove_from_vrf_list(1)
382         self.reset_vrf_and_remove_from_vrf_list(2)
383
384         # Verify 2
385         for vrf_id in self.vrf_reset_list:
386             self.assert_equal(self.verify_vrf(vrf_id),
387                               VRFState.reset, VRFState)
388         for vrf_id in self.vrf_list:
389             self.assert_equal(self.verify_vrf(vrf_id),
390                               VRFState.configured, VRFState)
391
392         # Test 2
393         self.run_verify_test()
394
395     def test_ip4_vrf_03(self):
396         """ IP4 VRF  Multi-instance 3 - add 2 VRFs
397         """
398         # Config 3
399         # Add 1 of reset VRFs and 1 new VRF
400         self.create_vrf_and_assign_interfaces(1)
401         self.create_vrf_and_assign_interfaces(1, start=5)
402
403         # Verify 3
404         for vrf_id in self.vrf_reset_list:
405             self.assert_equal(self.verify_vrf(vrf_id),
406                               VRFState.reset, VRFState)
407         for vrf_id in self.vrf_list:
408             self.assert_equal(self.verify_vrf(vrf_id),
409                               VRFState.configured, VRFState)
410
411         # Test 3
412         self.run_verify_test()
413
414     def test_ip4_vrf_04(self):
415         """ IP4 VRF  Multi-instance test 4 - reset 4 VRFs
416         """
417         # Config 4
418         # Reset all VRFs (i.e. no VRF except VRF=0 configured)
419         for i in range(len(self.vrf_list)):
420             self.reset_vrf_and_remove_from_vrf_list(self.vrf_list[0])
421
422         # Verify 4
423         for vrf_id in self.vrf_reset_list:
424             self.assert_equal(self.verify_vrf(vrf_id),
425                               VRFState.reset, VRFState)
426         vrf_list_length = len(self.vrf_list)
427         self.assertEqual(
428             vrf_list_length, 0,
429             "List of configured VRFs is not empty: %s != 0" % vrf_list_length)
430
431         # Test 4
432         self.run_verify_test()
433
434
435 if __name__ == '__main__':
436     unittest.main(testRunner=VppTestRunner)