Do not take cumulative network stats anymore

Use two maps to record the details of network stats and swap and clean
up after system server pulls the stats. The kernel program checks the
bpf map currently enabled before updating the stats and updates on the
corresponding map. Remove the TAG_STATS_MAP since we don't need to worry
about uid stats overflow problem. All the stats can be stored in the
same map until system server scrapes the stats and clean it up.

Bug: 79171384
Test: dumpsys netd trafficcontroller
      CtsUsageStatsTestCases

Change-Id: Ic79e382f51bf21eee78c4cac5a8a97edaf3654cd
diff --git a/libbpf_android/BpfUtils.cpp b/libbpf_android/BpfUtils.cpp
index 9f482d6..f5741bb 100644
--- a/libbpf_android/BpfUtils.cpp
+++ b/libbpf_android/BpfUtils.cpp
@@ -23,6 +23,7 @@
 #include <linux/bpf.h>
 #include <linux/if_ether.h>
 #include <linux/in.h>
+#include <linux/pfkeyv2.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/mman.h>
@@ -43,7 +44,9 @@
 using android::netdutils::MemBlock;
 using android::netdutils::Slice;
 
-constexpr size_t LOG_BUF_SIZE = 65536;
+// The buffer size for the buffer that records program loading logs, needs to be large enough for
+// the largest kernel program.
+constexpr size_t LOG_BUF_SIZE = 0x20000;
 
 namespace android {
 namespace bpf {
@@ -212,6 +215,27 @@
     return sock_cookie;
 }
 
+int synchronizeKernelRCU() {
+    // This is a temporary hack for network stats map swap on devices running
+    // 4.9 kernels. The kernel code of socket release on pf_key socket will
+    // explicitly call synchronize_rcu() which is exactly what we need.
+    int pfSocket = socket(AF_KEY, SOCK_RAW | SOCK_CLOEXEC, PF_KEY_V2);
+
+    if (pfSocket < 0) {
+        int ret = -errno;
+        ALOGE("create PF_KEY socket failed: %s", strerror(errno));
+        return ret;
+    }
+
+    // When closing socket, synchronize_rcu() gets called in sock_release().
+    if (close(pfSocket)) {
+        int ret = -errno;
+        ALOGE("failed to close the PF_KEY socket: %s", strerror(errno));
+        return ret;
+    }
+    return 0;
+}
+
 bool hasBpfSupport() {
     struct utsname buf;
     int kernel_version_major;
diff --git a/libbpf_android/include/bpf/BpfUtils.h b/libbpf_android/include/bpf/BpfUtils.h
index 6bb1f0f..426dc02 100644
--- a/libbpf_android/include/bpf/BpfUtils.h
+++ b/libbpf_android/include/bpf/BpfUtils.h
@@ -150,6 +150,7 @@
 bool hasBpfSupport();
 int parseProgramsFromFile(const char* path, BpfProgInfo* programs, size_t size,
                           const std::vector<BpfMapInfo>& mapPatterns);
+int synchronizeKernelRCU();
 
 #define SKIP_IF_BPF_NOT_SUPPORTED     \
     do {                              \
diff --git a/progs/netd.h b/progs/netd.h
index 6b2d103..ff97753 100644
--- a/progs/netd.h
+++ b/progs/netd.h
@@ -110,21 +110,21 @@
     .type = BPF_MAP_TYPE_HASH,
     .key_size = sizeof(uint32_t),
     .value_size = sizeof(struct stats_value),
-    .max_entries = UID_STATS_MAP_SIZE,
+    .max_entries = APP_STATS_MAP_SIZE,
 };
 
-struct bpf_map_def SEC("maps") uid_stats_map = {
+struct bpf_map_def SEC("maps") stats_map_A = {
     .type = BPF_MAP_TYPE_HASH,
     .key_size = sizeof(struct stats_key),
     .value_size = sizeof(struct stats_value),
-    .max_entries = UID_STATS_MAP_SIZE,
+    .max_entries = STATS_MAP_SIZE,
 };
 
-struct bpf_map_def SEC("maps") tag_stats_map = {
+struct bpf_map_def SEC("maps") stats_map_B = {
     .type = BPF_MAP_TYPE_HASH,
     .key_size = sizeof(struct stats_key),
     .value_size = sizeof(struct stats_value),
-    .max_entries = TAG_STATS_MAP_SIZE,
+    .max_entries = STATS_MAP_SIZE,
 };
 
 struct bpf_map_def SEC("maps") iface_stats_map = {
@@ -218,8 +218,8 @@
     return false;
 }
 
-static __always_inline BpfConfig getConfig() {
-    uint32_t mapSettingKey = CONFIGURATION_KEY;
+static __always_inline BpfConfig getConfig(uint32_t configKey) {
+    uint32_t mapSettingKey = configKey;
     BpfConfig* config = find_map_entry(&configuration_map, &mapSettingKey);
     if (!config) {
         // Couldn't read configuration entry. Assume everything is disabled.
@@ -233,7 +233,7 @@
 
     if ((uid <= MAX_SYSTEM_UID) && (uid >= MIN_SYSTEM_UID)) return BPF_PASS;
 
-    BpfConfig enabledRules = getConfig();
+    BpfConfig enabledRules = getConfig(UID_RULES_CONFIGURATION_KEY);
     if (!enabledRules) {
         return BPF_PASS;
     }
@@ -252,6 +252,15 @@
     return BPF_PASS;
 }
 
+static __always_inline inline void update_stats_with_config(struct __sk_buff* skb, int direction,
+                                                            void* key, uint8_t selectedMap) {
+    if (selectedMap == SELECT_MAP_A) {
+        bpf_update_stats(skb, &stats_map_A, direction, key);
+    } else if (selectedMap == SELECT_MAP_B) {
+        bpf_update_stats(skb, &stats_map_B, direction, key);
+    }
+}
+
 static __always_inline inline int bpf_traffic_account(struct __sk_buff* skb, int direction) {
     uint32_t sock_uid = get_socket_uid(skb);
     int match = bpf_owner_match(skb, sock_uid);
@@ -277,12 +286,18 @@
     uint8_t* counterSet = find_map_entry(&uid_counterset_map, &uid);
     if (counterSet) key.counterSet = (uint32_t)*counterSet;
 
+    uint32_t mapSettingKey = CURRENT_STATS_MAP_CONFIGURATION_KEY;
+    uint8_t* selectedMap = find_map_entry(&configuration_map, &mapSettingKey);
+    if (!selectedMap) {
+        return match;
+    }
+
     if (tag) {
-        bpf_update_stats(skb, &tag_stats_map, direction, &key);
+        update_stats_with_config(skb, direction, &key, *selectedMap);
     }
 
     key.tag = 0;
-    bpf_update_stats(skb, &uid_stats_map, direction, &key);
+    update_stats_with_config(skb, direction, &key, *selectedMap);
     bpf_update_stats(skb, &app_uid_stats_map, direction, &uid);
     return match;
 }