2 """L2BD Multi-instance Test Case HLD:
5 - higher number of pg-l2 interfaces causes problems => only 15 pg-l2 \
6 interfaces in 5 bridge domains are tested
7 - jumbo packets in configuration with 14 l2-pg interfaces leads to \
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
17 - send L2 MAC frames between all pg-l2 interfaces of all BDs
20 - check BD data by parsing output of bridge_domain_dump API command
21 - all packets received correctly
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
32 - check BD data by parsing output of bridge_domain_dump API command
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 \
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
49 - add 3 pg-l2 interfaces per BD
52 - send L2 MAC frames between all pg-l2 interfaces of all BDs
55 - check BD data by parsing output of bridge_domain_dump API command
56 - all packets received correctly
62 - check BD data by parsing output of bridge_domain_dump API command
68 from scapy.packet import Raw
69 from scapy.layers.l2 import Ether
70 from scapy.layers.inet import IP, UDP
72 from framework import VppTestCase, VppTestRunner
73 from util import Host, ppp
76 class TestL2bdMultiInst(VppTestCase):
77 """L2BD Multi-instance Test Case"""
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.
86 super(TestL2bdMultiInst, cls).setUpClass()
89 # Create pg interfaces
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))
95 # Packet flows mapping pg0 -> pg1, pg2 etc.
98 bd_ifs = cls.bd_if_range(b + 1)
100 cls.flows[cls.pg_interfaces[j]] = [
101 cls.pg_interfaces[x] for x in bd_ifs if x != j
103 assert len(cls.flows[cls.pg_interfaces[j]]) == ifs_per_bd - 1
105 # Mapping between packet-generator index and lists of test hosts
106 cls.hosts_by_pg_idx = dict()
108 # Create test host entries
111 # Packet sizes - jumbo packet (9018 bytes) skipped
112 cls.pg_if_packet_sizes = [64, 512, 1518]
114 # Set up all interfaces
115 for i in cls.pg_interfaces:
119 super(TestL2bdMultiInst, cls).tearDownClass()
123 def tearDownClass(cls):
124 super(TestL2bdMultiInst, cls).tearDownClass()
128 Clear trace and packet infos before running each test.
130 self.reset_packet_infos()
131 super(TestL2bdMultiInst, self).setUp()
135 # Create list of deleted BDs
136 self.bd_deleted_list = []
138 # Create list of pg_interfaces in BDs
143 Show various debug prints after each test.
145 super(TestL2bdMultiInst, self).tearDown()
146 if not self.vpp_dead:
147 self.logger.info(self.vapi.ppcli("show l2fib verbose"))
148 self.logger.info(self.vapi.ppcli("show bridge-domain"))
151 def create_hosts(cls, hosts_per_if):
153 Create required number of host MAC addresses and distribute them
154 among interfaces. Create host IPv4 address for every host MAC
157 :param int hosts_per_if: Number of hosts per if to create MAC/IPv4
161 assert not cls.hosts_by_pg_idx
162 for i in range(len(cls.pg_interfaces)):
163 pg_idx = cls.pg_interfaces[i].sw_if_index
164 cls.hosts_by_pg_idx[pg_idx] = [
166 "00:00:00:ff:%02x:%02x" % (pg_idx, j + 1),
167 "172.17.1%02u.%u" % (pg_idx, j + 1),
173 def bd_if_range(cls, b):
176 return range(start, start + n)
178 def create_bd_and_mac_learn(self, count, start=1):
180 Create required number of bridge domains with MAC learning enabled,
181 put 3 l2-pg interfaces to every bridge domain and send MAC learning
184 :param int count: Number of bridge domains to be created.
185 :param int start: Starting number of the bridge domain ID.
188 for b in range(start, start + count):
189 self.vapi.bridge_domain_add_del(bd_id=b)
190 self.logger.info("Bridge domain ID %d created" % b)
191 if self.bd_list.count(b) == 0:
192 self.bd_list.append(b)
193 if self.bd_deleted_list.count(b) == 1:
194 self.bd_deleted_list.remove(b)
195 for j in self.bd_if_range(b):
196 pg_if = self.pg_interfaces[j]
197 self.vapi.sw_interface_set_l2_bridge(
198 rx_sw_if_index=pg_if.sw_if_index, bd_id=b
201 "pg-interface %s added to bridge domain ID %d" % (pg_if.name, b)
203 self.pg_in_bd.append(pg_if)
204 hosts = self.hosts_by_pg_idx[pg_if.sw_if_index]
206 Ether(dst="ff:ff:ff:ff:ff:ff", src=host.mac) for host in hosts
208 pg_if.add_stream(packets)
209 self.logger.info("Sending broadcast eth frames for MAC learning")
211 self.logger.info(self.vapi.ppcli("show bridge-domain"))
212 self.logger.info(self.vapi.ppcli("show l2fib"))
214 def delete_bd(self, count, start=1):
216 Delete required number of bridge domains.
218 :param int count: Number of bridge domains to be created.
219 :param int start: Starting number of the bridge domain ID.
222 for b in range(start, start + count):
223 for j in self.bd_if_range(b):
224 pg_if = self.pg_interfaces[j]
225 self.vapi.sw_interface_set_l2_bridge(
226 rx_sw_if_index=pg_if.sw_if_index, bd_id=b, enable=0
228 self.pg_in_bd.remove(pg_if)
229 self.vapi.bridge_domain_add_del(bd_id=b, is_add=0)
230 self.bd_list.remove(b)
231 self.bd_deleted_list.append(b)
232 self.logger.info("Bridge domain ID %d deleted" % b)
234 def create_stream(self, src_if):
236 Create input packet stream for defined interface using hosts list.
238 :param object src_if: Interface to create packet stream for.
239 :param list packet_sizes: List of required packet sizes.
240 :return: Stream of packets.
242 packet_sizes = self.pg_if_packet_sizes
244 src_hosts = self.hosts_by_pg_idx[src_if.sw_if_index]
245 for dst_if in self.flows[src_if]:
246 dst_hosts = self.hosts_by_pg_idx[dst_if.sw_if_index]
247 for dst_host in dst_hosts:
248 pkt_info = self.create_packet_info(src_if, dst_if)
249 payload = self.info_to_payload(pkt_info)
250 src_host = random.choice(src_hosts)
252 Ether(dst=dst_host.mac, src=src_host.mac)
253 / IP(src=src_host.ip4, dst=dst_host.ip4)
254 / UDP(sport=1234, dport=1234)
257 pkt_info.data = p.copy()
258 size = random.choice(packet_sizes)
259 self.extend_packet(p, size)
262 "Input stream created for port %s. Length: %u pkt(s)"
263 % (src_if.name, len(pkts))
267 def verify_capture(self, dst_if):
269 Verify captured input packet stream for defined interface.
271 :param object dst_if: Interface to verify captured packet stream for.
274 for i in self.flows[dst_if]:
275 last_info[i.sw_if_index] = None
276 dst = dst_if.sw_if_index
277 for packet in dst_if.get_capture():
281 info = self.payload_to_info(packet[Raw])
282 self.assertEqual(info.dst, dst)
284 "Got packet on port %s: src=%u (id=%u)"
285 % (dst_if.name, info.src, info.index)
287 last_info[info.src] = self.get_next_packet_info_for_interface2(
288 info.src, dst, last_info[info.src]
290 pkt_info = last_info[info.src]
291 self.assertTrue(pkt_info is not None)
292 self.assertEqual(info.index, pkt_info.index)
293 # Check standard fields against saved data in pkt
294 saved = pkt_info.data
295 self.assertEqual(ip.src, saved[IP].src)
296 self.assertEqual(ip.dst, saved[IP].dst)
297 self.assertEqual(udp.sport, saved[UDP].sport)
298 self.assertEqual(udp.dport, saved[UDP].dport)
300 self.logger.error(ppp("Unexpected or invalid packet:", packet))
304 for src in self.flows[dst_if]:
305 remaining_packet = self.get_next_packet_info_for_interface2(
306 src.sw_if_index, dst, last_info[src.sw_if_index]
308 if remaining_packet is None:
309 s += "Port %u: Packet expected from source %u didn't arrive\n" % (
314 self.assertNotEqual(0, remaining, s)
316 def set_bd_flags(self, bd_id, **args):
318 Enable/disable defined feature(s) of the bridge domain.
320 :param int bd_id: Bridge domain ID.
321 :param list args: List of feature/status pairs. Allowed features: \
322 learn, forward, flood, uu_flood and arp_term. Status False means \
323 disable, status True means enable the feature.
324 :raise: ValueError in case of unknown feature in the input.
328 feature_bitmap = 1 << 0
329 elif flag == "forward":
330 feature_bitmap = 1 << 1
331 elif flag == "flood":
332 feature_bitmap = 1 << 2
333 elif flag == "uu_flood":
334 feature_bitmap = 1 << 3
335 elif flag == "arp_term":
336 feature_bitmap = 1 << 4
338 raise ValueError("Unknown feature used: %s" % flag)
339 is_set = 1 if args[flag] else 0
340 self.vapi.bridge_flags(bd_id=bd_id, is_set=is_set, flags=feature_bitmap)
341 self.logger.info("Bridge domain ID %d updated" % bd_id)
343 def verify_bd(self, bd_id, **args):
345 Check if the bridge domain is configured and verify expected status
348 :param int bd_id: Bridge domain ID.
349 :param list args: List of feature/status pairs. Allowed features: \
350 learn, forward, flood, uu_flood and arp_term. Status False means \
351 disable, status True means enable the feature.
352 :return: 1 if bridge domain is configured, otherwise return 0.
353 :raise: ValueError in case of unknown feature in the input.
355 bd_dump = self.vapi.bridge_domain_dump(bd_id)
356 if len(bd_dump) == 0:
357 self.logger.info("Bridge domain ID %d is not configured" % bd_id)
363 expected_status = 1 if args[flag] else 0
365 flag_status = bd_dump[6]
366 elif flag == "forward":
367 flag_status = bd_dump[5]
368 elif flag == "flood":
369 flag_status = bd_dump[3]
370 elif flag == "uu_flood":
371 flag_status = bd_dump[4]
372 elif flag == "arp_term":
373 flag_status = bd_dump[7]
375 raise ValueError("Unknown feature used: %s" % flag)
376 self.assertEqual(expected_status, flag_status)
379 def run_verify_test(self):
381 Create packet streams for all configured l2-pg interfaces, send all \
382 prepared packet streams and verify that:
383 - all packets received correctly on all pg-l2 interfaces assigned
385 - no packet received on all pg-l2 interfaces not assigned to
388 :raise RuntimeError: if no packet captured on l2-pg interface assigned
389 to the bridge domain or if any packet is captured
390 on l2-pg interface not assigned to the bridge
394 # Create incoming packet streams for packet-generator interfaces
395 # for pg_if in self.pg_interfaces:
396 assert len(self._packet_count_for_dst_if_idx) == 0
397 for pg_if in self.pg_in_bd:
398 pkts = self.create_stream(pg_if)
399 pg_if.add_stream(pkts)
401 # Enable packet capture and start packet sending
402 self.pg_enable_capture(self.pg_in_bd)
406 # Verify outgoing packet streams per packet-generator interface
407 for pg_if in self.pg_in_bd:
408 self.verify_capture(pg_if)
410 def test_l2bd_inst_01(self):
411 """L2BD Multi-instance test 1 - create 5 BDs"""
413 # Create 5 BDs, put interfaces to these BDs and send MAC learning
415 self.create_bd_and_mac_learn(5)
418 for bd_id in self.bd_list:
419 self.assertEqual(self.verify_bd(bd_id), 1)
422 # self.vapi.cli("clear trace")
423 self.run_verify_test()
426 def test_l2bd_inst_02(self):
427 """L2BD Multi-instance test 2 - update data of 5 BDs"""
429 # Update data of 5 BDs (disable learn, forward, flood, uu-flood)
430 self.create_bd_and_mac_learn(5)
432 self.bd_list[0], learn=False, forward=False, flood=False, uu_flood=False
434 self.set_bd_flags(self.bd_list[1], forward=False)
435 self.set_bd_flags(self.bd_list[2], flood=False)
436 self.set_bd_flags(self.bd_list[3], uu_flood=False)
437 self.set_bd_flags(self.bd_list[4], learn=False)
440 # Skipping check of uu_flood as it is not returned by
441 # bridge_domain_dump api command
443 self.bd_list[0], learn=False, forward=False, flood=False, uu_flood=False
446 self.bd_list[1], learn=True, forward=False, flood=True, uu_flood=True
449 self.bd_list[2], learn=True, forward=True, flood=False, uu_flood=True
452 self.bd_list[3], learn=True, forward=True, flood=True, uu_flood=False
455 self.bd_list[4], learn=False, forward=True, flood=True, uu_flood=True
459 def test_l2bd_inst_03(self):
460 """L2BD Multi-instance test 3 - delete 2 BDs"""
463 self.create_bd_and_mac_learn(5)
467 for bd_id in self.bd_deleted_list:
468 self.assertEqual(self.verify_bd(bd_id), 0)
469 for bd_id in self.bd_list:
470 self.assertEqual(self.verify_bd(bd_id), 1)
473 self.run_verify_test()
476 def test_l2bd_inst_04(self):
477 """L2BD Multi-instance test 4 - add 2 BDs"""
479 # Create 5 BDs, put interfaces to these BDs and send MAC learning
481 self.create_bd_and_mac_learn(2)
484 for bd_id in self.bd_list:
485 self.assertEqual(self.verify_bd(bd_id), 1)
488 # self.vapi.cli("clear trace")
489 self.run_verify_test()
492 def test_l2bd_inst_05(self):
493 """L2BD Multi-instance test 5 - delete 5 BDs"""
496 self.create_bd_and_mac_learn(5)
500 for bd_id in self.bd_deleted_list:
501 self.assertEqual(self.verify_bd(bd_id), 0)
502 for bd_id in self.bd_list:
503 self.assertEqual(self.verify_bd(bd_id), 1)
506 if __name__ == "__main__":
507 unittest.main(testRunner=VppTestRunner)