make test: improve handling of packet captures
[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(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         super(TestL2fib, self).setUp()
134         self.reset_packet_infos()
135
136     def tearDown(self):
137         """
138         Show various debug prints after each test.
139         """
140         super(TestL2fib, self).tearDown()
141         if not self.vpp_dead:
142             self.logger.info(self.vapi.ppcli("show l2fib verbose"))
143             self.logger.info(self.vapi.ppcli("show bridge-domain %s detail"
144                                              % self.bd_id))
145
146     def create_hosts(self, count, start=0):
147         """
148         Create required number of host MAC addresses and distribute them among
149         interfaces. Create host IPv4 address for every host MAC address.
150
151         :param int count: Number of hosts to create MAC/IPv4 addresses for.
152         :param int start: Number to start numbering from.
153         """
154         n_int = len(self.pg_interfaces)
155         macs_per_if = count / n_int
156         i = -1
157         for pg_if in self.pg_interfaces:
158             i += 1
159             start_nr = macs_per_if * i + start
160             end_nr = count + start if i == (n_int - 1) \
161                 else macs_per_if * (i + 1) + start
162             hosts = self.hosts_by_pg_idx[pg_if.sw_if_index]
163             for j in range(start_nr, end_nr):
164                 host = Host(
165                     "00:00:00:ff:%02x:%02x" % (pg_if.sw_if_index, j),
166                     "172.17.1%02x.%u" % (pg_if.sw_if_index, j))
167                 hosts.append(host)
168
169     def config_l2_fib_entries(self, count, start=0):
170         """
171         Create required number of L2 FIB entries.
172
173         :param int count: Number of L2 FIB entries to be created.
174         :param int start: Starting index of the host list. (Default value = 0)
175         """
176         n_int = len(self.pg_interfaces)
177         percent = 0
178         counter = 0.0
179         for pg_if in self.pg_interfaces:
180             end_nr = start + count / n_int
181             for j in range(start, end_nr):
182                 host = self.hosts_by_pg_idx[pg_if.sw_if_index][j]
183                 self.vapi.l2fib_add_del(host.mac, self.bd_id, pg_if.sw_if_index,
184                                         static_mac=1)
185                 counter += 1
186                 percentage = counter / count * 100
187                 if percentage > percent:
188                     self.logger.info("Configure %d L2 FIB entries .. %d%% done"
189                                      % (count, percentage))
190                     percent += 1
191         self.logger.info(self.vapi.ppcli("show l2fib"))
192
193     def delete_l2_fib_entry(self, count):
194         """
195         Delete required number of L2 FIB entries.
196
197         :param int count: Number of L2 FIB entries to be created.
198         """
199         n_int = len(self.pg_interfaces)
200         percent = 0
201         counter = 0.0
202         for pg_if in self.pg_interfaces:
203             for j in range(count / n_int):
204                 host = self.hosts_by_pg_idx[pg_if.sw_if_index][0]
205                 self.vapi.l2fib_add_del(host.mac, self.bd_id, pg_if.sw_if_index,
206                                         is_add=0)
207                 self.deleted_hosts_by_pg_idx[pg_if.sw_if_index].append(host)
208                 del self.hosts_by_pg_idx[pg_if.sw_if_index][0]
209                 counter += 1
210                 percentage = counter / count * 100
211                 if percentage > percent:
212                     self.logger.info("Delete %d L2 FIB entries .. %d%% done"
213                                      % (count, percentage))
214                     percent += 1
215         self.logger.info(self.vapi.ppcli("show l2fib"))
216
217     def create_stream(self, src_if, packet_sizes, deleted=False):
218         """
219         Create input packet stream for defined interface using hosts or
220         deleted_hosts list.
221
222         :param object src_if: Interface to create packet stream for.
223         :param list packet_sizes: List of required packet sizes.
224         :param boolean deleted: Set to True if deleted_hosts list required.
225         :return: Stream of packets.
226         """
227         pkts = []
228         src_hosts = self.hosts_by_pg_idx[src_if.sw_if_index]
229         for dst_if in self.flows[src_if]:
230             dst_hosts = self.deleted_hosts_by_pg_idx[dst_if.sw_if_index]\
231                 if deleted else self.hosts_by_pg_idx[dst_if.sw_if_index]
232             n_int = len(dst_hosts)
233             for i in range(0, n_int):
234                 dst_host = dst_hosts[i]
235                 src_host = random.choice(src_hosts)
236                 pkt_info = self.create_packet_info(src_if, dst_if)
237                 payload = self.info_to_payload(pkt_info)
238                 p = (Ether(dst=dst_host.mac, src=src_host.mac) /
239                      IP(src=src_host.ip4, dst=dst_host.ip4) /
240                      UDP(sport=1234, dport=1234) /
241                      Raw(payload))
242                 pkt_info.data = p.copy()
243                 size = random.choice(packet_sizes)
244                 self.extend_packet(p, size)
245                 pkts.append(p)
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             payload_info = self.payload_to_info(str(packet[Raw]))
261             try:
262                 ip = packet[IP]
263                 udp = packet[UDP]
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.assertTrue(next_info is not None)
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.assertTrue(
287                 remaining_packet is None,
288                 "Port %u: Packet expected from source %u didn't arrive" %
289                 (dst_sw_if_index, i.sw_if_index))
290
291     def run_verify_test(self):
292         # Test
293         # Create incoming packet streams for packet-generator interfaces
294         for i in self.pg_interfaces:
295             pkts = self.create_stream(i, self.pg_if_packet_sizes)
296             i.add_stream(pkts)
297
298         # Enable packet capture and start packet sending
299         self.pg_enable_capture(self.pg_interfaces)
300         self.pg_start()
301
302         # Verify
303         # Verify outgoing packet streams per packet-generator interface
304         for i in self.pg_interfaces:
305             capture = i.get_capture()
306             self.logger.info("Verifying capture on interface %s" % i.name)
307             self.verify_capture(i, capture)
308
309     def run_verify_negat_test(self):
310         # Test
311         # Create incoming packet streams for packet-generator interfaces for
312         # deleted MAC addresses
313         self.reset_packet_infos()
314         for i in self.pg_interfaces:
315             pkts = self.create_stream(i, self.pg_if_packet_sizes, deleted=True)
316             i.add_stream(pkts)
317
318         # Enable packet capture and start packet sending
319         self.pg_enable_capture(self.pg_interfaces)
320         self.pg_start()
321
322         # Verify
323         # Verify outgoing packet streams per packet-generator interface
324         for i in self.pg_interfaces:
325             i.assert_nothing_captured(remark="outgoing interface")
326
327     def test_l2_fib_01(self):
328         """ L2 FIB test 1 - program 100 MAC addresses
329         """
330         # Config 1
331         # Create test host entries
332         self.create_hosts(100)
333
334         # Add first 100 MAC entries to L2 FIB
335         self.config_l2_fib_entries(100)
336
337         # Test 1
338         self.run_verify_test()
339
340     def test_l2_fib_02(self):
341         """ L2 FIB test 2 - delete 12 MAC entries
342         """
343         # Config 2
344         # Delete 12 MAC entries (3 per interface) from L2 FIB
345         self.delete_l2_fib_entry(12)
346
347         # Test 2a
348         self.run_verify_test()
349
350         # Verify 2a
351         self.run_verify_negat_test()
352
353     def test_l2_fib_03(self):
354         """ L2 FIB test 3 - program new 100 MAC addresses
355         """
356         # Config 3
357         # Create new test host entries
358         self.create_hosts(100, start=100)
359
360         # Add new 100 MAC entries to L2 FIB
361         self.config_l2_fib_entries(100, start=22)
362
363         # Test 3
364         self.run_verify_test()
365
366     def test_l2_fib_04(self):
367         """ L2 FIB test 4 - delete 160 MAC entries
368         """
369         # Config 4
370         # Delete 160 MAC entries (40 per interface) from L2 FIB
371         self.delete_l2_fib_entry(160)
372
373         # Test 4a
374         self.run_verify_negat_test()
375
376
377 if __name__ == '__main__':
378     unittest.main(testRunner=VppTestRunner)