Support strict mode private DNS on VPNs that provide Internet.
Currently, strict mode private DNS does not work on VPNs because NetworkMonitor does not validate VPNs. When a VPN connects, it immediately transitions to ValidatedState, skipping private DNS hostname resolution. This change makes NetworkMonitor perform private DNS hostname resolution and evaluation even on VPNs. In order to ensure that the system always immediately switches to the VPN as soon as it connects, remove the unvalidated penalty for VPN networks. This ensures that the VPN score is always 101 and the VPN always outscores other networks as soon as it connects. Previously, it would only outscore other networks when no-op validation completed. Bug: 122652057 Test: atest FrameworksNetTests NetworkStackTests Test: manually ran a VPN with private DNS in strict mode atest android.net.cts.ConnectivityManagerTest com.android.cts.net.HostsideVpnTests Change-Id: Iaa78a7edcf23755c89d7b354edbc28d37d74d891 Merged-In: Iaa78a7edcf23755c89d7b354edbc28d37d74d891 (cherry picked from commit 414b8c8b1ce8ae2ad6ef95c1ffba19062077d3e6)
This commit is contained in:
committed by
Lorenzo Colitti
parent
015598ea52
commit
9ff61e4948
@@ -40,7 +40,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
|
|||||||
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
|
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
|
||||||
import static android.net.NetworkPolicyManager.RULE_NONE;
|
import static android.net.NetworkPolicyManager.RULE_NONE;
|
||||||
import static android.net.NetworkPolicyManager.uidRulesToString;
|
import static android.net.NetworkPolicyManager.uidRulesToString;
|
||||||
import static android.net.shared.NetworkMonitorUtils.isValidationRequired;
|
import static android.net.shared.NetworkMonitorUtils.isPrivateDnsValidationRequired;
|
||||||
import static android.os.Process.INVALID_UID;
|
import static android.os.Process.INVALID_UID;
|
||||||
import static android.system.OsConstants.IPPROTO_TCP;
|
import static android.system.OsConstants.IPPROTO_TCP;
|
||||||
import static android.system.OsConstants.IPPROTO_UDP;
|
import static android.system.OsConstants.IPPROTO_UDP;
|
||||||
@@ -2826,8 +2826,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean networkRequiresValidation(NetworkAgentInfo nai) {
|
private boolean networkRequiresPrivateDnsValidation(NetworkAgentInfo nai) {
|
||||||
return isValidationRequired(nai.networkCapabilities);
|
return isPrivateDnsValidationRequired(nai.networkCapabilities);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleFreshlyValidatedNetwork(NetworkAgentInfo nai) {
|
private void handleFreshlyValidatedNetwork(NetworkAgentInfo nai) {
|
||||||
@@ -2845,7 +2845,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
|
|
||||||
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
|
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
|
||||||
handlePerNetworkPrivateDnsConfig(nai, cfg);
|
handlePerNetworkPrivateDnsConfig(nai, cfg);
|
||||||
if (networkRequiresValidation(nai)) {
|
if (networkRequiresPrivateDnsValidation(nai)) {
|
||||||
handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
|
handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2854,7 +2854,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
private void handlePerNetworkPrivateDnsConfig(NetworkAgentInfo nai, PrivateDnsConfig cfg) {
|
private void handlePerNetworkPrivateDnsConfig(NetworkAgentInfo nai, PrivateDnsConfig cfg) {
|
||||||
// Private DNS only ever applies to networks that might provide
|
// Private DNS only ever applies to networks that might provide
|
||||||
// Internet access and therefore also require validation.
|
// Internet access and therefore also require validation.
|
||||||
if (!networkRequiresValidation(nai)) return;
|
if (!networkRequiresPrivateDnsValidation(nai)) return;
|
||||||
|
|
||||||
// Notify the NetworkAgentInfo/NetworkMonitor in case NetworkMonitor needs to cancel or
|
// Notify the NetworkAgentInfo/NetworkMonitor in case NetworkMonitor needs to cancel or
|
||||||
// schedule DNS resolutions. If a DNS resolution is required the
|
// schedule DNS resolutions. If a DNS resolution is required the
|
||||||
|
|||||||
@@ -483,11 +483,11 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
|
|||||||
// down an explicitly selected network before the user gets a chance to prefer it when
|
// down an explicitly selected network before the user gets a chance to prefer it when
|
||||||
// a higher-scoring network (e.g., Ethernet) is available.
|
// a higher-scoring network (e.g., Ethernet) is available.
|
||||||
if (networkMisc.explicitlySelected && (networkMisc.acceptUnvalidated || pretendValidated)) {
|
if (networkMisc.explicitlySelected && (networkMisc.acceptUnvalidated || pretendValidated)) {
|
||||||
return ConnectivityConstants.MAXIMUM_NETWORK_SCORE;
|
return ConnectivityConstants.EXPLICITLY_SELECTED_NETWORK_SCORE;
|
||||||
}
|
}
|
||||||
|
|
||||||
int score = currentScore;
|
int score = currentScore;
|
||||||
if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty()) {
|
if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty() && !isVPN()) {
|
||||||
score -= ConnectivityConstants.UNVALIDATED_SCORE_PENALTY;
|
score -= ConnectivityConstants.UNVALIDATED_SCORE_PENALTY;
|
||||||
}
|
}
|
||||||
if (score < 0) score = 0;
|
if (score < 0) score = 0;
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import static android.net.ConnectivityManager.TYPE_MOBILE;
|
|||||||
import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
|
import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
|
||||||
import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
|
import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
|
||||||
import static android.net.ConnectivityManager.TYPE_NONE;
|
import static android.net.ConnectivityManager.TYPE_NONE;
|
||||||
|
import static android.net.ConnectivityManager.TYPE_VPN;
|
||||||
import static android.net.ConnectivityManager.TYPE_WIFI;
|
import static android.net.ConnectivityManager.TYPE_WIFI;
|
||||||
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID;
|
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID;
|
||||||
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY;
|
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY;
|
||||||
@@ -489,7 +490,7 @@ public class ConnectivityServiceTest {
|
|||||||
|
|
||||||
MockNetworkAgent(int transport, LinkProperties linkProperties) {
|
MockNetworkAgent(int transport, LinkProperties linkProperties) {
|
||||||
final int type = transportToLegacyType(transport);
|
final int type = transportToLegacyType(transport);
|
||||||
final String typeName = ConnectivityManager.getNetworkTypeName(transport);
|
final String typeName = ConnectivityManager.getNetworkTypeName(type);
|
||||||
mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
|
mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
|
||||||
mNetworkCapabilities = new NetworkCapabilities();
|
mNetworkCapabilities = new NetworkCapabilities();
|
||||||
mNetworkCapabilities.addTransportType(transport);
|
mNetworkCapabilities.addTransportType(transport);
|
||||||
@@ -619,6 +620,10 @@ public class ConnectivityServiceTest {
|
|||||||
mNetworkAgent.sendNetworkScore(mScore);
|
mNetworkAgent.sendNetworkScore(mScore);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getScore() {
|
||||||
|
return mScore;
|
||||||
|
}
|
||||||
|
|
||||||
public void explicitlySelected(boolean acceptUnvalidated) {
|
public void explicitlySelected(boolean acceptUnvalidated) {
|
||||||
mNetworkAgent.explicitlySelected(acceptUnvalidated);
|
mNetworkAgent.explicitlySelected(acceptUnvalidated);
|
||||||
}
|
}
|
||||||
@@ -1330,6 +1335,8 @@ public class ConnectivityServiceTest {
|
|||||||
return TYPE_WIFI;
|
return TYPE_WIFI;
|
||||||
case TRANSPORT_CELLULAR:
|
case TRANSPORT_CELLULAR:
|
||||||
return TYPE_MOBILE;
|
return TYPE_MOBILE;
|
||||||
|
case TRANSPORT_VPN:
|
||||||
|
return TYPE_VPN;
|
||||||
default:
|
default:
|
||||||
return TYPE_NONE;
|
return TYPE_NONE;
|
||||||
}
|
}
|
||||||
@@ -5367,6 +5374,58 @@ public class ConnectivityServiceTest {
|
|||||||
mCm.unregisterNetworkCallback(defaultCallback);
|
mCm.unregisterNetworkCallback(defaultCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testVpnUnvalidated() throws Exception {
|
||||||
|
final TestNetworkCallback callback = new TestNetworkCallback();
|
||||||
|
mCm.registerDefaultNetworkCallback(callback);
|
||||||
|
|
||||||
|
// Bring up Ethernet.
|
||||||
|
mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET);
|
||||||
|
mEthernetNetworkAgent.connect(true);
|
||||||
|
callback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent);
|
||||||
|
callback.assertNoCallback();
|
||||||
|
|
||||||
|
// Bring up a VPN that has the INTERNET capability, initially unvalidated.
|
||||||
|
final int uid = Process.myUid();
|
||||||
|
final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
|
||||||
|
final ArraySet<UidRange> ranges = new ArraySet<>();
|
||||||
|
ranges.add(new UidRange(uid, uid));
|
||||||
|
mMockVpn.setNetworkAgent(vpnNetworkAgent);
|
||||||
|
mMockVpn.setUids(ranges);
|
||||||
|
vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */);
|
||||||
|
mMockVpn.connect();
|
||||||
|
|
||||||
|
// Even though the VPN is unvalidated, it becomes the default network for our app.
|
||||||
|
callback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
|
||||||
|
// TODO: this looks like a spurious callback.
|
||||||
|
callback.expectCallback(CallbackState.NETWORK_CAPABILITIES, vpnNetworkAgent);
|
||||||
|
callback.assertNoCallback();
|
||||||
|
|
||||||
|
assertTrue(vpnNetworkAgent.getScore() > mEthernetNetworkAgent.getScore());
|
||||||
|
assertEquals(ConnectivityConstants.VPN_DEFAULT_SCORE, vpnNetworkAgent.getScore());
|
||||||
|
assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
|
||||||
|
|
||||||
|
NetworkCapabilities nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
|
||||||
|
assertFalse(nc.hasCapability(NET_CAPABILITY_VALIDATED));
|
||||||
|
assertTrue(nc.hasCapability(NET_CAPABILITY_INTERNET));
|
||||||
|
|
||||||
|
assertFalse(NetworkMonitorUtils.isValidationRequired(vpnNetworkAgent.mNetworkCapabilities));
|
||||||
|
assertTrue(NetworkMonitorUtils.isPrivateDnsValidationRequired(
|
||||||
|
vpnNetworkAgent.mNetworkCapabilities));
|
||||||
|
|
||||||
|
// Pretend that the VPN network validates.
|
||||||
|
vpnNetworkAgent.setNetworkValid();
|
||||||
|
vpnNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
|
||||||
|
// Expect to see the validated capability, but no other changes, because the VPN is already
|
||||||
|
// the default network for the app.
|
||||||
|
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, vpnNetworkAgent);
|
||||||
|
callback.assertNoCallback();
|
||||||
|
|
||||||
|
vpnNetworkAgent.disconnect();
|
||||||
|
callback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
|
||||||
|
callback.expectAvailableCallbacksValidated(mEthernetNetworkAgent);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testVpnSetUnderlyingNetworks() {
|
public void testVpnSetUnderlyingNetworks() {
|
||||||
final int uid = Process.myUid();
|
final int uid = Process.myUid();
|
||||||
|
|||||||
Reference in New Issue
Block a user