Merge "Add a dumpService variant that uses libbinder_ndk."
diff --git a/staticlibs/device/com/android/net/module/util/netlink/RtNetlinkRouteMessage.java b/staticlibs/device/com/android/net/module/util/netlink/RtNetlinkRouteMessage.java
index 1705f1c..9acac69 100644
--- a/staticlibs/device/com/android/net/module/util/netlink/RtNetlinkRouteMessage.java
+++ b/staticlibs/device/com/android/net/module/util/netlink/RtNetlinkRouteMessage.java
@@ -51,6 +51,7 @@
     public static final short RTA_DST           = 1;
     public static final short RTA_OIF           = 4;
     public static final short RTA_GATEWAY       = 5;
+    public static final short RTA_CACHEINFO     = 12;
 
     private int mIfindex;
     @NonNull
@@ -59,6 +60,8 @@
     private IpPrefix mDestination;
     @Nullable
     private InetAddress mGateway;
+    @Nullable
+    private StructRtaCacheInfo mRtaCacheInfo;
 
     private RtNetlinkRouteMessage(StructNlMsgHdr header) {
         super(header);
@@ -66,6 +69,7 @@
         mDestination = null;
         mGateway = null;
         mIfindex = 0;
+        mRtaCacheInfo = null;
     }
 
     public int getInterfaceIndex() {
@@ -87,6 +91,11 @@
         return mGateway;
     }
 
+    @Nullable
+    public StructRtaCacheInfo getRtaCacheInfo() {
+        return mRtaCacheInfo;
+    }
+
     /**
      * Check whether the address families of destination and gateway match rtm_family in
      * StructRtmsg.
@@ -158,6 +167,13 @@
             routeMsg.mIfindex = nlAttr.getValueAsInt(0 /* 0 isn't a valid ifindex */);
         }
 
+        // RTA_CACHEINFO
+        byteBuffer.position(baseOffset);
+        nlAttr = StructNlAttr.findNextAttrOfType(RTA_CACHEINFO, byteBuffer);
+        if (nlAttr != null) {
+            routeMsg.mRtaCacheInfo = StructRtaCacheInfo.parse(nlAttr.getValueAsByteBuffer());
+        }
+
         return routeMsg;
     }
 
@@ -180,6 +196,11 @@
             final StructNlAttr ifindex = new StructNlAttr(RTA_OIF, mIfindex);
             ifindex.pack(byteBuffer);
         }
+        if (mRtaCacheInfo != null) {
+            final StructNlAttr cacheInfo = new StructNlAttr(RTA_CACHEINFO,
+                    mRtaCacheInfo.writeToBytes());
+            cacheInfo.pack(byteBuffer);
+        }
     }
 
     @Override
@@ -189,7 +210,8 @@
                 + "Rtmsg{" + mRtmsg.toString() + "}, "
                 + "destination{" + mDestination.getAddress().getHostAddress() + "}, "
                 + "gateway{" + (mGateway == null ? "" : mGateway.getHostAddress()) + "}, "
-                + "ifindex{" + mIfindex + "} "
+                + "ifindex{" + mIfindex + "}, "
+                + "rta_cacheinfo{" + (mRtaCacheInfo == null ? "" : mRtaCacheInfo.toString()) + "} "
                 + "}";
     }
 }
diff --git a/staticlibs/device/com/android/net/module/util/netlink/StructRtaCacheInfo.java b/staticlibs/device/com/android/net/module/util/netlink/StructRtaCacheInfo.java
new file mode 100644
index 0000000..fef1f9e
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/netlink/StructRtaCacheInfo.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2023 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 androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+import java.nio.ByteBuffer;
+
+/**
+ * struct rta_cacheinfo
+ *
+ * see also:
+ *
+ *     include/uapi/linux/rtnetlink.h
+ *
+ * @hide
+ */
+public class StructRtaCacheInfo extends Struct {
+    // Already aligned.
+    public static final int STRUCT_SIZE = 32;
+
+    @Field(order = 0, type = Type.U32)
+    public final long clntref;
+    @Field(order = 1, type = Type.U32)
+    public final long lastuse;
+    @Field(order = 2, type = Type.S32)
+    public final int expires;
+    @Field(order = 3, type = Type.U32)
+    public final long error;
+    @Field(order = 4, type = Type.U32)
+    public final long used;
+    @Field(order = 5, type = Type.U32)
+    public final long id;
+    @Field(order = 6, type = Type.U32)
+    public final long ts;
+    @Field(order = 7, type = Type.U32)
+    public final long tsage;
+
+    StructRtaCacheInfo(long clntref, long lastuse, int expires, long error, long used, long id,
+            long ts, long tsage) {
+        this.clntref = clntref;
+        this.lastuse = lastuse;
+        this.expires = expires;
+        this.error = error;
+        this.used = used;
+        this.id = id;
+        this.ts = ts;
+        this.tsage = tsage;
+    }
+
+    /**
+     * Parse an rta_cacheinfo struct from a {@link ByteBuffer}.
+     *
+     * @param byteBuffer The buffer from which to parse the rta_cacheinfo.
+     * @return the parsed rta_cacheinfo struct, or {@code null} if the rta_cacheinfo struct
+     *         could not be parsed successfully (for example, if it was truncated).
+     */
+    @Nullable
+    public static StructRtaCacheInfo parse(@NonNull final ByteBuffer byteBuffer) {
+        if (byteBuffer.remaining() < STRUCT_SIZE) return null;
+
+        // The ByteOrder must already have been set to native order.
+        return Struct.parse(StructRtaCacheInfo.class, byteBuffer);
+    }
+
+    /**
+     * Write a rta_cacheinfo struct to {@link ByteBuffer}.
+     */
+    public void pack(@NonNull final ByteBuffer byteBuffer) {
+        // The ByteOrder must already have been set to native order.
+        this.writeToByteBuffer(byteBuffer);
+    }
+}
diff --git a/staticlibs/native/bpf_headers/include/bpf/bpf_helpers.h b/staticlibs/native/bpf_headers/include/bpf/bpf_helpers.h
index b3946ec..67ac0e4 100644
--- a/staticlibs/native/bpf_headers/include/bpf/bpf_helpers.h
+++ b/staticlibs/native/bpf_headers/include/bpf/bpf_helpers.h
@@ -84,6 +84,16 @@
     size_t _size_of_bpf_prog_def SECTION("size_of_bpf_prog_def") = sizeof(struct bpf_prog_def); \
     char _license[] SECTION("license") = (NAME)
 
+/* This macro disables loading BTF map debug information on Android <=U *and* all user builds.
+ *
+ * Note: Bpfloader v0.39+ honours 'btf_user_min_bpfloader_ver' on user builds,
+ * and 'btf_min_bpfloader_ver' on non-user builds.
+ * Older BTF capable versions unconditionally honour 'btf_min_bpfloader_ver'
+ */
+#define DISABLE_BTF_ON_USER_BUILDS() \
+    unsigned _btf_min_bpfloader_ver SECTION("btf_min_bpfloader_ver") = 39u; \
+    unsigned _btf_user_min_bpfloader_ver SECTION("btf_user_min_bpfloader_ver") = 0xFFFFFFFFu
+
 /* flag the resulting bpf .o file as critical to system functionality,
  * loading all kernel version appropriate programs in it must succeed
  * for bpfloader success
@@ -223,14 +233,22 @@
                         selinux, pindir, share, KVER(5, 8, 0), KVER_INF,       \
                         min_loader, max_loader, ignore_eng, ignore_user,       \
                         ignore_userdebug);                                     \
+                                                                               \
+    _Static_assert((size_bytes) >= 4096, "min 4 kiB ringbuffer size");         \
+    _Static_assert((size_bytes) <= 0x10000000, "max 256 MiB ringbuffer size"); \
+    _Static_assert(((size_bytes) & ((size_bytes) - 1)) == 0,                   \
+                   "ring buffer size must be a power of two");                 \
+                                                                               \
     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);                                       \
diff --git a/staticlibs/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h b/staticlibs/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h
index 8502961..ba16d53 100644
--- a/staticlibs/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h
+++ b/staticlibs/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h
@@ -28,11 +28,13 @@
   #define BPF_FD_TO_U32(x) static_cast<__u32>((x).get())
 #endif
 
-#define ptr_to_u64(x) ((uint64_t)(uintptr_t)(x))
-
 namespace android {
 namespace bpf {
 
+inline uint64_t ptr_to_u64(const void * const x) {
+    return (uint64_t)(uintptr_t)x;
+}
+
 /* Note: bpf_attr is a union which might have a much larger size then the anonymous struct portion
  * of it that we are using.  The kernel's bpf() system call will perform a strict check to ensure
  * all unused portions are zero.  It will fail with E2BIG if we don't fully zero bpf_attr.
@@ -53,6 +55,23 @@
                                });
 }
 
+// Note:
+//   'map_type' must be one of BPF_MAP_TYPE_{ARRAY,HASH}_OF_MAPS
+//   'value_size' must be sizeof(u32), ie. 4
+//   'inner_map_fd' is basically a template specifying {map_type, key_size, value_size, max_entries, map_flags}
+//   of the inner map type (and possibly only key_size/value_size actually matter?).
+inline int createOuterMap(bpf_map_type map_type, uint32_t key_size, uint32_t value_size,
+                          uint32_t max_entries, uint32_t map_flags, const BPF_FD_TYPE inner_map_fd) {
+    return bpf(BPF_MAP_CREATE, {
+                                       .map_type = map_type,
+                                       .key_size = key_size,
+                                       .value_size = value_size,
+                                       .max_entries = max_entries,
+                                       .map_flags = map_flags,
+                                       .inner_map_fd = BPF_FD_TO_U32(inner_map_fd),
+                               });
+}
+
 inline int writeToMapEntry(const BPF_FD_TYPE map_fd, const void* key, const void* value,
                            uint64_t flags) {
     return bpf(BPF_MAP_UPDATE_ELEM, {
@@ -168,38 +187,38 @@
 // over time), so we need to check that the field we're interested in is actually
 // supported/returned by the running kernel.  We do this by checking it is fully
 // within the bounds of the struct size as reported by the kernel.
-#define DEFINE_BPF_GET_FD_INFO(NAME, FIELD) \
-inline int bpfGetFd ## NAME(const BPF_FD_TYPE map_fd) { \
-    struct bpf_map_info map_info = {}; \
+#define DEFINE_BPF_GET_FD(TYPE, NAME, FIELD) \
+inline int bpfGetFd ## NAME(const BPF_FD_TYPE fd) { \
+    struct bpf_ ## TYPE ## _info info = {}; \
     union bpf_attr attr = { .info = { \
-        .bpf_fd = BPF_FD_TO_U32(map_fd), \
-        .info_len = sizeof(map_info), \
-        .info = ptr_to_u64(&map_info), \
+        .bpf_fd = BPF_FD_TO_U32(fd), \
+        .info_len = sizeof(info), \
+        .info = ptr_to_u64(&info), \
     }}; \
     int rv = bpf(BPF_OBJ_GET_INFO_BY_FD, attr); \
     if (rv) return rv; \
-    if (attr.info.info_len < offsetof(bpf_map_info, FIELD) + sizeof(map_info.FIELD)) { \
+    if (attr.info.info_len < offsetof(bpf_ ## TYPE ## _info, FIELD) + sizeof(info.FIELD)) { \
         errno = EOPNOTSUPP; \
         return -1; \
     }; \
-    return map_info.FIELD; \
+    return info.FIELD; \
 }
 
-// All 6 of these fields are already present in Linux v4.14 (even ACK 4.14-P)
+// All 7 of these fields are already present in Linux v4.14 (even ACK 4.14-P)
 // while BPF_OBJ_GET_INFO_BY_FD is not implemented at all in v4.9 (even ACK 4.9-Q)
-DEFINE_BPF_GET_FD_INFO(MapType, type)            // int bpfGetFdMapType(const BPF_FD_TYPE map_fd)
-DEFINE_BPF_GET_FD_INFO(MapId, id)                // int bpfGetFdMapId(const BPF_FD_TYPE map_fd)
-DEFINE_BPF_GET_FD_INFO(KeySize, key_size)        // int bpfGetFdKeySize(const BPF_FD_TYPE map_fd)
-DEFINE_BPF_GET_FD_INFO(ValueSize, value_size)    // int bpfGetFdValueSize(const BPF_FD_TYPE map_fd)
-DEFINE_BPF_GET_FD_INFO(MaxEntries, max_entries)  // int bpfGetFdMaxEntries(const BPF_FD_TYPE map_fd)
-DEFINE_BPF_GET_FD_INFO(MapFlags, map_flags)      // int bpfGetFdMapFlags(const BPF_FD_TYPE map_fd)
+DEFINE_BPF_GET_FD(map, MapType, type)            // int bpfGetFdMapType(const BPF_FD_TYPE map_fd)
+DEFINE_BPF_GET_FD(map, MapId, id)                // int bpfGetFdMapId(const BPF_FD_TYPE map_fd)
+DEFINE_BPF_GET_FD(map, KeySize, key_size)        // int bpfGetFdKeySize(const BPF_FD_TYPE map_fd)
+DEFINE_BPF_GET_FD(map, ValueSize, value_size)    // int bpfGetFdValueSize(const BPF_FD_TYPE map_fd)
+DEFINE_BPF_GET_FD(map, MaxEntries, max_entries)  // int bpfGetFdMaxEntries(const BPF_FD_TYPE map_fd)
+DEFINE_BPF_GET_FD(map, MapFlags, map_flags)      // int bpfGetFdMapFlags(const BPF_FD_TYPE map_fd)
+DEFINE_BPF_GET_FD(prog, ProgId, id)              // int bpfGetFdProgId(const BPF_FD_TYPE prog_fd)
 
-#undef DEFINE_BPF_GET_FD_INFO
+#undef DEFINE_BPF_GET_FD
 
 }  // namespace bpf
 }  // namespace android
 
-#undef ptr_to_u64
 #undef BPF_FD_TO_U32
 #undef BPF_FD_TYPE
 #undef BPF_FD_JUST_USE_INT
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/RtNetlinkRouteMessageTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/RtNetlinkRouteMessageTest.java
index 55cfd50..9881653 100644
--- a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/RtNetlinkRouteMessageTest.java
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/RtNetlinkRouteMessageTest.java
@@ -87,6 +87,8 @@
         assertEquals(routeMsg.getDestination(), TEST_IPV6_GLOBAL_PREFIX);
         assertEquals(735, routeMsg.getInterfaceIndex());
         assertEquals((Inet6Address) routeMsg.getGateway(), TEST_IPV6_LINK_LOCAL_GATEWAY);
+
+        assertNotNull(routeMsg.getRtaCacheInfo());
     }
 
     @Test
@@ -106,7 +108,9 @@
             + "0A400000FC02000100000000"                   // struct rtmsg
             + "1400010020010DB8000100000000000000000000"   // RTA_DST
             + "14000500FE800000000000000000000000000001"   // RTA_GATEWAY
-            + "08000400DF020000";                          // RTA_OIF
+            + "08000400DF020000"                           // RTA_OIF
+            + "24000C0000000000000000005EEA000000000000"   // RTA_CACHEINFO
+            + "00000000000000000000000000000000";
 
     @Test
     public void testPackRtmNewRoute() {
@@ -117,7 +121,7 @@
         assertTrue(msg instanceof RtNetlinkRouteMessage);
         final RtNetlinkRouteMessage routeMsg = (RtNetlinkRouteMessage) msg;
 
-        final ByteBuffer packBuffer = ByteBuffer.allocate(76);
+        final ByteBuffer packBuffer = ByteBuffer.allocate(112);
         packBuffer.order(ByteOrder.LITTLE_ENDIAN);  // For testing.
         routeMsg.pack(packBuffer);
         assertEquals(RTM_NEWROUTE_PACK_HEX, HexDump.toHexString(packBuffer.array()));
@@ -216,7 +220,9 @@
                 + "scope: 0, type: 1, flags: 0}, "
                 + "destination{2001:db8:1::}, "
                 + "gateway{fe80::1}, "
-                + "ifindex{735} "
+                + "ifindex{735}, "
+                + "rta_cacheinfo{clntref: 0, lastuse: 0, expires: 59998, error: 0, used: 0, "
+                + "id: 0, ts: 0, tsage: 0} "
                 + "}";
         assertEquals(expected, routeMsg.toString());
     }