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, bd_id * cls.n_ifs_per_bd - 1)
85 Perform standard class setup (defined by class method setUpClass in
86 class VppTestCase) before running the test case, set test case related
87 variables and configure VPP.
89 :var int bd_id: Bridge domain ID.
91 super(TestL2fib, cls).setUpClass()
94 n_brs = cls.n_brs = range(1, 3)
96 n_ifs = range(cls.n_ifs_per_bd * len(cls.n_brs))
97 # Create pg interfaces
98 cls.create_pg_interfaces(n_ifs)
102 # Packet flows mapping pg0 -> pg1, pg2, pg3 etc.
103 ifs = cls.bd_ifs(bd_id)
105 cls.flows[cls.pg_interfaces[j]] = [
106 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_v2(
116 bd_id=bd_id, is_add=1, uu_flood=0, learn=0, flood=1, forward=1
118 ifs = [cls.pg_interfaces[i] for i in cls.bd_ifs(bd_id)]
120 cls.vapi.sw_interface_set_l2_bridge(
121 rx_sw_if_index=pg_if.sw_if_index, bd_id=bd_id
124 # Set up all interfaces
125 for i in cls.pg_interfaces:
128 super(TestL2fib, cls).tearDownClass()
132 def tearDownClass(cls):
133 super(TestL2fib, cls).tearDownClass()
136 super(TestL2fib, self).setUp()
137 self.reset_packet_infos()
141 Show various debug prints after each test.
143 super(TestL2fib, self).tearDown()
144 if not self.vpp_dead:
145 for bd_id in self.n_brs:
147 self.vapi.ppcli("show bridge-domain %s detail" % bd_id)
150 def show_commands_at_teardown(self):
151 self.logger.info(self.vapi.ppcli("show l2fib verbose"))
153 def create_hosts(self, n_hosts_per_if, subnet):
155 Create required number of host MAC addresses and distribute them among
156 interfaces. Create host IPv4 address for every host MAC address.
158 :param int n_hosts_per_if: Number of per interface hosts to
159 create MAC/IPv4 addresses for.
163 for pg_if in self.pg_interfaces:
164 swif = pg_if.sw_if_index
167 return "00:00:%02x:ff:%02x:%02x" % (subnet, swif, j)
170 return "172.%02u.1%02x.%u" % (subnet, swif, j)
173 return Host(mac(j), ip(j))
175 hosts[swif] = [h(j) for j in range(n_hosts_per_if)]
178 def split_hosts(self, hosts, n):
180 for pg_if in self.pg_interfaces:
181 swif = pg_if.sw_if_index
182 splits[swif] = hosts[swif][:n]
183 hosts[swif] = hosts[swif][n:]
186 def learn_hosts(self, bd_id, hosts):
188 Create and send per interface L2 MAC broadcast packet stream to
189 let the bridge domain learn these MAC addresses.
191 :param int bd_id: BD to teach
192 :param dict hosts: dict of hosts per interface
194 self.vapi.bridge_flags(bd_id=bd_id, is_set=1, flags=1)
195 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
197 swif = pg_if.sw_if_index
199 Ether(dst="ff:ff:ff:ff:ff:ff", src=host.mac) for host in hosts[swif]
201 pg_if.add_stream(packets)
202 self.logger.info("Sending broadcast eth frames for MAC learning")
205 def config_l2_fib_entries(self, bd_id, hosts):
207 Config required number of L2 FIB entries.
209 :param int bd_id: BD's id
210 :param int count: Number of L2 FIB entries to be created.
211 :param int start: Starting index of the host list. (Default value = 0)
213 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
215 swif = pg_if.sw_if_index
216 for host in hosts[swif]:
217 self.vapi.l2fib_add_del(mac_pton(host.mac), bd_id, swif, static_mac=1)
219 def delete_l2_fib_entry(self, bd_id, hosts):
221 Delete required number of L2 FIB entries.
223 :param int count: Number of L2 FIB entries to be created.
225 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
227 swif = pg_if.sw_if_index
228 for host in hosts[swif]:
229 self.vapi.l2fib_add_del(mac_pton(host.mac), bd_id, swif, is_add=0)
231 def flush_int(self, swif, learned_hosts):
233 Flush swif L2 FIB entries.
235 :param int swif: sw if index.
238 self.vapi.l2fib_flush_int(swif)
239 flushed[swif] = learned_hosts[swif]
240 learned_hosts[swif] = []
243 def flush_bd(self, bd_id, learned_hosts):
245 Flush bd_id L2 FIB entries.
247 :param int bd_id: Bridge Domain id.
249 self.vapi.l2fib_flush_bd(bd_id)
251 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
253 swif = pg_if.sw_if_index
254 flushed[swif] = learned_hosts[swif]
255 learned_hosts[swif] = []
260 Flush All L2 FIB entries.
262 self.vapi.l2fib_flush_all()
264 def create_stream(self, src_if, packet_sizes, if_src_hosts, if_dst_hosts):
266 Create input packet stream for defined interface using hosts or
269 :param object src_if: Interface to create packet stream for.
270 :param list packet_sizes: List of required packet sizes.
271 :param boolean deleted: Set to True if deleted_hosts list required.
272 :return: Stream of packets.
274 src_hosts = if_src_hosts[src_if.sw_if_index]
278 for dst_if in self.flows[src_if]:
279 dst_swif = dst_if.sw_if_index
280 if dst_swif not in if_dst_hosts:
282 dst_hosts = if_dst_hosts[dst_swif]
283 for dst_host in dst_hosts:
284 src_host = random.choice(src_hosts)
285 pkt_info = self.create_packet_info(src_if, dst_if)
286 payload = self.info_to_payload(pkt_info)
288 Ether(dst=dst_host.mac, src=src_host.mac)
289 / IP(src=src_host.ip4, dst=dst_host.ip4)
290 / UDP(sport=1234, dport=1234)
293 pkt_info.data = p.copy()
294 size = random.choice(packet_sizes)
295 self.extend_packet(p, size)
299 def verify_capture(self, pg_if, capture):
301 Verify captured input packet stream for defined interface.
303 :param object pg_if: Interface to verify captured packet stream for.
304 :param list capture: Captured packet stream.
307 for i in self.pg_interfaces:
308 last_info[i.sw_if_index] = None
309 dst_sw_if_index = pg_if.sw_if_index
310 for packet in capture:
311 payload_info = self.payload_to_info(packet[Raw])
315 packet_index = payload_info.index
316 self.assertEqual(payload_info.dst, dst_sw_if_index)
318 "Got packet on port %s: src=%u (id=%u)"
319 % (pg_if.name, payload_info.src, packet_index)
321 next_info = self.get_next_packet_info_for_interface2(
322 payload_info.src, dst_sw_if_index, last_info[payload_info.src]
324 last_info[payload_info.src] = next_info
325 self.assertTrue(next_info is not None)
326 self.assertEqual(packet_index, next_info.index)
327 saved_packet = next_info.data
328 # Check standard fields
329 self.assertEqual(ip.src, saved_packet[IP].src)
330 self.assertEqual(ip.dst, saved_packet[IP].dst)
331 self.assertEqual(udp.sport, saved_packet[UDP].sport)
332 self.assertEqual(udp.dport, saved_packet[UDP].dport)
333 except BaseException:
334 self.logger.error(ppp("Unexpected or invalid packet:", packet))
336 for i in self.pg_interfaces:
337 remaining_packet = self.get_next_packet_info_for_interface2(
338 i, dst_sw_if_index, last_info[i.sw_if_index]
341 remaining_packet is None,
342 "Port %u: Packet expected from source %u didn't arrive"
343 % (dst_sw_if_index, i.sw_if_index),
346 def run_verify_test(self, bd_id, src_hosts, dst_hosts):
348 # Create incoming packet streams for packet-generator interfaces
349 self.reset_packet_infos()
350 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
352 pkts = self.create_stream(
354 self.pg_if_packet_sizes,
355 if_src_hosts=src_hosts,
356 if_dst_hosts=dst_hosts,
361 self.vapi.bridge_flags(bd_id=bd_id, is_set=0, flags=1)
362 # Enable packet capture and start packet sending
363 self.pg_enable_capture(ifs)
367 # Verify outgoing packet streams per packet-generator interface
369 if not dst_hosts[i.sw_if_index]:
371 capture = i.get_capture()
372 self.logger.info("Verifying capture on interface %s" % i.name)
373 self.verify_capture(i, capture)
375 def run_verify_negat_test(self, bd_id, src_hosts, dst_hosts):
377 # Create incoming packet streams for packet-generator interfaces for
378 # deleted MAC addresses
379 self.reset_packet_infos()
380 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
382 pkts = self.create_stream(
384 self.pg_if_packet_sizes,
385 if_src_hosts=src_hosts,
386 if_dst_hosts=dst_hosts,
391 self.vapi.bridge_flags(bd_id=bd_id, is_set=0, flags=1)
392 # Enable packet capture and start packet sending
393 self.pg_enable_capture(ifs)
397 # Verify outgoing packet streams per packet-generator interface
400 i.get_capture(0, timeout=timeout)
401 i.assert_nothing_captured(remark="outgoing interface")
404 def test_l2_fib_program100(self):
405 """L2 FIB - program 100 MACs"""
407 hosts = self.create_hosts(100, subnet=17)
408 self.config_l2_fib_entries(bd_id, hosts)
409 self.run_verify_test(bd_id, hosts, hosts)
411 def test_l2_fib_program100_delete12(self):
412 """L2 FIB - program 100, delete 12 MACs"""
414 hosts = self.create_hosts(100, subnet=17)
415 self.config_l2_fib_entries(bd_id, hosts)
416 del_hosts = self.split_hosts(hosts, 12)
417 self.delete_l2_fib_entry(bd_id, del_hosts)
419 self.run_verify_test(bd_id, hosts, hosts)
420 self.run_verify_negat_test(bd_id, hosts, del_hosts)
422 def test_l2_fib_program100_add100(self):
423 """L2 FIB - program 100, add 100 MACs"""
425 hosts = self.create_hosts(100, subnet=17)
426 self.config_l2_fib_entries(bd_id, hosts)
427 hosts2 = self.create_hosts(100, subnet=22)
428 self.config_l2_fib_entries(bd_id, hosts2)
429 self.run_verify_test(bd_id, hosts, hosts2)
431 def test_l2_fib_program10_learn10(self):
432 """L2 FIB - program 10 MACs, learn 10"""
433 hosts = self.create_hosts(20, subnet=35)
434 lhosts = self.split_hosts(hosts, 10)
438 self.learn_hosts(bd1, lhosts)
439 self.learn_hosts(bd2, lhosts)
440 self.config_l2_fib_entries(bd1, hosts)
441 self.config_l2_fib_entries(bd2, hosts)
442 self.run_verify_test(bd1, lhosts, hosts)
443 self.run_verify_test(bd2, lhosts, hosts)
445 def test_l2_fib_flush_int(self):
446 """L2 FIB - flush interface"""
447 hosts = self.create_hosts(20, subnet=36)
448 lhosts = self.split_hosts(hosts, 10)
451 self.learn_hosts(bd1, lhosts)
452 self.config_l2_fib_entries(bd1, hosts)
453 self.run_verify_test(bd1, lhosts, hosts)
454 flushed = self.flush_int(self.pg_interfaces[0].sw_if_index, lhosts)
455 self.run_verify_test(bd1, hosts, lhosts)
456 self.run_verify_negat_test(bd1, hosts, flushed)
458 def test_l2_fib_flush_bd(self):
459 """L2 FIB - flush BD"""
460 hosts = self.create_hosts(20, subnet=37)
461 lhosts = self.split_hosts(hosts, 10)
464 self.learn_hosts(bd1, lhosts)
465 self.config_l2_fib_entries(bd1, hosts)
466 self.run_verify_test(bd1, lhosts, hosts)
467 flushed = self.flush_bd(bd1, lhosts)
468 self.run_verify_negat_test(bd1, hosts, flushed)
470 def test_l2_fib_flush_all(self):
471 """L2 FIB - flush all"""
472 hosts = self.create_hosts(20, subnet=38)
473 lhosts = self.split_hosts(hosts, 10)
477 self.learn_hosts(bd1, lhosts)
478 self.learn_hosts(bd2, lhosts)
479 self.config_l2_fib_entries(bd1, hosts)
480 self.config_l2_fib_entries(bd2, hosts)
481 self.run_verify_test(bd1, hosts, lhosts)
482 self.run_verify_test(bd2, hosts, lhosts)
486 self.run_verify_negat_test(bd1, hosts, lhosts)
487 self.run_verify_negat_test(bd2, hosts, lhosts)
489 def test_l2_fib_mac_learn_evs(self):
490 """L2 FIB - mac learning events"""
492 hosts = self.create_hosts(10, subnet=39)
494 self.vapi.want_l2_macs_events()
495 self.learn_hosts(bd1, hosts)
497 self.virtual_sleep(1)
498 self.logger.info(self.vapi.ppcli("show l2fib"))
499 evs = self.vapi.collect_events()
500 action = VppEnum.vl_api_mac_event_action_t.MAC_EVENT_ACTION_API_ADD
502 e.mac[i].mac_addr.packed
504 for i in range(e.n_macs)
505 if e.mac[i].action == action
509 for swif in self.bd_ifs(bd1)
510 for h in hosts[self.pg_interfaces[swif].sw_if_index]
512 self.vapi.want_l2_macs_events(enable_disable=0)
513 self.assertEqual(len(learned_macs ^ macs), 0)
515 def test_l2_fib_mac_learn_evs2(self):
516 """L2 FIB - mac learning events using want_l2_macs_events2"""
518 hosts = self.create_hosts(10, subnet=39)
520 self.vapi.l2fib_set_scan_delay(scan_delay=10)
521 self.vapi.want_l2_macs_events2()
523 self.learn_hosts(bd1, hosts)
525 self.virtual_sleep(1)
526 self.logger.info(self.vapi.ppcli("show l2fib"))
527 evs = self.vapi.collect_events()
528 action = VppEnum.vl_api_mac_event_action_t.MAC_EVENT_ACTION_API_ADD
530 e.mac[i].mac_addr.packed
532 for i in range(e.n_macs)
533 if e.mac[i].action == action
537 for swif in self.bd_ifs(bd1)
538 for h in hosts[self.pg_interfaces[swif].sw_if_index]
540 self.vapi.want_l2_macs_events2(enable_disable=0)
541 self.assertEqual(len(learned_macs ^ macs), 0)
543 def test_l2_fib_macs_learn_max(self):
544 """L2 FIB - mac learning max macs in event"""
546 hosts = self.create_hosts(10, subnet=40)
549 self.vapi.want_l2_macs_events(max_macs_in_event=ev_macs)
550 self.learn_hosts(bd1, hosts)
553 self.logger.info(self.vapi.ppcli("show l2fib"))
554 evs = self.vapi.collect_events()
555 self.vapi.want_l2_macs_events(enable_disable=0)
557 self.assertGreater(len(evs), 0)
558 action = VppEnum.vl_api_mac_event_action_t.MAC_EVENT_ACTION_API_ADD
560 e.mac[i].mac_addr.packed
562 for i in range(e.n_macs)
563 if e.mac[i].action == action
567 for swif in self.bd_ifs(bd1)
568 for h in hosts[self.pg_interfaces[swif].sw_if_index]
572 self.assertLess(len(e), ev_macs * 10)
573 self.assertEqual(len(learned_macs ^ macs), 0)
575 def test_l2_fib_macs_learn_max2(self):
576 """L2 FIB - mac learning max macs in event using want_l2_macs_events2"""
578 hosts = self.create_hosts(10, subnet=40)
581 self.vapi.l2fib_set_scan_delay(scan_delay=10)
582 self.vapi.want_l2_macs_events2(max_macs_in_event=ev_macs)
584 self.learn_hosts(bd1, hosts)
586 self.virtual_sleep(1)
587 self.logger.info(self.vapi.ppcli("show l2fib"))
588 evs = self.vapi.collect_events()
589 self.vapi.want_l2_macs_events2(enable_disable=0)
591 self.assertGreater(len(evs), 0)
592 action = VppEnum.vl_api_mac_event_action_t.MAC_EVENT_ACTION_API_ADD
594 e.mac[i].mac_addr.packed
596 for i in range(e.n_macs)
597 if e.mac[i].action == action
601 for swif in self.bd_ifs(bd1)
602 for h in hosts[self.pg_interfaces[swif].sw_if_index]
606 self.assertLess(len(e), ev_macs * 10)
607 self.assertEqual(len(learned_macs ^ macs), 0)
610 if __name__ == "__main__":
611 unittest.main(testRunner=VppTestRunner)