Add methods for updating ingressDiscardRule bpf map to BpfNetMaps am: b01647922d

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/modules/Connectivity/+/28712473

Change-Id: Idf060b234924379e27f12cd7cd3969b600817c30
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/common/src/com/android/net/module/util/bpf/IngressDiscardKey.java b/common/src/com/android/net/module/util/bpf/IngressDiscardKey.java
index eabcf3c..9fefb52 100644
--- a/common/src/com/android/net/module/util/bpf/IngressDiscardKey.java
+++ b/common/src/com/android/net/module/util/bpf/IngressDiscardKey.java
@@ -16,9 +16,12 @@
 
 package com.android.net.module.util.bpf;
 
+import com.android.net.module.util.InetAddressUtils;
 import com.android.net.module.util.Struct;
 
+import java.net.Inet4Address;
 import java.net.Inet6Address;
+import java.net.InetAddress;
 
 /** Key type for ingress discard map */
 public class IngressDiscardKey extends Struct {
@@ -29,4 +32,14 @@
     public IngressDiscardKey(final Inet6Address dstAddr) {
         this.dstAddr = dstAddr;
     }
+
+    private static Inet6Address getInet6Address(final InetAddress addr) {
+        return (addr instanceof Inet4Address)
+                ? InetAddressUtils.v4MappedV6Address((Inet4Address) addr)
+                : (Inet6Address) addr;
+    }
+
+    public IngressDiscardKey(final InetAddress dstAddr) {
+        this(getInet6Address(dstAddr));
+    }
 }
diff --git a/service/src/com/android/server/BpfNetMaps.java b/service/src/com/android/server/BpfNetMaps.java
index ec168dd..98c73d0 100644
--- a/service/src/com/android/server/BpfNetMaps.java
+++ b/service/src/com/android/server/BpfNetMaps.java
@@ -40,6 +40,7 @@
 import android.app.StatsManager;
 import android.content.Context;
 import android.net.INetd;
+import android.os.Build;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.provider.DeviceConfig;
@@ -51,6 +52,8 @@
 import android.util.Pair;
 import android.util.StatsEvent;
 
+import androidx.annotation.RequiresApi;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.modules.utils.BackgroundThread;
 import com.android.modules.utils.build.SdkLevel;
@@ -64,9 +67,12 @@
 import com.android.net.module.util.Struct.U8;
 import com.android.net.module.util.bpf.CookieTagMapKey;
 import com.android.net.module.util.bpf.CookieTagMapValue;
+import com.android.net.module.util.bpf.IngressDiscardKey;
+import com.android.net.module.util.bpf.IngressDiscardValue;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
+import java.net.InetAddress;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Set;
@@ -113,6 +119,8 @@
             "/sys/fs/bpf/netd_shared/map_netd_uid_permission_map";
     private static final String COOKIE_TAG_MAP_PATH =
             "/sys/fs/bpf/netd_shared/map_netd_cookie_tag_map";
+    public static final String INGRESS_DISCARD_MAP_PATH =
+            "/sys/fs/bpf/netd_shared/map_netd_ingress_discard_map";
     private static final S32 UID_RULES_CONFIGURATION_KEY = new S32(0);
     private static final S32 CURRENT_STATS_MAP_CONFIGURATION_KEY = new S32(1);
     private static final long UID_RULES_DEFAULT_CONFIGURATION = 0;
@@ -124,6 +132,7 @@
     private static IBpfMap<S32, UidOwnerValue> sUidOwnerMap = null;
     private static IBpfMap<S32, U8> sUidPermissionMap = null;
     private static IBpfMap<CookieTagMapKey, CookieTagMapValue> sCookieTagMap = null;
+    private static IBpfMap<IngressDiscardKey, IngressDiscardValue> sIngressDiscardMap = null;
 
     // LINT.IfChange(match_type)
     @VisibleForTesting public static final long NO_MATCH = 0;
@@ -201,6 +210,15 @@
         sCookieTagMap = cookieTagMap;
     }
 
+    /**
+     * Set ingressDiscardMap for test.
+     */
+    @VisibleForTesting
+    public static void setIngressDiscardMapForTest(
+            IBpfMap<IngressDiscardKey, IngressDiscardValue> ingressDiscardMap) {
+        sIngressDiscardMap = ingressDiscardMap;
+    }
+
     private static IBpfMap<S32, U32> getConfigurationMap() {
         try {
             return new BpfMap<>(
@@ -237,6 +255,15 @@
         }
     }
 
+    private static IBpfMap<IngressDiscardKey, IngressDiscardValue> getIngressDiscardMap() {
+        try {
+            return new BpfMap<>(INGRESS_DISCARD_MAP_PATH, BpfMap.BPF_F_RDWR,
+                    IngressDiscardKey.class, IngressDiscardValue.class);
+        } catch (ErrnoException e) {
+            throw new IllegalStateException("Cannot open ingress discard map", e);
+        }
+    }
+
     private static void initBpfMaps() {
         if (sConfigurationMap == null) {
             sConfigurationMap = getConfigurationMap();
@@ -270,6 +297,15 @@
         if (sCookieTagMap == null) {
             sCookieTagMap = getCookieTagMap();
         }
+
+        if (sIngressDiscardMap == null) {
+            sIngressDiscardMap = getIngressDiscardMap();
+        }
+        try {
+            sIngressDiscardMap.clear();
+        } catch (ErrnoException e) {
+            throw new IllegalStateException("Failed to initialize ingress discard map", e);
+        }
     }
 
     /**
@@ -308,6 +344,13 @@
         }
 
         /**
+         * Get interface name
+         */
+        public String getIfName(final int ifIndex) {
+            return Os.if_indextoname(ifIndex);
+        }
+
+        /**
          * Call synchronize_rcu()
          */
         public int synchronizeKernelRCU() {
@@ -987,6 +1030,45 @@
         }
     }
 
+    /**
+     * Set ingress discard rule
+     *
+     * @param address target address to set the ingress discard rule
+     * @param iface allowed interface
+     */
+    @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+    public void setIngressDiscardRule(final InetAddress address, final String iface) {
+        throwIfPreT("setIngressDiscardRule is not available on pre-T devices");
+        final int ifIndex = mDeps.getIfIndex(iface);
+        if (ifIndex == 0) {
+            Log.e(TAG, "Failed to get if index, skip setting ingress discard rule for " + address
+                    + "(" + iface + ")");
+            return;
+        }
+        try {
+            sIngressDiscardMap.updateEntry(new IngressDiscardKey(address),
+                    new IngressDiscardValue(ifIndex, ifIndex));
+        } catch (ErrnoException e) {
+            Log.e(TAG, "Failed to set ingress discard rule for " + address + "("
+                    + iface + "), " + e);
+        }
+    }
+
+    /**
+     * Remove ingress discard rule
+     *
+     * @param address target address to remove the ingress discard rule
+     */
+    @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+    public void removeIngressDiscardRule(final InetAddress address) {
+        throwIfPreT("removeIngressDiscardRule is not available on pre-T devices");
+        try {
+            sIngressDiscardMap.deleteEntry(new IngressDiscardKey(address));
+        } catch (ErrnoException e) {
+            Log.e(TAG, "Failed to remove ingress discard rule for " + address + ", " + e);
+        }
+    }
+
     /** Register callback for statsd to pull atom. */
     public void setPullAtomCallback(final Context context) {
         throwIfPreT("setPullAtomCallback is not available on pre-T devices");
@@ -1136,6 +1218,10 @@
                     });
             BpfDump.dumpMap(sUidPermissionMap, pw, "sUidPermissionMap",
                     (uid, permission) -> uid.val + " " + permissionToString(permission.val));
+            BpfDump.dumpMap(sIngressDiscardMap, pw, "sIngressDiscardMap",
+                    (key, value) -> "[" + key.dstAddr + "]: "
+                            + value.iif1 + "(" + mDeps.getIfName(value.iif1) + "), "
+                            + value.iif2 + "(" + mDeps.getIfName(value.iif2) + ")");
             pw.decreaseIndent();
         }
     }
diff --git a/tests/unit/java/com/android/server/BpfNetMapsTest.java b/tests/unit/java/com/android/server/BpfNetMapsTest.java
index 19fa41d..319af50 100644
--- a/tests/unit/java/com/android/server/BpfNetMapsTest.java
+++ b/tests/unit/java/com/android/server/BpfNetMapsTest.java
@@ -78,6 +78,8 @@
 import com.android.net.module.util.Struct.U8;
 import com.android.net.module.util.bpf.CookieTagMapKey;
 import com.android.net.module.util.bpf.CookieTagMapValue;
+import com.android.net.module.util.bpf.IngressDiscardKey;
+import com.android.net.module.util.bpf.IngressDiscardValue;
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
@@ -139,11 +141,14 @@
     private final IBpfMap<S32, U8> mUidPermissionMap = new TestBpfMap<>(S32.class, U8.class);
     private final IBpfMap<CookieTagMapKey, CookieTagMapValue> mCookieTagMap =
             spy(new TestBpfMap<>(CookieTagMapKey.class, CookieTagMapValue.class));
+    private final IBpfMap<IngressDiscardKey, IngressDiscardValue> mIngressDiscardMap =
+            new TestBpfMap<>(IngressDiscardKey.class, IngressDiscardValue.class);
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         doReturn(TEST_IF_INDEX).when(mDeps).getIfIndex(TEST_IF_NAME);
+        doReturn(TEST_IF_NAME).when(mDeps).getIfName(TEST_IF_INDEX);
         doReturn(0).when(mDeps).synchronizeKernelRCU();
         BpfNetMaps.setEnableJavaBpfMapForTest(true /* enable */);
         BpfNetMaps.setConfigurationMapForTest(mConfigurationMap);
@@ -153,6 +158,7 @@
         BpfNetMaps.setUidOwnerMapForTest(mUidOwnerMap);
         BpfNetMaps.setUidPermissionMapForTest(mUidPermissionMap);
         BpfNetMaps.setCookieTagMapForTest(mCookieTagMap);
+        BpfNetMaps.setIngressDiscardMapForTest(mIngressDiscardMap);
         mBpfNetMaps = new BpfNetMaps(mContext, mNetd, mDeps);
     }