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, running_extended_tests
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 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:
121 # Create list of deleted BDs
122 cls.bd_deleted_list = list()
124 # Create list of pg_interfaces in BDs
125 cls.pg_in_bd = list()
128 super(TestL2bdMultiInst, cls).tearDownClass()
132 def tearDownClass(cls):
133 super(TestL2bdMultiInst, cls).tearDownClass()
137 Clear trace and packet infos before running each test.
139 self.reset_packet_infos()
140 super(TestL2bdMultiInst, self).setUp()
144 Show various debug prints after each test.
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"))
152 def create_hosts(cls, hosts_per_if):
154 Create required number of host MAC addresses and distribute them
155 among interfaces. Create host IPv4 address for every host MAC
158 :param int hosts_per_if: Number of hosts per if to create MAC/IPv4
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)]
170 def bd_if_range(cls, b):
173 return range(start, start + n)
175 def create_bd_and_mac_learn(self, count, start=1):
177 Create required number of bridge domains with MAC learning enabled,
178 put 3 l2-pg interfaces to every bridge domain and send MAC learning
181 :param int count: Number of bridge domains to be created.
182 :param int start: Starting number of the bridge domain ID.
185 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]
194 self.vapi.sw_interface_set_l2_bridge(
195 rx_sw_if_index=pg_if.sw_if_index, bd_id=b)
196 self.logger.info("pg-interface %s added to bridge domain ID %d"
198 self.pg_in_bd.append(pg_if)
199 hosts = self.hosts_by_pg_idx[pg_if.sw_if_index]
200 packets = [Ether(dst="ff:ff:ff:ff:ff:ff", src=host.mac)
202 pg_if.add_stream(packets)
203 self.logger.info("Sending broadcast eth frames for MAC learning")
205 self.logger.info(self.vapi.ppcli("show bridge-domain"))
206 self.logger.info(self.vapi.ppcli("show l2fib"))
208 def delete_bd(self, count, start=1):
210 Delete required number of bridge domains.
212 :param int count: Number of bridge domains to be created.
213 :param int start: Starting number of the bridge domain ID.
216 for b in range(start, start + count):
217 for j in self.bd_if_range(b):
218 pg_if = self.pg_interfaces[j]
219 self.vapi.sw_interface_set_l2_bridge(
220 rx_sw_if_index=pg_if.sw_if_index, bd_id=b, enable=0)
221 self.pg_in_bd.remove(pg_if)
222 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)
227 def create_stream(self, src_if):
229 Create input packet stream for defined interface using hosts list.
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.
235 packet_sizes = self.pg_if_packet_sizes
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]
240 for dst_host in dst_hosts:
241 pkt_info = self.create_packet_info(src_if, dst_if)
242 payload = self.info_to_payload(pkt_info)
243 src_host = random.choice(src_hosts)
244 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) /
248 pkt_info.data = p.copy()
249 size = random.choice(packet_sizes)
250 self.extend_packet(p, size)
252 self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
253 % (src_if.name, len(pkts)))
256 def verify_capture(self, dst_if):
258 Verify captured input packet stream for defined interface.
260 :param object dst_if: Interface to verify captured packet stream for.
263 for i in self.flows[dst_if]:
264 last_info[i.sw_if_index] = None
265 dst = dst_if.sw_if_index
266 for packet in dst_if.get_capture():
270 info = self.payload_to_info(packet[Raw])
271 self.assertEqual(info.dst, dst)
272 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
273 (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)
286 self.logger.error(ppp("Unexpected or invalid packet:", packet))
290 for src in self.flows[dst_if]:
291 remaining_packet = self.get_next_packet_info_for_interface2(
292 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)
297 self.assertNotEqual(0, remaining, s)
299 def set_bd_flags(self, bd_id, **args):
301 Enable/disable defined feature(s) of the bridge domain.
303 :param int bd_id: Bridge domain ID.
304 :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.
307 :raise: ValueError in case of unknown feature in the input.
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
321 raise ValueError("Unknown feature used: %s" % flag)
322 is_set = 1 if args[flag] else 0
323 self.vapi.bridge_flags(bd_id=bd_id, is_set=is_set,
324 flags=feature_bitmap)
325 self.logger.info("Bridge domain ID %d updated" % bd_id)
327 def verify_bd(self, bd_id, **args):
329 Check if the bridge domain is configured and verify expected status
332 :param int bd_id: Bridge domain ID.
333 :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.
336 :return: 1 if bridge domain is configured, otherwise return 0.
337 :raise: ValueError in case of unknown feature in the input.
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)
347 expected_status = 1 if args[flag] else 0
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]
359 raise ValueError("Unknown feature used: %s" % flag)
360 self.assertEqual(expected_status, flag_status)
363 def run_verify_test(self):
365 Create packet streams for all configured l2-pg interfaces, send all \
366 prepared packet streams and verify that:
367 - all packets received correctly on all pg-l2 interfaces assigned
369 - no packet received on all pg-l2 interfaces not assigned to
372 :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
378 # Create incoming packet streams for packet-generator interfaces
379 # 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)
383 pg_if.add_stream(pkts)
385 # Enable packet capture and start packet sending
386 self.pg_enable_capture(self.pg_in_bd)
390 # Verify outgoing packet streams per packet-generator interface
391 for pg_if in self.pg_in_bd:
392 self.verify_capture(pg_if)
394 def test_l2bd_inst_01(self):
395 """ L2BD Multi-instance test 1 - create 5 BDs
398 # Create 5 BDs, put interfaces to these BDs and send MAC learning
400 self.create_bd_and_mac_learn(5)
403 for bd_id in self.bd_list:
404 self.assertEqual(self.verify_bd(bd_id), 1)
407 # self.vapi.cli("clear trace")
408 self.run_verify_test()
410 def test_l2bd_inst_02(self):
411 """ L2BD Multi-instance test 2 - update data of 5 BDs
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)
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)
436 def test_l2bd_inst_03(self):
437 """ L2BD Multi-instance test 3 - delete 2 BDs
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)
450 self.run_verify_test()
452 def test_l2bd_inst_04(self):
453 """ L2BD Multi-instance test 4 - add 2 BDs
456 # Create 5 BDs, put interfaces to these BDs and send MAC learning
458 self.create_bd_and_mac_learn(2)
461 for bd_id in self.bd_list:
462 self.assertEqual(self.verify_bd(bd_id), 1)
465 # self.vapi.cli("clear trace")
466 self.run_verify_test()
468 @unittest.skipUnless(running_extended_tests, "part of extended tests")
469 def test_l2bd_inst_05(self):
470 """ L2BD Multi-instance test 5 - delete 5 BDs
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)
483 if __name__ == '__main__':
484 unittest.main(testRunner=VppTestRunner)