Test whether APF program actually runs

Adds a test that sends a ping packet, asserts its reply, then installs a
filter and asserts that the second ping packet is dropped.

Test: test only
Change-Id: I832ea1b3cc35a44c98fbf8c86c16bf753f183c92
diff --git a/tests/cts/net/Android.bp b/tests/cts/net/Android.bp
index 074c587..768ba12 100644
--- a/tests/cts/net/Android.bp
+++ b/tests/cts/net/Android.bp
@@ -46,6 +46,7 @@
     ],
     jarjar_rules: "jarjar-rules-shared.txt",
     static_libs: [
+        "ApfGeneratorLib",
         "bouncycastle-unbundled",
         "FrameworksNetCommonTests",
         "core-tests-support",
diff --git a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt b/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
index e10a06c..6ce8b7c 100644
--- a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
+++ b/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
@@ -26,6 +26,12 @@
 import android.net.NetworkCapabilities
 import android.net.NetworkRequest
 import android.net.apf.ApfCapabilities
+import android.net.apf.ApfConstant.ETH_ETHERTYPE_OFFSET
+import android.net.apf.ApfConstant.ICMP6_TYPE_OFFSET
+import android.net.apf.ApfConstant.IPV6_NEXT_HEADER_OFFSET
+import android.net.apf.ApfV4Generator
+import android.net.apf.BaseApfGenerator
+import android.net.apf.BaseApfGenerator.Register.R0
 import android.os.Build
 import android.os.Handler
 import android.os.HandlerThread
@@ -36,6 +42,7 @@
 import android.system.Os
 import android.system.OsConstants
 import android.system.OsConstants.AF_INET6
+import android.system.OsConstants.ETH_P_IPV6
 import android.system.OsConstants.IPPROTO_ICMPV6
 import android.system.OsConstants.SOCK_DGRAM
 import android.system.OsConstants.SOCK_NONBLOCK
@@ -82,6 +89,7 @@
 private const val APF_NEW_RA_FILTER_VERSION = "apf_new_ra_filter_version"
 private const val POLLING_INTERVAL_MS: Int = 100
 private const val RCV_BUFFER_SIZE = 1480
+private const val PING_HEADER_LENGTH = 8
 
 @AppModeFull(reason = "CHANGE_NETWORK_STATE permission can't be granted to instant apps")
 @RunWith(DevSdkIgnoreRunner::class)
@@ -356,13 +364,47 @@
         }
     }
 
-    // TODO: this is a placeholder test to test the IcmpPacketReader functionality and will soon be
-    // replaced by a real test.
     @Test
-    fun testPing() {
-        val data = ByteArray(56)
-        Random.nextBytes(data)
+    fun testDropPingReply() {
+        assumeApfVersionSupportAtLeast(4)
+
+        // clear any active APF filter
+        var gen = ApfV4Generator(caps.apfVersionSupported).addPass()
+        installProgram(gen.generate())
+        readProgram() // wait for install completion
+
+        // Assert that initial ping does not get filtered.
+        val data = ByteArray(56).also { Random.nextBytes(it) }
         packetReader.sendPing(data)
         assertThat(packetReader.expectPingReply()).isEqualTo(data)
+
+        // Generate an APF program that drops the next ping
+        gen = ApfV4Generator(caps.apfVersionSupported)
+
+        // If not IPv6 -> PASS
+        gen.addLoad16(R0, ETH_ETHERTYPE_OFFSET)
+        gen.addJumpIfR0NotEquals(ETH_P_IPV6.toLong(), BaseApfGenerator.PASS_LABEL)
+
+        // If not ICMPv6 -> PASS
+        gen.addLoad8(R0, IPV6_NEXT_HEADER_OFFSET)
+        gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6.toLong(), BaseApfGenerator.PASS_LABEL)
+
+        // If not echo reply -> PASS
+        gen.addLoad8(R0, ICMP6_TYPE_OFFSET)
+        gen.addJumpIfR0NotEquals(0x81, BaseApfGenerator.PASS_LABEL)
+
+        // if not data matches -> PASS
+        gen.addLoadImmediate(R0, ICMP6_TYPE_OFFSET + PING_HEADER_LENGTH)
+        gen.addJumpIfBytesAtR0NotEqual(data, BaseApfGenerator.PASS_LABEL)
+
+        // else DROP
+        gen.addJump(BaseApfGenerator.DROP_LABEL)
+
+        val program = gen.generate()
+        installProgram(program)
+        readProgram() // wait for install completion
+
+        packetReader.sendPing(data)
+        packetReader.expectPingDropped()
     }
 }