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 self.logger.info(self.vapi.ppcli("show l2fib verbose"))
149 for bd_id in self.n_brs:
150 self.logger.info(self.vapi.ppcli("show bridge-domain %s detail"
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
166 def mac(j): return "00:00:%02x:ff:%02x:%02x" % (subnet, swif, j)
168 def ip(j): return "172.%02u.1%02x.%u" % (subnet, swif, j)
170 def h(j): return Host(mac(j), ip(j))
171 hosts[swif] = [h(j) for j in range(n_hosts_per_if)]
174 def split_hosts(self, hosts, n):
176 for pg_if in self.pg_interfaces:
177 swif = pg_if.sw_if_index
178 splits[swif] = hosts[swif][:n]
179 hosts[swif] = hosts[swif][n:]
182 def learn_hosts(self, bd_id, hosts):
184 Create and send per interface L2 MAC broadcast packet stream to
185 let the bridge domain learn these MAC addresses.
187 :param int bd_id: BD to teach
188 :param dict hosts: dict of hosts per interface
190 self.vapi.bridge_flags(bd_id=bd_id, is_set=1, flags=1)
191 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
193 swif = pg_if.sw_if_index
194 packets = [Ether(dst="ff:ff:ff:ff:ff:ff", src=host.mac)
195 for host in hosts[swif]]
196 pg_if.add_stream(packets)
197 self.logger.info("Sending broadcast eth frames for MAC learning")
200 def config_l2_fib_entries(self, bd_id, hosts):
202 Config required number of L2 FIB entries.
204 :param int bd_id: BD's id
205 :param int count: Number of L2 FIB entries to be created.
206 :param int start: Starting index of the host list. (Default value = 0)
208 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
210 swif = pg_if.sw_if_index
211 for host in hosts[swif]:
212 self.vapi.l2fib_add_del(
213 mac_pton(host.mac), bd_id, swif, static_mac=1)
215 def delete_l2_fib_entry(self, bd_id, hosts):
217 Delete required number of L2 FIB entries.
219 :param int count: Number of L2 FIB entries to be created.
221 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
223 swif = pg_if.sw_if_index
224 for host in hosts[swif]:
225 self.vapi.l2fib_add_del(
226 mac_pton(host.mac), bd_id, swif, is_add=0)
228 def flush_int(self, swif, learned_hosts):
230 Flush swif L2 FIB entries.
232 :param int swif: sw if index.
235 self.vapi.l2fib_flush_int(swif)
236 flushed[swif] = learned_hosts[swif]
237 learned_hosts[swif] = []
240 def flush_bd(self, bd_id, learned_hosts):
242 Flush bd_id L2 FIB entries.
244 :param int bd_id: Bridge Domain id.
246 self.vapi.l2fib_flush_bd(bd_id)
248 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
250 swif = pg_if.sw_if_index
251 flushed[swif] = learned_hosts[swif]
252 learned_hosts[swif] = []
257 Flush All L2 FIB entries.
259 self.vapi.l2fib_flush_all()
261 def create_stream(self, src_if, packet_sizes, if_src_hosts, if_dst_hosts):
263 Create input packet stream for defined interface using hosts or
266 :param object src_if: Interface to create packet stream for.
267 :param list packet_sizes: List of required packet sizes.
268 :param boolean deleted: Set to True if deleted_hosts list required.
269 :return: Stream of packets.
271 src_hosts = if_src_hosts[src_if.sw_if_index]
275 for dst_if in self.flows[src_if]:
276 dst_swif = dst_if.sw_if_index
277 if dst_swif not in if_dst_hosts:
279 dst_hosts = if_dst_hosts[dst_swif]
280 for dst_host in dst_hosts:
281 src_host = random.choice(src_hosts)
282 pkt_info = self.create_packet_info(src_if, dst_if)
283 payload = self.info_to_payload(pkt_info)
284 p = (Ether(dst=dst_host.mac, src=src_host.mac) /
285 IP(src=src_host.ip4, dst=dst_host.ip4) /
286 UDP(sport=1234, dport=1234) /
288 pkt_info.data = p.copy()
289 size = random.choice(packet_sizes)
290 self.extend_packet(p, size)
294 def verify_capture(self, pg_if, capture):
296 Verify captured input packet stream for defined interface.
298 :param object pg_if: Interface to verify captured packet stream for.
299 :param list capture: Captured packet stream.
302 for i in self.pg_interfaces:
303 last_info[i.sw_if_index] = None
304 dst_sw_if_index = pg_if.sw_if_index
305 for packet in capture:
306 payload_info = self.payload_to_info(packet[Raw])
310 packet_index = payload_info.index
311 self.assertEqual(payload_info.dst, dst_sw_if_index)
312 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
313 (pg_if.name, payload_info.src, packet_index))
314 next_info = self.get_next_packet_info_for_interface2(
315 payload_info.src, dst_sw_if_index,
316 last_info[payload_info.src])
317 last_info[payload_info.src] = next_info
318 self.assertTrue(next_info is not None)
319 self.assertEqual(packet_index, next_info.index)
320 saved_packet = next_info.data
321 # Check standard fields
322 self.assertEqual(ip.src, saved_packet[IP].src)
323 self.assertEqual(ip.dst, saved_packet[IP].dst)
324 self.assertEqual(udp.sport, saved_packet[UDP].sport)
325 self.assertEqual(udp.dport, saved_packet[UDP].dport)
327 self.logger.error(ppp("Unexpected or invalid packet:", packet))
329 for i in self.pg_interfaces:
330 remaining_packet = self.get_next_packet_info_for_interface2(
331 i, dst_sw_if_index, last_info[i.sw_if_index])
333 remaining_packet is None,
334 "Port %u: Packet expected from source %u didn't arrive" %
335 (dst_sw_if_index, i.sw_if_index))
337 def run_verify_test(self, bd_id, src_hosts, dst_hosts):
339 # Create incoming packet streams for packet-generator interfaces
340 self.reset_packet_infos()
341 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
343 pkts = self.create_stream(
344 i, self.pg_if_packet_sizes,
345 if_src_hosts=src_hosts,
346 if_dst_hosts=dst_hosts)
350 self.vapi.bridge_flags(bd_id=bd_id, is_set=0, flags=1)
351 # Enable packet capture and start packet sending
352 self.pg_enable_capture(ifs)
356 # Verify outgoing packet streams per packet-generator interface
358 if not dst_hosts[i.sw_if_index]:
360 capture = i.get_capture()
361 self.logger.info("Verifying capture on interface %s" % i.name)
362 self.verify_capture(i, capture)
364 def run_verify_negat_test(self, bd_id, src_hosts, dst_hosts):
366 # Create incoming packet streams for packet-generator interfaces for
367 # deleted MAC addresses
368 self.reset_packet_infos()
369 ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)]
371 pkts = self.create_stream(
372 i, self.pg_if_packet_sizes,
373 if_src_hosts=src_hosts,
374 if_dst_hosts=dst_hosts)
378 self.vapi.bridge_flags(bd_id=bd_id, is_set=0, flags=1)
379 # Enable packet capture and start packet sending
380 self.pg_enable_capture(ifs)
384 # Verify outgoing packet streams per packet-generator interface
387 i.get_capture(0, timeout=timeout)
388 i.assert_nothing_captured(remark="outgoing interface")
391 def test_l2_fib_program100(self):
392 """ L2 FIB - program 100 MACs
395 hosts = self.create_hosts(100, subnet=17)
396 self.config_l2_fib_entries(bd_id, hosts)
397 self.run_verify_test(bd_id, hosts, hosts)
399 def test_l2_fib_program100_delete12(self):
400 """ L2 FIB - program 100, delete 12 MACs
403 hosts = self.create_hosts(100, subnet=17)
404 self.config_l2_fib_entries(bd_id, hosts)
405 del_hosts = self.split_hosts(hosts, 12)
406 self.delete_l2_fib_entry(bd_id, del_hosts)
408 self.run_verify_test(bd_id, hosts, hosts)
409 self.run_verify_negat_test(bd_id, hosts, del_hosts)
411 def test_l2_fib_program100_add100(self):
412 """ L2 FIB - program 100, add 100 MACs
415 hosts = self.create_hosts(100, subnet=17)
416 self.config_l2_fib_entries(bd_id, hosts)
417 hosts2 = self.create_hosts(100, subnet=22)
418 self.config_l2_fib_entries(bd_id, hosts2)
419 self.run_verify_test(bd_id, hosts, hosts2)
421 def test_l2_fib_program10_learn10(self):
422 """ L2 FIB - program 10 MACs, learn 10
424 hosts = self.create_hosts(20, subnet=35)
425 lhosts = self.split_hosts(hosts, 10)
429 self.learn_hosts(bd1, lhosts)
430 self.learn_hosts(bd2, lhosts)
431 self.config_l2_fib_entries(bd1, hosts)
432 self.config_l2_fib_entries(bd2, hosts)
433 self.run_verify_test(bd1, lhosts, hosts)
434 self.run_verify_test(bd2, lhosts, hosts)
436 def test_l2_fib_flush_int(self):
437 """ L2 FIB - flush interface
439 hosts = self.create_hosts(20, subnet=36)
440 lhosts = self.split_hosts(hosts, 10)
443 self.learn_hosts(bd1, lhosts)
444 self.config_l2_fib_entries(bd1, hosts)
445 self.run_verify_test(bd1, lhosts, hosts)
446 flushed = self.flush_int(self.pg_interfaces[0].sw_if_index, lhosts)
447 self.run_verify_test(bd1, hosts, lhosts)
448 self.run_verify_negat_test(bd1, hosts, flushed)
450 def test_l2_fib_flush_bd(self):
451 """ L2 FIB - flush BD
453 hosts = self.create_hosts(20, subnet=37)
454 lhosts = self.split_hosts(hosts, 10)
457 self.learn_hosts(bd1, lhosts)
458 self.config_l2_fib_entries(bd1, hosts)
459 self.run_verify_test(bd1, lhosts, hosts)
460 flushed = self.flush_bd(bd1, lhosts)
461 self.run_verify_negat_test(bd1, hosts, flushed)
463 def test_l2_fib_flush_all(self):
464 """ L2 FIB - flush all
466 hosts = self.create_hosts(20, subnet=38)
467 lhosts = self.split_hosts(hosts, 10)
471 self.learn_hosts(bd1, lhosts)
472 self.learn_hosts(bd2, lhosts)
473 self.config_l2_fib_entries(bd1, hosts)
474 self.config_l2_fib_entries(bd2, hosts)
475 self.run_verify_test(bd1, hosts, lhosts)
476 self.run_verify_test(bd2, hosts, lhosts)
480 self.run_verify_negat_test(bd1, hosts, lhosts)
481 self.run_verify_negat_test(bd2, hosts, lhosts)
483 def test_l2_fib_mac_learn_evs(self):
484 """ L2 FIB - mac learning events
487 hosts = self.create_hosts(10, subnet=39)
489 self.vapi.want_l2_macs_events()
490 self.learn_hosts(bd1, hosts)
493 self.logger.info(self.vapi.ppcli("show l2fib"))
494 evs = self.vapi.collect_events()
496 e.mac[i].mac_addr for e in evs for i in range(e.n_macs)
497 if e.mac[i].action == MAC_EVENT_ACTION_ADD}
498 macs = {h.bin_mac for swif in self.bd_ifs(bd1)
499 for h in hosts[self.pg_interfaces[swif].sw_if_index]}
500 self.vapi.want_l2_macs_events(enable_disable=0)
501 self.assertEqual(len(learned_macs ^ macs), 0)
503 def test_l2_fib_macs_learn_max(self):
504 """ L2 FIB - mac learning max macs in event
507 hosts = self.create_hosts(10, subnet=40)
510 self.vapi.want_l2_macs_events(max_macs_in_event=ev_macs)
511 self.learn_hosts(bd1, hosts)
514 self.logger.info(self.vapi.ppcli("show l2fib"))
515 evs = self.vapi.collect_events()
516 self.vapi.want_l2_macs_events(enable_disable=0)
518 self.assertGreater(len(evs), 0)
520 e.mac[i].mac_addr for e in evs for i in range(e.n_macs)
521 if e.mac[i].action == MAC_EVENT_ACTION_ADD}
522 macs = {h.bin_mac for swif in self.bd_ifs(bd1)
523 for h in hosts[self.pg_interfaces[swif].sw_if_index]}
526 self.assertLess(len(e), ev_macs * 10)
527 self.assertEqual(len(learned_macs ^ macs), 0)
530 if __name__ == '__main__':
531 unittest.main(testRunner=VppTestRunner)