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]] = \
99 [cls.pg_interfaces[i], cls.pg_interfaces[i + 2]]
100 cls.flows[cls.pg_interfaces[i + 2]] = \
101 [cls.pg_interfaces[i], 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,
176 put 3 l2-pg interfaces to every bridge domain and send MAC learning
179 :param int count: Number of bridge domains to be created.
180 :param int start: Starting number of the bridge domain ID.
183 for i in range(count):
185 self.vapi.bridge_domain_add_del(bd_id=bd_id)
186 self.logger.info("Bridge domain ID %d created" % bd_id)
187 if self.bd_list.count(bd_id) == 0:
188 self.bd_list.append(bd_id)
189 if self.bd_deleted_list.count(bd_id) == 1:
190 self.bd_deleted_list.remove(bd_id)
192 pg_if = self.pg_interfaces[(i + start - 1) * 3 + j]
193 self.vapi.sw_interface_set_l2_bridge(pg_if.sw_if_index,
195 self.logger.info("pg-interface %s added to bridge domain ID %d"
196 % (pg_if.name, bd_id))
197 self.pg_in_bd.append(pg_if)
198 self.pg_not_in_bd.remove(pg_if)
200 for host in self.hosts_by_pg_idx[pg_if.sw_if_index]:
201 packet = (Ether(dst="ff:ff:ff:ff:ff:ff", src=host.mac))
202 packets.append(packet)
203 pg_if.add_stream(packets)
204 self.logger.info("Sending broadcast eth frames for MAC learning")
206 self.logger.info(self.vapi.ppcli("show bridge-domain"))
207 self.logger.info(self.vapi.ppcli("show l2fib"))
209 def delete_bd(self, count, start=1):
211 Delete required number of bridge domains.
213 :param int count: Number of bridge domains to be created.
214 :param int start: Starting number of the bridge domain ID.
217 for i in range(count):
219 self.vapi.bridge_domain_add_del(bd_id=bd_id, is_add=0)
220 if self.bd_list.count(bd_id) == 1:
221 self.bd_list.remove(bd_id)
222 if self.bd_deleted_list.count(bd_id) == 0:
223 self.bd_deleted_list.append(bd_id)
225 pg_if = self.pg_interfaces[(i + start - 1) * 3 + j]
226 self.pg_in_bd.remove(pg_if)
227 self.pg_not_in_bd.append(pg_if)
228 self.logger.info("Bridge domain ID %d deleted" % bd_id)
230 def create_stream(self, src_if, packet_sizes):
232 Create input packet stream for defined interface using hosts list.
234 :param object src_if: Interface to create packet stream for.
235 :param list packet_sizes: List of required packet sizes.
236 :return: Stream of packets.
239 src_hosts = self.hosts_by_pg_idx[src_if.sw_if_index]
240 for dst_if in self.flows[src_if]:
241 dst_hosts = self.hosts_by_pg_idx[dst_if.sw_if_index]
242 n_int = len(dst_hosts)
243 for i in range(0, n_int):
244 dst_host = dst_hosts[i]
245 src_host = random.choice(src_hosts)
246 pkt_info = self.create_packet_info(src_if, dst_if)
247 payload = self.info_to_payload(pkt_info)
248 p = (Ether(dst=dst_host.mac, src=src_host.mac) /
249 IP(src=src_host.ip4, dst=dst_host.ip4) /
250 UDP(sport=1234, dport=1234) /
252 pkt_info.data = p.copy()
253 size = random.choice(packet_sizes)
254 self.extend_packet(p, size)
256 self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
257 % (src_if.name, len(pkts)))
260 def verify_capture(self, pg_if, capture):
262 Verify captured input packet stream for defined interface.
264 :param object pg_if: Interface to verify captured packet stream for.
265 :param list capture: Captured packet stream.
268 for i in self.pg_interfaces:
269 last_info[i.sw_if_index] = None
270 dst_sw_if_index = pg_if.sw_if_index
271 for packet in capture:
272 payload_info = self.payload_to_info(str(packet[Raw]))
276 packet_index = payload_info.index
277 self.assertEqual(payload_info.dst, dst_sw_if_index)
278 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
279 (pg_if.name, payload_info.src, packet_index))
280 next_info = self.get_next_packet_info_for_interface2(
281 payload_info.src, dst_sw_if_index,
282 last_info[payload_info.src])
283 last_info[payload_info.src] = next_info
284 self.assertTrue(next_info is not None)
285 self.assertEqual(packet_index, next_info.index)
286 saved_packet = next_info.data
287 # Check standard fields
288 self.assertEqual(ip.src, saved_packet[IP].src)
289 self.assertEqual(ip.dst, saved_packet[IP].dst)
290 self.assertEqual(udp.sport, saved_packet[UDP].sport)
291 self.assertEqual(udp.dport, saved_packet[UDP].dport)
293 self.logger.error(ppp("Unexpected or invalid packet:", packet))
295 for i in self.pg_interfaces:
296 remaining_packet = self.get_next_packet_info_for_interface2(
297 i, dst_sw_if_index, last_info[i.sw_if_index])
299 remaining_packet is None,
300 "Port %u: Packet expected from source %u didn't arrive" %
301 (dst_sw_if_index, i.sw_if_index))
303 def set_bd_flags(self, bd_id, **args):
305 Enable/disable defined feature(s) of the bridge domain.
307 :param int bd_id: Bridge domain ID.
308 :param list args: List of feature/status pairs. Allowed features: \
309 learn, forward, flood, uu_flood and arp_term. Status False means \
310 disable, status True means enable the feature.
311 :raise: ValueError in case of unknown feature in the input.
315 feature_bitmap = 1 << 0
316 elif flag == "forward":
317 feature_bitmap = 1 << 1
318 elif flag == "flood":
319 feature_bitmap = 1 << 2
320 elif flag == "uu_flood":
321 feature_bitmap = 1 << 3
322 elif flag == "arp_term":
323 feature_bitmap = 1 << 4
325 raise ValueError("Unknown feature used: %s" % flag)
326 is_set = 1 if args[flag] else 0
327 self.vapi.bridge_flags(bd_id, is_set, feature_bitmap)
328 self.logger.info("Bridge domain ID %d updated" % bd_id)
330 def verify_bd(self, bd_id, **args):
332 Check if the bridge domain is configured and verify expected status
335 :param int bd_id: Bridge domain ID.
336 :param list args: List of feature/status pairs. Allowed features: \
337 learn, forward, flood, uu_flood and arp_term. Status False means \
338 disable, status True means enable the feature.
339 :return: 1 if bridge domain is configured, otherwise return 0.
340 :raise: ValueError in case of unknown feature in the input.
342 bd_dump = self.vapi.bridge_domain_dump(bd_id)
343 if len(bd_dump) == 0:
344 self.logger.info("Bridge domain ID %d is not configured" % bd_id)
350 expected_status = 1 if args[flag] else 0
352 flag_status = bd_dump[6]
353 elif flag == "forward":
354 flag_status = bd_dump[5]
355 elif flag == "flood":
356 flag_status = bd_dump[3]
357 elif flag == "uu_flood":
358 flag_status = bd_dump[4]
359 elif flag == "arp_term":
360 flag_status = bd_dump[7]
362 raise ValueError("Unknown feature used: %s" % flag)
363 self.assertEqual(expected_status, flag_status)
366 def run_verify_test(self):
368 Create packet streams for all configured l2-pg interfaces, send all \
369 prepared packet streams and verify that:
370 - all packets received correctly on all pg-l2 interfaces assigned
372 - no packet received on all pg-l2 interfaces not assigned to
375 :raise RuntimeError: if no packet captured on l2-pg interface assigned
376 to the bridge domain or if any packet is captured
377 on l2-pg interface not assigned to the bridge
381 # Create incoming packet streams for packet-generator interfaces
382 for pg_if in self.pg_interfaces:
383 pkts = self.create_stream(pg_if, self.pg_if_packet_sizes)
384 pg_if.add_stream(pkts)
386 # Enable packet capture and start packet sending
387 self.pg_enable_capture(self.pg_interfaces)
391 # Verify outgoing packet streams per packet-generator interface
392 for pg_if in self.pg_interfaces:
393 capture = pg_if.get_capture()
394 if pg_if in self.pg_in_bd:
395 self.verify_capture(pg_if, capture)
396 elif pg_if not in self.pg_not_in_bd:
397 self.logger.error("Unknown interface: %s" % pg_if.name)
399 def test_l2bd_inst_01(self):
400 """ L2BD Multi-instance test 1 - create 5 BDs
403 # Create 5 BDs, put interfaces to these BDs and send MAC learning
405 self.create_bd_and_mac_learn(5)
408 for bd_id in self.bd_list:
409 self.assertEqual(self.verify_bd(bd_id), 1)
412 # self.vapi.cli("clear trace")
413 self.run_verify_test()
415 def test_l2bd_inst_02(self):
416 """ L2BD Multi-instance test 2 - update data of 5 BDs
419 # Update data of 5 BDs (disable learn, forward, flood, uu-flood)
420 self.set_bd_flags(self.bd_list[0], learn=False, forward=False,
421 flood=False, uu_flood=False)
422 self.set_bd_flags(self.bd_list[1], forward=False)
423 self.set_bd_flags(self.bd_list[2], flood=False)
424 self.set_bd_flags(self.bd_list[3], uu_flood=False)
425 self.set_bd_flags(self.bd_list[4], learn=False)
428 # Skipping check of uu_flood as it is not returned by
429 # bridge_domain_dump api command
430 self.verify_bd(self.bd_list[0], learn=False, forward=False,
431 flood=False, uu_flood=False)
432 self.verify_bd(self.bd_list[1], learn=True, forward=False,
433 flood=True, uu_flood=True)
434 self.verify_bd(self.bd_list[2], learn=True, forward=True,
435 flood=False, uu_flood=True)
436 self.verify_bd(self.bd_list[3], learn=True, forward=True,
437 flood=True, uu_flood=False)
438 self.verify_bd(self.bd_list[4], learn=False, forward=True,
439 flood=True, uu_flood=True)
441 def test_l2bd_inst_03(self):
442 """ L2BD Multi-instance 3 - delete 2 BDs
449 for bd_id in self.bd_deleted_list:
450 self.assertEqual(self.verify_bd(bd_id), 0)
451 for bd_id in self.bd_list:
452 self.assertEqual(self.verify_bd(bd_id), 1)
455 self.run_verify_test()
457 def test_l2bd_inst_04(self):
458 """ L2BD Multi-instance test 4 - add 2 BDs
461 # Create 5 BDs, put interfaces to these BDs and send MAC learning
463 self.create_bd_and_mac_learn(2)
466 for bd_id in self.bd_list:
467 self.assertEqual(self.verify_bd(bd_id), 1)
470 # self.vapi.cli("clear trace")
471 self.run_verify_test()
473 def test_l2bd_inst_05(self):
474 """ L2BD Multi-instance 5 - delete 5 BDs
481 for bd_id in self.bd_deleted_list:
482 self.assertEqual(self.verify_bd(bd_id), 0)
483 for bd_id in self.bd_list:
484 self.assertEqual(self.verify_bd(bd_id), 1)
487 if __name__ == '__main__':
488 unittest.main(testRunner=VppTestRunner)