Merge "Add test for NetworkStackUtils.attachRaFilter" am: 0be71afff0 am: d0f401e44b am: e9db2e8a7a

Original change: https://android-review.googlesource.com/c/platform/packages/modules/NetworkStack/+/1440556

Change-Id: Ia24ad945bad66a277d0cfac06945dd16e4d0e1ec
diff --git a/tests/integration/src/android/net/util/NetworkStackUtilsIntegrationTest.kt b/tests/integration/src/android/net/util/NetworkStackUtilsIntegrationTest.kt
index 7e544ea..0ec43a5 100644
--- a/tests/integration/src/android/net/util/NetworkStackUtilsIntegrationTest.kt
+++ b/tests/integration/src/android/net/util/NetworkStackUtilsIntegrationTest.kt
@@ -19,6 +19,7 @@
 import android.Manifest.permission.MANAGE_TEST_NETWORKS
 import android.content.Context
 import android.net.InetAddresses.parseNumericAddress
+import android.net.IpPrefix
 import android.net.MacAddress
 import android.net.TestNetworkInterface
 import android.net.TestNetworkManager
@@ -26,12 +27,22 @@
 import android.os.HandlerThread
 import android.system.Os
 import android.system.OsConstants.AF_INET
+import android.system.OsConstants.AF_PACKET
+import android.system.OsConstants.ARPHRD_ETHER
+import android.system.OsConstants.ETH_P_IPV6
 import android.system.OsConstants.IPPROTO_UDP
 import android.system.OsConstants.SOCK_DGRAM
 import android.system.OsConstants.SOCK_NONBLOCK
 import androidx.test.platform.app.InstrumentationRegistry
+import android.system.OsConstants.SOCK_RAW
+import android.system.OsConstants.SOL_SOCKET
+import android.system.OsConstants.SO_RCVTIMEO
+import android.system.StructTimeval
+import com.android.net.module.util.Ipv6Utils
 import com.android.net.module.util.NetworkStackConstants.ETHER_ADDR_LEN
 import com.android.net.module.util.NetworkStackConstants.IPV4_ADDR_ANY
+import com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_NODES_MULTICAST
+import com.android.net.module.util.structs.PrefixInformationOption
 import com.android.testutils.ArpRequestFilter
 import com.android.testutils.ETHER_HEADER_LENGTH
 import com.android.testutils.IPV4_HEADER_LENGTH
@@ -42,9 +53,13 @@
 import org.junit.Assert.assertArrayEquals
 import org.junit.Before
 import org.junit.Test
+import java.io.FileDescriptor
 import java.net.Inet4Address
 import kotlin.reflect.KClass
+import java.net.Inet6Address
+import java.nio.ByteBuffer
 import kotlin.test.assertEquals
+import kotlin.test.assertTrue
 import kotlin.test.fail
 
 class NetworkStackUtilsIntegrationTest {
@@ -52,8 +67,12 @@
     private val context by lazy { inst.context }
 
     private val TEST_TIMEOUT_MS = 10_000L
+    private val TEST_MTU = 1500
     private val TEST_TARGET_IPV4_ADDR = parseNumericAddress("192.0.2.42") as Inet4Address
+    private val TEST_SRC_MAC = MacAddress.fromString("BA:98:76:54:32:10")
     private val TEST_TARGET_MAC = MacAddress.fromString("01:23:45:67:89:0A")
+    private val TEST_INET6ADDR_1 = parseNumericAddress("2001:db8::1") as Inet6Address
+    private val TEST_INET6ADDR_2 = parseNumericAddress("2001:db8::2") as Inet6Address
 
     private val readerHandler = HandlerThread(
             NetworkStackUtilsIntegrationTest::class.java.simpleName)
@@ -103,8 +122,7 @@
                 null /* hostname */, false /* metered */, 1500 /* mtu */,
                 null /* captivePortalUrl */)
         // Not using .array as per errorprone "ByteBufferBackingArray" recommendation
-        val originalPacket = ByteArray(buffer.limit())
-        buffer.get(originalPacket)
+        val originalPacket = buffer.readAsArray()
 
         Os.sendto(socket, originalPacket, 0 /* bytesOffset */, originalPacket.size /* bytesCount */,
                 0 /* flags */, TEST_TARGET_IPV4_ADDR, DhcpPacket.DHCP_CLIENT.toInt() /* port */)
@@ -112,7 +130,7 @@
         // Verify the packet was sent to the mac address specified in the ARP entry
         // Also accept ARP requests, but expect that none is sent before the UDP packet
         // IPv6 NS may be sent on the interface but will be filtered out
-        val sentPacket = reader.popPacket(TEST_TIMEOUT_MS, IPv4UdpFilter().or(ArpRequestFilter()))
+        val sentPacket = reader.poll(TEST_TIMEOUT_MS, IPv4UdpFilter().or(ArpRequestFilter()))
                 ?: fail("Packet was not sent on the interface")
 
         val sentTargetAddr = MacAddress.fromBytes(sentPacket.copyOfRange(0, ETHER_ADDR_LEN))
@@ -123,7 +141,55 @@
 
         assertArrayEquals("Sent packet != original packet", originalPacket, sentDhcpPacket)
     }
+
+    @Test
+    fun testAttachRaFilter() {
+        val socket = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IPV6)
+        val ifParams = InterfaceParams.getByName(iface.interfaceName)
+                ?: fail("Could not obtain interface params for ${iface.interfaceName}")
+        val socketAddr = SocketUtils.makePacketSocketAddress(ETH_P_IPV6, ifParams.index)
+        Os.bind(socket, socketAddr)
+        Os.setsockoptTimeval(socket, SOL_SOCKET, SO_RCVTIMEO,
+                StructTimeval.fromMillis(TEST_TIMEOUT_MS))
+
+        // Verify that before setting any filter, the socket receives pings
+        val echo = Ipv6Utils.buildEchoRequestPacket(TEST_SRC_MAC, TEST_TARGET_MAC, TEST_INET6ADDR_1,
+                TEST_INET6ADDR_2)
+        reader.sendResponse(echo)
+        echo.rewind()
+        assertNextPacketEquals(socket, echo.readAsArray(), "ICMPv6 echo")
+
+        NetworkStackUtils.attachRaFilter(socket, ARPHRD_ETHER)
+        // Send another echo, then an RA. After setting the filter expect only the RA.
+        echo.rewind()
+        reader.sendResponse(echo)
+        val pio = PrefixInformationOption.build(IpPrefix("2001:db8:1::/64"),
+                0.toByte() /* flags */, 3600 /* validLifetime */, 1800 /* preferredLifetime */)
+        val ra = Ipv6Utils.buildRaPacket(TEST_SRC_MAC, TEST_TARGET_MAC,
+                TEST_INET6ADDR_1 /* routerAddr */, IPV6_ADDR_ALL_NODES_MULTICAST,
+                0.toByte() /* flags */, 1800 /* lifetime */, 0 /* reachableTime */,
+                0 /* retransTimer */, pio)
+        reader.sendResponse(ra)
+        ra.rewind()
+
+        assertNextPacketEquals(socket, ra.readAsArray(), "ICMPv6 RA")
+    }
+
+    private fun assertNextPacketEquals(socket: FileDescriptor, expected: ByteArray, descr: String) {
+        val buffer = ByteArray(TEST_MTU)
+        val readPacket = Os.read(socket, buffer, 0 /* byteOffset */, buffer.size)
+        assertTrue(readPacket > 0, "$descr not received")
+        assertEquals(expected.size, readPacket, "Received packet size does not match for $descr")
+        assertArrayEquals("Received packet != expected $descr",
+                expected, buffer.copyOfRange(0, readPacket))
+    }
+}
+
+private fun ByteBuffer.readAsArray(): ByteArray {
+    val out = ByteArray(remaining())
+    get(out)
+    return out
 }
 
 private fun <T : Any> Context.assertHasService(manager: KClass<T>) = getSystemService(manager.java)
-        ?: fail("Could not find service $manager")
\ No newline at end of file
+        ?: fail("Could not find service $manager")