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