blob: 1726809bc73810f240d960fb08972d6a0bd3f1d1 [file] [log] [blame]
Renato Botelho do Coutoead1e532019-10-31 13:31:07 -05001#!/usr/bin/env python3
Jan0bd0b7f2016-12-05 16:22:41 +01002"""L2XC Multi-instance Test Case HLD:
3
4**NOTES:**
Klement Sekerada505f62017-01-04 12:58:53 +01005 - higher number (more than 15) of pg-l2 interfaces causes problems => only
6 14 pg-l2 interfaces and 10 cross-connects are tested
7 - jumbo packets in configuration with 14 l2-pg interfaces leads to
8 problems too
Jan0bd0b7f2016-12-05 16:22:41 +01009
10**config 1**
11 - add 14 pg-l2 interfaces
12 - add 10 cross-connects (two cross-connects per pair of l2-pg interfaces)
13
14**test 1**
15 - send L2 MAC frames between all pairs of pg-l2 interfaces
16
17**verify 1**
Klement Sekerada505f62017-01-04 12:58:53 +010018 - all packets received correctly in case of cross-connected l2-pg
19 interfaces
Jan0bd0b7f2016-12-05 16:22:41 +010020 - no packet received in case of not cross-connected l2-pg interfaces
21
22**config 2**
23 - delete 4 cross-connects
24
25**test 2**
26 - send L2 MAC frames between all pairs of pg-l2 interfaces
27
28**verify 2**
Klement Sekerada505f62017-01-04 12:58:53 +010029 - all packets received correctly in case of cross-connected l2-pg
30 interfaces
Jan0bd0b7f2016-12-05 16:22:41 +010031 - no packet received in case of not cross-connected l2-pg interfaces
32
33**config 3**
34 - add new 4 cross-connects
35
36**test 3**
37 - send L2 MAC frames between all pairs of pg-l2 interfaces
38
39**verify 3**
Klement Sekerada505f62017-01-04 12:58:53 +010040 - all packets received correctly in case of cross-connected l2-pg
41 interfaces
Jan0bd0b7f2016-12-05 16:22:41 +010042 - no packet received in case of not cross-connected l2-pg interfaces
43
44**config 4**
45 - delete 10 cross-connects
46
47**test 4**
48 - send L2 MAC frames between all pairs of pg-l2 interfaces
49
50**verify 4**
51 - no packet received on all of l2-pg interfaces (no cross-connect created)
52"""
53
54import unittest
55import random
56
57from scapy.packet import Raw
58from scapy.layers.l2 import Ether
59from scapy.layers.inet import IP, UDP
60
Dave Wallace8800f732023-08-31 00:47:44 -040061from framework import VppTestCase
62from asfframework import VppTestRunner
Klement Sekera9225dee2016-12-12 08:36:58 +010063from util import Host, ppp
Jan0bd0b7f2016-12-05 16:22:41 +010064
65
66class TestL2xcMultiInst(VppTestCase):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020067 """L2XC Multi-instance Test Case"""
Jan0bd0b7f2016-12-05 16:22:41 +010068
69 @classmethod
70 def setUpClass(cls):
71 """
72 Perform standard class setup (defined by class method setUpClass in
73 class VppTestCase) before running the test case, set test case related
74 variables and configure VPP.
75 """
76 super(TestL2xcMultiInst, cls).setUpClass()
77
78 try:
79 # Create pg interfaces
80 cls.create_pg_interfaces(range(14))
81
82 # Packet flows mapping pg0 -> pg1 etc.
83 cls.flows = dict()
84 for i in range(len(cls.pg_interfaces)):
85 delta = 1 if i % 2 == 0 else -1
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020086 cls.flows[cls.pg_interfaces[i]] = [cls.pg_interfaces[i + delta]]
Jan0bd0b7f2016-12-05 16:22:41 +010087
88 # Mapping between packet-generator index and lists of test hosts
89 cls.hosts_by_pg_idx = dict()
90 for pg_if in cls.pg_interfaces:
91 cls.hosts_by_pg_idx[pg_if.sw_if_index] = []
92
93 # Create test host entries
94 cls.create_hosts(70)
95
96 # Packet sizes - jumbo packet (9018 bytes) skipped
97 cls.pg_if_packet_sizes = [64, 512, 1518]
98
99 # Set up all interfaces
100 for i in cls.pg_interfaces:
101 i.admin_up()
102
103 # Create list of x-connected pg_interfaces
104 cls.pg_in_xc = list()
105
106 # Create list of not x-connected pg_interfaces
107 cls.pg_not_in_xc = list()
108 for pg_if in cls.pg_interfaces:
109 cls.pg_not_in_xc.append(pg_if)
110
111 except Exception:
112 super(TestL2xcMultiInst, cls).tearDownClass()
113 raise
114
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -0700115 @classmethod
116 def tearDownClass(cls):
117 super(TestL2xcMultiInst, cls).tearDownClass()
118
Jan0bd0b7f2016-12-05 16:22:41 +0100119 def setUp(self):
120 """
121 Clear trace and packet infos before running each test.
122 """
123 super(TestL2xcMultiInst, self).setUp()
Klement Sekeradab231a2016-12-21 08:50:14 +0100124 self.reset_packet_infos()
Jan0bd0b7f2016-12-05 16:22:41 +0100125
126 def tearDown(self):
127 """
128 Show various debug prints after each test.
129 """
130 super(TestL2xcMultiInst, self).tearDown()
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700131
132 def show_commands_at_teardown(self):
133 self.logger.info(self.vapi.ppcli("show l2patch"))
Jan0bd0b7f2016-12-05 16:22:41 +0100134
135 @classmethod
136 def create_hosts(cls, count):
137 """
138 Create required number of host MAC addresses and distribute them among
139 interfaces. Create host IPv4 address for every host MAC address.
140
141 :param int count: Number of hosts to create MAC/IPv4 addresses for.
142 """
143 n_int = len(cls.pg_interfaces)
Ole Troan3f2a9562019-10-21 20:37:30 +0200144 macs_per_if = count // n_int
Jan0bd0b7f2016-12-05 16:22:41 +0100145 i = -1
146 for pg_if in cls.pg_interfaces:
147 i += 1
148 start_nr = macs_per_if * i
149 end_nr = count if i == (n_int - 1) else macs_per_if * (i + 1)
150 hosts = cls.hosts_by_pg_idx[pg_if.sw_if_index]
151 for j in range(start_nr, end_nr):
152 host = Host(
153 "00:00:00:ff:%02x:%02x" % (pg_if.sw_if_index, j),
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200154 "172.17.1%02u.%u" % (pg_if.sw_if_index, j),
155 )
Jan0bd0b7f2016-12-05 16:22:41 +0100156 hosts.append(host)
157
158 def create_xconnects(self, count, start=0):
159 """
160 Create required number of cross-connects (always two cross-connects per
161 pair of packet-generator interfaces).
162
163 :param int count: Number of cross-connects to be created.
164 :param int start: Starting index of packet-generator interfaces. \
165 (Default value = 0)
166 """
167 for i in range(count):
Klement Sekera9225dee2016-12-12 08:36:58 +0100168 rx_if = self.pg_interfaces[i + start]
Jan0bd0b7f2016-12-05 16:22:41 +0100169 delta = 1 if i % 2 == 0 else -1
Klement Sekera9225dee2016-12-12 08:36:58 +0100170 tx_if = self.pg_interfaces[i + start + delta]
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200171 self.vapi.sw_interface_set_l2_xconnect(
172 rx_if.sw_if_index, tx_if.sw_if_index, 1
173 )
174 self.logger.info(
175 "Cross-connect from %s to %s created" % (tx_if.name, rx_if.name)
176 )
Jan0bd0b7f2016-12-05 16:22:41 +0100177 if self.pg_in_xc.count(rx_if) == 0:
178 self.pg_in_xc.append(rx_if)
179 if self.pg_not_in_xc.count(rx_if) == 1:
180 self.pg_not_in_xc.remove(rx_if)
181
182 def delete_xconnects(self, count, start=0):
183 """
184 Delete required number of cross-connects (always two cross-connects per
185 pair of packet-generator interfaces).
186
187 :param int count: Number of cross-connects to be deleted.
188 :param int start: Starting index of packet-generator interfaces. \
189 (Default value = 0)
190 """
191 for i in range(count):
Klement Sekera9225dee2016-12-12 08:36:58 +0100192 rx_if = self.pg_interfaces[i + start]
Jan0bd0b7f2016-12-05 16:22:41 +0100193 delta = 1 if i % 2 == 0 else -1
Klement Sekera9225dee2016-12-12 08:36:58 +0100194 tx_if = self.pg_interfaces[i + start + delta]
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200195 self.vapi.sw_interface_set_l2_xconnect(
196 rx_if.sw_if_index, tx_if.sw_if_index, 0
197 )
198 self.logger.info(
199 "Cross-connect from %s to %s deleted" % (tx_if.name, rx_if.name)
200 )
Jan0bd0b7f2016-12-05 16:22:41 +0100201 if self.pg_not_in_xc.count(rx_if) == 0:
202 self.pg_not_in_xc.append(rx_if)
203 if self.pg_in_xc.count(rx_if) == 1:
204 self.pg_in_xc.remove(rx_if)
205
206 def create_stream(self, src_if, packet_sizes):
207 """
208 Create input packet stream for defined interface using hosts list.
209
210 :param object src_if: Interface to create packet stream for.
211 :param list packet_sizes: List of required packet sizes.
212 :return: Stream of packets.
213 """
214 pkts = []
215 src_hosts = self.hosts_by_pg_idx[src_if.sw_if_index]
216 for dst_if in self.flows[src_if]:
217 dst_hosts = self.hosts_by_pg_idx[dst_if.sw_if_index]
218 n_int = len(dst_hosts)
219 for i in range(0, n_int):
220 dst_host = dst_hosts[i]
221 src_host = random.choice(src_hosts)
Klement Sekeradab231a2016-12-21 08:50:14 +0100222 pkt_info = self.create_packet_info(src_if, dst_if)
Jan0bd0b7f2016-12-05 16:22:41 +0100223 payload = self.info_to_payload(pkt_info)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200224 p = (
225 Ether(dst=dst_host.mac, src=src_host.mac)
226 / IP(src=src_host.ip4, dst=dst_host.ip4)
227 / UDP(sport=1234, dport=1234)
228 / Raw(payload)
229 )
Jan0bd0b7f2016-12-05 16:22:41 +0100230 pkt_info.data = p.copy()
231 size = random.choice(packet_sizes)
232 self.extend_packet(p, size)
233 pkts.append(p)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200234 self.logger.debug(
235 "Input stream created for port %s. Length: %u pkt(s)"
236 % (src_if.name, len(pkts))
237 )
Jan0bd0b7f2016-12-05 16:22:41 +0100238 return pkts
239
240 def verify_capture(self, pg_if, capture):
241 """
242 Verify captured input packet stream for defined interface.
243
244 :param object pg_if: Interface to verify captured packet stream for.
245 :param list capture: Captured packet stream.
246 """
247 last_info = dict()
248 for i in self.pg_interfaces:
249 last_info[i.sw_if_index] = None
250 dst_sw_if_index = pg_if.sw_if_index
251 for packet in capture:
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800252 payload_info = self.payload_to_info(packet[Raw])
Jan0bd0b7f2016-12-05 16:22:41 +0100253 try:
254 ip = packet[IP]
255 udp = packet[UDP]
256 packet_index = payload_info.index
257 self.assertEqual(payload_info.dst, dst_sw_if_index)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200258 self.logger.debug(
259 "Got packet on port %s: src=%u (id=%u)"
260 % (pg_if.name, payload_info.src, packet_index)
261 )
Jan0bd0b7f2016-12-05 16:22:41 +0100262 next_info = self.get_next_packet_info_for_interface2(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200263 payload_info.src, dst_sw_if_index, last_info[payload_info.src]
264 )
Jan0bd0b7f2016-12-05 16:22:41 +0100265 last_info[payload_info.src] = next_info
266 self.assertTrue(next_info is not None)
267 self.assertEqual(packet_index, next_info.index)
268 saved_packet = next_info.data
269 # Check standard fields
270 self.assertEqual(ip.src, saved_packet[IP].src)
271 self.assertEqual(ip.dst, saved_packet[IP].dst)
272 self.assertEqual(udp.sport, saved_packet[UDP].sport)
273 self.assertEqual(udp.dport, saved_packet[UDP].dport)
274 except:
Klement Sekera9225dee2016-12-12 08:36:58 +0100275 self.logger.error(ppp("Unexpected or invalid packet:", packet))
Jan0bd0b7f2016-12-05 16:22:41 +0100276 raise
277 for i in self.pg_interfaces:
278 remaining_packet = self.get_next_packet_info_for_interface2(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200279 i, dst_sw_if_index, last_info[i.sw_if_index]
280 )
Jan0bd0b7f2016-12-05 16:22:41 +0100281 self.assertTrue(
282 remaining_packet is None,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200283 "Port %u: Packet expected from source %u didn't arrive"
284 % (dst_sw_if_index, i.sw_if_index),
285 )
Jan0bd0b7f2016-12-05 16:22:41 +0100286
287 def run_verify_test(self):
288 """
Matej Klottondeb69842016-12-09 15:05:46 +0100289 Create packet streams for all configured l2-pg interfaces, send all \
Jan0bd0b7f2016-12-05 16:22:41 +0100290 prepared packet streams and verify that:
Matej Klottondeb69842016-12-09 15:05:46 +0100291 - all packets received correctly on all pg-l2 interfaces assigned
292 to cross-connects
293 - no packet received on all pg-l2 interfaces not assigned to
294 cross-connects
Jan0bd0b7f2016-12-05 16:22:41 +0100295
Matej Klottondeb69842016-12-09 15:05:46 +0100296 :raise RuntimeError: if no packet captured on l2-pg interface assigned
297 to the cross-connect or if any packet is captured
298 on l2-pg interface not assigned to the
299 cross-connect.
Jan0bd0b7f2016-12-05 16:22:41 +0100300 """
301 # Test
302 # Create incoming packet streams for packet-generator interfaces
303 for pg_if in self.pg_interfaces:
304 pkts = self.create_stream(pg_if, self.pg_if_packet_sizes)
305 pg_if.add_stream(pkts)
306
307 # Enable packet capture and start packet sending
308 self.pg_enable_capture(self.pg_interfaces)
309 self.pg_start()
310
311 # Verify
312 # Verify outgoing packet streams per packet-generator interface
313 for pg_if in self.pg_interfaces:
Jan0bd0b7f2016-12-05 16:22:41 +0100314 if pg_if in self.pg_in_xc:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200315 capture = pg_if.get_capture(remark="interface is a cross-connect sink")
Jan0bd0b7f2016-12-05 16:22:41 +0100316 self.verify_capture(pg_if, capture)
317 elif pg_if in self.pg_not_in_xc:
Klement Sekera9225dee2016-12-12 08:36:58 +0100318 pg_if.assert_nothing_captured(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200319 remark="interface is not a cross-connect sink"
320 )
Jan0bd0b7f2016-12-05 16:22:41 +0100321 else:
Klement Sekera9225dee2016-12-12 08:36:58 +0100322 raise Exception("Unexpected interface: %s" % pg_if.name)
Jan0bd0b7f2016-12-05 16:22:41 +0100323
324 def test_l2xc_inst_01(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200325 """L2XC Multi-instance test 1 - create 10 cross-connects"""
Jan0bd0b7f2016-12-05 16:22:41 +0100326 # Config 1
327 # Create 10 cross-connects
328 self.create_xconnects(10)
329
330 # Test 1
331 self.run_verify_test()
332
333 def test_l2xc_inst_02(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200334 """L2XC Multi-instance test 2 - delete 4 cross-connects"""
Jan0bd0b7f2016-12-05 16:22:41 +0100335 # Config 2
336 # Delete 4 cross-connects
337 self.delete_xconnects(4)
338
339 # Test 2
340 self.run_verify_test()
341
342 def test_l2xc_inst_03(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200343 """L2BD Multi-instance 3 - add new 4 cross-connects"""
Jan0bd0b7f2016-12-05 16:22:41 +0100344 # Config 3
345 # Add new 4 cross-connects
346 self.create_xconnects(4, start=10)
347
348 # Test 3
349 self.run_verify_test()
350
351 def test_l2xc_inst_04(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200352 """L2XC Multi-instance test 4 - delete 10 cross-connects"""
Jan0bd0b7f2016-12-05 16:22:41 +0100353 # Config 4
354 # Delete 10 cross-connects
355 self.delete_xconnects(10, start=4)
356
357 # Test 4
358 self.run_verify_test()
359
360
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200361if __name__ == "__main__":
Jan0bd0b7f2016-12-05 16:22:41 +0100362 unittest.main(testRunner=VppTestRunner)