Merge "Add skeleton BPF ring buffer integration test"
diff --git a/staticlibs/device/com/android/net/module/util/netlink/NetlinkUtils.java b/staticlibs/device/com/android/net/module/util/netlink/NetlinkUtils.java
new file mode 100644
index 0000000..34c468a
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/netlink/NetlinkUtils.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util.netlink;
+
+import static android.system.OsConstants.IPPROTO_TCP;
+
+/**
+ * Utilities for netlink related class.
+ * @hide
+ */
+public class NetlinkUtils {
+    /** Corresponds to enum from bionic/libc/include/netinet/tcp.h. */
+    private static final int TCP_ESTABLISHED = 1;
+    private static final int TCP_SYN_SENT = 2;
+    private static final int TCP_SYN_RECV = 3;
+
+    public static final int TCP_MONITOR_STATE_FILTER =
+            (1 << TCP_ESTABLISHED) | (1 << TCP_SYN_SENT) | (1 << TCP_SYN_RECV);
+
+    /**
+     * Construct an inet_diag_req_v2 message for querying alive TCP sockets from kernel.
+     */
+    public static byte[] buildInetDiagReqForAliveTcpSockets(int family) {
+        return InetDiagMessage.inetDiagReqV2(IPPROTO_TCP,
+                null /* local addr */,
+                null /* remote addr */,
+                family,
+                (short) (StructNlMsgHdr.NLM_F_REQUEST | StructNlMsgHdr.NLM_F_DUMP) /* flag */,
+                0 /* pad */,
+                1 << NetlinkConstants.INET_DIAG_MEMINFO /* idiagExt */,
+                TCP_MONITOR_STATE_FILTER);
+    }
+}
diff --git a/staticlibs/device/com/android/net/module/util/netlink/RtNetlinkAddressMessage.java b/staticlibs/device/com/android/net/module/util/netlink/RtNetlinkAddressMessage.java
index f7b0d02..c07cec0 100644
--- a/staticlibs/device/com/android/net/module/util/netlink/RtNetlinkAddressMessage.java
+++ b/staticlibs/device/com/android/net/module/util/netlink/RtNetlinkAddressMessage.java
@@ -16,6 +16,9 @@
 
 package com.android.net.module.util.netlink;
 
+import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_ACK;
+import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST;
+
 import android.system.OsConstants;
 
 import androidx.annotation.NonNull;
@@ -24,8 +27,11 @@
 
 import com.android.net.module.util.HexDump;
 
+import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Objects;
 
 /**
  * A NetlinkMessage subclass for rtnetlink address messages.
@@ -145,6 +151,51 @@
         flags.pack(byteBuffer);
     }
 
+    /**
+     * A convenience method to create an RTM_NEWADDR message.
+     */
+    public static byte[] newRtmNewAddressMessage(int seqNo, @NonNull final InetAddress ip,
+            short prefixlen, int flags, byte scope, int ifIndex, long preferred, long valid) {
+        Objects.requireNonNull(ip, "IP address to be set via netlink message cannot be null");
+
+        final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr();
+        nlmsghdr.nlmsg_type = NetlinkConstants.RTM_NEWADDR;
+        nlmsghdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+        nlmsghdr.nlmsg_seq = seqNo;
+
+        final RtNetlinkAddressMessage msg = new RtNetlinkAddressMessage(nlmsghdr);
+        final byte family =
+                (byte) ((ip instanceof Inet6Address) ? OsConstants.AF_INET6 : OsConstants.AF_INET);
+        // IFA_FLAGS attribute is always present within this method, just set flags from
+        // ifaddrmsg to 0. kernel will prefer the flags from IFA_FLAGS attribute.
+        msg.mIfaddrmsg =
+                new StructIfaddrMsg(family, prefixlen, (short) 0 /* flags */, scope, ifIndex);
+        msg.mIpAddress = ip;
+        msg.mIfacacheInfo = new StructIfacacheInfo(preferred, valid, 0 /* cstamp */,
+                0 /* tstamp */);
+        msg.mFlags = flags;
+
+        final byte[] bytes = new byte[msg.getRequiredSpace()];
+        nlmsghdr.nlmsg_len = bytes.length;
+        final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
+        byteBuffer.order(ByteOrder.nativeOrder());
+        msg.pack(byteBuffer);
+        return bytes;
+    }
+
+    private int getRequiredSpace() {
+        int spaceRequired = StructNlMsgHdr.STRUCT_SIZE + StructIfaddrMsg.STRUCT_SIZE;
+        // IFA_ADDRESS attr
+        spaceRequired += NetlinkConstants.alignedLengthOf(
+                StructNlAttr.NLA_HEADERLEN + mIpAddress.getAddress().length);
+        // IFA_CACHEINFO attr
+        spaceRequired += NetlinkConstants.alignedLengthOf(
+                StructNlAttr.NLA_HEADERLEN + StructIfacacheInfo.STRUCT_SIZE);
+        // IFA_FLAGS "u32" attr
+        spaceRequired += StructNlAttr.NLA_HEADERLEN + 4;
+        return spaceRequired;
+    }
+
     @Override
     public String toString() {
         return "RtNetlinkAddressMessage{ "
diff --git a/staticlibs/framework/com/android/net/module/util/BinderUtils.java b/staticlibs/framework/com/android/net/module/util/BinderUtils.java
index eb695d1..e4d14ea 100644
--- a/staticlibs/framework/com/android/net/module/util/BinderUtils.java
+++ b/staticlibs/framework/com/android/net/module/util/BinderUtils.java
@@ -19,6 +19,8 @@
 import android.annotation.NonNull;
 import android.os.Binder;
 
+import java.util.function.Supplier;
+
 /**
  * Collection of utilities for {@link Binder} and related classes.
  * @hide
@@ -56,4 +58,39 @@
         /** @see java.lang.Runnable */
         void run() throws T;
     }
+
+    /**
+     * Convenience method for running the provided action enclosed in
+     * {@link Binder#clearCallingIdentity}/{@link Binder#restoreCallingIdentity} returning the
+     * result.
+     *
+     * <p>Any exception thrown by the given action will be caught and rethrown after
+     * the call to {@link Binder#restoreCallingIdentity}.
+     *
+     * Note that this is copied from Binder#withCleanCallingIdentity with minor changes
+     * since it is not public.
+     *
+     * @hide
+     */
+    public static final <T, E extends Exception> T withCleanCallingIdentity(
+            @NonNull ThrowingSupplier<T, E> action) throws E {
+        final long callingIdentity = Binder.clearCallingIdentity();
+        try {
+            return action.get();
+        } finally {
+            Binder.restoreCallingIdentity(callingIdentity);
+        }
+    }
+
+    /**
+     * An equivalent of {@link Supplier}
+     *
+     * @param <T> The class which is declared to be returned.
+     * @param <E> The exception class which is declared to be thrown.
+     */
+    @FunctionalInterface
+    public interface ThrowingSupplier<T, E extends Exception> {
+        /** @see java.util.function.Supplier */
+        T get() throws E;
+    }
 }
diff --git a/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java b/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java
index ebef155..287473e 100644
--- a/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java
+++ b/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java
@@ -97,6 +97,8 @@
     public static final int IPV4_SRC_ADDR_OFFSET = 12;
     public static final int IPV4_DST_ADDR_OFFSET = 16;
     public static final int IPV4_ADDR_LEN = 4;
+    public static final int IPV4_FLAG_MF = 0x2000;
+    public static final int IPV4_FLAG_DF = 0x4000;
     public static final Inet4Address IPV4_ADDR_ALL = makeInet4Address(
             (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff);
     public static final Inet4Address IPV4_ADDR_ANY = makeInet4Address(
diff --git a/staticlibs/native/bpf_headers/include/bpf/bpf_helpers.h b/staticlibs/native/bpf_headers/include/bpf/bpf_helpers.h
index ea56593..4d48720 100644
--- a/staticlibs/native/bpf_headers/include/bpf/bpf_helpers.h
+++ b/staticlibs/native/bpf_headers/include/bpf/bpf_helpers.h
@@ -182,21 +182,21 @@
 //   accessing the ring buffer should set a program level min_kver >= 5.8.
 // * The definition below sets a map min_kver of 5.8 which requires targeting
 //   a BPFLOADER_MIN_VER >= BPFLOADER_S_VERSION.
-#define DEFINE_BPF_RINGBUF(the_map, ValueType, size_bytes, usr, grp, md, \
-                           selinux, pindir, share)                       \
-  DEFINE_BPF_MAP_BASE(the_map, RINGBUF, 0, 0, size_bytes, usr, grp, md,  \
-                      selinux, pindir, share, KVER(5, 8, 0), KVER_INF);  \
-  static inline __always_inline __unused int bpf_##the_map##_output(    \
-      const ValueType* v) {                                              \
-    return bpf_ringbuf_output_unsafe(&the_map, v, sizeof(*v), 0);        \
-  }                                                                      \
-  static inline __always_inline __unused                                 \
-      ValueType* bpf_##the_map##_reserve() {                             \
-    return bpf_ringbuf_reserve_unsafe(&the_map, sizeof(ValueType), 0);   \
-  }                                                                      \
-  static inline __always_inline __unused void bpf_##the_map##_submit(    \
-      const ValueType* v) {                                              \
-    bpf_ringbuf_submit_unsafe(v, 0);                                     \
+#define DEFINE_BPF_RINGBUF_EXT(the_map, ValueType, size_bytes, usr, grp, md, \
+                               selinux, pindir, share)                       \
+  DEFINE_BPF_MAP_BASE(the_map, RINGBUF, 0, 0, size_bytes, usr, grp, md,      \
+                      selinux, pindir, share, KVER(5, 8, 0), KVER_INF);      \
+  static inline __always_inline __unused int bpf_##the_map##_output(         \
+      const ValueType* v) {                                                  \
+    return bpf_ringbuf_output_unsafe(&the_map, v, sizeof(*v), 0);            \
+  }                                                                          \
+  static inline __always_inline __unused                                     \
+      ValueType* bpf_##the_map##_reserve() {                                 \
+    return bpf_ringbuf_reserve_unsafe(&the_map, sizeof(ValueType), 0);       \
+  }                                                                          \
+  static inline __always_inline __unused void bpf_##the_map##_submit(        \
+      const ValueType* v) {                                                  \
+    bpf_ringbuf_submit_unsafe(v, 0);                                         \
   }
 
 /* There exist buggy kernels with pre-T OS, that due to
diff --git a/staticlibs/native/tcutils/tests/tcutils_test.cpp b/staticlibs/native/tcutils/tests/tcutils_test.cpp
index 8129286..3a89696 100644
--- a/staticlibs/native/tcutils/tests/tcutils_test.cpp
+++ b/staticlibs/native/tcutils/tests/tcutils_test.cpp
@@ -79,7 +79,7 @@
 }
 
 TEST(LibTcUtilsTest, AddAndDeleteBpfFilter) {
-  // TODO: this should use bpf_shared.h rather than hardcoding the path
+  // TODO: this should likely be in the tethering module, where using netd.h would be ok
   static constexpr char bpfProgPath[] =
       "/sys/fs/bpf/tethering/prog_offload_schedcls_tether_downstream6_ether";
   const int errNOENT = isAtLeastKernelVersion(4, 19, 0) ? ENOENT : EINVAL;
@@ -111,7 +111,7 @@
 }
 
 TEST(LibTcUtilsTest, AddAndDeleteIngressPoliceFilter) {
-  // TODO: this should use bpf_shared.h rather than hardcoding the path
+  // TODO: this should likely be in the tethering module, where using netd.h would be ok
   static constexpr char bpfProgPath[] =
       "/sys/fs/bpf/netd_shared/prog_netd_schedact_ingress_account";
   int fd = bpf::retrieveProgram(bpfProgPath);
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/RtNetlinkAddressMessageTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/RtNetlinkAddressMessageTest.java
index 7d8dbd2..f845eb4 100644
--- a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/RtNetlinkAddressMessageTest.java
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/RtNetlinkAddressMessageTest.java
@@ -16,8 +16,14 @@
 
 package com.android.net.module.util.netlink;
 
+import static android.system.OsConstants.IFA_F_PERMANENT;
 import static android.system.OsConstants.NETLINK_ROUTE;
+import static android.system.OsConstants.RT_SCOPE_LINK;
+import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
 
+import static com.android.testutils.MiscAsserts.assertThrows;
+
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
@@ -31,6 +37,8 @@
 
 import com.android.net.module.util.HexDump;
 
+import libcore.util.HexEncoding;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -42,7 +50,9 @@
 @SmallTest
 public class RtNetlinkAddressMessageTest {
     private static final Inet6Address TEST_LINK_LOCAL =
-            (Inet6Address) InetAddresses.parseNumericAddress("fe80::2C41:5CFF:FE09:6665");
+            (Inet6Address) InetAddresses.parseNumericAddress("FE80::2C41:5CFF:FE09:6665");
+    private static final Inet6Address TEST_GLOBAL_ADDRESS =
+            (Inet6Address) InetAddresses.parseNumericAddress("2001:DB8:1::100");
 
     // An example of the full RTM_NEWADDR message.
     private static final String RTM_NEWADDR_HEX =
@@ -128,6 +138,99 @@
     }
 
     @Test
+    public void testCreateRtmNewAddressMessage() {
+        // Hexadecimal representation of our created packet.
+        final String expectedNewAddressHex =
+                // struct nlmsghdr
+                "48000000" +    // length = 72
+                "1400" +        // type = 20 (RTM_NEWADDR)
+                "0500" +        // flags = NLM_F_ACK | NLM_F_REQUEST
+                "01000000" +    // seqno = 1
+                "00000000" +    // pid = 0 (send to kernel)
+                // struct IfaddrMsg
+                "0A" +          // family = inet6
+                "40" +          // prefix len = 64
+                "00" +          // flags = 0
+                "FD" +          // scope = RT_SCOPE_LINK
+                "17000000" +    // ifindex = 23
+                // struct nlattr: IFA_ADDRESS
+                "1400" +        // len
+                "0100" +        // type
+                "FE800000000000002C415CFFFE096665" + // IP address = fe80::2C41:5cff:fe09:6665
+                // struct nlattr: IFA_CACHEINFO
+                "1400" +        // len
+                "0600" +        // type
+                "FFFFFFFF" +    // preferred = infinite
+                "FFFFFFFF" +    // valid = infinite
+                "00000000" +    // cstamp
+                "00000000" +    // tstamp
+                // struct nlattr: IFA_FLAGS
+                "0800" +        // len
+                "0800" +        // type
+                "80000000";     // flags = IFA_F_PERMANENT
+        final byte[] expectedNewAddress =
+                HexEncoding.decode(expectedNewAddressHex.toCharArray(), false);
+
+        final byte[] bytes = RtNetlinkAddressMessage.newRtmNewAddressMessage(1 /* seqno */,
+                TEST_LINK_LOCAL, (short) 64 /* prefix len */, IFA_F_PERMANENT /* flags */,
+                (byte) RT_SCOPE_LINK /* scope */, 23 /* ifindex */,
+                (long) 0xFFFFFFFF /* preferred */, (long) 0xFFFFFFFF /* valid */);
+        assertArrayEquals(expectedNewAddress, bytes);
+    }
+
+    @Test
+    public void testCreateRtmNewAddressMessage_nullIpAddress() {
+        assertThrows(NullPointerException.class,
+                () -> RtNetlinkAddressMessage.newRtmNewAddressMessage(1 /* seqno */,
+                        null /* IP address */, (short) 0 /* prefix len */,
+                        IFA_F_PERMANENT /* flags */, (byte) RT_SCOPE_LINK /* scope */,
+                        23 /* ifindex */, (long) 0xFFFFFFFF /* preferred */,
+                        (long) 0xFFFFFFFF /* valid */));
+    }
+
+    @Test
+    public void testCreateRtmNewAddressMessage_u32Flags() {
+        // Hexadecimal representation of our created packet.
+        final String expectedNewAddressHex =
+                // struct nlmsghdr
+                "48000000" +    // length = 72
+                "1400" +        // type = 20 (RTM_NEWADDR)
+                "0500" +        // flags = NLM_F_ACK | NLM_F_REQUEST
+                "01000000" +    // seqno = 1
+                "00000000" +    // pid = 0 (send to kernel)
+                // struct IfaddrMsg
+                "0A" +          // family = inet6
+                "80" +          // prefix len = 128
+                "00" +          // flags = 0
+                "00" +          // scope = RT_SCOPE_UNIVERSE
+                "17000000" +    // ifindex = 23
+                // struct nlattr: IFA_ADDRESS
+                "1400" +        // len
+                "0100" +        // type
+                "20010DB8000100000000000000000100" + // IP address = 2001:db8:1::100
+                // struct nlattr: IFA_CACHEINFO
+                "1400" +        // len
+                "0600" +        // type
+                "FFFFFFFF" +    // preferred = infinite
+                "FFFFFFFF" +    // valid = infinite
+                "00000000" +    // cstamp
+                "00000000" +    // tstamp
+                // struct nlattr: IFA_FLAGS
+                "0800" +        // len
+                "0800" +        // type
+                "00030000";     // flags = IFA_F_MANAGETEMPADDR | IFA_F_NOPREFIXROUTE
+        final byte[] expectedNewAddress =
+                HexEncoding.decode(expectedNewAddressHex.toCharArray(), false);
+
+        final byte[] bytes = RtNetlinkAddressMessage.newRtmNewAddressMessage(1 /* seqno */,
+                TEST_GLOBAL_ADDRESS, (short) 128 /* prefix len */,
+                (int) 0x300 /* flags: IFA_F_MANAGETEMPADDR | IFA_F_NOPREFIXROUTE */,
+                (byte) RT_SCOPE_UNIVERSE /* scope */, 23 /* ifindex */,
+                (long) 0xFFFFFFFF /* preferred */, (long) 0xFFFFFFFF /* valid */);
+        assertArrayEquals(expectedNewAddress, bytes);
+    }
+
+    @Test
     public void testToString() {
         final ByteBuffer byteBuffer = toByteBuffer(RTM_NEWADDR_HEX);
         byteBuffer.order(ByteOrder.LITTLE_ENDIAN);  // For testing.
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/TestableNetworkCallback.kt b/staticlibs/testutils/devicetests/com/android/testutils/TestableNetworkCallback.kt
index b84f9a6..92c83b7 100644
--- a/staticlibs/testutils/devicetests/com/android/testutils/TestableNetworkCallback.kt
+++ b/staticlibs/testutils/devicetests/com/android/testutils/TestableNetworkCallback.kt
@@ -326,7 +326,7 @@
         timeoutMs: Long = defaultTimeoutMs,
         errorMsg: String? = null,
         test: (T) -> Boolean = { true }
-    ) = pollOrThrow(timeoutMs).also {
+    ) = pollOrThrow(timeoutMs, "Did not receive ${T::class.simpleName} after ${timeoutMs}ms").also {
         if (it !is T) fail("Expected callback ${T::class.simpleName}, got $it")
         if (ANY_NETWORK !== network && it.network != network) {
             fail("Expected network $network for callback : $it")
diff --git a/staticlibs/testutils/host/com/android/testutils/ConnectivityCheckTargetPreparer.kt b/staticlibs/testutils/host/com/android/testutils/ConnectivityCheckTargetPreparer.kt
index 85589ad..ccc4064 100644
--- a/staticlibs/testutils/host/com/android/testutils/ConnectivityCheckTargetPreparer.kt
+++ b/staticlibs/testutils/host/com/android/testutils/ConnectivityCheckTargetPreparer.kt
@@ -21,6 +21,7 @@
 import com.android.tradefed.result.CollectingTestListener
 import com.android.tradefed.result.ddmlib.DefaultRemoteAndroidTestRunner
 import com.android.tradefed.targetprep.BaseTargetPreparer
+import com.android.tradefed.targetprep.TargetSetupError
 import com.android.tradefed.targetprep.suite.SuiteApkInstaller
 
 private const val CONNECTIVITY_CHECKER_APK = "ConnectivityChecker.apk"
@@ -52,13 +53,14 @@
 
         val receiver = CollectingTestListener()
         if (!testInformation.device.runInstrumentationTests(runner, receiver)) {
-            throw AssertionError("Device state check failed to complete")
+            throw TargetSetupError("Device state check failed to complete",
+                    testInformation.device.deviceDescriptor)
         }
 
         val runResult = receiver.currentRunResults
         if (runResult.isRunFailure) {
-            throw AssertionError("Failed to check device state before the test: " +
-                    runResult.runFailureMessage)
+            throw TargetSetupError("Failed to check device state before the test: " +
+                    runResult.runFailureMessage, testInformation.device.deviceDescriptor)
         }
 
         if (!runResult.hasFailedTests()) return
@@ -67,7 +69,8 @@
             else "$testDescription: ${testResult.stackTrace}"
         }.joinToString("\n")
 
-        throw AssertionError("Device setup checks failed. Check the test bench: \n$errorMsg")
+        throw TargetSetupError("Device setup checks failed. Check the test bench: \n$errorMsg",
+                testInformation.device.deviceDescriptor)
     }
 
     override fun tearDown(testInformation: TestInformation?, e: Throwable?) {