Add PASSDROP_OPCODE test, ensuring counter increment This commit introduces new testcases for PASSDROP_OPCODE. The test verifies that the packet is passed/dropped and the counter is incremented correctly when this opcode is executed. This commit also contains some minor stylistic changes. Test: TH Change-Id: I3f84e7e5a9f9846cd67e8298a7bd1e07adddc846
diff --git a/src/android/net/apf/ApfCounterTracker.java b/src/android/net/apf/ApfCounterTracker.java index 70c1dd7..e174821 100644 --- a/src/android/net/apf/ApfCounterTracker.java +++ b/src/android/net/apf/ApfCounterTracker.java
@@ -17,6 +17,7 @@ package android.net.apf; import android.util.ArrayMap; +import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -90,6 +91,14 @@ } /** + * Returns the counter sequence number from the end of the APF data segment for + * a given counter. + */ + public int value() { + return this.ordinal(); + } + + /** * Returns the total size of the data segment in bytes. */ public static int totalSize() { @@ -97,6 +106,8 @@ } } + private static final String TAG = ApfCounterTracker.class.getSimpleName(); + private final List<Counter> mCounterList; // Store the counters' value private final Map<Counter, Long> mCounters = new ArrayMap<>(); @@ -111,17 +122,31 @@ */ public static long getCounterValue(byte[] data, Counter counter) throws ArrayIndexOutOfBoundsException { + int offset = data.length + Counter.ENDIANNESS.offset(); + int endianness = 0; + for (int i = 0; i < 4; i++) { + endianness = endianness << 8 | (data[offset + i]); + } // Follow the same wrap-around addressing scheme of the interpreter. - int offset = counter.offset(); - if (offset < 0) { - offset = data.length + offset; + offset = data.length + counter.offset(); + + boolean isBe = true; + switch (endianness) { + case 0: + case 0x12345678: + isBe = true; + break; + case 0x78563412: + isBe = false; + break; + default: + Log.wtf(TAG, "Unknown endianness: 0x" + Integer.toHexString(endianness)); } // Decode 32bit big-endian integer into a long so we can count up beyond 2^31. long value = 0; for (int i = 0; i < 4; i++) { - value = value << 8 | (data[offset] & 0xFF); - offset++; + value = value << 8 | (data[offset + (isBe ? i : 3 - i)]); } return value; }
diff --git a/tests/unit/src/android/net/apf/ApfV5Test.kt b/tests/unit/src/android/net/apf/ApfV5Test.kt index c1c1e29..64072e1 100644 --- a/tests/unit/src/android/net/apf/ApfV5Test.kt +++ b/tests/unit/src/android/net/apf/ApfV5Test.kt
@@ -15,8 +15,13 @@ */ package android.net.apf +import android.net.apf.ApfCounterTracker.Counter +import android.net.apf.ApfTestUtils.DROP import android.net.apf.ApfTestUtils.MIN_PKT_SIZE +import android.net.apf.ApfTestUtils.PASS +import android.net.apf.ApfTestUtils.assertDrop import android.net.apf.ApfTestUtils.assertPass +import android.net.apf.ApfTestUtils.assertVerdict import android.net.apf.BaseApfGenerator.DROP_LABEL import android.net.apf.BaseApfGenerator.IllegalInstructionException import android.net.apf.BaseApfGenerator.MIN_APF_VERSION @@ -26,6 +31,7 @@ import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 import kotlin.test.assertContentEquals +import kotlin.test.assertEquals import kotlin.test.assertFailsWith import org.junit.Test import org.junit.runner.RunWith @@ -459,6 +465,50 @@ assertContentEquals(byteArrayOf(33, 34, 35, 1, 2, 3), ApfJniUtils.getTransmittedPacket()) } + @Test + fun testPassDrop() { + var program = ApfV6Generator() + .addDrop() + .addPass() + .generate() + assertDrop(MIN_APF_VERSION_IN_DEV, program, testPacket) + + var dataRegion = ByteArray(Counter.totalSize()) { 0 } + program = ApfV6Generator() + .addData(byteArrayOf()) + .addCountAndDrop(Counter.DROPPED_ETH_BROADCAST.value()) + .generate() + assertVerdict(MIN_APF_VERSION_IN_DEV, DROP, program, testPacket, dataRegion) + var counterMap = decodeCountersIntoMap(dataRegion) + assertEquals(mapOf<Counter, Long>( + Counter.TOTAL_PACKETS to 1, + Counter.DROPPED_ETH_BROADCAST to 1), counterMap) + + dataRegion = ByteArray(Counter.totalSize()) { 0 } + program = ApfV6Generator() + .addData(byteArrayOf()) + .addCountAndPass(Counter.PASSED_ARP.value()) + .generate() + assertVerdict(MIN_APF_VERSION_IN_DEV, PASS, program, testPacket, dataRegion) + counterMap = decodeCountersIntoMap(dataRegion) + assertEquals(mapOf<Counter, Long>( + Counter.TOTAL_PACKETS to 1, + Counter.PASSED_ARP to 1), counterMap) + } + + private fun decodeCountersIntoMap(counterBytes: ByteArray): Map<Counter, Long> { + val counters = Counter::class.java.enumConstants + val ret = HashMap<Counter, Long>() + // starting from index 2 to skip the endianness mark + for (c in listOf(*counters).subList(2, counters.size)) { + val value = ApfCounterTracker.getCounterValue(counterBytes, c) + if (value != 0L) { + ret[c] = value + } + } + return ret + } + private fun encodeInstruction(opcode: Int, immLength: Int, register: Int): Byte { val immLengthEncoding = if (immLength == 4) 3 else immLength return opcode.shl(3).or(immLengthEncoding.shl(1)).or(register).toByte()