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 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] = [Host(
165 "00:00:00:ff:%02x:%02x" % (pg_idx, j + 1),
166 "172.17.1%02u.%u" % (pg_idx, j + 1)) for j in range(c)]
169 def bd_if_range(cls, b):
172 return range(start, start + n)
174 def create_bd_and_mac_learn(self, count, start=1):
176 Create required number of bridge domains with MAC learning enabled,
177 put 3 l2-pg interfaces to every bridge domain and send MAC learning
180 :param int count: Number of bridge domains to be created.
181 :param int start: Starting number of the bridge domain ID.
184 for b in range(start, start + count):
185 self.vapi.bridge_domain_add_del(bd_id=b)
186 self.logger.info("Bridge domain ID %d created" % b)
187 if self.bd_list.count(b) == 0:
188 self.bd_list.append(b)
189 if self.bd_deleted_list.count(b) == 1:
190 self.bd_deleted_list.remove(b)
191 for j in self.bd_if_range(b):
192 pg_if = self.pg_interfaces[j]
193 self.vapi.sw_interface_set_l2_bridge(
194 rx_sw_if_index=pg_if.sw_if_index, bd_id=b)
195 self.logger.info("pg-interface %s added to bridge domain ID %d"
197 self.pg_in_bd.append(pg_if)
198 hosts = self.hosts_by_pg_idx[pg_if.sw_if_index]
199 packets = [Ether(dst="ff:ff:ff:ff:ff:ff", src=host.mac)
201 pg_if.add_stream(packets)
202 self.logger.info("Sending broadcast eth frames for MAC learning")
204 self.logger.info(self.vapi.ppcli("show bridge-domain"))
205 self.logger.info(self.vapi.ppcli("show l2fib"))
207 def delete_bd(self, count, start=1):
209 Delete required number of bridge domains.
211 :param int count: Number of bridge domains to be created.
212 :param int start: Starting number of the bridge domain ID.
215 for b in range(start, start + count):
216 for j in self.bd_if_range(b):
217 pg_if = self.pg_interfaces[j]
218 self.vapi.sw_interface_set_l2_bridge(
219 rx_sw_if_index=pg_if.sw_if_index, bd_id=b, enable=0)
220 self.pg_in_bd.remove(pg_if)
221 self.vapi.bridge_domain_add_del(bd_id=b, is_add=0)
222 self.bd_list.remove(b)
223 self.bd_deleted_list.append(b)
224 self.logger.info("Bridge domain ID %d deleted" % b)
226 def create_stream(self, src_if):
228 Create input packet stream for defined interface using hosts list.
230 :param object src_if: Interface to create packet stream for.
231 :param list packet_sizes: List of required packet sizes.
232 :return: Stream of packets.
234 packet_sizes = self.pg_if_packet_sizes
236 src_hosts = self.hosts_by_pg_idx[src_if.sw_if_index]
237 for dst_if in self.flows[src_if]:
238 dst_hosts = self.hosts_by_pg_idx[dst_if.sw_if_index]
239 for dst_host in dst_hosts:
240 pkt_info = self.create_packet_info(src_if, dst_if)
241 payload = self.info_to_payload(pkt_info)
242 src_host = random.choice(src_hosts)
243 p = (Ether(dst=dst_host.mac, src=src_host.mac) /
244 IP(src=src_host.ip4, dst=dst_host.ip4) /
245 UDP(sport=1234, dport=1234) /
247 pkt_info.data = p.copy()
248 size = random.choice(packet_sizes)
249 self.extend_packet(p, size)
251 self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
252 % (src_if.name, len(pkts)))
255 def verify_capture(self, dst_if):
257 Verify captured input packet stream for defined interface.
259 :param object dst_if: Interface to verify captured packet stream for.
262 for i in self.flows[dst_if]:
263 last_info[i.sw_if_index] = None
264 dst = dst_if.sw_if_index
265 for packet in dst_if.get_capture():
269 info = self.payload_to_info(packet[Raw])
270 self.assertEqual(info.dst, dst)
271 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
272 (dst_if.name, info.src, info.index))
273 last_info[info.src] = self.get_next_packet_info_for_interface2(
274 info.src, dst, last_info[info.src])
275 pkt_info = last_info[info.src]
276 self.assertTrue(pkt_info is not None)
277 self.assertEqual(info.index, pkt_info.index)
278 # Check standard fields against saved data in pkt
279 saved = pkt_info.data
280 self.assertEqual(ip.src, saved[IP].src)
281 self.assertEqual(ip.dst, saved[IP].dst)
282 self.assertEqual(udp.sport, saved[UDP].sport)
283 self.assertEqual(udp.dport, saved[UDP].dport)
285 self.logger.error(ppp("Unexpected or invalid packet:", packet))
289 for src in self.flows[dst_if]:
290 remaining_packet = self.get_next_packet_info_for_interface2(
291 src.sw_if_index, dst, last_info[src.sw_if_index])
292 if remaining_packet is None:
293 s += "Port %u: Packet expected from source %u didn't arrive\n"\
294 % (dst, src.sw_if_index)
296 self.assertNotEqual(0, remaining, s)
298 def set_bd_flags(self, bd_id, **args):
300 Enable/disable defined feature(s) of the bridge domain.
302 :param int bd_id: Bridge domain ID.
303 :param list args: List of feature/status pairs. Allowed features: \
304 learn, forward, flood, uu_flood and arp_term. Status False means \
305 disable, status True means enable the feature.
306 :raise: ValueError in case of unknown feature in the input.
310 feature_bitmap = 1 << 0
311 elif flag == "forward":
312 feature_bitmap = 1 << 1
313 elif flag == "flood":
314 feature_bitmap = 1 << 2
315 elif flag == "uu_flood":
316 feature_bitmap = 1 << 3
317 elif flag == "arp_term":
318 feature_bitmap = 1 << 4
320 raise ValueError("Unknown feature used: %s" % flag)
321 is_set = 1 if args[flag] else 0
322 self.vapi.bridge_flags(bd_id=bd_id, is_set=is_set,
323 flags=feature_bitmap)
324 self.logger.info("Bridge domain ID %d updated" % bd_id)
326 def verify_bd(self, bd_id, **args):
328 Check if the bridge domain is configured and verify expected status
331 :param int bd_id: Bridge domain ID.
332 :param list args: List of feature/status pairs. Allowed features: \
333 learn, forward, flood, uu_flood and arp_term. Status False means \
334 disable, status True means enable the feature.
335 :return: 1 if bridge domain is configured, otherwise return 0.
336 :raise: ValueError in case of unknown feature in the input.
338 bd_dump = self.vapi.bridge_domain_dump(bd_id)
339 if len(bd_dump) == 0:
340 self.logger.info("Bridge domain ID %d is not configured" % bd_id)
346 expected_status = 1 if args[flag] else 0
348 flag_status = bd_dump[6]
349 elif flag == "forward":
350 flag_status = bd_dump[5]
351 elif flag == "flood":
352 flag_status = bd_dump[3]
353 elif flag == "uu_flood":
354 flag_status = bd_dump[4]
355 elif flag == "arp_term":
356 flag_status = bd_dump[7]
358 raise ValueError("Unknown feature used: %s" % flag)
359 self.assertEqual(expected_status, flag_status)
362 def run_verify_test(self):
364 Create packet streams for all configured l2-pg interfaces, send all \
365 prepared packet streams and verify that:
366 - all packets received correctly on all pg-l2 interfaces assigned
368 - no packet received on all pg-l2 interfaces not assigned to
371 :raise RuntimeError: if no packet captured on l2-pg interface assigned
372 to the bridge domain or if any packet is captured
373 on l2-pg interface not assigned to the bridge
377 # Create incoming packet streams for packet-generator interfaces
378 # for pg_if in self.pg_interfaces:
379 assert(len(self._packet_count_for_dst_if_idx) == 0)
380 for pg_if in self.pg_in_bd:
381 pkts = self.create_stream(pg_if)
382 pg_if.add_stream(pkts)
384 # Enable packet capture and start packet sending
385 self.pg_enable_capture(self.pg_in_bd)
389 # Verify outgoing packet streams per packet-generator interface
390 for pg_if in self.pg_in_bd:
391 self.verify_capture(pg_if)
393 def test_l2bd_inst_01(self):
394 """ L2BD Multi-instance test 1 - create 5 BDs
397 # Create 5 BDs, put interfaces to these BDs and send MAC learning
399 self.create_bd_and_mac_learn(5)
402 for bd_id in self.bd_list:
403 self.assertEqual(self.verify_bd(bd_id), 1)
406 # self.vapi.cli("clear trace")
407 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.create_bd_and_mac_learn(5)
416 self.set_bd_flags(self.bd_list[0], learn=False, forward=False,
417 flood=False, uu_flood=False)
418 self.set_bd_flags(self.bd_list[1], forward=False)
419 self.set_bd_flags(self.bd_list[2], flood=False)
420 self.set_bd_flags(self.bd_list[3], uu_flood=False)
421 self.set_bd_flags(self.bd_list[4], learn=False)
424 # Skipping check of uu_flood as it is not returned by
425 # bridge_domain_dump api command
426 self.verify_bd(self.bd_list[0], learn=False, forward=False,
427 flood=False, uu_flood=False)
428 self.verify_bd(self.bd_list[1], learn=True, forward=False,
429 flood=True, uu_flood=True)
430 self.verify_bd(self.bd_list[2], learn=True, forward=True,
431 flood=False, uu_flood=True)
432 self.verify_bd(self.bd_list[3], learn=True, forward=True,
433 flood=True, uu_flood=False)
434 self.verify_bd(self.bd_list[4], learn=False, forward=True,
435 flood=True, uu_flood=True)
438 def test_l2bd_inst_03(self):
439 """ L2BD Multi-instance test 3 - delete 2 BDs
443 self.create_bd_and_mac_learn(5)
447 for bd_id in self.bd_deleted_list:
448 self.assertEqual(self.verify_bd(bd_id), 0)
449 for bd_id in self.bd_list:
450 self.assertEqual(self.verify_bd(bd_id), 1)
453 self.run_verify_test()
456 def test_l2bd_inst_04(self):
457 """ L2BD Multi-instance test 4 - add 2 BDs
460 # Create 5 BDs, put interfaces to these BDs and send MAC learning
462 self.create_bd_and_mac_learn(2)
465 for bd_id in self.bd_list:
466 self.assertEqual(self.verify_bd(bd_id), 1)
469 # self.vapi.cli("clear trace")
470 self.run_verify_test()
473 def test_l2bd_inst_05(self):
474 """ L2BD Multi-instance test 5 - delete 5 BDs
478 self.create_bd_and_mac_learn(5)
482 for bd_id in self.bd_deleted_list:
483 self.assertEqual(self.verify_bd(bd_id), 0)
484 for bd_id in self.bd_list:
485 self.assertEqual(self.verify_bd(bd_id), 1)
488 if __name__ == '__main__':
489 unittest.main(testRunner=VppTestRunner)