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(
191 rx_sw_if_index=pg_if.sw_if_index, bd_id=b)
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(
216 rx_sw_if_index=pg_if.sw_if_index, bd_id=b, enable=0)
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(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=bd_id, is_set=is_set,
320 flags=feature_bitmap)
321 self.logger.info("Bridge domain ID %d updated" % bd_id)
323 def verify_bd(self, bd_id, **args):
325 Check if the bridge domain is configured and verify expected status
328 :param int bd_id: Bridge domain ID.
329 :param list args: List of feature/status pairs. Allowed features: \
330 learn, forward, flood, uu_flood and arp_term. Status False means \
331 disable, status True means enable the feature.
332 :return: 1 if bridge domain is configured, otherwise return 0.
333 :raise: ValueError in case of unknown feature in the input.
335 bd_dump = self.vapi.bridge_domain_dump(bd_id)
336 if len(bd_dump) == 0:
337 self.logger.info("Bridge domain ID %d is not configured" % bd_id)
343 expected_status = 1 if args[flag] else 0
345 flag_status = bd_dump[6]
346 elif flag == "forward":
347 flag_status = bd_dump[5]
348 elif flag == "flood":
349 flag_status = bd_dump[3]
350 elif flag == "uu_flood":
351 flag_status = bd_dump[4]
352 elif flag == "arp_term":
353 flag_status = bd_dump[7]
355 raise ValueError("Unknown feature used: %s" % flag)
356 self.assertEqual(expected_status, flag_status)
359 def run_verify_test(self):
361 Create packet streams for all configured l2-pg interfaces, send all \
362 prepared packet streams and verify that:
363 - all packets received correctly on all pg-l2 interfaces assigned
365 - no packet received on all pg-l2 interfaces not assigned to
368 :raise RuntimeError: if no packet captured on l2-pg interface assigned
369 to the bridge domain or if any packet is captured
370 on l2-pg interface not assigned to the bridge
374 # Create incoming packet streams for packet-generator interfaces
375 # for pg_if in self.pg_interfaces:
376 assert(len(self._packet_count_for_dst_if_idx) == 0)
377 for pg_if in self.pg_in_bd:
378 pkts = self.create_stream(pg_if)
379 pg_if.add_stream(pkts)
381 # Enable packet capture and start packet sending
382 self.pg_enable_capture(self.pg_in_bd)
386 # Verify outgoing packet streams per packet-generator interface
387 for pg_if in self.pg_in_bd:
388 self.verify_capture(pg_if)
390 def test_l2bd_inst_01(self):
391 """ L2BD Multi-instance test 1 - create 5 BDs
394 # Create 5 BDs, put interfaces to these BDs and send MAC learning
396 self.create_bd_and_mac_learn(5)
399 for bd_id in self.bd_list:
400 self.assertEqual(self.verify_bd(bd_id), 1)
403 # self.vapi.cli("clear trace")
404 self.run_verify_test()
406 def test_l2bd_inst_02(self):
407 """ L2BD Multi-instance test 2 - update data of 5 BDs
410 # Update data of 5 BDs (disable learn, forward, flood, uu-flood)
411 self.set_bd_flags(self.bd_list[0], learn=False, forward=False,
412 flood=False, uu_flood=False)
413 self.set_bd_flags(self.bd_list[1], forward=False)
414 self.set_bd_flags(self.bd_list[2], flood=False)
415 self.set_bd_flags(self.bd_list[3], uu_flood=False)
416 self.set_bd_flags(self.bd_list[4], learn=False)
419 # Skipping check of uu_flood as it is not returned by
420 # bridge_domain_dump api command
421 self.verify_bd(self.bd_list[0], learn=False, forward=False,
422 flood=False, uu_flood=False)
423 self.verify_bd(self.bd_list[1], learn=True, forward=False,
424 flood=True, uu_flood=True)
425 self.verify_bd(self.bd_list[2], learn=True, forward=True,
426 flood=False, uu_flood=True)
427 self.verify_bd(self.bd_list[3], learn=True, forward=True,
428 flood=True, uu_flood=False)
429 self.verify_bd(self.bd_list[4], learn=False, forward=True,
430 flood=True, uu_flood=True)
432 def test_l2bd_inst_03(self):
433 """ L2BD Multi-instance test 3 - delete 2 BDs
440 for bd_id in self.bd_deleted_list:
441 self.assertEqual(self.verify_bd(bd_id), 0)
442 for bd_id in self.bd_list:
443 self.assertEqual(self.verify_bd(bd_id), 1)
446 self.run_verify_test()
448 def test_l2bd_inst_04(self):
449 """ L2BD Multi-instance test 4 - add 2 BDs
452 # Create 5 BDs, put interfaces to these BDs and send MAC learning
454 self.create_bd_and_mac_learn(2)
457 for bd_id in self.bd_list:
458 self.assertEqual(self.verify_bd(bd_id), 1)
461 # self.vapi.cli("clear trace")
462 self.run_verify_test()
464 @unittest.skipUnless(running_extended_tests, "part of extended tests")
465 def test_l2bd_inst_05(self):
466 """ L2BD Multi-instance test 5 - delete 5 BDs
473 for bd_id in self.bd_deleted_list:
474 self.assertEqual(self.verify_bd(bd_id), 0)
475 for bd_id in self.bd_list:
476 self.assertEqual(self.verify_bd(bd_id), 1)
479 if __name__ == '__main__':
480 unittest.main(testRunner=VppTestRunner)