From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 88A2F43746; Tue, 19 Dec 2023 17:54:36 +0100 (CET) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 7514142E11; Tue, 19 Dec 2023 17:54:36 +0100 (CET) Received: from mail-ed1-f53.google.com (mail-ed1-f53.google.com [209.85.208.53]) by mails.dpdk.org (Postfix) with ESMTP id 2ADFD42DED for ; Tue, 19 Dec 2023 17:54:34 +0100 (CET) Received: by mail-ed1-f53.google.com with SMTP id 4fb4d7f45d1cf-552fba34d69so4434072a12.3 for ; Tue, 19 Dec 2023 08:54:34 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pantheon.tech; s=google; t=1703004874; x=1703609674; darn=dpdk.org; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=xphsJ09nDZMNT77mZiEZMz5kMUhfLSwJvYtBVpZubvA=; b=MBcwjUdS0CqSH6o9T/6QobNIaO+4R0i7E8N2t5Sp8VXZUUzrgBdudl07Hew8WaNmcC sLH7Cn1wkXCjMLPJtcmX1ObsHeGkRKf337nC4+cj+A3LzMskyKKLwLLk3lfis+ECfOr9 ypdWkTA25zVaaSoPLIkKfjIJuk5wF16Rck7NyoJiw4TWjELmF1x2O67JrKFbAoDHzIX1 koSx4w1AcistjrcjRRgKIX6tKt1pynM72pf6g34pMwERzSIxL7Y41VDd3rwmDqJHS8M8 DVpHA8MJSOxB0Eu7H6RKGT38w620VpmsHNKFFJFaTiWWh3iUdeQ+UOukv02gjFJQugnn RXpg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1703004874; x=1703609674; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=xphsJ09nDZMNT77mZiEZMz5kMUhfLSwJvYtBVpZubvA=; b=VJfb49+HVhn4pMnkuwf71kj3JDlijhAXMLOFL+FoMig0Phd4NiKe0j55S6REiE+I2O egr3kx1QrjzZ6Akv2bk26Cxi5N7YqCZ4SNkB5DSvhtPFyYO6KN/sSH6DXFkbWzDP2lBy 3aLYcqMuOYLBajxZfqJenumf3E/Rd0oeylJV3xrkVGQu9mA19xV7ooiLBHM9vf0rb52P 5bnriu+mJxDs2SH2aUNQgGSiBgL1BJIeupG9UD5SnkdoOn4OcECvlhcMaAQf+hPFkOAg WhJ1LM2iECMoCx/2ahVeirudWfoohR05PP4bA1wTFIUBPfiWfpdZyMrHom8hrlQpWEoz x6+g== X-Gm-Message-State: AOJu0YwIlGizJ5YMkkCXpW96vi6pCdKyCP8EH08SxFvglWVOqrYTcj43 APGPiASZRZ+YrRmj4k97gtR4TyBtrZku5nS2CB7+KQ== X-Google-Smtp-Source: AGHT+IGzPspPFsmPl31oLs3K981qkgoMbkwqP4O5g+6pZvY6JmP535lpOdzznHzQBRjn2qDY+BM4c0hIul72CPzEXtw= X-Received: by 2002:a17:907:3e0c:b0:a23:333b:129a with SMTP id hp12-20020a1709073e0c00b00a23333b129amr2910878ejc.53.1703004873914; Tue, 19 Dec 2023 08:54:33 -0800 (PST) MIME-Version: 1.0 References: <20231218181221.10057-1-jspewock@iol.unh.edu> <20231218181221.10057-4-jspewock@iol.unh.edu> In-Reply-To: <20231218181221.10057-4-jspewock@iol.unh.edu> From: =?UTF-8?Q?Juraj_Linke=C5=A1?= Date: Tue, 19 Dec 2023 17:54:23 +0100 Message-ID: Subject: Re: [PATCH v4 3/7] dts: add optional packet filtering to scapy sniffer To: jspewock@iol.unh.edu Cc: Honnappa.Nagarahalli@arm.com, thomas@monjalon.net, wathsala.vithanage@arm.com, probb@iol.unh.edu, paul.szczepanek@arm.com, yoan.picchi@foss.arm.com, ferruh.yigit@amd.com, andrew.rybchenko@oktetlabs.ru, dev@dpdk.org Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Reviewed-by: Juraj Linke=C5=A1 On Mon, Dec 18, 2023 at 7:13=E2=80=AFPM wrote: > > From: Jeremy Spewock > > Added the options to filter out LLDP and ARP packets when > sniffing for packets with scapy. This was done using BPF filters to > ensure that the noise these packets provide does not interfere with test > cases. > > Signed-off-by: Jeremy Spewock > --- > dts/framework/test_suite.py | 14 ++++++++-- > .../capturing_traffic_generator.py | 22 ++++++++++++++- > dts/framework/testbed_model/scapy.py | 28 ++++++++++++++++++- > dts/framework/testbed_model/tg_node.py | 12 ++++++-- > 4 files changed, 70 insertions(+), 6 deletions(-) > > diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py > index 4a7907ec33..6dfa570041 100644 > --- a/dts/framework/test_suite.py > +++ b/dts/framework/test_suite.py > @@ -27,6 +27,7 @@ > from .settings import SETTINGS > from .test_result import BuildTargetResult, Result, TestCaseResult, Test= SuiteResult > from .testbed_model import SutNode, TGNode > +from .testbed_model.capturing_traffic_generator import PacketFilteringCo= nfig > from .testbed_model.hw.port import Port, PortLink > from .utils import get_packet_summaries > > @@ -149,7 +150,12 @@ def configure_testbed_ipv4(self, restore: bool =3D F= alse) -> None: > def _configure_ipv4_forwarding(self, enable: bool) -> None: > self.sut_node.configure_ipv4_forwarding(enable) > > - def send_packet_and_capture(self, packet: Packet, duration: float = =3D 1) -> list[Packet]: > + def send_packet_and_capture( > + self, > + packet: Packet, > + filter_config: PacketFilteringConfig =3D PacketFilteringConfig()= , > + duration: float =3D 1, > + ) -> list[Packet]: > """ > Send a packet through the appropriate interface and > receive on the appropriate interface. > @@ -158,7 +164,11 @@ def send_packet_and_capture(self, packet: Packet, du= ration: float =3D 1) -> list[P > """ > packet =3D self._adjust_addresses(packet) > return self.tg_node.send_packet_and_capture( > - packet, self._tg_port_egress, self._tg_port_ingress, duratio= n > + packet, > + self._tg_port_egress, > + self._tg_port_ingress, > + filter_config, > + duration, > ) > > def get_expected_packet(self, packet: Packet) -> Packet: > diff --git a/dts/framework/testbed_model/capturing_traffic_generator.py b= /dts/framework/testbed_model/capturing_traffic_generator.py > index e6512061d7..c40b030fe4 100644 > --- a/dts/framework/testbed_model/capturing_traffic_generator.py > +++ b/dts/framework/testbed_model/capturing_traffic_generator.py > @@ -11,6 +11,7 @@ > > import uuid > from abc import abstractmethod > +from dataclasses import dataclass > > import scapy.utils # type: ignore[import] > from scapy.packet import Packet # type: ignore[import] > @@ -29,6 +30,19 @@ def _get_default_capture_name() -> str: > return str(uuid.uuid4()) > > > +@dataclass(slots=3DTrue) > +class PacketFilteringConfig: > + """The supported filtering options for :class:`CapturingTrafficGener= ator`. > + > + Attributes: > + no_lldp: If :data:`True`, LLDP packets will be filtered out when= capturing. > + no_arp: If :data:`True`, ARP packets will be filtered out when c= apturing. > + """ > + > + no_lldp: bool =3D True > + no_arp: bool =3D True > + > + > class CapturingTrafficGenerator(TrafficGenerator): > """Capture packets after sending traffic. > > @@ -51,6 +65,7 @@ def send_packet_and_capture( > packet: Packet, > send_port: Port, > receive_port: Port, > + filter_config: PacketFilteringConfig, > duration: float, > capture_name: str =3D _get_default_capture_name(), > ) -> list[Packet]: > @@ -64,6 +79,7 @@ def send_packet_and_capture( > packet: The packet to send. > send_port: The egress port on the TG node. > receive_port: The ingress port in the TG node. > + filter_config: Filters to apply when capturing packets. > duration: Capture traffic for this amount of time after send= ing the packet. > capture_name: The name of the .pcap file where to store the = capture. > > @@ -71,7 +87,7 @@ def send_packet_and_capture( > A list of received packets. May be empty if no packets are = captured. > """ > return self.send_packets_and_capture( > - [packet], send_port, receive_port, duration, capture_name > + [packet], send_port, receive_port, filter_config, duration, = capture_name > ) > > def send_packets_and_capture( > @@ -79,6 +95,7 @@ def send_packets_and_capture( > packets: list[Packet], > send_port: Port, > receive_port: Port, > + filter_config: PacketFilteringConfig, > duration: float, > capture_name: str =3D _get_default_capture_name(), > ) -> list[Packet]: > @@ -92,6 +109,7 @@ def send_packets_and_capture( > packets: The packets to send. > send_port: The egress port on the TG node. > receive_port: The ingress port in the TG node. > + filter_config: Filters to apply when capturing packets. > duration: Capture traffic for this amount of time after send= ing the packets. > capture_name: The name of the .pcap file where to store the = capture. > > @@ -106,6 +124,7 @@ def send_packets_and_capture( > packets, > send_port, > receive_port, > + filter_config, > duration, > ) > > @@ -119,6 +138,7 @@ def _send_packets_and_capture( > packets: list[Packet], > send_port: Port, > receive_port: Port, > + filter_config: PacketFilteringConfig, > duration: float, > ) -> list[Packet]: > """ > diff --git a/dts/framework/testbed_model/scapy.py b/dts/framework/testbed= _model/scapy.py > index 9083e92b3d..94b0af7c6f 100644 > --- a/dts/framework/testbed_model/scapy.py > +++ b/dts/framework/testbed_model/scapy.py > @@ -30,6 +30,7 @@ > > from .capturing_traffic_generator import ( > CapturingTrafficGenerator, > + PacketFilteringConfig, > _get_default_capture_name, > ) > from .hw.port import Port > @@ -69,6 +70,7 @@ def scapy_send_packets_and_capture( > send_iface: str, > recv_iface: str, > duration: float, > + sniff_filter: str, > ) -> list[bytes]: > """RPC function to send and capture packets. > > @@ -90,6 +92,7 @@ def scapy_send_packets_and_capture( > iface=3Drecv_iface, > store=3DTrue, > started_callback=3Dlambda *args: scapy.all.sendp(scapy_packets, = iface=3Dsend_iface), > + filter=3Dsniff_filter, > ) > sniffer.start() > time.sleep(duration) > @@ -249,16 +252,38 @@ def _send_packets(self, packets: list[Packet], port= : Port) -> None: > packets =3D [packet.build() for packet in packets] > self.rpc_server_proxy.scapy_send_packets(packets, port.logical_n= ame) > > + def _create_packet_filter(self, filter_config: PacketFilteringConfig= ) -> str: > + """Combines filter settings from `filter_config` into a BPF that= scapy can use. > + > + Scapy allows for the use of Berkeley Packet Filters (BPFs) to fi= lter what packets are > + collected based on various attributes of the packet. > + > + Args: > + filter_config: Config class that specifies which filters sho= uld be applied. > + > + Returns: > + A string representing the combination of BPF filters to be p= assed to scapy. For > + example: > + > + "ether[12:2] !=3D 0x88cc && ether[12:2] !=3D 0x0806" > + """ > + bpf_filter: list[str] =3D [] > + if filter_config.no_arp: > + bpf_filter.append("ether[12:2] !=3D 0x0806") > + if filter_config.no_lldp: > + bpf_filter.append("ether[12:2] !=3D 0x88cc") > + return " && ".join(bpf_filter) > + > def _send_packets_and_capture( > self, > packets: list[Packet], > send_port: Port, > receive_port: Port, > + filter_config: PacketFilteringConfig, > duration: float, > capture_name: str =3D _get_default_capture_name(), > ) -> list[Packet]: > binary_packets =3D [packet.build() for packet in packets] > - > xmlrpc_packets: list[ > xmlrpc.client.Binary > ] =3D self.rpc_server_proxy.scapy_send_packets_and_capture( > @@ -266,6 +291,7 @@ def _send_packets_and_capture( > send_port.logical_name, > receive_port.logical_name, > duration, > + self._create_packet_filter(filter_config), > ) # type: ignore[assignment] > > scapy_packets =3D [Ether(packet.data) for packet in xmlrpc_packe= ts] > diff --git a/dts/framework/testbed_model/tg_node.py b/dts/framework/testb= ed_model/tg_node.py > index 79a55663b5..475dc2968d 100644 > --- a/dts/framework/testbed_model/tg_node.py > +++ b/dts/framework/testbed_model/tg_node.py > @@ -23,7 +23,10 @@ > ) > from framework.exception import ConfigurationError > > -from .capturing_traffic_generator import CapturingTrafficGenerator > +from .capturing_traffic_generator import ( > + CapturingTrafficGenerator, > + PacketFilteringConfig, > +) > from .hw.port import Port > from .node import Node > > @@ -53,6 +56,7 @@ def send_packet_and_capture( > packet: Packet, > send_port: Port, > receive_port: Port, > + filter_config: PacketFilteringConfig =3D PacketFilteringConfig()= , > duration: float =3D 1, > ) -> list[Packet]: > """Send a packet, return received traffic. > @@ -71,7 +75,11 @@ def send_packet_and_capture( > A list of received packets. May be empty if no packets are = captured. > """ > return self.traffic_generator.send_packet_and_capture( > - packet, send_port, receive_port, duration > + packet, > + send_port, > + receive_port, > + filter_config, > + duration, > ) > > def close(self) -> None: > -- > 2.43.0 >