Tests Cleanup: Fix missing calls to setUpClass/tearDownClass.
[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 from vpp_papi import mac_pton
73
74 # from src/vnet/l2/l2_fib.h
75 MAC_EVENT_ACTION_ADD = 0
76 MAC_EVENT_ACTION_DELETE = 1
77 MAC_EVENT_ACTION_MOVE = 2
78
79
80 class TestL2fib(VppTestCase):
81     """ L2 FIB Test Case """
82
83     @classmethod
84     def bd_ifs(cls, bd_id):
85         return range((bd_id - 1) * cls.n_ifs_per_bd,
86                      bd_id * cls.n_ifs_per_bd - 1)
87
88     @classmethod
89     def setUpClass(cls):
90         """
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.
94
95         :var int bd_id: Bridge domain ID.
96         """
97         super(TestL2fib, cls).setUpClass()
98
99         try:
100             n_brs = cls.n_brs = range(1, 3)
101             cls.n_ifs_per_bd = 4
102             n_ifs = range(cls.n_ifs_per_bd * len(cls.n_brs))
103             # Create pg interfaces
104             cls.create_pg_interfaces(n_ifs)
105
106             cls.flows = dict()
107             for bd_id in n_brs:
108                 # Packet flows mapping pg0 -> pg1, pg2, pg3 etc.
109                 ifs = cls.bd_ifs(bd_id)
110                 for j in ifs:
111                     cls.flows[cls.pg_interfaces[j]] = [
112                         cls.pg_interfaces[x] for x in ifs if x != j]
113
114             # Packet sizes
115             cls.pg_if_packet_sizes = [64, 512, 1518, 9018]
116
117             for bd_id in n_brs:
118                 # Create BD with MAC learning and unknown unicast flooding
119                 # disabled and put interfaces to this BD
120                 cls.vapi.bridge_domain_add_del(bd_id=bd_id, uu_flood=0,
121                                                learn=0)
122                 ifs = [cls.pg_interfaces[i] for i in cls.bd_ifs(bd_id)]
123                 for pg_if in ifs:
124                     cls.vapi.sw_interface_set_l2_bridge(
125                         rx_sw_if_index=pg_if.sw_if_index, bd_id=bd_id)
126
127             # Set up all interfaces
128             for i in cls.pg_interfaces:
129                 i.admin_up()
130         except Exception:
131             super(TestL2fib, cls).tearDownClass()
132             raise
133
134     @classmethod
135     def tearDownClass(cls):
136         super(TestL2fib, cls).tearDownClass()
137
138     def setUp(self):
139         super(TestL2fib, self).setUp()
140         self.reset_packet_infos()
141
142     def tearDown(self):
143         """
144         Show various debug prints after each test.
145         """
146         super(TestL2fib, self).tearDown()
147         if not self.vpp_dead:
148             self.logger.info(self.vapi.ppcli("show l2fib verbose"))
149             for bd_id in self.n_brs:
150                 self.logger.info(self.vapi.ppcli("show bridge-domain %s detail"
151                                                  % bd_id))
152
153     def create_hosts(self, n_hosts_per_if, subnet):
154         """
155         Create required number of host MAC addresses and distribute them among
156         interfaces. Create host IPv4 address for every host MAC address.
157
158         :param int n_hosts_per_if: Number of per interface hosts to
159         create MAC/IPv4 addresses for.
160         """
161
162         hosts = dict()
163         for pg_if in self.pg_interfaces:
164             swif = pg_if.sw_if_index
165
166             def mac(j): return "00:00:%02x:ff:%02x:%02x" % (subnet, swif, j)
167
168             def ip(j): return "172.%02u.1%02x.%u" % (subnet, swif, j)
169
170             def h(j): return Host(mac(j), ip(j))
171             hosts[swif] = [h(j) for j in range(n_hosts_per_if)]
172         return hosts
173
174     def split_hosts(self, hosts, n):
175         splits = dict()
176         for pg_if in self.pg_interfaces:
177             swif = pg_if.sw_if_index
178             splits[swif] = hosts[swif][:n]
179             hosts[swif] = hosts[swif][n:]
180         return splits
181
182     def learn_hosts(self, bd_id, hosts):
183         """
184         Create and send per interface L2 MAC broadcast packet stream to
185         let the bridge domain learn these MAC addresses.
186
187         :param int bd_id: BD to teach
188         :param dict hosts: dict of hosts per interface
189         """
190         self.vapi.bridge_flags(bd_id=bd_id, is_set=1, flags=1)
191         ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
192         for pg_if in ifs:
193             swif = pg_if.sw_if_index
194             packets = [Ether(dst="ff:ff:ff:ff:ff:ff", src=host.mac)
195                        for host in hosts[swif]]
196             pg_if.add_stream(packets)
197         self.logger.info("Sending broadcast eth frames for MAC learning")
198         self.pg_start()
199
200     def config_l2_fib_entries(self, bd_id, hosts):
201         """
202         Config required number of L2 FIB entries.
203
204         :param int bd_id: BD's id
205         :param int count: Number of L2 FIB entries to be created.
206         :param int start: Starting index of the host list. (Default value = 0)
207         """
208         ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
209         for pg_if in ifs:
210             swif = pg_if.sw_if_index
211             for host in hosts[swif]:
212                 self.vapi.l2fib_add_del(
213                     mac_pton(host.mac), bd_id, swif, static_mac=1)
214
215     def delete_l2_fib_entry(self, bd_id, hosts):
216         """
217         Delete required number of L2 FIB entries.
218
219         :param int count: Number of L2 FIB entries to be created.
220         """
221         ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
222         for pg_if in ifs:
223             swif = pg_if.sw_if_index
224             for host in hosts[swif]:
225                 self.vapi.l2fib_add_del(
226                     mac_pton(host.mac), bd_id, swif, is_add=0)
227
228     def flush_int(self, swif, learned_hosts):
229         """
230         Flush swif L2 FIB entries.
231
232         :param int swif: sw if index.
233         """
234         flushed = dict()
235         self.vapi.l2fib_flush_int(swif)
236         flushed[swif] = learned_hosts[swif]
237         learned_hosts[swif] = []
238         return flushed
239
240     def flush_bd(self, bd_id, learned_hosts):
241         """
242         Flush bd_id L2 FIB entries.
243
244         :param int bd_id: Bridge Domain id.
245         """
246         self.vapi.l2fib_flush_bd(bd_id)
247         flushed = dict()
248         ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
249         for pg_if in ifs:
250             swif = pg_if.sw_if_index
251             flushed[swif] = learned_hosts[swif]
252             learned_hosts[swif] = []
253         return flushed
254
255     def flush_all(self):
256         """
257         Flush All L2 FIB entries.
258         """
259         self.vapi.l2fib_flush_all()
260
261     def create_stream(self, src_if, packet_sizes, if_src_hosts, if_dst_hosts):
262         """
263         Create input packet stream for defined interface using hosts or
264         deleted_hosts list.
265
266         :param object src_if: Interface to create packet stream for.
267         :param list packet_sizes: List of required packet sizes.
268         :param boolean deleted: Set to True if deleted_hosts list required.
269         :return: Stream of packets.
270         """
271         src_hosts = if_src_hosts[src_if.sw_if_index]
272         if not src_hosts:
273             return []
274         pkts = []
275         for dst_if in self.flows[src_if]:
276             dst_swif = dst_if.sw_if_index
277             if dst_swif not in if_dst_hosts:
278                 continue
279             dst_hosts = if_dst_hosts[dst_swif]
280             for dst_host in dst_hosts:
281                 src_host = random.choice(src_hosts)
282                 pkt_info = self.create_packet_info(src_if, dst_if)
283                 payload = self.info_to_payload(pkt_info)
284                 p = (Ether(dst=dst_host.mac, src=src_host.mac) /
285                      IP(src=src_host.ip4, dst=dst_host.ip4) /
286                      UDP(sport=1234, dport=1234) /
287                      Raw(payload))
288                 pkt_info.data = p.copy()
289                 size = random.choice(packet_sizes)
290                 self.extend_packet(p, size)
291                 pkts.append(p)
292         return pkts
293
294     def verify_capture(self, pg_if, capture):
295         """
296         Verify captured input packet stream for defined interface.
297
298         :param object pg_if: Interface to verify captured packet stream for.
299         :param list capture: Captured packet stream.
300         """
301         last_info = dict()
302         for i in self.pg_interfaces:
303             last_info[i.sw_if_index] = None
304         dst_sw_if_index = pg_if.sw_if_index
305         for packet in capture:
306             payload_info = self.payload_to_info(packet[Raw])
307             try:
308                 ip = packet[IP]
309                 udp = packet[UDP]
310                 packet_index = payload_info.index
311                 self.assertEqual(payload_info.dst, dst_sw_if_index)
312                 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
313                                   (pg_if.name, payload_info.src, packet_index))
314                 next_info = self.get_next_packet_info_for_interface2(
315                     payload_info.src, dst_sw_if_index,
316                     last_info[payload_info.src])
317                 last_info[payload_info.src] = next_info
318                 self.assertTrue(next_info is not None)
319                 self.assertEqual(packet_index, next_info.index)
320                 saved_packet = next_info.data
321                 # Check standard fields
322                 self.assertEqual(ip.src, saved_packet[IP].src)
323                 self.assertEqual(ip.dst, saved_packet[IP].dst)
324                 self.assertEqual(udp.sport, saved_packet[UDP].sport)
325                 self.assertEqual(udp.dport, saved_packet[UDP].dport)
326             except:
327                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
328                 raise
329         for i in self.pg_interfaces:
330             remaining_packet = self.get_next_packet_info_for_interface2(
331                 i, dst_sw_if_index, last_info[i.sw_if_index])
332             self.assertTrue(
333                 remaining_packet is None,
334                 "Port %u: Packet expected from source %u didn't arrive" %
335                 (dst_sw_if_index, i.sw_if_index))
336
337     def run_verify_test(self, bd_id, src_hosts, dst_hosts):
338         # Test
339         # Create incoming packet streams for packet-generator interfaces
340         self.reset_packet_infos()
341         ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
342         for i in ifs:
343             pkts = self.create_stream(
344                 i, self.pg_if_packet_sizes,
345                 if_src_hosts=src_hosts,
346                 if_dst_hosts=dst_hosts)
347             if pkts:
348                 i.add_stream(pkts)
349
350         self.vapi.bridge_flags(bd_id=bd_id, is_set=0, flags=1)
351         # Enable packet capture and start packet sending
352         self.pg_enable_capture(ifs)
353         self.pg_start()
354
355         # Verify
356         # Verify outgoing packet streams per packet-generator interface
357         for i in ifs:
358             if not dst_hosts[i.sw_if_index]:
359                 continue
360             capture = i.get_capture()
361             self.logger.info("Verifying capture on interface %s" % i.name)
362             self.verify_capture(i, capture)
363
364     def run_verify_negat_test(self, bd_id, src_hosts, dst_hosts):
365         # Test
366         # Create incoming packet streams for packet-generator interfaces for
367         # deleted MAC addresses
368         self.reset_packet_infos()
369         ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
370         for i in ifs:
371             pkts = self.create_stream(
372                 i, self.pg_if_packet_sizes,
373                 if_src_hosts=src_hosts,
374                 if_dst_hosts=dst_hosts)
375             if pkts:
376                 i.add_stream(pkts)
377
378         self.vapi.bridge_flags(bd_id=bd_id, is_set=0, flags=1)
379         # Enable packet capture and start packet sending
380         self.pg_enable_capture(ifs)
381         self.pg_start()
382
383         # Verify
384         # Verify outgoing packet streams per packet-generator interface
385         timeout = 1
386         for i in ifs:
387             i.get_capture(0, timeout=timeout)
388             i.assert_nothing_captured(remark="outgoing interface")
389             timeout = 0.1
390
391     def test_l2_fib_program100(self):
392         """ L2 FIB - program 100 MACs
393         """
394         bd_id = 1
395         hosts = self.create_hosts(100, subnet=17)
396         self.config_l2_fib_entries(bd_id, hosts)
397         self.run_verify_test(bd_id, hosts, hosts)
398
399     def test_l2_fib_program100_delete12(self):
400         """ L2 FIB - program 100, delete 12 MACs
401         """
402         bd_id = 1
403         hosts = self.create_hosts(100, subnet=17)
404         self.config_l2_fib_entries(bd_id, hosts)
405         del_hosts = self.split_hosts(hosts, 12)
406         self.delete_l2_fib_entry(bd_id, del_hosts)
407
408         self.run_verify_test(bd_id, hosts, hosts)
409         self.run_verify_negat_test(bd_id, hosts, del_hosts)
410
411     def test_l2_fib_program100_add100(self):
412         """ L2 FIB - program 100, add 100 MACs
413         """
414         bd_id = 1
415         hosts = self.create_hosts(100, subnet=17)
416         self.config_l2_fib_entries(bd_id, hosts)
417         hosts2 = self.create_hosts(100, subnet=22)
418         self.config_l2_fib_entries(bd_id, hosts2)
419         self.run_verify_test(bd_id, hosts, hosts2)
420
421     def test_l2_fib_program10_learn10(self):
422         """ L2 FIB - program 10 MACs, learn 10
423         """
424         hosts = self.create_hosts(20, subnet=35)
425         lhosts = self.split_hosts(hosts, 10)
426
427         bd1 = 1
428         bd2 = 2
429         self.learn_hosts(bd1, lhosts)
430         self.learn_hosts(bd2, lhosts)
431         self.config_l2_fib_entries(bd1, hosts)
432         self.config_l2_fib_entries(bd2, hosts)
433         self.run_verify_test(bd1, lhosts, hosts)
434         self.run_verify_test(bd2, lhosts, hosts)
435
436     def test_l2_fib_flush_int(self):
437         """ L2 FIB - flush interface
438         """
439         hosts = self.create_hosts(20, subnet=36)
440         lhosts = self.split_hosts(hosts, 10)
441
442         bd1 = 1
443         self.learn_hosts(bd1, lhosts)
444         self.config_l2_fib_entries(bd1, hosts)
445         self.run_verify_test(bd1, lhosts, hosts)
446         flushed = self.flush_int(self.pg_interfaces[0].sw_if_index, lhosts)
447         self.run_verify_test(bd1, hosts, lhosts)
448         self.run_verify_negat_test(bd1, hosts, flushed)
449
450     def test_l2_fib_flush_bd(self):
451         """ L2 FIB - flush BD
452         """
453         hosts = self.create_hosts(20, subnet=37)
454         lhosts = self.split_hosts(hosts, 10)
455
456         bd1 = 1
457         self.learn_hosts(bd1, lhosts)
458         self.config_l2_fib_entries(bd1, hosts)
459         self.run_verify_test(bd1, lhosts, hosts)
460         flushed = self.flush_bd(bd1, lhosts)
461         self.run_verify_negat_test(bd1, hosts, flushed)
462
463     def test_l2_fib_flush_all(self):
464         """ L2 FIB - flush all
465         """
466         hosts = self.create_hosts(20, subnet=38)
467         lhosts = self.split_hosts(hosts, 10)
468
469         bd1 = 1
470         bd2 = 2
471         self.learn_hosts(bd1, lhosts)
472         self.learn_hosts(bd2, lhosts)
473         self.config_l2_fib_entries(bd1, hosts)
474         self.config_l2_fib_entries(bd2, hosts)
475         self.run_verify_test(bd1, hosts, lhosts)
476         self.run_verify_test(bd2, hosts, lhosts)
477
478         self.flush_all()
479
480         self.run_verify_negat_test(bd1, hosts, lhosts)
481         self.run_verify_negat_test(bd2, hosts, lhosts)
482
483     def test_l2_fib_mac_learn_evs(self):
484         """ L2 FIB - mac learning events
485         """
486         bd1 = 1
487         hosts = self.create_hosts(10, subnet=39)
488
489         self.vapi.want_l2_macs_events()
490         self.learn_hosts(bd1, hosts)
491
492         self.sleep(1)
493         self.logger.info(self.vapi.ppcli("show l2fib"))
494         evs = self.vapi.collect_events()
495         learned_macs = {
496             e.mac[i].mac_addr for e in evs for i in range(e.n_macs)
497             if e.mac[i].action == MAC_EVENT_ACTION_ADD}
498         macs = {h.bin_mac for swif in self.bd_ifs(bd1)
499                 for h in hosts[self.pg_interfaces[swif].sw_if_index]}
500         self.vapi.want_l2_macs_events(enable_disable=0)
501         self.assertEqual(len(learned_macs ^ macs), 0)
502
503     def test_l2_fib_macs_learn_max(self):
504         """ L2 FIB - mac learning max macs in event
505         """
506         bd1 = 1
507         hosts = self.create_hosts(10, subnet=40)
508
509         ev_macs = 1
510         self.vapi.want_l2_macs_events(max_macs_in_event=ev_macs)
511         self.learn_hosts(bd1, hosts)
512
513         self.sleep(1)
514         self.logger.info(self.vapi.ppcli("show l2fib"))
515         evs = self.vapi.collect_events()
516         self.vapi.want_l2_macs_events(enable_disable=0)
517
518         self.assertGreater(len(evs), 0)
519         learned_macs = {
520             e.mac[i].mac_addr for e in evs for i in range(e.n_macs)
521             if e.mac[i].action == MAC_EVENT_ACTION_ADD}
522         macs = {h.bin_mac for swif in self.bd_ifs(bd1)
523                 for h in hosts[self.pg_interfaces[swif].sw_if_index]}
524
525         for e in evs:
526             self.assertLess(len(e), ev_macs * 10)
527         self.assertEqual(len(learned_macs ^ macs), 0)
528
529
530 if __name__ == '__main__':
531     unittest.main(testRunner=VppTestRunner)