make test: improve documentation and PEP8 compliance
[vpp.git] / test / test_l2_fib.py
1 #!/usr/bin/env python
2 """L2 FIB Test Case HLD:
3
4 **config 1**
5     - add 4 pg-l2 interfaces
6     - configure them into l2bd
7     - configure 100 MAC entries in L2 fib - 25 MACs per interface
8     - L2 MAC learning and unknown unicast flooding disabled in l2bd
9     - configure 100 MAC entries in L2 fib - 25 MACs per interface
10
11 **test 1**
12     - send L2 MAC frames between all 4 pg-l2 interfaces for all of 100 MAC \
13     entries in the FIB
14
15 **verify 1**
16     - all packets received correctly
17
18 **config 2**
19     - delete 12 MAC entries - 3 MACs per interface
20
21 **test 2a**
22     - send L2 MAC frames between all 4 pg-l2 interfaces for non-deleted MAC \
23     entries
24
25 **verify 2a**
26     - all packets received correctly
27
28 **test 2b**
29     - send L2 MAC frames between all 4 pg-l2 interfaces for all of 12 deleted \
30     MAC entries
31
32 **verify 2b**
33     - no packet received on all 4 pg-l2 interfaces
34
35 **config 3**
36     - configure new 100 MAC entries in L2 fib - 25 MACs per interface
37
38 **test 3**
39     - send L2 MAC frames between all 4 pg-l2 interfaces for all of 188 MAC \
40     entries in the FIB
41
42 **verify 3**
43     - all packets received correctly
44
45 **config 4**
46     - delete 160 MAC entries, 40 MACs per interface
47
48 **test 4a**
49     - send L2 MAC frames between all 4 pg-l2 interfaces for all of 28 \
50     non-deleted MAC entries
51
52 **verify 4a**
53     - all packets received correctly
54
55 **test 4b**
56     - try send L2 MAC frames between all 4 pg-l2 interfaces for all of 172 \
57     deleted MAC entries
58
59 **verify 4b**
60     - no packet received on all 4 pg-l2 interfaces
61 """
62
63 import unittest
64 import random
65
66 from scapy.packet import Raw
67 from scapy.layers.l2 import Ether
68 from scapy.layers.inet import IP, UDP
69
70 from framework import VppTestCase, VppTestRunner
71 from util import Host, ppp
72
73
74 class TestL2fib(VppTestCase):
75     """ L2 FIB Test Case """
76
77     @classmethod
78     def setUpClass(cls):
79         """
80         Perform standard class setup (defined by class method setUpClass in
81         class VppTestCase) before running the test case, set test case related
82         variables and configure VPP.
83
84         :var int bd_id: Bridge domain ID.
85         :var int mac_entries_count: Number of MAC entries for bridge-domain.
86         """
87         super(TestL2fib, cls).setUpClass()
88
89         # Test variables
90         cls.bd_id = 1
91         cls.mac_entries_count = 200
92
93         try:
94             # Create 4 pg interfaces
95             cls.create_pg_interfaces(range(4))
96
97             # Packet flows mapping pg0 -> pg1, pg2, pg3 etc.
98             cls.flows = dict()
99             cls.flows[cls.pg0] = [cls.pg1, cls.pg2, cls.pg3]
100             cls.flows[cls.pg1] = [cls.pg0, cls.pg2, cls.pg3]
101             cls.flows[cls.pg2] = [cls.pg0, cls.pg1, cls.pg3]
102             cls.flows[cls.pg3] = [cls.pg0, cls.pg1, cls.pg2]
103
104             # Packet sizes
105             cls.pg_if_packet_sizes = [64, 512, 1518, 9018]
106
107             # Create BD with MAC learning and unknown unicast flooding disabled
108             # and put interfaces to this BD
109             cls.vapi.bridge_domain_add_del(
110                 bd_id=cls.bd_id, uu_flood=0, learn=0)
111             for pg_if in cls.pg_interfaces:
112                 cls.vapi.sw_interface_set_l2_bridge(pg_if.sw_if_index,
113                                                     bd_id=cls.bd_id)
114
115             # Set up all interfaces
116             for i in cls.pg_interfaces:
117                 i.admin_up()
118
119             # Mapping between packet-generator index and lists of test hosts
120             cls.hosts_by_pg_idx = dict()
121             for pg_if in cls.pg_interfaces:
122                 cls.hosts_by_pg_idx[pg_if.sw_if_index] = []
123
124             # Create list of deleted hosts
125             cls.deleted_hosts_by_pg_idx = dict()
126             for pg_if in cls.pg_interfaces:
127                 cls.deleted_hosts_by_pg_idx[pg_if.sw_if_index] = []
128
129         except Exception:
130             super(TestL2fib, cls).tearDownClass()
131             raise
132
133     def setUp(self):
134         super(TestL2fib, self).setUp()
135         self.reset_packet_infos()
136
137     def tearDown(self):
138         """
139         Show various debug prints after each test.
140         """
141         super(TestL2fib, self).tearDown()
142         if not self.vpp_dead:
143             self.logger.info(self.vapi.ppcli("show l2fib verbose"))
144             self.logger.info(self.vapi.ppcli("show bridge-domain %s detail"
145                                              % self.bd_id))
146
147     def create_hosts(self, count, start=0):
148         """
149         Create required number of host MAC addresses and distribute them among
150         interfaces. Create host IPv4 address for every host MAC address.
151
152         :param int count: Number of hosts to create MAC/IPv4 addresses for.
153         :param int start: Number to start numbering from.
154         """
155         n_int = len(self.pg_interfaces)
156         macs_per_if = count / n_int
157         i = -1
158         for pg_if in self.pg_interfaces:
159             i += 1
160             start_nr = macs_per_if * i + start
161             end_nr = count + start if i == (n_int - 1) \
162                 else macs_per_if * (i + 1) + start
163             hosts = self.hosts_by_pg_idx[pg_if.sw_if_index]
164             for j in range(start_nr, end_nr):
165                 host = Host(
166                     "00:00:00:ff:%02x:%02x" % (pg_if.sw_if_index, j),
167                     "172.17.1%02x.%u" % (pg_if.sw_if_index, j))
168                 hosts.append(host)
169
170     def config_l2_fib_entries(self, count, start=0):
171         """
172         Create required number of L2 FIB entries.
173
174         :param int count: Number of L2 FIB entries to be created.
175         :param int start: Starting index of the host list. (Default value = 0)
176         """
177         n_int = len(self.pg_interfaces)
178         percent = 0
179         counter = 0.0
180         for pg_if in self.pg_interfaces:
181             end_nr = start + count / n_int
182             for j in range(start, end_nr):
183                 host = self.hosts_by_pg_idx[pg_if.sw_if_index][j]
184                 self.vapi.l2fib_add_del(
185                     host.mac, self.bd_id, pg_if.sw_if_index, static_mac=1)
186                 counter += 1
187                 percentage = counter / count * 100
188                 if percentage > percent:
189                     self.logger.info("Configure %d L2 FIB entries .. %d%% done"
190                                      % (count, percentage))
191                     percent += 1
192         self.logger.info(self.vapi.ppcli("show l2fib"))
193
194     def delete_l2_fib_entry(self, count):
195         """
196         Delete required number of L2 FIB entries.
197
198         :param int count: Number of L2 FIB entries to be created.
199         """
200         n_int = len(self.pg_interfaces)
201         percent = 0
202         counter = 0.0
203         for pg_if in self.pg_interfaces:
204             for j in range(count / n_int):
205                 host = self.hosts_by_pg_idx[pg_if.sw_if_index][0]
206                 self.vapi.l2fib_add_del(
207                     host.mac, self.bd_id, pg_if.sw_if_index, is_add=0)
208                 self.deleted_hosts_by_pg_idx[pg_if.sw_if_index].append(host)
209                 del self.hosts_by_pg_idx[pg_if.sw_if_index][0]
210                 counter += 1
211                 percentage = counter / count * 100
212                 if percentage > percent:
213                     self.logger.info("Delete %d L2 FIB entries .. %d%% done"
214                                      % (count, percentage))
215                     percent += 1
216         self.logger.info(self.vapi.ppcli("show l2fib"))
217
218     def create_stream(self, src_if, packet_sizes, deleted=False):
219         """
220         Create input packet stream for defined interface using hosts or
221         deleted_hosts list.
222
223         :param object src_if: Interface to create packet stream for.
224         :param list packet_sizes: List of required packet sizes.
225         :param boolean deleted: Set to True if deleted_hosts list required.
226         :return: Stream of packets.
227         """
228         pkts = []
229         src_hosts = self.hosts_by_pg_idx[src_if.sw_if_index]
230         for dst_if in self.flows[src_if]:
231             dst_hosts = self.deleted_hosts_by_pg_idx[dst_if.sw_if_index]\
232                 if deleted else self.hosts_by_pg_idx[dst_if.sw_if_index]
233             n_int = len(dst_hosts)
234             for i in range(0, n_int):
235                 dst_host = dst_hosts[i]
236                 src_host = random.choice(src_hosts)
237                 pkt_info = self.create_packet_info(src_if, dst_if)
238                 payload = self.info_to_payload(pkt_info)
239                 p = (Ether(dst=dst_host.mac, src=src_host.mac) /
240                      IP(src=src_host.ip4, dst=dst_host.ip4) /
241                      UDP(sport=1234, dport=1234) /
242                      Raw(payload))
243                 pkt_info.data = p.copy()
244                 size = random.choice(packet_sizes)
245                 self.extend_packet(p, size)
246                 pkts.append(p)
247         return pkts
248
249     def verify_capture(self, pg_if, capture):
250         """
251         Verify captured input packet stream for defined interface.
252
253         :param object pg_if: Interface to verify captured packet stream for.
254         :param list capture: Captured packet stream.
255         """
256         last_info = dict()
257         for i in self.pg_interfaces:
258             last_info[i.sw_if_index] = None
259         dst_sw_if_index = pg_if.sw_if_index
260         for packet in capture:
261             payload_info = self.payload_to_info(str(packet[Raw]))
262             try:
263                 ip = packet[IP]
264                 udp = packet[UDP]
265                 packet_index = payload_info.index
266                 self.assertEqual(payload_info.dst, dst_sw_if_index)
267                 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
268                                   (pg_if.name, payload_info.src, packet_index))
269                 next_info = self.get_next_packet_info_for_interface2(
270                     payload_info.src, dst_sw_if_index,
271                     last_info[payload_info.src])
272                 last_info[payload_info.src] = next_info
273                 self.assertTrue(next_info is not None)
274                 self.assertEqual(packet_index, next_info.index)
275                 saved_packet = next_info.data
276                 # Check standard fields
277                 self.assertEqual(ip.src, saved_packet[IP].src)
278                 self.assertEqual(ip.dst, saved_packet[IP].dst)
279                 self.assertEqual(udp.sport, saved_packet[UDP].sport)
280                 self.assertEqual(udp.dport, saved_packet[UDP].dport)
281             except:
282                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
283                 raise
284         for i in self.pg_interfaces:
285             remaining_packet = self.get_next_packet_info_for_interface2(
286                 i, dst_sw_if_index, last_info[i.sw_if_index])
287             self.assertTrue(
288                 remaining_packet is None,
289                 "Port %u: Packet expected from source %u didn't arrive" %
290                 (dst_sw_if_index, i.sw_if_index))
291
292     def run_verify_test(self):
293         # Test
294         # Create incoming packet streams for packet-generator interfaces
295         for i in self.pg_interfaces:
296             pkts = self.create_stream(i, self.pg_if_packet_sizes)
297             i.add_stream(pkts)
298
299         # Enable packet capture and start packet sending
300         self.pg_enable_capture(self.pg_interfaces)
301         self.pg_start()
302
303         # Verify
304         # Verify outgoing packet streams per packet-generator interface
305         for i in self.pg_interfaces:
306             capture = i.get_capture()
307             self.logger.info("Verifying capture on interface %s" % i.name)
308             self.verify_capture(i, capture)
309
310     def run_verify_negat_test(self):
311         # Test
312         # Create incoming packet streams for packet-generator interfaces for
313         # deleted MAC addresses
314         self.reset_packet_infos()
315         for i in self.pg_interfaces:
316             pkts = self.create_stream(i, self.pg_if_packet_sizes, deleted=True)
317             i.add_stream(pkts)
318
319         # Enable packet capture and start packet sending
320         self.pg_enable_capture(self.pg_interfaces)
321         self.pg_start()
322
323         # Verify
324         # Verify outgoing packet streams per packet-generator interface
325         for i in self.pg_interfaces:
326             i.assert_nothing_captured(remark="outgoing interface")
327
328     def test_l2_fib_01(self):
329         """ L2 FIB test 1 - program 100 MAC addresses
330         """
331         # Config 1
332         # Create test host entries
333         self.create_hosts(100)
334
335         # Add first 100 MAC entries to L2 FIB
336         self.config_l2_fib_entries(100)
337
338         # Test 1
339         self.run_verify_test()
340
341     def test_l2_fib_02(self):
342         """ L2 FIB test 2 - delete 12 MAC entries
343         """
344         # Config 2
345         # Delete 12 MAC entries (3 per interface) from L2 FIB
346         self.delete_l2_fib_entry(12)
347
348         # Test 2a
349         self.run_verify_test()
350
351         # Verify 2a
352         self.run_verify_negat_test()
353
354     def test_l2_fib_03(self):
355         """ L2 FIB test 3 - program new 100 MAC addresses
356         """
357         # Config 3
358         # Create new test host entries
359         self.create_hosts(100, start=100)
360
361         # Add new 100 MAC entries to L2 FIB
362         self.config_l2_fib_entries(100, start=22)
363
364         # Test 3
365         self.run_verify_test()
366
367     def test_l2_fib_04(self):
368         """ L2 FIB test 4 - delete 160 MAC entries
369         """
370         # Config 4
371         # Delete 160 MAC entries (40 per interface) from L2 FIB
372         self.delete_l2_fib_entry(160)
373
374         # Test 4a
375         self.run_verify_negat_test()
376
377
378 if __name__ == '__main__':
379     unittest.main(testRunner=VppTestRunner)