blob: 6ff03d8b07309e941cbee97f6d2c1272aea7c56a [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
Paul Vinciguerra919efad2018-12-17 21:43:43 -080029from vpp_pg_interface import VppPGInterface
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
Matej Klotton16a14cd2016-12-07 15:09:13 +010074 def __eq__(self, other):
75 index = self.index == other.index
76 src = self.src == other.src
77 dst = self.dst == other.dst
78 data = self.data == other.data
79 return index and src and dst and data
80
Klement Sekeraf62ae122016-10-11 11:47:09 +020081
Naveen Joyc872cec2022-08-30 13:59:03 -070082@use_running
Dave Wallace8800f732023-08-31 00:47:44 -040083class VppTestCase(VppAsfTestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +010084 """This subclass is a base class for VPP test cases that are implemented as
85 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +020086 """
87
88 @property
89 def packet_infos(self):
90 """List of packet infos"""
91 return self._packet_infos
92
Klement Sekeradab231a2016-12-21 08:50:14 +010093 @classmethod
94 def get_packet_count_for_if_idx(cls, dst_if_index):
95 """Get the number of packet info for specified destination if index"""
96 if dst_if_index in cls._packet_count_for_dst_if_idx:
97 return cls._packet_count_for_dst_if_idx[dst_if_index]
98 else:
99 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200100
101 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200102 def setUpClass(cls):
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800103 super(VppTestCase, cls).setUpClass()
Klement Sekeradab231a2016-12-21 08:50:14 +0100104 cls.reset_packet_infos()
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200105 cls._pcaps = []
106 cls._old_pcaps = []
Klement Sekera277b89c2016-10-28 13:20:27 +0200107
Damjan Marionf56b77a2016-10-03 19:44:57 +0200108 @classmethod
109 def tearDownClass(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200110 cls.logger.debug("--- tearDownClass() for %s called ---" % cls.__name__)
Klement Sekeraebbaf552018-02-17 13:41:33 +0100111 cls.reset_packet_infos()
Dave Wallace8800f732023-08-31 00:47:44 -0400112 super(VppTestCase, cls).tearDownClass()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200113
Damjan Marionf56b77a2016-10-03 19:44:57 +0200114 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200115 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200116 """
117 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200118
Klement Sekera75e7d132017-09-20 08:26:30 +0200119 :param interfaces: iterable interface indexes (if None,
120 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200121
Klement Sekeraf62ae122016-10-11 11:47:09 +0200122 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200123 if interfaces is None:
124 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200125 for i in interfaces:
126 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200127
Damjan Marionf56b77a2016-10-03 19:44:57 +0200128 @classmethod
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200129 def register_pcap(cls, intf, worker):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200130 """Register a pcap in the testclass"""
Klement Sekera9225dee2016-12-12 08:36:58 +0100131 # add to the list of captures with current timestamp
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200132 cls._pcaps.append((intf, worker))
Klement Sekera9225dee2016-12-12 08:36:58 +0100133
134 @classmethod
adrianvillinc6fe6172023-12-06 19:21:49 +0100135 def pg_start(cls, trace=True, traceFilter=False):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200136 """Enable the PG, wait till it is done, then clean up"""
Dave Wallace7b8b4652023-08-15 19:05:26 -0400137 for intf, worker in cls._old_pcaps:
Dave Wallace8800f732023-08-31 00:47:44 -0400138 intf.remove_old_pcap_file(intf.get_in_path(worker))
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200139 cls._old_pcaps = []
Benoît Ganne8c45e512021-02-19 16:39:13 +0100140 if trace:
141 cls.vapi.cli("clear trace")
adrianvillinc6fe6172023-12-06 19:21:49 +0100142 cls.vapi.cli("trace add pg-input 1000" + (" filter" if traceFilter else ""))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200143 cls.vapi.cli("packet-generator enable")
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000144 # PG, when starts, runs to completion -
145 # so let's avoid a race condition,
146 # and wait a little till it's done.
147 # Then clean it up - and then be gone.
148 deadline = time.time() + 300
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200149 while cls.vapi.cli("show packet-generator").find("Yes") != -1:
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000150 cls.sleep(0.01) # yield
151 if time.time() > deadline:
152 cls.logger.error("Timeout waiting for pg to stop")
153 break
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200154 for intf, worker in cls._pcaps:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200155 cls.vapi.cli("packet-generator delete %s" % intf.get_cap_name(worker))
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200156 cls._old_pcaps = cls._pcaps
157 cls._pcaps = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200158
Damjan Marionf56b77a2016-10-03 19:44:57 +0200159 @classmethod
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200160 def create_pg_interfaces_internal(cls, interfaces, gso=0, gso_size=0, mode=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200161 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100162 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200163
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100164 :param interfaces: iterable indexes of the interfaces.
165 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200166
Klement Sekeraf62ae122016-10-11 11:47:09 +0200167 """
168 result = []
169 for i in interfaces:
Neale Ranns6197cb72021-06-03 14:43:21 +0000170 intf = VppPGInterface(cls, i, gso, gso_size, mode)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200171 setattr(cls, intf.name, intf)
172 result.append(intf)
173 cls.pg_interfaces = result
174 return result
175
Matej Klotton0178d522016-11-04 11:11:44 +0100176 @classmethod
Neale Ranns6197cb72021-06-03 14:43:21 +0000177 def create_pg_ip4_interfaces(cls, interfaces, gso=0, gso_size=0):
Dave Wallace670724c2022-09-20 21:52:18 -0400178 if not hasattr(cls, "vpp"):
179 cls.pg_interfaces = []
180 return cls.pg_interfaces
Neale Ranns6197cb72021-06-03 14:43:21 +0000181 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200182 return cls.create_pg_interfaces_internal(
183 interfaces, gso, gso_size, pgmode.PG_API_MODE_IP4
184 )
Neale Ranns6197cb72021-06-03 14:43:21 +0000185
186 @classmethod
187 def create_pg_ip6_interfaces(cls, interfaces, gso=0, gso_size=0):
Dave Wallace670724c2022-09-20 21:52:18 -0400188 if not hasattr(cls, "vpp"):
189 cls.pg_interfaces = []
190 return cls.pg_interfaces
Neale Ranns6197cb72021-06-03 14:43:21 +0000191 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200192 return cls.create_pg_interfaces_internal(
193 interfaces, gso, gso_size, pgmode.PG_API_MODE_IP6
194 )
Neale Ranns6197cb72021-06-03 14:43:21 +0000195
196 @classmethod
197 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
Dave Wallace670724c2022-09-20 21:52:18 -0400198 if not hasattr(cls, "vpp"):
199 cls.pg_interfaces = []
200 return cls.pg_interfaces
Neale Ranns6197cb72021-06-03 14:43:21 +0000201 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200202 return cls.create_pg_interfaces_internal(
203 interfaces, gso, gso_size, pgmode.PG_API_MODE_ETHERNET
204 )
Neale Ranns6197cb72021-06-03 14:43:21 +0000205
206 @classmethod
207 def create_pg_ethernet_interfaces(cls, interfaces, gso=0, gso_size=0):
Dave Wallace670724c2022-09-20 21:52:18 -0400208 if not hasattr(cls, "vpp"):
209 cls.pg_interfaces = []
210 return cls.pg_interfaces
Neale Ranns6197cb72021-06-03 14:43:21 +0000211 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200212 return cls.create_pg_interfaces_internal(
213 interfaces, gso, gso_size, pgmode.PG_API_MODE_ETHERNET
214 )
Neale Ranns6197cb72021-06-03 14:43:21 +0000215
216 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200217 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100218 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100219 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100220
Klement Sekerab9ef2732018-06-24 22:49:33 +0200221 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100222 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100223 """
Dave Wallace670724c2022-09-20 21:52:18 -0400224 if not hasattr(cls, "vpp"):
225 cls.lo_interfaces = []
226 return cls.lo_interfaces
Klement Sekerab9ef2732018-06-24 22:49:33 +0200227 result = [VppLoInterface(cls) for i in range(count)]
228 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100229 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100230 cls.lo_interfaces = result
231 return result
232
Neale Ranns192b13f2019-03-15 02:16:20 -0700233 @classmethod
234 def create_bvi_interfaces(cls, count):
235 """
236 Create BVI interfaces.
237
238 :param count: number of interfaces created.
239 :returns: List of created interfaces.
240 """
Dave Wallace670724c2022-09-20 21:52:18 -0400241 if not hasattr(cls, "vpp"):
242 cls.bvi_interfaces = []
243 return cls.bvi_interfaces
Neale Ranns192b13f2019-03-15 02:16:20 -0700244 result = [VppBviInterface(cls) for i in range(count)]
245 for intf in result:
246 setattr(cls, intf.name, intf)
247 cls.bvi_interfaces = result
248 return result
249
Damjan Marionf56b77a2016-10-03 19:44:57 +0200250 @staticmethod
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200251 def extend_packet(packet, size, padding=" "):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200252 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200253 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200254 NOTE: Currently works only when Raw layer is present.
255
256 :param packet: packet
257 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200258 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200259
260 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200261 packet_len = len(packet) + 4
262 extend = size - packet_len
263 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200264 num = (extend // len(padding)) + 1
265 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200266
Klement Sekeradab231a2016-12-21 08:50:14 +0100267 @classmethod
268 def reset_packet_infos(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200269 """Reset the list of packet info objects and packet counts to zero"""
Klement Sekeradab231a2016-12-21 08:50:14 +0100270 cls._packet_infos = {}
271 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200272
Klement Sekeradab231a2016-12-21 08:50:14 +0100273 @classmethod
274 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200275 """
276 Create packet info object containing the source and destination indexes
277 and add it to the testcase's packet info list
278
Klement Sekeradab231a2016-12-21 08:50:14 +0100279 :param VppInterface src_if: source interface
280 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200281
282 :returns: _PacketInfo object
283
284 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200285 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100286 info.index = len(cls._packet_infos)
287 info.src = src_if.sw_if_index
288 info.dst = dst_if.sw_if_index
289 if isinstance(dst_if, VppSubInterface):
290 dst_idx = dst_if.parent.sw_if_index
291 else:
292 dst_idx = dst_if.sw_if_index
293 if dst_idx in cls._packet_count_for_dst_if_idx:
294 cls._packet_count_for_dst_if_idx[dst_idx] += 1
295 else:
296 cls._packet_count_for_dst_if_idx[dst_idx] = 1
297 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200298 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200299
Damjan Marionf56b77a2016-10-03 19:44:57 +0200300 @staticmethod
301 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200302 """
303 Convert _PacketInfo object to packet payload
304
305 :param info: _PacketInfo object
306
307 :returns: string containing serialized data from packet info
308 """
Ray Kinsellab8165b92021-09-22 11:24:06 +0100309
310 # retrieve payload, currently 18 bytes (4 x ints + 1 short)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200311 return pack("iiiih", info.index, info.src, info.dst, info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200312
Damjan Marionf56b77a2016-10-03 19:44:57 +0200313 @staticmethod
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200314 def payload_to_info(payload, payload_field="load"):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200315 """
316 Convert packet payload to _PacketInfo object
317
318 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700319 :type payload: <class 'scapy.packet.Raw'>
320 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800321 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700322 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200323 :returns: _PacketInfo object containing de-serialized data from payload
324
325 """
Ray Kinsellab8165b92021-09-22 11:24:06 +0100326
327 # retrieve payload, currently 18 bytes (4 x ints + 1 short)
328 payload_b = getattr(payload, payload_field)[:18]
329
Damjan Marionf56b77a2016-10-03 19:44:57 +0200330 info = _PacketInfo()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200331 info.index, info.src, info.dst, info.ip, info.proto = unpack("iiiih", payload_b)
Ray Kinsellab8165b92021-09-22 11:24:06 +0100332
333 # some SRv6 TCs depend on get an exception if bad values are detected
334 if info.index > 0x4000:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200335 raise ValueError("Index value is invalid")
Ray Kinsellab8165b92021-09-22 11:24:06 +0100336
Damjan Marionf56b77a2016-10-03 19:44:57 +0200337 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200338
Damjan Marionf56b77a2016-10-03 19:44:57 +0200339 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200340 """
341 Iterate over the packet info list stored in the testcase
342 Start iteration with first element if info is None
343 Continue based on index in info if info is specified
344
345 :param info: info or None
346 :returns: next info in list or None if no more infos
347 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200348 if info is None:
349 next_index = 0
350 else:
351 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100352 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200353 return None
354 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100355 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200356
Klement Sekeraf62ae122016-10-11 11:47:09 +0200357 def get_next_packet_info_for_interface(self, src_index, info):
358 """
359 Search the packet info list for the next packet info with same source
360 interface index
361
362 :param src_index: source interface index to search for
363 :param info: packet info - where to start the search
364 :returns: packet info or None
365
366 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200367 while True:
368 info = self.get_next_packet_info(info)
369 if info is None:
370 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200371 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200372 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200373
Klement Sekeraf62ae122016-10-11 11:47:09 +0200374 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
375 """
376 Search the packet info list for the next packet info with same source
377 and destination interface indexes
378
379 :param src_index: source interface index to search for
380 :param dst_index: destination interface index to search for
381 :param info: packet info - where to start the search
382 :returns: packet info or None
383
384 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200385 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200386 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200387 if info is None:
388 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200389 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200390 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200391
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200392 def assert_packet_checksums_valid(self, packet, ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700393 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200394 udp_layers = ["UDP", "UDPerror"]
395 checksum_fields = ["cksum", "chksum"]
Klement Sekerad81ae412018-05-16 10:52:54 +0200396 checksums = []
397 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700398 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200399 while True:
400 layer = temp.getlayer(counter)
401 if layer:
Klement Sekera66cea092019-12-05 13:13:21 +0000402 layer = layer.copy()
403 layer.remove_payload()
Klement Sekerad81ae412018-05-16 10:52:54 +0200404 for cf in checksum_fields:
405 if hasattr(layer, cf):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200406 if (
407 ignore_zero_udp_checksums
408 and 0 == getattr(layer, cf)
409 and layer.name in udp_layers
410 ):
Klement Sekerad81ae412018-05-16 10:52:54 +0200411 continue
Klement Sekera66cea092019-12-05 13:13:21 +0000412 delattr(temp.getlayer(counter), cf)
Klement Sekerad81ae412018-05-16 10:52:54 +0200413 checksums.append((counter, cf))
414 else:
415 break
416 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +0200417 if 0 == len(checksums):
418 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700419 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekera738cf732022-11-14 11:26:18 +0100420 for layer, cf in reversed(checksums):
Klement Sekera31da2e32018-06-24 22:49:55 +0200421 calc_sum = getattr(temp[layer], cf)
422 self.assert_equal(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200423 getattr(received[layer], cf),
424 calc_sum,
425 "packet checksum on layer #%d: %s" % (layer, temp[layer].name),
426 )
Klement Sekera31da2e32018-06-24 22:49:55 +0200427 self.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200428 "Checksum field `%s` on `%s` layer has correct value `%s`"
429 % (cf, temp[layer].name, calc_sum)
430 )
Klement Sekerad81ae412018-05-16 10:52:54 +0200431
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200432 def assert_checksum_valid(
Klement Sekera738cf732022-11-14 11:26:18 +0100433 self,
434 received_packet,
435 layer,
436 checksum_field_names=["chksum", "cksum"],
437 ignore_zero_checksum=False,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200438 ):
439 """Check checksum of received packet on given layer"""
Klement Sekera738cf732022-11-14 11:26:18 +0100440 layer_copy = received_packet[layer].copy()
441 layer_copy.remove_payload()
442 field_name = None
443 for f in checksum_field_names:
444 if hasattr(layer_copy, f):
445 field_name = f
446 break
447 if field_name is None:
448 raise Exception(
449 f"Layer `{layer}` has none of checksum fields: `{checksum_field_names}`."
450 )
Klement Sekerad81ae412018-05-16 10:52:54 +0200451 received_packet_checksum = getattr(received_packet[layer], field_name)
452 if ignore_zero_checksum and 0 == received_packet_checksum:
453 return
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200454 recalculated = received_packet.__class__(scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +0200455 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700456 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200457 self.assert_equal(
458 received_packet_checksum,
459 getattr(recalculated[layer], field_name),
Klement Sekera738cf732022-11-14 11:26:18 +0100460 f"packet checksum (field: {field_name}) on layer: %s" % layer,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200461 )
Klement Sekerad81ae412018-05-16 10:52:54 +0200462
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200463 def assert_ip_checksum_valid(self, received_packet, ignore_zero_checksum=False):
464 self.assert_checksum_valid(
465 received_packet, "IP", ignore_zero_checksum=ignore_zero_checksum
466 )
Klement Sekerad81ae412018-05-16 10:52:54 +0200467
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200468 def assert_tcp_checksum_valid(self, received_packet, ignore_zero_checksum=False):
469 self.assert_checksum_valid(
470 received_packet, "TCP", ignore_zero_checksum=ignore_zero_checksum
471 )
Klement Sekerad81ae412018-05-16 10:52:54 +0200472
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200473 def assert_udp_checksum_valid(self, received_packet, ignore_zero_checksum=True):
474 self.assert_checksum_valid(
475 received_packet, "UDP", ignore_zero_checksum=ignore_zero_checksum
476 )
Klement Sekerad81ae412018-05-16 10:52:54 +0200477
478 def assert_embedded_icmp_checksum_valid(self, received_packet):
479 if received_packet.haslayer(IPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200480 self.assert_checksum_valid(received_packet, "IPerror")
Klement Sekerad81ae412018-05-16 10:52:54 +0200481 if received_packet.haslayer(TCPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200482 self.assert_checksum_valid(received_packet, "TCPerror")
Klement Sekerad81ae412018-05-16 10:52:54 +0200483 if received_packet.haslayer(UDPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200484 self.assert_checksum_valid(
485 received_packet, "UDPerror", ignore_zero_checksum=True
486 )
Klement Sekerad81ae412018-05-16 10:52:54 +0200487 if received_packet.haslayer(ICMPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200488 self.assert_checksum_valid(received_packet, "ICMPerror")
Klement Sekerad81ae412018-05-16 10:52:54 +0200489
490 def assert_icmp_checksum_valid(self, received_packet):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200491 self.assert_checksum_valid(received_packet, "ICMP")
Klement Sekerad81ae412018-05-16 10:52:54 +0200492 self.assert_embedded_icmp_checksum_valid(received_packet)
493
494 def assert_icmpv6_checksum_valid(self, pkt):
495 if pkt.haslayer(ICMPv6DestUnreach):
Klement Sekera738cf732022-11-14 11:26:18 +0100496 self.assert_checksum_valid(pkt, "ICMPv6DestUnreach")
Klement Sekerad81ae412018-05-16 10:52:54 +0200497 self.assert_embedded_icmp_checksum_valid(pkt)
498 if pkt.haslayer(ICMPv6EchoRequest):
Klement Sekera738cf732022-11-14 11:26:18 +0100499 self.assert_checksum_valid(pkt, "ICMPv6EchoRequest")
Klement Sekerad81ae412018-05-16 10:52:54 +0200500 if pkt.haslayer(ICMPv6EchoReply):
Klement Sekera738cf732022-11-14 11:26:18 +0100501 self.assert_checksum_valid(pkt, "ICMPv6EchoReply")
Klement Sekerad81ae412018-05-16 10:52:54 +0200502
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100503 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera107ad732022-02-18 10:32:08 +0000504 counter_value = self.get_counter(counter)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200505 self.assert_equal(
506 counter_value, expected_value, "packet counter `%s'" % counter
507 )
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100508
Benoît Ganne8c45e512021-02-19 16:39:13 +0100509 def pg_send(self, intf, pkts, worker=None, trace=True):
Neale Ranns4a56f4e2019-12-23 04:10:25 +0000510 intf.add_stream(pkts, worker=worker)
Neale Ranns52fae862018-01-08 04:41:42 -0800511 self.pg_enable_capture(self.pg_interfaces)
Benoît Ganne8c45e512021-02-19 16:39:13 +0100512 self.pg_start(trace=trace)
Paul Vinciguerraeb414432019-02-20 09:01:14 -0800513
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200514 def send_and_assert_no_replies(
515 self, intf, pkts, remark="", timeout=None, stats_diff=None, trace=True, msg=None
516 ):
Klement Sekeraad3187f2022-02-18 10:34:35 +0000517 if stats_diff:
518 stats_snapshot = self.snapshot_stats(stats_diff)
519
Paul Vinciguerraeb414432019-02-20 09:01:14 -0800520 self.pg_send(intf, pkts)
Klement Sekeraad3187f2022-02-18 10:34:35 +0000521
522 try:
523 if not timeout:
524 timeout = 1
525 for i in self.pg_interfaces:
Klement Sekera26cd0242022-02-18 10:35:08 +0000526 i.assert_nothing_captured(timeout=timeout, remark=remark)
Klement Sekeraad3187f2022-02-18 10:34:35 +0000527 timeout = 0.1
528 finally:
529 if trace:
530 if msg:
531 self.logger.debug(f"send_and_assert_no_replies: {msg}")
532 self.logger.debug(self.vapi.cli("show trace"))
533
534 if stats_diff:
535 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
Neale Ranns52fae862018-01-08 04:41:42 -0800536
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200537 def send_and_expect(
538 self,
539 intf,
540 pkts,
541 output,
542 n_rx=None,
543 worker=None,
544 trace=True,
545 msg=None,
546 stats_diff=None,
547 ):
Klement Sekeraad3187f2022-02-18 10:34:35 +0000548 if stats_diff:
549 stats_snapshot = self.snapshot_stats(stats_diff)
550
Neale Rannsd7603d92019-03-28 08:56:10 +0000551 if not n_rx:
Klement Sekera56c492a2022-01-10 21:57:27 +0000552 n_rx = 1 if isinstance(pkts, Packet) else len(pkts)
Benoît Ganne8c45e512021-02-19 16:39:13 +0100553 self.pg_send(intf, pkts, worker=worker, trace=trace)
Neale Rannsd7603d92019-03-28 08:56:10 +0000554 rx = output.get_capture(n_rx)
Klement Sekeraff334db2021-05-26 13:02:35 +0200555 if trace:
Klement Sekeraad3187f2022-02-18 10:34:35 +0000556 if msg:
557 self.logger.debug(f"send_and_expect: {msg}")
Klement Sekeraff334db2021-05-26 13:02:35 +0200558 self.logger.debug(self.vapi.cli("show trace"))
Klement Sekeraad3187f2022-02-18 10:34:35 +0000559
560 if stats_diff:
561 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
562
Neale Ranns93cc3ee2018-10-10 07:22:51 -0700563 return rx
564
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200565 def send_and_expect_load_balancing(
566 self, input, pkts, outputs, worker=None, trace=True
567 ):
Neale Ranns699bea22022-02-17 09:22:16 +0000568 self.pg_send(input, pkts, worker=worker, trace=trace)
569 rxs = []
570 for oo in outputs:
571 rx = oo._get_capture(1)
Dave Wallace8800f732023-08-31 00:47:44 -0400572 self.assertNotEqual(0, len(rx), f"0 != len(rx) ({len(rx)})")
Neale Ranns699bea22022-02-17 09:22:16 +0000573 rxs.append(rx)
574 if trace:
575 self.logger.debug(self.vapi.cli("show trace"))
576 return rxs
577
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200578 def send_and_expect_some(self, intf, pkts, output, worker=None, trace=True):
Neale Ranns5c6dd172022-02-17 09:08:47 +0000579 self.pg_send(intf, pkts, worker=worker, trace=trace)
580 rx = output._get_capture(1)
581 if trace:
582 self.logger.debug(self.vapi.cli("show trace"))
583 self.assertTrue(len(rx) > 0)
Dave Wallace8800f732023-08-31 00:47:44 -0400584 self.assertTrue(
585 len(rx) <= len(pkts), f"len(rx) ({len(rx)}) > len(pkts) ({len(pkts)})"
586 )
Neale Ranns5c6dd172022-02-17 09:08:47 +0000587 return rx
588
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200589 def send_and_expect_only(self, intf, pkts, output, timeout=None, stats_diff=None):
Klement Sekeraad3187f2022-02-18 10:34:35 +0000590 if stats_diff:
591 stats_snapshot = self.snapshot_stats(stats_diff)
592
Paul Vinciguerraeb414432019-02-20 09:01:14 -0800593 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -0800594 rx = output.get_capture(len(pkts))
595 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -0700596 if not timeout:
597 timeout = 1
598 for i in self.pg_interfaces:
599 if i not in outputs:
Klement Sekera26cd0242022-02-18 10:35:08 +0000600 i.assert_nothing_captured(timeout=timeout)
Neale Ranns93cc3ee2018-10-10 07:22:51 -0700601 timeout = 0.1
602
Klement Sekeraad3187f2022-02-18 10:34:35 +0000603 if stats_diff:
604 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
605
Neale Ranns52fae862018-01-08 04:41:42 -0800606 return rx
607
Damjan Marionf56b77a2016-10-03 19:44:57 +0200608
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200609if __name__ == "__main__":
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -0800610 pass