Set up forwarding rules for local network agents
Test: CSLocalAgentTests, new tests for this Change-Id: I8994af350a1799ab5f6ebb2872f2abfaf174bd61
This commit is contained in:
@@ -21,13 +21,19 @@ import android.net.LinkAddress
|
||||
import android.net.LinkProperties
|
||||
import android.net.LocalNetworkConfig
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.NetworkCapabilities.NET_CAPABILITY_DUN
|
||||
import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
|
||||
import android.net.NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK
|
||||
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED
|
||||
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING
|
||||
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED
|
||||
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED
|
||||
import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
|
||||
import android.net.NetworkCapabilities.TRANSPORT_WIFI
|
||||
import android.net.NetworkRequest
|
||||
import android.net.NetworkScore
|
||||
import android.net.NetworkScore.KEEP_CONNECTED_FOR_TEST
|
||||
import android.net.NetworkScore.KEEP_CONNECTED_LOCAL_NETWORK
|
||||
import android.net.RouteInfo
|
||||
import android.os.Build
|
||||
import com.android.testutils.DevSdkIgnoreRule
|
||||
@@ -40,8 +46,17 @@ import com.android.testutils.RecorderCallback.CallbackEntry.Lost
|
||||
import com.android.testutils.TestableNetworkCallback
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mockito.clearInvocations
|
||||
import org.mockito.Mockito.inOrder
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.timeout
|
||||
import org.mockito.Mockito.verify
|
||||
import kotlin.test.assertFailsWith
|
||||
|
||||
private const val TIMEOUT_MS = 200L
|
||||
private const val MEDIUM_TIMEOUT_MS = 1_000L
|
||||
private const val LONG_TIMEOUT_MS = 5_000
|
||||
|
||||
private fun nc(transport: Int, vararg caps: Int) = NetworkCapabilities.Builder().apply {
|
||||
addTransportType(transport)
|
||||
caps.forEach {
|
||||
@@ -60,6 +75,12 @@ private fun lp(iface: String) = LinkProperties().apply {
|
||||
addRoute(RouteInfo(IpPrefix("0.0.0.0/0"), null, null))
|
||||
}
|
||||
|
||||
// This allows keeping all the networks connected without having to file individual requests
|
||||
// for them.
|
||||
private fun keepScore() = FromS(
|
||||
NetworkScore.Builder().setKeepConnectedReason(KEEP_CONNECTED_FOR_TEST).build()
|
||||
)
|
||||
|
||||
@RunWith(DevSdkIgnoreRunner::class)
|
||||
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
|
||||
class CSLocalAgentTests : CSTest() {
|
||||
@@ -125,7 +146,8 @@ class CSLocalAgentTests : CSTest() {
|
||||
cb)
|
||||
|
||||
// Set up a local agent that should forward its traffic to the best DUN upstream.
|
||||
val localAgent = Agent(nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK),
|
||||
val localAgent = Agent(
|
||||
nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK),
|
||||
lp = lp("local0"),
|
||||
lnc = LocalNetworkConfig.Builder().build(),
|
||||
)
|
||||
@@ -145,4 +167,242 @@ class CSLocalAgentTests : CSTest() {
|
||||
|
||||
localAgent.disconnect()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUnregisterUpstreamAfterReplacement_SameIfaceName() {
|
||||
doTestUnregisterUpstreamAfterReplacement(true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUnregisterUpstreamAfterReplacement_DifferentIfaceName() {
|
||||
doTestUnregisterUpstreamAfterReplacement(false)
|
||||
}
|
||||
|
||||
fun doTestUnregisterUpstreamAfterReplacement(sameIfaceName: Boolean) {
|
||||
deps.setBuildSdk(VERSION_V)
|
||||
val cb = TestableNetworkCallback()
|
||||
cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities().build(), cb)
|
||||
|
||||
// Set up a local agent that should forward its traffic to the best wifi upstream.
|
||||
val localAgent = Agent(nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK),
|
||||
lp = lp("local0"),
|
||||
lnc = LocalNetworkConfig.Builder()
|
||||
.setUpstreamSelector(NetworkRequest.Builder()
|
||||
.addTransportType(TRANSPORT_WIFI)
|
||||
.build())
|
||||
.build(),
|
||||
score = FromS(NetworkScore.Builder()
|
||||
.setKeepConnectedReason(KEEP_CONNECTED_LOCAL_NETWORK)
|
||||
.build())
|
||||
)
|
||||
localAgent.connect()
|
||||
|
||||
cb.expectAvailableCallbacks(localAgent.network, validated = false)
|
||||
|
||||
val wifiAgent = Agent(lp = lp("wifi0"),
|
||||
nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET))
|
||||
wifiAgent.connect()
|
||||
|
||||
cb.expectAvailableCallbacks(wifiAgent.network, validated = false)
|
||||
|
||||
clearInvocations(netd)
|
||||
val inOrder = inOrder(netd)
|
||||
wifiAgent.unregisterAfterReplacement(LONG_TIMEOUT_MS)
|
||||
waitForIdle()
|
||||
inOrder.verify(netd).ipfwdRemoveInterfaceForward("local0", "wifi0")
|
||||
inOrder.verify(netd).networkDestroy(wifiAgent.network.netId)
|
||||
|
||||
val wifiIface2 = if (sameIfaceName) "wifi0" else "wifi1"
|
||||
val wifiAgent2 = Agent(lp = lp(wifiIface2),
|
||||
nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET))
|
||||
wifiAgent2.connect()
|
||||
|
||||
cb.expectAvailableCallbacks(wifiAgent2.network, validated = false)
|
||||
cb.expect<Lost> { it.network == wifiAgent.network }
|
||||
|
||||
inOrder.verify(netd).ipfwdAddInterfaceForward("local0", wifiIface2)
|
||||
if (sameIfaceName) {
|
||||
inOrder.verify(netd, never()).ipfwdRemoveInterfaceForward(any(), any())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUnregisterUpstreamAfterReplacement_neverReplaced() {
|
||||
deps.setBuildSdk(VERSION_V)
|
||||
val cb = TestableNetworkCallback()
|
||||
cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities().build(), cb)
|
||||
|
||||
// Set up a local agent that should forward its traffic to the best wifi upstream.
|
||||
val localAgent = Agent(nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK),
|
||||
lp = lp("local0"),
|
||||
lnc = LocalNetworkConfig.Builder()
|
||||
.setUpstreamSelector(NetworkRequest.Builder()
|
||||
.addTransportType(TRANSPORT_WIFI)
|
||||
.build())
|
||||
.build(),
|
||||
score = FromS(NetworkScore.Builder()
|
||||
.setKeepConnectedReason(KEEP_CONNECTED_LOCAL_NETWORK)
|
||||
.build())
|
||||
)
|
||||
localAgent.connect()
|
||||
|
||||
cb.expectAvailableCallbacks(localAgent.network, validated = false)
|
||||
|
||||
val wifiAgent = Agent(lp = lp("wifi0"),
|
||||
nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET))
|
||||
wifiAgent.connect()
|
||||
|
||||
cb.expectAvailableCallbacksUnvalidated(wifiAgent)
|
||||
|
||||
clearInvocations(netd)
|
||||
wifiAgent.unregisterAfterReplacement(TIMEOUT_MS.toInt())
|
||||
waitForIdle()
|
||||
verify(netd).networkDestroy(wifiAgent.network.netId)
|
||||
verify(netd).ipfwdRemoveInterfaceForward("local0", "wifi0")
|
||||
|
||||
cb.expect<Lost> { it.network == wifiAgent.network }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUnregisterLocalAgentAfterReplacement() {
|
||||
deps.setBuildSdk(VERSION_V)
|
||||
|
||||
val localCb = TestableNetworkCallback()
|
||||
cm.requestNetwork(NetworkRequest.Builder().clearCapabilities()
|
||||
.addCapability(NET_CAPABILITY_LOCAL_NETWORK)
|
||||
.build(),
|
||||
localCb)
|
||||
|
||||
val cb = TestableNetworkCallback()
|
||||
cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities().build(), cb)
|
||||
|
||||
val localNc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK)
|
||||
val lnc = LocalNetworkConfig.Builder()
|
||||
.setUpstreamSelector(NetworkRequest.Builder()
|
||||
.addTransportType(TRANSPORT_WIFI)
|
||||
.build())
|
||||
.build()
|
||||
val localScore = FromS(NetworkScore.Builder().build())
|
||||
|
||||
// Set up a local agent that should forward its traffic to the best wifi upstream.
|
||||
val localAgent = Agent(nc = localNc, lp = lp("local0"), lnc = lnc, score = localScore)
|
||||
localAgent.connect()
|
||||
|
||||
localCb.expectAvailableCallbacks(localAgent.network, validated = false)
|
||||
cb.expectAvailableCallbacks(localAgent.network, validated = false)
|
||||
|
||||
val wifiAgent = Agent(lp = lp("wifi0"), nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET))
|
||||
wifiAgent.connect()
|
||||
|
||||
cb.expectAvailableCallbacksUnvalidated(wifiAgent)
|
||||
|
||||
verify(netd).ipfwdAddInterfaceForward("local0", "wifi0")
|
||||
|
||||
localAgent.unregisterAfterReplacement(LONG_TIMEOUT_MS)
|
||||
|
||||
val localAgent2 = Agent(nc = localNc, lp = lp("local0"), lnc = lnc, score = localScore)
|
||||
localAgent2.connect()
|
||||
|
||||
localCb.expectAvailableCallbacks(localAgent2.network, validated = false)
|
||||
cb.expectAvailableCallbacks(localAgent2.network, validated = false)
|
||||
cb.expect<Lost> { it.network == localAgent.network }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDestroyedNetworkAsSelectedUpstream() {
|
||||
deps.setBuildSdk(VERSION_V)
|
||||
val cb = TestableNetworkCallback()
|
||||
cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities().build(), cb)
|
||||
|
||||
val wifiAgent = Agent(lp = lp("wifi0"), nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET))
|
||||
wifiAgent.connect()
|
||||
cb.expectAvailableCallbacksUnvalidated(wifiAgent)
|
||||
|
||||
// Set up a local agent that should forward its traffic to the best wifi upstream.
|
||||
val localAgent = Agent(nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK),
|
||||
lp = lp("local0"),
|
||||
lnc = LocalNetworkConfig.Builder()
|
||||
.setUpstreamSelector(NetworkRequest.Builder()
|
||||
.addTransportType(TRANSPORT_WIFI)
|
||||
.build())
|
||||
.build(),
|
||||
score = FromS(NetworkScore.Builder()
|
||||
.setKeepConnectedReason(KEEP_CONNECTED_LOCAL_NETWORK)
|
||||
.build())
|
||||
)
|
||||
|
||||
// ...but destroy the wifi agent before connecting it
|
||||
wifiAgent.unregisterAfterReplacement(LONG_TIMEOUT_MS)
|
||||
|
||||
localAgent.connect()
|
||||
cb.expectAvailableCallbacks(localAgent.network, validated = false)
|
||||
|
||||
verify(netd).ipfwdAddInterfaceForward("local0", "wifi0")
|
||||
verify(netd).ipfwdRemoveInterfaceForward("local0", "wifi0")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testForwardingRules() {
|
||||
deps.setBuildSdk(VERSION_V)
|
||||
// Set up a local agent that should forward its traffic to the best DUN upstream.
|
||||
val lnc = LocalNetworkConfig.Builder()
|
||||
.setUpstreamSelector(NetworkRequest.Builder()
|
||||
.addCapability(NET_CAPABILITY_DUN)
|
||||
.build())
|
||||
.build()
|
||||
val localAgent = Agent(nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK),
|
||||
lp = lp("local0"),
|
||||
lnc = lnc,
|
||||
score = FromS(NetworkScore.Builder()
|
||||
.setKeepConnectedReason(KEEP_CONNECTED_LOCAL_NETWORK)
|
||||
.build())
|
||||
)
|
||||
localAgent.connect()
|
||||
|
||||
val wifiAgent = Agent(score = keepScore(), lp = lp("wifi0"),
|
||||
nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET))
|
||||
val cellAgentDun = Agent(score = keepScore(), lp = lp("cell0"),
|
||||
nc = nc(TRANSPORT_CELLULAR, NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN))
|
||||
val wifiAgentDun = Agent(score = keepScore(), lp = lp("wifi1"),
|
||||
nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN))
|
||||
|
||||
val inOrder = inOrder(netd)
|
||||
inOrder.verify(netd, never()).ipfwdAddInterfaceForward(any(), any())
|
||||
|
||||
wifiAgent.connect()
|
||||
inOrder.verify(netd, never()).ipfwdAddInterfaceForward(any(), any())
|
||||
|
||||
cellAgentDun.connect()
|
||||
inOrder.verify(netd).ipfwdEnableForwarding(any())
|
||||
inOrder.verify(netd).ipfwdAddInterfaceForward("local0", "cell0")
|
||||
|
||||
wifiAgentDun.connect()
|
||||
inOrder.verify(netd).ipfwdRemoveInterfaceForward("local0", "cell0")
|
||||
inOrder.verify(netd).ipfwdAddInterfaceForward("local0", "wifi1")
|
||||
|
||||
// Make sure sending the same config again doesn't do anything
|
||||
repeat(5) {
|
||||
localAgent.sendLocalNetworkConfig(lnc)
|
||||
}
|
||||
inOrder.verifyNoMoreInteractions()
|
||||
|
||||
wifiAgentDun.disconnect()
|
||||
inOrder.verify(netd).ipfwdRemoveInterfaceForward("local0", "wifi1")
|
||||
// This can take a little bit of time because it needs to wait for the rematch
|
||||
inOrder.verify(netd, timeout(MEDIUM_TIMEOUT_MS)).ipfwdAddInterfaceForward("local0", "cell0")
|
||||
|
||||
cellAgentDun.disconnect()
|
||||
inOrder.verify(netd).ipfwdRemoveInterfaceForward("local0", "cell0")
|
||||
inOrder.verify(netd).ipfwdDisableForwarding(any())
|
||||
|
||||
val wifiAgentDun2 = Agent(score = keepScore(), lp = lp("wifi2"),
|
||||
nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN))
|
||||
wifiAgentDun2.connect()
|
||||
inOrder.verify(netd).ipfwdEnableForwarding(any())
|
||||
inOrder.verify(netd).ipfwdAddInterfaceForward("local0", "wifi2")
|
||||
|
||||
localAgent.disconnect()
|
||||
inOrder.verify(netd).ipfwdRemoveInterfaceForward("local0", "wifi2")
|
||||
inOrder.verify(netd).ipfwdDisableForwarding(any())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,6 +168,7 @@ class CSAgentWrapper(
|
||||
}
|
||||
|
||||
fun unregisterAfterReplacement(timeoutMs: Int) = agent.unregisterAfterReplacement(timeoutMs)
|
||||
|
||||
fun sendLocalNetworkConfig(lnc: LocalNetworkConfig) = agent.sendLocalNetworkConfig(lnc)
|
||||
fun sendNetworkCapabilities(nc: NetworkCapabilities) = agent.sendNetworkCapabilities(nc)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user