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(bd_id=cls.bd_id, uu_flood=0, learn=0)
110 for pg_if in cls.pg_interfaces:
111 cls.vapi.sw_interface_set_l2_bridge(pg_if.sw_if_index,
114 # Set up all interfaces
115 for i in cls.pg_interfaces:
118 # Mapping between packet-generator index and lists of test hosts
119 cls.hosts_by_pg_idx = dict()
120 for pg_if in cls.pg_interfaces:
121 cls.hosts_by_pg_idx[pg_if.sw_if_index] = []
123 # Create list of deleted hosts
124 cls.deleted_hosts_by_pg_idx = dict()
125 for pg_if in cls.pg_interfaces:
126 cls.deleted_hosts_by_pg_idx[pg_if.sw_if_index] = []
129 super(TestL2fib, cls).tearDownClass()
134 Clear trace and packet infos before running each test.
136 super(TestL2fib, self).setUp()
137 self.packet_infos = {}
141 Show various debug prints after each test.
143 super(TestL2fib, self).tearDown()
144 if not self.vpp_dead:
145 self.logger.info(self.vapi.ppcli("show l2fib verbose"))
146 self.logger.info(self.vapi.ppcli("show bridge-domain %s detail"
149 def create_hosts(self, count, start=0):
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 count: Number of hosts to create MAC/IPv4 addresses for.
155 :param int start: Number to start numbering from.
157 n_int = len(self.pg_interfaces)
158 macs_per_if = count / n_int
160 for pg_if in self.pg_interfaces:
162 start_nr = macs_per_if * i + start
163 end_nr = count + start if i == (n_int - 1) \
164 else macs_per_if * (i + 1) + start
165 hosts = self.hosts_by_pg_idx[pg_if.sw_if_index]
166 for j in range(start_nr, end_nr):
168 "00:00:00:ff:%02x:%02x" % (pg_if.sw_if_index, j),
169 "172.17.1%02x.%u" % (pg_if.sw_if_index, j))
172 def config_l2_fib_entries(self, count, start=0):
174 Create required number of L2 FIB entries.
176 :param int count: Number of L2 FIB entries to be created.
177 :param int start: Starting index of the host list. (Default value = 0)
179 n_int = len(self.pg_interfaces)
182 for pg_if in self.pg_interfaces:
183 end_nr = start + count / n_int
184 for j in range(start, end_nr):
185 host = self.hosts_by_pg_idx[pg_if.sw_if_index][j]
186 self.vapi.l2fib_add_del(host.mac, self.bd_id, pg_if.sw_if_index,
189 percentage = counter / count * 100
190 if percentage > percent:
191 self.logger.info("Configure %d L2 FIB entries .. %d%% done"
192 % (count, percentage))
194 self.logger.info(self.vapi.ppcli("show l2fib"))
196 def delete_l2_fib_entry(self, count):
198 Delete required number of L2 FIB entries.
200 :param int count: Number of L2 FIB entries to be created.
202 n_int = len(self.pg_interfaces)
205 for pg_if in self.pg_interfaces:
206 for j in range(count / n_int):
207 host = self.hosts_by_pg_idx[pg_if.sw_if_index][0]
208 self.vapi.l2fib_add_del(host.mac, self.bd_id, pg_if.sw_if_index,
210 self.deleted_hosts_by_pg_idx[pg_if.sw_if_index].append(host)
211 del self.hosts_by_pg_idx[pg_if.sw_if_index][0]
213 percentage = counter / count * 100
214 if percentage > percent:
215 self.logger.info("Delete %d L2 FIB entries .. %d%% done"
216 % (count, percentage))
218 self.logger.info(self.vapi.ppcli("show l2fib"))
220 def create_stream(self, src_if, packet_sizes, deleted=False):
222 Create input packet stream for defined interface using hosts or
225 :param object src_if: Interface to create packet stream for.
226 :param list packet_sizes: List of required packet sizes.
227 :param boolean deleted: Set to True if deleted_hosts list required.
228 :return: Stream of packets.
231 src_hosts = self.hosts_by_pg_idx[src_if.sw_if_index]
232 for dst_if in self.flows[src_if]:
233 dst_hosts = self.deleted_hosts_by_pg_idx[dst_if.sw_if_index]\
234 if deleted else self.hosts_by_pg_idx[dst_if.sw_if_index]
235 n_int = len(dst_hosts)
236 for i in range(0, n_int):
237 dst_host = dst_hosts[i]
238 src_host = random.choice(src_hosts)
239 pkt_info = self.create_packet_info(
240 src_if.sw_if_index, dst_if.sw_if_index)
241 payload = self.info_to_payload(pkt_info)
242 p = (Ether(dst=dst_host.mac, src=src_host.mac) /
243 IP(src=src_host.ip4, dst=dst_host.ip4) /
244 UDP(sport=1234, dport=1234) /
246 pkt_info.data = p.copy()
247 size = random.choice(packet_sizes)
248 self.extend_packet(p, size)
252 def verify_capture(self, pg_if, capture):
254 Verify captured input packet stream for defined interface.
256 :param object pg_if: Interface to verify captured packet stream for.
257 :param list capture: Captured packet stream.
260 for i in self.pg_interfaces:
261 last_info[i.sw_if_index] = None
262 dst_sw_if_index = pg_if.sw_if_index
263 for packet in capture:
264 payload_info = self.payload_to_info(str(packet[Raw]))
268 packet_index = payload_info.index
269 self.assertEqual(payload_info.dst, dst_sw_if_index)
270 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
271 (pg_if.name, payload_info.src, packet_index))
272 next_info = self.get_next_packet_info_for_interface2(
273 payload_info.src, dst_sw_if_index,
274 last_info[payload_info.src])
275 last_info[payload_info.src] = next_info
276 self.assertTrue(next_info is not None)
277 self.assertEqual(packet_index, next_info.index)
278 saved_packet = next_info.data
279 # Check standard fields
280 self.assertEqual(ip.src, saved_packet[IP].src)
281 self.assertEqual(ip.dst, saved_packet[IP].dst)
282 self.assertEqual(udp.sport, saved_packet[UDP].sport)
283 self.assertEqual(udp.dport, saved_packet[UDP].dport)
285 self.logger.error(ppp("Unexpected or invalid packet:", packet))
287 for i in self.pg_interfaces:
288 remaining_packet = self.get_next_packet_info_for_interface2(
289 i, dst_sw_if_index, last_info[i.sw_if_index])
291 remaining_packet is None,
292 "Port %u: Packet expected from source %u didn't arrive" %
293 (dst_sw_if_index, i.sw_if_index))
295 def run_verify_test(self):
297 # Create incoming packet streams for packet-generator interfaces
298 for i in self.pg_interfaces:
299 pkts = self.create_stream(i, self.pg_if_packet_sizes)
302 # Enable packet capture and start packet sending
303 self.pg_enable_capture(self.pg_interfaces)
307 # Verify outgoing packet streams per packet-generator interface
308 for i in self.pg_interfaces:
309 capture = i.get_capture()
310 self.logger.info("Verifying capture on interface %s" % i.name)
311 self.verify_capture(i, capture)
313 def run_verify_negat_test(self):
315 # Create incoming packet streams for packet-generator interfaces for
316 # deleted MAC addresses
317 self.packet_infos = {}
318 for i in self.pg_interfaces:
319 pkts = self.create_stream(i, self.pg_if_packet_sizes, deleted=True)
322 # Enable packet capture and start packet sending
323 self.pg_enable_capture(self.pg_interfaces)
327 # Verify outgoing packet streams per packet-generator interface
328 for i in self.pg_interfaces:
329 i.assert_nothing_captured(remark="outgoing interface")
331 def test_l2_fib_01(self):
332 """ L2 FIB test 1 - program 100 MAC addresses
335 # Create test host entries
336 self.create_hosts(100)
338 # Add first 100 MAC entries to L2 FIB
339 self.config_l2_fib_entries(100)
342 self.run_verify_test()
344 def test_l2_fib_02(self):
345 """ L2 FIB test 2 - delete 12 MAC entries
348 # Delete 12 MAC entries (3 per interface) from L2 FIB
349 self.delete_l2_fib_entry(12)
352 self.run_verify_test()
355 self.run_verify_negat_test()
357 def test_l2_fib_03(self):
358 """ L2 FIB test 3 - program new 100 MAC addresses
361 # Create new test host entries
362 self.create_hosts(100, start=100)
364 # Add new 100 MAC entries to L2 FIB
365 self.config_l2_fib_entries(100, start=22)
368 self.run_verify_test()
370 def test_l2_fib_04(self):
371 """ L2 FIB test 4 - delete 160 MAC entries
374 # Delete 160 MAC entries (40 per interface) from L2 FIB
375 self.delete_l2_fib_entry(160)
378 self.run_verify_negat_test()
381 if __name__ == '__main__':
382 unittest.main(testRunner=VppTestRunner)