Merge "Fix DevSdkIgnoreRule target SDK annotation processing."
diff --git a/staticlibs/device/com/android/net/module/util/FdEventsReader.java b/staticlibs/device/com/android/net/module/util/FdEventsReader.java
index ecf8e77..4825992 100644
--- a/staticlibs/device/com/android/net/module/util/FdEventsReader.java
+++ b/staticlibs/device/com/android/net/module/util/FdEventsReader.java
@@ -69,6 +69,7 @@
  * @param <BufferType> the type of the buffer used to read data.
  */
 public abstract class FdEventsReader<BufferType> {
+    private static final String TAG = FdEventsReader.class.getSimpleName();
     private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR;
     private static final int UNREGISTER_THIS_FD = 0;
 
@@ -167,6 +168,18 @@
     protected void handlePacket(@NonNull BufferType recvbuf, int length) {}
 
     /**
+     * Called by the subclasses of FdEventsReader, decide whether it should stop reading packet or
+     * just ignore the specific error other than EAGAIN or EINTR.
+     *
+     * @return {@code true} if this FdEventsReader should stop reading from the socket.
+     *         {@code false} if it should continue.
+     */
+    protected boolean handleReadError(@NonNull ErrnoException e) {
+        logError("readPacket error: ", e);
+        return true; // by default, stop reading on any error.
+    }
+
+    /**
      * Called by the main loop to log errors.  In some cases |e| may be null.
      */
     protected void logError(@NonNull String msg, @Nullable Exception e) {}
@@ -234,8 +247,10 @@
                 } else if (e.errno == OsConstants.EINTR) {
                     continue;
                 } else {
-                    if (isRunning()) logError("readPacket error: ", e);
-                    break;
+                    if (!isRunning()) break;
+                    final boolean shouldStop = handleReadError(e);
+                    if (shouldStop) break;
+                    continue;
                 }
             } catch (Exception e) {
                 if (isRunning()) logError("readPacket error: ", e);
@@ -246,7 +261,7 @@
                 handlePacket(mBuffer, bytesRead);
             } catch (Exception e) {
                 logError("handlePacket error: ", e);
-                Log.wtf(FdEventsReader.class.getSimpleName(), "Error handling packet", e);
+                Log.wtf(TAG, "Error handling packet", e);
             }
         }
 
diff --git a/staticlibs/native/bpf_headers/include/bpf/BpfMap.h b/staticlibs/native/bpf_headers/include/bpf/BpfMap.h
index 86c0756..a7720ea 100644
--- a/staticlibs/native/bpf_headers/include/bpf/BpfMap.h
+++ b/staticlibs/native/bpf_headers/include/bpf/BpfMap.h
@@ -49,16 +49,20 @@
   protected:
     // flag must be within BPF_OBJ_FLAG_MASK, ie. 0, BPF_F_RDONLY, BPF_F_WRONLY
     BpfMap<Key, Value>(const char* pathname, uint32_t flags) {
-        int map_fd = mapRetrieve(pathname, flags);
-        if (map_fd >= 0) mMapFd.reset(map_fd);
+        mMapFd.reset(mapRetrieve(pathname, flags));
+        if (mMapFd < 0) abort();
+        if (isAtLeastKernelVersion(4, 14, 0)) {
+            if (bpfGetFdKeySize(mMapFd) != sizeof(Key)) abort();
+            if (bpfGetFdValueSize(mMapFd) != sizeof(Value)) abort();
+        }
     }
 
   public:
     explicit BpfMap<Key, Value>(const char* pathname) : BpfMap<Key, Value>(pathname, 0) {}
 
     BpfMap<Key, Value>(bpf_map_type map_type, uint32_t max_entries, uint32_t map_flags = 0) {
-        int map_fd = createMap(map_type, sizeof(Key), sizeof(Value), max_entries, map_flags);
-        if (map_fd >= 0) mMapFd.reset(map_fd);
+        mMapFd.reset(createMap(map_type, sizeof(Key), sizeof(Value), max_entries, map_flags));
+        if (mMapFd < 0) abort();
     }
 
     base::Result<Key> getFirstKey() const {
@@ -100,14 +104,16 @@
     }
 
     // Function that tries to get map from a pinned path.
-    base::Result<void> init(const char* path);
+    [[clang::reinitializes]] base::Result<void> init(const char* path);
 
 #ifdef TEST_BPF_MAP
     // due to Android SELinux limitations which prevent map creation by anyone besides the bpfloader
     // this should only ever be used by test code, it is equivalent to:
     //   .reset(createMap(type, keysize, valuesize, max_entries, map_flags)
     // TODO: derive map_flags from BpfMap vs BpfMapRO
-    base::Result<void> resetMap(bpf_map_type map_type, uint32_t max_entries, uint32_t map_flags = 0) {
+    [[clang::reinitializes]] base::Result<void> resetMap(bpf_map_type map_type,
+                                                         uint32_t max_entries,
+                                                         uint32_t map_flags = 0) {
         int map_fd = createMap(map_type, sizeof(Key), sizeof(Value), max_entries, map_flags);
         if (map_fd < 0) {
              auto err = ErrnoErrorf("Unable to create map.");
@@ -152,14 +158,23 @@
 
     // Move assignment operator
     BpfMap<Key, Value>& operator=(BpfMap<Key, Value>&& other) noexcept {
-        mMapFd = std::move(other.mMapFd);
-        other.reset(-1);
+        if (this != &other) {
+            mMapFd = std::move(other.mMapFd);
+            other.reset();
+        }
         return *this;
     }
 
     void reset(base::unique_fd fd) = delete;
 
-    void reset(int fd) { mMapFd.reset(fd); }
+    [[clang::reinitializes]] void reset(int fd = -1) {
+        mMapFd.reset(fd);
+        if ((fd >= 0) && isAtLeastKernelVersion(4, 14, 0)) {
+            if (bpfGetFdKeySize(mMapFd) != sizeof(Key)) abort();
+            if (bpfGetFdValueSize(mMapFd) != sizeof(Value)) abort();
+            if (bpfGetFdMapFlags(mMapFd) != 0) abort(); // TODO: fix for BpfMapRO
+        }
+    }
 
     bool isValid() const { return mMapFd != -1; }
 
@@ -200,6 +215,14 @@
     if (mMapFd == -1) {
         return ErrnoErrorf("Pinned map not accessible or does not exist: ({})", path);
     }
+    if (isAtLeastKernelVersion(4, 14, 0)) {
+        // Normally we should return an error here instead of calling abort,
+        // but this cannot happen at runtime without a massive code bug (K/V type mismatch)
+        // and as such it's better to just blow the system up and let the developer fix it.
+        // Crashes are much more likely to be noticed than logs and missing functionality.
+        if (bpfGetFdKeySize(mMapFd) != sizeof(Key)) abort();
+        if (bpfGetFdValueSize(mMapFd) != sizeof(Value)) abort();
+    }
     return {};
 }
 
diff --git a/staticlibs/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h b/staticlibs/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h
index abf83da..4b29c44 100644
--- a/staticlibs/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h
+++ b/staticlibs/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h
@@ -150,6 +150,36 @@
                                 });
 }
 
+// requires 4.14+ 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 = {}; \
+    union bpf_attr attr = { .info = { \
+        .bpf_fd = BPF_FD_TO_U32(map_fd), \
+        .info_len = sizeof(map_info), \
+        .info = ptr_to_u64(&map_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)) { \
+        errno = EOPNOTSUPP; \
+        return -1; \
+    }; \
+    return map_info.FIELD; \
+}
+
+// All 6 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)
+
+#undef DEFINE_BPF_GET_FD_INFO
+
 }  // namespace bpf
 }  // namespace android
 
diff --git a/staticlibs/native/bpfmapjni/com_android_net_module_util_TcUtils.cpp b/staticlibs/native/bpfmapjni/com_android_net_module_util_TcUtils.cpp
index 073a46f..cb06afb 100644
--- a/staticlibs/native/bpfmapjni/com_android_net_module_util_TcUtils.cpp
+++ b/staticlibs/native/bpfmapjni/com_android_net_module_util_TcUtils.cpp
@@ -34,7 +34,7 @@
   int error = isEthernet(interface.c_str(), result);
   if (error) {
     throwIOException(
-        env, "com_android_net_module_util_TcUtils_isEthernet error: ", error);
+        env, "com_android_net_module_util_TcUtils_isEthernet error: ", -error);
   }
   // result is not touched when error is returned; leave false.
   return result;
@@ -50,7 +50,7 @@
   if (error) {
     throwIOException(
         env,
-        "com_android_net_module_util_TcUtils_tcFilterAddDevBpf error: ", error);
+        "com_android_net_module_util_TcUtils_tcFilterAddDevBpf error: ", -error);
   }
 }
 
@@ -68,7 +68,7 @@
     throwIOException(env,
                      "com_android_net_module_util_TcUtils_"
                      "tcFilterAddDevIngressPolice error: ",
-                     error);
+                     -error);
   }
 }
 
@@ -80,7 +80,7 @@
   if (error) {
     throwIOException(
         env,
-        "com_android_net_module_util_TcUtils_tcFilterDelDev error: ", error);
+        "com_android_net_module_util_TcUtils_tcFilterDelDev error: ", -error);
   }
 }
 
@@ -92,7 +92,7 @@
   if (error) {
     throwIOException(
         env,
-        "com_android_net_module_util_TcUtils_tcQdiscAddDevClsact error: ", error);
+        "com_android_net_module_util_TcUtils_tcQdiscAddDevClsact error: ", -error);
   }
 }
 
diff --git a/staticlibs/native/tcutils/logging.h b/staticlibs/native/tcutils/logging.h
index 70604b3..7ed8f66 100644
--- a/staticlibs/native/tcutils/logging.h
+++ b/staticlibs/native/tcutils/logging.h
@@ -32,4 +32,25 @@
   va_end(args);
 }
 
+static inline void ALOGW(const char *fmt...) {
+  va_list args;
+  va_start(args, fmt);
+  __android_log_vprint(ANDROID_LOG_WARN, LOG_TAG, fmt, args);
+  va_end(args);
+}
+
+static inline void ALOGI(const char *fmt...) {
+  va_list args;
+  va_start(args, fmt);
+  __android_log_vprint(ANDROID_LOG_INFO, LOG_TAG, fmt, args);
+  va_end(args);
+}
+
+static inline void ALOGD(const char *fmt...) {
+  va_list args;
+  va_start(args, fmt);
+  __android_log_vprint(ANDROID_LOG_DEBUG, LOG_TAG, fmt, args);
+  va_end(args);
+}
+
 }
diff --git a/staticlibs/native/tcutils/tcutils.cpp b/staticlibs/native/tcutils/tcutils.cpp
index 144a4c9..4101885 100644
--- a/staticlibs/native/tcutils/tcutils.cpp
+++ b/staticlibs/native/tcutils/tcutils.cpp
@@ -386,6 +386,12 @@
     return -error;
   }
 
+  if (setsockopt(fd, SOL_NETLINK, NETLINK_EXT_ACK, &on, sizeof(on))) {
+    int error = errno;
+    ALOGW("setsockopt(fd, SOL_NETLINK, NETLINK_EXT_ACK, 1): %d", error);
+    // will fail on 4.9 kernels so don't: return -error;
+  }
+
   // this is needed to get valid strace netlink parsing, it allocates the pid
   if (bind(fd, (const struct sockaddr *)&KERNEL_NLADDR,
            sizeof(KERNEL_NLADDR))) {