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