blob: 62fa1fe9727795843a5bf3479c27b62bf034b091 [file] [log] [blame]
Renato Botelho do Coutoead1e532019-10-31 13:31:07 -05001#!/usr/bin/env python3
Damjan Marionf56b77a2016-10-03 19:44:57 +02002
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01003from __future__ import print_function
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05004import logging
Paul Vinciguerra72f00042018-11-25 11:05:13 -08005import sys
Ole Trøan162989e2018-11-26 10:27:50 +00006import os
7import select
Paul Vinciguerra496b0de2019-06-20 12:24:12 -04008import signal
Paul Vinciguerrad6f22172020-12-05 22:39:14 +00009import subprocess
Ole Trøan162989e2018-11-26 10:27:50 +000010import unittest
Klement Sekerab23ffd72021-05-31 16:08:53 +020011import re
Klement Sekera277b89c2016-10-28 13:20:27 +020012import time
Paul Vinciguerra72f00042018-11-25 11:05:13 -080013import faulthandler
Ole Trøan162989e2018-11-26 10:27:50 +000014import random
15import copy
juraj.linkes68ebc832018-11-29 09:37:08 +010016import platform
Klement Sekerab23ffd72021-05-31 16:08:53 +020017import shutil
Ole Trøan162989e2018-11-26 10:27:50 +000018from collections import deque
19from threading import Thread, Event
20from inspect import getdoc, isclass
21from traceback import format_exception
22from logging import FileHandler, DEBUG, Formatter
Andrew Yourtchenko06f32812021-01-14 10:19:08 +000023from enum import Enum
Klement Sekera558ceab2021-04-08 19:37:41 +020024from abc import ABC, abstractmethod
Ray Kinsellab8165b92021-09-22 11:24:06 +010025from struct import pack, unpack
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -070026
27import scapy.compat
Klement Sekera56c492a2022-01-10 21:57:27 +000028from scapy.packet import Raw, Packet
Ivan Ivanetsb2b87e42024-09-27 17:11:18 +030029from vpp_pg_interface import VppPGInterface, is_ipv6_misc
Ole Troana45dc072018-12-21 16:04:22 +010030from vpp_sub_interface import VppSubInterface
Ole Trøan162989e2018-11-26 10:27:50 +000031from vpp_lo_interface import VppLoInterface
Neale Ranns192b13f2019-03-15 02:16:20 -070032from vpp_bvi_interface import VppBviInterface
Ole Trøan162989e2018-11-26 10:27:50 +000033from vpp_papi_provider import VppPapiProvider
Neale Ranns6197cb72021-06-03 14:43:21 +000034from vpp_papi import VppEnum
Paul Vinciguerra1043fd32019-12-02 21:42:28 -050035import vpp_papi
Ole Trøan162989e2018-11-26 10:27:50 +000036from vpp_object import VppObjectRegistry
37from util import ppp, is_core_present
Klement Sekerad81ae412018-05-16 10:52:54 +020038from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
39from scapy.layers.inet6 import ICMPv6DestUnreach, ICMPv6EchoRequest
40from scapy.layers.inet6 import ICMPv6EchoReply
Naveen Joyc872cec2022-08-30 13:59:03 -070041from vpp_running import use_running
Dave Wallace8800f732023-08-31 00:47:44 -040042from asfframework import VppAsfTestCase
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -080043
Klement Sekera558ceab2021-04-08 19:37:41 +020044
Klement Sekeraf62ae122016-10-11 11:47:09 +020045"""
Dave Wallace8800f732023-08-31 00:47:44 -040046 Packet Generator / Scapy Test framework module.
Klement Sekeraf62ae122016-10-11 11:47:09 +020047
48 The module provides a set of tools for constructing and running tests and
49 representing the results.
50"""
51
Klement Sekeraf62ae122016-10-11 11:47:09 +020052
Damjan Marionf56b77a2016-10-03 19:44:57 +020053class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +020054 """Private class to create packet info object.
55
56 Help process information about the next packet.
57 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +020058 """
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020059
Matej Klotton86d87c42016-11-11 11:38:55 +010060 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020061 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010062 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020063 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010064 #: Store the index of the destination packet generator interface
65 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020066 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +010067 #: Store expected ip version
68 ip = -1
69 #: Store expected upper protocol
70 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010071 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020072 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +020073
Dave Wallacecf9356d2024-07-23 01:28:19 -040074 def __repr__(self):
75 return f"_PacketInfo index:{self.index} src:{self.src} dst:{self.dst} ip:{self.ip} proto:{self.proto} data:{self.data}"
76
Matej Klotton16a14cd2016-12-07 15:09:13 +010077 def __eq__(self, other):
78 index = self.index == other.index
79 src = self.src == other.src
80 dst = self.dst == other.dst
81 data = self.data == other.data
82 return index and src and dst and data
83
Klement Sekeraf62ae122016-10-11 11:47:09 +020084
Naveen Joyc872cec2022-08-30 13:59:03 -070085@use_running
Dave Wallace8800f732023-08-31 00:47:44 -040086class VppTestCase(VppAsfTestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +010087 """This subclass is a base class for VPP test cases that are implemented as
88 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +020089 """
90
91 @property
92 def packet_infos(self):
93 """List of packet infos"""
94 return self._packet_infos
95
Klement Sekeradab231a2016-12-21 08:50:14 +010096 @classmethod
97 def get_packet_count_for_if_idx(cls, dst_if_index):
98 """Get the number of packet info for specified destination if index"""
99 if dst_if_index in cls._packet_count_for_dst_if_idx:
100 return cls._packet_count_for_dst_if_idx[dst_if_index]
101 else:
102 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200103
104 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200105 def setUpClass(cls):
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800106 super(VppTestCase, cls).setUpClass()
Klement Sekeradab231a2016-12-21 08:50:14 +0100107 cls.reset_packet_infos()
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200108 cls._pcaps = []
109 cls._old_pcaps = []
Klement Sekera277b89c2016-10-28 13:20:27 +0200110
Damjan Marionf56b77a2016-10-03 19:44:57 +0200111 @classmethod
112 def tearDownClass(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200113 cls.logger.debug("--- tearDownClass() for %s called ---" % cls.__name__)
Klement Sekeraebbaf552018-02-17 13:41:33 +0100114 cls.reset_packet_infos()
Dave Wallace8800f732023-08-31 00:47:44 -0400115 super(VppTestCase, cls).tearDownClass()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200116
Damjan Marionf56b77a2016-10-03 19:44:57 +0200117 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200118 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200119 """
120 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200121
Klement Sekera75e7d132017-09-20 08:26:30 +0200122 :param interfaces: iterable interface indexes (if None,
123 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200124
Klement Sekeraf62ae122016-10-11 11:47:09 +0200125 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200126 if interfaces is None:
127 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200128 for i in interfaces:
129 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200130
Damjan Marionf56b77a2016-10-03 19:44:57 +0200131 @classmethod
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200132 def register_pcap(cls, intf, worker):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200133 """Register a pcap in the testclass"""
Klement Sekera9225dee2016-12-12 08:36:58 +0100134 # add to the list of captures with current timestamp
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200135 cls._pcaps.append((intf, worker))
Klement Sekera9225dee2016-12-12 08:36:58 +0100136
137 @classmethod
adrianvillinc6fe6172023-12-06 19:21:49 +0100138 def pg_start(cls, trace=True, traceFilter=False):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200139 """Enable the PG, wait till it is done, then clean up"""
Dave Wallace7b8b4652023-08-15 19:05:26 -0400140 for intf, worker in cls._old_pcaps:
Dave Wallace8800f732023-08-31 00:47:44 -0400141 intf.remove_old_pcap_file(intf.get_in_path(worker))
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200142 cls._old_pcaps = []
Benoît Ganne8c45e512021-02-19 16:39:13 +0100143 if trace:
144 cls.vapi.cli("clear trace")
adrianvillinc6fe6172023-12-06 19:21:49 +0100145 cls.vapi.cli("trace add pg-input 1000" + (" filter" if traceFilter else ""))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200146 cls.vapi.cli("packet-generator enable")
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000147 # PG, when starts, runs to completion -
148 # so let's avoid a race condition,
149 # and wait a little till it's done.
150 # Then clean it up - and then be gone.
151 deadline = time.time() + 300
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200152 while cls.vapi.cli("show packet-generator").find("Yes") != -1:
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000153 cls.sleep(0.01) # yield
154 if time.time() > deadline:
155 cls.logger.error("Timeout waiting for pg to stop")
156 break
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200157 for intf, worker in cls._pcaps:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200158 cls.vapi.cli("packet-generator delete %s" % intf.get_cap_name(worker))
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200159 cls._old_pcaps = cls._pcaps
160 cls._pcaps = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200161
Damjan Marionf56b77a2016-10-03 19:44:57 +0200162 @classmethod
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200163 def create_pg_interfaces_internal(cls, interfaces, gso=0, gso_size=0, mode=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200164 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100165 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200166
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100167 :param interfaces: iterable indexes of the interfaces.
168 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200169
Klement Sekeraf62ae122016-10-11 11:47:09 +0200170 """
171 result = []
172 for i in interfaces:
Neale Ranns6197cb72021-06-03 14:43:21 +0000173 intf = VppPGInterface(cls, i, gso, gso_size, mode)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200174 setattr(cls, intf.name, intf)
175 result.append(intf)
176 cls.pg_interfaces = result
177 return result
178
Matej Klotton0178d522016-11-04 11:11:44 +0100179 @classmethod
Neale Ranns6197cb72021-06-03 14:43:21 +0000180 def create_pg_ip4_interfaces(cls, interfaces, gso=0, gso_size=0):
Dave Wallace670724c2022-09-20 21:52:18 -0400181 if not hasattr(cls, "vpp"):
182 cls.pg_interfaces = []
183 return cls.pg_interfaces
Neale Ranns6197cb72021-06-03 14:43:21 +0000184 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200185 return cls.create_pg_interfaces_internal(
186 interfaces, gso, gso_size, pgmode.PG_API_MODE_IP4
187 )
Neale Ranns6197cb72021-06-03 14:43:21 +0000188
189 @classmethod
190 def create_pg_ip6_interfaces(cls, interfaces, gso=0, gso_size=0):
Dave Wallace670724c2022-09-20 21:52:18 -0400191 if not hasattr(cls, "vpp"):
192 cls.pg_interfaces = []
193 return cls.pg_interfaces
Neale Ranns6197cb72021-06-03 14:43:21 +0000194 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200195 return cls.create_pg_interfaces_internal(
196 interfaces, gso, gso_size, pgmode.PG_API_MODE_IP6
197 )
Neale Ranns6197cb72021-06-03 14:43:21 +0000198
199 @classmethod
200 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
Dave Wallace670724c2022-09-20 21:52:18 -0400201 if not hasattr(cls, "vpp"):
202 cls.pg_interfaces = []
203 return cls.pg_interfaces
Neale Ranns6197cb72021-06-03 14:43:21 +0000204 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200205 return cls.create_pg_interfaces_internal(
206 interfaces, gso, gso_size, pgmode.PG_API_MODE_ETHERNET
207 )
Neale Ranns6197cb72021-06-03 14:43:21 +0000208
209 @classmethod
210 def create_pg_ethernet_interfaces(cls, interfaces, gso=0, gso_size=0):
Dave Wallace670724c2022-09-20 21:52:18 -0400211 if not hasattr(cls, "vpp"):
212 cls.pg_interfaces = []
213 return cls.pg_interfaces
Neale Ranns6197cb72021-06-03 14:43:21 +0000214 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200215 return cls.create_pg_interfaces_internal(
216 interfaces, gso, gso_size, pgmode.PG_API_MODE_ETHERNET
217 )
Neale Ranns6197cb72021-06-03 14:43:21 +0000218
219 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200220 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100221 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100222 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100223
Klement Sekerab9ef2732018-06-24 22:49:33 +0200224 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100225 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100226 """
Dave Wallace670724c2022-09-20 21:52:18 -0400227 if not hasattr(cls, "vpp"):
228 cls.lo_interfaces = []
229 return cls.lo_interfaces
Klement Sekerab9ef2732018-06-24 22:49:33 +0200230 result = [VppLoInterface(cls) for i in range(count)]
231 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100232 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100233 cls.lo_interfaces = result
234 return result
235
Neale Ranns192b13f2019-03-15 02:16:20 -0700236 @classmethod
237 def create_bvi_interfaces(cls, count):
238 """
239 Create BVI interfaces.
240
241 :param count: number of interfaces created.
242 :returns: List of created interfaces.
243 """
Dave Wallace670724c2022-09-20 21:52:18 -0400244 if not hasattr(cls, "vpp"):
245 cls.bvi_interfaces = []
246 return cls.bvi_interfaces
Neale Ranns192b13f2019-03-15 02:16:20 -0700247 result = [VppBviInterface(cls) for i in range(count)]
248 for intf in result:
249 setattr(cls, intf.name, intf)
250 cls.bvi_interfaces = result
251 return result
252
Damjan Marionf56b77a2016-10-03 19:44:57 +0200253 @staticmethod
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200254 def extend_packet(packet, size, padding=" "):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200255 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200256 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200257 NOTE: Currently works only when Raw layer is present.
258
259 :param packet: packet
260 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200261 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200262
263 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200264 packet_len = len(packet) + 4
265 extend = size - packet_len
266 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200267 num = (extend // len(padding)) + 1
268 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200269
Klement Sekeradab231a2016-12-21 08:50:14 +0100270 @classmethod
271 def reset_packet_infos(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200272 """Reset the list of packet info objects and packet counts to zero"""
Klement Sekeradab231a2016-12-21 08:50:14 +0100273 cls._packet_infos = {}
274 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200275
Klement Sekeradab231a2016-12-21 08:50:14 +0100276 @classmethod
277 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200278 """
279 Create packet info object containing the source and destination indexes
280 and add it to the testcase's packet info list
281
Klement Sekeradab231a2016-12-21 08:50:14 +0100282 :param VppInterface src_if: source interface
283 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200284
285 :returns: _PacketInfo object
286
287 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200288 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100289 info.index = len(cls._packet_infos)
290 info.src = src_if.sw_if_index
291 info.dst = dst_if.sw_if_index
292 if isinstance(dst_if, VppSubInterface):
293 dst_idx = dst_if.parent.sw_if_index
294 else:
295 dst_idx = dst_if.sw_if_index
296 if dst_idx in cls._packet_count_for_dst_if_idx:
297 cls._packet_count_for_dst_if_idx[dst_idx] += 1
298 else:
299 cls._packet_count_for_dst_if_idx[dst_idx] = 1
300 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200301 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200302
Damjan Marionf56b77a2016-10-03 19:44:57 +0200303 @staticmethod
304 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200305 """
306 Convert _PacketInfo object to packet payload
307
308 :param info: _PacketInfo object
309
310 :returns: string containing serialized data from packet info
311 """
Ray Kinsellab8165b92021-09-22 11:24:06 +0100312
313 # retrieve payload, currently 18 bytes (4 x ints + 1 short)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200314 return pack("iiiih", info.index, info.src, info.dst, info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200315
Damjan Marionf56b77a2016-10-03 19:44:57 +0200316 @staticmethod
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200317 def payload_to_info(payload, payload_field="load"):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200318 """
319 Convert packet payload to _PacketInfo object
320
321 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700322 :type payload: <class 'scapy.packet.Raw'>
323 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800324 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700325 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200326 :returns: _PacketInfo object containing de-serialized data from payload
327
328 """
Ray Kinsellab8165b92021-09-22 11:24:06 +0100329
330 # retrieve payload, currently 18 bytes (4 x ints + 1 short)
331 payload_b = getattr(payload, payload_field)[:18]
332
Damjan Marionf56b77a2016-10-03 19:44:57 +0200333 info = _PacketInfo()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200334 info.index, info.src, info.dst, info.ip, info.proto = unpack("iiiih", payload_b)
Ray Kinsellab8165b92021-09-22 11:24:06 +0100335
336 # some SRv6 TCs depend on get an exception if bad values are detected
337 if info.index > 0x4000:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200338 raise ValueError("Index value is invalid")
Ray Kinsellab8165b92021-09-22 11:24:06 +0100339
Damjan Marionf56b77a2016-10-03 19:44:57 +0200340 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200341
Damjan Marionf56b77a2016-10-03 19:44:57 +0200342 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200343 """
344 Iterate over the packet info list stored in the testcase
345 Start iteration with first element if info is None
346 Continue based on index in info if info is specified
347
348 :param info: info or None
349 :returns: next info in list or None if no more infos
350 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200351 if info is None:
352 next_index = 0
353 else:
354 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100355 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200356 return None
357 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100358 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200359
Klement Sekeraf62ae122016-10-11 11:47:09 +0200360 def get_next_packet_info_for_interface(self, src_index, info):
361 """
362 Search the packet info list for the next packet info with same source
363 interface index
364
365 :param src_index: source interface index to search for
366 :param info: packet info - where to start the search
367 :returns: packet info or None
368
369 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200370 while True:
371 info = self.get_next_packet_info(info)
372 if info is None:
373 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200374 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200375 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200376
Klement Sekeraf62ae122016-10-11 11:47:09 +0200377 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
378 """
379 Search the packet info list for the next packet info with same source
380 and destination interface indexes
381
382 :param src_index: source interface index to search for
383 :param dst_index: destination interface index to search for
384 :param info: packet info - where to start the search
385 :returns: packet info or None
386
387 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200388 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200389 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200390 if info is None:
391 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200392 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200393 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200394
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200395 def assert_packet_checksums_valid(self, packet, ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700396 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200397 udp_layers = ["UDP", "UDPerror"]
398 checksum_fields = ["cksum", "chksum"]
Klement Sekerad81ae412018-05-16 10:52:54 +0200399 checksums = []
400 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700401 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200402 while True:
403 layer = temp.getlayer(counter)
404 if layer:
Klement Sekera66cea092019-12-05 13:13:21 +0000405 layer = layer.copy()
406 layer.remove_payload()
Klement Sekerad81ae412018-05-16 10:52:54 +0200407 for cf in checksum_fields:
408 if hasattr(layer, cf):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200409 if (
410 ignore_zero_udp_checksums
411 and 0 == getattr(layer, cf)
412 and layer.name in udp_layers
413 ):
Klement Sekerad81ae412018-05-16 10:52:54 +0200414 continue
Klement Sekera66cea092019-12-05 13:13:21 +0000415 delattr(temp.getlayer(counter), cf)
Klement Sekerad81ae412018-05-16 10:52:54 +0200416 checksums.append((counter, cf))
417 else:
418 break
419 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +0200420 if 0 == len(checksums):
421 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700422 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekera738cf732022-11-14 11:26:18 +0100423 for layer, cf in reversed(checksums):
Klement Sekera31da2e32018-06-24 22:49:55 +0200424 calc_sum = getattr(temp[layer], cf)
425 self.assert_equal(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200426 getattr(received[layer], cf),
427 calc_sum,
428 "packet checksum on layer #%d: %s" % (layer, temp[layer].name),
429 )
Klement Sekera31da2e32018-06-24 22:49:55 +0200430 self.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200431 "Checksum field `%s` on `%s` layer has correct value `%s`"
432 % (cf, temp[layer].name, calc_sum)
433 )
Klement Sekerad81ae412018-05-16 10:52:54 +0200434
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200435 def assert_checksum_valid(
Klement Sekera738cf732022-11-14 11:26:18 +0100436 self,
437 received_packet,
438 layer,
439 checksum_field_names=["chksum", "cksum"],
440 ignore_zero_checksum=False,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200441 ):
442 """Check checksum of received packet on given layer"""
Klement Sekera738cf732022-11-14 11:26:18 +0100443 layer_copy = received_packet[layer].copy()
444 layer_copy.remove_payload()
445 field_name = None
446 for f in checksum_field_names:
447 if hasattr(layer_copy, f):
448 field_name = f
449 break
450 if field_name is None:
451 raise Exception(
452 f"Layer `{layer}` has none of checksum fields: `{checksum_field_names}`."
453 )
Klement Sekerad81ae412018-05-16 10:52:54 +0200454 received_packet_checksum = getattr(received_packet[layer], field_name)
455 if ignore_zero_checksum and 0 == received_packet_checksum:
456 return
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200457 recalculated = received_packet.__class__(scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +0200458 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700459 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200460 self.assert_equal(
461 received_packet_checksum,
462 getattr(recalculated[layer], field_name),
Klement Sekera738cf732022-11-14 11:26:18 +0100463 f"packet checksum (field: {field_name}) on layer: %s" % layer,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200464 )
Klement Sekerad81ae412018-05-16 10:52:54 +0200465
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200466 def assert_ip_checksum_valid(self, received_packet, ignore_zero_checksum=False):
467 self.assert_checksum_valid(
468 received_packet, "IP", ignore_zero_checksum=ignore_zero_checksum
469 )
Klement Sekerad81ae412018-05-16 10:52:54 +0200470
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200471 def assert_tcp_checksum_valid(self, received_packet, ignore_zero_checksum=False):
472 self.assert_checksum_valid(
473 received_packet, "TCP", ignore_zero_checksum=ignore_zero_checksum
474 )
Klement Sekerad81ae412018-05-16 10:52:54 +0200475
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200476 def assert_udp_checksum_valid(self, received_packet, ignore_zero_checksum=True):
477 self.assert_checksum_valid(
478 received_packet, "UDP", ignore_zero_checksum=ignore_zero_checksum
479 )
Klement Sekerad81ae412018-05-16 10:52:54 +0200480
481 def assert_embedded_icmp_checksum_valid(self, received_packet):
482 if received_packet.haslayer(IPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200483 self.assert_checksum_valid(received_packet, "IPerror")
Klement Sekerad81ae412018-05-16 10:52:54 +0200484 if received_packet.haslayer(TCPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200485 self.assert_checksum_valid(received_packet, "TCPerror")
Klement Sekerad81ae412018-05-16 10:52:54 +0200486 if received_packet.haslayer(UDPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200487 self.assert_checksum_valid(
488 received_packet, "UDPerror", ignore_zero_checksum=True
489 )
Klement Sekerad81ae412018-05-16 10:52:54 +0200490 if received_packet.haslayer(ICMPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200491 self.assert_checksum_valid(received_packet, "ICMPerror")
Klement Sekerad81ae412018-05-16 10:52:54 +0200492
493 def assert_icmp_checksum_valid(self, received_packet):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200494 self.assert_checksum_valid(received_packet, "ICMP")
Klement Sekerad81ae412018-05-16 10:52:54 +0200495 self.assert_embedded_icmp_checksum_valid(received_packet)
496
497 def assert_icmpv6_checksum_valid(self, pkt):
498 if pkt.haslayer(ICMPv6DestUnreach):
Klement Sekera738cf732022-11-14 11:26:18 +0100499 self.assert_checksum_valid(pkt, "ICMPv6DestUnreach")
Klement Sekerad81ae412018-05-16 10:52:54 +0200500 self.assert_embedded_icmp_checksum_valid(pkt)
501 if pkt.haslayer(ICMPv6EchoRequest):
Klement Sekera738cf732022-11-14 11:26:18 +0100502 self.assert_checksum_valid(pkt, "ICMPv6EchoRequest")
Klement Sekerad81ae412018-05-16 10:52:54 +0200503 if pkt.haslayer(ICMPv6EchoReply):
Klement Sekera738cf732022-11-14 11:26:18 +0100504 self.assert_checksum_valid(pkt, "ICMPv6EchoReply")
Klement Sekerad81ae412018-05-16 10:52:54 +0200505
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100506 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera107ad732022-02-18 10:32:08 +0000507 counter_value = self.get_counter(counter)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200508 self.assert_equal(
509 counter_value, expected_value, "packet counter `%s'" % counter
510 )
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100511
Benoît Ganne8c45e512021-02-19 16:39:13 +0100512 def pg_send(self, intf, pkts, worker=None, trace=True):
Neale Ranns4a56f4e2019-12-23 04:10:25 +0000513 intf.add_stream(pkts, worker=worker)
Neale Ranns52fae862018-01-08 04:41:42 -0800514 self.pg_enable_capture(self.pg_interfaces)
Benoît Ganne8c45e512021-02-19 16:39:13 +0100515 self.pg_start(trace=trace)
Paul Vinciguerraeb414432019-02-20 09:01:14 -0800516
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200517 def send_and_assert_no_replies(
518 self, intf, pkts, remark="", timeout=None, stats_diff=None, trace=True, msg=None
519 ):
Klement Sekeraad3187f2022-02-18 10:34:35 +0000520 if stats_diff:
521 stats_snapshot = self.snapshot_stats(stats_diff)
522
Paul Vinciguerraeb414432019-02-20 09:01:14 -0800523 self.pg_send(intf, pkts)
Klement Sekeraad3187f2022-02-18 10:34:35 +0000524
525 try:
526 if not timeout:
527 timeout = 1
528 for i in self.pg_interfaces:
Klement Sekera26cd0242022-02-18 10:35:08 +0000529 i.assert_nothing_captured(timeout=timeout, remark=remark)
Klement Sekeraad3187f2022-02-18 10:34:35 +0000530 timeout = 0.1
531 finally:
532 if trace:
533 if msg:
534 self.logger.debug(f"send_and_assert_no_replies: {msg}")
535 self.logger.debug(self.vapi.cli("show trace"))
536
537 if stats_diff:
538 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
Neale Ranns52fae862018-01-08 04:41:42 -0800539
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200540 def send_and_expect(
541 self,
542 intf,
543 pkts,
544 output,
545 n_rx=None,
546 worker=None,
547 trace=True,
548 msg=None,
549 stats_diff=None,
Ivan Ivanetsb2b87e42024-09-27 17:11:18 +0300550 filter_out_fn=is_ipv6_misc,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200551 ):
Klement Sekeraad3187f2022-02-18 10:34:35 +0000552 if stats_diff:
553 stats_snapshot = self.snapshot_stats(stats_diff)
554
Neale Rannsd7603d92019-03-28 08:56:10 +0000555 if not n_rx:
Klement Sekera56c492a2022-01-10 21:57:27 +0000556 n_rx = 1 if isinstance(pkts, Packet) else len(pkts)
Benoît Ganne8c45e512021-02-19 16:39:13 +0100557 self.pg_send(intf, pkts, worker=worker, trace=trace)
Ivan Ivanetsb2b87e42024-09-27 17:11:18 +0300558 rx = output.get_capture(n_rx, filter_out_fn=filter_out_fn)
Klement Sekeraff334db2021-05-26 13:02:35 +0200559 if trace:
Klement Sekeraad3187f2022-02-18 10:34:35 +0000560 if msg:
561 self.logger.debug(f"send_and_expect: {msg}")
Klement Sekeraff334db2021-05-26 13:02:35 +0200562 self.logger.debug(self.vapi.cli("show trace"))
Klement Sekeraad3187f2022-02-18 10:34:35 +0000563
564 if stats_diff:
565 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
566
Neale Ranns93cc3ee2018-10-10 07:22:51 -0700567 return rx
568
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200569 def send_and_expect_load_balancing(
570 self, input, pkts, outputs, worker=None, trace=True
571 ):
Neale Ranns699bea22022-02-17 09:22:16 +0000572 self.pg_send(input, pkts, worker=worker, trace=trace)
573 rxs = []
574 for oo in outputs:
575 rx = oo._get_capture(1)
Dave Wallace8800f732023-08-31 00:47:44 -0400576 self.assertNotEqual(0, len(rx), f"0 != len(rx) ({len(rx)})")
Neale Ranns699bea22022-02-17 09:22:16 +0000577 rxs.append(rx)
578 if trace:
579 self.logger.debug(self.vapi.cli("show trace"))
580 return rxs
581
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200582 def send_and_expect_some(self, intf, pkts, output, worker=None, trace=True):
Neale Ranns5c6dd172022-02-17 09:08:47 +0000583 self.pg_send(intf, pkts, worker=worker, trace=trace)
584 rx = output._get_capture(1)
585 if trace:
586 self.logger.debug(self.vapi.cli("show trace"))
587 self.assertTrue(len(rx) > 0)
Dave Wallace8800f732023-08-31 00:47:44 -0400588 self.assertTrue(
589 len(rx) <= len(pkts), f"len(rx) ({len(rx)}) > len(pkts) ({len(pkts)})"
590 )
Neale Ranns5c6dd172022-02-17 09:08:47 +0000591 return rx
592
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200593 def send_and_expect_only(self, intf, pkts, output, timeout=None, stats_diff=None):
Klement Sekeraad3187f2022-02-18 10:34:35 +0000594 if stats_diff:
595 stats_snapshot = self.snapshot_stats(stats_diff)
596
Paul Vinciguerraeb414432019-02-20 09:01:14 -0800597 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -0800598 rx = output.get_capture(len(pkts))
599 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -0700600 if not timeout:
601 timeout = 1
602 for i in self.pg_interfaces:
603 if i not in outputs:
Klement Sekera26cd0242022-02-18 10:35:08 +0000604 i.assert_nothing_captured(timeout=timeout)
Neale Ranns93cc3ee2018-10-10 07:22:51 -0700605 timeout = 0.1
606
Klement Sekeraad3187f2022-02-18 10:34:35 +0000607 if stats_diff:
608 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
609
Neale Ranns52fae862018-01-08 04:41:42 -0800610 return rx
611
Damjan Marionf56b77a2016-10-03 19:44:57 +0200612
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200613if __name__ == "__main__":
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -0800614 pass