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
73 # from src/vnet/l2/l2_fib.h
74 MAC_EVENT_ACTION_ADD = 0
75 MAC_EVENT_ACTION_DELETE = 1
76 MAC_EVENT_ACTION_MOVE = 2
79 class TestL2fib(VppTestCase):
80 """ L2 FIB Test Case """
83 def bd_ifs(cls, bd_id):
84 return range((bd_id - 1) * cls.n_ifs_per_bd, bd_id * cls.n_ifs_per_bd)
89 Perform standard class setup (defined by class method setUpClass in
90 class VppTestCase) before running the test case, set test case related
91 variables and configure VPP.
93 :var int bd_id: Bridge domain ID.
95 super(TestL2fib, cls).setUpClass()
98 n_brs = cls.n_brs = range(1, 3)
100 n_ifs = range(cls.n_ifs_per_bd * len(cls.n_brs))
101 # Create 4 pg interfaces
102 cls.create_pg_interfaces(n_ifs)
106 # Packet flows mapping pg0 -> pg1, pg2, pg3 etc.
107 ifs = cls.bd_ifs(bd_id)
109 cls.flows[cls.pg_interfaces[j]] = [
110 cls.pg_interfaces[x] for x in ifs if x != j]
113 cls.pg_if_packet_sizes = [64, 512, 1518, 9018]
116 # Create BD with MAC learning and unknown unicast flooding
117 # disabled and put interfaces to this BD
118 cls.vapi.bridge_domain_add_del(
119 bd_id=bd_id, uu_flood=0, learn=0)
120 ifs = [cls.pg_interfaces[i] for i in cls.bd_ifs(bd_id)]
122 cls.vapi.sw_interface_set_l2_bridge(pg_if.sw_if_index,
125 # Set up all interfaces
126 for i in cls.pg_interfaces:
129 super(TestL2fib, cls).tearDownClass()
133 super(TestL2fib, self).setUp()
134 self.reset_packet_infos()
138 Show various debug prints after each test.
140 super(TestL2fib, self).tearDown()
141 if not self.vpp_dead:
142 self.logger.info(self.vapi.ppcli("show l2fib verbose"))
143 for bd_id in self.n_brs:
144 self.logger.info(self.vapi.ppcli("show bridge-domain %s detail"
147 def create_hosts(self, n_hosts_per_if, subnet):
149 Create required number of host MAC addresses and distribute them among
150 interfaces. Create host IPv4 address for every host MAC address.
152 :param int n_hosts_per_if: Number of per interface hosts to
153 create MAC/IPv4 addresses for.
157 for pg_if in self.pg_interfaces:
158 swif = pg_if.sw_if_index
160 def mac(j): return "00:00:%02x:ff:%02x:%02x" % (subnet, swif, j)
162 def ip(j): return "172.%02u.1%02x.%u" % (subnet, swif, j)
164 def h(j): return Host(mac(j), ip(j))
165 hosts[swif] = [h(j) for j in range(n_hosts_per_if)]
168 def split_hosts(self, hosts, n):
170 for pg_if in self.pg_interfaces:
171 swif = pg_if.sw_if_index
172 splits[swif] = hosts[swif][:n]
173 hosts[swif] = hosts[swif][n:]
176 def learn_hosts(self, bd_id, hosts):
178 Create and send per interface L2 MAC broadcast packet stream to
179 let the bridge domain learn these MAC addresses.
181 :param int bd_id: BD to teach
182 :param dict hosts: dict of hosts per interface
184 self.vapi.bridge_flags(bd_id, 1, 1)
185 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
187 swif = pg_if.sw_if_index
188 packets = [Ether(dst="ff:ff:ff:ff:ff:ff", src=host.mac)
189 for host in hosts[swif]]
190 pg_if.add_stream(packets)
191 self.logger.info("Sending broadcast eth frames for MAC learning")
194 def config_l2_fib_entries(self, bd_id, hosts):
196 Config required number of L2 FIB entries.
198 :param int bd_id: BD's id
199 :param int count: Number of L2 FIB entries to be created.
200 :param int start: Starting index of the host list. (Default value = 0)
202 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
204 swif = pg_if.sw_if_index
205 for host in hosts[swif]:
206 self.vapi.l2fib_add_del(
207 host.mac, bd_id, swif, static_mac=1)
209 def delete_l2_fib_entry(self, bd_id, hosts):
211 Delete required number of L2 FIB entries.
213 :param int count: Number of L2 FIB entries to be created.
215 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
217 swif = pg_if.sw_if_index
218 for host in hosts[swif]:
219 self.vapi.l2fib_add_del(
220 host.mac, bd_id, swif, is_add=0)
222 def flush_int(self, swif, learned_hosts):
224 Flush swif L2 FIB entries.
226 :param int swif: sw if index.
229 self.vapi.l2fib_flush_int(swif)
230 flushed[swif] = learned_hosts[swif]
231 learned_hosts[swif] = []
234 def flush_bd(self, bd_id, learned_hosts):
236 Flush bd_id L2 FIB entries.
238 :param int bd_id: Bridge Domain id.
240 self.vapi.l2fib_flush_bd(bd_id)
242 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
244 swif = pg_if.sw_if_index
245 flushed[swif] = learned_hosts[swif]
246 learned_hosts[swif] = []
251 Flush All L2 FIB entries.
253 self.vapi.l2fib_flush_all()
255 def create_stream(self, src_if, packet_sizes, if_src_hosts, if_dst_hosts):
257 Create input packet stream for defined interface using hosts or
260 :param object src_if: Interface to create packet stream for.
261 :param list packet_sizes: List of required packet sizes.
262 :param boolean deleted: Set to True if deleted_hosts list required.
263 :return: Stream of packets.
265 src_hosts = if_src_hosts[src_if.sw_if_index]
269 for dst_if in self.flows[src_if]:
270 dst_swif = dst_if.sw_if_index
271 if dst_swif not in if_dst_hosts:
273 dst_hosts = if_dst_hosts[dst_swif]
274 for dst_host in dst_hosts:
275 src_host = random.choice(src_hosts)
276 pkt_info = self.create_packet_info(src_if, dst_if)
277 payload = self.info_to_payload(pkt_info)
278 p = (Ether(dst=dst_host.mac, src=src_host.mac) /
279 IP(src=src_host.ip4, dst=dst_host.ip4) /
280 UDP(sport=1234, dport=1234) /
282 pkt_info.data = p.copy()
283 size = random.choice(packet_sizes)
284 self.extend_packet(p, size)
288 def verify_capture(self, pg_if, capture):
290 Verify captured input packet stream for defined interface.
292 :param object pg_if: Interface to verify captured packet stream for.
293 :param list capture: Captured packet stream.
296 for i in self.pg_interfaces:
297 last_info[i.sw_if_index] = None
298 dst_sw_if_index = pg_if.sw_if_index
299 for packet in capture:
300 payload_info = self.payload_to_info(str(packet[Raw]))
304 packet_index = payload_info.index
305 self.assertEqual(payload_info.dst, dst_sw_if_index)
306 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
307 (pg_if.name, payload_info.src, packet_index))
308 next_info = self.get_next_packet_info_for_interface2(
309 payload_info.src, dst_sw_if_index,
310 last_info[payload_info.src])
311 last_info[payload_info.src] = next_info
312 self.assertTrue(next_info is not None)
313 self.assertEqual(packet_index, next_info.index)
314 saved_packet = next_info.data
315 # Check standard fields
316 self.assertEqual(ip.src, saved_packet[IP].src)
317 self.assertEqual(ip.dst, saved_packet[IP].dst)
318 self.assertEqual(udp.sport, saved_packet[UDP].sport)
319 self.assertEqual(udp.dport, saved_packet[UDP].dport)
321 self.logger.error(ppp("Unexpected or invalid packet:", packet))
323 for i in self.pg_interfaces:
324 remaining_packet = self.get_next_packet_info_for_interface2(
325 i, dst_sw_if_index, last_info[i.sw_if_index])
327 remaining_packet is None,
328 "Port %u: Packet expected from source %u didn't arrive" %
329 (dst_sw_if_index, i.sw_if_index))
331 def run_verify_test(self, bd_id, src_hosts, dst_hosts):
333 # Create incoming packet streams for packet-generator interfaces
334 self.reset_packet_infos()
335 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
337 pkts = self.create_stream(
338 i, self.pg_if_packet_sizes,
339 if_src_hosts=src_hosts,
340 if_dst_hosts=dst_hosts)
344 self.vapi.bridge_flags(bd_id, 0, 1)
345 # Enable packet capture and start packet sending
346 self.pg_enable_capture(ifs)
350 # Verify outgoing packet streams per packet-generator interface
352 if not dst_hosts[i.sw_if_index]:
354 capture = i.get_capture()
355 self.logger.info("Verifying capture on interface %s" % i.name)
356 self.verify_capture(i, capture)
358 def run_verify_negat_test(self, bd_id, src_hosts, dst_hosts):
360 # Create incoming packet streams for packet-generator interfaces for
361 # deleted MAC addresses
362 self.reset_packet_infos()
363 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
365 pkts = self.create_stream(
366 i, self.pg_if_packet_sizes,
367 if_src_hosts=src_hosts,
368 if_dst_hosts=dst_hosts)
372 self.vapi.bridge_flags(bd_id, 0, 1)
373 # Enable packet capture and start packet sending
374 self.pg_enable_capture(ifs)
378 # Verify outgoing packet streams per packet-generator interface
381 i.get_capture(0, timeout=timeout)
382 i.assert_nothing_captured(remark="outgoing interface")
385 def test_l2_fib_program100(self):
386 """ L2 FIB - program 100 MACs
389 hosts = self.create_hosts(100, subnet=17)
390 self.config_l2_fib_entries(bd_id, hosts)
391 self.run_verify_test(bd_id, hosts, hosts)
393 def test_l2_fib_delete12(self):
394 """ L2 FIB - program 100 + delete 12 MACs
397 hosts = self.create_hosts(100, subnet=17)
398 self.config_l2_fib_entries(bd_id, hosts)
399 del_hosts = self.split_hosts(hosts, 12)
400 self.delete_l2_fib_entry(bd_id, del_hosts)
402 self.run_verify_test(bd_id, hosts, hosts)
403 self.run_verify_negat_test(bd_id, hosts, del_hosts)
405 def test_l2_fib_add100_add100(self):
406 """ L2 FIB - program 100 + 100 MACs
409 hosts = self.create_hosts(100, subnet=17)
410 self.config_l2_fib_entries(bd_id, hosts)
411 hosts2 = self.create_hosts(100, subnet=22)
412 self.config_l2_fib_entries(bd_id, hosts2)
413 self.run_verify_test(bd_id, hosts, hosts2)
415 def test_l2_fib_program10_learn10(self):
416 """ L2 FIB - Program 10 MACs, learn 10
418 hosts = self.create_hosts(20, subnet=35)
419 lhosts = self.split_hosts(hosts, 10)
423 self.learn_hosts(bd1, lhosts)
424 self.learn_hosts(bd2, lhosts)
425 self.config_l2_fib_entries(bd1, hosts)
426 self.config_l2_fib_entries(bd2, hosts)
427 self.run_verify_test(bd1, lhosts, hosts)
428 self.run_verify_test(bd2, lhosts, hosts)
430 def test_l2_fib_flush_int(self):
431 """ L2 FIB - flush interface
433 hosts = self.create_hosts(20, subnet=36)
434 lhosts = self.split_hosts(hosts, 10)
437 self.learn_hosts(bd1, lhosts)
438 self.config_l2_fib_entries(bd1, hosts)
439 self.run_verify_test(bd1, lhosts, hosts)
440 flushed = self.flush_int(self.pg_interfaces[0].sw_if_index, lhosts)
441 self.run_verify_test(bd1, hosts, lhosts)
442 self.run_verify_negat_test(bd1, hosts, flushed)
444 def test_l2_fib_flush_bd(self):
445 """ L2 FIB - flush BD
447 hosts = self.create_hosts(20, subnet=37)
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_bd(bd1, lhosts)
455 self.run_verify_negat_test(bd1, hosts, flushed)
457 def test_l2_fib_flush_all(self):
458 """ L2 FIB - flush all
460 hosts = self.create_hosts(20, subnet=38)
461 lhosts = self.split_hosts(hosts, 10)
465 self.learn_hosts(bd1, lhosts)
466 self.learn_hosts(bd2, lhosts)
467 self.config_l2_fib_entries(bd1, hosts)
468 self.config_l2_fib_entries(bd2, hosts)
469 self.run_verify_test(bd1, hosts, lhosts)
470 self.run_verify_test(bd2, hosts, lhosts)
474 self.run_verify_negat_test(bd1, hosts, lhosts)
475 self.run_verify_negat_test(bd2, hosts, lhosts)
477 def test_l2_fib_mac_learn_evs(self):
478 """ L2 FIB - mac learning events
481 hosts = self.create_hosts(10, subnet=39)
483 self.vapi.want_macs_learn_events()
484 self.learn_hosts(bd1, hosts)
487 self.logger.info(self.vapi.ppcli("show l2fib"))
488 evs = self.vapi.collect_events()
490 e.mac[i].mac_addr for e in evs for i in range(e.n_macs)
491 if e.mac[i].action == MAC_EVENT_ACTION_ADD}
492 macs = {h.bin_mac for swif in self.bd_ifs(bd1)
493 for h in hosts[self.pg_interfaces[swif].sw_if_index]}
494 self.vapi.want_macs_learn_events(enable_disable=0)
495 self.assertEqual(len(learned_macs ^ macs), 0)
497 def test_l2_fib_macs_learn_max(self):
498 """ L2 FIB - mac learning max macs in event
501 hosts = self.create_hosts(10, subnet=40)
504 self.vapi.want_macs_learn_events(max_macs_in_event=ev_macs)
505 self.learn_hosts(bd1, hosts)
508 self.logger.info(self.vapi.ppcli("show l2fib"))
509 evs = self.vapi.collect_events()
510 self.vapi.want_macs_learn_events(enable_disable=0)
512 self.assertGreater(len(evs), 0)
514 e.mac[i].mac_addr for e in evs for i in range(e.n_macs)
515 if e.mac[i].action == MAC_EVENT_ACTION_ADD}
516 macs = {h.bin_mac for swif in self.bd_ifs(bd1)
517 for h in hosts[self.pg_interfaces[swif].sw_if_index]}
520 self.assertLess(len(e), ev_macs * 10)
521 self.assertEqual(len(learned_macs ^ macs), 0)
524 if __name__ == '__main__':
525 unittest.main(testRunner=VppTestRunner)