From 77b49996d23cae520b21fb1b046a07793955c660 Mon Sep 17 00:00:00 2001 From: Motomu Utsumi Date: Mon, 23 Oct 2023 17:06:12 +0900 Subject: [PATCH] Add methods for updating ingressDiscardRule bpf map to BpfNetMaps Bug 295800201 Test: NetworkStaticLibsTests Change-Id: I42bc0adc22c3018480029d624053f758d815e526 --- .../module/util/bpf/IngressDiscardKey.java | 13 +++ .../src/android/net/BpfNetMapsConstants.java | 2 + .../src/com/android/server/BpfNetMaps.java | 85 ++++++++++++++++++- .../com/android/server/BpfNetMapsTest.java | 69 ++++++++++++++- 4 files changed, 164 insertions(+), 5 deletions(-) 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 eabcf3cce7..9fefb521d9 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 class IngressDiscardKey extends Struct { 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/framework/src/android/net/BpfNetMapsConstants.java b/framework/src/android/net/BpfNetMapsConstants.java index 36848e73d3..f888298e62 100644 --- a/framework/src/android/net/BpfNetMapsConstants.java +++ b/framework/src/android/net/BpfNetMapsConstants.java @@ -45,6 +45,8 @@ public class BpfNetMapsConstants { "/sys/fs/bpf/netd_shared/map_netd_cookie_tag_map"; public static final String DATA_SAVER_ENABLED_MAP_PATH = "/sys/fs/bpf/netd_shared/map_netd_data_saver_enabled_map"; + public static final String INGRESS_DISCARD_MAP_PATH = + "/sys/fs/bpf/netd_shared/map_netd_ingress_discard_map"; public static final Struct.S32 UID_RULES_CONFIGURATION_KEY = new Struct.S32(0); public static final Struct.S32 CURRENT_STATS_MAP_CONFIGURATION_KEY = new Struct.S32(1); public static final Struct.S32 DATA_SAVER_ENABLED_KEY = new Struct.S32(0); diff --git a/service/src/com/android/server/BpfNetMaps.java b/service/src/com/android/server/BpfNetMaps.java index 6ade124885..ccff9c99b0 100644 --- a/service/src/com/android/server/BpfNetMaps.java +++ b/service/src/com/android/server/BpfNetMaps.java @@ -25,6 +25,7 @@ import static android.net.BpfNetMapsConstants.DATA_SAVER_ENABLED_KEY; import static android.net.BpfNetMapsConstants.DATA_SAVER_ENABLED_MAP_PATH; import static android.net.BpfNetMapsConstants.HAPPY_BOX_MATCH; import static android.net.BpfNetMapsConstants.IIF_MATCH; +import static android.net.BpfNetMapsConstants.INGRESS_DISCARD_MAP_PATH; import static android.net.BpfNetMapsConstants.LOCKDOWN_VPN_MATCH; import static android.net.BpfNetMapsConstants.PENALTY_BOX_MATCH; import static android.net.BpfNetMapsConstants.UID_OWNER_MAP_PATH; @@ -85,9 +86,12 @@ import com.android.net.module.util.Struct.U32; 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; @@ -136,6 +140,7 @@ public class BpfNetMaps { private static IBpfMap sCookieTagMap = null; // TODO: Add BOOL class and replace U8? private static IBpfMap sDataSaverEnabledMap = null; + private static IBpfMap sIngressDiscardMap = null; private static final List> PERMISSION_LIST = Arrays.asList( Pair.create(PERMISSION_INTERNET, "PERMISSION_INTERNET"), @@ -191,6 +196,15 @@ public class BpfNetMaps { sDataSaverEnabledMap = dataSaverEnabledMap; } + /** + * Set ingressDiscardMap for test. + */ + @VisibleForTesting + public static void setIngressDiscardMapForTest( + IBpfMap ingressDiscardMap) { + sIngressDiscardMap = ingressDiscardMap; + } + private static IBpfMap getConfigurationMap() { try { return new BpfMap<>( @@ -236,6 +250,15 @@ public class BpfNetMaps { } } + private static IBpfMap 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(); @@ -278,6 +301,15 @@ public class BpfNetMaps { } catch (ErrnoException e) { throw new IllegalStateException("Failed to initialize data saver configuration", e); } + + if (sIngressDiscardMap == null) { + sIngressDiscardMap = getIngressDiscardMap(); + } + try { + sIngressDiscardMap.clear(); + } catch (ErrnoException e) { + throw new IllegalStateException("Failed to initialize ingress discard map", e); + } } /** @@ -314,6 +346,13 @@ public class BpfNetMaps { return Os.if_nametoindex(ifName); } + /** + * Get interface name + */ + public String getIfName(final int ifIndex) { + return Os.if_indextoname(ifIndex); + } + /** * Call synchronize_rcu() */ @@ -575,7 +614,7 @@ public class BpfNetMaps { private Set asSet(final int[] uids) { final Set uidSet = new ArraySet<>(); - for (final int uid: uids) { + for (final int uid : uids) { uidSet.add(uid); } return uidSet; @@ -979,6 +1018,45 @@ public class BpfNetMaps { } } + /** + * 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. */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) public void setPullAtomCallback(final Context context) { @@ -1120,7 +1198,10 @@ public class BpfNetMaps { }); 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) + ")"); dumpDataSaverConfig(pw); pw.decreaseIndent(); } diff --git a/tests/unit/java/com/android/server/BpfNetMapsTest.java b/tests/unit/java/com/android/server/BpfNetMapsTest.java index ea2b26136c..cc33a3a259 100644 --- a/tests/unit/java/com/android/server/BpfNetMapsTest.java +++ b/tests/unit/java/com/android/server/BpfNetMapsTest.java @@ -69,6 +69,7 @@ import android.app.StatsManager; import android.content.Context; import android.net.BpfNetMapsUtils; import android.net.INetd; +import android.net.InetAddresses; import android.net.UidOwnerValue; import android.os.Build; import android.os.ServiceSpecificException; @@ -85,6 +86,8 @@ import com.android.net.module.util.Struct.U32; 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; @@ -100,6 +103,8 @@ import org.mockito.MockitoAnnotations; import java.io.FileDescriptor; import java.io.StringWriter; +import java.net.Inet4Address; +import java.net.Inet6Address; import java.util.ArrayList; import java.util.List; @@ -118,6 +123,10 @@ public final class BpfNetMapsTest { private static final int TEST_IF_INDEX = 7; private static final int NO_IIF = 0; private static final int NULL_IIF = 0; + private static final Inet4Address TEST_V4_ADDRESS = + (Inet4Address) InetAddresses.parseNumericAddress("192.0.2.1"); + private static final Inet6Address TEST_V6_ADDRESS = + (Inet6Address) InetAddresses.parseNumericAddress("2001:db8::1"); private static final String CHAINNAME = "fw_dozable"; private static final List FIREWALL_CHAINS = List.of( FIREWALL_CHAIN_DOZABLE, @@ -145,11 +154,14 @@ public final class BpfNetMapsTest { private final IBpfMap mCookieTagMap = spy(new TestBpfMap<>(CookieTagMapKey.class, CookieTagMapValue.class)); private final IBpfMap mDataSaverEnabledMap = new TestBpfMap<>(S32.class, U8.class); + private final IBpfMap 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); @@ -161,6 +173,7 @@ public final class BpfNetMapsTest { BpfNetMaps.setCookieTagMapForTest(mCookieTagMap); BpfNetMaps.setDataSaverEnabledMapForTest(mDataSaverEnabledMap); mDataSaverEnabledMap.updateEntry(DATA_SAVER_ENABLED_KEY, new U8(DATA_SAVER_DISABLED)); + BpfNetMaps.setIngressDiscardMapForTest(mIngressDiscardMap); mBpfNetMaps = new BpfNetMaps(mContext, mNetd, mDeps); } @@ -1208,7 +1221,7 @@ public final class BpfNetMapsTest { @Test @IgnoreAfter(Build.VERSION_CODES.S_V2) public void testSetDataSaverEnabledBeforeT() { - for (boolean enable : new boolean[] {true, false}) { + for (boolean enable : new boolean[]{true, false}) { assertThrows(UnsupportedOperationException.class, () -> mBpfNetMaps.setDataSaverEnabled(enable)); } @@ -1217,10 +1230,60 @@ public final class BpfNetMapsTest { @Test @IgnoreUpTo(Build.VERSION_CODES.S_V2) public void testSetDataSaverEnabled() throws Exception { - for (boolean enable : new boolean[] {true, false}) { + for (boolean enable : new boolean[]{true, false}) { mBpfNetMaps.setDataSaverEnabled(enable); assertEquals(enable ? DATA_SAVER_ENABLED : DATA_SAVER_DISABLED, - mDataSaverEnabledMap.getValue(DATA_SAVER_ENABLED_KEY).val); + mDataSaverEnabledMap.getValue(DATA_SAVER_ENABLED_KEY).val); } } + + @Test + @IgnoreUpTo(Build.VERSION_CODES.S_V2) + public void testSetIngressDiscardRule_V4address() throws Exception { + mBpfNetMaps.setIngressDiscardRule(TEST_V4_ADDRESS, TEST_IF_NAME); + final IngressDiscardValue val = mIngressDiscardMap.getValue(new IngressDiscardKey( + TEST_V4_ADDRESS)); + assertEquals(TEST_IF_INDEX, val.iif1); + assertEquals(TEST_IF_INDEX, val.iif2); + } + + @Test + @IgnoreUpTo(Build.VERSION_CODES.S_V2) + public void testSetIngressDiscardRule_V6address() throws Exception { + mBpfNetMaps.setIngressDiscardRule(TEST_V6_ADDRESS, TEST_IF_NAME); + final IngressDiscardValue val = + mIngressDiscardMap.getValue(new IngressDiscardKey(TEST_V6_ADDRESS)); + assertEquals(TEST_IF_INDEX, val.iif1); + assertEquals(TEST_IF_INDEX, val.iif2); + } + + @Test + @IgnoreUpTo(Build.VERSION_CODES.S_V2) + public void testRemoveIngressDiscardRule() throws Exception { + mBpfNetMaps.setIngressDiscardRule(TEST_V4_ADDRESS, TEST_IF_NAME); + mBpfNetMaps.setIngressDiscardRule(TEST_V6_ADDRESS, TEST_IF_NAME); + final IngressDiscardKey v4Key = new IngressDiscardKey(TEST_V4_ADDRESS); + final IngressDiscardKey v6Key = new IngressDiscardKey(TEST_V6_ADDRESS); + assertTrue(mIngressDiscardMap.containsKey(v4Key)); + assertTrue(mIngressDiscardMap.containsKey(v6Key)); + + mBpfNetMaps.removeIngressDiscardRule(TEST_V4_ADDRESS); + assertFalse(mIngressDiscardMap.containsKey(v4Key)); + assertTrue(mIngressDiscardMap.containsKey(v6Key)); + + mBpfNetMaps.removeIngressDiscardRule(TEST_V6_ADDRESS); + assertFalse(mIngressDiscardMap.containsKey(v4Key)); + assertFalse(mIngressDiscardMap.containsKey(v6Key)); + } + + @Test + @IgnoreUpTo(Build.VERSION_CODES.S_V2) + public void testDumpIngressDiscardRule() throws Exception { + mBpfNetMaps.setIngressDiscardRule(TEST_V4_ADDRESS, TEST_IF_NAME); + mBpfNetMaps.setIngressDiscardRule(TEST_V6_ADDRESS, TEST_IF_NAME); + final String dump = getDump(); + assertDumpContains(dump, TEST_V4_ADDRESS.getHostAddress()); + assertDumpContains(dump, TEST_V6_ADDRESS.getHostAddress()); + assertDumpContains(dump, TEST_IF_INDEX + "(" + TEST_IF_NAME + ")"); + } }