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()
133 Clear trace and packet infos before running each test.
135 self.reset_packet_infos()
136 super(TestL2bdMultiInst, self).setUp()
140 Show various debug prints after each test.
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"))
148 def create_hosts(cls, hosts_per_if):
150 Create required number of host MAC addresses and distribute them
151 among interfaces. Create host IPv4 address for every host MAC
154 :param int hosts_per_if: Number of hosts per if to create MAC/IPv4
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)]
166 def bd_if_range(cls, b):
169 return range(start, start + n)
171 def create_bd_and_mac_learn(self, count, start=1):
173 Create required number of bridge domains with MAC learning enabled,
174 put 3 l2-pg interfaces to every bridge domain and send MAC learning
177 :param int count: Number of bridge domains to be created.
178 :param int start: Starting number of the bridge domain ID.
181 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]
190 self.vapi.sw_interface_set_l2_bridge(pg_if.sw_if_index,
192 self.logger.info("pg-interface %s added to bridge domain ID %d"
194 self.pg_in_bd.append(pg_if)
195 hosts = self.hosts_by_pg_idx[pg_if.sw_if_index]
196 packets = [Ether(dst="ff:ff:ff:ff:ff:ff", src=host.mac)
198 pg_if.add_stream(packets)
199 self.logger.info("Sending broadcast eth frames for MAC learning")
201 self.logger.info(self.vapi.ppcli("show bridge-domain"))
202 self.logger.info(self.vapi.ppcli("show l2fib"))
204 def delete_bd(self, count, start=1):
206 Delete required number of bridge domains.
208 :param int count: Number of bridge domains to be created.
209 :param int start: Starting number of the bridge domain ID.
212 for b in range(start, start + count):
213 for j in self.bd_if_range(b):
214 pg_if = self.pg_interfaces[j]
215 self.vapi.sw_interface_set_l2_bridge(pg_if.sw_if_index,
217 self.pg_in_bd.remove(pg_if)
218 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)
223 def create_stream(self, src_if):
225 Create input packet stream for defined interface using hosts list.
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.
231 packet_sizes = self.pg_if_packet_sizes
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]
236 for dst_host in dst_hosts:
237 pkt_info = self.create_packet_info(src_if, dst_if)
238 payload = self.info_to_payload(pkt_info)
239 src_host = random.choice(src_hosts)
240 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) /
244 pkt_info.data = p.copy()
245 size = random.choice(packet_sizes)
246 self.extend_packet(p, size)
248 self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
249 % (src_if.name, len(pkts)))
252 def verify_capture(self, dst_if):
254 Verify captured input packet stream for defined interface.
256 :param object dst_if: Interface to verify captured packet stream for.
259 for i in self.flows[dst_if]:
260 last_info[i.sw_if_index] = None
261 dst = dst_if.sw_if_index
262 for packet in dst_if.get_capture():
266 info = self.payload_to_info(str(packet[Raw]))
267 self.assertEqual(info.dst, dst)
268 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
269 (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)
282 self.logger.error(ppp("Unexpected or invalid packet:", packet))
286 for src in self.flows[dst_if]:
287 remaining_packet = self.get_next_packet_info_for_interface2(
288 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)
293 self.assertNotEqual(0, remaining, s)
295 def set_bd_flags(self, bd_id, **args):
297 Enable/disable defined feature(s) of the bridge domain.
299 :param int bd_id: Bridge domain ID.
300 :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.
303 :raise: ValueError in case of unknown feature in the input.
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
317 raise ValueError("Unknown feature used: %s" % flag)
318 is_set = 1 if args[flag] else 0
319 self.vapi.bridge_flags(bd_id, is_set, feature_bitmap)
320 self.logger.info("Bridge domain ID %d updated" % bd_id)
322 def verify_bd(self, bd_id, **args):
324 Check if the bridge domain is configured and verify expected status
327 :param int bd_id: Bridge domain ID.
328 :param list args: List of feature/status pairs. Allowed features: \
329 learn, forward, flood, uu_flood and arp_term. Status False means \
330 disable, status True means enable the feature.
331 :return: 1 if bridge domain is configured, otherwise return 0.
332 :raise: ValueError in case of unknown feature in the input.
334 bd_dump = self.vapi.bridge_domain_dump(bd_id)
335 if len(bd_dump) == 0:
336 self.logger.info("Bridge domain ID %d is not configured" % bd_id)
342 expected_status = 1 if args[flag] else 0
344 flag_status = bd_dump[6]
345 elif flag == "forward":
346 flag_status = bd_dump[5]
347 elif flag == "flood":
348 flag_status = bd_dump[3]
349 elif flag == "uu_flood":
350 flag_status = bd_dump[4]
351 elif flag == "arp_term":
352 flag_status = bd_dump[7]
354 raise ValueError("Unknown feature used: %s" % flag)
355 self.assertEqual(expected_status, flag_status)
358 def run_verify_test(self):
360 Create packet streams for all configured l2-pg interfaces, send all \
361 prepared packet streams and verify that:
362 - all packets received correctly on all pg-l2 interfaces assigned
364 - no packet received on all pg-l2 interfaces not assigned to
367 :raise RuntimeError: if no packet captured on l2-pg interface assigned
368 to the bridge domain or if any packet is captured
369 on l2-pg interface not assigned to the bridge
373 # Create incoming packet streams for packet-generator interfaces
374 # for pg_if in self.pg_interfaces:
375 assert(len(self._packet_count_for_dst_if_idx) == 0)
376 for pg_if in self.pg_in_bd:
377 pkts = self.create_stream(pg_if)
378 pg_if.add_stream(pkts)
380 # Enable packet capture and start packet sending
381 self.pg_enable_capture(self.pg_in_bd)
385 # Verify outgoing packet streams per packet-generator interface
386 for pg_if in self.pg_in_bd:
387 self.verify_capture(pg_if)
389 def test_l2bd_inst_01(self):
390 """ L2BD Multi-instance test 1 - create 5 BDs
393 # Create 5 BDs, put interfaces to these BDs and send MAC learning
395 self.create_bd_and_mac_learn(5)
398 for bd_id in self.bd_list:
399 self.assertEqual(self.verify_bd(bd_id), 1)
402 # self.vapi.cli("clear trace")
403 self.run_verify_test()
405 def test_l2bd_inst_02(self):
406 """ L2BD Multi-instance test 2 - update data of 5 BDs
409 # Update data of 5 BDs (disable learn, forward, flood, uu-flood)
410 self.set_bd_flags(self.bd_list[0], learn=False, forward=False,
411 flood=False, uu_flood=False)
412 self.set_bd_flags(self.bd_list[1], forward=False)
413 self.set_bd_flags(self.bd_list[2], flood=False)
414 self.set_bd_flags(self.bd_list[3], uu_flood=False)
415 self.set_bd_flags(self.bd_list[4], learn=False)
418 # Skipping check of uu_flood as it is not returned by
419 # bridge_domain_dump api command
420 self.verify_bd(self.bd_list[0], learn=False, forward=False,
421 flood=False, uu_flood=False)
422 self.verify_bd(self.bd_list[1], learn=True, forward=False,
423 flood=True, uu_flood=True)
424 self.verify_bd(self.bd_list[2], learn=True, forward=True,
425 flood=False, uu_flood=True)
426 self.verify_bd(self.bd_list[3], learn=True, forward=True,
427 flood=True, uu_flood=False)
428 self.verify_bd(self.bd_list[4], learn=False, forward=True,
429 flood=True, uu_flood=True)
431 def test_l2bd_inst_03(self):
432 """ L2BD Multi-instance test 3 - delete 2 BDs
439 for bd_id in self.bd_deleted_list:
440 self.assertEqual(self.verify_bd(bd_id), 0)
441 for bd_id in self.bd_list:
442 self.assertEqual(self.verify_bd(bd_id), 1)
445 self.run_verify_test()
447 def test_l2bd_inst_04(self):
448 """ L2BD Multi-instance test 4 - add 2 BDs
451 # Create 5 BDs, put interfaces to these BDs and send MAC learning
453 self.create_bd_and_mac_learn(2)
456 for bd_id in self.bd_list:
457 self.assertEqual(self.verify_bd(bd_id), 1)
460 # self.vapi.cli("clear trace")
461 self.run_verify_test()
463 @unittest.skipUnless(running_extended_tests, "part of extended tests")
464 def test_l2bd_inst_05(self):
465 """ L2BD Multi-instance test 5 - delete 5 BDs
472 for bd_id in self.bd_deleted_list:
473 self.assertEqual(self.verify_bd(bd_id), 0)
474 for bd_id in self.bd_list:
475 self.assertEqual(self.verify_bd(bd_id), 1)
478 if __name__ == '__main__':
479 unittest.main(testRunner=VppTestRunner)