blob: 8ceec7457875e849e16fbd338e17922fbd08d27e [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
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -0700131 @classmethod
132 def tearDownClass(cls):
133 super(TestL2bdMultiInst, cls).tearDownClass()
134
Jan00dad122016-11-29 10:04:53 +0100135 def setUp(self):
136 """
137 Clear trace and packet infos before running each test.
138 """
Eyal Baric83c8ed2017-05-10 16:08:19 +0300139 self.reset_packet_infos()
Jan00dad122016-11-29 10:04:53 +0100140 super(TestL2bdMultiInst, self).setUp()
Jan00dad122016-11-29 10:04:53 +0100141
142 def tearDown(self):
143 """
144 Show various debug prints after each test.
145 """
146 super(TestL2bdMultiInst, self).tearDown()
147 if not self.vpp_dead:
148 self.logger.info(self.vapi.ppcli("show l2fib verbose"))
149 self.logger.info(self.vapi.ppcli("show bridge-domain"))
150
151 @classmethod
Eyal Baric83c8ed2017-05-10 16:08:19 +0300152 def create_hosts(cls, hosts_per_if):
Jan00dad122016-11-29 10:04:53 +0100153 """
Eyal Baric83c8ed2017-05-10 16:08:19 +0300154 Create required number of host MAC addresses and distribute them
155 among interfaces. Create host IPv4 address for every host MAC
156 address.
Jan00dad122016-11-29 10:04:53 +0100157
Eyal Baric83c8ed2017-05-10 16:08:19 +0300158 :param int hosts_per_if: Number of hosts per if to create MAC/IPv4
159 addresses for.
Jan00dad122016-11-29 10:04:53 +0100160 """
Eyal Baric83c8ed2017-05-10 16:08:19 +0300161 c = hosts_per_if
162 assert(not cls.hosts_by_pg_idx)
163 for i in range(len(cls.pg_interfaces)):
164 pg_idx = cls.pg_interfaces[i].sw_if_index
165 cls.hosts_by_pg_idx[pg_idx] = [Host(
166 "00:00:00:ff:%02x:%02x" % (pg_idx, j + 1),
167 "172.17.1%02u.%u" % (pg_idx, j + 1)) for j in range(c)]
168
169 @classmethod
170 def bd_if_range(cls, b):
171 n = cls.ifs_per_bd
172 start = (b - 1) * n
173 return range(start, start + n)
Jan00dad122016-11-29 10:04:53 +0100174
175 def create_bd_and_mac_learn(self, count, start=1):
Jan65209ed2016-12-05 23:29:17 +0100176 """
Klement Sekerada505f62017-01-04 12:58:53 +0100177 Create required number of bridge domains with MAC learning enabled,
178 put 3 l2-pg interfaces to every bridge domain and send MAC learning
179 packets.
Jan00dad122016-11-29 10:04:53 +0100180
181 :param int count: Number of bridge domains to be created.
182 :param int start: Starting number of the bridge domain ID.
183 (Default value = 1)
184 """
Eyal Baric83c8ed2017-05-10 16:08:19 +0300185 for b in range(start, start + count):
186 self.vapi.bridge_domain_add_del(bd_id=b)
187 self.logger.info("Bridge domain ID %d created" % b)
188 if self.bd_list.count(b) == 0:
189 self.bd_list.append(b)
190 if self.bd_deleted_list.count(b) == 1:
191 self.bd_deleted_list.remove(b)
192 for j in self.bd_if_range(b):
193 pg_if = self.pg_interfaces[j]
Ole Troana5b2eec2019-03-11 19:23:25 +0100194 self.vapi.sw_interface_set_l2_bridge(
195 rx_sw_if_index=pg_if.sw_if_index, bd_id=b)
Jan00dad122016-11-29 10:04:53 +0100196 self.logger.info("pg-interface %s added to bridge domain ID %d"
Eyal Baric83c8ed2017-05-10 16:08:19 +0300197 % (pg_if.name, b))
Jan00dad122016-11-29 10:04:53 +0100198 self.pg_in_bd.append(pg_if)
Eyal Baric83c8ed2017-05-10 16:08:19 +0300199 hosts = self.hosts_by_pg_idx[pg_if.sw_if_index]
200 packets = [Ether(dst="ff:ff:ff:ff:ff:ff", src=host.mac)
201 for host in hosts]
Jan00dad122016-11-29 10:04:53 +0100202 pg_if.add_stream(packets)
203 self.logger.info("Sending broadcast eth frames for MAC learning")
204 self.pg_start()
205 self.logger.info(self.vapi.ppcli("show bridge-domain"))
206 self.logger.info(self.vapi.ppcli("show l2fib"))
207
208 def delete_bd(self, count, start=1):
Jan65209ed2016-12-05 23:29:17 +0100209 """
Jan00dad122016-11-29 10:04:53 +0100210 Delete required number of bridge domains.
211
212 :param int count: Number of bridge domains to be created.
213 :param int start: Starting number of the bridge domain ID.
214 (Default value = 1)
215 """
Eyal Baric83c8ed2017-05-10 16:08:19 +0300216 for b in range(start, start + count):
217 for j in self.bd_if_range(b):
218 pg_if = self.pg_interfaces[j]
Ole Troana5b2eec2019-03-11 19:23:25 +0100219 self.vapi.sw_interface_set_l2_bridge(
220 rx_sw_if_index=pg_if.sw_if_index, bd_id=b, enable=0)
Jan00dad122016-11-29 10:04:53 +0100221 self.pg_in_bd.remove(pg_if)
Eyal Baric83c8ed2017-05-10 16:08:19 +0300222 self.vapi.bridge_domain_add_del(bd_id=b, is_add=0)
223 self.bd_list.remove(b)
224 self.bd_deleted_list.append(b)
225 self.logger.info("Bridge domain ID %d deleted" % b)
Jan00dad122016-11-29 10:04:53 +0100226
Eyal Baric83c8ed2017-05-10 16:08:19 +0300227 def create_stream(self, src_if):
Jan00dad122016-11-29 10:04:53 +0100228 """
229 Create input packet stream for defined interface using hosts list.
230
231 :param object src_if: Interface to create packet stream for.
232 :param list packet_sizes: List of required packet sizes.
233 :return: Stream of packets.
234 """
Eyal Baric83c8ed2017-05-10 16:08:19 +0300235 packet_sizes = self.pg_if_packet_sizes
Jan00dad122016-11-29 10:04:53 +0100236 pkts = []
237 src_hosts = self.hosts_by_pg_idx[src_if.sw_if_index]
238 for dst_if in self.flows[src_if]:
239 dst_hosts = self.hosts_by_pg_idx[dst_if.sw_if_index]
Eyal Baric83c8ed2017-05-10 16:08:19 +0300240 for dst_host in dst_hosts:
Klement Sekeradab231a2016-12-21 08:50:14 +0100241 pkt_info = self.create_packet_info(src_if, dst_if)
Jan00dad122016-11-29 10:04:53 +0100242 payload = self.info_to_payload(pkt_info)
Eyal Baric83c8ed2017-05-10 16:08:19 +0300243 src_host = random.choice(src_hosts)
Jan00dad122016-11-29 10:04:53 +0100244 p = (Ether(dst=dst_host.mac, src=src_host.mac) /
245 IP(src=src_host.ip4, dst=dst_host.ip4) /
246 UDP(sport=1234, dport=1234) /
247 Raw(payload))
248 pkt_info.data = p.copy()
249 size = random.choice(packet_sizes)
250 self.extend_packet(p, size)
251 pkts.append(p)
252 self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
253 % (src_if.name, len(pkts)))
254 return pkts
255
Eyal Baric83c8ed2017-05-10 16:08:19 +0300256 def verify_capture(self, dst_if):
Jan00dad122016-11-29 10:04:53 +0100257 """
258 Verify captured input packet stream for defined interface.
259
Eyal Baric83c8ed2017-05-10 16:08:19 +0300260 :param object dst_if: Interface to verify captured packet stream for.
Jan00dad122016-11-29 10:04:53 +0100261 """
262 last_info = dict()
Eyal Baric83c8ed2017-05-10 16:08:19 +0300263 for i in self.flows[dst_if]:
Jan00dad122016-11-29 10:04:53 +0100264 last_info[i.sw_if_index] = None
Eyal Baric83c8ed2017-05-10 16:08:19 +0300265 dst = dst_if.sw_if_index
266 for packet in dst_if.get_capture():
Jan00dad122016-11-29 10:04:53 +0100267 try:
268 ip = packet[IP]
269 udp = packet[UDP]
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800270 info = self.payload_to_info(packet[Raw])
Eyal Baric83c8ed2017-05-10 16:08:19 +0300271 self.assertEqual(info.dst, dst)
Jan00dad122016-11-29 10:04:53 +0100272 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
Eyal Baric83c8ed2017-05-10 16:08:19 +0300273 (dst_if.name, info.src, info.index))
274 last_info[info.src] = self.get_next_packet_info_for_interface2(
275 info.src, dst, last_info[info.src])
276 pkt_info = last_info[info.src]
277 self.assertTrue(pkt_info is not None)
278 self.assertEqual(info.index, pkt_info.index)
279 # Check standard fields against saved data in pkt
280 saved = pkt_info.data
281 self.assertEqual(ip.src, saved[IP].src)
282 self.assertEqual(ip.dst, saved[IP].dst)
283 self.assertEqual(udp.sport, saved[UDP].sport)
284 self.assertEqual(udp.dport, saved[UDP].dport)
Jan00dad122016-11-29 10:04:53 +0100285 except:
Klement Sekera9225dee2016-12-12 08:36:58 +0100286 self.logger.error(ppp("Unexpected or invalid packet:", packet))
Jan00dad122016-11-29 10:04:53 +0100287 raise
Eyal Baric83c8ed2017-05-10 16:08:19 +0300288 s = ""
289 remaining = 0
290 for src in self.flows[dst_if]:
Jan00dad122016-11-29 10:04:53 +0100291 remaining_packet = self.get_next_packet_info_for_interface2(
Eyal Baric83c8ed2017-05-10 16:08:19 +0300292 src.sw_if_index, dst, last_info[src.sw_if_index])
293 if remaining_packet is None:
294 s += "Port %u: Packet expected from source %u didn't arrive\n"\
295 % (dst, src.sw_if_index)
296 remaining += 1
297 self.assertNotEqual(0, remaining, s)
Jan00dad122016-11-29 10:04:53 +0100298
299 def set_bd_flags(self, bd_id, **args):
300 """
301 Enable/disable defined feature(s) of the bridge domain.
302
303 :param int bd_id: Bridge domain ID.
Jan65209ed2016-12-05 23:29:17 +0100304 :param list args: List of feature/status pairs. Allowed features: \
305 learn, forward, flood, uu_flood and arp_term. Status False means \
306 disable, status True means enable the feature.
Jan00dad122016-11-29 10:04:53 +0100307 :raise: ValueError in case of unknown feature in the input.
308 """
309 for flag in args:
310 if flag == "learn":
311 feature_bitmap = 1 << 0
312 elif flag == "forward":
313 feature_bitmap = 1 << 1
314 elif flag == "flood":
315 feature_bitmap = 1 << 2
316 elif flag == "uu_flood":
317 feature_bitmap = 1 << 3
318 elif flag == "arp_term":
319 feature_bitmap = 1 << 4
320 else:
321 raise ValueError("Unknown feature used: %s" % flag)
322 is_set = 1 if args[flag] else 0
Ole Troana5b2eec2019-03-11 19:23:25 +0100323 self.vapi.bridge_flags(bd_id=bd_id, is_set=is_set,
324 flags=feature_bitmap)
Jan00dad122016-11-29 10:04:53 +0100325 self.logger.info("Bridge domain ID %d updated" % bd_id)
326
327 def verify_bd(self, bd_id, **args):
328 """
329 Check if the bridge domain is configured and verify expected status
330 of listed features.
331
332 :param int bd_id: Bridge domain ID.
Jan65209ed2016-12-05 23:29:17 +0100333 :param list args: List of feature/status pairs. Allowed features: \
334 learn, forward, flood, uu_flood and arp_term. Status False means \
335 disable, status True means enable the feature.
Jan00dad122016-11-29 10:04:53 +0100336 :return: 1 if bridge domain is configured, otherwise return 0.
337 :raise: ValueError in case of unknown feature in the input.
338 """
339 bd_dump = self.vapi.bridge_domain_dump(bd_id)
340 if len(bd_dump) == 0:
341 self.logger.info("Bridge domain ID %d is not configured" % bd_id)
342 return 0
343 else:
344 bd_dump = bd_dump[0]
345 if len(args) > 0:
346 for flag in args:
347 expected_status = 1 if args[flag] else 0
348 if flag == "learn":
349 flag_status = bd_dump[6]
350 elif flag == "forward":
351 flag_status = bd_dump[5]
352 elif flag == "flood":
353 flag_status = bd_dump[3]
354 elif flag == "uu_flood":
355 flag_status = bd_dump[4]
356 elif flag == "arp_term":
357 flag_status = bd_dump[7]
358 else:
359 raise ValueError("Unknown feature used: %s" % flag)
360 self.assertEqual(expected_status, flag_status)
361 return 1
362
363 def run_verify_test(self):
364 """
Matej Klottondeb69842016-12-09 15:05:46 +0100365 Create packet streams for all configured l2-pg interfaces, send all \
Jan00dad122016-11-29 10:04:53 +0100366 prepared packet streams and verify that:
Matej Klottondeb69842016-12-09 15:05:46 +0100367 - all packets received correctly on all pg-l2 interfaces assigned
368 to bridge domains
369 - no packet received on all pg-l2 interfaces not assigned to
370 bridge domains
Jan00dad122016-11-29 10:04:53 +0100371
Matej Klottondeb69842016-12-09 15:05:46 +0100372 :raise RuntimeError: if no packet captured on l2-pg interface assigned
373 to the bridge domain or if any packet is captured
374 on l2-pg interface not assigned to the bridge
375 domain.
Jan00dad122016-11-29 10:04:53 +0100376 """
377 # Test
378 # Create incoming packet streams for packet-generator interfaces
Eyal Baric83c8ed2017-05-10 16:08:19 +0300379 # for pg_if in self.pg_interfaces:
380 assert(len(self._packet_count_for_dst_if_idx) == 0)
381 for pg_if in self.pg_in_bd:
382 pkts = self.create_stream(pg_if)
Jan00dad122016-11-29 10:04:53 +0100383 pg_if.add_stream(pkts)
384
385 # Enable packet capture and start packet sending
Eyal Baric83c8ed2017-05-10 16:08:19 +0300386 self.pg_enable_capture(self.pg_in_bd)
Jan00dad122016-11-29 10:04:53 +0100387 self.pg_start()
388
389 # Verify
390 # Verify outgoing packet streams per packet-generator interface
Eyal Baric83c8ed2017-05-10 16:08:19 +0300391 for pg_if in self.pg_in_bd:
392 self.verify_capture(pg_if)
Jan00dad122016-11-29 10:04:53 +0100393
394 def test_l2bd_inst_01(self):
395 """ L2BD Multi-instance test 1 - create 5 BDs
396 """
397 # Config 1
398 # Create 5 BDs, put interfaces to these BDs and send MAC learning
399 # packets
400 self.create_bd_and_mac_learn(5)
401
402 # Verify 1
403 for bd_id in self.bd_list:
404 self.assertEqual(self.verify_bd(bd_id), 1)
405
406 # Test 1
407 # self.vapi.cli("clear trace")
408 self.run_verify_test()
409
410 def test_l2bd_inst_02(self):
Eyal Bari31a71ab2017-06-25 14:42:33 +0300411 """ L2BD Multi-instance test 2 - update data of 5 BDs
Jan00dad122016-11-29 10:04:53 +0100412 """
413 # Config 2
414 # Update data of 5 BDs (disable learn, forward, flood, uu-flood)
415 self.set_bd_flags(self.bd_list[0], learn=False, forward=False,
416 flood=False, uu_flood=False)
417 self.set_bd_flags(self.bd_list[1], forward=False)
418 self.set_bd_flags(self.bd_list[2], flood=False)
419 self.set_bd_flags(self.bd_list[3], uu_flood=False)
420 self.set_bd_flags(self.bd_list[4], learn=False)
421
422 # Verify 2
423 # Skipping check of uu_flood as it is not returned by
424 # bridge_domain_dump api command
425 self.verify_bd(self.bd_list[0], learn=False, forward=False,
426 flood=False, uu_flood=False)
427 self.verify_bd(self.bd_list[1], learn=True, forward=False,
428 flood=True, uu_flood=True)
429 self.verify_bd(self.bd_list[2], learn=True, forward=True,
430 flood=False, uu_flood=True)
431 self.verify_bd(self.bd_list[3], learn=True, forward=True,
432 flood=True, uu_flood=False)
433 self.verify_bd(self.bd_list[4], learn=False, forward=True,
434 flood=True, uu_flood=True)
435
Eyal Bari31a71ab2017-06-25 14:42:33 +0300436 def test_l2bd_inst_03(self):
437 """ L2BD Multi-instance test 3 - delete 2 BDs
438 """
439 # Config 3
440 # Delete 2 BDs
441 self.delete_bd(2)
442
443 # Verify 3
444 for bd_id in self.bd_deleted_list:
445 self.assertEqual(self.verify_bd(bd_id), 0)
446 for bd_id in self.bd_list:
447 self.assertEqual(self.verify_bd(bd_id), 1)
448
449 # Test 3
450 self.run_verify_test()
451
452 def test_l2bd_inst_04(self):
453 """ L2BD Multi-instance test 4 - add 2 BDs
454 """
455 # Config 4
456 # Create 5 BDs, put interfaces to these BDs and send MAC learning
457 # packets
458 self.create_bd_and_mac_learn(2)
459
460 # Verify 4
461 for bd_id in self.bd_list:
462 self.assertEqual(self.verify_bd(bd_id), 1)
463
464 # Test 4
465 # self.vapi.cli("clear trace")
466 self.run_verify_test()
467
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800468 @unittest.skipUnless(running_extended_tests, "part of extended tests")
Jan00dad122016-11-29 10:04:53 +0100469 def test_l2bd_inst_05(self):
Eyal Bari284293a2017-06-06 14:18:55 +0300470 """ L2BD Multi-instance test 5 - delete 5 BDs
Jan00dad122016-11-29 10:04:53 +0100471 """
472 # Config 5
473 # Delete 5 BDs
474 self.delete_bd(5)
475
476 # Verify 5
477 for bd_id in self.bd_deleted_list:
478 self.assertEqual(self.verify_bd(bd_id), 0)
479 for bd_id in self.bd_list:
480 self.assertEqual(self.verify_bd(bd_id), 1)
481
482
483if __name__ == '__main__':
484 unittest.main(testRunner=VppTestRunner)