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 """
80 Perform standard class setup (defined by class method setUpClass in
81 class VppTestCase) before running the test case, set test case related
82 variables and configure VPP.
84 :var int bd_id: Bridge domain ID.
85 :var int mac_entries_count: Number of MAC entries for bridge-domain.
87 super(TestL2fib, cls).setUpClass()
91 cls.mac_entries_count = 200
94 # Create 4 pg interfaces
95 cls.create_pg_interfaces(range(4))
97 # Packet flows mapping pg0 -> pg1, pg2, pg3 etc.
99 cls.flows[cls.pg0] = [cls.pg1, cls.pg2, cls.pg3]
100 cls.flows[cls.pg1] = [cls.pg0, cls.pg2, cls.pg3]
101 cls.flows[cls.pg2] = [cls.pg0, cls.pg1, cls.pg3]
102 cls.flows[cls.pg3] = [cls.pg0, cls.pg1, cls.pg2]
105 cls.pg_if_packet_sizes = [64, 512, 1518, 9018]
107 # Create BD with MAC learning and unknown unicast flooding disabled
108 # and put interfaces to this BD
109 cls.vapi.bridge_domain_add_del(
110 bd_id=cls.bd_id, uu_flood=0, learn=0)
111 for pg_if in cls.pg_interfaces:
112 cls.vapi.sw_interface_set_l2_bridge(pg_if.sw_if_index,
115 # Set up all interfaces
116 for i in cls.pg_interfaces:
119 # Mapping between packet-generator index and lists of test hosts
120 cls.hosts_by_pg_idx = dict()
121 for pg_if in cls.pg_interfaces:
122 cls.hosts_by_pg_idx[pg_if.sw_if_index] = []
124 # Create list of deleted hosts
125 cls.deleted_hosts_by_pg_idx = dict()
126 for pg_if in cls.pg_interfaces:
127 cls.deleted_hosts_by_pg_idx[pg_if.sw_if_index] = []
130 super(TestL2fib, cls).tearDownClass()
134 super(TestL2fib, self).setUp()
135 self.reset_packet_infos()
139 Show various debug prints after each test.
141 super(TestL2fib, self).tearDown()
142 if not self.vpp_dead:
143 self.logger.info(self.vapi.ppcli("show l2fib verbose"))
144 self.logger.info(self.vapi.ppcli("show bridge-domain %s detail"
147 def create_hosts(self, count, start=0):
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 count: Number of hosts to create MAC/IPv4 addresses for.
153 :param int start: Number to start numbering from.
155 n_int = len(self.pg_interfaces)
156 macs_per_if = count / n_int
158 for pg_if in self.pg_interfaces:
160 start_nr = macs_per_if * i + start
161 end_nr = count + start if i == (n_int - 1) \
162 else macs_per_if * (i + 1) + start
163 hosts = self.hosts_by_pg_idx[pg_if.sw_if_index]
164 for j in range(start_nr, end_nr):
166 "00:00:00:ff:%02x:%02x" % (pg_if.sw_if_index, j),
167 "172.17.1%02x.%u" % (pg_if.sw_if_index, j))
170 def config_l2_fib_entries(self, count, start=0):
172 Create required number of L2 FIB entries.
174 :param int count: Number of L2 FIB entries to be created.
175 :param int start: Starting index of the host list. (Default value = 0)
177 n_int = len(self.pg_interfaces)
180 for pg_if in self.pg_interfaces:
181 end_nr = start + count / n_int
182 for j in range(start, end_nr):
183 host = self.hosts_by_pg_idx[pg_if.sw_if_index][j]
184 self.vapi.l2fib_add_del(
185 host.mac, self.bd_id, pg_if.sw_if_index, static_mac=1)
187 percentage = counter / count * 100
188 if percentage > percent:
189 self.logger.info("Configure %d L2 FIB entries .. %d%% done"
190 % (count, percentage))
192 self.logger.info(self.vapi.ppcli("show l2fib"))
194 def delete_l2_fib_entry(self, count):
196 Delete required number of L2 FIB entries.
198 :param int count: Number of L2 FIB entries to be created.
200 n_int = len(self.pg_interfaces)
203 for pg_if in self.pg_interfaces:
204 for j in range(count / n_int):
205 host = self.hosts_by_pg_idx[pg_if.sw_if_index][0]
206 self.vapi.l2fib_add_del(
207 host.mac, self.bd_id, pg_if.sw_if_index, is_add=0)
208 self.deleted_hosts_by_pg_idx[pg_if.sw_if_index].append(host)
209 del self.hosts_by_pg_idx[pg_if.sw_if_index][0]
211 percentage = counter / count * 100
212 if percentage > percent:
213 self.logger.info("Delete %d L2 FIB entries .. %d%% done"
214 % (count, percentage))
216 self.logger.info(self.vapi.ppcli("show l2fib"))
218 def create_stream(self, src_if, packet_sizes, deleted=False):
220 Create input packet stream for defined interface using hosts or
223 :param object src_if: Interface to create packet stream for.
224 :param list packet_sizes: List of required packet sizes.
225 :param boolean deleted: Set to True if deleted_hosts list required.
226 :return: Stream of packets.
229 src_hosts = self.hosts_by_pg_idx[src_if.sw_if_index]
230 for dst_if in self.flows[src_if]:
231 dst_hosts = self.deleted_hosts_by_pg_idx[dst_if.sw_if_index]\
232 if deleted else self.hosts_by_pg_idx[dst_if.sw_if_index]
233 n_int = len(dst_hosts)
234 for i in range(0, n_int):
235 dst_host = dst_hosts[i]
236 src_host = random.choice(src_hosts)
237 pkt_info = self.create_packet_info(src_if, dst_if)
238 payload = self.info_to_payload(pkt_info)
239 p = (Ether(dst=dst_host.mac, src=src_host.mac) /
240 IP(src=src_host.ip4, dst=dst_host.ip4) /
241 UDP(sport=1234, dport=1234) /
243 pkt_info.data = p.copy()
244 size = random.choice(packet_sizes)
245 self.extend_packet(p, size)
249 def verify_capture(self, pg_if, capture):
251 Verify captured input packet stream for defined interface.
253 :param object pg_if: Interface to verify captured packet stream for.
254 :param list capture: Captured packet stream.
257 for i in self.pg_interfaces:
258 last_info[i.sw_if_index] = None
259 dst_sw_if_index = pg_if.sw_if_index
260 for packet in capture:
261 payload_info = self.payload_to_info(str(packet[Raw]))
265 packet_index = payload_info.index
266 self.assertEqual(payload_info.dst, dst_sw_if_index)
267 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
268 (pg_if.name, payload_info.src, packet_index))
269 next_info = self.get_next_packet_info_for_interface2(
270 payload_info.src, dst_sw_if_index,
271 last_info[payload_info.src])
272 last_info[payload_info.src] = next_info
273 self.assertTrue(next_info is not None)
274 self.assertEqual(packet_index, next_info.index)
275 saved_packet = next_info.data
276 # Check standard fields
277 self.assertEqual(ip.src, saved_packet[IP].src)
278 self.assertEqual(ip.dst, saved_packet[IP].dst)
279 self.assertEqual(udp.sport, saved_packet[UDP].sport)
280 self.assertEqual(udp.dport, saved_packet[UDP].dport)
282 self.logger.error(ppp("Unexpected or invalid packet:", packet))
284 for i in self.pg_interfaces:
285 remaining_packet = self.get_next_packet_info_for_interface2(
286 i, dst_sw_if_index, last_info[i.sw_if_index])
288 remaining_packet is None,
289 "Port %u: Packet expected from source %u didn't arrive" %
290 (dst_sw_if_index, i.sw_if_index))
292 def run_verify_test(self):
294 # Create incoming packet streams for packet-generator interfaces
295 for i in self.pg_interfaces:
296 pkts = self.create_stream(i, self.pg_if_packet_sizes)
299 # Enable packet capture and start packet sending
300 self.pg_enable_capture(self.pg_interfaces)
304 # Verify outgoing packet streams per packet-generator interface
305 for i in self.pg_interfaces:
306 capture = i.get_capture()
307 self.logger.info("Verifying capture on interface %s" % i.name)
308 self.verify_capture(i, capture)
310 def run_verify_negat_test(self):
312 # Create incoming packet streams for packet-generator interfaces for
313 # deleted MAC addresses
314 self.reset_packet_infos()
315 for i in self.pg_interfaces:
316 pkts = self.create_stream(i, self.pg_if_packet_sizes, deleted=True)
319 # Enable packet capture and start packet sending
320 self.pg_enable_capture(self.pg_interfaces)
324 # Verify outgoing packet streams per packet-generator interface
325 for i in self.pg_interfaces:
326 i.assert_nothing_captured(remark="outgoing interface")
328 def test_l2_fib_01(self):
329 """ L2 FIB test 1 - program 100 MAC addresses
332 # Create test host entries
333 self.create_hosts(100)
335 # Add first 100 MAC entries to L2 FIB
336 self.config_l2_fib_entries(100)
339 self.run_verify_test()
341 def test_l2_fib_02(self):
342 """ L2 FIB test 2 - delete 12 MAC entries
345 # Delete 12 MAC entries (3 per interface) from L2 FIB
346 self.delete_l2_fib_entry(12)
349 self.run_verify_test()
352 self.run_verify_negat_test()
354 def test_l2_fib_03(self):
355 """ L2 FIB test 3 - program new 100 MAC addresses
358 # Create new test host entries
359 self.create_hosts(100, start=100)
361 # Add new 100 MAC entries to L2 FIB
362 self.config_l2_fib_entries(100, start=22)
365 self.run_verify_test()
367 def test_l2_fib_04(self):
368 """ L2 FIB test 4 - delete 160 MAC entries
371 # Delete 160 MAC entries (40 per interface) from L2 FIB
372 self.delete_l2_fib_entry(160)
375 self.run_verify_negat_test()
378 if __name__ == '__main__':
379 unittest.main(testRunner=VppTestRunner)