CSIT-473: L2 FIB tests
[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
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(bd_id=cls.bd_id, uu_flood=0, learn=0)
110             for pg_if in cls.pg_interfaces:
111                 cls.vapi.sw_interface_set_l2_bridge(pg_if.sw_if_index,
112                                                     bd_id=cls.bd_id)
113
114             # Set up all interfaces
115             for i in cls.pg_interfaces:
116                 i.admin_up()
117
118             # Mapping between packet-generator index and lists of test hosts
119             cls.hosts_by_pg_idx = dict()
120             for pg_if in cls.pg_interfaces:
121                 cls.hosts_by_pg_idx[pg_if.sw_if_index] = []
122
123             # Create list of deleted hosts
124             cls.deleted_hosts_by_pg_idx = dict()
125             for pg_if in cls.pg_interfaces:
126                 cls.deleted_hosts_by_pg_idx[pg_if.sw_if_index] = []
127
128         except Exception:
129             super(TestL2fib, cls).tearDownClass()
130             raise
131
132     def setUp(self):
133         """
134         Clear trace and packet infos before running each test.
135         """
136         super(TestL2fib, self).setUp()
137         self.packet_infos = {}
138
139     def tearDown(self):
140         """
141         Show various debug prints after each test.
142         """
143         super(TestL2fib, self).tearDown()
144         if not self.vpp_dead:
145             self.logger.info(self.vapi.ppcli("show l2fib verbose"))
146             self.logger.info(self.vapi.ppcli("show bridge-domain %s detail"
147                                              % self.bd_id))
148
149     def create_hosts(self, count, start=0):
150         """
151         Create required number of host MAC addresses and distribute them among
152         interfaces. Create host IPv4 address for every host MAC address.
153
154         :param int count: Number of hosts to create MAC/IPv4 addresses for.
155         :param int start: Number to start numbering from.
156         """
157         n_int = len(self.pg_interfaces)
158         macs_per_if = count / n_int
159         i = -1
160         for pg_if in self.pg_interfaces:
161             i += 1
162             start_nr = macs_per_if * i + start
163             end_nr = count + start if i == (n_int - 1) \
164                 else macs_per_if * (i + 1) + start
165             hosts = self.hosts_by_pg_idx[pg_if.sw_if_index]
166             for j in range(start_nr, end_nr):
167                 host = Host(
168                     "00:00:00:ff:%02x:%02x" % (pg_if.sw_if_index, j),
169                     "172.17.1%02x.%u" % (pg_if.sw_if_index, j))
170                 hosts.append(host)
171
172     def config_l2_fib_entries(self, count, start=0):
173         """
174         Create required number of L2 FIB entries.
175
176         :param int count: Number of L2 FIB entries to be created.
177         :param int start: Starting index of the host list. (Default value = 0)
178         """
179         n_int = len(self.pg_interfaces)
180         percent = 0
181         counter = 0.0
182         for pg_if in self.pg_interfaces:
183             end_nr = start + count / n_int
184             for j in range(start, end_nr):
185                 host = self.hosts_by_pg_idx[pg_if.sw_if_index][j]
186                 self.vapi.l2fib_add_del(host.mac, self.bd_id, pg_if.sw_if_index,
187                                         static_mac=1)
188                 counter += 1
189                 percentage = counter / count * 100
190                 if percentage > percent:
191                     self.logger.info("Configure %d L2 FIB entries .. %d%% done"
192                                      % (count, percentage))
193                     percent += 1
194         self.logger.info(self.vapi.ppcli("show l2fib"))
195
196     def delete_l2_fib_entry(self, count):
197         """
198         Delete required number of L2 FIB entries.
199
200         :param int count: Number of L2 FIB entries to be created.
201         """
202         n_int = len(self.pg_interfaces)
203         percent = 0
204         counter = 0.0
205         for pg_if in self.pg_interfaces:
206             for j in range(count / n_int):
207                 host = self.hosts_by_pg_idx[pg_if.sw_if_index][0]
208                 self.vapi.l2fib_add_del(host.mac, self.bd_id, pg_if.sw_if_index,
209                                         is_add=0)
210                 self.deleted_hosts_by_pg_idx[pg_if.sw_if_index].append(host)
211                 del self.hosts_by_pg_idx[pg_if.sw_if_index][0]
212                 counter += 1
213                 percentage = counter / count * 100
214                 if percentage > percent:
215                     self.logger.info("Delete %d L2 FIB entries .. %d%% done"
216                                      % (count, percentage))
217                     percent += 1
218         self.logger.info(self.vapi.ppcli("show l2fib"))
219
220     def create_stream(self, src_if, packet_sizes, deleted=False):
221         """
222         Create input packet stream for defined interface using hosts or
223         deleted_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         :param boolean deleted: Set to True if deleted_hosts list required.
228         :return: Stream of packets.
229         """
230         pkts = []
231         src_hosts = self.hosts_by_pg_idx[src_if.sw_if_index]
232         for dst_if in self.flows[src_if]:
233             dst_hosts = self.deleted_hosts_by_pg_idx[dst_if.sw_if_index]\
234                 if deleted else self.hosts_by_pg_idx[dst_if.sw_if_index]
235             n_int = len(dst_hosts)
236             for i in range(0, n_int):
237                 dst_host = dst_hosts[i]
238                 src_host = random.choice(src_hosts)
239                 pkt_info = self.create_packet_info(
240                     src_if.sw_if_index, dst_if.sw_if_index)
241                 payload = self.info_to_payload(pkt_info)
242                 p = (Ether(dst=dst_host.mac, src=src_host.mac) /
243                      IP(src=src_host.ip4, dst=dst_host.ip4) /
244                      UDP(sport=1234, dport=1234) /
245                      Raw(payload))
246                 pkt_info.data = p.copy()
247                 size = random.choice(packet_sizes)
248                 self.extend_packet(p, size)
249                 pkts.append(p)
250         return pkts
251
252     def verify_capture(self, pg_if, capture):
253         """
254         Verify captured input packet stream for defined interface.
255
256         :param object pg_if: Interface to verify captured packet stream for.
257         :param list capture: Captured packet stream.
258         """
259         last_info = dict()
260         for i in self.pg_interfaces:
261             last_info[i.sw_if_index] = None
262         dst_sw_if_index = pg_if.sw_if_index
263         for packet in capture:
264             payload_info = self.payload_to_info(str(packet[Raw]))
265             try:
266                 ip = packet[IP]
267                 udp = packet[UDP]
268                 packet_index = payload_info.index
269                 self.assertEqual(payload_info.dst, dst_sw_if_index)
270                 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
271                                   (pg_if.name, payload_info.src, packet_index))
272                 next_info = self.get_next_packet_info_for_interface2(
273                     payload_info.src, dst_sw_if_index,
274                     last_info[payload_info.src])
275                 last_info[payload_info.src] = next_info
276                 self.assertTrue(next_info is not None)
277                 self.assertEqual(packet_index, next_info.index)
278                 saved_packet = next_info.data
279                 # Check standard fields
280                 self.assertEqual(ip.src, saved_packet[IP].src)
281                 self.assertEqual(ip.dst, saved_packet[IP].dst)
282                 self.assertEqual(udp.sport, saved_packet[UDP].sport)
283                 self.assertEqual(udp.dport, saved_packet[UDP].dport)
284             except:
285                 self.logger.error("Unexpected or invalid packet:")
286                 self.logger.error(packet.show())
287                 raise
288         for i in self.pg_interfaces:
289             remaining_packet = self.get_next_packet_info_for_interface2(
290                 i, dst_sw_if_index, last_info[i.sw_if_index])
291             self.assertTrue(
292                 remaining_packet is None,
293                 "Port %u: Packet expected from source %u didn't arrive" %
294                 (dst_sw_if_index, i.sw_if_index))
295
296     def run_verify_test(self):
297         # Test
298         # Create incoming packet streams for packet-generator interfaces
299         for i in self.pg_interfaces:
300             pkts = self.create_stream(i, self.pg_if_packet_sizes)
301             i.add_stream(pkts)
302
303         # Enable packet capture and start packet sending
304         self.pg_enable_capture(self.pg_interfaces)
305         self.pg_start()
306
307         # Verify
308         # Verify outgoing packet streams per packet-generator interface
309         for i in self.pg_interfaces:
310             capture = i.get_capture()
311             self.logger.info("Verifying capture on interface %s" % i.name)
312             self.verify_capture(i, capture)
313
314     def run_verify_negat_test(self):
315         # Test
316         # Create incoming packet streams for packet-generator interfaces for
317         # deleted MAC addresses
318         self.packet_infos = {}
319         for i in self.pg_interfaces:
320             pkts = self.create_stream(i, self.pg_if_packet_sizes, deleted=True)
321             i.add_stream(pkts)
322
323         # Enable packet capture and start packet sending
324         self.pg_enable_capture(self.pg_interfaces)
325         self.pg_start()
326
327         # Verify
328         # Verify outgoing packet streams per packet-generator interface
329         for i in self.pg_interfaces:
330             capture = i.get_capture()
331             self.logger.info("Verifying capture on interface %s" % i.name)
332             try:
333                 self.assertEqual(len(capture), 0)
334             except AssertionError:
335                 self.logger.error("The capture on interface %s is not empty!"
336                                   % i.name)
337                 raise AssertionError("%d != 0" % len(capture))
338
339     def test_l2_fib_01(self):
340         """ L2 FIB test 1 - program 100 MAC addresses
341         """
342         # Config 1
343         # Create test host entries
344         self.create_hosts(100)
345
346         # Add first 100 MAC entries to L2 FIB
347         self.config_l2_fib_entries(100)
348
349         # Test 1
350         self.run_verify_test()
351
352     def test_l2_fib_02(self):
353         """ L2 FIB test 2 - delete 12 MAC entries
354         """
355         # Config 2
356         # Delete 12 MAC entries (3 per interface) from L2 FIB
357         self.delete_l2_fib_entry(12)
358
359         # Test 2a
360         self.run_verify_test()
361
362         # Verify 2a
363         self.run_verify_negat_test()
364
365     def test_l2_fib_03(self):
366         """ L2 FIB test 3 - program new 100 MAC addresses
367         """
368         # Config 3
369         # Create new test host entries
370         self.create_hosts(100, start=100)
371
372         # Add new 100 MAC entries to L2 FIB
373         self.config_l2_fib_entries(100, start=22)
374
375         # Test 3
376         self.run_verify_test()
377
378     def test_l2_fib_04(self):
379         """ L2 FIB test 4 - delete 160 MAC entries
380         """
381         # Config 4
382         # Delete 160 MAC entries (40 per interface) from L2 FIB
383         self.delete_l2_fib_entry(160)
384
385         # Test 4a
386         self.run_verify_negat_test()
387
388
389 if __name__ == '__main__':
390     unittest.main(testRunner=VppTestRunner)