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
74 class TestL2fib(VppTestCase):
75 """ L2 FIB Test Case """
78 def bd_ifs(cls, bd_id):
79 return range((bd_id - 1) * cls.n_ifs_per_bd, bd_id * cls.n_ifs_per_bd)
84 Perform standard class setup (defined by class method setUpClass in
85 class VppTestCase) before running the test case, set test case related
86 variables and configure VPP.
88 :var int bd_id: Bridge domain ID.
90 super(TestL2fib, cls).setUpClass()
93 n_brs = cls.n_brs = range(1, 3)
95 n_ifs = range(cls.n_ifs_per_bd * len(cls.n_brs))
96 # Create 4 pg interfaces
97 cls.create_pg_interfaces(n_ifs)
101 # Packet flows mapping pg0 -> pg1, pg2, pg3 etc.
102 ifs = cls.bd_ifs(bd_id)
104 cls.flows[cls.pg_interfaces[j]] = [
105 cls.pg_interfaces[x] for x in ifs if x != j]
108 cls.pg_if_packet_sizes = [64, 512, 1518, 9018]
111 # Create BD with MAC learning and unknown unicast flooding
112 # disabled and put interfaces to this BD
113 cls.vapi.bridge_domain_add_del(
114 bd_id=bd_id, uu_flood=0, learn=0)
115 ifs = [cls.pg_interfaces[i] for i in cls.bd_ifs(bd_id)]
117 cls.vapi.sw_interface_set_l2_bridge(pg_if.sw_if_index,
120 # Set up all interfaces
121 for i in cls.pg_interfaces:
124 super(TestL2fib, cls).tearDownClass()
128 super(TestL2fib, self).setUp()
129 self.reset_packet_infos()
133 Show various debug prints after each test.
135 super(TestL2fib, self).tearDown()
136 if not self.vpp_dead:
137 self.logger.info(self.vapi.ppcli("show l2fib verbose"))
138 for bd_id in self.n_brs:
139 self.logger.info(self.vapi.ppcli("show bridge-domain %s detail"
142 def create_hosts(self, n_hosts_per_if, subnet):
144 Create required number of host MAC addresses and distribute them among
145 interfaces. Create host IPv4 address for every host MAC address.
147 :param int n_hosts_per_if: Number of per interface hosts to
148 create MAC/IPv4 addresses for.
152 for pg_if in self.pg_interfaces:
153 swif = pg_if.sw_if_index
155 def mac(j): return "00:00:%02x:ff:%02x:%02x" % (subnet, swif, j)
157 def ip(j): return "172.%02u.1%02x.%u" % (subnet, swif, j)
159 def h(j): return Host(mac(j), ip(j))
160 hosts[swif] = [h(j) for j in range(n_hosts_per_if)]
163 def split_hosts(self, hosts, n):
165 for pg_if in self.pg_interfaces:
166 swif = pg_if.sw_if_index
167 splits[swif] = hosts[swif][:n]
168 hosts[swif] = hosts[swif][n:]
171 def learn_hosts(self, bd_id, hosts):
173 Create and send per interface L2 MAC broadcast packet stream to
174 let the bridge domain learn these MAC addresses.
176 :param int bd_id: BD to teach
177 :param dict hosts: dict of hosts per interface
179 self.vapi.bridge_flags(bd_id, 1, 1)
180 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
182 swif = pg_if.sw_if_index
183 packets = [Ether(dst="ff:ff:ff:ff:ff:ff", src=host.mac)
184 for host in hosts[swif]]
185 pg_if.add_stream(packets)
186 self.logger.info("Sending broadcast eth frames for MAC learning")
189 def config_l2_fib_entries(self, bd_id, hosts):
191 Config required number of L2 FIB entries.
193 :param int bd_id: BD's id
194 :param int count: Number of L2 FIB entries to be created.
195 :param int start: Starting index of the host list. (Default value = 0)
197 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
199 swif = pg_if.sw_if_index
200 for host in hosts[swif]:
201 self.vapi.l2fib_add_del(
202 host.mac, bd_id, swif, static_mac=1)
204 def delete_l2_fib_entry(self, bd_id, hosts):
206 Delete required number of L2 FIB entries.
208 :param int count: Number of L2 FIB entries to be created.
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 host.mac, bd_id, swif, is_add=0)
217 def flush_int(self, swif, learned_hosts):
219 Flush swif L2 FIB entries.
221 :param int swif: sw if index.
224 self.vapi.l2fib_flush_int(swif)
225 flushed[swif] = learned_hosts[swif]
226 learned_hosts[swif] = []
229 def flush_bd(self, bd_id, learned_hosts):
231 Flush bd_id L2 FIB entries.
233 :param int bd_id: Bridge Domain id.
235 self.vapi.l2fib_flush_bd(bd_id)
237 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
239 swif = pg_if.sw_if_index
240 flushed[swif] = learned_hosts[swif]
241 learned_hosts[swif] = []
246 Flush All L2 FIB entries.
248 self.vapi.l2fib_flush_all()
250 def create_stream(self, src_if, packet_sizes, if_src_hosts, if_dst_hosts):
252 Create input packet stream for defined interface using hosts or
255 :param object src_if: Interface to create packet stream for.
256 :param list packet_sizes: List of required packet sizes.
257 :param boolean deleted: Set to True if deleted_hosts list required.
258 :return: Stream of packets.
260 src_hosts = if_src_hosts[src_if.sw_if_index]
264 for dst_if in self.flows[src_if]:
265 dst_swif = dst_if.sw_if_index
266 if dst_swif not in if_dst_hosts:
268 dst_hosts = if_dst_hosts[dst_swif]
269 for dst_host in dst_hosts:
270 src_host = random.choice(src_hosts)
271 pkt_info = self.create_packet_info(src_if, dst_if)
272 payload = self.info_to_payload(pkt_info)
273 p = (Ether(dst=dst_host.mac, src=src_host.mac) /
274 IP(src=src_host.ip4, dst=dst_host.ip4) /
275 UDP(sport=1234, dport=1234) /
277 pkt_info.data = p.copy()
278 size = random.choice(packet_sizes)
279 self.extend_packet(p, size)
283 def verify_capture(self, pg_if, capture):
285 Verify captured input packet stream for defined interface.
287 :param object pg_if: Interface to verify captured packet stream for.
288 :param list capture: Captured packet stream.
291 for i in self.pg_interfaces:
292 last_info[i.sw_if_index] = None
293 dst_sw_if_index = pg_if.sw_if_index
294 for packet in capture:
295 payload_info = self.payload_to_info(str(packet[Raw]))
299 packet_index = payload_info.index
300 self.assertEqual(payload_info.dst, dst_sw_if_index)
301 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
302 (pg_if.name, payload_info.src, packet_index))
303 next_info = self.get_next_packet_info_for_interface2(
304 payload_info.src, dst_sw_if_index,
305 last_info[payload_info.src])
306 last_info[payload_info.src] = next_info
307 self.assertTrue(next_info is not None)
308 self.assertEqual(packet_index, next_info.index)
309 saved_packet = next_info.data
310 # Check standard fields
311 self.assertEqual(ip.src, saved_packet[IP].src)
312 self.assertEqual(ip.dst, saved_packet[IP].dst)
313 self.assertEqual(udp.sport, saved_packet[UDP].sport)
314 self.assertEqual(udp.dport, saved_packet[UDP].dport)
316 self.logger.error(ppp("Unexpected or invalid packet:", packet))
318 for i in self.pg_interfaces:
319 remaining_packet = self.get_next_packet_info_for_interface2(
320 i, dst_sw_if_index, last_info[i.sw_if_index])
322 remaining_packet is None,
323 "Port %u: Packet expected from source %u didn't arrive" %
324 (dst_sw_if_index, i.sw_if_index))
326 def run_verify_test(self, bd_id, src_hosts, dst_hosts):
328 # Create incoming packet streams for packet-generator interfaces
329 self.reset_packet_infos()
330 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
332 pkts = self.create_stream(
333 i, self.pg_if_packet_sizes,
334 if_src_hosts=src_hosts,
335 if_dst_hosts=dst_hosts)
339 self.vapi.bridge_flags(bd_id, 0, 1)
340 # Enable packet capture and start packet sending
341 self.pg_enable_capture(ifs)
345 # Verify outgoing packet streams per packet-generator interface
347 if not dst_hosts[i.sw_if_index]:
349 capture = i.get_capture()
350 self.logger.info("Verifying capture on interface %s" % i.name)
351 self.verify_capture(i, capture)
353 def run_verify_negat_test(self, bd_id, src_hosts, dst_hosts):
355 # Create incoming packet streams for packet-generator interfaces for
356 # deleted MAC addresses
357 self.reset_packet_infos()
358 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
360 pkts = self.create_stream(
361 i, self.pg_if_packet_sizes,
362 if_src_hosts=src_hosts,
363 if_dst_hosts=dst_hosts)
367 self.vapi.bridge_flags(bd_id, 0, 1)
368 # Enable packet capture and start packet sending
369 self.pg_enable_capture(ifs)
373 # Verify outgoing packet streams per packet-generator interface
376 i.get_capture(0, timeout=timeout)
377 i.assert_nothing_captured(remark="outgoing interface")
380 def test_l2_fib_program100(self):
381 """ L2 FIB - program 100 MACs
384 hosts = self.create_hosts(100, subnet=17)
385 self.config_l2_fib_entries(bd_id, hosts)
386 self.run_verify_test(bd_id, hosts, hosts)
388 def test_l2_fib_delete12(self):
389 """ L2 FIB - program 100 + delete 12 MACs
392 hosts = self.create_hosts(100, subnet=17)
393 self.config_l2_fib_entries(bd_id, hosts)
394 del_hosts = self.split_hosts(hosts, 12)
395 self.delete_l2_fib_entry(bd_id, del_hosts)
397 self.run_verify_test(bd_id, hosts, hosts)
398 self.run_verify_negat_test(bd_id, hosts, del_hosts)
400 def test_l2_fib_add100_add100(self):
401 """ L2 FIB - program 100 + 100 MACs
404 hosts = self.create_hosts(100, subnet=17)
405 self.config_l2_fib_entries(bd_id, hosts)
406 hosts2 = self.create_hosts(100, subnet=22)
407 self.config_l2_fib_entries(bd_id, hosts2)
408 self.run_verify_test(bd_id, hosts, hosts2)
410 def test_l2_fib_program10_learn10(self):
411 """ L2 FIB - Program 10 MACs, learn 10
413 hosts = self.create_hosts(20, subnet=35)
414 lhosts = self.split_hosts(hosts, 10)
418 self.learn_hosts(bd1, lhosts)
419 self.learn_hosts(bd2, lhosts)
420 self.config_l2_fib_entries(bd1, hosts)
421 self.config_l2_fib_entries(bd2, hosts)
422 self.run_verify_test(bd1, lhosts, hosts)
423 self.run_verify_test(bd2, lhosts, hosts)
425 def test_l2_fib_flush_int(self):
426 """ L2 FIB - flush interface
428 hosts = self.create_hosts(20, subnet=36)
429 lhosts = self.split_hosts(hosts, 10)
432 self.learn_hosts(bd1, lhosts)
433 self.config_l2_fib_entries(bd1, hosts)
434 self.run_verify_test(bd1, lhosts, hosts)
435 flushed = self.flush_int(self.pg_interfaces[0].sw_if_index, lhosts)
436 self.run_verify_test(bd1, hosts, lhosts)
437 self.run_verify_negat_test(bd1, hosts, flushed)
439 def test_l2_fib_flush_bd(self):
440 """ L2 FIB - flush BD
442 hosts = self.create_hosts(20, subnet=37)
443 lhosts = self.split_hosts(hosts, 10)
446 self.learn_hosts(bd1, lhosts)
447 self.config_l2_fib_entries(bd1, hosts)
448 self.run_verify_test(bd1, lhosts, hosts)
449 flushed = self.flush_bd(bd1, lhosts)
450 self.run_verify_negat_test(bd1, hosts, flushed)
452 def test_l2_fib_flush_all(self):
453 """ L2 FIB - flush all
455 hosts = self.create_hosts(20, subnet=38)
456 lhosts = self.split_hosts(hosts, 10)
460 self.learn_hosts(bd1, lhosts)
461 self.learn_hosts(bd2, lhosts)
462 self.config_l2_fib_entries(bd1, hosts)
463 self.config_l2_fib_entries(bd2, hosts)
464 self.run_verify_test(bd1, hosts, lhosts)
465 self.run_verify_test(bd2, hosts, lhosts)
469 self.run_verify_negat_test(bd1, hosts, lhosts)
470 self.run_verify_negat_test(bd2, hosts, lhosts)
472 def test_l2_fib_mac_learn_evs(self):
473 """ L2 FIB - mac learning events
476 hosts = self.create_hosts(10, subnet=39)
478 self.vapi.want_macs_learn_events()
479 self.learn_hosts(bd1, hosts)
482 self.logger.info(self.vapi.ppcli("show l2fib"))
483 evs = self.vapi.collect_events()
485 e.mac[i].mac_addr for e in evs for i in range(e.n_macs)
486 if not e.mac[i].is_del}
487 macs = {h.bin_mac for swif in self.bd_ifs(bd1)
488 for h in hosts[self.pg_interfaces[swif].sw_if_index]}
489 self.vapi.want_macs_learn_events(enable_disable=0)
490 self.assertEqual(len(learned_macs ^ macs), 0)
492 def test_l2_fib_macs_learn_max(self):
493 """ L2 FIB - mac learning max macs in event
496 hosts = self.create_hosts(10, subnet=40)
499 self.vapi.want_macs_learn_events(max_macs_in_event=ev_macs)
500 self.learn_hosts(bd1, hosts)
503 self.logger.info(self.vapi.ppcli("show l2fib"))
504 evs = self.vapi.collect_events()
505 self.vapi.want_macs_learn_events(enable_disable=0)
507 self.assertGreater(len(evs), 0)
509 e.mac[i].mac_addr for e in evs for i in range(e.n_macs)
510 if not e.mac[i].is_del}
511 macs = {h.bin_mac for swif in self.bd_ifs(bd1)
512 for h in hosts[self.pg_interfaces[swif].sw_if_index]}
515 self.assertLess(len(e), ev_macs * 10)
516 self.assertEqual(len(learned_macs ^ macs), 0)
519 if __name__ == '__main__':
520 unittest.main(testRunner=VppTestRunner)