On Fri, Nov 17, 2023 at 7:06 PM Jeremy Spewock <jspew...@iol.unh.edu> wrote: > > > > On Thu, Nov 16, 2023 at 1:34 PM Juraj Linkeš <juraj.lin...@pantheon.tech> > wrote: >> >> As I'm thinking about the filtering, it's not a trivial manner. For >> now, I'd like to pass a class instead of multiple parameters, as that >> will be easier to extend if we need to add to the filter. The class >> could be a dataclass holding the various booleans. Then the capturing >> traffic generators would need to implement a method that would >> translate the dataclass into a TG-specific filters. I'm not sure we >> want to introduce an abstract method for this, as the return value may >> differ for different TGs - this needs more thought. > > > I agree that making it a dataclass and having a method within Scapy for > interpreting it for now would work. You're right there there are some other > considerations to be had like if the return types could be different. I would > think that for the most part a string is what would be used, but it could be > different depending on what traffic generator you use. I know that tcpdump > supports BPFs as they are shown here, so if we went down the route of using > that for collecting packets with other traffic generators we would be fine on > that front, but that might need more discussion as well when we add those > traffic generators. > > If we needed this to be more generic as well, it could be possible to assign > each traffic generator a filter type and then we could make subclasses of the > dataclass for each filter type. This way, in the filter subclasses, they > could specify what each of the parameters meant and how to create the filter. > This would always return the right data type for the filter and allow us to > implement methods for some of the more generic filters (like the one I use > here, a BPF) rather than implementing them on the traffic generator that uses > the filter. Then we could just use a TypeVar to generically create the > filters based on what the capturing traffic generator class says it would > need. > > I think another way we could go about this problem however is just not > filtering out packets that are generally just noise like these and instead > just check the packets we receive when capturing and only return ones that > are relevant (like ones that have the same layers as the packet we sent for > example). However, I think because of only having one traffic generator it > would be fine to just create a method i nthe scapy capturing traffic > generator that knows how to create BPF filters and I can do that for the next > version if that is preferred. >
Let's just create a method in the scapy tg (and the dataclass, probably in traffic_generator.py?) in this patch and keep discussing the issue for the next release(s). >> >> >> On Mon, Nov 13, 2023 at 9:28 PM <jspew...@iol.unh.edu> wrote: >> > >> > From: Jeremy Spewock <jspew...@iol.unh.edu> >> > >> > 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 <jspew...@iol.unh.edu> >> > --- >> > dts/framework/test_suite.py | 13 +++++++++++-- >> > .../testbed_model/capturing_traffic_generator.py | 12 +++++++++++- >> > dts/framework/testbed_model/scapy.py | 11 ++++++++++- >> > dts/framework/testbed_model/tg_node.py | 4 +++- >> > 4 files changed, 35 insertions(+), 5 deletions(-) >> > >> > diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py >> > index 3b890c0451..3222f172f3 100644 >> > --- a/dts/framework/test_suite.py >> > +++ b/dts/framework/test_suite.py >> > @@ -152,7 +152,11 @@ 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 = 1 >> > + self, >> > + packet: Packet, >> > + duration: float = 1, >> > + no_lldp: bool = True, >> > + no_arp: bool = True, >> >> The parameters of this method are not documented in the docstring, but >> we should add these new parameters to the docstring. The same goes for >> the other methods (and other parameters of other methods) if they're >> not overriding an abstract method. >> >> The default should be False if these are meant to be optional, but I >> like these defaulting to True, so I'd change the wording from optional >> to configurable. > > > That is a good catch, I didn't think about the wording that way but that > makes a lot of sense. I will make this change. > >> >> >> > ) -> list[Packet]: >> > """ >> > Send a packet through the appropriate interface and >> > @@ -162,7 +166,12 @@ def send_packet_and_capture( >> > """ >> > packet = self._adjust_addresses(packet) >> > return self.tg_node.send_packet_and_capture( >> > - packet, self._tg_port_egress, self._tg_port_ingress, duration >> > + packet, >> > + self._tg_port_egress, >> > + self._tg_port_ingress, >> > + duration, >> > + no_lldp, >> > + no_arp, >> > ) >> > >> > 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 ab98987f8e..0a0d0f7d0d 100644 >> > --- a/dts/framework/testbed_model/capturing_traffic_generator.py >> > +++ b/dts/framework/testbed_model/capturing_traffic_generator.py >> > @@ -52,6 +52,8 @@ def send_packet_and_capture( >> > send_port: Port, >> > receive_port: Port, >> > duration: float, >> > + no_lldp: bool, >> > + no_arp: bool, >> > capture_name: str = _get_default_capture_name(), >> > ) -> list[Packet]: >> > """Send a packet, return received traffic. >> > @@ -71,7 +73,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, duration, no_lldp, no_arp, >> > capture_name >> > ) >> > >> > def send_packets_and_capture( >> > @@ -80,6 +82,8 @@ def send_packets_and_capture( >> > send_port: Port, >> > receive_port: Port, >> > duration: float, >> > + no_lldp: bool, >> > + no_arp: bool, >> > capture_name: str = _get_default_capture_name(), >> > ) -> list[Packet]: >> > """Send packets, return received traffic. >> > @@ -93,6 +97,8 @@ def send_packets_and_capture( >> > send_port: The egress port on the TG node. >> > receive_port: The ingress port in the TG node. >> > duration: Capture traffic for this amount of time after >> > sending the packets. >> > + no_lldp: Option to disable capturing LLDP packets >> > + no_arp: Option to disable capturing ARP packets >> > capture_name: The name of the .pcap file where to store the >> > capture. >> > >> > Returns: >> > @@ -108,6 +114,8 @@ def send_packets_and_capture( >> > send_port, >> > receive_port, >> > duration, >> > + no_lldp, >> > + no_arp, >> > ) >> > >> > self._logger.debug( >> > @@ -123,6 +131,8 @@ def _send_packets_and_capture( >> > send_port: Port, >> > receive_port: Port, >> > duration: float, >> > + no_lldp: bool, >> > + no_arp: bool, >> > ) -> list[Packet]: >> > """ >> > The extended classes must implement this method which >> > diff --git a/dts/framework/testbed_model/scapy.py >> > b/dts/framework/testbed_model/scapy.py >> > index af0d4dbb25..58f01af21a 100644 >> > --- a/dts/framework/testbed_model/scapy.py >> > +++ b/dts/framework/testbed_model/scapy.py >> > @@ -69,6 +69,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 +91,7 @@ def scapy_send_packets_and_capture( >> > iface=recv_iface, >> > store=True, >> > started_callback=lambda *args: scapy.all.sendp(scapy_packets, >> > iface=send_iface), >> > + filter=sniff_filter, >> > ) >> > sniffer.start() >> > time.sleep(duration) >> > @@ -264,10 +266,16 @@ def _send_packets_and_capture( >> > send_port: Port, >> > receive_port: Port, >> > duration: float, >> > + no_lldp: bool, >> > + no_arp: bool, >> > capture_name: str = _get_default_capture_name(), >> > ) -> list[Packet]: >> > binary_packets = [packet.build() for packet in packets] >> > - >> > + sniff_filter = [] >> > + if no_lldp: >> > + sniff_filter.append("ether[12:2] != 0x88cc") >> > + if no_arp: >> > + sniff_filter.append("ether[12:2] != 0x0806") >> > xmlrpc_packets: list[ >> > xmlrpc.client.Binary >> > ] = self.rpc_server_proxy.scapy_send_packets_and_capture( >> > @@ -275,6 +283,7 @@ def _send_packets_and_capture( >> > send_port.logical_name, >> > receive_port.logical_name, >> > duration, >> > + " && ".join(sniff_filter), >> > ) # type: ignore[assignment] >> > >> > scapy_packets = [Ether(packet.data) for packet in xmlrpc_packets] >> > diff --git a/dts/framework/testbed_model/tg_node.py >> > b/dts/framework/testbed_model/tg_node.py >> > index 27025cfa31..98e55b7831 100644 >> > --- a/dts/framework/testbed_model/tg_node.py >> > +++ b/dts/framework/testbed_model/tg_node.py >> > @@ -56,6 +56,8 @@ def send_packet_and_capture( >> > send_port: Port, >> > receive_port: Port, >> > duration: float = 1, >> > + no_lldp: bool = True, >> > + no_arp: bool = True, >> > ) -> list[Packet]: >> > """Send a packet, return received traffic. >> > >> > @@ -73,7 +75,7 @@ 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, duration, no_lldp, no_arp >> > ) >> > >> > def close(self) -> None: >> > -- >> > 2.42.0 >> >