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 super(TestL2fib, self).setUp()
136 self.reset_packet_infos()
140 Show various debug prints after each test.
142 super(TestL2fib, self).tearDown()
143 if not self.vpp_dead:
144 self.logger.info(self.vapi.ppcli("show l2fib verbose"))
145 for bd_id in self.n_brs:
146 self.logger.info(self.vapi.ppcli("show bridge-domain %s detail"
149 def create_hosts(self, n_hosts_per_if, subnet):
151 Create required number of host MAC addresses and distribute them among
152 interfaces. Create host IPv4 address for every host MAC address.
154 :param int n_hosts_per_if: Number of per interface hosts to
155 create MAC/IPv4 addresses for.
159 for pg_if in self.pg_interfaces:
160 swif = pg_if.sw_if_index
162 def mac(j): return "00:00:%02x:ff:%02x:%02x" % (subnet, swif, j)
164 def ip(j): return "172.%02u.1%02x.%u" % (subnet, swif, j)
166 def h(j): return Host(mac(j), ip(j))
167 hosts[swif] = [h(j) for j in range(n_hosts_per_if)]
170 def split_hosts(self, hosts, n):
172 for pg_if in self.pg_interfaces:
173 swif = pg_if.sw_if_index
174 splits[swif] = hosts[swif][:n]
175 hosts[swif] = hosts[swif][n:]
178 def learn_hosts(self, bd_id, hosts):
180 Create and send per interface L2 MAC broadcast packet stream to
181 let the bridge domain learn these MAC addresses.
183 :param int bd_id: BD to teach
184 :param dict hosts: dict of hosts per interface
186 self.vapi.bridge_flags(bd_id=bd_id, is_set=1, flags=1)
187 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
189 swif = pg_if.sw_if_index
190 packets = [Ether(dst="ff:ff:ff:ff:ff:ff", src=host.mac)
191 for host in hosts[swif]]
192 pg_if.add_stream(packets)
193 self.logger.info("Sending broadcast eth frames for MAC learning")
196 def config_l2_fib_entries(self, bd_id, hosts):
198 Config required number of L2 FIB entries.
200 :param int bd_id: BD's id
201 :param int count: Number of L2 FIB entries to be created.
202 :param int start: Starting index of the host list. (Default value = 0)
204 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
206 swif = pg_if.sw_if_index
207 for host in hosts[swif]:
208 self.vapi.l2fib_add_del(
209 mac_pton(host.mac), bd_id, swif, static_mac=1)
211 def delete_l2_fib_entry(self, bd_id, hosts):
213 Delete required number of L2 FIB entries.
215 :param int count: Number of L2 FIB entries to be created.
217 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
219 swif = pg_if.sw_if_index
220 for host in hosts[swif]:
221 self.vapi.l2fib_add_del(
222 mac_pton(host.mac), bd_id, swif, is_add=0)
224 def flush_int(self, swif, learned_hosts):
226 Flush swif L2 FIB entries.
228 :param int swif: sw if index.
231 self.vapi.l2fib_flush_int(swif)
232 flushed[swif] = learned_hosts[swif]
233 learned_hosts[swif] = []
236 def flush_bd(self, bd_id, learned_hosts):
238 Flush bd_id L2 FIB entries.
240 :param int bd_id: Bridge Domain id.
242 self.vapi.l2fib_flush_bd(bd_id)
244 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
246 swif = pg_if.sw_if_index
247 flushed[swif] = learned_hosts[swif]
248 learned_hosts[swif] = []
253 Flush All L2 FIB entries.
255 self.vapi.l2fib_flush_all()
257 def create_stream(self, src_if, packet_sizes, if_src_hosts, if_dst_hosts):
259 Create input packet stream for defined interface using hosts or
262 :param object src_if: Interface to create packet stream for.
263 :param list packet_sizes: List of required packet sizes.
264 :param boolean deleted: Set to True if deleted_hosts list required.
265 :return: Stream of packets.
267 src_hosts = if_src_hosts[src_if.sw_if_index]
271 for dst_if in self.flows[src_if]:
272 dst_swif = dst_if.sw_if_index
273 if dst_swif not in if_dst_hosts:
275 dst_hosts = if_dst_hosts[dst_swif]
276 for dst_host in dst_hosts:
277 src_host = random.choice(src_hosts)
278 pkt_info = self.create_packet_info(src_if, dst_if)
279 payload = self.info_to_payload(pkt_info)
280 p = (Ether(dst=dst_host.mac, src=src_host.mac) /
281 IP(src=src_host.ip4, dst=dst_host.ip4) /
282 UDP(sport=1234, dport=1234) /
284 pkt_info.data = p.copy()
285 size = random.choice(packet_sizes)
286 self.extend_packet(p, size)
290 def verify_capture(self, pg_if, capture):
292 Verify captured input packet stream for defined interface.
294 :param object pg_if: Interface to verify captured packet stream for.
295 :param list capture: Captured packet stream.
298 for i in self.pg_interfaces:
299 last_info[i.sw_if_index] = None
300 dst_sw_if_index = pg_if.sw_if_index
301 for packet in capture:
302 payload_info = self.payload_to_info(packet[Raw])
306 packet_index = payload_info.index
307 self.assertEqual(payload_info.dst, dst_sw_if_index)
308 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
309 (pg_if.name, payload_info.src, packet_index))
310 next_info = self.get_next_packet_info_for_interface2(
311 payload_info.src, dst_sw_if_index,
312 last_info[payload_info.src])
313 last_info[payload_info.src] = next_info
314 self.assertTrue(next_info is not None)
315 self.assertEqual(packet_index, next_info.index)
316 saved_packet = next_info.data
317 # Check standard fields
318 self.assertEqual(ip.src, saved_packet[IP].src)
319 self.assertEqual(ip.dst, saved_packet[IP].dst)
320 self.assertEqual(udp.sport, saved_packet[UDP].sport)
321 self.assertEqual(udp.dport, saved_packet[UDP].dport)
323 self.logger.error(ppp("Unexpected or invalid packet:", packet))
325 for i in self.pg_interfaces:
326 remaining_packet = self.get_next_packet_info_for_interface2(
327 i, dst_sw_if_index, last_info[i.sw_if_index])
329 remaining_packet is None,
330 "Port %u: Packet expected from source %u didn't arrive" %
331 (dst_sw_if_index, i.sw_if_index))
333 def run_verify_test(self, bd_id, src_hosts, dst_hosts):
335 # Create incoming packet streams for packet-generator interfaces
336 self.reset_packet_infos()
337 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
339 pkts = self.create_stream(
340 i, self.pg_if_packet_sizes,
341 if_src_hosts=src_hosts,
342 if_dst_hosts=dst_hosts)
346 self.vapi.bridge_flags(bd_id=bd_id, is_set=0, flags=1)
347 # Enable packet capture and start packet sending
348 self.pg_enable_capture(ifs)
352 # Verify outgoing packet streams per packet-generator interface
354 if not dst_hosts[i.sw_if_index]:
356 capture = i.get_capture()
357 self.logger.info("Verifying capture on interface %s" % i.name)
358 self.verify_capture(i, capture)
360 def run_verify_negat_test(self, bd_id, src_hosts, dst_hosts):
362 # Create incoming packet streams for packet-generator interfaces for
363 # deleted MAC addresses
364 self.reset_packet_infos()
365 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
367 pkts = self.create_stream(
368 i, self.pg_if_packet_sizes,
369 if_src_hosts=src_hosts,
370 if_dst_hosts=dst_hosts)
374 self.vapi.bridge_flags(bd_id=bd_id, is_set=0, flags=1)
375 # Enable packet capture and start packet sending
376 self.pg_enable_capture(ifs)
380 # Verify outgoing packet streams per packet-generator interface
383 i.get_capture(0, timeout=timeout)
384 i.assert_nothing_captured(remark="outgoing interface")
387 def test_l2_fib_program100(self):
388 """ L2 FIB - program 100 MACs
391 hosts = self.create_hosts(100, subnet=17)
392 self.config_l2_fib_entries(bd_id, hosts)
393 self.run_verify_test(bd_id, hosts, hosts)
395 def test_l2_fib_program100_delete12(self):
396 """ L2 FIB - program 100, delete 12 MACs
399 hosts = self.create_hosts(100, subnet=17)
400 self.config_l2_fib_entries(bd_id, hosts)
401 del_hosts = self.split_hosts(hosts, 12)
402 self.delete_l2_fib_entry(bd_id, del_hosts)
404 self.run_verify_test(bd_id, hosts, hosts)
405 self.run_verify_negat_test(bd_id, hosts, del_hosts)
407 def test_l2_fib_program100_add100(self):
408 """ L2 FIB - program 100, add 100 MACs
411 hosts = self.create_hosts(100, subnet=17)
412 self.config_l2_fib_entries(bd_id, hosts)
413 hosts2 = self.create_hosts(100, subnet=22)
414 self.config_l2_fib_entries(bd_id, hosts2)
415 self.run_verify_test(bd_id, hosts, hosts2)
417 def test_l2_fib_program10_learn10(self):
418 """ L2 FIB - program 10 MACs, learn 10
420 hosts = self.create_hosts(20, subnet=35)
421 lhosts = self.split_hosts(hosts, 10)
425 self.learn_hosts(bd1, lhosts)
426 self.learn_hosts(bd2, lhosts)
427 self.config_l2_fib_entries(bd1, hosts)
428 self.config_l2_fib_entries(bd2, hosts)
429 self.run_verify_test(bd1, lhosts, hosts)
430 self.run_verify_test(bd2, lhosts, hosts)
432 def test_l2_fib_flush_int(self):
433 """ L2 FIB - flush interface
435 hosts = self.create_hosts(20, subnet=36)
436 lhosts = self.split_hosts(hosts, 10)
439 self.learn_hosts(bd1, lhosts)
440 self.config_l2_fib_entries(bd1, hosts)
441 self.run_verify_test(bd1, lhosts, hosts)
442 flushed = self.flush_int(self.pg_interfaces[0].sw_if_index, lhosts)
443 self.run_verify_test(bd1, hosts, lhosts)
444 self.run_verify_negat_test(bd1, hosts, flushed)
446 def test_l2_fib_flush_bd(self):
447 """ L2 FIB - flush BD
449 hosts = self.create_hosts(20, subnet=37)
450 lhosts = self.split_hosts(hosts, 10)
453 self.learn_hosts(bd1, lhosts)
454 self.config_l2_fib_entries(bd1, hosts)
455 self.run_verify_test(bd1, lhosts, hosts)
456 flushed = self.flush_bd(bd1, lhosts)
457 self.run_verify_negat_test(bd1, hosts, flushed)
459 def test_l2_fib_flush_all(self):
460 """ L2 FIB - flush all
462 hosts = self.create_hosts(20, subnet=38)
463 lhosts = self.split_hosts(hosts, 10)
467 self.learn_hosts(bd1, lhosts)
468 self.learn_hosts(bd2, lhosts)
469 self.config_l2_fib_entries(bd1, hosts)
470 self.config_l2_fib_entries(bd2, hosts)
471 self.run_verify_test(bd1, hosts, lhosts)
472 self.run_verify_test(bd2, hosts, lhosts)
476 self.run_verify_negat_test(bd1, hosts, lhosts)
477 self.run_verify_negat_test(bd2, hosts, lhosts)
479 def test_l2_fib_mac_learn_evs(self):
480 """ L2 FIB - mac learning events
483 hosts = self.create_hosts(10, subnet=39)
485 self.vapi.want_l2_macs_events()
486 self.learn_hosts(bd1, hosts)
489 self.logger.info(self.vapi.ppcli("show l2fib"))
490 evs = self.vapi.collect_events()
492 e.mac[i].mac_addr for e in evs for i in range(e.n_macs)
493 if e.mac[i].action == MAC_EVENT_ACTION_ADD}
494 macs = {h.bin_mac for swif in self.bd_ifs(bd1)
495 for h in hosts[self.pg_interfaces[swif].sw_if_index]}
496 self.vapi.want_l2_macs_events(enable_disable=0)
497 self.assertEqual(len(learned_macs ^ macs), 0)
499 def test_l2_fib_macs_learn_max(self):
500 """ L2 FIB - mac learning max macs in event
503 hosts = self.create_hosts(10, subnet=40)
506 self.vapi.want_l2_macs_events(max_macs_in_event=ev_macs)
507 self.learn_hosts(bd1, hosts)
510 self.logger.info(self.vapi.ppcli("show l2fib"))
511 evs = self.vapi.collect_events()
512 self.vapi.want_l2_macs_events(enable_disable=0)
514 self.assertGreater(len(evs), 0)
516 e.mac[i].mac_addr for e in evs for i in range(e.n_macs)
517 if e.mac[i].action == MAC_EVENT_ACTION_ADD}
518 macs = {h.bin_mac for swif in self.bd_ifs(bd1)
519 for h in hosts[self.pg_interfaces[swif].sw_if_index]}
522 self.assertLess(len(e), ev_macs * 10)
523 self.assertEqual(len(learned_macs ^ macs), 0)
526 if __name__ == '__main__':
527 unittest.main(testRunner=VppTestRunner)