From b41b67a52a314d66d314fdfdf92f141ad642cfcd Mon Sep 17 00:00:00 2001 From: Yuyang Huang Date: Wed, 12 Jun 2024 19:14:33 +0900 Subject: [PATCH] Add CTS test for APFv6 ICMPv6 ping reply filtering Added a CTS test case to verify APFv6 implementation by sending an ICMPv6 ping request with a 56-byte payload. The reply was expected to be dropped by the APF program. The APF program was expected to sent another ICMPv6 ping request with a 1-byte payload. The reply was expected to be accepted by the APF program. Finally, The test confirms if the second ping reply is received. Test: TH Change-Id: I322e7c0674ac9df60579d9e6d44690e383004d36 --- .../src/android/net/cts/ApfIntegrationTest.kt | 116 +++++++++++++++++- 1 file changed, 114 insertions(+), 2 deletions(-) diff --git a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt b/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt index f6cbeebdae..5662fca209 100644 --- a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt +++ b/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt @@ -27,10 +27,17 @@ import android.net.NetworkCapabilities import android.net.NetworkRequest import android.net.apf.ApfCapabilities import android.net.apf.ApfConstants.ETH_ETHERTYPE_OFFSET +import android.net.apf.ApfConstants.ETH_HEADER_LEN +import android.net.apf.ApfConstants.ICMP6_CHECKSUM_OFFSET import android.net.apf.ApfConstants.ICMP6_TYPE_OFFSET +import android.net.apf.ApfConstants.IPV6_DEST_ADDR_OFFSET +import android.net.apf.ApfConstants.IPV6_HEADER_LEN import android.net.apf.ApfConstants.IPV6_NEXT_HEADER_OFFSET +import android.net.apf.ApfConstants.IPV6_SRC_ADDR_OFFSET import android.net.apf.ApfCounterTracker +import android.net.apf.ApfCounterTracker.Counter.DROPPED_IPV6_MULTICAST_PING import android.net.apf.ApfCounterTracker.Counter.FILTER_AGE_16384THS +import android.net.apf.ApfCounterTracker.Counter.PASSED_IPV6_ICMP import android.net.apf.ApfV4Generator import android.net.apf.ApfV4GeneratorBase import android.net.apf.ApfV6Generator @@ -61,6 +68,12 @@ import com.android.compatibility.common.util.SystemUtil.runShellCommand import com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow import com.android.compatibility.common.util.VsrTest import com.android.internal.util.HexDump +import com.android.net.module.util.NetworkStackConstants.ETHER_ADDR_LEN +import com.android.net.module.util.NetworkStackConstants.ETHER_DST_ADDR_OFFSET +import com.android.net.module.util.NetworkStackConstants.ETHER_HEADER_LEN +import com.android.net.module.util.NetworkStackConstants.ETHER_SRC_ADDR_OFFSET +import com.android.net.module.util.NetworkStackConstants.ICMPV6_HEADER_MIN_LEN +import com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_LEN import com.android.net.module.util.PacketReader import com.android.testutils.DevSdkIgnoreRule import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo @@ -216,8 +229,8 @@ class ApfIntegrationTest { Os.sendto(sockFd!!, packet, 0, packet.size, 0, PING_DESTINATION) } - fun expectPingReply(): ByteArray { - return futureReply!!.get(TIMEOUT_MS, TimeUnit.MILLISECONDS) + fun expectPingReply(timeoutMs: Long = TIMEOUT_MS): ByteArray { + return futureReply!!.get(timeoutMs, TimeUnit.MILLISECONDS) } fun expectPingDropped() { @@ -622,4 +635,103 @@ class ApfIntegrationTest { assertThat(timeDiff).isGreaterThan(timeDiffLowerBound) assertThat(timeDiff).isLessThan(timeDiffUpperBound) } + + @VsrTest( + requirements = ["VSR-5.3.12-002", "VSR-5.3.12-005", "VSR-5.3.12-012", "VSR-5.3.12-013", + "VSR-5.3.12-014", "VSR-5.3.12-015", "VSR-5.3.12-016", "VSR-5.3.12-017"] + ) + @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Test + fun testReplyPing() { + assumeApfVersionSupportAtLeast(6000) + installProgram(ByteArray(caps.maximumApfProgramSize) { 0 }) // Clear previous program + readProgram() // Ensure installation is complete + + val payloadSize = 56 + val payload = ByteArray(payloadSize).also { Random.nextBytes(it) } + val firstByte = payload.take(1).toByteArray() + + val pingRequestIpv6PayloadLen = PING_HEADER_LENGTH + 1 + val pingRequestPktLen = ETH_HEADER_LEN + IPV6_HEADER_LEN + pingRequestIpv6PayloadLen + + val gen = ApfV6Generator( + caps.apfVersionSupported, + caps.maximumApfProgramSize, + caps.maximumApfProgramSize + ) + val skipPacketLabel = gen.uniqueLabel + + // Summary of the program: + // if the packet is not ICMPv6 echo reply + // pass + // else if the echo reply payload size is 1 + // increase PASSED_IPV6_ICMP counter + // pass + // else + // transmit a ICMPv6 echo request packet with the first byte of the payload in the reply + // increase DROPPED_IPV6_MULTICAST_PING counter + // drop + val program = gen + .addLoad16(R0, ETH_ETHERTYPE_OFFSET) + .addJumpIfR0NotEquals(ETH_P_IPV6.toLong(), skipPacketLabel) + .addLoad8(R0, IPV6_NEXT_HEADER_OFFSET) + .addJumpIfR0NotEquals(IPPROTO_ICMPV6.toLong(), skipPacketLabel) + .addLoad8(R0, ICMP6_TYPE_OFFSET) + .addJumpIfR0NotEquals(0x81, skipPacketLabel) // Echo reply type + .addLoadFromMemory(R0, MemorySlot.PACKET_SIZE) + .addCountAndPassIfR0Equals( + (ETHER_HEADER_LEN + IPV6_HEADER_LEN + PING_HEADER_LENGTH + firstByte.size) + .toLong(), + PASSED_IPV6_ICMP + ) + // Ping Packet Generation + .addAllocate(pingRequestPktLen) + // Eth header + .addPacketCopy(ETHER_SRC_ADDR_OFFSET, ETHER_ADDR_LEN) // dst MAC address + .addPacketCopy(ETHER_DST_ADDR_OFFSET, ETHER_ADDR_LEN) // src MAC address + .addWriteU16(ETH_P_IPV6) // IPv6 type + // IPv6 Header + .addWrite32(0x60000000) // IPv6 Header: version, traffic class, flowlabel + // payload length (2 bytes) | next header: ICMPv6 (1 byte) | hop limit (1 byte) + .addWrite32(pingRequestIpv6PayloadLen shl 16 or (IPPROTO_ICMPV6 shl 8 or 64)) + .addPacketCopy(IPV6_DEST_ADDR_OFFSET, IPV6_ADDR_LEN) // src ip + .addPacketCopy(IPV6_SRC_ADDR_OFFSET, IPV6_ADDR_LEN) // dst ip + // ICMPv6 + .addWriteU8(0x80) // type: echo request + .addWriteU8(0) // code + .addWriteU16(pingRequestIpv6PayloadLen) // checksum + // identifier + .addPacketCopy(ETHER_HEADER_LEN + IPV6_HEADER_LEN + ICMPV6_HEADER_MIN_LEN, 2) + .addWriteU16(0) // sequence number + .addDataCopy(firstByte) // data + .addTransmitL4( + ETHER_HEADER_LEN, // ip_ofs + ICMP6_CHECKSUM_OFFSET, // csum_ofs + IPV6_SRC_ADDR_OFFSET, // csum_start + IPPROTO_ICMPV6, // partial_sum + false // udp + ) + // Warning: the program abuse DROPPED_IPV6_MULTICAST_PING for debugging purpose + .addCountAndDrop(DROPPED_IPV6_MULTICAST_PING) + .defineLabel(skipPacketLabel) + .addPass() + .generate() + + installProgram(program) + readProgram() // Ensure installation is complete + + packetReader.sendPing(payload, payloadSize) + + val replyPayload = try { + packetReader.expectPingReply(TIMEOUT_MS * 2) + } catch (e: TimeoutException) { + byteArrayOf() // Empty payload if timeout occurs + } + + val apfCounterTracker = ApfCounterTracker() + apfCounterTracker.updateCountersFromData(readProgram()) + Log.i(TAG, "counter map: ${apfCounterTracker.counters}") + + assertThat(replyPayload).isEqualTo(firstByte) + } }