blob: 67c208837c84cd9875d4d99811c9708fbcd0d792 [file] [log] [blame]
Jan00dad122016-11-29 10:04:53 +01001#!/usr/bin/env python
2"""L2BD Multi-instance Test Case HLD:
3
4**NOTES:**
5 - higher number of pg-l2 interfaces causes problems => only 15 pg-l2 \
6 interfaces in 5 bridge domains are tested
Jan65209ed2016-12-05 23:29:17 +01007 - jumbo packets in configuration with 14 l2-pg interfaces leads to \
8 problems too
Jan00dad122016-11-29 10:04:53 +01009
10**config 1**
11 - add 15 pg-l2 interfaces
12 - configure one host per pg-l2 interface
13 - configure 5 bridge domains (BD)
14 - add 3 pg-l2 interfaces per BD
15
16**test 1**
17 - send L2 MAC frames between all pg-l2 interfaces of all BDs
18
19**verify 1**
20 - check BD data by parsing output of bridge_domain_dump API command
21 - all packets received correctly
22
23**config 2**
24 - update data of 5 BD
25 - disable learning, forwarding, flooding and uu_flooding for BD1
26 - disable forwarding for BD2
27 - disable flooding for BD3
28 - disable uu_flooding for BD4
29 - disable learning for BD5
30
31**verify 2**
32 - check BD data by parsing output of bridge_domain_dump API command
33
34**config 3**
35 - delete 2 BDs
36
37**test 3**
38 - send L2 MAC frames between all pg-l2 interfaces of all BDs
39 - send L2 MAC frames between all pg-l2 interfaces formerly assigned to \
40 deleted BDs
41
42**verify 3**
43 - check BD data by parsing output of bridge_domain_dump API command
44 - all packets received correctly on all 3 pg-l2 interfaces assigned to BDs
45 - no packet received on all 3 pg-l2 interfaces of all deleted BDs
46
47**config 4**
48 - add 2 BDs
49 - add 3 pg-l2 interfaces per BD
50
51**test 4**
52 - send L2 MAC frames between all pg-l2 interfaces of all BDs
53
54**verify 4**
55 - check BD data by parsing output of bridge_domain_dump API command
56 - all packets received correctly
57
58**config 5**
59 - delete 5 BDs
60
61**verify 5**
62 - check BD data by parsing output of bridge_domain_dump API command
63"""
64
65import unittest
66import random
67
68from scapy.packet import Raw
69from scapy.layers.l2 import Ether
70from scapy.layers.inet import IP, UDP
71
Florin Corasd05c1552017-09-05 16:15:49 -070072from framework import VppTestCase, VppTestRunner, running_extended_tests
Klement Sekera9225dee2016-12-12 08:36:58 +010073from util import Host, ppp
74
Jan00dad122016-11-29 10:04:53 +010075
Jan00dad122016-11-29 10:04:53 +010076class TestL2bdMultiInst(VppTestCase):
77 """ L2BD Multi-instance Test Case """
78
79 @classmethod
80 def setUpClass(cls):
81 """
82 Perform standard class setup (defined by class method setUpClass in
83 class VppTestCase) before running the test case, set test case related
84 variables and configure VPP.
85 """
86 super(TestL2bdMultiInst, cls).setUpClass()
87
88 try:
89 # Create pg interfaces
Eyal Baric83c8ed2017-05-10 16:08:19 +030090 n_bd = 5
91 cls.ifs_per_bd = ifs_per_bd = 3
92 n_ifs = n_bd * ifs_per_bd
93 cls.create_pg_interfaces(range(n_ifs))
Jan00dad122016-11-29 10:04:53 +010094
95 # Packet flows mapping pg0 -> pg1, pg2 etc.
96 cls.flows = dict()
Eyal Baric83c8ed2017-05-10 16:08:19 +030097 for b in range(n_bd):
98 bd_ifs = cls.bd_if_range(b + 1)
99 for j in bd_ifs:
100 cls.flows[cls.pg_interfaces[j]] = [
101 cls.pg_interfaces[x] for x in bd_ifs if x != j]
102 assert(
103 len(cls.flows[cls.pg_interfaces[j]]) == ifs_per_bd - 1)
Jan00dad122016-11-29 10:04:53 +0100104
105 # Mapping between packet-generator index and lists of test hosts
106 cls.hosts_by_pg_idx = dict()
Jan00dad122016-11-29 10:04:53 +0100107
108 # Create test host entries
Eyal Baric83c8ed2017-05-10 16:08:19 +0300109 cls.create_hosts(5)
Jan00dad122016-11-29 10:04:53 +0100110
Jan65209ed2016-12-05 23:29:17 +0100111 # Packet sizes - jumbo packet (9018 bytes) skipped
112 cls.pg_if_packet_sizes = [64, 512, 1518]
Jan00dad122016-11-29 10:04:53 +0100113
114 # Set up all interfaces
115 for i in cls.pg_interfaces:
116 i.admin_up()
117
118 # Create list of BDs
119 cls.bd_list = list()
120
121 # Create list of deleted BDs
122 cls.bd_deleted_list = list()
123
124 # Create list of pg_interfaces in BDs
125 cls.pg_in_bd = list()
126
Jan00dad122016-11-29 10:04:53 +0100127 except Exception:
128 super(TestL2bdMultiInst, cls).tearDownClass()
129 raise
130
131 def setUp(self):
132 """
133 Clear trace and packet infos before running each test.
134 """
Eyal Baric83c8ed2017-05-10 16:08:19 +0300135 self.reset_packet_infos()
Jan00dad122016-11-29 10:04:53 +0100136 super(TestL2bdMultiInst, self).setUp()
Jan00dad122016-11-29 10:04:53 +0100137
138 def tearDown(self):
139 """
140 Show various debug prints after each test.
141 """
142 super(TestL2bdMultiInst, self).tearDown()
143 if not self.vpp_dead:
144 self.logger.info(self.vapi.ppcli("show l2fib verbose"))
145 self.logger.info(self.vapi.ppcli("show bridge-domain"))
146
147 @classmethod
Eyal Baric83c8ed2017-05-10 16:08:19 +0300148 def create_hosts(cls, hosts_per_if):
Jan00dad122016-11-29 10:04:53 +0100149 """
Eyal Baric83c8ed2017-05-10 16:08:19 +0300150 Create required number of host MAC addresses and distribute them
151 among interfaces. Create host IPv4 address for every host MAC
152 address.
Jan00dad122016-11-29 10:04:53 +0100153
Eyal Baric83c8ed2017-05-10 16:08:19 +0300154 :param int hosts_per_if: Number of hosts per if to create MAC/IPv4
155 addresses for.
Jan00dad122016-11-29 10:04:53 +0100156 """
Eyal Baric83c8ed2017-05-10 16:08:19 +0300157 c = hosts_per_if
158 assert(not cls.hosts_by_pg_idx)
159 for i in range(len(cls.pg_interfaces)):
160 pg_idx = cls.pg_interfaces[i].sw_if_index
161 cls.hosts_by_pg_idx[pg_idx] = [Host(
162 "00:00:00:ff:%02x:%02x" % (pg_idx, j + 1),
163 "172.17.1%02u.%u" % (pg_idx, j + 1)) for j in range(c)]
164
165 @classmethod
166 def bd_if_range(cls, b):
167 n = cls.ifs_per_bd
168 start = (b - 1) * n
169 return range(start, start + n)
Jan00dad122016-11-29 10:04:53 +0100170
171 def create_bd_and_mac_learn(self, count, start=1):
Jan65209ed2016-12-05 23:29:17 +0100172 """
Klement Sekerada505f62017-01-04 12:58:53 +0100173 Create required number of bridge domains with MAC learning enabled,
174 put 3 l2-pg interfaces to every bridge domain and send MAC learning
175 packets.
Jan00dad122016-11-29 10:04:53 +0100176
177 :param int count: Number of bridge domains to be created.
178 :param int start: Starting number of the bridge domain ID.
179 (Default value = 1)
180 """
Eyal Baric83c8ed2017-05-10 16:08:19 +0300181 for b in range(start, start + count):
182 self.vapi.bridge_domain_add_del(bd_id=b)
183 self.logger.info("Bridge domain ID %d created" % b)
184 if self.bd_list.count(b) == 0:
185 self.bd_list.append(b)
186 if self.bd_deleted_list.count(b) == 1:
187 self.bd_deleted_list.remove(b)
188 for j in self.bd_if_range(b):
189 pg_if = self.pg_interfaces[j]
Ole Troana5b2eec2019-03-11 19:23:25 +0100190 self.vapi.sw_interface_set_l2_bridge(
191 rx_sw_if_index=pg_if.sw_if_index, bd_id=b)
Jan00dad122016-11-29 10:04:53 +0100192 self.logger.info("pg-interface %s added to bridge domain ID %d"
Eyal Baric83c8ed2017-05-10 16:08:19 +0300193 % (pg_if.name, b))
Jan00dad122016-11-29 10:04:53 +0100194 self.pg_in_bd.append(pg_if)
Eyal Baric83c8ed2017-05-10 16:08:19 +0300195 hosts = self.hosts_by_pg_idx[pg_if.sw_if_index]
196 packets = [Ether(dst="ff:ff:ff:ff:ff:ff", src=host.mac)
197 for host in hosts]
Jan00dad122016-11-29 10:04:53 +0100198 pg_if.add_stream(packets)
199 self.logger.info("Sending broadcast eth frames for MAC learning")
200 self.pg_start()
201 self.logger.info(self.vapi.ppcli("show bridge-domain"))
202 self.logger.info(self.vapi.ppcli("show l2fib"))
203
204 def delete_bd(self, count, start=1):
Jan65209ed2016-12-05 23:29:17 +0100205 """
Jan00dad122016-11-29 10:04:53 +0100206 Delete required number of bridge domains.
207
208 :param int count: Number of bridge domains to be created.
209 :param int start: Starting number of the bridge domain ID.
210 (Default value = 1)
211 """
Eyal Baric83c8ed2017-05-10 16:08:19 +0300212 for b in range(start, start + count):
213 for j in self.bd_if_range(b):
214 pg_if = self.pg_interfaces[j]
Ole Troana5b2eec2019-03-11 19:23:25 +0100215 self.vapi.sw_interface_set_l2_bridge(
216 rx_sw_if_index=pg_if.sw_if_index, bd_id=b, enable=0)
Jan00dad122016-11-29 10:04:53 +0100217 self.pg_in_bd.remove(pg_if)
Eyal Baric83c8ed2017-05-10 16:08:19 +0300218 self.vapi.bridge_domain_add_del(bd_id=b, is_add=0)
219 self.bd_list.remove(b)
220 self.bd_deleted_list.append(b)
221 self.logger.info("Bridge domain ID %d deleted" % b)
Jan00dad122016-11-29 10:04:53 +0100222
Eyal Baric83c8ed2017-05-10 16:08:19 +0300223 def create_stream(self, src_if):
Jan00dad122016-11-29 10:04:53 +0100224 """
225 Create input packet stream for defined interface using hosts list.
226
227 :param object src_if: Interface to create packet stream for.
228 :param list packet_sizes: List of required packet sizes.
229 :return: Stream of packets.
230 """
Eyal Baric83c8ed2017-05-10 16:08:19 +0300231 packet_sizes = self.pg_if_packet_sizes
Jan00dad122016-11-29 10:04:53 +0100232 pkts = []
233 src_hosts = self.hosts_by_pg_idx[src_if.sw_if_index]
234 for dst_if in self.flows[src_if]:
235 dst_hosts = self.hosts_by_pg_idx[dst_if.sw_if_index]
Eyal Baric83c8ed2017-05-10 16:08:19 +0300236 for dst_host in dst_hosts:
Klement Sekeradab231a2016-12-21 08:50:14 +0100237 pkt_info = self.create_packet_info(src_if, dst_if)
Jan00dad122016-11-29 10:04:53 +0100238 payload = self.info_to_payload(pkt_info)
Eyal Baric83c8ed2017-05-10 16:08:19 +0300239 src_host = random.choice(src_hosts)
Jan00dad122016-11-29 10:04:53 +0100240 p = (Ether(dst=dst_host.mac, src=src_host.mac) /
241 IP(src=src_host.ip4, dst=dst_host.ip4) /
242 UDP(sport=1234, dport=1234) /
243 Raw(payload))
244 pkt_info.data = p.copy()
245 size = random.choice(packet_sizes)
246 self.extend_packet(p, size)
247 pkts.append(p)
248 self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
249 % (src_if.name, len(pkts)))
250 return pkts
251
Eyal Baric83c8ed2017-05-10 16:08:19 +0300252 def verify_capture(self, dst_if):
Jan00dad122016-11-29 10:04:53 +0100253 """
254 Verify captured input packet stream for defined interface.
255
Eyal Baric83c8ed2017-05-10 16:08:19 +0300256 :param object dst_if: Interface to verify captured packet stream for.
Jan00dad122016-11-29 10:04:53 +0100257 """
258 last_info = dict()
Eyal Baric83c8ed2017-05-10 16:08:19 +0300259 for i in self.flows[dst_if]:
Jan00dad122016-11-29 10:04:53 +0100260 last_info[i.sw_if_index] = None
Eyal Baric83c8ed2017-05-10 16:08:19 +0300261 dst = dst_if.sw_if_index
262 for packet in dst_if.get_capture():
Jan00dad122016-11-29 10:04:53 +0100263 try:
264 ip = packet[IP]
265 udp = packet[UDP]
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800266 info = self.payload_to_info(packet[Raw])
Eyal Baric83c8ed2017-05-10 16:08:19 +0300267 self.assertEqual(info.dst, dst)
Jan00dad122016-11-29 10:04:53 +0100268 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
Eyal Baric83c8ed2017-05-10 16:08:19 +0300269 (dst_if.name, info.src, info.index))
270 last_info[info.src] = self.get_next_packet_info_for_interface2(
271 info.src, dst, last_info[info.src])
272 pkt_info = last_info[info.src]
273 self.assertTrue(pkt_info is not None)
274 self.assertEqual(info.index, pkt_info.index)
275 # Check standard fields against saved data in pkt
276 saved = pkt_info.data
277 self.assertEqual(ip.src, saved[IP].src)
278 self.assertEqual(ip.dst, saved[IP].dst)
279 self.assertEqual(udp.sport, saved[UDP].sport)
280 self.assertEqual(udp.dport, saved[UDP].dport)
Jan00dad122016-11-29 10:04:53 +0100281 except:
Klement Sekera9225dee2016-12-12 08:36:58 +0100282 self.logger.error(ppp("Unexpected or invalid packet:", packet))
Jan00dad122016-11-29 10:04:53 +0100283 raise
Eyal Baric83c8ed2017-05-10 16:08:19 +0300284 s = ""
285 remaining = 0
286 for src in self.flows[dst_if]:
Jan00dad122016-11-29 10:04:53 +0100287 remaining_packet = self.get_next_packet_info_for_interface2(
Eyal Baric83c8ed2017-05-10 16:08:19 +0300288 src.sw_if_index, dst, last_info[src.sw_if_index])
289 if remaining_packet is None:
290 s += "Port %u: Packet expected from source %u didn't arrive\n"\
291 % (dst, src.sw_if_index)
292 remaining += 1
293 self.assertNotEqual(0, remaining, s)
Jan00dad122016-11-29 10:04:53 +0100294
295 def set_bd_flags(self, bd_id, **args):
296 """
297 Enable/disable defined feature(s) of the bridge domain.
298
299 :param int bd_id: Bridge domain ID.
Jan65209ed2016-12-05 23:29:17 +0100300 :param list args: List of feature/status pairs. Allowed features: \
301 learn, forward, flood, uu_flood and arp_term. Status False means \
302 disable, status True means enable the feature.
Jan00dad122016-11-29 10:04:53 +0100303 :raise: ValueError in case of unknown feature in the input.
304 """
305 for flag in args:
306 if flag == "learn":
307 feature_bitmap = 1 << 0
308 elif flag == "forward":
309 feature_bitmap = 1 << 1
310 elif flag == "flood":
311 feature_bitmap = 1 << 2
312 elif flag == "uu_flood":
313 feature_bitmap = 1 << 3
314 elif flag == "arp_term":
315 feature_bitmap = 1 << 4
316 else:
317 raise ValueError("Unknown feature used: %s" % flag)
318 is_set = 1 if args[flag] else 0
Ole Troana5b2eec2019-03-11 19:23:25 +0100319 self.vapi.bridge_flags(bd_id=bd_id, is_set=is_set,
320 flags=feature_bitmap)
Jan00dad122016-11-29 10:04:53 +0100321 self.logger.info("Bridge domain ID %d updated" % bd_id)
322
323 def verify_bd(self, bd_id, **args):
324 """
325 Check if the bridge domain is configured and verify expected status
326 of listed features.
327
328 :param int bd_id: Bridge domain ID.
Jan65209ed2016-12-05 23:29:17 +0100329 :param list args: List of feature/status pairs. Allowed features: \
330 learn, forward, flood, uu_flood and arp_term. Status False means \
331 disable, status True means enable the feature.
Jan00dad122016-11-29 10:04:53 +0100332 :return: 1 if bridge domain is configured, otherwise return 0.
333 :raise: ValueError in case of unknown feature in the input.
334 """
335 bd_dump = self.vapi.bridge_domain_dump(bd_id)
336 if len(bd_dump) == 0:
337 self.logger.info("Bridge domain ID %d is not configured" % bd_id)
338 return 0
339 else:
340 bd_dump = bd_dump[0]
341 if len(args) > 0:
342 for flag in args:
343 expected_status = 1 if args[flag] else 0
344 if flag == "learn":
345 flag_status = bd_dump[6]
346 elif flag == "forward":
347 flag_status = bd_dump[5]
348 elif flag == "flood":
349 flag_status = bd_dump[3]
350 elif flag == "uu_flood":
351 flag_status = bd_dump[4]
352 elif flag == "arp_term":
353 flag_status = bd_dump[7]
354 else:
355 raise ValueError("Unknown feature used: %s" % flag)
356 self.assertEqual(expected_status, flag_status)
357 return 1
358
359 def run_verify_test(self):
360 """
Matej Klottondeb69842016-12-09 15:05:46 +0100361 Create packet streams for all configured l2-pg interfaces, send all \
Jan00dad122016-11-29 10:04:53 +0100362 prepared packet streams and verify that:
Matej Klottondeb69842016-12-09 15:05:46 +0100363 - all packets received correctly on all pg-l2 interfaces assigned
364 to bridge domains
365 - no packet received on all pg-l2 interfaces not assigned to
366 bridge domains
Jan00dad122016-11-29 10:04:53 +0100367
Matej Klottondeb69842016-12-09 15:05:46 +0100368 :raise RuntimeError: if no packet captured on l2-pg interface assigned
369 to the bridge domain or if any packet is captured
370 on l2-pg interface not assigned to the bridge
371 domain.
Jan00dad122016-11-29 10:04:53 +0100372 """
373 # Test
374 # Create incoming packet streams for packet-generator interfaces
Eyal Baric83c8ed2017-05-10 16:08:19 +0300375 # for pg_if in self.pg_interfaces:
376 assert(len(self._packet_count_for_dst_if_idx) == 0)
377 for pg_if in self.pg_in_bd:
378 pkts = self.create_stream(pg_if)
Jan00dad122016-11-29 10:04:53 +0100379 pg_if.add_stream(pkts)
380
381 # Enable packet capture and start packet sending
Eyal Baric83c8ed2017-05-10 16:08:19 +0300382 self.pg_enable_capture(self.pg_in_bd)
Jan00dad122016-11-29 10:04:53 +0100383 self.pg_start()
384
385 # Verify
386 # Verify outgoing packet streams per packet-generator interface
Eyal Baric83c8ed2017-05-10 16:08:19 +0300387 for pg_if in self.pg_in_bd:
388 self.verify_capture(pg_if)
Jan00dad122016-11-29 10:04:53 +0100389
390 def test_l2bd_inst_01(self):
391 """ L2BD Multi-instance test 1 - create 5 BDs
392 """
393 # Config 1
394 # Create 5 BDs, put interfaces to these BDs and send MAC learning
395 # packets
396 self.create_bd_and_mac_learn(5)
397
398 # Verify 1
399 for bd_id in self.bd_list:
400 self.assertEqual(self.verify_bd(bd_id), 1)
401
402 # Test 1
403 # self.vapi.cli("clear trace")
404 self.run_verify_test()
405
406 def test_l2bd_inst_02(self):
Eyal Bari31a71ab2017-06-25 14:42:33 +0300407 """ L2BD Multi-instance test 2 - update data of 5 BDs
Jan00dad122016-11-29 10:04:53 +0100408 """
409 # Config 2
410 # Update data of 5 BDs (disable learn, forward, flood, uu-flood)
411 self.set_bd_flags(self.bd_list[0], learn=False, forward=False,
412 flood=False, uu_flood=False)
413 self.set_bd_flags(self.bd_list[1], forward=False)
414 self.set_bd_flags(self.bd_list[2], flood=False)
415 self.set_bd_flags(self.bd_list[3], uu_flood=False)
416 self.set_bd_flags(self.bd_list[4], learn=False)
417
418 # Verify 2
419 # Skipping check of uu_flood as it is not returned by
420 # bridge_domain_dump api command
421 self.verify_bd(self.bd_list[0], learn=False, forward=False,
422 flood=False, uu_flood=False)
423 self.verify_bd(self.bd_list[1], learn=True, forward=False,
424 flood=True, uu_flood=True)
425 self.verify_bd(self.bd_list[2], learn=True, forward=True,
426 flood=False, uu_flood=True)
427 self.verify_bd(self.bd_list[3], learn=True, forward=True,
428 flood=True, uu_flood=False)
429 self.verify_bd(self.bd_list[4], learn=False, forward=True,
430 flood=True, uu_flood=True)
431
Eyal Bari31a71ab2017-06-25 14:42:33 +0300432 def test_l2bd_inst_03(self):
433 """ L2BD Multi-instance test 3 - delete 2 BDs
434 """
435 # Config 3
436 # Delete 2 BDs
437 self.delete_bd(2)
438
439 # Verify 3
440 for bd_id in self.bd_deleted_list:
441 self.assertEqual(self.verify_bd(bd_id), 0)
442 for bd_id in self.bd_list:
443 self.assertEqual(self.verify_bd(bd_id), 1)
444
445 # Test 3
446 self.run_verify_test()
447
448 def test_l2bd_inst_04(self):
449 """ L2BD Multi-instance test 4 - add 2 BDs
450 """
451 # Config 4
452 # Create 5 BDs, put interfaces to these BDs and send MAC learning
453 # packets
454 self.create_bd_and_mac_learn(2)
455
456 # Verify 4
457 for bd_id in self.bd_list:
458 self.assertEqual(self.verify_bd(bd_id), 1)
459
460 # Test 4
461 # self.vapi.cli("clear trace")
462 self.run_verify_test()
463
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800464 @unittest.skipUnless(running_extended_tests, "part of extended tests")
Jan00dad122016-11-29 10:04:53 +0100465 def test_l2bd_inst_05(self):
Eyal Bari284293a2017-06-06 14:18:55 +0300466 """ L2BD Multi-instance test 5 - delete 5 BDs
Jan00dad122016-11-29 10:04:53 +0100467 """
468 # Config 5
469 # Delete 5 BDs
470 self.delete_bd(5)
471
472 # Verify 5
473 for bd_id in self.bd_deleted_list:
474 self.assertEqual(self.verify_bd(bd_id), 0)
475 for bd_id in self.bd_list:
476 self.assertEqual(self.verify_bd(bd_id), 1)
477
478
479if __name__ == '__main__':
480 unittest.main(testRunner=VppTestRunner)