2 """L2 FIB Test Case HLD:
5 - add 4 pg-l2 interfaces
6 - configure them into l2bd
7 - configure 100 MAC entries in L2 fib - 25 MACs per interface
8 - L2 MAC learning and unknown unicast flooding disabled in l2bd
9 - configure 100 MAC entries in L2 fib - 25 MACs per interface
12 - send L2 MAC frames between all 4 pg-l2 interfaces for all of 100 MAC \
16 - all packets received correctly
19 - delete 12 MAC entries - 3 MACs per interface
22 - send L2 MAC frames between all 4 pg-l2 interfaces for non-deleted MAC \
26 - all packets received correctly
29 - send L2 MAC frames between all 4 pg-l2 interfaces for all of 12 deleted \
33 - no packet received on all 4 pg-l2 interfaces
36 - configure new 100 MAC entries in L2 fib - 25 MACs per interface
39 - send L2 MAC frames between all 4 pg-l2 interfaces for all of 188 MAC \
43 - all packets received correctly
46 - delete 160 MAC entries, 40 MACs per interface
49 - send L2 MAC frames between all 4 pg-l2 interfaces for all of 28 \
50 non-deleted MAC entries
53 - all packets received correctly
56 - try send L2 MAC frames between all 4 pg-l2 interfaces for all of 172 \
60 - no packet received on all 4 pg-l2 interfaces
66 from scapy.packet import Raw
67 from scapy.layers.l2 import Ether
68 from scapy.layers.inet import IP, UDP
70 from framework import VppTestCase, VppTestRunner
71 from util import Host, ppp
72 from vpp_papi import mac_pton, VppEnum
75 class TestL2fib(VppTestCase):
76 """ L2 FIB Test Case """
79 def bd_ifs(cls, bd_id):
80 return range((bd_id - 1) * cls.n_ifs_per_bd,
81 bd_id * cls.n_ifs_per_bd - 1)
86 Perform standard class setup (defined by class method setUpClass in
87 class VppTestCase) before running the test case, set test case related
88 variables and configure VPP.
90 :var int bd_id: Bridge domain ID.
92 super(TestL2fib, cls).setUpClass()
95 n_brs = cls.n_brs = range(1, 3)
97 n_ifs = range(cls.n_ifs_per_bd * len(cls.n_brs))
98 # Create pg interfaces
99 cls.create_pg_interfaces(n_ifs)
103 # Packet flows mapping pg0 -> pg1, pg2, pg3 etc.
104 ifs = cls.bd_ifs(bd_id)
106 cls.flows[cls.pg_interfaces[j]] = [
107 cls.pg_interfaces[x] for x in ifs if x != j]
110 cls.pg_if_packet_sizes = [64, 512, 1518, 9018]
113 # Create BD with MAC learning and unknown unicast flooding
114 # disabled and put interfaces to this BD
115 cls.vapi.bridge_domain_add_del(bd_id=bd_id, uu_flood=0,
117 ifs = [cls.pg_interfaces[i] for i in cls.bd_ifs(bd_id)]
119 cls.vapi.sw_interface_set_l2_bridge(
120 rx_sw_if_index=pg_if.sw_if_index, bd_id=bd_id)
122 # Set up all interfaces
123 for i in cls.pg_interfaces:
126 super(TestL2fib, cls).tearDownClass()
130 def tearDownClass(cls):
131 super(TestL2fib, cls).tearDownClass()
134 super(TestL2fib, self).setUp()
135 self.reset_packet_infos()
139 Show various debug prints after each test.
141 super(TestL2fib, self).tearDown()
142 if not self.vpp_dead:
143 for bd_id in self.n_brs:
144 self.logger.info(self.vapi.ppcli("show bridge-domain %s detail"
147 def show_commands_at_teardown(self):
148 self.logger.info(self.vapi.ppcli("show l2fib verbose"))
150 def create_hosts(self, n_hosts_per_if, subnet):
152 Create required number of host MAC addresses and distribute them among
153 interfaces. Create host IPv4 address for every host MAC address.
155 :param int n_hosts_per_if: Number of per interface hosts to
156 create MAC/IPv4 addresses for.
160 for pg_if in self.pg_interfaces:
161 swif = pg_if.sw_if_index
163 def mac(j): return "00:00:%02x:ff:%02x:%02x" % (subnet, swif, j)
165 def ip(j): return "172.%02u.1%02x.%u" % (subnet, swif, j)
167 def h(j): return Host(mac(j), ip(j))
168 hosts[swif] = [h(j) for j in range(n_hosts_per_if)]
171 def split_hosts(self, hosts, n):
173 for pg_if in self.pg_interfaces:
174 swif = pg_if.sw_if_index
175 splits[swif] = hosts[swif][:n]
176 hosts[swif] = hosts[swif][n:]
179 def learn_hosts(self, bd_id, hosts):
181 Create and send per interface L2 MAC broadcast packet stream to
182 let the bridge domain learn these MAC addresses.
184 :param int bd_id: BD to teach
185 :param dict hosts: dict of hosts per interface
187 self.vapi.bridge_flags(bd_id=bd_id, is_set=1, flags=1)
188 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
190 swif = pg_if.sw_if_index
191 packets = [Ether(dst="ff:ff:ff:ff:ff:ff", src=host.mac)
192 for host in hosts[swif]]
193 pg_if.add_stream(packets)
194 self.logger.info("Sending broadcast eth frames for MAC learning")
197 def config_l2_fib_entries(self, bd_id, hosts):
199 Config required number of L2 FIB entries.
201 :param int bd_id: BD's id
202 :param int count: Number of L2 FIB entries to be created.
203 :param int start: Starting index of the host list. (Default value = 0)
205 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
207 swif = pg_if.sw_if_index
208 for host in hosts[swif]:
209 self.vapi.l2fib_add_del(
210 mac_pton(host.mac), bd_id, swif, static_mac=1)
212 def delete_l2_fib_entry(self, bd_id, hosts):
214 Delete required number of L2 FIB entries.
216 :param int count: Number of L2 FIB entries to be created.
218 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
220 swif = pg_if.sw_if_index
221 for host in hosts[swif]:
222 self.vapi.l2fib_add_del(
223 mac_pton(host.mac), bd_id, swif, is_add=0)
225 def flush_int(self, swif, learned_hosts):
227 Flush swif L2 FIB entries.
229 :param int swif: sw if index.
232 self.vapi.l2fib_flush_int(swif)
233 flushed[swif] = learned_hosts[swif]
234 learned_hosts[swif] = []
237 def flush_bd(self, bd_id, learned_hosts):
239 Flush bd_id L2 FIB entries.
241 :param int bd_id: Bridge Domain id.
243 self.vapi.l2fib_flush_bd(bd_id)
245 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
247 swif = pg_if.sw_if_index
248 flushed[swif] = learned_hosts[swif]
249 learned_hosts[swif] = []
254 Flush All L2 FIB entries.
256 self.vapi.l2fib_flush_all()
258 def create_stream(self, src_if, packet_sizes, if_src_hosts, if_dst_hosts):
260 Create input packet stream for defined interface using hosts or
263 :param object src_if: Interface to create packet stream for.
264 :param list packet_sizes: List of required packet sizes.
265 :param boolean deleted: Set to True if deleted_hosts list required.
266 :return: Stream of packets.
268 src_hosts = if_src_hosts[src_if.sw_if_index]
272 for dst_if in self.flows[src_if]:
273 dst_swif = dst_if.sw_if_index
274 if dst_swif not in if_dst_hosts:
276 dst_hosts = if_dst_hosts[dst_swif]
277 for dst_host in dst_hosts:
278 src_host = random.choice(src_hosts)
279 pkt_info = self.create_packet_info(src_if, dst_if)
280 payload = self.info_to_payload(pkt_info)
281 p = (Ether(dst=dst_host.mac, src=src_host.mac) /
282 IP(src=src_host.ip4, dst=dst_host.ip4) /
283 UDP(sport=1234, dport=1234) /
285 pkt_info.data = p.copy()
286 size = random.choice(packet_sizes)
287 self.extend_packet(p, size)
291 def verify_capture(self, pg_if, capture):
293 Verify captured input packet stream for defined interface.
295 :param object pg_if: Interface to verify captured packet stream for.
296 :param list capture: Captured packet stream.
299 for i in self.pg_interfaces:
300 last_info[i.sw_if_index] = None
301 dst_sw_if_index = pg_if.sw_if_index
302 for packet in capture:
303 payload_info = self.payload_to_info(packet[Raw])
307 packet_index = payload_info.index
308 self.assertEqual(payload_info.dst, dst_sw_if_index)
309 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
310 (pg_if.name, payload_info.src, packet_index))
311 next_info = self.get_next_packet_info_for_interface2(
312 payload_info.src, dst_sw_if_index,
313 last_info[payload_info.src])
314 last_info[payload_info.src] = next_info
315 self.assertTrue(next_info is not None)
316 self.assertEqual(packet_index, next_info.index)
317 saved_packet = next_info.data
318 # Check standard fields
319 self.assertEqual(ip.src, saved_packet[IP].src)
320 self.assertEqual(ip.dst, saved_packet[IP].dst)
321 self.assertEqual(udp.sport, saved_packet[UDP].sport)
322 self.assertEqual(udp.dport, saved_packet[UDP].dport)
323 except BaseException:
324 self.logger.error(ppp("Unexpected or invalid packet:", packet))
326 for i in self.pg_interfaces:
327 remaining_packet = self.get_next_packet_info_for_interface2(
328 i, dst_sw_if_index, last_info[i.sw_if_index])
330 remaining_packet is None,
331 "Port %u: Packet expected from source %u didn't arrive" %
332 (dst_sw_if_index, i.sw_if_index))
334 def run_verify_test(self, bd_id, src_hosts, dst_hosts):
336 # Create incoming packet streams for packet-generator interfaces
337 self.reset_packet_infos()
338 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
340 pkts = self.create_stream(
341 i, self.pg_if_packet_sizes,
342 if_src_hosts=src_hosts,
343 if_dst_hosts=dst_hosts)
347 self.vapi.bridge_flags(bd_id=bd_id, is_set=0, flags=1)
348 # Enable packet capture and start packet sending
349 self.pg_enable_capture(ifs)
353 # Verify outgoing packet streams per packet-generator interface
355 if not dst_hosts[i.sw_if_index]:
357 capture = i.get_capture()
358 self.logger.info("Verifying capture on interface %s" % i.name)
359 self.verify_capture(i, capture)
361 def run_verify_negat_test(self, bd_id, src_hosts, dst_hosts):
363 # Create incoming packet streams for packet-generator interfaces for
364 # deleted MAC addresses
365 self.reset_packet_infos()
366 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
368 pkts = self.create_stream(
369 i, self.pg_if_packet_sizes,
370 if_src_hosts=src_hosts,
371 if_dst_hosts=dst_hosts)
375 self.vapi.bridge_flags(bd_id=bd_id, is_set=0, flags=1)
376 # Enable packet capture and start packet sending
377 self.pg_enable_capture(ifs)
381 # Verify outgoing packet streams per packet-generator interface
384 i.get_capture(0, timeout=timeout)
385 i.assert_nothing_captured(remark="outgoing interface")
388 def test_l2_fib_program100(self):
389 """ L2 FIB - program 100 MACs
392 hosts = self.create_hosts(100, subnet=17)
393 self.config_l2_fib_entries(bd_id, hosts)
394 self.run_verify_test(bd_id, hosts, hosts)
396 def test_l2_fib_program100_delete12(self):
397 """ L2 FIB - program 100, delete 12 MACs
400 hosts = self.create_hosts(100, subnet=17)
401 self.config_l2_fib_entries(bd_id, hosts)
402 del_hosts = self.split_hosts(hosts, 12)
403 self.delete_l2_fib_entry(bd_id, del_hosts)
405 self.run_verify_test(bd_id, hosts, hosts)
406 self.run_verify_negat_test(bd_id, hosts, del_hosts)
408 def test_l2_fib_program100_add100(self):
409 """ L2 FIB - program 100, add 100 MACs
412 hosts = self.create_hosts(100, subnet=17)
413 self.config_l2_fib_entries(bd_id, hosts)
414 hosts2 = self.create_hosts(100, subnet=22)
415 self.config_l2_fib_entries(bd_id, hosts2)
416 self.run_verify_test(bd_id, hosts, hosts2)
418 def test_l2_fib_program10_learn10(self):
419 """ L2 FIB - program 10 MACs, learn 10
421 hosts = self.create_hosts(20, subnet=35)
422 lhosts = self.split_hosts(hosts, 10)
426 self.learn_hosts(bd1, lhosts)
427 self.learn_hosts(bd2, lhosts)
428 self.config_l2_fib_entries(bd1, hosts)
429 self.config_l2_fib_entries(bd2, hosts)
430 self.run_verify_test(bd1, lhosts, hosts)
431 self.run_verify_test(bd2, lhosts, hosts)
433 def test_l2_fib_flush_int(self):
434 """ L2 FIB - flush interface
436 hosts = self.create_hosts(20, subnet=36)
437 lhosts = self.split_hosts(hosts, 10)
440 self.learn_hosts(bd1, lhosts)
441 self.config_l2_fib_entries(bd1, hosts)
442 self.run_verify_test(bd1, lhosts, hosts)
443 flushed = self.flush_int(self.pg_interfaces[0].sw_if_index, lhosts)
444 self.run_verify_test(bd1, hosts, lhosts)
445 self.run_verify_negat_test(bd1, hosts, flushed)
447 def test_l2_fib_flush_bd(self):
448 """ L2 FIB - flush BD
450 hosts = self.create_hosts(20, subnet=37)
451 lhosts = self.split_hosts(hosts, 10)
454 self.learn_hosts(bd1, lhosts)
455 self.config_l2_fib_entries(bd1, hosts)
456 self.run_verify_test(bd1, lhosts, hosts)
457 flushed = self.flush_bd(bd1, lhosts)
458 self.run_verify_negat_test(bd1, hosts, flushed)
460 def test_l2_fib_flush_all(self):
461 """ L2 FIB - flush all
463 hosts = self.create_hosts(20, subnet=38)
464 lhosts = self.split_hosts(hosts, 10)
468 self.learn_hosts(bd1, lhosts)
469 self.learn_hosts(bd2, lhosts)
470 self.config_l2_fib_entries(bd1, hosts)
471 self.config_l2_fib_entries(bd2, hosts)
472 self.run_verify_test(bd1, hosts, lhosts)
473 self.run_verify_test(bd2, hosts, lhosts)
477 self.run_verify_negat_test(bd1, hosts, lhosts)
478 self.run_verify_negat_test(bd2, hosts, lhosts)
480 def test_l2_fib_mac_learn_evs(self):
481 """ L2 FIB - mac learning events
484 hosts = self.create_hosts(10, subnet=39)
486 self.vapi.want_l2_macs_events()
487 self.learn_hosts(bd1, hosts)
489 self.virtual_sleep(1)
490 self.logger.info(self.vapi.ppcli("show l2fib"))
491 evs = self.vapi.collect_events()
492 action = VppEnum.vl_api_mac_event_action_t.MAC_EVENT_ACTION_API_ADD
494 e.mac[i].mac_addr.packed for e in evs for i in range(e.n_macs)
495 if e.mac[i].action == action}
496 macs = {h.bin_mac for swif in self.bd_ifs(bd1)
497 for h in hosts[self.pg_interfaces[swif].sw_if_index]}
498 self.vapi.want_l2_macs_events(enable_disable=0)
499 self.assertEqual(len(learned_macs ^ macs), 0)
501 def test_l2_fib_mac_learn_evs2(self):
502 """ L2 FIB - mac learning events using want_l2_macs_events2
505 hosts = self.create_hosts(10, subnet=39)
507 self.vapi.l2fib_set_scan_delay(scan_delay=10)
508 self.vapi.want_l2_macs_events2()
510 self.learn_hosts(bd1, hosts)
512 self.virtual_sleep(1)
513 self.logger.info(self.vapi.ppcli("show l2fib"))
514 evs = self.vapi.collect_events()
515 action = VppEnum.vl_api_mac_event_action_t.MAC_EVENT_ACTION_API_ADD
517 e.mac[i].mac_addr.packed for e in evs for i in range(e.n_macs)
518 if e.mac[i].action == action}
519 macs = {h.bin_mac for swif in self.bd_ifs(bd1)
520 for h in hosts[self.pg_interfaces[swif].sw_if_index]}
521 self.vapi.want_l2_macs_events2(enable_disable=0)
522 self.assertEqual(len(learned_macs ^ macs), 0)
524 def test_l2_fib_macs_learn_max(self):
525 """ L2 FIB - mac learning max macs in event
528 hosts = self.create_hosts(10, subnet=40)
531 self.vapi.want_l2_macs_events(max_macs_in_event=ev_macs)
532 self.learn_hosts(bd1, hosts)
535 self.logger.info(self.vapi.ppcli("show l2fib"))
536 evs = self.vapi.collect_events()
537 self.vapi.want_l2_macs_events(enable_disable=0)
539 self.assertGreater(len(evs), 0)
540 action = VppEnum.vl_api_mac_event_action_t.MAC_EVENT_ACTION_API_ADD
542 e.mac[i].mac_addr.packed for e in evs for i in range(e.n_macs)
543 if e.mac[i].action == action}
544 macs = {h.bin_mac for swif in self.bd_ifs(bd1)
545 for h in hosts[self.pg_interfaces[swif].sw_if_index]}
548 self.assertLess(len(e), ev_macs * 10)
549 self.assertEqual(len(learned_macs ^ macs), 0)
551 def test_l2_fib_macs_learn_max2(self):
552 """ L2 FIB - mac learning max macs in event using want_l2_macs_events2
555 hosts = self.create_hosts(10, subnet=40)
558 self.vapi.l2fib_set_scan_delay(scan_delay=10)
559 self.vapi.want_l2_macs_events2(max_macs_in_event=ev_macs)
561 self.learn_hosts(bd1, hosts)
563 self.virtual_sleep(1)
564 self.logger.info(self.vapi.ppcli("show l2fib"))
565 evs = self.vapi.collect_events()
566 self.vapi.want_l2_macs_events2(enable_disable=0)
568 self.assertGreater(len(evs), 0)
569 action = VppEnum.vl_api_mac_event_action_t.MAC_EVENT_ACTION_API_ADD
571 e.mac[i].mac_addr.packed for e in evs for i in range(e.n_macs)
572 if e.mac[i].action == action}
573 macs = {h.bin_mac for swif in self.bd_ifs(bd1)
574 for h in hosts[self.pg_interfaces[swif].sw_if_index]}
577 self.assertLess(len(e), ev_macs * 10)
578 self.assertEqual(len(learned_macs ^ macs), 0)
580 if __name__ == '__main__':
581 unittest.main(testRunner=VppTestRunner)