xref: /dpdk/dts/tests/TestSuite_mac_filter.py (revision 4f7b228f30a43d0a1ff27cbe0745cce85285d55c)
1# SPDX-License-Identifier: BSD-3-Clause
2# Copyright(c) 2023-2024 University of New Hampshire
3"""Mac address filtering test suite.
4
5This test suite ensures proper and expected behavior of Allowlist filtering via mac
6addresses on devices bound to the Poll Mode Driver. If a packet received on a device
7contains a mac address not contained within its mac address pool, the packet should
8be dropped. Alternatively, if a packet is received that contains a destination mac
9within the devices address pool, the packet should be accepted and forwarded. This
10behavior should remain consistent across all packets, namely those containing dot1q
11tags or otherwise.
12
13The following test suite assesses behaviors based on the aforementioned logic.
14Additionally, testing is done within the PMD itself to ensure that the mac address
15allow list is behaving as expected.
16"""
17
18from scapy.layers.inet import IP  # type: ignore[import-untyped]
19from scapy.layers.l2 import Ether  # type: ignore[import-untyped]
20from scapy.packet import Raw  # type: ignore[import-untyped]
21
22from framework.exception import InteractiveCommandExecutionError
23from framework.remote_session.testpmd_shell import NicCapability, TestPmdShell
24from framework.test_suite import TestSuite, func_test
25from framework.testbed_model.capability import requires
26
27
28class TestMacFilter(TestSuite):
29    """Mac address allowlist filtering test suite.
30
31    Configure mac address filtering on a given port, and test the port's filtering behavior
32    using both a given port's hardware address as well as dummy addresses. If a port accepts
33    a packet that is not contained within its mac address allowlist, then a given test case
34    fails. Alternatively, if a port drops a packet that is designated within its mac address
35    allowlist, a given test case will fail.
36
37    Moreover, a given port should demonstrate proper behavior when bound to the Poll Mode
38    Driver. A port should not have a mac address allowlist that exceeds its designated size.
39    A port's default hardware address should not be removed from its address pool, and invalid
40    addresses should not be included in the allowlist. If a port abides by the above rules, the
41    test case passes.
42    """
43
44    def send_packet_and_verify(
45        self,
46        mac_address: str,
47        should_receive: bool = True,
48    ) -> None:
49        """Generate, send, and verify a packet based on specified parameters.
50
51        Test cases within this suite utilize this method to create, send, and verify
52        packets based on criteria relating to the packet's destination mac address,
53        and vlan tag. Packets are verified using an inserted payload. Assuming the test case
54        expects to receive a specified packet, if the list of received packets contains this
55        payload within any of its packets, the test case passes. Alternatively, if the designed
56        packet should not be received, and the packet payload is received, then the test case
57        fails. Each call with this method sends exactly one packet.
58
59        Args:
60            mac_address: The destination mac address of the packet being sent.
61            add_vlan: If :data:'True', add a vlan tag to the packet being sent. The
62                vlan tag will be :data:'2' if the packet should be received and
63                :data:'1' if the packet should not be received but requires a vlan tag.
64            should_receive: If :data:'True', assert whether or not the sent packet
65                has been received. If :data:'False', assert that the send packet was not
66                received. :data:'True' by default
67        """
68        packet = Ether() / IP() / Raw(load="X" * 22)
69        packet.dst = mac_address
70        received_packets = [
71            packets
72            for packets in self.send_packet_and_capture(packet)
73            if hasattr(packets, "load") and "X" * 22 in str(packets.load)
74        ]
75        if should_receive:
76            self.verify(
77                len(received_packets) == 1,
78                "Packet sent by test case should be forwarded and received.",
79            )
80        else:
81            self.verify(
82                len(received_packets) == 0,
83                "Packet sent by test case should not be forwarded and received.",
84            )
85
86    @func_test
87    def test_add_remove_mac_addresses(self) -> None:
88        """Assess basic mac addressing filtering functionalities.
89
90        This test case validates for proper behavior of mac address filtering with both
91        a port's default, burned-in mac address, as well as additional mac addresses
92        added to the PMD. Packets should either be received or not received depending on
93        the properties applied to the PMD at any given time.
94
95        Test:
96            Start TestPMD without promiscuous mode.
97            Send a packet with the port's default mac address. (Should receive)
98            Send a packet with fake mac address. (Should not receive)
99            Add fake mac address to the PMD's address pool.
100            Send a packet with the fake mac address to the PMD. (Should receive)
101            Remove the fake mac address from the PMD's address pool.
102            Send a packet with the fake mac address to the PMD. (Should not receive)
103        """
104        with TestPmdShell(self.sut_node) as testpmd:
105            testpmd.set_promisc(0, enable=False)
106            testpmd.start()
107            mac_address = self._sut_port_ingress.mac_address
108
109            # Send a packet with NIC default mac address
110            self.send_packet_and_verify(mac_address=mac_address, should_receive=True)
111            # Send a packet with different mac address
112            fake_address = "00:00:00:00:00:01"
113            self.send_packet_and_verify(mac_address=fake_address, should_receive=False)
114
115            # Add mac address to pool and rerun tests
116            testpmd.set_mac_addr(0, mac_address=fake_address, add=True)
117            self.send_packet_and_verify(mac_address=fake_address, should_receive=True)
118            testpmd.set_mac_addr(0, mac_address=fake_address, add=False)
119            self.send_packet_and_verify(mac_address=fake_address, should_receive=False)
120
121    @func_test
122    def test_invalid_address(self) -> None:
123        """Assess the behavior of a NIC mac address pool while bound to the PMD.
124
125        An assessment of a NIC's behavior when mounted to a PMD as it relates to mac addresses
126        and address pooling. Devices should not be able to use invalid mac addresses, remove their
127        built-in hardware address, or exceed their address pools.
128
129        Test:
130            Start TestPMD.
131            Attempt to add an invalid mac address. (Should fail)
132            Attempt to remove the device's hardware address with no additional addresses in the
133                address pool. (Should fail)
134            Add a fake mac address to the pool twice in succession. (Should not create any errors)
135            Attempt to remove the device's hardware address with other addresses in the address
136                pool. (Should fail)
137            Determine the device's mac address pool size, and fill the pool with fake addresses.
138            Attempt to add another fake mac address, overloading the address pool. (Should fail)
139        """
140        with TestPmdShell(self.sut_node) as testpmd:
141            testpmd.start()
142            mac_address = self._sut_port_ingress.mac_address
143            try:
144                testpmd.set_mac_addr(0, "00:00:00:00:00:00", add=True)
145                self.verify(False, "Invalid mac address added.")
146            except InteractiveCommandExecutionError:
147                pass
148            try:
149                testpmd.set_mac_addr(0, mac_address, add=False)
150                self.verify(False, "Default mac address removed.")
151            except InteractiveCommandExecutionError:
152                pass
153            # Should be no errors adding this twice
154            testpmd.set_mac_addr(0, "1" + mac_address[1:], add=True)
155            testpmd.set_mac_addr(0, "1" + mac_address[1:], add=True)
156            # Double check to see if default mac address can be removed
157            try:
158                testpmd.set_mac_addr(0, mac_address, add=False)
159                self.verify(False, "Default mac address removed.")
160            except InteractiveCommandExecutionError:
161                pass
162
163            for i in range(testpmd.show_port_info(0).max_mac_addresses_num - 1):
164                # A0 fake address based on the index 'i'.
165                fake_address = str(hex(i)[2:].zfill(12))
166                # Insert ':' characters every two indexes to create a fake mac address.
167                fake_address = ":".join(
168                    fake_address[x : x + 2] for x in range(0, len(fake_address), 2)
169                )
170                testpmd.set_mac_addr(0, fake_address, add=True, verify=False)
171            try:
172                testpmd.set_mac_addr(0, "E" + mac_address[1:], add=True)
173                # We add an extra address to compensate for mac address pool inconsistencies.
174                testpmd.set_mac_addr(0, "F" + mac_address[1:], add=True)
175                self.verify(False, "Mac address limit exceeded.")
176            except InteractiveCommandExecutionError:
177                pass
178
179    @requires(NicCapability.MCAST_FILTERING)
180    @func_test
181    def test_multicast_filter(self) -> None:
182        """Assess basic multicast address filtering functionalities.
183
184        Ensure that multicast filtering performs as intended when a given device is bound
185        to the PMD.
186
187        Test:
188            Start TestPMD without promiscuous mode.
189            Add a fake multicast address to the PMD's multicast address pool.
190            Send a packet with the fake multicast address to the PMD. (Should receive)
191            Remove the fake multicast address from the PMDs multicast address filter.
192            Send a packet with the fake multicast address to the PMD. (Should not receive)
193        """
194        with TestPmdShell(self.sut_node) as testpmd:
195            testpmd.start()
196            testpmd.set_promisc(0, enable=False)
197            multicast_address = "01:00:5E:00:00:00"
198
199            testpmd.set_multicast_mac_addr(0, multi_addr=multicast_address, add=True)
200            self.send_packet_and_verify(multicast_address, should_receive=True)
201
202            # Remove multicast filter and verify the packet was not received.
203            testpmd.set_multicast_mac_addr(0, multicast_address, add=False)
204            self.send_packet_and_verify(multicast_address, should_receive=False)
205