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