ip6: fix icmp throttling error index
[vpp.git] / test / test_l2xc_multi_instance.py
1 #!/usr/bin/env python3
2 """L2XC Multi-instance Test Case HLD:
3
4 **NOTES:**
5     - 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
9
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**
18     - all packets received correctly in case of cross-connected l2-pg
19       interfaces
20     - 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**
29     - all packets received correctly in case of cross-connected l2-pg
30       interfaces
31     - 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**
40     - all packets received correctly in case of cross-connected l2-pg
41       interfaces
42     - 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
54 import unittest
55 import random
56
57 from scapy.packet import Raw
58 from scapy.layers.l2 import Ether
59 from scapy.layers.inet import IP, UDP
60
61 from framework import VppTestCase
62 from asfframework import VppTestRunner
63 from util import Host, ppp
64
65
66 class TestL2xcMultiInst(VppTestCase):
67     """L2XC Multi-instance Test Case"""
68
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
86                 cls.flows[cls.pg_interfaces[i]] = [cls.pg_interfaces[i + delta]]
87
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
115     @classmethod
116     def tearDownClass(cls):
117         super(TestL2xcMultiInst, cls).tearDownClass()
118
119     def setUp(self):
120         """
121         Clear trace and packet infos before running each test.
122         """
123         super(TestL2xcMultiInst, self).setUp()
124         self.reset_packet_infos()
125
126     def tearDown(self):
127         """
128         Show various debug prints after each test.
129         """
130         super(TestL2xcMultiInst, self).tearDown()
131
132     def show_commands_at_teardown(self):
133         self.logger.info(self.vapi.ppcli("show l2patch"))
134
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)
144         macs_per_if = count // n_int
145         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),
154                     "172.17.1%02u.%u" % (pg_if.sw_if_index, j),
155                 )
156                 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):
168             rx_if = self.pg_interfaces[i + start]
169             delta = 1 if i % 2 == 0 else -1
170             tx_if = self.pg_interfaces[i + start + delta]
171             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             )
177             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):
192             rx_if = self.pg_interfaces[i + start]
193             delta = 1 if i % 2 == 0 else -1
194             tx_if = self.pg_interfaces[i + start + delta]
195             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             )
201             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)
222                 pkt_info = self.create_packet_info(src_if, dst_if)
223                 payload = self.info_to_payload(pkt_info)
224                 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                 )
230                 pkt_info.data = p.copy()
231                 size = random.choice(packet_sizes)
232                 self.extend_packet(p, size)
233                 pkts.append(p)
234         self.logger.debug(
235             "Input stream created for port %s. Length: %u pkt(s)"
236             % (src_if.name, len(pkts))
237         )
238         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:
252             payload_info = self.payload_to_info(packet[Raw])
253             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)
258                 self.logger.debug(
259                     "Got packet on port %s: src=%u (id=%u)"
260                     % (pg_if.name, payload_info.src, packet_index)
261                 )
262                 next_info = self.get_next_packet_info_for_interface2(
263                     payload_info.src, dst_sw_if_index, last_info[payload_info.src]
264                 )
265                 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:
275                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
276                 raise
277         for i in self.pg_interfaces:
278             remaining_packet = self.get_next_packet_info_for_interface2(
279                 i, dst_sw_if_index, last_info[i.sw_if_index]
280             )
281             self.assertTrue(
282                 remaining_packet is None,
283                 "Port %u: Packet expected from source %u didn't arrive"
284                 % (dst_sw_if_index, i.sw_if_index),
285             )
286
287     def run_verify_test(self):
288         """
289         Create packet streams for all configured l2-pg interfaces, send all \
290         prepared packet streams and verify that:
291             - 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
295
296         :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.
300         """
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:
314             if pg_if in self.pg_in_xc:
315                 capture = pg_if.get_capture(remark="interface is a cross-connect sink")
316                 self.verify_capture(pg_if, capture)
317             elif pg_if in self.pg_not_in_xc:
318                 pg_if.assert_nothing_captured(
319                     remark="interface is not a cross-connect sink"
320                 )
321             else:
322                 raise Exception("Unexpected interface: %s" % pg_if.name)
323
324     def test_l2xc_inst_01(self):
325         """L2XC Multi-instance test 1 - create 10 cross-connects"""
326         # 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):
334         """L2XC Multi-instance test 2 - delete 4 cross-connects"""
335         # 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):
343         """L2BD Multi-instance 3 - add new 4 cross-connects"""
344         # 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):
352         """L2XC Multi-instance test 4 - delete 10 cross-connects"""
353         # 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
361 if __name__ == "__main__":
362     unittest.main(testRunner=VppTestRunner)