Snap for 7379850 from bcfe90a6b1f1e1cb56bfbee5d8d77513232e5483 to sc-v2-release
Change-Id: If4ecb7906b48f92d1d53502f674d9ef387080b73
diff --git a/res/values/config.xml b/res/values/config.xml
index d6a11ab..805ca04 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -105,4 +105,20 @@
increased until reaching the config_max_retry_timer. -->
<integer name="config_evaluating_bandwidth_min_retry_timer_ms"></integer>
<integer name="config_evaluating_bandwidth_max_retry_timer_ms"></integer>
+
+ <!-- Whether the APF Filter in the device should filter out IEEE 802.3 Frames
+ Those frames are identified by the field Eth-type having values
+ less than 0x600 -->
+ <bool name="config_apfDrop802_3Frames">true</bool>
+
+ <!-- An array of Denylisted EtherType, packets with EtherTypes within this array
+ will be dropped
+ TODO: need to put proper values, these are for testing purposes only -->
+ <integer-array name="config_apfEthTypeDenyList">
+ <item>0x88A2</item>
+ <item>0x88A4</item>
+ <item>0x88B8</item>
+ <item>0x88CD</item>
+ <item>0x88E3</item>
+ </integer-array>
</resources>
diff --git a/res/values/overlayable.xml b/res/values/overlayable.xml
index b2967b9..bfb450e 100644
--- a/res/values/overlayable.xml
+++ b/res/values/overlayable.xml
@@ -77,6 +77,13 @@
<item type="integer" name="config_evaluating_bandwidth_timeout_ms"/>
<item type="integer" name="config_evaluating_bandwidth_min_retry_timer_ms"/>
<item type="integer" name="config_evaluating_bandwidth_max_retry_timer_ms"/>
+
+ <!-- Whether the APF Filter in the device should filter out IEEE 802.3 Frames
+ Those frames are identified by the field Eth-type having values less than 0x600 -->
+ <item type="bool" name="config_apfDrop802_3Frames"/>
+ <!-- An array of Denylisted EtherType, packets with EtherTypes within this array
+ will be dropped -->
+ <item type="array" name="config_apfEthTypeDenyList"/>
</policy>
</overlayable>
</resources>
diff --git a/src/android/net/ip/IpClient.java b/src/android/net/ip/IpClient.java
index f7c728b..a57e99d 100644
--- a/src/android/net/ip/IpClient.java
+++ b/src/android/net/ip/IpClient.java
@@ -18,6 +18,7 @@
import static android.net.RouteInfo.RTN_UNICAST;
import static android.net.dhcp.DhcpResultsParcelableUtil.toStableParcelable;
+import static android.net.util.NetworkStackUtils.IPCLIENT_DISABLE_ACCEPT_RA_VERSION;
import static android.net.util.NetworkStackUtils.IPCLIENT_GARP_NA_ROAMING_VERSION;
import static android.net.util.NetworkStackUtils.IPCLIENT_GRATUITOUS_NA_VERSION;
import static android.net.util.SocketUtils.makePacketSocketAddress;
@@ -35,6 +36,7 @@
import static com.android.server.util.PermissionUtil.enforceNetworkStackCallingPermission;
import android.content.Context;
+import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.DhcpResults;
import android.net.INetd;
@@ -93,6 +95,7 @@
import com.android.internal.util.StateMachine;
import com.android.internal.util.WakeupMessage;
import com.android.net.module.util.DeviceConfigUtils;
+import com.android.networkstack.R;
import com.android.networkstack.apishim.NetworkInformationShimImpl;
import com.android.networkstack.apishim.SocketUtilsShimImpl;
import com.android.networkstack.apishim.common.NetworkInformationShim;
@@ -527,7 +530,7 @@
private boolean mMulticastFiltering;
private long mStartTimeMillis;
private MacAddress mCurrentBssid;
- private boolean mHasDisabledIPv6OnProvLoss;
+ private boolean mHasDisabledIpv6OrAcceptRaOnProvLoss;
/**
* Reading the snapshot is an asynchronous operation initiated by invoking
@@ -604,6 +607,16 @@
return DeviceConfigUtils.isFeatureEnabled(context, NAMESPACE_CONNECTIVITY, name,
defaultEnabled);
}
+
+ /**
+ * Create an APF filter if apfCapabilities indicates support for packet filtering using
+ * APF programs.
+ * @see ApfFilter#maybeCreate
+ */
+ public ApfFilter maybeCreateApfFilter(Context context, ApfFilter.ApfConfiguration config,
+ InterfaceParams ifParams, IpClientCallbacksWrapper cb) {
+ return ApfFilter.maybeCreate(context, config, ifParams, cb);
+ }
}
public IpClient(Context context, String ifName, IIpClientCallbacks callback,
@@ -881,6 +894,11 @@
}
}
+ private boolean shouldDisableAcceptRaOnProvisioningLoss() {
+ return mDependencies.isFeatureEnabled(mContext, IPCLIENT_DISABLE_ACCEPT_RA_VERSION,
+ true /* defaultEnabled */);
+ }
+
@Override
protected void onQuitting() {
mCallback.onQuit();
@@ -1198,6 +1216,21 @@
return config.isProvisionedBy(lp.getLinkAddresses(), lp.getRoutes());
}
+ private void setIpv6AcceptRa(int acceptRa) {
+ try {
+ mNetd.setProcSysNet(INetd.IPV6, INetd.CONF, mInterfaceParams.name, "accept_ra",
+ Integer.toString(acceptRa));
+ } catch (Exception e) {
+ Log.e(mTag, "Failed to set accept_ra to " + acceptRa);
+ }
+ }
+
+ private void restartIpv6WithAcceptRaDisabled() {
+ mInterfaceCtrl.disableIPv6();
+ setIpv6AcceptRa(0 /* accept_ra */);
+ startIPv6();
+ }
+
// TODO: Investigate folding all this into the existing static function
// LinkProperties.compareProvisioning() or some other single function that
// takes two LinkProperties objects and returns a ProvisioningChange
@@ -1247,7 +1280,7 @@
// Note that we can still be disconnected by IpReachabilityMonitor
// if the IPv6 default gateway (but not the IPv6 DNS servers; see
// accompanying code in IpReachabilityMonitor) is unreachable.
- final boolean ignoreIPv6ProvisioningLoss = mHasDisabledIPv6OnProvLoss
+ final boolean ignoreIPv6ProvisioningLoss = mHasDisabledIpv6OrAcceptRaOnProvLoss
|| (mConfiguration != null && mConfiguration.mUsingMultinetworkPolicyTracker
&& !mCm.shouldAvoidBadWifi());
@@ -1275,18 +1308,31 @@
if (oldLp.hasGlobalIpv6Address() && (lostIPv6Router && !ignoreIPv6ProvisioningLoss)) {
// Although link properties have lost IPv6 default route in this case, if IPv4 is still
// working with appropriate routes and DNS servers, we can keep the current connection
- // without disconnecting from the network, just disable IPv6 on that given network until
- // to the next provisioning. Disabling IPv6 will result in all IPv6 connectivity torn
- // down and all IPv6 sockets being closed, the non-routable IPv6 DNS servers will be
- // stripped out, so applications will be able to reconnect immediately over IPv4. See
- // b/131781810.
+ // without disconnecting from the network, just disable IPv6 or accept_ra parameter on
+ // that given network until to the next provisioning.
+ //
+ // Disabling IPv6 stack will result in all IPv6 connectivity torn down and all IPv6
+ // sockets being closed, the non-routable IPv6 DNS servers will be stripped out, so
+ // applications will be able to reconnect immediately over IPv4. See b/131781810.
+ //
+ // Sometimes disabling IPv6 stack might introduce other issues(see b/179222860),
+ // instead disabling accept_ra will result in only IPv4 provisioning and IPv6 link
+ // local address left on the interface, so applications will be able to reconnect
+ // immediately over IPv4 and keep IPv6 link-local capable.
if (newLp.isIpv4Provisioned()) {
- mInterfaceCtrl.disableIPv6();
+ if (shouldDisableAcceptRaOnProvisioningLoss()) {
+ restartIpv6WithAcceptRaDisabled();
+ } else {
+ mInterfaceCtrl.disableIPv6();
+ }
mNetworkQuirkMetrics.setEvent(NetworkQuirkEvent.QE_IPV6_PROVISIONING_ROUTER_LOST);
mNetworkQuirkMetrics.statsWrite();
- mHasDisabledIPv6OnProvLoss = true;
+ mHasDisabledIpv6OrAcceptRaOnProvLoss = true;
delta = PROV_CHANGE_STILL_PROVISIONED;
- mLog.log("Disable IPv6 stack completely when the default router has gone");
+ mLog.log(shouldDisableAcceptRaOnProvisioningLoss()
+ ? "Disabled accept_ra parameter "
+ : "Disabled IPv6 stack completely "
+ + "when the IPv6 default router has gone");
} else {
delta = PROV_CHANGE_LOST_PROVISIONING;
}
@@ -1839,7 +1885,8 @@
@Override
public void enter() {
stopAllIP();
- mHasDisabledIPv6OnProvLoss = false;
+ setIpv6AcceptRa(2 /* accept_ra */);
+ mHasDisabledIpv6OrAcceptRaOnProvLoss = false;
mGratuitousNaTargetAddresses.clear();
mLinkObserver.clearInterfaceParams();
@@ -2160,10 +2207,19 @@
apfConfig.apfCapabilities = mConfiguration.mApfCapabilities;
apfConfig.multicastFilter = mMulticastFiltering;
// Get the Configuration for ApfFilter from Context
- apfConfig.ieee802_3Filter = ApfCapabilities.getApfDrop8023Frames();
- apfConfig.ethTypeBlackList = ApfCapabilities.getApfEtherTypeBlackList();
+ // Resource settings were moved from ApfCapabilities APIs to NetworkStack resources in S
+ if (ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.R)) {
+ final Resources res = mContext.getResources();
+ apfConfig.ieee802_3Filter = res.getBoolean(R.bool.config_apfDrop802_3Frames);
+ apfConfig.ethTypeBlackList = res.getIntArray(R.array.config_apfEthTypeDenyList);
+ } else {
+ apfConfig.ieee802_3Filter = ApfCapabilities.getApfDrop8023Frames();
+ apfConfig.ethTypeBlackList = ApfCapabilities.getApfEtherTypeBlackList();
+ }
+
apfConfig.minRdnssLifetimeSec = mMinRdnssLifetimeSec;
- mApfFilter = ApfFilter.maybeCreate(mContext, apfConfig, mInterfaceParams, mCallback);
+ mApfFilter = mDependencies.maybeCreateApfFilter(mContext, apfConfig, mInterfaceParams,
+ mCallback);
// TODO: investigate the effects of any multicast filtering racing/interfering with the
// rest of this IP configuration startup.
if (mApfFilter == null) {
diff --git a/src/android/net/util/NetworkStackUtils.java b/src/android/net/util/NetworkStackUtils.java
index 098ce2e..81d0c08 100755
--- a/src/android/net/util/NetworkStackUtils.java
+++ b/src/android/net/util/NetworkStackUtils.java
@@ -249,6 +249,12 @@
public static final String IPCLIENT_GARP_NA_ROAMING_VERSION =
"ipclient_garp_na_roaming_version";
+ /**
+ * Experiment flag to disable accept_ra parameter when IPv6 provisioning loss happens due to
+ * the default route has gone.
+ */
+ public static final String IPCLIENT_DISABLE_ACCEPT_RA_VERSION = "ipclient_disable_accept_ra";
+
static {
System.loadLibrary("networkstackutilsjni");
}
diff --git a/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java b/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java
index f3419a3..c121b3d 100644
--- a/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java
+++ b/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java
@@ -2404,12 +2404,15 @@
reset(mCb);
}
- private void doDualStackProvisioning() throws Exception {
+ private void doDualStackProvisioning(boolean shouldDisableAcceptRa) throws Exception {
when(mCm.shouldAvoidBadWifi()).thenReturn(true);
final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
.withoutIpReachabilityMonitor()
.build();
+
+ setFeatureEnabled(NetworkStackUtils.IPCLIENT_DISABLE_ACCEPT_RA_VERSION,
+ shouldDisableAcceptRa);
// Enable rapid commit to accelerate DHCP handshake to shorten test duration,
// not strictly necessary.
setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, true /* isRapidCommitEnabled */,
@@ -2419,9 +2422,9 @@
performDualStackProvisioning();
}
- @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
- public void testIgnoreIpv6ProvisioningLoss() throws Exception {
- doDualStackProvisioning();
+ @Test @SignatureRequiredTest(reason = "signature perms are required due to mocked callabck")
+ public void testIgnoreIpv6ProvisioningLoss_disableIPv6Stack() throws Exception {
+ doDualStackProvisioning(false /* shouldDisableAcceptRa */);
final CompletableFuture<LinkProperties> lpFuture = new CompletableFuture<>();
@@ -2450,9 +2453,45 @@
(long) NetworkQuirkEvent.QE_IPV6_PROVISIONING_ROUTER_LOST.ordinal());
}
+ @Test @SignatureRequiredTest(reason = "signature perms are required due to mocked callabck")
+ public void testIgnoreIpv6ProvisioningLoss_disableAcceptRa() throws Exception {
+ doDualStackProvisioning(true /* shouldDisableAcceptRa */);
+
+ final CompletableFuture<LinkProperties> lpFuture = new CompletableFuture<>();
+
+ // Send RA with 0-lifetime and wait until all global IPv6 addresses, IPv6-related default
+ // route and DNS servers have been removed, then verify if there is IPv4-only, IPv6 link
+ // local address and route to fe80::/64 info left in the LinkProperties.
+ sendRouterAdvertisementWithZeroLifetime();
+ verify(mCb, timeout(TEST_TIMEOUT_MS).atLeastOnce()).onLinkPropertiesChange(
+ argThat(x -> {
+ // Only IPv4 provisioned and IPv6 link-local address
+ final boolean isIPv6LinkLocalAndIPv4OnlyProvisioned =
+ (x.getLinkAddresses().size() == 2
+ && x.getDnsServers().size() == 1
+ && x.getAddresses().get(0) instanceof Inet4Address
+ && x.getDnsServers().get(0) instanceof Inet4Address);
+
+ if (!isIPv6LinkLocalAndIPv4OnlyProvisioned) return false;
+ lpFuture.complete(x);
+ return true;
+ }));
+ final LinkProperties lp = lpFuture.get(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertNotNull(lp);
+ assertEquals(lp.getAddresses().get(0), CLIENT_ADDR);
+ assertEquals(lp.getDnsServers().get(0), SERVER_ADDR);
+ assertTrue(lp.getAddresses().get(1).isLinkLocalAddress());
+
+ reset(mCb);
+
+ // Send an RA to verify that global IPv6 addresses won't be configured on the interface.
+ sendBasicRouterAdvertisement(false /* waitForRs */);
+ verify(mCb, timeout(TEST_TIMEOUT_MS).times(0)).onLinkPropertiesChange(any());
+ }
+
@Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
public void testDualStackProvisioning() throws Exception {
- doDualStackProvisioning();
+ doDualStackProvisioning(false /* shouldDisableAcceptRa */);
verify(mCb, never()).onProvisioningFailure(any());
}
@@ -2672,7 +2711,7 @@
public void testNoFdLeaks() throws Exception {
// Shut down and restart IpClient once to ensure that any fds that are opened the first
// time it runs do not cause the test to fail.
- doDualStackProvisioning();
+ doDualStackProvisioning(false /* shouldDisableAcceptRa */);
shutdownAndRecreateIpClient();
// Unfortunately we cannot use a large number of iterations as it would make the test run
@@ -2680,7 +2719,7 @@
final int iterations = 10;
final int before = getNumOpenFds();
for (int i = 0; i < iterations; i++) {
- doDualStackProvisioning();
+ doDualStackProvisioning(false /* shouldDisableAcceptRa */);
shutdownAndRecreateIpClient();
// The last time this loop runs, mIpc will be shut down in tearDown.
}
diff --git a/tests/unit/src/android/net/ip/IpClientTest.java b/tests/unit/src/android/net/ip/IpClientTest.java
index e991ea7..7260332 100644
--- a/tests/unit/src/android/net/ip/IpClientTest.java
+++ b/tests/unit/src/android/net/ip/IpClientTest.java
@@ -18,12 +18,14 @@
import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -48,22 +50,30 @@
import android.net.MacAddress;
import android.net.NetworkStackIpMemoryStore;
import android.net.RouteInfo;
+import android.net.apf.ApfCapabilities;
+import android.net.apf.ApfFilter.ApfConfiguration;
import android.net.ipmemorystore.NetworkAttributes;
import android.net.metrics.IpConnectivityLog;
import android.net.shared.InitialConfiguration;
import android.net.shared.ProvisioningConfiguration;
import android.net.util.InterfaceParams;
+import android.os.Build;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.networkstack.R;
import com.android.server.NetworkObserver;
import com.android.server.NetworkObserverRegistry;
import com.android.server.NetworkStackService;
import com.android.server.connectivity.ipmemorystore.IpMemoryStoreService;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.HandlerUtils;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -85,6 +95,9 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class IpClientTest {
+ @Rule
+ public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
+
private static final String VALID = "VALID";
private static final String INVALID = "INVALID";
private static final String TEST_IFNAME = "test_wlan0";
@@ -631,6 +644,71 @@
return out;
}
+ private ApfConfiguration verifyApfFilterCreatedOnStart(IpClient ipc) {
+ ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
+ .withoutIPv4()
+ .withoutIpReachabilityMonitor()
+ .withInitialConfiguration(
+ conf(links(TEST_LOCAL_ADDRESSES), prefixes(TEST_PREFIXES), ips()))
+ .withApfCapabilities(new ApfCapabilities(
+ 4 /* version */, 4096 /* maxProgramSize */, 4 /* format */))
+ .build();
+
+ ipc.startProvisioning(config);
+ final ArgumentCaptor<ApfConfiguration> configCaptor = ArgumentCaptor.forClass(
+ ApfConfiguration.class);
+ verify(mDependencies, timeout(TEST_TIMEOUT_MS)).maybeCreateApfFilter(
+ any(), configCaptor.capture(), any(), any());
+
+ return configCaptor.getValue();
+ }
+
+ @Test @IgnoreAfter(Build.VERSION_CODES.R)
+ public void testApfConfiguration_R() throws Exception {
+ final IpClient ipc = makeIpClient(TEST_IFNAME);
+ final ApfConfiguration config = verifyApfFilterCreatedOnStart(ipc);
+
+ assertEquals(ApfCapabilities.getApfDrop8023Frames(), config.ieee802_3Filter);
+ assertArrayEquals(ApfCapabilities.getApfEtherTypeBlackList(), config.ethTypeBlackList);
+
+ verify(mResources, never()).getBoolean(R.bool.config_apfDrop802_3Frames);
+ verify(mResources, never()).getIntArray(R.array.config_apfEthTypeDenyList);
+
+ verifyShutdown(ipc);
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ public void testApfConfiguration() throws Exception {
+ doReturn(true).when(mResources).getBoolean(R.bool.config_apfDrop802_3Frames);
+ final int[] ethTypeDenyList = new int[] { 0x88A2, 0x88A4 };
+ doReturn(ethTypeDenyList).when(mResources).getIntArray(
+ R.array.config_apfEthTypeDenyList);
+
+ final IpClient ipc = makeIpClient(TEST_IFNAME);
+ final ApfConfiguration config = verifyApfFilterCreatedOnStart(ipc);
+
+ assertTrue(config.ieee802_3Filter);
+ assertArrayEquals(ethTypeDenyList, config.ethTypeBlackList);
+
+ verifyShutdown(ipc);
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ public void testApfConfiguration_NoApfDrop8023Frames() throws Exception {
+ doReturn(false).when(mResources).getBoolean(R.bool.config_apfDrop802_3Frames);
+ final int[] ethTypeDenyList = new int[] { 0x88A3, 0x88A5 };
+ doReturn(ethTypeDenyList).when(mResources).getIntArray(
+ R.array.config_apfEthTypeDenyList);
+
+ final IpClient ipc = makeIpClient(TEST_IFNAME);
+ final ApfConfiguration config = verifyApfFilterCreatedOnStart(ipc);
+
+ assertFalse(config.ieee802_3Filter);
+ assertArrayEquals(ethTypeDenyList, config.ethTypeBlackList);
+
+ verifyShutdown(ipc);
+ }
+
interface Fn<A,B> {
B call(A a) throws Exception;
}