Merge "Add methods for updating ingressDiscardRule bpf map to BpfNetMaps" into main

This commit is contained in:
Motomu Utsumi
2023-11-06 10:41:53 +00:00
committed by Gerrit Code Review
4 changed files with 164 additions and 5 deletions

View File

@@ -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));
}
}

View File

@@ -54,6 +54,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);

View File

@@ -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;
@@ -86,9 +87,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;
@@ -137,6 +141,7 @@ public class BpfNetMaps {
private static IBpfMap<CookieTagMapKey, CookieTagMapValue> sCookieTagMap = null;
// TODO: Add BOOL class and replace U8?
private static IBpfMap<S32, U8> sDataSaverEnabledMap = null;
private static IBpfMap<IngressDiscardKey, IngressDiscardValue> sIngressDiscardMap = null;
private static final List<Pair<Integer, String>> PERMISSION_LIST = Arrays.asList(
Pair.create(PERMISSION_INTERNET, "PERMISSION_INTERNET"),
@@ -192,6 +197,15 @@ public class BpfNetMaps {
sDataSaverEnabledMap = dataSaverEnabledMap;
}
/**
* 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 +251,15 @@ public class BpfNetMaps {
}
}
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();
@@ -279,6 +302,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);
}
}
/**
@@ -315,6 +347,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()
*/
@@ -553,7 +592,7 @@ public class BpfNetMaps {
private Set<Integer> asSet(final int[] uids) {
final Set<Integer> uidSet = new ArraySet<>();
for (final int uid: uids) {
for (final int uid : uids) {
uidSet.add(uid);
}
return uidSet;
@@ -957,6 +996,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) {
@@ -1098,7 +1176,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();
}

View File

@@ -71,6 +71,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;
@@ -87,6 +88,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;
@@ -102,6 +105,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;
@@ -120,6 +125,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 long STATS_SELECT_MAP_A = 0;
@@ -143,11 +152,14 @@ public final class BpfNetMapsTest {
private final IBpfMap<CookieTagMapKey, CookieTagMapValue> mCookieTagMap =
spy(new TestBpfMap<>(CookieTagMapKey.class, CookieTagMapValue.class));
private final IBpfMap<S32, U8> mDataSaverEnabledMap = new TestBpfMap<>(S32.class, U8.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);
@@ -159,6 +171,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);
}
@@ -1206,7 +1219,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));
}
@@ -1215,10 +1228,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 + ")");
}
}