blob: 801951657af0008cfafc79f257fff63c89b03d27 [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
61from framework import VppTestCase, VppTestRunner
Klement Sekera9225dee2016-12-12 08:36:58 +010062from util import Host, ppp
Jan0bd0b7f2016-12-05 16:22:41 +010063
64
65class TestL2xcMultiInst(VppTestCase):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020066 """L2XC Multi-instance Test Case"""
Jan0bd0b7f2016-12-05 16:22:41 +010067
68 @classmethod
69 def setUpClass(cls):
70 """
71 Perform standard class setup (defined by class method setUpClass in
72 class VppTestCase) before running the test case, set test case related
73 variables and configure VPP.
74 """
75 super(TestL2xcMultiInst, cls).setUpClass()
76
77 try:
78 # Create pg interfaces
79 cls.create_pg_interfaces(range(14))
80
81 # Packet flows mapping pg0 -> pg1 etc.
82 cls.flows = dict()
83 for i in range(len(cls.pg_interfaces)):
84 delta = 1 if i % 2 == 0 else -1
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020085 cls.flows[cls.pg_interfaces[i]] = [cls.pg_interfaces[i + delta]]
Jan0bd0b7f2016-12-05 16:22:41 +010086
87 # Mapping between packet-generator index and lists of test hosts
88 cls.hosts_by_pg_idx = dict()
89 for pg_if in cls.pg_interfaces:
90 cls.hosts_by_pg_idx[pg_if.sw_if_index] = []
91
92 # Create test host entries
93 cls.create_hosts(70)
94
95 # Packet sizes - jumbo packet (9018 bytes) skipped
96 cls.pg_if_packet_sizes = [64, 512, 1518]
97
98 # Set up all interfaces
99 for i in cls.pg_interfaces:
100 i.admin_up()
101
102 # Create list of x-connected pg_interfaces
103 cls.pg_in_xc = list()
104
105 # Create list of not x-connected pg_interfaces
106 cls.pg_not_in_xc = list()
107 for pg_if in cls.pg_interfaces:
108 cls.pg_not_in_xc.append(pg_if)
109
110 except Exception:
111 super(TestL2xcMultiInst, cls).tearDownClass()
112 raise
113
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -0700114 @classmethod
115 def tearDownClass(cls):
116 super(TestL2xcMultiInst, cls).tearDownClass()
117
Jan0bd0b7f2016-12-05 16:22:41 +0100118 def setUp(self):
119 """
120 Clear trace and packet infos before running each test.
121 """
122 super(TestL2xcMultiInst, self).setUp()
Klement Sekeradab231a2016-12-21 08:50:14 +0100123 self.reset_packet_infos()
Jan0bd0b7f2016-12-05 16:22:41 +0100124
125 def tearDown(self):
126 """
127 Show various debug prints after each test.
128 """
129 super(TestL2xcMultiInst, self).tearDown()
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700130
131 def show_commands_at_teardown(self):
132 self.logger.info(self.vapi.ppcli("show l2patch"))
Jan0bd0b7f2016-12-05 16:22:41 +0100133
134 @classmethod
135 def create_hosts(cls, count):
136 """
137 Create required number of host MAC addresses and distribute them among
138 interfaces. Create host IPv4 address for every host MAC address.
139
140 :param int count: Number of hosts to create MAC/IPv4 addresses for.
141 """
142 n_int = len(cls.pg_interfaces)
Ole Troan3f2a9562019-10-21 20:37:30 +0200143 macs_per_if = count // n_int
Jan0bd0b7f2016-12-05 16:22:41 +0100144 i = -1
145 for pg_if in cls.pg_interfaces:
146 i += 1
147 start_nr = macs_per_if * i
148 end_nr = count if i == (n_int - 1) else macs_per_if * (i + 1)
149 hosts = cls.hosts_by_pg_idx[pg_if.sw_if_index]
150 for j in range(start_nr, end_nr):
151 host = Host(
152 "00:00:00:ff:%02x:%02x" % (pg_if.sw_if_index, j),
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200153 "172.17.1%02u.%u" % (pg_if.sw_if_index, j),
154 )
Jan0bd0b7f2016-12-05 16:22:41 +0100155 hosts.append(host)
156
157 def create_xconnects(self, count, start=0):
158 """
159 Create required number of cross-connects (always two cross-connects per
160 pair of packet-generator interfaces).
161
162 :param int count: Number of cross-connects to be created.
163 :param int start: Starting index of packet-generator interfaces. \
164 (Default value = 0)
165 """
166 for i in range(count):
Klement Sekera9225dee2016-12-12 08:36:58 +0100167 rx_if = self.pg_interfaces[i + start]
Jan0bd0b7f2016-12-05 16:22:41 +0100168 delta = 1 if i % 2 == 0 else -1
Klement Sekera9225dee2016-12-12 08:36:58 +0100169 tx_if = self.pg_interfaces[i + start + delta]
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200170 self.vapi.sw_interface_set_l2_xconnect(
171 rx_if.sw_if_index, tx_if.sw_if_index, 1
172 )
173 self.logger.info(
174 "Cross-connect from %s to %s created" % (tx_if.name, rx_if.name)
175 )
Jan0bd0b7f2016-12-05 16:22:41 +0100176 if self.pg_in_xc.count(rx_if) == 0:
177 self.pg_in_xc.append(rx_if)
178 if self.pg_not_in_xc.count(rx_if) == 1:
179 self.pg_not_in_xc.remove(rx_if)
180
181 def delete_xconnects(self, count, start=0):
182 """
183 Delete required number of cross-connects (always two cross-connects per
184 pair of packet-generator interfaces).
185
186 :param int count: Number of cross-connects to be deleted.
187 :param int start: Starting index of packet-generator interfaces. \
188 (Default value = 0)
189 """
190 for i in range(count):
Klement Sekera9225dee2016-12-12 08:36:58 +0100191 rx_if = self.pg_interfaces[i + start]
Jan0bd0b7f2016-12-05 16:22:41 +0100192 delta = 1 if i % 2 == 0 else -1
Klement Sekera9225dee2016-12-12 08:36:58 +0100193 tx_if = self.pg_interfaces[i + start + delta]
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200194 self.vapi.sw_interface_set_l2_xconnect(
195 rx_if.sw_if_index, tx_if.sw_if_index, 0
196 )
197 self.logger.info(
198 "Cross-connect from %s to %s deleted" % (tx_if.name, rx_if.name)
199 )
Jan0bd0b7f2016-12-05 16:22:41 +0100200 if self.pg_not_in_xc.count(rx_if) == 0:
201 self.pg_not_in_xc.append(rx_if)
202 if self.pg_in_xc.count(rx_if) == 1:
203 self.pg_in_xc.remove(rx_if)
204
205 def create_stream(self, src_if, packet_sizes):
206 """
207 Create input packet stream for defined interface using hosts list.
208
209 :param object src_if: Interface to create packet stream for.
210 :param list packet_sizes: List of required packet sizes.
211 :return: Stream of packets.
212 """
213 pkts = []
214 src_hosts = self.hosts_by_pg_idx[src_if.sw_if_index]
215 for dst_if in self.flows[src_if]:
216 dst_hosts = self.hosts_by_pg_idx[dst_if.sw_if_index]
217 n_int = len(dst_hosts)
218 for i in range(0, n_int):
219 dst_host = dst_hosts[i]
220 src_host = random.choice(src_hosts)
Klement Sekeradab231a2016-12-21 08:50:14 +0100221 pkt_info = self.create_packet_info(src_if, dst_if)
Jan0bd0b7f2016-12-05 16:22:41 +0100222 payload = self.info_to_payload(pkt_info)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200223 p = (
224 Ether(dst=dst_host.mac, src=src_host.mac)
225 / IP(src=src_host.ip4, dst=dst_host.ip4)
226 / UDP(sport=1234, dport=1234)
227 / Raw(payload)
228 )
Jan0bd0b7f2016-12-05 16:22:41 +0100229 pkt_info.data = p.copy()
230 size = random.choice(packet_sizes)
231 self.extend_packet(p, size)
232 pkts.append(p)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200233 self.logger.debug(
234 "Input stream created for port %s. Length: %u pkt(s)"
235 % (src_if.name, len(pkts))
236 )
Jan0bd0b7f2016-12-05 16:22:41 +0100237 return pkts
238
239 def verify_capture(self, pg_if, capture):
240 """
241 Verify captured input packet stream for defined interface.
242
243 :param object pg_if: Interface to verify captured packet stream for.
244 :param list capture: Captured packet stream.
245 """
246 last_info = dict()
247 for i in self.pg_interfaces:
248 last_info[i.sw_if_index] = None
249 dst_sw_if_index = pg_if.sw_if_index
250 for packet in capture:
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800251 payload_info = self.payload_to_info(packet[Raw])
Jan0bd0b7f2016-12-05 16:22:41 +0100252 try:
253 ip = packet[IP]
254 udp = packet[UDP]
255 packet_index = payload_info.index
256 self.assertEqual(payload_info.dst, dst_sw_if_index)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200257 self.logger.debug(
258 "Got packet on port %s: src=%u (id=%u)"
259 % (pg_if.name, payload_info.src, packet_index)
260 )
Jan0bd0b7f2016-12-05 16:22:41 +0100261 next_info = self.get_next_packet_info_for_interface2(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200262 payload_info.src, dst_sw_if_index, last_info[payload_info.src]
263 )
Jan0bd0b7f2016-12-05 16:22:41 +0100264 last_info[payload_info.src] = next_info
265 self.assertTrue(next_info is not None)
266 self.assertEqual(packet_index, next_info.index)
267 saved_packet = next_info.data
268 # Check standard fields
269 self.assertEqual(ip.src, saved_packet[IP].src)
270 self.assertEqual(ip.dst, saved_packet[IP].dst)
271 self.assertEqual(udp.sport, saved_packet[UDP].sport)
272 self.assertEqual(udp.dport, saved_packet[UDP].dport)
273 except:
Klement Sekera9225dee2016-12-12 08:36:58 +0100274 self.logger.error(ppp("Unexpected or invalid packet:", packet))
Jan0bd0b7f2016-12-05 16:22:41 +0100275 raise
276 for i in self.pg_interfaces:
277 remaining_packet = self.get_next_packet_info_for_interface2(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200278 i, dst_sw_if_index, last_info[i.sw_if_index]
279 )
Jan0bd0b7f2016-12-05 16:22:41 +0100280 self.assertTrue(
281 remaining_packet is None,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200282 "Port %u: Packet expected from source %u didn't arrive"
283 % (dst_sw_if_index, i.sw_if_index),
284 )
Jan0bd0b7f2016-12-05 16:22:41 +0100285
286 def run_verify_test(self):
287 """
Matej Klottondeb69842016-12-09 15:05:46 +0100288 Create packet streams for all configured l2-pg interfaces, send all \
Jan0bd0b7f2016-12-05 16:22:41 +0100289 prepared packet streams and verify that:
Matej Klottondeb69842016-12-09 15:05:46 +0100290 - all packets received correctly on all pg-l2 interfaces assigned
291 to cross-connects
292 - no packet received on all pg-l2 interfaces not assigned to
293 cross-connects
Jan0bd0b7f2016-12-05 16:22:41 +0100294
Matej Klottondeb69842016-12-09 15:05:46 +0100295 :raise RuntimeError: if no packet captured on l2-pg interface assigned
296 to the cross-connect or if any packet is captured
297 on l2-pg interface not assigned to the
298 cross-connect.
Jan0bd0b7f2016-12-05 16:22:41 +0100299 """
300 # Test
301 # Create incoming packet streams for packet-generator interfaces
302 for pg_if in self.pg_interfaces:
303 pkts = self.create_stream(pg_if, self.pg_if_packet_sizes)
304 pg_if.add_stream(pkts)
305
306 # Enable packet capture and start packet sending
307 self.pg_enable_capture(self.pg_interfaces)
308 self.pg_start()
309
310 # Verify
311 # Verify outgoing packet streams per packet-generator interface
312 for pg_if in self.pg_interfaces:
Jan0bd0b7f2016-12-05 16:22:41 +0100313 if pg_if in self.pg_in_xc:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200314 capture = pg_if.get_capture(remark="interface is a cross-connect sink")
Jan0bd0b7f2016-12-05 16:22:41 +0100315 self.verify_capture(pg_if, capture)
316 elif pg_if in self.pg_not_in_xc:
Klement Sekera9225dee2016-12-12 08:36:58 +0100317 pg_if.assert_nothing_captured(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200318 remark="interface is not a cross-connect sink"
319 )
Jan0bd0b7f2016-12-05 16:22:41 +0100320 else:
Klement Sekera9225dee2016-12-12 08:36:58 +0100321 raise Exception("Unexpected interface: %s" % pg_if.name)
Jan0bd0b7f2016-12-05 16:22:41 +0100322
323 def test_l2xc_inst_01(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200324 """L2XC Multi-instance test 1 - create 10 cross-connects"""
Jan0bd0b7f2016-12-05 16:22:41 +0100325 # Config 1
326 # Create 10 cross-connects
327 self.create_xconnects(10)
328
329 # Test 1
330 self.run_verify_test()
331
332 def test_l2xc_inst_02(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200333 """L2XC Multi-instance test 2 - delete 4 cross-connects"""
Jan0bd0b7f2016-12-05 16:22:41 +0100334 # Config 2
335 # Delete 4 cross-connects
336 self.delete_xconnects(4)
337
338 # Test 2
339 self.run_verify_test()
340
341 def test_l2xc_inst_03(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200342 """L2BD Multi-instance 3 - add new 4 cross-connects"""
Jan0bd0b7f2016-12-05 16:22:41 +0100343 # Config 3
344 # Add new 4 cross-connects
345 self.create_xconnects(4, start=10)
346
347 # Test 3
348 self.run_verify_test()
349
350 def test_l2xc_inst_04(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200351 """L2XC Multi-instance test 4 - delete 10 cross-connects"""
Jan0bd0b7f2016-12-05 16:22:41 +0100352 # Config 4
353 # Delete 10 cross-connects
354 self.delete_xconnects(10, start=4)
355
356 # Test 4
357 self.run_verify_test()
358
359
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200360if __name__ == "__main__":
Jan0bd0b7f2016-12-05 16:22:41 +0100361 unittest.main(testRunner=VppTestRunner)