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
71 from asfframework import VppTestRunner
72 from util import Host, ppp
73 from vpp_papi import mac_pton, VppEnum
76 class TestL2fib(VppTestCase):
77 """L2 FIB Test Case"""
80 def bd_ifs(cls, bd_id):
81 return range((bd_id - 1) * cls.n_ifs_per_bd, 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
111 cls.pg_if_packet_sizes = [64, 512, 1518, 9018]
114 # Create BD with MAC learning and unknown unicast flooding
115 # disabled and put interfaces to this BD
116 cls.vapi.bridge_domain_add_del_v2(
117 bd_id=bd_id, is_add=1, uu_flood=0, learn=0, flood=1, forward=1
119 ifs = [cls.pg_interfaces[i] for i in cls.bd_ifs(bd_id)]
121 cls.vapi.sw_interface_set_l2_bridge(
122 rx_sw_if_index=pg_if.sw_if_index, bd_id=bd_id
125 # Set up all interfaces
126 for i in cls.pg_interfaces:
129 super(TestL2fib, cls).tearDownClass()
133 def tearDownClass(cls):
134 super(TestL2fib, cls).tearDownClass()
137 super(TestL2fib, self).setUp()
138 self.reset_packet_infos()
142 Show various debug prints after each test.
144 super(TestL2fib, self).tearDown()
145 if not self.vpp_dead:
146 for bd_id in self.n_brs:
148 self.vapi.ppcli("show bridge-domain %s detail" % bd_id)
151 def show_commands_at_teardown(self):
152 self.logger.info(self.vapi.ppcli("show l2fib verbose"))
154 def create_hosts(self, n_hosts_per_if, subnet):
156 Create required number of host MAC addresses and distribute them among
157 interfaces. Create host IPv4 address for every host MAC address.
159 :param int n_hosts_per_if: Number of per interface hosts to
160 create MAC/IPv4 addresses for.
164 for pg_if in self.pg_interfaces:
165 swif = pg_if.sw_if_index
168 return "00:00:%02x:ff:%02x:%02x" % (subnet, swif, j)
171 return "172.%02u.1%02x.%u" % (subnet, swif, j)
174 return Host(mac(j), ip(j))
176 hosts[swif] = [h(j) for j in range(n_hosts_per_if)]
179 def split_hosts(self, hosts, n):
181 for pg_if in self.pg_interfaces:
182 swif = pg_if.sw_if_index
183 splits[swif] = hosts[swif][:n]
184 hosts[swif] = hosts[swif][n:]
187 def learn_hosts(self, bd_id, hosts):
189 Create and send per interface L2 MAC broadcast packet stream to
190 let the bridge domain learn these MAC addresses.
192 :param int bd_id: BD to teach
193 :param dict hosts: dict of hosts per interface
195 self.vapi.bridge_flags(bd_id=bd_id, is_set=1, flags=1)
196 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
198 swif = pg_if.sw_if_index
200 Ether(dst="ff:ff:ff:ff:ff:ff", src=host.mac) for host in hosts[swif]
202 pg_if.add_stream(packets)
203 self.logger.info("Sending broadcast eth frames for MAC learning")
206 def config_l2_fib_entries(self, bd_id, hosts):
208 Config required number of L2 FIB entries.
210 :param int bd_id: BD's id
211 :param int count: Number of L2 FIB entries to be created.
212 :param int start: Starting index of the host list. (Default value = 0)
214 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
216 swif = pg_if.sw_if_index
217 for host in hosts[swif]:
218 self.vapi.l2fib_add_del(mac_pton(host.mac), bd_id, swif, static_mac=1)
220 def delete_l2_fib_entry(self, bd_id, hosts):
222 Delete required number of L2 FIB entries.
224 :param int count: Number of L2 FIB entries to be created.
226 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
228 swif = pg_if.sw_if_index
229 for host in hosts[swif]:
230 self.vapi.l2fib_add_del(mac_pton(host.mac), bd_id, swif, is_add=0)
232 def flush_int(self, swif, learned_hosts):
234 Flush swif L2 FIB entries.
236 :param int swif: sw if index.
239 self.vapi.l2fib_flush_int(swif)
240 flushed[swif] = learned_hosts[swif]
241 learned_hosts[swif] = []
244 def flush_bd(self, bd_id, learned_hosts):
246 Flush bd_id L2 FIB entries.
248 :param int bd_id: Bridge Domain id.
250 self.vapi.l2fib_flush_bd(bd_id)
252 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
254 swif = pg_if.sw_if_index
255 flushed[swif] = learned_hosts[swif]
256 learned_hosts[swif] = []
261 Flush All L2 FIB entries.
263 self.vapi.l2fib_flush_all()
265 def create_stream(self, src_if, packet_sizes, if_src_hosts, if_dst_hosts):
267 Create input packet stream for defined interface using hosts or
270 :param object src_if: Interface to create packet stream for.
271 :param list packet_sizes: List of required packet sizes.
272 :param boolean deleted: Set to True if deleted_hosts list required.
273 :return: Stream of packets.
275 src_hosts = if_src_hosts[src_if.sw_if_index]
279 for dst_if in self.flows[src_if]:
280 dst_swif = dst_if.sw_if_index
281 if dst_swif not in if_dst_hosts:
283 dst_hosts = if_dst_hosts[dst_swif]
284 for dst_host in dst_hosts:
285 src_host = random.choice(src_hosts)
286 pkt_info = self.create_packet_info(src_if, dst_if)
287 payload = self.info_to_payload(pkt_info)
289 Ether(dst=dst_host.mac, src=src_host.mac)
290 / IP(src=src_host.ip4, dst=dst_host.ip4)
291 / UDP(sport=1234, dport=1234)
294 pkt_info.data = p.copy()
295 size = random.choice(packet_sizes)
296 self.extend_packet(p, size)
300 def verify_capture(self, pg_if, capture):
302 Verify captured input packet stream for defined interface.
304 :param object pg_if: Interface to verify captured packet stream for.
305 :param list capture: Captured packet stream.
308 for i in self.pg_interfaces:
309 last_info[i.sw_if_index] = None
310 dst_sw_if_index = pg_if.sw_if_index
311 for packet in capture:
312 payload_info = self.payload_to_info(packet[Raw])
316 packet_index = payload_info.index
317 self.assertEqual(payload_info.dst, dst_sw_if_index)
319 "Got packet on port %s: src=%u (id=%u)"
320 % (pg_if.name, payload_info.src, packet_index)
322 next_info = self.get_next_packet_info_for_interface2(
323 payload_info.src, dst_sw_if_index, last_info[payload_info.src]
325 last_info[payload_info.src] = next_info
326 self.assertTrue(next_info is not None)
327 self.assertEqual(packet_index, next_info.index)
328 saved_packet = next_info.data
329 # Check standard fields
330 self.assertEqual(ip.src, saved_packet[IP].src)
331 self.assertEqual(ip.dst, saved_packet[IP].dst)
332 self.assertEqual(udp.sport, saved_packet[UDP].sport)
333 self.assertEqual(udp.dport, saved_packet[UDP].dport)
334 except BaseException:
335 self.logger.error(ppp("Unexpected or invalid packet:", packet))
337 for i in self.pg_interfaces:
338 remaining_packet = self.get_next_packet_info_for_interface2(
339 i, dst_sw_if_index, last_info[i.sw_if_index]
342 remaining_packet is None,
343 "Port %u: Packet expected from source %u didn't arrive"
344 % (dst_sw_if_index, i.sw_if_index),
347 def run_verify_test(self, bd_id, src_hosts, dst_hosts):
349 # Create incoming packet streams for packet-generator interfaces
350 self.reset_packet_infos()
351 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
353 pkts = self.create_stream(
355 self.pg_if_packet_sizes,
356 if_src_hosts=src_hosts,
357 if_dst_hosts=dst_hosts,
362 self.vapi.bridge_flags(bd_id=bd_id, is_set=0, flags=1)
363 # Enable packet capture and start packet sending
364 self.pg_enable_capture(ifs)
368 # Verify outgoing packet streams per packet-generator interface
370 if not dst_hosts[i.sw_if_index]:
372 capture = i.get_capture()
373 self.logger.info("Verifying capture on interface %s" % i.name)
374 self.verify_capture(i, capture)
376 def run_verify_negat_test(self, bd_id, src_hosts, dst_hosts):
378 # Create incoming packet streams for packet-generator interfaces for
379 # deleted MAC addresses
380 self.reset_packet_infos()
381 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
383 pkts = self.create_stream(
385 self.pg_if_packet_sizes,
386 if_src_hosts=src_hosts,
387 if_dst_hosts=dst_hosts,
392 self.vapi.bridge_flags(bd_id=bd_id, is_set=0, flags=1)
393 # Enable packet capture and start packet sending
394 self.pg_enable_capture(ifs)
398 # Verify outgoing packet streams per packet-generator interface
401 i.get_capture(0, timeout=timeout)
402 i.assert_nothing_captured(remark="outgoing interface")
405 def test_l2_fib_program100(self):
406 """L2 FIB - program 100 MACs"""
408 hosts = self.create_hosts(100, subnet=17)
409 self.config_l2_fib_entries(bd_id, hosts)
410 self.run_verify_test(bd_id, hosts, hosts)
412 def test_l2_fib_program100_delete12(self):
413 """L2 FIB - program 100, delete 12 MACs"""
415 hosts = self.create_hosts(100, subnet=17)
416 self.config_l2_fib_entries(bd_id, hosts)
417 del_hosts = self.split_hosts(hosts, 12)
418 self.delete_l2_fib_entry(bd_id, del_hosts)
420 self.run_verify_test(bd_id, hosts, hosts)
421 self.run_verify_negat_test(bd_id, hosts, del_hosts)
423 def test_l2_fib_program100_add100(self):
424 """L2 FIB - program 100, add 100 MACs"""
426 hosts = self.create_hosts(100, subnet=17)
427 self.config_l2_fib_entries(bd_id, hosts)
428 hosts2 = self.create_hosts(100, subnet=22)
429 self.config_l2_fib_entries(bd_id, hosts2)
430 self.run_verify_test(bd_id, hosts, hosts2)
432 def test_l2_fib_program10_learn10(self):
433 """L2 FIB - program 10 MACs, learn 10"""
434 hosts = self.create_hosts(20, subnet=35)
435 lhosts = self.split_hosts(hosts, 10)
439 self.learn_hosts(bd1, lhosts)
440 self.learn_hosts(bd2, lhosts)
441 self.config_l2_fib_entries(bd1, hosts)
442 self.config_l2_fib_entries(bd2, hosts)
443 self.run_verify_test(bd1, lhosts, hosts)
444 self.run_verify_test(bd2, lhosts, hosts)
446 def test_l2_fib_flush_int(self):
447 """L2 FIB - flush interface"""
448 hosts = self.create_hosts(20, subnet=36)
449 lhosts = self.split_hosts(hosts, 10)
452 self.learn_hosts(bd1, lhosts)
453 self.config_l2_fib_entries(bd1, hosts)
454 self.run_verify_test(bd1, lhosts, hosts)
455 flushed = self.flush_int(self.pg_interfaces[0].sw_if_index, lhosts)
456 self.run_verify_test(bd1, hosts, lhosts)
457 self.run_verify_negat_test(bd1, hosts, flushed)
459 def test_l2_fib_flush_bd(self):
460 """L2 FIB - flush BD"""
461 hosts = self.create_hosts(20, subnet=37)
462 lhosts = self.split_hosts(hosts, 10)
465 self.learn_hosts(bd1, lhosts)
466 self.config_l2_fib_entries(bd1, hosts)
467 self.run_verify_test(bd1, lhosts, hosts)
468 flushed = self.flush_bd(bd1, lhosts)
469 self.run_verify_negat_test(bd1, hosts, flushed)
471 def test_l2_fib_flush_all(self):
472 """L2 FIB - flush all"""
473 hosts = self.create_hosts(20, subnet=38)
474 lhosts = self.split_hosts(hosts, 10)
478 self.learn_hosts(bd1, lhosts)
479 self.learn_hosts(bd2, lhosts)
480 self.config_l2_fib_entries(bd1, hosts)
481 self.config_l2_fib_entries(bd2, hosts)
482 self.run_verify_test(bd1, hosts, lhosts)
483 self.run_verify_test(bd2, hosts, lhosts)
487 self.run_verify_negat_test(bd1, hosts, lhosts)
488 self.run_verify_negat_test(bd2, hosts, lhosts)
490 def test_l2_fib_mac_learn_evs(self):
491 """L2 FIB - mac learning events"""
493 hosts = self.create_hosts(10, subnet=39)
495 self.vapi.want_l2_macs_events()
496 self.learn_hosts(bd1, hosts)
498 self.virtual_sleep(1)
499 self.logger.info(self.vapi.ppcli("show l2fib"))
500 evs = self.vapi.collect_events()
501 action = VppEnum.vl_api_mac_event_action_t.MAC_EVENT_ACTION_API_ADD
503 e.mac[i].mac_addr.packed
505 for i in range(e.n_macs)
506 if e.mac[i].action == action
510 for swif in self.bd_ifs(bd1)
511 for h in hosts[self.pg_interfaces[swif].sw_if_index]
513 self.vapi.want_l2_macs_events(enable_disable=0)
514 self.assertEqual(len(learned_macs ^ macs), 0)
516 def test_l2_fib_mac_learn_evs2(self):
517 """L2 FIB - mac learning events using want_l2_macs_events2"""
519 hosts = self.create_hosts(10, subnet=39)
521 self.vapi.l2fib_set_scan_delay(scan_delay=10)
522 self.vapi.want_l2_macs_events2()
524 self.learn_hosts(bd1, hosts)
526 self.virtual_sleep(1)
527 self.logger.info(self.vapi.ppcli("show l2fib"))
528 evs = self.vapi.collect_events()
529 action = VppEnum.vl_api_mac_event_action_t.MAC_EVENT_ACTION_API_ADD
531 e.mac[i].mac_addr.packed
533 for i in range(e.n_macs)
534 if e.mac[i].action == action
538 for swif in self.bd_ifs(bd1)
539 for h in hosts[self.pg_interfaces[swif].sw_if_index]
541 self.vapi.want_l2_macs_events2(enable_disable=0)
542 self.assertEqual(len(learned_macs ^ macs), 0)
544 def test_l2_fib_macs_learn_max(self):
545 """L2 FIB - mac learning max macs in event"""
547 hosts = self.create_hosts(10, subnet=40)
550 self.vapi.want_l2_macs_events(max_macs_in_event=ev_macs)
551 self.learn_hosts(bd1, hosts)
554 self.logger.info(self.vapi.ppcli("show l2fib"))
555 evs = self.vapi.collect_events()
556 self.vapi.want_l2_macs_events(enable_disable=0)
558 self.assertGreater(len(evs), 0)
559 action = VppEnum.vl_api_mac_event_action_t.MAC_EVENT_ACTION_API_ADD
561 e.mac[i].mac_addr.packed
563 for i in range(e.n_macs)
564 if e.mac[i].action == action
568 for swif in self.bd_ifs(bd1)
569 for h in hosts[self.pg_interfaces[swif].sw_if_index]
573 self.assertLess(len(e), ev_macs * 10)
574 self.assertEqual(len(learned_macs ^ macs), 0)
576 def test_l2_fib_macs_learn_max2(self):
577 """L2 FIB - mac learning max macs in event using want_l2_macs_events2"""
579 hosts = self.create_hosts(10, subnet=40)
582 self.vapi.l2fib_set_scan_delay(scan_delay=10)
583 self.vapi.want_l2_macs_events2(max_macs_in_event=ev_macs)
585 self.learn_hosts(bd1, hosts)
587 self.virtual_sleep(1)
588 self.logger.info(self.vapi.ppcli("show l2fib"))
589 evs = self.vapi.collect_events()
590 self.vapi.want_l2_macs_events2(enable_disable=0)
592 self.assertGreater(len(evs), 0)
593 action = VppEnum.vl_api_mac_event_action_t.MAC_EVENT_ACTION_API_ADD
595 e.mac[i].mac_addr.packed
597 for i in range(e.n_macs)
598 if e.mac[i].action == action
602 for swif in self.bd_ifs(bd1)
603 for h in hosts[self.pg_interfaces[swif].sw_if_index]
607 self.assertLess(len(e), ev_macs * 10)
608 self.assertEqual(len(learned_macs ^ macs), 0)
611 if __name__ == "__main__":
612 unittest.main(testRunner=VppTestRunner)