bpf netd: Add local_net_access bpf map along with lookup method.

local_net_access is of type BPF_MAP_TYPE_LPM_TRIE (longest prefix
matching map) which stores interface index, remote address, protocol
and remote port. Lookup method is_local_net_access_allowed() returns
false if the arguments are found in the map and disallowed, otherwise
it returns true.

Test: Locally tested by adding values from userspace and querying the
bpf map.

Bug: 373608779
Change-Id: Ia34b3cb5613b56f6db5b224ec03f8c9909a49fe1
diff --git a/bpf/progs/netd.c b/bpf/progs/netd.c
index 8897627..41ea82d 100644
--- a/bpf/progs/netd.c
+++ b/bpf/progs/netd.c
@@ -99,6 +99,11 @@
 DEFINE_BPF_MAP_RO_NETD(data_saver_enabled_map, ARRAY, uint32_t, bool,
                        DATA_SAVER_ENABLED_MAP_SIZE)
 
+DEFINE_BPF_MAP_EXT(local_net_access_map, LPM_TRIE, LocalNetAccessKey, bool, 1000,
+                   AID_ROOT, AID_NET_BW_ACCT, 0060, "fs_bpf_net_shared", "", PRIVATE,
+                   BPFLOADER_MAINLINE_25Q2_VERSION, BPFLOADER_MAX_VER, LOAD_ON_ENG, LOAD_ON_USER,
+                   LOAD_ON_USERDEBUG, 0)
+
 // iptables xt_bpf programs need to be usable by both netd and netutils_wrappers
 // selinux contexts, because even non-xt_bpf iptables mutations are implemented as
 // a full table dump, followed by an update in userspace, and then a reload into the kernel,
@@ -230,6 +235,23 @@
         : bpf_skb_load_bytes(skb, L3_off, to, len);
 }
 
+/*
+ * False iff arguments are found with longest prefix match lookup and disallowed.
+ */
+static inline __always_inline __unused bool is_local_net_access_allowed(const uint32_t if_index,
+        const struct in6_addr* remote_ip6, const uint16_t protocol, const __be16 remote_port) {
+    LocalNetAccessKey query_key = {
+        .lpm_bitlen = 8 * (sizeof(if_index) + sizeof(*remote_ip6) + sizeof(protocol)
+            + sizeof(remote_port)),
+        .if_index = if_index,
+        .remote_ip6 = *remote_ip6,
+        .protocol = protocol,
+        .remote_port = remote_port
+    };
+    bool* v = bpf_local_net_access_map_lookup_elem(&query_key);
+    return v ? *v : true;
+}
+
 static __always_inline inline void do_packet_tracing(
         const struct __sk_buff* const skb, const struct egress_bool egress, const uint32_t uid,
         const uint32_t tag, const struct kver_uint kver) {
diff --git a/bpf/progs/netd.h b/bpf/progs/netd.h
index be7c311..6561311 100644
--- a/bpf/progs/netd.h
+++ b/bpf/progs/netd.h
@@ -185,6 +185,7 @@
 #define PACKET_TRACE_RINGBUF_PATH BPF_NETD_PATH "map_netd_packet_trace_ringbuf"
 #define PACKET_TRACE_ENABLED_MAP_PATH BPF_NETD_PATH "map_netd_packet_trace_enabled_map"
 #define DATA_SAVER_ENABLED_MAP_PATH BPF_NETD_PATH "map_netd_data_saver_enabled_map"
+#define LOCAL_NET_ACCESS_MAP_PATH BPF_NETD_PATH "map_netd_local_net_access_map"
 
 #endif // __cplusplus
 
@@ -245,6 +246,18 @@
 } IngressDiscardValue;
 STRUCT_SIZE(IngressDiscardValue, 2 * 4);  // 8
 
+typedef struct {
+  // Longest prefix match length in bits (value from 0 to 192).
+  uint32_t lpm_bitlen;
+  uint32_t if_index;
+  // IPv4 uses IPv4-mapped IPv6 address format.
+  struct in6_addr remote_ip6;
+  // u16 instead of u8 to avoid padding due to alignment requirement.
+  uint16_t protocol;
+  __be16 remote_port;
+} LocalNetAccessKey;
+STRUCT_SIZE(LocalNetAccessKey, 4 + 4 + 16 + 2 + 2);  // 28
+
 // Entry in the configuration map that stores which UID rules are enabled.
 #define UID_RULES_CONFIGURATION_KEY 0
 // Entry in the configuration map that stores which stats map is currently in use.
diff --git a/bpf/tests/mts/bpf_existence_test.cpp b/bpf/tests/mts/bpf_existence_test.cpp
index 0b5b7be..d605379 100644
--- a/bpf/tests/mts/bpf_existence_test.cpp
+++ b/bpf/tests/mts/bpf_existence_test.cpp
@@ -20,7 +20,9 @@
 #include <set>
 #include <string>
 
+#include <android-base/properties.h>
 #include <android-modules-utils/sdk_level.h>
+#include <android/api-level.h>
 #include <bpf/BpfUtils.h>
 
 #include <gtest/gtest.h>
@@ -46,6 +48,11 @@
 class BpfExistenceTest : public ::testing::Test {
 };
 
+//ToDo: replace isAtLeast25Q2 with IsAtLeastB once sdk_level have been upgraded to 36 on aosp/main
+const bool unreleased = (android::base::GetProperty("ro.build.version.codename", "REL") != "REL");
+const int api_level = unreleased ? __ANDROID_API_FUTURE__ : android_get_device_api_level();
+const bool isAtLeast25Q2 = (api_level > __ANDROID_API_V__);
+
 // Part of Android R platform (for 4.9+), but mainlined in S
 static const set<string> PLATFORM_ONLY_IN_R = {
     PLATFORM "map_offload_tether_ingress_map",
@@ -159,6 +166,11 @@
     NETD "prog_netd_setsockopt_prog",
 };
 
+// Provided by *current* mainline module for 25Q2+ devices
+static const set<string> MAINLINE_FOR_25Q2_PLUS = {
+    NETD "map_netd_local_net_access_map",
+};
+
 static void addAll(set<string>& a, const set<string>& b) {
     a.insert(b.begin(), b.end());
 }
@@ -209,6 +221,9 @@
     DO_EXPECT(IsAtLeastV(), MAINLINE_FOR_V_PLUS);
     DO_EXPECT(IsAtLeastV() && isAtLeastKernelVersion(5, 4, 0), MAINLINE_FOR_V_5_4_PLUS);
 
+    if (isAtLeast25Q2) ASSERT_TRUE(isAtLeastKernelVersion(5, 4, 0));
+    DO_EXPECT(isAtLeast25Q2, MAINLINE_FOR_25Q2_PLUS);
+
     for (const auto& file : mustExist) {
         EXPECT_EQ(0, access(file.c_str(), R_OK)) << file << " does not exist";
     }