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