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
74 # from src/vnet/l2/l2_fib.h
75 MAC_EVENT_ACTION_ADD = 0
76 MAC_EVENT_ACTION_DELETE = 1
77 MAC_EVENT_ACTION_MOVE = 2
80 class TestL2fib(VppTestCase):
81 """ L2 FIB Test Case """
84 def bd_ifs(cls, bd_id):
85 return range((bd_id - 1) * cls.n_ifs_per_bd,
86 bd_id * cls.n_ifs_per_bd - 1)
91 Perform standard class setup (defined by class method setUpClass in
92 class VppTestCase) before running the test case, set test case related
93 variables and configure VPP.
95 :var int bd_id: Bridge domain ID.
97 super(TestL2fib, cls).setUpClass()
100 n_brs = cls.n_brs = range(1, 3)
102 n_ifs = range(cls.n_ifs_per_bd * len(cls.n_brs))
103 # Create pg interfaces
104 cls.create_pg_interfaces(n_ifs)
108 # Packet flows mapping pg0 -> pg1, pg2, pg3 etc.
109 ifs = cls.bd_ifs(bd_id)
111 cls.flows[cls.pg_interfaces[j]] = [
112 cls.pg_interfaces[x] for x in ifs if x != j]
115 cls.pg_if_packet_sizes = [64, 512, 1518, 9018]
118 # Create BD with MAC learning and unknown unicast flooding
119 # disabled and put interfaces to this BD
120 cls.vapi.bridge_domain_add_del(bd_id=bd_id, uu_flood=0,
122 ifs = [cls.pg_interfaces[i] for i in cls.bd_ifs(bd_id)]
124 cls.vapi.sw_interface_set_l2_bridge(
125 rx_sw_if_index=pg_if.sw_if_index, bd_id=bd_id)
127 # Set up all interfaces
128 for i in cls.pg_interfaces:
131 super(TestL2fib, cls).tearDownClass()
135 def tearDownClass(cls):
136 super(TestL2fib, cls).tearDownClass()
139 super(TestL2fib, self).setUp()
140 self.reset_packet_infos()
144 Show various debug prints after each test.
146 super(TestL2fib, self).tearDown()
147 if not self.vpp_dead:
148 for bd_id in self.n_brs:
149 self.logger.info(self.vapi.ppcli("show bridge-domain %s detail"
152 def show_commands_at_teardown(self):
153 self.logger.info(self.vapi.ppcli("show l2fib verbose"))
155 def create_hosts(self, n_hosts_per_if, subnet):
157 Create required number of host MAC addresses and distribute them among
158 interfaces. Create host IPv4 address for every host MAC address.
160 :param int n_hosts_per_if: Number of per interface hosts to
161 create MAC/IPv4 addresses for.
165 for pg_if in self.pg_interfaces:
166 swif = pg_if.sw_if_index
168 def mac(j): return "00:00:%02x:ff:%02x:%02x" % (subnet, swif, j)
170 def ip(j): return "172.%02u.1%02x.%u" % (subnet, swif, j)
172 def h(j): return Host(mac(j), ip(j))
173 hosts[swif] = [h(j) for j in range(n_hosts_per_if)]
176 def split_hosts(self, hosts, n):
178 for pg_if in self.pg_interfaces:
179 swif = pg_if.sw_if_index
180 splits[swif] = hosts[swif][:n]
181 hosts[swif] = hosts[swif][n:]
184 def learn_hosts(self, bd_id, hosts):
186 Create and send per interface L2 MAC broadcast packet stream to
187 let the bridge domain learn these MAC addresses.
189 :param int bd_id: BD to teach
190 :param dict hosts: dict of hosts per interface
192 self.vapi.bridge_flags(bd_id=bd_id, is_set=1, flags=1)
193 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
195 swif = pg_if.sw_if_index
196 packets = [Ether(dst="ff:ff:ff:ff:ff:ff", src=host.mac)
197 for host in hosts[swif]]
198 pg_if.add_stream(packets)
199 self.logger.info("Sending broadcast eth frames for MAC learning")
202 def config_l2_fib_entries(self, bd_id, hosts):
204 Config required number of L2 FIB entries.
206 :param int bd_id: BD's id
207 :param int count: Number of L2 FIB entries to be created.
208 :param int start: Starting index of the host list. (Default value = 0)
210 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
212 swif = pg_if.sw_if_index
213 for host in hosts[swif]:
214 self.vapi.l2fib_add_del(
215 mac_pton(host.mac), bd_id, swif, static_mac=1)
217 def delete_l2_fib_entry(self, bd_id, hosts):
219 Delete required number of L2 FIB entries.
221 :param int count: Number of L2 FIB entries to be created.
223 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
225 swif = pg_if.sw_if_index
226 for host in hosts[swif]:
227 self.vapi.l2fib_add_del(
228 mac_pton(host.mac), bd_id, swif, is_add=0)
230 def flush_int(self, swif, learned_hosts):
232 Flush swif L2 FIB entries.
234 :param int swif: sw if index.
237 self.vapi.l2fib_flush_int(swif)
238 flushed[swif] = learned_hosts[swif]
239 learned_hosts[swif] = []
242 def flush_bd(self, bd_id, learned_hosts):
244 Flush bd_id L2 FIB entries.
246 :param int bd_id: Bridge Domain id.
248 self.vapi.l2fib_flush_bd(bd_id)
250 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
252 swif = pg_if.sw_if_index
253 flushed[swif] = learned_hosts[swif]
254 learned_hosts[swif] = []
259 Flush All L2 FIB entries.
261 self.vapi.l2fib_flush_all()
263 def create_stream(self, src_if, packet_sizes, if_src_hosts, if_dst_hosts):
265 Create input packet stream for defined interface using hosts or
268 :param object src_if: Interface to create packet stream for.
269 :param list packet_sizes: List of required packet sizes.
270 :param boolean deleted: Set to True if deleted_hosts list required.
271 :return: Stream of packets.
273 src_hosts = if_src_hosts[src_if.sw_if_index]
277 for dst_if in self.flows[src_if]:
278 dst_swif = dst_if.sw_if_index
279 if dst_swif not in if_dst_hosts:
281 dst_hosts = if_dst_hosts[dst_swif]
282 for dst_host in dst_hosts:
283 src_host = random.choice(src_hosts)
284 pkt_info = self.create_packet_info(src_if, dst_if)
285 payload = self.info_to_payload(pkt_info)
286 p = (Ether(dst=dst_host.mac, src=src_host.mac) /
287 IP(src=src_host.ip4, dst=dst_host.ip4) /
288 UDP(sport=1234, dport=1234) /
290 pkt_info.data = p.copy()
291 size = random.choice(packet_sizes)
292 self.extend_packet(p, size)
296 def verify_capture(self, pg_if, capture):
298 Verify captured input packet stream for defined interface.
300 :param object pg_if: Interface to verify captured packet stream for.
301 :param list capture: Captured packet stream.
304 for i in self.pg_interfaces:
305 last_info[i.sw_if_index] = None
306 dst_sw_if_index = pg_if.sw_if_index
307 for packet in capture:
308 payload_info = self.payload_to_info(packet[Raw])
312 packet_index = payload_info.index
313 self.assertEqual(payload_info.dst, dst_sw_if_index)
314 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
315 (pg_if.name, payload_info.src, packet_index))
316 next_info = self.get_next_packet_info_for_interface2(
317 payload_info.src, dst_sw_if_index,
318 last_info[payload_info.src])
319 last_info[payload_info.src] = next_info
320 self.assertTrue(next_info is not None)
321 self.assertEqual(packet_index, next_info.index)
322 saved_packet = next_info.data
323 # Check standard fields
324 self.assertEqual(ip.src, saved_packet[IP].src)
325 self.assertEqual(ip.dst, saved_packet[IP].dst)
326 self.assertEqual(udp.sport, saved_packet[UDP].sport)
327 self.assertEqual(udp.dport, saved_packet[UDP].dport)
329 self.logger.error(ppp("Unexpected or invalid packet:", packet))
331 for i in self.pg_interfaces:
332 remaining_packet = self.get_next_packet_info_for_interface2(
333 i, dst_sw_if_index, last_info[i.sw_if_index])
335 remaining_packet is None,
336 "Port %u: Packet expected from source %u didn't arrive" %
337 (dst_sw_if_index, i.sw_if_index))
339 def run_verify_test(self, bd_id, src_hosts, dst_hosts):
341 # Create incoming packet streams for packet-generator interfaces
342 self.reset_packet_infos()
343 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
345 pkts = self.create_stream(
346 i, self.pg_if_packet_sizes,
347 if_src_hosts=src_hosts,
348 if_dst_hosts=dst_hosts)
352 self.vapi.bridge_flags(bd_id=bd_id, is_set=0, flags=1)
353 # Enable packet capture and start packet sending
354 self.pg_enable_capture(ifs)
358 # Verify outgoing packet streams per packet-generator interface
360 if not dst_hosts[i.sw_if_index]:
362 capture = i.get_capture()
363 self.logger.info("Verifying capture on interface %s" % i.name)
364 self.verify_capture(i, capture)
366 def run_verify_negat_test(self, bd_id, src_hosts, dst_hosts):
368 # Create incoming packet streams for packet-generator interfaces for
369 # deleted MAC addresses
370 self.reset_packet_infos()
371 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
373 pkts = self.create_stream(
374 i, self.pg_if_packet_sizes,
375 if_src_hosts=src_hosts,
376 if_dst_hosts=dst_hosts)
380 self.vapi.bridge_flags(bd_id=bd_id, is_set=0, flags=1)
381 # Enable packet capture and start packet sending
382 self.pg_enable_capture(ifs)
386 # Verify outgoing packet streams per packet-generator interface
389 i.get_capture(0, timeout=timeout)
390 i.assert_nothing_captured(remark="outgoing interface")
393 def test_l2_fib_program100(self):
394 """ L2 FIB - program 100 MACs
397 hosts = self.create_hosts(100, subnet=17)
398 self.config_l2_fib_entries(bd_id, hosts)
399 self.run_verify_test(bd_id, hosts, hosts)
401 def test_l2_fib_program100_delete12(self):
402 """ L2 FIB - program 100, delete 12 MACs
405 hosts = self.create_hosts(100, subnet=17)
406 self.config_l2_fib_entries(bd_id, hosts)
407 del_hosts = self.split_hosts(hosts, 12)
408 self.delete_l2_fib_entry(bd_id, del_hosts)
410 self.run_verify_test(bd_id, hosts, hosts)
411 self.run_verify_negat_test(bd_id, hosts, del_hosts)
413 def test_l2_fib_program100_add100(self):
414 """ L2 FIB - program 100, add 100 MACs
417 hosts = self.create_hosts(100, subnet=17)
418 self.config_l2_fib_entries(bd_id, hosts)
419 hosts2 = self.create_hosts(100, subnet=22)
420 self.config_l2_fib_entries(bd_id, hosts2)
421 self.run_verify_test(bd_id, hosts, hosts2)
423 def test_l2_fib_program10_learn10(self):
424 """ L2 FIB - program 10 MACs, learn 10
426 hosts = self.create_hosts(20, subnet=35)
427 lhosts = self.split_hosts(hosts, 10)
431 self.learn_hosts(bd1, lhosts)
432 self.learn_hosts(bd2, lhosts)
433 self.config_l2_fib_entries(bd1, hosts)
434 self.config_l2_fib_entries(bd2, hosts)
435 self.run_verify_test(bd1, lhosts, hosts)
436 self.run_verify_test(bd2, lhosts, hosts)
438 def test_l2_fib_flush_int(self):
439 """ L2 FIB - flush interface
441 hosts = self.create_hosts(20, subnet=36)
442 lhosts = self.split_hosts(hosts, 10)
445 self.learn_hosts(bd1, lhosts)
446 self.config_l2_fib_entries(bd1, hosts)
447 self.run_verify_test(bd1, lhosts, hosts)
448 flushed = self.flush_int(self.pg_interfaces[0].sw_if_index, lhosts)
449 self.run_verify_test(bd1, hosts, lhosts)
450 self.run_verify_negat_test(bd1, hosts, flushed)
452 def test_l2_fib_flush_bd(self):
453 """ L2 FIB - flush BD
455 hosts = self.create_hosts(20, subnet=37)
456 lhosts = self.split_hosts(hosts, 10)
459 self.learn_hosts(bd1, lhosts)
460 self.config_l2_fib_entries(bd1, hosts)
461 self.run_verify_test(bd1, lhosts, hosts)
462 flushed = self.flush_bd(bd1, lhosts)
463 self.run_verify_negat_test(bd1, hosts, flushed)
465 def test_l2_fib_flush_all(self):
466 """ L2 FIB - flush all
468 hosts = self.create_hosts(20, subnet=38)
469 lhosts = self.split_hosts(hosts, 10)
473 self.learn_hosts(bd1, lhosts)
474 self.learn_hosts(bd2, lhosts)
475 self.config_l2_fib_entries(bd1, hosts)
476 self.config_l2_fib_entries(bd2, hosts)
477 self.run_verify_test(bd1, hosts, lhosts)
478 self.run_verify_test(bd2, hosts, lhosts)
482 self.run_verify_negat_test(bd1, hosts, lhosts)
483 self.run_verify_negat_test(bd2, hosts, lhosts)
485 def test_l2_fib_mac_learn_evs(self):
486 """ L2 FIB - mac learning events
489 hosts = self.create_hosts(10, subnet=39)
491 self.vapi.want_l2_macs_events()
492 self.learn_hosts(bd1, hosts)
495 self.logger.info(self.vapi.ppcli("show l2fib"))
496 evs = self.vapi.collect_events()
498 e.mac[i].mac_addr for e in evs for i in range(e.n_macs)
499 if e.mac[i].action == MAC_EVENT_ACTION_ADD}
500 macs = {h.bin_mac for swif in self.bd_ifs(bd1)
501 for h in hosts[self.pg_interfaces[swif].sw_if_index]}
502 self.vapi.want_l2_macs_events(enable_disable=0)
503 self.assertEqual(len(learned_macs ^ macs), 0)
505 def test_l2_fib_macs_learn_max(self):
506 """ L2 FIB - mac learning max macs in event
509 hosts = self.create_hosts(10, subnet=40)
512 self.vapi.want_l2_macs_events(max_macs_in_event=ev_macs)
513 self.learn_hosts(bd1, hosts)
516 self.logger.info(self.vapi.ppcli("show l2fib"))
517 evs = self.vapi.collect_events()
518 self.vapi.want_l2_macs_events(enable_disable=0)
520 self.assertGreater(len(evs), 0)
522 e.mac[i].mac_addr for e in evs for i in range(e.n_macs)
523 if e.mac[i].action == MAC_EVENT_ACTION_ADD}
524 macs = {h.bin_mac for swif in self.bd_ifs(bd1)
525 for h in hosts[self.pg_interfaces[swif].sw_if_index]}
528 self.assertLess(len(e), ev_macs * 10)
529 self.assertEqual(len(learned_macs ^ macs), 0)
532 if __name__ == '__main__':
533 unittest.main(testRunner=VppTestRunner)