Use java BpfMap in BpfNetMaps#setChildChain

Bug: 217624062
Test: atest BpfNetMapsTest android.net.cts.ConnectivityManagerTest#testFirewallBlocking

Change-Id: I13e96911eccd7d1d0545a156ddc2859bcaac09eb
This commit is contained in:
Motomu Utsumi
2022-06-19 10:45:30 +00:00
parent be3ff1e923
commit 18b287d285
3 changed files with 113 additions and 10 deletions

View File

@@ -5903,6 +5903,7 @@ public class ConnectivityManager {
* *
* @param chain target chain. * @param chain target chain.
* @param enable whether the chain should be enabled. * @param enable whether the chain should be enabled.
* @throws UnsupportedOperationException if called on pre-T devices.
* @throws IllegalStateException if enabling or disabling the firewall chain failed. * @throws IllegalStateException if enabling or disabling the firewall chain failed.
* @hide * @hide
*/ */
@@ -5926,7 +5927,6 @@ public class ConnectivityManager {
* @param chain target chain. * @param chain target chain.
* @return {@code true} if chain is enabled, {@code false} if chain is disabled. * @return {@code true} if chain is enabled, {@code false} if chain is disabled.
* @throws UnsupportedOperationException if called on pre-T devices. * @throws UnsupportedOperationException if called on pre-T devices.
* @throws IllegalArgumentException if {@code chain} is a invalid value.
* @throws ServiceSpecificException in case of failure, with an error code indicating the * @throws ServiceSpecificException in case of failure, with an error code indicating the
* cause of the failure. * cause of the failure.
* @hide * @hide

View File

@@ -24,6 +24,7 @@ import static android.net.ConnectivityManager.FIREWALL_CHAIN_OEM_DENY_3;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE; import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED; import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY; import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY;
import static android.system.OsConstants.EINVAL;
import static android.system.OsConstants.ENOENT; import static android.system.OsConstants.ENOENT;
import static android.system.OsConstants.EOPNOTSUPP; import static android.system.OsConstants.EOPNOTSUPP;
@@ -55,6 +56,11 @@ public class BpfNetMaps {
private static final boolean USE_NETD = !SdkLevel.isAtLeastT(); private static final boolean USE_NETD = !SdkLevel.isAtLeastT();
private static boolean sInitialized = false; private static boolean sInitialized = false;
// Lock for sConfigurationMap entry for UID_RULES_CONFIGURATION_KEY.
// This entry is not accessed by others.
// BpfNetMaps acquires this lock while sequence of read, modify, and write.
private static final Object sUidRulesConfigBpfMapLock = new Object();
private static final String CONFIGURATION_MAP_PATH = private static final String CONFIGURATION_MAP_PATH =
"/sys/fs/bpf/netd_shared/map_netd_configuration_map"; "/sys/fs/bpf/netd_shared/map_netd_configuration_map";
private static final U32 UID_RULES_CONFIGURATION_KEY = new U32(0); private static final U32 UID_RULES_CONFIGURATION_KEY = new U32(0);
@@ -152,7 +158,7 @@ public class BpfNetMaps {
public long getMatchByFirewallChain(final int chain) { public long getMatchByFirewallChain(final int chain) {
final long match = FIREWALL_CHAIN_TO_MATCH.get(chain, NO_MATCH); final long match = FIREWALL_CHAIN_TO_MATCH.get(chain, NO_MATCH);
if (match == NO_MATCH) { if (match == NO_MATCH) {
throw new IllegalArgumentException("Invalid firewall chain: " + chain); throw new ServiceSpecificException(EINVAL, "Invalid firewall chain: " + chain);
} }
return match; return match;
} }
@@ -163,6 +169,12 @@ public class BpfNetMaps {
} }
} }
private void throwIfUseNetd(final String msg) {
if (USE_NETD) {
throw new UnsupportedOperationException(msg);
}
}
/** /**
* Add naughty app bandwidth rule for specific app * Add naughty app bandwidth rule for specific app
* *
@@ -216,12 +228,29 @@ public class BpfNetMaps {
* *
* @param childChain target chain to enable * @param childChain target chain to enable
* @param enable whether to enable or disable child chain. * @param enable whether to enable or disable child chain.
* @throws UnsupportedOperationException if called on pre-T devices.
* @throws ServiceSpecificException in case of failure, with an error code indicating the * @throws ServiceSpecificException in case of failure, with an error code indicating the
* cause of the failure. * cause of the failure.
*/ */
public void setChildChain(final int childChain, final boolean enable) { public void setChildChain(final int childChain, final boolean enable) {
final int err = native_setChildChain(childChain, enable); throwIfUseNetd("setChildChain is not available on pre-T devices");
maybeThrow(err, "Unable to set child chain");
final long match = getMatchByFirewallChain(childChain);
try {
synchronized (sUidRulesConfigBpfMapLock) {
final U32 config = sConfigurationMap.getValue(UID_RULES_CONFIGURATION_KEY);
if (config == null) {
throw new ServiceSpecificException(ENOENT,
"Unable to get firewall chain status: sConfigurationMap does not have"
+ " entry for UID_RULES_CONFIGURATION_KEY");
}
final long newConfig = enable ? (config.val | match) : (config.val & (~match));
sConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, new U32(newConfig));
}
} catch (ErrnoException e) {
throw new ServiceSpecificException(e.errno,
"Unable to set child chain: " + Os.strerror(e.errno));
}
} }
/** /**
@@ -230,15 +259,11 @@ public class BpfNetMaps {
* @param childChain target chain * @param childChain target chain
* @return {@code true} if chain is enabled, {@code false} if chain is not enabled. * @return {@code true} if chain is enabled, {@code false} if chain is not enabled.
* @throws UnsupportedOperationException if called on pre-T devices. * @throws UnsupportedOperationException if called on pre-T devices.
* @throws IllegalArgumentException if {@code childChain} is a invalid value.
* @throws ServiceSpecificException in case of failure, with an error code indicating the * @throws ServiceSpecificException in case of failure, with an error code indicating the
* cause of the failure. * cause of the failure.
*/ */
public boolean getChainEnabled(final int childChain) { public boolean getChainEnabled(final int childChain) {
if (USE_NETD) { throwIfUseNetd("getChainEnabled is not available on pre-T devices");
throw new UnsupportedOperationException("getChainEnabled is not available on pre-T"
+ " devices");
}
final long match = getMatchByFirewallChain(childChain); final long match = getMatchByFirewallChain(childChain);
try { try {

View File

@@ -26,6 +26,7 @@ import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY; import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY;
import static android.net.INetd.PERMISSION_INTERNET; import static android.net.INetd.PERMISSION_INTERNET;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@@ -168,7 +169,7 @@ public final class BpfNetMapsTest {
@Test @Test
@IgnoreUpTo(Build.VERSION_CODES.S_V2) @IgnoreUpTo(Build.VERSION_CODES.S_V2)
public void testGetChainEnabledInvalidChain() { public void testGetChainEnabledInvalidChain() {
final Class<IllegalArgumentException> expected = IllegalArgumentException.class; final Class<ServiceSpecificException> expected = ServiceSpecificException.class;
assertThrows(expected, () -> mBpfNetMaps.getChainEnabled(-1 /* childChain */)); assertThrows(expected, () -> mBpfNetMaps.getChainEnabled(-1 /* childChain */));
assertThrows(expected, () -> mBpfNetMaps.getChainEnabled(1000 /* childChain */)); assertThrows(expected, () -> mBpfNetMaps.getChainEnabled(1000 /* childChain */));
} }
@@ -187,4 +188,81 @@ public final class BpfNetMapsTest {
assertThrows(UnsupportedOperationException.class, assertThrows(UnsupportedOperationException.class,
() -> mBpfNetMaps.getChainEnabled(FIREWALL_CHAIN_DOZABLE)); () -> mBpfNetMaps.getChainEnabled(FIREWALL_CHAIN_DOZABLE));
} }
private void doTestSetChildChain(final List<Integer> testChains) throws Exception {
long expectedMatch = 0;
for (final int chain: testChains) {
expectedMatch |= mBpfNetMaps.getMatchByFirewallChain(chain);
}
assertEquals(0, sConfigurationMap.getValue(UID_RULES_CONFIGURATION_KEY).val);
for (final int chain: testChains) {
mBpfNetMaps.setChildChain(chain, true /* enable */);
}
assertEquals(expectedMatch, sConfigurationMap.getValue(UID_RULES_CONFIGURATION_KEY).val);
for (final int chain: testChains) {
mBpfNetMaps.setChildChain(chain, false /* enable */);
}
assertEquals(0, sConfigurationMap.getValue(UID_RULES_CONFIGURATION_KEY).val);
}
private void doTestSetChildChain(final int testChain) throws Exception {
doTestSetChildChain(List.of(testChain));
}
@Test
@IgnoreUpTo(Build.VERSION_CODES.S_V2)
public void testSetChildChain() throws Exception {
sConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, new U32(0));
doTestSetChildChain(FIREWALL_CHAIN_DOZABLE);
doTestSetChildChain(FIREWALL_CHAIN_STANDBY);
doTestSetChildChain(FIREWALL_CHAIN_POWERSAVE);
doTestSetChildChain(FIREWALL_CHAIN_RESTRICTED);
doTestSetChildChain(FIREWALL_CHAIN_LOW_POWER_STANDBY);
doTestSetChildChain(FIREWALL_CHAIN_OEM_DENY_1);
doTestSetChildChain(FIREWALL_CHAIN_OEM_DENY_2);
doTestSetChildChain(FIREWALL_CHAIN_OEM_DENY_3);
}
@Test
@IgnoreUpTo(Build.VERSION_CODES.S_V2)
public void testSetChildChainMultipleChain() throws Exception {
sConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, new U32(0));
doTestSetChildChain(List.of(
FIREWALL_CHAIN_DOZABLE,
FIREWALL_CHAIN_STANDBY));
doTestSetChildChain(List.of(
FIREWALL_CHAIN_DOZABLE,
FIREWALL_CHAIN_STANDBY,
FIREWALL_CHAIN_POWERSAVE,
FIREWALL_CHAIN_RESTRICTED));
doTestSetChildChain(FIREWALL_CHAINS);
}
@Test
@IgnoreUpTo(Build.VERSION_CODES.S_V2)
public void testSetChildChainInvalidChain() {
final Class<ServiceSpecificException> expected = ServiceSpecificException.class;
assertThrows(expected,
() -> mBpfNetMaps.setChildChain(-1 /* childChain */, true /* enable */));
assertThrows(expected,
() -> mBpfNetMaps.setChildChain(1000 /* childChain */, true /* enable */));
}
@Test
@IgnoreUpTo(Build.VERSION_CODES.S_V2)
public void testSetChildChainMissingConfiguration() {
// sConfigurationMap does not have entry for UID_RULES_CONFIGURATION_KEY
assertThrows(ServiceSpecificException.class,
() -> mBpfNetMaps.setChildChain(FIREWALL_CHAIN_DOZABLE, true /* enable */));
}
@Test
@IgnoreAfter(Build.VERSION_CODES.S_V2)
public void testSetChildChainBeforeT() {
assertThrows(UnsupportedOperationException.class,
() -> mBpfNetMaps.setChildChain(FIREWALL_CHAIN_DOZABLE, true /* enable */));
}
} }