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 @unittest.skip("Crashes VPP")
77 class TestL2bdMultiInst(VppTestCase):
78 """ L2BD Multi-instance Test Case """
83 Perform standard class setup (defined by class method setUpClass in
84 class VppTestCase) before running the test case, set test case related
85 variables and configure VPP.
87 super(TestL2bdMultiInst, cls).setUpClass()
90 # Create pg interfaces
91 cls.create_pg_interfaces(range(15))
93 # Packet flows mapping pg0 -> pg1, pg2 etc.
95 for i in range(0, len(cls.pg_interfaces), 3):
96 cls.flows[cls.pg_interfaces[i]] = [cls.pg_interfaces[i + 1],
97 cls.pg_interfaces[i + 2]]
98 cls.flows[cls.pg_interfaces[i + 1]] = [cls.pg_interfaces[i],
99 cls.pg_interfaces[i + 2]]
100 cls.flows[cls.pg_interfaces[i + 2]] = [cls.pg_interfaces[i],
101 cls.pg_interfaces[i + 1]]
103 # Mapping between packet-generator index and lists of test hosts
104 cls.hosts_by_pg_idx = dict()
105 for pg_if in cls.pg_interfaces:
106 cls.hosts_by_pg_idx[pg_if.sw_if_index] = []
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()
127 # Create list of pg_interfaces not in BDs
128 cls.pg_not_in_bd = list()
129 for pg_if in cls.pg_interfaces:
130 cls.pg_not_in_bd.append(pg_if)
133 super(TestL2bdMultiInst, cls).tearDownClass()
138 Clear trace and packet infos before running each test.
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, count):
154 Create required number of host MAC addresses and distribute them among
155 interfaces. Create host IPv4 address for every host MAC address.
157 :param int count: Number of hosts to create MAC/IPv4 addresses for.
159 n_int = len(cls.pg_interfaces)
160 macs_per_if = count / n_int
162 for pg_if in cls.pg_interfaces:
164 start_nr = macs_per_if * i
165 end_nr = count if i == (n_int - 1) else macs_per_if * (i + 1)
166 hosts = cls.hosts_by_pg_idx[pg_if.sw_if_index]
167 for j in range(start_nr, end_nr):
169 "00:00:00:ff:%02x:%02x" % (pg_if.sw_if_index, j),
170 "172.17.1%02u.%u" % (pg_if.sw_if_index, j))
173 def create_bd_and_mac_learn(self, count, start=1):
175 Create required number of bridge domains with MAC learning enabled, put
176 3 l2-pg interfaces to every bridge domain and send MAC learning packets.
178 :param int count: Number of bridge domains to be created.
179 :param int start: Starting number of the bridge domain ID.
182 for i in range(count):
184 self.vapi.bridge_domain_add_del(bd_id=bd_id)
185 self.logger.info("Bridge domain ID %d created" % bd_id)
186 if self.bd_list.count(bd_id) == 0:
187 self.bd_list.append(bd_id)
188 if self.bd_deleted_list.count(bd_id) == 1:
189 self.bd_deleted_list.remove(bd_id)
191 pg_if = self.pg_interfaces[(i + start - 1) * 3 + j]
192 self.vapi.sw_interface_set_l2_bridge(pg_if.sw_if_index,
194 self.logger.info("pg-interface %s added to bridge domain ID %d"
195 % (pg_if.name, bd_id))
196 self.pg_in_bd.append(pg_if)
197 self.pg_not_in_bd.remove(pg_if)
199 for host in self.hosts_by_pg_idx[pg_if.sw_if_index]:
200 packet = (Ether(dst="ff:ff:ff:ff:ff:ff", src=host.mac))
201 packets.append(packet)
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 i in range(count):
218 self.vapi.bridge_domain_add_del(bd_id=bd_id, is_add=0)
219 if self.bd_list.count(bd_id) == 1:
220 self.bd_list.remove(bd_id)
221 if self.bd_deleted_list.count(bd_id) == 0:
222 self.bd_deleted_list.append(bd_id)
224 pg_if = self.pg_interfaces[(i + start - 1) * 3 + j]
225 self.pg_in_bd.remove(pg_if)
226 self.pg_not_in_bd.append(pg_if)
227 self.logger.info("Bridge domain ID %d deleted" % bd_id)
229 def create_stream(self, src_if, packet_sizes):
231 Create input packet stream for defined interface using hosts list.
233 :param object src_if: Interface to create packet stream for.
234 :param list packet_sizes: List of required packet sizes.
235 :return: Stream of packets.
238 src_hosts = self.hosts_by_pg_idx[src_if.sw_if_index]
239 for dst_if in self.flows[src_if]:
240 dst_hosts = self.hosts_by_pg_idx[dst_if.sw_if_index]
241 n_int = len(dst_hosts)
242 for i in range(0, n_int):
243 dst_host = dst_hosts[i]
244 src_host = random.choice(src_hosts)
245 pkt_info = self.create_packet_info(src_if, dst_if)
246 payload = self.info_to_payload(pkt_info)
247 p = (Ether(dst=dst_host.mac, src=src_host.mac) /
248 IP(src=src_host.ip4, dst=dst_host.ip4) /
249 UDP(sport=1234, dport=1234) /
251 pkt_info.data = p.copy()
252 size = random.choice(packet_sizes)
253 self.extend_packet(p, size)
255 self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
256 % (src_if.name, len(pkts)))
259 def verify_capture(self, pg_if, capture):
261 Verify captured input packet stream for defined interface.
263 :param object pg_if: Interface to verify captured packet stream for.
264 :param list capture: Captured packet stream.
267 for i in self.pg_interfaces:
268 last_info[i.sw_if_index] = None
269 dst_sw_if_index = pg_if.sw_if_index
270 for packet in capture:
271 payload_info = self.payload_to_info(str(packet[Raw]))
275 packet_index = payload_info.index
276 self.assertEqual(payload_info.dst, dst_sw_if_index)
277 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
278 (pg_if.name, payload_info.src, packet_index))
279 next_info = self.get_next_packet_info_for_interface2(
280 payload_info.src, dst_sw_if_index,
281 last_info[payload_info.src])
282 last_info[payload_info.src] = next_info
283 self.assertTrue(next_info is not None)
284 self.assertEqual(packet_index, next_info.index)
285 saved_packet = next_info.data
286 # Check standard fields
287 self.assertEqual(ip.src, saved_packet[IP].src)
288 self.assertEqual(ip.dst, saved_packet[IP].dst)
289 self.assertEqual(udp.sport, saved_packet[UDP].sport)
290 self.assertEqual(udp.dport, saved_packet[UDP].dport)
292 self.logger.error(ppp("Unexpected or invalid packet:", packet))
294 for i in self.pg_interfaces:
295 remaining_packet = self.get_next_packet_info_for_interface2(
296 i, dst_sw_if_index, last_info[i.sw_if_index])
298 remaining_packet is None,
299 "Port %u: Packet expected from source %u didn't arrive" %
300 (dst_sw_if_index, i.sw_if_index))
302 def set_bd_flags(self, bd_id, **args):
304 Enable/disable defined feature(s) of the bridge domain.
306 :param int bd_id: Bridge domain ID.
307 :param list args: List of feature/status pairs. Allowed features: \
308 learn, forward, flood, uu_flood and arp_term. Status False means \
309 disable, status True means enable the feature.
310 :raise: ValueError in case of unknown feature in the input.
314 feature_bitmap = 1 << 0
315 elif flag == "forward":
316 feature_bitmap = 1 << 1
317 elif flag == "flood":
318 feature_bitmap = 1 << 2
319 elif flag == "uu_flood":
320 feature_bitmap = 1 << 3
321 elif flag == "arp_term":
322 feature_bitmap = 1 << 4
324 raise ValueError("Unknown feature used: %s" % flag)
325 is_set = 1 if args[flag] else 0
326 self.vapi.bridge_flags(bd_id, is_set, feature_bitmap)
327 self.logger.info("Bridge domain ID %d updated" % bd_id)
329 def verify_bd(self, bd_id, **args):
331 Check if the bridge domain is configured and verify expected status
334 :param int bd_id: Bridge domain ID.
335 :param list args: List of feature/status pairs. Allowed features: \
336 learn, forward, flood, uu_flood and arp_term. Status False means \
337 disable, status True means enable the feature.
338 :return: 1 if bridge domain is configured, otherwise return 0.
339 :raise: ValueError in case of unknown feature in the input.
341 bd_dump = self.vapi.bridge_domain_dump(bd_id)
342 if len(bd_dump) == 0:
343 self.logger.info("Bridge domain ID %d is not configured" % bd_id)
349 expected_status = 1 if args[flag] else 0
351 flag_status = bd_dump[6]
352 elif flag == "forward":
353 flag_status = bd_dump[5]
354 elif flag == "flood":
355 flag_status = bd_dump[3]
356 elif flag == "uu_flood":
357 flag_status = bd_dump[4]
358 elif flag == "arp_term":
359 flag_status = bd_dump[7]
361 raise ValueError("Unknown feature used: %s" % flag)
362 self.assertEqual(expected_status, flag_status)
365 def run_verify_test(self):
367 Create packet streams for all configured l2-pg interfaces, send all \
368 prepared packet streams and verify that:
369 - all packets received correctly on all pg-l2 interfaces assigned
371 - no packet received on all pg-l2 interfaces not assigned to
374 :raise RuntimeError: if no packet captured on l2-pg interface assigned
375 to the bridge domain or if any packet is captured
376 on l2-pg interface not assigned to the bridge
380 # Create incoming packet streams for packet-generator interfaces
381 for pg_if in self.pg_interfaces:
382 pkts = self.create_stream(pg_if, self.pg_if_packet_sizes)
383 pg_if.add_stream(pkts)
385 # Enable packet capture and start packet sending
386 self.pg_enable_capture(self.pg_interfaces)
390 # Verify outgoing packet streams per packet-generator interface
391 for pg_if in self.pg_interfaces:
392 capture = pg_if.get_capture()
393 if pg_if in self.pg_in_bd:
394 self.verify_capture(pg_if, capture)
395 elif pg_if not in self.pg_not_in_bd:
396 self.logger.error("Unknown interface: %s" % pg_if.name)
398 def test_l2bd_inst_01(self):
399 """ L2BD Multi-instance test 1 - create 5 BDs
402 # Create 5 BDs, put interfaces to these BDs and send MAC learning
404 self.create_bd_and_mac_learn(5)
407 for bd_id in self.bd_list:
408 self.assertEqual(self.verify_bd(bd_id), 1)
411 # self.vapi.cli("clear trace")
412 self.run_verify_test()
414 def test_l2bd_inst_02(self):
415 """ L2BD Multi-instance test 2 - update data of 5 BDs
418 # Update data of 5 BDs (disable learn, forward, flood, uu-flood)
419 self.set_bd_flags(self.bd_list[0], learn=False, forward=False,
420 flood=False, uu_flood=False)
421 self.set_bd_flags(self.bd_list[1], forward=False)
422 self.set_bd_flags(self.bd_list[2], flood=False)
423 self.set_bd_flags(self.bd_list[3], uu_flood=False)
424 self.set_bd_flags(self.bd_list[4], learn=False)
427 # Skipping check of uu_flood as it is not returned by
428 # bridge_domain_dump api command
429 self.verify_bd(self.bd_list[0], learn=False, forward=False,
430 flood=False, uu_flood=False)
431 self.verify_bd(self.bd_list[1], learn=True, forward=False,
432 flood=True, uu_flood=True)
433 self.verify_bd(self.bd_list[2], learn=True, forward=True,
434 flood=False, uu_flood=True)
435 self.verify_bd(self.bd_list[3], learn=True, forward=True,
436 flood=True, uu_flood=False)
437 self.verify_bd(self.bd_list[4], learn=False, forward=True,
438 flood=True, uu_flood=True)
440 def test_l2bd_inst_03(self):
441 """ L2BD Multi-instance 3 - delete 2 BDs
448 for bd_id in self.bd_deleted_list:
449 self.assertEqual(self.verify_bd(bd_id), 0)
450 for bd_id in self.bd_list:
451 self.assertEqual(self.verify_bd(bd_id), 1)
454 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()
472 def test_l2bd_inst_05(self):
473 """ L2BD Multi-instance 5 - delete 5 BDs
480 for bd_id in self.bd_deleted_list:
481 self.assertEqual(self.verify_bd(bd_id), 0)
482 for bd_id in self.bd_list:
483 self.assertEqual(self.verify_bd(bd_id), 1)
486 if __name__ == '__main__':
487 unittest.main(testRunner=VppTestRunner)