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